/* **************************** NOTICE! ************************** * Contrary to the current trend in MS-DOS software this * * program, for whatever it is worth, is NOT copyrighted * * (with the exception of the runtime library from Borland * * International's Turbo C)! The program, in whole or in * * part, may be used freely in any fashion or environment * * desired. If you find this program to be useful to you, * * do NOT send any contribution to the author; in the words * * of Rick Conn, 'Enjoy!' However, if you make any * * improvements, I would enjoy receiving a copy of the * * modified source. I can be reached at the following: * * * * on CompuServ: 70410,1004 * * on Channel 1 BBS (xxx) xxx-xxx * * or by mail or phone: * * * * Don A. Williams * * 3913 W. Solano Dr. N. * * Phoenix, AZ 85019 * * (602) 841-5333 * * * * Every effort has been made to avoid error and moderately * * extensive testing has been performed on this program, * * however, the author does not warrant it to be fit for any * * purpose or to be free from error and disclaims any * * liability for actual or any other damage arising from the * * use of this program. * * * * Calls to absread/abswrite modified for DOS 4+ and large * * disk partition sizes * * * * Also added -Z option * * * * Edgar W. Swank * * 5515 Spinnaker Dr., #4 * * San Jose, CA 95123 * * (408)227-3471 * * Internet: edgar@spectrx.sbay.org * ***************************************************************** */ #include #include #include #include #include #include #include #include #define MAIN #include "dosstruc.h" /*--- Function Prototypes ---*/ void Usage(void); void GetDPB(int Disk, struct DpbStruct * Dpb); unsigned GetDosVersion(void); char *strrspn(char *s1, char *s2); int AbortProgram(void); void SortDir(void); void INT3(void); /*--- End of Prototypes ---*/ #define MAX12BIT 0x0FF6 #define MAX16BIT 0xFFF6 #if defined(__TINY__) #define MODEL "Tiny" #elif defined(__SMALL__) #define MODEL "Small" #elif defined(__COMPACT__) #define MODEL "Compact" #elif defined(__MEDIUM__) #define MODEL "Medium" #elif defined(__LARGE__) #define MODEL "Large" #elif defined(__HUGE__) #define MODEL "Huge" #endif char Disk; /* Alpha working disk ('A', 'B', .... ) */ char CurDir[67]; /* Storage for Current Directory of disk */ char Path[67]; /* Storage for Path to sort */ char Parent[67]; /* Storage for Parent part of Path */ char Element[13]; /* Storage for Child part of Path */ char Line[80]; /* Working storage for strings */ char Order = 'N'; /* Sort key indicator (Default=Name/Ext) */ char Inverse = 0; /* Ascend/Descend indic. (Default=Ascend) */ char Level = 0; /* Recursive sort indic. (default=Recursive) */ char RSwt = 0; /* Report switch (Default=No Report) */ char VerSwt = 0; /* Pause for operator verify (Default=off) */ char Packed = 1; /* Elim. "erased" entries (Default=on) */ char TruncateSwt = 0;/* Truncate directories (Default=off) */ char ZeroSwt = 0; /* Zero Dir unused trailing entries (dflt=off); */ char FatDirty = 0; /* FAT needs to be rewritten */ char a32sw=0; /* 32-bit call required*/ int Is12Bit; /* 12 / 16 bit cluster indicator */ int *CluArray; /* Cluster Array ptr, dynamically allocated */ int Lim, i, j, k, l; long m; int OutSectors, OutClusters, BytesPerCluster, ECount; int TruncSectors, TruncClusters; int ZCount; unsigned LastCluster; /* Value for end of cluster chain */ unsigned Cluster, Sector, NumSec; unsigned FatSize; long FatRecSize; short FatRecCount; struct FatStruct { unsigned char *Ptr; unsigned Size; } *FatArray; struct absr32m a32; unsigned Version; long MinMem; /* Minimum available memory */ unsigned Freed = 0; /* Freed cluster count */ unsigned Version; unsigned DirStart; struct DpbStruct Dpb; /* Disk Parameter Block (see dosstruc.h) */ struct ClusterQueue CluQ; /* Queue of cluster for directory */ struct DirEntry *DirBuff; /* Buffer for directory to be sorted */ struct ExtendedEntry Dir; struct ClusterEntry *p, *t; struct ExtFcb Fcb; void main (int argc, char *argv[]) { char *strrspn(); void SortDir(), Usage(); char *p, *p1, t1; int i, j; bdos(0x0D, 0, 0); /* Reset Disk Subsystem - Flush all buffers */ fputs("C-Sort And Pack [CSAP]: Version 4.2.3: Date: July 21, 1995", stderr); fputs(" [", stderr); fputs(MODEL, stderr); fputs(" Model]\n", stderr); fputs(" use \"CSAP -H\" or \"CSAP ?\" for help.\n\n", stderr); Disk = getdisk() + 'A'; Line[0] = '\\'; getcurdir(Disk - '@', &Line[1]); ctrlbrk(AbortProgram); /* Install "wrap-up" in Control Break vec. */ /* Interpret command line arguments, if any */ for (i = 1; i < argc; ++i) { if (argv[i][0] == '-') { for (j = 1; j < strlen(argv[i]); ++j) { switch (toupper(argv[i][j])) { case 'F': /* Sort Key = Freeze */ Order = 'F'; break; case 'N': /* Sort Key = Name/Ext (default) */ Order = 'N'; break; case 'D': /* Sort Key = Date/Time */ Order = 'D'; break; case 'E': /* Sort Key = Ext/Name */ Order = 'E'; break; case 'S': /* Sort Key = File Size */ Order = 'S'; break; case 'R': /* Report Dir loc. & "erased" */ RSwt = 1; break; case 'I': /* Sort order inverse */ Inverse = 1; break; case 'P': /* Do NOT remove "erased" entries */ Packed = 0; break; case 'L': /* Limit sort to one level */ Level = 1; break; case 'V': /* Request approval before sort */ VerSwt = 1; break; case 'T': /* Truncate directories */ TruncateSwt = 1; break; case 'Z': /* Zero directory unused trailing entries */ ZeroSwt = 1; break; case 'H': Usage(); default: /* Illegal option */ fprintf(stderr, "Invalid option %s.\n", argv[i]); Usage(); break; } } } else { /* Not switch, assume directory name or '?' */ if (argv[i][0] == '?') Usage(); if (argv[i][1] == ':') { /* Check for disk specified */ Disk = toupper(argv[i][0]); p = &argv[i][2]; } else p = &argv[i][0]; if ((p[0] != '\\') && (p[0] != '/')) { Line[0] = '\\'; getcurdir(Disk - '@', &Line[1]); p1 = &p[strcspn(p, "\\/")]; t1 = *p1; *p1 = '\0'; if (!strcmp(p, ".")) { p = p1; *p1 = t1; } else if (!strcmp(p, "..")) { while (!strcmp(p, "..")) { p = strrspn(Line, "\\/"); if ((p - Line) == 0) ++p; *p = '\0'; if (t1 != '\0') p = ++p1; else p = p1; p1 = &p[strcspn(p, "\\/")]; t1 = *p1; *p1 = '\0'; } *p1 = t1; } else *p1 = t1; if (*p != '\0') strcat(Line, "\\"); strcat(Line, p); } else strcpy(Line, p); } } /* * Get disk information - uses un-documented DOS call, Int 21H, Func. * 32H This function has been verified to work correctly in PC/MS-DOS * versions 2.0 through 3.3. It is heavily used by DOS programs such * as CHKDSK. */ GetDPB(Disk, &Dpb); Version = GetDosVersion() & 0xFF; switch (Version) { case 2: FatSize = Dpb.V.V2.FatSize; DirStart = Dpb.V.V2.DirStart; break; case 3: FatSize = Dpb.V.V3.FatSize; DirStart = Dpb.V.V3.DirStart; break; case 4: case 5: case 6: FatSize = Dpb.V.V4.FatSize; DirStart = Dpb.V.V4.DirStart; break; default: fprintf(stderr, "Invalid DOS version: %d\n", Version); exit(1); } FatRecSize = (long) FatSize * Dpb.SectorSize; /* Establish whether disk has 16-bit or 12-bit clusters */ if (Dpb.LastCluster > MAX16BIT) { fprintf(stderr, "Sorry, CSAP does not yet support FAT entries > 16 bits.\n"); exit(1); } Is12Bit = (Dpb.LastCluster > MAX12BIT) ? 0 : 1; LastCluster = (Is12Bit) ? 0x0FF8 : 0xFFF8; /* * Get & save current directory of working disk. We have to change * to sort and must restore on termination */ CurDir[0] = Disk; CurDir[1] = ':'; CurDir[2] = '\\'; getcurdir(Disk - '@', (char *) &CurDir[3]); /* Allocate space to hold entire FAT in memory and read it in */ FatRecCount = ((FatRecSize + (long) (FAT_BLK - 1)) / FAT_BLK); /* EWS */ if ((FatArray = malloc(FatRecCount * sizeof(struct FatStruct))) == NULL) { fprintf(stderr, "Insufficient memory for FAT array.\n"); exit(1); } for (m = FatRecSize, i = 0; i < FatRecCount; i++, m -= (m > FAT_BLK) ? FAT_BLK : m) { if ((FatArray[i].Ptr = malloc((m > FAT_BLK) ? FAT_BLK : (unsigned) m)) == NULL) { fprintf(stderr, "Insufficient memory for FAT Array entry.\n"); exit(1); } FatArray[i].Size = (m > FAT_BLK) ? FAT_BLK : (unsigned) m; } for (i = 0, m = Dpb.FatStart; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) { if (a32sw | (absread(Disk - 'A', FatArray[i].Size / Dpb.SectorSize, (unsigned) m, FatArray[i].Ptr) != 0)) { #if 1 a32.nsect = FatArray[i].Size / Dpb.SectorSize; a32.sector = (unsigned) m; a32.xferad = FatArray[i].Ptr; a32sw=1; if (absread(Disk - 'A', -1, 0, &a32) != 0) { /* EWS */ #endif fprintf(stderr, "Error reading FAT.\n"); perror(""); exit(1); } #if 1 } #endif } /* * Develop full path name for directory to be sorted and separate * into Parent and Child portions */ Path[0] = Parent[0] = Element[0] = '\0'; Path[0] = Disk; Path[1] = ':'; Path[2] = '\0'; if ((Line[0] != '\\') && (Line[0] != '/')) { strcat(Path, "\\"); strcpy(&Path[3], &CurDir[3]); if ((Path[strlen(Path) - 1] != '\\') && (Path[strlen(Path) - 1] != '/')) strcat(Path, "\\"); } strcat(Path, Line); p = strrspn(Path, "\\/"); strcpy(Element, &p[1]); if (p[-1] == ':') p++; strncpy(Parent, Path, (int) (p - Path)); Parent[(int) (p - Path)] = '\0'; MinMem = coreleft(); /* Initialize minimum available memory */ /* * Perform sort. SortDir is recursive and, if Level is not on, will * sort sort all levels of the hierarchy from the starting level down */ SortDir(); printf("Minimum memory= %ld\n", MinMem); bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */ if (FatDirty) { for (i = 0, m = Dpb.FatStart; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) { if (a32sw | (abswrite(Disk - 'A', FatArray[i].Size / Dpb.SectorSize, m, FatArray[i].Ptr) != 0)) { #if 1 a32.nsect = FatArray[i].Size / Dpb.SectorSize; a32.sector = m; a32.xferad = FatArray[i].Ptr; a32sw=1; if (abswrite(Disk - 'A', -1, 0, &a32) != 0) { /* EWS */ #endif fprintf(stderr, "Error writing FAT.\n"); perror(""); exit(1); } #if 1 } #endif } if (Dpb.FatCopies == 2) { for (i = 0, m = Dpb.FatStart + FatSize; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) { if (abswrite(Disk - 'A', FatArray[i].Size / Dpb.SectorSize, m, FatArray[i].Ptr) != 0) { #if 1 a32.nsect = FatArray[i].Size / Dpb.SectorSize; a32.sector = m; a32.xferad = FatArray[i].Ptr; a32sw=1; if (abswrite(Disk - 'A', -1, 0, &a32) != 0) { /* EWS */ #endif fprintf(stderr, "Error writing 2nd copy of FAT.\n"); perror(""); exit(1); } #if 1 } #endif } } printf("There %s %d cluster%s (%d bytes) freed\n", (Freed == 1) ? "was" : "were", Freed, (Freed == 1) ? "" : "s", Freed * Dpb.SectorSize * (Dpb.ClusterSize + 1)); } bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */ bdosptr(0x3B, CurDir, 0); /* Restore input "current" directory */ if (a32sw && RSwt) printf("\nAlternate absread/write call used.\n"); } /* end Main */ /* * STRRSPN is simply a reverse version of STRSPN. It finds the LAST * occurance in S1 of any member of S2. For some reason, none of the C * compilers that I use provide this although they all provide STRSPN */ char * strrspn (char *s1, char *s2) { char *p1; p1 = s1 + strlen(s1) - 1; while (p1 >= s1) { if (strchr(s2, *p1) != NULL) return (p1); --p1; } return ((char *) NULL); } /* * SearchFirst -- Search for First Directory Entry. On entry fcb contains an * extended File Control Block with file name and attribute bits set. On * exit, fcb contains matched entry unless return code is 255, in which case * no match was found. This routine is used instead of the ones provided by * the compiler so that the cluster information for the directory can be * obtained. */ int SearchFirst (struct ExtFcb * Fcb) { union REGS regs; regs.x.ax = 0x1100; regs.x.dx = (unsigned) Fcb; intdos(®s, ®s); return ((int) (regs.x.ax & 0xFF)); } /* * Alu2Sec -- Converts an input cluster number [ALU] into the disk-relative * sector for use with DOS Absolute Disk Read [interrupt 25H] or Absolute * Disk Write [interrupt 26H]. Requires access to the undocumented DOS Disk * Parameter Block [use funtion GetDPB]. */ long Alu2Sec (struct DpbStruct * Dpb, unsigned Alu) { return ((long) (Alu - 2) * (Dpb->ClusterSize + 1) + Dpb->DataStart); } /* * NextCl -- This function calculates the logical "chaining" of cluster * numbers in a File Allocation Table [FAT]. Given an entry cluster number * it calculates the next cluster using the array Fat[]. * * If Is12Bit is TRUE then Fat[] is assumed to contain 12 bit entries, otherwise * Fat[] is assumed to contain 16 bit entries. */ unsigned NextCl (int Is12Bit, unsigned Cluster) { unsigned ClWord, ClOffset; long Factor = FAT_BLK / 2; if (Is12Bit) { /* 12 bit FAT lookup */ ClOffset = 3 * Cluster / 2; ClWord = (FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK] & 0xFF) + (FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] << 8); if (Cluster & 1) return (ClWord >> 4); /* odd cluster */ else return (ClWord & 0x0FFF); /* even cluster */ } else return (((unsigned int *) (FatArray[Cluster / Factor].Ptr))[Cluster % Factor]); } void FreeCluster (int Is12Bit, unsigned Val, unsigned Cluster) { extern char FatDirty; extern unsigned Freed; unsigned ClWord, ClOffset; if (Is12Bit) { /* 12 bit FAT lookup */ ClOffset = 3 * Cluster / 2; ClWord = FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK] + (FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] << 8); if (Cluster & 1) ClWord = (ClWord & 0xF) | (Val & 0xFFF0); /* odd */ else ClWord = (ClWord & 0xF000) | (Val & 0x0FFF); FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] = ClWord >> 8; FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK] = ClWord & 0xFF; } else ((unsigned int *) FatArray[Cluster / INT_FAT_BLK].Ptr)[Cluster % INT_FAT_BLK] = Val; /* 16 bit FAT lookup */ if (!Val) ++Freed; } /* * PutQueue -- Builds a simple FIFO linked list using dynamically acquired * memory. */ void PutQueue (struct ClusterQueue * Q, unsigned Cluster) { struct ClusterEntry *p; if ((p = malloc(sizeof(struct ClusterEntry))) == NULL) { fprintf(stderr, "Insufficient memory(1).\n"); AbortProgram(); } p->Next = NULL; p->Cluster = Cluster; if (Q->Head == NULL) Q->Head = p; else Q->Current->Next = p; Q->Current = p; Q->Count++; } /* * AbortProgram -- Aborts the program, resetting the current directory, with * an error code of 1. */ int AbortProgram (void) { bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */ bdosptr(0x3B, CurDir, 0); /* Reset input Current Directory */ exit(1); return (0); } /* * strincmp -- The comparsion routine for the qsort algorithm. */ int strincmp (struct DirEntry * a, struct DirEntry * b) { long t; /* * Ensure that "erased" entries sort high no matter what the sort key * is. */ if ((a->Name[0] == 0xE5) && (b->Name[0] != 0xE5)) return (1); if (b->Name[0] == 0xE5) return (-1); /* * Ensure that directories sort lower than files no matter what sort * key */ if ((a->Name[0] != 0xE5) && (b->Name[0] != 0xE5)) { if ((a->Attribute & 0x10) ^ (b->Attribute & 0x10)) { if (a->Attribute & 0x10) return (-1); else return (1); } } /* Actual sort key compare routines */ switch (Order) { case 'F': /* Sort key is Freeze */ t=0; break; case 'D': /* Sort key is Date/Time */ if (a->ModifyDate < b->ModifyDate) t = -1; else if (a->ModifyDate > b->ModifyDate) t = 1; else { if (a->ModifyTime < b->ModifyTime) t = -1; else if (a->ModifyTime > b->ModifyTime) t = 1; else t = 0; } break; case 'N': /* Sort key is Name/Ext (default) */ t = strncmp((char *) a->Name, (char *) b->Name, 11); break; case 'E': /* Sort key is Ext/Name */ if ((t = strncmp(a->Ext, b->Ext, 3)) == 0) { t = strncmp((char *) a->Name, (char *) b->Name, 8); } break; case 'S': /* Sort key is File Size */ t = a->FileSize - b->FileSize; break; default: t = strncmp((char *) a->Name, (char *) b->Name, 11); break; } if (Inverse) t = -t; /* Sort order is inverse */ return ((t < 0) ? -1 : ((t > 0) ? 1 : 0)); } unsigned GetDosVersion (void) { union REGS Regs; Regs.h.ah = 0x30; intdos(&Regs, &Regs); return (Regs.x.ax); } void Usage (void) { printf("USAGE: CSAP [options] [[d:]directory_name]\n"); printf(" or\n"); printf(" CSAP [[d:]directory_name] [options]\n"); printf("\n"); printf("Options:\n"); printf(" -F Freeze (Sort only Dir/Free entries).\n"); printf(" -N Sort on Name/Ext (default).\n"); printf(" -D Sort on Date/Time.\n"); printf(" -E Sort on Ext/Name.\n"); printf(" -S Sort on File Size.\n"); printf("\n"); printf(" -R Report number of \"erased\" entries and directory location.\n"); printf(" -I Inverse sort order, i.e. descending.\n"); printf(" -P Do NOT remove \"erased\" entries.\n"); printf(" -L Limit sort to a single level.\n"); printf(" -V Request confirmation before sorting.\n"); printf(" -T Truncate directories.\n"); printf(" -Z Zero trailing unused directory entries.\n"); printf(" -H This message.\n"); exit(0); }