home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / SHFRG2.ZIP / SHOWFRAG.C < prev   
C/C++ Source or Header  |  1992-04-14  |  30KB  |  771 lines

  1. /* ******************************************************************** *
  2.     ShowFrag - Show disk file fragmentation.
  3.     Version  - v1.2  14-Apr-1992
  4.  
  5.     OS2'ized by Dennis Lang   Corporate Account, Compuserve [70310,1050]
  6.  
  7.  
  8.     Original code:
  9.       CHKFRAG.C from PC Magazine
  10.                      Copyright (c) 1991 Ziff Communications Co.
  11.       CHKFRAG.c authored by Bob Flanders and Michael Holmes
  12.  
  13.  
  14.     Compiler/link (Msoft C600A):
  15.       (os2) CL -D__OS2__ -J -G2s -Ox showfrag.c disk_api.c
  16.       (dos) CL -D__DOS__ -J -G2s -Ox showfrag.c disk_api.c
  17.  
  18.     NOTE: Before/during compilation please define either __DOS__ or __OS2__
  19.  
  20.  
  21.  
  22.     Maintenance log
  23.  
  24.     Version   Date   Description                Who
  25.     ------- -------- -------------------------------------- ----------
  26.     1.0     2/4/92   Port ChkFrag.c to os/2  ShowFrag.c     D.Lang
  27.     1.2     4/14/92  Fix for those small 32meg disk drives  D.Lang
  28.                      Fix for sectors not 512 bytes in size  D.Lang
  29.  
  30.  * ******************************************************************** */
  31.  
  32. #pragma pack(1)            // Force structure byte alignment packing.
  33.  
  34. #include <stdio.h>         // standard library
  35. #include <stdlib.h>        // common lib modules
  36. #include <malloc.h>        // memory allocation
  37. #include <direct.h>        // system api
  38. #include <io.h>
  39. #include <string.h>
  40. #include <conio.h>         // direct console i/o
  41.  
  42. #include "disk_api.h"      // get Disk API call defs and stuff
  43.  
  44. #define  EOF_CLUSTER     (0xfff0)     // define what a EOF  cluster number is
  45. #define  INVALID_CLUSTER ((USHORT)-1) // define what a bad  cluster number is
  46. #define  BAD_CLUSTER     ((USHORT)-2) // define what a bad  cluster number is
  47. #define  FREE_CLUSTER    ((USHORT)0 ) // define what a free cluster number is
  48. #define  USED_CLUSTER    ((USHORT)1 ) // define what a used cluster number is
  49.  
  50. //  Globals
  51.  
  52. USHORT huge *fat;          // address of FAT
  53. USHORT huge *fat_12;       // address of FAT for 12bit entry work.
  54. USHORT fat_16;             // true if 16 bit FAT entries
  55. USHORT nclusters;          // number of clusters
  56.  
  57. short  sections   = 0;     // statistics - file sections
  58. short  frag       = 0;     // statistics - fragmented files
  59. short  unfrag     = 0;     // statistics - unfragmented files
  60. short  freespaces = 0;     // statistics - freespaces
  61. short  files      = 0;     // statistics - processed files
  62. short  dirs       = 0;     // statistics - processed directories
  63. short  bad_spots  = 0;     // statistics - clusters marked bad.
  64. short  list       = 0;     // run options - list frag'd files switch
  65. short  detail     = 0;     // run options - list file detail
  66. short  verbose    = 0;     // run options - verbose listing
  67.  
  68.  
  69. // ***********************************************************************
  70. //                                                                       *
  71. //      next_cluster -- return next cluster number from FAT              *
  72. //                                                                       *
  73. //         n    current cluster number                                   *
  74. //         x    1 = reset FAT entry, 2 = return entry                    *
  75. //         rc   error return code                                        *
  76. // ***********************************************************************
  77. USHORT next_cluster(USHORT n, int x, int *rc)
  78. {
  79.     USHORT  e;                            // entry number in FAT
  80.     USHORT  nc;                           // next cluster value
  81.     USHORT  mask1, mask2;                 // mask for and'ing and or'ing
  82.     USHORT  flag;
  83.  
  84.     *rc = 0;                              // clear return code
  85.     nc  = e = n;                          // initialize nc
  86.  
  87.     if (nc == 0)                          // q. invalid cluster nbr
  88.         return (EOF_CLUSTER);             // a. yes .. rtn EOF
  89.  
  90.     if (fat_16)                           // q. 16 bit FAT entries?
  91.     {
  92.         nc = fat[e];                      // a. yes .. retrieve next entry
  93.  
  94.         if (x != 2)                       // q. return value?
  95.         {                                 // a. no ... process it
  96.             if (nc == 0)                  // q. unallocated cluster?
  97.             {
  98.                 nc = INVALID_CLUSTER;     // a. yes .. error condition
  99.                 *rc = 1;                  // set return code
  100.             }
  101.  
  102.             if (x == 1)                   // q. need to reset entry?
  103.                 fat[e] = USED_CLUSTER;    // a. yes .. show processed
  104.         }
  105.     }
  106.     else
  107.     {                                     // fat entries are 12 bits
  108.         e  = ((ULONG)nc * 2 + nc) >> 1;   // cluster number * 1.5
  109.         flag = nc & 1;                    // shift flag
  110.         fat_12 = (char huge*)fat+e;
  111.         nc = *fat_12;                     // get next cluster
  112.  
  113.         if (flag)                         //
  114.         {
  115.             nc >>= 4;                     // shift by 4 bits
  116.             mask1 = 0x000f;               // mask to clear upper bits
  117.             mask2 = 0x0010;               // ..and footprint mask
  118.         }
  119.         else
  120.         {
  121.             nc &= 0xfff;                  // strip upper bits
  122.             mask1 = 0xf000;               // mask to clear lower bits
  123.             mask2 = 0x0001;               // ..and footprint mask
  124.         }
  125.  
  126.         if (x != 2)                       // q. return value?
  127.         {                                 // a. no ... process it
  128.             if (nc == 0)                  // q. unallocated cluster?
  129.             {
  130.                 nc = INVALID_CLUSTER;     // a. yes .. error condition
  131.                 *rc = 1;                  // set return code
  132.             }
  133.  
  134.             if (x == 1)                   // q. need to reset entry?
  135.             {
  136.                 *fat_12 &= mask1;          // a. yes .. 'and' off bits
  137.                 *fat_12 |= mask2;          // ..and put down footprint
  138.             }
  139.         }
  140.  
  141.         if (nc >= 0xff0) nc = EOF_CLUSTER;
  142.     }
  143.  
  144.     if (nc >= EOF_CLUSTER)
  145.     {
  146.       if (nc == 0xfff7)                   // Bad cluster.
  147.         nc = BAD_CLUSTER;
  148.       else
  149.         nc = EOF_CLUSTER;                 // EOF cluster
  150.     }
  151.  
  152.     return (nc);
  153. }
  154.  
  155. // **********************************************************************
  156. //                                    *
  157. //    get_fat -- read boot record and fat into memory         *
  158. //                                    *
  159. // **********************************************************************
  160. short get_fat(char drive_letter, USHORT disk_handle)
  161. {
  162.     long    max_secs;      // Maximum sectors to read
  163.     long    next_sector;   // next sector to start read at
  164.     long    num_secs;      // number of sectors to read
  165.     long    read_secs;     // number of sectors to read
  166.     long    nsectors;      // number of sectors on drive
  167.     USHORT  bytes_per_sector;
  168.     USHORT  root_sector;
  169.     USHORT  first_file_sector;
  170.     ULONG   fat_size;
  171.     char    huge *fat_ptr; // pointer into fat buffer
  172.     USHORT  i, j;          // work
  173.     short   last_free = 0;
  174.     short   last_bad  = 0;
  175.     short   err, rc;
  176.     char    volume[20];
  177.  
  178.     struct  bootrec
  179.     {
  180.         char   jmp[3];             // 03 jump instruction
  181.         char   oem[8];             // 08 OEM name
  182.         USHORT usBytesPerSector;   // 02 bytes per sector
  183.         BYTE   bSectorsPerCluster; // 01 sectors per cluster
  184.         USHORT usReservedSectors;  // 02 reserved sectors
  185.         BYTE   cFATs;              // 01 number of fats
  186.         USHORT cRootEntries;       // 02 number of root dir entries
  187.         USHORT cSectors;           // 02 total sectors
  188.         BYTE   bMedia;             // 01 media descriptor block
  189.         USHORT usSectorsPerFAT;    // 02
  190.         USHORT usSectorsPerTrack;  // 02
  191.         USHORT cHeads;             // 02
  192.         ULONG  cHiddenSectors;     // 04
  193.         ULONG  cLargeSectors;      // 04 sectors if above 32Mb
  194.         BYTE   abReserved[6];      // 06
  195.         USHORT cCylinders;         // 02
  196.         BYTE   bDeviceType;        // 01
  197.         USHORT fsDeviceAttr;       // 02
  198.     } *boot;                       // 47 = Total bytes for boot record def.
  199.  
  200.  
  201.     static char *nomem    = "Not enough memory for processing\n";
  202.     static char *readerr  = "Error reading boot record\n";
  203.  
  204.     bytes_per_sector = DosBytesPerDiskSector(disk_handle);
  205.  
  206.     if ((boot = (struct bootrec *)malloc(bytes_per_sector)) == NULL)
  207.     {                                              // q. no memory?
  208.         printf(nomem);                             // a. yes, give error msg
  209.         return (FALSE);                            // ..and return to DOS
  210.     }
  211.  
  212.     if (DosReadSector(disk_handle, boot, 0, 1, bytes_per_sector))
  213.     {                                              // q. error reading disk?
  214.         printf(readerr);                           // a. yes, give error msg
  215.         free(boot);                                // ..free memory
  216.         return (FALSE);                            // ..and return to DOS
  217.     }
  218.  
  219.     nsectors = (boot->cSectors ? (long)boot->cSectors : boot->cLargeSectors);
  220.  
  221.     fat_size = (long)boot->usSectorsPerFAT * (long)boot->usBytesPerSector;
  222.     if ((fat = (USHORT huge *)halloc(fat_size, 1)) == (char huge *)NULL)
  223.     {                                               // q. no memory?
  224.         printf(nomem);                              // a. yes .. give
  225.         free(boot);                                 // ..free memory
  226.         return (FALSE);                             // ..error msg/exit
  227.     }
  228.  
  229.     // Load entire FAT into memory.
  230.  
  231.     max_secs    = 0x8000L / boot->usBytesPerSector; // max we can read
  232.     fat_ptr     = (char huge *)fat;                 // initial offset in table
  233.     next_sector = boot->usReservedSectors;          // get first sector of FAT
  234.  
  235.  
  236.     for (num_secs = boot->usSectorsPerFAT; num_secs;)  // while there are some left
  237.     {
  238.         read_secs = min(max_secs, num_secs);        // size of this read
  239.  
  240.         err = DosReadSector(disk_handle, fat_ptr, next_sector, read_secs, bytes_per_sector);
  241.  
  242.         num_secs    -= read_secs;                   // .. .. smaller of max, num
  243.         next_sector += read_secs;                   // Calc next sector number
  244.                                                     // Calc next buffer address
  245.         fat_ptr     += (long)read_secs * boot->usBytesPerSector;
  246.     }
  247.  
  248.     if (err)                                        // q. error reading disk?
  249.     {
  250.         printf(readerr);                            // a. yes .. give error msg
  251.         free(boot);                                 // ..and free memory
  252.         hfree(fat);
  253.         return (FALSE);                             // ..and return to DOS
  254.     }
  255.  
  256.     // Calculate some magic numbers.
  257.  
  258.     root_sector = boot->usReservedSectors + (boot->usSectorsPerFAT * boot->cFATs);
  259.     first_file_sector = root_sector + (boot->cRootEntries * 32) / boot->usBytesPerSector;
  260.     nclusters = (nsectors - first_file_sector) / boot->bSectorsPerCluster;
  261.  
  262.     // Determine FAT format
  263.  
  264.     // fat_16 = ((nsectors / boot->bSectorsPerCluster) > 4087);
  265.     fat_16 = (fat_size / nclusters) >= 2;
  266.  
  267.  
  268.     // Initialize directory search system (OS/2 only)
  269.  
  270.     DosDirInitialize(disk_handle, fat, fat_size, boot->bSectorsPerCluster,
  271.       boot->usBytesPerSector, root_sector, first_file_sector);
  272.  
  273.     printf("Drive %c:%s\n", drive_letter,
  274.       DosDiskVolume(drive_letter, volume, sizeof(volume)));
  275.  
  276.     if (verbose)
  277.     {
  278.       printf("  Disk Specifications:\n");
  279.       printf("    %lu Sectors, %u Clusters, %u Clustersize\n",
  280.         nsectors, nclusters, boot->bSectorsPerCluster * boot->usBytesPerSector);
  281.     }
  282.  
  283.     printf("\n  Checking disk for bad cluster ..\n    ");
  284.  
  285.     last_free = 0;
  286.     last_bad  = 0;
  287.     for (i = 2; i < nclusters; i++)             // look for freespaces
  288.     {
  289.         j = next_cluster(i, 2, &rc);
  290.         switch (j)
  291.         {
  292.           case FREE_CLUSTER:
  293.             if (last_free+1 != i)
  294.                 freespaces++;                   // a. yes. increment free count
  295.             last_free = i;
  296.             break;
  297.  
  298.           case BAD_CLUSTER:
  299.             if (verbose) printf("%u ", i);
  300.             if (last_bad+1 != i)
  301.                 bad_spots++;                    // a. yes. increment bad count
  302.             last_bad = i;
  303.             break;
  304.         }
  305.     }
  306.  
  307.     if (verbose) putchar('\n');
  308.     free(boot);                                 // clean up and return
  309.     return (TRUE);
  310. }
  311.  
  312.  
  313. // ***********************************************************************
  314. //                                                                       *
  315. //      check_frag -- check a file/directory for fragmentation           *
  316. //                                                                       *
  317. //    s      file/directory name                                         *
  318. //    n      starting cluster number                                     *
  319. //    dflag  directory flag                                              *
  320. // ***********************************************************************
  321. short check_frag(char *s, USHORT n, short dflag)
  322. {
  323.     USHORT j;                                     // working storage
  324.     USHORT nc;                                    // next cluster
  325.     USHORT start_of_group;                        // verbose printing
  326.     int    rc;                                    // error return code
  327.     int    pieces = 0;                            // flag for frag'd file
  328.     int    gaps = 0;
  329.  
  330.  
  331.     if (verbose)
  332.     {
  333.         if (verbose != -1)
  334.         {
  335.           printf("\n  Verbose file cluster usage:\n");
  336.           verbose = -1;
  337.         }
  338.         printf("    %s Cluster=%u", s, n);
  339.     }
  340.  
  341.  
  342.     start_of_group = n;
  343.     for (;;)                                      // walk down the chain
  344.     {
  345.         nc = next_cluster(n, 1, &rc);
  346.         if (nc == EOF_CLUSTER) break;
  347.  
  348.         if (nc == INVALID_CLUSTER)                // q. invalid cluster?
  349.         {                                         // a. yes .. give err msg
  350.             printf("\n\tError -- File: %s has %s\n%s, cluster %u\n",
  351.                s, rc ? "an invalid cluster" : "a cross-linked cluster", n,
  352.                "\n\t** Please run CHKDSK **");
  353.             return 0;
  354.         }
  355.  
  356.  
  357.         if ((n + 1) != nc)                      // q. non-contiguous area?
  358.         {
  359.             if (verbose)
  360.             {
  361.                 if (start_of_group != n) printf("-%u", n);
  362.                 printf(", %u", nc);
  363.                 start_of_group = nc;
  364.             }
  365.  
  366.             if (detail)
  367.             {
  368.                 if (gaps++ == 0)
  369.                 {
  370.                     if (detail != -1)
  371.                     {
  372.                         printf("\n    Fragmented files: Cluster Gaps...\n"
  373.                         "    (Negative gap=backwards linkage, *=Gap caused by bad cluster)\n\n");
  374.                         detail = -1;
  375.                     }
  376.                     printf("    %s:", s);
  377.                 }
  378.                 printf(" %d", nc - n);
  379.             }
  380.  
  381.             pieces++;                           // show fragmented file
  382.             if (nc > n)                         // q. possibly bad cluster?
  383.             {
  384.                 // check for bad spots
  385.                 for (j = n + 1;
  386.                   next_cluster(j, 0, &rc) == BAD_CLUSTER && j < nc; j++);
  387.  
  388.                 if (j == nc)                    // q. was entire area bad?
  389.                 {
  390.                     pieces--;                   // a. yes .. don't report
  391.                     if (detail) putchar('*');
  392.                 }
  393.                 else
  394.                     sections++;                 // incr files sections count
  395.             }
  396.             else
  397.                 sections++;                     // incr files sections count
  398.         }
  399.  
  400.         n = nc;
  401.     }
  402.  
  403.     if (verbose)
  404.     {
  405.         if (start_of_group != n) printf("-%u", n);
  406.         putchar('\n');
  407.     }
  408.     else if (detail && gaps)
  409.         putchar('\n');
  410.  
  411.     if (pieces)                                 // q. fragmented file
  412.     {
  413.         if (list)                               // q. list frag'd files?
  414.         {                                       // a. yes .. give it to them
  415.             if (!frag)
  416.                 printf("\n    Fragmented Files/Directories:\n"
  417.                   "    Pieces: File or [Directory]\n");
  418.  
  419.             if (dflag)
  420.               printf("    %6u: [%s]\n", pieces, s);
  421.             else
  422.               printf("    %6u: %s\n", pieces, s);
  423.         }
  424.         frag++;                                 // accumulate frag'd count
  425.     }
  426.     else
  427.         unfrag++;                               // else total unfrag'd files
  428.  
  429.     return (pieces);
  430. }
  431.  
  432.  
  433. // ***********************************************************************
  434. //                                                                       *
  435. //      check_unlinked -- check for unlinked clusters                    *
  436. //                                                                       *
  437. // ***********************************************************************
  438. void check_unlinked(void)
  439. {
  440.     int     rc;                               // error return code
  441.     USHORT  i;                                // loop counter
  442.     long    j;                                // work return cluster nbr
  443.  
  444.     if (verbose) printf("\n  Checking for lost clusters: \n");
  445.  
  446.     for (i = 2; i < nclusters; i++)           // check thru entire FAT
  447.     {
  448.         j = next_cluster(i, 2, &rc);
  449.  
  450.         if (j != FREE_CLUSTER && j != USED_CLUSTER && j != BAD_CLUSTER)
  451.         {
  452.             if (verbose)
  453.             {
  454.               printf("    %u ", i);
  455.             }
  456.             else
  457.             {
  458.               printf("\n    Lost cluster %u detected, ** Please run CHKDSK **\n", i);
  459.               return;
  460.             }
  461.         }
  462.     }
  463.  
  464.     if (verbose) putchar('\n');
  465. }
  466.  
  467.  
  468. // ***********************************************************************
  469. //                                                                       *
  470. //      dir_search -- recursively search all files & subdirectories      *
  471. //                                                                       *
  472. // ***********************************************************************
  473. void dir_search(char *base_dir, short cluster)
  474. {
  475.     Directory* dir = NULL;                       // Directory scan info
  476.     char       pass = 0;                         // pass number
  477.     char       work_dir[65];                     // work directory
  478.  
  479.     //  The following areas are STATIC .. not allocated on recursion
  480.  
  481.     static char   *bc = "\\|/-";                 // bar characters to use
  482.     static short  bar = 0;                       // next bar character
  483.  
  484.     //  End of static area
  485.  
  486.     if (strcmp(base_dir, translate_name(base_dir)))  // q. JOIN'd?
  487.         return;                                  // a. yes .. skip it
  488.  
  489.     chdir(base_dir);
  490.  
  491.     for (;;)                                     // look through current dir
  492.     {
  493.         if (dir == NULL)                         // q. find first done?
  494.         {                                        // a. no .. do it
  495.             dir = DosDirFindFirst("*", FILES, cluster);
  496.         }
  497.         else
  498.         {
  499.             dir = DosDirFindNext(dir);
  500.         }
  501.  
  502.         if (!list && !detail && !verbose)        // q. list in progress?
  503.         {                                        // a. no ..
  504.             putch(bc[bar]);                      // print bar
  505.             putch(8);                            // print backspace
  506.             if (++bar > 3)                       // q. limit on chars?
  507.                 bar = 0;                         // a. yes .. reset to zero
  508.         }
  509.  
  510.         strcpy(work_dir, base_dir);              // get current base
  511.         if (work_dir[strlen(work_dir)-1] != '\\')
  512.           strcat(work_dir, "\\");                // .. add a backslash
  513.         if (dir) strcat(work_dir, dir->name);    // .. add the name
  514.  
  515.         if (pass)                                // q. second pass?
  516.         {
  517.             if (dir == NULL)                     // q. more files found?
  518.                 break;                           // a. no .. exit
  519.  
  520.             if (!(dir->attrib & DIRECTORY)       // q. directory?
  521.               || (dir->name[0] == '.'))          // .. or a dot dir?
  522.                 continue;                        // a. get next entry
  523.  
  524.             dirs++;                              // accumulate dir count
  525.             dir_search(work_dir, dir->cluster);  // recursively call ourself
  526.         }
  527.         else                                     // first pass processing
  528.         {
  529.             if (dir == NULL)                     // q. anything found?
  530.             {                                    // a. no ..
  531.                 pass++;                          // go to next pass
  532.                 continue;                        // .. continue processing
  533.             }
  534.  
  535.             if (dir->name[0] == '.')             // q. dot directory?
  536.                 continue;                        // a. yes .. skip it
  537.  
  538.             if (!(dir->attrib & DIRECTORY))      // q. a file?
  539.                 files++;                         // a. yes .. count them
  540.  
  541.             // check for frag'd file
  542.             check_frag(work_dir, dir->cluster,
  543.               (short)(dir->attrib & DIRECTORY));
  544.         }
  545.     }
  546.  
  547.     putch(' ');
  548.     putch(8);
  549.     DosDirClose(dir);
  550. }
  551.  
  552.  
  553. // ***********************************************************************
  554. //                                                                       *
  555. //      chkdrv -- assure drive is LOCAL and not SUBST'd or ASSIGN'd      *
  556. //                                                                       *
  557. // ***********************************************************************
  558. short chkdrv(char drive_letter)
  559. {
  560.     short  invalid_err;
  561.  
  562.     if (_osmode == DOS_MODE && _osmajor < 2)    // q. pre-DOS 2.00?
  563.         return (4);                             // a. yes .. can't run it
  564.  
  565.                                                 // q. OS/2 or DOS 3.1 or higher?
  566.     if (_osmode == OS2_MODE || (_osmajor >= 3 && _osminor >= 1))
  567.     {
  568.         if ((invalid_err = DosDiskValid(drive_letter)))
  569.             return invalid_err;
  570.     }
  571.  
  572.     return (0);
  573. }
  574.  
  575.  
  576.  
  577. // ***********************************************************************
  578. //                                                                       *
  579. //       mainline                                                        *
  580. //                                                                       *
  581. // ***********************************************************************
  582. int main(int argc, char *argv[])
  583. {
  584.     USHORT rc;                    // return code
  585.     long   pf     = 0;            // percent fragmented
  586.     int    helpme = 0;            // parm in error
  587.     char   option = 0;            // program option
  588.     int    i;                     // loop counter; work
  589.     char   *p;                    // work pointer
  590.     char   cdrive[66];            // startup drive and path
  591.     char   *msg;                  // output message pointer
  592.     USHORT drive_handle;          // physical disk handle
  593.  
  594.     static char *cantopen = "Can't open drive %c\n";
  595.  
  596.     static char drive[] = " :\\";  // drive and path to check
  597.     static char *rc_type[] =
  598.     {
  599.         "Percentage",
  600.         "Number of Files",
  601.         "Number of Extra Segments",
  602.         "Number of Free Areas"
  603.     };
  604.     static char *suggestion[] =
  605.     {
  606.         "No fragmentation found -- Defrag unnecessary",
  607.         "Little fragmentation -- Defrag optional",
  608.         "Moderate fragmentation -- Defrag should be performed soon",
  609.         "Fragmentation critical -- Defrag or Backup/Format/Restore"
  610.     };
  611.     static char *errors[] =
  612.     {
  613.         "Invalid drive specified",
  614.         "Cannot CHKFRAG a network drive",
  615.         "Cannot CHKFRAG a SUBST'd or ASSIGN'd drive",
  616.         "Must run with DOS 2.0 or greater"
  617.     };
  618.  
  619.  
  620.     printf("ShowFrag v1.2 (D.Lang)  " __DATE__ "\n");
  621.  
  622.     *drive = *getcwd(cdrive, sizeof(cdrive));  // get current drive/path
  623.  
  624.     for (i = 1; i < argc; i++)                 // check each argument
  625.     {
  626.         strupr(p = argv[i]);                   // uppercase argument
  627.  
  628.         if (p[0] && p[1] == ':')               // q. drive parm specified?
  629.         {
  630.             *drive = *p;                       // a. yes .. setup drive
  631.  
  632.             if (p[2] == '-' || p[2] == '/')    // option with drive letter
  633.                argv[argc++] = p+2;
  634.         }
  635.         else
  636.         {                                      // search arguments
  637.             for (; *p; p++)
  638.             {
  639.                 if (*p == '/' || *p == '-')
  640.                 {
  641.                   switch (*++p)
  642.                   {
  643.                       case '%':                   // /% option
  644.                       case 'N':                   // /N option
  645.                       case 'E':                   // /E option
  646.                       case 'F':                   // /F option
  647.                           option = *p;            // set up the option value
  648.                           break;                  // exit switch
  649.  
  650.                       case 'D':                   // /D switch
  651.                           detail = 1;             // .. show detail listing
  652.                           break;
  653.  
  654.                       case 'L':                   // /L switch
  655.                           list = 1;               // .. show listing
  656.                           break;
  657.  
  658.                       case 'V':                   // /V switch
  659.                           verbose = 1;            // .. show verbose listing
  660.                           break;
  661.  
  662.                       default:                    // error
  663.                           helpme = 1;             // argument in error
  664.                           break;                  // .. error
  665.                   }
  666.                 }
  667.                 else
  668.                   helpme++;
  669.             }
  670.         }
  671.     }
  672.  
  673.     if (helpme)                                     // q. any error?
  674.     {                                               // a. yes .. handle error
  675.         printf("\n\tformat\tShowFrag [d:] [options]\n\n"
  676.           "\twhere\td: is the drive to check for fragmentation\n"
  677.           "\tOptions (/x or -x):\n"
  678.           "\t\t%% = sets errorlevel as a percentage\n"
  679.           "\t\tN = sets errorlevel to number of fragmented files (max 254)\n"
  680.           "\t\tE = sets errorlevel number of extra sections (max 254)\n"
  681.           "\t\tF = sets errorlevel number of free space areas (max 254)\n"
  682.           "\t\tD = List fragmented files with cluster gaps, *=bad cluster skip\n"
  683.           "\t\tL = List fragmented files\n"
  684.           "\t\tV = Verbose file listing\n");
  685.  
  686.         return (FALSE);
  687.     }
  688.  
  689.     if (i = chkdrv(*drive))                // check drive, version err
  690.     {
  691.         printf("Error: %s", errors[--i]);  // display any error
  692.         return (FALSE);                    // tell the batch job
  693.     }
  694.  
  695.     drive_handle = DosOpenDrive(*drive);
  696.     if (drive_handle == -1)
  697.     {
  698.         printf(cantopen, *drive);
  699.         return FALSE;
  700.     }
  701.  
  702.     if (get_fat(*drive, drive_handle) == FALSE)  // read FAT into memory
  703.     {                                      // error
  704.         hfree(fat);
  705.         return (FALSE);                    // tell the batch job
  706.     }
  707.  
  708.     DosSetDrive(*drive);
  709.     dir_search(drive, 0);                  // search for files
  710.  
  711.     check_unlinked();                      // check unlinked clusters
  712.  
  713.     hfree(fat);
  714.     DosSetDrive(*cdrive);
  715.     chdir(cdrive);
  716.  
  717.     DosCloseDrive(drive_handle);
  718.  
  719.     if (files + dirs)                       // q. any files and dirs?
  720.         pf = ((long)frag * 100L) /  (files + dirs);  // a. yes .. % files frag'd
  721.  
  722.     if (!pf && frag)                        // q. something frag'd
  723.         pf = 1;                             // a. yes .. show non-zero
  724.  
  725.     // Report some info to user.
  726.  
  727.     printf("\n  Analysis summary: \n");
  728.     printf("    %d Files, %d Directories,\n",  files, dirs);
  729.     printf("    %d Unfragmented, %d Fragmented, %d Extra Sections, %d Free Spaces\n",
  730.       unfrag, frag, sections, freespaces);
  731.     printf("    %d Bad clusters, %d%% of files are fragmented\n\n", bad_spots, pf);
  732.  
  733.     switch (option)  // return w/errorlevel
  734.     {
  735.         case 'N':  // files return
  736.             rc  = frag;
  737.             msg = rc_type[1];
  738.             break;
  739.  
  740.         case 'E':  // extra sections return
  741.             rc  = sections;
  742.             msg = rc_type[2];
  743.             break;
  744.  
  745.         case 'F':  // freespace areas
  746.             rc  = freespaces;
  747.             msg = rc_type[3];
  748.             break;
  749.  
  750.         default:   // percentage return
  751.             rc  = pf;
  752.             msg = rc_type[0];
  753.             break;
  754.  
  755.     }  /* switch option */
  756.  
  757.     if (pf == 0)      // q. no fragments?
  758.         i = 0;        // a. yes .. tell 'em
  759.     else if (pf < 11) // q. little fragmentation?
  760.         i = 1;        // a. yes .. set index
  761.     else if (pf < 76) // q. moderate fragm'tion
  762.         i = 2;        // a. yes .. setup msg
  763.     else
  764.         i = 3;        // ..push the button, Jim
  765.  
  766.     printf("  Suggestion:\n    %s\n\n", suggestion[i]);
  767.     printf("  Return code is:\n     %s=%u\n", msg, rc);
  768.  
  769.     return (rc > 254 ? 254 : rc);         // return w/errorlevel
  770. }
  771.