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
new_device(const char * name)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
device_destroy(Device * dev)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
device_load(const char * name)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", ¤t_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
device_find_font(Device * dev,const char * name)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
load_font(Device * dev,const char * name)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, ¤t_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
new_font(const char * name,Device * dev)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
delete_font(DeviceFont * f)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
hash_name(const char * name)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
scale_round(int n,int x,int y)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
canonicalize_name(const char * s)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
device_char_width(DeviceFont * f,int ps,const char * name,int * widthp)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
device_code_width(DeviceFont * f,int ps,int code,int * widthp)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
device_name_for_code(DeviceFont * f,int code)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
device_font_special(DeviceFont * f)363 int device_font_special(DeviceFont *f)
364 {
365 return f->special;
366 }
367
368 static
add_char(DeviceFont * f,const char * name,int width,int code)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
read_charset_section(DeviceFont * f,FILE * fp)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
find_file(const char * file,char ** result)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
open_device_file(const char * device_name,const char * file_name,char ** result)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
error(const char * s)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 */
566