[Back to TEXTFILE SWAG index] [Back to Main SWAG index] [Original]
{
Fast driver for backwards reading... Aha!
This is the way to do it.
Below you will find the source of a "tail" program.
I wrote it because I needed to check the status of some log files,
and I didn't want to go through the entire file every time, as the
files could grow quite large.
It is currently limited to 255 chars per line, but that
can easily be fixed (see the Limit const).
Although it's not an exact solution to your problem, it will show you
how to do "backwards" reading.
}
PROGRAM Tail;
{
Shows the tailing lines of a text file.
Syntax: TAIL [d:\path]filespec.ext [-<lines>]
Default number of lines is 10.
"TAIL filename -20" will show the 20 last lines
Written by Lars Fosdal, 1993
Released to the Public Domain by Lars Fosdal, 1993
}
USES
DOS, Objects, Strings;
CONST
MaxBufSize = 32000;
TYPE
pBuffer = ^TBuffer;
TBuffer = ARRAY[0..MaxBufSize-1] OF Char;
pRawStrCollection = ^TRawStrCollection;
TRawStrCollection = OBJECT(TCollection)
PROCEDURE FreeItem(Item:Pointer); VIRTUAL;
END;
PROCEDURE TRawStrCollection.FreeItem(Item:Pointer);
BEGIN
IF Item<>nil
THEN StrDispose(pChar(Item));
END; {PROC TRawStrCollection.FreeItem}
FUNCTION ShowTail(FileName:String; n:Integer):Integer;
PROCEDURE DumpLine(p:pChar); FAR;
BEGIN
IF p^=#255
THEN Writeln
ELSE Writeln(p);
END;
CONST
Limit = 255;
VAR
lines : pRawStrCollection;
fm : Byte;
f : File;
fs,fp : LongInt;
MaxRead : Word;
Buf : pBuffer;
lc,ix,ex : Integer;
sp : ARRAY[0..Limit] OF Char;
BEGIN
lines:=nil;
fm:=FileMode;
FileMode:=$40; {Read-only, deny none}
Assign(f, FileName);
Reset(f, 1);
lc:=IOResult;
IF lc=0
THEN BEGIN
New(Buf);
fs:=FileSize(f); {First, let's find out how much to read}
fp:=fs-MaxBufSize;
IF fp<0
THEN fp:=0;
Seek(f,fp); {Then, read it}
BlockRead(f, Buf^, MaxBufSize, MaxRead);
Close(f);
IF MaxRead>0
THEN BEGIN
New(Lines, Init(n,10));
ix:=MaxRead-1;
IF Buf^[ix]=^J THEN Dec(ix);
IF (ix>0) and (Buf^[ix]=^M) THEN Dec(ix); {Skip trailing line break}
WHILE (lc<n) and (ix>0)
DO BEGIN
ex:=ix;
FillChar(sp, SizeOf(sp), 0);
WHILE (ix>0) and not (Buf^[ix] =^J)
DO Dec(ix);
IF ex-ix<=Limit {If no break was found within limit, it's no txt file}
THEN BEGIN
IF ix=ex
THEN sp[0]:=#255 {Pad empty lines to avoid zero-length pchar}
ELSE StrLCopy(sp, @Buf^[ix+1], ex-ix);
Inc(lc);
Lines^.AtInsert(0, StrNew(sp));
Dec(ix);
WHILE (ix>0) and (Buf^[ix] =^M)
DO Dec(ix);
END
ELSE BEGIN
Writeln('"',FileName,'" doesn''t seem to be a text file');
ix:=-1;
END;
END; {lc<n and ix>0}
END {Maxread>0}
ELSE Lines:=nil;
Dispose(Buf);
END
ELSE lc:=-lc;
IF Lines<>nil
THEN BEGIN
Lines^.ForEach(@DumpLine);
Dispose(Lines, Done);
END;
ShowTail:=lc;
FileMode:=fm;
END; {FUNC ShowTail}
TYPE
CharSet = Set of Char;
FUNCTION StripAll(CONST Exclude:CharSet; S:String):String;
VAR
ix : Integer;
BEGIN
ix:=Length(S);
WHILE ix>0
DO BEGIN
IF S[ix] in Exclude
THEN Delete(S, ix, 1);
Dec(ix);
END;
StripAll:=S;
END; {FUNC StripAll}
VAR
r : Integer;
l : Integer;
e : Integer;
BEGIN
IF (ParamCount<1) or (ParamCount>2)
THEN BEGIN
Writeln('TAIL v.1.0 - PD 1993 Lars Fosdal');
Writeln(' TAIL [d:\path]filename.ext [-n]');
Writeln(' Default is 10 lines');
END
ELSE BEGIN
IF ParamCount=2
THEN BEGIN
Val(StripAll(['/','-'], ParamStr(2)), l, e);
IF e<>0
THEN l:=10
END
ELSE l:=10;
r:=ShowTail(ParamStr(1), l);
IF r<0
THEN BEGIN
Writeln('Couldn''t open "',ParamStr(1),'"! (Error ', -r,')');
Halt(Word(-r));
END;
END;
END.
[Back to TEXTFILE SWAG index] [Back to Main SWAG index] [Original]