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