home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 1998 July / Image.bin / FESTPLAT / CLUSTRS / CLUSTR!.EXE / CLUSTERS.BAS next >
Encoding:
BASIC Source File  |  1995-11-07  |  15.1 KB  |  384 lines

  1. $STACK 20480
  2. defint a-z
  3. %False = 0
  4. %True = NOT(%False)
  5.  
  6. %FLAGS = 0                    ' constants for CPU registers -- we will be
  7. %AX    = 1                    ' calling some DOS routines later
  8. %BX    = 2
  9. %CX    = 3
  10. %DX    = 4
  11. %SI    = 5
  12. %DI    = 6
  13. %BP    = 7
  14. %DS    = 8
  15. %ES    = 9
  16.  
  17. TYPE DTAData                  ' all the information we want about a file
  18.     Reserved AS STRING * 21     ' is in this data structure in DOS memory
  19.   Attr     AS BYTE
  20.   FileTime AS WORD
  21.   FileDate AS WORD
  22.   Size     AS DWORD
  23.   TheName  AS STRING * 13
  24.   TheRest  AS STRING * 21
  25. END TYPE
  26.  
  27. SHARED ReportData&(), TotalFileSize&&, DirNames$(), Pain!, DrvCluster&
  28. DIM HUGE DirNames$(0:4000)
  29.  
  30. DIM ReportData&(1:5,1:5)
  31. '
  32. '  Each primary record in the ReportData& array has 5 elements:
  33. '    ReportData&(x%,1) = Cluster Size
  34. '    ReportData&(x%,2) = Total Clusters Allocated
  35. '    ReportData&(x%,3) = Total Bytes Allocated
  36. '    ReportData&(x%,4) = Total Bytes Used
  37. '    ReportData&(x%,5) = Total Bytes Wasted
  38. '
  39. ReportData&(1,1) = 2048       ' initialize hte records with default sizes
  40. ReportData&(2,1) = 4096
  41. ReportData&(3,1) = 8192
  42. ReportData&(4,1) = 16384
  43. ReportData&(5,1) = 32768
  44.  
  45.  
  46. ' Make a title bar
  47. CLS
  48. t$ = "(c) Nathan C. Durland -- ndurland@aol.com"
  49. PRINT sClr$(15,1);"Disk usage prediction program";TAB(81-LEN(t$));t$
  50.  
  51. '+---------------------------------------------------------------------+
  52. '| First, we need to know what the user wants to know                  |
  53. '+---------------------------------------------------------------------+
  54. VIEW TEXT (1,2)-(80,24)       ' so title bar always stays on screen
  55. PRINT sClr$(7,0);
  56. CLS
  57. INPUT "Enter drive to project cluster usage for: ";t$
  58. IF LEN(t$) = 0 THEN t$ = CURDIR$
  59. DirNames$(0) = LEFT$(UCASE$(t$),1) + ":\"
  60. DrvCluster& = GetClusterSize&(t$)
  61. IF DrvCluster& < 0 THEN
  62.   PRINT "Could not determine drive cluster size"
  63.   END
  64. END IF
  65. INPUT "Pain Level: How much (in percent) of slack space is OK";tt$
  66. Pain! = VAL(tt$)
  67. IF (Pain! < 1) OR (Pain! > 90) THEN Pain! = 30
  68.  
  69. '+--------------------------------------------------------------------------+
  70. '| Now we have to fill an array with a list of all the directries on        |
  71. '| the drive -- the array is DIMed to 4000 above, so we should have enough  |
  72. '| capacity for most users hard drives                                      |
  73. '+--------------------------------------------------------------------------+
  74. PRINT "Loading a list of directory names . . ."
  75. CALL ScanForDirs(DirNames$(),DirCount&)
  76.  
  77. CLS
  78.  
  79. FileCnt& = 0
  80.  
  81. '+-------------------------------------------------------------------------+
  82. '| This is the stuff that stays on the screen unchanged.                   |
  83. '+-------------------------------------------------------------------------+
  84.  
  85. PRINT sClr$(7,0);"Processing ";DirCount&;" directories. ";
  86. t$ = USING$("###.##",Pain!)
  87. PRINT "Pain threshold is ";sClr$(12,0);t$;"%"
  88. PRINT sClr$(7,0);"Current drive cluster size is ";DrvCluster&
  89.  
  90. LOCATE 17,1
  91. PRINT sClr$(2,0);"DOS partion size/cluster size relationship:"
  92. PRINT "      0MB - 128MB  =  2K (2048 byte) Clusters"
  93. PRINT "    129MB - 256MB  =  4K (4096 byte) Clusters"
  94. PRINT "    257MB - 512MB  =  8K (8192 byte) Clusters"
  95. PRINT "    513MB - 1.02GB = 16K (16384 byte) Clusters"
  96. PRINT "   1.02GB - 2.04GB = 32K (32768 byte) Clusters";sClr$(7,0)
  97.  
  98.  
  99. '+---------------------------------------------------------------------------+
  100. '|  Here, we look at the files in each directory.  We obtain the size of the |
  101. '|  file from DOS, and compute how many clusters that file will consume with |
  102. '|  each of our cluster sizes.  The screen is then updated accordingly       |
  103. '+---------------------------------------------------------------------------+
  104.  
  105. FOR DirPtr& = 0 TO DirCount&
  106.  
  107.   LOCATE 3,1                                    ' update the user on what's
  108.   PRINT sClr$(7,0);"Directory ";                ' going on
  109.   t$ = LEFT$(DirNames$(DirPtr&)+SPACE$(75),68)
  110.   PRINT sClr$(10,0);t$
  111.  
  112.   IF INKEY$ = CHR$(27) THEN END                 ' ESC wil abort the program
  113.  
  114.   FileSpec$ = DirNames$(DirPtr&) + "*.*"
  115. '+--------------------------------------------------------------------------+
  116. '| We use the attribute of 55 so that we get all files - hidden, system,    |
  117. '| archive, -- everything except the volume label                           |
  118. '+--------------------------------------------------------------------------+
  119.   TheFile$ = DIR$(FileSpec$,55)
  120.   WHILE LEN(TheFile$) > 0
  121.     INCR FileCnt&,1
  122.     IF (TheFile$ = ".") OR (TheFile$ = "..") THEN   ' the entries "." and ".."
  123.       TheFile$ = DIR$                               ' represent special dir
  124.       ITERATE                                       ' entries to DOS, and
  125.     END IF                                          ' dont' count
  126.  
  127.     FileName$ = DirNames$(DirPtr&) + TheFile$       ' An informed user is
  128.     LOCATE 4,1                                      ' a happy user
  129.     PRINT sClr$(7,0);"File:  ";TAB(11);
  130.     PRINT sClr$(11,0);TheFile$;"        "
  131.     LOCATE 6,1
  132.  
  133.     PRINT USING "#,###,###";FileCnt&;
  134.     PRINT sClr$(7,0);" Files have been checked. ";
  135.     PRINT USING "###,### directories have been checked";DirPtr&
  136.     fSize& = GetFileSize&(FileName$)
  137.  
  138.     FOR x% = 1 TO 5                                 ' Compute file stats for
  139.         CALL ComputeClusters(x%, fSize&)              ' each cluster size
  140.         NEXT x%
  141.  
  142.     CALL PrintStats                                 ' tell what we found
  143.  
  144.     TheFile$ = DIR$                                 ' get the next file
  145.  
  146.   WEND
  147.  
  148. NEXT DirPtr&
  149.  
  150. VIEW TEXT (1,1)-(80,25)
  151. locate 24,1
  152.  
  153. END
  154.  
  155.  
  156. SUB ScanForDirs(DirNames$(), DirCount&) LOCAL PUBLIC
  157. '+----------------------------------------------------------------------------+
  158. '|Scans for all the subdirs in a directory tree.                              |
  159. '|    DirPtr& is a pointer to which directory we are currently scanning for     |
  160. '|    more directories.  Each time we find one, we put the name of the new      |
  161. '|    directory on the end of the list, and increase DirCount& by one.  Thus,   |
  162. '|    DirCount& becomes a moving target, and we will just continually move      |
  163. '|    down farther and farther untill all directories have been search to their |
  164. '|  deepest level.                                                            |
  165. '|                                                                            |
  166. '|After we've found all the directories, (DirCount doesn't stay ahead of      |
  167. '|    DirPtr&), we'll sort the list, and exit.                                  |
  168. '|                                                                            |
  169. '|The scan starts at whatever directory is specified in DirNames$(0).  If     |
  170. '| that element is blank, the root directory of the current drive is used     |
  171. '+----------------------------------------------------------------------------+
  172. '
  173. DirPtr& = -1
  174. DirCount& = 0
  175. IF RIGHT$(DirNames$(0),1) <> "\" THEN DirNames$(0) = DirNames$(0) + "\"
  176.  
  177. WHILE DirCount& > DirPtr&
  178.     INCR DirPtr&,1
  179.     TheDir$ = DirNames$(DirPtr&)
  180.   PRINT "-----> ";TheDir$
  181. '+----------------------------------------------------------------------------+
  182. '| Use an attribute value of 23.  This will make DIR$ return directory names  |
  183. '| and "normal" files, as well has hidden and system directories              |
  184. '+----------------------------------------------------------------------------+
  185.     x$ = DIR$(TheDir$+"*.*",23)
  186.     WHILE x$ <> ""
  187.         TheFile$ = TheDir$ + x$
  188. '+----------------------------------------------------------------------------+
  189. '| Check the attribute of the returned file name.  If bit 4 is set, then the  |
  190. '| file is a directory.  If it's a directory, then increase the DirCount& and |
  191. '| store this directory name.                                                 |
  192. '+----------------------------------------------------------------------------+
  193.     z? = ATTRIB(TheFile$)
  194.         IF BIT(z?,4) THEN
  195.             INCR DirCount&,1
  196.             IF RIGHT$(TheFile$,1) <> "\" THEN TheFile$ = TheFile$ + "\"
  197.             DirNames$(DirCount&) = TheFile$
  198.         END IF
  199.         x$ = DIR$
  200.     WEND
  201. WEND
  202.  
  203. ARRAY SORT DirNames$() FOR DirCount&+1    ' sort directory names
  204.  
  205. END SUB
  206.  
  207.  
  208.  
  209. SUB ComputeClusters(BYVAL What%, BYVAL k&) SHARED PUBLIC
  210. '+----------------------------------------------------------------------+
  211. '| This little routine computes the cluster statistics for a single     |
  212. '| record in the ReportData& array.                                     |
  213. '|                                                                      |
  214. '|      What%  =  Which Record to compute                               |
  215. '|         k&  =  The size of the current file to be added in           |
  216. '+----------------------------------------------------------------------+
  217.  
  218.   Cluster& = ReportData&(What%,1)
  219.     TotalFSize&& = TotalFSize&& + k&
  220.  
  221.   ClustAloc& = (k& \ Cluster&) + 1
  222.   f& = k& MOD Cluster&
  223.   IF f& > 0 THEN INCR ClustAloc&,1
  224.   fWaste& = (ClustAloc& * Cluster&) - k&
  225.  
  226.   ReportData&(What%,2) = ReportData&(What%,2) + ClustAloc&
  227.   ReportData&(What%,3) = ReportData&(What%,3) + (ClustAloc& * Cluster&)
  228.   ReportData&(What%,4) = ReportData&(What%,4) + k&
  229.   ReportData&(What%,5) = ReportData&(What%,5) + fWaste&
  230.  
  231. END SUB
  232.  
  233. SUB PrintStats SHARED PUBLIC
  234. '+---------------------------------------------------------------------------+
  235. '| This routie simply puts the numbers on the screen.  Percentages that are  |
  236. '| over the pain threshold will blink                                        |
  237. '+---------------------------------------------------------------------------+
  238.  
  239.     LOCATE 9,1
  240.   PRINT sClr(14,0);"Clust Size   Clust Alloc     Bytes Alloc     Bytes used         Wasted   %Slack"
  241.   PRINT            "~~~~~~~~~~   ~~~~~~~~~~~     ~~~~~~~~~~~    ~~~~~~~~~~~        ~~~~~~~  ~~~~~~~"
  242.  
  243.   FOR x% = 1 TO 5
  244.       PRINT sClr$(13,0);
  245.     IF ReportData&(x%,1) = DrvCluster& THEN x$ = sClr$(15,1)
  246.     PRINT USING " ##,###";ReportData&(x%,1);
  247.     PRINT sClr$(13,0);"        ";
  248.     PRINT USING " ###,###  ";ReportData&(x%,2);
  249.  
  250.       FOR y% = 3 TO 5
  251.         PRINT USING "    ###,###,###";ReportData&(x%,y%);
  252.     NEXT y%
  253.  
  254.  
  255.     a! = 100 - CFIX((ReportData&(x%,4) / ReportData&(x%,3)) * 100)
  256.  
  257.     PRINT "  ";
  258.     IF a! < Pain! THEN
  259.         PRINT sClr$(15,0);
  260.     ELSE
  261.         PRINT sClr$(30,0);
  262.     END IF
  263.     PRINT USING " ###.##"; a!
  264.  
  265.   NEXT x%
  266.  
  267. END SUB
  268.  
  269. FUNCTION sClr$(f%,b%) LOCAL PUBLIC
  270. '
  271. '  I use this simply to set the text color in a print statement
  272. '
  273.  
  274.     COLOR f%,b%
  275.   sClr$ = ""
  276.  
  277. END FUNCTION
  278.  
  279.  
  280. FUNCTION GetFileSize&(BYVAL TheFile$) LOCAL PUBLIC
  281. '+---------------------------------------------------------------------------+
  282. '| Returns the size (in bytes) of the specified file.  Will preserve the     |
  283. '| current DOS DTA. (see below)                                              |
  284. '+---------------------------------------------------------------------------+
  285.  
  286.   DIM MyDTA as DtaData
  287.   CALL FindOldDta(OldSeg??, OldOff??)              ' so we can go back
  288.   a$ = SPACE$(LEN(MyDTA))
  289.   NewSeg?? = BITS??(STRSEG(a$))
  290.   NewOff?? = BITS??(STRPTR(a$))
  291.   CALL SetNewDta(NewSeg??, NewOff??)
  292.  
  293.   REG %AX, &H4E00
  294.   Victim$ = TheFile$ + CHR$(0)
  295.   REG %CX, &H16        ' find all files and sub dirs
  296.   REG %DS, STRSEG(Victim$)
  297.   REG %DX, STRPTR(Victim$)
  298.   CALL INTERRUPT &H21
  299.  
  300.   RetCode?? = REG(%Flags)
  301.   IF BIT(RetCode??,0) THEN                      ' error flag set
  302.     GetFileSize& = -1
  303.   ELSE
  304.     LSET MyDta = a$
  305.     GetFileSize& = MyDta.Size
  306.   END IF
  307.  
  308. ' Reset the old DTA
  309.   CALL SetNewDTA(OldSeg??, OldOff??)
  310. END FUNCTION
  311.  
  312.  
  313. '+--------------------------------------------------------------------------+
  314. '| The DTA (disk transfer address) is a data structure in DOS memory that   |
  315. '| holds information about the last file DOS was asked to look for.  DOS    |
  316. '| allows a program to determine where the DTA is, and to set it's own area |
  317. '| for the DTA.                                                             |
  318. '|                                                                          |
  319. '| PowerBASIC uses the DTA when you use the DIR$ function.   The first      |
  320. '| time you use it, you specify a file name: a$ = DIR$("*.*").  After that, |
  321. '| you DOS uses information leftover in the DTA to find the next file when  |
  322. '| you use b$ = DIR$.  Everytime you specify a file, even with wildcards,   |
  323. '| the search starts over.                                                  |
  324. '|                                                                          |
  325. '| In this program, that could be a pain -- our main loop is searching      |
  326. '| through all the file in a directory {x$ = DIR$(TheDir$+"*.*",23)} to     |
  327. '| get the names, then CALLing a sub to get the file size.  It's important  |
  328. '| that the SUB not distrub the DTA, or the program would never get past    |
  329. '| the first file in a directory!                                           |
  330. '|                                                                          |
  331. '| So, these two routines allow us to find & preserve the old DTA data,     |
  332. '| while temporarily substituting a different one for doing the size check  |
  333. '+--------------------------------------------------------------------------+
  334.  
  335.  
  336. SUB FindOldDTA(TheSeg??, TheOffSet??) LOCAL PUBLIC
  337. '+------------------------------------------------------------------------+
  338. '|Determines where the current DTA is, and returns it's segment/offset    |
  339. '+------------------------------------------------------------------------+
  340.  REG %AX, &H2F00          'Use DOS service INT 21/function 2F, to
  341.  CALL INTERRUPT &H21      'Find where the current DTA is
  342.  TheSeg?? = REG(%ES)       'Segment of location of default DTA
  343.  TheOffset??  = REG(%BX)   'Offset into the segment for default DTA
  344. END SUB
  345.  
  346.  
  347. SUB SetNewDTA(BYVAL TheSeg??, BYVAL TheOffSet??) LOCAL PUBLIC
  348. '+------------------------------------------------------------------------+
  349. '|Installs a new DTA address for DOS to use                               |
  350. '+------------------------------------------------------------------------+
  351.  REG %AX, &H1A00              'Use another Dos Service (21/1A) to set the DTA
  352.  REG %DS, TheSeg??             'Load the segment of it's location
  353.  REG %DX, TheOffSet??          '  and the offset
  354.  CALL INTERRUPT &H21          'Call the DOS service
  355. END SUB
  356.  
  357. FUNCTION GetClusterSize&(BYVAL TheDrive$) LOCAL PUBLIC
  358. '+--------------------------------------------------------------------------+
  359. '| This sub will determine some of the geometry of the drive -- the sector  |
  360. '| size, and the number of sectors per cluster.  From that, we can compute  |
  361. '| the cluster sze for this drive                                           |
  362. '+--------------------------------------------------------------------------+
  363.  
  364.   Stat% = %True
  365.   dd$ = UCASE$(TheDrive$)
  366.   drv% = ASC(dd$) - 64                  ' unlike BIOS functions, A is drive 1
  367.   IF (drv% < 1) OR (drv% > 26) THEN     ' exit if bad disk name
  368.     GetClusterSize& = -1
  369.     EXIT FUNCTION
  370.   END IF
  371.  
  372.   REG %AX, &H3600             ' DOS Function call - INT 21H/36
  373.   REG %DX, drv%               ' Drive to check
  374.   CALL INTERRUPT &H21         ' Call DOS Int
  375.  
  376.  
  377.   Sectors??         = CWRD(REG(%AX))        ' Sectors per cluster
  378.   SectorSize??      = CWRD(REG(%CX))        ' Sector size in bytes
  379.   GetClusterSize&   = CLNG(Sectors?? * SectorSize??)
  380.  
  381. END FUNCTION
  382.  
  383.  
  384.