1/* device.c */ 2 3#ifdef HAVE_CONFIG_H 4#include <config.h> 5#endif 6 7#include <stdio.h> 8#include <ctype.h> 9#include <stdlib.h> 10#include <string.h> 11 12#include <X11/Xos.h> 13#include <X11/Intrinsic.h> 14 15#include "device.h" 16#include "defs.h" 17 18#ifndef isascii 19#define isascii(c) (1) 20#endif 21 22/* Name of environment variable containing path to be used for 23searching for device and font description files. */ 24#define FONTPATH_ENV_VAR "GROFF_FONT_PATH" 25 26#define WS " \t\r\n" 27 28#ifndef INT_MIN 29/* Minimum and maximum values a `signed int' can hold. */ 30#define INT_MIN (-INT_MAX-1) 31#define INT_MAX 2147483647 32#endif 33 34#define CHAR_TABLE_SIZE 307 35 36struct _DeviceFont { 37 char *name; 38 int special; 39 DeviceFont *next; 40 Device *dev; 41 struct charinfo *char_table[CHAR_TABLE_SIZE]; 42 struct charinfo *code_table[256]; 43}; 44 45struct charinfo { 46 int width; 47 int code; 48 struct charinfo *next; 49 struct charinfo *code_next; 50 char name[1]; 51}; 52 53static char *current_filename = 0; 54static int current_lineno = -1; 55 56static void error(const char *s); 57static FILE *open_device_file(const char *, const char *, char **); 58static DeviceFont *load_font(Device *, const char *); 59static Device *new_device(const char *); 60static DeviceFont *new_font(const char *, Device *); 61static void delete_font(DeviceFont *); 62static unsigned hash_name(const char *); 63static struct charinfo *add_char(DeviceFont *, const char *, int, int); 64static int read_charset_section(DeviceFont *, FILE *); 65static char *canonicalize_name(const char *); 66static int scale_round(int, int, int); 67 68static 69Device *new_device(const char *name) 70{ 71 Device *dev; 72 73 dev = XtNew(Device); 74 dev->sizescale = 1; 75 dev->res = 0; 76 dev->unitwidth = 0; 77 dev->fonts = 0; 78 dev->X11 = 0; 79 dev->paperlength = 0; 80 dev->paperwidth = 0; 81 dev->name = XtNewString(name); 82 return dev; 83} 84 85void device_destroy(Device *dev) 86{ 87 DeviceFont *f; 88 89 if (!dev) 90 return; 91 f = dev->fonts; 92 while (f) { 93 DeviceFont *tem = f; 94 f = f->next; 95 delete_font(tem); 96 } 97 98 XtFree(dev->name); 99 XtFree((char *)dev); 100} 101 102Device *device_load(const char *name) 103{ 104 Device *dev; 105 FILE *fp; 106 int err = 0; 107 char buf[256]; 108 109 fp = open_device_file(name, "DESC", ¤t_filename); 110 if (!fp) 111 return 0; 112 dev = new_device(name); 113 current_lineno = 0; 114 while (fgets(buf, sizeof(buf), fp)) { 115 char *p; 116 current_lineno++; 117 p = strtok(buf, WS); 118 if (p) { 119 int *np = 0; 120 char *q; 121 122 if (strcmp(p, "charset") == 0) 123 break; 124 if (strcmp(p, "X11") == 0) 125 dev->X11 = 1; 126 else if (strcmp(p, "sizescale") == 0) 127 np = &dev->sizescale; 128 else if (strcmp(p, "res") == 0) 129 np = &dev->res; 130 else if (strcmp(p, "unitwidth") == 0) 131 np = &dev->unitwidth; 132 else if (strcmp(p, "paperwidth") == 0) 133 np = &dev->paperwidth; 134 else if (strcmp(p, "paperlength") == 0) 135 np = &dev->paperlength; 136 137 if (np) { 138 q = strtok((char *)0, WS); 139 if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) { 140 error("bad argument"); 141 err = 1; 142 break; 143 } 144 } 145 } 146 } 147 fclose(fp); 148 current_lineno = -1; 149 if (!err) { 150 if (dev->res == 0) { 151 error("missing res line"); 152 err = 1; 153 } 154 else if (dev->unitwidth == 0) { 155 error("missing unitwidth line"); 156 err = 1; 157 } 158 } 159 if (dev->paperlength == 0) 160 dev->paperlength = dev->res*11; 161 if (dev->paperwidth == 0) 162 dev->paperwidth = dev->res*8 + dev->res/2; 163 if (err) { 164 device_destroy(dev); 165 dev = 0; 166 } 167 XtFree(current_filename); 168 current_filename = 0; 169 return dev; 170} 171 172 173DeviceFont *device_find_font(Device *dev, const char *name) 174{ 175 DeviceFont *f; 176 177 if (!dev) 178 return 0; 179 for (f = dev->fonts; f; f = f->next) 180 if (strcmp(f->name, name) == 0) 181 return f; 182 return load_font(dev, name); 183} 184 185static 186DeviceFont *load_font(Device *dev, const char *name) 187{ 188 FILE *fp; 189 char buf[256]; 190 DeviceFont *f; 191 int special = 0; 192 193 fp = open_device_file(dev->name, name, ¤t_filename); 194 if (!fp) 195 return 0; 196 current_lineno = 0; 197 for (;;) { 198 char *p; 199 200 if (!fgets(buf, sizeof(buf), fp)) { 201 error("no charset line"); 202 return 0; 203 } 204 current_lineno++; 205 p = strtok(buf, WS); 206 /* charset must be on a line by itself */ 207 if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0) 208 break; 209 if (p && strcmp(p, "special") == 0) 210 special = 1; 211 } 212 f = new_font(name, dev); 213 f->special = special; 214 if (!read_charset_section(f, fp)) { 215 delete_font(f); 216 f = 0; 217 } 218 else { 219 f->next = dev->fonts; 220 dev->fonts = f; 221 } 222 fclose(fp); 223 XtFree(current_filename); 224 current_filename = 0; 225 return f; 226} 227 228static 229DeviceFont *new_font(const char *name, Device *dev) 230{ 231 int i; 232 DeviceFont *f; 233 234 f = XtNew(DeviceFont); 235 f->name = XtNewString(name); 236 f->dev = dev; 237 f->special = 0; 238 f->next = 0; 239 for (i = 0; i < CHAR_TABLE_SIZE; i++) 240 f->char_table[i] = 0; 241 for (i = 0; i < 256; i++) 242 f->code_table[i] = 0; 243 return f; 244} 245 246static 247void delete_font(DeviceFont *f) 248{ 249 int i; 250 251 if (!f) 252 return; 253 XtFree(f->name); 254 for (i = 0; i < CHAR_TABLE_SIZE; i++) { 255 struct charinfo *ptr = f->char_table[i]; 256 while (ptr) { 257 struct charinfo *tem = ptr; 258 ptr = ptr->next; 259 XtFree((char *)tem); 260 } 261 } 262 XtFree((char *)f); 263} 264 265 266static 267unsigned hash_name(const char *name) 268{ 269 unsigned n = 0; 270 /* XXX do better than this */ 271 while (*name) 272 n = (n << 1) ^ *name++; 273 274 return n; 275} 276 277static 278int scale_round(int n, int x, int y) 279{ 280 int y2; 281 282 if (x == 0) 283 return 0; 284 y2 = y/2; 285 if (n >= 0) { 286 if (n <= (INT_MAX - y2)/x) 287 return (n*x + y2)/y; 288 return (int)(n*(double)x/(double)y + .5); 289 } 290 else { 291 if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x) 292 return (n*x - y2)/y; 293 return (int)(n*(double)x/(double)y + .5); 294 } 295} 296 297static 298char *canonicalize_name(const char *s) 299{ 300 static char ch[2]; 301 if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') { 302 const char *p; 303 int n; 304 305 for (p = s + 4; *p; p++) 306 if (!isascii(*p) || !isdigit((unsigned char)*p)) 307 return (char *)s; 308 n = atoi(s + 4); 309 if (n >= 0 && n <= 0xff) { 310 ch[0] = (char)n; 311 return ch; 312 } 313 } 314 return (char *)s; 315} 316 317/* Return 1 if the character is present in the font; widthp gets the 318width if non-null. */ 319 320int device_char_width(DeviceFont *f, int ps, const char *name, int *widthp) 321{ 322 struct charinfo *p; 323 324 name = canonicalize_name(name); 325 for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) { 326 if (!p) 327 return 0; 328 if (strcmp(p->name, name) == 0) 329 break; 330 } 331 *widthp = scale_round(p->width, ps, f->dev->unitwidth); 332 return 1; 333} 334 335int device_code_width(DeviceFont *f, int ps, int code, int *widthp) 336{ 337 struct charinfo *p; 338 339 for (p = f->code_table[code & 0xff];; p = p->code_next) { 340 if (!p) 341 return 0; 342 if (p->code == code) 343 break; 344 } 345 *widthp = scale_round(p->width, ps, f->dev->unitwidth); 346 return 1; 347} 348 349char *device_name_for_code(DeviceFont *f, int code) 350{ 351 static struct charinfo *state = 0; 352 if (f) 353 state = f->code_table[code & 0xff]; 354 for (; state; state = state->code_next) 355 if (state->code == code && state->name[0] != '\0') { 356 char *name = state->name; 357 state = state->code_next; 358 return name; 359 } 360 return 0; 361} 362 363int device_font_special(DeviceFont *f) 364{ 365 return f->special; 366} 367 368static 369struct charinfo *add_char(DeviceFont *f, const char *name, int width, int code) 370{ 371 struct charinfo **pp; 372 struct charinfo *ci; 373 374 name = canonicalize_name(name); 375 if (strcmp(name, "---") == 0) 376 name = ""; 377 378 ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0]) 379 + strlen(name) + 1); 380 381 strcpy(ci->name, name); 382 ci->width = width; 383 ci->code = code; 384 385 if (*name != '\0') { 386 pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE]; 387 ci->next = *pp; 388 *pp = ci; 389 } 390 pp = &f->code_table[code & 0xff]; 391 ci->code_next = *pp; 392 *pp = ci; 393 return ci; 394} 395 396/* Return non-zero for success. */ 397 398static 399int read_charset_section(DeviceFont *f, FILE *fp) 400{ 401 struct charinfo *last_charinfo = 0; 402 char buf[256]; 403 404 while (fgets(buf, sizeof(buf), fp)) { 405 char *name; 406 int width; 407 int code; 408 char *p; 409 410 current_lineno++; 411 name = strtok(buf, WS); 412 if (!name) 413 continue; /* ignore blank lines */ 414 p = strtok((char *)0, WS); 415 if (!p) /* end of charset section */ 416 break; 417 if (strcmp(p, "\"") == 0) { 418 if (!last_charinfo) { 419 error("first line of charset section cannot use `\"'"); 420 return 0; 421 } 422 else 423 (void)add_char(f, name, 424 last_charinfo->width, last_charinfo->code); 425 } 426 else { 427 char *q; 428 if (sscanf(p, "%d", &width) != 1) { 429 error("bad width field"); 430 return 0; 431 } 432 p = strtok((char *)0, WS); 433 if (!p) { 434 error("missing type field"); 435 return 0; 436 } 437 p = strtok((char *)0, WS); 438 if (!p) { 439 error("missing code field"); 440 return 0; 441 } 442 code = (int)strtol(p, &q, 0); 443 if (q == p) { 444 error("bad code field"); 445 return 0; 446 } 447 last_charinfo = add_char(f, name, width, code); 448 } 449 } 450 return 1; 451} 452 453static 454FILE *find_file(const char *file, char **result) 455{ 456 char *buf = NULL; 457 int bufsiz = 0; 458 int flen; 459 FILE *fp; 460 char *path; 461 char *env; 462 463 env = getenv(FONTPATH_ENV_VAR); 464 path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0) 465 + strlen(FONTPATH) + 1); 466 *path = '\0'; 467 if (env && *env) { 468 strcat(path, env); 469 strcat(path, ":"); 470 } 471 strcat(path, FONTPATH); 472 473 *result = NULL; 474 475 if (file == NULL) 476 return NULL; 477 if (*file == '\0') 478 return NULL; 479 480 if (*file == '/') { 481 fp = fopen(file, "r"); 482 if (fp) 483 *result = XtNewString(file); 484 return fp; 485 } 486 487 flen = strlen(file); 488 489 while (*path) { 490 int len; 491 char *start, *end; 492 493 start = path; 494 end = strchr(path, ':'); 495 if (end) 496 path = end + 1; 497 else 498 path = end = strchr(path, '\0'); 499 if (start >= end) 500 continue; 501 if (end[-1] == '/') 502 --end; 503 len = (end - start) + 1 + flen + 1; 504 if (len > bufsiz) { 505 if (buf) 506 buf = XtRealloc(buf, len); 507 else 508 buf = XtMalloc(len); 509 bufsiz = len; 510 } 511 memcpy(buf, start, end - start); 512 buf[end - start] = '/'; 513 strcpy(buf + (end - start) + 1, file); 514 fp = fopen(buf, "r"); 515 if (fp) { 516 *result = buf; 517 return fp; 518 } 519 } 520 XtFree(buf); 521 return NULL; 522} 523 524static 525FILE *open_device_file(const char *device_name, const char *file_name, 526 char **result) 527{ 528 char *buf; 529 FILE *fp; 530 531 buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1); 532 sprintf(buf, "dev%s/%s", device_name, file_name); 533 fp = find_file(buf, result); 534 if (!fp) { 535 fprintf(stderr, "can't find device file `%s'\n", file_name); 536 fflush(stderr); 537 } 538 XtFree(buf); 539 return fp; 540} 541 542static 543void error(const char *s) 544{ 545 if (current_filename) { 546 fprintf(stderr, "%s:", current_filename); 547 if (current_lineno > 0) 548 fprintf(stderr, "%d:", current_lineno); 549 putc(' ', stderr); 550 } 551 fputs(s, stderr); 552 putc('\n', stderr); 553 fflush(stderr); 554} 555 556/* 557Local Variables: 558c-indent-level: 4 559c-continued-statement-offset: 4 560c-brace-offset: -4 561c-argdecl-indent: 4 562c-label-offset: -4 563c-tab-always-indent: nil 564End: 565*/ 566