{************************************************************************
 ***                                                                  ***
 ***                    AUDIT/FINGER INTEGRATER                       ***
 *** M C Hill                                                 March 92***
 ************************************************************************

 Takes csv output of Finger Audit program, with its list of software
 in the form Filename, Ver/Size, [Desc if Ver,] Finger print file, directory

 and compares with the audit database of physical objects, in the format:

  various gubbins (such as machine type)
  MANUAL                            ' keyword
  Description, ProgramName, Version ' lots of lines of this format
                                    ' blank line at end (with comma)

 The PROGRAMNAME must correspond to the main program of the package, as set
 up in the finger program.

{}

program WHO;


uses DOS, CRT, PRINTER;

{$I strings.inc}

var
  IgnoreNo : integer;
  Ignore : array[1..99] of record
             Product : string;
             Ver : string[6];
           end;

  NoAUDFile4No : integer;
  NoAUDFile4 : array[1..99] of string[8];

  Key : string;

   FingerCSV : text;
   Total2Buy, Total2Check : integer;
   prog : record
                Name : string;
                Ver : string;
                Desc : string;
                User : string;
                Dir : string;
             end;
   OldProgName : string;
   PrintLine, OldPrintLine : string;


{=== READ COMMA-DELIMITED FIELD FROM TEXT FILE ===========}

procedure ReadCSV(var F : text; var Field : string);
{Assumes an end of line is [13] followed by [10]}
var
  C : char;
  Quoted : boolean;
  EOField : boolean;

begin
  Field := ''; Quoted := False; EOField := false;
  repeat
    read(F, C);
    if (C = ',') and (not Quoted) then EOField := True
      else if (C=#13) then begin Read(F, C); EOField := True; end {Read off the LF}
        else if C='"' then Quoted := (not Quoted)
          else Field := Field + C;
  until EOField or eof(F);
end;



{ **************************************
  ***         READ IGNORE FILE       ***
  **************************************
  FILE IGNORE.AUD, format PROGRAM NAME, Version
  Version = "" ignores all versions of that program
{}

procedure ReadIgnore;
var
  IgnoreFile : Text;
  S : string;

begin
  writeln('Reading Ignore File...');
  assign(IgnoreFile,'IGNORE.LST');
  Reset(IgnoreFile);

  IgnoreNo := 0;
  WHILE NOT EOF(IgnoreFile) do begin
    Readcsv(IgnoreFile, S); {Read into temporary strin}

    IF (S[1] <> ':') AND (S <> '') THEN begin
       IgnoreNo := IgnoreNo +1;
       DelSpace(S); Ignore[IgnoreNo].Product := ucase(S);
       ReadCSV(IgnoreFile, S);
       DelSpace(S); Ignore[IgnoreNo].Ver     := ucase(S);
    END;

  END;
  CLOSE(IgnoreFile);

end;

{---- Check to see if program (& ver) should be ignored ----}
function InIgnoreList(var Name, ver : string) : boolean;
var
  B : boolean;
  I : integer;

begin
    B := False;
    FOR I := 1 TO IgnoreNo do
      IF (Name = Ignore[I].Product) and
        ( (Ver = Ignore[I].Ver) or (Ignore[I].Ver = '') ) then
           B := True;

    InIgnoreList := B;
end;

{********************************************
 ***          PRINTER STREAM OBJECT       ***
 ********************************************}

type
  OPRN = object
     PageNo : integer;
     LineNo : integer;
     procedure Init;
     procedure Writeln(S : string);
     procedure EndPage;
     procedure NewPage;
     procedure PageHeader;
  end;

procedure OPRN.Init;
begin
  PageNo := 0; LineNo := 0;
end;

procedure OPRN.PageHeader;
begin
  Writeln('--------- AUDIT REPORT: IDENTIFYING LOCATION OF ILLEGAL SOFTWARE ---------');
  Writeln(' Date  Time                                                     PAGE ' + I2STR(PageNo));
  WriteLn(' ');
end;

procedure OPRN.EndPage;
begin
  write(LST, #12);  {Print Form feed}
end;

procedure OPRN.NewPage;
begin
  EndPage;   {Print Form Feed}
  PageNo := PageNo +1;
  LineNo := 0;
  PageHeader;
end;

procedure OPRN.Writeln(S : string);
begin
  write(LST,S + #13 + #10);
  LineNo := LineNo +1;
  IF (LineNo > 58) then NewPage;
end;

var
  PRN : OPRN;


{***************************************************************
 *** CHECK IN AUDIT DATABASE TO SEE IF PROGRAM ITEM IS LEGAL ***
 ***************************************************************}


procedure CheckIfBought;
var
  UserAUD : text;
  S : string;
  Licence : record
              Desc : string;
              Name : string;
              Ver  : string;
            end;

  LegalStatus : byte;
  LegalVer : string;

begin
  {--- Open/Check for User Audit file -----}
  DelSpace(Prog.Name);
  Write('Doing ' + Prog.user + ' for ' + Prog.Name);

  assign(UserAUD, Prog.user + '.aud');
{$I-}
  reset(UserAUD);
{$I+}

  If (IOResult <> 0) then begin
    {--- Could not find an audit file ---}
    Writeln(' No audit file');

  end else begin

    {--- Read guff before Manuals list ---}
    repeat
      Readln(UserAUD, S);
    until (POS('MANUAL', S) >0) or EOF(UserAUD);

    { --- now read in list of licences ---}

    LegalStatus := 1; {Illegal}

    with Licence do
    repeat
      ReadCSV(UserAUD, Desc); DelSpace(Desc);
      Write(' (',Desc);
      IF Desc <> '' THEN begin
        ReadCSV(UserAUD,Name); DelSpace(Name); name := ucase(name); write(',',Name);
        ReadCSV(UserAUD, Ver); DelSpace(Ver); ver := ucase(ver); write(',',Ver,') ');

        IF prog.Name = Name THEN
          IF prog.Ver = Ver THEN
             LegalStatus := 0 {Legal}
          else
             begin LegalStatus := 2; LegalVer := Ver; end; {Some sort of licence}
      end;

    until (Desc = '' ) or (LegalStatus =0) or eof(UserAUD); {end of product list or product found}

    CLOSE(UserAUD);

    case LegalStatus of
      0 : S := 'Legal';
      1 : begin Total2Buy := Total2Buy + 1; S := '*** ILLEGAL ***'; end;
      2 : begin Total2Check := Total2Check +1; S := 'Legal for ' + LegalVer; end;
    end;

    writeln(Prog.Ver,' ',S);

    PRN.WriteLn('      '+PadSpaceR(prog.User,15)+PadSPaceR(prog.Ver,10)+S);
  end;
end;


{*********************************************
 ***      FILE NAME DISCREPANCY REPORT     ***
 *********************************************}
{Compares the names of .FSF files with names of
 .AUD files, and prints those that are NOT duplicates
 - ie all those that do not have both a fingerprint
 and audit database file}

procedure FNameDiscRpt;
var
  DirInf : SearchRec;
  Who : string;

  procedure FNameSearch(Check,Wth : string);
  {Looks through all the .<Check> files, comparing with the .<Wth> files}

  begin
    FindFirst('*.'+check, AnyFile, DirInf);

    repeat
      Who := copy(DirInf.Name,1,Pos('.',DirInf.Name)) + Wth;

      if FSearch(who, '.') = '' then PRN.writeln(' ' + DirInf.Name);

      FindNext(DirInf);
   until DOSError <>0;  {New file not found}
  end;


begin
 PRN.Writeln('**********************');
 PRN.Writeln('*** DATABASE CHECK ***');
 PRN.Writeln('**********************');
 PRN.Writeln('');
 PRN.writeln('Existing Files with no corresponding AUD/FSF:');

 FNameSearch('AUD','FSF');
 FNameSearch('FSF','AUD');

end;



{**********************************************
 ***               MAIN PROGRAM             ***
 **********************************************}

procedure PrintTotals;
begin
   PRN.WriteLn(SPACE(45) + 'TOTAL TO BUY:' + I2STR(total2buy) );
end;

var
  I : integer;

begin
  {---- Initialise -----}
  Clrscr;
  writeln('Initialising...');

  ReadIgnore;

  PRN.Init;
  PRN.NewPage;

  FNameDiscRpt;

  assign(FingerCSV, 'Tags.csv');                {output from FINGER audit}
  reset(FingerCSV);

  total2buy := -1;                             {to prevent printing first line round}

  PRN.Writeln('*********************************************');
  PRN.Writeln('***       LIST OF LEGAL/ILLEGAL COPIES    ***');
  PRN.Writeln('*********************************************');
  PRN.Writeln('');
  PRN.Writeln('<Program Name>  (Description)');
  PRN.Writeln('      <User Data File>  <Prog Ver/Siz>   <Manual Ver>');
  PRN.Writeln('      <User Data File>  <Prog Ver/Siz>   <Manual Ver>');
  PRN.Writeln('              :              :                 :');


  WHILE NOT EOF(FingerCSV) do with prog do begin

    ReadCSV(FingerCSV,Name); ReadCSV(FingerCSV,Ver);
    IF Str2R(Ver) < 5 THEN ReadCSV(FingerCSV, Desc) ELSE Desc := '';
    ReadCSV(FingerCSV, User); ReadCSV(FingerCSV, Dir);

    if not InIgnoreList(Name, Ver) then begin

      PrintLine := PadSpaceR(Name,15);
      IF (desc <> '') then PrintLine := PrintLine + ' (' + desc + ')';

      IF (PrintLine <> OldPrintLine) then begin  {Change of program name/version}
        IF (total2buy > -1) then PrintTotals;

        IF (Name <> oldprogname) then begin
           PRN.WriteLn('__________________________________________________________________');
           oldprogname := Name;
        end ELSE
           PRN.WriteLn(' ');

        PRN.WriteLn(PrintLine);
        oldPrintLine := PrintLine;
        total2buy := 0; total2check := 0; {reset total illegals}

      end;

      CheckIfBought;

    end; {if not ignore}

  END;  {while more to check}

  PrintTotals;

  PRN.EndPage;
END.
