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