home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / DIR / CSAP302.ZIP / CSAP.C next >
Encoding:
C/C++ Source or Header  |  1990-06-20  |  17.0 KB  |  553 lines

  1. /*
  2.    ****************************  NOTICE!  **************************
  3.    *   Contrary to the current trend  in  MS-DOS  software  this   *
  4.    *   program,  for  whatever  it is worth,  is NOT copyrighted   *
  5.    *   (with the exception of the runtime library  from  Borland   *
  6.    *   International's  Turbo  C)!  The program,  in whole or in   *
  7.    *   part,  may be used freely in any fashion  or  environment   *
  8.    *   desired.  If  you  find this program to be useful to you,   *
  9.    *   do NOT send any contribution to the author;  in the words   *
  10.    *   of  Rick  Conn,   'Enjoy!'  However,   if  you  make  any   *
  11.    *   improvements,  I would enjoy  receiving  a  copy  of  the   *
  12.    *   modified  source.  I  can  be reached,  usually within 24   *
  13.    *   hours,  by  messages  on  any  of  the  Phoenix  systems,   *
  14.    *   particularly:                                               *
  15.    *                                                               *
  16.    *               Bob's Answering Machine [OPUS]                  *
  17.    *                   (602) 242-3158   1200/2400 bps              *
  18.    *               Radioactive West        [PCBOARD]               *
  19.    *                   (602) 873-0810   1200/2400 bps              *
  20.    *               The Tool Shop BBS       [PCBOARD]               *
  21.    *                   (602) 279-2673   1200/2400/9600 bps         *
  22.    *                   (Good luck trying!  VERY BUSY!)             *
  23.    *               Technoids Anonymous     [PCBOARD]               *
  24.    *                   (602) 899-4876   300/1200/2400 bps          *
  25.    *                                                               *
  26.    *   All can be reached through PC Pursuit.                      *
  27.    *                                                               *
  28.    *   or:                                                         *
  29.    *                on GEnie, mail address: DON-WILL               *
  30.    *                on CompuServ:           75410,543              *
  31.    *                                                               *
  32.    *   Every  effort has been made to avoid error and moderately   *
  33.    *   extensive testing has been  performed  on  this  program,   *
  34.    *   however, the author does not warrant it to be fit for any   *
  35.    *   purpose  or  to  be  free  from  error  and disclaims any   *
  36.    *   liability for actual or any other damage arising from the   *
  37.    *   use of this program.                                        *
  38.    *****************************************************************
  39. */
  40.  
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #include <dos.h>
  45. #include <ctype.h>
  46. #include <dir.h>
  47. #include <mem.h>
  48. #include <alloc.h>
  49.  
  50. #include "dosstruc.h"
  51.  
  52. /*---   Function Prototypes  ---*/
  53.  
  54. void            Usage (void);
  55. void            GetDPB (int Disk, struct DpbStruct * Dpb);
  56. unsigned        GetDosVersion (void);
  57. char           *strrspn (char *s1, char *s2);
  58. int             AbortProgram (void);
  59.  
  60. void            SortDir (void);
  61.  
  62. /*---   End of Prototypes  ---*/
  63.  
  64. #define MAX12BIT 0x0FF6
  65. #define MAX16BIT 0xFFF6
  66.  
  67. #if defined(__TINY__)
  68.     #define MODEL "Tiny"
  69. #elif defined(__SMALL__)
  70.     #define MODEL "Small"
  71. #elif defined(__COMPACT__)
  72.     #define MODEL "Compact"
  73. #elif defined(__MEDIUM__)
  74.     #define MODEL "Medium"
  75. #elif defined(__LARGE__)
  76.     #define MODEL "Large"
  77. #elif defined(__HUGE__)
  78.     #define MODEL "Huge"
  79. #endif
  80.  
  81. char            Disk;            /* Alpha working disk ('A', 'B', .... )         */
  82. char            CurDir[67];        /* Storage for Current Directory of disk     */
  83. char            Path[67];        /* Storage for Path to sort                     */
  84. char            Parent[67];        /* Storage for Parent part of Path             */
  85. char            Element[13];    /* Storage for Child part of Path             */
  86. char            Line[80];        /* Working storage for strings                 */
  87. char            Order = 'N';    /* Sort key indicator (Default=Name/Ext)     */
  88. char            Inverse = 0;    /* Ascend/Descend indic. (Default=Ascend)     */
  89. char            Level = 0;        /* Recursive sort indic. (default=Recursive) */
  90. char            RSwt = 0;        /* Report switch (Default=No Report)         */
  91. char            VerSwt = 0;        /* Pause for operator verify (Default=off)     */
  92. char            Packed = 1;        /* Elim. "erased" entries (Default=on)         */
  93. char            TruncateSwt = 0;/* Truncate directories (Default=off)     */
  94. unsigned char  *Fat;            /* Pointer to FAT buffer (dynamic)             */
  95. char            FatDirty = 0;    /* FAT needs to be rewritten                 */
  96. int             Is12Bit;        /* 12 / 16 bit cluster indicator             */
  97. int            *CluArray;        /* Cluster Array ptr, dynamically allocated     */
  98. int             Lim, i, j, k, l;
  99. int             OutSectors, OutClusters, BytesPerCluster, ECount;
  100. unsigned        LastCluster;    /* Value for end of cluster chain             */
  101. unsigned        Cluster, Sector, NumSec;
  102. unsigned        FatSize;
  103. unsigned        Version;
  104. long            MinMem;            /* Minimum available memory                     */
  105. unsigned        Freed = 0;        /* Freed clusster count                         */
  106. unsigned        Version;
  107. unsigned        FatSize;
  108. unsigned        DirStart;
  109.  
  110. struct DpbStruct Dpb;            /* Disk Parameter Blcok (see dosstruc.h)     */
  111. struct ClusterQueue CluQ;        /* Queue of cluster for directory             */
  112. struct DirEntry *DirBuff;        /* Buffer for directory to be sorted         */
  113. struct ExtendedEntry Dir;
  114. struct ClusterEntry *p, *t;
  115. struct ExtFcb   Fcb;
  116.  
  117.  
  118.  void
  119. main (int argc, char *argv[]) {
  120.     char           *strrspn();
  121.     void            SortDir(), Usage();
  122.  
  123.     char           *p, *p1, t1;
  124.     int             i, j;
  125.  
  126.     bdos(0x0D, 0, 0);            /* Reset Disk Subsystem - Flush all buffers */
  127.     fputs("C-Sort And Pack [CSAP]: Version 3.0.2: Date: June 20, 1990", stderr);
  128.     fputs(" [", stderr);
  129.     fputs(MODEL, stderr);
  130.     fputs(" Model]\n", stderr);
  131.     fputs("    use \"CSAP -H\" or \"CSAP ?\" for help.\n\n", stderr);
  132.  
  133.  
  134.     Disk = getdisk() + 'A';
  135.     Line[0] = '\\';
  136.     getcurdir(Disk - '@', &Line[1]);
  137.  
  138.     ctrlbrk(AbortProgram);        /* Install "wrap-up" in Control Break vec. */
  139.  
  140.     /* Interpret command line arguments, if any      */
  141.  
  142.     for (i = 1; i < argc; ++i) {
  143.         if (argv[i][0] == '-') {
  144.             for (j = 1; j < strlen(argv[i]); ++j) {
  145.                 switch (toupper(argv[i][j])) {
  146.                     case 'N':    /* Sort Key = Name/Ext (default)     */
  147.                         Order = 'N';
  148.                         break;
  149.                     case 'D':    /* Sort Key = Date/Time                */
  150.                         Order = 'D';
  151.                         break;
  152.                     case 'E':    /* Sort Key = Ext/Name                 */
  153.                         Order = 'E';
  154.                         break;
  155.                     case 'S':    /* Sort Key = File Size                 */
  156.                         Order = 'S';
  157.                         break;
  158.                     case 'R':    /* Report Dir loc. & "erased"         */
  159.                         RSwt = 1;
  160.                         break;
  161.                     case 'I':    /* Sort order inverse                 */
  162.                         Inverse = 1;
  163.                         break;
  164.                     case 'P':    /* Do NOT remove "erased" entries     */
  165.                         Packed = 0;
  166.                         break;
  167.                     case 'L':    /* Limit sort to one level             */
  168.                         Level = 1;
  169.                         break;
  170.                     case 'V':    /* Request approval before sort         */
  171.                         VerSwt = 1;
  172.                         break;
  173.                     case 'T':    /* Truncate directories                 */
  174.                         TruncateSwt = 1;
  175.                         break;
  176.                     case 'H':
  177.                         Usage();
  178.                     default:    /* Illegal option                     */
  179.                         fprintf(stderr, "Invalid option %s.\n", argv[i]);
  180.                         Usage();
  181.                         break;
  182.                     }
  183.                 }
  184.             }
  185.         else {                    /* Not switch, assume directory name or '?'         */
  186.             if (argv[i][0] == '?') Usage();
  187.             if (argv[i][1] == ':') {    /* Check for disk specified     */
  188.                 Disk = toupper(argv[i][0]);
  189.                 p = &argv[i][2];
  190.                 }
  191.             else p = &argv[i][0];
  192.             if ((p[0] != '\\') && (p[0] != '/')) {
  193.                 Line[0] = '\\';
  194.                 getcurdir(Disk - '@', &Line[1]);
  195.                 p1 = &p[strcspn(p, "\\/")];
  196.                 t1 = *p1;
  197.                 *p1 = '\0';
  198.                 if (!strcmp(p, ".")) {
  199.                     p = p1;
  200.                     *p1 = t1;
  201.                     }
  202.                 else if (!strcmp(p, "..")) {
  203.                     while (!strcmp(p, "..")) {
  204.                         p = strrspn(Line, "\\/");
  205.                         if ((p - Line) == 0) ++p;
  206.                         *p = '\0';
  207.                         if (t1 != '\0') p = ++p1;
  208.                         else p = p1;
  209.                         p1 = &p[strcspn(p, "\\/")];
  210.                         t1 = *p1;
  211.                         *p1 = '\0';
  212.                         }
  213.                     *p1 = t1;
  214.                     }
  215.                 else *p1 = t1;
  216.                 if (*p != '\0') strcat(Line, "\\");
  217.                 strcat(Line, p);
  218.                 }
  219.             else strcpy(Line, p);
  220.             }
  221.         }
  222.  
  223.     /*
  224.      * Get disk information - uses un-documented DOS call, Int 21H, Func. 32H
  225.      * This function has been verified to work correctly in PC/MS-DOS
  226.      * versions 2.0 through 3.3.  It is heavily used by DOS programs such as
  227.      * CHKDSK.
  228.      */
  229.  
  230.     GetDPB(Disk, &Dpb);
  231.     Version = GetDosVersion() & 0xFF;
  232.     switch (Version) {
  233.         case 2:
  234.             FatSize = Dpb.V.V2.FatSize;
  235.             DirStart = Dpb.V.V2.DirStart;
  236.             break;
  237.         case 3:
  238.             FatSize = Dpb.V.V3.FatSize;
  239.             DirStart = Dpb.V.V3.DirStart;
  240.             break;
  241.         case 4:
  242.             FatSize = Dpb.V.V4.FatSize;
  243.             DirStart = Dpb.V.V4.DirStart;
  244.             break;
  245.         default:
  246.             fprintf(stderr, "Invalid DOS version: %d\n", Version);
  247.             exit(1);
  248.         }
  249.  
  250.     /* Establish whether disk has 16-bit or 12-bit clusters  */
  251.  
  252.     if (Dpb.LastCluster > MAX16BIT) {
  253.         fprintf(stderr, "Sorry, CSAP does not yet support FAT entries > 16 bits.\n");
  254.         exit(1);
  255.         }
  256.     Is12Bit = (Dpb.LastCluster > MAX12BIT) ? 0 : 1;
  257.     LastCluster = (Is12Bit) ? 0x0FF8 : 0xFFF8;
  258.  
  259.     /*
  260.      * Get & save current directory of working disk.  We have to change to
  261.      * sort and must restore on termination
  262.      */
  263.  
  264.     CurDir[0] = Disk;
  265.     CurDir[1] = ':';
  266.     CurDir[2] = '\\';
  267.     getcurdir(Disk - '@', (char *) &CurDir[3]);
  268.  
  269.     /* Allocate space to hold entire FAT in memory and read it in  */
  270.  
  271.     if ((Fat = malloc(FatSize * Dpb.SectorSize)) == NULL) {
  272.         fprintf(stderr, "Insufficient memory for FAT.\n");
  273.         exit(1);
  274.         }
  275.     if (absread(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
  276.         fprintf(stderr, "Error reading FAT.\n");
  277.         exit(1);
  278.         }
  279.  
  280.     /*
  281.      * Develop full path name for directory to be sorted and separate into
  282.      * Parent and Child portions
  283.      */
  284.  
  285.     Path[0] = Parent[0] = Element[0] = '\0';
  286.     Path[0] = Disk;
  287.     Path[1] = ':';
  288.     Path[2] = '\0';
  289.     if ((Line[0] != '\\') && (Line[0] != '/')) {
  290.         strcat(Path, "\\");
  291.         strcpy(&Path[3], &CurDir[3]);
  292.         if ((Path[strlen(Path) - 1] != '\\') && (Path[strlen(Path) - 1] != '/'))
  293.             strcat(Path, "\\");
  294.         }
  295.     strcat(Path, Line);
  296.     p = strrspn(Path, "\\/");
  297.     strcpy(Element, &p[1]);
  298.     if (p[-1] == ':') p++;
  299.     strncpy(Parent, Path, (int) (p - Path));
  300.     Parent[(int) (p - Path)] = '\0';
  301.  
  302.     MinMem = coreleft();        /* Initialize minimum available memory         */
  303.  
  304.     /*
  305.      * Perform sort.  SortDir is recursive and, if Level is not on, will sort
  306.      * sort all levels of the hierarchy from the starting level down
  307.      */
  308.  
  309.     SortDir();
  310.  
  311.     printf("Minimum memory= %ld\n", MinMem);
  312.  
  313.     bdos(0x0D, 0, 0);            /* Reset disk subsystem - flush all buffers */
  314.     if (FatDirty) {
  315.         if (abswrite(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
  316.             fprintf(stderr, "Error writing FAT.\n");
  317.             exit(1);
  318.             }
  319.         if (Dpb.FatCopies == 2) {
  320.             if (abswrite(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
  321.                 fprintf(stderr, "Error writing 2nd copy of Fat.\n");
  322.                 exit(1);
  323.                 }
  324.             }
  325.         printf("There were %d clusters freed\n", Freed);
  326.         }
  327.     bdos(0x0D, 0, 0);            /* Reset disk subsystem - flush all buffers */
  328.     bdosptr(0x3B, CurDir, 0);    /* Restore input "current" directory     */
  329.     }                                /* end Main */
  330.  
  331.  
  332. /*
  333.  * STRRSPN is simply a reverse version of STRSPN.  It finds the LAST
  334.  * occurance in S1 of any member of S2.  For some reason, none of the C
  335.  * compilers that I use provide this although they all provide STRSPN
  336.  */
  337.  
  338.  char           *
  339. strrspn (char *s1, char *s2) {
  340.     char           *p1;
  341.  
  342.     p1 = s1 + strlen(s1) - 1;
  343.     while (p1 >= s1) {
  344.         if (strchr(s2, *p1) != NULL) return (p1);
  345.         --p1;
  346.         }
  347.     return ((char *) NULL);
  348.     }
  349.  
  350.  
  351. /*
  352.  * SearchFirst --  Search for First Directory Entry. On entry fcb contains an
  353.  * extended File Control Block with file name and attribute bits set.  On
  354.  * exit, fcb contains matched entry unless return code is 255, in which case
  355.  * no match was found.  This routine is used instead of the ones provided by
  356.  * the caller so that the cluster information for the directory can be
  357.  * obtained.
  358.  */
  359.  
  360.  int
  361. SearchFirst (struct ExtFcb * Fcb) {
  362.     union REGS      regs;
  363.  
  364.     regs.x.ax = 0x1100;
  365.     regs.x.dx = (unsigned) Fcb;
  366.     intdos(®s, ®s);
  367.     return ((int) (regs.x.ax & 0xFF));
  368.     }
  369.  
  370.  
  371.  
  372. /*
  373.  * Alu2Sec -- Converts an input cluster number [ALU] into the disk-relative
  374.  * sector for use with DOS Absolute Disk Read [interrupt 25H] or Absolute
  375.  * Disk Write [interrupt 26H].  Requires access to the undocumented DOS Disk
  376.  * Parameter Block [use funtion GetDPB].
  377.  */
  378.  
  379.  long
  380. Alu2Sec (struct DpbStruct * Dpb, unsigned Alu) {
  381.  
  382.     return ((long) (Alu - 2) * (Dpb->ClusterSize + 1) + Dpb->DataStart);
  383.     }
  384.  
  385. /*
  386.  * NextCl -- This function calculates the logical "chaining" of cluster
  387.  * numbers in a File Allocation Table [FAT].  Given an entry cluster number
  388.  * it calculates the next cluster using the array Fat[].
  389.  *
  390.  * If Is12Bit is TRUE then Fat[] is assumed to contain 12 bit entries, otherwise
  391.  * Fat[] is assumed to contain 16 bit entries.
  392.  */
  393.  
  394.  unsigned
  395. NextCl (int Is12Bit, unsigned Cluster, unsigned char Fat[]) {
  396.     unsigned        ClWord, ClOffset;
  397.  
  398.     if (Is12Bit) {                /* 12 bit FAT lookup */
  399.         ClOffset = 3 * Cluster / 2;
  400.         ClWord = Fat[ClOffset] + (Fat[ClOffset + 1] << 8);
  401.         if (Cluster & 1) return (ClWord >> 4);    /* odd cluster  */
  402.         else return (ClWord & 0x0FFF);    /* even cluster */
  403.         }
  404.     else return (((unsigned int *) Fat)[Cluster]);        /* 16 bit FAT lookup */
  405.     }
  406.  
  407.  
  408.  void
  409. FreeCluster (int Is12Bit, unsigned Val, unsigned Cluster, unsigned char Fat[]) {
  410.     extern char     FatDirty;
  411.     extern unsigned    Freed;
  412.     unsigned        ClWord, ClOffset;
  413.  
  414.     if (Is12Bit) {                /* 12 bit FAT lookup */
  415.         ClOffset = 3 * Cluster / 2;
  416.         ClWord = Fat[ClOffset] + (Fat[ClOffset + 1] << 8);
  417.         if (Cluster & 1) ClWord = (ClWord & 0xF) | (Val & 0xFFF0);        /* odd     */
  418.         else ClWord = (ClWord & 0xF000) | (Val & 0x0FFF);
  419.         Fat[ClOffset + 1] = ClWord >> 8;
  420.         Fat[ClOffset] = ClWord & 0xFF;
  421.         }
  422.     else ((unsigned int *) Fat)[Cluster] = Val;    /* 16 bit FAT lookup */
  423.     ++Freed;
  424.     }
  425.  
  426.  
  427. /*
  428.  * PutQueue -- Builds a simple FIFO linked list using dynamically acquired
  429.  * memory.
  430.  */
  431.  
  432.  void
  433. PutQueue (struct ClusterQueue * Q, unsigned Cluster) {
  434.     struct ClusterEntry *p;
  435.  
  436.     if ((p = malloc(sizeof(struct ClusterEntry))) == NULL) {
  437.         fprintf(stderr, "Insufficient memory(1).\n");
  438.         AbortProgram();
  439.         }
  440.     p->Next = NULL;
  441.     p->Cluster = Cluster;
  442.     if (Q->Head == NULL) Q->Head = p;
  443.     else Q->Current->Next = p;
  444.     Q->Current = p;
  445.     Q->Count++;
  446.     }
  447.  
  448.  
  449. /*
  450.  * AbortProgram -- Aborts the program, resetting the current directory, with
  451.  * an error code of 1.
  452.  */
  453.  
  454.  int
  455. AbortProgram (void) {
  456.  
  457.     bdos(0x0D, 0, 0);            /* Reset disk subsystem - flush all buffers */
  458.     bdosptr(0x3B, CurDir, 0);    /* Reset input Current Directory */
  459.     exit(1);
  460.     return (0);
  461.     }
  462.  
  463.  
  464. /*
  465.  * strincmp -- The comparsion routine for the qsort algorithm.
  466.  */
  467.  
  468.  int
  469. strincmp (struct DirEntry * a, struct DirEntry * b) {
  470.     long            t;
  471.  
  472.     /*
  473.      * Ensure that "erased" entries sort high no matter what the sort key is.
  474.      */
  475.  
  476.     if ((a->Name[0] == 0xE5) && (b->Name[0] != 0xE5)) return (1);
  477.     if (b->Name[0] == 0xE5) return (-1);
  478.  
  479.     /*
  480.      * Ensure that directories sort lower than files no matter what sort key
  481.      */
  482.  
  483.     if ((a->Name[0] != 0xE5) && (b->Name[0] != 0xE5)) {
  484.         if ((a->Attribute & 0x10) ^ (b->Attribute & 0x10)) {
  485.             if (a->Attribute & 0x10) return (-1);
  486.             else return (1);
  487.             }
  488.         }
  489.  
  490.     /* Actual sort key compare routines  */
  491.  
  492.     switch (Order) {
  493.         case 'D':                /* Sort key is Date/Time             */
  494.             if (a->ModifyDate < b->ModifyDate) t = -1;
  495.             else if (a->ModifyDate > b->ModifyDate) t = 1;
  496.             else {
  497.                 if (a->ModifyTime < b->ModifyTime) t = -1;
  498.                 else if (a->ModifyTime > b->ModifyTime) t = 1;
  499.                 else t = 0;
  500.                 }
  501.             break;
  502.         case 'N':                /* Sort key is Name/Ext (default)     */
  503.             t = strncmp((char *) a->Name, (char *) b->Name, 11);
  504.             break;
  505.         case 'E':                /* Sort key is Ext/Name                 */
  506.             if ((t = strncmp(a->Ext, b->Ext, 3)) == 0) {
  507.                 t = strncmp((char *) a->Name, (char *) b->Name, 8);
  508.                 }
  509.             break;
  510.         case 'S':                /* Sort key is File Size             */
  511.             t = a->FileSize - b->FileSize;
  512.             break;
  513.         default:
  514.             t = strncmp((char *) a->Name, (char *) b->Name, 11);
  515.             break;
  516.         }
  517.     if (Inverse) t = -t;        /* Sort order is inverse     */
  518.     return ((t < 0) ? -1 : ((t > 0) ? 1 : 0));
  519.     }
  520.  
  521.  unsigned
  522. GetDosVersion (void) {
  523.     union REGS      Regs;
  524.  
  525.     Regs.h.ah = 0x30;
  526.     intdos(&Regs, &Regs);
  527.     return (Regs.x.ax);
  528.     }
  529.  
  530.  
  531.  void
  532. Usage (void) {
  533.  
  534.     printf("USAGE:    CSAP [options] [[d:]directory_name]\n");
  535.     printf("                        or\n");
  536.     printf("          CSAP [[d:]directory_name] [options]\n");
  537.     printf("\n");
  538.     printf("Options:\n");
  539.     printf("    -N    Sort on Name/Ext (default).\n");
  540.     printf("    -D    Sort on Date/Time.\n");
  541.     printf("    -E    Sort on Ext/Name.\n");
  542.     printf("    -S    Sort on File Size.\n");
  543.     printf("\n");
  544.     printf("    -R    Report number of \"erased\" entries and directory location.\n");
  545.     printf("    -I    Inverse sort order, i.e. descending.\n");
  546.     printf("    -P    Do NOT remove \"erased\" entries.\n");
  547.     printf("    -L    Limit sort to a single level.\n");
  548.     printf("    -V    Request confirmation before sorting.\n");
  549.     printf("    -T    Truncate directories.\n");
  550.     printf("    -H    This message.\n");
  551.     exit(0);
  552.     }
  553.