* Makefile.in (SEP): Replaced with...
[s-roff.git] / src / xditview / device.c
blob0716bbdb7ef9d37a05256c7f9677dbe542877535
1 /* device.c */
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <stdlib.h>
7 #include <X11/Xos.h>
8 #include <X11/Intrinsic.h>
10 #include "device.h"
12 #ifndef FONTPATH
13 #define FONTPATH "/usr/local/share/groff/font:/usr/local/lib/font:/usr/lib/font"
14 #endif
16 #ifndef isascii
17 #define isascii(c) (1)
18 #endif
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
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"
35 #define WS " \t\r\n"
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
43 #define CHAR_TABLE_SIZE 307
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];
54 struct charinfo {
55 int width;
56 int code;
57 struct charinfo *next;
58 struct charinfo *code_next;
59 char name[1];
62 static char *current_filename = 0;
63 static int current_lineno = -1;
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();
76 static
77 Device *new_device(name)
78 char *name;
80 Device *dev;
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;
94 void device_destroy(dev)
95 Device *dev;
97 DeviceFont *f;
99 if (!dev)
100 return;
101 f = dev->fonts;
102 while (f) {
103 DeviceFont *tem = f;
104 f = f->next;
105 delete_font(tem);
108 XtFree(dev->name);
109 XtFree((char *)dev);
112 Device *device_load(name)
113 char *name;
115 Device *dev;
116 FILE *fp;
117 int err = 0;
118 char buf[256];
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;
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;
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;
158 fclose(fp);
159 current_lineno = -1;
160 if (!err) {
161 if (dev->res == 0) {
162 error("missing res line");
163 err = 1;
165 else if (dev->unitwidth == 0) {
166 error("missing unitwidth line");
167 err = 1;
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;
178 XtFree(current_filename);
179 current_filename = 0;
180 return dev;
184 DeviceFont *device_find_font(dev, name)
185 Device *dev;
186 char *name;
188 DeviceFont *f;
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);
198 static
199 DeviceFont *load_font(dev, name)
200 Device *dev;
201 char *name;
203 FILE *fp;
204 char buf[256];
205 DeviceFont *f;
206 int special = 0;
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;
215 if (!fgets(buf, sizeof(buf), fp)) {
216 error("no charset line");
217 return 0;
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;
227 f = new_font(name, dev);
228 f->special = special;
229 if (!read_charset_section(f, fp)) {
230 delete_font(f);
231 f = 0;
233 else {
234 f->next = dev->fonts;
235 dev->fonts = f;
237 fclose(fp);
238 XtFree(current_filename);
239 current_filename = 0;
240 return f;
243 static
244 DeviceFont *new_font(name, dev)
245 char *name;
246 Device *dev;
248 int i;
249 DeviceFont *f;
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;
263 static
264 void delete_font(f)
265 DeviceFont *f;
267 int i;
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);
280 XtFree((char *)f);
284 static
285 unsigned hash_name(name)
286 char *name;
288 unsigned n = 0;
289 /* XXX do better than this */
290 while (*name)
291 n = (n << 1) ^ *name++;
293 return n;
296 static
297 int scale_round(n, x, y)
298 int n, x, y;
300 int y2;
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;
309 else if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
310 return (n*x - y2)/y;
311 return (int)(n*(double)x/(double)y + .5);
314 static
315 char *canonicalize_name(s)
316 char *s;
318 static char ch[2];
319 if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
320 char *p;
321 int n;
323 for (p = s + 4; *p; p++)
324 if (!isascii(*p) || !isdigit((unsigned char)*p))
325 return s;
326 n = atoi(s + 4);
327 if (n >= 0 && n <= 0xff) {
328 ch[0] = (char)n;
329 return ch;
332 return s;
335 /* Return 1 if the character is present in the font; widthp gets the
336 width if non-null. */
338 int device_char_width(f, ps, name, widthp)
339 DeviceFont *f;
340 int ps;
341 char *name;
342 int *widthp;
344 struct charinfo *p;
346 name = canonicalize_name(name);
347 for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) {
348 if (!p)
349 return 0;
350 if (strcmp(p->name, name) == 0)
351 break;
353 *widthp = scale_round(p->width, ps, f->dev->unitwidth);
354 return 1;
357 int device_code_width(f, ps, code, widthp)
358 DeviceFont *f;
359 int ps;
360 int code;
361 int *widthp;
363 struct charinfo *p;
365 for (p = f->code_table[code & 0xff];; p = p->code_next) {
366 if (!p)
367 return 0;
368 if (p->code == code)
369 break;
371 *widthp = scale_round(p->width, ps, f->dev->unitwidth);
372 return 1;
375 char *device_name_for_code(f, code)
376 DeviceFont *f;
377 int code;
379 static struct charinfo *state = 0;
380 if (f)
381 state = f->code_table[code & 0xff];
382 for (; state; state = state->code_next)
383 if (state->code == code && state->name[0] != '\0') {
384 char *name = state->name;
385 state = state->code_next;
386 return name;
388 return 0;
391 int device_font_special(f)
392 DeviceFont *f;
394 return f->special;
397 static
398 struct charinfo *add_char(f, name, width, code)
399 DeviceFont *f;
400 char *name;
401 int width, code;
403 struct charinfo **pp;
404 struct charinfo *ci;
406 name = canonicalize_name(name);
407 if (strcmp(name, "---") == 0)
408 name = "";
410 ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
411 + strlen(name) + 1);
413 strcpy(ci->name, name);
414 ci->width = width;
415 ci->code = code;
417 if (*name != '\0') {
418 pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
419 ci->next = *pp;
420 *pp = ci;
422 pp = &f->code_table[code & 0xff];
423 ci->code_next = *pp;
424 *pp = ci;
425 return ci;
428 /* Return non-zero for success. */
430 static
431 int read_charset_section(f, fp)
432 DeviceFont *f;
433 FILE *fp;
435 struct charinfo *last_charinfo = 0;
436 char buf[256];
438 while (fgets(buf, sizeof(buf), fp)) {
439 char *name;
440 int width;
441 int code;
442 char *p;
444 current_lineno++;
445 name = strtok(buf, WS);
446 if (!name)
447 continue; /* ignore blank lines */
448 p = strtok((char *)0, WS);
449 if (!p) /* end of charset section */
450 break;
451 if (strcmp(p, "\"") == 0) {
452 if (!last_charinfo) {
453 error("first line of charset section cannot use `\"'");
454 return 0;
456 else
457 (void)add_char(f, name,
458 last_charinfo->width, last_charinfo->code);
460 else {
461 char *q;
462 if (sscanf(p, "%d", &width) != 1) {
463 error("bad width field");
464 return 0;
466 p = strtok((char *)0, WS);
467 if (!p) {
468 error("missing type field");
469 return 0;
471 p = strtok((char *)0, WS);
472 if (!p) {
473 error("missing code field");
474 return 0;
476 code = (int)strtol(p, &q, 0);
477 if (q == p) {
478 error("bad code field");
479 return 0;
481 last_charinfo = add_char(f, name, width, code);
484 return 1;
487 static
488 FILE *find_file(file, result)
489 char *file, **result;
491 char *buf = NULL;
492 int bufsiz = 0;
493 int flen;
494 FILE *fp;
495 char *path;
496 char *env;
498 env = getenv(FONTPATH_ENV_VAR);
499 path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0)
500 + strlen(FONTPATH) + 1);
501 *path = '\0';
502 if (env && *env) {
503 strcat(path, env);
504 strcat(path, ":");
506 strcat(path, FONTPATH);
508 *result = NULL;
510 if (file == NULL)
511 return NULL;
512 if (*file == '\0')
513 return NULL;
515 if (*file == '/') {
516 fp = fopen(file, "r");
517 if (fp)
518 *result = XtNewString(file);
519 return fp;
522 flen = strlen(file);
524 while (*path) {
525 int len;
526 char *start, *end;
528 start = path;
529 end = strchr(path, ':');
530 if (end)
531 path = end + 1;
532 else
533 path = end = strchr(path, '\0');
534 if (start >= end)
535 continue;
536 if (end[-1] == '/')
537 --end;
538 len = (end - start) + 1 + flen + 1;
539 if (len > bufsiz) {
540 if (buf)
541 buf = XtRealloc(buf, len);
542 else
543 buf = XtMalloc(len);
544 bufsiz = len;
546 memcpy(buf, start, end - start);
547 buf[end - start] = '/';
548 strcpy(buf + (end - start) + 1, file);
549 fp = fopen(buf, "r");
550 if (fp) {
551 *result = buf;
552 return fp;
555 XtFree(buf);
556 return NULL;
559 static
560 FILE *open_device_file(device_name, file_name, result)
561 char *device_name, *file_name, **result;
563 char *buf;
564 FILE *fp;
566 buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
567 sprintf(buf, "dev%s/%s", device_name, file_name);
568 fp = find_file(buf, result);
569 if (!fp) {
570 fprintf(stderr, "can't find device file `%s'\n", file_name);
571 fflush(stderr);
573 XtFree(buf);
574 return fp;
577 static
578 void error(s)
579 char *s;
581 if (current_filename) {
582 fprintf(stderr, "%s:", current_filename);
583 if (current_lineno > 0)
584 fprintf(stderr, "%d:", current_lineno);
585 putc(' ', stderr);
587 fputs(s, stderr);
588 putc('\n', stderr);
589 fflush(stderr);
593 Local Variables:
594 c-indent-level: 4
595 c-continued-statement-offset: 4
596 c-brace-offset: -4
597 c-argdecl-indent: 4
598 c-label-offset: -4
599 c-tab-always-indent: nil
600 End: