xref: /csrg-svn/local/transcript/src/psgrind.c (revision 32172)
1 /*	@(#)psgrind.c	1.1 09/15/87	*/
2 /*
3  *  psgrind - quick hack to grind C source files directly into
4  *  PostScript.
5  *
6  *  John Coker
7  *  University of California, Berkeley
8  *
9  *  The basis for this program is the enscript utility provided
10  *  by TranScript to driver the Apple LaserWriter printer.  This
11  *  code was taken and mangled without permission of any kind;
12  *  don't tell anyone.  -john
13  */
14 
15 #define POSTSCRIPTPRINTER "gp"
16 
17 #define HEADERFONT	"Helvetica-Bold"
18 #define BODYFONT 	"Helvetica"
19 #define KWORDFONT 	"Helvetica-Bold"
20 #define COMMENTFONT	"Helvetica-Oblique"
21 #define LITERALFONT  	"Courier"
22 
23 #ifndef PSGRINDPRO
24 #define PSGRINDPRO	"/psgrind.pro"
25 #endif !PSGRINDPRO
26 
27 #ifndef PSGRINDTEMP
28 #define PSGRINDTEMP	"/GRXXXXXX"
29 #endif !PSGRINDTEMP
30 
31 #define UperInch 1440
32 #define PtsPerInch 72
33 #define UperPt 20
34 #define TruePageWidth  (UperInch*17/2)
35 #define PageWidth  (UperInch*(8*17-3)/4)
36 #define PageLength (UperInch*(8*11-3)/8)
37 #define TruePageLength (UperInch*11)
38 
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <strings.h>
42 #include <pwd.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include "transcript.h"
46 
47 #define LPR "lpr"
48 #define REVERSE "psrev"
49 
50 #define MAXBAD 20	/* number of bad chars to pass before complaint */
51 
52 private struct stat S;
53 char   *ctime ();
54 char   *getenv ();
55 char   *rindex ();
56 
57 #define FSIZEMAX 256		/* number of chars per font */
58 
59 /* the layout of a font information block */
60 struct font {
61     char    name[100];		/* PostScript font name */
62     int     dsize;		/* size */
63     int     Xwid[FSIZEMAX];	/* X widths for each character */
64 };
65 
66 private struct font fonts[16];		/* 16 possible fonts at one time */
67 private int nf = 0;			/* number of fonts known about */
68 
69 private int HeaderFont = -1;		/* handle for header font */
70 private int BodyFont = -1;		/* handle for body font */
71 private int KwordFont = -1;		/* handle for keyword font */
72 private int LiteralFont = -1;		/* handle for literal font */
73 private int CommentFont = -1;		/* handle for comment font */
74 
75 private int TabWidth = TruePageWidth / 10;	/* width of a tab */
76 private int BSWidth;				/* width of a backspace */
77 
78 private int UperLine = UperInch / 7;
79 private int UperHLine = UperInch / 7;
80 
81 private char *prog;		/* program name argv[0] */
82 private char TempName[100];	/* name of temporary PostScript file */
83 private char OutName[256] = 0;	/* filename for disk output */
84 private int PipeOut = 0;	/* true if output to stdout (-p -) */
85 private int ListOmitted = 0;	/* list omitted chars on the tty */
86 private int LPTsimulate = 0;	/* true if an lpt should be simulated */
87 private int LinesLeft = 64;	/* lines left on page when in LPT mode */
88 private int LineNo;		/* line number in current file */
89 private int SeenText = 1;	/* true if seen some text on this page */
90 private int OutOnly = 0;	/* true if PS file only wanted */
91 private int Rotated = 0;	/* true if the page is to be rotated */
92 private int Reverse = 0;	/* output should be piped to psrev */
93 private char *PageSpec = 0;	/* ditto */
94 private int PreFeed = 0;	/* true if prefeed should be enabled */
95 private int TwoColumn = 0;	/* true of if in two-column mode */
96 private int FirstCol = 1;	/* true if we're printing column 1 */
97 private int NoTitle = 0;	/* true if title line is to be suppressed */
98 private int Cvted = 0;		/* true if converted a file to PS format */
99 
100 private int IgnoreGarbage = 0;	/* true if garbage should be ignored */
101 private int SeenFile = 0;	/* true if a file has been processed */
102 private int SeenFont = 0;	/* true if we've seen a font request */
103 private int ScannedFonts = 0;	/* true if we've scanned the font file */
104 private char *FileName = 0;	/* name of file currently being PSed */
105 private char *FileDate = 0;	/* last mod date of file being PSed */
106 private char DateStr[27];	/* thanks, but no thanks ctime! */
107 
108 private char *spoolJobClass = 0;
109 private char *spoolJobName = 0;
110 private char *PrinterName = 0;
111 private int spoolNotify = 0;
112 private int spoolNoBurst = 0;
113 private int spoolCopies = 1;
114 
115 private char tempstr[256];	/* various path names */
116 
117 private int CurFont;		/* current Font */
118 private int LastFont;		/* previous Font */
119 
120 private int cX, cY;		/* current page positions */
121 private int dX, dY;		/* desired page positions */
122 private int lX, lY;		/* page positions of the start of the line */
123 private int crX, crY;		/* X and Y increments to apply to CR's */
124 
125 #define None	0
126 #define RelX	1
127 #define	RelY	2
128 #define RelXY	3
129 #define AbsX	4
130 #define AbsY	8
131 #define AbsXY	12
132 
133 private int movepending;	/* moveto pending coords on stack */
134 private int showpending;	/* number of characters waiting to be shown */
135 private char *UsersHeader = 0;	/* user specified heading */
136 private char *Header = 0;	/* generated header (usually FileName) */
137 private int Page = 0;		/* current page number */
138 private int TotalPages = 0;	/* total number of pages printed */
139 private int TruncChars = 0;	/* number of characters truncated */
140 private int UndefChars = 0;	/* number of characters skipped because
141 				   they weren't defined in some font */
142 private int BadChars   = 0;	/* number of bad characters seen so far */
143 
144 private FILE *OutFile = 0;	/* output ps file */
145 
146 /* decode a fontname string - e.g. Courier10 Helvetica-Bold12 */
147 private decodefont (name, f)
148 register char  *name;
149 register struct font *f; {
150     register char *d, *p;
151 
152     SeenFont++;
153     if (ScannedFonts) {
154 	fprintf(stderr,"Fonts must be specified before any files are processed\n");
155 	exit(1);
156     }
157     p = name;
158     d = f->name;
159     while (isascii(*p) && (isalpha(*p) || (*p == '-'))) {*d++ = *p++;}
160     *d++ = '\0';
161     if (isascii(*p) && isdigit(*p)) {
162 	f->dsize = 0;
163 	do
164 	    f->dsize = f->dsize * 10 + *p++ - '0';
165 	while ('0' <= *p && *p <= '9');
166     }
167     if (*p || !f->dsize || !f->name[0]) {
168 	fprintf (stderr, "Poorly formed font name: \"%s\"\n", name);
169 	exit (1);
170     }
171 }
172 
173 
174 #define NOTDEF 0x8000
175 #define ForAllFonts(p) for(p = &fonts[nf-1]; p >= &fonts[0]; p--)
176 
177 /* Scan the font metrics directory looking for entries that match the
178  * entries in ``fonts''.  For entries
179  * that are found the data in the font description is filled in,
180  * if any are missing, it dies horribly.
181  */
182 private ScanFont () {
183     register struct font   *f;
184     register FILE *FontData;		/* afm file */
185     char    *c;
186     int     ccode, cwidth, inChars;
187     char   *MetricsDir = (char *) getenv ("METRICS");
188     char    FontFile[512];		/* afm file name */
189     char    afmbuf[BUFSIZ];
190 
191     if (MetricsDir == 0)
192 	MetricsDir = LibDir;
193 
194     if (!SeenFont & Rotated & TwoColumn) {
195 	fonts[HeaderFont].dsize = 10;
196 	fonts[BodyFont].dsize = 7;
197 	fonts[KwordFont].dsize = 7;
198 	fonts[LiteralFont].dsize = 8;
199 	fonts[CommentFont].dsize = 7;
200     }
201 
202     /* loop through fonts, find and read metric entry in dir */
203     ForAllFonts (f) {
204 	mstrcat(FontFile, MetricsDir, "/", sizeof FontFile);
205 	mstrcat(FontFile, FontFile, f->name, sizeof FontFile);
206 	mstrcat(FontFile, FontFile, ".afm", sizeof FontFile);
207 	if ((FontData = fopen(FontFile,"r")) == NULL){
208 	    fprintf(stderr,"Can't open font metrics file %s\n",FontFile);
209 	    exit(1);
210 	}
211 	/* read the .afm file to get the widths */
212 	for (ccode = 0; ccode < FSIZEMAX; ccode++) f->Xwid[ccode] = NOTDEF;
213 
214 	inChars = 0;
215 	while(fgets(afmbuf, sizeof afmbuf, FontData) != NULL) {
216 	    /* strip off newline */
217 	    if ((c = index(afmbuf, '\n')) == 0) {
218 		fprintf(stderr, "AFM file %s line too long %s\n", FontFile, afmbuf);
219 		exit(1);
220 	    }
221 	    *c = '\0';
222 	    if (*afmbuf == '\0') continue;
223 	    if (strcmp(afmbuf, "StartCharMetrics") == 0) {
224 		inChars++;
225 		continue;
226 	    }
227 	    if (strcmp(afmbuf, "EndCharMetrics") == 0) break;
228 	    if (inChars == 1) {
229 		if (sscanf(afmbuf, "C %d ; WX %d ;",&ccode,&cwidth) != 2) {
230 		    fprintf(stderr,"Trouble with AFM file %s\n",FontFile);
231 		    exit(1);
232 		}
233 		/* get out once we see an unencoded char */
234 		if (ccode == -1) break;
235 		if (ccode > 255) continue;
236 		f->Xwid[ccode] = (cwidth * f->dsize * UperPt) / 1000;
237 		continue;
238 	    }
239 	}
240     fclose (FontData);
241     }
242 
243     TabWidth = fonts[BodyFont].Xwid['0'] * 8; /* 8 * figure width */
244     BSWidth = fonts[BodyFont].Xwid[' ']; /* space width */
245 
246     UperLine = (fonts[BodyFont].dsize + 1) * UperPt;
247 
248     if (LPTsimulate)
249 	UperHLine = UperLine;
250     else
251 	UperHLine = (fonts[HeaderFont].dsize + 1) * UperPt;
252 
253     crX = 0;
254     crY = -UperLine;
255 }
256 
257 /* Return a font number for the font with the indicated name
258  * and size.  Adds info to the font list for the eventual search.
259  */
260 private DefineFont (name, size)
261 char   *name; {
262     register struct font   *p;
263     p = &fonts[nf];
264     strcpy (p->name, name);
265     p->dsize = size;
266     return (nf++);
267 }
268 
269 /* add a shown character to the PS file */
270 private OUTputc (c)
271 unsigned char    c; {
272     if (showpending == 0) {putc('(', OutFile); showpending++;}
273     if (c == '\\' || c=='(' || c == ')') putc('\\', OutFile);
274     if ((c > 0176) || (c < 040)) {
275 	putc('\\',OutFile);
276 	putc((c >> 6) +'0',OutFile);
277 	putc(((c >> 3) & 07)+'0', OutFile);
278 	putc((c & 07)+'0',OutFile);
279     }
280     else putc (c, OutFile);
281 }
282 
283 /* Set the current font */
284 private SetFont (f) {
285     FlushShow();
286     LastFont = CurFont;
287     fprintf(OutFile, "%d F\n", CurFont = f);
288 }
289 
290 /* Reset to previous font */
291 private PrevFont () {
292     int	temp;
293 
294     FlushShow();
295     temp = CurFont;
296     CurFont = LastFont;
297     LastFont = temp;
298     fprintf(OutFile, "%d F\n", CurFont);
299 }
300 
301 /* put a character onto the page at the desired X and Y positions.
302  * If the current position doesn't agree with the desired position, put out
303  * movement directives.  Leave the current position updated
304  * to account for the character.
305  */
306 private ShowChar (c)
307 register int c; {
308     register struct font   *f;
309     register    nX, nY;
310     static level = 0;
311 
312     level++;
313     f = &fonts[CurFont];
314 
315     if (f->Xwid[c] == NOTDEF) {
316 	UndefChars++;
317 	if(ListOmitted)
318 	    printf("\'%c\' (%03o) not found in font %s\n",c,c,f->name);
319 	if(level<=1){
320 	    ShowChar('\\');
321 	    ShowChar((c>>6)+'0');
322 	    ShowChar(((c>>3)&07)+'0');
323 	    ShowChar((c&07)+'0');
324 	}
325 	level--;
326 	return;
327     }
328     nX = dX + f->Xwid[c]; /* resulting position after showing this char */
329     nY = dY;
330 
331     if (c != ' ' || ((cX == dX) && (cY == dY)))
332 	if ((!Rotated && (nX <= PageWidth) && (nY <= PageLength))
333 	|| (Rotated && (nX <= PageLength) && (nY <= PageWidth))) {
334 	    if (cX != dX) {
335 	       if (cY != dY) {
336 		  FlushShow();
337 		  /* absolute x, relative y */
338 		  fprintf(OutFile,"%d %d",dX, dY);
339 		  movepending = AbsXY;
340 		  }
341 	       else {
342 		  FlushShow();
343 		  fprintf(OutFile,"%d",dX-cX); /* relative x */
344 		  movepending = RelX;
345 		  }
346 	    }
347 	    else if (cY != dY) {
348 		FlushShow();
349 		fprintf(OutFile,"%d",dY-cY); /* relative y */
350 		movepending = RelY;
351 		}
352 	    OUTputc (c);
353 	    showpending++;
354 	    cX = nX;
355 	    cY = nY;
356 	}
357 	else
358 	    TruncChars++;
359     dX = nX;
360     dY = nY;
361 
362     level--;
363 }
364 
365 /* put out a shown string to the PS file */
366 private ShowStr (s)
367 register char  *s; {
368     while (*s) {
369 	if (*s >= 040) ShowChar (*s);
370 	s++;
371     }
372 }
373 
374 /* flush pending show */
375 private FlushShow() {
376     if (showpending) {
377        putc(')',OutFile);
378 	switch (movepending) {
379 	    case RelX:
380 		putc('X',OutFile);
381 		break;
382 	    case RelY:
383 	    	putc('Y',OutFile);
384 		break;
385 	    case AbsXY:
386 	    	putc('B',OutFile);
387 		break;
388 	    case None:
389 	    	putc('S',OutFile);
390 	    	break;
391 	}
392 	putc('\n',OutFile);
393 	movepending = None;
394 	showpending = 0;
395     }
396 }
397 
398 /* put out a page heading to the PS file */
399 private InitPage () {
400     char    *basename();
401     char    header[200];
402     register int  OldFont = CurFont;
403 
404     TotalPages++;
405     fprintf(OutFile, "%%%%Page: ? %d\n", TotalPages);
406     fprintf(OutFile, "StartPage\n");
407     LinesLeft = 64;
408     SeenText = 0;
409     cX = cY = -1;
410     showpending = 0;
411     FirstCol = 1;
412     lX = dX = UperInch;
413     lY = dY = PageLength - UperHLine * 3 / 2;
414     if (Rotated) {
415         fprintf(OutFile, "Landscape\n");
416 	lX = dX = UperInch / 4;
417     }
418     movepending = None;
419     cX = dX; cY = dY;
420     if (!NoTitle) {
421 	SetFont (HeaderFont);
422         fprintf(OutFile, "%d %d ", cX, cY);
423         movepending = AbsXY;
424 	if (UsersHeader) {
425 	    if (*UsersHeader == 0) {
426 		fprintf(OutFile,"()B\n");
427 		movepending = None;
428 		showpending = 0;
429 	    }
430 	    else ShowStr (UsersHeader);
431 	}
432 	else {
433 	    Page++;
434 	    if (FileName == 0)
435 		    sprintf(header, "Page %d", Page);
436 	    else
437 	    sprintf (header, "%s,  page %d", basename(FileName), Page);
438 	    ShowStr (header);
439 	}
440 	FlushShow();
441 	dX = lX = lX + crX * 3;
442 	dY = lY = lY + crY * 3;
443     }
444     else {
445 	/* fake it to force a moveto */
446 	cX = cY = 0;
447     }
448     SetFont (OldFont);
449 }
450 
451 /* terminate a page. */
452 private ClosePage () {
453     FlushShow();
454     fprintf(OutFile,"EndPage\n");
455 }
456 
457 /* skip to a new page */
458 private PageEject () {
459     if (TwoColumn && FirstCol) {
460 	FirstCol = 0;
461 	lX = dX = TruePageWidth / 2;
462 	lY = dY = PageLength - (UperHLine * 3 / 2);
463 	if (Rotated) {
464 	   lX = dX = TruePageLength / 2;
465 	   }
466 	if (!NoTitle) {
467 	    dX = lX = lX + crX * 3;
468 	    dY = lY = lY + crY * 3;
469 	}
470     }
471     else {
472 	ClosePage ();
473 	InitPage ();
474     }
475     LinesLeft = 64;
476     SeenText = 0;
477 }
478 
479 #if 0
480 /* commented out AIS Fri Feb 22 10:00:36 1985 */
481 private PageMessage (TotalPages)
482 {
483     if (TotalPages > 0) {
484 	printf ("psgrind formatted[ %d page%s * %d cop%s ]\n",
485 		TotalPages, TotalPages > 1 ? "s" : "",
486 		spoolCopies, spoolCopies > 1 ? "ies" : "y" );
487     }
488 }
489 #endif
490 
491 private CommentHeader() {
492     long clock;
493     struct passwd *pswd;
494     char hostname[40];
495     /* copy the file, prepending a new comment header */
496     fprintf(OutFile,"%%!%s\n",COMMENTVERSION);
497     fprintf(OutFile,"%%%%Creator: ");
498     pswd = getpwuid(getuid());
499     gethostname(hostname, (int) sizeof hostname);
500     fprintf(OutFile,"%s:%s (%s)%s\n", hostname, pswd->pw_name,
501     	pswd->pw_gecos, spoolJobClass);
502 
503     fprintf(OutFile,"%%%%Title: %s %s\n",
504     	(FileName?FileName:"stdin"),
505 	spoolJobName);
506 
507     fprintf(OutFile,"%%%%CreationDate: %s",(time(&clock),ctime(&clock)));
508 }
509 
510 
511 /* list of C keywords to put in KwordFont */
512 private char *KeyWordList[] = {
513     "int", "char", "float", "double", "struct", "union",
514     "long", "short", "unsigned", "auto", "extern", "register",
515     "typedef", "static", "goto", "return", "sizeof", "break",
516     "continue", "if", "else", "for", "do", "while", "switch",
517     "case", "default", "entry", NULL
518 };
519 
520 /* macros identifying C identifiers */
521 #define isfirst(c)	(isalpha(c) || (c) == '_')
522 #define isident(c)	(isalnum(c) || (c) == '_')
523 
524 /* Copy the standard input file to the PS file */
525 private CopyFile () {
526     register int   c, last;
527     register int    col = 1;
528     int	InComment, InString, InChar, IsKword;
529     char token[50], *tend = token + sizeof (token) - 1;
530     register char *tp, **kwp;
531 
532     if (OutFile == 0) {
533 	if (OutOnly && !(PageSpec || Reverse)) {
534 	   OutFile = PipeOut ? stdout : fopen(OutName,"w");
535 	}
536 	else {
537 	    mktemp(mstrcat(TempName,TempDir,PSGRINDTEMP,sizeof TempName));
538 	    OutFile = fopen (TempName, "w");
539 	}
540     }
541     if (OutFile == NULL) {
542 	fprintf(stderr, "Can't create PS file %s\n",TempName);
543 	exit(1);
544     }
545     if (!ScannedFonts) {
546 	ScannedFonts++;
547 	ScanFont();
548     }
549     if (!Cvted) {
550 	CommentHeader();
551 	if (nf) {
552 	    register struct font *f;
553 	    fprintf(OutFile,"%%%%DocumentFonts:");
554  	    ForAllFonts(f) {
555 		fprintf(OutFile," %s",f->name);
556 	    }
557 	    fprintf(OutFile,"\n");
558 	}
559 	/* copy in fixed prolog */
560 	if (copyfile(mstrcat(tempstr,LibDir,PSGRINDPRO,sizeof tempstr),
561 		OutFile)) {
562 	    fprintf(stderr,"trouble copying prolog file \"%s\".\n",tempstr);
563 	    exit(1);
564 	}
565 	fprintf(OutFile,"StartEnscriptDoc %% end fixed prolog\n");
566 	DumpFonts();
567 	fprintf(OutFile,"%%%%EndProlog\n");
568 	if (PreFeed) {
569 	    fprintf(OutFile,"true DoPreFeed\n");
570 	}
571     }
572     Cvted++;
573 
574     Page = 0;
575     LineNo = 1;
576     BadChars = 0;		/* give each file a clean slate */
577     InitPage ();
578     last = '\0';
579     InComment = InChar = InString = 0;
580     while ((c = getchar ()) != EOF) {
581 	if ((c > 0177 || c < 0) && (!IgnoreGarbage)) {
582 	    if (BadChars++ > MAXBAD) {/* allow some kruft but not much */
583 	      fprintf(stderr,"\"%s\" not a text file? - char '\\%03o'@0%o\nTry -g.\n",
584 		    FileName ? FileName : "stdin", c, ftell (stdin) - 1);
585 	      exit(1);
586 	    }
587 	} else {
588 	    switch (c) {
589 		case 010: /* backspace */
590 		    dX -= BSWidth;
591 		    break;
592 		case 015: /* carriage return ^M */
593 		    dY = lY;
594 		    dX = lX;
595 		    break;
596 		case 012: /* linefeed ^J */
597 		    LineNo++;
598 		    if (dX != lX || dY != lY || !LPTsimulate || SeenText){
599 			SeenText++;
600 			dY = lY = lY + crY;
601 			dX = lX = lX + crX;
602 		    }
603 		    else
604 			LinesLeft = 64;
605 		    if (((!Rotated) && (dY < UperLine))
606 		    || (Rotated && (dY < ((PageLength-TruePageWidth) +
607 						3*UperLine+480)))
608 		    || (LPTsimulate && (--LinesLeft <= 0)))
609 			PageEject ();
610 		    col = 1;
611 		    break;
612 		case 014: /* form feed ^L */
613 		    PageEject ();
614 		    col = 1;
615 		    break;
616 		case 011: /* tab ^I */
617 		    col = (col - 1) / 8 * 8 + 9;
618 		    dX = lX + (col - 1) / 8 * TabWidth;
619 		    break;
620 	        case '\\': /* special escape */
621 		    last = c;
622 		    if ((c = getchar()) == EOF)
623 			goto done;
624 		    ShowChar('\\');
625 		    col++;
626 		    if (c == '\n') {
627 			/* want to leave newlines alone */
628 			ungetc(c, stdin);
629 		    } else {
630 			ShowChar(c);
631 			col++;
632 		    }
633 		    break;
634 	        case '"': /* a string quote mark */
635 		    if (InComment || InChar) {
636 			/* just put out the quote */
637 			ShowChar('"');
638 			col++;
639 		    } else if (InString) {
640 			ShowChar('"');
641 			col++;
642 			PrevFont();
643 			InString = 0;
644 		    } else {
645 			SetFont(LiteralFont);
646 			ShowChar('"');
647 			col++;
648 			InString = 1;
649 		    }
650 		    break;
651 	        case '\'': /* a char quote mark */
652 		    if (InComment || InString) {
653 			/* just put out the character */
654 			ShowChar('\'');
655 			col++;
656 		    } else if (InChar) {
657 			ShowChar('\'');
658 			col++;
659 			PrevFont();
660 			InChar = 0;
661 		    } else {
662 			SetFont(LiteralFont);
663 			ShowChar('\'');
664 			col++;
665 			InChar = 1;
666 		    }
667 		    break;
668 	        case '/':
669 		    if (InComment && last == '*') {
670 			ShowChar('/');
671 			col++;
672 			SetFont(BodyFont);
673 			InComment = 0;
674 		    } else if ((c = getchar()) == '*' && !InComment) {
675 			SetFont(CommentFont);
676 			InComment = 1;
677 			ShowChar('/');
678 			ShowChar('*');
679 			col += 2;
680 		    } else {
681 			ungetc(c, stdin);
682 			ShowChar('/');
683 			col++;
684 			c = '/';
685 		    }
686 		    break;
687 		default: /* plain text, put it out */
688 		    if (!InComment && !InString && isfirst(c)) {
689 			tp = token;
690 			while (isident(c) && tp < tend) {
691 			    *tp++ = c;
692 			    last = c;
693 			    c = getchar();
694 			}
695 			*tp = '\0';
696 			ungetc(c, stdin);
697 			tp = token;
698 			IsKword = 0;
699 			for (kwp = KeyWordList; *kwp != NULL; kwp++)
700 				if (!strcmp(*kwp, tp)) {
701 					IsKword = 1;
702 					break;
703 				}
704 			if (IsKword)
705 			    SetFont(KwordFont);
706 			ShowStr(tp);
707 			col += strlen(tp);
708 			if (IsKword)
709 			    SetFont(BodyFont);
710 		    } else if (fonts[CurFont].Xwid[c] != NOTDEF) {
711 			/* other normal character */
712 			ShowChar (c);
713 			col++;
714 		    } else { /* not in font, quote it */
715 			ShowChar ('\\');
716 			ShowChar ((c >> 6) + '0');
717 			ShowChar (((c >> 3) & 7) + '0');
718 			ShowChar ((c & 7) + '0');
719 			col += 4;
720 		    }
721 		    break;
722 	     }
723 	}
724 	last = c;
725     }
726 
727 done:
728     ClosePage ();
729 }
730 
731 /* dump the fonts to the PS file for setup */
732 private DumpFonts () {
733     register struct font   *f;
734 
735     ForAllFonts (f) {
736         fprintf(OutFile,"%d %d /%s\n",f-&fonts[0],f->dsize*UperPt,f->name);
737     }
738     fprintf(OutFile, "%d SetUpFonts\n", nf);
739 }
740 
741 
742 /*
743  * close the PS file
744  */
745 private ClosePS () {
746     fprintf(OutFile,"%%%%Trailer\n");
747     if (PreFeed) {
748 	fprintf(OutFile,"false DoPreFeed\n");
749     }
750     fprintf(OutFile,"EndEnscriptDoc\nEnscriptJob restore\n");
751 }
752 
753 private ProcessArg (p)
754 register char  *p; {
755     static  enum State {
756 	normal, PSname,
757 	H_fontname, B_fontname, K_fontname, C_fontname, L_fontname,
758 	grabheader, getclass, getjobname
759     } state = normal;
760 
761     switch (state) {
762 	case PSname:
763 	    strcpy (OutName, p);
764 	    if (strcmp(OutName,"-") == 0) PipeOut++;
765 	    state = normal;
766 	    break;
767 	case H_fontname:
768 	    decodefont (p, &fonts[HeaderFont]);
769 	    state = normal;
770 	    break;
771 	case B_fontname:
772 	    decodefont (p, &fonts[BodyFont]);
773 	    state = normal;
774 	    break;
775 	case K_fontname:
776 	    decodefont (p, &fonts[KwordFont]);
777 	    state = normal;
778 	    break;
779 	case L_fontname:
780 	    decodefont (p, &fonts[LiteralFont]);
781 	    state = normal;
782 	    break;
783 	case C_fontname:
784 	    decodefont (p, &fonts[CommentFont]);
785 	    state = normal;
786 	    break;
787 	case grabheader:
788 	    UsersHeader = p;
789 	    state = normal;
790 	    break;
791 	case getclass:
792 	    spoolJobClass = p;
793 	    state = normal;
794 	    break;
795 	case getjobname:
796 	    spoolJobName = p;
797 	    state = normal;
798 	    break;
799 	default:
800 	    if (*p == '-') while (*++p) switch (*p) {
801 		case '1':
802 		    TwoColumn = 0;
803 		    if (SeenFile) {
804 			fprintf(stderr,"Specify -1 before any files\n");
805 			exit(1);
806 		    }
807 		    break;
808 		case '2':
809 		    TwoColumn++;
810 		    if (SeenFile){
811 			fprintf(stderr,"Specify -2 before any files\n");
812 			exit(1);
813 		    }
814 		    break;
815 		case 'v':
816 		    Reverse = 1;
817 		    break;
818 		case 's':
819 		     PageSpec = (++p);
820 		     while (*p != '\0') p++;
821 		     return;
822 
823 		/* the following options allow uswer specification
824 		   of the five files used by the program */
825 		case 'H': state = H_fontname; break;
826 		case 'B': state = B_fontname; break;
827 		case 'K': state = K_fontname; break;
828 		case 'L': state = L_fontname; break;
829 		case 'C': state = C_fontname; break;
830 
831 		case 'g': IgnoreGarbage++; break;
832 		case 'o': ListOmitted++; break;
833 		case 'p': OutOnly++; state = PSname; break;
834 		case 'r':
835 		    Rotated++;
836 		    if (SeenFile){
837 			fprintf(stderr,"Specify rotation before any files\n");
838 			exit(1);
839 		    }
840 		    break;
841 		case 'R':
842 		    Rotated = 0;
843 		    if (SeenFile){
844 			fprintf(stderr,"Specify rotation before any files\n");
845 			exit(1);
846 		    }
847 		    break;
848 		case 'k':
849 		    PreFeed++;
850 		    if (SeenFile){
851 			fprintf(stderr,"Specify prefeed before any files\n");
852 			exit(1);
853 		    }
854 		    break;
855 
856 		/* the following switches are as in lpr(1) and */
857 		/* are passed through when spooling to a printer */
858 		case 'P': /* printer name */
859 		    PrinterName = (++p);
860 		    while (*p != '\0') p++;
861 		    return;
862 		case 'J': /* job name (title) for the Job: field */
863 		    state = getjobname;
864 		    break;
865 		case 'm': /* notify by mail */
866 		    spoolNotify = 1;
867 		    break;
868 		case 'h':
869 		    spoolNoBurst = 1;
870 		    break;
871 		case '#':
872 		    spoolCopies = atoi(++p);
873 		    if (spoolCopies < 1){
874 		        fprintf(stderr,"Bad argument for -# (number of copies)\n");
875 			exit(1);
876 		    }
877 		    break;
878 
879 		default:
880 		    printf ("Unknown option: %c\n", *p);
881 		    SeenFile++;
882 		    break;
883 		}
884 	    else {/* not a flag -- a filename */
885 		FileName = Header = p;
886 		if (freopen (FileName, "r", stdin) == NULL) {
887 		    printf ("Can't open %s\n", FileName);
888 		    exit (1);
889 		}
890 		fstat (fileno (stdin), &S);
891 		FileDate = strcpy(DateStr,ctime (&S.st_mtime));
892 		CopyFile ();
893 		fclose (stdin);
894 		SeenFile = 1;
895 	    }
896     }
897 }
898 
899 main (argc, argv)
900 char  **argv; {
901     register char  *p, *arg;
902 
903     prog = *argv;
904 
905     BodyFont = LastFont = CurFont = DefineFont (BODYFONT, 10);
906     HeaderFont = DefineFont (HEADERFONT, 12);
907     KwordFont = DefineFont (KWORDFONT, 10);
908     CommentFont = DefineFont (COMMENTFONT, 10);
909     LiteralFont = DefineFont (LITERALFONT, 11);
910 
911     /* process args in environment variable PSGRIND */
912     if (p = getenv ("PSGRIND"))
913 	while (1) {
914 	    register char   quote = ' ';
915 	    while (*p == ' ')
916 		p++;
917 	    if (*p == '"' || *p == '\'')
918 		quote = *p++;
919 	    arg = p;
920 	    while (*p != quote && *p != '\0')
921 		p++;
922 	    if (*p == '\0') {
923 		if (*arg)
924 		    ProcessArg (arg);
925 		break;
926 	    }
927 	    *p++ = '\0';
928 	    ProcessArg (arg);
929 	}
930 
931     /* process the command line arguments */
932     while (argc > 1) {
933 	argc--;
934 	ProcessArg (*++argv);
935     }
936 
937     if (!SeenFile) {
938 	FileName = Header = 0;
939 	FileDate = "";
940 	fstat (fileno (stdin), &S);
941 
942 	if ((S.st_mode & S_IFMT) == S_IFREG)
943 	    FileDate = strcpy(DateStr, ctime (&S.st_mtime));
944 	CopyFile ();
945     }
946 
947     if (Cvted) {
948 	ClosePS ();
949 	fclose (OutFile);
950 	OutFile = 0;
951     }
952     if (TruncChars)
953 	printf ("%d characters omitted because of long lines.\n",
954 		TruncChars);
955     if (UndefChars)
956 	printf ("%d characters omitted because of incomplete fonts.\n",
957 		UndefChars);
958 /*  PageMessage (TotalPages); */
959     if (Cvted) {
960 	if (OutOnly) {
961 	    if (Reverse || PageSpec) {
962 		char temparg[200];
963 		char *sargs[200];
964 		int args = 0;
965 
966 		int cpid = 0;
967 		/* feed Temporary through psrev */
968 		freopen(TempName, "r", stdin);
969 		if (!PipeOut) freopen(OutName, "w", stdout);
970 		unlink(TempName);
971 
972 		addarg(sargs, REVERSE, &args);
973 		addarg(sargs, "-r", &args);
974 		if (!Reverse) addarg(sargs, "-R", &args);
975 
976 		if (PageSpec) {
977 		    sprintf(temparg,"-s%s",PageSpec);
978 		    addarg(sargs, temparg, &args);
979 		}
980 		if ((cpid = fork()) < 0) pexit(prog,1);
981 		if (cpid == 0) {
982 		    execvp(REVERSE, sargs);
983 		    pexit(prog,1);
984 		}
985 		else {
986 		    wait(0);
987 		}
988 	    }
989 	  /*  fprintf (stderr,"PS file left on %s\n", OutName); */
990 	}
991 	else
992 	    SpoolIt();
993     }
994 }
995 
996 private addarg(argv, argstr, argc)
997 char **argv;
998 char *argstr;
999 register int *argc;
1000 {
1001     register char *p = (char *) malloc (strlen(argstr) + 1);
1002     strcpy (p, argstr);
1003     argv[(*argc)++] = p;
1004     argv[*argc] = '\0';
1005 }
1006 
1007 private SpoolIt()
1008 {
1009     char temparg[200];
1010     char *argstr[200];
1011     int nargs = 0;
1012 
1013     char *rargs[40];
1014     int  nr = 0;
1015     int cpid =0;
1016     int fdpipe[2];
1017 
1018     addarg(argstr, LPR, &nargs);
1019     if (spoolCopies > 1) {
1020 	sprintf(temparg,"-#%d",spoolCopies);
1021 	addarg(argstr, temparg, &nargs);
1022     }
1023     if (PrinterName) {
1024 	sprintf(temparg,"-P%s",PrinterName);
1025 	addarg(argstr, temparg, &nargs);
1026     }
1027     else if (getenv("PRINTER") == 0) {
1028 	/* no printer name known anywhere, use default */
1029 	sprintf(temparg,"-P%s",POSTSCRIPTPRINTER);
1030 	addarg(argstr, temparg, &nargs);
1031     }
1032     if (spoolJobClass) {
1033 	addarg(argstr, "-C", &nargs);
1034 	addarg(argstr, spoolJobClass, &nargs);
1035     }
1036     addarg(argstr, "-J", &nargs);
1037     if (spoolJobName) {
1038 	addarg(argstr, spoolJobName, &nargs);
1039     }
1040     else {
1041 	if (!FileName) addarg(argstr, "stdin", &nargs);
1042 	else addarg(argstr, FileName, &nargs);
1043     }
1044     if (spoolNotify) {
1045 	addarg(argstr, "-m", &nargs);
1046     }
1047     if (spoolNoBurst) {
1048 	addarg(argstr, "-h", &nargs);
1049     }
1050 
1051     if (Reverse || PageSpec) {
1052 	/* lpr input will be stdin */
1053 
1054 	addarg(rargs, REVERSE, &nr);
1055 	addarg(rargs, "-r", &nr);
1056 	if (!Reverse) addarg(rargs, "-R", &nr);
1057 	if (PageSpec) {
1058 	    sprintf(temparg,"-s%s",PageSpec);
1059 	    addarg(rargs, temparg, &nr);
1060         }
1061 /*	addarg(rargs, TempName, &nr); */
1062 
1063 	freopen(TempName,"r",stdin);
1064 	unlink(TempName);
1065 	if (pipe(fdpipe)) pexit(prog,1);
1066 	if ((cpid = fork()) < 0) pexit(prog,1);
1067 	else if (!cpid) { /* child */
1068 	    if (close(1)) {
1069 		pexit(prog,1);
1070 	    }
1071 	    /* set stdout to be the output pipe */
1072 	    if (dup (fdpipe[1]) == -1) {
1073 		pexit(prog,1);
1074 	    }
1075 	    /* don't want to read or write the pipe itself, since dup */
1076 	    if (close (fdpipe[1]) || close (fdpipe[0])) {
1077 		pexit(prog,1);
1078 	    }
1079 	    /* leave stderr alone */
1080 	    execvp (REVERSE, rargs);
1081 	    pexit(prog,1);
1082 	}
1083 	else {
1084 	    /* parent */
1085 	    /* replace stdin with pipe */
1086 	    if (close(0)) {
1087 		pexit(prog,1);
1088 	    }
1089 
1090 	    if (dup(fdpipe[0]) == -1) {
1091 		pexit(prog,1);
1092 	    }
1093 	    if (close (fdpipe[0]) || close (fdpipe[1])) {
1094 		pexit(prog,1);
1095 	    }
1096 
1097 	    /* leave stdout and stderr alone */
1098 	    execvp(LPR, argstr);
1099 	    pexit(prog,1);
1100 	}
1101     }
1102     else { /* just do lpr */
1103 	/* remove the temporary file after spooling */
1104 	addarg(argstr, "-r", &nargs); /* should we use a symbolic link too? */
1105 	addarg(argstr, TempName, &nargs);
1106 	execvp(LPR, argstr);
1107 	pexit(prog,1);
1108     }
1109 }
1110 
1111 char *
1112 basename(path)
1113 	char	*path;
1114 {
1115 	register char	*cp;
1116 
1117 	for (cp = path; *cp != '\0'; cp++)
1118 		;
1119 	for (--cp; cp > path && *cp != '/'; cp--)
1120 		;
1121 	if (*cp == '/' && *(cp+1) != '\0')
1122 		return (cp + 1);
1123 	else
1124 		return (path);
1125 }
1126