DIRLIB for MS-DOS ----------------- Enclosed is an implementation of the `dirlib' package for MS-DOS. The implementation is targeted for MS-C, although any reasonably competent C compiler should manage. The package consists of: dir.h the header file dir.c the functions testdir.c a q&d test program The package tries to view directory naming in a Un*x light; in particular, directories such as '/.' and '/..' (as well as `.' and `..' if your current directory is root) are understood. Indefinite paths like `/../.././../..' will correctly refer to the root (of the particular disk). Names such as `a:////./../' are okay too. I've tried to be as sensible about DTA's as possible, since you never know who will be using one; they are set before use, and reset afterwards. There is some cruft in the package, namely the way `seekdir' and `telldir' are done. The code was derived from a little experimentation, and may not work after a certain point (although I believe the 2.x version to be solid). Caveat utilitor. Documentation for the package is available in the public domain; the package's functionality was derived from this documentation. Bug reports and comments are welcome. Enjoy! - Matt ------- UUCP: {ucbvax,ihnp4,randvax,trwrb!trwspp,ism780}!ucla-cs!matt ARPA: matt@LOCUS.UCLA.EDU Ph: (213) 825-2756 ---------------------------- dirlib.h ------------------------------- /* DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89 */ #define MAXNAMLEN 15 struct direct { long d_ino; unsigned short d_reclen; unsigned short d_namlen; char d_name[MAXNAMLEN+1]; }; typedef struct { struct { char fcb[21]; char attr; short time; short date; long size; char name[13]; } dd_dta; short dd_dosver; /* which version of dos? */ short dd_stat; /* status return from last lookup */ char dd_name[1]; /* full name of file -- struct is extended */ } DIR; #ifdef LINT_ARGS DIR *opendir(char *); struct direct *readdir(DIR *); long telldir(DIR *); void seekdir(DIR *, long); void closedir(DIR *); #else DIR *opendir(); struct direct *readdir(); long telldir(); #endif LINT_ARGS #define rewinddir(dirp) seekdir(dirp,0L) --------------------------- dirlib.c --------------------------------- /* Directory Access Library DIRLIB.C by M. J. Weinstein Released to public domain 1-Jan-89 The author may be contacted at: matt@cs.ucla.edu -or- POB 84524, L.A., CA 90073 */ /* * revision history: * * VER MM/DD/YY COMMENTS * ---- -------- -------- * 0.99 02/24/86 Beta release to INTERNET */ #define LINT_ARGS #include #include #include #include #include #include #include "dir.h" #ifdef DEBUG # define PRIVATE #else # define PRIVATE static #endif DEBUG PRIVATE _err; PRIVATE DIR *_findfirst(char *, DIR *); PRIVATE DIR *_findnext(DIR *); PRIVATE char far *_getsetdta(char far *); #ifdef DEBUG #include PRIVATE void _dumpdir(DIR *); PRIVATE void _dumpdir(dirp) DIR *dirp; { int i; char *cp; printf("\ndump of DIR at %xH: fcb:", dirp); for (i = 0, cp = (char *)&dirp->dd_dta; i < sizeof(dirp->dd_dta.fcb); i++, cp++) { if ((i%16) == 0) printf("\n%3.3d (%3.3xH): ", i); printf("%3d ", (unsigned) *cp & 0xff); } printf("\nattr %xH time %d date %d size %ld name %s\n", dirp->dd_dta.attr, dirp->dd_dta.time, dirp->dd_dta.date, dirp->dd_dta.size, dirp->dd_dta.name); printf("ver %d stat %d pattern %s\n\n", dirp->dd_dosver, dirp->dd_stat, dirp->dd_name); } #endif DEBUG /* * return dos major version # */ PRIVATE int dosver() { union REGS r; r.h.ah = 0x30; intdos(&r, &r); return (int)r.h.al; } /* * get/set dta address */ PRIVATE char far *_getsetdta(newdta) char far *newdta; { char far *olddta; union REGS r; struct SREGS s; /* get old dta */ r.h.ah = 0x2f; intdos(&r, &r); segread(&s); FP_SEG(olddta) = s.es; FP_OFF(olddta) = r.x.bx; /* conditionally set new dta */ if (newdta) { r.h.ah = 0x1a; s.ds = FP_SEG(newdta); r.x.dx = FP_OFF(newdta); intdosx(&r, &r, &s); } return olddta; } /* * dos findfirst */ PRIVATE DIR *_findfirst(name, dirp) char *name; DIR *dirp; { union REGS r; struct SREGS s; char far *dtasave; dtasave = _getsetdta((char far *)dirp); /* do directory lookup */ segread(&s); r.h.ah = 0x4e; r.x.cx = 0x10; r.x.dx = FP_OFF((char far *)name); s.es = FP_SEG((char far *)name); intdosx(&r, &r, &s); /* restore dta */ _getsetdta(dtasave); _err = r.x.ax; if (r.x.cflag) return (DIR *) 0; #ifdef DEBUG _dumpdir(dirp); #endif DEBUG return dirp; } /* * dos findnext */ PRIVATE DIR *_findnext(dirp) DIR *dirp; { union REGS r; struct SREGS s; char far *dtasave; dtasave = _getsetdta((char far *)dirp); /* do directory lookup */ r.h.ah = 0x4f; intdos(&r, &r); /* restore old dta */ _getsetdta(dtasave); _err = r.x.ax; if (r.x.cflag) return (DIR *) 0; #ifdef DEBUG _dumpdir(dirp); #endif DEBUG return dirp; } /* * get working directory for a particular drive */ PRIVATE char *getdcwd(drive) int drive; { union REGS r; struct SREGS s; static char xcwd[64]; char far *cwd = xcwd; r.h.ah = 0x47; r.h.dl = drive; r.x.si = FP_OFF(cwd); s.ds = FP_SEG(cwd); intdosx(&r, &r, &s); _err = r.x.ax; if (r.x.cflag) return (char *) 0; return xcwd; } /* * opendir */ #define SUFFIX "\\*.*" #define SLASH "\\" #define streq(a,b) (strcmp(a,b)==0) DIR *opendir(name) char *name; { register DIR *nd; char *cwd; char drive[3]; int atroot = 0; int rooted = 0; /* * hack off drive designator if present */ if (name[1] == ':') { cwd = getdcwd(toupper(name[0]) - 'A' + 1); drive[0] = name[0]; drive[1] = ':'; drive[2] = '\0'; name += 2; } else { cwd = getdcwd(0); drive[0] = '\0'; } #ifdef DEBUG printf("working on drive %s = /%s\n", drive, cwd); #endif DEBUG /* is the name 'rooted'? */ if ((*name == '/') || (*name == '\\')) ++rooted; /* see if we are at the root directory for this device */ if (!*cwd) ++atroot; /* * MSDOS '/' doesn't have a '.' or '..' * also, double '/' sequences don't make sense. * many ported programs expect them to work, so we fix it up... */ /* chop off leading . and .. if at root */ if (atroot && (*name == '.')) { switch (*++name) { case '\0': case '/': case '\\': break; case '.': switch (*++name) { case '\0': case '/': case '\\': break; default: --name; --name; } break; default: --name; } } #ifdef DEBUG printf("after chopping leading .'s: %s\n", name); #endif DEBUG /* chop off leading /'s, /.'s and /..'s to make naming sensible */ while (*name && ((*name == '/') || (*name == '\\'))) { if (*++name == '.') { switch (*++name) { case '\0': case '/': case '\\': break; case '.': switch (*++name) { case '\0': case '/': case '\\': break; default: --name; --name; } break; default: --name; } } } #ifdef DEBUG printf("after chopping /'s: %s\n", name); #endif DEBUG /* * name should now look like: path/path/path * we must now construct name based on whether or not it * was 'rooted' (started with a /) */ if (rooted) cwd = ""; /* construct DIR */ if (!(nd = (DIR *)malloc( sizeof(DIR)+strlen(drive)+strlen(cwd)+strlen(SLASH)+ strlen(name)+strlen(SUFFIX)))) return (DIR *) 0; /* create long name */ strcpy(nd->dd_name, drive); if (*cwd) { strcat(nd->dd_name, SLASH); strcat(nd->dd_name, cwd); } if (*name) { strcat(nd->dd_name, SLASH); strcat(nd->dd_name, name); } strcat(nd->dd_name, SUFFIX); #ifdef DEBUG printf("calling findfirst(%s)\n", nd->dd_name); #endif DEBUG /* search */ if (!_findfirst(nd->dd_name, nd)) { free((char *)nd); errno = ENOENT; return (DIR *) 0; } nd->dd_stat = 0; nd->dd_dosver = dosver(); return nd; } struct direct *readdir(dirp) DIR *dirp; { static struct direct dir; if (dirp->dd_stat) return (struct direct *) 0; /* format structure */ dir.d_ino = 0; /* not valid for DOS */ dir.d_reclen = 0; strcpy(dir.d_name, dirp->dd_dta.name); dir.d_namlen = strlen(dir.d_name); strlwr(dir.d_name); /* DOSism */ /* read ahead */ if (_findnext(dirp)) dirp->dd_stat = 0; else dirp->dd_stat = _err; return &dir; } void closedir(dirp) DIR *dirp; { free((char *)dirp); } /* * fake seek for DOS 2.x */ PRIVATE void dos2seek(dirp, pos) DIR *dirp; long pos; { /* * check against DOS limits */ if ((pos < 0) || (pos > 4095)) { dirp->dd_stat = 1; return; } if (pos == 0) { if (_findfirst(dirp->dd_name, dirp)) dirp->dd_stat = 0; else dirp->dd_stat = _err; } else { pos--; dirp->dd_dta.fcb[1] = 2 - (pos % 3); *(short *)&dirp->dd_dta.fcb[13] = pos; /* read ahead */ if (_findnext(dirp)) dirp->dd_stat = 0; else dirp->dd_stat = _err; } } /* * fake seek for DOS 3.x */ PRIVATE void dos3seek(dirp, pos) DIR *dirp; long pos; { /* * check against DOS limits */ if ((pos < 0) || (pos > 4095)) { dirp->dd_stat = 1; return; } *(short *)&dirp->dd_dta.fcb[13] = pos + 1; /* read ahead */ if (_findnext(dirp)) dirp->dd_stat = 0; else dirp->dd_stat = _err; } void seekdir(dirp, newpos) DIR *dirp; long newpos; { switch (dirp->dd_dosver) { case 2: dos2seek(dirp, newpos); break; case 3: dos3seek(dirp, newpos); break; default: abort(); break; } } PRIVATE long dos2tell(dirp) DIR *dirp; { return (long) *(short *)&dirp->dd_dta.fcb[13]; } PRIVATE long dos3tell(dirp) DIR *dirp; { return (long) (*(short *)&dirp->dd_dta.fcb[13] - 2); } long telldir(dirp) DIR *dirp; { switch (dirp->dd_dosver) { case 2: return dos2tell(dirp); case 3: return dos3tell(dirp); default: abort(); } } ----------------------- testdir.c follows ------------------ /* Directory Access Library Test Program TESTDIR.C by M. J. Weinstein Released to public domain 1-Jan-89 The author may be contacted at: matt@cs.ucla.edu -or- POB 84524, L.A., CA 90073 */ #define LINT_ARGS #include #include #include #include #include "dir.h" #define streqi(a,b) (strcmpi(a,b) == 0) main() { char *token; char buff[128]; DIR *dir = 0; struct direct *dp; while (1) { printf("> "); if (!gets(buff)) break; if (!(token = strtok(buff, " \t\n"))) { printf("eh?\n"); } else if (streqi("open", token)) { if (dir) { closedir(dir); dir = 0; printf("previous directory closed\n"); } if (!(token = strtok(NULL, "\n"))) { printf("please specify directory name\n"); } else if (!(dir = opendir(token))) { perror(token); } else { printf("directory %s opened successfully\n", token); } } else if (streqi("close", token)) { if (!dir) { printf("no directory open\n"); } else { closedir(dir); dir = 0; printf("directory closed\n"); } } else if (streqi("rew", token)) { if (!dir) { printf("no directory open\n"); } else { rewinddir(dir); printf("directory rewind completed\n"); } } else if (streqi("seek", token)) { if (!dir) { printf("no directory open\n"); } else if (!(token = strtok(NULL, "\n"))) { printf("specify an entry number\n"); } else { seekdir(dir, atol(token)); printf("seek completed\n"); } } else if (streqi("read", token)) { if (!dir) { printf("no directory open\n"); } else if (!(dp = readdir(dir))) { perror("couldn't read entry"); } else { printf("ino=%2ld reclen=%2d namlen=%2d name=%s\n", dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_name); } } else if (streqi("tell", token)) { if (!dir) { printf("no directory open\n"); } else { printf("positioned at %ld\n", telldir(dir)); } } else if (streqi("dir", token)) { if (!dir) { printf("no directory open\n"); } else { while (dp = readdir(dir)) { printf("%-16.16s", dp->d_name); } putchar('\n'); } } else if (streqi("cd", token)) { if (!(token = strtok(NULL, "\n"))) { printf("specify a new directory\n"); } else if (chdir(token) < 0) { perror(token); } else { char buff[128]; printf("new directory is %s\n", getcwd(buff, sizeof(buff))); } } else if (streqi("quit", token) || streqi("exit", token)) { break; } else { printf("unknown command: %s\n", token); } } }