home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / sysutils / showfrag / showfrag.c < prev   
C/C++ Source or Header  |  1994-02-04  |  28KB  |  712 lines

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