/* device.c */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "device.h" #include "defs.h" #ifndef isascii #define isascii(c) (1) #endif /* Name of environment variable containing path to be used for searching for device and font description files. */ #define FONTPATH_ENV_VAR "GROFF_FONT_PATH" #define WS " \t\r\n" #ifndef INT_MIN /* Minimum and maximum values a `signed int' can hold. */ #define INT_MIN (-INT_MAX-1) #define INT_MAX 2147483647 #endif #define CHAR_TABLE_SIZE 307 struct _DeviceFont { char *name; int special; DeviceFont *next; Device *dev; struct charinfo *char_table[CHAR_TABLE_SIZE]; struct charinfo *code_table[256]; }; struct charinfo { int width; int code; struct charinfo *next; struct charinfo *code_next; char name[1]; }; static char *current_filename = 0; static int current_lineno = -1; static void error(const char *s); static FILE *open_device_file(const char *, const char *, char **); static DeviceFont *load_font(Device *, const char *); static Device *new_device(const char *); static DeviceFont *new_font(const char *, Device *); static void delete_font(DeviceFont *); static unsigned hash_name(const char *); static struct charinfo *add_char(DeviceFont *, const char *, int, int); static int read_charset_section(DeviceFont *, FILE *); static char *canonicalize_name(const char *); static int scale_round(int, int, int); static Device *new_device(const char *name) { Device *dev; dev = XtNew(Device); dev->sizescale = 1; dev->res = 0; dev->unitwidth = 0; dev->fonts = 0; dev->X11 = 0; dev->paperlength = 0; dev->paperwidth = 0; dev->name = XtNewString(name); return dev; } void device_destroy(Device *dev) { DeviceFont *f; if (!dev) return; f = dev->fonts; while (f) { DeviceFont *tem = f; f = f->next; delete_font(tem); } XtFree(dev->name); XtFree((char *)dev); } Device *device_load(const char *name) { Device *dev; FILE *fp; int err = 0; char buf[256]; fp = open_device_file(name, "DESC", ¤t_filename); if (!fp) return 0; dev = new_device(name); current_lineno = 0; while (fgets(buf, sizeof(buf), fp)) { char *p; current_lineno++; p = strtok(buf, WS); if (p) { int *np = 0; char *q; if (strcmp(p, "charset") == 0) break; if (strcmp(p, "X11") == 0) dev->X11 = 1; else if (strcmp(p, "sizescale") == 0) np = &dev->sizescale; else if (strcmp(p, "res") == 0) np = &dev->res; else if (strcmp(p, "unitwidth") == 0) np = &dev->unitwidth; else if (strcmp(p, "paperwidth") == 0) np = &dev->paperwidth; else if (strcmp(p, "paperlength") == 0) np = &dev->paperlength; if (np) { q = strtok((char *)0, WS); if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) { error("bad argument"); err = 1; break; } } } } fclose(fp); current_lineno = -1; if (!err) { if (dev->res == 0) { error("missing res line"); err = 1; } else if (dev->unitwidth == 0) { error("missing unitwidth line"); err = 1; } } if (dev->paperlength == 0) dev->paperlength = dev->res*11; if (dev->paperwidth == 0) dev->paperwidth = dev->res*8 + dev->res/2; if (err) { device_destroy(dev); dev = 0; } XtFree(current_filename); current_filename = 0; return dev; } DeviceFont *device_find_font(Device *dev, const char *name) { DeviceFont *f; if (!dev) return 0; for (f = dev->fonts; f; f = f->next) if (strcmp(f->name, name) == 0) return f; return load_font(dev, name); } static DeviceFont *load_font(Device *dev, const char *name) { FILE *fp; char buf[256]; DeviceFont *f; int special = 0; fp = open_device_file(dev->name, name, ¤t_filename); if (!fp) return 0; current_lineno = 0; for (;;) { char *p; if (!fgets(buf, sizeof(buf), fp)) { error("no charset line"); return 0; } current_lineno++; p = strtok(buf, WS); /* charset must be on a line by itself */ if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0) break; if (p && strcmp(p, "special") == 0) special = 1; } f = new_font(name, dev); f->special = special; if (!read_charset_section(f, fp)) { delete_font(f); f = 0; } else { f->next = dev->fonts; dev->fonts = f; } fclose(fp); XtFree(current_filename); current_filename = 0; return f; } static DeviceFont *new_font(const char *name, Device *dev) { int i; DeviceFont *f; f = XtNew(DeviceFont); f->name = XtNewString(name); f->dev = dev; f->special = 0; f->next = 0; for (i = 0; i < CHAR_TABLE_SIZE; i++) f->char_table[i] = 0; for (i = 0; i < 256; i++) f->code_table[i] = 0; return f; } static void delete_font(DeviceFont *f) { int i; if (!f) return; XtFree(f->name); for (i = 0; i < CHAR_TABLE_SIZE; i++) { struct charinfo *ptr = f->char_table[i]; while (ptr) { struct charinfo *tem = ptr; ptr = ptr->next; XtFree((char *)tem); } } XtFree((char *)f); } static unsigned hash_name(const char *name) { unsigned n = 0; /* XXX do better than this */ while (*name) n = (n << 1) ^ *name++; return n; } static int scale_round(int n, int x, int y) { int y2; if (x == 0) return 0; y2 = y/2; if (n >= 0) { if (n <= (INT_MAX - y2)/x) return (n*x + y2)/y; return (int)(n*(double)x/(double)y + .5); } else { if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x) return (n*x - y2)/y; return (int)(n*(double)x/(double)y + .5); } } static char *canonicalize_name(const char *s) { static char ch[2]; if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') { const char *p; int n; for (p = s + 4; *p; p++) if (!isascii(*p) || !isdigit((unsigned char)*p)) return (char *)s; n = atoi(s + 4); if (n >= 0 && n <= 0xff) { ch[0] = (char)n; return ch; } } return (char *)s; } /* Return 1 if the character is present in the font; widthp gets the width if non-null. */ int device_char_width(DeviceFont *f, int ps, const char *name, int *widthp) { struct charinfo *p; name = canonicalize_name(name); for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) { if (!p) return 0; if (strcmp(p->name, name) == 0) break; } *widthp = scale_round(p->width, ps, f->dev->unitwidth); return 1; } int device_code_width(DeviceFont *f, int ps, int code, int *widthp) { struct charinfo *p; for (p = f->code_table[code & 0xff];; p = p->code_next) { if (!p) return 0; if (p->code == code) break; } *widthp = scale_round(p->width, ps, f->dev->unitwidth); return 1; } char *device_name_for_code(DeviceFont *f, int code) { static struct charinfo *state = 0; if (f) state = f->code_table[code & 0xff]; for (; state; state = state->code_next) if (state->code == code && state->name[0] != '\0') { char *name = state->name; state = state->code_next; return name; } return 0; } int device_font_special(DeviceFont *f) { return f->special; } static struct charinfo *add_char(DeviceFont *f, const char *name, int width, int code) { struct charinfo **pp; struct charinfo *ci; name = canonicalize_name(name); if (strcmp(name, "---") == 0) name = ""; ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0]) + strlen(name) + 1); strcpy(ci->name, name); ci->width = width; ci->code = code; if (*name != '\0') { pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE]; ci->next = *pp; *pp = ci; } pp = &f->code_table[code & 0xff]; ci->code_next = *pp; *pp = ci; return ci; } /* Return non-zero for success. */ static int read_charset_section(DeviceFont *f, FILE *fp) { struct charinfo *last_charinfo = 0; char buf[256]; while (fgets(buf, sizeof(buf), fp)) { char *name; int width; int code; char *p; current_lineno++; name = strtok(buf, WS); if (!name) continue; /* ignore blank lines */ p = strtok((char *)0, WS); if (!p) /* end of charset section */ break; if (strcmp(p, "\"") == 0) { if (!last_charinfo) { error("first line of charset section cannot use `\"'"); return 0; } else (void)add_char(f, name, last_charinfo->width, last_charinfo->code); } else { char *q; if (sscanf(p, "%d", &width) != 1) { error("bad width field"); return 0; } p = strtok((char *)0, WS); if (!p) { error("missing type field"); return 0; } p = strtok((char *)0, WS); if (!p) { error("missing code field"); return 0; } code = (int)strtol(p, &q, 0); if (q == p) { error("bad code field"); return 0; } last_charinfo = add_char(f, name, width, code); } } return 1; } static FILE *find_file(const char *file, char **result) { char *buf = NULL; int bufsiz = 0; int flen; FILE *fp; char *path; char *env; env = getenv(FONTPATH_ENV_VAR); path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0) + strlen(FONTPATH) + 1); *path = '\0'; if (env && *env) { strcat(path, env); strcat(path, ":"); } strcat(path, FONTPATH); *result = NULL; if (file == NULL) return NULL; if (*file == '\0') return NULL; if (*file == '/') { fp = fopen(file, "r"); if (fp) *result = XtNewString(file); return fp; } flen = strlen(file); while (*path) { int len; char *start, *end; start = path; end = strchr(path, ':'); if (end) path = end + 1; else path = end = strchr(path, '\0'); if (start >= end) continue; if (end[-1] == '/') --end; len = (end - start) + 1 + flen + 1; if (len > bufsiz) { if (buf) buf = XtRealloc(buf, len); else buf = XtMalloc(len); bufsiz = len; } memcpy(buf, start, end - start); buf[end - start] = '/'; strcpy(buf + (end - start) + 1, file); fp = fopen(buf, "r"); if (fp) { *result = buf; return fp; } } XtFree(buf); return NULL; } static FILE *open_device_file(const char *device_name, const char *file_name, char **result) { char *buf; FILE *fp; buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1); sprintf(buf, "dev%s/%s", device_name, file_name); fp = find_file(buf, result); if (!fp) { fprintf(stderr, "can't find device file `%s'\n", file_name); fflush(stderr); } XtFree(buf); return fp; } static void error(const char *s) { if (current_filename) { fprintf(stderr, "%s:", current_filename); if (current_lineno > 0) fprintf(stderr, "%d:", current_lineno); putc(' ', stderr); } fputs(s, stderr); putc('\n', stderr); fflush(stderr); } /* Local Variables: c-indent-level: 4 c-continued-statement-offset: 4 c-brace-offset: -4 c-argdecl-indent: 4 c-label-offset: -4 c-tab-always-indent: nil End: */