588 lines
11 KiB
C
588 lines
11 KiB
C
/* device.c */
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include <X11/Xos.h>
|
|
#include <X11/Intrinsic.h>
|
|
|
|
#include "device.h"
|
|
|
|
#ifndef FONTPATH
|
|
#define FONTPATH "/usr/local/lib/groff/font:/usr/local/lib/font:/usr/lib/font"
|
|
#endif
|
|
|
|
#ifndef isascii
|
|
#define isascii(c) (1)
|
|
#endif
|
|
|
|
extern void exit();
|
|
extern char *strtok(), *strchr();
|
|
extern char *getenv();
|
|
|
|
/* 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();
|
|
static FILE *open_device_file();
|
|
static DeviceFont *load_font();
|
|
static Device *new_device();
|
|
static DeviceFont *new_font();
|
|
static void delete_font();
|
|
static unsigned hash_name();
|
|
static struct charinfo *add_char();
|
|
static int read_charset_section();
|
|
static char *canonicalize_name();
|
|
|
|
static
|
|
Device *new_device(name)
|
|
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(dev)
|
|
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(name)
|
|
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(dev, name)
|
|
Device *dev;
|
|
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(dev, name)
|
|
Device *dev;
|
|
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(name, dev)
|
|
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(f)
|
|
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(name)
|
|
char *name;
|
|
{
|
|
unsigned n = 0;
|
|
/* XXX do better than this */
|
|
while (*name)
|
|
n = (n << 1) ^ *name++;
|
|
|
|
return n;
|
|
}
|
|
|
|
static
|
|
int scale_round(n, x, y)
|
|
int n, x, 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;
|
|
}
|
|
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(s)
|
|
char *s;
|
|
{
|
|
static char ch[2];
|
|
if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
|
|
char *p;
|
|
int n;
|
|
|
|
for (p = s + 4; *p; p++)
|
|
if (!isascii(*p) || !isdigit((unsigned char)*p))
|
|
return s;
|
|
n = atoi(s + 4);
|
|
if (n >= 0 && n <= 0xff) {
|
|
ch[0] = (char)n;
|
|
return ch;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* Return 1 if the character is present in the font; widthp gets the
|
|
width if non-null. */
|
|
|
|
int device_char_width(f, ps, name, widthp)
|
|
DeviceFont *f;
|
|
int ps;
|
|
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(f, ps, code, widthp)
|
|
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(f, 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(f)
|
|
DeviceFont *f;
|
|
{
|
|
return f->special;
|
|
}
|
|
|
|
static
|
|
struct charinfo *add_char(f, name, width, code)
|
|
DeviceFont *f;
|
|
char *name;
|
|
int width, 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(f, fp)
|
|
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(file, path, result)
|
|
char *file, *path, **result;
|
|
{
|
|
char *buf = NULL;
|
|
int bufsiz = 0;
|
|
int flen;
|
|
FILE *fp;
|
|
|
|
*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);
|
|
|
|
if (!path)
|
|
return NULL;
|
|
|
|
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(device_name, file_name, result)
|
|
char *device_name, *file_name, **result;
|
|
{
|
|
char *buf, *path;
|
|
FILE *fp;
|
|
|
|
buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
|
|
sprintf(buf, "dev%s/%s", device_name, file_name);
|
|
path = getenv(FONTPATH_ENV_VAR);
|
|
if (!path)
|
|
path = FONTPATH;
|
|
fp = find_file(buf, path, result);
|
|
if (!fp) {
|
|
fprintf(stderr, "can't find device file `%s'\n", file_name);
|
|
fflush(stderr);
|
|
}
|
|
XtFree(buf);
|
|
return fp;
|
|
}
|
|
|
|
static
|
|
void error(s)
|
|
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:
|
|
*/
|