xref: /original-bsd/local/transcript/src/psgrind.c (revision 2301fdfb)
1 /*	@(#)psgrind.c	1.2 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     "asm", "auto", "break", "case", "char", "continue", "default", "do",
514     "double", "else", "entry", "enum", "extern", "float", "for", "fortran",
515     "goto", "if", "int", "long", "register", "return", "short", "sizeof",
516     "static", "struct", "switch", "typedef", "union", "unsigned", "void",
517     "while", 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;
700 			     *kwp != NULL && **kwp <= *tp; kwp++)
701 				if (!strcmp(*kwp, tp)) {
702 					IsKword = 1;
703 					break;
704 				}
705 			if (IsKword)
706 			    SetFont(KwordFont);
707 			ShowStr(tp);
708 			col += strlen(tp);
709 			if (IsKword)
710 			    SetFont(BodyFont);
711 		    } else if (fonts[CurFont].Xwid[c] != NOTDEF) {
712 			/* other normal character */
713 			ShowChar (c);
714 			col++;
715 		    } else { /* not in font, quote it */
716 			ShowChar ('\\');
717 			ShowChar ((c >> 6) + '0');
718 			ShowChar (((c >> 3) & 7) + '0');
719 			ShowChar ((c & 7) + '0');
720 			col += 4;
721 		    }
722 		    break;
723 	     }
724 	}
725 	last = c;
726     }
727 
728 done:
729     ClosePage ();
730 }
731 
732 /* dump the fonts to the PS file for setup */
733 private DumpFonts () {
734     register struct font   *f;
735 
736     ForAllFonts (f) {
737         fprintf(OutFile,"%d %d /%s\n",f-&fonts[0],f->dsize*UperPt,f->name);
738     }
739     fprintf(OutFile, "%d SetUpFonts\n", nf);
740 }
741 
742 
743 /*
744  * close the PS file
745  */
746 private ClosePS () {
747     fprintf(OutFile,"%%%%Trailer\n");
748     if (PreFeed) {
749 	fprintf(OutFile,"false DoPreFeed\n");
750     }
751     fprintf(OutFile,"EndEnscriptDoc\nEnscriptJob restore\n");
752 }
753 
754 private ProcessArg (p)
755 register char  *p; {
756     static  enum State {
757 	normal, PSname,
758 	H_fontname, B_fontname, K_fontname, C_fontname, L_fontname,
759 	grabheader, getclass, getjobname
760     } state = normal;
761 
762     switch (state) {
763 	case PSname:
764 	    strcpy (OutName, p);
765 	    if (strcmp(OutName,"-") == 0) PipeOut++;
766 	    state = normal;
767 	    break;
768 	case H_fontname:
769 	    decodefont (p, &fonts[HeaderFont]);
770 	    state = normal;
771 	    break;
772 	case B_fontname:
773 	    decodefont (p, &fonts[BodyFont]);
774 	    state = normal;
775 	    break;
776 	case K_fontname:
777 	    decodefont (p, &fonts[KwordFont]);
778 	    state = normal;
779 	    break;
780 	case L_fontname:
781 	    decodefont (p, &fonts[LiteralFont]);
782 	    state = normal;
783 	    break;
784 	case C_fontname:
785 	    decodefont (p, &fonts[CommentFont]);
786 	    state = normal;
787 	    break;
788 	case grabheader:
789 	    UsersHeader = p;
790 	    state = normal;
791 	    break;
792 	case getclass:
793 	    spoolJobClass = p;
794 	    state = normal;
795 	    break;
796 	case getjobname:
797 	    spoolJobName = p;
798 	    state = normal;
799 	    break;
800 	default:
801 	    if (*p == '-') while (*++p) switch (*p) {
802 		case '1':
803 		    TwoColumn = 0;
804 		    if (SeenFile) {
805 			fprintf(stderr,"Specify -1 before any files\n");
806 			exit(1);
807 		    }
808 		    break;
809 		case '2':
810 		    TwoColumn++;
811 		    if (SeenFile){
812 			fprintf(stderr,"Specify -2 before any files\n");
813 			exit(1);
814 		    }
815 		    break;
816 		case 'v':
817 		    Reverse = 1;
818 		    break;
819 		case 's':
820 		     PageSpec = (++p);
821 		     while (*p != '\0') p++;
822 		     return;
823 
824 		/* the following options allow uswer specification
825 		   of the five files used by the program */
826 		case 'H': state = H_fontname; break;
827 		case 'B': state = B_fontname; break;
828 		case 'K': state = K_fontname; break;
829 		case 'L': state = L_fontname; break;
830 		case 'C': state = C_fontname; break;
831 
832 		case 'g': IgnoreGarbage++; break;
833 		case 'o': ListOmitted++; break;
834 		case 'p': OutOnly++; state = PSname; break;
835 		case 'r':
836 		    Rotated++;
837 		    if (SeenFile){
838 			fprintf(stderr,"Specify rotation before any files\n");
839 			exit(1);
840 		    }
841 		    break;
842 		case 'R':
843 		    Rotated = 0;
844 		    if (SeenFile){
845 			fprintf(stderr,"Specify rotation before any files\n");
846 			exit(1);
847 		    }
848 		    break;
849 		case 'k':
850 		    PreFeed++;
851 		    if (SeenFile){
852 			fprintf(stderr,"Specify prefeed before any files\n");
853 			exit(1);
854 		    }
855 		    break;
856 
857 		/* the following switches are as in lpr(1) and */
858 		/* are passed through when spooling to a printer */
859 		case 'P': /* printer name */
860 		    PrinterName = (++p);
861 		    while (*p != '\0') p++;
862 		    return;
863 		case 'J': /* job name (title) for the Job: field */
864 		    state = getjobname;
865 		    break;
866 		case 'm': /* notify by mail */
867 		    spoolNotify = 1;
868 		    break;
869 		case 'h':
870 		    spoolNoBurst = 1;
871 		    break;
872 		case '#':
873 		    spoolCopies = atoi(++p);
874 		    if (spoolCopies < 1){
875 		        fprintf(stderr,"Bad argument for -# (number of copies)\n");
876 			exit(1);
877 		    }
878 		    break;
879 
880 		default:
881 		    printf ("Unknown option: %c\n", *p);
882 		    SeenFile++;
883 		    break;
884 		}
885 	    else {/* not a flag -- a filename */
886 		FileName = Header = p;
887 		if (freopen (FileName, "r", stdin) == NULL) {
888 		    printf ("Can't open %s\n", FileName);
889 		    exit (1);
890 		}
891 		fstat (fileno (stdin), &S);
892 		FileDate = strcpy(DateStr,ctime (&S.st_mtime));
893 		CopyFile ();
894 		fclose (stdin);
895 		SeenFile = 1;
896 	    }
897     }
898 }
899 
900 main (argc, argv)
901 char  **argv; {
902     register char  *p, *arg;
903 
904     prog = *argv;
905 
906     BodyFont = LastFont = CurFont = DefineFont (BODYFONT, 10);
907     HeaderFont = DefineFont (HEADERFONT, 12);
908     KwordFont = DefineFont (KWORDFONT, 10);
909     CommentFont = DefineFont (COMMENTFONT, 10);
910     LiteralFont = DefineFont (LITERALFONT, 11);
911 
912     /* process args in environment variable PSGRIND */
913     if (p = getenv ("PSGRIND"))
914 	while (1) {
915 	    register char   quote = ' ';
916 	    while (*p == ' ')
917 		p++;
918 	    if (*p == '"' || *p == '\'')
919 		quote = *p++;
920 	    arg = p;
921 	    while (*p != quote && *p != '\0')
922 		p++;
923 	    if (*p == '\0') {
924 		if (*arg)
925 		    ProcessArg (arg);
926 		break;
927 	    }
928 	    *p++ = '\0';
929 	    ProcessArg (arg);
930 	}
931 
932     /* process the command line arguments */
933     while (argc > 1) {
934 	argc--;
935 	ProcessArg (*++argv);
936     }
937 
938     if (!SeenFile) {
939 	FileName = Header = 0;
940 	FileDate = "";
941 	fstat (fileno (stdin), &S);
942 
943 	if ((S.st_mode & S_IFMT) == S_IFREG)
944 	    FileDate = strcpy(DateStr, ctime (&S.st_mtime));
945 	CopyFile ();
946     }
947 
948     if (Cvted) {
949 	ClosePS ();
950 	fclose (OutFile);
951 	OutFile = 0;
952     }
953     if (TruncChars)
954 	printf ("%d characters omitted because of long lines.\n",
955 		TruncChars);
956     if (UndefChars)
957 	printf ("%d characters omitted because of incomplete fonts.\n",
958 		UndefChars);
959 /*  PageMessage (TotalPages); */
960     if (Cvted) {
961 	if (OutOnly) {
962 	    if (Reverse || PageSpec) {
963 		char temparg[200];
964 		char *sargs[200];
965 		int args = 0;
966 
967 		int cpid = 0;
968 		/* feed Temporary through psrev */
969 		freopen(TempName, "r", stdin);
970 		if (!PipeOut) freopen(OutName, "w", stdout);
971 		unlink(TempName);
972 
973 		addarg(sargs, REVERSE, &args);
974 		addarg(sargs, "-r", &args);
975 		if (!Reverse) addarg(sargs, "-R", &args);
976 
977 		if (PageSpec) {
978 		    sprintf(temparg,"-s%s",PageSpec);
979 		    addarg(sargs, temparg, &args);
980 		}
981 		if ((cpid = fork()) < 0) pexit(prog,1);
982 		if (cpid == 0) {
983 		    execvp(REVERSE, sargs);
984 		    pexit(prog,1);
985 		}
986 		else {
987 		    wait(0);
988 		}
989 	    }
990 	  /*  fprintf (stderr,"PS file left on %s\n", OutName); */
991 	}
992 	else
993 	    SpoolIt();
994     }
995 }
996 
997 private addarg(argv, argstr, argc)
998 char **argv;
999 char *argstr;
1000 register int *argc;
1001 {
1002     register char *p = (char *) malloc (strlen(argstr) + 1);
1003     strcpy (p, argstr);
1004     argv[(*argc)++] = p;
1005     argv[*argc] = '\0';
1006 }
1007 
1008 private SpoolIt()
1009 {
1010     char temparg[200];
1011     char *argstr[200];
1012     int nargs = 0;
1013 
1014     char *rargs[40];
1015     int  nr = 0;
1016     int cpid =0;
1017     int fdpipe[2];
1018 
1019     addarg(argstr, LPR, &nargs);
1020     if (spoolCopies > 1) {
1021 	sprintf(temparg,"-#%d",spoolCopies);
1022 	addarg(argstr, temparg, &nargs);
1023     }
1024     if (PrinterName) {
1025 	sprintf(temparg,"-P%s",PrinterName);
1026 	addarg(argstr, temparg, &nargs);
1027     }
1028     else if (getenv("PRINTER") == 0) {
1029 	/* no printer name known anywhere, use default */
1030 	sprintf(temparg,"-P%s",POSTSCRIPTPRINTER);
1031 	addarg(argstr, temparg, &nargs);
1032     }
1033     if (spoolJobClass) {
1034 	addarg(argstr, "-C", &nargs);
1035 	addarg(argstr, spoolJobClass, &nargs);
1036     }
1037     addarg(argstr, "-J", &nargs);
1038     if (spoolJobName) {
1039 	addarg(argstr, spoolJobName, &nargs);
1040     }
1041     else {
1042 	if (!FileName) addarg(argstr, "stdin", &nargs);
1043 	else addarg(argstr, FileName, &nargs);
1044     }
1045     if (spoolNotify) {
1046 	addarg(argstr, "-m", &nargs);
1047     }
1048     if (spoolNoBurst) {
1049 	addarg(argstr, "-h", &nargs);
1050     }
1051 
1052     if (Reverse || PageSpec) {
1053 	/* lpr input will be stdin */
1054 
1055 	addarg(rargs, REVERSE, &nr);
1056 	addarg(rargs, "-r", &nr);
1057 	if (!Reverse) addarg(rargs, "-R", &nr);
1058 	if (PageSpec) {
1059 	    sprintf(temparg,"-s%s",PageSpec);
1060 	    addarg(rargs, temparg, &nr);
1061         }
1062 /*	addarg(rargs, TempName, &nr); */
1063 
1064 	freopen(TempName,"r",stdin);
1065 	unlink(TempName);
1066 	if (pipe(fdpipe)) pexit(prog,1);
1067 	if ((cpid = fork()) < 0) pexit(prog,1);
1068 	else if (!cpid) { /* child */
1069 	    if (close(1)) {
1070 		pexit(prog,1);
1071 	    }
1072 	    /* set stdout to be the output pipe */
1073 	    if (dup (fdpipe[1]) == -1) {
1074 		pexit(prog,1);
1075 	    }
1076 	    /* don't want to read or write the pipe itself, since dup */
1077 	    if (close (fdpipe[1]) || close (fdpipe[0])) {
1078 		pexit(prog,1);
1079 	    }
1080 	    /* leave stderr alone */
1081 	    execvp (REVERSE, rargs);
1082 	    pexit(prog,1);
1083 	}
1084 	else {
1085 	    /* parent */
1086 	    /* replace stdin with pipe */
1087 	    if (close(0)) {
1088 		pexit(prog,1);
1089 	    }
1090 
1091 	    if (dup(fdpipe[0]) == -1) {
1092 		pexit(prog,1);
1093 	    }
1094 	    if (close (fdpipe[0]) || close (fdpipe[1])) {
1095 		pexit(prog,1);
1096 	    }
1097 
1098 	    /* leave stdout and stderr alone */
1099 	    execvp(LPR, argstr);
1100 	    pexit(prog,1);
1101 	}
1102     }
1103     else { /* just do lpr */
1104 	/* remove the temporary file after spooling */
1105 	addarg(argstr, "-r", &nargs); /* should we use a symbolic link too? */
1106 	addarg(argstr, TempName, &nargs);
1107 	execvp(LPR, argstr);
1108 	pexit(prog,1);
1109     }
1110 }
1111 
1112 char *
1113 basename(path)
1114 	char	*path;
1115 {
1116 	register char	*cp;
1117 
1118 	for (cp = path; *cp != '\0'; cp++)
1119 		;
1120 	for (--cp; cp > path && *cp != '/'; cp--)
1121 		;
1122 	if (*cp == '/' && *(cp+1) != '\0')
1123 		return (cp + 1);
1124 	else
1125 		return (path);
1126 }
1127