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.
7 #pragma ident "%Z%%M% %I% %E% SMI"
11 #include <sys/types.h>
27 * Dave Presotto 1/12/81 (adapted from an earlier version by Bill Joy)
31 #define STRLEN 10 /* length of strings introducing things */
32 #define PNAMELEN 40 /* length of a function/procedure name */
33 #define PSMAX 20 /* size of procedure name stacking */
35 /* regular expression routines */
37 char *expmatch(); /* match a string to an expression */
38 char *STRNCMP(); /* a different kind of strncmp */
39 char *convexp(); /* convert expression to internal form */
41 char *tgetstr(); /* extract a string-valued capability */
50 boolean incomm
; /* in a comment of the primary type */
51 boolean instr
; /* in a string constant */
52 boolean inchr
; /* in a string constant */
53 boolean nokeyw
= FALSE
; /* no keywords being flagged */
54 boolean doindex
= FALSE
; /* form an index */
55 boolean twocol
= FALSE
; /* in two-column mode */
56 boolean filter
= FALSE
; /* act as a filter (like eqn) */
57 boolean pass
= FALSE
; /* when acting as a filter, pass indicates
58 * whether we are currently processing
61 boolean prccont
; /* continue last procedure */
62 int comtype
; /* type of comment */
64 int psptr
; /* the stack index of the current procedure */
65 char pstack
[PSMAX
][PNAMELEN
+1]; /* the procedure name stack */
66 int plstack
[PSMAX
]; /* the procedure nesting level stack */
67 int blklevel
; /* current nesting level */
68 int prclevel
; /* nesting level at which procedure definitions
69 may be found, -1 if none currently valid
70 (meaningful only if l_prclevel is true) */
71 char *defsfile
= "/usr/lib/vgrindefs"; /* name of language definitions file */
75 * The language specific globals
78 char *language
= "c"; /* the language indicator */
79 char *l_keywds
[BUFSIZ
/2]; /* keyword table address */
80 char *l_prcbeg
; /* regular expr for procedure begin */
81 char *l_combeg
; /* string introducing a comment */
82 char *l_comend
; /* string ending a comment */
83 char *l_acmbeg
; /* string introducing a comment */
84 char *l_acmend
; /* string ending a comment */
85 char *l_blkbeg
; /* string begining of a block */
86 char *l_blkend
; /* string ending a block */
87 char *l_strbeg
; /* delimiter for string constant */
88 char *l_strend
; /* delimiter for string constant */
89 char *l_chrbeg
; /* delimiter for character constant */
90 char *l_chrend
; /* delimiter for character constant */
91 char *l_prcenable
; /* re indicating that procedure definitions
92 can be found in the next lexical level --
93 kludge for lisp-like languages that use
98 to define procedures */
99 char l_escape
; /* character used to escape characters */
100 boolean l_toplex
; /* procedures only defined at top lex level */
101 boolean l_prclevel
; /* procedure definitions valid only within
102 the nesting level indicated by the px
103 (l_prcenable) capability */
106 * for the benefit of die-hards who aren't convinced that tabs
107 * occur every eight columns
112 * global variables also used by expmatch
114 boolean _escaped
; /* if last character was an escape */
115 char *Start
; /* start of the current string */
116 boolean l_onecase
; /* upper and lower case are equivalent */
117 char *l_idchars
; /* characters legal in identifiers in addition
118 to letters and digits (default "_") */
120 static void putcp(int c
);
121 static int width(char *s
, char *os
);
122 static int tabs(char *s
, char *os
);
123 static void putKcp(char *start
, char *end
, boolean force
);
124 static void putScp(char *os
);
125 static int iskw(char *s
);
128 * The code below emits troff macros and directives that consume part of the
129 * troff macro and register space. See tmac.vgrind for an enumeration of
130 * these macros and registers.
134 main(int argc
, char *argv
[])
140 char idbuf
[256]; /* enough for all 8 bit chars */
141 char strings
[2 * BUFSIZ
];
142 char defs
[2 * BUFSIZ
];
147 (void) setlocale(LC_ALL
, "");
148 #if !defined(TEXT_DOMAIN)
149 #define TEXT_DOMAIN "SYS_TEST"
151 (void) textdomain(TEXT_DOMAIN
);
154 * Dump the name by which we were invoked.
159 * Process arguments. For the sake of compatibility with older versions
160 * of the program, the syntax accepted below is very idiosyncratic. Some
161 * options require space between the option and its argument; others
162 * disallow it. No options may be bundled together.
164 * Actually, there is one incompatibility. Files and options formerly
165 * could be arbitrarily intermixed, but this is no longer allowed. (This
166 * possiblity was never documented.)
168 while (argc
> 0 && *argv
[0] == '-') {
169 switch (*(cp
= argv
[0] + 1)) {
172 /* Take input from stdin. */
174 * This option implies the end of the flag arguments. Leave the
175 * "-" in place for the file processing code to see.
180 /* Enter two column mode. */
182 printf("'nr =2 1\n");
185 case 'd': /* -d <defs-file> */
186 /* Specify the language description file. */
192 /* Act as a filter like eqn. */
195 * Slide remaining arguments down one position and postpend "-",
196 * to force reading from stdin.
198 for (i
= 0; i
< argc
- 1; i
++)
199 argv
[i
] = argv
[i
+ 1];
200 argv
[argc
- 1] = "-";
203 case 'h': /* -h [header] */
204 /* Specify header string. */
209 printf("'ds =H %s\n", argv
[1]);
213 case 'l': /* -l<language> */
214 /* Specify the language. */
219 /* Indicate no keywords. */
223 case 's': /* -s<size> */
224 /* Specify the font size. */
228 i
= i
* 10 + (*cp
++ - '0');
229 printf("'nr vP %d\n", i
);
233 /* Specify a nondefault tab size. */
238 /* Build an index. */
240 /* This option implies "-n" as well; turn it on. */
245 /* Advance to next argument. */
252 * Get the language definition from the defs file.
254 i
= tgetent (defs
, language
, defsfile
);
256 fprintf (stderr
, gettext("no entry for language %s\n"), language
);
259 fprintf (stderr
, gettext("cannot find vgrindefs file %s\n"), defsfile
);
263 if (tgetstr ("kw", &cp
) == NIL
)
271 while (*cp
== ' ' || *cp
=='\t')
275 while (*cp
!= ' ' && *cp
!= '\t' && *cp
)
281 l_prcbeg
= convexp (tgetstr ("pb", &cp
));
283 l_combeg
= convexp (tgetstr ("cb", &cp
));
285 l_comend
= convexp (tgetstr ("ce", &cp
));
287 l_acmbeg
= convexp (tgetstr ("ab", &cp
));
289 l_acmend
= convexp (tgetstr ("ae", &cp
));
291 l_strbeg
= convexp (tgetstr ("sb", &cp
));
293 l_strend
= convexp (tgetstr ("se", &cp
));
295 l_blkbeg
= convexp (tgetstr ("bb", &cp
));
297 l_blkend
= convexp (tgetstr ("be", &cp
));
299 l_chrbeg
= convexp (tgetstr ("lb", &cp
));
301 l_chrend
= convexp (tgetstr ("le", &cp
));
303 l_prcenable
= convexp (tgetstr ("px", &cp
));
305 l_idchars
= tgetstr ("id", &cp
);
306 /* Set default, for compatibility with old version */
307 if (l_idchars
== NIL
)
310 l_onecase
= tgetflag ("oc");
311 l_toplex
= tgetflag ("tl");
312 l_prclevel
= tgetflag ("pl");
315 * Emit a call to the initialization macro. If not in filter mode, emit a
316 * call to the vS macro, so that tmac.vgrind can uniformly assume that all
317 * program input is bracketed with vS-vE pairs.
325 * XXX: Hard-wired spacing information. This should probably turn
326 * into the emission of a macro invocation, so that tmac.vgrind
327 * can make up its own mind about what spacing is appropriate.
330 printf("'ta 2.5i 2.75i 4.0iR\n'in .25i\n");
332 printf("'ta 4i 4.25i 5.5iR\n'in .5i\n");
336 if (strcmp(argv
[0], "-") == 0) {
337 /* Embed an instance of the original stdin. */
338 in
= fdopen(fileno(stdin
), "r");
341 /* Open the file for input. */
342 if ((in
= fopen(argv
[0], "r")) == NULL
) {
351 * Reinitialize for the current file.
359 for (psptr
=0; psptr
<PSMAX
; psptr
++) {
360 pstack
[psptr
][0] = NULL
;
368 printf(".ds =F %s\n", fname
);
374 fstat(fileno(in
), &stbuf
);
375 cp
= ctime(&stbuf
.st_mtime
);
378 printf(".ds =M %s %s\n", cp
+4, cp
+20);
379 printf("'wh 0 vH\n");
380 printf("'wh -1i vF\n");
382 if (needbp
&& filter
) {
391 while (fgets(buf
, sizeof buf
, in
) != NULL
) {
392 if (buf
[0] == '\f') {
397 if (!strncmp (buf
+1, "vS", 2))
399 if (!strncmp (buf
+1, "vE", 2))
408 if (prccont
&& (psptr
>= 0))
409 printf("'FC %s\n", pstack
[psptr
]);
411 printf ("com %o str %o chr %o ptr %d\n", incomm
, instr
, inchr
, psptr
);
420 /* Close off the vS-vE pair. */
427 #define isidchr(c) (isalnum(c) || ((c) != NIL && strchr(l_idchars, (c)) != NIL))
432 char *s
= os
; /* pointer to unmatched string */
433 char dummy
[BUFSIZ
]; /* dummy to be used by expmatch */
434 char *comptr
; /* end of a comment delimiter */
435 char *acmptr
; /* end of a comment delimiter */
436 char *strptr
; /* end of a string delimiter */
437 char *chrptr
; /* end of a character const delimiter */
438 char *blksptr
; /* end of a lexical block start */
439 char *blkeptr
; /* end of a lexical block end */
441 Start
= os
; /* remember the start for expmatch */
443 if (nokeyw
|| incomm
|| instr
)
446 printf("'FN %s\n", pname
);
447 if (psptr
< PSMAX
-1) {
449 strncpy (pstack
[psptr
], pname
, PNAMELEN
);
450 pstack
[psptr
][PNAMELEN
] = NULL
;
451 plstack
[psptr
] = blklevel
;
455 * if l_prclevel is set, check to see whether this lexical level
456 * is one immediately below which procedure definitions are allowed.
458 if (l_prclevel
&& !incomm
&& !instr
&& !inchr
) {
459 if (expmatch (s
, l_prcenable
, dummy
) != NIL
)
460 prclevel
= blklevel
+ 1;
464 /* check for string, comment, blockstart, etc */
465 if (!incomm
&& !instr
&& !inchr
) {
467 blkeptr
= expmatch (s
, l_blkend
, dummy
);
468 blksptr
= expmatch (s
, l_blkbeg
, dummy
);
469 comptr
= expmatch (s
, l_combeg
, dummy
);
470 acmptr
= expmatch (s
, l_acmbeg
, dummy
);
471 strptr
= expmatch (s
, l_strbeg
, dummy
);
472 chrptr
= expmatch (s
, l_chrbeg
, dummy
);
474 /* start of a comment? */
476 if ((comptr
< strptr
|| strptr
== NIL
)
477 && (comptr
< acmptr
|| acmptr
== NIL
)
478 && (comptr
< chrptr
|| chrptr
== NIL
)
479 && (comptr
< blksptr
|| blksptr
== NIL
)
480 && (comptr
< blkeptr
|| blkeptr
== NIL
)) {
481 putKcp (s
, comptr
-1, FALSE
);
487 printf ("\\c\n'+C\n");
491 /* start of a comment? */
493 if ((acmptr
< strptr
|| strptr
== NIL
)
494 && (acmptr
< chrptr
|| chrptr
== NIL
)
495 && (acmptr
< blksptr
|| blksptr
== NIL
)
496 && (acmptr
< blkeptr
|| blkeptr
== NIL
)) {
497 putKcp (s
, acmptr
-1, FALSE
);
503 printf ("\\c\n'+C\n");
507 /* start of a string? */
509 if ((strptr
< chrptr
|| chrptr
== NIL
)
510 && (strptr
< blksptr
|| blksptr
== NIL
)
511 && (strptr
< blkeptr
|| blkeptr
== NIL
)) {
512 putKcp (s
, strptr
-1, FALSE
);
518 /* start of a character string? */
520 if ((chrptr
< blksptr
|| blksptr
== NIL
)
521 && (chrptr
< blkeptr
|| blkeptr
== NIL
)) {
522 putKcp (s
, chrptr
-1, FALSE
);
528 /* end of a lexical block */
529 if (blkeptr
!= NIL
) {
530 if (blkeptr
< blksptr
|| blksptr
== NIL
) {
531 /* reset prclevel if necessary */
532 if (l_prclevel
&& prclevel
== blklevel
)
534 putKcp (s
, blkeptr
- 1, FALSE
);
537 if (psptr
>= 0 && plstack
[psptr
] >= blklevel
) {
539 /* end of current procedure */
542 printf ("\\c\n'-F\n");
543 blklevel
= plstack
[psptr
];
545 /* see if we should print the last proc name */
555 /* start of a lexical block */
556 if (blksptr
!= NIL
) {
557 putKcp (s
, blksptr
- 1, FALSE
);
563 /* check for end of comment */
565 comptr
= expmatch (s
, l_comend
, dummy
);
566 acmptr
= expmatch (s
, l_acmend
, dummy
);
567 if (((comtype
== STANDARD
) && (comptr
!= NIL
)) ||
568 ((comtype
== ALTERNATE
) && (acmptr
!= NIL
))) {
569 if (comtype
== STANDARD
) {
570 putKcp (s
, comptr
-1, TRUE
);
573 putKcp (s
, acmptr
-1, TRUE
);
577 printf("\\c\n'-C\n");
580 putKcp (s
, s
+ strlen(s
) -1, TRUE
);
585 /* check for end of string */
587 if ((strptr
= expmatch (s
, l_strend
, dummy
)) != NIL
) {
588 putKcp (s
, strptr
-1, TRUE
);
593 putKcp (s
, s
+strlen(s
)-1, TRUE
);
598 /* check for end of character string */
600 if ((chrptr
= expmatch (s
, l_chrend
, dummy
)) != NIL
) {
601 putKcp (s
, chrptr
-1, TRUE
);
606 putKcp (s
, s
+strlen(s
)-1, TRUE
);
612 /* print out the line */
613 putKcp (s
, s
+ strlen(s
) -1, FALSE
);
619 putKcp(char *start
, char *end
, boolean force
)
620 /* start - start of string to write */
621 /* end - end of string to write */
622 /* force - true if we should force nokeyw */
627 while (start
<= end
) {
629 if (*start
== ' ' || *start
== '\t') {
634 while (*start
== ' ' || *start
== '\t')
640 /* take care of nice tab stops */
641 if (*start
== '\t') {
642 while (*start
== '\t')
644 i
= tabs(Start
, start
) - margin
/ tabsize
;
646 i
* (tabsize
== 4 ? 5 : 10) + 1 - margin
% tabsize
);
650 if (!nokeyw
&& !force
)
651 if ( (*start
== '#' || isidchr(*start
))
652 && (start
== Start
|| !isidchr(start
[-1]))
671 tabs(char *s
, char *os
)
674 return (width(s
, os
) / tabsize
);
678 width(char *s
, char *os
)
686 i
= (i
+ tabsize
) &~ (tabsize
-1);
690 c
= *(unsigned char *)s
;
693 else if (c
>= 0200) {
694 if ((n
= mblen(s
, MB_CUR_MAX
)) > 0) {
695 i
+= csetcol(csetno(c
));
718 printf("\\*(+K{\\*(-K");
722 printf("\\*(+K}\\*(-K");
738 * The following two cases deal with the accent characters.
739 * If they're part of a comment, we assume that they're part
740 * of running text and hand them to troff as regular quote
741 * characters. Otherwise, we assume they're being used as
742 * special characters (e.g., string delimiters) and arrange
743 * for troff to render them as accents. This is an imperfect
744 * heuristic that produces slightly better appearance than the
745 * former behavior of unconditionally rendering the characters
746 * as accents. (See bug 1040343.)
768 * The following two cases contain special hacking
769 * to make C-style comments line up. The tests aren't
770 * really adequate; they lead to grotesqueries such
771 * as italicized multiplication and division operators.
772 * However, the obvious test (!incomm) doesn't work,
773 * because incomm isn't set until after we've put out
774 * the comment-begin characters. The real problem is
775 * that expmatch() doesn't give us enough information.
789 printf("\\f2\\h'\\w' 'u-\\w'/'u'/\\fP");
794 putchar('^'), c
|= '@';
802 * look for a process beginning on this line
809 if (l_prclevel
? (prclevel
== blklevel
) : (!l_toplex
|| blklevel
== 0))
810 if (expmatch (s
, l_prcbeg
, pname
) != NIL
) {
818 * iskw - check to see if the next word is a keyword
819 * Return its length if it is or 0 if it isn't.
825 char **ss
= l_keywds
;
829 /* Get token length. */
830 while (++cp
, isidchr(*cp
))
834 if (!STRNCMP(s
,cp
,i
) && !isidchr(cp
[i
]))