/*** *files.c - disked file indexing * *Copyright (c) 1991-1995, Gregg Jennings. All wrongs reserved. * P O Box 200, Falmouth, MA 02541-0200 * *Purpose: * Indexes all files. * *Notice: * This progam may be freely used and distributed. Any distrubution * with modifications must retain the above copyright statement and * modifications noted. * No pulp-publication, in whole or in part, permitted without * permission (magazines or books). *******************************************************************************/ /* Versions 2.5 22-Dec-1994 enums, dDIR was DIR 2.4 04-Sep-1994 extern reference consolodation; getdir() changes 2.3 30-Jul-1994 added struct DIR * in getdir() 2.2 26-Feb-1994 huge clusters/files etc 2.1 14-Jan-1994 bug fix in initfiles() Notes: The error handling stuff is a bit crude (there has been sleight improvements since last release but it could still use some work). */ #include #include #include #include #include #include #include "disked.h" #include "console.h" /* conout, print */ #include "alloc.h" #include "diskio.h" #include "files.h" #include "error.h" #include "dirent.h" #include "direct.h" enum DOS_DIR_FILENAME { FILENAM = 8, FILETYP = 3, FILELEN = FILENAM+FILETYP }; enum DOS_DIR_OFFSETS { ATTRIB = 11, CLUSTL = 26, CLUSTH = 27 }; enum DOS_DIR_FIRST_CHARS { ERACHR = 0xE5, DIRCHR = '.', WEIRDC = 5, ENDDIR = 0 }; enum DOS_DIR_ATTRIBUTES /* the ones we care about here */ { DIRATR = 0x10, /* (bits) */ VOLATR = 8 }; /* global data referenced here all of the DISKIO stuff */ /* global data defined here */ struct files_t __huge *files; unsigned int n_files,n_dirs; /* number of files and directories */ int __huge *clusters; /* pointer to array of cluster entries */ /* int limits this to 32000 clusters */ /* static data */ static unsigned int tn_files; static const char *module = "files"; /* static functions */ static int getdir(unsigned long sector,int parent,int nsecs); static int getfat12(int nsecs,unsigned int nclusters); static int getfat16(int nsecs,unsigned int nclusters); __inline static void namecpy(char *n, char *d); /*** *initfiles() - read/index all files * * ver 3.0 13-Jan-1994 fixed getfatxx() return signed/unsigned mismatch * ****/ extern int initfiles(void) { int mem,status; mem = status = 0; if (_osmajor < 3) /* always off if DOS != 3.x */ { status = REQ_DOS_3; goto error; } if ((clusters=(int _huge *)hugealloc(num_clusters+1,sizeof(int)))==NULL) { status = NO_MEM; goto error; } mem = 1; if (num_clusters <= 4086) /* 12 bit FAT */ status = getfat12(secs_fat,num_clusters); else status = getfat16(secs_fat,num_clusters); if (status == -1) goto error; n_files = status; tn_files = n_files + 2; /* NOTE extra entries */ if ((files=(struct files_t _huge *)hugealloc(tn_files,sizeof(struct files_t)))==NULL) { status = NO_MEM; goto error; } mem = 2; n_files = n_dirs = 1; strcpy(files[0].name,"\\"); /* root directory name */ files[0].parent = 0; files[0].dir = 1; if (getdir((dword)dir_sector,0,dir_sectors) != 0) { if (Display) { output('\r'); clreol(); } else output(' '); return 1; /* RETURN */ } output(' '); error: if (mem == 2) hugefreep(files); if (mem) hugefreep(clusters); if (status == NO_MEM) { set_error(NULL,module,status,"initfiles"); if (mem) set_err_arg("files:%lu",(long) \ (((long)num_clusters*sizeof(int))+1) + \ ((n_files+10)*sizeof(struct files_t))); else set_err_arg("clusters:%lu", \ (long)((long)num_clusters*sizeof(int))+1); } return 0; } /*** *getdir - read in ALL files on the drive * * * ver 2.1 04-Sep-1994 twirl chars; struct DIR, namecpy() ****/ static int getdir(unsigned long sector,int parent,int nsecs) { unsigned int i,l; unsigned char *buffer; int status; dDIR *dir; status = 0; if (!Display) { static int ind; print("%c\b","\\|/-"[++ind%4]); /* for slow machines */ /*if (ind == 5) ind = 0;*/ /* get rid of %4 and add this */ } if ((buffer=(unsigned char *)alloc(sec_size,sizeof(char)))==NULL) { status = NO_MEM; goto error; } while (nsecs-- >0) { if (diskio(DISK_READ,sector++,buffer) != DISK_OK) goto error; dir = (dDIR *)buffer; for (i = 0; i < sec_size/sizeof(dDIR); i++,dir++) { if (dir->name[0] == ERACHR || dir->name[0] == DIRCHR) continue; if (dir->name[0] == ENDDIR) { freep(buffer); return n_files; /* so return */ } l = dir->start; if (dir->attr.volume) { namecpy(volume,(char *)dir->name); continue; } if (l == 0) /* skip 0 length files */ continue; namecpy(files[n_files].name,(char *)dir->name); files[n_files].parent=parent; ++n_files; if (n_files == tn_files) /* trying to write past end? */ { status = NUM_FILES; goto error; } if (l > num_clusters) { status = INV_ST_CLUS; goto error; } if (dir->attr.subdir) /* is a directory? */ { register int k; register int nf = n_files-1; if (Display) { print("\r%s",files[nf].name); clreol(); } ++n_dirs; files[nf].dir = 1; /* get subdirectory */ if (!getdir(clustertosector(l),nf,secs_cluster)) goto error; while ((k = clusters[l]) > 0) { clusters[l] = nf; if (!getdir(clustertosector(k),nf,secs_cluster)) goto error; l = k; } clusters[l] = nf; } else { register int k; register int nf = n_files-1; while ((k = clusters[l]) > 0) { if ((unsigned int)k == l) { status = FAT_PHASE; goto error; } clusters[l] = nf; l = k; } clusters[l] = nf; } } } freep(buffer); return n_files; error: freep(buffer); if (status) { set_error(err_msg[status],module,status,"getdir"); if (status == INV_ST_CLUS) set_err_arg(" \"%s\" (%04x)",gfile(n_files-1),l); else if (status == NUM_FILES) set_err_arg(" (%d)",n_files); } return 0; } /* convert "AAAA XXX" to "AAAA.XXX\0" */ __inline static void namecpy(char *n, char *d) { int j; for (j = 0; j < FILELEN; j++,d++) { if (*d == ' ') continue; if (j == FILENAM) *n++ = '.'; *n++ = *d; } *n = '\0'; } /* How FATs work: function: getfat12(number_of_sectors, number_of_clusters) reads all FAT sectors into a buffer and then convert the buffer into the clusters[] array. 12 bit: used on drives with up to 4086 clusters start from fatbuf[3], taking 3 unsigned chars at a time: ___ take the 0 from 40 (last 4 bits) and put it in || \ front of the 03 which results in 003 03 40 00 \__|| take the 4 from 40 (first 4 bits) and put it in back of the 00 which results in 004 But Remember: int's are stored in memory Least Significant Byte first. 000 unused ff7 bad ff8-fff last used cluster of the file What MS-DOS Programmer's Reference says: Start with the starting cluster number. Multiply cluster number just used by 1.5. The whole part of the product is an offset into the FAT, pointing to the entry that maps the cluster just used. That entry contains the cluster number of the next cluster of the file. Get the entry. If the last cluster used was an even number AND the entry with 0xfff to keep the low-order 12 bits otherwise shift it to the right 4 times. 16 bit: drives > 4086 clusters start from fatbuf[4], take an integer at a time (2 bytes) 03 00 results in 0003 0000 unused fff7 bad fff8-ffff last used cluster of the file */ static int getfat12(int nsecs,unsigned nclusters) { register unsigned int h,n; unsigned int l; int status,i; int nf; unsigned char *buffer; unsigned char *bufptr; unsigned int *po; unsigned int *pe; int stat; stat = status = nf = 0; h = l = 0; if ((buffer = (unsigned char *)alloc(sec_size*nsecs,sizeof(char))) == NULL) { status = NO_MEM; goto error; } /* read logical sectors 1 to nsecs */ stat = 1; for (i = 1, bufptr = buffer; i <= nsecs; i++, bufptr += sec_size) if (diskio(DISK_READ,(long)i,bufptr) != DISK_OK) goto error; for (h = 2, n = 3; h <= nclusters; n+=3) { pe = (unsigned int *)(buffer+n); po = (unsigned int *)(buffer+n+1); l = *pe&0xfff; if (l >= 0xff8) /* end of file */ { clusters[h] = -1; ++nf; } else if (l==0xff7) /* bad */ clusters[h] = -2; else clusters[h] = l; if (++h > nclusters) break; if (l > nclusters && l < 0xff7) { status = INV_CLUS; goto error; } l = *po>>4; if (l >= 0xff8) { ++nf; clusters[h] = -1; } else if (l == 0xff7) clusters[h] = -2; else clusters[h] = l; if (++h > nclusters) break; if (l > nclusters && l < 0xff7) { status = INV_CLUS; goto error; } } freep(buffer); return nf; error: if (stat) freep(buffer); if (status) { set_error(err_msg[status],module,status,"getfat12"); if (status == NO_MEM) set_err_arg("%u",sec_size*nsecs); else if (status == INV_CLUS) set_err_arg("entry: %d value: %x",h,l); } return ERROR; } /* ver 2.0 14-Nov-1993 fixed bug where not all sectors read (oops!) (do..while tested: sector < nsecs) */ static int getfat16(int nsecs,unsigned nclusters) { register unsigned int h = 0; unsigned int *pi; int n; unsigned int l = 0; int status; int nf; unsigned int sector; unsigned char *buffer; int stat; stat = status = nf = 0; /* allocate space for the FAT to be read */ /* NOTE: sectors are read one at a time due due to 16 bit FATs being usually large. */ if ((buffer=(unsigned char *)alloc(sec_size,sizeof(char)))==NULL) { status = NO_MEM; goto error; } stat = 1; sector = reserved_secs; h = 2; do { if (diskio(DISK_READ,(long)sector,buffer) != DISK_OK) { goto error; } if (sector == reserved_secs) n = 4; /* skip first 4 bytes in first sector */ else n = 0; for (;n<(int)sec_size;n+=2) { pi = (unsigned int *)(buffer+n); l = *pi; if (l >= 0xfff8) /* end of entries */ { ++nf; /* increment file count */ clusters[h] = -1; /* mark file end */ } else if (l == 0xfff7) /* cluster marked bad */ clusters[h] = -2; /* flag it */ else clusters[h] = l; /* else assume good cluster entry */ if (++h > nclusters) break; if (l > nclusters && l < 0xfff7) /* check for out-of-range number */ { status = INV_CLUS; goto error; } } ++sector; } while (sector < (unsigned)nsecs + reserved_secs); freep(buffer); return nf; error: if (stat) freep(buffer); if (status) { set_error(err_msg[status],module,status,"getfat16"); if (status == NO_MEM) set_err_arg("%u",sec_size); else if (status == INV_CLUS) set_err_arg("entry: %d value: %x",h,l); } return ERROR; } /* print file name */ extern int pfile(char *file, unsigned int i) { while (i != 0) i = pfile(files[i].name,files[i].parent); print("\\%s",file); return files[i].parent; } /* get file name matched to cluster number */ #define F_LEV 10 /* directory name depth level */ extern char *gfile(unsigned int index) { static char buf[67]; int flev[F_LEV],i; buf[0] = '\0'; i=0; memset(flev,0,sizeof(int)*F_LEV); while (index != 0) { if (i > F_LEV) { i = 0; strcpy(buf,"max lev error"); break; } flev[i++] = index; index = files[index].parent; } while (i) { strcat(buf,"\\"); strcat(buf,files[flev[i-1]].name); --i; } return buf; } /* * Findfile: Returns the first cluster number of the passed filename * if it is in the files structure. * *** BUG *** sometimes finds improperly; "temp" finds first string whether its "\foo\temp" or "\win\system\temp" * ver 0.0 9/91 */ extern unsigned int findfile(char *file) { register char *token; register unsigned int i,j; token = strtok(file,"\\"); for (i = 0; i < n_files; i++) { if (stricmp(token,files[i].name) == 0) { token = strtok(NULL,"\\"); if (token == NULL) { for (j = 2; j < num_clusters; j++) if (clusters[j] > 0 && (unsigned)clusters[j] == i) return j; break; } } } return 0; } extern unsigned int get_avail_clusters(void) { union REGS regs; unsigned c,j; if (!Files) { regs.x.ax = 0x3600; regs.x.dx = disk; intdos(®s,®s); return regs.x.bx; } else for (c = 0,j = 2; j <= num_clusters; j++) if (clusters[j] == 0) ++c; return c; } #ifdef MAP_FATS /* display FAT -- not done yet */ extern void mapfat12(int sec) { register unsigned int h,n; int i; unsigned char *buffer = sec_buf; unsigned int *po; unsigned int *pe; if (sec == sec) n = 3; send('\n'); for (h=2,n=3;n>4); } } } #endif