diff options
Diffstat (limited to 'src/devices/xditview/device.c')
-rw-r--r-- | src/devices/xditview/device.c | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/src/devices/xditview/device.c b/src/devices/xditview/device.c new file mode 100644 index 0000000..1eaafbe --- /dev/null +++ b/src/devices/xditview/device.c @@ -0,0 +1,565 @@ +/* device.c */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include <X11/Xos.h> +#include <X11/Intrinsic.h> + +#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: +*/ |