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