]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/groff/src/devices/xditview/device.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / groff / src / devices / xditview / device.c
1 /* device.c */
2
3 #ifdef HAVE_CONFIG_H
4 #include <config.h>
5 #endif
6
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include <X11/Xos.h>
13 #include <X11/Intrinsic.h>
14
15 #include "device.h"
16 #include "defs.h"
17
18 #ifndef isascii
19 #define isascii(c) (1)
20 #endif
21
22 /* Name of environment variable containing path to be used for
23 searching for device and font description files. */
24 #define FONTPATH_ENV_VAR  "GROFF_FONT_PATH"
25
26 #define WS " \t\r\n"
27
28 #ifndef INT_MIN
29 /* Minimum and maximum values a `signed int' can hold.  */
30 #define INT_MIN (-INT_MAX-1)
31 #define INT_MAX 2147483647
32 #endif
33
34 #define CHAR_TABLE_SIZE 307
35
36 struct _DeviceFont {
37     char *name;
38     int special;
39     DeviceFont *next;
40     Device *dev;
41     struct charinfo *char_table[CHAR_TABLE_SIZE];
42     struct charinfo *code_table[256];
43 };
44
45 struct charinfo {
46     int width;
47     int code;
48     struct charinfo *next;
49     struct charinfo *code_next;
50     char name[1];
51 };
52
53 static char *current_filename = 0;
54 static int current_lineno = -1;
55
56 static void error(const char *s);
57 static FILE *open_device_file(const char *, const char *, char **);
58 static DeviceFont *load_font(Device *, const char *);
59 static Device *new_device(const char *);
60 static DeviceFont *new_font(const char *, Device *);
61 static void delete_font(DeviceFont *);
62 static unsigned hash_name(const char *);
63 static struct charinfo *add_char(DeviceFont *, const char *, int, int);
64 static int read_charset_section(DeviceFont *, FILE *);
65 static char *canonicalize_name(const char *);
66 static int scale_round(int, int, int);
67
68 static
69 Device *new_device(const char *name)
70 {
71     Device *dev;
72
73     dev = XtNew(Device);
74     dev->sizescale = 1;
75     dev->res = 0;
76     dev->unitwidth = 0;
77     dev->fonts = 0;
78     dev->X11 = 0;
79     dev->paperlength = 0;
80     dev->paperwidth = 0;
81     dev->name = XtNewString(name);
82     return dev;
83 }
84
85 void device_destroy(Device *dev)
86 {
87     DeviceFont *f;
88     
89     if (!dev)
90         return;
91     f = dev->fonts;
92     while (f) {
93         DeviceFont *tem = f;
94         f = f->next;
95         delete_font(tem);
96     }
97     
98     XtFree(dev->name);
99     XtFree((char *)dev);
100 }
101
102 Device *device_load(const char *name)
103 {
104     Device *dev;
105     FILE *fp;
106     int err = 0;
107     char buf[256];
108
109     fp = open_device_file(name, "DESC", &current_filename);
110     if (!fp)
111         return 0;
112     dev = new_device(name);
113     current_lineno = 0;
114     while (fgets(buf, sizeof(buf), fp)) {
115         char *p;
116         current_lineno++;
117         p = strtok(buf, WS);
118         if (p) {
119             int *np = 0;
120             char *q;
121
122             if (strcmp(p, "charset") == 0)
123                 break;
124             if (strcmp(p, "X11") == 0)
125                 dev->X11 = 1;
126             else if (strcmp(p, "sizescale") == 0)
127                 np = &dev->sizescale;
128             else if (strcmp(p, "res") == 0)
129                 np = &dev->res;
130             else if (strcmp(p, "unitwidth") == 0)
131                 np = &dev->unitwidth;
132             else if (strcmp(p, "paperwidth") == 0)
133                 np = &dev->paperwidth;
134             else if (strcmp(p, "paperlength") == 0)
135                 np = &dev->paperlength;
136             
137             if (np) {
138                 q = strtok((char *)0, WS);
139                 if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) {
140                     error("bad argument");
141                     err = 1;
142                     break;
143                 }
144             }   
145         }
146     }
147     fclose(fp);
148     current_lineno = -1;
149     if (!err) {
150         if (dev->res == 0) {
151             error("missing res line");
152             err = 1;
153         }
154         else if (dev->unitwidth == 0) {
155             error("missing unitwidth line");
156             err = 1;
157         }
158     }
159     if (dev->paperlength == 0)
160         dev->paperlength = dev->res*11;
161     if (dev->paperwidth == 0)
162         dev->paperwidth = dev->res*8 + dev->res/2;
163     if (err) {
164         device_destroy(dev);
165         dev = 0;
166     }
167     XtFree(current_filename);
168     current_filename = 0;
169     return dev;
170 }
171
172
173 DeviceFont *device_find_font(Device *dev, const char *name)
174 {
175     DeviceFont *f;
176
177     if (!dev)
178         return 0;
179     for (f = dev->fonts; f; f = f->next)
180         if (strcmp(f->name, name) == 0)
181             return f;
182     return load_font(dev, name);
183 }
184
185 static
186 DeviceFont *load_font(Device *dev, const char *name)
187 {
188     FILE *fp;
189     char buf[256];
190     DeviceFont *f;
191     int special = 0;
192
193     fp = open_device_file(dev->name, name, &current_filename);
194     if (!fp)
195         return 0;
196     current_lineno = 0;
197     for (;;) {
198         char *p;
199
200         if (!fgets(buf, sizeof(buf), fp)) {
201             error("no charset line");
202             return 0;
203         }
204         current_lineno++;
205         p = strtok(buf, WS);
206         /* charset must be on a line by itself */
207         if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0)
208             break;
209         if (p && strcmp(p, "special") == 0)
210             special = 1;
211     }
212     f = new_font(name, dev);
213     f->special = special;
214     if (!read_charset_section(f, fp)) {
215         delete_font(f);
216         f = 0;
217     }
218     else {
219         f->next = dev->fonts;
220         dev->fonts = f;
221     }
222     fclose(fp);
223     XtFree(current_filename);
224     current_filename = 0;
225     return f;
226 }
227
228 static
229 DeviceFont *new_font(const char *name, Device *dev)
230 {
231     int i;
232     DeviceFont *f;
233
234     f = XtNew(DeviceFont);
235     f->name = XtNewString(name);
236     f->dev = dev;
237     f->special = 0;
238     f->next = 0;
239     for (i = 0; i < CHAR_TABLE_SIZE; i++)
240         f->char_table[i] = 0;
241     for (i = 0; i < 256; i++)
242         f->code_table[i] = 0;
243     return f;
244 }
245
246 static
247 void delete_font(DeviceFont *f)
248 {
249     int i;
250
251     if (!f)
252         return;
253     XtFree(f->name);
254     for (i = 0; i < CHAR_TABLE_SIZE; i++) {
255         struct charinfo *ptr = f->char_table[i];
256         while (ptr) {
257             struct charinfo *tem = ptr;
258             ptr = ptr->next;
259             XtFree((char *)tem);
260         }
261     }
262     XtFree((char *)f);
263 }
264
265
266 static
267 unsigned hash_name(const char *name)
268 {
269     unsigned n = 0;
270     /* XXX do better than this */
271     while (*name)
272         n = (n << 1) ^ *name++;
273
274     return n;
275 }
276
277 static
278 int scale_round(int n, int x, int y)
279 {
280   int y2;
281
282   if (x == 0)
283     return 0;
284   y2 = y/2;
285   if (n >= 0) {
286     if (n <= (INT_MAX - y2)/x)
287       return (n*x + y2)/y;
288     return (int)(n*(double)x/(double)y + .5);
289   }
290   else {
291     if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
292       return (n*x - y2)/y;
293     return (int)(n*(double)x/(double)y + .5);
294   }
295 }
296
297 static
298 char *canonicalize_name(const char *s)
299 {
300     static char ch[2];
301     if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
302         const char *p;
303         int n;
304
305         for (p = s + 4; *p; p++)
306             if (!isascii(*p) || !isdigit((unsigned char)*p))
307                 return (char *)s;
308         n = atoi(s + 4);
309         if (n >= 0 && n <= 0xff) {
310             ch[0] = (char)n;
311             return ch;
312         }
313     }
314     return (char *)s;
315 }
316
317 /* Return 1 if the character is present in the font; widthp gets the
318 width if non-null. */
319
320 int device_char_width(DeviceFont *f, int ps, const char *name, int *widthp)
321 {
322     struct charinfo *p;
323
324     name = canonicalize_name(name);
325     for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) {
326         if (!p)
327             return 0;
328         if (strcmp(p->name, name) == 0)
329             break;
330     }
331     *widthp = scale_round(p->width, ps, f->dev->unitwidth);
332     return 1;
333 }
334
335 int device_code_width(DeviceFont *f, int ps, int code, int *widthp)
336 {
337     struct charinfo *p;
338
339     for (p = f->code_table[code & 0xff];; p = p->code_next) {
340         if (!p)
341             return 0;
342         if (p->code == code)
343             break;
344     }
345     *widthp = scale_round(p->width, ps, f->dev->unitwidth);
346     return 1;
347 }
348
349 char *device_name_for_code(DeviceFont *f, int code)
350 {
351     static struct charinfo *state = 0;
352     if (f)
353         state = f->code_table[code & 0xff];
354     for (; state; state = state->code_next)
355         if (state->code == code && state->name[0] != '\0') {
356             char *name = state->name;
357             state = state->code_next;
358             return name;
359         }
360     return 0;
361 }
362
363 int device_font_special(DeviceFont *f)
364 {
365     return f->special;
366 }
367     
368 static
369 struct charinfo *add_char(DeviceFont *f, const char *name, int width, int code)
370 {
371     struct charinfo **pp;
372     struct charinfo *ci;
373     
374     name = canonicalize_name(name);
375     if (strcmp(name, "---") == 0)
376         name = "";
377
378     ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
379                                      + strlen(name) + 1);
380     
381     strcpy(ci->name, name);
382     ci->width = width;
383     ci->code = code;
384     
385     if (*name != '\0') {
386         pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
387         ci->next = *pp;
388         *pp = ci;
389     }
390     pp = &f->code_table[code & 0xff];
391     ci->code_next = *pp;
392     *pp = ci;
393     return ci;
394 }
395
396 /* Return non-zero for success. */
397
398 static
399 int read_charset_section(DeviceFont *f, FILE *fp)
400 {
401     struct charinfo *last_charinfo = 0;
402     char buf[256];
403
404     while (fgets(buf, sizeof(buf), fp)) {
405         char *name;
406         int width;
407         int code;
408         char *p;
409
410         current_lineno++;
411         name = strtok(buf, WS);
412         if (!name)
413             continue;           /* ignore blank lines */
414         p = strtok((char *)0, WS);
415         if (!p)                 /* end of charset section */
416             break;
417         if (strcmp(p, "\"") == 0) {
418             if (!last_charinfo) {
419                 error("first line of charset section cannot use `\"'");
420                 return 0;
421             }
422             else
423                 (void)add_char(f, name,
424                                last_charinfo->width, last_charinfo->code);
425         }
426         else {
427             char *q;
428             if (sscanf(p, "%d", &width) != 1) {
429                 error("bad width field");
430                 return 0;
431             }
432             p = strtok((char *)0, WS);
433             if (!p) {
434                 error("missing type field");
435                 return 0;
436             }
437             p = strtok((char *)0, WS);
438             if (!p) {
439                 error("missing code field");
440                 return 0;
441             }
442             code = (int)strtol(p, &q, 0);
443             if (q == p) {
444                 error("bad code field");
445                 return 0;
446             }
447             last_charinfo = add_char(f, name, width, code);
448         }
449     }
450     return 1;
451 }
452
453 static
454 FILE *find_file(const char *file, char **result)
455 {
456   char *buf = NULL;
457   int bufsiz = 0;
458   int flen;
459   FILE *fp;
460   char *path;
461   char *env;
462
463   env = getenv(FONTPATH_ENV_VAR);
464   path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0)
465                   + strlen(FONTPATH) + 1);
466   *path = '\0';
467   if (env && *env) {
468     strcat(path, env);
469     strcat(path, ":");
470   }
471   strcat(path, FONTPATH);
472
473   *result = NULL;
474   
475   if (file == NULL)
476     return NULL;
477   if (*file == '\0')
478     return NULL;
479   
480   if (*file == '/') {
481     fp = fopen(file, "r");
482     if (fp)
483       *result = XtNewString(file);
484     return fp;
485   }
486   
487   flen = strlen(file);
488   
489   while (*path) {
490     int len;
491     char *start, *end;
492     
493     start = path;
494     end = strchr(path, ':');
495     if (end)
496       path = end + 1;
497     else
498       path = end = strchr(path, '\0');
499     if (start >= end)
500       continue;
501     if (end[-1] == '/')
502       --end;
503     len = (end - start) + 1 + flen + 1;
504     if (len > bufsiz) {
505       if (buf)
506         buf = XtRealloc(buf, len);
507       else
508         buf = XtMalloc(len);
509       bufsiz = len;
510     }
511     memcpy(buf, start, end - start);
512     buf[end - start] = '/';
513     strcpy(buf + (end - start) + 1, file);
514     fp = fopen(buf, "r");
515     if (fp) {
516       *result = buf;
517       return fp;
518     }
519   }
520   XtFree(buf);
521   return NULL;
522 }
523
524 static
525 FILE *open_device_file(const char *device_name, const char *file_name,
526                        char **result)
527 {
528   char *buf;
529   FILE *fp;
530
531   buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
532   sprintf(buf, "dev%s/%s", device_name, file_name);
533   fp = find_file(buf, result);
534   if (!fp) {
535       fprintf(stderr, "can't find device file `%s'\n", file_name);
536       fflush(stderr);
537   }
538   XtFree(buf);
539   return fp;
540 }
541
542 static
543 void error(const char *s)
544 {
545     if (current_filename) {
546         fprintf(stderr, "%s:", current_filename);
547         if (current_lineno > 0)
548             fprintf(stderr, "%d:", current_lineno);
549         putc(' ', stderr);
550     }
551     fputs(s, stderr);
552     putc('\n', stderr);
553     fflush(stderr);
554 }
555
556 /*
557 Local Variables:
558 c-indent-level: 4
559 c-continued-statement-offset: 4
560 c-brace-offset: -4
561 c-argdecl-indent: 4
562 c-label-offset: -4
563 c-tab-always-indent: nil
564 End:
565 */