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