A SIMPLE TEXT FORMATTER

by Donald Daniel, Mar 2020, revised Jan 2024

up one level

If the lines of text are too long you can fix the problem with these instructions.

DESCRIPTION

You may create text with the vi editor instead of an office word processor. If you write a program, surely you will use the vi editor. You might also use it for other things. If you create text with the vi editor you may want a printed copy to put in a file cabinet or in a loose leaf notebook. For this you will need a text formatter. If you were writing a book you would want a very powerful formatter such as nroff. But if you are only creating a few pages you would prefer a simple text formatter. This article gives source code for such a formatter written in oberon-2. If you have the oberon-2 compiler installed in your computer, you can compile the formatter and use it on your computer. Instructions for installing the compiler are given in the article how to program a computer elsewhere in this website.

To download the program in usable form, do not use a browser. In the directory where you will work use "wget waltzballs.org/other/linux/format.html". Then "unhtml format.html > format.txt". Then open format.txt with the vi editor. Use the vi editor to send the program to ~/wkspc/prog/src, then in ~/wkspc/prog compile it and run it.

The simplest use of the prog is illustrated by the following example of using it to print the file "dart.Mod". The part preceded by "-->" was done by the program. Since the output of the program is a file called "pfile" the line "lpr pfile" sends that file to the printer.

$ prt
fn dart.Mod
go
-->end of file
q
$ lpr pfile

THE PROGRAM

MODULE prt;
(*copyright 1980 donald daniel. Originally in pascal,
converted to Oberon in 2008.  This program is free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General
Public License along with this program.  If not, see
http://www.gnu.org/licenses/.  *)
IMPORT In,Out,Msg,Files,TextRider;
CONST strlngth=10;strlngth2=50;bell=7;formfeed=12;lf=10;cr=13;
fn=1;tm=2;lm=3;sp=4;sl=5;pl=6;ll=7;go=8;ff=9;pn=10;
npn=11;menu=12;init=13;q=15;er=20;
TYPE 
str=ARRAY strlngth OF CHAR; str2=ARRAY strlngth2 OF CHAR;
cma=ARRAY 22 OF str;
VAR resv:Msg.Msg;invar,outvar:Files.File;
infile:TextRider.Reader; outfile:TextRider.Writer;
cmdara:cma; command:LONGINT; filename: str2;
strng:str; pagenum,line,pagelength,linelength,
topmarg,leftmarg,centerpage,chnum:LONGINT;
ch:CHAR; numpg,topofpage,leftofpage:BOOLEAN;

PROCEDURE readword(VAR s:str);
VAR ch:CHAR; i:LONGINT;
BEGIN
In.Char(ch);WHILE ch=' ' DO In.Char(ch);END;
i:=0;REPEAT s[i]:=ch;INC(i);In.Char(ch);
UNTIL ((ch=' ')OR(ch=0AX)); s[i]:=00X;
END readword;

PROCEDURE readln(VAR s:str2);
VAR ch:CHAR; i:LONGINT;
BEGIN
In.Char(ch);WHILE ch=' ' DO In.Char(ch);END;
i:=0;REPEAT s[i]:=ch;INC(i);In.Char(ch);
UNTIL ch=0AX; END readln;

PROCEDURE leftmg;
VAR i:LONGINT;
BEGIN FOR i:=1 TO leftmarg DO outfile.WriteChar(" ");END;
leftofpage:=FALSE END leftmg;

PROCEDURE topmg;
VAR i:LONGINT;
BEGIN
FOR i:=1 TO topmarg DO outfile.WriteLn;END;
chnum:=1;topofpage:=FALSE;leftofpage:=TRUE END topmg;

PROCEDURE newpage;
BEGIN
centerpage:=linelength DIV 2 + leftmarg;
IF topofpage THEN topmg;END;
WHILE line<=(pagelength-2)DO
outfile.WriteLn;line:=line+1 END; outfile.WriteLn;
IF numpg=TRUE THEN outfile.WriteLInt(pagenum,centerpage);END;
outfile.WriteLn; outfile.WriteChar(CHR(formfeed));
line:=1;chnum:=1;pagenum:=pagenum+1;topofpage:=TRUE;
leftofpage:=TRUE; END newpage;

PROCEDURE outchar;
BEGIN 
IF ORD(ch)=lf THEN outfile.WriteChar(CHR(cr));END;
outfile.WriteChar(ch);chnum:=chnum+1;END outchar;

PROCEDURE inputline;
BEGIN
WHILE (~ infile.Eol()) DO 
IF topofpage THEN topmg;END;
IF leftofpage THEN leftmg;END;
infile.ReadChar(ch);
IF ORD(ch)=formfeed THEN newpage ELSE outchar; END;
IF ((chnum>linelength)&(~ infile.Eol())) THEN line:=line+1;
IF(line>(pagelength-2))THEN newpage ELSE outfile.WriteLn;
leftofpage:=TRUE;chnum:=1;END;END; END(*while*);
infile.ReadLn;outfile.WriteLn; 
leftofpage:=TRUE;line:=line+1;chnum:=1;
END inputline;

PROCEDURE printfile;
BEGIN
chnum:=1;
invar:=Files.Old(filename,{Files.read},resv);
infile:=TextRider.ConnectReader(invar);
WHILE ~(infile.res#Files.done) DO inputline;
IF line>(pagelength-2)THEN newpage;END; END(*while*);
invar.Close; Out.String('end of file');Out.Ln;
END printfile;

PROCEDURE determine(VAR cmdvar:LONGINT);
VAR cmi:LONGINT;strvar:str;
BEGIN
cmdvar:=er; readword(strvar);
FOR cmi:=fn TO q DO 
IF (cmdara[cmi]=strvar)THEN cmdvar:=cmi;END;END;
IF cmdvar=er THEN Out.Char(CHR(bell));
Out.String('<---<< error');Out.Ln;END;END determine;

PROCEDURE initvar;
BEGIN
cmdara[fn]:='fn'; cmdara[tm]:='tm'; cmdara[lm]:='lm';
cmdara[sp]:='sp'; cmdara[sl]:='sl'; cmdara[pl]:='pl';
cmdara[ll]:='ll'; cmdara[go]:='go'; cmdara[q]:='q';
cmdara[ff]:='ff'; cmdara[pn]:='pn'; cmdara[npn]:='npn';
cmdara[menu]:='menu'; cmdara[init]:='init'; filename:='';
leftmarg:=10;topmarg:=6; pagenum:=1;line:=1;
linelength:=65;pagelength:=53; topofpage:=TRUE;numpg:=TRUE;
END initvar;

PROCEDURE menuproc;
BEGIN
Out.String('MENU');Out.Ln;
Out.String('fn filename [enter]'); Out.Ln;
Out.String('pn for page numbering or npn for no numbering=');
IF numpg THEN Out.String('pn') 
ELSE Out.String('npn');END; Out.Ln;
Out.String('the next few items may be entered'); Out.Ln;
Out.String('as item [space] value [enter]'); Out.Ln;
Out.String('sp startpage='); Out.LongInt(pagenum,2);Out.Ln;
Out.String('ll linelength='); Out.LongInt(linelength,2);
Out.String('  pl pagelength=');Out.LongInt(pagelength,2);Out.Ln;
Out.String('tm top margin=');Out.LongInt(topmarg,2);
Out.String('  lm left margin=');Out.LongInt(leftmarg,2); Out.Ln;
Out.String('the remaining items have no value');Out.Ln;
Out.String('go to process file. You can process as many');
Out.Ln;Out.String('files as you want before you type q.');
Out.Ln;
Out.String('ff to formfeed before starting next file');
Out.Ln;
Out.String('menu to see this menu');Out.Ln;
Out.String('init to reinitialize to default values'); Out.Ln;
Out.String('q to quit processing files and write result to pfile'); 
Out.Ln;
Out.String('lpr pfile to print when finished');Out.Ln;
END menuproc;

BEGIN
initvar;
outvar:=Files.New('pfile',{Files.write},resv);
outfile:=TextRider.ConnectWriter(outvar);
menuproc; determine(command);
WHILE command # q DO
CASE command OF 
fn: readln(filename);| 
sp: In.LongInt(pagenum);In.Line(strng);|
pl: In.LongInt(pagelength);In.Line(strng);|
ll: In.LongInt(linelength);In.Line(strng);| 
pn: numpg:=TRUE| npn: numpg:=FALSE| 
tm: In.LongInt(topmarg);In.Line(strng);|
lm: In.LongInt(leftmarg);In.Line(strng);| 
go: printfile| ff: newpage| menu: menuproc| 
init: initvar|
er:|END(*CASE*);
determine(command) END(*WHILE*); END prt.

up one level

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.