{++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++} {+ +} {+ PROGRAM TITLE: RUNOFF ROUTINE +} {+ +} {+ +} {+ SUMMARY: +} {+ Complete instructions are found in file RUNOFF.DOC +} {+ +} {+ VERSION RECORD +} {+ 04/22/82 - added single sheet, continuous sheet, pause, +} {+ and message commands. R.E. Penley +} {+ 04/21/82 - added .OUT command. R.E. Penley +} {+ 04/17/82 - first complete run under Pascal/Z with no +} {+ errors. R.E. Penley +} {+ 02/19/82 - First attempt at modification for operation +} {+ under CP/M operating system. R.E. Penley +} {+ 01/01/79 - TRW KERNAL OPERATING SYSTEM VERS 1A +} {+ MULTIPLE MINICOMPUTER ARCHITECTURE +} {+ IR&D PROJECT. Michelle Feraud +} {+ +} {+ PROGRAMMERS NOTES: +} {+ -Pascal/Z compiler v 4.0 by Ithaca Intersystems. +} {+ -The program tries to use as much in line code as possible. +} {+ This makes the program much faster since we cut down on +} {+ calls to procedures/functions and the extra code associated +} {+ with procedure calls. +} {+ -Under Pascal/Z the following was observed: +} {+ case 1 - conversion of a chr() takes 6 bytes of code. +} {+ const +} {+ nl = 10; +} {+ begin +} {+ c := chr(nl); +} {+ case 2 - conversion of a variable takes 7 bytes of code. +} {+ var newline: char; +} {+ begin +} {+ newline := chr(10); +} {+ c := newline; +} {+ +} {+ -If any changes are made to the source program the +} {+ following steps will recompile RUNOFF.PAS (assume dr A:). +} {+ pascal runoff +} {+ asmbl main,runoff.aa/rel +} {+ era runoff.src +} {+ link /n:runoff runoff/v asl/s /e +} {+ era runoff.rel +} {+ +} {+ required files are: +} {+ asl.rel, runoff.pas, runinit.p, +} {+ runcomm.p, stdopen.p, open.p +} {+ +} {+ NICE TO HAVE: +} {+ 1. chaining to other text files +} {+ 2. ability to read text/data from another file. +} {+ 3. read/get inputs from console/disk files. +} {+ 4. top and bottom margin settings. +} {+ 5. Indent command. +} {+ +} {+ BUGS: +} {+ 1. Program does not seem to like blank lines in text files. +} {+ +} {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++} PROGRAM RUNOFF; CONST DfltLeftMrgn = 0; { default left margin } DfltRightMrgn = 60; { " right margin } DfltLineSpacing = 1; { " line spacing } DfltIndent = 5; { " indent } DftlTestPage = 0; { " test page } DfltPageSize = 60; { " page size } ZR = 0; { ASCII NULL } NL = 10; { ASCII Line feed CODE / New line } FF = 12; { ASCII FORM feed CODE } CR = 13; { ASCII carriage return CODE } SPACE = ' '; NmbrArgs = 8; { MAX # OF NUMERICAL ARGUMENTS << 04/21/82 >>} LineLength = 132; { Max length of a single "line" } MaxBuffer = 128 * 8; { use 1K buffers. } {<<< 04.26.82 >>>} IDLENGTH = 12; CmdSize = 4; anull = -maxint; TYPE ARGARRAY = ARRAY [0..NmbrArgs] OF INTEGER; cstring = PACKED ARRAY [1..4] OF CHAR; IDENTIFIER = PACKED ARRAY [1..IDLENGTH] OF CHAR; Line = PACKED ARRAY [1..MaxBuffer] OF CHAR; {<<< 04.26.82 >>>} LISTRECORD = RECORD NUMBER, SPACING, OFFSET : INTEGER END; {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++} {+ NOTE: commands MUST be inserted here in order of most frequent +} {+ usage. Only by trial and error can the correct/most +} {+ correct sequence be found. +} {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++} CmdType = ( FIRST, CmdJustify, { MOST USED COMMAND FIRST } CmdNoFill, CmdParaGraph, CmdFill, CmdRem, {Remarks lines added <<< 04.16.82 >>>} CmdLeftMrgn, CmdHeadLevel, CmdNoJustify, CmdBreak, CmdSkip, CmdBlank, CmdPage, CmdCenter, CmdSpacing, CmdTitle, CmdNumber, CmdHeader, CmdNoHeader, CmdMessage, {Show message on console device < 04.22.82 >} CmdLeftJustify, {Left Justify CMD added <<< 04.18.82 >>>} CmdSingle, {Single sheet paper <<< 04.22.82 >>>} CmdCont, {Continuous sheet paper <<< 04.22.82 >>>} CmdPause, {Pause await console input <<< 04.22.82 >>>} CmdPageSize, CmdTestPage, CmdOut, {Output direct commands to printer } CmdRightMrgn, CmdTempIndent, { Temporary indent command } CmdPeriod, CmdNoPeriod, CmdNoNumber, CmdList, CmdListEntry, CmdEndList, Invalid, {Sentinal in CommandTable <<< 04.16.82 >>>} Cmdp1c, Cmdp2c, Cmdp3c, Cmdpgs1c, Cmdpgs2c ); VAR Cmdchar : CHAR; { character defining the start of a command } CCmd : CmdType; INBUF : Line; { input line buffer } ipos : integer; { position of cursor in input line } OUTBUF : Line; { output line buffer } opos : integer; { position of cursor in output line } CommandTable : ARRAY [FIRST..Invalid] OF cstring; Line_count, PAGE_count, Line_SPACING, PARA_SPACING, PARA_INDENT, PARA_TESTPAGE, PAGE_SIZE, PAGE_CENTER, LEFT_MARGIN, RIGHT_MARGIN : INTEGER; { FORMATTING FLAGS } Headerflag, Numberflag, Periodflag, Single_sheet, {<<< 04.22.82 >>>} Fillflag, Justifyflag : BOOLEAN; { PARAMETER INITIALIZATION PHASE } Setup : BOOLEAN; INITPARAMCommandS : SET OF CmdType; { HEAD LEVEL DECLARATONS } OLDHeadLevel : integer; Level : ARRAY [1..5] OF INTEGER; { LIST AND BULLET DECLARATIONS } LISTLevel : integer; LISTPARAM : ARRAY [1..5] OF LISTRECORD; { FILL AND JUSTIFY DECLARATIONS } wrdbuffull, STARTOFLine, EndOfFile, EndOfSENTENCE : BOOLEAN; inval, { indent value } tival, { temp indent value } ceval, { # of lines to center <<< 04.22.82 >>>} SPACES, WORDLENGTH, outwds, DIRECTION : integer; CURRENT_TITLE : Line; SENTENCE_ENDERS, DIGITS : SET OF CHAR; STDIN, { standard input file } STDOUT : TEXT; { standard output file } {++++++++++++++++++++++++++++++++++++++++++} {+ COMPILER OPTIONS FOR PASCAL/Z COMPILER +} {++++++++++++++++++++++++++++++++++++++++++} {$C-}{ control-c checking OFF } {$F-}{ floating point error checking OFF } {$M-}{ integer mult & divd error checking OFF } {************************************} {* GENERAL UTILITY ROUTINES *} {************************************} function toupper ( ch: char ): char; external; function max ( x,y: integer ): integer; begin if x>y then max := x else max := y end; function min ( x,y: integer ): integer; begin if x' ); writeln(' RUNOFF INPUTFILE '); writeln(' RUNOFF INPUTFILE LST:' ); writeln(' RUNOFF INPUTFILE CON: ' ); writeln; end{help}; {***************************************} {* I/O BUFFER ROUTINES *} {***************************************} PROCEDURE getc ( VAR ch: char );{$R-}(*** RANGE CHECKING OFF ***) var xeoln: boolean; begin xeoln := eoln(stdin); EndOfFile := eof(stdin); if not EndOfFile then Read(stdin,ch); if xeoln or EndOfFile then ch := CHR(NL); end{ getc }; {$R+}(*** RANGE CHECKING ON ***) PROCEDURE getline; (************************************************) (* GET ONE LINE FROM SOURCE FILE INTO INBUF *) (* GLOBAL: *) (* NL, EndOfFile, MaxBuffer *) (************************************************) var ch: char; ix: integer; BEGIN {$R-} ix := 0; repeat ix := ix + 1; getc(ch); if ORD(ch) > 127 then ch := CHR( ORD(ch)-128 ); INBUF[ix] := ch; until (ch=CHR(NL)) or (EndOfFile) or (ix=MaxBuffer); { set cursor position to beginning of input buffer less one } ipos := 0; end{ getline }; {$R+} PROCEDURE putc ( C: CHAR ); { WRITE ONE CHAR TO OUTPUT FILE } begin if ( c = CHR(NL) ) then writeln(stdout) else write(stdout,c); {output the character} end{ putc }; PROCEDURE putline { var outbuf: line }; { Put current output line to output file. Line is expected to have appropriate end-of-line character when received. Also, keeps track of line count AND StartOfLine flag (for fill routines). } VAR I: integer; BEGIN IF ( opos > LEFT_MARGIN ) THEN BEGIN FOR I:=1 TO opos DO putc ( OUTBUF[I] ); opos := LEFT_MARGIN; FOR I:=1 TO opos DO OUTBUF[I] := SPACE; STARTOFLine := TRUE; Line_count := Line_count + 1; END; END{putline}; function value { var INBUF: line; var ipos: integer }: INTEGER; { RETURNS } { Integer value of source string 'INBUF' } { starting at position "ipos" } const zero = 48; { ordinal value of '0' } VAR sign : -1..1; NUM : INTEGER; BEGIN IF INBUF[ipos] = '-' THEN BEGIN sign := -1; ipos := ipos + 1 END ELSE BEGIN sign := 1; IF INBUF[ipos] = '+' THEN ipos := ipos + 1 END; NUM := 0; REPEAT NUM := 10 * NUM + ord(INBUF[ipos]) - zero; ipos := ipos + 1 UNTIL NOT ( INBUF[ipos] IN DIGITS ); VALUE := NUM * SIGN END{VALUE}; {++++++++++++++++++++++++++++++++++++++++++++++++} {+ CHECK BOUNDS AND/OR SET PARAMETERS +} {++++++++++++++++++++++++++++++++++++++++++++++++} PROCEDURE Check_Set ( argtype: CmdType; { command argument } var val: INTEGER ); { value to check/set } VAR I: INTEGER; BEGIN {$R-} CASE argtype OF CmdSkip, CmdBlank: { CHECK SKIP & BLANK ARGUMENT } val := max ( val,1 ); { always space at least 1 line } CmdTempIndent : { CHECK INDENT ARGUMENT } IF ( (LEFT_MARGIN+val) < 0 ) THEN val := LEFT_MARGIN ELSE IF ( (LEFT_MARGIN+val) > (RIGHT_MARGIN-1) ) THEN val := 0; Cmdp1c : { IF NOT NULL RESET PARAGRAPH INDENT } IF ( val <> anull ) THEN IF ( (LEFT_MARGIN+val) < 0 ) OR ( (LEFT_MARGIN+val) > (RIGHT_MARGIN-1) ) THEN PARA_INDENT := DfltIndent ELSE PARA_INDENT := val; Cmdp2c : { IF NOT NULL RESET PARAGRAPH VERTICAL SPACING } IF ( val <> anull ) THEN IF ( val < 0 ) THEN PARA_SPACING := (Line_SPACING+1) DIV 2 ELSE PARA_SPACING := val; Cmdp3c : { IF NOT NULL RESET PARAGRAPH TEST PAGE ARGUMENT } IF ( val <> anull ) THEN IF ( val < 0 ) THEN PARA_TESTPAGE := DftlTestPage ELSE PARA_TESTPAGE := val; CmdCenter : { Compute value for page center } begin ceval := max ( val,1 ); { always center 1 line } page_center := ( ( right_margin-left_margin ) DIV 2 ) + left_margin; end; CmdTestPage : { CHECK TESTPAGE ARGUMENT } IF NOT ( (val <> anull) AND (val >= 0) ) THEN val := 0; CmdHeadLevel : { CHECK HeadLevel ARGUMENT } begin val := max ( val,1 ); { set floor to larger of val or 1 } val := min ( val,5 ); { set ceiling to smaller of val or 5 } end; CmdList : { CHECK LIST ARGUMENTS } IF ( val < 0 ) THEN val := DfltLineSpacing CmdLeftMrgn : { RESET LEFT MARGIN & BLANK OUTBUF UP TO LEFT MARGIN } BEGIN IF ( val < 0 ) OR ( val >= RIGHT_MARGIN ) THEN LEFT_MARGIN := DfltLeftMrgn ELSE LEFT_MARGIN := val; FOR I:=1 TO LEFT_MARGIN DO OUTBUF[I] := SPACE; opos := LEFT_MARGIN; END; CmdRightMrgn : { RESET RIGHT MARGIN. No further than LineLength } IF ( val > (LineLength-1) ) OR ( val < LEFT_MARGIN ) THEN RIGHT_MARGIN := DfltRightMrgn ELSE RIGHT_MARGIN := val; CmdSpacing : { RESET Line SPACING AND PARAGRAPH SPACING } begin IF ( val < 1 ) OR ( val > 5 ) THEN Line_SPACING := DfltLineSpacing ELSE Line_SPACING := val; PARA_SPACING := (Line_SPACING+1) DIV 2; end; Cmdpgs1c: { RESET PAGE SIZE } IF ( val < 11 ) THEN PAGE_SIZE := DfltPageSize ELSE PAGE_SIZE := val; Cmdpgs2c : { IF NOT NULL RESET RIGHT MARGIN } IF ( val <> anull ) THEN IF ( val > (LineLength-1) ) OR ( val < LEFT_MARGIN ) THEN RIGHT_MARGIN := DfltRightMrgn ELSE RIGHT_MARGIN := val; CmdNumber : { IF NOT NULL RESET PAGE count } IF ( val <> anull ) THEN IF ( val > 0 ) THEN PAGE_count := val - 1 ELSE PAGE_count := 0 END{CASE}; END{Check_Set}; {R+} PROCEDURE SETTITLE ( argstring : Line ); { REPLACE CURRENT TITLE WITH Command STRING ARGUMENT } VAR CTP, STP: integer; BEGIN FOR CTP:=1 TO RIGHT_MARGIN DO CURRENT_TITLE[CTP] := SPACE; CTP := LEFT_MARGIN + 1; STP := 1; WHILE ( argstring[STP] <> CHR(ZR) ) AND ( CTP<=RIGHT_MARGIN ) DO BEGIN CURRENT_TITLE[CTP] := argstring[STP]; CTP := CTP + 1; STP := STP + 1 END END{SETTITLE}; FUNCTION ATTOPOFPAGE: BOOLEAN; { IS CURRENT OutPutLine THE FIRST Line OF TEXT AFTER THE PAGE Head? } BEGIN ATTOPOFPAGE := ( Line_count=5 ) END; FUNCTION TEST_PAGE ( argc: INTEGER ): BOOLEAN; { ARE THERE argc lines LEFT ON THE CURRENT PAGE? } BEGIN TEST_PAGE := ( (PAGE_SIZE-Line_count) >= argc ) END; PROCEDURE SKIPLines ( N: INTEGER ); { INSERT N BLANK Lines INTO OUTPUT FILE } VAR I: integer; BEGIN {$R-} IF ( N>0 ) THEN FOR I:=1 TO N DO BEGIN putc(CHR(NL)); Line_count := Line_count + 1 END END{SKIPLines}; {$R+} PROCEDURE PUTPAGEHead; { PUT CURRENT TITLE AND PAGE NUMBER INTO OUTPUT Line AND PRINT } VAR PAGE_NUMBER: INTEGER; BEGIN {$R-} OUTBUF := CURRENT_TITLE; IF ( NUMBERflag ) THEN BEGIN { TRANSLATE AND OUTPUT PAGE NUMBER } opos := RIGHT_MARGIN; PAGE_NUMBER := PAGE_count; REPEAT OUTBUF[opos] := CHR((PAGE_NUMBER MOD 10)+48); opos := opos - 1; PAGE_NUMBER := PAGE_NUMBER DIV 10 UNTIL ( PAGE_NUMBER=0 ); END; OUTBUF[opos-4] := 'P'; OUTBUF[opos-3] := 'A'; OUTBUF[opos-2] := 'G'; OUTBUF[opos-1] := 'E'; OUTBUF[opos ] := ' '; opos := RIGHT_MARGIN + 1; OUTBUF[opos] := CHR(NL); putline END{PUTPAGEHead}; {$R+} PROCEDURE NEWPAGE; { GO TO TOP OF NEW PAGE AND PRINT PAGE Head } var dummy: char; BEGIN {$R-} putc ( CHR(FF) ); {*** assumes printer recognizes formfeed char ***} PAGE_count := PAGE_count + 1; Line_count := 0; if single_sheet then begin {pause for operator intervention << 04.22.82 >>} writeln; write ( 'Insert new page. Press return to continue. ' ); readln ( dummy ); end; IF Headerflag THEN BEGIN { PRINTED PAGE Head } SKIPLines(1); PUTPAGEHead; SKIPLines(3) END ELSE { BLANK PAGE Head } SKIPLines(5) END{NEWPAGE}; {$R+} PROCEDURE MOVE_opos ( mvarg: INTEGER ); { MOVE OUTPUT Line Cursor position FORWARD OR BACKWARD. A } { FORWARD MOVE BLANKS THE Line UP TO THE NEW POSITION OF opos. } VAR I: integer; BEGIN IF ( mvarg > 0 ) THEN BEGIN opos := opos + 1; FOR i:=opos TO (opos+mvarg-1) DO OUTBUF[i] := SPACE; opos := opos + mvarg - 1 END ELSE IF ( mvarg < 0 ) THEN opos := opos + mvarg END{MOVE_opos}; PROCEDURE PUTHeadLevel ( NEWHeadLevel: INTEGER; HeadSTRING: Line ); { PUT Head Level NUMBER AND Head Level TITLE INTO OUTPUT Line } VAR chars, k, I, HSP : integer; LevelsOut, LevelNUM, NUMBER : INTEGER; BEGIN {$R-} IF ( NEWHeadLevel 1 ) THEN OUTBUF[opos] := '.'; NUMBER := Level[LevelNUM]; k := number; CHARS := 1; WHILE ( k>9 ) DO BEGIN k := k DIV 10; CHARS := CHARS + 1 END; FOR I:=(opos+CHARS) DOWNTO (opos+1) DO BEGIN OUTBUF[I] := CHR( (NUMBER MOD 10)+48 ); NUMBER := NUMBER DIV 10 END; opos := opos + CHARS + 1 END; OLDHeadLevel := NEWHeadLevel; IF ( HeadSTRING[1] <> CHR(ZR) ) THEN BEGIN { PRINT Head Level TITLE } OUTBUF[opos] := SPACE; OUTBUF[opos+1] := SPACE; opos := opos + 2; HSP := 1; WHILE ( HeadSTRING[HSP] <> CHR(ZR) ) AND ( opos<=RIGHT_MARGIN ) DO BEGIN OUTBUF[opos] := HeadSTRING[HSP]; opos := opos + 1; HSP := HSP + 1 END; END; OUTBUF[opos] := CHR(NL); putline; END{PUTHeadLevel}; {$R+} PROCEDURE STARTLIST ( VAR N: INTEGER ); { INITIALIZE THIS Level OF LIST } VAR NEWLEFTMARGIN: INTEGER; BEGIN LISTLevel := LISTLevel + 1; WITH LISTPARAM[LISTLevel] DO BEGIN NUMBER := 0; SPACING := N; NEWLEFTMARGIN := LEFT_MARGIN + OFFSET; Check_Set ( CmdLeftMrgn, NEWLEFTMARGIN ); END END{STARTLIST}; PROCEDURE PUTLISTNUMBER ( LISTTYPE: CmdType ); { TRANSLATE LIST ELEMENT NUMBER INTO CHARACTERS } VAR NUMBER: INTEGER; BEGIN OUTBUF[LEFT_MARGIN-2] := '.'; OUTBUF[LEFT_MARGIN-1] := ' '; OUTBUF[LEFT_MARGIN ] := ' '; NUMBER := LISTPARAM[LISTLevel].NUMBER; opos := LEFT_MARGIN - 3; REPEAT OUTBUF[opos] := CHR( (NUMBER MOD 10)+48 ); NUMBER := NUMBER DIV 10; opos := opos - 1; UNTIL NUMBER=0; opos := LEFT_MARGIN; END{PUTLISTNUMBER}; PROCEDURE LISTMEMBER ( LISTTYPE: CMDTYPE ); { SPACE DOWN AND NUMBER A LIST ENTRY } BEGIN WITH LISTPARAM[LISTLevel] DO BEGIN IF TEST_PAGE ( SPACING+1 ) THEN SKIPLINES ( SPACING ) ELSE NEWPAGE; NUMBER := NUMBER + 1; END; PUTLISTNUMBER ( LISTTYPE ); END{LISTMEMBER}; PROCEDURE STOPLIST; { TERMINATE THIS Level OF LIST AND RESET TO PRIOR Level } VAR NEWLEFTMARGIN: INTEGER; BEGIN WITH LISTPARAM[LISTLevel] DO BEGIN IF TEST_PAGE ( SPACING+1 ) THEN SKIPLines ( SPACING ) ELSE NEWPAGE; NEWLEFTMARGIN := LEFT_MARGIN - OFFSET; Check_Set ( CmdLeftMrgn, NEWLEFTMARGIN ) END; LISTLevel := LISTLevel - 1 END{STOPLIST}; PROCEDURE BREAK; BEGIN putline; IF TEST_PAGE ( Line_SPACING ) THEN SKIPLines ( Line_SPACING-1 ) ELSE NEWPAGE END{BREAK}; {************************************} {* TEXT PROCESSING ROUTINES *} {************************************} PROCEDURE DoText ( var INBUF: Line ); { FORMAT TEXT } VAR wrdbuffer : Line; PROCEDURE PUTCENTERED; { CENTER TEXT FROM INPUT LINE } VAR i, width, { width of input text } fudge: integer; { computed center of input text } BEGIN (*** width := length(INBUF); ***) repeat ipos := ipos + 1 until EndOfLine ( INBUF ); width := ipos - 1; (*** Compute center char of line to be centered ***) fudge := width DIV 2; if odd(width) then { pretty it up } fudge := fudge + 1; (*** Now compute how much to indent to get there ***) tival := (page_center - fudge) + 1; { have to add 1 to get off of zero base } (*** However don't go less than left margin ***) tival := max ( tival, (left_margin+1) ); for i:=(left_margin+1) to (tival-1) do outbuf[i] := space; opos := tival; ipos := 1; WHILE ( not EndOfLine(INBUF) ) AND ( opos <= RIGHT_MARGIN ) DO BEGIN { PUT CENTERED TEXT } OUTBUF[opos] := INBUF[ipos]; opos := opos + 1; ipos := ipos + 1 END; OUTBUF[opos] := CHR(NL) END{PUTCENTERED}; PROCEDURE GETWORD; { REMOVE A CONTIGUOUS GROUP OF CHARS FROM INPUT Line } VAR WBP: integer; BEGIN REPEAT ipos := ipos + 1 UNTIL INBUF[ipos] <> SPACE; IF NOT EndOfLine(INBUF) THEN BEGIN { GET WORD } wrdbuffull := FALSE; WBP := 1; WHILE NOT wrdbuffull DO begin IF ( EndOfLine(INBUF) ) OR ( INBUF[ipos]=SPACE ) THEN BEGIN (* WORD HAS BEEN GOTTEN *) wrdbuffull := TRUE; WORDLENGTH := WBP - 1; EndOfSENTENCE := (wrdbuffer[WORDLENGTH] IN SENTENCE_ENDERS); END{IF} ELSE BEGIN wrdbuffer[WBP] := INBUF[ipos]; WBP := WBP + 1; ipos := ipos + 1 END{ELSE} end{while} END{IF} ELSE BEGIN {AT END OF INPUT LINE AND NO WORD HAS BEEN GOTTEN} wrdbuffull := FALSE; WORDLENGTH := 0; END; END{GETWORD}; FUNCTION SpaceRemaining: BOOLEAN; { Is there enough room left in output line for current word? } BEGIN SpaceRemaining := ( (SPACES+WORDLENGTH+opos-1) <= RIGHT_MARGIN ) END; procedure justify ( var outbuf: line ); { JUSTIFY OUTPUT LINE OUT TO RIGHT MARGIN. { ALGORITHM FROM "SOFTWARE TOOLS" BY K & F, PG 241. } VAR I, nextra, nmbrholes, LEFTSIDE, RIGHTSIDE, BLANKS : INTEGER; BEGIN {$R-} { COMPUTE NUMBER OF BLANKS THAT WILL HAVE TO BE INSERTED } nextra := (RIGHT_MARGIN+1) - opos; IF (nextra>0) AND (outwds>1) THEN BEGIN { REVERSE PREVIOUS DIRECTION FOR INSERTING BLANKS } DIRECTION := 1 - DIRECTION; { COMPUTE # OF HOLES IN WHICH TO ADD BLANKS } nmbrholes := outwds - 1; LEFTSIDE := opos; RIGHTSIDE := RIGHT_MARGIN + 1; opos := RIGHTSIDE; WHILE ( LEFTSIDE < RIGHTSIDE ) DO BEGIN { JUSTIFY TEXT } OUTBUF[RIGHTSIDE] := OUTBUF[LEFTSIDE]; IF ( OUTBUF[LEFTSIDE]=' ' ) THEN BEGIN {END OF WORD} IF NOT (PERIODflag AND (OUTBUF[LEFTSIDE-1] IN SENTENCE_ENDERS)) THEN BEGIN { COMPUTE # OF EXTRA BLANKS TO INSERT } IF DIRECTION=0 THEN BLANKS := ((nextra-1) DIV nmbrholes) + 1 ELSE BLANKS := nextra DIV nmbrholes; nextra := nextra - BLANKS; nmbrholes := nmbrholes - 1; FOR I:=1 TO BLANKS DO BEGIN { INSERT EXTRA BLANKS } RIGHTSIDE := RIGHTSIDE - 1; OUTBUF[RIGHTSIDE] := ' ' END; END{IF} END{IF}; LEFTSIDE := LEFTSIDE - 1; RIGHTSIDE := RIGHTSIDE - 1 END{WHILE} END{IF} END{justify}; {$R+} PROCEDURE PUTWORD ( var wrdbuffer : line ); { PUT CURRENT WORD INTO OUTPUT Line. KEEP } { TRACK OF WORD count FOR JUSTIFY ROUTINE. } VAR I, WBP: integer; BEGIN IF NOT STARTOFLine THEN BEGIN { SPACING BETWEEN WORDS } FOR I:=1 TO SPACES DO BEGIN OUTBUF[opos] := SPACE; opos := opos + 1 END{FOR} END ELSE BEGIN { THIS IS THE FIRST WORD ON THE Line } STARTOFLine := FALSE; outwds := 0; opos := opos + 1 END; FOR WBP:=1 TO WORDLENGTH DO BEGIN { COPY WORD INTO OUTPUT Line } OUTBUF[opos] := wrdbuffer[WBP]; opos := opos + 1 END; OUTBUF[opos] := CHR(NL); outwds := outwds + 1 END{PUTWORD}; PROCEDURE Fill_Lines; { Fill AND JUSTIFY ONE OR MORE OUTPUT Lines FROM CURRENT INPUT Line } VAR LineFilled: BOOLEAN; PROCEDURE Fill_ONE_Line; { Fill OUTPUT Line FROM CURRENT INPUT Line } VAR FINISHED: BOOLEAN; BEGIN IF NOT wrdbuffull THEN GETWORD; LineFilled := FALSE; FINISHED := FALSE; WHILE NOT FINISHED DO BEGIN IF ( spaceremaining ) then begin if ( WORDLENGTH <> 0 ) then begin { CONTINUE FillING Line } PUTWORD ( wrdbuffer ); IF EndOfSENTENCE THEN { SET SPACING BEFORE NEXT WORD } SPACES := 2 ELSE SPACES := 1; IF NOT EndOfLine(INBUF) THEN GETWORD ELSE BEGIN { NO MORE WORDS IN THIS INPUT Line } FINISHED := TRUE; wrdbuffull := FALSE; END{else} end{if wordlength <> 0} END{if spaceremaining} ELSE BEGIN { Stop filling line } FINISHED := TRUE; LineFilled := Not SpaceRemaining; END{Stop filling line} END END{Fill ONE Line}; BEGIN {Fill_Lines} Fill_ONE_Line; WHILE ( LineFilled ) DO BEGIN IF JUSTIFYflag THEN justify ( OUTBUF ); BREAK; Fill_ONE_Line; END END{Fill_Lines}; PROCEDURE CopyAsIs; { COPY INPUT Line LITERALLY AS FOUND IN SOURCE FILE } VAR LineCOPIED: BOOLEAN; BEGIN LineCOPIED := FALSE; WHILE NOT LineCOPIED DO BEGIN REPEAT opos := opos + 1; ipos := ipos + 1; OUTBUF[opos] := INBUF[ipos]; UNTIL (opos=RIGHT_MARGIN) OR ( EndOfLine(INBUF) ); IF EndOfLine(INBUF) THEN { INPUT Line HAS BEEN COPIED } LineCOPIED := TRUE ELSE BEGIN { INPUT Line MAY BE TOO LONG, REMAINDER GOES TO NEXT Line } IF INBUF[ipos+1]=CHR(NL) THEN {Line IS EXACTLY THE RIGHT SIZE} LineCOPIED := TRUE; opos := opos + 1; OUTBUF[opos] := CHR(NL) END; BREAK; END END{CopyAsIs}; BEGIN {DoText} if ceval>0 then begin PUTCENTERED; ceval := ceval - 1; BREAK END ELSE IF Fillflag THEN Fill_Lines ELSE CopyAsIs END{DoText}; FUNCTION ScanCommand: CmdType; { REMOVE Command STRING FROM INPUT Line AND SEARCH Command Table FOR MATCHING Command TYPE } VAR CommandLine : cstring; CmdIndex : CmdType; hash, j, cpos : integer; BEGIN {$R-} FOR cpos:=1 TO (CmdSize-1) DO CommandLine[cpos] := SPACE; ipos := 2; { skip CmdChar } cpos := 1; WHILE ( INBUF[ipos] <> ' ' ) AND ( not EndOfLine(INBUF) ) AND ( cpos <= CmdSize ) DO BEGIN { get Command string } CommandLine[cpos] := toupper ( INBUF[ipos] ); ipos := ipos + 1; cpos := cpos + 1 END{WHILE}; CommandLine[CmdSize] := CHR(ZR); { since the table is so short just do a sequential search. <<<04.15.82>>>} CmdIndex := FIRST; CommandTable[invalid] := CommandLine; { insert the sentinal } repeat CmdIndex := SUCC(CmdIndex); until CommandTable[CmdIndex]=CommandLine; ScanCommand := CmdIndex END{ScanCommand}; {$R+} {$iRUNCOMM.P } {$iSTDOPEN.P } {$iRUNINIT.P } BEGIN {* MAIN PROGRAM *} for ipos:=1 to 24 do writeln; WRITELN ( ' RUNOFF' ); writeln ( ' CP/M Version 1.0 Created April 30, 1982' ); OpenFiles; INITIALIZE{ all parameters now }; {$C+}{ allow program termination from this section } getline; Setup := TRUE; { PROCESS THOSE Commands THAT AFFECT THE VARIOUS PARAMETER & flag SETTINGS } WHILE NOT EndOfFile AND Setup DO BEGIN IF INBUF[1]=Cmdchar THEN BEGIN CCmd := ScanCommand; IF CCmd IN INITPARAMCommandS THEN BEGIN DoCommand ( INBUF ); getline END ELSE { First non-init Command ends setup phase } Setup := FALSE; END ELSE { First text line ends setup phase } Setup := FALSE; END{WHILE}; NEWPAGE; { TOP OF FIRST PAGE } WHILE NOT EndOfFile DO BEGIN { PROCEED WITH NORMAL SOURCE FILE PROCESSING } IF INBUF[1]=Cmdchar THEN DoCommand ( INBUF ) ELSE DoText ( INBUF ); getline {+++ test for break key press here +++} END{WHILE}; putline;{ terminate } putc ( CHR(FF) ); writeln ( 'End of job.' ); writeln;writeln; END{ RUNOFF }. .