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