home *** CD-ROM | disk | FTP | other *** search
- $STACK 20480
- defint a-z
- %False = 0
- %True = NOT(%False)
-
- %FLAGS = 0 ' constants for CPU registers -- we will be
- %AX = 1 ' calling some DOS routines later
- %BX = 2
- %CX = 3
- %DX = 4
- %SI = 5
- %DI = 6
- %BP = 7
- %DS = 8
- %ES = 9
-
- TYPE DTAData ' all the information we want about a file
- Reserved AS STRING * 21 ' is in this data structure in DOS memory
- Attr AS BYTE
- FileTime AS WORD
- FileDate AS WORD
- Size AS DWORD
- TheName AS STRING * 13
- TheRest AS STRING * 21
- END TYPE
-
- SHARED ReportData&(), TotalFileSize&&, DirNames$(), Pain!, DrvCluster&
- DIM HUGE DirNames$(0:4000)
-
- DIM ReportData&(1:5,1:5)
- '
- ' Each primary record in the ReportData& array has 5 elements:
- ' ReportData&(x%,1) = Cluster Size
- ' ReportData&(x%,2) = Total Clusters Allocated
- ' ReportData&(x%,3) = Total Bytes Allocated
- ' ReportData&(x%,4) = Total Bytes Used
- ' ReportData&(x%,5) = Total Bytes Wasted
- '
- ReportData&(1,1) = 2048 ' initialize hte records with default sizes
- ReportData&(2,1) = 4096
- ReportData&(3,1) = 8192
- ReportData&(4,1) = 16384
- ReportData&(5,1) = 32768
-
-
- ' Make a title bar
- CLS
- t$ = "(c) Nathan C. Durland -- ndurland@aol.com"
- PRINT sClr$(15,1);"Disk usage prediction program";TAB(81-LEN(t$));t$
-
- '+---------------------------------------------------------------------+
- '| First, we need to know what the user wants to know |
- '+---------------------------------------------------------------------+
- VIEW TEXT (1,2)-(80,24) ' so title bar always stays on screen
- PRINT sClr$(7,0);
- CLS
- INPUT "Enter drive to project cluster usage for: ";t$
- IF LEN(t$) = 0 THEN t$ = CURDIR$
- DirNames$(0) = LEFT$(UCASE$(t$),1) + ":\"
- DrvCluster& = GetClusterSize&(t$)
- IF DrvCluster& < 0 THEN
- PRINT "Could not determine drive cluster size"
- END
- END IF
- INPUT "Pain Level: How much (in percent) of slack space is OK";tt$
- Pain! = VAL(tt$)
- IF (Pain! < 1) OR (Pain! > 90) THEN Pain! = 30
-
- '+--------------------------------------------------------------------------+
- '| Now we have to fill an array with a list of all the directries on |
- '| the drive -- the array is DIMed to 4000 above, so we should have enough |
- '| capacity for most users hard drives |
- '+--------------------------------------------------------------------------+
- PRINT "Loading a list of directory names . . ."
- CALL ScanForDirs(DirNames$(),DirCount&)
-
- CLS
-
- FileCnt& = 0
-
- '+-------------------------------------------------------------------------+
- '| This is the stuff that stays on the screen unchanged. |
- '+-------------------------------------------------------------------------+
-
- PRINT sClr$(7,0);"Processing ";DirCount&;" directories. ";
- t$ = USING$("###.##",Pain!)
- PRINT "Pain threshold is ";sClr$(12,0);t$;"%"
- PRINT sClr$(7,0);"Current drive cluster size is ";DrvCluster&
-
- LOCATE 17,1
- PRINT sClr$(2,0);"DOS partion size/cluster size relationship:"
- PRINT " 0MB - 128MB = 2K (2048 byte) Clusters"
- PRINT " 129MB - 256MB = 4K (4096 byte) Clusters"
- PRINT " 257MB - 512MB = 8K (8192 byte) Clusters"
- PRINT " 513MB - 1.02GB = 16K (16384 byte) Clusters"
- PRINT " 1.02GB - 2.04GB = 32K (32768 byte) Clusters";sClr$(7,0)
-
-
- '+---------------------------------------------------------------------------+
- '| Here, we look at the files in each directory. We obtain the size of the |
- '| file from DOS, and compute how many clusters that file will consume with |
- '| each of our cluster sizes. The screen is then updated accordingly |
- '+---------------------------------------------------------------------------+
-
- FOR DirPtr& = 0 TO DirCount&
-
- LOCATE 3,1 ' update the user on what's
- PRINT sClr$(7,0);"Directory "; ' going on
- t$ = LEFT$(DirNames$(DirPtr&)+SPACE$(75),68)
- PRINT sClr$(10,0);t$
-
- IF INKEY$ = CHR$(27) THEN END ' ESC wil abort the program
-
- FileSpec$ = DirNames$(DirPtr&) + "*.*"
- '+--------------------------------------------------------------------------+
- '| We use the attribute of 55 so that we get all files - hidden, system, |
- '| archive, -- everything except the volume label |
- '+--------------------------------------------------------------------------+
- TheFile$ = DIR$(FileSpec$,55)
- WHILE LEN(TheFile$) > 0
- INCR FileCnt&,1
- IF (TheFile$ = ".") OR (TheFile$ = "..") THEN ' the entries "." and ".."
- TheFile$ = DIR$ ' represent special dir
- ITERATE ' entries to DOS, and
- END IF ' dont' count
-
- FileName$ = DirNames$(DirPtr&) + TheFile$ ' An informed user is
- LOCATE 4,1 ' a happy user
- PRINT sClr$(7,0);"File: ";TAB(11);
- PRINT sClr$(11,0);TheFile$;" "
- LOCATE 6,1
-
- PRINT USING "#,###,###";FileCnt&;
- PRINT sClr$(7,0);" Files have been checked. ";
- PRINT USING "###,### directories have been checked";DirPtr&
- fSize& = GetFileSize&(FileName$)
-
- FOR x% = 1 TO 5 ' Compute file stats for
- CALL ComputeClusters(x%, fSize&) ' each cluster size
- NEXT x%
-
- CALL PrintStats ' tell what we found
-
- TheFile$ = DIR$ ' get the next file
-
- WEND
-
- NEXT DirPtr&
-
- VIEW TEXT (1,1)-(80,25)
- locate 24,1
-
- END
-
-
- SUB ScanForDirs(DirNames$(), DirCount&) LOCAL PUBLIC
- '+----------------------------------------------------------------------------+
- '|Scans for all the subdirs in a directory tree. |
- '| DirPtr& is a pointer to which directory we are currently scanning for |
- '| more directories. Each time we find one, we put the name of the new |
- '| directory on the end of the list, and increase DirCount& by one. Thus, |
- '| DirCount& becomes a moving target, and we will just continually move |
- '| down farther and farther untill all directories have been search to their |
- '| deepest level. |
- '| |
- '|After we've found all the directories, (DirCount doesn't stay ahead of |
- '| DirPtr&), we'll sort the list, and exit. |
- '| |
- '|The scan starts at whatever directory is specified in DirNames$(0). If |
- '| that element is blank, the root directory of the current drive is used |
- '+----------------------------------------------------------------------------+
- '
- DirPtr& = -1
- DirCount& = 0
- IF RIGHT$(DirNames$(0),1) <> "\" THEN DirNames$(0) = DirNames$(0) + "\"
-
- WHILE DirCount& > DirPtr&
- INCR DirPtr&,1
- TheDir$ = DirNames$(DirPtr&)
- PRINT "-----> ";TheDir$
- '+----------------------------------------------------------------------------+
- '| Use an attribute value of 23. This will make DIR$ return directory names |
- '| and "normal" files, as well has hidden and system directories |
- '+----------------------------------------------------------------------------+
- x$ = DIR$(TheDir$+"*.*",23)
- WHILE x$ <> ""
- TheFile$ = TheDir$ + x$
- '+----------------------------------------------------------------------------+
- '| Check the attribute of the returned file name. If bit 4 is set, then the |
- '| file is a directory. If it's a directory, then increase the DirCount& and |
- '| store this directory name. |
- '+----------------------------------------------------------------------------+
- z? = ATTRIB(TheFile$)
- IF BIT(z?,4) THEN
- INCR DirCount&,1
- IF RIGHT$(TheFile$,1) <> "\" THEN TheFile$ = TheFile$ + "\"
- DirNames$(DirCount&) = TheFile$
- END IF
- x$ = DIR$
- WEND
- WEND
-
- ARRAY SORT DirNames$() FOR DirCount&+1 ' sort directory names
-
- END SUB
-
-
-
- SUB ComputeClusters(BYVAL What%, BYVAL k&) SHARED PUBLIC
- '+----------------------------------------------------------------------+
- '| This little routine computes the cluster statistics for a single |
- '| record in the ReportData& array. |
- '| |
- '| What% = Which Record to compute |
- '| k& = The size of the current file to be added in |
- '+----------------------------------------------------------------------+
-
- Cluster& = ReportData&(What%,1)
- TotalFSize&& = TotalFSize&& + k&
-
- ClustAloc& = (k& \ Cluster&) + 1
- f& = k& MOD Cluster&
- IF f& > 0 THEN INCR ClustAloc&,1
- fWaste& = (ClustAloc& * Cluster&) - k&
-
- ReportData&(What%,2) = ReportData&(What%,2) + ClustAloc&
- ReportData&(What%,3) = ReportData&(What%,3) + (ClustAloc& * Cluster&)
- ReportData&(What%,4) = ReportData&(What%,4) + k&
- ReportData&(What%,5) = ReportData&(What%,5) + fWaste&
-
- END SUB
-
- SUB PrintStats SHARED PUBLIC
- '+---------------------------------------------------------------------------+
- '| This routie simply puts the numbers on the screen. Percentages that are |
- '| over the pain threshold will blink |
- '+---------------------------------------------------------------------------+
-
- LOCATE 9,1
- PRINT sClr(14,0);"Clust Size Clust Alloc Bytes Alloc Bytes used Wasted %Slack"
- PRINT "~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~ ~~~~~~~"
-
- FOR x% = 1 TO 5
- PRINT sClr$(13,0);
- IF ReportData&(x%,1) = DrvCluster& THEN x$ = sClr$(15,1)
- PRINT USING " ##,###";ReportData&(x%,1);
- PRINT sClr$(13,0);" ";
- PRINT USING " ###,### ";ReportData&(x%,2);
-
- FOR y% = 3 TO 5
- PRINT USING " ###,###,###";ReportData&(x%,y%);
- NEXT y%
-
-
- a! = 100 - CFIX((ReportData&(x%,4) / ReportData&(x%,3)) * 100)
-
- PRINT " ";
- IF a! < Pain! THEN
- PRINT sClr$(15,0);
- ELSE
- PRINT sClr$(30,0);
- END IF
- PRINT USING " ###.##"; a!
-
- NEXT x%
-
- END SUB
-
- FUNCTION sClr$(f%,b%) LOCAL PUBLIC
- '
- ' I use this simply to set the text color in a print statement
- '
-
- COLOR f%,b%
- sClr$ = ""
-
- END FUNCTION
-
-
- FUNCTION GetFileSize&(BYVAL TheFile$) LOCAL PUBLIC
- '+---------------------------------------------------------------------------+
- '| Returns the size (in bytes) of the specified file. Will preserve the |
- '| current DOS DTA. (see below) |
- '+---------------------------------------------------------------------------+
-
- DIM MyDTA as DtaData
- CALL FindOldDta(OldSeg??, OldOff??) ' so we can go back
- a$ = SPACE$(LEN(MyDTA))
- NewSeg?? = BITS??(STRSEG(a$))
- NewOff?? = BITS??(STRPTR(a$))
- CALL SetNewDta(NewSeg??, NewOff??)
-
- REG %AX, &H4E00
- Victim$ = TheFile$ + CHR$(0)
- REG %CX, &H16 ' find all files and sub dirs
- REG %DS, STRSEG(Victim$)
- REG %DX, STRPTR(Victim$)
- CALL INTERRUPT &H21
-
- RetCode?? = REG(%Flags)
- IF BIT(RetCode??,0) THEN ' error flag set
- GetFileSize& = -1
- ELSE
- LSET MyDta = a$
- GetFileSize& = MyDta.Size
- END IF
-
- ' Reset the old DTA
- CALL SetNewDTA(OldSeg??, OldOff??)
- END FUNCTION
-
-
- '+--------------------------------------------------------------------------+
- '| The DTA (disk transfer address) is a data structure in DOS memory that |
- '| holds information about the last file DOS was asked to look for. DOS |
- '| allows a program to determine where the DTA is, and to set it's own area |
- '| for the DTA. |
- '| |
- '| PowerBASIC uses the DTA when you use the DIR$ function. The first |
- '| time you use it, you specify a file name: a$ = DIR$("*.*"). After that, |
- '| you DOS uses information leftover in the DTA to find the next file when |
- '| you use b$ = DIR$. Everytime you specify a file, even with wildcards, |
- '| the search starts over. |
- '| |
- '| In this program, that could be a pain -- our main loop is searching |
- '| through all the file in a directory {x$ = DIR$(TheDir$+"*.*",23)} to |
- '| get the names, then CALLing a sub to get the file size. It's important |
- '| that the SUB not distrub the DTA, or the program would never get past |
- '| the first file in a directory! |
- '| |
- '| So, these two routines allow us to find & preserve the old DTA data, |
- '| while temporarily substituting a different one for doing the size check |
- '+--------------------------------------------------------------------------+
-
-
- SUB FindOldDTA(TheSeg??, TheOffSet??) LOCAL PUBLIC
- '+------------------------------------------------------------------------+
- '|Determines where the current DTA is, and returns it's segment/offset |
- '+------------------------------------------------------------------------+
- REG %AX, &H2F00 'Use DOS service INT 21/function 2F, to
- CALL INTERRUPT &H21 'Find where the current DTA is
- TheSeg?? = REG(%ES) 'Segment of location of default DTA
- TheOffset?? = REG(%BX) 'Offset into the segment for default DTA
- END SUB
-
-
- SUB SetNewDTA(BYVAL TheSeg??, BYVAL TheOffSet??) LOCAL PUBLIC
- '+------------------------------------------------------------------------+
- '|Installs a new DTA address for DOS to use |
- '+------------------------------------------------------------------------+
- REG %AX, &H1A00 'Use another Dos Service (21/1A) to set the DTA
- REG %DS, TheSeg?? 'Load the segment of it's location
- REG %DX, TheOffSet?? ' and the offset
- CALL INTERRUPT &H21 'Call the DOS service
- END SUB
-
- FUNCTION GetClusterSize&(BYVAL TheDrive$) LOCAL PUBLIC
- '+--------------------------------------------------------------------------+
- '| This sub will determine some of the geometry of the drive -- the sector |
- '| size, and the number of sectors per cluster. From that, we can compute |
- '| the cluster sze for this drive |
- '+--------------------------------------------------------------------------+
-
- Stat% = %True
- dd$ = UCASE$(TheDrive$)
- drv% = ASC(dd$) - 64 ' unlike BIOS functions, A is drive 1
- IF (drv% < 1) OR (drv% > 26) THEN ' exit if bad disk name
- GetClusterSize& = -1
- EXIT FUNCTION
- END IF
-
- REG %AX, &H3600 ' DOS Function call - INT 21H/36
- REG %DX, drv% ' Drive to check
- CALL INTERRUPT &H21 ' Call DOS Int
-
-
- Sectors?? = CWRD(REG(%AX)) ' Sectors per cluster
- SectorSize?? = CWRD(REG(%CX)) ' Sector size in bytes
- GetClusterSize& = CLNG(Sectors?? * SectorSize??)
-
- END FUNCTION
-
-
-