1151497Sru/* device.c */
2151497Sru
3151497Sru#ifdef HAVE_CONFIG_H
4151497Sru#include <config.h>
5151497Sru#endif
6151497Sru
7151497Sru#include <stdio.h>
8151497Sru#include <ctype.h>
9151497Sru#include <stdlib.h>
10151497Sru#include <string.h>
11151497Sru
12151497Sru#include <X11/Xos.h>
13151497Sru#include <X11/Intrinsic.h>
14151497Sru
15151497Sru#include "device.h"
16151497Sru#include "defs.h"
17151497Sru
18151497Sru#ifndef isascii
19151497Sru#define isascii(c) (1)
20151497Sru#endif
21151497Sru
22151497Sru/* Name of environment variable containing path to be used for
23151497Srusearching for device and font description files. */
24151497Sru#define FONTPATH_ENV_VAR  "GROFF_FONT_PATH"
25151497Sru
26151497Sru#define WS " \t\r\n"
27151497Sru
28151497Sru#ifndef INT_MIN
29151497Sru/* Minimum and maximum values a `signed int' can hold.  */
30151497Sru#define INT_MIN (-INT_MAX-1)
31151497Sru#define INT_MAX 2147483647
32151497Sru#endif
33151497Sru
34151497Sru#define CHAR_TABLE_SIZE 307
35151497Sru
36151497Srustruct _DeviceFont {
37151497Sru    char *name;
38151497Sru    int special;
39151497Sru    DeviceFont *next;
40151497Sru    Device *dev;
41151497Sru    struct charinfo *char_table[CHAR_TABLE_SIZE];
42151497Sru    struct charinfo *code_table[256];
43151497Sru};
44151497Sru
45151497Srustruct charinfo {
46151497Sru    int width;
47151497Sru    int code;
48151497Sru    struct charinfo *next;
49151497Sru    struct charinfo *code_next;
50151497Sru    char name[1];
51151497Sru};
52151497Sru
53151497Srustatic char *current_filename = 0;
54151497Srustatic int current_lineno = -1;
55151497Sru
56151497Srustatic void error(const char *s);
57151497Srustatic FILE *open_device_file(const char *, const char *, char **);
58151497Srustatic DeviceFont *load_font(Device *, const char *);
59151497Srustatic Device *new_device(const char *);
60151497Srustatic DeviceFont *new_font(const char *, Device *);
61151497Srustatic void delete_font(DeviceFont *);
62151497Srustatic unsigned hash_name(const char *);
63151497Srustatic struct charinfo *add_char(DeviceFont *, const char *, int, int);
64151497Srustatic int read_charset_section(DeviceFont *, FILE *);
65151497Srustatic char *canonicalize_name(const char *);
66151497Srustatic int scale_round(int, int, int);
67151497Sru
68151497Srustatic
69151497SruDevice *new_device(const char *name)
70151497Sru{
71151497Sru    Device *dev;
72151497Sru
73151497Sru    dev = XtNew(Device);
74151497Sru    dev->sizescale = 1;
75151497Sru    dev->res = 0;
76151497Sru    dev->unitwidth = 0;
77151497Sru    dev->fonts = 0;
78151497Sru    dev->X11 = 0;
79151497Sru    dev->paperlength = 0;
80151497Sru    dev->paperwidth = 0;
81151497Sru    dev->name = XtNewString(name);
82151497Sru    return dev;
83151497Sru}
84151497Sru
85151497Sruvoid device_destroy(Device *dev)
86151497Sru{
87151497Sru    DeviceFont *f;
88151497Sru
89151497Sru    if (!dev)
90151497Sru	return;
91151497Sru    f = dev->fonts;
92151497Sru    while (f) {
93151497Sru	DeviceFont *tem = f;
94151497Sru	f = f->next;
95151497Sru	delete_font(tem);
96151497Sru    }
97151497Sru
98151497Sru    XtFree(dev->name);
99151497Sru    XtFree((char *)dev);
100151497Sru}
101151497Sru
102151497SruDevice *device_load(const char *name)
103151497Sru{
104151497Sru    Device *dev;
105151497Sru    FILE *fp;
106151497Sru    int err = 0;
107151497Sru    char buf[256];
108151497Sru
109151497Sru    fp = open_device_file(name, "DESC", &current_filename);
110151497Sru    if (!fp)
111151497Sru	return 0;
112151497Sru    dev = new_device(name);
113151497Sru    current_lineno = 0;
114151497Sru    while (fgets(buf, sizeof(buf), fp)) {
115151497Sru	char *p;
116151497Sru	current_lineno++;
117151497Sru	p = strtok(buf, WS);
118151497Sru	if (p) {
119151497Sru	    int *np = 0;
120151497Sru	    char *q;
121151497Sru
122151497Sru	    if (strcmp(p, "charset") == 0)
123151497Sru		break;
124151497Sru	    if (strcmp(p, "X11") == 0)
125151497Sru		dev->X11 = 1;
126151497Sru	    else if (strcmp(p, "sizescale") == 0)
127151497Sru		np = &dev->sizescale;
128151497Sru 	    else if (strcmp(p, "res") == 0)
129151497Sru		np = &dev->res;
130151497Sru 	    else if (strcmp(p, "unitwidth") == 0)
131151497Sru		np = &dev->unitwidth;
132151497Sru 	    else if (strcmp(p, "paperwidth") == 0)
133151497Sru		np = &dev->paperwidth;
134151497Sru 	    else if (strcmp(p, "paperlength") == 0)
135151497Sru		np = &dev->paperlength;
136151497Sru
137151497Sru	    if (np) {
138151497Sru		q = strtok((char *)0, WS);
139151497Sru		if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) {
140151497Sru		    error("bad argument");
141151497Sru		    err = 1;
142151497Sru		    break;
143151497Sru		}
144151497Sru	    }
145151497Sru	}
146151497Sru    }
147151497Sru    fclose(fp);
148151497Sru    current_lineno = -1;
149151497Sru    if (!err) {
150151497Sru	if (dev->res == 0) {
151151497Sru	    error("missing res line");
152151497Sru	    err = 1;
153151497Sru	}
154151497Sru	else if (dev->unitwidth == 0) {
155151497Sru	    error("missing unitwidth line");
156151497Sru	    err = 1;
157151497Sru	}
158151497Sru    }
159151497Sru    if (dev->paperlength == 0)
160151497Sru	dev->paperlength = dev->res*11;
161151497Sru    if (dev->paperwidth == 0)
162151497Sru	dev->paperwidth = dev->res*8 + dev->res/2;
163151497Sru    if (err) {
164151497Sru	device_destroy(dev);
165151497Sru	dev = 0;
166151497Sru    }
167151497Sru    XtFree(current_filename);
168151497Sru    current_filename = 0;
169151497Sru    return dev;
170151497Sru}
171151497Sru
172151497Sru
173151497SruDeviceFont *device_find_font(Device *dev, const char *name)
174151497Sru{
175151497Sru    DeviceFont *f;
176151497Sru
177151497Sru    if (!dev)
178151497Sru	return 0;
179151497Sru    for (f = dev->fonts; f; f = f->next)
180151497Sru	if (strcmp(f->name, name) == 0)
181151497Sru	    return f;
182151497Sru    return load_font(dev, name);
183151497Sru}
184151497Sru
185151497Srustatic
186151497SruDeviceFont *load_font(Device *dev, const char *name)
187151497Sru{
188151497Sru    FILE *fp;
189151497Sru    char buf[256];
190151497Sru    DeviceFont *f;
191151497Sru    int special = 0;
192151497Sru
193151497Sru    fp = open_device_file(dev->name, name, &current_filename);
194151497Sru    if (!fp)
195151497Sru	return 0;
196151497Sru    current_lineno = 0;
197151497Sru    for (;;) {
198151497Sru	char *p;
199151497Sru
200151497Sru	if (!fgets(buf, sizeof(buf), fp)) {
201151497Sru	    error("no charset line");
202151497Sru	    return 0;
203151497Sru	}
204151497Sru	current_lineno++;
205151497Sru	p = strtok(buf, WS);
206151497Sru	/* charset must be on a line by itself */
207151497Sru	if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0)
208151497Sru	    break;
209151497Sru	if (p && strcmp(p, "special") == 0)
210151497Sru	    special = 1;
211151497Sru    }
212151497Sru    f = new_font(name, dev);
213151497Sru    f->special = special;
214151497Sru    if (!read_charset_section(f, fp)) {
215151497Sru	delete_font(f);
216151497Sru	f = 0;
217151497Sru    }
218151497Sru    else {
219151497Sru	f->next = dev->fonts;
220151497Sru	dev->fonts = f;
221151497Sru    }
222151497Sru    fclose(fp);
223151497Sru    XtFree(current_filename);
224151497Sru    current_filename = 0;
225151497Sru    return f;
226151497Sru}
227151497Sru
228151497Srustatic
229151497SruDeviceFont *new_font(const char *name, Device *dev)
230151497Sru{
231151497Sru    int i;
232151497Sru    DeviceFont *f;
233151497Sru
234151497Sru    f = XtNew(DeviceFont);
235151497Sru    f->name = XtNewString(name);
236151497Sru    f->dev = dev;
237151497Sru    f->special = 0;
238151497Sru    f->next = 0;
239151497Sru    for (i = 0; i < CHAR_TABLE_SIZE; i++)
240151497Sru	f->char_table[i] = 0;
241151497Sru    for (i = 0; i < 256; i++)
242151497Sru	f->code_table[i] = 0;
243151497Sru    return f;
244151497Sru}
245151497Sru
246151497Srustatic
247151497Sruvoid delete_font(DeviceFont *f)
248151497Sru{
249151497Sru    int i;
250151497Sru
251151497Sru    if (!f)
252151497Sru	return;
253151497Sru    XtFree(f->name);
254151497Sru    for (i = 0; i < CHAR_TABLE_SIZE; i++) {
255151497Sru	struct charinfo *ptr = f->char_table[i];
256151497Sru	while (ptr) {
257151497Sru	    struct charinfo *tem = ptr;
258151497Sru	    ptr = ptr->next;
259151497Sru	    XtFree((char *)tem);
260151497Sru	}
261151497Sru    }
262151497Sru    XtFree((char *)f);
263151497Sru}
264151497Sru
265151497Sru
266151497Srustatic
267151497Sruunsigned hash_name(const char *name)
268151497Sru{
269151497Sru    unsigned n = 0;
270151497Sru    /* XXX do better than this */
271151497Sru    while (*name)
272151497Sru	n = (n << 1) ^ *name++;
273151497Sru
274151497Sru    return n;
275151497Sru}
276151497Sru
277151497Srustatic
278151497Sruint scale_round(int n, int x, int y)
279151497Sru{
280151497Sru  int y2;
281151497Sru
282151497Sru  if (x == 0)
283151497Sru    return 0;
284151497Sru  y2 = y/2;
285151497Sru  if (n >= 0) {
286151497Sru    if (n <= (INT_MAX - y2)/x)
287151497Sru      return (n*x + y2)/y;
288151497Sru    return (int)(n*(double)x/(double)y + .5);
289151497Sru  }
290151497Sru  else {
291151497Sru    if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
292151497Sru      return (n*x - y2)/y;
293151497Sru    return (int)(n*(double)x/(double)y + .5);
294151497Sru  }
295151497Sru}
296151497Sru
297151497Srustatic
298151497Sruchar *canonicalize_name(const char *s)
299151497Sru{
300151497Sru    static char ch[2];
301151497Sru    if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
302151497Sru	const char *p;
303151497Sru	int n;
304151497Sru
305151497Sru	for (p = s + 4; *p; p++)
306151497Sru	    if (!isascii(*p) || !isdigit((unsigned char)*p))
307151497Sru		return (char *)s;
308151497Sru	n = atoi(s + 4);
309151497Sru	if (n >= 0 && n <= 0xff) {
310151497Sru	    ch[0] = (char)n;
311151497Sru	    return ch;
312151497Sru	}
313151497Sru    }
314151497Sru    return (char *)s;
315151497Sru}
316151497Sru
317151497Sru/* Return 1 if the character is present in the font; widthp gets the
318151497Sruwidth if non-null. */
319151497Sru
320151497Sruint device_char_width(DeviceFont *f, int ps, const char *name, int *widthp)
321151497Sru{
322151497Sru    struct charinfo *p;
323151497Sru
324151497Sru    name = canonicalize_name(name);
325151497Sru    for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) {
326151497Sru	if (!p)
327151497Sru	    return 0;
328151497Sru	if (strcmp(p->name, name) == 0)
329151497Sru	    break;
330151497Sru    }
331151497Sru    *widthp = scale_round(p->width, ps, f->dev->unitwidth);
332151497Sru    return 1;
333151497Sru}
334151497Sru
335151497Sruint device_code_width(DeviceFont *f, int ps, int code, int *widthp)
336151497Sru{
337151497Sru    struct charinfo *p;
338151497Sru
339151497Sru    for (p = f->code_table[code & 0xff];; p = p->code_next) {
340151497Sru	if (!p)
341151497Sru	    return 0;
342151497Sru	if (p->code == code)
343151497Sru	    break;
344151497Sru    }
345151497Sru    *widthp = scale_round(p->width, ps, f->dev->unitwidth);
346151497Sru    return 1;
347151497Sru}
348151497Sru
349151497Sruchar *device_name_for_code(DeviceFont *f, int code)
350151497Sru{
351151497Sru    static struct charinfo *state = 0;
352151497Sru    if (f)
353151497Sru	state = f->code_table[code & 0xff];
354151497Sru    for (; state; state = state->code_next)
355151497Sru	if (state->code == code && state->name[0] != '\0') {
356151497Sru	    char *name = state->name;
357151497Sru	    state = state->code_next;
358151497Sru	    return name;
359151497Sru	}
360151497Sru    return 0;
361151497Sru}
362151497Sru
363151497Sruint device_font_special(DeviceFont *f)
364151497Sru{
365151497Sru    return f->special;
366151497Sru}
367151497Sru
368151497Srustatic
369151497Srustruct charinfo *add_char(DeviceFont *f, const char *name, int width, int code)
370151497Sru{
371151497Sru    struct charinfo **pp;
372151497Sru    struct charinfo *ci;
373151497Sru
374151497Sru    name = canonicalize_name(name);
375151497Sru    if (strcmp(name, "---") == 0)
376151497Sru	name = "";
377151497Sru
378151497Sru    ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
379151497Sru				     + strlen(name) + 1);
380151497Sru
381151497Sru    strcpy(ci->name, name);
382151497Sru    ci->width = width;
383151497Sru    ci->code = code;
384151497Sru
385151497Sru    if (*name != '\0') {
386151497Sru	pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
387151497Sru	ci->next = *pp;
388151497Sru	*pp = ci;
389151497Sru    }
390151497Sru    pp = &f->code_table[code & 0xff];
391151497Sru    ci->code_next = *pp;
392151497Sru    *pp = ci;
393151497Sru    return ci;
394151497Sru}
395151497Sru
396151497Sru/* Return non-zero for success. */
397151497Sru
398151497Srustatic
399151497Sruint read_charset_section(DeviceFont *f, FILE *fp)
400151497Sru{
401151497Sru    struct charinfo *last_charinfo = 0;
402151497Sru    char buf[256];
403151497Sru
404151497Sru    while (fgets(buf, sizeof(buf), fp)) {
405151497Sru	char *name;
406151497Sru	int width;
407151497Sru	int code;
408151497Sru	char *p;
409151497Sru
410151497Sru	current_lineno++;
411151497Sru	name = strtok(buf, WS);
412151497Sru	if (!name)
413151497Sru	    continue;		/* ignore blank lines */
414151497Sru	p = strtok((char *)0, WS);
415151497Sru	if (!p)			/* end of charset section */
416151497Sru	    break;
417151497Sru	if (strcmp(p, "\"") == 0) {
418151497Sru	    if (!last_charinfo) {
419151497Sru		error("first line of charset section cannot use `\"'");
420151497Sru		return 0;
421151497Sru	    }
422151497Sru	    else
423151497Sru		(void)add_char(f, name,
424151497Sru			       last_charinfo->width, last_charinfo->code);
425151497Sru	}
426151497Sru	else {
427151497Sru	    char *q;
428151497Sru	    if (sscanf(p, "%d", &width) != 1) {
429151497Sru		error("bad width field");
430151497Sru		return 0;
431151497Sru	    }
432151497Sru	    p = strtok((char *)0, WS);
433151497Sru	    if (!p) {
434151497Sru		error("missing type field");
435151497Sru		return 0;
436151497Sru	    }
437151497Sru	    p = strtok((char *)0, WS);
438151497Sru	    if (!p) {
439151497Sru		error("missing code field");
440151497Sru		return 0;
441151497Sru	    }
442151497Sru	    code = (int)strtol(p, &q, 0);
443151497Sru	    if (q == p) {
444151497Sru		error("bad code field");
445151497Sru		return 0;
446151497Sru	    }
447151497Sru	    last_charinfo = add_char(f, name, width, code);
448151497Sru	}
449151497Sru    }
450151497Sru    return 1;
451151497Sru}
452151497Sru
453151497Srustatic
454151497SruFILE *find_file(const char *file, char **result)
455151497Sru{
456151497Sru  char *buf = NULL;
457151497Sru  int bufsiz = 0;
458151497Sru  int flen;
459151497Sru  FILE *fp;
460151497Sru  char *path;
461151497Sru  char *env;
462151497Sru
463151497Sru  env = getenv(FONTPATH_ENV_VAR);
464151497Sru  path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0)
465151497Sru		  + strlen(FONTPATH) + 1);
466151497Sru  *path = '\0';
467151497Sru  if (env && *env) {
468151497Sru    strcat(path, env);
469151497Sru    strcat(path, ":");
470151497Sru  }
471151497Sru  strcat(path, FONTPATH);
472151497Sru
473151497Sru  *result = NULL;
474151497Sru
475151497Sru  if (file == NULL)
476151497Sru    return NULL;
477151497Sru  if (*file == '\0')
478151497Sru    return NULL;
479151497Sru
480151497Sru  if (*file == '/') {
481151497Sru    fp = fopen(file, "r");
482151497Sru    if (fp)
483151497Sru      *result = XtNewString(file);
484151497Sru    return fp;
485151497Sru  }
486151497Sru
487151497Sru  flen = strlen(file);
488151497Sru
489151497Sru  while (*path) {
490151497Sru    int len;
491151497Sru    char *start, *end;
492151497Sru
493151497Sru    start = path;
494151497Sru    end = strchr(path, ':');
495151497Sru    if (end)
496151497Sru      path = end + 1;
497151497Sru    else
498151497Sru      path = end = strchr(path, '\0');
499151497Sru    if (start >= end)
500151497Sru      continue;
501151497Sru    if (end[-1] == '/')
502151497Sru      --end;
503151497Sru    len = (end - start) + 1 + flen + 1;
504151497Sru    if (len > bufsiz) {
505151497Sru      if (buf)
506151497Sru	buf = XtRealloc(buf, len);
507151497Sru      else
508151497Sru	buf = XtMalloc(len);
509151497Sru      bufsiz = len;
510151497Sru    }
511151497Sru    memcpy(buf, start, end - start);
512151497Sru    buf[end - start] = '/';
513151497Sru    strcpy(buf + (end - start) + 1, file);
514151497Sru    fp = fopen(buf, "r");
515151497Sru    if (fp) {
516151497Sru      *result = buf;
517151497Sru      return fp;
518151497Sru    }
519151497Sru  }
520151497Sru  XtFree(buf);
521151497Sru  return NULL;
522151497Sru}
523151497Sru
524151497Srustatic
525151497SruFILE *open_device_file(const char *device_name, const char *file_name,
526151497Sru		       char **result)
527151497Sru{
528151497Sru  char *buf;
529151497Sru  FILE *fp;
530151497Sru
531151497Sru  buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
532151497Sru  sprintf(buf, "dev%s/%s", device_name, file_name);
533151497Sru  fp = find_file(buf, result);
534151497Sru  if (!fp) {
535151497Sru      fprintf(stderr, "can't find device file `%s'\n", file_name);
536151497Sru      fflush(stderr);
537151497Sru  }
538151497Sru  XtFree(buf);
539151497Sru  return fp;
540151497Sru}
541151497Sru
542151497Srustatic
543151497Sruvoid error(const char *s)
544151497Sru{
545151497Sru    if (current_filename) {
546151497Sru	fprintf(stderr, "%s:", current_filename);
547151497Sru	if (current_lineno > 0)
548151497Sru	    fprintf(stderr, "%d:", current_lineno);
549151497Sru	putc(' ', stderr);
550151497Sru    }
551151497Sru    fputs(s, stderr);
552151497Sru    putc('\n', stderr);
553151497Sru    fflush(stderr);
554151497Sru}
555151497Sru
556151497Sru/*
557151497SruLocal Variables:
558151497Sruc-indent-level: 4
559151497Sruc-continued-statement-offset: 4
560151497Sruc-brace-offset: -4
561151497Sruc-argdecl-indent: 4
562151497Sruc-label-offset: -4
563151497Sruc-tab-always-indent: nil
564151497SruEnd:
565151497Sru*/
566