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