home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 165 / CHKFRA.ZIP / CHKFRAG.C < prev    next >
Text File  |  1992-03-11  |  46KB  |  1,002 lines

  1. /***************************************************
  2.  *                                                 *
  3.  *      chkfrag - check disk for fragmentation     *
  4.  *                                                 *
  5.  ***************************************************/
  6.  
  7. /* ******************************************************************** *
  8.  
  9.     Maintenance log
  10.  
  11.     Version   Date   Description                            Who
  12.     ------- -------- -------------------------------------- ----------
  13.     1.0 to 1.3       Various bug fixes                      Flanders/Holmes
  14.     1.3
  15.  
  16.     1.4     16Dec91  DOS 5 compatibility                    Flanders/Holmes
  17.                       .. Read FAT greater than 64K long
  18.                       .. Minor compiler error on readlabel()
  19.                       .. Compiles under MSC 6.0
  20.                       .. Added pack pragma to remove need for
  21.                          /Zp operand on compile
  22.                       (Thanks to J. Stephen Myers for his fixes
  23.                        to v1.2)
  24.  
  25.     1.5     15Jan92   Two unrelated bugs ..                 Flanders
  26.                       .. /F returned EXTRA SEGMENT value.
  27.                          Now returns free segments.
  28.                       .. Didn't check SUBST under DOS 5
  29.                       (Thanks to Bruce Vrana for reporting
  30.                        these bugs!)
  31.  
  32.     1.6     16Jan92   Didn't recognize a LANTASTIC local    Flanders/Holmes
  33.                          shared drive as a network drive
  34.  
  35.     1.7     11Mar92   Miscalulated 12 or 16 bit fat fixed   Flanders/Holmes
  36.  
  37.  * ******************************************************************** */
  38.  
  39.  
  40. #pragma pack(1)                             /* Pack to byte alignment   */
  41.  
  42. #include <stdio.h>                          /* standard library         */
  43. #include <dos.h>                            /* dos access and registers */
  44. #include <malloc.h>                         /* memory allocation        */
  45. #include <stdlib.h>                         /* common lib modules       */
  46.  
  47. #define UINT unsigned int                   /* unsigned integer type    */
  48. #define ULONG unsigned long                 /* unsigned long type       */
  49. #define NOT !                               /* logical not              */
  50. #define BOOT b_rec                          /* boot record shorthand    */
  51. #define FILES (_A_SYSTEM | _A_HIDDEN | _A_SUBDIR)   /* files type       */
  52. #define LABEL (_A_VOLID)                            /* label type       */
  53. #define CLEAR(s,c) strclr(s,c,sizeof(s))    /* string clear             */
  54.  
  55. /*
  56.  *  Globals
  57.  */
  58.  
  59. char    huge *fat,                      /* address of FAT               */
  60.         cdrive[66],                     /* startup drive and path       */
  61.         fat_16;                         /* true if 16 bit FAT entries   */
  62.  
  63. int     sections = 0,                   /* file sections                */
  64.         secsize = 0 ,                   /* sector size of drive         */
  65.         frag = 0,                       /* fragmented files             */
  66.         unfrag = 0,                     /* unfragmented files           */
  67.         freespaces = 0,                 /* freespaces                   */
  68.         files = 0,                      /* processed files              */
  69.         dirs = 0,                       /* processed directories        */
  70.         dos4 = 0,                       /* use dos version 4 int24      */
  71.         list = 0;                       /* list frag'd files switch     */
  72.  
  73. UINT    nclusters,                      /* number of clusters           */
  74.         sdrive;                         /* startup drive                */
  75.  
  76. long    nsectors;                       /* number of sectors on drive   */
  77.  
  78. /*
  79.  *  formal declarations
  80.  */
  81.  
  82. void    get_fat(int),                   /* read FAT into memory         */
  83.         cfexit(int),                    /* exit routine                 */
  84.         check_frag(char *, UINT, int),  /* check if file/dir is frag'd  */
  85.         dir_search(char *),             /* search directory             */
  86.         strclr(char *, int, int),       /* clear a string               */
  87.         check_unlinked();               /* check for unlinked clusters  */
  88. int     chkdrv(char);                   /* check local, SUBST/ASSIGN    */
  89. char    *fname(char *, char*),          /* fcb filename to normal fname */
  90.         *readlabel(int),                /* read the drive label         */
  91.         *translate_name(char far *);    /* translate name               */
  92. long    next_cluster(UINT, int, int *); /* find the next cluster in FAT */
  93.  
  94. struct  fcb
  95.     {
  96.     char    hexff;                      /* extended fcb first byte      */
  97.     char    extra[5];                   /* extended fcb work area       */
  98.     char    attrib;                     /* extended fcb attribute       */
  99.     char    drive;                      /* fcb - drive                  */
  100.     char    filename[8];                /* fcb - filename               */
  101.     char    ext[3];                     /* fcb - extension              */
  102.     unsigned
  103.     int     block;                      /* fcb - block number           */
  104.     unsigned
  105.     long    filesize;                   /* fcb - file size              */
  106.     int     date;                       /* fcb - file date              */
  107.     char    system[10];                 /* fcb - reserved area          */
  108.     char    record;                     /* fcb - current record         */
  109.     unsigned
  110.     long    rnd_recno;                  /* fcb - random record number   */
  111.     } ;
  112.  
  113. /************************************************************************
  114.  *                                                                      *
  115.  *      mainline                                                        *
  116.  *                                                                      *
  117.  ************************************************************************/
  118.  
  119. main(argc, argv)
  120. int     argc;                           /* count of arguments           */
  121. char    *argv[];                        /* argument strings             */
  122. {
  123. long    pf = 0;                         /* percent fragmented           */
  124. UINT    rc;                             /* return code                  */
  125. int     ctc = 0,                        /* return code chosen           */
  126.         dc = 0,                         /* drive chosen                 */
  127.         pe = 0,                         /* parm in error                */
  128.         i, j,                           /* loop counter, work           */
  129.         option = 0;                     /* program option               */
  130. char    *p;                             /* work pointer                 */
  131. static
  132. char    drive[] = " :\\",               /* drive and path to check      */
  133.         *rc_type[] =
  134.             { "Percentage",
  135.               "Number of Files",
  136.               "Number of Extra Segments",
  137.               "Number of Free Areas" },
  138.  
  139.         *suggestion[] =
  140.             { "No fragmentation found -- Defrag unnecessary",
  141.               "Little fragmentation -- Defrag optional",
  142.               "Moderate fragmentation -- Defrag should be performed soon",
  143.               "Fragmentation critical -- Defrag or Backup/Format/Restore" },
  144.  
  145.         *errors[] =
  146.             { "Invalid drive specified",
  147.               "Cannot CHKFRAG a network drive",
  148.               "Cannot CHKFRAG a SUBST'd or ASSIGN'd drive",
  149.               "Must run with DOS 2.0 or greater" },
  150.  
  151.         *options[] =
  152.              { "/%", "/N", "/E", "/F", "/L", "/4"} ;
  153.  
  154. printf("CHKFRAG 1.7 -- Copyright (c) 1992 Ziff Communications Co.\n%s%c%s\n",
  155.        "PC Magazine ", 254, " Bob Flanders & Michael Holmes\n");
  156.  
  157.  
  158. _dos_getdrive(&sdrive);                     /* get the default drive    */
  159. *drive = sdrive + 'A' - 1;                  /* ..setup default drive    */
  160.  
  161.  
  162. for (i = 1; i < argc; i++)                  /* check each argument      */
  163.     {
  164.     strupr(p = argv[i]);                    /* uppercase argument       */
  165.  
  166.     if (strlen(p) == 2 && p[1] == ':')      /* q. drive parm specified? */
  167.         {
  168.         *drive = *p;                        /* a. yes .. setup drive    */
  169.         dc++;                               /* .. show drive selected   */
  170.         }
  171.  
  172.      else
  173.         {
  174.         for (j = 0; strcmp(p, options[j])   /* search arguments         */
  175.                     && (j < 6); j++);
  176.  
  177.         switch(j)                           /* based on argument        */
  178.             {
  179.             case 0:                         /* /% option                */
  180.             case 1:                         /* /N option                */
  181.             case 2:                         /* /E option                */
  182.             case 3:                         /* /F option                */
  183.  
  184.                 option = j;                 /* set up the option value  */
  185.  
  186.                 ctc++;                      /* increment code type count*/
  187.                 break;                      /* exit switch              */
  188.  
  189.             case 4:                         /* /L switch                */
  190.                 list++;                     /* .. show listing wanted   */
  191.                 break;
  192.  
  193.             case 5:                         /* /4 switch                */
  194.                 dos4++;                     /* .. use version 4 int25   */
  195.                 break;
  196.  
  197.             case 6:                         /* error                    */
  198.                 pe = j;                     /* argument in error        */
  199.                 break;                      /* .. error                 */
  200.             }
  201.         }
  202.     }
  203.  
  204. if (pe || (ctc > 1) || (list>1) || (dc > 1))/* q. any error?            */
  205.     {                                       /* a. yes .. handle error   */
  206.     printf("\n\tformat\tCHKFRAG  [d:] [/%% | /N | /E] [/L]\n\n");
  207.     printf("\twhere\td: is the drive to check for fragmentation\n");
  208.     printf("\t\t/%% sets errorlevel as a percentage\n");
  209.     printf("\t\t/N sets errorlevel to number of fragmented files (max 254)\n");
  210.     printf("\t\t/E sets errorlevel number of extra sections (max 254)\n");
  211.     printf("\t\t/F sets errorlevel number of free space areas (max 254)\n");
  212.     printf("\t\t/L causes fragmented files to be listed\n");
  213.     printf("\t\t/4 causes DOS 4.0 interface to be used\n");
  214.     cfexit(255);
  215.     }
  216.  
  217. _dos_setdrive((*drive-'A')+1, &i);          /* set up the work drive    */
  218. getcwd(cdrive, sizeof(cdrive));             /* get current drive/path   */
  219.  
  220. if (i = chkdrv(*drive))                     /* check drive, version err */
  221.     {
  222.     printf("Error: %s", errors[--i]);       /* display any error        */
  223.     cfexit(255);                            /* tell the batch job       */
  224.     }
  225.  
  226. get_fat(*drive - 'A');                      /* read FAT into memory     */
  227. dir_search(drive);                          /* search for files         */
  228. check_unlinked();                           /* check unlinked clusters  */
  229.  
  230. if (files + dirs)                           /* q. any files and dirs?   */
  231.     pf = ((long) frag * 100L) /             /* a. yes .. % files frag'd */
  232.                           (files + dirs);
  233.  
  234. if (!pf && frag)                            /* q. something frag'd      */
  235.     pf = 1;                                 /* a. yes .. show non-zero  */
  236.  
  237. printf("\n%d Files, %d Directories,\n",     /* report to user           */
  238.             files, dirs);
  239. printf("%d Unfragmented, %d Fragmented, %d Extra Sections, %d Free Spaces\n",
  240.             unfrag, frag, sections, freespaces);
  241. printf("%d%% of files are fragmented\n\n", pf);
  242.  
  243. switch(option)                              /* return w/errorlevel      */
  244.     {
  245.     case 0:                                 /* percentage return        */
  246.         rc = pf;
  247.         break;
  248.  
  249.     case 1:                                 /* files return             */
  250.         rc = frag;
  251.         break;
  252.  
  253.     case 2:                                 /* extra sections return    */
  254.         rc = sections;
  255.         break;
  256.  
  257.     case 3:                                 /* freespace areas          */
  258.         rc = freespaces;
  259.     }
  260.  
  261. if (pf == 0)                                /* q. no fragments?         */
  262.     i = 0;                                  /* a. yes .. tell 'em       */
  263.  
  264.  else if (pf < 11)                          /* q. little fragmentation? */
  265.     i = 1;                                  /* a. yes .. set index      */
  266.  
  267.  else if (pf < 76)                          /* q. moderate fragm'tion   */
  268.     i = 2;                                  /* a. yes .. setup msg      */
  269.  
  270.  else
  271.     i = 3;                                  /* ..push the button, Jim   */
  272.  
  273. printf("%s%s\nCHKFRAG finished, Return code %d\n\n%s%s\n",
  274.         "Return type chosen: ", rc_type[option], rc,
  275.         "Suggestion:\n     ", suggestion[i]);
  276.  
  277. cfexit(rc > 254 ? 254 : rc);;               /* return w/errorlevel      */
  278. }
  279.  
  280. /************************************************************************
  281.  *                                                                      *
  282.  *      get_fat -- read boot record and fat into memory                 *
  283.  *                                                                      *
  284.  ************************************************************************/
  285.  
  286. void    get_fat(drv)
  287. int     drv;                            /* drive number                 */
  288. {
  289. UINT    i;                              /* work                         */
  290. long    max_secs,                       /* Maximum sectors to read      */
  291.         next_sector,                    /* next sector to start read at */
  292.         num_secs;                       /* number of sectors to read    */
  293. char    huge *fat_ptr;                  /* pointer into fat buffer      */
  294. int     rc;                             /* return code work area        */
  295. union   REGS r;                         /* work registers               */
  296. struct  SREGS s;                        /* ..and work segment regs      */
  297. struct  bootrec
  298.         {
  299.         char jmp[3],                    /* jump instruction             */
  300.              oem[8];                    /* OEM name                     */
  301.         UINT bytes;                     /* bytes per sector             */
  302.         char cluster;                   /* sectors per cluster          */
  303.         UINT res_sectors;               /* reserved sectors             */
  304.         char fats;                      /* number of fats               */
  305.         UINT roots,                     /* number of root dir entries   */
  306.              sectors;                   /* total sectors                */
  307.         char media;                     /* media descriptor block       */
  308.         UINT fatsize,                   /* sectors per fat              */
  309.              tracksize,                 /* sectors per track            */
  310.              heads;                     /* number of heads              */
  311.         long hidden,                    /* hidden sectors               */
  312.              sectors_32;                /* sectors if above 32Mb        */
  313.         } far *b_rec;                   /* boot record definition       */
  314.  
  315. struct  dos4_i25                        /* dos 4.0 int 25 block         */
  316.         {
  317.         long sector;                    /* sector to read               */
  318.         int  num_secs;                  /* number of sectors to read    */
  319.         char far *read_addr;            /* address of input area        */
  320.         } d4_i25, far *d4_i25p;         /* area and pointer             */
  321.  
  322. char *nomem = "Not enough memory for processing\n";
  323.  
  324. r.h.ah = 0x36;                              /* ah = get freespace       */
  325. r.h.dl = drv + 1;                           /* get drive                */
  326. int86(0x21, &r, &r);                        /* r.x.cx = bytes/sector    */
  327.  
  328. if ((BOOT = (struct bootrec far *) malloc(r.x.cx)) == NULL)
  329.     {                                       /* q. no memory?            */
  330.     printf(nomem);                          /* a. yes .. give           */
  331.     cfexit(255);                            /* ..error msg/exit         */
  332.     }
  333.  
  334. if (dos4)                                   /* dos version 4 interface? */
  335.     {
  336.     r.x.cx = -1;                            /* cx = 0xffff              */
  337.     d4_i25.sector = 0L;                     /* read sector 0            */
  338.     d4_i25.num_secs = 1;                    /* .. for 1 sector          */
  339.     d4_i25.read_addr = (char far *) BOOT;   /* .. into boot record      */
  340.     d4_i25p = &d4_i25;                      /* set up pointer           */
  341.     r.x.bx = FP_OFF(d4_i25p);               /* bx = offset of parm block*/
  342.     s.ds   = FP_SEG(d4_i25p);               /* ds = segment of block    */
  343.     }
  344.  else
  345.     {
  346.     r.x.cx = 1;                             /* cx = number of sectors   */
  347.     r.x.dx = 0;                             /* dx = starting sector     */
  348.     r.x.bx = FP_OFF(BOOT);                  /* bx = offset of buffer    */
  349.     s.ds   = FP_SEG(BOOT);                  /* ds = segment of buffer   */
  350.     }
  351.  
  352. r.h.al = drv;                               /* al = drive number        */
  353. int86x(0x25, &r, &r, &s);                   /* read boot sector         */
  354.  
  355. if (r.x.cflag)                              /* q. error reading disk?   */
  356.     {
  357.     printf("Error reading boot record\n");  /* a. yes .. give error msg */
  358.     cfexit(255);                            /* ..and return to DOS      */
  359.     }
  360.  
  361. nsectors = (BOOT->sectors ? (long) BOOT->sectors : BOOT->sectors_32);
  362.  
  363. if ((fat = (char huge *) halloc((long) BOOT->fatsize * (long) BOOT->bytes, 1))
  364.                              == (char huge *) NULL)
  365.     {                                       /* q. no memory?            */
  366.     printf(nomem);                          /* a. yes .. give           */
  367.     cfexit(255);                            /* ..error msg/exit         */
  368.     }
  369.  
  370. if (dos4)                                   /* dos version 4 interface? */
  371.     {
  372.     max_secs = 65536L / BOOT->bytes;        /* max we can read/int 25   */
  373.     fat_ptr = (char far *) fat;             /* initial offset in table  */
  374.     next_sector = BOOT->res_sectors;        /* get first sector of FAT  */
  375.     d4_i25p = &d4_i25;                      /* set up pointer           */
  376.  
  377.     for (num_secs = BOOT->fatsize;          /* get number of secs to rd */
  378.             num_secs;)                      /* while there are some left*/
  379.         {
  380.         r.x.cx = -1;                        /* cx = 0xffff              */
  381.  
  382.         d4_i25.sector = next_sector;        /* read FAT area            */
  383.         d4_i25.read_addr = fat_ptr;         /* point at target of read  */
  384.  
  385.         num_secs -=                         /* size of next read ...    */
  386.             (d4_i25.num_secs =              /* .. size of this read     */
  387.                    min(max_secs, num_secs));/* .. .. smaller of max, num*/
  388.  
  389.         next_sector += d4_i25.num_secs;     /* Calc next sector number  */
  390.  
  391.         fat_ptr += (long) d4_i25.num_secs * /* Calc next buffer address */
  392.                         BOOT->bytes;
  393.  
  394.         r.x.bx = FP_OFF(d4_i25p);           /* bx = offset of parm block*/
  395.         s.ds   = FP_SEG(d4_i25p);           /* ds = segment of block    */
  396.         r.h.al = drv;                       /* al = drive number        */
  397.  
  398.         int86x(0x25, &r, &r, &s);           /* read boot sector         */
  399.         }
  400.     }
  401.  else
  402.     {
  403.     r.x.cx = BOOT->fatsize;                 /* cx = number of sectors   */
  404.     r.x.dx = BOOT->res_sectors;             /* dx = starting sector     */
  405.     r.x.bx = FP_OFF(fat);                   /* bx = offset of buffer    */
  406.     s.ds   = FP_SEG(fat);                   /* ds = segment of buffer   */
  407.     r.h.al = drv;                           /* al = drive number        */
  408.     int86x(0x25, &r, &r, &s);               /* read boot sector         */
  409.     }
  410.  
  411. if (r.x.cflag)                              /* q. error reading disk?   */
  412.     {
  413.     printf("%02.2x %02.2x Error reading FAT\n",
  414.                   r.h.ah, r.x.di);          /* a. yes .. give error msg */
  415.     cfexit(255);                            /* ..and return to DOS      */
  416.     }
  417.  
  418. nclusters = (nsectors - (BOOT->res_sectors
  419.                 + (BOOT->fatsize * BOOT->fats)
  420.                 + ((BOOT->roots * 32) / BOOT->bytes)))
  421.                 / BOOT->cluster;
  422.  
  423. fat_16 = nclusters > 4086;                  /* set if 16bit FAT tbl */
  424.  
  425. printf("Drive %c:%s %lu Sectors, %u Clusters, %u Clustersize\n",
  426.         drv + 'A', readlabel(drv + 'A'),
  427.         nsectors,
  428.         nclusters,
  429.         BOOT->cluster * BOOT->bytes);
  430.  
  431. printf("\nChecking disk structure ..");
  432.  
  433. for(i = 2; i < nclusters;)              /* look for freespaces          */
  434.     {
  435.     if (next_cluster(i, 2, &rc) == 0)   /* q. free?                     */
  436.         {
  437.         freespaces++;                   /* a. yes. increment free count */
  438.  
  439.         while ((next_cluster(i, 2, &rc) == 0) && ( i < nclusters) )
  440.             i++;                        /* skip free spaces             */
  441.  
  442.         }
  443.      else
  444.         i++;                            /* else .. check next cluster   */
  445.      }
  446. }
  447.  
  448.  
  449. /************************************************************************
  450.  *                                                                      *
  451.  *      check_frag -- check a file/directory for fragmentation          *
  452.  *                                                                      *
  453.  ************************************************************************/
  454.  
  455. void    check_frag(s, n, dflag)
  456. char    *s;                             /* file/directory name          */
  457. UINT    n;                              /* starting cluster number      */
  458. int     dflag;                          /* directory flag               */
  459. {
  460. UINT    i, j;                           /* working storage              */
  461. long    nc;                             /* next cluster                 */
  462. int     flag = 0,                       /* flag for frag'd file         */
  463.         rc;                             /* error return code            */
  464.  
  465.  
  466. for(; nc = next_cluster(n, 1, &rc); n = nc) /* walk down the chain      */
  467.     {
  468.     if (nc < 0)                             /* q. invalid cluster?      */
  469.         {
  470.         printf("\n\t%s -- %s%s\n%s\n",      /* a. yes .. give err msg   */
  471.                 s, rc ? "Invalid cluster detected"
  472.                       : "File cross-linked",
  473.                 ", Run aborted",
  474.                 "\n\t** Please run CHKDSK **");
  475.         cfexit(255);                        /* ..and exit w/error code  */
  476.         }
  477.  
  478.     if ((n + 1) != nc)                      /* q. non-contiguous area?  */
  479.         {
  480.         flag++;                             /* show fragmented file     */
  481.  
  482.         if (nc > n)                         /* q. possibly bad cluster? */
  483.             {
  484.             for (j = n + 1;                 /* check for bad spots      */
  485.                  next_cluster(j, 0, &rc) == 0xfff7 && j < nc;
  486.                  j++);
  487.  
  488.             if (j == i)                     /* q. was entire area bad?  */
  489.                 flag--;                     /* a. yes .. don't report   */
  490.  
  491.              else
  492.                 sections++;                 /* incr files sections count*/
  493.             }
  494.  
  495.          else
  496.             sections++;                     /* incr files sections count*/
  497.         }
  498.     }
  499.  
  500. if (flag)                                   /* q. fragmented file       */
  501.     {
  502.     if (NOT frag && list)                   /* q. first frag file?      */
  503.         printf("\nFragmented Files/Directories:\n");
  504.  
  505.     if (list)                               /* q. list frag'd files?    */
  506.         printf("%s%s\n",                    /* a. yes .. give it to them*/
  507.                 dflag ? "DIR> " : "     ", s);
  508.  
  509.     frag++;                                 /* accumulate frag'd count  */
  510.     }
  511.  
  512.  else
  513.     unfrag++;                               /* else total unfrag'd files*/
  514.  
  515. }
  516.  
  517.  
  518. /************************************************************************
  519.  *                                                                      *
  520.  *      next_cluster -- return next cluster number from FAT             *
  521.  *                                                                      *
  522.  ************************************************************************/
  523.  
  524. long    next_cluster(n, x, rc)
  525. UINT    n;                              /* current cluster number       */
  526. int     x,                              /* flag, 1 = reset FAT entry    */
  527.         *rc;                            /* error return code            */
  528. {
  529. ULONG   e;                              /* entry number in FAT          */
  530. long    nc;                             /* next cluster value           */
  531. UINT    huge *p,                        /* pointer for 16 bit FAT entry */
  532.         mask1, mask2;                   /* mask for and'ing and or'ing  */
  533. int     flag;                           /* shift/and flag               */
  534.  
  535.  
  536. *rc = 0;                                    /* clear return code        */
  537. nc = n;                                     /* initialize nc            */
  538.  
  539. if (! (e = nc))                             /* q. invalid cluster nbr   */
  540.     return(0);                              /* a. yes .. rtn EOF        */
  541.  
  542. if (fat_16)                                 /* q. 16 bit FAT entries?   */
  543.     {
  544.     p = (UINT huge *) &fat[0];              /* a. yes .. get FAT addr   */
  545.     nc = p[e];                              /* retrieve next entry      */
  546.  
  547.     if (x == 2)                             /* q. return value?         */
  548.         return(nc);                         /* a. yes .. return it      */
  549.  
  550.     if (NOT nc)                             /* q. unallocated cluster?  */
  551.         {
  552.         nc = -1;                            /* a. yes .. error condition*/
  553.         *rc = 1;                            /* set return code          */
  554.         }
  555.  
  556.     if (x == 1)                             /* q. need to reset entry?  */
  557.         p[e] = 1;                           /* a. yes .. show processed */
  558.  
  559.     if (nc >= 0xfff0 && nc != 0xfff7)       /* q. reserved and not bad  */
  560.         nc = 0;                             /* a. yes .. show EOF       */
  561.     }
  562.  
  563.  else
  564.     {
  565.     e = (nc << 1) + nc;                     /* cluster number * 3       */
  566.     flag = e & 1;                           /* need to do shift later?  */
  567.     e >>= 1;                                /* cluster number * 1.5     */
  568.     nc = *(UINT huge *) &fat[e];            /* get next cluster         */
  569.  
  570.     if (flag)                               /* q. need to do shift?     */
  571.         {
  572.         nc >>= 4;                           /* a. yes .. shift by 4 bits*/
  573.         mask1 = 0x000f;                     /* mask to clear upper bits */
  574.         mask2 = 0x0010;                     /* ..and footprint mask     */
  575.         }
  576.  
  577.      else
  578.         {
  579.         nc &= 0xfff;                        /* else .. strip upper bits */
  580.         mask1 = 0xf000;                     /* mask to clear lower bits */
  581.         mask2 = 0x0001;                     /* ..and footprint mask     */
  582.         }
  583.  
  584.     if (x == 2)                             /* q. return value?         */
  585.         return(nc);                         /* a. yes .. return value   */
  586.  
  587.     if (NOT nc)                             /* q. unallocated cluster?  */
  588.         {
  589.         nc = -1;                            /* a. yes .. error condition*/
  590.         *rc = 1;                            /* set return code          */
  591.         }
  592.  
  593.     if (x == 1)                             /* q. need to reset entry?  */
  594.         {
  595.         *(UINT huge *) &fat[e] &= mask1;    /* a. yes .. 'and' off bits */
  596.         *(UINT huge *) &fat[e] |= mask2;    /* ..and put down footprint */
  597.         }
  598.  
  599.     if (nc >= 0xff0)                        /* q. EOF/reserved range?   */
  600.         if (nc == 0xff7)                    /* q. bad cluster?          */
  601.             nc = 0xfff7;                    /* a. yes .. show bad one   */
  602.          else
  603.             nc = 0;                         /* else .. show EOF         */
  604.     }
  605.  
  606. return(nc);
  607. }
  608.  
  609. /************************************************************************
  610.  *                                                                      *
  611.  *      check_unlinked -- check for unlinked clusters                   *
  612.  *                                                                      *
  613.  ************************************************************************/
  614.  
  615.  
  616. void    check_unlinked()
  617. {
  618. int     rc;                             /* error return code            */
  619. UINT    i;                              /* loop counter                 */
  620. long    j;                              /* work return cluster nbr      */
  621.  
  622.  
  623. for (i = 2; i < nclusters; i++)             /* check thru entire FAT    */
  624.     {
  625.     if ((j = next_cluster(i, 2, &rc)) != 0  /* q. unallocated cluster?  */
  626.                 && j != 1                   /* ..or used                */
  627.                 && j != 0xfff7)             /* ..or bad cluster?        */
  628.         {
  629.         printf("\nLost clusters detected, %s%s",/* a. no .. give msg    */
  630.                 "Run aborted\n",
  631.                 "\t** Please run CHKDSK **\n");
  632.         cfexit(255);                            /* ..and exit w/error   */
  633.         }
  634.     }
  635. }
  636.  
  637.  
  638. /************************************************************************
  639.  *                                                                      *
  640.  *      dir_search -- recursively search all files & subdirectories     *
  641.  *                                                                      *
  642.  ************************************************************************/
  643.  
  644. void    dir_search(base_dir)
  645. char    *base_dir;                      /* base subdirectory to search  */
  646. {
  647. int     oldds,                          /* old dta segment              */
  648.         oldda;                          /* old dta address              */
  649. char    pass,                           /* pass number                  */
  650.         work_dir[65],                   /* work directory               */
  651.         first_done;                     /* find first done              */
  652. struct  fcb find_work;                  /* fcb work area                */
  653.  
  654. /*
  655.  *  The following areas are STATIC .. not allocated on recursion
  656.  */
  657.  
  658. static
  659. struct  SREGS   s;                      /* segment registers            */
  660.  
  661. static
  662. union   REGS    r;                      /* other registers              */
  663.  
  664. static
  665. char    far *cftmp,                     /* work pointer                 */
  666.         *bc = "\\|/-";                  /* bar characters to use        */
  667.  
  668. static
  669. int     rc,                             /* work return code             */
  670.         bar = 0;                        /* next bar character           */
  671.  
  672. static
  673. union
  674.     {
  675.     char    dtabuff[128];               /* dta area                     */
  676.  
  677.     struct                              /* Disk transfer area layout    */
  678.         {
  679.         char dta1[6];                   /* first part of dta            */
  680.         char attrib;                    /* attribute byte               */
  681.         char drive;                     /* drive                        */
  682.         char filename[8];               /* filename                     */
  683.         char ext[3];                    /* extension                    */
  684.         char d_attrib;                  /* directory attribute          */
  685.         char dta2[10];                  /* more reserved space          */
  686.         unsigned
  687.         int  d_time;                    /* directory time               */
  688.         unsigned
  689.         int  d_date;                    /* directory date               */
  690.         unsigned
  691.         int  d_cluster;                 /* first cluster                */
  692.         unsigned
  693.         long d_filesize;                /* size of file                 */
  694.         } dta;
  695.     } dta;
  696.  
  697. /*
  698.  *  End of static area
  699.  */
  700.  
  701. r.h.ah = 0x2f;                              /* ah = get dta             */
  702. int86x(0x21, &r, &r, &s);                   /* .. ask DOS               */
  703.  
  704. oldds = s.es;                               /* save old DTA segment     */
  705. oldda = r.x.bx;                             /* .. and offset            */
  706.  
  707. cftmp = (char far *) &dta;                  /* get current fcb address  */
  708.  
  709. r.h.ah = 0x1a;                              /* ah = set DTA             */
  710. s.ds = FP_SEG(cftmp);                       /* ds -> DTA segment        */
  711. r.x.dx = FP_OFF(cftmp);                     /* ds:dx -> DTA             */
  712. int86x(0x21, &r, &r, &s);                   /* setup new DTA            */
  713.  
  714. if (strcmp(base_dir, translate_name(base_dir))) /* q. JOIN'd?           */
  715.     return;                                     /* a. yes .. skip it    */
  716.  
  717. chdir(base_dir);                            /* get the base directory   */
  718.  
  719. for(first_done=pass=0;;)                    /* look through current dir */
  720.     {
  721.     if (first_done == 0)                    /* q. find first done?      */
  722.         {                                   /* a. no .. do it           */
  723.  
  724.         if (base_dir[1] == ':')             /* q. disk specified?       */
  725.             find_work.drive =               /* a. yes .. set fcb drive  */
  726.                     ((base_dir[0] & 0xdf) - 'A') + 1;
  727.          else
  728.             find_work.drive = 0;            /* else use default         */
  729.  
  730.         find_work.hexff = 0xff;             /* extended fcb             */
  731.         CLEAR(find_work.extra, 0);          /* set extra area           */
  732.         CLEAR(find_work.filename, '?');     /* .. and file name         */
  733.         CLEAR(find_work.ext, '?');          /* .. and extension         */
  734.         find_work.attrib = FILES;           /* set up attribute to find */
  735.  
  736.         r.h.ah = 0x11;                      /* ah = find first          */
  737.         cftmp = (char far *) &find_work;    /* get pointer to work fcb  */
  738.         s.ds = FP_SEG(cftmp);               /* ds -> segment of fcb     */
  739.         r.x.dx = FP_OFF(cftmp);             /* ds:dx -> offset          */
  740.         int86x(0x21, &r, &r, &s);           /* .. find first            */
  741.  
  742.         rc = r.h.al;                        /* get return code          */
  743.  
  744.         first_done = 1;                     /* first find done          */
  745.         }
  746.  
  747.      else
  748.         {
  749.         r.h.ah = 0x12;                      /* ah = find next           */
  750.         cftmp = (char far *) &find_work;    /* get pointer to work fcb  */
  751.         s.ds = FP_SEG(cftmp);               /* ds -> segment of fcb     */
  752.         r.x.dx = FP_OFF(cftmp);             /* ds:dx -> offset          */
  753.         int86x(0x21, &r, &r, &s);           /* .. find first            */
  754.  
  755.         rc = r.h.al;                        /* get return code          */
  756.         }
  757.  
  758.     if (NOT list)                           /* q. list in progress?     */
  759.         {                                   /* a. no ..                 */
  760.         fprintf(stderr, "%c%c", bc[bar], 8);/* print bar, backspace     */
  761.         if (++bar > 3)                      /* q. limit on chars?       */
  762.             bar = 0;                        /* a. yes .. reset to zero  */
  763.         }
  764.  
  765.     strcpy(work_dir, base_dir);             /* get current base         */
  766.  
  767.     if (work_dir[strlen(work_dir)-1] != '\\')   /* if needed ..         */
  768.          strcat(work_dir, "\\");                /* .. add a backslash   */
  769.  
  770.     strcat(work_dir,                        /* .. add the name          */
  771.          fname(dta.dta.filename, dta.dta.ext));
  772.  
  773.     if (pass)                               /* q. second pass?          */
  774.         {
  775.         if (rc)                             /* q. more files found?     */
  776.             break;                          /* a. no .. exit            */
  777.  
  778.         if (!(dta.dta.d_attrib & _A_SUBDIR)     /* q. directory?        */
  779.               || (dta.dta.filename[0] == '.'))  /* .. or a dot dir?     */
  780.               continue;                         /* a. get next entry    */
  781.  
  782.         dirs++;                             /* accumulate dir count     */
  783.         dir_search(work_dir);               /* recursively call ourself */
  784.         }
  785.  
  786.      else                                   /* first pass processing    */
  787.         {
  788.         if (rc)                             /* q. anything found?       */
  789.             {                               /* a. no ..                 */
  790.             first_done = 0;                 /* re-execute find-first    */
  791.             pass++;                         /* go to next pass          */
  792.             continue;                       /* .. continue processing   */
  793.             }
  794.  
  795.         if (dta.dta.filename[0] == '.')     /* q. dot directory?        */
  796.             continue;                       /* a. yes .. skip it        */
  797.  
  798.         if (!(dta.dta.d_attrib & _A_SUBDIR))    /* q. a file?           */
  799.             files++;                            /* a. yes .. count them */
  800.  
  801.         check_frag(work_dir,                /* check for frag'd file    */
  802.                 dta.dta.d_cluster,
  803.                 dta.dta.d_attrib & _A_SUBDIR);
  804.         }
  805.     }
  806.  
  807. r.h.ah = 0x1a;                              /* ah = set DTA             */
  808. s.ds = oldds;                               /* ds -> DTA segment        */
  809. r.x.dx = oldda;                             /* ds:dx -> DTA             */
  810. int86x(0x21, &r, &r, &s);                   /* setup new DTA            */
  811.  
  812. }
  813.  
  814.  
  815. /************************************************************************
  816.  *                                                                      *
  817.  *      fname -- build a normalized filename from an FCB                *
  818.  *                                                                      *
  819.  ************************************************************************/
  820.  
  821. char    *fname(filename, ext)
  822. char    *filename;                      /* filename with trailing blanks*/
  823. char    *ext;                           /* extension                    */
  824. {
  825. int     i;                              /* loop control                 */
  826. char    *p;                             /* work pointer                 */
  827. static
  828. char    fwork[13];                      /* returned work area           */
  829.  
  830.  
  831. p = fwork;                                  /* initialize string pointer*/
  832.  
  833. for (i = 0; (i < 8) && (*filename != ' '); i++) /* move fname w/o blanks*/
  834.     *p++ = *filename++;
  835.  
  836. if (*ext != ' ')                            /* q. extension blank?      */
  837.     {
  838.     *p++ = '.';                             /* a. no .. add the dot     */
  839.  
  840.     for (i = 0; (i < 3) && (*ext != ' '); i++)  /* add ext w/o blanks   */
  841.         *p++ = *ext++;
  842.     }
  843.  
  844. *p = 0;                                     /* terminate string w/null  */
  845.  
  846. return(fwork);                              /* return string to caller  */
  847.  
  848. }
  849.  
  850.  
  851. /************************************************************************
  852.  *                                                                      *
  853.  *      strclr -- clear an area to a value                              *
  854.  *                                                                      *
  855.  ************************************************************************/
  856.  
  857. void    strclr(s, c, n)
  858. char    *s;                             /* area to initialize           */
  859. int     c,                              /* value to use                 */
  860.         n;                              /* length of area to clear      */
  861. {
  862.  
  863. while(n--)                                  /* initialize whole area    */
  864.     *s++ = c;                               /* ..to value specified     */
  865.  
  866. }
  867.  
  868.  
  869. /************************************************************************
  870.  *                                                                      *
  871.  *      translate_name --  translate a DOS name                         *
  872.  *                                                                      *
  873.  ************************************************************************/
  874.  
  875. char    *translate_name(name)
  876. char    far *name;                      /* name to translate            */
  877. {
  878. static
  879. char    translate_area[65],             /* work/return area             */
  880.         far *sp;                        /* work pointer                 */
  881.  
  882. union   REGS r;                         /* work registers               */
  883. struct  SREGS s;                        /* ..and work segment regs      */
  884.  
  885.  
  886. r.h.ah = 0x60;                          /* ah = translate               */
  887.  
  888. sp = (char far *) name;                 /* set up a pointer ..          */
  889. r.x.si = FP_OFF(sp);                    /* set pointer to input name    */
  890. s.ds = FP_SEG(sp);                      /* .. and segment               */
  891.  
  892. sp = (char far *) translate_area;       /* set up a pointer ..          */
  893. r.x.di = FP_OFF(sp);                    /* set pointer to output area   */
  894. s.es = FP_SEG(sp);                      /* .. and segment               */
  895. int86x(0x21, &r, &r, &s);               /* translate the name           */
  896.  
  897. if (r.x.cflag)                          /* if bad name ..               */
  898.     return((char far *) NULL);          /* .. return error              */
  899.  
  900.  else
  901.     return(translate_area);             /* return xlated name           */
  902. }
  903.  
  904.  
  905. /************************************************************************
  906.  *                                                                      *
  907.  *      chkdrv -- assure drive is LOCAL and not SUBST'd or ASSIGN'd     *
  908.  *                                                                      *
  909.  ************************************************************************/
  910.  
  911. int     chkdrv(c)
  912. char    c;                              /* drive to check               */
  913. {
  914.  
  915. static
  916. char    wdrv[] = " :\\";                /* work area for drive name     */
  917.  
  918. union   REGS r;                         /* work registers               */
  919. struct  SREGS s;                        /* ..and work segment regs      */
  920.  
  921. if (_osmajor < 2)                           /* q. pre-DOS 2.00?         */
  922.     return(4);                              /* a. yes .. can't run it   */
  923.  
  924. if ((_osmajor >= 3) ||                      /* q. Higher than DOS 3..   */
  925.    ((_osmajor =  3) && (_osminor >= 1)))    /* - or - Higher than 3.1?  */
  926.  
  927.     {
  928.     r.x.ax = 0x4409;                        /* ah = ioctl, local test   */
  929.     r.h.bl = (c - 'A') + 1;                 /* bl = drive to test       */
  930.     int86(0x21, &r, &r);                    /* test drive               */
  931.  
  932.     if (r.x.cflag)                          /* q. bad drive?            */
  933.         return(1);                          /* a. yes .. error          */
  934.  
  935.     if (r.x.dx & 0x1200)                    /* q. remote?               */
  936.         return(2);                          /* a. yes .. error          */
  937.  
  938.     wdrv[0] = c;                            /* set up name              */
  939.  
  940.     if (strcmp(wdrv, translate_name(wdrv))) /* q. SUBST or ASSIGNED?    */
  941.         return(3);                          /* a. yes .. return error   */
  942.     }
  943.  
  944. dos4 |= (_osmajor > 3);                     /* check for dos v.4        */
  945.  
  946. return(0);                                  /* return ok                */
  947.  
  948. }
  949.  
  950.  
  951. /************************************************************************
  952.  *                                                                      *
  953.  *      Read drive label, if available                                  *
  954.  *                                                                      *
  955.  ************************************************************************/
  956.  
  957. char    *readlabel(c)
  958. int     c;                              /* drive to check               */
  959. {
  960. char    *p, *q;                         /* work pointers                */
  961. struct  find_t f;                       /* structure for directory entry*/
  962.  
  963. static
  964. char    work_dir[13] = { " :\\*.*" } ;  /* directory to check           */
  965.  
  966.  
  967. work_dir[0] = c;                            /* setup for find first     */
  968.  
  969. if (_dos_findfirst(work_dir, LABEL, &f))    /* q. error on label get?   */
  970.     work_dir[0] = 0;                        /* a. yes .. then no label  */
  971.  
  972.  else
  973.     {
  974.     for(p = work_dir, q = f.name; *q; q++)  /* copy label w/o middle .  */
  975.         if (*q != '.')                      /* q. is this char a dot?   */
  976.             *p++ = *q;                      /* a. no .. copy it         */
  977.  
  978.     *p = 0;                                 /* terminate string         */
  979.     }
  980.  
  981. return(work_dir);                           /* ..and return label string*/
  982.  
  983. }
  984.  
  985. /************************************************************************
  986.  *                                                                      *
  987.  *      cfexit() - return to DOS after resetting dir, dir               *
  988.  *                                                                      *
  989.  ************************************************************************/
  990.  
  991. void    cfexit(rc)
  992. int     rc;                             /* return code to exit with     */
  993.  
  994. {
  995. int     i;                              /* work variable                */
  996.  
  997. _dos_setdrive(sdrive, &i);                  /* reset the default drive  */
  998. chdir(cdrive);                              /* .. and directory         */
  999.  
  1000. exit(rc);                                   /* .. and return to DOS     */
  1001. }
  1002.