plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 /*****************************************************************************/