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