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", ¤t_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, ¤t_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