[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]