1 #include <u.h> 2 #include <libc.h> 3 #include <ctype.h> 4 #include <bio.h> 5 6 enum 7 { 8 SSIZE = 10, 9 10 Maxnh= 8, /* highest NH level */ 11 HH= 4, /* heading level used for SH and NH */ 12 Maxmstack= 10, /* deepest macro/string nesting */ 13 Narg= 20, /* max args to a macro */ 14 Maxsstack= 5, /* deepest nesting of .so's */ 15 Nline= 1024, 16 Maxget= 10, 17 Maxif = 20, 18 Maxfsp = 100, 19 20 /* list types */ 21 Lordered = 1, 22 Lunordered, 23 Ldef, 24 Lother, 25 }; 26 27 char *delim = "$$"; 28 char *basename; 29 char *title; 30 int eqnmode; 31 32 int quiet; 33 float indent; /* from .in */ 34 Biobuf bout; 35 int isup; 36 int isdown; 37 int debug; 38 39 int nh[Maxnh]; 40 int ifwastrue[Maxif]; 41 42 int list, listnum, example; 43 int hangingau, hangingdt, hanginghead, hangingcenter; 44 int indirective, paragraph, sol, titleseen, ignore_nl, weBref; 45 void dohangingcenter(void); 46 47 typedef struct Goobie Goobie; 48 typedef struct Goobieif Goobieif; 49 struct Goobie 50 { 51 char *name; 52 void (*f)(int, char**); 53 }; 54 55 typedef void F(int, char**); 56 typedef void Fif(char*, char*); 57 58 struct Goobieif 59 { 60 char *name; 61 Fif *f; 62 }; 63 64 /* if, ie */ 65 Fif g_as, g_ds, g_el, g_ie, g_if; 66 Goobieif gtabif[] = { 67 { "as", g_as }, 68 { "ds", g_ds }, 69 { "if", g_if }, 70 { "ie", g_ie }, 71 { "el", g_el }, 72 { nil, nil }, 73 }; 74 75 /* pseudo ops */ 76 F g_notyet, g_ignore, g_hrule, g_startgif; 77 78 /* ms macros */ 79 F g_AU, g_B, g_BI, g_CW, g_I, g_IP, g_LP, g_PP, g_SH, g_NH; 80 F g_P1, g_P2, g_TL, g_R, g_AB, g_AE, g_EQ, g_TS, g_TE, g_FS, g_FE; 81 F g_PY, g_IH, g_MH, g_HO, g_BX, g_QS, g_QE, g_RS, g_RE; 82 83 /* pictures macro */ 84 F g_BP; 85 86 /* real troff */ 87 F g_br, g_ft, g_sp, g_de, g_lf, g_so, g_rm, g_in; 88 F g_nr, g_ig, g_RT, g_BS, g_BE, g_LB, g_ta; 89 90 /* macros to include ML in output */ 91 F g__H, g__T; 92 93 Goobie gtab[] = 94 { 95 { "_T", g__T, }, 96 { "_H", g__H, }, 97 { "1C", g_ignore, }, 98 { "2C", g_ignore, }, 99 { "AB", g_AB, }, 100 { "AE", g_AE, }, 101 { "AI", g_ignore, }, 102 { "AU", g_AU, }, 103 { "B", g_B, }, 104 { "B1", g_hrule, }, 105 { "B2", g_hrule, }, 106 { "BI", g_BI, }, 107 { "BP", g_BP, }, 108 { "BT", g_ignore, }, 109 { "BX", g_BX, }, 110 { "CW", g_CW, }, 111 { "CT", g_ignore, }, 112 { "DA", g_ignore, }, 113 { "DE", g_P2, }, 114 { "DS", g_P1, }, 115 { "EG", g_ignore, }, 116 { "EN", g_ignore, }, 117 { "EQ", g_startgif, }, 118 { "FE", g_FE, }, 119 { "FP", g_ignore, }, 120 { "FS", g_FS, }, 121 { "HO", g_HO, }, 122 { "I", g_I, }, 123 { "IH", g_IH, }, 124 { "IM", g_ignore, }, 125 { "IP", g_IP, }, 126 { "KE", g_ignore, }, 127 { "KF", g_ignore, }, 128 { "KS", g_ignore, }, 129 { "LG", g_ignore, }, 130 { "LP", g_LP, }, 131 { "LT", g_ignore, }, 132 { "MF", g_ignore, }, 133 { "MH", g_MH, }, 134 { "MR", g_ignore, }, 135 { "ND", g_ignore, }, 136 { "NH", g_NH, }, 137 { "NL", g_ignore, }, 138 { "P1", g_P1, }, 139 { "P2", g_P2, }, 140 { "PE", g_ignore, }, 141 { "PF", g_ignore, }, 142 { "PP", g_PP, }, 143 { "PS", g_startgif, }, 144 { "PY", g_PY, }, 145 { "QE", g_QE, }, 146 { "QP", g_QS, }, 147 { "QS", g_QS, }, 148 { "R", g_R, }, 149 { "RE", g_RE, }, 150 { "RP", g_ignore, }, 151 { "RS", g_RS, }, 152 { "SG", g_ignore, }, 153 { "SH", g_SH, }, 154 { "SM", g_ignore, }, 155 { "TA", g_ignore, }, 156 { "TE", g_ignore, }, 157 { "TH", g_TL, }, 158 { "TL", g_TL, }, 159 { "TM", g_ignore, }, 160 { "TR", g_ignore, }, 161 { "TS", g_startgif, }, 162 { "UL", g_I, }, 163 { "UX", g_ignore, }, 164 { "WH", g_ignore, }, 165 { "RT", g_RT, }, 166 167 { "br", g_br, }, 168 { "ti", g_br, }, 169 { "nf", g_P1, }, 170 { "fi", g_P2, }, 171 { "ft", g_ft, }, 172 { "sp", g_sp, }, 173 { "rm", g_rm, }, 174 { "de", g_de, }, 175 { "am", g_de, }, 176 { "lf", g_lf, }, 177 { "so", g_so, }, 178 { "ps", g_ignore }, 179 { "vs", g_ignore }, 180 { "nr", g_nr }, 181 { "in", g_in }, 182 { "ne", g_ignore }, 183 { "ig", g_ig }, 184 { "BS", g_BS }, 185 { "BE", g_BE }, 186 { "LB", g_LB }, 187 { nil, nil }, 188 }; 189 190 typedef struct Entity Entity; 191 struct Entity 192 { 193 char *name; 194 int value; 195 }; 196 197 Entity entity[] = 198 { 199 { "&#SPACE;", L' ', }, 200 { "&#RS;", L'\n', }, 201 { "&#RE;", L'\r', }, 202 { """, L'"', }, 203 { "Æ", L'Æ', }, 204 { "Á", L'Á', }, 205 { "Â", L'Â', }, 206 { "À", L'À', }, 207 { "Å", L'Å', }, 208 { "Ã", L'Ã', }, 209 { "Ä", L'Ä', }, 210 { "Ç", L'Ç', }, 211 { "Ð", L'Ð', }, 212 { "É", L'É', }, 213 { "Ê", L'Ê', }, 214 { "È", L'È', }, 215 { "Ë", L'Ë', }, 216 { "Í", L'Í', }, 217 { "Î", L'Î', }, 218 { "Ì", L'Ì', }, 219 { "Ï", L'Ï', }, 220 { "Ñ", L'Ñ', }, 221 { "Ó", L'Ó', }, 222 { "Ô", L'Ô', }, 223 { "Ò", L'Ò', }, 224 { "Ø", L'Ø', }, 225 { "Õ", L'Õ', }, 226 { "Ö", L'Ö', }, 227 { "Þ", L'Þ', }, 228 { "Ú", L'Ú', }, 229 { "Û", L'Û', }, 230 { "Ù", L'Ù', }, 231 { "Ü", L'Ü', }, 232 { "Ý", L'Ý', }, 233 { "á", L'á', }, 234 { "â", L'â', }, 235 { "æ", L'æ', }, 236 { "à", L'à', }, 237 { "&", L'&', }, 238 { "å", L'å', }, 239 { "ã", L'ã', }, 240 { "ä", L'ä', }, 241 { "ç", L'ç', }, 242 { "é", L'é', }, 243 { "ê", L'ê', }, 244 { "è", L'è', }, 245 { "ð", L'ð', }, 246 { "ë", L'ë', }, 247 { ">", L'>', }, 248 { "í", L'í', }, 249 { "î", L'î', }, 250 { "ì", L'ì', }, 251 { "ï", L'ï', }, 252 { "<", L'<', }, 253 { "ñ", L'ñ', }, 254 { "ó", L'ó', }, 255 { "ô", L'ô', }, 256 { "ò", L'ò', }, 257 { "ø", L'ø', }, 258 { "õ", L'õ', }, 259 { "ö", L'ö', }, 260 { "ß", L'ß', }, 261 { "þ", L'þ', }, 262 { "ú", L'ú', }, 263 { "û", L'û', }, 264 { "ù", L'ù', }, 265 { "ü", L'ü', }, 266 { "ý", L'ý', }, 267 { "ÿ", L'ÿ', }, 268 { "¡", L'¡', }, 269 { "¢", L'¢', }, 270 { "£", L'£', }, 271 { "¤", L'¤', }, 272 { "¥", L'¥', }, 273 { "¦", L'¦', }, 274 { "§", L'§', }, 275 { "¨", L'¨', }, 276 { "©", L'©', }, 277 { "ª", L'ª', }, 278 { "«", L'«', }, 279 { "¬", L'¬', }, 280 { "­", L'', }, 281 { "®", L'®', }, 282 { "¯", L'¯', }, 283 { "°", L'°', }, 284 { "±", L'±', }, 285 { "²", L'²', }, 286 { "³", L'³', }, 287 { "´", L'´', }, 288 { "µ", L'µ', }, 289 { "¶", L'¶', }, 290 { "·", L'·', }, 291 { "¸", L'¸', }, 292 { "¹", L'¹', }, 293 { "º", L'º', }, 294 { "»", L'»', }, 295 { "¼", L'¼', }, 296 { "½", L'½', }, 297 { "¾", L'¾', }, 298 { "¿", L'¿', }, 299 300 { "*", L'•', }, 301 { "¤", L'□', }, 302 { "º", L'◊', }, 303 { "(tm)", L'™', }, 304 {"Α", L'Α',}, 305 {"Β", L'Β',}, 306 {"Γ", L'Γ',}, 307 {"Δ", L'Δ',}, 308 {"Ε", L'Ε',}, 309 {"Ζ", L'Ζ',}, 310 {"Η", L'Η',}, 311 {"Θ", L'Θ',}, 312 {"Ι", L'Ι',}, 313 {"Κ", L'Κ',}, 314 {"Λ", L'Λ',}, 315 {"Μ", L'Μ',}, 316 {"Ν", L'Ν',}, 317 {"Ξ", L'Ξ',}, 318 {"Ο", L'Ο',}, 319 {"Π", L'Π',}, 320 {"Ρ", L'Ρ',}, 321 {"΢", L'',}, 322 {"Σ", L'Σ',}, 323 {"Τ", L'Τ',}, 324 {"Υ", L'Υ',}, 325 {"Φ", L'Φ',}, 326 {"Χ", L'Χ',}, 327 {"Ψ", L'Ψ',}, 328 {"Ω", L'Ω',}, 329 {"α", L'α',}, 330 {"β", L'β',}, 331 {"γ", L'γ',}, 332 {"δ", L'δ',}, 333 {"ε", L'ε',}, 334 {"ζ", L'ζ',}, 335 {"η", L'η',}, 336 {"θ", L'θ',}, 337 {"ι", L'ι',}, 338 {"κ", L'κ',}, 339 {"λ", L'λ',}, 340 {"μ", L'μ',}, 341 {"ν", L'ν',}, 342 {"ξ", L'ξ',}, 343 {"ο", L'ο',}, 344 {"π", L'π',}, 345 {"ρ", L'ρ',}, 346 {"ς", L'ς',}, 347 {"σ", L'σ',}, 348 {"τ", L'τ',}, 349 {"υ", L'υ',}, 350 {"φ", L'φ',}, 351 {"χ", L'χ',}, 352 {"ψ", L'ψ',}, 353 {"ω", L'ω',}, 354 355 { "<-", L'←', }, 356 { "^", L'↑', }, 357 { "->", L'→', }, 358 { "v", L'↓', }, 359 { "!=", L'≠', }, 360 { "<=", L'≤', }, 361 { "...", L'⋯', }, 362 {"∈", L'∈', }, 363 364 {"–", L'–', }, 365 {"—", L'—', }, 366 367 { "CYRILLIC XYZZY", L'й', }, 368 { "CYRILLIC XYZZY", L'ъ', }, 369 { "CYRILLIC Y", L'ь', }, 370 { "CYRILLIC YA", L'я', }, 371 { "CYRILLIC YA", L'ё', }, 372 { "¿", L'ℱ', }, 373 374 { nil, 0 }, 375 }; 376 377 typedef struct Troffspec Troffspec; 378 struct Troffspec 379 { 380 char *name; 381 char *value; 382 }; 383 384 Troffspec tspec[] = 385 { 386 { "A*", "Å", }, 387 { "o\"", "ö", }, 388 { "ff", "ff", }, 389 { "fi", "fi", }, 390 { "fl", "fl", }, 391 { "Fi", "ffi", }, 392 { "ru", "_", }, 393 { "em", "­", }, 394 { "14", "¼", }, 395 { "12", "½", }, 396 { "co", "©", }, 397 { "de", "°", }, 398 { "dg", "¡", }, 399 { "fm", "´", }, 400 { "rg", "®", }, 401 { "bu", "*", }, 402 { "sq", "¤", }, 403 { "hy", "-", }, 404 { "pl", "+", }, 405 { "mi", "-", }, 406 { "mu", "×", }, 407 { "di", "÷", }, 408 { "eq", "=", }, 409 { "==", "==", }, 410 { ">=", ">=", }, 411 { "<=", "<=", }, 412 { "!=", "!=", }, 413 { "+-", "±", }, 414 { "no", "¬", }, 415 { "sl", "/", }, 416 { "ap", "&", }, 417 { "~=", "~=", }, 418 { "pt", "oc", }, 419 { "gr", "GRAD", }, 420 { "->", "->", }, 421 { "<-", "<-", }, 422 { "ua", "^", }, 423 { "da", "v", }, 424 { "is", "Integral", }, 425 { "pd", "DIV", }, 426 { "if", "oo", }, 427 { "sr", "-/", }, 428 { "sb", "(~", }, 429 { "sp", "~)", }, 430 { "cu", "U", }, 431 { "ca", "(^)", }, 432 { "ib", "(=", }, 433 { "ip", "=)", }, 434 { "mo", "C", }, 435 { "es", "Ø", }, 436 { "aa", "´", }, 437 { "ga", "`", }, 438 { "ci", "O", }, 439 { "L1", "DEATHSTAR", }, 440 { "sc", "§", }, 441 { "dd", "++", }, 442 { "lh", "<=", }, 443 { "rh", "=>", }, 444 { "lt", "(", }, 445 { "rt", ")", }, 446 { "lc", "|", }, 447 { "rc", "|", }, 448 { "lb", "(", }, 449 { "rb", ")", }, 450 { "lf", "|", }, 451 { "rf", "|", }, 452 { "lk", "|", }, 453 { "rk", "|", }, 454 { "bv", "|", }, 455 { "ts", "s", }, 456 { "br", "|", }, 457 { "or", "|", }, 458 { "ul", "_", }, 459 { "rn", " ", }, 460 { "**", "*", }, 461 { "tm", "™", }, 462 { nil, nil, }, 463 }; 464 465 typedef struct Font Font; 466 struct Font 467 { 468 char *start; 469 char *end; 470 }; 471 Font bfont = { "<B>", "</B>" }; 472 Font ifont = { "<I>", "</I>" }; 473 Font bifont = { "<B><I>", "</I></B>" }; 474 Font cwfont = { "<TT>", "</TT>" }; 475 Font *fstack[Maxfsp]; 476 int fsp = -1; 477 478 typedef struct String String; 479 struct String 480 { 481 String *next; 482 char *name; 483 char *val; 484 }; 485 String *numregs, *strings; 486 char *strstack[Maxmstack]; 487 char *mustfree[Maxmstack]; 488 int strsp = -1; 489 int elsetop = -1; 490 491 typedef struct Mstack Mstack; 492 struct Mstack 493 { 494 char *ptr; 495 char *argv[Narg+1]; 496 }; 497 String *macros; 498 Mstack mstack[Maxmstack]; 499 int msp = -1; 500 501 typedef struct Srcstack Srcstack; 502 struct Srcstack 503 { 504 char filename[256]; 505 int fd; 506 int lno; 507 int rlno; 508 Biobuf in; 509 }; 510 Srcstack sstack[Maxsstack]; 511 Srcstack *ssp = &sstack[-1]; 512 513 char token[128]; 514 515 void closel(void); 516 void closefont(void); 517 518 void* 519 emalloc(uint n) 520 { 521 void *p; 522 523 p = mallocz(n, 1); 524 if(p == nil){ 525 fprint(2, "ms2html: malloc failed: %r\n"); 526 exits("malloc"); 527 } 528 return p; 529 } 530 531 532 /* define a string variable */ 533 void 534 dsnr(char *name, char *val, String **l) 535 { 536 String *s; 537 538 for(s = *l; s != nil; s = *l){ 539 if(strcmp(s->name, name) == 0) 540 break; 541 l = &s->next; 542 } 543 if(s == nil){ 544 s = emalloc(sizeof(String)); 545 *l = s; 546 s->name = strdup(name); 547 } else 548 free(s->val); 549 s->val = strdup(val); 550 } 551 552 void 553 ds(char *name, char *val) 554 { 555 dsnr(name, val, &strings); 556 } 557 558 /* look up a defined string */ 559 char* 560 getds(char *name) 561 { 562 String *s; 563 564 for(s = strings; s != nil; s = s->next) 565 if(strcmp(name, s->name) == 0) 566 break; 567 if(s != nil) 568 return s->val; 569 return ""; 570 } 571 572 char * 573 getnr(char *name) 574 { 575 String *s; 576 577 for(s = numregs; s != nil; s = s->next) 578 if(strcmp(name, s->name) == 0) 579 break; 580 if(s != nil) 581 return s->val; 582 return "0"; 583 } 584 585 void 586 pushstr(char *p) 587 { 588 if(p == nil) 589 return; 590 if(strsp >= Maxmstack - 1) 591 return; 592 strstack[++strsp] = p; 593 } 594 595 596 /* lookup a defined macro */ 597 char* 598 getmacro(char *name) 599 { 600 String *s; 601 602 for(s = macros; s != nil; s = s->next) 603 if(strcmp(name, s->name) == 0) 604 return s->val; 605 return nil; 606 } 607 608 enum 609 { 610 Dstring, 611 Macro, 612 Input, 613 }; 614 int lastsrc; 615 616 void 617 pushsrc(char *name) 618 { 619 Dir *d; 620 int fd; 621 622 if(ssp == &sstack[Maxsstack-1]){ 623 fprint(2, "ms2html: .so's too deep\n"); 624 return; 625 } 626 d = nil; 627 if(name == nil){ 628 d = dirfstat(0); 629 if(d == nil){ 630 fprint(2, "ms2html: can't stat %s: %r\n", name); 631 return; 632 } 633 name = d->name; 634 fd = 0; 635 } else { 636 fd = open(name, OREAD); 637 if(fd < 0){ 638 fprint(2, "ms2html: can't open %s: %r\n", name); 639 return; 640 } 641 } 642 ssp++; 643 ssp->fd = fd; 644 Binit(&ssp->in, fd, OREAD); 645 snprint(ssp->filename, sizeof(ssp->filename), "%s", name); 646 ssp->lno = ssp->rlno = 1; 647 free(d); 648 } 649 650 /* get next logical byte. from stdin or a defined string */ 651 int 652 getrune(void) 653 { 654 int i; 655 Rune r; 656 int c; 657 Mstack *m; 658 659 while(strsp >= 0){ 660 i = chartorune(&r, strstack[strsp]); 661 if(r != 0){ 662 strstack[strsp] += i; 663 lastsrc = Dstring; 664 return r; 665 } 666 if (mustfree[strsp]) { 667 free(mustfree[strsp]); 668 mustfree[strsp] = nil; 669 } 670 strsp--; 671 } 672 while(msp >= 0){ 673 m = &mstack[msp]; 674 i = chartorune(&r, m->ptr); 675 if(r != 0){ 676 m->ptr += i; 677 lastsrc = Macro; 678 return r; 679 } 680 for(i = 0; m->argv[i] != nil; i++) 681 free(m->argv[i]); 682 msp--; 683 } 684 685 lastsrc = Input; 686 for(;;) { 687 if(ssp < sstack) 688 return -1; 689 c = Bgetrune(&ssp->in); 690 if(c >= 0){ 691 r = c; 692 break; 693 } 694 close(ssp->fd); 695 ssp--; 696 } 697 698 return r; 699 } 700 701 void 702 ungetrune(void) 703 { 704 switch(lastsrc){ 705 case Dstring: 706 if(strsp >= 0) 707 strstack[strsp]--; 708 break; 709 case Macro: 710 if(msp >= 0) 711 mstack[msp].ptr--; 712 break; 713 case Input: 714 if(ssp >= sstack) 715 Bungetrune(&ssp->in); 716 break; 717 } 718 } 719 720 int vert; 721 722 char* 723 changefont(Font *f) 724 { 725 token[0] = 0; 726 if(fsp == Maxfsp) 727 return token; 728 if(fsp >= 0 && fstack[fsp]) 729 strcpy(token, fstack[fsp]->end); 730 if(f != nil) 731 strcat(token, f->start); 732 fstack[++fsp] = f; 733 return token; 734 } 735 736 char* 737 changebackfont(void) 738 { 739 token[0] = 0; 740 if(fsp >= 0){ 741 if(fstack[fsp]) 742 strcpy(token, fstack[fsp]->end); 743 fsp--; 744 } 745 if(fsp >= 0 && fstack[fsp]) 746 strcat(token, fstack[fsp]->start); 747 return token; 748 } 749 750 char* 751 changesize(int amount) 752 { 753 static int curamount; 754 static char buf[200]; 755 int i; 756 757 buf[0] = 0; 758 if (curamount >= 0) 759 for (i = 0; i < curamount; i++) 760 strcat(buf, "</big>"); 761 else 762 for (i = 0; i < -curamount; i++) 763 strcat(buf, "</small>"); 764 curamount = 0; 765 if (amount >= 0) 766 for (i = 0; i < amount; i++) 767 strcat(buf, "<big>"); 768 else 769 for (i = 0; i < -amount; i++) 770 strcat(buf, "<small>"); 771 curamount = amount; 772 return buf; 773 } 774 775 /* get next logical character. expand it with escapes */ 776 char* 777 getnext(void) 778 { 779 int r; 780 Entity *e; 781 Troffspec *t; 782 Rune R; 783 char str[4]; 784 static char buf[8]; 785 786 r = getrune(); 787 if(r < 0) 788 return nil; 789 if(r > 128 || r == '<' || r == '>'){ 790 for(e = entity; e->name; e++) 791 if(e->value == r) 792 return e->name; 793 sprint(buf, "&#%d;", r); 794 return buf; 795 } 796 797 if (r == delim[eqnmode]){ 798 if (eqnmode == 0){ 799 eqnmode = 1; 800 return changefont(&ifont); 801 } 802 eqnmode = 0; 803 return changebackfont(); 804 } 805 switch(r){ 806 case '\\': 807 r = getrune(); 808 if(r < 0) 809 return nil; 810 switch(r){ 811 case ' ': 812 return " "; 813 814 /* chars to ignore */ 815 case '&': 816 case '|': 817 case '%': 818 return ""; 819 820 /* small space in troff, nothing in nroff */ 821 case '^': 822 return getnext(); 823 824 /* ignore arg */ 825 case 'k': 826 getrune(); 827 return getnext(); 828 829 /* comment */ 830 case '"': 831 while(getrune() != '\n') 832 ; 833 return "\n"; 834 /* ignore line */ 835 case '!': 836 while(getrune() != '\n') 837 ; 838 ungetrune(); 839 return getnext(); 840 841 /* defined strings */ 842 case '*': 843 r = getrune(); 844 if(r == '('){ 845 str[0] = getrune(); 846 str[1] = getrune(); 847 str[2] = 0; 848 } else { 849 str[0] = r; 850 str[1] = 0; 851 } 852 pushstr(getds(str)); 853 return getnext(); 854 855 /* macro args */ 856 case '$': 857 r = getrune(); 858 if(r < '1' || r > '9'){ 859 token[0] = '\\'; 860 token[1] = '$'; 861 token[2] = r; 862 token[3] = 0; 863 return token; 864 } 865 r -= '0'; 866 if(msp >= 0) 867 pushstr(mstack[msp].argv[r]); 868 return getnext(); 869 870 /* special chars */ 871 case '(': 872 token[0] = getrune(); 873 token[1] = getrune(); 874 token[2] = 0; 875 for(t = tspec; t->name; t++) 876 if(strcmp(token, t->name) == 0) 877 return t->value; 878 return "¿"; 879 880 /* ignore immediately following newline */ 881 case 'c': 882 r = getrune(); 883 if (r == '\n') { 884 sol = ignore_nl = 1; 885 if (indirective) 886 break; 887 } 888 else 889 ungetrune(); 890 return getnext(); 891 892 /* escape backslash */ 893 case 'e': 894 return "\\"; 895 896 /* font change */ 897 case 'f': 898 r = getrune(); 899 switch(r){ 900 case '(': 901 str[0] = getrune(); 902 str[1] = getrune(); 903 str[2] = 0; 904 token[0] = 0; 905 if(strcmp("BI", str) == 0) 906 return changefont(&bifont); 907 else if(strcmp("CW", str) == 0) 908 return changefont(&cwfont); 909 else 910 return changefont(nil); 911 case '3': 912 case 'B': 913 return changefont(&bfont); 914 case '2': 915 case 'I': 916 return changefont(&ifont); 917 case '4': 918 return changefont(&bifont); 919 case '5': 920 return changefont(&cwfont); 921 case 'P': 922 return changebackfont(); 923 case 'R': 924 default: 925 return changefont(nil); 926 } 927 928 /* number register */ 929 case 'n': 930 r = getrune(); 931 if (r == '(') /*)*/ { 932 r = getrune(); 933 if (r < 0) 934 return nil; 935 str[0] = r; 936 r = getrune(); 937 if (r < 0) 938 return nil; 939 str[1] = r; 940 str[2] = 0; 941 } 942 else { 943 str[0] = r; 944 str[1] = 0; 945 } 946 pushstr(getnr(str)); 947 return getnext(); 948 949 /* font size */ 950 case 's': 951 r = getrune(); 952 switch(r){ 953 case '0': 954 return changesize(0); 955 case '-': 956 r = getrune(); 957 if (!isdigit(r)) 958 return getnext(); 959 return changesize(-(r - '0')); 960 case '+': 961 r = getrune(); 962 if (!isdigit(r)) 963 return getnext(); 964 return changesize(r - '0'); 965 } 966 return getnext(); 967 /* vertical movement */ 968 case 'v': 969 r = getrune(); 970 if(r != '\''){ 971 ungetrune(); 972 return getnext(); 973 } 974 r = getrune(); 975 if(r != '-') 976 vert--; 977 else 978 vert++; 979 while(r != '\'' && r != '\n') 980 r = getrune(); 981 if(r != '\'') 982 ungetrune(); 983 984 if(vert > 0) 985 return "^"; 986 return getnext(); 987 988 989 /* horizontal line */ 990 case 'l': 991 r = getrune(); 992 if(r != '\''){ 993 ungetrune(); 994 return "<HR>"; 995 } 996 while(getrune() != '\'') 997 ; 998 return "<HR>"; 999 1000 /* character height and slant */ 1001 case 'S': 1002 case 'H': 1003 r = getrune(); 1004 if(r != '\''){ 1005 ungetrune(); 1006 return "<HR>"; 1007 } 1008 while(getrune() != '\'') 1009 ; 1010 return getnext(); 1011 1012 /* digit-width space */ 1013 case '0': 1014 return " "; 1015 1016 /*for .if, .ie, .el */ 1017 case '{': 1018 return "\\{"; /*}*/ 1019 case '}': 1020 return ""; 1021 /* up and down */ 1022 case 'u': 1023 if (isdown) { 1024 isdown = 0; 1025 return "</sub>"; 1026 } 1027 isup = 1; 1028 return "<sup>"; 1029 case 'd': 1030 if (isup) { 1031 isup = 0; 1032 return "</sup>"; 1033 } 1034 isdown = 1; 1035 return "<sub>"; 1036 } 1037 break; 1038 case '&': 1039 if(msp >= 0 || strsp >= 0) 1040 return "&"; 1041 return "&"; 1042 case '<': 1043 if(msp >= 0 || strsp >= 0) 1044 return "<"; 1045 return "<"; 1046 case '>': 1047 if(msp >= 0 || strsp >= 0) 1048 return ">"; 1049 return ">"; 1050 } 1051 if (r < Runeself) { 1052 token[0] = r; 1053 token[1] = 0; 1054 } 1055 else { 1056 R = r; 1057 token[runetochar(token,&R)] = 0; 1058 } 1059 return token; 1060 } 1061 1062 /* if arg0 is set, read up to (and expand) to the next whitespace, else to the end of line */ 1063 char* 1064 copyline(char *p, char *e, int arg0) 1065 { 1066 int c; 1067 Rune r; 1068 char *p1; 1069 1070 while((c = getrune()) == ' ' || c == '\t') 1071 ; 1072 for(indirective = 1; p < e; c = getrune()) { 1073 if (c < 0) 1074 goto done; 1075 switch(c) { 1076 case '\\': 1077 break; 1078 case '\n': 1079 if (arg0) 1080 ungetrune(); 1081 goto done; 1082 case ' ': 1083 case '\t': 1084 if (arg0) 1085 goto done; 1086 default: 1087 r = c; 1088 p += runetochar(p,&r); 1089 continue; 1090 } 1091 ungetrune(); 1092 p1 = getnext(); 1093 if (p1 == nil) 1094 goto done; 1095 if (*p1 == '\n') { 1096 if (arg0) 1097 ungetrune(); 1098 break; 1099 } 1100 while((*p = *p1++) && p < e) 1101 p++; 1102 } 1103 done: 1104 indirective = 0; 1105 *p++ = 0; 1106 return p; 1107 } 1108 1109 char* 1110 copyarg(char *p, char *e, int *nullarg) 1111 { 1112 int c, quoted, last; 1113 Rune r; 1114 1115 *nullarg = 0; 1116 quoted = 0; 1117 do{ 1118 c = getrune(); 1119 } while(c == ' ' || c == '\t'); 1120 1121 if(c == '"'){ 1122 quoted = 1; 1123 *nullarg = 1; 1124 c = getrune(); 1125 } 1126 1127 if(c == '\n') 1128 goto done; 1129 1130 last = 0; 1131 for(; p < e; c = getrune()) { 1132 if (c < 0) 1133 break; 1134 switch(c) { 1135 case '\n': 1136 ungetrune(); 1137 goto done; 1138 case '\\': 1139 r = c; 1140 p += runetochar(p,&r); 1141 if(last == '\\') 1142 r = 0; 1143 break; 1144 case ' ': 1145 case '\t': 1146 if(!quoted && last != '\\') 1147 goto done; 1148 r = c; 1149 p += runetochar(p,&r); 1150 break; 1151 case '"': 1152 if(quoted && last != '\\') 1153 goto done; 1154 r = c; 1155 p += runetochar(p,&r); 1156 break; 1157 default: 1158 r = c; 1159 p += runetochar(p,&r); 1160 break; 1161 } 1162 last = r; 1163 } 1164 done: 1165 *p++ = 0; 1166 return p; 1167 1168 } 1169 1170 int 1171 parseargs(char *p, char *e, char **argv) 1172 { 1173 int argc; 1174 char *np; 1175 int nullarg; 1176 1177 indirective = 1; 1178 *p++ = 0; 1179 for(argc = 1; argc < Narg; argc++){ 1180 np = copyarg(p, e, &nullarg); 1181 if(nullarg==0 && np == p+1) 1182 break; 1183 argv[argc] = p; 1184 p = np; 1185 } 1186 argv[argc] = nil; 1187 indirective = 0; 1188 1189 1190 return argc; 1191 } 1192 1193 void 1194 dodirective(void) 1195 { 1196 char *p, *e; 1197 Goobie *g; 1198 Goobieif *gif; 1199 char line[Nline], *line1; 1200 int i, argc; 1201 char *argv[Narg]; 1202 Mstack *m; 1203 1204 /* read line, translate special bytes */ 1205 e = line + sizeof(line) - UTFmax - 1; 1206 line1 = copyline(line, e, 1); 1207 if (!line[0]) 1208 return; 1209 argv[0] = line; 1210 1211 /* first look through user defined macros */ 1212 p = getmacro(argv[0]); 1213 if(p != nil){ 1214 if(msp == Maxmstack-1){ 1215 fprint(2, "ms2html: macro stack overflow\n"); 1216 return; 1217 } 1218 argc = parseargs(line1, e, argv); 1219 m = &mstack[++msp]; 1220 m->ptr = p; 1221 memset(m->argv, 0, sizeof(m->argv)); 1222 for(i = 0; i < argc; i++) 1223 m->argv[i] = strdup(argv[i]); 1224 return; 1225 } 1226 1227 /* check for .if or .ie */ 1228 for(gif = gtabif; gif->name; gif++) 1229 if(strcmp(gif->name, argv[0]) == 0){ 1230 (*gif->f)(line1, e); 1231 return; 1232 } 1233 1234 argc = parseargs(line1, e, argv); 1235 1236 /* try standard ms macros */ 1237 for(g = gtab; g->name; g++) 1238 if(strcmp(g->name, argv[0]) == 0){ 1239 (*g->f)(argc, argv); 1240 return; 1241 } 1242 1243 if(debug) 1244 fprint(2, "stdin %d(%s:%d): unknown directive %s\n", 1245 ssp->lno, ssp->filename, ssp->rlno, line); 1246 } 1247 1248 void 1249 printarg(char *a) 1250 { 1251 char *e, *p; 1252 1253 e = a + strlen(a); 1254 pushstr(a); 1255 while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){ 1256 p = getnext(); 1257 if(p == nil) 1258 return; 1259 Bprint(&bout, "%s", p); 1260 } 1261 } 1262 1263 void 1264 printargs(int argc, char **argv) 1265 { 1266 argc--; 1267 argv++; 1268 while(--argc > 0){ 1269 printarg(*argv++); 1270 Bprint(&bout, " "); 1271 } 1272 if(argc == 0) 1273 printarg(*argv); 1274 } 1275 1276 void 1277 dohangingdt(void) 1278 { 1279 switch(hangingdt){ 1280 case 3: 1281 hangingdt--; 1282 break; 1283 case 2: 1284 Bprint(&bout, "<dd>"); 1285 hangingdt = 0; 1286 break; 1287 } 1288 1289 } 1290 1291 void 1292 dohangingau(void) 1293 { 1294 if(hangingau == 0) 1295 return; 1296 Bprint(&bout, "</I></DL>\n"); 1297 hangingau = 0; 1298 } 1299 1300 void 1301 dohanginghead(void) 1302 { 1303 if(hanginghead == 0) 1304 return; 1305 Bprint(&bout, "</H%d>\n", hanginghead); 1306 hanginghead = 0; 1307 } 1308 1309 /* 1310 * convert a man page to html and output 1311 */ 1312 void 1313 doconvert(void) 1314 { 1315 char c, *p; 1316 Tm *t; 1317 1318 pushsrc(nil); 1319 1320 sol = 1; 1321 Bprint(&bout, "<html>\n"); 1322 Bflush(&bout); 1323 for(;;){ 1324 p = getnext(); 1325 if(p == nil) 1326 break; 1327 c = *p; 1328 if(c == '.' && sol){ 1329 dodirective(); 1330 dohangingdt(); 1331 ssp->lno++; 1332 ssp->rlno++; 1333 sol = 1; 1334 } else if(c == '\n'){ 1335 if (ignore_nl) 1336 ignore_nl = 0; 1337 else { 1338 if(hangingau) 1339 Bprint(&bout, "<br>\n"); 1340 else 1341 Bprint(&bout, "%s", p); 1342 dohangingdt(); 1343 } 1344 ssp->lno++; 1345 ssp->rlno++; 1346 sol = 1; 1347 } else{ 1348 Bprint(&bout, "%s", p); 1349 ignore_nl = sol = 0; 1350 } 1351 } 1352 dohanginghead(); 1353 dohangingdt(); 1354 closel(); 1355 if(fsp >= 0 && fstack[fsp]) 1356 Bprint(&bout, "%s", fstack[fsp]->end); 1357 Bprint(&bout, "<br> <br>\n"); 1358 Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n"); 1359 t = localtime(time(nil)); 1360 Bprint(&bout, "Copyright</A> © %d Lucent Technologies Inc. All rights reserved.\n", 1361 t->year+1900); 1362 Bprint(&bout, "</body></html>\n"); 1363 } 1364 1365 static void 1366 usage(void) 1367 { 1368 sysfatal("usage: ms2html [-q] [-b basename] [-d '$$'] [-t title]\n"); 1369 } 1370 1371 void 1372 main(int argc, char **argv) 1373 { 1374 quiet = 1; 1375 ARGBEGIN { 1376 case 't': 1377 title = EARGF(usage()); 1378 break; 1379 case 'b': 1380 basename = EARGF(usage()); 1381 break; 1382 case 'q': 1383 quiet = 0; 1384 break; 1385 case 'd': 1386 delim = EARGF(usage()); 1387 break; 1388 case '?': 1389 default: 1390 usage(); 1391 } ARGEND; 1392 1393 Binit(&bout, 1, OWRITE); 1394 1395 ds("R", "®"); 1396 1397 doconvert(); 1398 exits(nil); 1399 } 1400 1401 void 1402 g_notyet(int, char **argv) 1403 { 1404 fprint(2, "ms2html: .%s not yet supported\n", argv[0]); 1405 } 1406 1407 void 1408 g_ignore(int, char **argv) 1409 { 1410 if(quiet) 1411 return; 1412 fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]); 1413 } 1414 1415 void 1416 g_PP(int, char**) 1417 { 1418 dohanginghead(); 1419 closel(); 1420 closefont(); 1421 Bprint(&bout, "<P>\n"); 1422 paragraph = 1; 1423 } 1424 1425 void 1426 g_LP(int, char**) 1427 { 1428 dohanginghead(); 1429 closel(); 1430 closefont(); 1431 Bprint(&bout, "<br> <br>\n"); 1432 } 1433 1434 /* close a list */ 1435 void 1436 closel(void) 1437 { 1438 g_P2(1, nil); 1439 dohangingau(); 1440 if(paragraph){ 1441 Bprint(&bout, "</P>\n"); 1442 paragraph = 0; 1443 } 1444 switch(list){ 1445 case Lordered: 1446 Bprint(&bout, "</ol>\n"); 1447 break; 1448 case Lunordered: 1449 Bprint(&bout, "</ul>\n"); 1450 break; 1451 case Lother: 1452 case Ldef: 1453 Bprint(&bout, "</dl>\n"); 1454 break; 1455 } 1456 list = 0; 1457 1458 } 1459 1460 1461 void 1462 g_IP(int argc, char **argv) 1463 { 1464 switch(list){ 1465 default: 1466 closel(); 1467 if(argc > 1){ 1468 if(strcmp(argv[1], "1") == 0){ 1469 list = Lordered; 1470 listnum = 1; 1471 Bprint(&bout, "<OL>\n"); 1472 } else if(strcmp(argv[1], "\\(bu") == 0){ 1473 list = Lunordered; 1474 Bprint(&bout, "<UL>\n"); 1475 } else { 1476 list = Lother; 1477 Bprint(&bout, "<DL COMPACT>\n"); 1478 } 1479 } else { 1480 list = Lother; 1481 Bprint(&bout, "<DL>\n"); 1482 } 1483 break; 1484 case Lother: 1485 case Lordered: 1486 case Lunordered: 1487 break; 1488 } 1489 1490 switch(list){ 1491 case Lother: 1492 Bprint(&bout, "<DT>"); 1493 if(argc > 1) 1494 printarg(argv[1]); 1495 else 1496 Bprint(&bout, "<DT> "); 1497 Bprint(&bout, "<DD>\n"); 1498 break; 1499 case Lordered: 1500 case Lunordered: 1501 Bprint(&bout, "<LI>\n"); 1502 break; 1503 } 1504 } 1505 1506 /* 1507 * .5i is one <DL><DT><DD> 1508 */ 1509 void 1510 g_in(int argc, char **argv) 1511 { 1512 float f; 1513 int delta, x; 1514 char *p; 1515 1516 f = indent/0.5; 1517 delta = f; 1518 if(argc <= 1){ 1519 indent = 0.0; 1520 } else { 1521 f = strtod(argv[1], &p); 1522 switch(*p){ 1523 case 'i': 1524 break; 1525 case 'c': 1526 f = f / 2.54; 1527 break; 1528 case 'P': 1529 f = f / 6; 1530 break; 1531 default: 1532 case 'u': 1533 case 'm': 1534 f = f * (12 / 72); 1535 break; 1536 case 'n': 1537 f = f * (6 / 72); 1538 break; 1539 case 'p': 1540 f = f / 72.0; 1541 break; 1542 } 1543 switch(argv[1][0]){ 1544 case '+': 1545 case '-': 1546 indent += f; 1547 break; 1548 default: 1549 indent = f; 1550 break; 1551 } 1552 } 1553 if(indent < 0.0) 1554 indent = 0.0; 1555 f = (indent/0.5); 1556 x = f; 1557 delta = x - delta; 1558 while(delta < 0){ 1559 Bprint(&bout, "</DL>\n"); 1560 delta++; 1561 } 1562 while(delta > 0){ 1563 Bprint(&bout, "<DL><DT><DD>\n"); 1564 delta--; 1565 } 1566 } 1567 1568 void 1569 g_HP(int, char**) 1570 { 1571 switch(list){ 1572 default: 1573 closel(); 1574 list = Ldef; 1575 hangingdt = 1; 1576 Bprint(&bout, "<DL><DT>\n"); 1577 break; 1578 case Ldef: 1579 if(hangingdt) 1580 Bprint(&bout, "<DD>"); 1581 Bprint(&bout, "<DT>"); 1582 hangingdt = 1; 1583 break; 1584 } 1585 } 1586 1587 void 1588 g_SH(int, char**) 1589 { 1590 dohanginghead(); 1591 dohangingcenter(); 1592 closel(); 1593 closefont(); 1594 Bprint(&bout, "<H%d>", HH); 1595 hanginghead = HH; 1596 } 1597 1598 void 1599 g_NH(int argc, char **argv) 1600 { 1601 int i, level; 1602 1603 closel(); 1604 closefont(); 1605 1606 dohangingcenter(); 1607 if(argc == 1) 1608 level = 0; 1609 else { 1610 level = atoi(argv[1])-1; 1611 if(level < 0 || level >= Maxnh) 1612 level = Maxnh - 1; 1613 } 1614 nh[level]++; 1615 1616 Bprint(&bout, "<H%d>", HH); 1617 hanginghead = HH; 1618 1619 Bprint(&bout, "%d", nh[0]); 1620 for(i = 1; i <= level; i++) 1621 Bprint(&bout, ".%d", nh[i]); 1622 Bprint(&bout, " "); 1623 1624 for(i = level+1; i < Maxnh; i++) 1625 nh[i] = 0; 1626 } 1627 1628 void 1629 g_TL(int, char**) 1630 { 1631 char *p, *np; 1632 char name[128]; 1633 1634 closefont(); 1635 1636 if(!titleseen){ 1637 if(!title){ 1638 /* get base part of filename */ 1639 p = strrchr(ssp->filename, '/'); 1640 if(p == nil) 1641 p = ssp->filename; 1642 else 1643 p++; 1644 strncpy(name, p, sizeof(name)); 1645 name[sizeof(name)-1] = 0; 1646 1647 /* dump any extensions */ 1648 np = strchr(name, '.'); 1649 if(np) 1650 *np = 0; 1651 title = p; 1652 } 1653 Bprint(&bout, "<title>\n"); 1654 Bprint(&bout, "%s\n", title); 1655 Bprint(&bout, "</title>\n"); 1656 Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n"); 1657 titleseen = 1; 1658 } 1659 1660 Bprint(&bout, "<center>"); 1661 hangingcenter = 1; 1662 Bprint(&bout, "<H%d>", 1); 1663 hanginghead = 1; 1664 } 1665 1666 void 1667 dohangingcenter(void) 1668 { 1669 if(hangingcenter){ 1670 Bprint(&bout, "</center>"); 1671 hangingcenter = 1; 1672 } 1673 } 1674 1675 void 1676 g_AU(int, char**) 1677 { 1678 closel(); 1679 dohanginghead(); 1680 Bprint(&bout, "<DL><DD><I>"); 1681 hangingau = 1; 1682 } 1683 1684 void 1685 pushfont(Font *f) 1686 { 1687 if(fsp == Maxfsp) 1688 return; 1689 if(fsp >= 0 && fstack[fsp]) 1690 Bprint(&bout, "%s", fstack[fsp]->end); 1691 if(f != nil) 1692 Bprint(&bout, "%s", f->start); 1693 fstack[++fsp] = f; 1694 } 1695 1696 void 1697 popfont(void) 1698 { 1699 if(fsp >= 0){ 1700 if(fstack[fsp]) 1701 Bprint(&bout, "%s", fstack[fsp]->end); 1702 fsp--; 1703 } 1704 } 1705 1706 /* 1707 * for 3 args print arg3 \fxarg1\fP arg2 1708 * for 2 args print arg1 \fxarg2\fP 1709 * for 1 args print \fxarg1\fP 1710 */ 1711 void 1712 font(Font *f, int argc, char **argv) 1713 { 1714 if(argc == 1){ 1715 pushfont(nil); 1716 return; 1717 } 1718 if(argc > 3) 1719 printarg(argv[3]); 1720 pushfont(f); 1721 printarg(argv[1]); 1722 popfont(); 1723 if(argc > 2) 1724 printarg(argv[2]); 1725 Bprint(&bout, "\n"); 1726 } 1727 1728 void 1729 closefont(void) 1730 { 1731 if(fsp >= 0 && fstack[fsp]) 1732 Bprint(&bout, "%s", fstack[fsp]->end); 1733 fsp = -1; 1734 } 1735 1736 void 1737 g_B(int argc, char **argv) 1738 { 1739 font(&bfont, argc, argv); 1740 } 1741 1742 void 1743 g_R(int argc, char **argv) 1744 { 1745 font(nil, argc, argv); 1746 } 1747 1748 void 1749 g_BI(int argc, char **argv) 1750 { 1751 font(&bifont, argc, argv); 1752 } 1753 1754 void 1755 g_CW(int argc, char **argv) 1756 { 1757 font(&cwfont, argc, argv); 1758 } 1759 1760 char* 1761 lower(char *p) 1762 { 1763 char *x; 1764 1765 for(x = p; *x; x++) 1766 if(*x >= 'A' && *x <= 'Z') 1767 *x -= 'A' - 'a'; 1768 return p; 1769 } 1770 1771 void 1772 g_I(int argc, char **argv) 1773 { 1774 int anchor; 1775 char *p; 1776 1777 anchor = 0; 1778 if(argc > 2){ 1779 p = argv[2]; 1780 if(p[0] == '(') 1781 if(p[1] >= '0' && p[1] <= '9') 1782 if(p[2] == ')'){ 1783 anchor = 1; 1784 Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">", 1785 p[1], lower(argv[1])); 1786 } 1787 } 1788 font(&ifont, argc, argv); 1789 if(anchor) 1790 Bprint(&bout, "</A>"); 1791 } 1792 1793 void 1794 g_br(int, char**) 1795 { 1796 if(hangingdt){ 1797 Bprint(&bout, "<dd>"); 1798 hangingdt = 0; 1799 }else 1800 Bprint(&bout, "<br>\n"); 1801 } 1802 1803 void 1804 g_P1(int, char**) 1805 { 1806 if(example == 0){ 1807 example = 1; 1808 Bprint(&bout, "<DL><DT><DD><TT><PRE>\n"); 1809 } 1810 } 1811 1812 void 1813 g_P2(int, char**) 1814 { 1815 if(example){ 1816 example = 0; 1817 Bprint(&bout, "</PRE></TT></DL>\n"); 1818 } 1819 } 1820 1821 void 1822 g_SM(int, char **argv) 1823 { 1824 Bprint(&bout, "%s", argv[1]); 1825 } 1826 1827 void 1828 g_ft(int argc, char **argv) 1829 { 1830 if(argc < 2){ 1831 pushfont(nil); 1832 return; 1833 } 1834 1835 switch(argv[1][0]){ 1836 case '3': 1837 case 'B': 1838 pushfont(&bfont); 1839 break; 1840 case '2': 1841 case 'I': 1842 pushfont(&ifont); 1843 break; 1844 case '4': 1845 pushfont(&bifont); 1846 break; 1847 case '5': 1848 pushfont(&cwfont); 1849 break; 1850 case 'P': 1851 popfont(); 1852 break; 1853 case 'R': 1854 default: 1855 pushfont(nil); 1856 break; 1857 } 1858 } 1859 1860 void 1861 g_sp(int argc, char **argv) 1862 { 1863 int n; 1864 1865 n = 1; 1866 if(argc > 1){ 1867 n = atoi(argv[1]); 1868 if(n < 1) 1869 n = 1; 1870 if(argv[1][strlen(argv[1])-1] == 'i') 1871 n *= 4; 1872 } 1873 if(n > 5){ 1874 Bprint(&bout, "<br> <br>\n"); 1875 Bprint(&bout, "<HR>\n"); 1876 Bprint(&bout, "<br> <br>\n"); 1877 } else 1878 for(; n > 0; n--) 1879 Bprint(&bout, "<br> <br>\n"); 1880 } 1881 1882 void 1883 rm_loop(char *name, String **l) 1884 { 1885 String *s; 1886 for(s = *l; s != nil; s = *l){ 1887 if(strcmp(name, s->name) == 0){ 1888 *l = s->next; 1889 free(s->name); 1890 free(s->val); 1891 free(s); 1892 break; 1893 } 1894 l = &s->next; 1895 } 1896 } 1897 1898 void 1899 g_rm(int argc, char **argv) 1900 { 1901 Goobie *g; 1902 char *name; 1903 int i; 1904 1905 for(i = 1; i < argc; i++) { 1906 name = argv[i]; 1907 rm_loop(name, &strings); 1908 rm_loop(name, ¯os); 1909 for(g = gtab; g->name; g++) 1910 if (strcmp(g->name, name) == 0) { 1911 g->f = g_ignore; 1912 break; 1913 } 1914 } 1915 } 1916 1917 void 1918 g_AB(int, char**) 1919 { 1920 closel(); 1921 dohangingcenter(); 1922 Bprint(&bout, "<center><H4>ABSTRACT</H4></center><DL><DD>\n"); 1923 } 1924 1925 void 1926 g_AE(int, char**) 1927 { 1928 Bprint(&bout, "</DL>\n"); 1929 } 1930 1931 void 1932 g_FS(int, char **) 1933 { 1934 char *argv[3]; 1935 1936 argv[0] = "IP"; 1937 argv[1] = nil; 1938 argv[2] = nil; 1939 g_IP(1, argv); 1940 Bprint(&bout, "NOTE:<I> "); 1941 } 1942 1943 void 1944 g_FE(int, char **) 1945 { 1946 Bprint(&bout, "</I><DT> <DD>"); 1947 closel(); 1948 Bprint(&bout, "<br>\n"); 1949 } 1950 1951 void 1952 g_de(int argc, char **argv) 1953 { 1954 int r; 1955 char *p, *cp; 1956 String *m; 1957 int len; 1958 1959 if(argc < 2) 1960 return; 1961 1962 m = nil; 1963 len = 0; 1964 if(strcmp(argv[0], "am") == 0){ 1965 for(m = macros; m != nil; m = m->next) 1966 if(strcmp(argv[1], m->name) == 0){ 1967 len = strlen(m->val); 1968 break; 1969 } 1970 1971 if(m == nil){ 1972 /* nothing to append to */ 1973 for(;;){ 1974 p = Brdline(&ssp->in, '\n'); 1975 if(p == nil) 1976 break; 1977 p[Blinelen(&ssp->in)-1] = 0; 1978 if(strcmp(p, "..") == 0) 1979 break; 1980 } 1981 return; 1982 } 1983 } 1984 1985 if(m == nil){ 1986 m = emalloc(sizeof(*m)); 1987 m->next = macros; 1988 macros = m; 1989 m->name = strdup(argv[1]); 1990 m->val = nil; 1991 len = 0; 1992 } 1993 1994 /* read up to a .. removing double backslashes */ 1995 for(;;){ 1996 p = Brdline(&ssp->in, '\n'); 1997 if(p == nil) 1998 break; 1999 p[Blinelen(&ssp->in)-1] = 0; 2000 if(strcmp(p, "..") == 0) 2001 break; 2002 m->val = realloc(m->val, len + Blinelen(&ssp->in)+1); 2003 cp = m->val + len; 2004 while(*p){ 2005 r = *p++; 2006 if(r == '\\' && *p == '\\') 2007 p++; 2008 *cp++ = r; 2009 } 2010 *cp++ = '\n'; 2011 len = cp - m->val; 2012 *cp = 0; 2013 } 2014 } 2015 2016 void 2017 g_hrule(int, char**) 2018 { 2019 Bprint(&bout, "<HR>\n"); 2020 } 2021 2022 void 2023 g_BX(int argc, char **argv) 2024 { 2025 Bprint(&bout, "<HR>\n"); 2026 printargs(argc, argv); 2027 Bprint(&bout, "<HR>\n"); 2028 } 2029 2030 void 2031 g_IH(int, char**) 2032 { 2033 Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n"); 2034 } 2035 2036 void 2037 g_MH(int, char**) 2038 { 2039 Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n"); 2040 } 2041 2042 void 2043 g_PY(int, char**) 2044 { 2045 Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n"); 2046 } 2047 2048 void 2049 g_HO(int, char**) 2050 { 2051 Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n"); 2052 } 2053 2054 void 2055 g_QS(int, char**) 2056 { 2057 Bprint(&bout, "<BLOCKQUOTE>\n"); 2058 } 2059 2060 void 2061 g_QE(int, char**) 2062 { 2063 Bprint(&bout, "</BLOCKQUOTE>\n"); 2064 } 2065 2066 void 2067 g_RS(int, char**) 2068 { 2069 Bprint(&bout, "<DL><DD>\n"); 2070 } 2071 2072 void 2073 g_RE(int, char**) 2074 { 2075 Bprint(&bout, "</DL>\n"); 2076 } 2077 2078 int gif; 2079 2080 void 2081 g_startgif(int, char **argv) 2082 { 2083 int fd; 2084 int pfd[2]; 2085 char *e, *p; 2086 char name[32]; 2087 Dir *d; 2088 2089 if(strcmp(argv[0], "EQ") == 0) 2090 e = ".EN"; 2091 else if(strcmp(argv[0], "TS") == 0) 2092 e = ".TE"; 2093 else if(strcmp(argv[0], "PS") == 0) 2094 e = ".PE"; 2095 else 2096 return; 2097 2098 if(basename) 2099 p = basename; 2100 else{ 2101 p = strrchr(sstack[0].filename, '/'); 2102 if(p != nil) 2103 p++; 2104 else 2105 p = sstack[0].filename; 2106 } 2107 snprint(name, sizeof(name), "%s.%d.gif", p, gif++); 2108 fd = create(name, OWRITE, 0664); 2109 if(fd < 0){ 2110 fprint(2, "ms2html: can't create %s: %r\n", name); 2111 return; 2112 } 2113 2114 if(pipe(pfd) < 0){ 2115 fprint(2, "ms2html: can't create pipe: %r\n"); 2116 close(fd); 2117 return; 2118 } 2119 switch(rfork(RFFDG|RFPROC)){ 2120 case -1: 2121 fprint(2, "ms2html: can't fork: %r\n"); 2122 close(fd); 2123 return; 2124 case 0: 2125 dup(fd, 1); 2126 close(fd); 2127 dup(pfd[0], 0); 2128 close(pfd[0]); 2129 close(pfd[1]); 2130 execl("/bin/troff2gif", "troff2gif", nil); 2131 fprint(2, "ms2html: couldn't exec troff2gif: %r\n"); 2132 _exits(nil); 2133 default: 2134 close(fd); 2135 close(pfd[0]); 2136 fprint(pfd[1], ".ll 7i\n"); 2137 /* fprint(pfd[1], ".EQ\ndelim %s\n.EN\n", delim); */ 2138 /* fprint(pfd[1], ".%s\n", argv[0]); */ 2139 for(;;){ 2140 p = Brdline(&ssp->in, '\n'); 2141 if(p == nil) 2142 break; 2143 ssp->lno++; 2144 ssp->rlno++; 2145 if(write(pfd[1], p, Blinelen(&ssp->in)) < 0) 2146 break; 2147 if(strncmp(p, e, 3) == 0) 2148 break; 2149 } 2150 close(pfd[1]); 2151 waitpid(); 2152 d = dirstat(name); 2153 if(d == nil) 2154 break; 2155 if(d->length == 0){ 2156 remove(name); 2157 free(d); 2158 break; 2159 } 2160 free(d); 2161 fprint(2, "ms2html: created auxiliary file %s\n", name); 2162 Bprint(&bout, "<br><img src=\"%s\"><br>\n", name); 2163 break; 2164 } 2165 } 2166 2167 void 2168 g_lf(int argc, char **argv) 2169 { 2170 if(argc > 2) 2171 snprint(ssp->filename, sizeof(ssp->filename), argv[2]); 2172 if(argc > 1) 2173 ssp->rlno = atoi(argv[1]); 2174 } 2175 2176 void 2177 g_so(int argc, char **argv) 2178 { 2179 ssp->lno++; 2180 ssp->rlno++; 2181 if(argc > 1) 2182 pushsrc(argv[1]); 2183 } 2184 2185 2186 void 2187 g_BP(int argc, char **argv) 2188 { 2189 int fd; 2190 char *p, *ext; 2191 char name[32]; 2192 Dir *d; 2193 2194 if(argc < 2) 2195 return; 2196 2197 p = strrchr(argv[1], '/'); 2198 if(p != nil) 2199 p++; 2200 else 2201 p = argv[1]; 2202 2203 2204 ext = strrchr(p, '.'); 2205 if(ext){ 2206 if(strcmp(ext, ".jpeg") == 0 2207 || strcmp(ext, ".gif") == 0){ 2208 Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]); 2209 return; 2210 } 2211 } 2212 2213 2214 snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++); 2215 fd = create(name, OWRITE, 0664); 2216 if(fd < 0){ 2217 fprint(2, "ms2html: can't create %s: %r\n", name); 2218 return; 2219 } 2220 2221 switch(rfork(RFFDG|RFPROC)){ 2222 case -1: 2223 fprint(2, "ms2html: can't fork: %r\n"); 2224 close(fd); 2225 return; 2226 case 0: 2227 dup(fd, 1); 2228 close(fd); 2229 execl("/bin/ps2gif", "ps2gif", argv[1], nil); 2230 fprint(2, "ms2html: couldn't exec ps2gif: %r\n"); 2231 _exits(nil); 2232 default: 2233 close(fd); 2234 waitpid(); 2235 d = dirstat(name); 2236 if(d == nil) 2237 break; 2238 if(d->length == 0){ 2239 remove(name); 2240 free(d); 2241 break; 2242 } 2243 free(d); 2244 fprint(2, "ms2html: created auxiliary file %s\n", name); 2245 Bprint(&bout, "<br><img src=\"%s\"><br>\n", name); 2246 break; 2247 } 2248 } 2249 2250 /* insert straight HTML into output */ 2251 void 2252 g__H(int argc, char **argv) 2253 { 2254 int i; 2255 2256 for(i = 1; i < argc; i++) 2257 Bprint(&bout, "%s ", argv[i]); 2258 Bprint(&bout, "\n"); 2259 } 2260 2261 /* HTML page title */ 2262 void 2263 g__T(int argc, char **argv) 2264 { 2265 if(titleseen) 2266 return; 2267 2268 Bprint(&bout, "<title>\n"); 2269 printargs(argc, argv); 2270 Bprint(&bout, "</title></head><body>\n"); 2271 titleseen = 1; 2272 } 2273 2274 void 2275 g_nr(int argc, char **argv) 2276 { 2277 char *val; 2278 2279 if (argc > 1) { 2280 if (argc == 2) 2281 val = "0"; 2282 else 2283 val = argv[2]; 2284 dsnr(argv[1], val, &numregs); 2285 } 2286 } 2287 2288 void 2289 zerodivide(void) 2290 { 2291 fprint(2, "stdin %d(%s:%d): division by 0\n", 2292 ssp->lno, ssp->filename, ssp->rlno); 2293 } 2294 2295 int 2296 numval(char **pline, int recur) 2297 { 2298 char *p; 2299 int neg, x, y; 2300 2301 x = neg = 0; 2302 p = *pline; 2303 while(*p == '-') { 2304 neg = 1 - neg; 2305 p++; 2306 } 2307 if (*p == '(') { 2308 p++; 2309 x = numval(&p, 1); 2310 if (*p != ')') 2311 goto done; 2312 p++; 2313 } 2314 else while(*p >= '0' && *p <= '9') 2315 x = 10*x + *p++ - '0'; 2316 if (neg) 2317 x = -x; 2318 if (recur) 2319 for(;;) { 2320 switch(*p++) { 2321 case '+': 2322 x += numval(&p, 0); 2323 continue; 2324 case '-': 2325 x -= numval(&p, 0); 2326 continue; 2327 case '*': 2328 x *= numval(&p, 0); 2329 continue; 2330 case '/': 2331 y = numval(&p, 0); 2332 if (y == 0) { 2333 zerodivide(); 2334 x = 0; 2335 goto done; 2336 } 2337 x /= y; 2338 continue; 2339 case '<': 2340 if (*p == '=') { 2341 p++; 2342 x = x <= numval(&p, 0); 2343 continue; 2344 } 2345 x = x < numval(&p, 0); 2346 continue; 2347 case '>': 2348 if (*p == '=') { 2349 p++; 2350 x = x >= numval(&p, 0); 2351 continue; 2352 } 2353 x = x > numval(&p, 0); 2354 continue; 2355 case '=': 2356 if (*p == '=') 2357 p++; 2358 x = x == numval(&p, 0); 2359 continue; 2360 case '&': 2361 x &= numval(&p, 0); 2362 continue; 2363 case ':': 2364 x |= numval(&p, 0); 2365 continue; 2366 case '%': 2367 y = numval(&p, 0); 2368 if (!y) { 2369 zerodivide(); 2370 goto done; 2371 } 2372 x %= y; 2373 continue; 2374 } 2375 --p; 2376 break; 2377 } 2378 done: 2379 *pline = p; 2380 return x; 2381 } 2382 2383 int 2384 iftest(char *p, char **bp) 2385 { 2386 char *p1; 2387 int c, neg, rv; 2388 2389 rv = neg = 0; 2390 if (*p == '!') { 2391 neg = 1; 2392 p++; 2393 } 2394 c = *p; 2395 if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) { 2396 if (numval(&p,1) >= 1) 2397 rv = 1; 2398 goto done; 2399 } 2400 switch(c) { 2401 case 't': 2402 case 'o': 2403 rv = 1; 2404 case 'n': 2405 case 'e': 2406 p++; 2407 goto done; 2408 } 2409 for(p1 = ++p; *p != c; p++) 2410 if (!*p) 2411 goto done; 2412 for(p++;;) { 2413 if (*p != *p1++) { 2414 while(*p && *p++ != c); 2415 goto done; 2416 } 2417 if (*p++ == c) 2418 break; 2419 } 2420 rv = 1; 2421 done: 2422 if (neg) 2423 rv = 1 - rv; 2424 while(*p == ' ' || *p == '\t') 2425 p++; 2426 *bp = p; 2427 return rv; 2428 } 2429 2430 void 2431 scanline(char *p, char *e, int wantnl) 2432 { 2433 int c; 2434 Rune r; 2435 2436 while((c = getrune()) == ' ' || c == '\t') ; 2437 while(p < e) { 2438 if (c < 0) 2439 break; 2440 if (c < Runeself) { 2441 if (c == '\n') { 2442 if (wantnl) 2443 *p++ = c; 2444 break; 2445 } 2446 *p++ = c; 2447 } 2448 else { 2449 r = c; 2450 p += runetochar(p, &r); 2451 } 2452 c = getrune(); 2453 } 2454 *p = 0; 2455 } 2456 2457 void 2458 pushbody(char *line) 2459 { 2460 char *b; 2461 2462 if (line[0] == '\\' && line[1] == '{' /*}*/ ) 2463 line += 2; 2464 if (strsp < Maxmstack - 1) { 2465 pushstr(b = strdup(line)); 2466 mustfree[strsp] = b; 2467 } 2468 } 2469 2470 void 2471 skipbody(char *line) 2472 { 2473 int c, n; 2474 2475 if (line[0] != '\\' || line[1] != '{' /*}*/ ) 2476 return; 2477 for(n = 1;;) { 2478 while((c = getrune()) != '\\') 2479 if (c < 0) 2480 return; 2481 c = getrune(); 2482 if (c == '{') 2483 n++; 2484 else if ((c == '}' && (c = getrune()) == '\n' && !--n) 2485 || c < 0) 2486 return; 2487 } 2488 } 2489 2490 int 2491 ifstart(char *line, char *e, char **bp) 2492 { 2493 int it; 2494 char *b; 2495 2496 b = copyline(line, e, 1); 2497 ungetrune(); 2498 b[-1] = getrune(); 2499 scanline(b, e, 1); 2500 it = iftest(line, bp); 2501 return it; 2502 } 2503 2504 void 2505 g_ie(char *line, char *e) 2506 { 2507 char *b; 2508 2509 if (elsetop >= Maxif-1) { 2510 fprint(2, "ms2html: .ie's too deep\n"); 2511 return; 2512 } 2513 if (ifwastrue[++elsetop] = ifstart(line, e, &b)) 2514 pushbody(b); 2515 else 2516 skipbody(b); 2517 } 2518 2519 void 2520 g_if(char *line, char *e) 2521 { 2522 char *b; 2523 2524 if (ifstart(line, e, &b)) 2525 pushbody(b); 2526 else 2527 skipbody(b); 2528 } 2529 2530 void 2531 g_el(char *line, char *e) 2532 { 2533 if (elsetop < 0) 2534 return; 2535 scanline(line, e, 1); 2536 if (ifwastrue[elsetop--]) 2537 skipbody(line); 2538 else 2539 pushbody(line); 2540 } 2541 2542 void 2543 g_ig(int argc, char **argv) 2544 { 2545 char *p, *s; 2546 2547 s = ".."; 2548 if (argc > 1) 2549 s = argv[1]; 2550 for(;;) { 2551 p = Brdline(&ssp->in, '\n'); 2552 if(p == nil) 2553 break; 2554 p[Blinelen(&ssp->in)-1] = 0; 2555 if(strcmp(p, s) == 0) 2556 break; 2557 } 2558 } 2559 2560 void 2561 g_ds(char *line, char *e) 2562 { 2563 char *b; 2564 2565 b = copyline(line, e, 1); 2566 if (b > line) { 2567 copyline(b, e, 0); 2568 if (*b == '"') 2569 b++; 2570 ds(line, b); 2571 } 2572 } 2573 2574 void 2575 g_as(char *line, char *e) 2576 { 2577 String *s; 2578 char *b; 2579 2580 b = copyline(line, e, 1); 2581 if (b == line) 2582 return; 2583 copyline(b, e, 0); 2584 if (*b == '"') 2585 b++; 2586 for(s = strings; s != nil; s = s->next) 2587 if(strcmp(line, s->name) == 0) 2588 break; 2589 2590 if(s == nil){ 2591 ds(line, b); 2592 return; 2593 } 2594 2595 s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1); 2596 strcat(s->val, b); 2597 } 2598 2599 void 2600 g_BS(int argc, char **argv) 2601 { 2602 int i; 2603 2604 if (argc > 1 && !weBref) { 2605 Bprint(&bout, "<a href=\"%s\"", argv[1]); 2606 for(i = 2; i < argc; i++) 2607 Bprint(&bout, " %s", argv[i]); 2608 Bprint(&bout, ">"); 2609 weBref = 1; 2610 } 2611 } 2612 2613 void 2614 g_BE(int, char**) 2615 { 2616 if (weBref) { 2617 Bprint(&bout, "</a>"); 2618 weBref = 0; 2619 } 2620 } 2621 2622 void 2623 g_LB(int argc, char **argv) 2624 { 2625 if (argc > 1) { 2626 if (weBref) 2627 g_BE(0,nil); 2628 Bprint(&bout, "<a name=\"%s\"></a>", argv[1]); 2629 } 2630 } 2631 2632 void 2633 g_RT(int, char**) 2634 { 2635 g_BE(0,nil); 2636 dohanginghead(); 2637 closel(); 2638 closefont(); 2639 } 2640