1implement Cook; 2 3include "sys.m"; 4 sys: Sys; 5 FD: import Sys; 6 7include "draw.m"; 8 draw: Draw; 9 10include "bufio.m"; 11 B: Bufio; 12 Iobuf: import B; 13 14include "string.m"; 15 S: String; 16 splitl, splitr, splitstrl, drop, take, in, prefix, tolower : import S; 17 18include "brutus.m"; 19 Size6, Size8, Size10, Size12, Size16, NSIZE, 20 Roman, Italic, Bold, Type, NFONT, NFONTTAG, 21 Example, Caption, List, Listelem, Label, Labelref, 22 Exercise, Heading, Nofill, Author, Title, 23 Index, Indextopic, 24 DefFont, DefSize, TitleFont, TitleSize, HeadingFont, HeadingSize: import Brutus; 25 26# following are needed for types in brutusext.m 27include "tk.m"; 28 tk: Tk; 29include "tkclient.m"; 30 31include "brutusext.m"; 32 SGML, Text, Par, Extension, Float, Special, Celem, 33 FLatex, FLatexProc, FLatexBook, FLatexPart, FLatexSlides, FHtml: import Brutusext; 34 35include "strinttab.m"; 36 T: StringIntTab; 37 38Cook: module 39{ 40 init: fn(ctxt: ref Draw->Context, args: list of string); 41}; 42 43# keep this sorted by name 44tagstringtab := array[] of { T->StringInt 45 ("Author", Author), 46 ("Bold.10", Bold*NSIZE + Size10), 47 ("Bold.12", Bold*NSIZE + Size12), 48 ("Bold.16", Bold*NSIZE + Size16), 49 ("Bold.6", Bold*NSIZE + Size6), 50 ("Bold.8", Bold*NSIZE + Size8), 51 ("Caption", Caption), 52 ("Example", Example), 53 ("Exercise", Exercise), 54 ("Extension", Extension), 55 ("Float", Float), 56 ("Heading", Heading), 57 ("Index", Index), 58 ("Index-topic", Indextopic), 59 ("Italic.10", Italic*NSIZE + Size10), 60 ("Italic.12", Italic*NSIZE + Size12), 61 ("Italic.16", Italic*NSIZE + Size16), 62 ("Italic.6", Italic*NSIZE + Size6), 63 ("Italic.8", Italic*NSIZE + Size8), 64 ("Label", Label), 65 ("Label-ref", Labelref), 66 ("List", List), 67 ("List-elem", Listelem), 68 ("No-fill", Nofill), 69 ("Par", Par), 70 ("Roman.10", Roman*NSIZE + Size10), 71 ("Roman.12", Roman*NSIZE + Size12), 72 ("Roman.16", Roman*NSIZE + Size16), 73 ("Roman.6", Roman*NSIZE + Size6), 74 ("Roman.8", Roman*NSIZE + Size8), 75 ("SGML", SGML), 76 ("Title", Title), 77 ("Type.10", Type*NSIZE + Size10), 78 ("Type.12", Type*NSIZE + Size12), 79 ("Type.16", Type*NSIZE + Size16), 80 ("Type.6", Type*NSIZE + Size6), 81 ("Type.8", Type*NSIZE + Size8), 82}; 83 84# This table must be sorted 85fmtstringtab := array[] of { T->StringInt 86 ("html", FHtml), 87 ("latex", FLatex), 88 ("latexbook", FLatexBook), 89 ("latexpart", FLatexPart), 90 ("latexproc", FLatexProc), 91 ("latexslides", FLatexSlides), 92}; 93 94Transtab: adt 95{ 96 ch: int; 97 trans: string; 98}; 99 100# Order doesn't matter for these table 101 102ltranstab := array[] of { Transtab 103 ('$', "\\textdollar{}"), 104 ('&', "\\&"), 105 ('%', "\\%"), 106 ('#', "\\#"), 107 ('_', "\\textunderscore{}"), 108 ('{', "\\{"), 109 ('}', "\\}"), 110 ('~', "\\textasciitilde{}"), 111 ('^', "\\textasciicircum{}"), 112 ('\\', "\\textbackslash{}"), 113 ('+', "\\textplus{}"), 114 ('=', "\\textequals{}"), 115 ('|', "\\textbar{}"), 116 ('<', "\\textless{}"), 117 ('>', "\\textgreater{}"), 118 (' ', "~"), 119 ('-', "-"), # needs special case ligature treatment 120 ('\t', " "), # needs special case treatment 121}; 122 123htranstab := array[] of { Transtab 124 ('α', "α"), 125 ('Æ', "Æ"), 126 ('Á', "Á"), 127 ('Â', "Â"), 128 ('À', "À"), 129 ('Å', "Å"), 130 ('Ã', "Ã"), 131 ('Ä', "Ä"), 132 ('Ç', "Ç"), 133 ('Ð', "Ð"), 134 ('É', "É"), 135 ('Ê', "Ê"), 136 ('È', "È"), 137 ('Ë', "Ë"), 138 ('Í', "Í"), 139 ('Î', "Î"), 140 ('Ì', "Ì"), 141 ('Ï', "Ï"), 142 ('Ñ', "Ñ"), 143 ('Ó', "Ó"), 144 ('Ô', "Ô"), 145 ('Ò', "Ò"), 146 ('Ø', "Ø"), 147 ('Õ', "Õ"), 148 ('Ö', "Ö"), 149 ('Þ', "Þ"), 150 ('Ú', "Ú"), 151 ('Û', "Û"), 152 ('Ù', "Ù"), 153 ('Ü', "Ü"), 154 ('Ý', "Ý"), 155 ('æ', "&aElig;"), 156 ('á', "á"), 157 ('â', "â"), 158 ('à', "à"), 159 ('α', "α"), 160 ('&', "&"), 161 ('å', "å"), 162 ('ã', "ã"), 163 ('ä', "ä"), 164 ('β', "β"), 165 ('ç', "ç"), 166 ('⋯', "&cdots;"), 167 ('χ', "χ"), 168 ('©', "©"), 169 ('⋱', "&ddots;"), 170 ('δ', "δ"), 171 ('é', "é"), 172 ('ê', "ê"), 173 ('è', "è"), 174 ('—', "&emdash;"), 175 (' ', " "), 176 ('–', "&endash;"), 177 ('ε', "ε"), 178 ('η', "η"), 179 ('ð', "ð"), 180 ('ë', "ë"), 181 ('γ', "γ"), 182 ('>', ">"), 183 ('í', "í"), 184 ('î', "î"), 185 ('ì', "ì"), 186 ('ι', "ι"), 187 ('ï', "ï"), 188 ('κ', "κ"), 189 ('λ', "λ"), 190 ('…', "&ldots;"), 191 ('<', "<"), 192 ('μ', "μ"), 193 (' ', " "), 194 ('ñ', "ñ"), 195 ('ν', "ν"), 196 ('ó', "ó"), 197 ('ô', "ô"), 198 ('ò', "ò"), 199 ('ω', "ω"), 200 ('ο', "ο"), 201 ('ø', "ø"), 202 ('õ', "õ"), 203 ('ö', "ö"), 204 ('φ', "φ"), 205 ('π', "π"), 206 ('ψ', "ψ"), 207 (' ', "&quad;"), 208 ('"', """), 209 ('®', "®"), 210 ('ρ', "ρ"), 211 ('', "­"), 212 ('σ', "σ"), 213 ('ß', "ß"), 214 ('τ', "τ"), 215 ('θ', "θ"), 216 (' ', " "), 217 ('þ', "þ"), 218 ('™', "™"), 219 ('ú', "ú"), 220 ('û', "û"), 221 ('ù', "ù"), 222 ('υ', "υ"), 223 ('ü', "ü"), 224 ('∈', "ϵ"), 225 ('ϕ', "ϕ"), 226 ('ϖ', "ϖ"), 227 ('ϱ', "ϱ"), 228 ('⋮', "&vdots;"), 229 ('ς', "&vsigma;"), 230 ('ϑ', "&vtheta;"), 231 ('ξ', "ξ"), 232 ('ý', "ý"), 233 ('ÿ', "ÿ"), 234 ('ζ', "ζ"), 235 ('−', "-"), 236}; 237 238# For speedy lookups of ascii char translation, use asciitrans. 239# It should be initialized by ascii elements from one of above tables 240asciitrans := array[128] of string; 241 242stderr: ref FD; 243infilename := ""; 244outfilename := ""; 245linenum := 0; 246fin : ref Iobuf = nil; 247fout : ref Iobuf = nil; 248debug := 0; 249fmt := FLatex; 250 251init(nil: ref Draw->Context, argv: list of string) 252{ 253 sys = load Sys Sys->PATH; 254 S = load String String->PATH; 255 B = load Bufio Bufio->PATH; 256 draw = load Draw Draw->PATH; 257 tk = load Tk Tk->PATH; 258 T = load StringIntTab StringIntTab->PATH; 259 stderr = sys->fildes(2); 260 261 for(argv = tl argv; argv != nil; ) { 262 s := hd argv; 263 tlargv := tl argv; 264 case s { 265 "-f" => 266 if(tlargv == nil) 267 usage(); 268 fnd: int; 269 (fnd, fmt) = T->lookup(fmtstringtab, hd(tlargv)); 270 if(!fnd) { 271 sys->fprint(stderr, "unknown format: %s\n", hd(tlargv)); 272 exit; 273 } 274 argv = tlargv; 275 "-o" => 276 if(tlargv == nil) 277 usage(); 278 outfilename = hd(tlargv); 279 argv = tlargv; 280 "-d" => 281 debug = 1; 282 "-dd" => 283 debug = 2; 284 * => 285 if(tlargv == nil) 286 infilename = s; 287 else 288 usage(); 289 } 290 argv = tl argv; 291 } 292 if(infilename == "") { 293 fin = B->fopen(sys->fildes(0), sys->OREAD); 294 infilename = "<stdin>"; 295 } 296 else 297 fin = B->open(infilename, sys->OREAD); 298 if(fin == nil) { 299 sys->fprint(stderr, "cook: error opening %s: %r\n", infilename); 300 exit; 301 } 302 if(outfilename == "") { 303 fout = B->fopen(sys->fildes(1), sys->OWRITE); 304 outfilename = "<stdout>"; 305 } 306 else 307 fout = B->create(outfilename, sys->OWRITE, 8r664); 308 if(fout == nil) { 309 sys->fprint(stderr, "cook: error creating %s: %r\n", outfilename); 310 exit; 311 } 312 line0 := fin.gets('\n'); 313 if(line0 != "<SGML>\n") { 314 parse_err("not an SGML file\n"); 315 exit; 316 } 317 linenum = 1; 318 e := parse(SGML); 319 findpars(e, 1, nil); 320 e = delemptystrs(e); 321 (e, nil) = canonfonts(e, DefFont*NSIZE+DefSize, DefFont*NSIZE+DefSize); 322 mergeadjs(e); 323 findfloats(e); 324 cleanexts(e); 325 cleanpars(e); 326 if(debug) { 327 fout.puts("After Initial transformations:\n"); 328 printelem(e, "", 1); 329 fout.flush(); 330 } 331 case fmt { 332 FLatex or FLatexProc or FLatexBook or FLatexPart or FLatexSlides => 333 latexconv(e); 334 FHtml => 335 htmlconv(e); 336 } 337 fin.close(); 338 fout.close(); 339} 340 341usage() 342{ 343 sys->fprint(stderr, "Usage: cook [-f (latex|html)] [-o outfile] [infile]\n"); 344 exit; 345} 346 347parse_err(msg: string) 348{ 349 sys->fprint(stderr, "%s:%d: %s\n", infilename, linenum, msg); 350} 351 352# Parse into elements. 353# Assumes tags are balanced. 354# String elements are split so that there is never an internal newline. 355parse(id: int) : ref Celem 356{ 357 els : ref Celem = nil; 358 elstail : ref Celem = nil; 359 for(;;) { 360 c := fin.getc(); 361 if(c == Bufio->EOF) { 362 if(id == SGML) 363 break; 364 else { 365 parse_err(sys->sprint("EOF while parsing %s", tagname(id))); 366 return nil; 367 } 368 } 369 if(c == '<') { 370 tag := ""; 371 start := 1; 372 i := 0; 373 for(;;) { 374 c = fin.getc(); 375 if(c == Bufio->EOF) { 376 parse_err("EOF in middle of tag"); 377 return nil; 378 } 379 if(c == '\n') { 380 linenum++; 381 parse_err("newline in middle of tag"); 382 break; 383 } 384 if(c == '>') 385 break; 386 if(i == 0 && c == '/') 387 start = 0; 388 else 389 tag[i++] = c; 390 } 391 (fnd, tid) := T->lookup(tagstringtab, tag); 392 if(!fnd) { 393 if(prefix("Extension ", tag)) { 394 el := ref Celem(Extension, tag[10:], nil, nil, nil, nil); 395 if(els == nil) { 396 els = el; 397 elstail = el; 398 } 399 else { 400 el.prev = elstail; 401 elstail.next = el; 402 elstail = el; 403 } 404 } 405 else 406 parse_err(sys->sprint("unknown tag <%s>\n", tag)); 407 continue; 408 } 409 if(start) { 410 el := parse(tid); 411 if(el == nil) 412 return nil; 413 if(els == nil) { 414 els = el; 415 elstail = el; 416 } 417 else { 418 el.prev = elstail; 419 elstail.next = el; 420 elstail = el; 421 } 422 } 423 else { 424 if(tid != id) { 425 parse_err(sys->sprint("<%s> ended by </%s>", 426 tagname(id), tag)); 427 continue; 428 } 429 break; 430 } 431 } 432 else { 433 s := ""; 434 i := 0; 435 for(;;) { 436 if(c == Bufio->EOF) 437 break; 438 if(c == '<') { 439 fin.ungetc(); 440 break; 441 } 442 if(c == ';' && i >=3 && s[i-1] == 't' && s[i-2] == 'l' && s[i-3] == '&') { 443 i -= 2; 444 s[i-1] = '<'; 445 s = s[0:i]; 446 } 447 else 448 s[i++] = c; 449 if(c == '\n') { 450 linenum++; 451 break; 452 } 453 else 454 c = fin.getc(); 455 } 456 if(s != "") { 457 el := ref Celem(Text, s, nil, nil, nil, nil); 458 if(els == nil) { 459 els = el; 460 elstail = el; 461 } 462 else { 463 el.prev = elstail; 464 elstail.next = el; 465 elstail = el; 466 } 467 } 468 } 469 } 470 ans := ref Celem(id, "", els, nil, nil, nil); 471 if(els != nil) 472 els.parent = ans; 473 return ans; 474} 475 476# Modify tree e so that blank lines become Par elements. 477# Only do it if parize is set, and unset parize when descending into TExample's. 478# Pass in most recent TString or TPar element, and return updated most-recent-TString/TPar. 479# This function may set some TString strings to "" 480findpars(e: ref Celem, parize: int, prevspe: ref Celem) : ref Celem 481{ 482 while(e != nil) { 483 prevnl := 0; 484 prevpar := 0; 485 if(prevspe != nil) { 486 if(prevspe.tag == Text && len prevspe.s != 0 487 && prevspe.s[(len prevspe.s)-1] == '\n') 488 prevnl = 1; 489 else if(prevspe.tag == Par) 490 prevpar = 1; 491 } 492 if(e.tag == Text) { 493 if(parize && (prevnl || prevpar) && e.s[0] == '\n') { 494 if(prevnl) 495 prevspe.s = prevspe.s[0 : (len prevspe.s)-1]; 496 e.tag = Par; 497 e.s = nil; 498 } 499 prevspe = e; 500 } 501 else { 502 nparize := parize; 503 if(e.tag == Example) 504 nparize = 0; 505 prevspe = findpars(e.contents, nparize, prevspe); 506 } 507 e = e.next; 508 } 509 return prevspe; 510} 511 512# Delete any empty strings from e's tree and return modified e. 513# Also, delete any entity that has empty contents, except the 514# Par ones 515delemptystrs(e: ref Celem) : ref Celem 516{ 517 if(e.tag == Text) { 518 if(e.s == "") 519 return nil; 520 else 521 return e; 522 } 523 if(e.tag == Par || e.tag == Extension || e.tag == Special) 524 return e; 525 h := e.contents; 526 while(h != nil) { 527 hnext := h.next; 528 hh := delemptystrs(h); 529 if(hh == nil) 530 delete(h); 531 h = hnext; 532 } 533 if(e.contents == nil) 534 return nil; 535 return e; 536} 537 538# Change tree under e so that any font elems contain only strings 539# (by pushing the font changes down). 540# Answer an be a list, so return beginning and end of list. 541# Leave strings bare if font change would be to deffont, 542# and adjust deffont appropriately when entering Title and 543# Heading environments. 544canonfonts(e: ref Celem, curfont, deffont: int) : (ref Celem, ref Celem) 545{ 546 f := curfont; 547 head : ref Celem = nil; 548 tail : ref Celem = nil; 549 tocombine : ref Celem = nil; 550 if(e.tag == Text) { 551 if(f == deffont) { 552 head = e; 553 tail = e; 554 } 555 else { 556 head = ref Celem(f, nil, e, nil, nil, nil); 557 e.parent = head; 558 tail = head; 559 } 560 } 561 else if(e.contents == nil) { 562 head = e; 563 tail = e; 564 } 565 else if(e.tag < NFONTTAG) { 566 f = e.tag; 567 allstrings := 1; 568 for(g := e.contents; g != nil; g = g.next) { 569 if(g.tag != Text) 570 allstrings = 0; 571 tail = g; 572 } 573 if(allstrings) { 574 if(f == deffont) 575 head = e.contents; 576 else { 577 head = e; 578 tail = e; 579 } 580 } 581 } 582 if(head == nil) { 583 if(e.tag == Title) 584 deffont = TitleFont*NSIZE+TitleSize; 585 else if(e.tag == Heading) 586 deffont = HeadingFont*NSIZE+HeadingSize; 587 for(h := e.contents; h != nil; ) { 588 prev := h.prev; 589 next := h.next; 590 excise(h); 591 (e1, en) := canonfonts(h, f, deffont); 592 splicebetween(e1, en, prev, next); 593 if(prev == nil) 594 head = e1; 595 tail = en; 596 h = next; 597 } 598 tocombine = head; 599 if(e.tag >= NFONTTAG) { 600 e.contents = head; 601 head.parent = e; 602 head = e; 603 tail = e; 604 } 605 } 606 if(tocombine != nil) { 607 # combine adjacent font changes to same font 608 r := tocombine; 609 while(r != nil) { 610 if(r.tag < NFONTTAG && r.next != nil && r.next.tag == r.tag) { 611 for(v := r.next; v != nil; v = v.next) { 612 if(v.tag != r.tag) 613 break; 614 if(v == tail) 615 tail = r; 616 } 617 # now r up to, not including v, all change to same font 618 for(p := r.next; p != v; p = p.next) { 619 append(r.contents, p.contents); 620 } 621 r.next = v; 622 if(v != nil) 623 v.prev = r; 624 r = v; 625 } 626 else 627 r = r.next; 628 } 629 } 630 head.parent = nil; 631 return (head, tail); 632} 633 634# Remove Pars that appear just before or just after Heading, Title, Examples, Extensions 635# Really should worry about this happening at different nesting levels, but in 636# practice this happens all at the same nesting level 637cleanpars(e: ref Celem) 638{ 639 for(h := e.contents; h != nil; h = h.next) { 640 cleanpars(h); 641 if(h.tag == Title || h.tag == Heading || h.tag == Example || h.tag == Extension) { 642 hp := h.prev; 643 hn := h.next; 644 if(hp !=nil && hp.tag == Par) 645 delete(hp); 646 if(hn != nil && hn.tag == Par) 647 delete(hn); 648 } 649 } 650} 651 652# Remove a single tab if it appears before an Extension 653cleanexts(e: ref Celem) 654{ 655 for(h := e.contents; h != nil; h = h.next) { 656 cleanexts(h); 657 if(h.tag == Extension) { 658 hp := h.prev; 659 if(hp != nil && stringof(hp) == "\t") 660 delete(hp); 661 } 662 } 663} 664 665mergeable := array[] of { List, Exercise, Caption,Index, Indextopic }; 666 667# Merge some adjacent elements (which were probably created separate 668# because of font changes) 669mergeadjs(e: ref Celem) 670{ 671 for(h := e.contents; h != nil; h = h.next) { 672 hn := h.next; 673 domerge := 0; 674 if(hn != nil) { 675 for(i := 0; i < len mergeable; i++) { 676 mi := mergeable[i]; 677 if(h.tag == mi && hn.tag == mi) 678 domerge = 1; 679 } 680 } 681 if(domerge) { 682 append(h.contents, hn.contents); 683 delete(hn); 684 } 685 else 686 mergeadjs(h); 687 } 688} 689 690# Find floats: they are paragraphs with Captions at the end. 691findfloats(e: ref Celem) 692{ 693 lastpar : ref Celem = nil; 694 for(h := e.contents; h != nil; h = h.next) { 695 if(h.tag == Par) 696 lastpar = h; 697 else if(h.tag == Caption) { 698 ne := ref Celem(Float, "", nil, nil, nil, nil); 699 if(lastpar == nil) 700 flhead := e.contents; 701 else 702 flhead = lastpar.next; 703 insertbefore(ne, flhead); 704 # now move flhead ... h into contents of ne 705 ne.contents = flhead; 706 flhead.parent = ne; 707 flhead.prev = nil; 708 ne.next = h.next; 709 if(ne.next != nil) 710 ne.next.prev = ne; 711 h.next = nil; 712 h = ne; 713 } 714 else 715 findfloats(h); 716 } 717} 718 719insertbefore(e, ebefore: ref Celem) 720{ 721 e.prev = ebefore.prev; 722 if(e.prev == nil) { 723 e.parent = ebefore.parent; 724 ebefore.parent = nil; 725 e.parent.contents = e; 726 } 727 else 728 e.prev.next = e; 729 e.next = ebefore; 730 ebefore.prev = e; 731} 732 733insertafter(e, eafter: ref Celem) 734{ 735 e.next = eafter.next; 736 if(e.next != nil) 737 e.next.prev = e; 738 e.prev = eafter; 739 eafter.next = e; 740} 741 742# remove e from its list, leaving siblings disconnected 743excise(e: ref Celem) 744{ 745 next := e. next; 746 prev := e.prev; 747 e.next = nil; 748 e.prev = nil; 749 if(prev != nil) 750 prev.next = nil; 751 if(next != nil) 752 next.prev = nil; 753 e.parent = nil; 754} 755 756splicebetween(e1, en, prev, next: ref Celem) 757{ 758 if(prev != nil) 759 prev.next = e1; 760 e1.prev = prev; 761 en.next = next; 762 if(next != nil) 763 next.prev = en; 764} 765 766append(e1, e2: ref Celem) 767{ 768 e1last := last(e1); 769 e1last.next = e2; 770 e2.prev = e1last; 771 e2.parent = nil; 772} 773 774last(e: ref Celem) : ref Celem 775{ 776 if(e != nil) 777 while(e.next != nil) 778 e = e.next; 779 return e; 780} 781 782succ(e: ref Celem) : ref Celem 783{ 784 if(e == nil) 785 return nil; 786 if(e.next != nil) 787 return e.next; 788 return succ(e.parent); 789} 790 791delete(e: ref Celem) 792{ 793 ep := e.prev; 794 en := e.next; 795 eu := e.parent; 796 if(ep == nil) { 797 if(eu != nil) 798 eu.contents = en; 799 if(en != nil) 800 en.parent = eu; 801 } 802 else 803 ep.next = en; 804 if(en != nil) 805 en.prev = ep; 806} 807 808# return string represented by e, peering through font changes 809stringof(e: ref Celem) : string 810{ 811 if(e != nil) { 812 if(e.tag == Text) 813 return e.s; 814 if(e.tag < NFONTTAG) 815 return stringof(e.contents); 816 } 817 return ""; 818} 819 820# remove any initial whitespace from e and its sucessors, 821dropwhite(e: ref Celem) 822{ 823 if(e == nil) 824 return; 825 del := 0; 826 if(e.tag == Text) { 827 e.s = drop(e.s, " \t\n"); 828 if(e.s == "") 829 del = 1;; 830 } 831 else if(e.tag < NFONTTAG) { 832 dropwhite(e.contents); 833 if(e.contents == nil) 834 del = 1; 835 } 836 if(del) { 837 enext := e.next; 838 delete(e); 839 dropwhite(enext); 840 } 841 842} 843 844firstchar(e: ref Celem) : int 845{ 846 s := stringof(e); 847 if(len s >= 1) 848 return s[0]; 849 return -1; 850} 851 852lastchar(e: ref Celem) : int 853{ 854 if(e == nil) 855 return -1; 856 while(e.next != nil) 857 e = e.next; 858 s := stringof(e); 859 if(len s >= 1) 860 return s[len s -1]; 861 return -1; 862} 863 864tlookup(t: array of Transtab, v: int) : string 865{ 866 n := len t; 867 for(i := 0; i < n; i++) 868 if(t[i].ch == v) 869 return t[i].trans; 870 return ""; 871} 872 873initasciitrans(t: array of Transtab) 874{ 875 n := len t; 876 for(i := 0; i < n; i++) { 877 c := t[i].ch; 878 if(c < 128) 879 asciitrans[c] = t[i].trans; 880 } 881} 882 883tagname(id: int) : string 884{ 885 name := T->revlookup(tagstringtab, id); 886 if(name == nil) 887 name = "_unknown_"; 888 return name; 889} 890 891printelem(e: ref Celem, indent: string, recurse: int) 892{ 893 fout.puts(indent); 894 if(debug > 1) { 895 fout.puts(sys->sprint("%x: ", e)); 896 if(e != nil && e.parent != nil) 897 fout.puts(sys->sprint("(parent %x): ", e.parent)); 898 } 899 if(e == nil) 900 fout.puts("NIL\n"); 901 else if(e.tag == Text || e.tag == Special || e.tag == Extension) { 902 if(e.tag == Special) 903 fout.puts("S"); 904 else if(e.tag == Extension) 905 fout.puts("E"); 906 fout.puts("«"); 907 fout.puts(e.s); 908 fout.puts("»\n"); 909 } 910 else { 911 name := tagname(e.tag); 912 fout.puts("<" + name + ">\n"); 913 if(recurse && e.contents != nil) 914 printelems(e.contents, indent + " ", recurse); 915 } 916} 917 918printelems(els: ref Celem, indent: string, recurse: int) 919{ 920 for(; els != nil; els = els.next) 921 printelem(els, indent, recurse); 922} 923 924check(e: ref Celem, msg: string) 925{ 926 err := checke(e); 927 if(err != "") { 928 fout.puts(msg + ": tree is inconsistent:\n" + err); 929 printelem(e, "", 1); 930 fout.flush(); 931 exit; 932 } 933} 934 935checke(e: ref Celem) : string 936{ 937 err := ""; 938 if(e.tag == SGML && e.next != nil) 939 err = sys->sprint("root %x has a next field\n", e); 940 ec := e.contents; 941 if(ec != nil) { 942 if(ec.parent != e) 943 err += sys->sprint("node %x contents %x has bad parent %x\n", e, ec, e.parent); 944 if(ec.prev != nil) 945 err += sys->sprint("node %x contents %x has non-nil prev %x\n", e, ec, e.prev); 946 p := ec; 947 for(h := ec.next; h != nil; h = h.next) { 948 if(h.prev != p) 949 err += sys->sprint("node %x comes after %x, but prev is %x\n", h, p, h.prev); 950 if(h.parent != nil) 951 err += sys->sprint("node %x, not first in siblings, has parent %x\n", h, h.parent); 952 p = h; 953 } 954 for(h = ec; h != nil; h = h.next) { 955 err2 := checke(h); 956 if(err2 != nil) 957 err += err2; 958 } 959 } 960 return err; 961} 962 963# Translation to Latex 964 965# state bits 966SLT, SLB, SLI, SLS6, SLS8, SLS12, SLS16, SLE, SLO, SLF : con (1<<iota); 967 968SLFONTMASK : con SLT|SLB|SLI|SLS6|SLS8|SLS12|SLS16; 969SLSIZEMASK : con SLS6|SLS8|SLS12|SLS16; 970 971# fonttag-to-state-bit table 972lftagtostate := array[NFONTTAG] of { 973 Roman*NSIZE+Size6 => SLS6, 974 Roman*NSIZE+Size8 => SLS8, 975 Roman*NSIZE+Size10 => 0, 976 Roman*NSIZE+Size12 => SLS12, 977 Roman*NSIZE+Size16 => SLS16, 978 Italic*NSIZE+Size6 => SLI | SLS6, 979 Italic*NSIZE+Size8 => SLI | SLS8, 980 Italic*NSIZE+Size10 => SLI, 981 Italic*NSIZE+Size12 => SLI | SLS12, 982 Italic*NSIZE+Size16 => SLI | SLS16, 983 Bold*NSIZE+Size6 => SLB | SLS6, 984 Bold*NSIZE+Size8 => SLB | SLS8, 985 Bold*NSIZE+Size10 => SLB, 986 Bold*NSIZE+Size12 => SLB | SLS12, 987 Bold*NSIZE+Size16 => SLB | SLS16, 988 Type*NSIZE+Size6 => SLT | SLS6, 989 Type*NSIZE+Size8 => SLT | SLS8, 990 Type*NSIZE+Size10 => SLT, 991 Type*NSIZE+Size12 => SLT | SLS12, 992 Type*NSIZE+Size16 => SLT | SLS16 993}; 994 995lsizecmd := array[] of { "\\footnotesize", "\\small", "\\normalsize", "\\large", "\\Large"}; 996llinepos : int; 997lslidenum : int; 998LTABSIZE : con 4; 999 1000latexconv(e: ref Celem) 1001{ 1002 initasciitrans(ltranstab); 1003 1004 case fmt { 1005 FLatex or FLatexProc => 1006 if(fmt == FLatex) { 1007 fout.puts("\\documentclass{article}\n"); 1008 fout.puts("\\def\\encodingdefault{T1}\n"); 1009 } 1010 else { 1011 fout.puts("\\documentclass[10pt,twocolumn]{article}\n"); 1012 fout.puts("\\def\\encodingdefault{T1}\n"); 1013 fout.puts("\\usepackage{latex8}\n"); 1014 fout.puts("\\bibliographystyle{latex8}\n"); 1015 } 1016 fout.puts("\\usepackage{times}\n"); 1017 fout.puts("\\usepackage{brutus}\n"); 1018 fout.puts("\\usepackage{unicode}\n"); 1019 fout.puts("\\usepackage{epsf}\n"); 1020 title := lfindtitle(e); 1021 authors := lfindauthors(e); 1022 abstract := lfindabstract(e); 1023 fout.puts("\\begin{document}\n"); 1024 if(title != nil) { 1025 fout.puts("\\title{"); 1026 llinepos = 0; 1027 lconvl(title, 0); 1028 fout.puts("}\n"); 1029 if(authors != nil) { 1030 fout.puts("\\author{"); 1031 for(l := authors; l != nil; l = tl l) { 1032 llinepos = 0; 1033 lconvl(hd l, SLO|SLI); 1034 if(tl l != nil) 1035 fout.puts("\n\\and\n"); 1036 } 1037 fout.puts("}\n"); 1038 } 1039 fout.puts("\\maketitle\n"); 1040 } 1041 fout.puts("\\pagestyle{empty}\\thispagestyle{empty}\n"); 1042 if(abstract != nil) { 1043 if(fmt == FLatexProc) { 1044 fout.puts("\\begin{abstract}\n"); 1045 llinepos = 0; 1046 lconvl(abstract, 0); 1047 fout.puts("\\end{abstract}\n"); 1048 } 1049 else { 1050 fout.puts("\\section*{Abstract}\n"); 1051 llinepos = 0; 1052 lconvl(abstract, 0); 1053 } 1054 } 1055 FLatexBook => 1056 fout.puts("\\documentclass{ibook}\n"); 1057 fout.puts("\\usepackage{brutus}\n"); 1058 fout.puts("\\usepackage{epsf}\n"); 1059 fout.puts("\\begin{document}\n"); 1060 FLatexSlides => 1061 fout.puts("\\documentclass[portrait]{seminar}\n"); 1062 fout.puts("\\def\\encodingdefault{T1}\n"); 1063 fout.puts("\\usepackage{times}\n"); 1064 fout.puts("\\usepackage{brutus}\n"); 1065 fout.puts("\\usepackage{unicode}\n"); 1066 fout.puts("\\usepackage{epsf}\n"); 1067 fout.puts("\\centerslidesfalse\n"); 1068 fout.puts("\\slideframe{none}\n"); 1069 fout.puts("\\slidestyle{empty}\n"); 1070 fout.puts("\\pagestyle{empty}\n"); 1071 fout.puts("\\begin{document}\n"); 1072 lslidenum = 0; 1073 } 1074 1075 llinepos = 0; 1076 if(e.tag == SGML) 1077 lconvl(e.contents, 0); 1078 1079 if(fmt == FLatexSlides && lslidenum > 0) 1080 fout.puts("\\vfill\\end{slide*}\n"); 1081 if(fmt != FLatexPart) 1082 fout.puts("\\end{document}\n"); 1083} 1084 1085lconvl(el: ref Celem, state: int) 1086{ 1087 for(e := el; e != nil; e = e.next) { 1088 tag := e.tag; 1089 op := ""; 1090 cl := ""; 1091 parlike := 1; 1092 nstate := state; 1093 if(tag < NFONTTAG) { 1094 parlike = 0; 1095 ss := lftagtostate[tag]; 1096 if((state & SLFONTMASK) != ss) { 1097 t := state & SLT; 1098 b := state & SLB; 1099 i := state & SLI; 1100 newt := ss & SLT; 1101 newb := ss & SLB; 1102 newi := ss & SLI; 1103 op = "{"; 1104 cl = "}"; 1105 if(t && !newt) 1106 op += "\\rmfamily"; 1107 else if(!t && newt) 1108 op += "\\ttfamily"; 1109 if(b && !newb) 1110 op += "\\mdseries"; 1111 else if(!b && newb) 1112 op += "\\bfseries"; 1113 if(i && !newi) 1114 op += "\\upshape"; 1115 else if(!i && newi) { 1116 op += "\\itshape"; 1117 bc := lastchar(e.contents); 1118 ac := firstchar(e.next); 1119 if(bc != -1 && bc != ' ' && bc != '\n' && ac != -1 && ac != '.' && ac != ',') 1120 cl = "\\/}"; 1121 } 1122 if((state & SLSIZEMASK) != (ss & SLSIZEMASK)) { 1123 nsize := 2; 1124 if(ss & SLS6) 1125 nsize = 0; 1126 else if(ss & SLS8) 1127 nsize = 1; 1128 else if(ss & SLS12) 1129 nsize = 3; 1130 else if(ss & SLS16) 1131 nsize = 4; 1132 # examples shrunk one size 1133 if((state & SLE) && nsize > 0) 1134 nsize--; 1135 op += lsizecmd[nsize]; 1136 } 1137 fc := firstchar(e.contents); 1138 if(fc == ' ') 1139 op += "{}"; 1140 else 1141 op += " "; 1142 nstate = (state & ~SLFONTMASK) | ss; 1143 } 1144 } 1145 else 1146 case tag { 1147 Text => 1148 parlike = 0; 1149 if(state & SLO) { 1150 asciitrans[' '] = "\\ "; 1151 asciitrans['\n'] = "\\\\\n"; 1152 } 1153 s := e.s; 1154 n := len s; 1155 for(k := 0; k < n; k++) { 1156 c := s[k]; 1157 x := ""; 1158 if(c < 128) 1159 x = asciitrans[c]; 1160 else 1161 x = tlookup(ltranstab, c); 1162 if(x == "") { 1163 fout.putc(c); 1164 if(c == '\n') 1165 llinepos = 0; 1166 else 1167 llinepos++; 1168 } 1169 else { 1170 # split up ligatures 1171 if(c == '-' && k < n-1 && s[k+1] == '-') 1172 x = "-{}"; 1173 # Avoid the 'no line to end here' latex error 1174 if((state&SLO) && c == '\n' && llinepos == 0) 1175 fout.puts("\\ "); 1176 else if((state&SLO) && c == '\t') { 1177 nspace := LTABSIZE - llinepos%LTABSIZE; 1178 llinepos += nspace; 1179 while(nspace-- > 0) 1180 fout.puts("\\ "); 1181 1182 } 1183 else { 1184 fout.puts(x); 1185 if(x[len x - 1] == '\n') 1186 llinepos = 0; 1187 else 1188 llinepos++; 1189 } 1190 } 1191 } 1192 if(state & SLO) { 1193 asciitrans[' '] = nil; 1194 asciitrans['\n'] = nil; 1195 } 1196 Example => 1197 if(!(state&SLE)) { 1198 op = "\\begin{example}"; 1199 cl = "\\end{example}\\noindent "; 1200 nstate |= SLE | SLO; 1201 } 1202 List => 1203 (n, bigle) := lfindbigle(e.contents); 1204 if(n <= 2) { 1205 op = "\\begin{itemize}\n"; 1206 cl = "\\end{itemize}"; 1207 } 1208 else { 1209 fout.puts("\\begin{itemizew}{"); 1210 lconvl(bigle.contents, nstate); 1211 op = "}\n"; 1212 cl = "\\end{itemizew}"; 1213 } 1214 Listelem => 1215 op = "\\item[{"; 1216 cl = "}]"; 1217 Heading => 1218 if(fmt == FLatexProc) 1219 op = "\n\\Section{"; 1220 else 1221 op = "\n\\section{"; 1222 cl = "}\n"; 1223 nstate = (state & ~SLFONTMASK) | (SLB | SLS12); 1224 Nofill => 1225 op = "\\begin{nofill}"; 1226 cl = "\\end{nofill}\\noindent "; 1227 nstate |= SLO; 1228 Title => 1229 if(fmt == FLatexSlides) { 1230 op = "\\begin{slide*}\n" + 1231 "\\begin{center}\\Large\\bfseries "; 1232 if(lslidenum > 0) 1233 op = "\\vfill\\end{slide*}\n" + op; 1234 cl = "\\end{center}\n"; 1235 lslidenum++; 1236 } 1237 else { 1238 if(stringof(e.contents) == "Index") { 1239 op = "\\printindex\n"; 1240 e.contents = nil; 1241 } 1242 else { 1243 op = "\\chapter{"; 1244 cl = "}\n"; 1245 } 1246 } 1247 nstate = (state & ~SLFONTMASK) | (SLB | SLS16); 1248 Par => 1249 op = "\n\\par\n"; 1250 while(e.next != nil && e.next.tag == Par) 1251 e = e.next; 1252 Extension => 1253 e.contents = convextension(e.s); 1254 if(e.contents != nil) 1255 e.contents.parent = e; 1256 Special => 1257 fout.puts(e.s); 1258 Float => 1259 if(!(state&SLF)) { 1260 isfig := lfixfloat(e); 1261 if(isfig) { 1262 op = "\\begin{figure}\\begin{center}\\leavevmode "; 1263 cl = "\\end{center}\\end{figure}"; 1264 } 1265 else { 1266 op = "\\begin{table}\\begin{center}\\leavevmode "; 1267 cl = "\\end{center}\\end{table}"; 1268 } 1269 nstate |= SLF; 1270 } 1271 Caption=> 1272 if(state&SLF) { 1273 op = "\\caption{"; 1274 cl = "}"; 1275 nstate = (state & ~SLFONTMASK) | SLS8; 1276 } 1277 else { 1278 op = "\\begin{center}"; 1279 cl = "\\end{center}"; 1280 } 1281 Label or Labelref => 1282 parlike = 0; 1283 if(tag == Label) 1284 op = "\\label"; 1285 else 1286 op = "\\ref"; 1287 cl = "{" + stringof(e.contents) + "}"; 1288 e.contents = nil; 1289 Exercise => 1290 lfixexercise(e); 1291 op = "\\begin{exercise}"; 1292 cl = "\\end{exercise}"; 1293 Index or Indextopic => 1294 parlike = 0; 1295 if(tag == Index) 1296 lconvl(e.contents, nstate); 1297 fout.puts("\\showidx{"); 1298 lconvl(e.contents, nstate); 1299 fout.puts("}"); 1300 lconvindex(e.contents, nstate); 1301 e.contents = nil; 1302 } 1303 if(op != "") 1304 fout.puts(op); 1305 if(e.contents != nil) { 1306 if(parlike) 1307 llinepos = 0; 1308 lconvl(e.contents, nstate); 1309 if(parlike) 1310 llinepos = 0; 1311 } 1312 if(cl != "") 1313 fout.puts(cl); 1314 } 1315} 1316 1317lfixfloat(e: ref Celem) : int 1318{ 1319 dropwhite(e.contents); 1320 fstart := e.contents; 1321 fend := last(fstart); 1322 hasfig := 0; 1323 hastab := 0; 1324 if(fend.tag == Caption) { 1325 dropwhite(fend.prev); 1326 if(fend.prev != nil && stringof(fstart) == "\t") 1327 delete(fend.prev); 1328 # If fend.contents is "YYY " <Label> "." rest 1329 # where YYY is Figure or Table, 1330 # then replace it with just rest, and move <Label> 1331 # after the caption. 1332 # Probably should be more robust about what to accept. 1333 ec := fend.contents; 1334 s := stringof(ec); 1335 if(s == "Figure ") 1336 hasfig = 1; 1337 else if(s == "Table ") 1338 hastab = 1; 1339 if(hasfig || hastab) { 1340 ec2 := ec.next; 1341 ec3 : ref Celem = nil; 1342 ec4 : ref Celem = nil; 1343 if(ec2 != nil && ec2.tag == Label) { 1344 ec3 = ec2.next; 1345 if(ec3 != nil && stringof(ec3) == ".") 1346 ec4 = ec3.next; 1347 } 1348 if(ec4 != nil) { 1349 dropwhite(ec4); 1350 ec4 = ec3.next; 1351 if(ec4 != nil) { 1352 excise(ec); 1353 excise(ec2); 1354 excise(ec3); 1355 fend.contents = ec4; 1356 ec4.parent = fend; 1357 insertafter(ec2, fend); 1358 } 1359 } 1360 } 1361 } 1362 return !hastab; 1363} 1364 1365lfixexercise(e: ref Celem) 1366{ 1367 dropwhite(e.contents); 1368 ec := e.contents; 1369 # Expect: 1370 # "Exercise " <Label> ":" rest 1371 # If so, drop the first and third. 1372 # Or 1373 # "Exercise:" rest 1374 # If so, drop the first. 1375 s := stringof(ec); 1376 if(s == "Exercise ") { 1377 ec2 := ec.next; 1378 ec3 : ref Celem = nil; 1379 ec4 : ref Celem = nil; 1380 if(ec2 != nil && ec2.tag == Label) { 1381 ec3 = ec2.next; 1382 if(ec3 != nil && stringof(ec3) == ":") 1383 ec4 = ec3.next; 1384 } 1385 if(ec4 != nil) { 1386 dropwhite(ec4); 1387 ec4 = ec3.next; 1388 if(ec4 != nil) { 1389 excise(ec); 1390 excise(ec3); 1391 e.contents = ec2; 1392 ec2.parent = e; 1393 ec2.next = ec4; 1394 ec4.prev = ec2; 1395 } 1396 } 1397 } 1398 else if(s == "Exercise:") { 1399 dropwhite(ec.next); 1400 e.contents = ec.next; 1401 excise(ec); 1402 if(e.contents != nil) 1403 e.contents.parent = e; 1404 } 1405} 1406 1407# convert content list headed by e to \\index{...} 1408lconvindex(e: ref Celem, state: int) 1409{ 1410 fout.puts("\\index{"); 1411 g := lsplitind(e); 1412 gp := g; 1413 needat := 0; 1414 while(g != nil) { 1415 gnext := g.next; 1416 s := stringof(g); 1417 if(s == "!" || s == "|") { 1418 if(gp != g) { 1419 g.next = nil; 1420 g.s = ""; 1421 lprintindsort(gp); 1422 if(needat) { 1423 fout.puts("@"); 1424 lconvl(gp, state); 1425 } 1426 } 1427 fout.puts(s); 1428 gp = gnext; 1429 needat = 0; 1430 if(s == "|") { 1431 if(g == nil) 1432 break; 1433 g = gnext; 1434 # don't lconvl the Text items, so 1435 # that "see{" and "}" come out untranslated. 1436 # (code is wrong if stuff inside see is plain 1437 # text but with special tex characters) 1438 while(g != nil) { 1439 gnext = g.next; 1440 g.next = nil; 1441 if(g.tag != Text) 1442 lconvl(g, state); 1443 else 1444 fout.puts(g.s); 1445 g = gnext; 1446 } 1447 gp = nil; 1448 break; 1449 } 1450 } 1451 else { 1452 if(g.tag != Text) 1453 needat = 1; 1454 } 1455 g = gnext; 1456 } 1457 if(gp != nil) { 1458 lprintindsort(gp); 1459 if(needat) { 1460 fout.puts("@"); 1461 lconvl(gp, state); 1462 } 1463 } 1464 fout.puts("}"); 1465} 1466 1467lprintindsort(e: ref Celem) 1468{ 1469 while(e != nil) { 1470 fout.puts(stringof(e)); 1471 e = e.next; 1472 } 1473} 1474 1475# return copy of e 1476lsplitind(e: ref Celem) : ref Celem 1477{ 1478 dummy := ref Celem; 1479 for( ; e != nil; e = e.next) { 1480 te := e; 1481 if(e.tag < NFONTTAG) 1482 te = te.contents; 1483 if(te.tag != Text) 1484 continue; 1485 s := te.s; 1486 i := 0; 1487 for(j := 0; j < len s; j++) { 1488 if(s[j] == '!' || s[j] == '|') { 1489 if(j > i) { 1490 nte := ref Celem(Text, s[i:j], nil, nil, nil, nil); 1491 if(e == te) 1492 ne := nte; 1493 else 1494 ne = ref Celem(e.tag, nil, nte, nil, nil, nil); 1495 append(dummy, ne); 1496 } 1497 append(dummy, ref Celem(Text, s[j:j+1], nil, nil, nil, nil)); 1498 i = j+1; 1499 } 1500 } 1501 if(j > i) { 1502 nte := ref Celem(Text, s[i:j], nil, nil, nil, nil); 1503 if(e == te) 1504 ne := nte; 1505 else 1506 ne = ref Celem(e.tag, nil, nte, nil, nil, nil); 1507 append(dummy, ne); 1508 } 1509 } 1510 return dummy.next; 1511} 1512 1513# return key part of an index entry corresponding to e list 1514indexkey(e: ref Celem) : string 1515{ 1516 s := ""; 1517 while(e != nil) { 1518 s += stringof(e); 1519 e = e.next; 1520 } 1521 return s; 1522} 1523 1524# find title, excise it from e, and return contents as list 1525lfindtitle(e: ref Celem) : ref Celem 1526{ 1527 if(e.tag == Title) { 1528 ans := e.contents; 1529 delete(e); 1530 return ans; 1531 } 1532 else if (e.contents != nil) { 1533 for(h := e.contents; h != nil; h = h.next) { 1534 a := lfindtitle(h); 1535 if(a != nil) 1536 return a; 1537 } 1538 } 1539 return nil; 1540} 1541 1542# find authors, excise them from e, and return as list of lists 1543lfindauthors(e: ref Celem) : list of ref Celem 1544{ 1545 if(e.tag == Author) { 1546 a := e.contents; 1547 en := e.next; 1548 delete(e); 1549 rans : list of ref Celem = a :: nil; 1550 if(en != nil) { 1551 e = en; 1552 while(e != nil) { 1553 if(e.tag == Par) { 1554 en = e.next; 1555 if(en.tag == Author) { 1556 delete(e); 1557 a = en.contents; 1558 for(y := a; y != nil; ) { 1559 yn := y.next; 1560 if(y.tag == Par) 1561 delete(y); 1562 y = yn; 1563 } 1564 e = en.next; 1565 delete(en); 1566 rans = a :: rans; 1567 } 1568 else 1569 break; 1570 } 1571 else 1572 break; 1573 } 1574 } 1575 ans : list of ref Celem = nil; 1576 while(rans != nil) { 1577 ans = hd rans :: ans; 1578 rans = tl rans; 1579 } 1580 return ans; 1581 } 1582 else if (e.contents != nil) { 1583 for(h := e.contents; h != nil; h = h.next) { 1584 a := lfindauthors(h); 1585 if(a != nil) 1586 return a; 1587 } 1588 } 1589 return nil; 1590} 1591 1592# find section called abstract, excise it from e, and return as list 1593lfindabstract(e: ref Celem) : ref Celem 1594{ 1595 if(e.tag == Heading) { 1596 c := e.contents; 1597 if(c.tag == Text && c.s == "Abstract") { 1598 for(h2 := e.next; h2 != nil; h2 = h2.next) { 1599 if(h2.tag == Heading) 1600 break; 1601 } 1602 ans := e.next; 1603 ans.prev = nil; 1604 ep := e.prev; 1605 eu := e.parent; 1606 if(ep == nil) { 1607 if(eu != nil) 1608 eu.contents = h2; 1609 if(h2 != nil) 1610 h2.parent = eu; 1611 } 1612 else 1613 ep.next = h2; 1614 if(h2 != nil) { 1615 ansend := h2.prev; 1616 ansend.next = nil; 1617 h2.prev = ep; 1618 } 1619 return ans; 1620 } 1621 } 1622 else if (e.contents != nil) { 1623 for(h := e.contents; h != nil; h = h.next) { 1624 a := lfindabstract(h); 1625 if(a != nil) 1626 return a; 1627 } 1628 } 1629 return nil; 1630} 1631 1632# find biggest list element with longest contents in e list 1633lfindbigle(e: ref Celem) : (int, ref Celem) 1634{ 1635 ans : ref Celem = nil; 1636 maxlen := 0; 1637 for(h := e; h != nil; h = h.next) { 1638 if(h.tag == Listelem) { 1639 n := 0; 1640 for(p := h.contents; p != nil; p = p.next) { 1641 if(p.tag == Text) 1642 n += len p.s; 1643 else if(p.tag < NFONTTAG) { 1644 q := p.contents; 1645 if(q.tag == Text) 1646 n += len q.s; 1647 } 1648 } 1649 if(n > maxlen) { 1650 maxlen = n; 1651 ans = h; 1652 } 1653 } 1654 } 1655 return (maxlen, ans); 1656} 1657 1658# Translation to HTML 1659 1660# state bits 1661SHA, SHO, SHFL, SHDT: con (1<<iota); 1662 1663htmlconv(e: ref Celem) 1664{ 1665 initasciitrans(htranstab); 1666 1667 fout.puts("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"); 1668 fout.puts("<HTML>\n"); 1669 1670 if(e.tag == SGML) { 1671 # Conforming 3.2 documents require a Title. 1672 # Use the Title tag both for the document title and 1673 # for an H1-level heading. 1674 # (SHDT state bit enforces: Font change markup, etc., not allowed in Title) 1675 fout.puts("<TITLE>\n"); 1676 title := hfindtitle(e); 1677 if(title != nil) 1678 hconvl(title.contents, SHDT); 1679 else if(infilename != "") 1680 fout.puts(infilename); 1681 else 1682 fout.puts("An HTML document"); 1683 fout.puts("</TITLE>\n"); 1684 fout.puts("<BODY>\n"); 1685 hconvl(e.contents, 0); 1686 fout.puts("</BODY>\n"); 1687 } 1688 1689 fout.puts("</HTML>\n"); 1690} 1691 1692hconvl(el: ref Celem, state: int) 1693{ 1694 for(e := el; e != nil; e = e.next) { 1695 tag := e.tag; 1696 op := ""; 1697 cl := ""; 1698 nstate := state; 1699 if(tag == Text) { 1700 s := e.s; 1701 n := len s; 1702 for(k := 0; k < n; k++) { 1703 c := s[k]; 1704 x := ""; 1705 if(c < 128) { 1706 if(c == '\n' && (state&SHO)) 1707 x = "\n\t"; 1708 else 1709 x = asciitrans[c]; 1710 } 1711 else 1712 x = tlookup(htranstab, c); 1713 if(x == "") 1714 fout.putc(c); 1715 else 1716 fout.puts(x); 1717 } 1718 } 1719 else if(!(state&SHDT)) 1720 case tag { 1721 Roman*NSIZE+Size6 => 1722 op = "<FONT SIZE=1>"; 1723 cl = "</FONT>"; 1724 nstate |= SHA; 1725 Roman*NSIZE+Size8 => 1726 op = "<FONT SIZE=2>"; 1727 cl = "</FONT>"; 1728 nstate |= SHA; 1729 Roman*NSIZE+Size10 => 1730 if(state & SHA) { 1731 op = "<FONT SIZE=3>"; 1732 cl = "</FONT>"; 1733 nstate &= ~SHA; 1734 } 1735 Roman*NSIZE+Size12 => 1736 op = "<FONT SIZE=4>"; 1737 cl = "</FONT>"; 1738 nstate |= SHA; 1739 Roman*NSIZE+Size16 => 1740 op = "<FONT SIZE=5>"; 1741 cl = "</FONT>"; 1742 nstate |= SHA; 1743 Italic*NSIZE+Size6 => 1744 op = "<I><FONT SIZE=1>"; 1745 cl = "</FONT></I>"; 1746 nstate |= SHA; 1747 Italic*NSIZE+Size8 => 1748 op = "<I><FONT SIZE=2>"; 1749 cl = "</FONT></I>"; 1750 nstate |= SHA; 1751 Italic*NSIZE+Size10 => 1752 if(state & SHA) { 1753 op = "<I><FONT SIZE=3>"; 1754 cl = "</FONT></I>"; 1755 nstate &= ~SHA; 1756 } 1757 else { 1758 op = "<I>"; 1759 cl = "</I>"; 1760 } 1761 Italic*NSIZE+Size12 => 1762 op = "<I><FONT SIZE=4>"; 1763 cl = "</FONT></I>"; 1764 nstate |= SHA; 1765 Italic*NSIZE+Size16 => 1766 op = "<I><FONT SIZE=5>"; 1767 cl = "</FONT></I>"; 1768 nstate |= SHA; 1769 Bold*NSIZE+Size6 => 1770 op = "<B><FONT SIZE=1>"; 1771 cl = "</FONT></B>"; 1772 nstate |= SHA; 1773 Bold*NSIZE+Size8 => 1774 op = "<B><FONT SIZE=2>"; 1775 cl = "</FONT></B>"; 1776 nstate |= SHA; 1777 Bold*NSIZE+Size10 => 1778 if(state & SHA) { 1779 op = "<B><FONT SIZE=3>"; 1780 cl = "</FONT></B>"; 1781 nstate &= ~SHA; 1782 } 1783 else { 1784 op = "<B>"; 1785 cl = "</B>"; 1786 } 1787 Bold*NSIZE+Size12 => 1788 op = "<B><FONT SIZE=4>"; 1789 cl = "</FONT></B>"; 1790 nstate |= SHA; 1791 Bold*NSIZE+Size16 => 1792 op = "<B><FONT SIZE=5>"; 1793 cl = "</FONT></B>"; 1794 nstate |= SHA; 1795 Type*NSIZE+Size6 => 1796 op = "<TT><FONT SIZE=1>"; 1797 cl = "</FONT></TT>"; 1798 nstate |= SHA; 1799 Type*NSIZE+Size8 => 1800 op = "<TT><FONT SIZE=2>"; 1801 cl = "</FONT></TT>"; 1802 nstate |= SHA; 1803 Type*NSIZE+Size10 => 1804 if(state & SHA) { 1805 op = "<TT><FONT SIZE=3>"; 1806 cl = "</FONT></TT>"; 1807 nstate &= ~SHA; 1808 } 1809 else { 1810 op = "<TT>"; 1811 cl = "</TT>"; 1812 } 1813 Type*NSIZE+Size12 => 1814 op = "<TT><FONT SIZE=4>"; 1815 cl = "</FONT></TT>"; 1816 nstate |= SHA; 1817 Type*NSIZE+Size16 => 1818 op = "<TT><FONT SIZE=5>"; 1819 cl = "</FONT></TT>"; 1820 nstate |= SHA; 1821 Example => 1822 op = "<P><PRE>\t"; 1823 cl = "</PRE><P>\n"; 1824 nstate |= SHO; 1825 List => 1826 op = "<DL>"; 1827 cl = "</DD></DL>"; 1828 nstate |= SHFL; 1829 Listelem => 1830 if(state & SHFL) 1831 op = "<DT>"; 1832 else 1833 op = "</DD><DT>"; 1834 cl = "</DT><DD>"; 1835 # change first-list-elem state for this level 1836 state &= ~SHFL; 1837 Heading => 1838 op = "<H2>"; 1839 cl = "</H2>\n"; 1840 Nofill => 1841 op = "<P><PRE>"; 1842 cl = "</PRE>"; 1843 Title => 1844 op = "<H1>"; 1845 cl = "</H1>\n"; 1846 Par => 1847 op = "<P>\n"; 1848 Extension => 1849 e.contents = convextension(e.s); 1850 Special => 1851 fout.puts(e.s); 1852 } 1853 if(op != "") 1854 fout.puts(op); 1855 hconvl(e.contents, nstate); 1856 if(cl != "") 1857 fout.puts(cl); 1858 } 1859} 1860 1861# find title, if there is one, and return it (but leave it in contents too) 1862hfindtitle(e: ref Celem) : ref Celem 1863{ 1864 if(e.tag == Title) 1865 return e; 1866 else if (e.contents != nil) { 1867 for(h := e.contents; h != nil; h = h.next) { 1868 a := hfindtitle(h); 1869 if(a != nil) 1870 return a; 1871 } 1872 } 1873 return nil; 1874} 1875 1876Exten: adt 1877{ 1878 name: string; 1879 mod: Brutusext; 1880}; 1881 1882extens: list of Exten = nil; 1883 1884convextension(s: string) : ref Celem 1885{ 1886 for(i:=0; i<len s; i++) 1887 if(s[i] == ' ') 1888 break; 1889 if(i == len s) { 1890 sys->fprint(stderr, "badly formed extension %s\n", s); 1891 return nil; 1892 } 1893 modname := s[0:i]; 1894 s = s[i+1:]; 1895 mod: Brutusext = nil; 1896 for(le := extens; le != nil; le = tl le) { 1897 el := hd le; 1898 if(el.name == modname) 1899 mod = el.mod; 1900 } 1901 if(mod == nil) { 1902 file := modname; 1903 if(i < 4 || file[i-4:i] != ".dis") 1904 file += ".dis"; 1905 if(file[0] != '/') 1906 file = "/dis/wm/brutus/" + file; 1907 mod = load Brutusext file; 1908 if(mod == nil) { 1909 sys->fprint(stderr, "can't load extension module %s: %r\n", file); 1910 return nil; 1911 } 1912 mod->init(sys, draw, B, tk, nil); 1913 extens = Exten(modname, mod) :: extens; 1914 } 1915 f := infilename; 1916 if(f == "<stdin>") 1917 f = ""; 1918 (ans, err) := mod->cook(f, fmt, s); 1919 if(err != "") { 1920 sys->fprint(stderr, "extension module %s cook error: %s\n", modname, err); 1921 return nil; 1922 } 1923 return ans; 1924} 1925