1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <ctype.h> 35 #include <err.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <stdbool.h> 39 #include <string.h> 40 #include <time.h> 41 #include "pathnames.h" 42 #include "extern.h" 43 44 #define STANDARD 0 45 #define ALTERNATE 1 46 47 /* 48 * Vfontedpr. 49 * 50 * Dave Presotto 1/12/81 (adapted from an earlier version by Bill Joy) 51 * 52 */ 53 54 #define STRLEN 10 /* length of strings introducing things */ 55 #define PNAMELEN 40 /* length of a function/procedure name */ 56 #define PSMAX 20 /* size of procedure name stacking */ 57 58 static int iskw(char *); 59 static bool isproc(char *); 60 static void putKcp(char *, char *, bool); 61 static void putScp(char *); 62 static void putcp(int); 63 static int tabs(char *, char *); 64 static int width(char *, char *); 65 66 /* 67 * The state variables 68 */ 69 70 static bool filter = false; /* act as a filter (like eqn) */ 71 static bool inchr; /* in a string constant */ 72 static bool incomm; /* in a comment of the primary type */ 73 static bool idx = false; /* form an index */ 74 static bool instr; /* in a string constant */ 75 static bool nokeyw = false; /* no keywords being flagged */ 76 static bool pass = false; /* 77 * when acting as a filter, pass indicates 78 * whether we are currently processing 79 * input. 80 */ 81 82 static int blklevel; /* current nesting level */ 83 static int comtype; /* type of comment */ 84 static char * defsfile[2] = { _PATH_VGRINDEFS, 0 }; 85 /* name of language definitions file */ 86 static int margin; 87 static int plstack[PSMAX]; /* the procedure nesting level stack */ 88 static char pname[BUFSIZ+1]; 89 static bool prccont; /* continue last procedure */ 90 static int psptr; /* the stack index of the current procedure */ 91 static char pstack[PSMAX][PNAMELEN+1]; /* the procedure name stack */ 92 93 /* 94 * The language specific globals 95 */ 96 97 char *l_acmbeg; /* string introducing a comment */ 98 char *l_acmend; /* string ending a comment */ 99 char *l_blkbeg; /* string beginning of a block */ 100 char *l_blkend; /* string ending a block */ 101 char *l_chrbeg; /* delimiter for character constant */ 102 char *l_chrend; /* delimiter for character constant */ 103 char *l_combeg; /* string introducing a comment */ 104 char *l_comend; /* string ending a comment */ 105 char l_escape; /* character used to escape characters */ 106 char *l_keywds[BUFSIZ/2]; /* keyword table address */ 107 char *l_nocom; /* regexp for non-comments */ 108 char *l_prcbeg; /* regular expr for procedure begin */ 109 char *l_strbeg; /* delimiter for string constant */ 110 char *l_strend; /* delimiter for string constant */ 111 bool l_toplex; /* procedures only defined at top lex level */ 112 const char *language = "c"; /* the language indicator */ 113 114 #define ps(x) printf("%s", x) 115 static char minus[] = "-"; 116 static char minusn[] = "-n"; 117 118 int 119 main(int argc, char **argv) 120 { 121 const char *fname = ""; 122 struct stat stbuf; 123 char buf[BUFSIZ]; 124 char *defs; 125 int needbp = 0; 126 127 argc--, argv++; 128 do { 129 char *cp; 130 int i; 131 132 if (argc > 0) { 133 if (!strcmp(argv[0], "-h")) { 134 if (argc == 1) { 135 printf("'ds =H\n"); 136 argc = 0; 137 goto rest; 138 } 139 printf("'ds =H %s\n", argv[1]); 140 argc--, argv++; 141 argc--, argv++; 142 if (argc > 0) 143 continue; 144 goto rest; 145 } 146 147 /* act as a filter like eqn */ 148 if (!strcmp(argv[0], "-f")) { 149 filter = true; 150 argv[0] = argv[argc-1]; 151 argv[argc-1] = minus; 152 continue; 153 } 154 155 /* take input from the standard place */ 156 if (!strcmp(argv[0], "-")) { 157 argc = 0; 158 goto rest; 159 } 160 161 /* build an index */ 162 if (!strcmp(argv[0], "-x")) { 163 idx = true; 164 argv[0] = minusn; 165 } 166 167 /* indicate no keywords */ 168 if (!strcmp(argv[0], "-n")) { 169 nokeyw = true; 170 argc--, argv++; 171 continue; 172 } 173 174 /* specify the font size */ 175 if (!strncmp(argv[0], "-s", 2)) { 176 i = 0; 177 cp = argv[0] + 2; 178 while (*cp) 179 i = i * 10 + (*cp++ - '0'); 180 printf("'ps %d\n'vs %d\n", i, i+1); 181 argc--, argv++; 182 continue; 183 } 184 185 /* specify the language */ 186 if (!strncmp(argv[0], "-l", 2)) { 187 language = argv[0]+2; 188 argc--, argv++; 189 continue; 190 } 191 192 /* specify the language description file */ 193 if (!strncmp(argv[0], "-d", 2)) { 194 defsfile[0] = argv[1]; 195 argc--, argv++; 196 argc--, argv++; 197 continue; 198 } 199 200 /* open the file for input */ 201 if (freopen(argv[0], "r", stdin) == NULL) 202 err(1, "%s", argv[0]); 203 if (idx) 204 printf("'ta 4i 4.25i 5.5iR\n'in .5i\n"); 205 fname = argv[0]; 206 argc--, argv++; 207 } 208 rest: 209 210 /* 211 * get the language definition from the defs file 212 */ 213 i = cgetent(&defs, defsfile, language); 214 if (i == -1) { 215 fprintf (stderr, "no entry for language %s\n", language); 216 exit(0); 217 } else if (i == -2) { fprintf(stderr, 218 "cannot find vgrindefs file %s\n", defsfile[0]); 219 exit(0); 220 } else if (i == -3) { fprintf(stderr, 221 "potential reference loop detected in vgrindefs file %s\n", 222 defsfile[0]); 223 exit(0); 224 } 225 if (cgetustr(defs, "kw", &cp) == -1) 226 nokeyw = true; 227 else { 228 char **cpp; 229 230 cpp = l_keywds; 231 while (*cp) { 232 while (*cp == ' ' || *cp =='\t') 233 *cp++ = '\0'; 234 if (*cp) 235 *cpp++ = cp; 236 while (*cp != ' ' && *cp != '\t' && *cp) 237 cp++; 238 } 239 *cpp = NULL; 240 } 241 cgetustr(defs, "pb", &cp); 242 l_prcbeg = convexp(cp); 243 cgetustr(defs, "cb", &cp); 244 l_combeg = convexp(cp); 245 cgetustr(defs, "ce", &cp); 246 l_comend = convexp(cp); 247 cgetustr(defs, "ab", &cp); 248 l_acmbeg = convexp(cp); 249 cgetustr(defs, "ae", &cp); 250 l_acmend = convexp(cp); 251 cgetustr(defs, "sb", &cp); 252 l_strbeg = convexp(cp); 253 cgetustr(defs, "se", &cp); 254 l_strend = convexp(cp); 255 cgetustr(defs, "bb", &cp); 256 l_blkbeg = convexp(cp); 257 cgetustr(defs, "be", &cp); 258 l_blkend = convexp(cp); 259 cgetustr(defs, "lb", &cp); 260 l_chrbeg = convexp(cp); 261 cgetustr(defs, "le", &cp); 262 l_chrend = convexp(cp); 263 if (cgetustr(defs, "nc", &cp) >= 0) 264 l_nocom = convexp(cp); 265 l_escape = '\\'; 266 l_onecase = (cgetcap(defs, "oc", ':') != NULL); 267 l_toplex = (cgetcap(defs, "tl", ':') != NULL); 268 269 /* initialize the program */ 270 271 incomm = false; 272 instr = false; 273 inchr = false; 274 _escaped = false; 275 blklevel = 0; 276 for (psptr=0; psptr<PSMAX; psptr++) { 277 pstack[psptr][0] = '\0'; 278 plstack[psptr] = 0; 279 } 280 psptr = -1; 281 ps("'-F\n"); 282 if (!filter) { 283 printf(".ds =F %s\n", fname); 284 ps("'wh 0 vH\n"); 285 ps("'wh -1i vF\n"); 286 } 287 if (needbp) { 288 needbp = 0; 289 printf(".()\n"); 290 printf(".bp\n"); 291 } 292 if (!filter) { 293 fstat(fileno(stdin), &stbuf); 294 cp = ctime(&stbuf.st_mtime); 295 cp[16] = '\0'; 296 cp[24] = '\0'; 297 printf(".ds =M %s %s\n", cp+4, cp+20); 298 } 299 300 /* 301 * MAIN LOOP!!! 302 */ 303 while (fgets(buf, sizeof buf, stdin) != NULL) { 304 if (buf[0] == '\f') { 305 printf(".bp\n"); 306 } 307 if (buf[0] == '.') { 308 printf("%s", buf); 309 if (!strncmp (buf+1, "vS", 2)) 310 pass = true; 311 if (!strncmp (buf+1, "vE", 2)) 312 pass = false; 313 continue; 314 } 315 prccont = false; 316 if (!filter || pass) 317 putScp(buf); 318 else 319 printf("%s", buf); 320 if (prccont && (psptr >= 0)) { 321 ps("'FC "); 322 ps(pstack[psptr]); 323 ps("\n"); 324 } 325 #ifdef DEBUG 326 printf ("com %o str %o chr %o ptr %d\n", incomm, instr, inchr, psptr); 327 #endif 328 margin = 0; 329 } 330 needbp = 1; 331 } while (argc > 0); 332 exit(0); 333 } 334 335 #define isidchr(c) (isalnum(c) || (c) == '_') 336 337 static void 338 putScp(char *os) 339 { 340 register char *s = os; /* pointer to unmatched string */ 341 char dummy[BUFSIZ]; /* dummy to be used by expmatch */ 342 char *comptr; /* end of a comment delimiter */ 343 char *acmptr; /* end of a comment delimiter */ 344 char *strptr; /* end of a string delimiter */ 345 char *chrptr; /* end of a character const delimiter */ 346 char *blksptr; /* end of a lexical block start */ 347 char *blkeptr; /* end of a lexical block end */ 348 char *nocomptr; /* end of a non-comment delimiter */ 349 350 s_start = os; /* remember the start for expmatch */ 351 _escaped = false; 352 if (nokeyw || incomm || instr) 353 goto skip; 354 if (isproc(s)) { 355 ps("'FN "); 356 ps(pname); 357 ps("\n"); 358 if (psptr < PSMAX) { 359 ++psptr; 360 strncpy (pstack[psptr], pname, PNAMELEN); 361 pstack[psptr][PNAMELEN] = '\0'; 362 plstack[psptr] = blklevel; 363 } 364 } 365 skip: 366 do { 367 /* check for string, comment, blockstart, etc */ 368 if (!incomm && !instr && !inchr) { 369 370 blkeptr = expmatch(s, l_blkend, dummy); 371 blksptr = expmatch(s, l_blkbeg, dummy); 372 comptr = expmatch(s, l_combeg, dummy); 373 acmptr = expmatch(s, l_acmbeg, dummy); 374 strptr = expmatch(s, l_strbeg, dummy); 375 chrptr = expmatch(s, l_chrbeg, dummy); 376 nocomptr = expmatch (s, l_nocom, dummy); 377 378 /* start of non-comment? */ 379 if (nocomptr != NULL) 380 if ((nocomptr <= comptr || comptr == NULL) 381 && (nocomptr <= acmptr || acmptr == NULL)) { 382 /* continue after non-comment */ 383 putKcp (s, nocomptr-1, false); 384 s = nocomptr; 385 continue; 386 } 387 388 /* start of a comment? */ 389 if (comptr != NULL) 390 if ((comptr < strptr || strptr == NULL) 391 && (comptr < acmptr || acmptr == NULL) 392 && (comptr < chrptr || chrptr == NULL) 393 && (comptr < blksptr || blksptr == NULL) 394 && (comptr < blkeptr || blkeptr == NULL)) { 395 putKcp(s, comptr-1, false); 396 s = comptr; 397 incomm = true; 398 comtype = STANDARD; 399 if (s != os) 400 ps("\\c"); 401 ps("\\c\n'+C\n"); 402 continue; 403 } 404 405 /* start of a comment? */ 406 if (acmptr != NULL) 407 if ((acmptr < strptr || strptr == NULL) 408 && (acmptr < chrptr || chrptr == NULL) 409 && (acmptr < blksptr || blksptr == NULL) 410 && (acmptr < blkeptr || blkeptr == NULL)) { 411 putKcp(s, acmptr-1, false); 412 s = acmptr; 413 incomm = true; 414 comtype = ALTERNATE; 415 if (s != os) 416 ps("\\c"); 417 ps("\\c\n'+C\n"); 418 continue; 419 } 420 421 /* start of a string? */ 422 if (strptr != NULL) 423 if ((strptr < chrptr || chrptr == NULL) 424 && (strptr < blksptr || blksptr == NULL) 425 && (strptr < blkeptr || blkeptr == NULL)) { 426 putKcp(s, strptr-1, false); 427 s = strptr; 428 instr = true; 429 continue; 430 } 431 432 /* start of a character string? */ 433 if (chrptr != NULL) 434 if ((chrptr < blksptr || blksptr == NULL) 435 && (chrptr < blkeptr || blkeptr == NULL)) { 436 putKcp(s, chrptr-1, false); 437 s = chrptr; 438 inchr = true; 439 continue; 440 } 441 442 /* end of a lexical block */ 443 if (blkeptr != NULL) { 444 if (blkeptr < blksptr || blksptr == NULL) { 445 putKcp(s, blkeptr - 1, false); 446 s = blkeptr; 447 if (blklevel > 0 /* sanity */) 448 blklevel--; 449 if (psptr >= 0 && plstack[psptr] >= blklevel) { 450 451 /* end of current procedure */ 452 if (s != os) 453 ps("\\c"); 454 ps("\\c\n'-F\n"); 455 blklevel = plstack[psptr]; 456 457 /* see if we should print the last proc name */ 458 if (--psptr >= 0) 459 prccont = true; 460 else 461 psptr = -1; 462 } 463 continue; 464 } 465 } 466 467 /* start of a lexical block */ 468 if (blksptr != NULL) { 469 putKcp(s, blksptr - 1, false); 470 s = blksptr; 471 blklevel++; 472 continue; 473 } 474 475 /* check for end of comment */ 476 } else if (incomm) { 477 comptr = expmatch(s, l_comend, dummy); 478 acmptr = expmatch(s, l_acmend, dummy); 479 if (((comtype == STANDARD) && (comptr != NULL)) || 480 ((comtype == ALTERNATE) && (acmptr != NULL))) { 481 if (comtype == STANDARD) { 482 putKcp(s, comptr-1, true); 483 s = comptr; 484 } else { 485 putKcp(s, acmptr-1, true); 486 s = acmptr; 487 } 488 incomm = false; 489 ps("\\c\n'-C\n"); 490 continue; 491 } else { 492 putKcp(s, s + strlen(s) -1, true); 493 s = s + strlen(s); 494 continue; 495 } 496 497 /* check for end of string */ 498 } else if (instr) { 499 if ((strptr = expmatch(s, l_strend, dummy)) != NULL) { 500 putKcp(s, strptr-1, true); 501 s = strptr; 502 instr = false; 503 continue; 504 } else { 505 putKcp(s, s+strlen(s)-1, true); 506 s = s + strlen(s); 507 continue; 508 } 509 510 /* check for end of character string */ 511 } else if (inchr) { 512 if ((chrptr = expmatch(s, l_chrend, dummy)) != NULL) { 513 putKcp(s, chrptr-1, true); 514 s = chrptr; 515 inchr = false; 516 continue; 517 } else { 518 putKcp(s, s+strlen(s)-1, true); 519 s = s + strlen(s); 520 continue; 521 } 522 } 523 524 /* print out the line */ 525 putKcp(s, s + strlen(s) -1, false); 526 s = s + strlen(s); 527 } while (*s); 528 } 529 530 /* 531 * start: start of string to write 532 * end: end of string to write 533 * force: true if we should force nokeyw 534 */ 535 static void 536 putKcp(char *start, char *end, bool force) 537 { 538 int i; 539 int xfld = 0; 540 541 while (start <= end) { 542 if (idx) { 543 if (*start == ' ' || *start == '\t') { 544 if (xfld == 0) 545 printf("\001"); 546 printf("\t"); 547 xfld = 1; 548 while (*start == ' ' || *start == '\t') 549 start++; 550 continue; 551 } 552 } 553 554 /* take care of nice tab stops */ 555 if (*start == '\t') { 556 while (*start == '\t') 557 start++; 558 i = tabs(s_start, start) - margin / 8; 559 printf("\\h'|%dn'", i * 10 + 1 - margin % 8); 560 continue; 561 } 562 563 if (!nokeyw && !force) 564 if ((*start == '#' || isidchr(*start)) 565 && (start == s_start || !isidchr(start[-1]))) { 566 i = iskw(start); 567 if (i > 0) { 568 ps("\\*(+K"); 569 do 570 putcp((unsigned char)*start++); 571 while (--i > 0); 572 ps("\\*(-K"); 573 continue; 574 } 575 } 576 577 putcp((unsigned char)*start++); 578 } 579 } 580 581 582 static int 583 tabs(char *s, char *os) 584 { 585 586 return (width(s, os) / 8); 587 } 588 589 static int 590 width(register char *s, register char *os) 591 { 592 register int i = 0; 593 594 while (s < os) { 595 if (*s == '\t') { 596 i = (i + 8) &~ 7; 597 s++; 598 continue; 599 } 600 if (*s < ' ') 601 i += 2; 602 else 603 i++; 604 s++; 605 } 606 return (i); 607 } 608 609 static void 610 putcp(register int c) 611 { 612 613 switch(c) { 614 615 case 0: 616 break; 617 618 case '\f': 619 break; 620 621 case '\r': 622 break; 623 624 case '{': 625 ps("\\*(+K{\\*(-K"); 626 break; 627 628 case '}': 629 ps("\\*(+K}\\*(-K"); 630 break; 631 632 case '\\': 633 ps("\\e"); 634 break; 635 636 case '_': 637 ps("\\*_"); 638 break; 639 640 case '-': 641 ps("\\*-"); 642 break; 643 644 case '`': 645 ps("\\`"); 646 break; 647 648 case '\'': 649 ps("\\'"); 650 break; 651 652 case '.': 653 ps("\\&."); 654 break; 655 656 case '*': 657 ps("\\fI*\\fP"); 658 break; 659 660 case '/': 661 ps("\\fI\\h'\\w' 'u-\\w'/'u'/\\fP"); 662 break; 663 664 default: 665 if (c < 040) 666 putchar('^'), c |= '@'; 667 case '\t': 668 case '\n': 669 putchar(c); 670 } 671 } 672 673 /* 674 * look for a process beginning on this line 675 */ 676 static bool 677 isproc(char *s) 678 { 679 pname[0] = '\0'; 680 if (!l_toplex || blklevel == 0) 681 if (expmatch(s, l_prcbeg, pname) != NULL) { 682 return (true); 683 } 684 return (false); 685 } 686 687 688 /* iskw - check to see if the next word is a keyword 689 */ 690 691 static int 692 iskw(register char *s) 693 { 694 register char **ss = l_keywds; 695 register int i = 1; 696 register char *cp = s; 697 698 while (++cp, isidchr(*cp)) 699 i++; 700 while ((cp = *ss++)) 701 if (!STRNCMP(s,cp,i) && !isidchr(cp[i])) 702 return (i); 703 return (0); 704 } 705