ref: 059b863fab96cf3ba65e74ad67221eec6cbcbe0c
dir: /sys/src/cmd/postscript/download/download.c/
/* * * download - host resident font downloader * * Prepends host resident fonts to PostScript input files. The program assumes * the input files are part of a single PostScript job and that requested fonts * can be downloaded at the start of each input file. Downloaded fonts are the * ones named in a %%DocumentFonts: comment and listed in a special map table. * Map table pathnames (supplied using the -m option) that begin with a / are * taken as is. Otherwise the final pathname is built using *hostfontdir (-H * option), *mapname (-m option), and *suffix. * * The map table consists of fontname-filename pairs, separated by white space. * Comments are introduced by % (as in PostScript) and extend to the end of the * current line. The only fonts that can be downloaded are the ones listed in * the active map table that point the program to a readable Unix file. A request * for an unlisted font or inaccessible file is ignored. All font requests are * ignored if the map table can't be read. In that case the program simply copies * the input files to stdout. * * An example (but not one to follow) of what can be in a map table is, * * % * % Map requests for Bookman-Light to file *hostfontdir/KR * % * * Bookman-Light KR % Keeping everything (including the map * % table) in *hostfontdir seems like the * % cleanest approach. * * % * % Map Palatino-Roman to file *hostfontdir/palatino/Roman * % * Palatino-Roman palatino/Roman * * % Map ZapfDingbats to file /usr/lib/host/dingbats * * ZapfDingbats /usr/lib/host/dingbats * * Once again, file names that begin with a / are taken as is. All others have * *hostfontdir/ prepended to the file string associated with a particular font. * * Map table can be associated with a printer model (e.g. a LaserWriter), a * printer destination, or whatever - the choice is up to an administrator. * By destination may be best if your spooler is running several private * printers. Host resident fonts are usually purchased under a license that * restricts their use to a limited number of printers. A font licensed for * a single printer should only be used on that printer. * * Was written quickly, so there's much room for improvement. Undoubtedly should * be a more general program (e.g. scan for other comments). * */ #include <stdio.h> #include <signal.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include "comments.h" /* PostScript file structuring comments */ #include "gen.h" /* general purpose definitions */ #include "path.h" /* for temporary directory */ #include "ext.h" /* external variable declarations */ #include "download.h" /* a few special definitions */ char *temp_dir = TEMPDIR; /* temp directory - for copying stdin */ char *hostfontdir = HOSTDIR; /* host resident directory */ char *mapname = "map"; /* map table - usually in *hostfontdir */ char *suffix = ""; /* appended to the map table pathname */ Map *map = NULL; /* device font map table */ char *stringspace = NULL; /* for storing font and file strings */ int next = 0; /* next free slot in map[] */ char *residentfonts = NULL; /* list of printer resident fonts */ char *printer = NULL; /* printer name - only for Unix 4.0 lp */ char buf[2048]; /* input file line buffer */ char *comment = DOCUMENTFONTS; /* look for this comment */ int atend = FALSE; /* TRUE only if a comment says so */ FILE *fp_in = stdin; /* next input file */ FILE *fp_temp = NULL; /* for copying stdin */ /*****************************************************************************/ main(agc, agv) int agc; char *agv[]; { /* * * Host resident font downloader. The input files are assumed to be part of a * single PostScript job. * */ argc = agc; /* other routines may want them */ argv = agv; prog_name = argv[0]; /* just for error messages */ init_signals(); /* sets up interrupt handling */ options(); /* first get command line options */ readmap(); /* read the font map table */ readresident(); /* and the optional resident font list */ arguments(); /* then process non-option arguments */ done(); /* and clean things up */ exit(x_stat); /* not much could be wrong */ } /* End of main */ /*****************************************************************************/ init_signals() { /* * * Makes sure we handle interrupts properly. * */ if ( signal(SIGINT, interrupt) == SIG_IGN ) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); } else { signal(SIGHUP, interrupt); signal(SIGQUIT, interrupt); } /* End else */ signal(SIGTERM, interrupt); } /* End of init_signals */ /*****************************************************************************/ options() { int ch; /* return value from getopt() */ char *optnames = "c:fm:p:r:H:T:DI"; extern char *optarg; /* used by getopt() */ extern int optind; /* * * Reads and processes the command line options. * */ while ( (ch = getopt(argc, argv, optnames)) != EOF ) { switch ( ch ) { case 'c': /* look for this comment */ comment = optarg; break; case 'f': /* force a complete input file scan */ atend = TRUE; break; case 'm': /* printer map table name */ mapname = optarg; break; case 'p': /* printer name - for Unix 4.0 lp */ printer = optarg; break; case 'r': /* resident font list */ residentfonts = optarg; break; case 'H': /* host resident font directory */ hostfontdir = optarg; break; case 'T': /* temporary file directory */ temp_dir = optarg; break; case 'D': /* debug flag */ debug = ON; break; case 'I': /* ignore FATAL errors */ ignore = ON; break; case '?': /* don't understand the option */ error(FATAL, ""); break; default: /* don't know what to do for ch */ error(FATAL, "missing case for option %c\n", ch); break; } /* End switch */ } /* End while */ argc -= optind; /* get ready for non-option args */ argv += optind; } /* End of options */ /*****************************************************************************/ readmap() { char *path; char *ptr; int fd; struct stat sbuf; /* * * Initializes the map table by reading an ASCII mapping file. If mapname begins * with a / it's the map table. Otherwise hostfontdir, mapname, and suffix are * combined to build the final pathname. If we can open the file we read it all * into memory, erase comments, and separate the font and file name pairs. When * we leave next points to the next free slot in the map[] array. If it's zero * nothing was in the file or we couldn't open it. * */ if ( hostfontdir == NULL || mapname == NULL ) return; if ( *mapname != '/' ) { if ( (path = (char *)malloc(strlen(hostfontdir) + strlen(mapname) + strlen(suffix) + 2)) == NULL ) error(FATAL, "no memory"); sprintf(path, "%s/%s%s", hostfontdir, mapname, suffix); } else path = mapname; if ( (fd = open(path, 0)) != -1 ) { if ( fstat(fd, &sbuf) == -1 ) error(FATAL, "can't fstat %s", path); if ( (stringspace = (char *)malloc(sbuf.st_size + 2)) == NULL ) error(FATAL, "no memory"); if ( read(fd, stringspace, sbuf.st_size) == -1 ) error(FATAL, "can't read %s", path); close(fd); stringspace[sbuf.st_size] = '\n'; /* just to be safe */ stringspace[sbuf.st_size+1] = '\0'; for ( ptr = stringspace; *ptr != '\0'; ptr++ ) /* erase comments */ if ( *ptr == '%' ) for ( ; *ptr != '\n' ; ptr++ ) *ptr = ' '; for ( ptr = stringspace; ; next++ ) { if ( (next % 50) == 0 ) map = allocate(map, next+50); map[next].downloaded = FALSE; map[next].font = strtok(ptr, " \t\n"); map[next].file = strtok(ptr = NULL, " \t\n"); if ( map[next].font == NULL ) break; if ( map[next].file == NULL ) error(FATAL, "map table format error - check %s", path); } /* End for */ } /* End if */ } /* End of readmap */ /*****************************************************************************/ readresident() { FILE *fp; char *path; int ch; int n; /* * * Reads a file that lists the resident fonts for a particular printer and marks * each font as already downloaded. Nothing's done if the file can't be read or * there's no mapping file. Comments, as in the map file, begin with a % and * extend to the end of the line. Added for Unix 4.0 lp. * */ if ( next == 0 || (printer == NULL && residentfonts == NULL) ) return; if ( printer != NULL ) { /* use Unix 4.0 lp pathnames */ sprintf(buf, "%s/printers/%s", HOSTDIR, printer); path = buf; } else path = residentfonts; if ( (fp = fopen(path, "r")) != NULL ) { while ( fscanf(fp, "%s", buf) != EOF ) if ( buf[0] == '%' ) while ( (ch = getc(fp)) != EOF && ch != '\n' ) ; else if ( (n = lookup(buf)) < next ) map[n].downloaded = TRUE; fclose(fp); } /* End if */ } /* End of readresident */ /*****************************************************************************/ arguments() { /* * * Makes sure all the non-option command line arguments are processed. If we get * here and there aren't any arguments left, or if '-' is one of the input files * we'll translate stdin. Assumes input files are part of a single PostScript * job and fonts can be downloaded at the start of each file. * */ if ( argc < 1 ) download(); else { while ( argc > 0 ) { fp_temp = NULL; if ( strcmp(*argv, "-") == 0 ) fp_in = stdin; else if ( (fp_in = fopen(*argv, "r")) == NULL ) error(FATAL, "can't open %s", *argv); download(); if ( fp_in != stdin ) fclose(fp_in); if ( fp_temp != NULL ) fclose(fp_temp); argc--; argv++; } /* End while */ } /* End else */ } /* End of arguments */ /*****************************************************************************/ done() { /* * * Clean things up before we quit. * */ if ( temp_file != NULL ) unlink(temp_file); } /* End of done */ /*****************************************************************************/ download() { int infontlist = FALSE; /* * * If next is zero the map table is empty and all we do is copy the input file * to stdout. Otherwise we read the input file looking for %%DocumentFonts: or * continuation comments, add any accessible fonts to the output file, and then * append the input file. When reading stdin we append lines to fp_temp and * recover them when we're ready to copy the input file. fp_temp will often * only contain part of stdin - if there's no %%DocumentFonts: (atend) comment * we stop reading fp_in after the header. * */ if ( next > 0 ) { if ( fp_in == stdin ) { if ( (temp_file = tempnam(temp_dir, "post")) == NULL ) error(FATAL, "can't generate temp file name"); if ( (fp_temp = fopen(temp_file, "w+r")) == NULL ) error(FATAL, "can't open %s", temp_file); unlink(temp_file); } /* End if */ while ( fgets(buf, sizeof(buf), fp_in) != NULL ) { if ( fp_temp != NULL ) fprintf(fp_temp, "%s", buf); if ( buf[0] != '%' || buf[1] != '%' ) { if ( (buf[0] != '%' || buf[1] != '!') && atend == FALSE ) break; infontlist = FALSE; } else if ( strncmp(buf, comment, strlen(comment)) == 0 ) { copyfonts(buf); infontlist = TRUE; } else if ( buf[2] == '+' && infontlist == TRUE ) copyfonts(buf); else infontlist = FALSE; } /* End while */ } /* End if */ copyinput(); } /* End of download */ /*****************************************************************************/ copyfonts(list) char *list; { char *font; char *path; int n; /* * * list points to a %%DocumentFonts: or continuation comment. What follows the * the keyword will be a list of fonts separated by white space (or (atend)). * Look for each font in the map table and if it's found copy the font file to * stdout (once only). * */ strtok(list, " \n"); /* skip to the font list */ while ( (font = strtok(NULL, " \t\n")) != NULL ) { if ( strcmp(font, ATEND) == 0 ) { atend = TRUE; break; } /* End if */ if ( (n = lookup(font)) < next ) { if ( *map[n].file != '/' ) { if ( (path = (char *)malloc(strlen(hostfontdir)+strlen(map[n].file)+2)) == NULL ) error(FATAL, "no memory"); sprintf(path, "%s/%s", hostfontdir, map[n].file); cat(path); free(path); } else cat(map[n].file); map[n].downloaded = TRUE; } /* End if */ } /* End while */ } /* End of copyfonts */ /*****************************************************************************/ copyinput() { /* * * Copies the input file to stdout. If fp_temp isn't NULL seek to the start and * add it to the output file - it's a partial (or complete) copy of stdin made * by download(). Then copy fp_in, but only seek to the start if it's not stdin. * */ if ( fp_temp != NULL ) { fseek(fp_temp, 0L, 0); while ( fgets(buf, sizeof(buf), fp_temp) != NULL ) printf("%s", buf); } /* End if */ if ( fp_in != stdin ) fseek(fp_in, 0L, 0); while ( fgets(buf, sizeof(buf), fp_in) != NULL ) printf("%s", buf); } /* End of copyinput */ /*****************************************************************************/ lookup(font) char *font; { int i; /* * * Looks for *font in the map table. Return the map table index if found and * not yet downloaded - otherwise return next. * */ for ( i = 0; i < next; i++ ) if ( strcmp(font, map[i].font) == 0 ) { if ( map[i].downloaded == TRUE ) i = next; break; } /* End if */ return(i); } /* End of lookup */ /*****************************************************************************/ Map *allocate(ptr, num) Map *ptr; int num; { /* * * Allocates space for num Map elements. Calls malloc() if ptr is NULL and * realloc() otherwise. * */ if ( ptr == NULL ) ptr = (Map *)malloc(num * sizeof(Map)); else ptr = (Map *)realloc(ptr, num * sizeof(Map)); if ( ptr == NULL ) error(FATAL, "no map memory"); return(ptr); } /* End of allocate */ /*****************************************************************************/