home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / simtel / sigm / vols000 / vol085 / runoff.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1984-04-29  |  26.8 KB  |  1,043 lines

  1. {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  2. {+                                +}
  3. {+ PROGRAM TITLE:         RUNOFF ROUTINE            +}
  4. {+                                +}
  5. {+                                +}
  6. {+ SUMMARY:                            +}
  7. {+    Complete instructions are found in file RUNOFF.DOC    +}
  8. {+                                +}
  9. {+  VERSION RECORD                        +}
  10. {+    04/22/82  - added single sheet, continuous sheet, pause,  +}
  11. {+                and message commands. R.E. Penley            +}
  12. {+    04/21/82  - added .OUT command. R.E. Penley        +}
  13. {+    04/17/82  - first complete run under Pascal/Z with no    +}
  14. {+          errors. R.E. Penley                +}
  15. {+    02/19/82    - First attempt at modification for operation   +}
  16. {+          under CP/M operating system. R.E. Penley    +}
  17. {+    01/01/79  - TRW KERNAL OPERATING SYSTEM VERS 1A        +}
  18. {+          MULTIPLE MINICOMPUTER ARCHITECTURE        +}
  19. {+          IR&D PROJECT. Michelle Feraud            +}
  20. {+                                +}
  21. {+ PROGRAMMERS NOTES:                        +}
  22. {+ -Pascal/Z compiler v 4.0 by Ithaca Intersystems.        +}
  23. {+ -The program tries to use as much in line code as possible.  +}
  24. {+  This makes the program much faster since we cut down on    +}
  25. {+  calls to procedures/functions and the extra code associated +}
  26. {+  with procedure calls.                    +}
  27. {+ -Under Pascal/Z the following was observed:            +}
  28. {+  case 1 - conversion of a chr() takes 6 bytes of code.    +}
  29. {+    const                            +}
  30. {+    nl = 10;                        +}
  31. {+    begin                            +}
  32. {+      c := chr(nl);                        +}
  33. {+  case 2 - conversion of a variable takes 7 bytes of code.    +}
  34. {+    var newline: char;                    +}
  35. {+    begin                            +}
  36. {+      newline := chr(10);                    +}
  37. {+      c := newline;                        +}
  38. {+                                +}
  39. {+ -If any changes are made to the source program the        +}
  40. {+  following steps will recompile RUNOFF.PAS (assume dr A:).    +}
  41. {+    pascal runoff                        +}
  42. {+    asmbl main,runoff.aa/rel                +}
  43. {+    era runoff.src                        +}
  44. {+    link /n:runoff runoff/v asl/s /e            +}
  45. {+    era runoff.rel                        +}
  46. {+                                +}
  47. {+  required files are:                        +}
  48. {+     asl.rel, runoff.pas, runinit.p,            +}
  49. {+     runcomm.p, stdopen.p, open.p                +}
  50. {+                                +}
  51. {+ NICE TO HAVE:                        +}
  52. {+ 1. chaining to other text files                +}
  53. {+ 2. ability to read text/data from another file.        +}
  54. {+ 3. read/get inputs from console/disk files.            +}
  55. {+ 4. top and bottom margin settings.                +}
  56. {+ 5. Indent command.                        +}
  57. {+                                +}
  58. {+ BUGS:                            +}
  59. {+ 1. Program does not seem to like blank lines in text files.    +}
  60. {+                                +}
  61. {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  62. PROGRAM RUNOFF;
  63.  
  64. CONST
  65.   DfltLeftMrgn     =  0;    { default left margin  }
  66.   DfltRightMrgn    = 60;    {    "    right margin }
  67.   DfltLineSpacing  =  1;    {    "    line spacing }
  68.   DfltIndent       =  5;    {    "    indent       }
  69.   DftlTestPage       =  0;    {    "    test page    }
  70.   DfltPageSize       = 60;    {    "    page size    }
  71.  
  72.   ZR        = 0;        { ASCII NULL }
  73.   NL        = 10;        { ASCII Line feed CODE / New line }
  74.   FF        = 12;        { ASCII FORM feed CODE }
  75.   CR        = 13;        { ASCII carriage return CODE }
  76.   SPACE        = ' ';
  77.   NmbrArgs    = 8;        { MAX # OF NUMERICAL ARGUMENTS << 04/21/82 >>}
  78.  
  79.   LineLength    = 132;        { Max length of a single "line" }
  80.   MaxBuffer    = 128 * 8;    { use 1K buffers. }    {<<< 04.26.82 >>>}
  81.  
  82.   IDLENGTH    = 12;
  83.   CmdSize    = 4;
  84.   anull        = -maxint;
  85.  
  86. TYPE
  87.   ARGARRAY    = ARRAY [0..NmbrArgs] OF INTEGER;
  88.   cstring    = PACKED ARRAY [1..4] OF CHAR;
  89.   IDENTIFIER    = PACKED ARRAY [1..IDLENGTH] OF CHAR;
  90.   Line        = PACKED ARRAY [1..MaxBuffer] OF CHAR;    {<<< 04.26.82 >>>}
  91.   LISTRECORD    = RECORD
  92.             NUMBER,
  93.             SPACING,
  94.             OFFSET : INTEGER
  95.               END;
  96.  
  97.   {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  98.   {+ NOTE: commands MUST be inserted here in order of most frequent +}
  99.   {+       usage.  Only by trial and error can the correct/most     +}
  100.   {+       correct sequence be found.                    +}
  101.   {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  102.  
  103.   CmdType = (    FIRST,
  104.         CmdJustify,            { MOST USED COMMAND FIRST }
  105.         CmdNoFill,
  106.         CmdParaGraph,
  107.         CmdFill,
  108.         CmdRem,        {Remarks lines added <<< 04.16.82 >>>}
  109.         CmdLeftMrgn,
  110.         CmdHeadLevel,
  111.         CmdNoJustify,
  112.         CmdBreak,
  113.         CmdSkip,
  114.         CmdBlank,
  115.         CmdPage,
  116.         CmdCenter,
  117.         CmdSpacing,
  118.         CmdTitle,
  119.         CmdNumber,
  120.         CmdHeader,
  121.         CmdNoHeader,
  122.         CmdMessage,    {Show message on console device < 04.22.82 >}
  123.         CmdLeftJustify,    {Left Justify CMD added     <<< 04.18.82 >>>}
  124.         CmdSingle,    {Single sheet paper         <<< 04.22.82 >>>}
  125.         CmdCont,    {Continuous sheet paper     <<< 04.22.82 >>>}
  126.         CmdPause,    {Pause await console input  <<< 04.22.82 >>>}
  127.         CmdPageSize,
  128.         CmdTestPage,
  129.         CmdOut,        {Output direct commands to printer }
  130.         CmdRightMrgn,
  131.         CmdTempIndent,    { Temporary indent command }
  132.         CmdPeriod,
  133.         CmdNoPeriod,
  134.         CmdNoNumber,
  135.         CmdList,
  136.         CmdListEntry,
  137.         CmdEndList,
  138.         Invalid,    {Sentinal in CommandTable <<< 04.16.82 >>>}
  139.         Cmdp1c,
  140.         Cmdp2c,
  141.         Cmdp3c,
  142.         Cmdpgs1c,
  143.         Cmdpgs2c );
  144.  
  145. VAR
  146.   Cmdchar    : CHAR;        { character defining the start of a command }
  147.   CCmd        : CmdType;
  148.  
  149.   INBUF        : Line;        { input line buffer }
  150.   ipos        : integer;    { position of cursor in input line }
  151.  
  152.   OUTBUF    : Line;        { output line buffer }
  153.   opos        : integer;    { position of cursor in output line }
  154.  
  155.   CommandTable    : ARRAY [FIRST..Invalid] OF cstring;
  156.  
  157.   Line_count,
  158.   PAGE_count,
  159.   Line_SPACING,
  160.   PARA_SPACING,
  161.   PARA_INDENT,
  162.   PARA_TESTPAGE,
  163.   PAGE_SIZE,
  164.   PAGE_CENTER,
  165.   LEFT_MARGIN,
  166.   RIGHT_MARGIN    : INTEGER;
  167.  
  168.   { FORMATTING FLAGS }
  169.   Headerflag,
  170.   Numberflag,
  171.   Periodflag,
  172.   Single_sheet,                {<<< 04.22.82 >>>}
  173.   Fillflag,
  174.   Justifyflag    : BOOLEAN;
  175.  
  176.   { PARAMETER INITIALIZATION PHASE }
  177.   Setup     : BOOLEAN;
  178.   INITPARAMCommandS : SET OF CmdType;
  179.  
  180.   { HEAD LEVEL DECLARATONS }
  181.   OLDHeadLevel : integer;
  182.   Level        : ARRAY [1..5] OF INTEGER;
  183.  
  184.   { LIST AND BULLET DECLARATIONS }
  185.   LISTLevel : integer;
  186.   LISTPARAM : ARRAY [1..5] OF LISTRECORD;
  187.  
  188.   { FILL AND JUSTIFY DECLARATIONS }
  189.   wrdbuffull,
  190.   STARTOFLine,
  191.   EndOfFile,
  192.   EndOfSENTENCE : BOOLEAN;
  193.   inval,            { indent value }
  194.   tival,            { temp indent value }
  195.   ceval,            { # of lines to center <<< 04.22.82 >>>}
  196.   SPACES,
  197.   WORDLENGTH,
  198.   outwds,
  199.   DIRECTION    : integer;
  200.  
  201.   CURRENT_TITLE : Line;
  202.  
  203.   SENTENCE_ENDERS,
  204.   DIGITS    : SET OF CHAR;
  205.  
  206.   STDIN,            { standard input file }
  207.   STDOUT    : TEXT;        { standard output file }
  208.  
  209.  
  210.     {++++++++++++++++++++++++++++++++++++++++++}
  211.     {+ COMPILER OPTIONS FOR PASCAL/Z COMPILER +}
  212.     {++++++++++++++++++++++++++++++++++++++++++}
  213.  
  214. {$C-}{ control-c checking OFF         }
  215. {$F-}{ floating point error checking OFF }
  216. {$M-}{ integer mult & divd error checking OFF }
  217.  
  218.  
  219.     {************************************}
  220.     {*     GENERAL UTILITY ROUTINES     *}
  221.     {************************************}
  222.  
  223. function toupper ( ch: char ): char;
  224.     external;
  225.  
  226. function max ( x,y: integer ): integer;
  227. begin
  228.   if x>y then max := x
  229.      else max := y
  230. end;
  231.  
  232. function min ( x,y: integer ): integer;
  233. begin
  234.   if x<y then min := x
  235.      else min := y
  236. end;
  237.  
  238.     {*****************************************}
  239.     {*   PROGRAM SPECIFIC UTILITY ROUTINES   *}
  240.     {*****************************************}
  241.  
  242. function EndOfLine ( var buf: line ): boolean;
  243. begin
  244.   EndOfLine := (buf[ipos]=chr(nl));
  245. end;
  246.  
  247.  
  248. PROCEDURE print ( var TEXT: Line );
  249. { Prints the string TEXT to the console device }
  250. { last modified 04/14/82 rep }
  251. VAR    I: integer;
  252.     ch : char;
  253. BEGIN
  254.   i := 1;
  255.   ch := text[i];
  256.   while not ( (ch=chr(nl)) OR (ch=CHR(ZR)) )
  257.     do begin
  258.          write ( ch );
  259.      i := i + 1;
  260.      ch := text[i];
  261.        end;
  262.    writeln;
  263. END{print};
  264.  
  265.  
  266. PROCEDURE HELP;
  267. BEGIN
  268.   writeln;
  269.   writeln('TRY AGAIN:');
  270.   writeln('  RUNOFF INPUTFILE OUTPUTFILE <output to disk file>' );
  271.   writeln('  RUNOFF INPUTFILE            <output to list device>');
  272.   writeln('  RUNOFF INPUTFILE LST:' );
  273.   writeln('  RUNOFF INPUTFILE CON:       <output to console>' );
  274.   writeln;
  275. end{help};
  276.  
  277.  
  278.  
  279.  
  280.     {***************************************}
  281.     {*      I/O BUFFER ROUTINES           *}
  282.     {***************************************}
  283.  
  284. PROCEDURE getc ( VAR ch: char );{$R-}(*** RANGE CHECKING OFF ***)
  285. var    xeoln: boolean;
  286. begin
  287.   xeoln := eoln(stdin);
  288.   EndOfFile := eof(stdin);
  289.   if not EndOfFile then Read(stdin,ch);
  290.   if xeoln or EndOfFile then
  291.      ch := CHR(NL);
  292. end{ getc };            {$R+}(*** RANGE CHECKING ON ***)
  293.  
  294.  
  295. PROCEDURE getline;
  296. (************************************************)
  297. (* GET ONE LINE FROM SOURCE FILE INTO INBUF    *)
  298. (* GLOBAL:                    *)
  299. (*    NL, EndOfFile, MaxBuffer         *)
  300. (************************************************)
  301. var    ch: char;
  302.     ix: integer;
  303. BEGIN                {$R-}
  304.   ix := 0;
  305.   repeat
  306.     ix := ix + 1;
  307.     getc(ch);
  308.     if ORD(ch) > 127 then ch := CHR( ORD(ch)-128 );
  309.     INBUF[ix] := ch;
  310.   until (ch=CHR(NL)) or (EndOfFile) or (ix=MaxBuffer);
  311.   { set cursor position to beginning of input buffer less one }
  312.   ipos := 0;
  313. end{ getline };            {$R+}
  314.  
  315.  
  316. PROCEDURE putc ( C: CHAR );
  317. { WRITE ONE CHAR TO OUTPUT FILE }
  318. begin
  319.   if ( c = CHR(NL) ) then
  320.     writeln(stdout)
  321.   else
  322.     write(stdout,c);    {output the character}
  323. end{ putc };
  324.  
  325.  
  326. PROCEDURE putline { var outbuf: line };
  327. {
  328.   Put current output line to output file.  Line is
  329.   expected to have appropriate end-of-line character
  330.   when received.  Also, keeps track of line count
  331.   AND StartOfLine flag (for fill routines).
  332. }
  333. VAR    I: integer;
  334. BEGIN
  335.   IF ( opos > LEFT_MARGIN ) THEN BEGIN
  336.     FOR I:=1 TO opos
  337.        DO putc ( OUTBUF[I] );
  338.     opos := LEFT_MARGIN;
  339.     FOR I:=1 TO opos
  340.        DO OUTBUF[I] := SPACE;
  341.     STARTOFLine := TRUE;
  342.     Line_count := Line_count + 1;
  343.   END;
  344. END{putline};
  345.  
  346.  
  347. function value { var INBUF: line; var ipos: integer }: INTEGER;
  348. { RETURNS                    }
  349. {    Integer value of source string 'INBUF'  }
  350. {    starting at position "ipos"        }
  351. const    zero = 48; { ordinal value of '0' }
  352. VAR    sign : -1..1;
  353.     NUM  : INTEGER;
  354. BEGIN
  355.   IF INBUF[ipos] = '-' THEN BEGIN
  356.     sign := -1;
  357.     ipos := ipos + 1
  358.   END
  359.   ELSE BEGIN
  360.     sign := 1;
  361.     IF INBUF[ipos] = '+' THEN ipos := ipos + 1
  362.   END;
  363.   NUM := 0;
  364.   REPEAT
  365.     NUM := 10 * NUM + ord(INBUF[ipos]) - zero;
  366.     ipos := ipos + 1
  367.   UNTIL NOT ( INBUF[ipos] IN DIGITS );
  368.   VALUE := NUM * SIGN
  369. END{VALUE};
  370.  
  371.  
  372.     {++++++++++++++++++++++++++++++++++++++++++++++++}
  373.     {+    CHECK BOUNDS AND/OR SET PARAMETERS    +}
  374.     {++++++++++++++++++++++++++++++++++++++++++++++++}
  375.  
  376. PROCEDURE Check_Set ( argtype: CmdType;    { command argument }
  377.                 var val: INTEGER );    { value to check/set }
  378. VAR    I: INTEGER;
  379. BEGIN                {$R-}
  380.   CASE argtype OF
  381.  
  382.     CmdSkip, CmdBlank: { CHECK SKIP & BLANK ARGUMENT }
  383.     val := max ( val,1 ); { always space at least 1 line }
  384.  
  385.     CmdTempIndent :     { CHECK INDENT ARGUMENT }
  386.     IF ( (LEFT_MARGIN+val) < 0 ) THEN
  387.       val := LEFT_MARGIN
  388.     ELSE IF ( (LEFT_MARGIN+val) > (RIGHT_MARGIN-1) ) THEN
  389.       val := 0;
  390.  
  391.     Cmdp1c : { IF NOT NULL RESET PARAGRAPH INDENT }
  392.     IF ( val <> anull ) THEN
  393.       IF ( (LEFT_MARGIN+val) < 0 ) OR
  394.          ( (LEFT_MARGIN+val) > (RIGHT_MARGIN-1) ) THEN
  395.         PARA_INDENT := DfltIndent
  396.       ELSE
  397.         PARA_INDENT := val;
  398.  
  399.     Cmdp2c : { IF NOT NULL RESET PARAGRAPH VERTICAL SPACING }
  400.     IF ( val <> anull ) THEN
  401.       IF ( val < 0 ) THEN
  402.         PARA_SPACING := (Line_SPACING+1) DIV 2
  403.       ELSE
  404.         PARA_SPACING := val;
  405.  
  406.     Cmdp3c : { IF NOT NULL RESET PARAGRAPH TEST PAGE ARGUMENT }
  407.     IF ( val <> anull ) THEN
  408.       IF ( val < 0 ) THEN
  409.         PARA_TESTPAGE := DftlTestPage
  410.       ELSE
  411.         PARA_TESTPAGE := val;
  412.  
  413.     CmdCenter    : { Compute value for page center }
  414.     begin
  415.       ceval := max ( val,1 ); { always center 1 line }
  416.       page_center := ( ( right_margin-left_margin ) DIV 2 ) + left_margin;
  417.     end;
  418.  
  419.     CmdTestPage : { CHECK TESTPAGE ARGUMENT }
  420.     IF NOT ( (val <> anull) AND (val >= 0) ) THEN val := 0;
  421.  
  422.     CmdHeadLevel : { CHECK HeadLevel ARGUMENT }
  423.     begin
  424.       val := max ( val,1 ); { set floor to larger of val or 1 }
  425.       val := min ( val,5 ); { set ceiling to smaller of val or 5 }
  426.     end;
  427.  
  428.     CmdList : { CHECK LIST ARGUMENTS }
  429.     IF ( val < 0 ) THEN val := DfltLineSpacing
  430.  
  431.     CmdLeftMrgn : { RESET LEFT MARGIN & BLANK OUTBUF UP TO LEFT MARGIN }
  432.     BEGIN
  433.       IF ( val < 0 ) OR ( val >= RIGHT_MARGIN ) THEN
  434.         LEFT_MARGIN := DfltLeftMrgn
  435.       ELSE
  436.         LEFT_MARGIN := val;
  437.       FOR I:=1 TO LEFT_MARGIN
  438.         DO OUTBUF[I] := SPACE;
  439.       opos := LEFT_MARGIN;
  440.     END;
  441.  
  442.     CmdRightMrgn : { RESET RIGHT MARGIN. No further than LineLength }
  443.     IF ( val > (LineLength-1) ) OR ( val < LEFT_MARGIN ) THEN
  444.       RIGHT_MARGIN := DfltRightMrgn
  445.     ELSE
  446.       RIGHT_MARGIN := val;
  447.  
  448.    CmdSpacing : { RESET Line SPACING AND PARAGRAPH SPACING }
  449.     begin
  450.       IF ( val < 1 ) OR ( val > 5 ) THEN
  451.         Line_SPACING := DfltLineSpacing
  452.       ELSE
  453.         Line_SPACING := val;
  454.       PARA_SPACING := (Line_SPACING+1) DIV 2;
  455.     end;
  456.  
  457.     Cmdpgs1c: { RESET PAGE SIZE }
  458.     IF ( val < 11 ) THEN
  459.         PAGE_SIZE := DfltPageSize
  460.     ELSE
  461.         PAGE_SIZE := val;
  462.  
  463.     Cmdpgs2c : { IF NOT NULL RESET RIGHT MARGIN }
  464.     IF ( val <> anull ) THEN
  465.       IF ( val > (LineLength-1) ) OR ( val < LEFT_MARGIN ) THEN
  466.         RIGHT_MARGIN := DfltRightMrgn
  467.       ELSE
  468.         RIGHT_MARGIN := val;
  469.  
  470.     CmdNumber : { IF NOT NULL RESET PAGE count }
  471.     IF ( val <> anull ) THEN
  472.       IF ( val > 0 ) THEN
  473.         PAGE_count := val - 1
  474.       ELSE
  475.         PAGE_count := 0
  476.   END{CASE};
  477. END{Check_Set};        {R+}
  478.  
  479.  
  480. PROCEDURE SETTITLE ( argstring : Line );
  481. { REPLACE CURRENT TITLE WITH Command STRING ARGUMENT }
  482. VAR    CTP, STP: integer;
  483. BEGIN
  484.   FOR CTP:=1 TO RIGHT_MARGIN
  485.      DO CURRENT_TITLE[CTP] := SPACE;
  486.   CTP := LEFT_MARGIN + 1;
  487.   STP := 1;
  488.   WHILE ( argstring[STP] <> CHR(ZR) ) AND ( CTP<=RIGHT_MARGIN )
  489.      DO BEGIN
  490.       CURRENT_TITLE[CTP] := argstring[STP];
  491.       CTP := CTP + 1;
  492.       STP := STP + 1
  493.         END
  494. END{SETTITLE};
  495.  
  496.  
  497. FUNCTION ATTOPOFPAGE: BOOLEAN;
  498. { IS CURRENT OutPutLine THE FIRST Line OF TEXT AFTER THE PAGE Head? }
  499. BEGIN
  500.   ATTOPOFPAGE := ( Line_count=5 )
  501. END;
  502.  
  503.  
  504. FUNCTION TEST_PAGE ( argc: INTEGER ): BOOLEAN;
  505. { ARE THERE argc lines LEFT ON THE CURRENT PAGE? }
  506. BEGIN
  507.   TEST_PAGE := ( (PAGE_SIZE-Line_count) >= argc )
  508. END;
  509.  
  510.  
  511. PROCEDURE SKIPLines ( N: INTEGER );
  512. { INSERT N BLANK Lines INTO OUTPUT FILE }
  513. VAR    I: integer;
  514. BEGIN        {$R-}
  515.   IF ( N>0 ) THEN
  516.     FOR I:=1 TO N DO BEGIN
  517.       putc(CHR(NL));
  518.       Line_count := Line_count + 1
  519.     END
  520. END{SKIPLines};    {$R+}
  521.  
  522.  
  523. PROCEDURE PUTPAGEHead;
  524. { PUT CURRENT TITLE AND PAGE NUMBER INTO OUTPUT Line AND PRINT }
  525. VAR    PAGE_NUMBER: INTEGER;
  526. BEGIN        {$R-}
  527.   OUTBUF := CURRENT_TITLE;
  528.   IF ( NUMBERflag ) THEN BEGIN { TRANSLATE AND OUTPUT PAGE NUMBER }
  529.     opos := RIGHT_MARGIN;
  530.     PAGE_NUMBER := PAGE_count;
  531.     REPEAT
  532.     OUTBUF[opos] := CHR((PAGE_NUMBER MOD 10)+48);
  533.     opos := opos - 1;
  534.     PAGE_NUMBER := PAGE_NUMBER DIV 10
  535.     UNTIL ( PAGE_NUMBER=0 );
  536.   END;
  537.  
  538.   OUTBUF[opos-4] := 'P';
  539.   OUTBUF[opos-3] := 'A';
  540.   OUTBUF[opos-2] := 'G';
  541.   OUTBUF[opos-1] := 'E';
  542.   OUTBUF[opos  ] := ' ';
  543.   opos := RIGHT_MARGIN + 1;
  544.   OUTBUF[opos] := CHR(NL);
  545.   putline
  546. END{PUTPAGEHead};    {$R+}
  547.  
  548.  
  549. PROCEDURE NEWPAGE;
  550. { GO TO TOP OF NEW PAGE AND PRINT PAGE Head }
  551. var    dummy: char;
  552. BEGIN            {$R-}
  553.   putc ( CHR(FF) );    {*** assumes printer recognizes formfeed char ***}
  554.   PAGE_count := PAGE_count + 1;
  555.   Line_count := 0;
  556.  
  557.   if single_sheet then begin {pause for operator intervention  << 04.22.82 >>}
  558.     writeln;
  559.     write ( 'Insert new page. Press return to continue. ' );
  560.     readln ( dummy );
  561.   end;
  562.  
  563.   IF Headerflag THEN BEGIN { PRINTED PAGE Head }
  564.      SKIPLines(1);
  565.      PUTPAGEHead;
  566.      SKIPLines(3)
  567.   END
  568.   ELSE { BLANK PAGE Head }
  569.     SKIPLines(5)
  570. END{NEWPAGE};        {$R+}
  571.  
  572.  
  573. PROCEDURE MOVE_opos ( mvarg: INTEGER );
  574. { MOVE OUTPUT Line Cursor position FORWARD OR BACKWARD. A      }
  575. { FORWARD MOVE BLANKS THE Line UP TO THE NEW POSITION OF opos. }
  576. VAR    I: integer;
  577. BEGIN
  578.   IF ( mvarg > 0 ) THEN BEGIN
  579.     opos := opos + 1;
  580.     FOR i:=opos TO (opos+mvarg-1)
  581.       DO OUTBUF[i] := SPACE;
  582.     opos := opos + mvarg - 1
  583.   END
  584.   ELSE IF ( mvarg < 0 ) THEN
  585.     opos := opos + mvarg
  586. END{MOVE_opos};
  587.  
  588.  
  589. PROCEDURE PUTHeadLevel ( NEWHeadLevel: INTEGER; HeadSTRING: Line );
  590. { PUT Head Level NUMBER AND Head Level TITLE INTO OUTPUT Line }
  591. VAR    chars,
  592.     k, I,
  593.     HSP    : integer;
  594.     LevelsOut,
  595.     LevelNUM,
  596.     NUMBER    : INTEGER;
  597. BEGIN            {$R-}
  598.   IF ( NEWHeadLevel<OLDHeadLevel ) THEN { ZERO UNNEEDED Head Level NUMBERS }
  599.      FOR i:=(NEWHeadLevel+1) TO OLDHeadLevel
  600.     DO Level[i] := 0;
  601.   Level[NEWHeadLevel] := Level[NEWHeadLevel] + 1;
  602.   IF ( NEWHeadLevel=1 ) THEN (* WILL PRINT FIRST 2 Head Level NUMBERS *)
  603.      LevelsOut := 2
  604.   ELSE (* WILL PRINT SPECIFIED Head Level NUMBERS *)
  605.      LevelsOut := NEWHeadLevel;
  606.   FOR LevelNUM:=1 TO LevelsOut
  607.     DO BEGIN { PRINT Head Level NUMBERS }
  608.      IF ( LevelNUM <> 1 ) THEN
  609.         OUTBUF[opos] := '.';
  610.      NUMBER := Level[LevelNUM];
  611.       k := number;
  612.       CHARS := 1;
  613.       WHILE ( k>9 )
  614.         DO BEGIN
  615.          k := k DIV 10;
  616.          CHARS := CHARS + 1
  617.            END;
  618.      FOR I:=(opos+CHARS) DOWNTO (opos+1)
  619.        DO BEGIN
  620.         OUTBUF[I] := CHR( (NUMBER MOD 10)+48 );
  621.         NUMBER := NUMBER DIV 10
  622.           END;
  623.      opos := opos + CHARS + 1
  624.        END;
  625.   OLDHeadLevel := NEWHeadLevel;
  626.   IF ( HeadSTRING[1] <> CHR(ZR) ) THEN BEGIN { PRINT Head Level TITLE }
  627.     OUTBUF[opos] := SPACE;
  628.     OUTBUF[opos+1] := SPACE;
  629.     opos := opos + 2;
  630.     HSP := 1;
  631.     WHILE ( HeadSTRING[HSP] <> CHR(ZR) ) AND ( opos<=RIGHT_MARGIN )
  632.        DO BEGIN
  633.          OUTBUF[opos] := HeadSTRING[HSP];
  634.          opos := opos + 1;
  635.          HSP := HSP + 1
  636.       END;
  637.   END;
  638.   OUTBUF[opos] := CHR(NL);
  639.   putline;
  640. END{PUTHeadLevel};    {$R+}
  641.  
  642.  
  643. PROCEDURE STARTLIST ( VAR N: INTEGER );
  644. { INITIALIZE THIS Level OF LIST }
  645. VAR    NEWLEFTMARGIN: INTEGER;
  646. BEGIN
  647.   LISTLevel := LISTLevel + 1;
  648.   WITH LISTPARAM[LISTLevel]
  649.      DO BEGIN
  650.       NUMBER := 0;
  651.       SPACING := N;
  652.       NEWLEFTMARGIN := LEFT_MARGIN + OFFSET;
  653.       Check_Set ( CmdLeftMrgn, NEWLEFTMARGIN );
  654.     END
  655. END{STARTLIST};
  656.  
  657.  
  658. PROCEDURE PUTLISTNUMBER ( LISTTYPE: CmdType );
  659. { TRANSLATE LIST ELEMENT NUMBER INTO CHARACTERS }
  660. VAR    NUMBER: INTEGER;
  661. BEGIN
  662.   OUTBUF[LEFT_MARGIN-2] := '.';
  663.   OUTBUF[LEFT_MARGIN-1] := ' ';
  664.   OUTBUF[LEFT_MARGIN  ] := ' ';
  665.   NUMBER := LISTPARAM[LISTLevel].NUMBER;
  666.   opos := LEFT_MARGIN - 3;
  667.   REPEAT
  668.     OUTBUF[opos] := CHR( (NUMBER MOD 10)+48 );
  669.     NUMBER := NUMBER DIV 10;
  670.     opos := opos - 1;
  671.   UNTIL NUMBER=0;
  672.   opos := LEFT_MARGIN;
  673. END{PUTLISTNUMBER};
  674.  
  675.  
  676. PROCEDURE LISTMEMBER ( LISTTYPE: CMDTYPE );
  677. { SPACE DOWN AND NUMBER A LIST ENTRY }
  678. BEGIN
  679.   WITH LISTPARAM[LISTLevel]
  680.      DO BEGIN
  681.       IF TEST_PAGE ( SPACING+1 ) THEN
  682.         SKIPLINES ( SPACING )
  683.       ELSE
  684.         NEWPAGE;
  685.       NUMBER := NUMBER + 1;
  686.     END;
  687.   PUTLISTNUMBER ( LISTTYPE );
  688. END{LISTMEMBER};
  689.  
  690.  
  691. PROCEDURE STOPLIST;
  692. { TERMINATE THIS Level OF LIST AND RESET TO PRIOR Level }
  693. VAR    NEWLEFTMARGIN: INTEGER;
  694. BEGIN
  695.   WITH LISTPARAM[LISTLevel] DO BEGIN
  696.     IF TEST_PAGE ( SPACING+1 ) THEN
  697.     SKIPLines ( SPACING )
  698.     ELSE
  699.     NEWPAGE;
  700.     NEWLEFTMARGIN := LEFT_MARGIN - OFFSET;
  701.     Check_Set ( CmdLeftMrgn, NEWLEFTMARGIN )
  702.   END;
  703.   LISTLevel := LISTLevel - 1
  704. END{STOPLIST};
  705.  
  706.  
  707. PROCEDURE BREAK;
  708. BEGIN
  709.   putline;
  710.   IF TEST_PAGE ( Line_SPACING ) THEN
  711.     SKIPLines ( Line_SPACING-1 )
  712.   ELSE
  713.     NEWPAGE
  714. END{BREAK};
  715.  
  716.  
  717.     {************************************}
  718.     {*     TEXT PROCESSING ROUTINES     *}
  719.     {************************************}
  720.  
  721.  
  722. PROCEDURE DoText ( var INBUF: Line );
  723. { FORMAT TEXT }
  724. VAR
  725.   wrdbuffer    : Line;
  726.  
  727.  
  728. PROCEDURE PUTCENTERED;
  729. { CENTER TEXT FROM INPUT LINE }
  730. VAR    i,
  731.     width,            { width of input text }
  732.     fudge: integer;        { computed center of input text }
  733. BEGIN
  734.   (*** width := length(INBUF); ***)
  735.   repeat ipos := ipos + 1
  736.   until EndOfLine ( INBUF );
  737.   width := ipos - 1;
  738.  
  739.   (***    Compute center char of line to be centered ***)
  740.   fudge := width DIV 2;
  741.   if odd(width) then { pretty it up }
  742.     fudge := fudge + 1;
  743.  
  744.   (*** Now compute how much to indent to get there ***)
  745.   tival := (page_center - fudge) + 1; { have to add 1 to get off of zero base }
  746.  
  747.   (*** However don't go less than left margin ***)
  748.   tival := max ( tival, (left_margin+1) );
  749.  
  750.   for i:=(left_margin+1) to (tival-1)
  751.     do outbuf[i] := space;
  752.   opos := tival;
  753.   ipos := 1;
  754.   WHILE ( not EndOfLine(INBUF) ) AND ( opos <= RIGHT_MARGIN )
  755.      DO BEGIN { PUT CENTERED TEXT }
  756.       OUTBUF[opos] := INBUF[ipos];
  757.       opos := opos + 1;
  758.       ipos := ipos + 1
  759.     END;
  760.   OUTBUF[opos] := CHR(NL)
  761. END{PUTCENTERED};
  762.  
  763.  
  764. PROCEDURE GETWORD;
  765. { REMOVE A CONTIGUOUS GROUP OF CHARS FROM INPUT Line }
  766. VAR    WBP: integer;
  767. BEGIN
  768.   REPEAT ipos := ipos + 1
  769.   UNTIL INBUF[ipos] <> SPACE;
  770.   IF NOT EndOfLine(INBUF) THEN BEGIN { GET WORD }
  771.     wrdbuffull := FALSE;
  772.     WBP := 1;
  773.     WHILE NOT wrdbuffull
  774.        DO begin
  775.         IF ( EndOfLine(INBUF) ) OR ( INBUF[ipos]=SPACE ) THEN BEGIN
  776.         (* WORD HAS BEEN GOTTEN *)
  777.         wrdbuffull := TRUE;
  778.         WORDLENGTH := WBP - 1;
  779.         EndOfSENTENCE := (wrdbuffer[WORDLENGTH] IN SENTENCE_ENDERS);
  780.         END{IF}
  781.         ELSE BEGIN
  782.         wrdbuffer[WBP] := INBUF[ipos];
  783.         WBP := WBP + 1;
  784.         ipos := ipos + 1
  785.         END{ELSE}
  786.     end{while}
  787.   END{IF}
  788.   ELSE BEGIN {AT END OF INPUT LINE AND NO WORD HAS BEEN GOTTEN} 
  789.     wrdbuffull := FALSE;
  790.     WORDLENGTH := 0;
  791.   END;
  792. END{GETWORD};
  793.  
  794.  
  795. FUNCTION SpaceRemaining: BOOLEAN;
  796. { Is there enough room left in output line for current word? }
  797. BEGIN
  798.   SpaceRemaining := ( (SPACES+WORDLENGTH+opos-1) <= RIGHT_MARGIN )
  799. END;
  800.  
  801.  
  802. procedure justify ( var outbuf: line );
  803. { JUSTIFY OUTPUT LINE OUT TO RIGHT MARGIN.
  804. { ALGORITHM FROM "SOFTWARE TOOLS" BY K & F, PG 241. }
  805. VAR    I, nextra,
  806.     nmbrholes,
  807.     LEFTSIDE,
  808.     RIGHTSIDE,
  809.     BLANKS    : INTEGER;
  810. BEGIN            {$R-}
  811.   { COMPUTE NUMBER OF BLANKS THAT WILL HAVE TO BE INSERTED }
  812.   nextra := (RIGHT_MARGIN+1) - opos;
  813.   IF (nextra>0) AND (outwds>1) THEN BEGIN
  814.     { REVERSE PREVIOUS DIRECTION FOR INSERTING BLANKS }
  815.     DIRECTION := 1 - DIRECTION;
  816.     { COMPUTE # OF HOLES IN WHICH TO ADD BLANKS }
  817.     nmbrholes := outwds - 1;
  818.     LEFTSIDE := opos;
  819.     RIGHTSIDE := RIGHT_MARGIN + 1;
  820.     opos := RIGHTSIDE;
  821.     WHILE ( LEFTSIDE < RIGHTSIDE )
  822.       DO BEGIN { JUSTIFY TEXT }
  823.        OUTBUF[RIGHTSIDE] := OUTBUF[LEFTSIDE];
  824.        IF ( OUTBUF[LEFTSIDE]=' ' ) THEN BEGIN {END OF WORD}
  825.            IF NOT (PERIODflag AND (OUTBUF[LEFTSIDE-1] IN SENTENCE_ENDERS))
  826.          THEN BEGIN { COMPUTE # OF EXTRA BLANKS TO INSERT }
  827.             IF DIRECTION=0 THEN
  828.                 BLANKS := ((nextra-1) DIV nmbrholes) + 1
  829.             ELSE
  830.                 BLANKS := nextra DIV nmbrholes;
  831.             nextra := nextra - BLANKS;
  832.             nmbrholes := nmbrholes - 1;
  833.             FOR I:=1 TO BLANKS
  834.               DO BEGIN { INSERT EXTRA BLANKS }
  835.                 RIGHTSIDE := RIGHTSIDE - 1;
  836.                 OUTBUF[RIGHTSIDE] := ' '
  837.                  END;
  838.               END{IF}
  839.        END{IF};
  840.        LEFTSIDE := LEFTSIDE - 1;
  841.        RIGHTSIDE := RIGHTSIDE - 1
  842.      END{WHILE}
  843.   END{IF}
  844. END{justify};            {$R+}
  845.  
  846.  
  847. PROCEDURE PUTWORD ( var wrdbuffer : line );
  848. {  PUT CURRENT WORD INTO OUTPUT Line. KEEP  }
  849. {  TRACK OF WORD count FOR JUSTIFY ROUTINE. }
  850. VAR    I, WBP: integer;
  851. BEGIN
  852.   IF NOT STARTOFLine THEN BEGIN { SPACING BETWEEN WORDS }
  853.     FOR I:=1 TO SPACES
  854.        DO BEGIN
  855.         OUTBUF[opos] := SPACE;
  856.         opos := opos + 1
  857.       END{FOR}
  858.   END
  859.   ELSE BEGIN { THIS IS THE FIRST WORD ON THE Line }
  860.     STARTOFLine := FALSE;
  861.     outwds := 0;
  862.     opos := opos + 1
  863.   END;
  864.   FOR WBP:=1 TO WORDLENGTH
  865.      DO BEGIN { COPY WORD INTO OUTPUT Line }
  866.       OUTBUF[opos] := wrdbuffer[WBP];
  867.       opos := opos + 1
  868.     END;
  869.   OUTBUF[opos] := CHR(NL);
  870.   outwds := outwds + 1
  871. END{PUTWORD};
  872.  
  873.  
  874. PROCEDURE Fill_Lines;
  875. { Fill AND JUSTIFY ONE OR MORE OUTPUT Lines FROM CURRENT INPUT Line }
  876. VAR    LineFilled: BOOLEAN;
  877.  
  878.  
  879.    PROCEDURE Fill_ONE_Line;
  880.    { Fill OUTPUT Line FROM CURRENT INPUT Line }
  881.    VAR    FINISHED: BOOLEAN;
  882.    BEGIN
  883.      IF NOT wrdbuffull THEN GETWORD;
  884.      LineFilled := FALSE;
  885.      FINISHED := FALSE;
  886.      WHILE NOT FINISHED
  887.        DO BEGIN
  888.         IF ( spaceremaining ) then begin
  889.           if ( WORDLENGTH <> 0 ) then begin
  890.         { CONTINUE FillING Line }
  891.         PUTWORD ( wrdbuffer );
  892.         IF EndOfSENTENCE THEN { SET SPACING BEFORE NEXT WORD }
  893.             SPACES := 2
  894.         ELSE
  895.             SPACES := 1;
  896.         IF NOT EndOfLine(INBUF) THEN
  897.             GETWORD
  898.         ELSE BEGIN { NO MORE WORDS IN THIS INPUT Line }
  899.             FINISHED := TRUE;
  900.             wrdbuffull := FALSE;
  901.         END{else}
  902.           end{if wordlength <> 0}
  903.         END{if spaceremaining}
  904.         ELSE BEGIN { Stop filling line }
  905.         FINISHED := TRUE;
  906.         LineFilled := Not SpaceRemaining;
  907.         END{Stop filling line}
  908.       END
  909.    END{Fill ONE Line};
  910.  
  911. BEGIN {Fill_Lines}
  912.   Fill_ONE_Line;
  913.   WHILE ( LineFilled )
  914.     DO BEGIN
  915.       IF JUSTIFYflag THEN justify ( OUTBUF );
  916.       BREAK;
  917.       Fill_ONE_Line;
  918.        END
  919. END{Fill_Lines};
  920.  
  921.  
  922. PROCEDURE CopyAsIs;
  923. { COPY INPUT Line LITERALLY AS FOUND IN SOURCE FILE }
  924. VAR    LineCOPIED: BOOLEAN;
  925. BEGIN
  926.   LineCOPIED := FALSE;
  927.   WHILE NOT LineCOPIED DO BEGIN
  928.     REPEAT
  929.     opos := opos + 1;
  930.     ipos := ipos + 1;
  931.     OUTBUF[opos] := INBUF[ipos];
  932.     UNTIL (opos=RIGHT_MARGIN) OR ( EndOfLine(INBUF) );
  933.     IF EndOfLine(INBUF) THEN { INPUT Line HAS BEEN COPIED }
  934.     LineCOPIED := TRUE
  935.     ELSE BEGIN { INPUT Line MAY BE TOO LONG, REMAINDER GOES TO NEXT Line }
  936.     IF INBUF[ipos+1]=CHR(NL) THEN {Line IS EXACTLY THE RIGHT SIZE}
  937.         LineCOPIED := TRUE;
  938.     opos := opos + 1;
  939.     OUTBUF[opos] := CHR(NL)
  940.     END;
  941.     BREAK;
  942.   END
  943. END{CopyAsIs};
  944.  
  945.  
  946. BEGIN {DoText}
  947.   if ceval>0 then begin
  948.      PUTCENTERED;
  949.      ceval := ceval - 1;
  950.      BREAK
  951.   END
  952.   ELSE
  953.      IF Fillflag THEN
  954.     Fill_Lines
  955.      ELSE
  956.     CopyAsIs
  957. END{DoText};
  958.  
  959.  
  960. FUNCTION ScanCommand: CmdType;
  961. { REMOVE Command STRING FROM INPUT Line AND SEARCH
  962.   Command Table FOR MATCHING Command TYPE       }
  963. VAR    CommandLine  : cstring;
  964.     CmdIndex     : CmdType;
  965.     hash,
  966.     j,
  967.     cpos         : integer;
  968. BEGIN            {$R-}
  969.   FOR cpos:=1 TO (CmdSize-1)
  970.     DO CommandLine[cpos] := SPACE;
  971.   ipos := 2; { skip CmdChar }
  972.   cpos := 1;
  973.   WHILE ( INBUF[ipos] <> ' ' )
  974.      AND ( not EndOfLine(INBUF) )
  975.         AND ( cpos <= CmdSize )
  976.      DO BEGIN { get Command string }
  977.       CommandLine[cpos] := toupper ( INBUF[ipos] );
  978.       ipos := ipos + 1;
  979.       cpos := cpos + 1
  980.     END{WHILE};
  981.   CommandLine[CmdSize] := CHR(ZR);
  982.  
  983.   { since the table is so short just do a sequential search. <<<04.15.82>>>}
  984.   CmdIndex := FIRST;
  985.   CommandTable[invalid] := CommandLine; { insert the sentinal }
  986.   repeat CmdIndex := SUCC(CmdIndex);
  987.   until CommandTable[CmdIndex]=CommandLine;
  988.   ScanCommand := CmdIndex
  989. END{ScanCommand};        {$R+}
  990.  
  991.  
  992. {$iRUNCOMM.P }
  993.  
  994.  
  995. {$iSTDOPEN.P }
  996.  
  997.  
  998. {$iRUNINIT.P }
  999.  
  1000.  
  1001. BEGIN    {*     MAIN PROGRAM    *}
  1002.   for ipos:=1 to 24 do writeln;
  1003.   WRITELN ( ' RUNOFF' );
  1004.   writeln ( ' CP/M Version 1.0 Created April 30, 1982' );
  1005.   OpenFiles;
  1006.   INITIALIZE{ all parameters now };
  1007.  
  1008. {$C+}{ allow program termination from this section }
  1009.   getline;
  1010.   Setup := TRUE;
  1011.   { PROCESS THOSE Commands THAT AFFECT THE VARIOUS PARAMETER & flag SETTINGS }
  1012.   WHILE NOT EndOfFile AND Setup
  1013.     DO BEGIN
  1014.     IF INBUF[1]=Cmdchar THEN BEGIN
  1015.        CCmd := ScanCommand;
  1016.        IF CCmd IN INITPARAMCommandS THEN BEGIN
  1017.           DoCommand ( INBUF );
  1018.           getline
  1019.        END
  1020.        ELSE { First non-init Command ends setup phase }
  1021.           Setup := FALSE;
  1022.     END
  1023.     ELSE { First text line ends setup phase }
  1024.        Setup := FALSE;
  1025.        END{WHILE};
  1026.  
  1027.   NEWPAGE; { TOP OF FIRST PAGE }
  1028.   WHILE NOT EndOfFile
  1029.      DO BEGIN { PROCEED WITH NORMAL SOURCE FILE PROCESSING }
  1030.       IF INBUF[1]=Cmdchar THEN
  1031.         DoCommand ( INBUF )
  1032.       ELSE
  1033.         DoText ( INBUF );
  1034.       getline
  1035.       {+++ test for break key press here +++}
  1036.     END{WHILE};
  1037.  
  1038.   putline;{ terminate }
  1039.   putc ( CHR(FF) );
  1040.   writeln ( 'End of job.' );
  1041.   writeln;writeln;
  1042. END{ RUNOFF }.
  1043.