xref: /illumos-gate/usr/src/cmd/vgrind/vfontedpr.c (revision ea4ea50f064c5468142b24627acad09a41f060cb)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <locale.h>
12 #include <euc.h>
13 #include <stdlib.h>
14 
15 #define boolean int
16 #define TRUE 1
17 #define FALSE 0
18 #define NIL 0
19 #define STANDARD 0
20 #define ALTERNATE 1
21 
22 /*
23  * Vfontedpr.
24  *
25  * Dave Presotto 1/12/81 (adapted from an earlier version by Bill Joy)
26  *
27  */
28 
29 #define STRLEN 10		/* length of strings introducing things */
30 #define PNAMELEN 40		/* length of a function/procedure name */
31 #define PSMAX 20		/* size of procedure name stacking */
32 
33 /* regular expression routines */
34 
35 char	*expmatch();		/* match a string to an expression */
36 char	*STRNCMP();		/* a different kind of strncmp */
37 char	*convexp();		/* convert expression to internal form */
38 
39 char	*tgetstr();		/* extract a string-valued capability */
40 boolean	isproc();
41 char	*ctime();
42 char	*strchr();
43 
44 /*
45  *	The state variables
46  */
47 
48 boolean	incomm;			/* in a comment of the primary type */
49 boolean	instr;			/* in a string constant */
50 boolean	inchr;			/* in a string constant */
51 boolean	nokeyw = FALSE;		/* no keywords being flagged */
52 boolean	doindex = FALSE;	/* form an index */
53 boolean twocol = FALSE;		/* in two-column mode */
54 boolean	filter = FALSE;		/* act as a filter (like eqn) */
55 boolean	pass = FALSE;		/* when acting as a filter, pass indicates
56 				 * whether we are currently processing
57 				 * input.
58 				 */
59 boolean	prccont;		/* continue last procedure */
60 int	comtype;		/* type of comment */
61 int	margin;
62 int	psptr;			/* the stack index of the current procedure */
63 char	pstack[PSMAX][PNAMELEN+1];	/* the procedure name stack */
64 int	plstack[PSMAX];		/* the procedure nesting level stack */
65 int	blklevel;		/* current nesting level */
66 int	prclevel;		/* nesting level at which procedure definitions
67 				   may be found, -1 if none currently valid
68 				   (meaningful only if l_prclevel is true) */
69 char	*defsfile = "/usr/lib/vgrindefs";	/* name of language definitions file */
70 char	pname[BUFSIZ+1];
71 
72 /*
73  *	The language specific globals
74  */
75 
76 char	*language = "c";	/* the language indicator */
77 char	*l_keywds[BUFSIZ/2];	/* keyword table address */
78 char	*l_prcbeg;		/* regular expr for procedure begin */
79 char	*l_combeg;		/* string introducing a comment */
80 char	*l_comend;		/* string ending a comment */
81 char	*l_acmbeg;		/* string introducing a comment */
82 char	*l_acmend;		/* string ending a comment */
83 char	*l_blkbeg;		/* string begining of a block */
84 char	*l_blkend;		/* string ending a block */
85 char	*l_strbeg;		/* delimiter for string constant */
86 char	*l_strend;		/* delimiter for string constant */
87 char	*l_chrbeg;		/* delimiter for character constant */
88 char	*l_chrend;		/* delimiter for character constant */
89 char	*l_prcenable;		/* re indicating that procedure definitions
90 				   can be found in the next lexical level --
91 				   kludge for lisp-like languages that use
92 				   something like
93 					   (defun (proc ...)
94 					  	(proc ...)
95 					   )
96 				   to define procedures */
97 char	l_escape;		/* character used to  escape characters */
98 boolean	l_toplex;		/* procedures only defined at top lex level */
99 boolean l_prclevel;		/* procedure definitions valid only within
100 				   the nesting level indicated by the px
101 				   (l_prcenable) capability */
102 
103 /*
104  *  for the benefit of die-hards who aren't convinced that tabs
105  *  occur every eight columns
106  */
107 short tabsize = 8;
108 
109 /*
110  *  global variables also used by expmatch
111  */
112 boolean	_escaped;		/* if last character was an escape */
113 char	*Start;			/* start of the current string */
114 boolean	l_onecase;		/* upper and lower case are equivalent */
115 char	*l_idchars;		/* characters legal in identifiers in addition
116 				   to letters and digits (default "_") */
117 
118 static void putcp(int c);
119 static int width(char *s, char *os);
120 static int tabs(char *s, char *os);
121 static void putKcp(char *start, char *end, boolean force);
122 static void putScp(char *os);
123 static int iskw(char *s);
124 
125 /*
126  * The code below emits troff macros and directives that consume part of the
127  * troff macro and register space.  See tmac.vgrind for an enumeration of
128  * these macros and registers.
129  */
130 
131 int
132 main(int argc, char *argv[])
133 {
134     FILE *in;
135     char *fname;
136     struct stat stbuf;
137     char buf[BUFSIZ];
138     char idbuf[256];	/* enough for all 8 bit chars */
139     char strings[2 * BUFSIZ];
140     char defs[2 * BUFSIZ];
141     int needbp = 0;
142     int i;
143     char *cp;
144 
145 	(void) setlocale(LC_ALL, "");
146 #if !defined(TEXT_DOMAIN)
147 #define	TEXT_DOMAIN	"SYS_TEST"
148 #endif
149 	(void) textdomain(TEXT_DOMAIN);
150 
151     /*
152      * Dump the name by which we were invoked.
153      */
154     argc--, argv++;
155 
156     /*
157      * Process arguments.  For the sake of compatibility with older versions
158      * of the program, the syntax accepted below is very idiosyncratic.  Some
159      * options require space between the option and its argument; others
160      * disallow it.  No options may be bundled together.
161      *
162      * Actually, there is one incompatibility.  Files and options formerly
163      * could be arbitrarily intermixed, but this is no longer allowed.  (This
164      * possiblity was never documented.)
165      */
166     while (argc > 0 && *argv[0] == '-') {
167 	switch (*(cp = argv[0] + 1)) {
168 
169 	case '\0':				/* - */
170 	    /* Take input from stdin. */
171 	    /*
172 	     * This option implies the end of the flag arguments.  Leave the
173 	     * "-" in place for the file processing code to see.
174 	     */
175 	    goto flagsdone;
176 
177 	case '2':				/* -2 */
178 	    /* Enter two column mode. */
179 	    twocol = 1;
180 	    printf("'nr =2 1\n");
181 	    break;
182 
183 	case 'd':				/* -d <defs-file> */
184 	    /* Specify the language description file. */
185 	    defsfile = argv[1];
186 	    argc--, argv++;
187 	    break;
188 
189 	case 'f':				/* -f */
190 	    /* Act as a filter like eqn. */
191 	    filter = 1;
192 	    /*
193 	     * Slide remaining arguments down one position and postpend "-",
194 	     * to force reading from stdin.
195 	     */
196 	    for (i = 0; i < argc - 1; i++)
197 		argv[i] = argv[i + 1];
198 	    argv[argc - 1] = "-";
199 	    continue;
200 
201 	case 'h':				/* -h [header] */
202 	    /* Specify header string. */
203 	    if (argc == 1) {
204 		printf("'ds =H\n");
205 		break;
206 	    }
207 	    printf("'ds =H %s\n", argv[1]);
208 	    argc--, argv++;
209 	    break;
210 
211 	case 'l':				/* -l<language> */
212 	    /* Specify the language. */
213 	    language = cp + 1;
214 	    break;
215 
216 	case 'n':				/* -n */
217 	    /* Indicate no keywords. */
218 	    nokeyw = 1;
219 	    break;
220 
221 	case 's':				/* -s<size> */
222 	    /* Specify the font size. */
223 	    i = 0;
224 	    cp++;
225 	    while (*cp)
226 		i = i * 10 + (*cp++ - '0');
227 	    printf("'nr vP %d\n", i);
228 	    break;
229 
230 	case 't':				/* -t */
231 	    /* Specify a nondefault tab size. */
232 	    tabsize = 4;
233 	    break;
234 
235 	case 'x':				/* -x */
236 	    /* Build an index. */
237 	    doindex = 1;
238 	    /* This option implies "-n" as well; turn it on. */
239 	    argv[0] = "-n";
240 	    continue;
241 	}
242 
243 	/* Advance to next argument. */
244 	argc--, argv++;
245     }
246 
247 flagsdone:
248 
249     /*
250      * Get the language definition from the defs file.
251      */
252     i = tgetent (defs, language, defsfile);
253     if (i == 0) {
254 	fprintf (stderr, gettext("no entry for language %s\n"), language);
255 	exit (0);
256     } else  if (i < 0) {
257 	fprintf (stderr,  gettext("cannot find vgrindefs file %s\n"), defsfile);
258 	exit (0);
259     }
260     cp = strings;
261     if (tgetstr ("kw", &cp) == NIL)
262 	nokeyw = TRUE;
263     else  {
264 	char **cpp;
265 
266 	cpp = l_keywds;
267 	cp = strings;
268 	while (*cp) {
269 	    while (*cp == ' ' || *cp =='\t')
270 		*cp++ = NULL;
271 	    if (*cp)
272 		*cpp++ = cp;
273 	    while (*cp != ' ' && *cp  != '\t' && *cp)
274 		cp++;
275 	}
276 	*cpp = NIL;
277     }
278     cp = buf;
279     l_prcbeg = convexp (tgetstr ("pb", &cp));
280     cp = buf;
281     l_combeg = convexp (tgetstr ("cb", &cp));
282     cp = buf;
283     l_comend = convexp (tgetstr ("ce", &cp));
284     cp = buf;
285     l_acmbeg = convexp (tgetstr ("ab", &cp));
286     cp = buf;
287     l_acmend = convexp (tgetstr ("ae", &cp));
288     cp = buf;
289     l_strbeg = convexp (tgetstr ("sb", &cp));
290     cp = buf;
291     l_strend = convexp (tgetstr ("se", &cp));
292     cp = buf;
293     l_blkbeg = convexp (tgetstr ("bb", &cp));
294     cp = buf;
295     l_blkend = convexp (tgetstr ("be", &cp));
296     cp = buf;
297     l_chrbeg = convexp (tgetstr ("lb", &cp));
298     cp = buf;
299     l_chrend = convexp (tgetstr ("le", &cp));
300     cp = buf;
301     l_prcenable = convexp (tgetstr ("px", &cp));
302     cp = idbuf;
303     l_idchars = tgetstr ("id", &cp);
304     /* Set default, for compatibility with old version */
305     if (l_idchars == NIL)
306 	l_idchars = "_";
307     l_escape = '\\';
308     l_onecase = tgetflag ("oc");
309     l_toplex = tgetflag ("tl");
310     l_prclevel = tgetflag ("pl");
311 
312     /*
313      * Emit a call to the initialization macro.  If not in filter mode, emit a
314      * call to the vS macro, so that tmac.vgrind can uniformly assume that all
315      * program input is bracketed with vS-vE pairs.
316      */
317     printf("'vI\n");
318     if (!filter)
319 	printf("'vS\n");
320 
321     if (doindex) {
322 	/*
323 	 * XXX:	Hard-wired spacing information.  This should probably turn
324 	 *	into the emission of a macro invocation, so that tmac.vgrind
325 	 *	can make up its own mind about what spacing is appropriate.
326 	 */
327 	if (twocol)
328 	    printf("'ta 2.5i 2.75i 4.0iR\n'in .25i\n");
329 	else
330 	    printf("'ta 4i 4.25i 5.5iR\n'in .5i\n");
331     }
332 
333     while (argc > 0) {
334 	if (strcmp(argv[0], "-") == 0) {
335 	    /* Embed an instance of the original stdin. */
336 	    in = fdopen(fileno(stdin), "r");
337 	    fname = "";
338 	} else {
339 	    /* Open the file for input. */
340 	    if ((in = fopen(argv[0], "r")) == NULL) {
341 		perror(argv[0]);
342 		exit(1);
343 	    }
344 	    fname = argv[0];
345 	}
346 	argc--, argv++;
347 
348 	/*
349 	 * Reinitialize for the current file.
350 	 */
351 	incomm = FALSE;
352 	instr = FALSE;
353 	inchr = FALSE;
354 	_escaped = FALSE;
355 	blklevel = 0;
356 	prclevel = -1;
357 	for (psptr=0; psptr<PSMAX; psptr++) {
358 	    pstack[psptr][0] = NULL;
359 	    plstack[psptr] = 0;
360 	}
361 	psptr = -1;
362 	printf("'-F\n");
363 	if (!filter) {
364 	    char *cp;
365 
366 	    printf(".ds =F %s\n", fname);
367 	    if (needbp) {
368 		needbp = 0;
369 		printf(".()\n");
370 		printf(".bp\n");
371 	    }
372 	    fstat(fileno(in), &stbuf);
373 	    cp = ctime(&stbuf.st_mtime);
374 	    cp[16] = '\0';
375 	    cp[24] = '\0';
376 	    printf(".ds =M %s %s\n", cp+4, cp+20);
377 	    printf("'wh 0 vH\n");
378 	    printf("'wh -1i vF\n");
379 	}
380 	if (needbp && filter) {
381 	    needbp = 0;
382 	    printf(".()\n");
383 	    printf(".bp\n");
384 	}
385 
386 	/*
387 	 *	MAIN LOOP!!!
388 	 */
389 	while (fgets(buf, sizeof buf, in) != NULL) {
390 	    if (buf[0] == '\f') {
391 		printf(".bp\n");
392 	    }
393 	    if (buf[0] == '.') {
394 		printf("%s", buf);
395 		if (!strncmp (buf+1, "vS", 2))
396 		    pass = TRUE;
397 		if (!strncmp (buf+1, "vE", 2))
398 		    pass = FALSE;
399 		continue;
400 	    }
401 	    prccont = FALSE;
402 	    if (!filter || pass)
403 		putScp(buf);
404 	    else
405 		printf("%s", buf);
406 	    if (prccont && (psptr >= 0))
407 		printf("'FC %s\n", pstack[psptr]);
408 #ifdef DEBUG
409 	    printf ("com %o str %o chr %o ptr %d\n", incomm, instr, inchr, psptr);
410 #endif
411 	    margin = 0;
412 	}
413 
414 	needbp = 1;
415 	(void) fclose(in);
416     }
417 
418     /* Close off the vS-vE pair. */
419     if (!filter)
420 	printf("'vE\n");
421 
422     return (0);
423 }
424 
425 #define isidchr(c) (isalnum(c) || ((c) != NIL && strchr(l_idchars, (c)) != NIL))
426 
427 static void
428 putScp(char *os)
429 {
430     char *s = os;			/* pointer to unmatched string */
431     char dummy[BUFSIZ];			/* dummy to be used by expmatch */
432     char *comptr;			/* end of a comment delimiter */
433     char *acmptr;			/* end of a comment delimiter */
434     char *strptr;			/* end of a string delimiter */
435     char *chrptr;			/* end of a character const delimiter */
436     char *blksptr;			/* end of a lexical block start */
437     char *blkeptr;			/* end of a lexical block end */
438 
439     Start = os;			/* remember the start for expmatch */
440     _escaped = FALSE;
441     if (nokeyw || incomm || instr)
442 	goto skip;
443     if (isproc(s)) {
444 	printf("'FN %s\n", pname);
445 	if (psptr < PSMAX-1) {
446 	    ++psptr;
447 	    strncpy (pstack[psptr], pname, PNAMELEN);
448 	    pstack[psptr][PNAMELEN] = NULL;
449 	    plstack[psptr] = blklevel;
450 	}
451     }
452     /*
453      * if l_prclevel is set, check to see whether this lexical level
454      * is one immediately below which procedure definitions are allowed.
455      */
456     if (l_prclevel && !incomm && !instr && !inchr) {
457 	if (expmatch (s, l_prcenable, dummy) != NIL)
458 	    prclevel = blklevel + 1;
459     }
460 skip:
461     do {
462 	/* check for string, comment, blockstart, etc */
463 	if (!incomm && !instr && !inchr) {
464 
465 	    blkeptr = expmatch (s, l_blkend, dummy);
466 	    blksptr = expmatch (s, l_blkbeg, dummy);
467 	    comptr = expmatch (s, l_combeg, dummy);
468 	    acmptr = expmatch (s, l_acmbeg, dummy);
469 	    strptr = expmatch (s, l_strbeg, dummy);
470 	    chrptr = expmatch (s, l_chrbeg, dummy);
471 
472 	    /* start of a comment? */
473 	    if (comptr != NIL)
474 		if ((comptr < strptr || strptr == NIL)
475 		  && (comptr < acmptr || acmptr == NIL)
476 		  && (comptr < chrptr || chrptr == NIL)
477 		  && (comptr < blksptr || blksptr == NIL)
478 		  && (comptr < blkeptr || blkeptr == NIL)) {
479 		    putKcp (s, comptr-1, FALSE);
480 		    s = comptr;
481 		    incomm = TRUE;
482 		    comtype = STANDARD;
483 		    if (s != os)
484 			printf ("\\c");
485 		    printf ("\\c\n'+C\n");
486 		    continue;
487 		}
488 
489 	    /* start of a comment? */
490 	    if (acmptr != NIL)
491 		if ((acmptr < strptr || strptr == NIL)
492 		  && (acmptr < chrptr || chrptr == NIL)
493 		  && (acmptr < blksptr || blksptr == NIL)
494 		  && (acmptr < blkeptr || blkeptr == NIL)) {
495 		    putKcp (s, acmptr-1, FALSE);
496 		    s = acmptr;
497 		    incomm = TRUE;
498 		    comtype = ALTERNATE;
499 		    if (s != os)
500 			printf ("\\c");
501 		    printf ("\\c\n'+C\n");
502 		    continue;
503 		}
504 
505 	    /* start of a string? */
506 	    if (strptr != NIL)
507 		if ((strptr < chrptr || chrptr == NIL)
508 		  && (strptr < blksptr || blksptr == NIL)
509 		  && (strptr < blkeptr || blkeptr == NIL)) {
510 		    putKcp (s, strptr-1, FALSE);
511 		    s = strptr;
512 		    instr = TRUE;
513 		    continue;
514 		}
515 
516 	    /* start of a character string? */
517 	    if (chrptr != NIL)
518 		if ((chrptr < blksptr || blksptr == NIL)
519 		  && (chrptr < blkeptr || blkeptr == NIL)) {
520 		    putKcp (s, chrptr-1, FALSE);
521 		    s = chrptr;
522 		    inchr = TRUE;
523 		    continue;
524 		}
525 
526 	    /* end of a lexical block */
527 	    if (blkeptr != NIL) {
528 		if (blkeptr < blksptr || blksptr == NIL) {
529 		    /* reset prclevel if necessary */
530 		    if (l_prclevel && prclevel == blklevel)
531 			prclevel = -1;
532 		    putKcp (s, blkeptr - 1, FALSE);
533 		    s = blkeptr;
534 		    blklevel--;
535 		    if (psptr >= 0 && plstack[psptr] >= blklevel) {
536 
537 			/* end of current procedure */
538 			if (s != os)
539 			    printf ("\\c");
540 			printf ("\\c\n'-F\n");
541 			blklevel = plstack[psptr];
542 
543 			/* see if we should print the last proc name */
544 			if (--psptr >= 0)
545 			    prccont = TRUE;
546 			else
547 			    psptr = -1;
548 		    }
549 		    continue;
550 		}
551 	    }
552 
553 	    /* start of a lexical block */
554 	    if (blksptr != NIL) {
555 		putKcp (s, blksptr - 1, FALSE);
556 		s = blksptr;
557 		blklevel++;
558 		continue;
559 	    }
560 
561 	/* check for end of comment */
562 	} else if (incomm) {
563 	    comptr = expmatch (s, l_comend, dummy);
564 	    acmptr = expmatch (s, l_acmend, dummy);
565 	    if (((comtype == STANDARD) && (comptr != NIL)) ||
566 	        ((comtype == ALTERNATE) && (acmptr != NIL))) {
567 		if (comtype == STANDARD) {
568 		    putKcp (s, comptr-1, TRUE);
569 		    s = comptr;
570 		} else {
571 		    putKcp (s, acmptr-1, TRUE);
572 		    s = acmptr;
573 		}
574 		incomm = FALSE;
575 		printf("\\c\n'-C\n");
576 		continue;
577 	    } else {
578 		putKcp (s, s + strlen(s) -1, TRUE);
579 		s = s + strlen(s);
580 		continue;
581 	    }
582 
583 	/* check for end of string */
584 	} else if (instr) {
585 	    if ((strptr = expmatch (s, l_strend, dummy)) != NIL) {
586 		putKcp (s, strptr-1, TRUE);
587 		s = strptr;
588 		instr = FALSE;
589 		continue;
590 	    } else {
591 		putKcp (s, s+strlen(s)-1, TRUE);
592 		s = s + strlen(s);
593 		continue;
594 	    }
595 
596 	/* check for end of character string */
597 	} else if (inchr) {
598 	    if ((chrptr = expmatch (s, l_chrend, dummy)) != NIL) {
599 		putKcp (s, chrptr-1, TRUE);
600 		s = chrptr;
601 		inchr = FALSE;
602 		continue;
603 	    } else {
604 		putKcp (s, s+strlen(s)-1, TRUE);
605 		s = s + strlen(s);
606 		continue;
607 	    }
608 	}
609 
610 	/* print out the line */
611 	putKcp (s, s + strlen(s) -1, FALSE);
612 	s = s + strlen(s);
613     } while (*s);
614 }
615 
616 static void
617 putKcp(char *start, char *end, boolean force)
618 	/* start - start of string to write */
619 	/* end - end of string to write */
620 	/* force - true if we should force nokeyw */
621 {
622     int i;
623     int xfld = 0;
624 
625     while (start <= end) {
626 	if (doindex) {
627 	    if (*start == ' ' || *start == '\t') {
628 		if (xfld == 0)
629 		    printf("");
630 		printf("\t");
631 		xfld = 1;
632 		while (*start == ' ' || *start == '\t')
633 		    start++;
634 		continue;
635 	    }
636 	}
637 
638 	/* take care of nice tab stops */
639 	if (*start == '\t') {
640 	    while (*start == '\t')
641 		start++;
642 	    i = tabs(Start, start) - margin / tabsize;
643 	    printf ("\\h'|%dn'",
644 		    i * (tabsize == 4 ? 5 : 10) + 1 - margin % tabsize);
645 	    continue;
646 	}
647 
648 	if (!nokeyw && !force)
649 	    if (  (*start == '#'   ||  isidchr(*start))
650 	       && (start == Start || !isidchr(start[-1]))
651 	       ) {
652 		i = iskw(start);
653 		if (i > 0) {
654 		    printf("\\*(+K");
655 		    do
656 			putcp(*start++);
657 		    while (--i > 0);
658 		    printf("\\*(-K");
659 		    continue;
660 		}
661 	    }
662 
663 	putcp (*start++);
664     }
665 }
666 
667 
668 static int
669 tabs(char *s, char *os)
670 {
671 
672     return (width(s, os) / tabsize);
673 }
674 
675 static int
676 width(char *s, char *os)
677 {
678 	int i = 0;
679 	unsigned char c;
680 	int n;
681 
682 	while (s < os) {
683 		if (*s == '\t') {
684 			i = (i + tabsize) &~ (tabsize-1);
685 			s++;
686 			continue;
687 		}
688 		c = *(unsigned char *)s;
689 		if (c < ' ')
690 			i += 2, s++;
691 		else if (c >= 0200) {
692 			if ((n = mblen(s, MB_CUR_MAX)) > 0) {
693 				i += csetcol(csetno(c));
694 				s += n;
695 			} else
696 				s++;
697 		} else
698 			i++, s++;
699 	}
700 	return (i);
701 }
702 
703 static void
704 putcp(int c)
705 {
706 
707 	switch(c) {
708 
709 	case 0:
710 		break;
711 
712 	case '\f':
713 		break;
714 
715 	case '{':
716 		printf("\\*(+K{\\*(-K");
717 		break;
718 
719 	case '}':
720 		printf("\\*(+K}\\*(-K");
721 		break;
722 
723 	case '\\':
724 		printf("\\e");
725 		break;
726 
727 	case '_':
728 		printf("\\*_");
729 		break;
730 
731 	case '-':
732 		printf("\\*-");
733 		break;
734 
735 		/*
736 		 * The following two cases deal with the accent characters.
737 		 * If they're part of a comment, we assume that they're part
738 		 * of running text and hand them to troff as regular quote
739 		 * characters.  Otherwise, we assume they're being used as
740 		 * special characters (e.g., string delimiters) and arrange
741 		 * for troff to render them as accents.  This is an imperfect
742 		 * heuristic that produces slightly better appearance than the
743 		 * former behavior of unconditionally rendering the characters
744 		 * as accents.  (See bug 1040343.)
745 		 */
746 
747 	case '`':
748 		if (incomm)
749 			printf("`");
750 		else
751 			printf("\\`");
752 		break;
753 
754 	case '\'':
755 		if (incomm)
756 			printf("'");
757 		else
758 			printf("\\'");
759 		break;
760 
761 	case '.':
762 		printf("\\&.");
763 		break;
764 
765 		/*
766 		 * The following two cases contain special hacking
767 		 * to make C-style comments line up.  The tests aren't
768 		 * really adequate; they lead to grotesqueries such
769 		 * as italicized multiplication and division operators.
770 		 * However, the obvious test (!incomm) doesn't work,
771 		 * because incomm isn't set until after we've put out
772 		 * the comment-begin characters.  The real problem is
773 		 * that expmatch() doesn't give us enough information.
774 		 */
775 
776 	case '*':
777 		if (instr || inchr)
778 			printf("*");
779 		else
780 			printf("\\f2*\\fP");
781 		break;
782 
783 	case '/':
784 		if (instr || inchr)
785 			printf("/");
786 		else
787 			printf("\\f2\\h'\\w' 'u-\\w'/'u'/\\fP");
788 		break;
789 
790 	default:
791 		if (c < 040)
792 			putchar('^'), c |= '@';
793 		/* FALLTHROUGH */
794 	case '\t':
795 	case '\n':
796 		putchar(c);
797 	}
798 }
799 
800 /*
801  *	look for a process beginning on this line
802  */
803 boolean
804 isproc(s)
805     char *s;
806 {
807     pname[0] = NULL;
808     if (l_prclevel ? (prclevel == blklevel) : (!l_toplex || blklevel == 0))
809 	if (expmatch (s, l_prcbeg, pname) != NIL) {
810 	    return (TRUE);
811 	}
812     return (FALSE);
813 }
814 
815 
816 /*
817  * iskw - check to see if the next word is a keyword
818  *	Return its length if it is or 0 if it isn't.
819  */
820 
821 static int
822 iskw(char *s)
823 {
824 	char **ss = l_keywds;
825 	int i = 1;
826 	char *cp = s;
827 
828 	/* Get token length. */
829 	while (++cp, isidchr(*cp))
830 		i++;
831 
832 	while (cp = *ss++) {
833 		if (!STRNCMP(s,cp,i) && !isidchr(cp[i]))
834 			return (i);
835 	}
836 	return (0);
837 }
838