program typebackwards (input, output, textfile); {**************************************************************} { } { Copyright (c) 1987 Bob Schor } { Eye and Ear Hospital } { 230 Lothrop Street } { Pittsburgh, PA 15213 } { } { All rights reserved. May not be copied without this notice. } { } {**************************************************************} { Type file, starting from the last line and working forwards } { Version 7.11 -- first incarnation } { Version 7.12 -- trivial fixes } CONST version = 'TYPBAK Version 7.12'; CONST escape = 33B; return = 15B; namesize = 20; blocksize = 512; TYPE smalltype = 1 .. 20; nameindextype = 1 .. namesize; nametype = PACKED ARRAY [nameindextype] OF char; blockindextype = 1 .. blocksize; blocktype = PACKED ARRAY [blockindextype] OF char; VAR textfile : FILE OF blocktype; nextblock : blocktype; filename : nametype; PROCEDURE initialize; BEGIN { initialize } writeln; writeln (version); writeln; writeln ('Type text from end of file'); writeln; write ('Enter text file name -- '); readln (filename) END; PROCEDURE csi (string : PACKED ARRAY [low .. high : smalltype] OF char); VAR index : smalltype; BEGIN { csi } write (chr(escape), '['); FOR index := low TO high DO write (string[index]) END; FUNCTION exists (filename : nametype) : boolean; VAR length : integer; BEGIN { exists } reset (textfile, filename, ' ', length); exists := length > 0 END; PROCEDURE open (filename : nametype; VAR length : integer); BEGIN { exists } reset (textfile, filename, '/SEEK', length) END; PROCEDURE typereverse (filename : nametype); VAR nextblock : blocktype; currentblock : integer; startofline, endofline, index : blockindextype; FUNCTION nonnull (index : blockindextype) : blockindextype; CONST null = 0B; BEGIN { nonnull } WHILE textfile^[index] = chr(null) DO index := pred(index); nonnull := index END; FUNCTION atbeginning (index : blockindextype) : boolean; BEGIN { atbeginning } atbeginning := (index = 1) AND (currentblock = 1) END; FUNCTION previous (index : blockindextype) : blockindextype; BEGIN { previous } IF index > 1 THEN previous := pred(index) ELSE IF currentblock > 1 THEN BEGIN currentblock := pred (currentblock); nextblock := textfile^; seek (textfile, currentblock); previous := blocksize END END; FUNCTION backsearch (index : blockindextype) : blockindextype; CONST linefeed = 12B; FUNCTION successor (index : blockindextype) : blockindextype; BEGIN { successor } IF index < blocksize THEN successor := succ(index) ELSE BEGIN currentblock := succ(currentblock); seek (textfile, currentblock); successor := 1 END END; BEGIN { backsearch } IF atbeginning (index) THEN backsearch := index ELSE REPEAT index := previous (index) UNTIL atbeginning (index) OR (textfile^[index] = chr(linefeed)); IF atbeginning (index) THEN backsearch := index ELSE backsearch := successor (index) END; BEGIN { typereverse } open (filename, currentblock); seek (textfile, currentblock); endofline := nonnull (blocksize); startofline := backsearch (endofline); csi ('1;1H'); csi ('0K'); IF startofline <= endofline THEN FOR index := startofline TO endofline DO write (textfile^[index]) ELSE BEGIN FOR index := startofline TO blocksize DO write (textfile^[index]); FOR index := 1 TO endofline DO write (nextblock[index]) END; WHILE NOT atbeginning (startofline) DO BEGIN csi ('1A'); csi ('1L'); endofline := previous (startofline); startofline := backsearch (endofline); IF startofline <= endofline THEN FOR index := startofline TO endofline DO write (textfile^[index]) ELSE BEGIN FOR index := startofline TO blocksize DO write (textfile^[index]); FOR index := 1 TO endofline DO write (nextblock[index]) END END; csi ('24;1H'); csi ('0J'); csi ('1A') END; BEGIN { typebackwards } initialize; IF exists (filename) THEN typereverse (filename) ELSE writeln ('File not found') END.