[comp.sys.m6809] Tformat - A Simple BASIC09 text formatting tool

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