|
|
1.1 ! root 1: ...\" Thu May 22 16:54:35 EDT 1986 ! 2: .de UC ! 3: \&\\$3\s-2\\$1\s+2\&\\$2 ! 4: .. ! 5: .fp 1 PA ! 6: .fp 2 PI ! 7: .fp 3 PB ! 8: .nr dT 8 \" tab stops this far apart in .P1/.P2 ! 9: .hy 14 \" set hyphenation: 2=not last lines; 4= no -xx; 8=no xx- ! 10: .\" ! 11: .\" ! 12: .ND "Revised, March, 1982" ! 13: .TR 97 ! 14: ....TM 80-1272-8 39199 39199-11 ! 15: .TL ! 16: A Typesetter-independent TROFF ! 17: .AU "MH 2C-518" 6021 ! 18: Brian W. Kernighan ! 19: .AI ! 20: .MH ! 21: .AB ! 22: .PP ! 23: Although ! 24: .UC TROFF ! 25: has been the mainstay of document preparation ! 26: at Bell Labs for several years, ! 27: it has heretofore been very dependent ! 28: on one particular typesetter, ! 29: the Graphic Systems CAT. ! 30: .PP ! 31: This paper describes conversion of ! 32: .UC TROFF ! 33: to deal with a wide class of typesetters. ! 34: .PP ! 35: Some of these typesetters provide many more facilities ! 36: than the CAT does. ! 37: Typical extra features include ! 38: more sizes and fonts, ! 39: larger alphabets, ! 40: and the ability to ! 41: create new characters ! 42: and to draw graphical objects. ! 43: The paper describes the enhancements that permit ! 44: .UC TROFF ! 45: to take advantage of some ! 46: of these capabilities as well. ! 47: .AE ! 48: .CS 12 1 13 0 0 2 ! 49: .NH ! 50: A Bit of History ! 51: .LP ! 52: .QS ! 53: ``I will be speaking today about work in progress, ! 54: instead of completed research; ! 55: this was not my original intention when I chose the subject ! 56: of this lecture, ! 57: but the fact is I couldn't get my ! 58: computer programs working in time.'' ! 59: .sp .5 ! 60: .ta 4i ! 61: Donald E. Knuth ! 62: .[ ! 63: %A Donald E. Knuth ! 64: %T Mathematical typography ! 65: %J Bulletin (New Series) of the American Mathematical Society ! 66: %V 1 ! 67: %N 2 ! 68: %D 1979 ! 69: .] ! 70: .QE ! 71: .PP ! 72: The ! 73: .UC TROFF ! 74: text formatter ! 75: .[ ! 76: %A J. F. Ossanna ! 77: %T NROFF/TROFF User's Manual ! 78: %D October 1976 ! 79: %R Comp. Sci. Tech. Rep. 54 ! 80: %I Bell Laboratories ! 81: %C Murray Hill, NJ ! 82: .] ! 83: was originally written by the late Joe Ossanna ! 84: in about 1973, in assembly language for the PDP-11. ! 85: .UC NROFF , ( ! 86: which drives terminals instead of a typesetter, ! 87: is essentially identical to ! 88: .UC TROFF ; ! 89: we will use ! 90: .UC TROFF '' `` ! 91: as a generic term hence\%forth.) ! 92: It was rewritten in C around 1975, ! 93: and underwent slow but steady evolution until ! 94: Ossanna's death late in 1977. ! 95: .PP ! 96: In spite of some obvious deficiencies ! 97: \(em a rebarbative input syntax, ! 98: mysterious and undocumented properties in some areas, ! 99: and a voracious appetite for computer resources ! 100: (especially when used with macro packages and preprocessors like ! 101: .UC EQN ! 102: and ! 103: .UC TBL ) ! 104: \(em ! 105: .UC TROFF ! 106: has been the basis of document preparation at ! 107: Bell Labs for some years, ! 108: and is likely to remain so for years to come. ! 109: .PP ! 110: Early in 1979, ! 111: the Computing Science Research Center ! 112: decided to acquire a new typesetter, ! 113: primarily because of our interests in typesetting graphics. ! 114: At the same time, ! 115: the Murray Hill Computer Center ! 116: began to investigate the possibility of replacing their ! 117: family of aging CAT's ! 118: with a new, high-performance typesetter, ! 119: simply to keep up with their rapidly expanding load. ! 120: .PP ! 121: My first thought ! 122: (a thought shared by many others) ! 123: was that this would be a glorious opportunity ! 124: to replace ! 125: .UC TROFF ! 126: with a new formatting language: ! 127: better designed, ! 128: easier to work with, ! 129: and of course much faster. ! 130: This remains a desirable goal, ! 131: but, after quite a bit of thought spread over several ! 132: years, ! 133: I am still not really much closer to a better design, ! 134: let alone an implementation. ! 135: Furthermore, ! 136: a great deal of software depends on ! 137: .UC TROFF ! 138: \(em ! 139: the preprocessors, the macro packages, ! 140: and of course all of their documentation and our accumulated expertise. ! 141: Tossing this aside is not something to be done lightly. ! 142: .PP ! 143: Accordingly, in the spring of 1979, ! 144: I set about to modify ! 145: .UC TROFF ! 146: so that it would run hence\%forth without change ! 147: on a variety of typesetters. ! 148: The ground rule was that ! 149: .UC TROFF ! 150: should retain its current specifications, ! 151: so that existing software like ! 152: .UC EQN , ! 153: .UC TBL ! 154: and the macro packages would continue to work with it. ! 155: .PP ! 156: Since much of the rest of this paper ! 157: is encrusted with details that could appeal only to ! 158: maintainers or masochists, ! 159: I will give here a brief summary of what has been done. ! 160: Non-specialists can stop reading at the end of the section. ! 161: .PP ! 162: .UC TROFF ! 163: is highly dependent on the Graphic Systems CAT typesetter, ! 164: not just in details of code ! 165: but also in many aspects of its design. ! 166: The language design issues have been largely ignored ! 167: (few are truly fundamental), ! 168: while the code has been modified ! 169: so that dependencies are either eliminated ! 170: or at least parameterized. ! 171: .PP ! 172: .UC TROFF ! 173: originally had parameters of the typesetter ! 174: compiled into the code, ! 175: often in non-obvious ways. ! 176: The new version reads a parameter file ! 177: each time it is invoked, to ! 178: set values for machine resolution, ! 179: legal sizes, fonts and characters, ! 180: character widths ! 181: and the like. ! 182: .PP ! 183: .UC TROFF ! 184: output used to be binary device codes ! 185: specific to the CAT and arcane beyond description. ! 186: The output of the new version is ! 187: .UC ASCII ! 188: characters ! 189: in a simple and (I hope) universal language ! 190: that describes where each character is to be placed ! 191: and in what size and font. ! 192: A post-processor must be written for each typesetter ! 193: to convert this typesetter-independent language ! 194: into specific codes for that typesetter. ! 195: Post-processors currently exist ! 196: for the CAT, ! 197: the Mergenthaler Linotron 202, ! 198: the Autologic APS-5, ! 199: the Tektronix 4014 terminal, ! 200: The Imagen Canon laser printer, ! 201: Versatec printers, ! 202: and a bit-map terminal. ! 203: New ones can generally be written in less than a day; ! 204: they share much of their code with previous ones. ! 205: .PP ! 206: The new output language contains information that is not readily ! 207: identifiable in the older output. ! 208: Most notably, the beginning of each page and line ! 209: is marked, ! 210: so post-processors can do device-specific optimizations ! 211: such as sorting the data vertically or printing it boustrophedonically, ! 212: independently of ! 213: .UC TROFF . ! 214: .PP ! 215: Since actual output is done by a post-processor, ! 216: not ! 217: .UC TROFF , ! 218: new capabilities for graphics have been easy to add. ! 219: .UC TROFF ! 220: now recognizes commands for drawing diagonal lines, ! 221: circles, ellipses, circular arcs, ! 222: and quadratic B-splines; ! 223: these are used in the ! 224: PIC ! 225: .[ ! 226: %A Brian W. Kernighan ! 227: %T PIC \(em A Crude Graphics Language for Typesetting ! 228: %R Comp. Sci. Tech. Rep. 85 ! 229: %I Bell Laboratories ! 230: %C Murray Hill, NJ ! 231: %O Also in SIGPLAN Symposium on Text Manipulation, Portland, June 1981. ! 232: .] ! 233: and ! 234: IDEAL ! 235: .[ ! 236: %A Christopher J. Van Wyk ! 237: %T A Graphics Typesetting Language ! 238: %R SIGPLAN Symposium on Text Manipulation, Portland ! 239: %D June, 1981 ! 240: .] ! 241: languages. ! 242: .PP ! 243: A number of limitations have been eased or eliminated. ! 244: A document may have an arbitrary number of fonts on any page ! 245: (if the output device permits it, of course). ! 246: Fonts may be accessed merely by naming them; ! 247: ``mounting'' is no longer necessary. ! 248: Character \H'8'height\H'10' and \S'-10'sl\S'0'a\S'10'nt\S'0' may be set ! 249: independently of width. ! 250: .PP ! 251: The new ! 252: .UC TROFF ! 253: is about 1000 bytes larger in instruction space ! 254: and 13000 bytes larger in data space ! 255: (thus guaranteeing that it will not run on ! 256: PDP-11/40 style machines). ! 257: It runs about as fast as the original version, ! 258: though ! 259: a simple improvement that I made could be retrofitted into ! 260: the earlier version to keep it about 20% faster. ! 261: The post-processors are not included in these time comparisons; ! 262: they typically take 10-20% of the ! 263: .UC TROFF ! 264: time. ! 265: .NH ! 266: Typesetter Dependencies ! 267: .PP ! 268: .UC TROFF ! 269: turns out to be surprisingly dependent on ! 270: the Graphic Systems CAT, ! 271: not just in the code but in its design. ! 272: .PP ! 273: Some of the design dependencies are pretty obvious. ! 274: For example, the CAT provides four fonts ! 275: (of 102 characters each) and 15 sizes. ! 276: The specific sizes ! 277: are wired into the syntax of the language: ! 278: since the CAT has no sizes larger than 36 points, ! 279: .CW \es46 ! 280: can be uniquely decoded as a 4-point ! 281: .CW 6 , ! 282: while ! 283: .CW \es36 ! 284: is simply a switch into size 36. ! 285: .PP ! 286: .UC TROFF ! 287: makes the assumption that there are four fonts, ! 288: three of which are physically isomorphic ! 289: (that is, the same characters appear in the same position in each) ! 290: and one ``special'' font ! 291: that is logically a part of each of the others. ! 292: The reserved font name ! 293: .CW S ! 294: finds its way into several commands ! 295: and receives special treatment in a variety of contexts. ! 296: .PP ! 297: Some commands have the basic resolution of the ! 298: CAT wired into their definition; ! 299: for example, the units of the ! 300: .CW .ss ! 301: command (which sets the size of the inter-word spacing) ! 302: are 36th's of an em, ! 303: because the CAT typesetter itself works in those units. ! 304: .PP ! 305: Some command line options reflect idiosyncrasies of the CAT. ! 306: For example, the option ! 307: .CW -p ! 308: requests that the output all be printed in one size; ! 309: since the CAT is excruciatingly slow at changing point sizes, ! 310: this ! 311: prints an approximation to final output ! 312: comparatively quickly. ! 313: The ! 314: .CW -g ! 315: option causes font information to appear in the output file ! 316: for the benefit of the operations staff at the Murray Hill ! 317: Computer Center. ! 318: .PP ! 319: At the same time, ! 320: there are myriad places ! 321: where the characteristics of the CAT are an integral part of ! 322: the code for ! 323: .UC TROFF . ! 324: Some of these are quite evident; ! 325: others are subtle indeed. ! 326: .PP ! 327: The most obvious instance is the internal encoding of ! 328: a character. ! 329: Within ! 330: .UC TROFF , ! 331: objects are passed around as 16 bit quantities. ! 332: There are two fundamental objects \(em ! 333: printable characters and motions. ! 334: An object looks like this: ! 335: .TS ! 336: center; ! 337: cw(2) cw(8) cw(4) cw(2) cw(16) ! 338: |c0|c0|c0|c0|c0|. ! 339: 1 4 2 1 8 ! 340: _ ! 341: .ft CW ! 342: m s f z c ! 343: .ft ! 344: _ ! 345: .TE ! 346: If the ! 347: .CW m ! 348: bit is a 1, the object represents a motion; ! 349: if it is zero, the object is something to be printed. ! 350: In that case, ! 351: .CW s ! 352: is the size (actually an index into a table of legal sizes), ! 353: .CW f ! 354: is the font, ! 355: .CW z ! 356: is the zero-motion bit ! 357: (i.e., no space after printing), ! 358: and ! 359: .CW c ! 360: is the character. ! 361: If the high order bit of ! 362: .CW c ! 363: is set, ! 364: the character is to be looked up in a table of ! 365: special names ! 366: (for example, ! 367: .CW 0200 ! 368: is the hyphen ! 369: .CW \e(hy ); ! 370: otherwise it is ! 371: .UC ASCII . ! 372: Furthermore, ! 373: if ! 374: .CW c ! 375: is less than octal 40 or greater than octal 370, ! 376: the character is actually some encoded control function ! 377: or very special character ! 378: such as ! 379: .CW \ee ! 380: or ! 381: .CW \e{ . ! 382: .PP ! 383: Clearly the tight packing makes it utterly impossible ! 384: to add another size or font \(em ! 385: there are no bits left over. ! 386: It also implies limits on the number of characters ! 387: in a font and on the number of special names ! 388: (names of the form ! 389: .CW \e(xx ). ! 390: .PP ! 391: Motions are encoded as ! 392: .TS ! 393: center; ! 394: cw(2) cw(2) cw(2) cw(26) ! 395: |c0|c0|c0|c0|. ! 396: 1 1 1 13 ! 397: _ ! 398: .ft CW ! 399: m v n mag ! 400: .ft ! 401: _ ! 402: .TE ! 403: .CW m ! 404: is the ``motion bit'', which is 1 for a motion, ! 405: .CW v ! 406: is 1 for a vertical motion, ! 407: and ! 408: .CW n ! 409: is 1 for a negative motion. ! 410: The remaining 13 bits give the magnitude. ! 411: Since there are only 13 bits, ! 412: the maximum amount of motion is ! 413: 8191 machine units. ! 414: With the CAT's resolution of 432 units per inch, ! 415: this is a generous 19 inches. ! 416: But for the Linotron 202 (resolution 972/inch), ! 417: it is only 8.5 inches. ! 418: .PP ! 419: Within the code, ! 420: certain character values are used in tests and assignments ! 421: without identification. ! 422: For instance the octal value ! 423: .CW 0200 ! 424: is used (without identification) as a hyphen. ! 425: But of course the mask ! 426: .CW 0200 ! 427: also occurs many times. ! 428: As might be imagined, it takes some study to determine whether ! 429: any particular ! 430: .CW 0200 ! 431: is a hyphen or a mask. ! 432: .PP ! 433: The basic resolution of the CAT is 432 units per inch ! 434: horizontally and 144 vertically. ! 435: Character widths are given as the number of units at size 6 points. ! 436: There are 72 points in an inch. ! 437: Thus the program contains as magic numbers ! 438: every factor of 432, ! 439: and \(+-1 from each factor as well. ! 440: .PP ! 441: Finally, ! 442: .UC TROFF ! 443: is simply a big program (at least by my standards) \(em ! 444: about 7000 lines of virtually uncommented C. ! 445: (I am indebted to Lorinda Cherry for a new version of the C beautifier ! 446: that made ! 447: the code legible, if not comprehensible.) ! 448: It was implemented before the recent additions to C, ! 449: so there are no ! 450: .CW typedef 's ! 451: to distinguish among the various kinds of integers, ! 452: relatively few macros with arguments, ! 453: no internal ! 454: .CW static ! 455: variables, ! 456: and a startling number of global variables ! 457: with two-character names. ! 458: .PP ! 459: None of these remarks should be taken as denigrating ! 460: Ossanna's accomplishment with ! 461: .UC TROFF . ! 462: It has proven a remarkably robust tool, ! 463: taking unbelievable abuse from a variety of preprocessors ! 464: and being forced into uses that were never conceived of ! 465: in the original design, ! 466: all with considerable grace under fire. ! 467: .NH ! 468: Modifying TROFF ! 469: .PP ! 470: The first step was to widen the 16-bit internal representation of a character ! 471: to 32 bits, to accommodate more sizes and fonts. ! 472: The current representation is ! 473: .TS ! 474: center; ! 475: cw(2) cw(14) cw(16) cw(2) cw(30) ! 476: |c0|c0|c0|c0|c0|. ! 477: 1 7 8 1 15 ! 478: _ ! 479: .ft CW ! 480: z s f m c ! 481: .ft ! 482: _ ! 483: .TE ! 484: If ! 485: .CW m ! 486: is 1, bits 16 and 17 are ! 487: .CW v ! 488: and ! 489: .CW n . ! 490: Access to this representation is entirely through ! 491: macros; ! 492: for example, a macro called ! 493: .CW cbits ! 494: fetches the character bits, ! 495: another called ! 496: .CW setsfbits ! 497: sets the size and font bits, and so on. ! 498: .PP ! 499: This stage took several weeks of meticulous checking, ! 500: since it was necessary to examine every integer ! 501: constant, variable and function in the program ! 502: to decide whether it was being used to store ! 503: an internal character. ! 504: These are now all identified and ! 505: .CW typedef 'd ! 506: for future reference. ! 507: .PP ! 508: Widening 16 bits to 32 turns out to be quite costly on the PDP-11, ! 509: since the program must process ! 510: .CW long ! 511: integers instead of short ones. ! 512: The result is approximately a 25% increase in program size ! 513: and perhaps 25% increase in run time. ! 514: Furthermore the temporary file in which ! 515: .UC TROFF ! 516: keeps its macro and string definitions doubles in size ! 517: (to 256k bytes). ! 518: .PP ! 519: Note that at this stage the program has not been changed in any fundamental ! 520: way \(em ! 521: it still generates CAT output, ! 522: so a bit-for-bit regression test against the original version can be performed after each change. ! 523: This proved to be very important for maintaining sanity ! 524: in both program and programmer. ! 525: .NH ! 526: Dynamic Machine Parameters ! 527: .PP ! 528: The next step was to find all the the numbers in the program that depend ! 529: on the CAT and replace them by variables. ! 530: This also contributed marginally to slower execution, ! 531: since many values and expressions that were constants now became variables. ! 532: .PP ! 533: With parameters identified as such, ! 534: the next step was ! 535: to make it possible to load a description of ! 536: the typesetter each time ! 537: .UC TROFF ! 538: is run, rather than creating a compiled version for each typesetter. ! 539: Accordingly, a set of description files was designed and created ! 540: for each typesetter and each font. ! 541: The description really comes in two pieces \(em ! 542: a table describing parameters of the machine, ! 543: and a table of widths for each font. ! 544: To illustrate, here is the parameter file for the CAT: ! 545: .P1 .2i ! 546: # Graphic Systems CAT-4 ! 547: res 432 ! 548: hor 1 ! 549: vert 3 ! 550: unitwidth 6 ! 551: sizes 6 7 8 9 10 11 12 14 16 18 20 22 24 28 36 0 ! 552: fonts 4 R I B S ! 553: charset ! 554: \e| \e^ \e- \e_ ! 555: hy bu sq em ru 14 12 34 mi fi fl ff Fi Fl de dg sc fm aa ga ! 556: ul sl *a *b *g *d *e *z *y *h *i *k *l *m *n *c *o *p *r *s ! 557: *t *u *f *x *q *w *A *B *G *D *E *Z *Y *H *I *K *L *M *N *C ! 558: *O *P *R *S *T *U *F *X *Q *W sr ts rn >= <= == ~= ap != -> ! 559: <- ua da eq mu di +- cu ca sb sp ib ip if pd gr no is pt es ! 560: mo pl rg co br ct dd rh lh ** bs or ci lt lb rt rb lk rk bv ! 561: lf rf lc rc ! 562: .P2 ! 563: A ! 564: .CW # ! 565: introduces a comment. ! 566: .CW res ! 567: is the machine resolution in units per inch. ! 568: .CW hor ! 569: and ! 570: .CW vert ! 571: give the minimum number of machine units that it is possible ! 572: to move in the corresponding direction. ! 573: .CW unitwidth ! 574: specifies the point size at which the character widths map directly ! 575: into machine units. ! 576: .CW sizes ! 577: lists the set of legal point sizes, ! 578: terminated by a zero. ! 579: .CW fonts ! 580: lists the default set of fonts ! 581: (which can be overridden by subsequent ! 582: .CW .fp ! 583: commands). ! 584: .CW charset ! 585: introduces the set of legitimate special names ! 586: (names of the form ! 587: .CW \e(xx , ! 588: including some special cases like ! 589: .CW \e| ). ! 590: .PP ! 591: For comparison, here is the description file for the ! 592: Mergenthaler Linotron 202. ! 593: The 202 has many more sizes and fonts than the CAT, ! 594: and quite a few more characters as well. ! 595: (The 202 actually permits nearly 250 sizes; ! 596: no use has yet been found for most of them.) ! 597: .P1 ! 598: # Mergenthaler Linotron 202 ! 599: fonts 10 R I B BI H HB HK PO CH S ! 600: sizes 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ! 601: 21 22 23 24 25 26 27 28 29 30 32 34 36 38 40 ! 602: 45 50 55 60 66 72 78 84 90 96 102 108 0 ! 603: res 972 ! 604: hor 1 ! 605: vert 2 ! 606: unitwidth 4 ! 607: paperwidth 7500 ! 608: charset ! 609: \e| \e^ \e- \e_ ! 610: ** *C *D *F *G *H *L *P *Q *S *W *a *b *c *d *e *f *g *h ! 611: *i *k *l *m *n *p *q *r *s *t *w *x *y *z +- -> <- <= ! 612: == >= L. Sl al aa ap b0 br bs bu bv ca ! 613: cd ci co ct cu dd de dg di em eq es fe fm ga gr hy ! 614: ib if ip is l. lh ma mi mo mu no or pd pl pp pt rg rh ru ! 615: sb sc sl sp sq sr tm tp ts ~= ~~ ul rn en ! 616: lf rf lc rc lt rt lb rb lk rk != ua da 12 fa te ma fe ! 617: hc .. ob bx *o *u b9 14 34 ss vr ! 618: .P2 ! 619: .CW paperwidth ! 620: specifies the maximum width of paper in units, ! 621: overriding the default 7\(34". ! 622: .PP ! 623: In addition to this description file (one per typesetter), ! 624: there is one file per font that lists properties of that font. ! 625: Here is part of the description of Times Roman ! 626: for the CAT: ! 627: .P1 ! 628: # Times Roman for CAT-4 ! 629: name R ! 630: internalname 1 ! 631: ligatures ff fi fl ffi ffl 0 ! 632: charset ! 633: \e| 6 0 0 ! 634: \e^ 3 0 0 ! 635: a 17 0 025 ! 636: b 20 2 012 ! 637: c 16 0 027 ! 638: d 20 2 011 ! 639: e 18 0 031 ! 640: f 13 2 014 ! 641: \&... ! 642: ! 12 2 0145 ! 643: & 28 2 050 ! 644: ( 16 2 0132 ! 645: ) 16 2 0133 ! 646: * 16 0 0122 ! 647: + 36 0 0143 ! 648: , 12 0 047 ! 649: hy 13 0 040 hyphen ! 650: - " =hy ! 651: \e- 36 0 0123 ! 652: \&. 10 0 044 ! 653: de 15 0 0136 degree ! 654: dg 20 0 0137 dagger ! 655: fm 8 0 0150 ! 656: rg 20 0 0141 ! 657: co 20 0 0153 ! 658: ct 19 0 0127 ! 659: \&... ! 660: .P2 ! 661: .PP ! 662: The ! 663: .CW name ! 664: is the external ! 665: .UC TROFF ! 666: name, one or two characters, ! 667: as used in ! 668: .CW .ft ! 669: and ! 670: .CW \ef ! 671: commands. ! 672: The internal name is not used by ! 673: .UC TROFF ! 674: itself, but is necessary for the postprocessors. ! 675: If the font has ! 676: ligatures, they are listed. ! 677: It is possible to set the width of the space for each font ! 678: separately with the ! 679: .CW spacewidth ! 680: command, ! 681: not illustrated here. ! 682: There is also a keyword ! 683: .CW special ! 684: to indicate that the font is a ``special'' font \(em ! 685: one that is to be searched if the regular font ! 686: does not contain the character requested. ! 687: There is no limit on the number of special fonts, ! 688: but they should be listed last in the ! 689: .CW fonts ! 690: part of the description file. ! 691: .PP ! 692: The four columns of data are ! 693: the character name, ! 694: its width, ! 695: its ascender/descender information ! 696: (1 \(-> descender, 2 \(-> ascender, 3 \(-> both), ! 697: and the actual typesetter code required to print it. ! 698: When the point size is ! 699: .CW unitwidth , ! 700: the width ! 701: is the character width in machine units. ! 702: If the name is a single character, ! 703: it is taken simply as a normal ! 704: .UC ASCII ! 705: character. ! 706: Two or more characters ! 707: indicate a name of the form ! 708: .CW \e(xx ; ! 709: there are a handful of historical exceptions like ! 710: .CW \e- . ! 711: A width of ! 712: .CW \&" ! 713: indicates that the character is a synonym for the ! 714: immediately preceding character, ! 715: as in ! 716: .CW - ! 717: and ! 718: .CW hy ! 719: above. ! 720: Comments may follow the four data fields. ! 721: .PP ! 722: There are significant advantages to having the font and typesetter ! 723: descriptions merely text files that can be edited easily, ! 724: but it is too time-consuming to load all this ! 725: .UC ASCII ! 726: information ! 727: each time ! 728: .UC TROFF ! 729: is invoked. ! 730: Thus a separate program called ! 731: .CW makedev ! 732: is used to compile ! 733: it into a binary file that can be read by ! 734: .UC TROFF ! 735: in a single read. ! 736: When ! 737: .UC TROFF ! 738: is invoked, ! 739: an argument of the form ! 740: .CW -Txxx ! 741: tells it to load the description file for typesetter ! 742: .CW xxx ! 743: from a standard directory. ! 744: .PP ! 745: Descriptions for the default fonts are compiled into the description file; ! 746: if a new font is requested by a ! 747: .CW .fp ! 748: command, ! 749: its description data replaces the original values. ! 750: The ! 751: .CW .ft ! 752: command has been modified so that if the requested font ! 753: is not currently ``mounted'', ! 754: its description data will be placed in the hidden ! 755: font position 0. ! 756: Since there is only one such position, ! 757: each new non-standard font overlays the previous one. ! 758: This mechanism is intended to make ! 759: occasional use of non-standard fonts easy; ! 760: the ! 761: .CW .fp ! 762: mechanism remains necessary for other purposes. ! 763: .PP ! 764: The notion of special fonts has been generalized somewhat \(em ! 765: rather than a single special font, ! 766: there can be several. ! 767: The algorithm currently used is to search for each character ! 768: on the current font; ! 769: if it is not found there, then the special fonts are searched ! 770: in order (as given in the description file). ! 771: Other than that, ! 772: the treatment of special fonts is essentially unchanged. ! 773: .PP ! 774: At this stage in the modifications to ! 775: .UC TROFF , ! 776: all internal arithmetic is done in terms of ! 777: the resolution of the specified typesetter. ! 778: The character set and character widths are defined by ! 779: the values loaded from the font description. ! 780: Indeed, the mapping from character name to what gets printed ! 781: is determined by this table \(em ! 782: fonts are no longer all isomorphic. ! 783: .PP ! 784: Although it is certainly convenient for testing during ! 785: program development, ! 786: it is not clear that loading the font information for ! 787: the typesetter each time ! 788: .UC TROFF ! 789: is invoked is the right way to operate in an environment ! 790: that supports only a single kind of typesetter. ! 791: In a production mode it might be desirable to compile in ! 792: the default information for the standard typesetter. ! 793: .NH ! 794: Output Language ! 795: .PP ! 796: The final step is to modify the output of ! 797: .UC TROFF ! 798: so that it is typesetter-independent. ! 799: The new version of ! 800: .UC TROFF ! 801: produces output that is not intended to go directly to ! 802: a typesetter. ! 803: Rather, it is more or less independent of any typesetter, ! 804: except that the numbers in it have been computed on the ! 805: basis of the resolution specified in the description ! 806: file for the intended typesetter. ! 807: .PP ! 808: The output language is simple: ! 809: .P1 ! 810: .ta .7i ! 811: s\f2n\fP size in points ! 812: f\f2n\fP font as number from 1 to \f2n\fP ! 813: c\f2x\fP ASCII character \f2x\fP ! 814: C\f2xy\fP character \e(\f2xy\fP; terminate \f2xy\fP by white space ! 815: H\f2n\fP go to absolute horizontal position \f2n\fP. (\f2n\fP > 0) ! 816: V\f2n\fP go to absolute vertical position \f2n\fP (down is positive) ! 817: h\f2n\fP go \f2n\fP units horizontally (to the right; \f2n\fP > 0) ! 818: v\f2n\fP go \f2n\fP units vertically (down; \f2n\fP > 0) ! 819: \f2nnc\fP move right \f2nn\fP, then print \f2c\fP (\f2nn\fP is exactly 2 digits!) ! 820: n\f2b a\fP end of line (information only -- no action needed) ! 821: \f2b\fP = space before line, \f2a\fP = after ! 822: w paddable word space (information only -- no action needed) ! 823: p\f2n\fP new page \f2n\fP begins -- set V to 0 ! 824: x ...\en device control functions ! 825: D ...\en drawing functions (graphics) ! 826: .P2 ! 827: Encoding small horizontal motions followed by a character as ! 828: .I nnc ! 829: shrinks the output file size by about 35% ! 830: and run-time by about 15%. ! 831: .PP ! 832: The device control and graphics commands are intended as open-ended ! 833: families, to be expanded as needed. ! 834: .P1 ! 835: .ta .7i ! 836: x i init ! 837: x T \f2s\fP name of typesetter is \f2s\fP ! 838: x r \f2n h v\fP resolution is \f2n\fP/inch, \f2h\fP = minimum horizontal motion, \f2v\fP = min vert ! 839: x p pause (can restart) ! 840: x s stop -- done forever ! 841: x t generate trailer ! 842: x f \f2n s\fP font position \f2n\fP contains font \f2s\fP ! 843: x H \f2n\fP set character height to \f2n\fP ! 844: x S \f2n\fP set slant to \f2n\fP ! 845: .P2 ! 846: Subcommands like ! 847: .CW i '' `` ! 848: are often spelled out like ! 849: .CW init ''. `` ! 850: .PP ! 851: The drawing functions are ! 852: .P1 ! 853: .ta 1i ! 854: Dl \f2dh dv\fP draw line from current position by \f2dh dv\fP ! 855: Dc \f2d\fP draw circle of diameter \f2d\fP with left side here ! 856: De \f2d1 d2\fP draw ellipse of diameters \f2d1 d2\fP ! 857: Da \f2dh1 dv1 dh2 dv2\fP ! 858: draw arc from current position to \f2dh1+dh2 dv1+dv2\fP, ! 859: center at \f2dh1 dv1\fP from current position ! 860: D~ \f2dh1 dv1 dh2 dv2 ...\fP ! 861: draw B-spline from current position to \f2dh1 dv1\fP, ! 862: then to \f2dh2 dv2\fP, then to ... ! 863: .P2 ! 864: In all of these, \f2dh dv\fP is an increment on the current horizontal and ! 865: vertical position, ! 866: with down and right positive. ! 867: .PP ! 868: Blanks, tabs and newlines may occur as separators ! 869: in the input, and are mandatory to separate constructions ! 870: that would otherwise be confused. ! 871: .PP ! 872: To illustrate, the following is the output from the input ! 873: .P1 ! 874: hello ! 875: \&.br ! 876: \&.ps 20 ! 877: \&.ft H ! 878: goodbye ! 879: .P2 ! 880: using ! 881: .CW -Tcat : ! 882: .P1 ! 883: x T cat ! 884: x res 432 1 3 ! 885: x init ! 886: x font 1 R ! 887: x font 2 I ! 888: x font 3 B ! 889: x font 4 S ! 890: V0 ! 891: p1 ! 892: s10 ! 893: f1 ! 894: H416 ! 895: V72 ! 896: ch ! 897: 35e30l17l17on72 0 ! 898: x font 0 H ! 899: f1 ! 900: H416 ! 901: f0 ! 902: s20 ! 903: V144 ! 904: cg ! 905: 70o70o70d73b73y67en72 0 ! 906: x trailer ! 907: V4752 ! 908: x stop ! 909: .P2 ! 910: Here is the same output from the 202. ! 911: Notice how the numbers are more than twice as big, ! 912: reflecting the higher resolution of the 202. ! 913: Also notice that the font ! 914: .CW H ! 915: is a standard font on the 202 so no special loading is needed ! 916: to switch to it. ! 917: .P1 ! 918: x T 202 ! 919: x res 972 1 2 ! 920: x init ! 921: x font 1 R ! 922: x font 2 I ! 923: x font 3 B ! 924: x font 4 BI ! 925: x font 5 H ! 926: x font 6 HB ! 927: x font 7 HK ! 928: x font 8 PO ! 929: x font 9 CH ! 930: x font 10 S ! 931: V0 ! 932: p1 ! 933: s10 ! 934: f1 ! 935: H936 ! 936: V156 ! 937: ch ! 938: 73e63l35l35on156 0 ! 939: H936 ! 940: f5 ! 941: s20 ! 942: V312 ! 943: cg ! 944: h150co ! 945: h150co ! 946: h150cd ! 947: h150cb ! 948: h150cy ! 949: h135ce ! 950: n156 0 ! 951: x trailer ! 952: V10692 ! 953: x stop ! 954: .P2 ! 955: .PP ! 956: The output is guaranteed to be ! 957: .UC ASCII , ! 958: and thus amenable to processing by all the normal ! 959: Unix ! 960: tools. ! 961: The language is simple \(em ! 962: it is straightforward to write a prototype driver ! 963: for a particular typesetter, ! 964: especially when one can steal an existing one ! 965: as a model, ! 966: although it may take some effort to make one of production quality. ! 967: This form also demystifies ! 968: .UC TROFF ! 969: output ! 970: and makes it possible for anyone to write other programs ! 971: to process or generate it. ! 972: .PP ! 973: On the other hand, ! 974: it is about twice as voluminous as the output intended for ! 975: the CAT. ! 976: Cleverness (or sacrificing ! 977: .UC ASCII -ness) ! 978: could bring ! 979: it down to not much more than one byte per character printed, ! 980: but so far I have not felt this to be very important. ! 981: .PP ! 982: There is not really much to say about the post-processors. ! 983: .UC D202 ! 984: drives the 202, ! 985: .UC DCAT ! 986: drives the CAT (although not very efficiently \(em ! 987: the vital boustrophedon and size-sorting features are not there), ! 988: .UC DAPS ! 989: drives the APS-5, ! 990: .UC DCAN ! 991: drives the Canon, ! 992: and ! 993: .UC TC ! 994: (notice the parallel name structure) ! 995: drives the Tektronix 4014. ! 996: There are also rough and ready drivers for other display terminals. ! 997: .PP ! 998: Since it is easy to identify page and line boundaries, ! 999: some drivers are able to offer useful features ! 1000: that were not feasible with the older ! 1001: .UC TROFF . ! 1002: For example, ! 1003: .UC TC ! 1004: permits the user to ask for specific pages by number, ! 1005: and to skip back and forth in the document. ! 1006: .UC D202 ! 1007: displays the output page number on the operator's control panel and will also ! 1008: stop at specified pages if requested. ! 1009: An experimental postprocessor ! 1010: called ! 1011: .UC DSORT ! 1012: sorts the intermediate language by vertical position, ! 1013: to minimize vertical motion; ! 1014: it is intended for printing complicated ! 1015: .UC PIC ! 1016: diagrams and for circuit diagrams produced by ! 1017: .UC PLTROFF . ! 1018: .UC PLTROFF \& ( ! 1019: converts the standard Unix ! 1020: plot language into ! 1021: .UC TROFF ! 1022: commands.) ! 1023: All of the post-processors can scan their input relatively quickly ! 1024: to print only selected pages. ! 1025: .NH ! 1026: New Things ! 1027: .PP ! 1028: During the process of making these changes to ! 1029: .UC TROFF , ! 1030: I have made some changes to the program ! 1031: that are visible to users ! 1032: (thus violating the avowed goal of keeping it compatible). ! 1033: .PP ! 1034: Some changes are simply the easing of restrictions. ! 1035: For example, ! 1036: there is no longer any limit on the number of fonts ! 1037: that can occur on a page ! 1038: (if one's typesetter is up to it), ! 1039: nor does the ! 1040: .CW .fp ! 1041: command that requests ``mounting'' a new font ! 1042: have to occur only at the beginning. ! 1043: Furthermore, a font may be accessed merely by naming it \(em ! 1044: the command ! 1045: .P1 ! 1046: \ef(ARBell Laboratories\efP ! 1047: .P2 ! 1048: will produce ! 1049: .P1 ! 1050: \f(ARBell Laboratories\fP ! 1051: .P2 ! 1052: (The font ! 1053: .CW AR ! 1054: is Avant-Garde Book. ! 1055: Obviously two-letter font names will have to go.) ! 1056: .PP ! 1057: Character sets can be sensibly defined ! 1058: since they no longer have to all be isomorphic. ! 1059: Thus, for example, on the 202 the ``printout'' font ! 1060: .P1 ! 1061: This one ! 1062: .P2 ! 1063: includes a complete ! 1064: .UC ASCII ! 1065: alphabet. ! 1066: All of the characters have the same width, ! 1067: even the word space ! 1068: (defined by the ! 1069: .CW spacewidth ! 1070: attribute in the font description file). ! 1071: Thus this font is suitable for printing program listings ! 1072: without any special precautions. ! 1073: On the other hand, ! 1074: some fonts are rather small, ! 1075: containing only a handful of characters. ! 1076: .PP ! 1077: Larger and smaller point sizes are available, ! 1078: although there are still some limitations. ! 1079: For example, ! 1080: .CW \es72 ! 1081: is still parsed as a 7 point ! 1082: .CW 2 , ! 1083: since changing this would affect at least ! 1084: .UC EQN ! 1085: and probably other programs as well. ! 1086: (Sadly, ! 1087: .UC EQN ! 1088: had to be changed ! 1089: anyway \(em ! 1090: it also accepts an option of the form ! 1091: .CW -Txxx .) ! 1092: But ! 1093: .CW .ps\ 72 ! 1094: works fine: ! 1095: each input size is mapped into the closest legal size ! 1096: for the current typesetter. ! 1097: Fractional point sizes are not allowed ! 1098: and may never be. ! 1099: .PP ! 1100: Character height and slant may be set independently of character ! 1101: width, for those typesetters that allow these operations. ! 1102: .CW \eH'n' ! 1103: sets the character height to ! 1104: .CW n ! 1105: points, ! 1106: .CW \eH'\(+-n' ! 1107: sets it to ! 1108: .CW \(+-n ! 1109: from the current point size, ! 1110: and ! 1111: .CW \eH'0' ! 1112: restores it to normal height. ! 1113: .CW \eS'n' ! 1114: sets the slant to ! 1115: .CW n ! 1116: degrees positive or negative; ! 1117: if ! 1118: .CW n ! 1119: is zero, slanting is turned off. ! 1120: .PP ! 1121: Some obsolete commands have been eliminated ! 1122: (e.g., ! 1123: .CW .fz , ! 1124: .CW .li ; ! 1125: the command line options for constant-size printing and ! 1126: suppressing boustrophedon; ! 1127: all code related to ! 1128: .UC GCOS ). ! 1129: .PP ! 1130: A new command ! 1131: .CW .sy ! 1132: has been added to permit calling another program from ! 1133: within ! 1134: .UC TROFF : ! 1135: .P1 ! 1136: \&.sy \f2command line \fP ! 1137: .P2 ! 1138: causes ! 1139: .I "command line" ! 1140: to be executed. ! 1141: The output is ! 1142: .I not ! 1143: automatically collected anywhere, ! 1144: but the new number register ! 1145: .CW \en($$ ! 1146: (the process id of the ! 1147: .UC TROFF ! 1148: process) ! 1149: can be used ! 1150: to create unique file names ! 1151: to be picked up with subsequent ! 1152: .CW .so ! 1153: commands. ! 1154: The built-in string ! 1155: .CW \e*(.T ! 1156: contains the name of the current typesetter ! 1157: obtained from the ! 1158: .CW -T ! 1159: argument or its default. ! 1160: .NH ! 1161: Graphics Commands ! 1162: .PP ! 1163: The most significant new facility is the ability to draw ! 1164: simple graphical objects ! 1165: \(em diagonal lines, circles, ellipses, arcs, and splines \(em ! 1166: in ! 1167: .UC TROFF . ! 1168: .PP ! 1169: The new graphical commands are ! 1170: .P1 ! 1171: .ta 1i ! 1172: \eD'l\f2 dh dv\fP' draw line from current position by \f2dh, dv\fP ! 1173: \eD'c\f2 d\fP' draw circle of diameter \f2d\fP with left side at current position ! 1174: \eD'e\f2 d1 d2\fP' draw ellipse of diameters \f2d1 d2\fP ! 1175: \eD'a\f2 dh1 dv1 dh2 dv2\fP' ! 1176: draw arc from current position to \f2dh1+dh2 dv1+dv2\fP, ! 1177: with center at \f2dh1 dv1\fP from current position ! 1178: \eD'~\f2 dh1 dv1 dh2 dv2 ...\fP' ! 1179: draw B-spline from current position by \f2dh1 dv1\fP ! 1180: then by \f2dh2 dv2\fP, then by \f2dh2, dv2\fP, then ... ! 1181: .P2 ! 1182: For example, the input ! 1183: .CW "\eD'e0.2i 0.1i'" ! 1184: draws the ellipse ! 1185: \D'e.2i .1i'\|, ! 1186: and the input ! 1187: .CW "\eD'l.2i -.1i'\eD'l.1i .1i'" ! 1188: will draw the line ! 1189: \D'l.2i -.1i'\D'l.1i .1i'\|. ! 1190: The position after a graphical object has been drawn is ! 1191: at its ``end'', where for circles and ellipses, the end ! 1192: is at the right side. ! 1193: As with other commands, ! 1194: default units are ems horizontally and line spaces vertically. ! 1195: .PP ! 1196: Realistically, these commands are not intended for direct use, ! 1197: but for preprocessors like ! 1198: .UC PIC ! 1199: and ! 1200: .UC IDEAL . ! 1201: .PP ! 1202: The output generated by these commands is shown in the ! 1203: discussion of ! 1204: the ! 1205: .UC TROFF ! 1206: output language in an earlier section. ! 1207: Rather than drawing the shape in ! 1208: .UC TROFF , ! 1209: a command with the proper parameters is passed through to the device ! 1210: post-processor, ! 1211: which does whatever it can with the request. ! 1212: .NH ! 1213: Loose Ends ! 1214: .PP ! 1215: This version of ! 1216: .UC TROFF ! 1217: has been in use ! 1218: since September of 1979. ! 1219: Most of our experience with it has been on the ! 1220: 202 and Tektronix scopes, but the CAT and APS-5 drivers have been ! 1221: exercised to some degree. ! 1222: .PP ! 1223: As mentioned, there are some obvious things that could ! 1224: be improved or that remain to be added. ! 1225: For instance, ! 1226: .UC TROFF 's ! 1227: bracket-building function ! 1228: .CW \eb ! 1229: really ought to be implemented by postprocessor, ! 1230: so that typesetters like the 202 can draw a character like ! 1231: \v'1'\H'48'{\H'10'\v'-1' directly instead of synthesizing it from pieces. ! 1232: It also appears to be necessary to add ``modes'' ! 1233: to permit graphical objects to be dotted, dashed, etc. ! 1234: .PP ! 1235: Efficiency is always a problem, ! 1236: and is likely to be so forever, ! 1237: especially with the proliferation of preprocessors ! 1238: generating ever more complicated input. ! 1239: Small improvements (perhaps 10 percent) ! 1240: can be had from artifices like better table searching. ! 1241: Placing the temporary file upon which ! 1242: .UC TROFF ! 1243: stores macro definitions in memory is good for another 10-20 percent ! 1244: on machines like the ! 1245: .UC VAX ! 1246: that have enough memory. ! 1247: But no order-of-magnitude speedup is likely without a gross revision ! 1248: of the basic design. ! 1249: .PP ! 1250: It is clear that ! 1251: .UC TROFF ! 1252: ought to be replaced by something better, ! 1253: but, as I said above, ! 1254: it is far from clear how to do the job ! 1255: a lot better. ! 1256: So for better or worse, ! 1257: .UC TROFF ! 1258: is likely to be with us for a long time. ! 1259: .SH ! 1260: Acknowledgements ! 1261: .PP ! 1262: I am deeply indebted to Ken Thompson and Joe Condon, ! 1263: without whose efforts in taming the Mergenthaler Linotron 202 ! 1264: the ! 1265: .UC TROFF ! 1266: modifications discussed here would be irrelevant. ! 1267: Thompson also provided most of the character-generating software. ! 1268: I am also grateful to Chris Van Wyk for the line and circle drawing ! 1269: algorithms and to Theo Pavlidis for the spline algorithm ! 1270: used by all the postprocessors. ! 1271: .[ ! 1272: $LIST$ ! 1273: .] ! 1274: .sp 100 ! 1275: .SH ! 1276: Appendix: Summary of Language Changes ! 1277: .PP ! 1278: This appendix enumerates the changes to the ! 1279: .UC TROFF ! 1280: language since the last printing of ! 1281: the ! 1282: manual. ! 1283: .SH ! 1284: Command line arguments ! 1285: .PP ! 1286: The argument ! 1287: .CW -Txxx ! 1288: loads parameters and character definitions for typesetter ! 1289: .CW xxx , ! 1290: which at the moment is typically one of ! 1291: .CW 202 , ! 1292: .CW aps ! 1293: or ! 1294: .CW cat . ! 1295: .PP ! 1296: .CW -Fxxx ! 1297: causes font information to be loaded from ! 1298: directory ! 1299: .CW xxx ! 1300: instead of the default ! 1301: .CW /usr/lib/font/dev202 . ! 1302: .SH ! 1303: Graphics commands ! 1304: .PP ! 1305: As described in section 7. ! 1306: .SH ! 1307: Other new commands ! 1308: .PP ! 1309: .CW .sy ! 1310: .I commandline ! 1311: executes the command, then returns. ! 1312: Output is not captured anyplace. ! 1313: .PP ! 1314: .CW .cf ! 1315: .I file ! 1316: copies ! 1317: .I file ! 1318: into the ! 1319: .UC TROFF ! 1320: output file at this point, uninterpreted. ! 1321: Havoc ensues unless the motions in the file ! 1322: restore current horizontal and vertical position. ! 1323: This command hasn't been used much, ! 1324: and is probably a bad idea anyway. ! 1325: .PP ! 1326: .CW .pi ! 1327: .I program ! 1328: (pipe the output into ! 1329: .I program ) ! 1330: now works in ! 1331: .UC TROFF ! 1332: as well as ! 1333: .UC NROFF , ! 1334: since it makes somewhat more sense to allow it. ! 1335: .PP ! 1336: .CW \eH'n' ! 1337: sets the character height to ! 1338: .CW n ! 1339: points. ! 1340: A height of the form ! 1341: .I \(+-n ! 1342: is an increment on the current point size; ! 1343: a height of zero restores the height to the point size. ! 1344: .PP ! 1345: .CW \eS'n' ! 1346: sets the slant to ! 1347: .CW n ! 1348: degrees. ! 1349: .I n ! 1350: may be negative. ! 1351: .PP ! 1352: The number register ! 1353: .CW $$ ! 1354: contains the process id of the ! 1355: .UC TROFF ! 1356: process. ! 1357: .PP ! 1358: The string ! 1359: .CW .T ! 1360: contains the name of the current typesetter ! 1361: (e.g., ! 1362: .CW 202 , ! 1363: .CW aps , ! 1364: .CW cat ). ! 1365: .PP ! 1366: The ! 1367: .CW .fp ! 1368: command accepts a third argument that causes the ! 1369: data for the font to be loaded from that directory. ! 1370: This has been added as a first step to allowing dynamic character definitions. ! 1371: .PP ! 1372: The ! 1373: .CW .ft ! 1374: command causes the named font to be loaded on font position 0 ! 1375: (which is in all other ways inaccessible) if the font exists and is not currently mounted ! 1376: by default or by ! 1377: a ! 1378: .CW .fp ! 1379: command. ! 1380: The font must be still or again in position 0 when the line is printed. ! 1381: .PP ! 1382: Transparent mode ! 1383: .CW \e! ) ( ! 1384: has been fixed so that transparent output actually appears ! 1385: in the output; ! 1386: thus special commands can be passed through to postprocessors ! 1387: by witchcraft like ! 1388: .P1 ! 1389: \&.if "\e*(.T"202" \e!x ... ! 1390: .P2 ! 1391: (If this makes no sense to you, ! 1392: you shouldn't be using it anyway.) ! 1393: .SH ! 1394: Deletions ! 1395: .PP ! 1396: The ! 1397: .CW .fz ! 1398: and ! 1399: .CW .li ! 1400: commands are no more. ! 1401: The ! 1402: .CW -p , ! 1403: .CW -g ! 1404: and ! 1405: .CW +n ! 1406: command line arguments have also been eliminated, ! 1407: as has the ! 1408: .CW hp ! 1409: number register.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.