ingoldsby@calgary.UUCP (03/25/87)
I recently promised to put a simple text formatter on the net. I wrote it several years ago in a single evening. I had just arrived at a new university and was assigned to write a paper. Like all good students I left it 'til the last minute and didn't have much time. The professor wanted all the papers formatted, and I didn't have time to learn UNIX and nroff in a single night so I decided to write a simple text formatter - tformat. I usually PACK the code and execute it using RUNB. Here it is: PROCEDURE Tformat (* A simple text formatting tool. *) (* Written on October 7, 1986 by Terrance R. Ingoldsby *) (* Modifications: *) (* March 5, 1987 - Fixed bug in /BRK/ and /PAR/ so that multiple /BRK/ *) (* or /PAR/ directives will output multiple CR-LFs *) (* Restrictions: -no word in the source file *) (* may be longer than the *) (* string size of `word' *) (* -no line in the source file *) (* may be longer than the *) (* string size of `buffer' *) (* *) (* The following formatter directives may be *) (* imbedded in the source file *) (* /MAR/ value - Resets the default number of *) (* left margin spaces (5) to *) (* value. *) (* /WID/ value - Resets the default number of *) (* characters in output lines *) (* (not including left margin) *) (* (default=70) to value. *) (* /SPA/ value - Resets the default spacing *) (* (initially 2) to value. *) (* /IND/ value - Resets the default indent- *) (* ation used in paragraphs *) (* from 5 to value. *) (* /TIT/ string(s) /ENDTIT/ - Causes the *) (* string between the delimiters*) (* to be centered when output. *) (* /BRK/ - Causes a CR-LF to be issued. *) (* /PAR/ - Causes a CR-LF-Indentation. *) (* ___________________________________________ *) (* Since BASIC09 does not permit global or *) (* static variables, it is simulated by *) (* declaring them in the main procedure, and *) (* passing to the sub-procedures which need *) (* them. *) DIM buffer:STRING[300] \ REM File input buffer DIM currentline:INTEGER (* The following are formatter parameter variables *) DIM print_lines_pg:INTEGER DIM tot_lines_pg:INTEGER DIM linewidth, lmargin:INTEGER DIM spacing, indentsp:INTEGER DIM lmargstr, indentstr:STRING[25] DIM title:STRING[150] DIM word:STRING[40] DIM outline:STRING[200] DIM at_eof:BOOLEAN DIM i, j, k:INTEGER (* Set defaults and static parameters *) print_lines_pg = 60 tot_lines_pg = 66 linewidth = 70 \ REM ember, does not include left margin spacing = 2 \ REM Initially double space indentsp = 5 lmargin = 5 (* Initialize variables based on parameters *) lmargstr = "" \ REM Build a left margin string FOR i = 1 TO lmargin \ lmargstr = lmargstr + " " \ NEXT i indentstr = "" FOR i = 1 TO indentsp \ indentstr = indentstr + " " \ NEXT i (* Go to initial state *) buffer = "" outline = lmargstr currentline = 1 (* Continue until we run out of text *) LOOP RUN getword( word, buffer, at_eof ) EXITIF at_eof = TRUE THEN \ ENDEXIT (* Decide if we have a directive, or just a normal word *) IF word = "/MAR/ " THEN RUN getword( word, buffer, at_eof ) \ REM Get new margin value lmargin = VAL( word ) \ lmargstr = "" FOR i = 1 TO lmargin lmargstr = lmargstr + " " NEXT i ELSE \ IF word = "/WID/ " THEN RUN getword( word, buffer, at_eof ) linewidth = VAL( word ) ELSE \ IF word = "/SPA/ " THEN RUN getword( word, buffer, at_eof ) spacing = VAL( word ) ELSE \ IF word = "/IND/ " THEN RUN getword( word, buffer, at_eof ) indentsp = VAL( word ) \ indentstr = "" FOR i = 1 TO indentsp indentstr = indentstr + " " NEXT i ELSE \ IF word = "/BRK/ " THEN (* Is there anything in the current output *) (* buffer then flush it and do linefeeds *) outline = TRIM$( outline ) IF outline <> "" THEN PRINT outline; ENDIF RUN spacer( spacing, currentline ) RUN pager( currentline, print_lines_pg, tot_lines_pg ) outline = lmargstr ELSE \ IF word = "/PAR/ " THEN (* Same as BRK except indent as well *) outline = TRIM$( outline ) IF outline <> "" THEN PRINT outline; ENDIF RUN spacer( spacing, currentline ) RUN pager( currentline, print_lines_pg, tot_lines_pg ) outline = lmargstr + indentstr ELSE \ IF word = "/TIT/ " THEN outline = TRIM$( outline ) IF outline <> "" THEN PRINT outline; RUN spacer( spacing, currentline ) RUN pager( currentline, print_lines_pg, tot_lines_pg ) ENDIF title = "" RUN getword( word, buffer, at_eof ) WHILE( word <> "/ENDTIT/ " ) DO title = title + word RUN getword( word, buffer, at_eof ) ENDWHILE PRINT lmargstr; PRINT USING "S"+STR$(linewidth)+"^", title; RUN spacer( spacing, currentline ) RUN pager( currentline, print_lines_pg, tot_lines_pg ) outline = lmargstr ELSE \ REM Just a normal word (* If still room in line buffer then add word *) IF( LEN( outline ) + LEN( word ) ) < ( linewidth + lmargin ) THEN outline = outline + word ELSE RUN justify( outline, linewidth+lmargin ) RUN spacer( spacing, currentline ) RUN pager( currentline, print_lines_pg, tot_lines_pg ) outline = lmargstr + word ENDIF ENDIF \ ENDIF \ ENDIF \ ENDIF \ ENDIF \ ENDIF \ ENDIF ENDLOOP (* Flush anything left over *) PRINT outline; FOR i = currentline TO tot_lines_pg PRINT NEXT i END PROCEDURE Getword (* Getword returns a word in `word', a word *) (* being defined as a sequence of non-space *) (* characters terminated by a space. *) (* `buffer' is essentially a global variable. *) (* On the first call it must be ""; subsequently *) (* the caller shouldn't touch it. When no words *) (* are left, getword returns at_eof = TRUE *) PARAM word:STRING[40] PARAM buffer:STRING[300] PARAM at_eof:BOOLEAN buffer = TRIM$( buffer ) WHILE (buffer = "") AND NOT EOF(#0) DO READ #0, buffer buffer = TRIM$( buffer ) ENDWHILE IF buffer = "" THEN at_eof = TRUE ELSE (* Trim leading spaces *) WHILE LEFT$( buffer, 1 ) = " " DO buffer = RIGHT$( buffer, LEN( buffer)-1 ) ENDWHILE (* Find end of next word *) i = SUBSTR( " ", buffer ) IF i = 0 THEN \ (* Last word in buffer *) word = buffer + " " buffer = "" ELSE word = MID$( buffer, 1, i-1 ) + " " buffer = RIGHT$( buffer, LEN( buffer )-i ) ENDIF at_eof = FALSE ENDIF END PROCEDURE Justify (* Right justifies `line' and outputs it to *) (* the standard output. `line' is set NULL on return *) PARAM line:STRING[200] PARAM linewidth:INTEGER DIM dummy:STRING[200] DIM i,j:INTEGER DIM numgaps,numblanks,pad:INTEGER DIM gap(200):BOOLEAN FOR i=1 TO 200 gap(i)=FALSE NEXT i (* Find number of inter-word gaps *) (* Trim trailing blanks *) line=TRIM$(line) dummy=line (* Trim leading blanks from dummy *) WHILE LEFT$(dummy,1)=" " DO dummy=RIGHT$(dummy,LEN(dummy)-1) ENDWHILE numgaps=0 WHILE SUBSTR(" ",dummy)<>0 DO i=SUBSTR(" ",dummy) numgaps=numgaps+1 dummy=RIGHT$(dummy,LEN(dummy)-i) WHILE LEFT$(dummy,1)=" " DO dummy=RIGHT$(dummy,LEN(dummy)-1) ENDWHILE ENDWHILE (* Determine where the gaps are *) i=1 (* Skip leading blanks *) WHILE MID$(line,i,1)=" " DO i=i+1 ENDWHILE WHILE i<=LEN(line) DO IF MID$(line,i,1)=" " THEN gap(i)=TRUE WHILE MID$(line,i,1)=" " DO i=i+1 ENDWHILE ELSE i=i+1 ENDIF ENDWHILE numblanks=linewidth-LEN(line) FOR i=1 TO LEN(line) IF gap(i)=TRUE THEN pad=numblanks/numgaps FOR j=1 TO pad+1 PRINT " "; NEXT j numblanks=numblanks-pad numgaps=numgaps-1 ELSE PRINT MID$(line,i,1); ENDIF NEXT i line = "" END PROCEDURE Spacer (* Prints `spacing' CR-LF and increments *) (* `currentline' by `spacing' *) PARAM spacing, currentline:INTEGER DIM i:INTEGER FOR i = 1 TO spacing PRINT NEXT i currentline = currentline + spacing END PROCEDURE Pager (* If currentline >= print_lines_pg then pager *) (* prints (tot_lines_pg - print_lines_pg) CR-LF *) (* and resets currentline to 1 *) PARAM currentline:INTEGER PARAM print_lines_pg, tot_lines_pg:INTEGER DIM i:INTEGER IF currentline > print_lines_pg THEN FOR i = currentline to tot_lines_pg PRINT NEXT i currentline=1 ENDIF END ----------------------------------------------------------------------------- I hope it proves useful to someone. It reads from STDIN and writes to STDOUT. Terry Ingoldsby ihnp4!alberta!calgary!ingoldsby