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