home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / DD11.ZIP / DD11.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1989-07-01  |  19.3 KB  |  633 lines

  1.  {$Define RAM}
  2.  
  3.  { undefine above symbol to use virtual array
  4.          for debugging in IDE }
  5.  
  6.  {$IFDEF RAM}
  7.    Uses Dos,TPDos,
  8.      TPInline,TPString,TPDate,TPRArray,TPMemchk;
  9.  {$ELSE}
  10.    Uses Dos,TPDos,
  11.      TPInline,TPString,TPDate,TPVArray,TPMemChk;
  12.  {$ENDIF}
  13.  
  14.  const
  15.    NoDate   : longint = $0;
  16.    DriveLetter : char = #0;
  17.    FileMask : byte = Directory;
  18.    { used to set sector buffer size }
  19.    MaxSectorSize  = 2048;
  20.    { must be 1/2 of above }
  21.    MaxWordSize   =  MaxSectorSize div 2;
  22.    { MaxSectorSize div 32 }
  23.    MaxEntry = MaxSectorSize div 32;
  24.    { used to hold files for
  25.      matching cluster search }
  26.    MaxDirEntry  = 150;
  27.    Verbose: boolean = False;
  28.  
  29.  {$IFNDEF RAM}
  30.    DeleteArray : boolean = true;
  31.    { set path and file name virtual array }
  32.    FatWName: string = 'c:\FatW.dta';
  33.    FatBName: string = 'c:\FatB.dta';
  34.  {$ENDIF}
  35.  
  36.  type
  37.  
  38.    { holds entries found by findfirst
  39.      to be used in matching directory entries }
  40.    DataPtr  = ^DataRec;
  41.    DataRec  =  string[12];
  42.    Searchtype = (Root,Cluster);
  43.  
  44.    SearName = object
  45.          FName : array[1..MaxDirEntry] of DataPtr;
  46.          MatchFound : word;
  47.          function PrepFileName(SearDir :String) : String;
  48.          procedure DirFind;
  49.          procedure ShowVerbose(SearDir :String ; Fattr :byte);
  50.       end;
  51.  
  52.    DirRec   =
  53.       record
  54.         FileNameExt  : array[1..11] of char;
  55.         FileAttr     : byte;
  56.         DirResv      : array[1..10] of byte;
  57.         TimeDate     : longint;
  58.         StartCluster : word;
  59.         FileSize     : longint;
  60.       end;
  61.  
  62.    BootRec   =
  63.       record
  64.         Junk             : array[1..3] of byte;
  65.         OEM              : array[1..8] of char;
  66.         BytePerSector    : word;
  67.         SectorPerCluster : byte;
  68.         ReservedSectors  : word;
  69.         NumOfFats        : byte;
  70.         RootDirEntries   : word;
  71.         TotalSectors     : word;
  72.         Media            : byte;
  73.         SectorsPerFat    : word;
  74.         SectorPerTrack   : word;
  75.         NumOfHeads       : word;
  76.         NumHiddenSectors : word;
  77.       end;
  78.  
  79.  var
  80.    SName       : SearName;
  81.    DirBuffer   : array[1..MaxEntry] of DirRec;
  82.    BootEntry   : BootRec;
  83.    FatBits     : byte;
  84.    PakTime     : longint;
  85.    DirTime     : DateTime;
  86.    VerboseDate : DateString;
  87.    VerboseTime : DateString;
  88.    TpBArr      : TpArray;
  89.    TpWArr      : TpArray;
  90.    SearchMode  : SearchType;
  91.    DirChanged,
  92.    RootBegin,RootEnd,
  93.    FatBegin,FatEnd,
  94.    EntryPerSector,
  95.    ClustersAvaiable,
  96.    TotalClusters,
  97.    BytesPerSector,
  98.    SectorsPerClusters :Word;
  99.  
  100.   function IsWholeNumber(Check :real): Boolean;
  101.   var
  102.     x : Longint;
  103.     y : real;
  104.   begin
  105.     X:=Trunc(Check);
  106.     y:=X;
  107.     IsWholeNumber := Y = Check;
  108.   end; { function IsWholeNumber }
  109.  
  110.   function ReadBootRecord: boolean;
  111.   var
  112.     DriveNumber        : byte;
  113.     GetInfo,GetBoot    : boolean;
  114.     BootBuffer         : array[1..MaxSectorSize] of byte;
  115.   begin
  116.       FillWord(BootBuffer, SizeOf(BootBuffer) div 2,Word(0));
  117.       DriveNumber := Ord(DriveLetter)-64;
  118.       GetInfo := GetDiskInfo(DriveNumber,ClustersAvaiable,TotalClusters,
  119.           BytesPerSector,SectorsPerClusters);
  120.       {$B+}
  121.       if Not GetInfo or (BytesPerSector > MaxSectorSize) then
  122.       {$B-}
  123.          begin
  124.            ReadBootRecord:=False;
  125.            exit;
  126.          end;
  127.       EntryPerSector:= BytesPerSector div 32;
  128.       GetBoot:= ReadDiskSectors(DriveNumber-1,0,1,BootBuffer);
  129.       if Not GetBoot then
  130.          begin
  131.            ReadBootRecord:=False;
  132.            exit;
  133.          end;
  134.       Move(BootBuffer,BootEntry,Sizeof(BootEntry));
  135.       With BootEntry do begin
  136.           FatBegin := ReservedSectors;
  137.           FatEnd   := FatBegin + SectorsPerFat - 1;
  138.           RootBegin:= ReservedSectors + (NumOfFats * SectorsPerFat);
  139.           RootEnd  := RootDirEntries * 32 div BytePerSector + RootBegin - 1;
  140.         end;
  141.       ReadBootRecord := GetInfo and GetBoot;
  142.   end; { function ReadBootRecord }
  143.  
  144.   function Cluster2Sector(Cluster: Word): Word;
  145.   begin
  146.      Cluster2Sector := ((Cluster - 2) *
  147.          BootEntry.SectorPerCluster) + RootEnd + 1;
  148.   end; { function Cluster2Sector }
  149.  
  150.   { convert standard file name to DOS storage format }
  151.   function SearName.PrepFileName(SearDir :String):String;
  152.   var
  153.     Dir   : DirStr;
  154.     Name  : NameStr;
  155.     Ext   : ExtStr;
  156.   begin
  157.     If (SearDir = '.') or (SearDir = '..') then
  158.       PrepFilename:=Pad(SearDir,11)
  159.     else
  160.        begin
  161.          Fsplit(SearDir,Dir,Name,Ext);
  162.          Dir:=Pad(Name,8)+Pad(Ext,4);
  163.          { remove dot from name }
  164.          Delete(Dir,9,1);
  165.          PrepFileName:=Dir;
  166.        end;
  167.   end; { function PrepFileName }
  168.  
  169.   { check if search file name matches directory entry }
  170.   function FoundMatch(SearDir :String;Ndx :word):Boolean;
  171.   begin
  172.     FoundMatch:=CompStruct(SearDir[1],DirBuffer[Ndx].FileNameExt,11)=Equal;
  173.   end; { function FoundMatch }
  174.  
  175.   {$I cluster.inc}
  176.  
  177.   procedure SearName.ShowVerbose(SearDir :String ; FAttr :Byte);
  178.   begin
  179.     Insert(' ',SearDir,9);
  180.     If FlagIsSet(FAttr,VolumeID) then
  181.       Writeln(SearDir,' <VOL>     ',VerboseDate,'  ',VerboseTime)
  182.     else if FlagIsSet(FAttr,Directory) then
  183.       Writeln(SearDir,' <DIR>     ',VerboseDate,'  ',VerboseTime)
  184.     else
  185.       Writeln(SearDir,' <FIL>     ',VerboseDate,'  ',VerboseTime)
  186.   end; { procedure ShowVerbose }
  187.  
  188.   procedure CalcFatBits;
  189.   const
  190.     BreakPoint : word = 4087;
  191.   Var
  192.     DosVer : word;
  193.     DosLo,DosHi :byte;
  194.   begin
  195.     DosVer := TpDos.DOSVersion;
  196.     DosHi := Hi(DosVer);
  197.     If (DosVer < $0200) or (DosVer > $031E) then
  198.       begin
  199.         Writeln('DOS version ',DosHi,'.',Lo(DosVer),' unsupported');
  200.         halt(1);
  201.       end;
  202.     {$B+}
  203.     If (DosHi < $03) or
  204.        ((DosHi = $03) and (TotalClusters < BreakPoint)) then
  205.       FatBits := 12
  206.     else
  207.       FatBits := 16;
  208.     {$B-}
  209.   end; { procedure FatBits }
  210.  
  211.   procedure BuildArray;
  212.   begin
  213.     {$IFDEF RAM}
  214.       MakeA(TpWArr,$FFFF,1,Sizeof(Word));
  215.       If Fatbits=12 then
  216.          MakeA(TpBArr,$FFFF,1,Sizeof(byte));
  217.     {$ELSE}
  218.        MakeA(TpWArr,$FFFF,1,Sizeof(Word),FatWName,$FFFF);
  219.        If Fatbits=12 then
  220.           MakeA(TpBArr,$FFFF,1,Sizeof(byte),FatBName,$FFFF);
  221.     {$ENDIF}
  222.   end; { procedure BuildArray }
  223.  
  224.   procedure GetFat;
  225.   Var
  226.     FatBufferW  : array[1..MaxWordSize] of word;
  227.     FatBufferB  : array[1..MaxSectorSize] of byte absolute FatBufferW;
  228.     DriveNumber : byte;
  229.     Start,Ndx,
  230.     FatRead     : word;
  231.   begin
  232.     Start:=0;
  233.     DriveNumber := Ord(DriveLetter)-65;
  234.     for FatRead := FatBegin to FatEnd do
  235.        begin
  236.          FillWord(FatBufferW, (Sizeof(FatBufferW) div 2),Word(0));
  237.          If Not ReadDiskSectors(DriveNumber,FatRead,1,FatBufferW) then
  238.            begin
  239.              Writeln('error reading fat table');
  240.              halt(1);
  241.            end;
  242.          case fatbits of
  243.            12 : begin
  244.                   for Ndx:= 1 to BytesPerSector do
  245.                     begin
  246.                       SetA(TpBArr,Start,0,FatBufferB[Ndx]);
  247.                       inc(Start);
  248.                     end;
  249.                 end;
  250.            16 : begin
  251.                   For Ndx:= 1 to (BytesPerSector div 2) do
  252.                     begin
  253.                       SetA(TpWArr,Start,0,FatBufferW[Ndx]);
  254.                       inc(Start);
  255.                     end;
  256.                 end;
  257.          end; { case fatbits }
  258.        end;
  259.   {$IFNDEF RAM}
  260.       If Fatbits=12 then
  261.          FlushA(TpBArr);
  262.       FlushA(TpWArr);
  263.   {$ENDIF}
  264.   end; { procedure GetFat }
  265.  
  266.   { converts 12 bit entries to words }
  267.   procedure CookTheFat;
  268.   Var
  269.     ByteLocR   : Real;
  270.     ByteLocL   : longint;
  271.     ClusterNum : Word;
  272.     WorkWord   : Word;
  273.     Workbyte   : Array[1..2] of byte Absolute WorkWord;
  274.   begin
  275.     CalcFatBits;
  276.     BuildArray;
  277.     GetFat;
  278.     case fatbits of
  279.       12 : Begin
  280.              For ClusterNum := 2 to TotalClusters do
  281.                begin
  282.                  ByteLocR:= ClusterNum * 1.5;
  283.                  BytelocL := Trunc(BytelocR);
  284.                  RetA(TpBArr,BytelocL+0,0,Workbyte[1]);
  285.                  RetA(TpBArr,BytelocL+1,0,Workbyte[2]);
  286.                  If IsWholeNumber(BytelocR) then
  287.                    WorkWord:=WorkWord and 4095
  288.                  else
  289.                    WorkWord:=WorkWord SHR 4;
  290.                  SetA(TpWArr,ClusterNum,0,WorkWord);
  291.                end;
  292.            end;
  293.       16 : {}
  294.     end; { case fatbits }
  295.  
  296.     { done with 12 bit entries }
  297.     {$IFDEF RAM}
  298.        If Fatbits=12 then
  299.          DisposeA(TpBArr);
  300.     {$ELSE}
  301.        If Fatbits=12 then
  302.          DisposeA(TpBArr,DeleteArray);
  303.        FlushA(TpWArr);
  304.     {$ENDIF}
  305.   end; { procedure CookTheFat }
  306.  
  307.   procedure TimeAndDate;
  308.   const
  309.     Done        : boolean = False;
  310.     NeedToWrite : boolean = False;
  311.   var
  312.     DirNdx,StartPoint,
  313.     SectorNumber,
  314.     SectorCount,
  315.     CurrentEntry,
  316.     FatReturned,
  317.     CurrentCluster : word;
  318.     DriveNumber    :byte;
  319.   begin
  320.     DriveNumber := Ord(DriveLetter)-65;
  321.     { comes from include to locate starting
  322.       cluster number for searching FAT table}
  323.     CurrentCluster := ClusterNumber;
  324.     StartPoint:=1;
  325.     SectorCount:=1;
  326.     DirChanged:=0;
  327.     FatReturned:=0;
  328.     { include set search mode }
  329.     Case SearchMode of
  330.        Cluster: SectorNumber := Cluster2Sector(CurrentCluster);
  331.           Root: SectorNumber := RootBegin;
  332.     end; { case searchmode }
  333.     Repeat
  334.       FillWord(DirBuffer,SizeOf(DirBuffer) Div 2,Word(0));
  335.       If not ReadDiskSectors(DriveNumber,SectorNumber,1,DirBuffer) then
  336.         begin
  337.           Writeln('error ',DosError,' reading sector ',SectorNumber);
  338.           DosError:=0;
  339.           exit;
  340.         end;
  341.       for CurrentEntry := 1 to EntryPerSector do
  342.         begin
  343.           { check for end of directory }
  344.           If DirBuffer[CurrentEntry].FileNameExt[1]=#0 then Done:=True;
  345.           If FlagIsSet(DirBuffer[CurrentEntry].FileAttr,FileMask) then
  346.             begin
  347.               { scan list for matching entry }
  348.               for DirNdx := StartPoint to SName.MatchFound do
  349.                 begin
  350.                   If FoundMatch(SName.Fname[DirNdx]^,CurrentEntry) then
  351.                     Begin
  352.                       DirBuffer[CurrentEntry].TimeDate:=PakTime;
  353.                       If Verbose then Sname.ShowVerbose(Sname.FName[DirNdx]^,
  354.                           DirBuffer[CurrentEntry].FileAttr);
  355.                       Inc(DirChanged);
  356.                       { found match set start point }
  357.                       StartPoint:=DirNdx+1;
  358.                       { set to update sector }
  359.                       NeedToWrite:=True;
  360.                     end;
  361.                 end;
  362.             end;
  363.         end;
  364.       If NeedToWrite then
  365.         begin
  366.           If not WriteDiskSectors(DriveNumber,SectorNumber,1,DirBuffer) then
  367.              begin
  368.                 Writeln('error ',DosError,' writing sector ',SectorNumber);
  369.                 DosError:=0;
  370.                 Exit;
  371.              end;
  372.           NeedToWrite:=False;
  373.         end;
  374.       { process all sector or get next cluster }
  375.       Case SearchMode of
  376.          Cluster:Begin
  377.                    If SectorCount >= SectorsPerClusters then
  378.                       begin
  379.                         RetA(TpWArr,CurrentCluster,0,FatReturned);
  380.                         CurrentCluster:=FatReturned;
  381.                         SectorNumber := Cluster2Sector(FatReturned);
  382.                         SectorCount:=1;
  383.                       end
  384.                    else
  385.                       begin
  386.                         Inc(SectorNumber);
  387.                         Inc(SectorCount);
  388.                       end;
  389.                    { check for last cluster in chain }
  390.                    case fatbits of
  391.                      12 : if FatReturned >= $0FF8 Then Done:=True;
  392.                      16 : if FatReturned >= $FFF8 Then Done:=True;
  393.                    end; { case fatbits }
  394.                  end;
  395.             Root:Begin
  396.                    inc(SectorNumber);
  397.                    { check for last entry in root directory }
  398.                    If SectorNumber > RootEnd then Done:=True;
  399.                  end;
  400.        end; { case searchmode }
  401.     Until Done;
  402.   end; { procedure TimeAndDate }
  403.  
  404.   { search directory for file spec to match }
  405.   Procedure SearName.DirFind;
  406.   var
  407.     DirInfo       : SearchRec;
  408.     SearStr       : DirStr;
  409.     FileMaskName  : NameStr;
  410.     FileMaskExt   : ExtStr;
  411.   begin
  412.     MatchFound:=0;
  413.     DriveLetter:=DefaultDrive;
  414.     FSplit(ParamStr(1),SearStr,FileMaskName,FileMaskExt);
  415.     SearStr := FileMaskName + FileMaskExt;
  416.     { must be in root to change VolumeID }
  417.     If FileMask=VolumeID then
  418.       begin
  419.         {$I-}
  420.         ChDir(DriveLetter+':\');
  421.         {$I+}
  422.         If IOResult=0 then {};
  423.       end;
  424.     { check for dot entries in filespec }
  425.     If (SearStr = '.') or (SearStr = '..') then
  426.        begin
  427.          inc(MatchFound);
  428.          If Not GetMemCheck(FName[MatchFound],Sizeof(DataRec)) then
  429.             begin
  430.               MatchFound:=0;
  431.               exit;
  432.             end;
  433.          Fname[MatchFound]^:=PrepFileName(SearStr);
  434.          exit;
  435.        end;
  436.     FindFirst(SearStr,FileMask,DirInfo);
  437.     While DosError = 0 do
  438.       begin
  439.         If FlagIsSet(DirInfo.Attr,FileMask) then
  440.           begin
  441.             inc(MatchFound);
  442.             if (MatchFound > MaxDirEntry) then
  443.               begin
  444.                 Writeln('Will only process first ',MaxDirEntry,' entries.');
  445.                 Dec(MatchFound);
  446.                 exit;
  447.               end;
  448.             If Not GetMemCheck(FName[MatchFound],Sizeof(DataRec)) then
  449.               begin
  450.                 Dec(MatchFound);
  451.                 exit;
  452.               end;
  453.             Fname[MatchFound]^:=PrepFileName(DirInfo.Name);
  454.           end;
  455.         FindNext(DirInfo);
  456.       end;
  457.   end;
  458.  
  459.   { flush dos buffers
  460.     to see changes }
  461.   procedure ResetDir;
  462.   var
  463.     reg : registers;
  464.   begin
  465.     reg.ah:=$0D;
  466.     Msdos(reg)
  467.   end;
  468.  
  469.   procedure Clock;
  470.   var
  471.     sec100,
  472.     dayofWeek : word;
  473.   begin
  474.     Fillchar(DirTime,Sizeof(DirTime),0);
  475.     with DirTime do
  476.       begin
  477.         GetTime(hour,min,sec,sec100);
  478.         GetDate(year,month,day,dayofweek);
  479.       end;
  480.   end;{ procedure Clock }
  481.  
  482.   Procedure ShowHelp;
  483.   begin
  484.     Writeln('DD-Directory Date, Version 1.1, (C) Copr 1989, Ted Stephens');
  485.     Writeln;
  486.     Writeln('    DD filespec [switches]');
  487.     {$IFNDEF RAM}
  488.        Writeln;
  489.        Writeln('    VIRTUAL MODE ');
  490.     {$ENDIF}
  491.     Writeln;
  492.     Writeln('Switches');
  493.     Writeln('    /D[mm-dd-yy]  Set the date to [month-day-year]');
  494.     Writeln('    /T[hh:mm:ss]  Set the time to [hour:minute:second]');
  495.     Writeln('    /N            Eliminate time & date stamp');
  496.     Writeln('    /V            Verbose listing of changes');
  497.     Writeln('    /SD           Select only directories (default)');
  498.     Writeln('    /SF           Select only files');
  499.     Writeln('    /SV           Select only volume label');
  500.     Writeln('    /SA           Select all directory entries');
  501.     Writeln;
  502.     Writeln('Running DD without /D and /T will stamp the current');
  503.     Writeln('date and time on all entries matching the file specified');
  504.     halt(1);
  505.   end;
  506.  
  507.   procedure CheckCommand;
  508.   const
  509.     DateOut :boolean = False;
  510.   var
  511.     ParamS : string;
  512.     myhr,mymin,mysec :byte;
  513.     TimeWork  : time;
  514.     i,myday,mymonth,myyear  : integer;
  515.   begin
  516.     if ParamCount = 0 then ShowHelp; { no switches }
  517.     Clock; { set directory time and date to dos }
  518.     for i := 2 TO ParamCount DO
  519.        begin
  520.          ParamS := StUpCase(ParamStr(i));
  521.          If ParamS[1] = '/' Then
  522.            case ParamS[2] OF
  523.              'D' : begin
  524.                      ParamS:=Copy(ParamS,3,8);
  525.                      If DateStringToDMY('mm/dd/yy',
  526.                           ParamS,myday,mymonth,myyear) then
  527.                             begin
  528.                               { date cannot be less than 1-1-80 }
  529.                               If myyear in [80..99] then
  530.                                  With DirTime do
  531.                                    begin
  532.                                      Year := 1900+myyear;
  533.                                      month:= mymonth;
  534.                                      day  := myday;
  535.                                    end
  536.                               else
  537.                                 ShowHelp; { invalid date }
  538.                             end
  539.                      else
  540.                        ShowHelp; { bad date format }
  541.                    end;
  542.              'S' : begin
  543.                      Case ParamS[3] OF
  544.                         'F' : FileMask:=AnyFile-Directory-VolumeID;
  545.                         'A' : FileMask:=AnyFile;
  546.                         'D' : FileMask:=Directory;
  547.                         'V' : FileMask:=VolumeID;
  548.                      else
  549.                        ShowHelp; { bad switch character }
  550.                      end;
  551.                    end;
  552.              'T' : begin
  553.                      ParamS:=Copy(ParamS,3,8);
  554.                      If Length(ParamS) = 8 then
  555.                         begin
  556.                           TimeWork:=TimeStringToTime('hh:mm:ss',ParamS);
  557.                           If (TimeWork=BadTime) then ShowHelp;
  558.                           If (TimeWork=0) and (ParamS<>'00:00:00') then ShowHelp;
  559.                           TimeToHMS(TimeWork,myHr,mymin,mysec);
  560.                           With DirTime do
  561.                             begin
  562.                               hour:= myhr;
  563.                               Min := mymin;
  564.                               Sec := mysec;
  565.                             end;
  566.                         end
  567.                      else
  568.                        ShowHelp; { bad time format }
  569.                    end;
  570.              'V' : Verbose:=True;
  571.              'N' : DateOut:=True;
  572.            else
  573.              ShowHelp; { bad switch character }
  574.            end { case params }
  575.          else
  576.             ShowHelp; { bad switch character }
  577.        end; { for i }
  578.        If Verbose then
  579.          begin
  580.            With DirTime do
  581.              begin
  582.                VerboseDate:=DMYtoDateString('Mm-dd-yy',day,month,year);
  583.                TimeWork:=HMSToTime(hour,min,sec);
  584.                If TimeWork=0 then
  585.                  VerboseTime:='no time'
  586.                else
  587.                  VerboseTime:=TimeToAmPmString('Hh:mmt',TimeWork);
  588.              end;
  589.          end;
  590.        If DateOut then
  591.          begin
  592.            VerboseDate:=' no date';
  593.            VerboseTime:='no time';
  594.            PakTime:=NoDate;
  595.          end
  596.        else
  597.          Packtime(DirTime,PakTime);
  598.     SName.DirFind; { find matching files }
  599.   end; { procedure CheckCommand }
  600.  
  601.   { main processing }
  602.   begin
  603.     CheckCommand;
  604.     If SName.MatchFound=0 then
  605.        begin
  606.          Writeln(' ');
  607.          Writeln('no directory entries found');
  608.          halt(1);
  609.        end;
  610.     If Not ReadBootRecord then
  611.        begin
  612.          Writeln(' ');
  613.          Writeln('error reading drive');
  614.          halt(1);
  615.        end;
  616.      CookTheFat;
  617.      TimeAndDate;
  618.      If DirChanged >0 then
  619.        begin
  620.          Writeln(' ');
  621.          If DirChanged=1 then
  622.            Writeln(DirChanged,' directory entry restamped')
  623.          else
  624.            Writeln(DirChanged,' directory entries restamped');
  625.          ResetDir;
  626.        end;
  627.      {$IFDEF RAM}
  628.          DisposeA(TpWArr);
  629.      {$ELSE}
  630.          DisposeA(TpWArr,DeleteArray);
  631.      {$ENDIF}
  632.   end.
  633.