1 #ifndef lint
2 static char Notice[] = "Copyright (c) 1985 Adobe Systems Incorporated";
3 static char sccsid[] = "@(#)enscript.c	1.8 (Berkeley) 12/31/90";
4 static char *RCSID = "$Header: enscript.c,v 1.7 89/03/12 01:31:55 van Exp $";
5 
6 #endif
7 /*
8  * enscript.c
9  *
10  * Copyright (c) 1985 Adobe Systems Incorporated
11  *
12  * inspired by Gosling's cz there have been major overhauls, but the input
13  * language is the same: new widths format generate PostScript (much easier
14  * than Press) new and renamed switches (to match 4.2bsd lpr spooler) obeys
15  * PostScript comment conventions doesn't worry so much about fonts
16  * (everything is scalable and rotatable, use PS font names, no face
17  * properties)
18  *
19  * enscript generates POSTSCRIPT print files of a special kind the coordinate
20  * system is in 20ths of a point. (1440 per inch)
21  *
22  * Edit History: Andrew Shore: Mon Nov 18 14:05:05 1985 End Edit History.
23  *
24  * RCSLOG: $Log:	enscript.c,v $
25  * Revision 1.7  89/03/12  01:31:55  van
26  * we have to escape special chars in title strings.
27  *
28  * Revision 1.6  89/03/10  00:30:39  van
29  * might as well let the user change everything.
30  *
31  * Revision 1.5  89/03/09  23:19:17  van
32  * gcc lint.
33  *
34  * Revision 1.4  89/03/09  23:08:50  van
35  * let user set the fonts used in 'gaudy' mode
36  *
37  * Revision 1.3  88/03/06  17:23:58  leres
38  * Fix logic bug; only spool output if that's want we want.
39  *
40  * Revision 1.2  86/07/03  00:06:31  van
41  * reformatted.  removed SYSV ifdefs.
42  *  Revision 1.1  86/07/03  00:03:12  van Initial
43  * revision
44  *
45  * Revision 2.1  85/11/24  11:48:55  shore Product Release 2.0
46  *
47  * Revision 1.3  85/11/20  00:10:01  shore Added System V support (input options
48  * and spooling) margins/linecount reworked (Dataproducts) incompatible
49  * options changes, getopt! Guy Riddle's Gaudy mode and other changes output
50  * spooling messages, pages, copies
51  *
52  * Revision 1.2  85/05/14  11:22:14  shore *** empty log message ***
53  *
54  *
55  */
56 
57 #define POSTSCRIPTPRINTER "PostScript"
58 
59 #define BODYROMAN "Courier"
60 #define BODYSZ	  10
61 #define HEADFONT  "Courier-Bold"
62 #define HEADSZ	  10
63 #define GHEADFONT "Helvetica-Bold"
64 #define GHEADSZ	  14
65 #define SHEADFONT "Times-Bold"
66 #define SHEADSZ	  12
67 #define PGNUMFONT "Helvetica-Bold"
68 #define PGNUMSZ   24
69 #define DATEFONT  "Times-Bold"
70 #define DATESZ    12
71 
72 #ifdef DEBUG
73 #define debugp(x) {fprintf x ; VOIDC fflush(stderr);}
74 #else
75 #define debugp(x)
76 #endif
77 
78 #define UperInch (1440L)
79 #define PtsPerInch 72
80 #define UperPt 20
81 
82 /* virtual page is 8 x 10.5 inches (for Toshiba compat) */
83 #define PageWidth ((long) UperInch*8)
84 #define PageLength ((long)((UperInch*21)/2))
85 
86 /* #define PageLength ((long) ((long) (UperInch*(8*11-3)))/8) */
87 /* #define PageWidth  ((long) ((long) (UperInch*(8*17-3)))/8) */
88 
89 /* true page is 8.5 x 11 inches */
90 #define TruePageWidth  (UperInch*17/2)
91 #define TruePageLength ((long)(UperInch*11))
92 
93 #include <stdio.h>
94 #include <ctype.h>
95 #include <pwd.h>
96 #include <strings.h>
97 #include <sys/time.h>
98 #include <signal.h>
99 #include <sys/types.h>
100 #include <sys/stat.h>
101 #include "transcript.h"
102 
103 #define LPR "lpr"
104 
105 #define MAXBAD 20		/* number of bad chars to pass before
106 				 * complaint */
107 
108 private struct stat S;
109 
110 extern double atof();
111 extern char *optarg;		/* getopt current opt char */
112 extern int optind;		/* getopt argv index */
113 
114 private VOID int1 ();
115 private FlushShow();
116 
117 #define FSIZEMAX 256		/* number of chars per font */
118 
119 /* the layout of a font information block */
120 struct font {
121 	char    name[100];	/* PostScript font name */
122 	int     dsize;		/* size */
123 	int     Xwid[FSIZEMAX];	/* X widths for each character */
124 };
125 
126 private struct font fonts[16];	/* 16 possible fonts at one time */
127 private int nf = 0;		/* number of fonts known about */
128 
129 private int TabWidth;		/* width of a tab */
130 private int BSWidth;		/* width of a backspace */
131 
132 private long UperLine = UperInch / 7;
133 private long UperHLine = UperInch / 7;
134 
135 private char *prog;		/* program name argv[0] */
136 private char *libdir;		/* place for prolog and widths files */
137 private char *tempdir;		/* place for temp file */
138 private char TempName[100];	/* name of temporary PostScript file */
139 private char OutName[256] = "";	/* filename for disk output */
140 private int PipeOut = FALSE;	/* output to stdout (-p -) */
141 private int ListOmitted = FALSE;/* list omitted chars on the tty */
142 private int BeQuiet = FALSE;	/* suppress stderr error messages */
143 private int Verbose = FALSE;	/* silly informational messages */
144 private int Gaudy = FALSE;	/* pretty bars along the top */
145 private int LPTsimulate = FALSE;/* an lpt should be simulated */
146 private int Lines = 0;		/* max lines per page */
147 private int LinesLeft = 66;	/* lines left on page when in LPT mode */
148 private int LineMax = 64;	/* ? */
149 private int SeenText = TRUE;	/* true if seen some text on this page */
150 private int OutOnly = FALSE;	/* PS file only wanted */
151 private int Rotated = FALSE;	/* pages to be rotated landscape */
152 private int PreFeed = FALSE;	/* prefeed should be enabled */
153 private int TwoColumn = FALSE;	/* two-column mode */
154 private int FirstCol = TRUE;	/* we're printing column 1 */
155 private int NoTitle = FALSE;	/* title line is suppressed */
156 private int Cvted = FALSE;	/* converted a file to PS format */
157 
158 private int IgnoreGarbage = FALSE;	/* garbage should be ignored */
159 private int SeenFont = FALSE;	/* we've seen a font request */
160 private int SeenFile = FALSE;	/* a file has been processed */
161 private int ScannedFonts = FALSE;	/* we've scanned the font file */
162 private char *FileName = 0;	/* name of file currently being PSed */
163 private char *FileDate = 0;	/* last mod date of file being PSed */
164 private char DateStr[27];	/* thanks, but no thanks ctime! */
165 private int spoolNoBurst = FALSE;	/* no break page flag for spooler */
166 
167 #ifdef BSD
168 private char *spoolJobClass = NULL;
169 private char *spoolJobName = NULL;
170 
171 #endif
172 private char *PrinterName = NULL;
173 private int spoolNotify = 0;
174 private char *spoolCopies = "1";
175 
176 private char tempstr[256];	/* various path names */
177 
178 private int CurFont;		/* current Font */
179 private int fontindex[26];	/* table of fonts, indexed by font designator
180 				 * ('a' to 'z') */
181 
182 /* indexes for default fonts */
183 
184 #define Roman fontindex['r'-'a']
185 #define HeaderFont fontindex['h'-'a']
186 #define SHeaderFont fontindex['i'-'a']
187 #define DateFont fontindex['j'-'a']
188 #define PgNumFont fontindex['k'-'a']
189 
190 private long cX, cY;		/* current page positions */
191 private long dX, dY;		/* desired page positions */
192 private long lX, lY;		/* page positions of the start of the line */
193 private long crX, crY;		/* X and Y increments to apply to CR's */
194 private long maxX;		/* maximum x coord on line */
195 private long minY;		/* minimum y coord on page */
196 private long Xoffset;		/* amount to offset left margin */
197 
198 #define None	0
199 #define RelX	1
200 #define	RelY	2
201 #define RelXY	3
202 #define AbsX	4
203 #define AbsY	8
204 #define AbsXY	12
205 
206 private int movepending;	/* moveto pending coords on stack */
207 private int showpending;	/* number of characters waiting to be shown */
208 private int pagepending;	/* start on next page when have something to
209 				 * print */
210 private char *UsersHeader = NULL;	/* user specified heading */
211 private char *Header = NULL;	/* generated header (usually FileName) */
212 private char *Header2 = NULL;	/* second header line for Gaudy */
213 private int Page = 0;		/* current page number */
214 private int TotalPages = 0;	/* total number of pages printed */
215 private int TruncChars = 0;	/* number of characters truncated */
216 private int UndefChars = 0;	/* number of characters skipped because they
217 				 * weren't defined in some font */
218 private int BadChars = 0;	/* number of bad characters seen so far */
219 private FILE *OutFile = NULL;	/* output ps file */
220 
221 
222 /* decode a fontname string - e.g. Courier10 Helvetica-Bold12 */
223 private
224 decodefont (name, f)
225 	register char *name;
226 	register struct font *f;
227 {
228 	register char *d, *p;
229 
230 	p = name;
231 	d = f->name;
232 	f->dsize = 0;
233 	while (isascii (*p) && (isalpha (*p) || (*p == '-'))) {
234 		*d++ = *p++;
235 	}
236 	*d++ = '\0';
237 	while (isascii (*p) && isdigit (*p)) {
238 		f->dsize = f->dsize * 10 + *p++ - '0';
239 	}
240 	if (*p || !f->dsize || !f->name[0]) {
241 		fprintf (stderr, "%s: poorly formed font name & size: \"%s\"\n",
242 			 prog, name);
243 		exit (1);
244 	}
245 }
246 
247 
248 #define NOTDEF 0x8000
249 #define ForAllFonts(p) for(p = &fonts[nf-1]; p >= &fonts[0]; p--)
250 
251 
252 /*
253  * Scan the font metrics directory looking for entries that match the entries
254  * in ``fonts''.  For entries that are found the data in the font description
255  * is filled in, if any are missing, it dies horribly.
256  */
257 private VOID ScanFont ()
258 {
259 	register struct font *f;
260 	register FILE *FontData;/* afm file */
261 	char   *c;
262 	int     ccode, cwidth, inChars;
263 	char    FontFile[512];	/* afm file name */
264 	char    afmbuf[BUFSIZ];
265 
266 
267 	if (!SeenFont) {
268 		if (Lines == 0)
269 			Lines = 64;
270 		if (Rotated && TwoColumn)
271 			fonts[Roman].dsize = 7;
272 	}
273 	/* loop through fonts, find and read metric entry in dir */
274 	ForAllFonts (f) {
275 		VOIDC   mstrcat (FontFile, libdir, "/", sizeof FontFile);
276 
277 		VOIDC   mstrcat (FontFile, FontFile, f->name, sizeof FontFile);
278 
279 		VOIDC   mstrcat (FontFile, FontFile, ".afm", sizeof FontFile);
280 
281 		if ((FontData = fopen (FontFile, "r")) == NULL) {
282 			fprintf (stderr, "%s: can't open font metrics file %s\n",
283 				 prog, FontFile);
284 			exit (1);
285 		}
286 		/* read the .afm file to get the widths */
287 		for (ccode = 0; ccode < FSIZEMAX; ccode++)
288 			f->Xwid[ccode] = NOTDEF;
289 
290 		inChars = 0;
291 		while (fgets (afmbuf, sizeof afmbuf, FontData) != NULL) {
292 			/* strip off newline */
293 			if ((c = INDEX (afmbuf, '\n')) == 0) {
294 				fprintf (stderr, "%s: AFM file %s line too long %s\n",
295 					 prog, FontFile, afmbuf);
296 				exit (1);
297 			}
298 			*c = '\0';
299 			if (*afmbuf == '\0')
300 				continue;
301 			if (strcmp (afmbuf, "StartCharMetrics") == 0) {
302 				inChars++;
303 				continue;
304 			}
305 			if (strcmp (afmbuf, "EndCharMetrics") == 0)
306 				break;
307 			if (inChars == 1) {
308 				if (sscanf (afmbuf, "C %d ; WX %d ;", &ccode, &cwidth) != 2) {
309 					fprintf (stderr, "%s: trouble with AFM file %s\n",
310 						 prog, FontFile);
311 					exit (1);
312 				}
313 				/* get out once we see an unencoded char */
314 				if (ccode == -1)
315 					break;
316 				if (ccode > 255)
317 					continue;
318 				f->Xwid[ccode] =
319 					(short) (((long) cwidth * (long) f->dsize * (long) UperPt)
320 						 / 1000L);
321 				continue;
322 			}
323 		}
324 		VOIDC   fclose (FontData);
325 	}
326 
327 	/*
328 	 * Tab width is problematical for proportionally-spaced fonts.
329 	 * Attempt to make tabs wide enough that things hand-tabulated
330 	 * for monospaced fonts still fit in columns.
331 	 */
332 	if (fonts[Roman].Xwid['0'] == fonts[Roman].Xwid['M'])
333 		TabWidth = fonts[Roman].Xwid['0'] * 8;	/* 8 * figure width */
334 	else
335 		TabWidth = fonts[Roman].Xwid['0'] * 10;	/* 10 * figure width */
336 	BSWidth = fonts[Roman].Xwid[' '];	/* space width */
337 
338 	UperLine = (fonts[Roman].dsize + 1) * UperPt;
339 
340 	if (LPTsimulate) {
341 		UperHLine = UperLine;
342 		Lines = LineMax = 66;
343 	} else {
344 		UperHLine = (fonts[HeaderFont].dsize + 1) * UperPt;
345 	}
346 
347 	crX = 0;
348 	crY = -UperLine;
349 
350 }
351 
352 
353 /*
354  * Return a font number for the font with the indicated name and size.  Adds
355  * info to the font list for the eventual search.
356  */
357 private int
358 DefineFont (name, size)
359 	char   *name;
360 {
361 	register struct font *p;
362 
363 	p = &fonts[nf];
364 	VOIDC   strcpy (p->name, name);
365 
366 	p->dsize = size;
367 	return (nf++);
368 }
369 
370 ResetFont(indx, name, size)
371 	char   *name;
372 {
373 	register struct font *p;
374 
375 	p = &fonts[indx];
376 	VOIDC   strcpy (p->name, name);
377 	p->dsize = size;
378 }
379 
380 /* dump the fonts to the PS file for setup */
381 private VOID
382 DumpFonts ()
383 {
384 	register struct font *f;
385 
386 	ForAllFonts (f) {
387 		fprintf (OutFile, "%d %d /%s\n", f - &fonts[0], f->dsize * UperPt, f->name);
388 	}
389 	fprintf (OutFile, "%d SetUpFonts\n", nf);
390 }
391 
392 /* add a shown character to the PS file */
393 private VOID
394 OUTputc (c)
395 	register int c;
396 {
397 	if (!showpending) {
398 		putc ('(', OutFile);
399 		showpending = TRUE;
400 	}
401 	if (c == '\\' || c == '(' || c == ')')
402 		putc ('\\', OutFile);
403 	if ((c > 0176) || (c < 040)) {
404 		putc ('\\', OutFile);
405 		putc ((c >> 6) + '0', OutFile);
406 		putc (((c >> 3) & 07) + '0', OutFile);
407 		putc ((c & 07) + '0', OutFile);
408 	} else
409 		putc (c, OutFile);
410 }
411 
412 /* put a correctly escaped string to the PS file */
413 private VOID
414 OUTstr(s)
415 	register char *s;
416 {
417 	if (!showpending) {
418 		putc ('(', OutFile);
419 		showpending = TRUE;
420 	}
421 	while (*s)
422 		OUTputc(*s++);
423 
424 	putc(')', OutFile);
425 	showpending = FALSE;
426 }
427 
428 /* Set the current font */
429 private VOID
430 SetFont (f)
431 	int     f;
432 {
433 	FlushShow ();
434 	CurFont = f;
435 	fprintf (OutFile, "%d F\n", f);
436 }
437 
438 /*
439  * put a character onto the page at the desired X and Y positions. If the
440  * current position doesn't agree with the desired position, put out movement
441  * directives.  Leave the current position updated to account for the
442  * character.
443  */
444 private VOID
445 ShowChar (c)
446 	register int c;
447 {
448 	register struct font *f;
449 	register long nX, nY;
450 	static  level = 0;
451 	VOID PageEject(), InitPage();
452 
453 	level++;
454 	f = &fonts[CurFont];
455 
456 	if (f->Xwid[c] == NOTDEF) {
457 		UndefChars++;
458 		if (ListOmitted)
459 			fprintf (stderr, "%s: \\%03o not found in font %s\n",
460 				 prog, c, f->name);
461 		if (level <= 1) {
462 			ShowChar ('\\');
463 			ShowChar ((c >> 6) + '0');
464 			ShowChar (((c >> 3) & 07) + '0');
465 			ShowChar ((c & 07) + '0');
466 		}
467 		level--;
468 		return;
469 	}
470 	nX = dX + f->Xwid[c];	/* resulting position after showing this char */
471 	nY = dY;
472 
473 	if (c != ' ' || ((cX == dX) && (cY == dY))) {
474 		/*
475 		 * If character doesn't fit on this line
476 		 * (and we're not at left margin), simulate newline
477 		 * and then call ourselves recursively.
478 		 */
479 		if (nX > maxX && dX > lX) {
480 			SeenText = TRUE;
481 			dY = lY = lY + crY;
482 			dX = lX = lX + crX;
483 			if ((dY < minY) || (--LinesLeft <= 0)) {
484 				PageEject ();
485 				if (pagepending)
486 					InitPage ();
487 			}
488 			ShowChar(c);
489 			level--;
490 			return;
491 		}
492 		if (cX != dX) {
493 			if (cY != dY) {
494 				FlushShow ();
495 				/* absolute x, relative y */
496 				fprintf (OutFile, "%ld %ld", dX, dY);
497 				movepending = AbsXY;
498 			} else {
499 				FlushShow ();
500 				fprintf (OutFile, "%ld", dX - cX);	/* relative x */
501 				movepending = RelX;
502 			}
503 		} else if (cY != dY) {
504 			FlushShow ();
505 			fprintf (OutFile, "%ld", dY - cY);	/* relative y */
506 			movepending = RelY;
507 		}
508 		OUTputc (c);
509 		showpending = TRUE;
510 		cX = nX;
511 		cY = nY;
512 	}
513 	dX = nX;
514 	dY = nY;
515 
516 	level--;
517 }
518 
519 /* put out a shown string to the PS file */
520 private VOID
521 ShowStr (s)
522 	register char *s;
523 {
524 	while (*s) {
525 		if (*s >= 040)
526 			ShowChar (*s);
527 		s++;
528 	}
529 }
530 
531 /* flush pending show */
532 private
533 FlushShow ()
534 {
535 	if (showpending) {
536 		putc (')', OutFile);
537 		switch (movepending) {
538 		case RelX:
539 			putc ('X', OutFile);
540 			break;
541 		case RelY:
542 			putc ('Y', OutFile);
543 			break;
544 		case AbsXY:
545 			putc ('B', OutFile);
546 			break;
547 		case None:
548 			putc ('S', OutFile);
549 			break;
550 		}
551 		putc ('\n', OutFile);
552 		movepending = None;
553 		showpending = FALSE;
554 	}
555 }
556 
557 /* put out a page heading to the PS file */
558 private VOID
559 InitPage ()
560 {
561 	char    header[200];
562 	register int OldFont = CurFont;
563 
564 	TotalPages++;
565 	fprintf (OutFile, "%%%%Page: ? %d\n", TotalPages);
566 	fprintf (OutFile, "StartPage\n");
567 	SeenText = FALSE;
568 	cX = cY = -1;
569 	showpending = pagepending = FALSE;
570 	FirstCol = TRUE;
571 	if (Rotated) {
572 		fprintf (OutFile, "Landscape\n");
573 		lX = dX = UperInch / 4 + Xoffset;
574 		lY = dY = PageLength - (UperHLine * 3) / 2;
575 		maxX = TruePageLength;
576 		/* minY = (PageLength - TruePageWidth) + 3*UperLine+480; */
577 		minY = (TruePageLength - TruePageWidth) + (TruePageWidth - PageWidth) / 2;
578 	} else {
579 		lX = dX = Xoffset +
580 			   (TwoColumn? (UperInch * 0.3) : ((UperInch * 5) / 8));
581 		lY = dY = PageLength - UperHLine;
582 		maxX = TruePageWidth;
583 		minY = (UperInch / 4);	/* 0.25 inches */
584 	}
585 	movepending = None;
586 	cX = dX;
587 	cY = dY;
588 
589 	if (!NoTitle) {
590 		if (Gaudy) {
591 			OUTstr(UsersHeader);
592 			if (Header2)
593 			    OUTstr(Header2);
594 			else
595 			    OUTstr(Header);
596 			fprintf (OutFile, "[%s](%d)Gaudy\n", FileDate, ++Page);
597 			cX = cY = 0;	/* force moveto here */
598 		} else {
599 			SetFont (HeaderFont);
600 			fprintf (OutFile, "%ld %ld ", cX, cY);
601 			movepending = AbsXY;
602 			if (UsersHeader) {
603 				if (*UsersHeader == 0) {
604 					fprintf (OutFile, "()B\n");
605 					movepending = None;
606 					showpending = FALSE;
607 				} else
608 					ShowStr (UsersHeader);
609 			} else {
610 				VOIDC sprintf (header, "%s        %s        %d",
611 					       Header? Header : "              ", FileDate, ++Page);
612 
613 				ShowStr (header);
614 			}
615 			FlushShow ();
616 		}
617 		dX = lX = lX + crX * 2;
618 		dY = lY = lY + crY * 2;
619 	} else {
620 		/* fake it to force a moveto */
621 		cX = cY = 0;
622 	}
623 	if (TwoColumn)
624 		maxX = maxX / 2 - BSWidth;
625 	else
626 		maxX -= ((long) (UperInch * 0.3));
627 	LineMax = (lY - minY) / (-crY);
628 	if ((Lines <= 0) || (Lines > LineMax))
629 		Lines = LinesLeft = LineMax;
630 	else
631 		LinesLeft = Lines;
632 	SetFont (OldFont);
633 }
634 
635 private VOID
636 ClosePage ()
637 {
638 	FlushShow ();
639 	if (!pagepending)
640 		fprintf (OutFile, "EndPage\n");
641 	pagepending = TRUE;
642 }
643 
644 /* skip to a new page */
645 private VOID
646 PageEject ()
647 {
648 	if (TwoColumn && FirstCol) {
649 		FirstCol = FALSE;
650 		if (Rotated) {
651 			lY = dY = PageLength - (UperHLine * 3) / 2;
652 			lX = dX = Xoffset + TruePageLength / 2;
653 			maxX = TruePageLength - UperInch * 0.3;
654 		} else {
655 			lY = dY = PageLength - UperHLine;
656 			lX = dX = Xoffset + TruePageWidth / 2;
657 			maxX = TruePageWidth - UperInch * 0.3;
658 		}
659 		if (!NoTitle) {
660 			dX = lX = lX + crX * 2;
661 			dY = lY = lY + crY * 2;
662 		}
663 	} else
664 		ClosePage ();
665 	LinesLeft = Lines;
666 	SeenText = FALSE;
667 }
668 
669 private VOID
670 CommentHeader ()
671 {
672 	long    clock;
673 	struct passwd *pswd;
674 	char    hostname[40];
675 
676 	/* copy the file, prepending a new comment header */
677 	fprintf (OutFile, "%%!%s\n", COMMENTVERSION);
678 	fprintf (OutFile, "%%%%Creator: ");
679 	pswd = getpwuid ((int) getuid ());
680 	VOIDC   gethostname (hostname, (int) sizeof hostname);
681 
682 	fprintf (OutFile, "%s:%s (%s)\n", hostname, pswd->pw_name, pswd->pw_gecos);
683 	fprintf (OutFile, "%%%%Title: %s\n", (FileName ? FileName : "stdin"));
684 	fprintf (OutFile, "%%%%CreationDate: %s", (VOIDC time (&clock), ctime (&clock)));
685 }
686 
687 /* Copy the standard input file to the PS file */
688 private VOID
689 CopyFile ()
690 {
691 	register int c;
692 
693 	if (OutFile == 0) {
694 		if (OutOnly) {
695 			OutFile = PipeOut ? stdout : fopen (OutName, "w");
696 		} else {
697 			VOIDC   mktemp (mstrcat (TempName, tempdir,
698 				            ENSCRIPTTEMP, sizeof TempName));
699 			VOIDC   strcpy (OutName, TempName);
700 
701 			VOIDC   umask (077);
702 
703 			OutFile = fopen (TempName, "w");
704 		}
705 	}
706 	if (OutFile == NULL) {
707 		fprintf (stderr, "%s: can't create PS file %s\n", prog, TempName);
708 		exit (1);
709 	}
710 	if (!ScannedFonts) {
711 		ScannedFonts = TRUE;
712 		ScanFont ();
713 	}
714 	if (!Cvted) {
715 		CommentHeader ();
716 		if (nf) {
717 			register struct font *f;
718 
719 			fprintf (OutFile, "%%%%DocumentFonts:");
720 			ForAllFonts (f) {
721 				fprintf (OutFile, " %s", f->name);
722 			}
723 			fprintf (OutFile, "\n");
724 		}
725 		/* copy in fixed prolog */
726 		if (copyfile (mstrcat (tempstr, libdir, ENSCRIPTPRO, sizeof tempstr),
727 			      OutFile)) {
728 			fprintf (stderr, "%s: trouble copying prolog file\n", prog);
729 			exit (1);
730 		}
731 		fprintf (OutFile, "StartEnscriptDoc %% end fixed prolog\n");
732 		DumpFonts ();
733 		if (Gaudy)
734 			fprintf (OutFile, "%s %s InitGaudy\n",
735 				 Rotated ? "10.55" : "8.0", TwoColumn ? "true" : "false");
736 		if (PreFeed) {
737 			fprintf (OutFile, "true DoPreFeed\n");
738 		}
739 		fprintf (OutFile, "%%%%EndProlog\n");
740 	}
741 	Cvted = TRUE;
742 
743 	Page = 0;
744 	BadChars = 0;		/* give each file a clean slate */
745 	pagepending = TRUE;
746 	while ((c = getchar ()) != EOF)
747 		if ((c > 0177 || c < 0) && (!IgnoreGarbage)) {
748 			if (BadChars++ > MAXBAD) {	/* allow some kruft but
749 							 * not much */
750 				fprintf (stderr, "%s: \"%s\" not a text file? Try -g.\n",
751 				     prog, FileName ? FileName : "(stdin)");
752 				if (!PipeOut)
753 					VOIDC   unlink (OutName);
754 
755 				exit (1);
756 			}
757 		} else if (c >= ' ') {
758 			if (pagepending)
759 				InitPage ();
760 			ShowChar (c);
761 		} else
762 			switch (c) {
763 			case 010:	/* backspace */
764 				dX -= BSWidth;
765 				break;
766 			case 015:	/* carriage return ^M */
767 				dY = lY;
768 				dX = lX;
769 				break;
770 			case 012:	/* linefeed ^J */
771 				if (pagepending)
772 					InitPage ();
773 				if (dX != lX || dY != lY || !LPTsimulate || SeenText) {
774 					SeenText = TRUE;
775 					dY = lY = lY + crY;
776 					dX = lX = lX + crX;
777 				} else
778 					LinesLeft = LineMax;
779 				if ((dY < minY) || (--LinesLeft <= 0))
780 					PageEject ();
781 				break;
782 			case 033:	/* escape */
783 				switch (c = getchar ()) {
784 				case '7':	/* backup one line */
785 					dY = lY = lY - crY;
786 					dX = lX = lX - crX;
787 					break;
788 				case '8':	/* backup 1/2 line */
789 					dY -= crY / 2;
790 					dX -= crX / 2;
791 					break;
792 				case '9':	/* forward 1/2 linefeed */
793 					dY += crY / 2;
794 					dX += crX / 2;
795 					break;
796 				case 'F':	/* font setting */
797 					c = getchar ();
798 					if ('a' <= c && c <= 'z')
799 						if (fontindex[c - 'a'] >= 0)
800 							SetFont (fontindex[c - 'a']);
801 						else {
802 							fprintf (stderr, "%s: font '%c' not defined\n",
803 								 prog, c);
804 							exit (1);
805 						}
806 					else {
807 						fprintf (stderr, "%s: bad font code in file: '%c'\n",
808 							 prog, c);
809 						exit (1);
810 					}
811 					break;
812 				case 'D':	/* date string */
813 					VOIDC fgets (DateStr, sizeof(DateStr), stdin);
814 					FileDate = DateStr;
815 					break;
816 				case 'U':	/* new "user's" heading */
817 					{
818 						static char header[100];
819 						VOIDC   fgets (header, sizeof(header), stdin);
820 
821 						UsersHeader = header;
822 						break;
823 					}
824 				case 'H':	/* new heading */
825 					{
826 						static char header[100];
827 
828 						VOIDC   fgets (header, sizeof(header), stdin);
829 
830 						ClosePage ();
831 						Header2 = header;
832 						Page = 0;
833 						break;
834 					}
835 				}
836 				break;
837 			case '%':	/* included PostScript line */
838 				{
839 					char    psline[200];
840 					VOIDC   fgets (psline, sizeof(psline), stdin);
841 
842 					fprintf (OutFile, "%s\n", psline);
843 					break;
844 				}
845 			case 014:	/* form feed ^L */
846 				PageEject ();
847 				break;
848 			case 011:	/* tab ^I */
849 				if (pagepending)
850 					InitPage ();
851 				dX += TabWidth - ((dX - lX) % TabWidth);
852 				break;
853 			default:	/* other control character, take your
854 					 * chances */
855 				if (pagepending)
856 					InitPage ();
857 				ShowChar (c);
858 			}
859 	ClosePage ();
860 }
861 
862 
863 /*
864  * close the PS file
865  */
866 private VOID
867 ClosePS ()
868 {
869 	fprintf (OutFile, "%%%%Trailer\n");
870 	if (PreFeed) {
871 		fprintf (OutFile, "false DoPreFeed\n");
872 	}
873 	fprintf (OutFile, "EndEnscriptDoc\nEnscriptJob restore\n");
874 }
875 
876 
877 private VOID
878 SetTime (tval)
879 	long    tval;
880 {
881 	struct tm *tp;
882 
883 	if (Gaudy) {
884 		tp = localtime (&tval);
885 		VOIDC   sprintf (DateStr, "(%02d/%02d/%02d)(%02d:%02d:%02d)",
886 			           tp->tm_year, tp->tm_mon + 1, tp->tm_mday,
887 			               tp->tm_hour, tp->tm_min, tp->tm_sec);
888 	} else {
889 		VOIDC   strcpy (DateStr, ctime (&tval));
890 
891 		DateStr[24] = '\0';	/* get rid of newline */
892 	}
893 
894 	FileDate = DateStr;
895 }
896 
897 
898 
899 
900 #define ARGS "12gGBlL:oqrRkKf:F:b:p:J:C:P:#:mhO:s:v"
901 
902 private VOID ParseArgs (ac, av)
903 	int     ac;
904 	char  **av;
905 {
906 	int     argp;
907 
908 	while ((argp = getopt (ac, av, ARGS)) != EOF) {
909 		debugp ((stderr, "option: %c\n", argp));
910 		switch (argp) {
911 		case '1':
912 			TwoColumn = FALSE;
913 			break;
914 		case '2':
915 			TwoColumn = TRUE;
916 			break;
917 		case 'G':
918 			Gaudy = TRUE;
919 			if (UsersHeader == NULL)
920 				UsersHeader = "";
921 			if (Header == NULL)
922 				Header = "";
923 			/* warning: fonts must be defined in this order! */
924 			ResetFont(HeaderFont, GHEADFONT, GHEADSZ);
925 			if (SHeaderFont == -1)
926 				SHeaderFont = DefineFont(SHEADFONT, SHEADSZ);
927 			DateFont = DefineFont(DATEFONT, DATESZ);
928 			PgNumFont = DefineFont(PGNUMFONT, PGNUMSZ);
929 			break;
930 		case 'g':
931 			IgnoreGarbage = TRUE;
932 			break;
933 		case 'B':
934 			NoTitle = TRUE;
935 			break;
936 		case 'l':
937 			LPTsimulate = TRUE;
938 			NoTitle = TRUE;
939 			Lines = 66;
940 			break;
941 		case 'L':
942 			Lines = atoi (optarg);
943 			break;
944 		case 'o':
945 			ListOmitted = TRUE;
946 			break;
947 		case 'q':
948 			BeQuiet = TRUE;
949 			break;
950 		case 'r':
951 			Rotated = TRUE;
952 			break;
953 		case 'R':
954 			Rotated = FALSE;
955 			break;
956 		case 'k':
957 			PreFeed = TRUE;
958 			break;
959 		case 'K':
960 			PreFeed = FALSE;
961 			break;
962 		case 'f':{
963 				register char font = 'r';
964 				int    *whichfont;
965 
966 				if (*optarg == '-') {
967 					font = *++optarg;
968 					optarg++;
969 				}
970 				if ((font < 'a') || ('z' < font)) {
971 					fprintf (stderr,
972 						 "%s: '%c' isn't a valid font designator.\n",
973 						 prog, font);
974 					exit (1);
975 				}
976 				whichfont = &fontindex[font - 'a'];
977 				if (*whichfont < 0)
978 					*whichfont = nf++;
979 				decodefont (optarg, &fonts[*whichfont]);
980 				if (font == 'r')
981 					SeenFont++;
982 			}
983 			break;
984 		case 'F':
985 			if (HeaderFont == -1)
986 				HeaderFont = nf++;
987 			decodefont (optarg, &fonts[HeaderFont]);
988 			break;
989 		case 'b':
990 			UsersHeader = optarg;
991 			break;
992 		case 's':
993 			Header2 = optarg;
994 			break;
995 		case 'p':
996 			OutOnly = TRUE;
997 			VOIDC   strcpy (OutName, optarg);
998 
999 			if (strcmp (OutName, "-") == 0)
1000 				PipeOut = TRUE;
1001 			break;
1002 		case 'h':
1003 			spoolNoBurst = TRUE;
1004 			break;
1005 			/* BSD lpr options processing */
1006 		case 'm':
1007 			spoolNotify = argp;
1008 			break;
1009 		case '#':
1010 			spoolCopies = optarg;
1011 			break;
1012 		case 'C':
1013 			spoolJobClass = optarg;
1014 			break;
1015 		case 'J':
1016 			spoolJobName = optarg;
1017 			break;
1018 		case 'P':
1019 			PrinterName = optarg;
1020 			break;
1021 		case '?':
1022 			/* bad option */
1023 			break;
1024 		case 'O':
1025 			Xoffset = atof(optarg) * (double)UperInch;
1026 			if (Xoffset < 0)
1027 				Xoffset = 0;
1028 			break;
1029 		case 'v':
1030 			Verbose = TRUE;
1031 			break;
1032 		default:
1033 			break;
1034 		}
1035 	}
1036 }
1037 
1038 
1039 /* addarg is used to construct an argv for the spooler */
1040 private VOID
1041 addarg (argv, argstr, argc)
1042 	char  **argv;
1043 	char   *argstr;
1044 	register int *argc;
1045 {
1046 	register char *p = (char *) malloc ((unsigned) (strlen (argstr) + 1));
1047 	VOIDC   strcpy (p, argstr);
1048 
1049 	argv[(*argc)++] = p;
1050 	argv[*argc] = '\0';
1051 }
1052 
1053 
1054 private VOID
1055 SpoolIt ()
1056 {
1057 	char    temparg[200];
1058 	char   *argstr[200];
1059 	int     nargs = 0;
1060 
1061 
1062 	addarg (argstr, LPR, &nargs);
1063 	/* BSD spooler */
1064 	if (atoi (spoolCopies) > 1) {
1065 		VOIDC   sprintf (temparg, "-#%s", spoolCopies);
1066 
1067 		addarg (argstr, temparg, &nargs);
1068 	}
1069 	if ((PrinterName == NULL) && ((PrinterName = envget ("PRINTER")) == NULL)) {
1070 		PrinterName = POSTSCRIPTPRINTER;
1071 	}
1072 	VOIDC   sprintf (temparg, "-P%s", PrinterName);
1073 
1074 	addarg (argstr, temparg, &nargs);
1075 
1076 	if (spoolJobClass) {
1077 		addarg (argstr, "-C", &nargs);
1078 		addarg (argstr, spoolJobClass, &nargs);
1079 	}
1080 	addarg (argstr, "-J", &nargs);
1081 	if (spoolJobName) {
1082 		addarg (argstr, spoolJobName, &nargs);
1083 	} else {
1084 		if (!FileName)
1085 			addarg (argstr, "stdin", &nargs);
1086 		else
1087 			addarg (argstr, FileName, &nargs);
1088 	}
1089 	if (spoolNotify) {
1090 		addarg (argstr, "-m", &nargs);
1091 	}
1092 	if (spoolNoBurst) {
1093 		addarg (argstr, "-h", &nargs);
1094 	}
1095 	/* remove the temporary file after spooling */
1096 	addarg (argstr, "-r", &nargs);	/* should we use a symbolic link too? */
1097 	addarg (argstr, TempName, &nargs);
1098 
1099 #ifdef DEBUG
1100 	{
1101 		int     i;
1102 
1103 		fprintf (stderr, "called spooler with: ");
1104 		for (i = 0; i < nargs; i++)
1105 			fprintf (stderr, "(%s)", argstr[i]);
1106 		fprintf (stderr, "\n");
1107 	}
1108 #endif
1109 
1110 	execvp (LPR, argstr);
1111 	pexit2 (prog, "can't exec spooler", 1);
1112 }
1113 
1114 
1115 private char *eargv[60];
1116 private int eargc = 1;
1117 
1118 
1119 main (argc, argv)
1120 	int     argc;
1121 	char  **argv;
1122 {
1123 	register char *p;	/* pointer to "ENSCRIPT" in env */
1124 
1125 	prog = *argv;		/* argv[0] is program name */
1126 
1127 	debugp ((stderr, "PL %ld PW %ld TPL %ld TPW %ld\n", PageLength, PageWidth, TruePageLength, TruePageWidth));
1128 
1129 	if (signal (SIGINT, int1) == SIG_IGN) {
1130 		VOIDC   signal (SIGINT, SIG_IGN);
1131 		VOIDC   signal (SIGQUIT, SIG_IGN);
1132 		VOIDC   signal (SIGHUP, SIG_IGN);
1133 		VOIDC   signal (SIGTERM, SIG_IGN);
1134 	} else {
1135 		VOIDC   signal (SIGQUIT, int1);
1136 		VOIDC   signal (SIGHUP, int1);
1137 		VOIDC   signal (SIGTERM, int1);
1138 	}
1139 
1140 	{
1141 		register int i;
1142 
1143 		for (i = 0; i < 26; i++)
1144 			fontindex[i] = -1;
1145 	}
1146 
1147 	if ((libdir = envget ("PSLIBDIR")) == NULL)
1148 		libdir = LibDir;
1149 	if ((tempdir = envget ("PSTEMPDIR")) == NULL)
1150 		tempdir = TempDir;
1151 
1152 	Roman = CurFont = DefineFont (BODYROMAN, BODYSZ);
1153 	HeaderFont = DefineFont (HEADFONT, HEADSZ);
1154 
1155 	/* process args in environment variable ENSCRIPT */
1156 	if (p = envget ("ENSCRIPT")) {
1157 		while (1) {
1158 			register char quote = ' ';
1159 
1160 			while (*p == ' ')
1161 				p++;
1162 			if ((*p == '"') || (*p == '\''))
1163 				quote = *p++;
1164 			eargv[eargc++] = p;
1165 			while ((*p != quote) && (*p != '\0'))
1166 				p++;
1167 			if (*p == '\0')
1168 				break;
1169 			*p++ = '\0';
1170 		}
1171 		ParseArgs (eargc, eargv);
1172 		if (eargc != optind) {
1173 			fprintf (stderr, "%s: bad environment variable ENSCRIPT \"%s\"\n",
1174 				 prog, envget ("ENSCRIPT"));
1175 			exit (1);
1176 		}
1177 	}
1178 	/* process the command line arguments */
1179 	optind = 1;		/* reset getopt global */
1180 	ParseArgs (argc, argv);
1181 
1182 	/* process non-option args */
1183 	for (; optind < argc; optind++) {
1184 		FileName = Header = argv[optind];
1185 		if (freopen (FileName, "r", stdin) == NULL) {
1186 			fprintf (stderr, "%s: can't open %s\n", prog, FileName);
1187 			exit (1);
1188 		}
1189 		VOIDC   fstat (fileno (stdin), &S);
1190 
1191 		SetTime (S.st_mtime);
1192 		CopyFile ();
1193 		VOIDC   fclose (stdin);
1194 
1195 		SeenFile = TRUE;
1196 	}
1197 	if (!SeenFile) {
1198 		FileName = Header = Gaudy ? "" : 0;
1199 		VOIDC   fstat (fileno (stdin), &S);
1200 
1201 		if ((S.st_mode & S_IFMT) == S_IFREG)
1202 			SetTime (S.st_mtime);
1203 		else
1204 			SetTime (time ((long *) 0));
1205 		CopyFile ();
1206 	}
1207 	if (Cvted) {
1208 		ClosePS ();
1209 		VOIDC   fclose (OutFile);
1210 
1211 		OutFile = 0;
1212 	}
1213 	if (TruncChars && !BeQuiet)
1214 		fprintf (stderr, "%s: %d characters omitted because of long lines.\n",
1215 			 prog, TruncChars);
1216 	if (UndefChars && !BeQuiet)
1217 		fprintf (stderr, "%s: %d characters omitted because of incomplete fonts.\n",
1218 			 prog, UndefChars);
1219 	if (Verbose && (TotalPages > 0)) {
1220 	    fprintf(stderr,"[ %d page%s * %s cop%s ]\n",
1221 		    TotalPages, TotalPages > 1 ? "s" : "",
1222 		    spoolCopies, atoi(spoolCopies) > 1 ? "ies" : "y" );
1223 	}
1224 	if (Cvted && !OutOnly) {
1225 		SpoolIt ();	/* does an exec */
1226 	}
1227 }
1228 
1229 
1230 /* signal catcher */
1231 private VOID
1232 int1 ()
1233 {
1234 	if ((!PipeOut) && (OutName != NULL) && (*OutName != '\0')) {
1235 		VOIDC   unlink (OutName);
1236 	}
1237 	exit (1);
1238 }
1239