'=========================================================================== ' Subject: DIRECTORY SCAN Date: 06-23-98 (01:48) ' Author: Antoni Gual Code: QB, PDS ' Origin: agual@eic.ictnet.es Packet: DISK.ABC '=========================================================================== DECLARE SUB ShowHelp () DECLARE SUB Scan (sumbyte&, sumfiles&, sumcluster&, FirstOfLevel%, newlevel$) DECLARE SUB dosprint (txt$) DEFINT A-Z '------------------------------------------------------------------------ 'DIRSCAN.BAS 'Antoni Gual 1998 agual@eic.ictnet.es PUBLIC DOMAIN 'This program is a ShowHelp to disk maintenanceit scans directories in a disk and 'displays: ' tree structure ' directory (short) name ' total files ' size ' % of filling 'for the single directory and for the whole branch. 'It runs from parameters passed from command line, and uses DOS interrupts 'to print, so you can redirect and sort his output. 'I prevent you: the grand total never matches with the disk size! ;-) 'Needs the /lqb parameter in Quick Basic.Does not work in QBasic, i've lost 'the interrupt calling routine somewhere in my HD 'Try DIRSCAN c: or DIRSCAN /? for Help '------------------------------------------------------------------------ 'programming features: '--------------------- 'does not use a single float related function - shorter code when compiled 'does use recursion, no arrays, it can handle up to 2000 000 000 files 'at the price of displaying the directory tree upside down 'limited to 2 Gb disks, due to the limitations of the LONG integer type '----------------------------------------------------------------------- 'Help needed!!! '-------------- 'Not tested with FAT 32 'Does not check for removable disks-How to do it for floppies, CD's, Zips? 'The total size of clusters is never equal to size given by the Disk Size 'function'minus the free space.Is there any hidden sectors?Where are LFN stored? 'For the CD's the total size of clusters is bigger?? than the total disk space 'Why? Is the concept of cluster size valid for CD's? 'Should display LFN '------------------------------------------------------------------------ 'general constants '----------------- CONST false = 0, true = NOT false 'interrupt call structures '------------------------- TYPE RegTypeX ax AS INTEGER bx AS INTEGER cx AS INTEGER dx AS INTEGER bp AS INTEGER si AS INTEGER di AS INTEGER flags AS INTEGER ds AS INTEGER es AS INTEGER END TYPE DECLARE SUB interruptx (intnum AS INTEGER, inreg AS RegTypeX, outreg AS RegTypeX) CONST CarryFlag = 1 'dos structures '-------------- TYPE dtatype reserved AS STRING * 21 atr AS STRING * 1 tim AS INTEGER dat AS INTEGER siz AS LONG nam AS STRING * 13 END TYPE CONST DtaHour = &HF800, DtaMin = &H7E0, DtaSec = &H1F CONST DtaYear = &HFE00, DtaMonth = &H1E0, DtaDay = &H1F CONST AttRO = 1, AttHid = 2, AttSys = 4, attfile = &H6, AttLab = 8 CONST AttSub = &H10 CONST AttArch = &H20, AttNotAFile = &H18 'dos calls constants '------------------- CONST DosInt = &H21 CONST DisplString = &H900, SetDisk = &HE00, SearchFirst = &H4E00 CONST SearchNext = &H4F00, SetDta = &H1A00, GetDTA = &H2F00, GetDOSVer = &H3000 CONST GetDiskSpace = &H3600, SetDir = &H3B00, GetDir = &H4700, GetDeFDrive = &H1900 'functions '--------- DEF FNLONG2STR$ (x&, L%) 'converts a LONG to a right justificated string, with thousand'separators '------------------------------------------------------------------------ y& = x&: i = L%: FNLONG2STR$ = SPACE$(L%) DO IF (L% - i) MOD 4 = 3 THEN MID$(FNLONG2STR$, i, 1) = ".": i = i - 1 MID$(FNLONG2STR$, i, 1) = CHR$((y& MOD 10) + 48): y& = y& \ 10: i = i - 1 LOOP UNTIL y& = 0 OR i = 0 IF y& THEN FNLONG2STR$ = STRING$(L%, 35) END DEF DEF fnAsciiz2Str$ (txt$) 'truncates a string before ascii 0 char '-------------------------------------- fnAsciiz2Str$ = LEFT$(txt$, INSTR(txt$, CHR$(0)) - 1) END DEF DEF fnWord2Long& (x) 'converts an unsigned integer (2 bytes) to a LONG '------------------------------------------------ fnWord2Long& = x IF x < 0 THEN fnWord2Long& = x + 65536 END DEF 'variables '--------- DIM SHARED tree$: tree$ = SPACE$(10) DIM SHARED temp$: temp$ = SPACE$(83) DIM SHARED regs AS RegTypeX DIM SHARED BytesSector&, SectorsCluster&, ClustersDrive&, FreeClusters& DIM SHARED FreeBytes&, bytescluster&, TotalBytes& DIM SHARED DriveToScan 'StartDir$ = SPACE$(65) DIM SHARED builddir AS STRING DIM SHARED screenline AS STRING: screenline = SPACE$(79) DIM StartDrive AS INTEGER DIM SHARED maxlevel AS INTEGER DIM SHARED mode AS INTEGER 'parameters read from command line '--------------------------------- maxlevel = 10 mode = 1 a$ = UCASE$(COMMAND$) dp% = INSTR(a$, ":") IF dp% THEN DriveToScan = ASC(MID$(a$, dp% - 1, 1)) - 64 ba% = INSTR(a$, "/") IF ba% THEN IF INSTR(ba%, a$, "?") THEN ShowHelp: END IF INSTR(ba%, a$, "H") THEN ShowHelp: END IF INSTR(ba%, a$, "S") THEN mode = 2 lp% = INSTR(ba%, a$, "L") IF lp% THEN maxlevel = ASC(MID$(a$, lp% + 1, 1)) - 47 a1% = ba% - 1 ELSE a1% = LEN(a$) END IF IF (a1% - dp%) > 0 THEN StartDir$ = LTRIM$(RTRIM$(MID$(a$, dp% + 1, a1% - dp%))) CLS 'get general disk data '--------------------- 'current disk '------------ regs.ax = GetDeFDrive CALL interruptx(DosInt, regs, regs) CurDrive = (regs.ax AND 255) + 1 IF mode = 1 THEN dosprint "Current Drive: " + CHR$(CurDrive + 64) 'current dir '----------- regs.ax = GetDir regs.dx = StartDrive regs.ds = VARSEG(CurDir$) regs.si = SADD(CurDir$) CALL interruptx(DosInt, regs, regs) IF regs.flags AND CarryFlag THEN dosprint "Invalid Drive": END IF mode = 1 THEN dosprint "Current Dir: " + CurDir$ IF DriveToScan = 0 THEN DriveToScan = CurDrive IF mode = 1 THEN dosprint "Scanning Disk " + CHR$(DriveToScan + 64) + ":" 'get disk general values '----------------------- regs.ax = GetDiskSpace regs.dx = DriveToScan CALL interruptx(DosInt, regs, regs) BytesSector& = fnWord2Long&(regs.cx) SectorsCluster& = fnWord2Long&(regs.ax) ClustersDrive& = fnWord2Long&(regs.dx) FreeClusters& = fnWord2Long&(regs.bx) bytescluster& = BytesSector& * SectorsCluster& TotalBytes& = bytescluster& * ClustersDrive& FreeBytes& = bytescluster& * FreeClusters& sumbyte& = 0: sumfiles& = 0 'scans disk '---------- 'IF LEN(StartDir$) THEN StartDir$ = "\" + StartDir$ StartDir$ = CHR$(DriveToScan + 64) + ":" + StartDir$ Scan sumbyte&, sumfiles&, sumcluster&, true, StartDir$ 'write disk space '---------------- IF mode = 1 THEN LSET screenline = "" MID$(screenline, 14) = FNLONG2STR$(BytesSector& * SectorsCluster& * FreeClusters&, 13) MID$(screenline, 49) = " bytes Free disk Space" dosprint screenline LSET screenline = "" MID$(screenline, 14) = FNLONG2STR$(BytesSector& * SectorsCluster& * ClustersDrive&, 13) MID$(screenline, 49) = " bytes Total disk Space" dosprint screenline dosprint "" END IF END SUB dosprint (txt$) '----------------------------------------------------------------- 'prints a string using DOS Interrupt, the output can be redirected 'and sorted. Adds a return at the end '----------------------------------------------------------------- LSET temp$ = txt$ + CHR$(13) + CHR$(10) + "$" regs.ax = DisplString regs.ds = VARSEG(temp$) regs.dx = SADD(temp$) CALL interruptx(DosInt, regs, regs) END SUB SUB Scan (sumbyte&, sumfiles&, sumcluster&, FirstOfLevel, newdir$) '------------------------------------------------------------------------ 'Uses DOS services to scan a directory and get each file in turn 'gets file data and adds to statistics 'When it finds a subdirectory it calls himself recursively 'Before exiting it prints the info found, so the program does'nt need big data 'arrays. It returns data needed to accumulate sizes, and file nrs '------------------------------------------------------------------------- DIM dta AS dtatype 'new dta for this level STATIC level 'only one var for all levels 'local values '----------------- level = level + 1 locbyte& = 0: locfiles& = 0: loccluster& = 0 sumbyte& = 0: sumfiles& = 0: sumcluster& = 0 Startlen = LEN(builddir) fol = true 'init file search '---------------- IF LEN(newdir$) THEN builddir = builddir + newdir$ + "\" Filename$ = builddir + "*.*" + CHR$(0) GOSUB changedta firstfile = true atribute = &HFFFF 'find files with any attribute 'loop for all entries in dir '--------------------------- GOSUB findfile DO 'process the values found fy findfile '------------------------------------ loccluster& = loccluster& + (dta.siz \ bytescluster&) IF dta.siz MOD bytescluster& THEN loccluster& = loccluster& + 1 locbyte& = locbyte& + dta.siz IF (ASC(dta.atr) AND AttNotAFile) = 0 THEN locfiles& = locfiles& + 1 'new dir found, if it's not . or .. call Scan again '-------------------------------------------------- IF ASC(dta.atr) AND AttSub THEN namedir$ = fnAsciiz2Str$(dta.nam) 'forget . and .. that are not entries to sub-directories '------------------------------------------------------- IF ASC(namedir$) <> ASC(".") THEN 'call Scan again for the subdir '------------------------------- Scan ThisByte&, ThisFiles&, ThisCluster&, fol, namedir$ fol = false 'accumulate values found by scan in the subdir '--------------------------------------------- sumbyte& = sumbyte& + ThisByte&: sumfiles& = sumfiles& + ThisFiles& sumcluster& = sumcluster& + ThisCluster& 'back to this level's DTA '------------------------ GOSUB changedta END IF END IF GOSUB findfile LOOP UNTIL enddir 'accumulate values of this dir '----------------------------- sumbyte& = sumbyte& + locbyte&: sumfiles& = sumfiles& + locfiles& sumcluster& = sumcluster& + loccluster& IF locbyte& THEN locpercent& = (locbyte& \ loccluster&) * 100 \ bytescluster& ELSE locpercent& = 100 IF sumbyte& THEN sumpercent& = (sumbyte& \ sumcluster&) * 100 \ bytescluster& ELSE sumpercent& = 100 'display accumulated only if below maxlevel '------------------------------------------ IF level <= maxlevel THEN LSET screenline = "" MID$(screenline, 1) = FNLONG2STR$(loccluster& * bytescluster&, 13) MID$(screenline, 14) = FNLONG2STR$(sumcluster& * bytescluster&, 13) IF mode% = 1 THEN 'create tree line '---------------- IF level > 1 THEN FOR i = 1 TO level - 1 IF MID$(tree$, i, 1) <> CHR$(32) THEN MID$(tree$, i) = CHR$(186) NEXT END IF IF FirstOfLevel THEN MID$(tree$, level) = CHR$(201) ELSE MID$(tree$, level) = CHR$(204) LSET tree$ = LEFT$(tree$, level) 'MID$(screenline, 1) = FNLONG2STR$(locbyte&, 13) 'MID$(screenline, 14) = FNLONG2STR$(sumbyte&, 13) MID$(screenline, 28) = FNLONG2STR$(locfiles&, 5) MID$(screenline, 34) = FNLONG2STR$(sumfiles&, 6) MID$(screenline, 42) = FNLONG2STR$(locpercent&, 3) MID$(screenline, 46) = FNLONG2STR$(sumpercent&, 3) MID$(screenline, 50) = LEFT$(tree$, level) + newdir$ ELSE MID$(screenline, 28) = builddir END IF dosprint screenline END IF 'reset level parameter and back to previous level '------------------------------------------------ level = level - 1 builddir = LEFT$(builddir, Startlen) EXIT SUB 'GO SUBS '------- findfile: 'finds any file that match path in Filename$ and attribute in "atribute" '----------------------------------------------------------------------- 'a DTA must be set before first call 'first search must use SearchFirst function IF firstfile THEN regs.ax = SearchFirst regs.cx = atribute regs.ds = VARSEG(Filename$) regs.dx = SADD(Filename$) CALL interruptx(DosInt, regs, regs) firstfile = false ELSE regs.ax = SearchNext CALL interruptx(DosInt, regs, regs) END IF 'detection of no more files error IF regs.flags AND CarryFlag THEN IF regs.ax = &H12 THEN enddir = true ELSE PRINT "Error:", regs.ax: END END IF RETURN changedta: 'sets a new DTA for this level of search '--------------------------------------- regs.ax = SetDta regs.ds = VARSEG(dta) regs.dx = VARPTR(dta) CALL interruptx(DosInt, regs, regs) RETURN END SUB SUB ShowHelp CLS dosprint "DIRSCAN [disk:][path] [/[Lx][?|H]" dosprint "Scans a dir and his sub-dirs and outputs values to screen" dosprint "" dosprint "If you omit parameters it will scan default disk/ base directory,10 levels" dosprint "and display tree" dosprint "Options: " dosprint "Lx -Displays x directory levels under path " dosprint "S -Displays no header and footer, only sizes and path, this way" dosprint " the output can be sorted" dosprint "? or H -Displays Help" dosprint "All screen prints ar done thru'DOS services, you can use DOS sort and redirect" dosprint " DIRSCAN a:/S |SORT |MORE will sort and stop between pages" dosprint " DIRSCAN C: >LPT1 will print the listing" dosprint " DIRSCAN A: |SORT >FILENAME will direct the sorted output to a file " dosprint "" dosprint "" dosprint "Programmed 1998 in QuickBasic by Antoni Gual agual@eic.ictnet.es" END SUB