URI:
       tdownload.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tdownload.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 /*****************************************************************************/