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