Home | History | Annotate | Line # | Download | only in xditview
      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
     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 
     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 
    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 
    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
    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
    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
    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
    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
    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
    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 
    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 
    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 
    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 
    365 int device_font_special(DeviceFont *f)
    366 {
    367     return f->special;
    368 }
    369 
    370 static
    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
    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
    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
    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
    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