download.c (15003B)
1 /* 2 * 3 * download - host resident font downloader 4 * 5 * Prepends host resident fonts to PostScript input files. The program assumes 6 * the input files are part of a single PostScript job and that requested fonts 7 * can be downloaded at the start of each input file. Downloaded fonts are the 8 * ones named in a %%DocumentFonts: comment and listed in a special map table. 9 * Map table pathnames (supplied using the -m option) that begin with a / are 10 * taken as is. Otherwise the final pathname is built using *hostfontdir (-H 11 * option), *mapname (-m option), and *suffix. 12 * 13 * The map table consists of fontname-filename pairs, separated by white space. 14 * Comments are introduced by % (as in PostScript) and extend to the end of the 15 * current line. The only fonts that can be downloaded are the ones listed in 16 * the active map table that point the program to a readable Unix file. A request 17 * for an unlisted font or inaccessible file is ignored. All font requests are 18 * ignored if the map table can't be read. In that case the program simply copies 19 * the input files to stdout. 20 * 21 * An example (but not one to follow) of what can be in a map table is, 22 * 23 * % 24 * % Map requests for Bookman-Light to file *hostfontdir/KR 25 * % 26 * 27 * Bookman-Light KR % Keeping everything (including the map 28 * % table) in *hostfontdir seems like the 29 * % cleanest approach. 30 * 31 * % 32 * % Map Palatino-Roman to file *hostfontdir/palatino/Roman 33 * % 34 * Palatino-Roman palatino/Roman 35 * 36 * % Map ZapfDingbats to file /usr/lib/host/dingbats 37 * 38 * ZapfDingbats /usr/lib/host/dingbats 39 * 40 * Once again, file names that begin with a / are taken as is. All others have 41 * *hostfontdir/ prepended to the file string associated with a particular font. 42 * 43 * Map table can be associated with a printer model (e.g. a LaserWriter), a 44 * printer destination, or whatever - the choice is up to an administrator. 45 * By destination may be best if your spooler is running several private 46 * printers. Host resident fonts are usually purchased under a license that 47 * restricts their use to a limited number of printers. A font licensed for 48 * a single printer should only be used on that printer. 49 * 50 * Was written quickly, so there's much room for improvement. Undoubtedly should 51 * be a more general program (e.g. scan for other comments). 52 * 53 */ 54 55 #include <u.h> 56 #include <stdio.h> 57 #include <signal.h> 58 #include <sys/types.h> 59 #include <fcntl.h> 60 #include <sys/stat.h> 61 #include <string.h> 62 #include <stdlib.h> 63 #include <unistd.h> 64 #include <libc.h> 65 66 #include "../common/ext.h" 67 #include "comments.h" /* PostScript file structuring comments */ 68 #include "gen.h" /* general purpose definitions */ 69 #include "path.h" /* for temporary directory */ 70 #include "ext.h" /* external variable declarations */ 71 #include "download.h" /* a few special definitions */ 72 73 char *temp_dir = TEMPDIR; /* temp directory - for copying stdin */ 74 char *hostfontdir = HOSTDIR; /* host resident directory */ 75 char *mapname = "map"; /* map table - usually in *hostfontdir */ 76 char *suffix = ""; /* appended to the map table pathname */ 77 Map *map = NULL; /* device font map table */ 78 char *stringspace = NULL; /* for storing font and file strings */ 79 int next = 0; /* next free slot in map[] */ 80 81 char *residentfonts = NULL; /* list of printer resident fonts */ 82 char *printer = NULL; /* printer name - only for Unix 4.0 lp */ 83 84 char buf[2048]; /* input file line buffer */ 85 char *comment = DOCUMENTFONTS; /* look for this comment */ 86 int atend = FALSE; /* TRUE only if a comment says so */ 87 88 FILE *fp_in; /* next input file */ 89 FILE *fp_temp = NULL; /* for copying stdin */ 90 91 void init_signals(void); 92 void options(void); 93 void readmap(void); 94 void readresident(void); 95 void arguments(void); 96 void done(void); 97 void download(void); 98 int lookup(char *font); 99 void copyfonts(char *list); 100 void copyinput(void); 101 extern int cat(char *file); 102 extern void error(int errtype, char *fmt, ...); 103 104 /*****************************************************************************/ 105 106 int 107 main(agc, agv) 108 109 int agc; 110 char *agv[]; 111 112 { 113 114 /* 115 * 116 * Host resident font downloader. The input files are assumed to be part of a 117 * single PostScript job. 118 * 119 */ 120 121 argc = agc; /* other routines may want them */ 122 argv = agv; 123 124 hostfontdir = unsharp(hostfontdir); 125 126 fp_in = stdin; 127 128 prog_name = argv[0]; /* just for error messages */ 129 130 init_signals(); /* sets up interrupt handling */ 131 options(); /* first get command line options */ 132 readmap(); /* read the font map table */ 133 readresident(); /* and the optional resident font list */ 134 arguments(); /* then process non-option arguments */ 135 done(); /* and clean things up */ 136 exit(x_stat); /* not much could be wrong */ 137 138 } /* End of main */ 139 140 /*****************************************************************************/ 141 142 void 143 init_signals(void) 144 { 145 146 /* 147 * 148 * Makes sure we handle interrupts properly. 149 * 150 */ 151 152 if ( signal(SIGINT, interrupt) == SIG_IGN ) { 153 signal(SIGINT, SIG_IGN); 154 signal(SIGQUIT, SIG_IGN); 155 signal(SIGHUP, SIG_IGN); 156 } else { 157 signal(SIGHUP, interrupt); 158 signal(SIGQUIT, interrupt); 159 } /* End else */ 160 161 signal(SIGTERM, interrupt); 162 163 } /* End of init_signals */ 164 165 /*****************************************************************************/ 166 167 void 168 options(void) 169 { 170 171 int ch; /* return value from getopt() */ 172 char *optnames = "c:fm:p:r:H:T:DI"; 173 174 extern char *optarg; /* used by getopt() */ 175 extern int optind; 176 177 /* 178 * 179 * Reads and processes the command line options. 180 * 181 */ 182 183 while ( (ch = getopt(argc, argv, optnames)) != EOF ) { 184 switch ( ch ) { 185 case 'c': /* look for this comment */ 186 comment = optarg; 187 break; 188 189 case 'f': /* force a complete input file scan */ 190 atend = TRUE; 191 break; 192 193 case 'm': /* printer map table name */ 194 mapname = optarg; 195 break; 196 197 case 'p': /* printer name - for Unix 4.0 lp */ 198 printer = optarg; 199 break; 200 201 case 'r': /* resident font list */ 202 residentfonts = optarg; 203 break; 204 205 case 'H': /* host resident font directory */ 206 hostfontdir = optarg; 207 break; 208 209 case 'T': /* temporary file directory */ 210 temp_dir = optarg; 211 break; 212 213 case 'D': /* debug flag */ 214 debug = ON; 215 break; 216 217 case 'I': /* ignore FATAL errors */ 218 ignore = ON; 219 break; 220 221 case '?': /* don't understand the option */ 222 error(FATAL, ""); 223 break; 224 225 default: /* don't know what to do for ch */ 226 error(FATAL, "missing case for option %c\n", ch); 227 break; 228 } /* End switch */ 229 } /* End while */ 230 231 argc -= optind; /* get ready for non-option args */ 232 argv += optind; 233 234 } /* End of options */ 235 236 /*****************************************************************************/ 237 238 void 239 readmap(void) 240 { 241 242 char *path; 243 char *ptr; 244 int fd; 245 struct stat sbuf; 246 247 /* 248 * 249 * Initializes the map table by reading an ASCII mapping file. If mapname begins 250 * with a / it's the map table. Otherwise hostfontdir, mapname, and suffix are 251 * combined to build the final pathname. If we can open the file we read it all 252 * into memory, erase comments, and separate the font and file name pairs. When 253 * we leave next points to the next free slot in the map[] array. If it's zero 254 * nothing was in the file or we couldn't open it. 255 * 256 */ 257 258 if ( hostfontdir == NULL || mapname == NULL ) 259 return; 260 261 if ( *mapname != '/' ) { 262 if ( (path = (char *)malloc(strlen(hostfontdir) + strlen(mapname) + 263 strlen(suffix) + 2)) == NULL ) 264 error(FATAL, "no memory"); 265 sprintf(path, "%s/%s%s", hostfontdir, mapname, suffix); 266 } else path = mapname; 267 268 if ( (fd = open(path, 0)) != -1 ) { 269 if ( fstat(fd, &sbuf) == -1 ) 270 error(FATAL, "can't fstat %s", path); 271 if ( (stringspace = (char *)malloc(sbuf.st_size + 2)) == NULL ) 272 error(FATAL, "no memory"); 273 if ( read(fd, stringspace, sbuf.st_size) == -1 ) 274 error(FATAL, "can't read %s", path); 275 close(fd); 276 277 stringspace[sbuf.st_size] = '\n'; /* just to be safe */ 278 stringspace[sbuf.st_size+1] = '\0'; 279 for ( ptr = stringspace; *ptr != '\0'; ptr++ ) /* erase comments */ 280 if ( *ptr == '%' ) 281 for ( ; *ptr != '\n' ; ptr++ ) 282 *ptr = ' '; 283 284 for ( ptr = stringspace; ; next++ ) { 285 if ( (next % 50) == 0 ) 286 map = allocate(map, next+50); 287 map[next].downloaded = FALSE; 288 map[next].font = strtok(ptr, " \t\n"); 289 map[next].file = strtok(ptr = NULL, " \t\n"); 290 if ( map[next].font == NULL ) 291 break; 292 if ( map[next].file == NULL ) 293 error(FATAL, "map table format error - check %s", path); 294 } /* End for */ 295 } /* End if */ 296 297 } /* End of readmap */ 298 299 /*****************************************************************************/ 300 301 void 302 readresident(void) 303 { 304 305 FILE *fp; 306 char *path; 307 int ch; 308 int n; 309 310 /* 311 * 312 * Reads a file that lists the resident fonts for a particular printer and marks 313 * each font as already downloaded. Nothing's done if the file can't be read or 314 * there's no mapping file. Comments, as in the map file, begin with a % and 315 * extend to the end of the line. Added for Unix 4.0 lp. 316 * 317 */ 318 319 if ( next == 0 || (printer == NULL && residentfonts == NULL) ) 320 return; 321 322 if ( printer != NULL ) { /* use Unix 4.0 lp pathnames */ 323 sprintf(buf, "%s/printers/%s", HOSTDIR, printer); 324 path = unsharp(buf); 325 } else path = residentfonts; 326 327 if ( (fp = fopen(path, "r")) != NULL ) { 328 while ( fscanf(fp, "%s", buf) != EOF ) 329 if ( buf[0] == '%' ) 330 while ( (ch = getc(fp)) != EOF && ch != '\n' ) ; 331 else if ( (n = lookup(buf)) < next ) 332 map[n].downloaded = TRUE; 333 fclose(fp); 334 } /* End if */ 335 336 } /* End of readresident */ 337 338 /*****************************************************************************/ 339 340 void 341 arguments(void) 342 { 343 344 /* 345 * 346 * Makes sure all the non-option command line arguments are processed. If we get 347 * here and there aren't any arguments left, or if '-' is one of the input files 348 * we'll translate stdin. Assumes input files are part of a single PostScript 349 * job and fonts can be downloaded at the start of each file. 350 * 351 */ 352 353 if ( argc < 1 ) 354 download(); 355 else { 356 while ( argc > 0 ) { 357 fp_temp = NULL; 358 if ( strcmp(*argv, "-") == 0 ) 359 fp_in = stdin; 360 else if ( (fp_in = fopen(*argv, "r")) == NULL ) 361 error(FATAL, "can't open %s", *argv); 362 download(); 363 if ( fp_in != stdin ) 364 fclose(fp_in); 365 if ( fp_temp != NULL ) 366 fclose(fp_temp); 367 argc--; 368 argv++; 369 } /* End while */ 370 } /* End else */ 371 372 } /* End of arguments */ 373 374 /*****************************************************************************/ 375 376 void 377 done(void) 378 { 379 380 /* 381 * 382 * Clean things up before we quit. 383 * 384 */ 385 386 if ( temp_file != NULL ) 387 unlink(temp_file); 388 389 } /* End of done */ 390 391 /*****************************************************************************/ 392 393 void 394 download(void) 395 { 396 397 int infontlist = FALSE; 398 399 /* 400 * 401 * If next is zero the map table is empty and all we do is copy the input file 402 * to stdout. Otherwise we read the input file looking for %%DocumentFonts: or 403 * continuation comments, add any accessible fonts to the output file, and then 404 * append the input file. When reading stdin we append lines to fp_temp and 405 * recover them when we're ready to copy the input file. fp_temp will often 406 * only contain part of stdin - if there's no %%DocumentFonts: (atend) comment 407 * we stop reading fp_in after the header. 408 * 409 */ 410 411 if ( next > 0 ) { 412 if ( fp_in == stdin ) { 413 if ( (temp_file = tempnam(temp_dir, "post")) == NULL ) 414 error(FATAL, "can't generate temp file name"); 415 if ( (fp_temp = fopen(temp_file, "w+r")) == NULL ) 416 error(FATAL, "can't open %s", temp_file); 417 unlink(temp_file); 418 } /* End if */ 419 420 while ( fgets(buf, sizeof(buf), fp_in) != NULL ) { 421 if ( fp_temp != NULL ) 422 fprintf(fp_temp, "%s", buf); 423 if ( buf[0] != '%' || buf[1] != '%' ) { 424 if ( (buf[0] != '%' || buf[1] != '!') && atend == FALSE ) 425 break; 426 infontlist = FALSE; 427 } else if ( strncmp(buf, comment, strlen(comment)) == 0 ) { 428 copyfonts(buf); 429 infontlist = TRUE; 430 } else if ( buf[2] == '+' && infontlist == TRUE ) 431 copyfonts(buf); 432 else infontlist = FALSE; 433 } /* End while */ 434 } /* End if */ 435 436 copyinput(); 437 438 } /* End of download */ 439 440 /*****************************************************************************/ 441 442 void 443 copyfonts(list) 444 445 char *list; 446 447 { 448 449 char *font; 450 char *path; 451 int n; 452 453 /* 454 * 455 * list points to a %%DocumentFonts: or continuation comment. What follows the 456 * the keyword will be a list of fonts separated by white space (or (atend)). 457 * Look for each font in the map table and if it's found copy the font file to 458 * stdout (once only). 459 * 460 */ 461 462 strtok(list, " \n"); /* skip to the font list */ 463 464 while ( (font = strtok(NULL, " \t\n")) != NULL ) { 465 if ( strcmp(font, ATEND) == 0 ) { 466 atend = TRUE; 467 break; 468 } /* End if */ 469 if ( (n = lookup(font)) < next ) { 470 if ( *map[n].file != '/' ) { 471 if ( (path = (char *)malloc(strlen(hostfontdir)+strlen(map[n].file)+2)) == NULL ) 472 error(FATAL, "no memory"); 473 sprintf(path, "%s/%s", hostfontdir, map[n].file); 474 cat(path); 475 free(path); 476 } else cat(map[n].file); 477 map[n].downloaded = TRUE; 478 } /* End if */ 479 } /* End while */ 480 481 } /* End of copyfonts */ 482 483 /*****************************************************************************/ 484 485 void 486 copyinput(void) 487 { 488 489 /* 490 * 491 * Copies the input file to stdout. If fp_temp isn't NULL seek to the start and 492 * add it to the output file - it's a partial (or complete) copy of stdin made 493 * by download(). Then copy fp_in, but only seek to the start if it's not stdin. 494 * 495 */ 496 497 if ( fp_temp != NULL ) { 498 fseek(fp_temp, 0L, 0); 499 while ( fgets(buf, sizeof(buf), fp_temp) != NULL ) 500 printf("%s", buf); 501 } /* End if */ 502 503 if ( fp_in != stdin ) 504 fseek(fp_in, 0L, 0); 505 506 while ( fgets(buf, sizeof(buf), fp_in) != NULL ) 507 printf("%s", buf); 508 509 } /* End of copyinput */ 510 511 /*****************************************************************************/ 512 513 int 514 lookup(font) 515 516 char *font; 517 518 { 519 520 int i; 521 522 /* 523 * 524 * Looks for *font in the map table. Return the map table index if found and 525 * not yet downloaded - otherwise return next. 526 * 527 */ 528 529 for ( i = 0; i < next; i++ ) 530 if ( strcmp(font, map[i].font) == 0 ) { 531 if ( map[i].downloaded == TRUE ) 532 i = next; 533 break; 534 } /* End if */ 535 536 return(i); 537 538 } /* End of lookup */ 539 540 /*****************************************************************************/ 541 542 Map *allocate(ptr, num) 543 544 Map *ptr; 545 int num; 546 547 { 548 549 /* 550 * 551 * Allocates space for num Map elements. Calls malloc() if ptr is NULL and 552 * realloc() otherwise. 553 * 554 */ 555 556 if ( ptr == NULL ) 557 ptr = (Map *)malloc(num * sizeof(Map)); 558 else ptr = (Map *)realloc(ptr, num * sizeof(Map)); 559 560 if ( ptr == NULL ) 561 error(FATAL, "no map memory"); 562 563 return(ptr); 564 565 } /* End of allocate */ 566 567 /*****************************************************************************/