home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 6 File
/
06-File.zip
/
drchk034.zip
/
drivechk.cmd
next >
Wrap
OS/2 REXX Batch file
|
1996-11-27
|
55KB
|
1,564 lines
/**********************************************************************/
/* */
/* DriveCheck v0.034 */
/* */
/* Written by Mike Ruskai <thanny@ibm.net> */
/* */
/* Distribute and modify freely, but it'd be nice if you give me */
/* credit if you distribute a modified form. */
/* */
/* Usage: DRIVECHK.CMD [/T] [<drive letter>: [/S|/N] */
/* [/O:<filename>|/C]] */
/* */
/* Parameters are optional. <drive letter>: is self explanatory. */
/* The /S switch tells the program skip extended attributes, and */
/* can only be used when the drive letter is passed. Using /N in */
/* its place indicates that EA's should be tallied. The /O switch */
/* tells the program to write the report to <filename>. Using /C */
/* in its place indicates that output should go to the CON device. */
/* If /S|/N or /O|/C aren't used, the program will prompt for a */
/* course of action concerning EA's and the display of results. */
/* A valid drive letter must be either the first or second */
/* argument. The /T switch has the sole purpose of forcing text */
/* mode operation, instead of Visrual REXX (if present). It is */
/* independant on the presence of a drive letter. */
/* */
/* This REXX script will scan an entire drive to determine how much */
/* space would be wasted for your specific files with the HPFS and */
/* FAT file systems. The display of this information depends on */
/* which file system is actually being used. */
/* */
/* I've tried to include as much information as I know how. In */
/* addition to the normal file system usage for a file and a */
/* directory, the extended attributes are also added up and used */
/* in space and slack space determinations. For files that do not */
/* have any extended attributes, the size of the Fnode (512 bytes) */
/* for the HPFS scenario is added to the slack space for the file, */
/* since it would not exist in the FAT scenario. That also goes */
/* for zero-byte files, which take up 512 bytes on HPFS drives, but */
/* zero bytes on FAT drives. */
/* */
/* What's *not* included is the space consumed by the system areas */
/* specific to the file system. The reason for this exclusion is */
/* that I don't know how to measure this size for HPFS volumes */
/* without capturing the output of CHKDSK (time-consuming), since I */
/* see no correlation between system area size and total disk size. */
/* My own HPFS drives have system areas ranging from 2MB to 6.5MB, */
/* with smaller drives sometimes having bigger system areas than */
/* some larger drives. */
/* */
/* As stated, extended attributes are included, but the process by */
/* which they are is quite slow. The only way via REXX to find the */
/* size of a file's extended attributes was to use the output of */
/* CMD.EXE or 4OS2.EXE's DIR command (using the /N parameter on FAT */
/* volumes). Piping the output to RXQUEUE was very slow, and */
/* redirecting the output to a file, then reading from the file was */
/* much faster, but still slow. 4OS2.EXE tends to be about 60% */
/* slower than CMD.EXE in loading itself into memory (more features */
/* to load). This would normally prompt you to type CMD before */
/* running this, given the frequency with which the interpreter is */
/* called (if you knew this about CMD and 4OS2, that is), but here, */
/* you needn't bother. Because of some extra work necessitated by */
/* the output of CMD, the speed balances out - both CMD and 4OS2 */
/* are equally snail-like in this process. Because of the horrible */
/* speed, you might want to skip EA checking altogether. The */
/* significance of EA's in space determinations vary from drive to */
/* drive, though. If you know that your files have quite a lot of */
/* extended attributes, you should just suffer to get results which */
/* more accurately represent reality. */
/* */
/* A new consideration is 32-bit 4OS2. Due to a bug in the 32-bit */
/* OS/2 API, extended attributes sizes are reported at twice their */
/* actual value. 4OS2 gets around this by halving the returned */
/* result, but this only works up to 32k. This is because the API */
/* call returns a maximum value of 64k, which is the maximum size */
/* of a file's EA. 16-bit CMD and 4OS2 do not have this problem. */
/* If you want to get accurate EA counts, you should use one of */
/* these, by typing CMD or 4OS2-16 before running DriveCheck. */
/* */
/* The output of the program is either plain text or graphical */
/* using the IBM EWS program Visual REXX. You must have VREXX on */
/* your system for the graphical method to work. */
/* */
/* Both interfaces show scan progress, and at the end provide a */
/* report on usage along with a conclusion about which file system */
/* would be more space-efficient for your drive. They also provide */
/* simple bar graphs that show used, wasted, and free space for the */
/* drive with both file systems. */
/* */
/**********************************************************************/
call RxFuncAdd 'SysLoadFuncs','RexxUtil','SysLoadFuncs'
call SysLoadFuncs
/* Sets digits to allow for large numbers, so that those of you with */
/* the big drives don't get a mess in the reported number. Using 12 */
/* digits guarantees that even if HPFS's theoretical limit of 512GB */
/* is implemented, the number will look alright (provided something */
/* odd doesn't happen to turn it into a fractional number, should it */
/* be larger than 4,294,967,296 - unless REXX can somehow use more */
/* than a 32-bit unsigned integer). */
numeric digits 12
/* To prevent VREXX from remaining after a crash of some sort, all */
/* errors will result in it being deinitialized before exiting the */
/* program. */
signal on halt
signal on error
signal on syntax
signal on failure
/* Checks whether or not to skip using Visual REXX, and independently */
/* whether other parameters were given. If only /T is passed, the */
/* program will run interactively in text-mode. With no parameters, */
/* it will run interactively with graphical dialogs using Visual REXX */
/* if it is present on the system. */
DCVer='0.034'
CurrDir=directory()
parse version rexxver
if word(rexxver,1)='OBJREXX' then orexx=1
else orexx=0
arg par1 par2 par3 par4
if par1='?'|par1='/?'|translate(par1)='/H' then signal Usage
if translate(par1)='/T' then do
TestDrive=par2
SkipSwitch=par3
OutFile=par4
Visual=0
end
else do
TestDrive=par1
SkipSwitch=par2
OutFile=par3
TestVis=1
end
if TestVis=1 then do
signal on syntax name vrtrap
success=VInit()
if success='ERROR' then Visual=0
else Visual=1
end
vrtrap:
if Visual\=1 then Visual=0
signal on syntax name syntax
/* Finds the INI file, if it exists, and digs out the colors to use */
/* for the graphs. If the file is "corrupt", it will use default */
/* colors. */
if Visual=1 then do
parse source SourceString
SourceFile=word(SourceString,3)
SourceDrive=filespec('drive',SourceFile)
SourcePath=filespec('path',SourceFile)
SourceDir=SourceDrive||SourcePath
call directory SourceDir
INIfile='drivechk.ini'
check=stream(INIfile,'c','query exists')
if check='' then defaults=1
else do
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then MainColor=cc
else MainColor='WHITE'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then MainTextColor=cc
else MainTextColor='BLACK'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then DirColor=cc
else DirColor='CYAN'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then DirTextColor=cc
else DirTextColor='BLACK'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then FileColor=cc
else FileColor='CYAN'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then FileTextColor=cc
else FileTextColor='BLACK'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then GraphColor=cc
else GraphColor='BLACK'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then GraphTextColor=cc
else GraphTextColor='WHITE'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then FreeColor=cc
else FreeColor='WHITE'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then UsedColor=cc
else UsedColor='GREEN'
cc=translate(linein(INIfile))
ccheck=ColorCheck(cc)
if ccheck=1 then WasteColor=cc
else WasteColor='RED'
res=translate(linein(INIfile))
rcheck=ResCheck(res)
if rcheck=1 then ScrRes=res
else ScrRes='640X480'
end
if defaults=1 then do
MainColor='WHITE'
MainTextColor='BLACK'
DirColor='CYAN'
DirTextColor='BLACK'
FileColor='CYAN'
FileTextColor='BLACK'
GraphColor='BLACK'
GraphTextColor='WHITE'
FreeColor='WHITE'
UsedColor='GREEN'
WasteColor='RED'
ScrRes='640X480'
end
/* Sets the variables for different screen resolutions based on what */
/* was in the INI file. */
if ScrRes='640X480' then do
FileDiagWidth=30
FontSize=16
TopText=960
TextSpace=38
DirListEnd=365
StatsHalfTwo=515
TopKey=890
BottomKey=825
KeyName=845
GraphLeft=12
GraphBottom=30
GraphRight=65
GraphTop=85
GTopText=930
GraphTopOne=760
GraphTopTwo=350
GraphTextOne=480
GraphTextTwo=50
end
if ScrRes='800X600' then do
FileDiagWidth=28
FontSize=14
TopText=965
TextSpace=38
DirListEnd=320
StatsHalfTwo=450
TopKey=905
BottomKey=845
KeyName=860
GraphLeft=15
GraphBottom=40
GraphRight=60
GraphTop=80
GTopText=940
GraphTopOne=775
GraphTopTwo=360
GraphTextOne=485
GraphTextTwo=50
end
if ScrRes='1024X768' then do
FileDiagWidth=25
FontSize=14
TopText=970
TextSpace=35
DirListEnd=325
StatsHalfTwo=460
TopKey=905
BottomKey=850
KeyName=860
GraphLeft=15
GraphBottom=40
GraphRight=60
GraphTop=80
GTopText=940
GraphTopOne=775
GraphTopTwo=360
GraphTextOne=485
GraphTextTwo=50
end
if ScrRes='1280X1024' then do
FileDiagWidth=25
FontSize=14
TopText=970
TextSpace=33
DirListEnd=325
StatsHalfTwo=470
TopKey=900
BottomKey=855
KeyName=860
GraphLeft=15
GraphBottom=40
GraphRight=60
GraphTop=80
GTopText=945
GraphTopOne=765
GraphTopTwo=350
GraphTextOne=480
GraphTextTwo=50
end
if ScrRes='1600X1200' then do
FileDiagWidth=24
FontSize=16
TopText=970
TextSpace=32
DirListEnd=305
StatsHalfTwo=435
TopKey=905
BottomKey=860
KeyName=865
GraphLeft=15
GraphBottom=40
GraphRight=60
GraphTop=80
GTopText=940
GraphTopOne=775
GraphTopTwo=360
GraphTextOne=485
GraphTextTwo=50
end
end
/* If using Visual REXX, the main window is now drawn with some info */
/* written into it. */
if Visual=1 then do
title='DriveCheck v'||DCVer
mpos.left=20
mpos.bottom=10
mpos.right=80
mpos.top=90
MainID=VOpenWindow(title,MainColor,mpos)
call VForeColor MainID,MainTextColor
call VSetFont MainID,'TIME',FontSize
iline.1="DriveCheck v"||DCVer||" is a program written in REXX that"
iline.2="scans each file on the drive you specify to determine how"
iline.3="much slack space it is wasting. Slackspace is the difference"
iline.4="between the size of the file and how much space is allocated"
iline.5="to it on the hard drive."
iline.6=""
iline.7="FAT formatted drives have more slack space per file than HPFS"
iline.8="formatted drives because of the way each file system"
iline.9="allocates space. The difference in slack space between the"
iline.10="two file systems increases with each doubling of the drive"
iline.11="size, starting from 16MB (below 16MB, FAT changes so that"
iline.12="the per-file waste is equivalent to that of a drive between"
iline.13="128MB and 256MB in size). This program will show you"
iline.14="exactly what the different file systems would waste on the"
iline.15="drive you specify, with the current mix of files."
iline.16=""
iline.17="DriveCheck may be modified and distributed freely, but it"
iline.18="would be nice if you gave credit where credit is due if"
iline.19="you make any changes to it and distribute that modified"
iline.20="form (though I can't forsee a reason for this)."
iline.21=""
iline.22="I'd appreciate any comments about the program, which you"
iline.23="can direct to:"
iline.24=""
iline.25="mruskai@microfone.net"
iline.26="Mike Ruskai 1:107/634@fidonet"
PosY=TopText
do i=1 to 26
call VSay MainID,15,PosY,iline.i
PosY=PosY-TextSpace
end
call VDialogPos 50,50
end
/* Processes the command-line arguments, asking for input if it is */
/* necessary. */
if TestDrive\='' then do
if directory(TestDrive||'\')\=TestDrive||'\' then do
say 'Invalid drive, '||'"'TestDrive'"'||'.'
say ''
say 'Type DRIVECHK /? for usage.'
exit
end
else call directory CurrDir
end
else do
if Visual=1 then do
drives=SysDriveMap('C','USED')
numdrv=words(drives)
dnum=0
do i=1 to numdrv
chkdrv=word(drives,i)
chkwrt=lineout(chkdrv||'\test.txt','Testing')
if chkwrt=0 then do
dnum=dnum+1
dlabel=subword(SysDriveInfo(chkdrv),4)
if dlabel='' then label=''
else label='<'||dlabel||'>'
drive.dnum=chkdrv||' '||label
call stream chkdrv||'\test.txt','c',close
call SysFileDelete chkdrv||'\test.txt'
end
end
drive.0=dnum
if dnum>10 then height=10
else if dnum<4 then height=dnum
else if dnum<7 then height=dnum-1
else height=dnum-2
call VListBox 'Drive Selection',drive,FileDiagWidth,height,1
TestDrive=substr(drive.vstring,1,2)
end
else do until ans=1
call SysCls
if ans=0 then do
call SysCurPos 8,0
say '"'TestDrive'"'||', is an invalid drive.'
end
call SysCurPos 10,0
say 'Which drive do you wish to check (e.g. C)?'
call charout ,': '
TDrive=translate(SysGetKey('echo'))
TestDrive=TDrive||':'
if directory(TestDrive||'\')\=TestDrive||'\' then ans=0
else ans=1
end
end
if SkipSwitch\='' then do
if translate(SkipSwitch)='/S' then SkipEA=1
else if translate(SkipSwitch)='/N' then SkipEA=0
else if translate(substr(SkipSwitch,1,3))='/O:' then do
OutFile=SkipSwitch
SkipEA=0
end
else do
say 'Invalid skip switch or outfile option.'
say ''
say 'Type DRIVECHK /? for usage.'
exit
end
end
else do
ans=2
if Visual=1 then do
msg.0=1
msg.1=' Do you wish to skip EA checking?'
ans=VMsgBox('EA Skipping',msg,6)
if ans='YES' then SkipEA=1
else SkipEA=0
end
else do until ans=1
call SysCls
if ans=0 then do
call SysCurPos 8,0
say "Please answer only with 'Y' or 'N'."
end
call SysCurPos 10,0
say 'Do you wish to skip EA checking? (Y/N)'
call charout ,': '
res=translate(SysGetKey('echo'))
if res\='Y' & res\='N' then ans=0
else do
ans=1
if res='Y' then SkipEA=1
else SkipEA=0
end
end
end
if OutFile\='' then do
if translate(OutFile)='/C' then OutFile='CON'
else if translate(substr(OutFile,1,3))\='/O:' then do
say 'Invalid outfile option.'
say ''
say 'Type DRIVECHK /? for usage.'
exit
end
else do
OutFile=substr(OutFile,4)
if substr(OutFile,2,1)\=':' then do
if pos('\',OutFile)=0 then
OutFile=CurrDir||'\'||OutFile
else if pos('\',OutFile)=1 then
OutFile=left(CurrDir,2)||OutFile
else OutFile=CurrDir||'\'||OutFile
end
check=lineout(OutFile,'1')
if check\=0 then do
say 'Invalid outfile option, '||OutFile
say ''
say 'Type DRIVECHK /? for usage.'
exit
end
else do
call stream OutFile,'c','close'
call SysFileDelete OutFile
end
end
end
else do
ans=2
if Visual=1 then do until confirm=1
msg.0=1
msg.1='Do you wish to save the results to a file?'
filech=VMsgBox('Output Choice',msg,6)
if filech='YES' then do
click=VFileBox('File to save results to','',log)
if click='CANCEL' then confirm=0
else do
logfile=log.vstring
check=lineout(logfile,'check')
if check=1 then do
msg.0=1
msg.1='Invalid filename chosen.'
call VMsgBox 'Invalid Filename',msg,1
confirm=0
end
else do
call stream logfile,'c','close'
call SysFileDelete logfile
msg.0=1
msg.1='Save results to '||logfile||'?'
ans=VMsgBox('Confirmation',msg,3)
if ans='OK' then do
OutFile=logfile
confirm=1
end
end
end
end
else do
OutFile='CON'
confirm=1
end
end
else do until ans=1
call SysCls
if ans=0 then do
call SysCurPos 8,0
say "Please answer only with 'Y' or 'N'."
end
call SysCurPos 10,0
say 'Do you wish to direct output to a file? (Y/N)'
call charout ,': '
res=translate(SysGetKey('echo'))
if res\='Y' & res\='N' then ans=0
else do
ans=1
if res='Y' then GetOF=1
else OutFile='CON'
end
end
if GetOF=1 then do
ans=2
do until ans=1
call SysCls
if ans=0 then do
call SysCurPos 8,0
say '"'OFName'"'||' is not a valid filename.'
end
call SysCurPos 10,0
say 'Enter the name for the output file.'
call charout ,': '
parse pull OFName
if substr(OFName,2,1)\=':' then do
if pos('\',OutFile)=0 then
OFName=CurrDir||'\'||OFName
else if pos('\',OFName)=1 then
OFName=left(CurrDir,2)||OFName
else OFName=CurrDir||'\'||OFName
end
check=lineout(OFName,'1')
if check\=0 then ans=0
else do
OutFile=OFName
call stream OFName,'c','close'
call SysFileDelete OFName
do until ans2=1
call SysCls
if ans2=0 then do
call SysCurPos 8,0
say "Please answer only with 'Y' or 'N'."
end
call SysCurPos 10,0
say 'Use '||'"'OutFile'"'||' for output? (Y/N)'
call charout ,': '
res=translate(SysGetKey('echo'))
if res\='Y' & res\='N' then ans2=0
else do
ans2=1
if res='Y' then ans=1
else ans=2
end
end
end
end
end
end
call directory TestDrive||'\'
/* Uses the total drive space as reported by SysDriveInfo to deter- */
/* mine the cluster size that FAT would use on the drive. It then */
/* attempts to write a file with a long name to determine if the */
/* drive being scanned is HPFS or FAT, and orders the allocation unit */
/* variables accordingly (affects report at program conclusion). */
DriveSize=word(SysDriveInfo(TestDrive),3)
if DriveSize<16777216 then ClustSize=4096
else do
if DriveSize>=16777216 & DriveSize<134217728 then ClustSize=2048
else do
RawClusterSize=format((DriveSize/65536),,0)
BinClust=x2b(d2x(RawClusterSize))
ValidPartStart=pos('1',BinClust)
DigitValue=length(substr(BinClust,ValidPartStart))
ClustSize=2**DigitValue
end
end
tfile='Testing For HPFS File System'
rc=lineout(tfile,'test')
if rc=1 then do
FileSystem.1='FAT'
FileSystem.2='HPFS'
AUnit.1=ClustSize
AUnit.2=512
Fnode.1=0
Fnode.2=512
end
else do
FileSystem.1='HPFS'
FileSystem.2='FAT'
call stream tfile,'c','close'
call SysFileDelete tfile
AUnit.1=512
AUnit.2=ClustSize
Fnode.1=512
Fnode.2=0
end
/* Quick check to see if CMD.EXE or 4OS2.EXE is being used. It has */
/* an effect on how EA size checking is done, since the display for */
/* each command processor is slightly different. */
/* Note also that if you are using CMD.EXE, you will have to switch */
/* to the desktop manually to interact with the VREXX components. */
/* I do not know the source of the problem. */
p1=right(d2x(random(65535)),4,'0')
p2=right(d2x(random(65535)),4,'0')
TShChk='\'||p1||p2||'.TMP'
'@ver>'||TShChk
call SysFileSearch '4OS2',TShChk,'ShCheck.'
call stream TShChk,'c','close'
call SysFileDelete TShChk
if ShCheck.0=0 then UShell='CMD'
else UShell='4OS2'
/* Initializes the variables used for the main procedure. */
crlf='0d0a'x
TotFiles=0
TotSize=0
TotDirs=0
TotWaste.1=0
TotWaste.2=0
TotDirSize.1=0
TotDirSize.2=0
/* Uses CMD.EXE or 4OS2.EXE to get a quick list of all subdirectories */
/* on the drive for the computation of their space consumption, and */
/* that of the files they contain. If Visual REXX is in use, the */
/* directory window and file window are drawn, and the scanning */
/* begins. */
if Visual=1 then do
title='DriveCheck v'||DCVer||' scanning drive '||TestDrive
call VSetTitle MainID,title
title='Current scan directory'
dpos.left=15
dpos.bottom=57
dpos.right=50
dpos.top=63
DirID=VOpenWindow(title,DirColor,dpos)
call VForeColor DirID,DirTextColor
call VSetFont DirID,'TIME',FontSize
title='Current scan file'
fpos.left=15
fpos.bottom=37
fpos.right=50
fpos.top=43
FileID=VOpenWindow(title,FileColor,fpos)
call VForeColor FileID,FileTextColor
call VSetFont FileID,'TIME',FontSize
call VSay DirID,5,500,TestDrive||'\'
end
else do
call SysCls
ClrStr=" "
ClrStr=ClrStr||ClrStr||' '
call SysCurPos 12,0
call charout ,'Scanning : '||TestDrive||'\'
end
Data=ProcDir(TestDrive||'\')
TotSize=TotSize+word(Data,3)
do i=1 to 2
TotWaste.i=TotWaste.i+word(Data,i)
TotDirSize.i=TotDirSize.i+AUnit.i
end
TotFiles=TotFiles+word(Data,4)
p1=right(d2x(random(65535)),4,'0')
p2=right(d2x(random(65535)),4,'0')
TDList='\'||p1||p2||'.TMP'
if Visual=1 then do
str='Getting list of directories... '
call VForeColor MainID,MainTextColor
call VClearWindow MainID
call VSay MainID,5,TopText,str
end
else do
call SysCurPos 10,0
call SysCurState 'off'
call charout ,"Getting list of directories... "
end
if UShell='4OS2' then do
signal off error
'*@dir/b/ad/s > '||TDList
signal on error
end
else '@dir/f/ad/s >'||TDList
if Visual=1 then do
str='Done'
call VSay MainID,DirListEnd,TopText,str
end
else call charout ,"Done"
check=lines(TDList)
if Visual\=1 then do
call SysCurPos 12,0
call charout ,'Scanning : '
end
do while lines(TDList)>0
WDir=linein(TDList)
if length(WDir)>68 then
DDir=left(WDir,68)||'>'
else
DDir=WDir
if Visual=1 then do
call VClearWindow DirID
str=WDir
call VSay DirID,5,500,str
end
else do
call SysCurPos 12,11
call charout ,ClrStr
call SysCurPos 12,11
call charout ,DDir
end
TotDirs=TotDirs+1
Data=ProcDir(WDir)
TotSize=TotSize+word(Data,3)
do i=1 to 2
TotWaste.i=TotWaste.i+word(Data,i)
TotDirSize.i=TotDirSize.i+AUnit.i
end
TotFiles=TotFiles+word(Data,4)
DEAData=TallyDirEA(WDir)
do i=1 to 2
TotDirSize.i=TotDirSize.i+word(DEAData,i)
end
if orexx=1 then call SysSleep 0.005
end
call stream TDList,'c','close'
call SysFileDelete TDList
do i=1 to 2
TotFree.i=DriveSize-(TotSize+TotDirSize.i+TotWaste.i)
end
AllWaste=TotWaste.1-TotWaste.2
AllDir=TotDirSize.1-TotDirSize.2
AllTot=AllWaste+AllDir
if AllTot>=0 then
if AllTot=0 then
TotalDiff=0
else do
Modifier='*LOST*'
TotalDiff=abs(AllTot)
end
else do
Modifier='*SAVED*'
TotalDiff=abs(AllTot)
end
if Visual\=1 then do
if OutFile='CON' then do
call SysCurPos 16,0
call lineout ,'Press any key for statistics...'
scrap=SysGetKey('noecho')
end
end
/* Reports the data in a formatted manner. Each of the numerical */
/* variables is deliminated with via the CoDel procedure. Then the */
/* screen is cleared, data presented, and conclusion stated. */
FormDriveSize=CoDel(DriveSize)
FormTotalDiff=CoDel(TotalDiff)
do i=1 to 2
FormDirSpace.i=CoDel(TotDirSize.i)
FormWasteSpace.i=CoDel(TotWaste.i)
FormUnitSize.i=CoDel(AUnit.i)
FormTotFree.i=CoDel(TotFree.i)
end
FormTotSize=CoDel(TotSize)
FormTotFiles=CoDel(TotFiles)
FormTotDirs=CoDel(TotDirs)
do i=1 to 2
PercentFree.i=(TotFree.i/DriveSize)*100
PercentUsed.i=((TotDirSize.i+TotSize)/DriveSize)*100
PercentWaste.i=(TotWaste.i/DriveSize)*100
fPercentFree.i=format(PercentFree.i,,2)
fPercentUsed.i=format(PercentUsed.i,,2)
fPercentWaste.i=format(PercentWaste.i,,2)
IntPFree.i=format(PercentFree.i,,0)
IntPUsed.i=format(PercentUsed.i,,0)
IntPWaste.i=format(PercentWaste.i,,0)
end
if Visual\=1 then do
call SysCls
call SysCurPos 5,0
end
if SkipEA=1 then
infochecked=" "
else
infochecked="and EA's"
rline.1.1="Drive "||TestDrive||" "||FormDriveSize||" bytes"
rline.1.2=""
rline.2.1=""
rline.2.2=""
rline.3.1="Total files "
rline.3.2=": "||FormTotFiles
rline.4.1="Total size of files "||infochecked||" "
rline.4.2=": "||FormTotSize||" bytes"
rline.5.1=""
rline.5.2=""
rline.6.1="Current statistics using "||FileSystem.1||" :"
rline.6.2=""
rline.7.1=""
rline.7.2=""
rline.8.1="Allocation unit size "
rline.8.2=": "||FormUnitSize.1||" bytes"
rline.9.1="Total space consumed by directories "
rline.9.2=": "||FormDirSpace.1||" bytes"
rline.10.1="Total wasted space for files "||infochecked||" "
rline.10.2=": "||FormWasteSpace.1||" bytes"
rline.11.1=""
rline.11.2=""
rline.12.1="If it was being used with "||FileSystem.2||" :"
rline.12.2=""
rline.13.1=""
rline.13.2=""
rline.14.1="Allocation unit size "
rline.14.2=": "||FormUnitSize.2||" bytes"
rline.15.1="Total space consumed by directories "
rline.15.2=": "||FormDirSpace.2||" bytes"
rline.16.1="Total wasted space for files "||infochecked||" "
rline.16.2=": "||FormWasteSpace.2||" bytes"
rline.17=""
if TotalDiff=0 then do
rline.18="Oddly enough, by using "||FileSystem.1||" instead of "||FileSystem.2||","
rline.19="you've neither saved nor lost any disk space on drive "||TestDrive||"."
end
else do
rline.18="By using "||FileSystem.1||" instead of "||FileSystem.2||", you've"
rline.19=Modifier||" "||FormTotalDiff||" bytes on drive "||TestDrive||"."
end
if Visual\=1 | (Visual=1 & Outfile\='CON') then do
do i=1 to 16
rline.i=rline.i.1||rline.i.2
end
do i=1 to 19
call lineout OutFile,rline.i
end
if Visual\=1 & OutFile='CON' then do
say ""
say "Press any key to see the graph..."
junk=SysGetKey('noecho')
call SysCls
UseChar=d2c(219)
WasteChar=d2c(177)
FreeChar=d2c(176)
do i=1 to 2
NumUse.i=format(((PercentUsed.i/100)*50),,0)
NumWaste.i=format(((PercentWaste.i/100)*50),,0)
NumFree.i=format(((PercentFree.i/100)*50),,0)
if NumUse.i+NumWaste.i+NumFree.i <> 50 then
NumFree.i=50-(NumUse.i+NumWaste.i)
end
Graph.1=''
Graph.2=''
do i=1 to 2
do j=1 to NumUse.i
Graph.i=Graph.i||UseChar
end
end
do i=1 to 2
do j=1 to NumWaste.i
Graph.i=Graph.i||WasteChar
end
end
do i=1 to 2
do j=1 to NumFree.i
Graph.i=Graph.i||FreeChar
end
end
GraphString.1=' '||Graph.1||crlf||' '||,
Graph.1||crlf
GraphString.2=' '||Graph.2||crlf||' '||,
Graph.2||crlf
tstring='Drive '||TestDrive||' '||FormDriveSize||' bytes'
tspos=format(((80-(length(tstring)))/2),,0)
kstring=d2c(219)||' = used '||d2c(177)||' = wasted '||,
d2c(176)||' = free'
kspos=format(((80-(length(kstring)))/2),,0)
do i=1 to 2
pstring.i='With '||FileSystem.i||', '||fPercentUsed.i||'%'||' used, '||,
fPercentWaste.i||'%'||' wasted, '||fPercentFree.i||'%'||' free.'
pspos.i=format(((80-(length(pstring.i)))/2),,0)
end
call SysCurPos 4,tspos
call charout ,tstring
call SysCurPos 6,kspos
call charout ,kstring
call SysCurPos 9,0
call charout ,GraphString.1
call SysCurPos 12,pspos.1
call charout ,pstring.1
call SysCurPos 15,0
call charout ,GraphString.2
call SysCurPos 18,pspos.2
call charout ,pstring.2
call charout ,crlf
end
if Visual=1 then do
msg.0=1
msg.1='Results saved to '||OutFile||'.'
call VMsgBox 'Info',msg,1
end
end
else do
if Visual=1 then do
call VCloseWindow FileID
call VCloseWindow DirID
call VClearWindow MainID
dtitle='Drive '||TestDrive||' scan results'
call VSetTitle MainID,dtitle
PosY=TopText
do i=1 to 16
call VSay MainID,5,PosY,rline.i.1
call VSay MainID,StatsHalfTwo,PosY,rline.i.2
PosY=PosY-TextSpace
end
do i=17 to 19
call VSay MainID,5,PosY,rline.i
PosY=PosY-TextSpace
end
title='Comparison graphs'
gpos.left=GraphLeft
gpos.bottom=GraphBottom
gpos.right=GraphRight
gpos.top=GraphTop
GraphID=VOpenWindow(title,GraphColor,gpos)
call VForeColor GraphID,GraphTextColor
call VSetFont GraphID,'TIME',FontSize
hstring='Drive '||TestDrive||' '||FormDriveSize||' bytes.'
call VSay GraphID,10,GTopText,hstring
ypos.1=TopKey
ypos.2=BottomKey
call VForeColor GraphID,UsedColor
call VDrawParms GraphID,0,0,5
ux.1=120
ux.2=160
ux.3=160
ux.4=120
ux.5=ux.1
uy.1=ypos.1
uy.2=ypos.1
uy.3=ypos.2
uy.4=ypos.2
uy.5=uy.1
call VDraw GraphID,'POLYGON',ux,uy,4
call VForeColor GraphID,WasteColor
call VDrawParms GraphID,0,0,1
wx.1=400
wx.2=440
wx.3=440
wx.4=400
wx.5=wx.1
wy.1=ypos.1
wy.2=ypos.1
wy.3=ypos.2
wy.4=ypos.2
wy.5=wy.1
call VDraw GraphID,'POLYGON',wx,wy,4
call VForeColor GraphID,FreeColor
call VDrawParms GraphID,0,0,2
fx.1=680
fx.2=720
fx.3=720
fx.4=680
fx.5=fx.1
fy.1=ypos.1
fy.2=ypos.1
fy.3=ypos.2
fy.4=ypos.2
fy.5=fy.1
call VDraw GraphID,'POLYGON',fx,fy,4
call VForeColor GraphID,GraphTextColor
call VDraw GraphID,'LINE',ux,uy,5
call VDraw GraphID,'LINE',wx,wy,5
call VDraw GraphID,'LINE',fx,fy,5
ustring='= Used'
wstring='= Wasted'
fstring='= Free'
call VSay GraphID,170,KeyName,ustring
call VSay GraphID,450,KeyName,wstring
call VSay GraphID,730,KeyName,fstring
ypos.1=GraphTopOne
ypos.2=GraphTopTwo
do i=1 to 2
UsedLength.i=format(((PercentUsed.i/100)*700),,0)
WasteLength.i=format(((PercentWaste.i/100)*700),,0)
FreeLength.i=format(((PercentFree.i/100)*700),,0)
end
ux1.1=150
ux1.2=150+UsedLength.1
ux1.3=ux1.2
ux1.4=150
uy1.1=ypos.1
uy1.2=ypos.1
uy1.3=ypos.1-150
uy1.4=ypos.1-150
wx1.1=ux1.2+1
wx1.2=wx1.1+WasteLength.1
wx1.3=wx1.2
wx1.4=wx1.1
wy1.1=ypos.1
wy1.2=ypos.1
wy1.3=ypos.1-150
wy1.4=ypos.1-150
fx1.1=wx1.2+1
fx1.2=fx1.1+FreeLength.1
fx1.3=fx1.2
fx1.4=fx1.1
fy1.1=ypos.1
fy1.2=ypos.1
fy1.3=ypos.1-150
fy1.4=ypos.1-150
ux2.1=150
ux2.2=150+UsedLength.2
ux2.3=ux2.2
ux2.4=150
uy2.1=ypos.2
uy2.2=ypos.2
uy2.3=ypos.2-150
uy2.4=ypos.2-150
wx2.1=ux2.2+1
wx2.2=wx2.1+WasteLength.2
wx2.3=wx2.2
wx2.4=wx2.1
wy2.1=ypos.2
wy2.2=ypos.2
wy2.3=ypos.2-150
wy2.4=ypos.2-150
fx2.1=wx2.2+1
fx2.2=fx2.1+FreeLength.2
fx2.3=fx2.2
fx2.4=fx2.1
fy2.1=ypos.2
fy2.2=ypos.2
fy2.3=ypos.2-150
fy2.4=ypos.2-150
call VForeColor GraphID,UsedColor
call VDrawParms GraphID,0,0,5
call VDraw GraphID,'POLYGON',ux1,uy1,4
call VDraw GraphID,'POLYGON',ux2,uy2,4
call VForeColor GraphID,WasteColor
call VDrawParms GraphID,0,0,1
call VDraw GraphID,'POLYGON',wx1,wy1,4
call VDraw GraphID,'POLYGON',wx2,wy2,4
call VForeColor GraphID,FreeColor
call VDrawParms GraphID,0,0,2
call VDraw GraphID,'POLYGON',fx1,fy1,4
call VDraw GraphID,'POLYGON',fx2,fy2,4
bx1.1=149
bx1.2=852
bx1.3=bx1.2
bx1.4=bx1.1
bx1.5=bx1.1
by1.1=ypos.1+1
by1.2=ypos.1+1
by1.3=ypos.1-151
by1.4=ypos.1-151
by1.5=by1.1
bx2.1=149
bx2.2=852
bx2.3=bx2.2
bx2.4=bx2.1
bx2.5=bx2.1
by2.1=ypos.2+1
by2.2=ypos.2+1
by2.3=ypos.2-151
by2.4=ypos.2-151
by2.5=by2.1
call VForeColor GraphID,GraphTextColor
call VDrawParms GraphID,0,0,0
call VDraw GraphID,'LINE',bx1,by1,5
call VDraw GraphID,'LINE',bx2,by2,5
do i=1 to 2
pstring.i='With '||FileSystem.i||', '||fPercentUsed.i||'%'||,
' used, '||fPercentWaste.i||'%'||' wasted, '||fPercentFree.i||,
'% free.'
end
call VForeColor GraphID,GraphTextColor
call VSay GraphID,100,GraphTextOne,pstring.1
call VSay GraphID,100,GraphTextTwo,pstring.2
call VDialogPos 80,50
do until confirm=1
ExitChoice.0=2
ExitChoice.1='Exit DriveCheck'
ExitChoice.2='Log results to file, then exit DriveCheck'
ExitChoice.vstring=ExitChoice.1
call VRadioBox 'Exiting DriveCheck',ExitChoice,1
choice=ExitChoice.vstring
if choice=ExitChoice.1 then confirm=1
else do
click=VFileBox('File to save results to','',log)
if click='CANCEL' then confirm=0
else do
logfile=log.vstring
check=lineout(logfile,'check')
if check=1 then do
msg.0=1
msg.1='Invalid filename chosen.'
call VMsgBox 'Invalid Filename',msg,1
confirm=0
end
else do
call stream logfile,'c','close'
call SysFileDelete logfile
msg.0=1
msg.1='Save results to '||logfile||'?'
ans=VMsgBox('Confirmation',msg,3)
if ans='OK' then do
confirm=1
do i=1 to 16
rline.i=rline.i.1||rline.i.2
end
do i=1 to 19
call lineout logfile,rline.i
end
end
else confirm=0
end
end
end
end
end
end
call directory CurrDir
if Visual\=1 & OutFile\='CON' then say 'Results saved to '||Outfile||'.'
if Visual=1 then call VExit
exit
SYNTAX:
prob='SYNTAX'
proba=1
ERROR:
if proba\=1 then do
prob='ERROR'
proba=1
end
FAILURE:
if proba\=1 then do
prob='FAILURE'
proba=1
end
HALT:
if proba\=1 then prob='HALT'
say ''
say ''
say 'Problem of type '||prob||' while program was on line '||sigl||'.'
say '"'Sourceline(sigl)'"'
call directory CurrDir
if Visual=1 then call VExit
exit
Usage:
call SysCls
call SysCurPos 5,0
say "DriveCheck v"||DCVer||" REXX disk space waste scanner, written by:"
say ""
say "Mike Ruskai <thanny@ibm.net>"
say ""
say "Usage: DRIVECHK.CMD [/T] [<drive letter>: [/S|/N] [/O:<filename>|/C]]"
say ""
say "/T - Run in text mode (no graphical interface)"
say "<drive letter>: - Drive to scan"
say "/S - Skip extended attributes checking"
say "/N - Do not skip extended attributes checking"
say "/O:<filename> - Write output to <filename>"
say "/C - Do not direct output to a file"
say ""
say "Example:"
say ""
say "DRIVECHK C: /S /O:scan.log"
say ""
say " - Scans drive C:, skips EA's, writes output to 'scan.log'"
say ""
say "Run DRIVECHK.CMD with no parameters to be prompted for input."
say "Use of the /T parameter alone results in text-only prompting."
say ""
exit
/******************** Begin TallyFiles Procedure **********************/
/* Procedure to determine total size and waste of all files in a */
/* given directory, for both FAT and HPFS file systems. The list of */
/* files is obtained using SysFileTree. For each file, it's total */
/* size and the size of its EA's (unless skipped) are tallied for the */
/* file size total, and the waste of the file and it's EA's is added */
/* to the waste total. Calculations are performed by the SlackSpace */
/* procedure, for both FAT and HPFS file systems. */
TallyFiles: Procedure expose Fnode.1 Fnode.2 SkipEA UShell AUnit.1 AUnit.2 Visual FileID orexx
ClrStr=' '
ClrStr=ClrStr||ClrStr||' '
TotSize=0
TotWaste.1=0
TotWaste.2=0
TotAlloc.1=0
TotAlloc.2=0
arg Dir
call directory Dir
call SysFileTree '*','files.','F'
if Visual\=1 then do
call SysCurPos 14,0
call charout ,'Working on file: '
end
do i=1 to files.0
if SkipEA=1 then do
CurFile=filespec('name',(substr(files.i,38)))
if length(CurFile)>63 then
dCurFile=substr(CurFile,1,62)||'>'
else dCurFile=CurFile
if Visual=1 then do
call VClearWindow FileID
str=CurFile
call VSay FileID,5,500,str
end
else do
call SysCurPos 14,17
say ClrStr
call SysCurPos 14,17
call charout ,dCurFile
end
FSize=word(files.i,3)
TotSize=TotSize+FSize
Waste=SlackSpace(Fsize)
TotWaste.1=TotWaste.1+word(Waste,1)+Fnode.1
TotWaste.2=TotWaste.2+word(Waste,2)+Fnode.2
end
else do
file=substr(files.i,38)
p1=right(d2x(random(65535)),4,'0')
p2=right(d2x(random(65535)),4,'0')
tfile='\'||p1||p2||'.TMP'
if UShell='4OS2' then do
signal off error
'*@dir/a/knm '||'"'file'"'||'>'||tfile
signal on error
CurFile=filespec('name',file)
if length(CurFile)>63 then
dCurFile=substr(CurFile,1,62)||'>'
else dCurFile=CurFile
if Visual=1 then do
call VClearWindow FileID
str=CurFile
call VSay FileID,5,500,str
end
else do
call SysCurPos 14,17
say ClrStr
call SysCurPos 14,17
call charout ,dCurFile
end
filestring=linein(tfile)
UFEASize=word(filestring,4)
UFFSize=word(filestring,3)
EACommaPos=pos(',',UFEASize)
if EACommaPos\=0 then
EASize=delstr(UFEASize,EACommaPos,1)
else EASize=UFEASize
FCommaPos=pos(',',UFFSize)
if FCommaPos\=0 then do until FCommaPos=0
UFFSize=delstr(UFFSize,FCommaPos,1)
FCommaPos=pos(',',UFFSize)
end
FSize=UFFSize
end
else do
'@dir/a/n '||'"'file'"'||'>'||tfile
CurFile=filespec('name',file)
if length(CurFile)>63 then
dCurFile=substr(CurFile,1,62)||'>'
else dCurFile=CurFile
if Visual=1 then do
call VClearWindow FileID
str=CurFile
call VSay FileID,5,500,str
end
else do
call SysCurPos 14,17
say ClrStr
call SysCurPos 14,17
call charout ,dCurFile
end
do 5
scrap=linein(tfile)
end
filestring=linein(tfile)
EASize=word(filestring,4)
FSize=word(filestring,3)
end
TotSize=TotSize+FSize+EASize
FWaste=SlackSpace(FSize)
FWaste.1=word(FWaste,1)
FWaste.2=word(FWaste,2)
if EASize=0 then do
TotWaste.1=TotWaste.1+FWaste.1+Fnode.1
TotWaste.2=TotWaste.2+FWaste.2+Fnode.2
end
else do
EAWaste=SlackSpace(EASize)
EAWaste.1=word(EAWaste,1)
EAWaste.2=word(EAWaste,2)
TotWaste.1=TotWaste.1+FWaste.1+EAWaste.1
TotWaste.2=TotWaste.2+FWaste.2+EAWaste.2
end
call stream tfile,'c','close'
call SysFileDelete tfile
end
if orexx=1 then call SysSleep 0.005
end
return TotSize TotWaste.1 TotWaste.2 files.0
/******************** End TallyFiles Procedure ************************/
/******************** Begin SlackSpace Procddure **********************/
/* Procedure to determine the wasted space of the given file size. */
/* This procedure receives the file size and allocation unit size */
/* from the main program. It divides the file size by the size of */
/* the allocation unit, then multiplies the integer portion the */
/* result to obtain the space occupied only by completely used */
/* allocation units. The difference between this and the size of the */
/* file is then subtracted from the size of the allocation unit which */
/* results in the amount of wasted space, in bytes. Of course, if */
/* there is no partial allocation unit usage, there is no wasted */
/* space, and 0 is returned in such a case. */
SlackSpace: Procedure expose AUnit.1 AUnit.2
arg FileSize
do i=1 to 2
IntUnit.i=FileSize%AUnit.i
FullUnits.i=IntUnit.i*AUnit.i
Diff.i=FileSize-FullUnits.i
if Diff.i=0 then do
Waste.i=0
AllocUnits.i=IntUnit.i
end
else do
Waste.i=AUnit.i-Diff.i
AllocUnits.i=IntUnit.i+1
end
TotAlloc.i=AllocUnits.i*AUnit.i
if orexx=1 then call SysSleep 0.005
end
return Waste.1 Waste.2
/******************** End SlackSpace Procedure ************************/
/******************** Begin ProcDir Procedure *************************/
/* Procedure to get all information about a specific directory, which */
/* includes total file and EA size and wasted space (for both FAT and */
/* HPFS). */
ProcDir: Procedure expose FNode.1 FNode.2 AUnit.1 AUnit.2 UShell SkipEA Visual FileID orexx
arg DirName
TSize=TallyFiles(DirName)
FSize=word(TSize,1)
Waste.1=word(TSize,2)
Waste.2=word(TSize,3)
NumFiles=word(TSize,4)
return Waste.1 Waste.2 FSize NumFiles
/******************** End ProcDir Procedure ***************************/
/******************** Begin TallyDirEA Procedure **********************/
/* Procedure to tally the extended attributes of a given directory, */
/* which is done regardless of the SkipEA status, since it doesn't */
/* really take that long. The parent of the given directory is */
/* changed to, so we can do a DIR listing of all directories, and */
/* parse the information about the specific one we want from the list */
/* returned. There seems to be no way to list only a specific */
/* directory in either CMD.EXE or 4OS2.EXE. Once the EA size for the */
/* directory is found, the total actual space taken up for each */
/* allocation unit is calculated, and returned. */
TallyDirEA: Procedure expose AUnit.1 AUnit.2 UShell
arg DirName
tDirName=reverse(DirName)
BSPos=pos('\',tDirName)
BDName=translate(reverse(substr(tDirName,1,(BSPos-1))))
call directory DirName||'\..'
p1=right(d2x(random(65535)),4,'0')
p2=right(d2x(random(65535)),4,'0')
tfile='\'||p1||p2||'.TMP'
if UShell='4OS2' then do
signal off error
'@*dir/n/m/k/h/ad > '||tfile
signal on error
do until found=1
dirstring=linein(tfile)
if translate(subword(dirstring,5))=BDName then do
UFEASize=word(dirstring,4)
EACommaPos=pos(',',UFEASize)
if EACommaPos\=0 then
EASize=delstr(UFEASize,EACommaPos,1)
else EASize=UFEASize
found=1
end
else found=0
end
end
else do
'@dir/n/ad > '||tfile
do 5
scrap=linein(tfile)
end
do until found=1
dirstring=linein(tfile)
if translate(subword(dirstring,5))=BDName then do
EASize=word(dirstring,4)
found=1
end
else found=0
end
end
call stream tfile,'c','close'
call SysFileDelete tfile
DSData=SlackSpace(EASize)
DirEATot.1=EASize+word(DSData,1)
DirEATot.2=EASize+word(DSData,2)
return DirEATot.1 DirEATot.2
/******************** End TallyDirEA Procedure ************************/
/******************** Begin CoDel Procedure ***************************/
/* Procedure to take a long decimal number, and deliminate it with a */
/* comma for each group of three digits, starting from the end of the */
/* number. First you reverse the string. Then you take the first */
/* three characters of the reversed string (i.e. last three */
/* characters of the original string), writing the remaining */
/* characters back to the variable. The three characters are */
/* appended to the final string with a comma at the end, and the */
/* process is repeated until there are three or less characters left */
/* in the reversed string variable. The final write of the formatted */
/* string appends the remaining digits. The string is then reversed */
/* again (putting it back to normal), which results in a readable */
/* number: */
/* */
/* 2484693813 - Original string */
/* 3183964842 - Reversed */
/* 318, - Three chars and comma appended to final string */
/* 318,396, - Next three and a comma */
/* 318,396,484, - Next three and a comma */
/* 318,396,484,2 - Last char(s), three or less, no comma */
/* 2,484,693,813 - Reversed back to normal */
CoDel: Procedure
arg RNum
rRNum=reverse(RNum)
FNum=''
do while length(rRNum)>3
parse var rRNum TriDig +3 rRNum
FNum=FNum||TriDig||','
end
FNum=reverse(FNum||rRNum)
return FNum
/******************** End CoDel Procedure *****************************/
/******************** Begin ColorCheck Procedure **********************/
/* This procedure simply verifies that the colors read in from the */
/* INI file (if present) are valid or not. */
ColorCheck: Procedure
arg color
if color='BLACK' then rv=1
else if color='WHITE' then rv=1
else if color='RED' then rv=1
else if color='GREEN' then rv=1
else if color='BLUE' then rv=1
else if color='CYAN' then rv=1
else if color='YELLOW' then rv=1
else if color='PINK' then rv=1
else rv=0
return rv
/******************** End ColorCheck Procedure ************************/
/******************** Begin ResCheck Procedure ************************/
/* Simply checks to see if a resolution given in the INI file is one */
/* which we know how to handle. */
ResCheck: Procedure
arg res
if res='640X480' then rv=1
else if res='800X600' then rv=1
else if res='1024X768' then rv=1
else if res='1280X1024' then rv=1
else if res='1600X1200' then rv=1
else rv=0
return rv
/******************** End ResCheck Procedure **************************/