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