PROGRAM lsetdate;

  CONST
    header     =
        'SETD01, by Brent B. Powers, based on LSETDATE by CB Falconer.';

    all        = '*.*';
    ovrwr      = '/O';
    binrcdmax  = 127;                  { 1 sector of file }
    fnmsize    = 12;                   { handles most systems }
    datelnsz   = 15;

  TYPE
    dateln     = string[datelnsz];
    fname      = string[fnmsize];
    err        = (needate, notlib, cantfind, crcerr);
    binchunkb    = array[0..binrcdmax] of byte;
    binchunkw    = array[0..63] of integer;
    binrecptr = ^binrecd;
    binrecd   = record
      next      : binrecptr;
      recd      : binchunkb;
    end; { binrecd }
    binrecptw = ^binrecdw;
    binrecdw  = record
      next      : binrecptw;
      recd      : binchunkw;
    end; { binrecw }

  VAR
    dateline   : dateln;
    libfile    : file;
    libfname   : fname;
    mdlname    : fname;
    size,                              { of directory }
    dircrc     : integer;              { extracted from dir[0] }
    minparam   : integer;
    root       : binrecptr;
    rootw      : binrecptw absolute root;
    crctbl     : array[0..512] of byte;
    ndate      : integer;
    europe     : boolean;
    overwrite  : boolean;

{$IDRDATE.INC} {  contains function drdate( dateline:dateln):integer;   }

{$ICRC.INC}    {  contains procedure crcinit;                           }
               {      and procedure crc(a:integer;var crcword:integer); }


  procedure help;

    begin { help }
      writeln;
      writeln(header);
      writeln('Examples :');
      writeln('       B>SETD libraryname MM/DD/YY');
      writeln('             sets all undated files in library');
      writeln;
      writeln('       B>SETD lbrname MM/DD/YY modulename');
      writeln('             sets selected, undated files');
      writeln;
      writeln('       B>SETD lbrname MM/DD/YY /O');
      writeln('             sets ALL files, dated or not, and');
      writeln;
      writeln('       B>SETD lbrname MM/DD/YY modulename /O');
      writeln('             sets selected files, date or not.');
      writeln('    Wildcards accepted. *.* equivalent to no entry');
      writeln('    MM/DD/YY is the date to be set.  Mandatory if the');
      writeln('    system does not supply the current date, otherwise');
      writeln('    overrides system date. The date may also be entered');
      writeln('    as EDD/MM/YY. Date separator may also be a ''\'' or ''-''.');
    end; { help }

  procedure signal(x : err);
    var a : char;

    begin { signal }
      write('*** ERROR *** ');
      case x of
        crcerr:   begin
                    writeln('CRC directory error, bad library: ', libfname);
                    write(chr(7), '^C aborts, <ret> continues');
                    read(kbd,a);
                    if a = ^C then
                      halt;
                  end;

        notlib:   write('Not a library: ', libfname);

        cantfind: write('Can''t find ', libfname);

        needate:  write('No system date or invalid date format');

      end;
      writeln;
      if x <> crcerr then
        help;
    end; { signal }

  function chkdate(var f):boolean;
    const valchar : string[13] = '0123456789/-\';
    var i  : integer;
        st : string[255] absolute f;
        l  : byte absolute f;
        eur : boolean;
    begin
      if UpCase(st[i]) = 'E' then
        begin
          i := 1;
          eur := true;
        end else begin
          i := 0;
          eur := false;
        end;
      repeat
        i := succ(i);
      until (i=l) or (pos(st[i],valchar)=0);
      if i = l then
        begin
          chkdate := true;
          europe := eur;
          if europe then
            delete(st,1,1);
        end else
          chkdate := false;
    end;

  procedure cap(var f);
    var
      i  : integer;
      st : string[255] absolute f;
    begin
      for i := 1 to ord(st[0]) do
        st[i] := UpCase(st[i]);
    end;

  procedure setdate(f : fname; var d : dateln);

    var
      da,mo: integer;
      d1 : dateln;
      dl : char;

    function getfield(min, max : integer; delimiter : char) : boolean;

      var
        i,j: integer;
        st : string[3];

      begin { getfield }
        st := copy(f,1,2);
        val(st,i,j);
        if (j = 0) and (i >= min) and (I <= max) then
          begin
            getfield := true;
            d1 := d1+st+delimiter;
            delete(f,1,3);
          end
        else
          getfield := false;
      end; { getfield }

    begin { setdate }
      if pos('/',f)>0 then
        dl := '/'
      else if pos('-',f)>0 then
        dl := '-'
      else
        dl := '\';
      f := f + ' ';
      d := '00/00/00 '; { default for error }
      d1[0] := #0;
      if europe then
        begin
          mo := 31;
          da := 12;
        end else begin
          mo := 12;
          da := 31;
        end;
      if getfield(1, mo, dl) then
        if getfield(1, da, dl) then
          if getfield(0, 99, ' ') then
            d := d1;
    end; { setdate }

  function verify(var size, dircrc : integer) : boolean;

    begin { verify }
      size := rootw^.recd[7];
      verify := (root^.recd[0] = 0) and (root^.recd[1] < 127) and
                (size > 0) and (size < 8192) and (rootw^.recd[6] = 0);
      dircrc := rootw^.recd[8];
      rootw^.recd[8] := 0;  { for dir crc calc. }
    end; { verify }

  procedure revise(dateline : dateln; size : integer);

    type
      mnametype = array[1..11] of char;

    var
      current   : binrecptr;
      currentw  : binrecptw absolute current;
      i, crcwd  : integer;
      mname     : mnametype;

    procedure getchunk;

      var
        i     : integer;

      begin { getchunk }
        new(current^.next);
        current := current^.next;
        BlockRead(libfile,current^.recd,1);
      end; { getchunk }

    procedure setupdate;

      procedure chkmatchat(ix : integer);
      { implements wild card matching against current^.recd[ix] }

        var
          i   : integer;
          iy  : integer;
        begin { chkmatchat }
          iy := ix*16;
          ix := iy*2;
          if current^.recd[ix] = 0 then begin { live entry }
            i := 0;
            repeat
              i := succ(i);
            until  (i=11)
               or  (   (current^.recd[i+ix] <> ord(mname[i]))
                   and (mname[i] <> '?'));
            if i = 11 then { match, update the date }
              if (currentw^.recd[iy+9] = 0) or overwrite then
              currentw^.recd[iy+9] := ndate;
          end;
        end; { chkmatchat }

      procedure chknodateat(ix : integer);
      { implements setting of any unset dates current^.recd[ix] }
        var iy : integer;


        begin { chknodateat }
          iy := ix*16;
          if (current^.recd[iy*2] = 0) then
            if (currentw^.recd[iy + 9] = 0) or overwrite then
              currentw^.recd[iy+9] := ndate;
        end; { chknodateat }

      begin { setupdate }
        if mdlname = all then
          begin { all undated }
            chknodateat(0);
            chknodateat(1);
            chknodateat(2);
            chknodateat(3);
          end
        else
          begin               { update the specific entry }
            chkmatchat(0);
            chkmatchat(1);
            chkmatchat(2);
            chkmatchat(3);
          end;
      end; { setupdate }

    procedure putchunk;
      begin { putchunk }
        BlockWrite(libfile,current^.recd,1);
        current := current^.next;
      end; { putchunk }

    procedure standardize(var mdlname : fname; var mname : mnametype);
    { input wild string format to directory format }

      var
        i, j     : integer;

      begin { standardize }
        Cap(mdlname);
        i := 1;
        j := 1;
        fillchar(mname,12,' ');
        mname[0] := #11;
        while (i <= 8) and not (mdlname[i] in [' ', '.', '*']) do
          begin
            mname[j] := mdlname[i];
            i := succ(i);
            j := succ(j);
          end;
        if mdlname[i] = '*' then
          begin
            for j := j to 8 do
              mname[j] := '?';
            i := succ(i);
          end;
        while not (mdlname[i] in [' ', '.']) do
          i := succ(i);
        if mdlname[i] = '.' then
          begin
            i := succ(i);
            j := 9;
            while (j <= 11) and not (mdlname[i] in [' ', '*']) do
              begin
                mname[j] := mdlname[i];
                i := succ(i);
                j := succ(j);
              end;
            if mdlname[i] = '*' then
              for j := j to 11 do
                mname[j] := '?';
          end;
      end; { standardize }

    begin { revise }
      standardize(mdlname, mname); { to the directory format }
      ndate := drdate(dateline);
      current := root;
      crcwd := 0;
      for i := 1 to pred(size) do
        getchunk; { get the whole directory into memory }
      current^.next := nil;
      current := root;
      repeat
        for i := 0 to binrcdmax do
          crc(current^.recd[i], crcwd);
        setupdate;
        current := current^.next;
      until current = nil;
      if (crcwd <> dircrc) and (dircrc <> 0) then
        signal(crcerr);
      current := root;
      crcwd := 0;
      repeat
        for i := 0 to binrcdmax do
          crc(current^.recd[i], crcwd);
        current := current^.next;
      until current = nil;
      seek(libfile, 0);
      rootw^.recd[8] := crcwd;
      current := root;
      while current <> nil do
        putchunk;
      close(libfile);
    end; { revise }

  procedure initialize;

    begin { initialize }
      dateline := paramstr(paramcount);
      cap(dateline);
      overwrite := dateline = ovrwr;

      { with system clock   }
      {     minparam := 1;   }
      {     dater(dateline); }

      { without system clock }
      minparam := 2;
      dateline := '00/00/00';

{     If you can get the date from your system, the routine to do that  }
{     should be coded as procedure dater(var dl : dateln);              }

      crcinit;
    end; { initialize }

  begin { lsetdate }
    initialize;
    if paramcount < minparam then
      help
    else begin
      if pos('.',paramstr(1)) = 0 then { set default .lbr }
        libfname := paramstr(1)+'.LBR '
      else
        libfname := paramstr(1);
      assign(libfile,libfname);
      {$I-}
      reset(libfile);
      {$I+}
      if IOResult=0 then
        begin
          new(root); { file was found, make sure it is a library. }
          BlockRead(libfile,root^.recd,1);
          if verify(size, dircrc) then
            begin
            { whether or not there is a system clock, they may have entered }
            { the date. Check if paramstr(2) is a date }
              mdlname := paramstr(2);
              if chkdate(mdlname) then
                begin
                  setdate(mdlname, dateline);
                  mdlname := paramstr(3) { paramstr2 was date }
                end;
              if (mdlname = ovrwr) or (mdlname[0] = #0) then
                mdlname := all;
              if dateline = '00/00/00' then
                signal(needate)
              else
                revise(dateline, size)
          end else
            signal(notlib);
        end else
          signal(cantfind);
    end;
  end. { lsetdate }
