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
main(int argc,char * argv[])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++ = '\0';
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] = '\0';
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
putScp(char * os)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] = '\0';
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
putKcp(char * start,char * end,boolean force)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
tabs(char * s,char * os)669 tabs(char *s, char *os)
670 {
671
672 return (width(s, os) / tabsize);
673 }
674
675 static int
width(char * s,char * os)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
putcp(int c)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
isproc(char * s)804 isproc(char *s)
805 {
806 pname[0] = '\0';
807 if (l_prclevel ? (prclevel == blklevel) : (!l_toplex || blklevel == 0))
808 if (expmatch (s, l_prcbeg, pname) != NIL) {
809 return (TRUE);
810 }
811 return (FALSE);
812 }
813
814
815 /*
816 * iskw - check to see if the next word is a keyword
817 * Return its length if it is or 0 if it isn't.
818 */
819
820 static int
iskw(char * s)821 iskw(char *s)
822 {
823 char **ss = l_keywds;
824 int i = 1;
825 char *cp = s;
826
827 /* Get token length. */
828 while (++cp, isidchr(*cp))
829 i++;
830
831 while (cp = *ss++) {
832 if (!STRNCMP(s,cp,i) && !isidchr(cp[i]))
833 return (i);
834 }
835 return (0);
836 }
837