program DSU; {* ****************************************************** ** ** ** DSU V2.1H/110693 by Andy Gruenenwald ** ** ** ****************************************************** Disk Statistics and Usage ----------------------------- This program has several exit points: 1) at end of program 2) in the procedure "DisplayCommandOptions" 3) in the procedure "GetDriveType" (lots of exit points = good structure = good programming) *} {$M 49152,0,655360} uses Dos, Crt, Andy; {* The following routines in the "Andy" unit are used here: PaddedNumber PaddedString InToStr Power PrintPercent *} {* ************************************************** ** VARIABLE DECLARATION ** ************************************************** *} var {------------ Variables used in general processing ------------} DisplayDriveStats : Boolean; DisplayFileStats : Boolean; MakeDLF,MakeFLF : Boolean; DLFfile,FLFfile : text; DriveNumber : Byte; { 0 = C, 1 = D, etc. } {------------ Variables used in parsing directory ------------} FileSpec : String; { this is file search string } NumOfFiles,TotFiles : Word; { # of files per dir/tot in path } NumOfBytes,TotBytes : LongInt; { # of bytes per dir/tot in path } TotDirs : Word; Regs : Registers; cy,i,j : Byte; DirEntry : SearchRec; { defined in DOS unit -> ref. p. 258 } EntryTime : DateTime; { defined in DOS unit -> ref. p. 391 } DriveLetter,s : String; DirCount,DirMarker : Array [1..12] of Byte; CurrentPath : Array [1..12] of String[80]; DirName : Array [1..12,1..50] of String[80]; Level : Byte; {------------ Used in the BIOS calls to get drive stats ------------} DriveSize,DriveFree : Longint; N1,N2,N3,N4 : Longint; NumCylndrs,NumHeads : Word; DriveSec,DriveClu : Word; DriveType : Word; {* ******************************** ** GENERAL PROCEDURES ** ******************************** *} procedure DisplayCommandOptions; begin Writeln; Writeln (' Usage: DSU2 [/D:drive] [/S] [/F] [/L] [/?]'); Writeln; Writeln (' /D:drive - Specifies the drive letter to work with (C-Z)'); Writeln (' /S - Display disk Statistics only'); Writeln (' /F - Display file statistics only'); Writeln (' /L - Creates a DLU.DSU (directory listing file)'); Writeln (' /I - Create a FLU.DSU (file listing file)'); Writeln (' /? - Display this help information'); Writeln; Writeln (' NOTE: the /L and /I options will not work with the /S option.'); Writeln; Writeln (' Example: DSU2 /D:C /F'); Writeln (' will analyze and print the file stats of drive C.'); Halt(0); end; procedure ParseCommandLine; var z : Byte; begin for z:=1 to ParamCount do begin s:= Copy(ParamStr(z),2,1); if (s='D') or (s='d') then begin DriveLetter:= Concat(Copy(ParamStr(z),4,1),':'); DriveNumber:= ord(DriveLetter[1])-67; if DriveNumber > 23 then dec(DriveNumber,32); if DriveNumber > 253 then begin { user is being stupid and using A or B } Sound(440); Delay(100); NoSound; Writeln; Writeln (' Error ---> Cannot specify a floppy drive.'); halt(0); end; end; if (s='F') or (s='f') then DisplayDriveStats:= FALSE; if (s='S') or (s='s') then DisplayFileStats:= FALSE; if (s='L') or (s='l') then MakeDLF:= TRUE; if (s='I') or (s='i') then MakeFLF:= TRUE; if s = '?' then DisplayCommandOptions; end; end; {* ********************************************** ** BIOS ROUTINES TO GET DRIVE STATS ** ********************************************** *} procedure GetDriveInfo; {* GetDriveInfo finds the following: ù DriveSize, DriveFree ù NumHeads, NumCylndrs *} begin DriveSize:=0; Regs.AX:= $3600; Regs.DX:= DriveNumber+3; Intr($21,Regs); if Regs.AX <> $FFFF then begin N1:= Regs.AX; N2:= Regs.CX; N3:= Regs.DX; N4:= Regs.BX; DriveSec:= N2; DriveClu:= N1; DriveSize:= N1*N2*N3; DriveFree:= N1*N2*N4; Regs.AX:= $0800; Regs.DX:= DriveNumber+$80; Intr($13,Regs); N1:= ((Regs.DX and 65280) div 256)+1; N2:= (Regs.CX and 192) div 32; N3:= (Regs.CX and 65280) div 256; N4:= (N2*128+N3)+2; NumHeads:= N1; NumCylndrs:= N4; end; end; procedure GetDriveType; {* Results from the two BIOS tests: 0 - local floppy drive (removable media) 10 - local hard disk (nonremovable media) 100 - network hard disk (nonremovable media) 110 - network floppy drive (removable media) 999 - invalid drive *} begin DriveType:= 0; Regs.AX:= $4408; Regs.BX:= DriveNumber+3; MsDos(Regs); { See if drive has removable media } if (Regs.Flags and 1) = 0 then DriveType:= Regs.AX * 10; Regs.AX:= $4409; Regs.BX:= DriveNumber+3; MsDos(Regs); { See if drive is local or remote } if (Regs.Flags and 1) = 0 then DriveType:= DriveType+((Regs.DX and 4096) div 4096) * 100 else DriveType:= 999; if DriveType = 999 then begin Sound(440); Delay(100); NoSound; Writeln; Writeln (' Error --> ',DriveLetter,' is not a valid drive.'); Halt(0); end; end; {* **************************************** ** PRINTING/DISPLAY ROUTINES ** **************************************** *} procedure Spc (i,j: LongInt); { Prints a right-justified number "i" } var { in a field that is "j" chars long. } k,n : Integer; begin n:= (j-Length(InToStr(i))); for k:=1 to n do begin write (' '); end; end; procedure PrintData (m : LongInt); { Formatted printing of a "m" bytes } begin TextColor(14);Spc(m,9);Write (m); TextColor(2);Write (' bytes '); TextColor(14);Spc(m div 1048576,5);Write (m div 1048576);TextColor(2);Write (' MB'); Writeln; end; procedure PrintDriveReport; begin TextColor(2); Writeln; Write ('Drive ',chr(DriveNumber+67),' Size: '); if DriveSize>0 then begin PrintData (DriveSize); Write ('Free drive space: '); PrintData (DriveFree); Write ('Format: '); TextColor(14);Write (DriveSec);TextColor(2); Write (' bytes per sector, '); TextColor(14);Write (DriveClu);TextColor(2); Writeln (' sectors per cluster.'); TextColor(14);Write (' ',DriveSec*DriveClu);TextColor(2); Writeln (' bytes per allocation unit.'); Write ('Number of heads: ');TextColor(14);Write (NumHeads);TextColor(2); Writeln; Write ('Number of cylinders: ');TextColor(14);Write (NumCylndrs);TextColor(2); Writeln; TextColor(2);Write ('Drive Interface Orientation: '); TextColor(14); if (NumHeads > 1) and (DriveType = 10) then Writeln ('Hard Drive'); if DriveType = 100 then Writeln ('Network Hard Drive'); if (NumHeads = 1) and (DriveType = 10) then Writeln ('Device Driver'); if (NumHeads > 2) and (DriveType = 0) then Writeln ('Removable media drive'); if (NumHeads = 2) and (DriveType = 0) then Writeln ('Floppy drive'); end else begin TextColor(14); Write ('Not Present'); Writeln; Writeln; end; end; procedure PrintFileReport; var BIUS : Longint; { Bytes In Unused Space } BID : Longint; { Bytes In Directories } begin TextColor(2); Writeln; if TotFiles > 0 then begin BIUS:= DriveSize-TotBytes-DriveFree; BID:= DriveSec*DriveClu;BID:=BID*TotDirs; Write ('Drive contains ');TextColor(14);Write (TotFiles);TextColor(2); Write (' files in ');TextColor(14);Write (TotDirs);TextColor(2); Writeln (' directories.'); Write ('Disk space used by file data: ');PrintData(TotBytes); Write ('Disk space used by directories: ');PrintData(BID); Write ('Slak space in file clusters: ');PrintData(BIUS); Write ('Percent of disk in slak space: ');TextColor(14); PrintPercent(BIUS/DriveSize*100); TextColor(2);Writeln (' %'); end else begin Writeln ('No files found on the specified drive.'); end; end; procedure GenerateDLFline; var s1,s2,s3 : String; begin s1:= PaddedString(Concat(DriveLetter,CurrentPath[Level]),' ',43,TRUE); s2:= PaddedNumber(NumOfFiles,' ',9,FALSE); s3:= PaddedNumber(NumOfBytes,' ',19,fALSE); Writeln (DLFfile,s1,s2,s3); end; procedure GenerateFLFline; var s1,s2,s3 : String; begin s1:= PaddedString(DirEntry.Name,' ',12,TRUE); s2:= PaddedNumber(DirEntry.Size,' ',9,FALSE); Write (FLFfile,s1,' ',s2,' '); s1:= PaddedNumber(EntryTime.Month,'0',2,FALSE); s2:= PaddedNumber(EntryTime.Day,'0',2,FALSE); s3:= PaddedNumber(EntryTime.Year,'0',4,FALSE); Write (FLFfile,s1,'-',s2,'-',s3,' '); case EntryTime.Hour of 12..24: begin s1:= PaddedNumber(EntryTime.Hour-12,'0',2,FALSE); s2:= PaddedNumber(EntryTime.Min,'0',2,FALSE); Write (FLFfile,s1,':',s2,'p'); end; 0: begin s1:= PaddedNumber(EntryTime.Min,'0',2,FALSE); Write (FLFfile,'12:',s1,'a'); end; else begin s1:= PaddedNumber(EntryTime.Hour,'0',2,FALSE); s2:= PaddedNumber(EntryTime.Min,'0',2,FALSE); Write (FLFfile,s1,':',s2,'a'); end; end; Write (FLFfile,' '); if DirEntry.Attr and 1 = 1 then Write (FLFfile,'R') else Write (FLFfile,'.'); if DirEntry.Attr and 2 = 2 then Write (FLFfile,'H') else Write (FLFfile,'.'); if DirEntry.Attr and 4 = 4 then Write (FLFfile,'S') else Write (FLFfile,'.'); if DirEntry.Attr and 8 = 8 then Write (FLFfile,'V') else Write (FLFfile,'.'); if DirEntry.Attr and 16=16 then Write (FLFfile,'D') else Write (FLFfile,'.'); if DirEntry.Attr and 32=32 then Write (FLFfile,'A') else Write (FLFfile,'.'); Writeln(FLFfile); end; {* ****************************************** ** DIRECTORY PARSING PROCEDURES ** ****************************************** *} procedure MoveInOneLevel; begin inc(Level); CurrentPath[Level]:= Concat(CurrentPath[Level-1],DirName[Level-1,DirMarker[Level-1]+1],'\'); DirMarker[Level]:=0; DirCount[Level]:=0; end; procedure MoveOutOneLevel; { May move out more than 1 } begin dec(Level); inc(DirMarker[Level]); if DirCount[Level] = DirMarker[Level] then begin if Level = 1 then Exit; MoveOutOneLevel; end; end; procedure SearchDirectories; begin repeat NumOfFiles:=0; NumOfBytes:=0; FindFirst(FileSpec,$3F,DirEntry); { find all files, regardless of attr. } { ***** Read files till no more entries in directory ***** } if DosError = 0 then begin if Level > 1 then begin { Show directory scan progress } GotoXY(50,WhereY);Write (' '); GotoXY(30,WhereY);Write ('Scanning directory: ',DirName[Level-1,DirMarker[Level-1]+1]); if MakeFLF then begin Writeln(FLFfile); Writeln(FLFfile,'Files for the directory ',Concat(DriveLetter,CurrentPath[Level]),'..........'); end; end; while DosError = 0 do begin { ***** Calculate hours and minutes ***** } UnpackTime(DirEntry.Time,EntryTime); { ***** Process a DIR ***** } if DirEntry.Attr and 16 = 16 then begin if DirEntry.Name[1] <> '.' then begin inc(DirCount[Level]); inc(TotDirs); DirName[Level,DirCount[Level]]:= Copy(DirEntry.Name,1,j-1); end; end; { ***** if a FLF file is being created, write a line ***** } if MakeFLF then GenerateFLFline; { ***** Increment file and byte count ***** } if DirEntry.Attr and 24 = 0 then inc(NumOfFiles); inc(NumOfBytes,DirEntry.Size); { ***** Get next filename ***** } FindNext(DirEntry); end; end; if MakeDLF then GenerateDLFline; if DirCount[Level] > 0 then MoveInOneLevel else begin if Level = 1 then Exit; MoveOutOneLevel; MoveInOneLevel; end; FileSpec:= Concat(DriveLetter,CurrentPath[Level],'*.*',#0); TotFiles:=TotFiles+NumOfFiles; TotBytes:=TotBytes+NumOfBytes; until DirMarker[1]=DirCount[1]; GotoXY(30,WhereY); Writeln (' '); end; { ************************************************************* } { ** ** } { ** Main body begins here ** } { ** ** } { ************************************************************* } begin TextColor(7); Writeln ('DSU (Disk Statistics and Usage) V2.1H/110693 by Andy Gruenenwald'); Writeln ('Incorporating the really exciting "Andy" unit V2.0H/110793!'); { ***** Set defaults ***** } GetDir(0,s); { Get current Drive } DriveLetter:= copy(s,1,2); DriveNumber:= ord(DriveLetter[1])-67; DisplayDriveStats:= TRUE; DisplayFileStats:= TRUE; MakeDLF:= FALSE; MakeFLF:= FALSE; if ParamCount > 0 then ParseCommandLine; TotFiles:=0; TotBytes:=0; TotDirs:=0; Level:=1; { ** clear all arrays because TP is to dumb to do it ** } for j:=1 to 12 do begin DirMarker[j]:=0; DirCount[j]:=0; CurrentPath[j]:=''; end; for i:=1 to 12 do begin for j:=1 to 50 do DirName[i,j]:=''; end; CurrentPath[1]:='\'; FileSpec:= Concat(DriveLetter,CurrentPath[1],'*.*',#0); { ********** Begin processing of drive ********** } Writeln ('Accumulating information on drive ',DriveLetter,' ...'); Writeln; GetDriveInfo; { ** called 'cause we need to know sizes ** } if DisplayDriveStats then begin { ** regardless of what we want displayed * } Writeln ('>> Accessing drive information... '); GetDriveType; end; if DisplayFileStats then begin Write ('>> Looking for files...'); if MakeDLF then begin assign (DLFfile,'DLF.DSU'); Rewrite (DLFfile); Writeln (DLFfile,'DLF (Directory Listing File) generated by:'); Writeln (DLFfile,'DSU (Disk Statistics and Usage) V2.1H/110693 by Andy Gruenenwald'); Writeln (DLFfile); Writeln (DLFfile,'Path name # of files Size in bytes'); Writeln (DLFfile,'--------------------------------------- ---------- -------------'); end; if MakeFLF then begin assign (FLFfile,'FLF.DSU'); rewrite (FLFfile); Writeln (FLFfile,'FLF (File Listing File) generated by:'); Writeln (FLFfile,'DSU (Disk Statistics and Usage) V2.1H/110693 by Andy Gruenenwald'); Writeln (FLFfile); end; SearchDirectories; end; if MakeDLF then begin Writeln (DLFfile); Write (DLFfile,' Totals: '); Write (DLFfile,PaddedNumber(TotFiles,' ',9,FALSE)); Writeln (DLFfile,PaddedNumber(TotBytes,' ',19,FALSE)); Close (DLFfile); end; if MakeFLF then close (FLFfile); if DisplayDriveStats then PrintDriveReport; if (DisplayFileStats) and (TotFiles > 0) then PrintFileReport; TextColor(7); end.