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