* src/roff/troff/node.cpp (suppress_node::tprint): Don't expect
[s-roff.git] / src / xditview / device.c
blob88e5ae5e71c4c3c0db07e5ba363c4114f239b854
1 /* device.c */
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <stdlib.h>
7 #include <X11/Xos.h>
8 #include <X11/Intrinsic.h>
10 #include "device.h"
12 #ifndef FONTPATH
13 #define FONTPATH "/usr/local/share/groff/font:/usr/local/lib/font:/usr/lib/font"
14 #endif
16 #ifndef isascii
17 #define isascii(c) (1)
18 #endif
20 extern void exit();
21 #ifndef strtok
22 extern char *strtok();
23 #endif
24 #ifndef strchr
25 extern char *strchr();
26 #endif
27 #ifndef getenv
28 extern char *getenv();
29 #endif
31 /* Name of environment variable containing path to be used for
32 searching for device and font description files. */
33 #define FONTPATH_ENV_VAR "GROFF_FONT_PATH"
35 #define WS " \t\r\n"
37 #ifndef INT_MIN
38 /* Minimum and maximum values a `signed int' can hold. */
39 #define INT_MIN (-INT_MAX-1)
40 #define INT_MAX 2147483647
41 #endif
43 #define CHAR_TABLE_SIZE 307
45 struct _DeviceFont {
46 char *name;
47 int special;
48 DeviceFont *next;
49 Device *dev;
50 struct charinfo *char_table[CHAR_TABLE_SIZE];
51 struct charinfo *code_table[256];
54 struct charinfo {
55 int width;
56 int code;
57 struct charinfo *next;
58 struct charinfo *code_next;
59 char name[1];
62 static char *current_filename = 0;
63 static int current_lineno = -1;
65 static void error();
66 static FILE *open_device_file();
67 static DeviceFont *load_font();
68 static Device *new_device();
69 static DeviceFont *new_font();
70 static void delete_font();
71 static unsigned hash_name();
72 static struct charinfo *add_char();
73 static int read_charset_section();
74 static char *canonicalize_name();
76 static
77 Device *new_device(name)
78 char *name;
80 Device *dev;
82 dev = XtNew(Device);
83 dev->sizescale = 1;
84 dev->res = 0;
85 dev->unitwidth = 0;
86 dev->fonts = 0;
87 dev->X11 = 0;
88 dev->paperlength = 0;
89 dev->paperwidth = 0;
90 dev->name = XtNewString(name);
91 return dev;
94 void device_destroy(dev)
95 Device *dev;
97 DeviceFont *f;
99 if (!dev)
100 return;
101 f = dev->fonts;
102 while (f) {
103 DeviceFont *tem = f;
104 f = f->next;
105 delete_font(tem);
108 XtFree(dev->name);
109 XtFree((char *)dev);
112 Device *device_load(name)
113 char *name;
115 Device *dev;
116 FILE *fp;
117 int err = 0;
118 char buf[256];
120 fp = open_device_file(name, "DESC", &current_filename);
121 if (!fp)
122 return 0;
123 dev = new_device(name);
124 current_lineno = 0;
125 while (fgets(buf, sizeof(buf), fp)) {
126 char *p;
127 current_lineno++;
128 p = strtok(buf, WS);
129 if (p) {
130 int *np = 0;
131 char *q;
133 if (strcmp(p, "charset") == 0)
134 break;
135 if (strcmp(p, "X11") == 0)
136 dev->X11 = 1;
137 else if (strcmp(p, "sizescale") == 0)
138 np = &dev->sizescale;
139 else if (strcmp(p, "res") == 0)
140 np = &dev->res;
141 else if (strcmp(p, "unitwidth") == 0)
142 np = &dev->unitwidth;
143 else if (strcmp(p, "paperwidth") == 0)
144 np = &dev->paperwidth;
145 else if (strcmp(p, "paperlength") == 0)
146 np = &dev->paperlength;
148 if (np) {
149 q = strtok((char *)0, WS);
150 if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) {
151 error("bad argument");
152 err = 1;
153 break;
158 fclose(fp);
159 current_lineno = -1;
160 if (!err) {
161 if (dev->res == 0) {
162 error("missing res line");
163 err = 1;
165 else if (dev->unitwidth == 0) {
166 error("missing unitwidth line");
167 err = 1;
170 if (dev->paperlength == 0)
171 dev->paperlength = dev->res*11;
172 if (dev->paperwidth == 0)
173 dev->paperwidth = dev->res*8 + dev->res/2;
174 if (err) {
175 device_destroy(dev);
176 dev = 0;
178 XtFree(current_filename);
179 current_filename = 0;
180 return dev;
184 DeviceFont *device_find_font(dev, name)
185 Device *dev;
186 char *name;
188 DeviceFont *f;
190 if (!dev)
191 return 0;
192 for (f = dev->fonts; f; f = f->next)
193 if (strcmp(f->name, name) == 0)
194 return f;
195 return load_font(dev, name);
198 static
199 DeviceFont *load_font(dev, name)
200 Device *dev;
201 char *name;
203 FILE *fp;
204 char buf[256];
205 DeviceFont *f;
206 int special = 0;
208 fp = open_device_file(dev->name, name, &current_filename);
209 if (!fp)
210 return 0;
211 current_lineno = 0;
212 for (;;) {
213 char *p;
215 if (!fgets(buf, sizeof(buf), fp)) {
216 error("no charset line");
217 return 0;
219 current_lineno++;
220 p = strtok(buf, WS);
221 /* charset must be on a line by itself */
222 if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0)
223 break;
224 if (p && strcmp(p, "special") == 0)
225 special = 1;
227 f = new_font(name, dev);
228 f->special = special;
229 if (!read_charset_section(f, fp)) {
230 delete_font(f);
231 f = 0;
233 else {
234 f->next = dev->fonts;
235 dev->fonts = f;
237 fclose(fp);
238 XtFree(current_filename);
239 current_filename = 0;
240 return f;
243 static
244 DeviceFont *new_font(name, dev)
245 char *name;
246 Device *dev;
248 int i;
249 DeviceFont *f;
251 f = XtNew(DeviceFont);
252 f->name = XtNewString(name);
253 f->dev = dev;
254 f->special = 0;
255 f->next = 0;
256 for (i = 0; i < CHAR_TABLE_SIZE; i++)
257 f->char_table[i] = 0;
258 for (i = 0; i < 256; i++)
259 f->code_table[i] = 0;
260 return f;
263 static
264 void delete_font(f)
265 DeviceFont *f;
267 int i;
269 if (!f)
270 return;
271 XtFree(f->name);
272 for (i = 0; i < CHAR_TABLE_SIZE; i++) {
273 struct charinfo *ptr = f->char_table[i];
274 while (ptr) {
275 struct charinfo *tem = ptr;
276 ptr = ptr->next;
277 XtFree((char *)tem);
280 XtFree((char *)f);
284 static
285 unsigned hash_name(name)
286 char *name;
288 unsigned n = 0;
289 /* XXX do better than this */
290 while (*name)
291 n = (n << 1) ^ *name++;
293 return n;
296 static
297 int scale_round(n, x, y)
298 int n, x, y;
300 int y2;
302 if (x == 0)
303 return 0;
304 y2 = y/2;
305 if (n >= 0) {
306 if (n <= (INT_MAX - y2)/x)
307 return (n*x + y2)/y;
308 return (int)(n*(double)x/(double)y + .5);
310 else {
311 if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
312 return (n*x - y2)/y;
313 return (int)(n*(double)x/(double)y + .5);
317 static
318 char *canonicalize_name(s)
319 char *s;
321 static char ch[2];
322 if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
323 char *p;
324 int n;
326 for (p = s + 4; *p; p++)
327 if (!isascii(*p) || !isdigit((unsigned char)*p))
328 return s;
329 n = atoi(s + 4);
330 if (n >= 0 && n <= 0xff) {
331 ch[0] = (char)n;
332 return ch;
335 return s;
338 /* Return 1 if the character is present in the font; widthp gets the
339 width if non-null. */
341 int device_char_width(f, ps, name, widthp)
342 DeviceFont *f;
343 int ps;
344 char *name;
345 int *widthp;
347 struct charinfo *p;
349 name = canonicalize_name(name);
350 for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) {
351 if (!p)
352 return 0;
353 if (strcmp(p->name, name) == 0)
354 break;
356 *widthp = scale_round(p->width, ps, f->dev->unitwidth);
357 return 1;
360 int device_code_width(f, ps, code, widthp)
361 DeviceFont *f;
362 int ps;
363 int code;
364 int *widthp;
366 struct charinfo *p;
368 for (p = f->code_table[code & 0xff];; p = p->code_next) {
369 if (!p)
370 return 0;
371 if (p->code == code)
372 break;
374 *widthp = scale_round(p->width, ps, f->dev->unitwidth);
375 return 1;
378 char *device_name_for_code(f, code)
379 DeviceFont *f;
380 int code;
382 static struct charinfo *state = 0;
383 if (f)
384 state = f->code_table[code & 0xff];
385 for (; state; state = state->code_next)
386 if (state->code == code && state->name[0] != '\0') {
387 char *name = state->name;
388 state = state->code_next;
389 return name;
391 return 0;
394 int device_font_special(f)
395 DeviceFont *f;
397 return f->special;
400 static
401 struct charinfo *add_char(f, name, width, code)
402 DeviceFont *f;
403 char *name;
404 int width, code;
406 struct charinfo **pp;
407 struct charinfo *ci;
409 name = canonicalize_name(name);
410 if (strcmp(name, "---") == 0)
411 name = "";
413 ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
414 + strlen(name) + 1);
416 strcpy(ci->name, name);
417 ci->width = width;
418 ci->code = code;
420 if (*name != '\0') {
421 pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
422 ci->next = *pp;
423 *pp = ci;
425 pp = &f->code_table[code & 0xff];
426 ci->code_next = *pp;
427 *pp = ci;
428 return ci;
431 /* Return non-zero for success. */
433 static
434 int read_charset_section(f, fp)
435 DeviceFont *f;
436 FILE *fp;
438 struct charinfo *last_charinfo = 0;
439 char buf[256];
441 while (fgets(buf, sizeof(buf), fp)) {
442 char *name;
443 int width;
444 int code;
445 char *p;
447 current_lineno++;
448 name = strtok(buf, WS);
449 if (!name)
450 continue; /* ignore blank lines */
451 p = strtok((char *)0, WS);
452 if (!p) /* end of charset section */
453 break;
454 if (strcmp(p, "\"") == 0) {
455 if (!last_charinfo) {
456 error("first line of charset section cannot use `\"'");
457 return 0;
459 else
460 (void)add_char(f, name,
461 last_charinfo->width, last_charinfo->code);
463 else {
464 char *q;
465 if (sscanf(p, "%d", &width) != 1) {
466 error("bad width field");
467 return 0;
469 p = strtok((char *)0, WS);
470 if (!p) {
471 error("missing type field");
472 return 0;
474 p = strtok((char *)0, WS);
475 if (!p) {
476 error("missing code field");
477 return 0;
479 code = (int)strtol(p, &q, 0);
480 if (q == p) {
481 error("bad code field");
482 return 0;
484 last_charinfo = add_char(f, name, width, code);
487 return 1;
490 static
491 FILE *find_file(file, result)
492 char *file, **result;
494 char *buf = NULL;
495 int bufsiz = 0;
496 int flen;
497 FILE *fp;
498 char *path;
499 char *env;
501 env = getenv(FONTPATH_ENV_VAR);
502 path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0)
503 + strlen(FONTPATH) + 1);
504 *path = '\0';
505 if (env && *env) {
506 strcat(path, env);
507 strcat(path, ":");
509 strcat(path, FONTPATH);
511 *result = NULL;
513 if (file == NULL)
514 return NULL;
515 if (*file == '\0')
516 return NULL;
518 if (*file == '/') {
519 fp = fopen(file, "r");
520 if (fp)
521 *result = XtNewString(file);
522 return fp;
525 flen = strlen(file);
527 while (*path) {
528 int len;
529 char *start, *end;
531 start = path;
532 end = strchr(path, ':');
533 if (end)
534 path = end + 1;
535 else
536 path = end = strchr(path, '\0');
537 if (start >= end)
538 continue;
539 if (end[-1] == '/')
540 --end;
541 len = (end - start) + 1 + flen + 1;
542 if (len > bufsiz) {
543 if (buf)
544 buf = XtRealloc(buf, len);
545 else
546 buf = XtMalloc(len);
547 bufsiz = len;
549 memcpy(buf, start, end - start);
550 buf[end - start] = '/';
551 strcpy(buf + (end - start) + 1, file);
552 fp = fopen(buf, "r");
553 if (fp) {
554 *result = buf;
555 return fp;
558 XtFree(buf);
559 return NULL;
562 static
563 FILE *open_device_file(device_name, file_name, result)
564 char *device_name, *file_name, **result;
566 char *buf;
567 FILE *fp;
569 buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
570 sprintf(buf, "dev%s/%s", device_name, file_name);
571 fp = find_file(buf, result);
572 if (!fp) {
573 fprintf(stderr, "can't find device file `%s'\n", file_name);
574 fflush(stderr);
576 XtFree(buf);
577 return fp;
580 static
581 void error(s)
582 char *s;
584 if (current_filename) {
585 fprintf(stderr, "%s:", current_filename);
586 if (current_lineno > 0)
587 fprintf(stderr, "%d:", current_lineno);
588 putc(' ', stderr);
590 fputs(s, stderr);
591 putc('\n', stderr);
592 fflush(stderr);
596 Local Variables:
597 c-indent-level: 4
598 c-continued-statement-offset: 4
599 c-brace-offset: -4
600 c-argdecl-indent: 4
601 c-label-offset: -4
602 c-tab-always-indent: nil
603 End: