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", ¤t_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, ¤t_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