Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / xditview / device.c
1 /* device.c */
2
3 #include <stdio.h>
4 #include <ctype.h>
5
6 #include <X11/Xos.h>
7 #include <X11/Intrinsic.h>
8
9 #include "device.h"
10
11 #ifndef FONTPATH
12 #define FONTPATH "/usr/local/share/groff/font:/usr/local/lib/font:/usr/lib/font"
13 #endif
14
15 #ifndef isascii
16 #define isascii(c) (1)
17 #endif
18
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
29
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"
33
34 #define WS " \t\r\n"
35
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
41
42 #define CHAR_TABLE_SIZE 307
43
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];
51 };
52
53 struct charinfo {
54     int width;
55     int code;
56     struct charinfo *next;
57     struct charinfo *code_next;
58     char name[1];
59 };
60
61 static char *current_filename = 0;
62 static int current_lineno = -1;
63
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();
74
75 static
76 Device *new_device(name)
77     char *name;
78 {
79     Device *dev;
80
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;
91 }
92
93 void device_destroy(dev)
94     Device *dev;
95 {
96     DeviceFont *f;
97     
98     if (!dev)
99         return;
100     f = dev->fonts;
101     while (f) {
102         DeviceFont *tem = f;
103         f = f->next;
104         delete_font(tem);
105     }
106     
107     XtFree(dev->name);
108     XtFree((char *)dev);
109 }
110
111 Device *device_load(name)
112     char *name;
113 {
114     Device *dev;
115     FILE *fp;
116     int err = 0;
117     char buf[256];
118
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;
131
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;
146             
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;
153                 }
154             }   
155         }
156     }
157     fclose(fp);
158     current_lineno = -1;
159     if (!err) {
160         if (dev->res == 0) {
161             error("missing res line");
162             err = 1;
163         }
164         else if (dev->unitwidth == 0) {
165             error("missing unitwidth line");
166             err = 1;
167         }
168     }
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;
176     }
177     XtFree(current_filename);
178     current_filename = 0;
179     return dev;
180 }
181
182
183 DeviceFont *device_find_font(dev, name)
184     Device *dev;
185     char *name;
186 {
187     DeviceFont *f;
188
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);
195 }
196
197 static
198 DeviceFont *load_font(dev, name)
199     Device *dev;
200     char *name;
201 {
202     FILE *fp;
203     char buf[256];
204     DeviceFont *f;
205     int special = 0;
206
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;
213
214         if (!fgets(buf, sizeof(buf), fp)) {
215             error("no charset line");
216             return 0;
217         }
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;
225     }
226     f = new_font(name, dev);
227     f->special = special;
228     if (!read_charset_section(f, fp)) {
229         delete_font(f);
230         f = 0;
231     }
232     else {
233         f->next = dev->fonts;
234         dev->fonts = f;
235     }
236     fclose(fp);
237     XtFree(current_filename);
238     current_filename = 0;
239     return f;
240 }
241
242 static
243 DeviceFont *new_font(name, dev)
244     char *name;
245     Device *dev;
246 {
247     int i;
248     DeviceFont *f;
249
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;
260 }
261
262 static
263 void delete_font(f)
264     DeviceFont *f;
265 {
266     int i;
267
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);
277         }
278     }
279     XtFree((char *)f);
280 }
281
282
283 static
284 unsigned hash_name(name)
285     char *name;
286 {
287     unsigned n = 0;
288     /* XXX do better than this */
289     while (*name)
290         n = (n << 1) ^ *name++;
291
292     return n;
293 }
294
295 static
296 int scale_round(n, x, y)
297     int n, x, y;
298 {
299   int y2;
300
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;
307   }
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);
311 }
312
313 static
314 char *canonicalize_name(s)
315     char *s;
316 {
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;
321
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;
329         }
330     }
331     return s;
332 }
333
334 /* Return 1 if the character is present in the font; widthp gets the
335 width if non-null. */
336
337 int device_char_width(f, ps, name, widthp)
338     DeviceFont *f;
339     int ps;
340     char *name;
341     int *widthp;
342 {
343     struct charinfo *p;
344
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;
351     }
352     *widthp = scale_round(p->width, ps, f->dev->unitwidth);
353     return 1;
354 }
355
356 int device_code_width(f, ps, code, widthp)
357     DeviceFont *f;
358     int ps;
359     int code;
360     int *widthp;
361 {
362     struct charinfo *p;
363
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;
369     }
370     *widthp = scale_round(p->width, ps, f->dev->unitwidth);
371     return 1;
372 }
373
374 char *device_name_for_code(f, code)
375     DeviceFont *f;
376     int code;
377 {
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;
386         }
387     return 0;
388 }
389
390 int device_font_special(f)
391     DeviceFont *f;
392 {
393     return f->special;
394 }
395     
396 static
397 struct charinfo *add_char(f, name, width, code)
398     DeviceFont *f;
399     char *name;
400     int width, code;
401 {
402     struct charinfo **pp;
403     struct charinfo *ci;
404     
405     name = canonicalize_name(name);
406     if (strcmp(name, "---") == 0)
407         name = "";
408
409     ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
410                                      + strlen(name) + 1);
411     
412     strcpy(ci->name, name);
413     ci->width = width;
414     ci->code = code;
415     
416     if (*name != '\0') {
417         pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
418         ci->next = *pp;
419         *pp = ci;
420     }
421     pp = &f->code_table[code & 0xff];
422     ci->code_next = *pp;
423     *pp = ci;
424     return ci;
425 }
426
427 /* Return non-zero for success. */
428
429 static
430 int read_charset_section(f, fp)
431     DeviceFont *f;
432     FILE *fp;
433 {
434     struct charinfo *last_charinfo = 0;
435     char buf[256];
436
437     while (fgets(buf, sizeof(buf), fp)) {
438         char *name;
439         int width;
440         int code;
441         char *p;
442
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;
454             }
455             else
456                 (void)add_char(f, name,
457                                last_charinfo->width, last_charinfo->code);
458         }
459         else {
460             char *q;
461             if (sscanf(p, "%d", &width) != 1) {
462                 error("bad width field");
463                 return 0;
464             }
465             p = strtok((char *)0, WS);
466             if (!p) {
467                 error("missing type field");
468                 return 0;
469             }
470             p = strtok((char *)0, WS);
471             if (!p) {
472                 error("missing code field");
473                 return 0;
474             }
475             code = (int)strtol(p, &q, 0);
476             if (q == p) {
477                 error("bad code field");
478                 return 0;
479             }
480             last_charinfo = add_char(f, name, width, code);
481         }
482     }
483     return 1;
484 }
485
486 static
487 FILE *find_file(file, result)
488     char *file, **result;
489 {
490   char *buf = NULL;
491   int bufsiz = 0;
492   int flen;
493   FILE *fp;
494   char *path;
495   char *env;
496
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, ":");
504   }
505   strcat(path, FONTPATH);
506
507   *result = NULL;
508   
509   if (file == NULL)
510     return NULL;
511   if (*file == '\0')
512     return NULL;
513   
514   if (*file == '/') {
515     fp = fopen(file, "r");
516     if (fp)
517       *result = XtNewString(file);
518     return fp;
519   }
520   
521   flen = strlen(file);
522   
523   while (*path) {
524     int len;
525     char *start, *end;
526     
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;
544     }
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;
552     }
553   }
554   XtFree(buf);
555   return NULL;
556 }
557
558 static
559 FILE *open_device_file(device_name, file_name, result)
560      char *device_name, *file_name, **result;
561 {
562   char *buf, *path;
563   FILE *fp;
564
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);
571   }
572   XtFree(buf);
573   return fp;
574 }
575
576 static
577 void error(s)
578     char *s;
579 {
580     if (current_filename) {
581         fprintf(stderr, "%s:", current_filename);
582         if (current_lineno > 0)
583             fprintf(stderr, "%d:", current_lineno);
584         putc(' ', stderr);
585     }
586     fputs(s, stderr);
587     putc('\n', stderr);
588     fflush(stderr);
589 }
590
591 /*
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:
600 */