* gxditview.c (main): Handle `-help' and `--help' correctly.
[s-roff.git] / src / xditview / device.c
blob264f681ea679815bb90d6d2460286ebc38977c7d
1 /* device.c */
3 #include <stdio.h>
4 #include <ctype.h>
6 #include <X11/Xos.h>
7 #include <X11/Intrinsic.h>
9 #include "device.h"
11 #ifndef FONTPATH
12 #define FONTPATH "/usr/local/share/groff/font:/usr/local/lib/font:/usr/lib/font"
13 #endif
15 #ifndef isascii
16 #define isascii(c) (1)
17 #endif
19 extern void exit();
20 #ifndef strtok
21 extern char *strtok();
22 #endif
23 #ifndef strchr
24 extern char *strchr();
25 #endif
26 #ifndef getenv
27 extern char *getenv();
28 #endif
30 /* Name of environment variable containing path to be used for
31 searching for device and font description files. */
32 #define FONTPATH_ENV_VAR "GROFF_FONT_PATH"
34 #define WS " \t\r\n"
36 #ifndef INT_MIN
37 /* Minimum and maximum values a `signed int' can hold. */
38 #define INT_MIN (-INT_MAX-1)
39 #define INT_MAX 2147483647
40 #endif
42 #define CHAR_TABLE_SIZE 307
44 struct _DeviceFont {
45 char *name;
46 int special;
47 DeviceFont *next;
48 Device *dev;
49 struct charinfo *char_table[CHAR_TABLE_SIZE];
50 struct charinfo *code_table[256];
53 struct charinfo {
54 int width;
55 int code;
56 struct charinfo *next;
57 struct charinfo *code_next;
58 char name[1];
61 static char *current_filename = 0;
62 static int current_lineno = -1;
64 static void error();
65 static FILE *open_device_file();
66 static DeviceFont *load_font();
67 static Device *new_device();
68 static DeviceFont *new_font();
69 static void delete_font();
70 static unsigned hash_name();
71 static struct charinfo *add_char();
72 static int read_charset_section();
73 static char *canonicalize_name();
75 static
76 Device *new_device(name)
77 char *name;
79 Device *dev;
81 dev = XtNew(Device);
82 dev->sizescale = 1;
83 dev->res = 0;
84 dev->unitwidth = 0;
85 dev->fonts = 0;
86 dev->X11 = 0;
87 dev->paperlength = 0;
88 dev->paperwidth = 0;
89 dev->name = XtNewString(name);
90 return dev;
93 void device_destroy(dev)
94 Device *dev;
96 DeviceFont *f;
98 if (!dev)
99 return;
100 f = dev->fonts;
101 while (f) {
102 DeviceFont *tem = f;
103 f = f->next;
104 delete_font(tem);
107 XtFree(dev->name);
108 XtFree((char *)dev);
111 Device *device_load(name)
112 char *name;
114 Device *dev;
115 FILE *fp;
116 int err = 0;
117 char buf[256];
119 fp = open_device_file(name, "DESC", &current_filename);
120 if (!fp)
121 return 0;
122 dev = new_device(name);
123 current_lineno = 0;
124 while (fgets(buf, sizeof(buf), fp)) {
125 char *p;
126 current_lineno++;
127 p = strtok(buf, WS);
128 if (p) {
129 int *np = 0;
130 char *q;
132 if (strcmp(p, "charset") == 0)
133 break;
134 if (strcmp(p, "X11") == 0)
135 dev->X11 = 1;
136 else if (strcmp(p, "sizescale") == 0)
137 np = &dev->sizescale;
138 else if (strcmp(p, "res") == 0)
139 np = &dev->res;
140 else if (strcmp(p, "unitwidth") == 0)
141 np = &dev->unitwidth;
142 else if (strcmp(p, "paperwidth") == 0)
143 np = &dev->paperwidth;
144 else if (strcmp(p, "paperlength") == 0)
145 np = &dev->paperlength;
147 if (np) {
148 q = strtok((char *)0, WS);
149 if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) {
150 error("bad argument");
151 err = 1;
152 break;
157 fclose(fp);
158 current_lineno = -1;
159 if (!err) {
160 if (dev->res == 0) {
161 error("missing res line");
162 err = 1;
164 else if (dev->unitwidth == 0) {
165 error("missing unitwidth line");
166 err = 1;
169 if (dev->paperlength == 0)
170 dev->paperlength = dev->res*11;
171 if (dev->paperwidth == 0)
172 dev->paperwidth = dev->res*8 + dev->res/2;
173 if (err) {
174 device_destroy(dev);
175 dev = 0;
177 XtFree(current_filename);
178 current_filename = 0;
179 return dev;
183 DeviceFont *device_find_font(dev, name)
184 Device *dev;
185 char *name;
187 DeviceFont *f;
189 if (!dev)
190 return 0;
191 for (f = dev->fonts; f; f = f->next)
192 if (strcmp(f->name, name) == 0)
193 return f;
194 return load_font(dev, name);
197 static
198 DeviceFont *load_font(dev, name)
199 Device *dev;
200 char *name;
202 FILE *fp;
203 char buf[256];
204 DeviceFont *f;
205 int special = 0;
207 fp = open_device_file(dev->name, name, &current_filename);
208 if (!fp)
209 return 0;
210 current_lineno = 0;
211 for (;;) {
212 char *p;
214 if (!fgets(buf, sizeof(buf), fp)) {
215 error("no charset line");
216 return 0;
218 current_lineno++;
219 p = strtok(buf, WS);
220 /* charset must be on a line by itself */
221 if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0)
222 break;
223 if (p && strcmp(p, "special") == 0)
224 special = 1;
226 f = new_font(name, dev);
227 f->special = special;
228 if (!read_charset_section(f, fp)) {
229 delete_font(f);
230 f = 0;
232 else {
233 f->next = dev->fonts;
234 dev->fonts = f;
236 fclose(fp);
237 XtFree(current_filename);
238 current_filename = 0;
239 return f;
242 static
243 DeviceFont *new_font(name, dev)
244 char *name;
245 Device *dev;
247 int i;
248 DeviceFont *f;
250 f = XtNew(DeviceFont);
251 f->name = XtNewString(name);
252 f->dev = dev;
253 f->special = 0;
254 f->next = 0;
255 for (i = 0; i < CHAR_TABLE_SIZE; i++)
256 f->char_table[i] = 0;
257 for (i = 0; i < 256; i++)
258 f->code_table[i] = 0;
259 return f;
262 static
263 void delete_font(f)
264 DeviceFont *f;
266 int i;
268 if (!f)
269 return;
270 XtFree(f->name);
271 for (i = 0; i < CHAR_TABLE_SIZE; i++) {
272 struct charinfo *ptr = f->char_table[i];
273 while (ptr) {
274 struct charinfo *tem = ptr;
275 ptr = ptr->next;
276 XtFree((char *)tem);
279 XtFree((char *)f);
283 static
284 unsigned hash_name(name)
285 char *name;
287 unsigned n = 0;
288 /* XXX do better than this */
289 while (*name)
290 n = (n << 1) ^ *name++;
292 return n;
295 static
296 int scale_round(n, x, y)
297 int n, x, y;
299 int y2;
301 if (x == 0)
302 return 0;
303 y2 = y/2;
304 if (n >= 0) {
305 if (n <= (INT_MAX - y2)/x)
306 return (n*x + y2)/y;
308 else if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
309 return (n*x - y2)/y;
310 return (int)(n*(double)x/(double)y + .5);
313 static
314 char *canonicalize_name(s)
315 char *s;
317 static char ch[2];
318 if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
319 char *p;
320 int n;
322 for (p = s + 4; *p; p++)
323 if (!isascii(*p) || !isdigit((unsigned char)*p))
324 return s;
325 n = atoi(s + 4);
326 if (n >= 0 && n <= 0xff) {
327 ch[0] = (char)n;
328 return ch;
331 return s;
334 /* Return 1 if the character is present in the font; widthp gets the
335 width if non-null. */
337 int device_char_width(f, ps, name, widthp)
338 DeviceFont *f;
339 int ps;
340 char *name;
341 int *widthp;
343 struct charinfo *p;
345 name = canonicalize_name(name);
346 for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) {
347 if (!p)
348 return 0;
349 if (strcmp(p->name, name) == 0)
350 break;
352 *widthp = scale_round(p->width, ps, f->dev->unitwidth);
353 return 1;
356 int device_code_width(f, ps, code, widthp)
357 DeviceFont *f;
358 int ps;
359 int code;
360 int *widthp;
362 struct charinfo *p;
364 for (p = f->code_table[code & 0xff];; p = p->code_next) {
365 if (!p)
366 return 0;
367 if (p->code == code)
368 break;
370 *widthp = scale_round(p->width, ps, f->dev->unitwidth);
371 return 1;
374 char *device_name_for_code(f, code)
375 DeviceFont *f;
376 int code;
378 static struct charinfo *state = 0;
379 if (f)
380 state = f->code_table[code & 0xff];
381 for (; state; state = state->code_next)
382 if (state->code == code && state->name[0] != '\0') {
383 char *name = state->name;
384 state = state->code_next;
385 return name;
387 return 0;
390 int device_font_special(f)
391 DeviceFont *f;
393 return f->special;
396 static
397 struct charinfo *add_char(f, name, width, code)
398 DeviceFont *f;
399 char *name;
400 int width, code;
402 struct charinfo **pp;
403 struct charinfo *ci;
405 name = canonicalize_name(name);
406 if (strcmp(name, "---") == 0)
407 name = "";
409 ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
410 + strlen(name) + 1);
412 strcpy(ci->name, name);
413 ci->width = width;
414 ci->code = code;
416 if (*name != '\0') {
417 pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
418 ci->next = *pp;
419 *pp = ci;
421 pp = &f->code_table[code & 0xff];
422 ci->code_next = *pp;
423 *pp = ci;
424 return ci;
427 /* Return non-zero for success. */
429 static
430 int read_charset_section(f, fp)
431 DeviceFont *f;
432 FILE *fp;
434 struct charinfo *last_charinfo = 0;
435 char buf[256];
437 while (fgets(buf, sizeof(buf), fp)) {
438 char *name;
439 int width;
440 int code;
441 char *p;
443 current_lineno++;
444 name = strtok(buf, WS);
445 if (!name)
446 continue; /* ignore blank lines */
447 p = strtok((char *)0, WS);
448 if (!p) /* end of charset section */
449 break;
450 if (strcmp(p, "\"") == 0) {
451 if (!last_charinfo) {
452 error("first line of charset section cannot use `\"'");
453 return 0;
455 else
456 (void)add_char(f, name,
457 last_charinfo->width, last_charinfo->code);
459 else {
460 char *q;
461 if (sscanf(p, "%d", &width) != 1) {
462 error("bad width field");
463 return 0;
465 p = strtok((char *)0, WS);
466 if (!p) {
467 error("missing type field");
468 return 0;
470 p = strtok((char *)0, WS);
471 if (!p) {
472 error("missing code field");
473 return 0;
475 code = (int)strtol(p, &q, 0);
476 if (q == p) {
477 error("bad code field");
478 return 0;
480 last_charinfo = add_char(f, name, width, code);
483 return 1;
486 static
487 FILE *find_file(file, result)
488 char *file, **result;
490 char *buf = NULL;
491 int bufsiz = 0;
492 int flen;
493 FILE *fp;
494 char *path;
495 char *env;
497 env = getenv(FONTPATH_ENV_VAR);
498 path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0)
499 + strlen(FONTPATH) + 1);
500 *path = '\0';
501 if (env && *env) {
502 strcat(path, env);
503 strcat(path, ":");
505 strcat(path, FONTPATH);
507 *result = NULL;
509 if (file == NULL)
510 return NULL;
511 if (*file == '\0')
512 return NULL;
514 if (*file == '/') {
515 fp = fopen(file, "r");
516 if (fp)
517 *result = XtNewString(file);
518 return fp;
521 flen = strlen(file);
523 while (*path) {
524 int len;
525 char *start, *end;
527 start = path;
528 end = strchr(path, ':');
529 if (end)
530 path = end + 1;
531 else
532 path = end = strchr(path, '\0');
533 if (start >= end)
534 continue;
535 if (end[-1] == '/')
536 --end;
537 len = (end - start) + 1 + flen + 1;
538 if (len > bufsiz) {
539 if (buf)
540 buf = XtRealloc(buf, len);
541 else
542 buf = XtMalloc(len);
543 bufsiz = len;
545 memcpy(buf, start, end - start);
546 buf[end - start] = '/';
547 strcpy(buf + (end - start) + 1, file);
548 fp = fopen(buf, "r");
549 if (fp) {
550 *result = buf;
551 return fp;
554 XtFree(buf);
555 return NULL;
558 static
559 FILE *open_device_file(device_name, file_name, result)
560 char *device_name, *file_name, **result;
562 char *buf, *path;
563 FILE *fp;
565 buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
566 sprintf(buf, "dev%s/%s", device_name, file_name);
567 fp = find_file(buf, result);
568 if (!fp) {
569 fprintf(stderr, "can't find device file `%s'\n", file_name);
570 fflush(stderr);
572 XtFree(buf);
573 return fp;
576 static
577 void error(s)
578 char *s;
580 if (current_filename) {
581 fprintf(stderr, "%s:", current_filename);
582 if (current_lineno > 0)
583 fprintf(stderr, "%d:", current_lineno);
584 putc(' ', stderr);
586 fputs(s, stderr);
587 putc('\n', stderr);
588 fflush(stderr);
592 Local Variables:
593 c-indent-level: 4
594 c-continued-statement-offset: 4
595 c-brace-offset: -4
596 c-argdecl-indent: 4
597 c-label-offset: -4
598 c-tab-always-indent: nil
599 End: