home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / simtel / sigm / vols000 / vol081 / tp.pas < prev    next >
Pascal/Delphi Source File  |  1984-04-29  |  40KB  |  1,525 lines

  1. { Text Processor }
  2.  
  3. { Author: Peter Grogono }
  4.  
  5. {$E- No statement numbers }
  6. {$F- No real overflow/underflow checking (no reals used anyway) }
  7. {$M- No integer multiply/divide check }
  8. {$R- No range and bounds checking }
  9. {$S+ Check stack overflow because dynamic storage is used }
  10. {$U- No range and bounds checking of parameters }
  11.  
  12. program TP;
  13.  
  14. const
  15.  
  16. {$ICONSTS.PAS }
  17.  
  18. { Strings }
  19.  
  20. extin = '.TEX';      { Default input file extension }
  21. extout = '.DOC';     { Default output file extension }
  22. extcon = '.CON';     { Extension for contents file }
  23. extref = '.REF';     { Extension for cross-reference file }
  24. period = '.';        { End of                  }
  25. query = '?';         {        sentence         }
  26. shriek = '!';        {                 markers }
  27. sentgap = '  ';      { Two blanks at end of sentence }
  28. secgap = ' ';        { Two blanks after a section number }
  29. hardblank = '`';     { Non-trivial blank }
  30. underscore = '_';    { Underlining character }
  31. concat = '-';        { Concatenation character }
  32. pagechar = '#';      { Translates to page number in titles }
  33.  
  34. { String lengths.  The most important of these is maxlinelen, which
  35.   determines the maximum possible length of a line of text.  When keeping
  36.   blocks of text, TP uses more than 2 * maxlinelen bytes of memory for each
  37.   line.  Consequently you can reduce the dynamic storage requirements by
  38.   reducing the value of maxlinelen, if your lines will never be as long
  39.   as 120 characters.  }
  40.  
  41. namelen = 14;        { CP/M file name length }
  42. maxlinelen = 120;    { Maximum length of text line }
  43. maxkeylen = 4;       { Maximum length of cross-reference key }
  44.  
  45. { For default values not defined here, see the initialization section
  46.   at the end of the listing.  }
  47.  
  48. { Horizontal control }
  49.  
  50. deffirstmargin = 6;  { Nothing can be printed left of this }
  51. defmaxwidth = 78;    { Width of text on page: 6.5" at 12 cpi }
  52. deflindent = 5;      { Indentation for list numbers }
  53. deflincr = 6;        { Additional indentation for list items }
  54. defparindent = 5;    { Indentation at start of paragraph }
  55. defdisindent = 10;   { Indentation for displays }
  56. deftabgap = 8;       { Tabs at 8, 16, 24, ... } 
  57. numpos = 70;         { Position for page # in table of contents }
  58. contmargin = 6;      { Left margin for contents file }
  59. contindent = 8;      { Indentation for contents file }
  60.  
  61. { Vertical control }
  62.  
  63. defleadin = 3;       { Lines between header and text }
  64. defmaxlines = 52;    { Maximum number of text lines on a page:
  65.                          8.7" at 6 lpi }
  66. deflinespacing = 2;  { Default line spacing }
  67. defparspacing = 4;   { Blank lines between paragraphs }
  68. defbhead = 6;        { Blank lines before a subheading }
  69. defahead = 4;        { Blank lines after a subheading }
  70. defbdisp = 3;        { Blank lines before a display }
  71. defadisp = 3;        { Blank lines after a display }
  72. defchapgap = 20;     { Blank lines after a chapter heading }
  73. deflastline = 55;    { Position of footer, relative to start of text }
  74. defminpara = 4;      { These three constants are used to avoid }
  75. defminsubsec = 8;    { starting something new near the bottom of }
  76. defminsec = 8;       { of a page }
  77. contpagsize = 52;    { Line on a page on the contents file }
  78. contlastline = 55;   { Line # for page # in contents file }
  79. contleadin = 3;      { Line feeds at top of contents page }
  80.  
  81. type
  82.  
  83. {$ITYPES.PAS }
  84.  
  85. filename = string namelen;
  86. linetype = string maxlinelen;
  87. pair = array [1..2] of char;
  88.  
  89. { A linerecord stores a line and the environment in which it must be
  90.   formatted.  TP stores a block of text to be 'kept' as a linked list
  91.   of line records.  Line records are also used by the procedures PUSH
  92.   and POP to save an environment.  A floatrecord is used to store an
  93.   entire block of text until it is required for output.  TP maintains
  94.   unprinted floating keeps as a linked list of floatrecords.
  95.  
  96.   There is a global variable corresponding to each field of these records.
  97.   It would be better programming practice to acknowledge this by using
  98.   global records rather than separate variables.  This, however, would
  99.   (1) make the program larger because of the offset addressing required;
  100.   (2) make the program slower for the same reason; and (3) penalize users
  101.   who are not using the features which require dynamic storage.  }
  102.  
  103. lineptr = ^ linerecord;
  104. linerecord = record
  105. suppressing, textonline, breakline : boolean;
  106. line, overline : linetype;
  107. spacing : byte;
  108. next : lineptr
  109. end; { linerecord }
  110.  
  111. floatptr = ^ floatrecord;
  112. floatrecord = record
  113. first, last : lineptr;
  114. keepcount : byte;
  115. next : floatptr
  116. end; { floatrecord }
  117.  
  118. { Cross-reference types }
  119.  
  120. keytype = string maxkeylen;
  121. refptr = ^ refrecord;
  122. refrecord = record
  123. key : keytype;
  124. pagenum : integer;
  125. chapnum, secnum, subsecnum, itemnum, entcount : byte;
  126. left, right : refptr
  127. end; { refrecord }
  128.  
  129. { Internal command codes. AA and ZZ are dummies }
  130.  
  131. codetype = (aa,bd,bf,bk,cc,ce,cx,co,ec,dl,ed,ef,ek,el,ep,
  132. fl,gp,hl,ic,il,im,li,ls,mr,mv,nu,ov,pa,pl,rb,rm,
  133. rr,sb,se,si,sl,sm,sp,ss,su,tc,tl,ts,ul,vl,zr,zz);
  134.  
  135. var
  136.  
  137. { Files }
  138.  
  139. infilename, outfilename, contfilename, refilename : filename;
  140. output, cont : text;
  141.  
  142. { Line buffers }
  143.  
  144. title, footer, line, overline : linetype;
  145.  
  146. { Command character }
  147.  
  148. comchar : char;
  149.  
  150. { Horizontal control }
  151.  
  152. maxwidth, firstmargin, margin, tabgap, parindent, disindent,
  153. listindent, listincr : byte;
  154. textonline, suppressing : boolean;
  155.  
  156. { Vertical control }
  157.  
  158. linesonpage, spacesdone, linespacing, spacing, minpara, minsec, minsubsec,
  159. leadin, maxlines, lastline, parspacing, chapgap, beforehead, afterhead, 
  160. beforedisp, afterdisp, beforeitem, afterlist : byte;
  161. breakline, pageready : boolean;
  162.  
  163. { Table of contents }
  164.  
  165. conttitle : linetype;
  166. contlines, contpage, contchapter, contsection : byte;
  167. contents, pageintc : boolean;
  168.  
  169. { Cross-references }
  170.  
  171. reftable : refptr;
  172. showrefs : boolean;
  173. currkey : keytype;
  174. entcount : byte;
  175.  
  176. { Section numbering }
  177.  
  178. pagenum : integer;
  179. chapnum, secnum, subsecnum : byte;
  180.  
  181. { Keeps and floating keeps }
  182.  
  183. freelist, first, last, stack : lineptr;
  184. firstfloat, lastfloat, freefloat : floatptr;
  185. keepcount : byte;
  186. keeping : boolean;
  187.  
  188. { Displays }
  189.  
  190. displaylevel, dispspacing, savespacing, diswidth, savewidth : byte;
  191.  
  192. { Itemized lists }
  193.  
  194. itemnum : byte;
  195. itemlist : boolean;
  196.  
  197. { Underlining }
  198.  
  199. uscharset : set of char;
  200. underlining : boolean;
  201.  
  202. { Special printer codes }
  203.  
  204. printwarning : boolean;
  205.  
  206. { Miscellaneous counters }
  207.  
  208. spaceleft, wordcount, pagecount : integer;
  209. errorcount : byte;
  210.  
  211. { Constant tables and sets }
  212.  
  213. codetable : array [codetype] of pair;
  214. wordends : set of char;
  215.  
  216. {$IPROCS.PAS }
  217. {$IGETFILES.PAS }
  218.  
  219. { Convert lower case letters to upper case }
  220.  
  221. function upper (ch : char) : char;
  222.  
  223. begin
  224. if ch in ['a'..'z'] then upper := chr(ord(ch) - ord('a') + ord('A'))
  225. else upper := ch
  226. end; { upper }
  227.  
  228. { Create a new file name from a given file name and the extension EXT.  }
  229.  
  230. procedure changext (inname : filename; ext : string255; var name : filename);
  231.  
  232. begin
  233. name := inname;
  234. setlength(name,pred(index(name,period)));
  235. append(name,ext)
  236. end; { changext }
  237.  
  238. { ---------------------- Cross-reference procedures ------------------------ }
  239.  
  240. { Store current global values into specified entry.  }
  241.  
  242. procedure update (ref : refptr);
  243.  
  244. begin
  245. ref^.pagenum := pagenum;
  246. ref^.chapnum := chapnum;
  247. ref^.secnum := secnum;
  248. ref^.subsecnum := subsecnum;
  249. ref^.itemnum := itemnum
  250. end; { update }
  251.  
  252. { Make a new entry or update an old entry in the cross-reference table.  }
  253.  
  254. procedure makentry (key : keytype; var ref : refptr);
  255.  
  256. begin
  257. if ref = nil then
  258. begin new(ref); ref^.left := nil; ref^.right := nil;
  259. ref^.key := key; ref^.entcount := 0; update(ref) end
  260. else
  261. if key < ref^.key then makentry(key,ref^.left)
  262. else
  263. if key > ref^.key then makentry(key,ref^.right)
  264. else update(ref) { old entry }
  265. end; { makentry }
  266.  
  267. { Look up an entry in the table, given the key.  }
  268.  
  269. procedure lookup (key : keytype; root : refptr; var ref : refptr);
  270.  
  271. begin
  272. if root = nil then ref := nil else
  273. if key < root^.key then lookup(key,root^.left,ref) else
  274. if key > root^.key then lookup(key,root^.right,ref)
  275. else ref := root
  276. end; { lookup }
  277.  
  278. { Write cross-reference table to a file.  }
  279.  
  280. procedure writerefs;
  281.  
  282. var
  283. refile : text;
  284.  
  285. { Write a sub-tree of entries to the file.  The sub-tree is traversed
  286.   in pre-order so that re-reading the file will not create a degenerate
  287.   tree.  }
  288.  
  289. procedure putentry (ref : refptr);
  290.  
  291. begin
  292. if ref <> nil then
  293. with ref ^ do
  294. begin
  295. writeln(refile,key,pagenum:6,chapnum:4,secnum:4,
  296. subsecnum:4,itemnum:4,entcount:4);
  297. putentry(left); putentry(right)
  298. end
  299. end; { putentry }
  300.  
  301. begin { writerefs }
  302. changext(infilename,extref,refilename); 
  303. rewrite(refilename,refile); putentry(reftable)
  304. end; { writerefs }
  305.  
  306. { Read a file of cross-references.  }
  307.  
  308. procedure readrefs;
  309.  
  310. var
  311. refile : text;
  312. key : keytype;
  313. ch : char;
  314.  
  315. begin
  316. reftable := nil;
  317. changext(infilename,extref,refilename); reset(refilename,refile);
  318. while not eof(refile) do
  319. begin
  320. setlength(key,0); read(refile,ch);
  321. while ch <> blank do
  322. begin append(key,ch); read(refile,ch) end; { while }
  323. readln(refile,pagenum,chapnum,secnum,subsecnum,itemnum);
  324. pad(key,maxkeylen); makentry(key,reftable)
  325. end { while }
  326. end; { readrefs }
  327.  
  328. procedure putline; forward;
  329.  
  330. { --------------------- Free store and keep management --------------------- }
  331.  
  332. { The next three procedures handle dynamic storage of lines.  There is a
  333.   stack for saving environments and a queue for storing 'kept' text.
  334.   The procedure POP is used to remove a line from the stack or the queue.
  335.   The procedure SAVE is used to insert a line into the stack or the queue,
  336.   it does not do the pointer updating because it doesn't know whether the
  337.   line is to go at the back of a queue or the front of a list.  }
  338.  
  339. procedure save (var ptr : lineptr);
  340.  
  341. begin
  342. if freelist = nil then new(ptr)
  343. else
  344. begin ptr := freelist; freelist := freelist^.next end;
  345. ptr^.suppressing := suppressing; ptr^.textonline := textonline;
  346. ptr^.breakline := breakline; ptr^.line := line; ptr^.overline := overline;
  347. ptr^.spacing := spacing
  348. end; { save }
  349.  
  350. procedure push;
  351.  
  352. var
  353. ptr : lineptr;
  354.  
  355. begin save(ptr); ptr^.next := stack; stack := ptr end; { push }
  356.  
  357. procedure pop (var ptr : lineptr);
  358.  
  359. var
  360. old : lineptr;
  361.  
  362. begin
  363. suppressing := ptr^.suppressing; textonline := ptr^.textonline;
  364. breakline := ptr^.breakline; line := ptr^.line; 
  365. overline := ptr^.overline; spacing := ptr^.spacing;
  366. old := ptr; ptr := ptr^.next; old^.next := freelist; freelist := old
  367. end; { pop }
  368.  
  369. { Reset the keep pointers and count.  This procedure does not affect the
  370.   contents of the keep queue.  }
  371.  
  372. procedure resetkeep;
  373.  
  374. begin first := nil; last := nil; keepcount := 0 end; { resetkeep }
  375.  
  376. { Put a line of text into a keep buffer }
  377.  
  378. procedure keep;
  379.  
  380. var
  381. ptr : lineptr;
  382.  
  383. begin
  384. save(ptr); keepcount := keepcount + spacing;
  385. if first = nil then first := ptr else last^.next := ptr;
  386. last := ptr; ptr^.next := nil
  387. end; { keep }
  388.  
  389. { End a keep.  Write kept lines to output file.  }
  390.  
  391. procedure endkeep;
  392.  
  393. var
  394. ptr : lineptr;
  395.  
  396. begin
  397. ptr := first; resetkeep;
  398. while ptr <> nil do begin pop(ptr); putline end { while }
  399. end; { endkeep }
  400.  
  401. { ------------------------- Table of Contents management ------------------- }
  402.  
  403. { Write a title in the contents file }
  404.  
  405. procedure putconttitle;
  406.  
  407. var
  408. count : byte;
  409.  
  410. begin
  411. writeln(cont,chr(FF));
  412. writeln(cont,blank:contmargin,conttitle);
  413. for count := 1 to contleadin do writeln(cont);
  414. contpage := succ(contpage);
  415. contlines := 0
  416. end; { putcontitle }
  417.  
  418. { End a page of the contents file }
  419.  
  420. procedure endcontpage;
  421.  
  422. begin
  423. while contlines < contlastline do
  424. begin
  425. writeln(cont); contlines := succ(contlines)
  426. end; { while }
  427. writeln(cont,blank:numpos,'C-',contpage:1)
  428. end; { endcontpage }
  429.  
  430. { Write blank lines followed by title or section name to contents file;
  431.   start a new page when necessary.  }
  432.  
  433. procedure putcontline (lines, indent : byte; line : linetype);
  434.  
  435. var
  436. count : byte;
  437. ch : char;
  438.  
  439. begin
  440. if contlines + lines > contpagsize then
  441. begin endcontpage; putconttitle end
  442. else
  443. begin
  444. for count := 1 to lines do writeln(cont);
  445. contlines := contlines + lines
  446. end;
  447. write(cont,blank:indent);
  448. for count := 1 to length(line) do
  449. begin
  450. ch := line[count];
  451. if ch = hardblank then write(cont,blank)
  452. else write(cont,ch)
  453. end; { for }
  454. if pageintc then write(cont,blank:3,pagenum:1)
  455. end; { putcontline }
  456.  
  457. { -------------------------- Page layout ----------------------------------- }
  458.  
  459. { Write a running header or footer }
  460.  
  461. procedure writerunner (runner : linetype);
  462.  
  463. var
  464. i : byte;
  465. ch : char;
  466.  
  467. begin
  468. write(output,blank:firstmargin);
  469. for i := 1 to length(runner) do
  470. begin
  471. ch := runner[i];
  472. if ch = hardblank then write(output,blank)
  473. else
  474. if ch = pagechar then write(output,pagenum:1)
  475. else write(output,ch)
  476. end; { for }
  477. writeln(output)
  478. end; { writerunner }
  479.  
  480. { Start a new page and write header on it.  If there are any floating keeps
  481.   in the list, as many are printed as will fit on the page.  When a floating
  482.   keep has been printed out the memory that it occupied is reclaimed.  }
  483.  
  484. procedure startpage;
  485.  
  486. var
  487. count : byte;
  488. float : floatptr;
  489. done : boolean;
  490.  
  491. begin
  492. writeln(output,chr(FF)); writerunner(title);
  493. for count := 1 to leadin do writeln(output);
  494. pagenum := succ(pagenum); pagecount := succ(pagecount);
  495. linesonpage := 0; pageready := true; done := false;
  496. repeat
  497. if firstfloat = nil then done := true
  498. else
  499. begin
  500. count := firstfloat^.keepcount;
  501. if (count + linesonpage > maxlines) and (count <= maxlines)
  502. then done := true { Not enough space }
  503. else
  504. begin
  505. push; first := firstfloat^.first; last := firstfloat^.last; 
  506. keepcount := count; endkeep; float := firstfloat; firstfloat := float^.next;
  507. float^.next := freefloat; freefloat := float; pop(stack)
  508. end
  509. end
  510. until done
  511. end; { startpage }
  512.  
  513. { End a page by filling it with blank lines and writing footer }
  514.  
  515. procedure endpage;
  516.  
  517. begin
  518. if pageready then
  519. begin
  520. while linesonpage < lastline do
  521. begin writeln(output); linesonpage := succ(linesonpage) end; { while }
  522. writerunner(footer);
  523. pageready := false
  524. end
  525. end; { endpage }
  526.  
  527. { Any floating keeps must be released at the end of a chapter and at
  528.   the end of the text.  }
  529.  
  530. procedure endchap;
  531.  
  532. begin
  533. putline; endpage;
  534. while firstfloat <> nil do begin startpage; endpage end { while }
  535. end; { endchap }
  536.  
  537. { -------------------------- Output management ----------------------------- }
  538.  
  539. { Initialize the current line }
  540.  
  541. procedure resetline;
  542.  
  543. begin
  544. setlength(line,0); setlength(overline,0); 
  545. spacing := linespacing; textonline := false; breakline := false
  546. end; { resetline }
  547.  
  548. { Output a completed line.  Where the line goes depends on whether
  549.   we are "keeping" or not.  Output blank lines after the line 
  550.   according to the value of SPACING.  Reset the line buffers. }
  551.  
  552. procedure putline;
  553.  
  554. var
  555. ch : char;
  556. count : byte;
  557.  
  558. { Write the left margin.  No user text can appear in margin, but it is used
  559.   for cross-reference entries if \ZR is called.  }
  560.  
  561. procedure writemargin;
  562.  
  563. begin
  564. if showrefs and (length(currkey) > 0)
  565. then
  566. begin
  567. write(output,currkey,blank:firstmargin - maxkeylen); setlength(currkey,0)
  568. end
  569. else write(output,blank:firstmargin)
  570. end; { writemargin }
  571.  
  572. begin { putline }
  573. if keeping then keep
  574. else
  575. begin
  576. if textonline or not suppressing then
  577. begin
  578. if linesonpage >= maxlines then endpage;
  579. if not pageready then startpage;
  580. writemargin;
  581. for count := 1 to length(line) do
  582. begin
  583. ch := line[count];
  584. if ch = hardblank then write(output,blank) else write(output,ch)
  585. end; { for }
  586. if length(overline) > 0
  587. then
  588. begin
  589. write(output,chr(CR)); writemargin; write(output,overline)
  590. end;
  591. spacesdone := 0
  592. end;
  593. while (spacesdone < spacing) and (linesonpage < maxlines) do
  594. begin
  595. writeln(output);
  596. linesonpage := succ(linesonpage); spacesdone := succ(spacesdone)
  597. end; { while }
  598. end;
  599. resetline
  600. end; { putline }
  601.  
  602. { Append one character to a line.  Start a new line if necessary.
  603.   Underline the character if UNDERLINING is true and the character
  604.   is in the underline set. }
  605.  
  606. procedure putchar (ch : char; underlining : boolean);
  607.  
  608. begin
  609. if breakline or (length(line) >= maxwidth) then putline;
  610. if not textonline then pad(line,margin);
  611. append(line,ch);
  612. if underlining and (ch in uscharset) then
  613. begin
  614. pad(overline,pred(length(line)));
  615. append(overline,underscore)
  616. end;
  617. textonline := true
  618. end; { putchar }
  619.  
  620. { Append a positive number to the line buffer without leading
  621.   or trailing blanks. }
  622.  
  623. procedure putnum (var line : string0; num : integer);
  624.  
  625. var
  626. buf : array [1..5] of char;
  627. bp, cp : byte;
  628.  
  629. begin
  630. bp := 0;
  631. repeat
  632. bp := succ(bp);
  633. buf[bp] := chr(num mod 10 + ord('0'));
  634. num := num div 10
  635. until num = 0;
  636. for cp := bp downto 1 do append(line,buf[cp])
  637. end; { putnum }
  638.  
  639. { Append a section number to a line }
  640.  
  641. procedure putsecnum (var line : string0; 
  642. chapnum, secnum, subsecnum : integer);
  643.  
  644. var
  645. trailing : boolean;
  646.  
  647. begin
  648. trailing := false;
  649. if chapnum > 0 then
  650. begin putnum(line,chapnum); trailing := true end;
  651. if secnum > 0 then
  652. begin
  653. if trailing then append(line,period);
  654. putnum(line,secnum); trailing := true
  655. end;
  656. if subsecnum > 0 then
  657. begin
  658. if trailing then append(line,period);
  659. putnum(line,subsecnum)
  660. end
  661. end; { putsecnum }
  662.  
  663. { Append a word to the line buffer.  Separate words by:
  664.     0 blanks if CONCAT character is last but not only character;
  665.     2 blanks if end of sentence;
  666.     1 blank otherwise.  
  667.   If first character is underscore then underline entire word. }
  668.  
  669. procedure putword (word : string255);
  670.  
  671. var
  672. ch, lastchar : char;
  673. wordlen, linelen, count : byte;
  674. space : integer;
  675. underline, concatenate, sentend : boolean;
  676.  
  677. begin
  678. linelen := length(line);
  679. if linelen = 0 then
  680. begin lastchar := blank; sentend := false; concatenate := false end
  681. else
  682. begin
  683. lastchar := line[linelen];
  684. if (lastchar = concat)
  685. and (linelen > 1)
  686. and (line[pred(linelen)] <> blank) 
  687. and (line[pred(linelen)] <> concat)
  688. then
  689. begin
  690. sentend := false; concatenate := true;
  691. setlength(line,pred(linelen))
  692. end
  693. else
  694. begin
  695. sentend := lastchar in [period,query,shriek];
  696. concatenate := false
  697. end
  698. end;
  699. wordlen := length(word);
  700. underline := (wordlen > 1) and (word[1] = underscore);
  701. if underline then wordlen := pred(wordlen);
  702. space := maxwidth - linelen - wordlen;
  703. if breakline or (sentend and (space <= 6))
  704. or (not sentend and (space <= 1)) then putline;
  705. if textonline then
  706. begin
  707. if sentend then append(line,sentgap)
  708. else
  709. if not concatenate then append(line,blank)
  710. end
  711. else pad(line,margin);
  712. if underline then
  713. begin
  714. pad(overline,length(line));
  715. for count := 2 to succ(wordlen) do
  716. begin
  717. ch := word[count];
  718. append(line,ch);
  719. if ch in uscharset
  720. then append(overline,underscore) else append(overline,blank)
  721. end { for }
  722. end
  723. else append(line,word);
  724. textonline := true; wordcount := succ(wordcount)
  725. end; { putword }
  726.  
  727. { Record the need to break a line, and the blank space needed after it }
  728.  
  729. procedure break (spaceneeded : byte);
  730.  
  731. begin
  732. breakline := true;
  733. if spaceneeded > spacing then spacing := spaceneeded
  734. end; { break }
  735.  
  736. { -------------------------- Text Processing ------------------------------- }
  737.  
  738. { Process a file of text.  This procedure calls itself recursively
  739.   to process included files.  Global variables are maintained while
  740.   an included file is processed, but variables local to this
  741.   procedure are saved implicitly on the stack until the included
  742.   file has been processed, and are then restored. }
  743.  
  744. procedure process (infilename : filename);
  745.  
  746. var
  747. input : text;
  748. word : linetype;
  749. ch : char;
  750. inlinecount : integer;
  751.  
  752. { Get a character from the input file.  Translate EOF to NUL (0)
  753.   and EOL to CR.  Count lines read. }
  754.  
  755. procedure getchar;
  756.  
  757. begin
  758. if eof(input) then ch := chr(0)
  759. else
  760. if eoln(input) then
  761. begin
  762. read(input,ch); ch := chr(CR);
  763. inlinecount := succ(inlinecount)
  764. end
  765. else read(input,ch)
  766. end; { getchar }
  767.  
  768. { Get a word from the input file.  The first character is already
  769.   in ch.  A word is terminated by blank, EOL, EOF, or TAB. }
  770.  
  771. procedure getword (var word : string0);
  772.  
  773. begin
  774. setlength(word,0);
  775. repeat
  776. append(word,ch);
  777. getchar
  778. until ch in wordends
  779. end; { getword }
  780.  
  781. { Read and store text up to the end of the input line }
  782.  
  783. procedure getline (var line : string0);
  784.  
  785. begin
  786. while ch <> chr(CR) do begin append(line,ch); getchar end { while }
  787. end; { getline }
  788.  
  789. { -------------------------  Command decoder  ------------------------- }
  790.  
  791. { Called when comchar is encountered in text. }
  792.  
  793. procedure command;
  794.  
  795. var
  796. infilename : filename;
  797. cmd : pair;
  798. code : codetype;
  799. count : byte;
  800. word : linetype;
  801. num : integer;
  802. key : keytype;
  803. ref : refptr;
  804. refcode : char;
  805. float : floatptr;
  806.  
  807. { Report an error }
  808.  
  809. procedure error (message : string255);
  810.  
  811. begin
  812. writeln('Line ',inlinecount:1,', command ',codetable[code],': ',message);
  813. errorcount := succ(errorcount)
  814. end; { error }
  815.  
  816. { Skip over blanks }
  817.  
  818. procedure skip;
  819.  
  820. begin 
  821. while ch = blank do getchar
  822. end; { skip }
  823.  
  824. { Read an unsigned integer.  Skip leading blanks.
  825.   Any non-digit terminates the number. }
  826.  
  827. procedure getnum (var num : integer);
  828.  
  829. begin
  830. num := 0;
  831. skip;
  832. while ch in ['0'..'9'] do
  833. begin
  834. num := 10 * num + ord(ch) - ord('0');
  835. getchar
  836. end { while }
  837. end; { getnum }
  838.  
  839. { Read a number.  The following cases are handled:
  840.     NNN    return value of NNN;
  841.     =      return DEFAULT;
  842.     +NNN   return DEFAULT + NNN;
  843.     -NNN   return DEFAULT - NNN. }
  844.  
  845. procedure getdefnum (var num : integer; default : integer);
  846.  
  847. var
  848. mode : (plus, minus, abs);
  849.  
  850. begin
  851. skip;
  852. if ch = '+' then
  853. begin mode := plus; getchar end
  854. else 
  855. if ch = '-' then
  856. begin mode := minus; getchar end
  857. else mode := abs;
  858. getnum(num);
  859. if (num = 0) and (ch = '=') then
  860. begin num := default; getchar end
  861. else
  862. case mode of
  863. plus : num := default + num;
  864. minus : num := default - num;
  865. abs :
  866. end { case }
  867. end; { getdefnum }
  868.  
  869. { Read a cross-reference key }
  870.  
  871. procedure getkey (var key : string0);
  872.  
  873. begin
  874. setlength(key,0); skip;
  875. while ch in ['a'..'z','A'..'Z','0'..'9'] do
  876. begin
  877. if length(key) < maxkeylen then append(key,ch);
  878. getchar
  879. end; { while }
  880. pad(key,maxkeylen)
  881. end; { getkey }
  882.  
  883. { Set vertical spacing parameters based on the value of linespacing }
  884.  
  885. procedure setspacing (linespacing : byte);
  886.  
  887. begin
  888. parspacing := 2 * linespacing; beforehead := 3 * linespacing;
  889. afterhead := 2 * linespacing; beforedisp := succ(linespacing); 
  890. afterdisp := succ(linespacing); beforeitem := succ(linespacing); 
  891. afterlist := succ(linespacing); dispspacing := linespacing
  892. end; { setspacing }
  893.  
  894. { This procedure is called when the command processor encounters a
  895.   command character that is not followed by a letter; ch contains
  896.   the character following the command character. }
  897.  
  898. procedure putcomchar;
  899.  
  900. var
  901. word : linetype;
  902.  
  903. begin
  904. if suppressing then 
  905. if ch in wordends then putword(comchar) else
  906. begin
  907. setlength(word,0); append(word,comchar);
  908. repeat append(word,ch); getchar
  909. until ch in wordends;
  910. putword(word)
  911. end
  912. else putchar(comchar,underlining)
  913. end; { putcomchar }
  914.  
  915. { Check amount of space on page and start a new page if necessary.
  916.   No effect in keep mode. }
  917.  
  918. procedure check (linesneeded : byte);
  919.  
  920. begin
  921. if not keeping then
  922. begin
  923. if linesonpage + linesneeded > maxlines then endpage;
  924. if not pageready then startpage
  925. end
  926. end; { check }
  927.  
  928. { Start a new paragraph, on a new page if necessary. }
  929.  
  930. procedure startpara (spaceneeded : byte);
  931.  
  932. begin
  933. break(spaceneeded); putline; check(minpara);
  934. pad(line,margin + parindent)
  935. end; { startpara }
  936.  
  937. { Write a subheading.  Write chapter number, section number,
  938.   subsection number if > 0, title.  Title is terminated by
  939.   EOL or command terminator.  Start a new paragraph. }
  940.  
  941. procedure putsubhead (min : byte; numbered : boolean);
  942.  
  943. var
  944. word : linetype;
  945.  
  946. begin
  947. break(beforehead); putline; check(min); setlength(word,0);
  948. if numbered then
  949. begin
  950. putsecnum(word,chapnum,secnum,subsecnum);
  951. if length(word) > 0 then
  952. begin append(word,secgap); putword(word) end
  953. end;
  954. skip;
  955. while ch <> chr(CR) do
  956. begin getword(word); skip; putword(word) end; { while }
  957. if contents and numbered
  958. then putcontline(contsection,contmargin+contindent,line);
  959. startpara(afterhead)
  960. end; { putsubhead }
  961.  
  962. { ---------------------- Command processor --------------------------------- }
  963.  
  964. begin { command }
  965. getchar;
  966. if not (ch in ['a'..'z','A'..'Z']) then putcomchar
  967. else
  968. begin
  969. cmd[1] := upper(ch); getchar;
  970. cmd[2] := upper(ch); getchar;
  971. code := zz; codetable[aa] := cmd;
  972. while codetable[code] <> cmd do code := pred(code);
  973.  
  974. case code of
  975.  
  976. { Illegal commands }
  977.  
  978. aa, zz : error('invalid command code');
  979.  
  980. { BD : Begin display }
  981.  
  982. bd : begin
  983. margin := margin + disindent; break(beforedisp); 
  984. displaylevel := succ(displaylevel);
  985. if displaylevel = 1 then
  986. begin
  987. savespacing := linespacing; linespacing := dispspacing; 
  988. setspacing(linespacing); savewidth := maxwidth; maxwidth := diswidth
  989. end
  990. end;
  991.  
  992. { BF : Begin floating keep }
  993.  
  994. bf : if keeping then error('already keeping')
  995. else
  996. begin push; resetline; keeping := true; keepcount := 0 end;
  997.  
  998. { BK : Begin keep }
  999.  
  1000. bk : if keeping then error('already keeping')
  1001. else
  1002. begin break(0); putline; keeping := true end;
  1003.  
  1004. { CC : Printer control characters }
  1005.  
  1006. cc : begin
  1007. skip;
  1008. while ch in ['0'..'9'] do
  1009. begin
  1010. getnum(num); skip;
  1011. if (1 <= num) and (num <= 31) then write(output,chr(num))
  1012. else
  1013. begin error('invalid control character'); getchar end
  1014. end; { while }
  1015. printwarning := true
  1016. end;
  1017.  
  1018. { CE : Print one line centered }
  1019.  
  1020. ce : begin
  1021. break(0); putline; setlength(word,0); skip; getline(word);
  1022. for count := 1 to (maxwidth - length(word)) div 2 do append(line,blank);
  1023. append(line,word); textonline := true; putline
  1024. end;
  1025.  
  1026. { CH : Start a new chapter }
  1027.  
  1028. cx : begin
  1029. if keeping then error('floating or keeping'); endchap;
  1030. chapnum := succ(chapnum); secnum := 0; subsecnum := 0;
  1031. setlength(title,0); putnum(title,chapnum); append(title,'.  ');
  1032. skip; getline(title); startpage; startpara(chapgap);
  1033. if contents then putcontline(contchapter,contmargin,title)
  1034. end;
  1035.  
  1036. { CO : Comment }
  1037.  
  1038. co : while ch <> chr(CR) do getchar;
  1039.  
  1040. { DL : Set display layout }
  1041.  
  1042. dl : begin
  1043. getdefnum(beforedisp,defbdisp); getdefnum(afterdisp,defadisp);
  1044. getdefnum(dispspacing,linespacing); getdefnum(disindent,defdisindent);
  1045. getdefnum(diswidth,maxwidth)
  1046. end;
  1047.  
  1048. { EC : Set escape character (= command character) }
  1049.  
  1050. ec : begin skip; comchar := ch; getchar end;
  1051.  
  1052. { ED : End display }
  1053.  
  1054. ed : if displaylevel > 0 then
  1055. begin
  1056. if displaylevel = 1 then
  1057. begin
  1058. linespacing := savespacing; setspacing(linespacing); maxwidth := savewidth
  1059. end; 
  1060. margin := margin - disindent; break(afterdisp); 
  1061. displaylevel := pred(displaylevel)
  1062. end
  1063. else error('not displaying');
  1064.  
  1065. { EF : End a floating keep.  If there are no keeps already in the queue
  1066.   and there is room on this page, then print the contents of the keep;
  1067.   otherwise put it in the queue.  }
  1068.  
  1069. ef : if keeping then
  1070. begin
  1071. putline; keeping := false;
  1072. if (firstfloat <> nil)
  1073. or (keepcount + linesonpage > maxlines)
  1074. and (keepcount <= maxlines) then
  1075. begin
  1076. if freefloat = nil then new(float)
  1077. else
  1078. begin float := freefloat; freefloat := freefloat^.next end;
  1079. float^.first := first; float^.last := last; float^.keepcount := keepcount;
  1080. float^.next := nil;
  1081. if firstfloat = nil then firstfloat := float
  1082. else lastfloat^.next := float;
  1083. lastfloat := float; resetkeep
  1084. end
  1085. else endkeep;
  1086. pop(stack)
  1087. end
  1088. else error('not keeping');
  1089.  
  1090. { EK : End keep.  If there is room on the page, then print the keep;
  1091.   otherwise start a new page and then print it.  There may be floating
  1092.   keeps waiting to be printed and so we must go on skipping pages until
  1093.   there is enough space for the keep.  }
  1094.  
  1095. ek : if keeping then
  1096. begin
  1097. putline; keeping := false;
  1098. if keepcount <= maxlines then
  1099. while keepcount + linesonpage > maxlines do
  1100. begin endpage; if not pageready then startpage end; { while }
  1101. endkeep
  1102. end
  1103. else error('not keeping');
  1104.  
  1105. { EL : End a list of items }
  1106.  
  1107. el : begin margin := 0; break(afterlist);
  1108. putline; itemnum := 0; itemlist := false end;
  1109.  
  1110. { EP : End page }
  1111.  
  1112. ep : if keeping then error('illegal in keep')
  1113. else
  1114. begin putline; endpage end;
  1115.  
  1116. { FL : Define new running footer.  The footer is terminated by
  1117.        EOL or command terminator.  No entry in table of contents. }
  1118.  
  1119. fl: begin setlength(footer,0); skip; getline(footer) end;
  1120.  
  1121. { GP : Get page number from keyboard or parameter }
  1122.  
  1123. gp : begin
  1124. skip;
  1125. if ch = query then
  1126. begin
  1127. getchar;
  1128. if pagenum = 0 then
  1129. begin write('Enter page number: '); read(num) end
  1130. else num := succ(pagenum)
  1131. end
  1132. else getnum(num);
  1133. pagenum := pred(num)
  1134. end;
  1135.  
  1136. { HL : Set horizontal layout parameters }
  1137.  
  1138. hl : begin
  1139. getdefnum(firstmargin,deffirstmargin);
  1140. getdefnum(maxwidth,defmaxwidth)
  1141. end;
  1142.  
  1143. { IC : Include named file }
  1144.  
  1145. ic : begin
  1146. setlength(infilename,0); skip; getline(infilename);
  1147. if index(infilename,period) = 0 then append(infilename,extin);
  1148. process(infilename)
  1149. end;
  1150.  
  1151. { IL : Set itemized list layout }
  1152.  
  1153. il : begin
  1154. getdefnum(beforeitem,succ(linespacing));
  1155. getdefnum(afterlist,succ(linespacing));
  1156. getdefnum(listindent,deflindent);
  1157. getdefnum(listincr,deflincr)
  1158. end;
  1159.  
  1160. { IM : Set immediate margin }
  1161.  
  1162. im : begin
  1163. count := length(line); getdefnum(num,count);
  1164. if count >= num then putline; pad(line,pred(num)); margin := num
  1165. end;
  1166.  
  1167. { LI : List item.  Put item number and indent. }
  1168.  
  1169. li : if itemlist then
  1170. begin
  1171. itemnum := succ(itemnum); margin := listindent; break(beforeitem); putline;
  1172. pad(line,margin); putchar('(',false); putnum(line,itemnum); 
  1173. putchar(')',false); margin := margin + listincr; pad(line,pred(margin))
  1174. end
  1175. else error('not in list mode');
  1176.  
  1177. { LS : Set linespacing }
  1178.  
  1179. ls : begin
  1180. getdefnum(linespacing,deflinespacing);
  1181. if (1 <= linespacing) and (linespacing <= 3) then
  1182. begin
  1183. setspacing(linespacing);
  1184. if spacing < linespacing then spacing := linespacing
  1185. end
  1186. else error('value out of range')
  1187. end;
  1188.  
  1189. { MR : make a cross-reference }
  1190.  
  1191. mr : begin getkey(key); currkey := key; makentry(key,reftable) end;
  1192.  
  1193. { MV : Set minimum values for starting something near bottom of page }
  1194.  
  1195. mv : begin 
  1196. getdefnum(minpara,defminpara); getdefnum(minsubsec,defminsubsec);
  1197. getdefnum(minsec,defminsec)
  1198. end;
  1199.  
  1200. { NU : Remove characters from underline set }
  1201.  
  1202. nu : while ch <> chr(CR) do
  1203. begin uscharset := uscharset - [ch]; getchar end; { while }
  1204.  
  1205. { OV : Overlay next two characters }
  1206.  
  1207. ov : begin
  1208. skip;
  1209. if suppressing then append(line,blank);
  1210. pad(overline,length(line));
  1211. append(line,ch); getchar; append(overline,ch); getchar
  1212. end;
  1213.  
  1214. { PA : Start a new paragraph }
  1215.  
  1216. pa : startpara(parspacing);
  1217.  
  1218. { PL : Set paragraph layout }
  1219.  
  1220. pl : begin
  1221. getdefnum(parspacing,defparspacing);
  1222. getdefnum(parindent,defparindent)
  1223. end;
  1224.  
  1225. { RB : Switch to retain blank mode }
  1226.  
  1227. rb : if suppressing then
  1228. begin suppressing := false; underlining := false end
  1229. else error('occurred twice');
  1230.  
  1231. { RM : Put next word in right margin }
  1232.  
  1233. rm : begin
  1234. skip; getword(word);
  1235. if length(line) + length(word) > maxwidth then putline;
  1236. pad(line,maxwidth - length(word)); append(line,word)
  1237. end;
  1238.  
  1239. { RR : Retrieve cross-reference data and print it }
  1240.  
  1241. rr : begin
  1242. skip; refcode := upper(ch); getchar; getkey(key); lookup(key,reftable,ref);
  1243. setlength(word,0);
  1244. if ref = nil then putnum(word,0)
  1245. else
  1246. with ref ^ do
  1247. begin
  1248. entcount := succ(entcount);
  1249. case refcode of
  1250. 'P' : putnum(word,pagenum);
  1251. 'C' : putnum(word,chapnum);
  1252. 'S' : putsecnum(word,chapnum,secnum,subsecnum);
  1253. 'I' : putnum(word,itemnum)
  1254. end { case }
  1255. end;
  1256. while not (ch in wordends) do
  1257. begin append(word,ch); getchar end;
  1258. putword(word)
  1259. end;
  1260.  
  1261. { SB : Switch to suppress blank and EOL mode }
  1262.  
  1263. sb : if suppressing
  1264. then error('occurred twice')
  1265. else suppressing := true;
  1266.  
  1267. { SE : Start section }
  1268.  
  1269. se : begin 
  1270. secnum := succ(secnum); subsecnum := 0; putsubhead(minsec,true) 
  1271. end;
  1272.  
  1273. { SI : Set item number }
  1274.  
  1275. si : if itemlist then error('inside list')
  1276. else
  1277. begin itemlist := true; getnum(itemnum) end;
  1278.  
  1279. { SL : Set subheading layout }
  1280.  
  1281. sl : begin
  1282. getdefnum(beforehead,defbhead); getdefnum(afterhead,defahead)
  1283. end;
  1284.  
  1285. { SM : Set left margin }
  1286.  
  1287. sm : getdefnum(margin,length(line));
  1288.  
  1289. { SP : Force line break and write blank lines. }
  1290.  
  1291. sp : begin getdefnum(count,linespacing); break(count); putline end;
  1292.  
  1293. { SS : Start subsection }
  1294.  
  1295. ss : begin
  1296. if secnum = 0 then error('no section');
  1297. subsecnum := succ(subsecnum); putsubhead(minsubsec,true)
  1298. end;
  1299.  
  1300. { SU : Start unnumbered section }
  1301.  
  1302. su : putsubhead(minsec,false);
  1303.  
  1304. { TC : write a table of contents.  Linespacing in contents file
  1305.        is determined by LS setting when this command is executed. }
  1306.  
  1307. tc : if contents then error('occurred twice')
  1308. else
  1309. begin
  1310. contents := true;
  1311. contsection := linespacing;
  1312. contchapter := 2 * linespacing;
  1313. changext(outfilename,extcon,contfilename);
  1314. rewrite(contfilename,cont);
  1315. setlength(conttitle,0);
  1316. skip;
  1317. if ch = '#' then
  1318. begin pageintc := true; getchar; skip end;
  1319. getline(conttitle); putconttitle
  1320. end;
  1321.  
  1322. { TL : Define new running title.  The title is terminated by
  1323.        EOL or command terminator.  Make an entry in the table
  1324.        of contents.  # will be translated to page number. }
  1325.  
  1326. tl : begin
  1327. setlength(title,0); skip; getline(title);
  1328. if contents then putcontline(contchapter,contmargin,title)
  1329. end;
  1330.  
  1331. { TS : Set tab spacing }
  1332.  
  1333. ts : getdefnum(tabgap,deftabgap);
  1334.  
  1335. { UL : Add characters to underline set }
  1336.  
  1337. ul : while ch <> chr(CR) do
  1338. begin if ch <> blank then uscharset := uscharset + [ch]; getchar end; { while }
  1339.  
  1340. { VL : Set vertical layout parameters }
  1341.  
  1342. vl : begin
  1343. getdefnum(leadin,defleadin); getdefnum(maxlines,defmaxlines);
  1344. getdefnum(lastline,deflastline); getdefnum(chapgap,defchapgap)
  1345. end;
  1346.  
  1347. { ZR : Show references in left margin }
  1348.  
  1349. zr : showrefs := true;
  1350.  
  1351. end; { case }
  1352. skip
  1353. end
  1354. end; { command }
  1355.  
  1356. { ----------------- Main text processing loop ------------------------------ }
  1357.  
  1358. { If suppressing is true (usual case) the input text is processed
  1359.   word by word.  If suppressing is false the text is processed
  1360.   character by character. }
  1361.  
  1362. begin { process }
  1363.  
  1364. writeln(infilename,' opened for input.');
  1365. reset(infilename,input);
  1366. inlinecount := 0;
  1367. getchar;
  1368.  
  1369. while ch <> chr(0) do
  1370. begin
  1371. while ch = comchar do command;
  1372. if suppressing then
  1373. if ch in wordends then getchar
  1374. else
  1375. begin
  1376. getword(word); putword(word)
  1377. end
  1378. else { retaining blanks and line breaks }
  1379. begin
  1380. if ch in wordends then
  1381. begin wordcount := succ(wordcount); underlining := false end;
  1382. if ch = chr(CR) then putline
  1383. else
  1384. if ch = chr(TAB) then
  1385. repeat append(line,blank) until length(line) mod tabgap = 0
  1386. else
  1387. if (ch = underscore) and not underlining then underlining := true
  1388. else putchar(ch,underlining);
  1389. getchar
  1390. end
  1391. end; { while }
  1392.  
  1393. writeln(infilename,' closed on page ',pagenum:1,'; ',
  1394. inlinecount:1,' lines read.')
  1395.  
  1396. end; { process }
  1397.  
  1398. { ------------------------------- Main program ----------------------------- }
  1399.  
  1400. begin
  1401.  
  1402. { Read file names from command line }
  1403.  
  1404. getfilenames(extin,extout);
  1405. if length(infilename) = 0
  1406. then writeln('No input file.')
  1407. else
  1408. begin
  1409.  
  1410. { Read cross-reference file.  This must be done before global variables
  1411.   are initialized because it changes some of them.  }
  1412.  
  1413. readrefs;
  1414.  
  1415. { Initialize keep space }
  1416.  
  1417. freelist := nil; stack := nil; resetkeep;
  1418. firstfloat := nil; lastfloat := nil; freefloat := nil; 
  1419.  
  1420. { Initialize sets.  The underline character set contains all characters
  1421.   except the common punctuation characters; this is to prevent the
  1422.   underlining of a punctuation character that follows an underlined word.
  1423.   Blank and rubout cannot be underlined.  See \UL and \NU. }
  1424.  
  1425. wordends := [blank,chr(0),chr(CR),chr(TAB)];
  1426. uscharset := [chr(33)..chr(126)] - [',','.',';',':','!','?','-','_'];
  1427.  
  1428. { Initialize flags }
  1429.  
  1430. suppressing := true; pageready := false; keeping := false; contents := false; 
  1431. pageintc := false; itemlist := false; underlining := false;
  1432. printwarning := false; showrefs := false;
  1433.  
  1434. { Initialize counters and parameters  }
  1435.  
  1436. linesonpage := 0; pagenum := 0; wordcount := 0; chapnum := 0; secnum := 0; 
  1437. subsecnum := 0; contpage := 0; pagecount := 0; margin := 0; spacesdone := 0; 
  1438. errorcount := 0; itemnum := 0; displaylevel := 0; spaceleft := maxint;
  1439.  
  1440. { Set defaults }
  1441.  
  1442. comchar := '\';                  { Default command character }
  1443.  
  1444. { Set horizontal defaults }
  1445.  
  1446. firstmargin := deffirstmargin;      { Nothing can be printed left of this }
  1447. maxwidth    := defmaxwidth;         { Width of text on page; 6.5" at 12 cpi }
  1448. parindent   := defparindent;        { Paragraph indentation }
  1449. tabgap      := deftabgap;           { Tabs at X where X mod tabgap = 0 }
  1450. diswidth    := maxwidth;            { Default length of displyed lines }
  1451. disindent   := defdisindent;        { Display indentation }
  1452. listindent  := deflindent;          { Indentation for a numbered list }
  1453. listincr    := deflincr;            { Additional indentation for list items }
  1454.  
  1455. { Set vertical defaults }
  1456.  
  1457. leadin      := defleadin;           { Lines between running header and text }
  1458. maxlines    := defmaxlines;         { Maximum # of text lines on a page:
  1459.                                               8.5" at 6 lpi }
  1460. lastline    := deflastline;         { Line #, relative to start of text,
  1461.                                               for footer }
  1462. linespacing := deflinespacing;      { Normal spacing between lines }
  1463. dispspacing := linespacing;         { Line spacing in a display }
  1464. parspacing  := defparspacing;       { Lines before a paragraph }
  1465. beforehead  := defbhead;            { Lines before a heading }
  1466. afterhead   := defahead;            { Lines after a heading }
  1467. beforedisp  := defbdisp;            { Lines before a display }
  1468. afterdisp   := defadisp;            { Lines after a display }
  1469. beforeitem  := succ(deflinespacing);  { Lines before a list item }
  1470. afterlist   := succ(deflinespacing);  { Lines after an itemized list }
  1471. chapgap     := defchapgap;          { Lines before first line of chapter }
  1472. minpara     := defminpara;          { Limit for starting paragraph }
  1473. minsubsec   := defminsubsec;        { Limit for starting subsection }
  1474. minsec      := defminsec;           { Limit for starting section }
  1475.  
  1476. { Initialize line buffers and strings }
  1477.  
  1478. resetline;
  1479. setlength(title,0); setlength(footer,0);
  1480. setlength(currkey,0);
  1481.  
  1482. { Define code mnemonic table }
  1483.  
  1484. codetable[bd] := 'BD'; codetable[bf] := 'BF'; codetable[bk] := 'BK';
  1485. codetable[cc] := 'CC'; codetable[ce] := 'CE'; codetable[cx] := 'CH'; 
  1486. codetable[co] := 'CO'; codetable[dl] := 'DL'; codetable[ec] := 'EC';
  1487. codetable[ed] := 'ED'; codetable[ef] := 'EF'; codetable[ek] := 'EK';
  1488. codetable[el] := 'EL'; codetable[ep] := 'EP'; codetable[fl] := 'FL'; 
  1489. codetable[gp] := 'GP'; codetable[hl] := 'HL'; codetable[ic] := 'IC'; 
  1490. codetable[il] := 'IL'; codetable[im] := 'IM'; codetable[li] := 'LI'; 
  1491. codetable[ls] := 'LS'; codetable[mr] := 'MR'; codetable[mv] := 'MV';
  1492. codetable[nu] := 'NU'; codetable[ov] := 'OV';
  1493. codetable[pa] := 'PA'; codetable[pl] := 'PL'; codetable[rb] := 'RB'; 
  1494. codetable[rm] := 'RM'; codetable[rr] := 'RR'; codetable[sb] := 'SB'; 
  1495. codetable[se] := 'SE'; codetable[si] := 'SI'; codetable[sl] := 'SL'; 
  1496. codetable[sm] := 'SM'; codetable[sp] := 'SP'; codetable[ss] := 'SS'; 
  1497. codetable[su] := 'SU'; codetable[tc] := 'TC'; codetable[tl] := 'TL'; 
  1498. codetable[ts] := 'TS'; codetable[ul] := 'UL'; codetable[vl] := 'VL'; 
  1499. codetable[zr] := 'ZR'; codetable[zz] := 'ZZ';
  1500.  
  1501. { Open the output file }
  1502.  
  1503. writeln(outfilename,' opened for output.');
  1504. rewrite(outfilename,output);
  1505.  
  1506. { Process the input file }
  1507.  
  1508. process(infilename); endchap;
  1509. if contents then endcontpage; if reftable <> nil then writerefs;
  1510.  
  1511. { Display the results }
  1512.  
  1513. writeln(outfilename,': ',pagecount:1,' pages; ',wordcount:1,' words.');
  1514. if contpage > 0 
  1515. then writeln(contfilename,': ',contpage:1,' pages.');
  1516. if space > 0 then writeln('Free memory: ',space:1,' bytes.');
  1517. if errorcount > 0 then writeln('Errors: ',errorcount:1,'.');
  1518. if printwarning then
  1519. begin
  1520. writeln;
  1521. writeln('WARNING: the output file contains printer control characters!')
  1522. end
  1523. end
  1524. end. { TP }
  1525.