diff options
Diffstat (limited to 'src/cmd/postscript/download/download.c')
-rw-r--r-- | src/cmd/postscript/download/download.c | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/src/cmd/postscript/download/download.c b/src/cmd/postscript/download/download.c new file mode 100644 index 00000000..ab6bdd52 --- /dev/null +++ b/src/cmd/postscript/download/download.c @@ -0,0 +1,545 @@ +/* + * + * 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; /* 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. + * + */ + + fp_in = stdin; + + 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(unsharp(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(unsharp(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(unsharp(*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(unsharp(path)); + free(path); + } else cat(unsharp(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 */ + +/*****************************************************************************/ + |