home *** CD-ROM | disk | FTP | other *** search
- Subject: MSDOS directory access routine
- Newsgroups: mod.sources
- Approved: jpn@panda.UUCP
-
- Mod.sources: Volume 3, Issue 60
- Submitted by: genrad!decvax!ihnp4!homebru!ignatz
-
- Several weeks ago, James Gillogly submitted this routine, directory(),
- for manipulating MS/PC-DOS directories. While it was quite useful,
- I've found that there were some things that I really needed; the
- result is the modified directory routine below. It's been in use for
- some time now, so I think all the bugs are out. Specifically, the
- changes I added are:
-
- - Provided a calling convention for the release of the
- acquired memory. (If processing multiple wildcard entries
- on the same command line, the old version ate memory)
-
- - Converted to run under both Aztec and Lattice 'C'
-
- - Modified returned namelist so that it's a null-terminated
- list of pointers, a la 'argv'
-
- - Provided an externally defined flag that allows the savvy
- user to specify inclusion or exclusion of different file
- types; most useful for finding subdirectories.
-
- - Modified so that it returns the full path, and the drive
- specifier if provided, rather than just the filename. This
- makes the returned filename strings directly usable by
- routines such as 'open', 'creat', etc.
-
- I hope you find these useful; I have.
-
- Dave Ihnat
- Analysts International Corporation
- ihnp4!aicchi!ignatz || ihnp4!homebru!ignatz
-
- ====================== Hier Schneiden =============================
- /* direct.c - Search a directory for files matching the description, and
- * return a sorted list of the matching files. Supports drive name,
- * path, and wildcards. Expand your argv and amaze your friends.
- *
- * For use with Lattice C; last compiled with version 2.15.
- * Requires STDIO.H and DOS.H .
- *
- * IF LATTICE:
- * Allocates space for the filenames with getmem, so be careful with
- * your rlsmem's.
- *
- * ELSE uses malloc(),free()
- *
- * (c) 1984 James J. Gillogly. All rights reserved.
- * Copyright abandoned 28 Sep 1985.
- *
- * Modified 10/6/85 by D. M. Ihnat
- *
- * Usage: n = directory(filename, names);
- * integer n; Returns number of files found.
- * char *filename; May include drive name, path, *, and ?
- * char ***names; Address of the char ** you want pointing to the list.
- *
- * sel_disk(letter); char letter;
- * Sets current drive to this one (A, B, etc); returns NO on error.
- * n = directory(filename, names); char *filename, **names;
- * Search current directory for this filename (may include ?'s), and
- * allocate enough space for all the resulting filenames.
- * Returns number of files found.
- * first_file(filename, buffer)
- * Looks for the first file set up in the global file control block;
- * returns NO if none.
- * next_file(buffer)
- * Looks for the next file; must be preceded by a first_file().
- * set_dta(adr); char *adr;
- * Sets the disk transfer address used by the directory functions.
- *
- * 29 Aug 84: sel_disk(letter), directory(), first_file(), next_file(),
- * set_dta(adr)
- *
- * 17 Sep 85: changed the calling sequence for directory:
- * n = directory(filename, names); char *filename, ***names;
- * It now takes the address of a char ** pointer pointer.
- * Support drive letters and paths (requires DOS 2.0 or higher).
- *
- * 6 Oct 85: Modified by Dave Ihnat
- * - Supports both Lattice and non-Lattice compilers (notably Aztec)
- * - Now returns a pointer to a NULL-terminated namelist , similar to **argv
- * - Supports a call of the form directory((char *)NULL,names) to
- * properly release used memory. Returns 0 if release OK, else -1.
- * - Added extern unsigned _dir_mode_ to select type of search. Changes
- * to this flag are only valid when starting a search; it is a
- * logical OR of:
- * 0x0 : Normal files (default)
- * 0x1 : Read-only files
- * 0x2 : Hidden files
- * 0x4 : System files
- * 0x8 : Volume ID (not recommended)
- * 0x10 : Directories
- *
- * 24 Oct 85: Modified by Dave Ihnat
- * - Modified to check for existence of a drive designation or a path
- * prefix; if one exists, remember it and prepend it to every filename
- * returned. This makes the returned file list directly usable by
- * in calls such as open(), close().
- */
-
- #include <stdio.h>
- #include <ctype.h>
-
- #ifdef LATTICE
- #include <dos.h>
- #define MEMALLOC getmem
- #define MEMFREE(a,b) rlsmem(a,b)
- #else
- #include <ctype.h>
- #define MEMALLOC malloc
- #define MEMFREE(a,b) free(a)
- #endif
-
- /*
- * Both Aztec 3.20d and Lattice 2.15 have strchr/strrchr; this is for any
- * others who may NOT.
- */
- #define RTCHAR strrchr
-
- #define SEL_DISK 0x0E /* BDOS: select current disk drive */
- #define SET_DTA 0x1A /* BDOS: set disk transfer address */
- #define FIND_FRST 0x4E /* BDOS: find first file, long form */
- #define FIND_NEXT 0x4F /* BDOS: find next file, long form */
-
- #define ERROR 0xFF /* BDOS: error return */
- #define NOTFOUND 2 /* Error return: file not found */
- #define NOMORE 18 /* Error return: no more files */
-
- #define YES 1
- #define NO 0
-
- #define MAXNAMES 100 /* temp - will get cleverer later */
-
- struct dirstruct /* temporary holding area for entries */
- { struct dirstruct *next; /* pointer to next entry */
- char *fname; /* filename of this entry */
- };
-
- struct EFCB /* FCB for path-type calls */
- { char reserved[21];
- char attrib_found;
- char file_time[2];
- char file_date[2];
- char low_size[2];
- char high_size[2];
- char file_name[13];
- };
-
- unsigned _dir_mode_ = 0; /* Initialize to use only normal files */
-
- directory(filename, names) /* look up filename in the directory */
- char *filename, ***names;
- { char buffer[80];
- struct EFCB dta;
- register struct dirstruct *p,*q;/* trundle down the directory list */
- struct dirstruct *dir_root; /* root of directory list */
- register int n; /* number of entries */
- register int i, j;
- char *ptr_prefix; /* For storage of any prefix data */
- int len_prefix;
-
- char *MEMALLOC();
- char *RTCHAR();
-
- /* If filename is a null path, then user is trying to release memory */
- if (filename == (char *)NULL)
- return(free_direct(names));
-
- /*
- * If there is a drive designator or a path in the filename,
- * save it. Check first for a path; if there is one, then
- * the drive designator, if any, will be saved as a side effect.
- * Otherwise, explicitly save the descriptor. Notice that, as either
- * the MS-DOS directory delimiter '\', or the Unix-style '/' may
- * be given to MS-DOS from within a program, BOTH must be tested!
- */
- len_prefix = 0;
-
- if(((ptr_prefix = RTCHAR(filename,'\\')) != (char *)NULL) ||
- ((ptr_prefix = RTCHAR(filename,'/')) != (char *)NULL) )
- {
- /* Path; get storage, and copy it */
-
- char *srcptr,*dstptr,*endptr; /* Local block storage */
-
- len_prefix = (ptr_prefix - filename) +2; /* Allow for null */
-
- endptr = ptr_prefix+1; /* Point just after the delimiter */
-
- ptr_prefix = MEMALLOC(len_prefix);
- for(srcptr=filename,dstptr=ptr_prefix;srcptr < endptr;dstptr++,srcptr++)
- *dstptr = _toupper(*srcptr);
-
- *dstptr = '\0'; /* Null terminate */
-
- }else if(filename[1] == ':')
- {
- /* Only a drive designator; save it. */
- ptr_prefix = MEMALLOC(3); /* 2 characters + null */
-
- ptr_prefix[0] = _toupper(filename[0]);
- ptr_prefix[1] = filename[1];
- ptr_prefix[2] = '\0';
- len_prefix = 2;
- }
-
- if (!first_file(filename, buffer, &dta))
- return 0; /* no files found */
- n = 1; /* one entry found so far */
- p->next = 0;
-
- dir_root = (struct dirstruct *) MEMALLOC(sizeof (struct dirstruct));
- p = dir_root; /* point at top of directory list */
-
- p->fname = (char *) MEMALLOC(strlen(buffer) + 1);
- strcpy(p->fname, buffer); /* save the first filename */
-
- while (next_file(buffer, &dta))
- { n++; /* looks like another winner */
- p->next = (struct dirstruct *) MEMALLOC(sizeof (struct dirstruct));
- p = p->next; /* point at the next entry */
- p->fname = (char *) MEMALLOC(strlen(buffer) + 1);
- strcpy(p->fname, buffer); /* save it */
- }
- p->next = 0; /* end of the line */
-
- /* (n names) + NULL ptr */
- *names = (char **) MEMALLOC((n+1) * sizeof (char *));
-
- for (i = 0, p = dir_root; i < n; i++) /* insertion sort on pointers */
- { for (j = i; j > 0; j--) /* where does this one go? */
- { if (strcmp((*names)[j - 1], p->fname) < 0) break;
- (*names)[j] = (*names)[j - 1];
- }
- (*names)[j] = (char *)MEMALLOC(strlen(p->fname)+len_prefix+1);
- if(len_prefix)
- {
- strcpy((*names)[j],ptr_prefix);
- strcat((*names)[j], p->fname);
- }else
- strcpy((*names)[j], p->fname);
-
- #ifdef VERBOSE
- printf("%s goes into slot %d.\n", p->fname, j);
- #endif VERBOSE
- q = p; /* save it so we can delete it */
- p = p->next; /* after pressing on to the next entry */
- MEMFREE(q, sizeof (struct dirstruct));
- }
-
- (*names)[n] = (char *)NULL; /* Provide a null terminator */
-
- if(len_prefix)
- MEMFREE(ptr_prefix,strlen(ptr_prefix)); /* Return the path memory */
-
- #ifdef VERBOSE
- printf("Here they are:\n");
- for (i = 0; i < n; i++)
- printf("\t%s\n", (*names)[i]);
-
- printf("Should be null terminator: 0x%x\n",(unsigned)names[n]);
- #endif VERBOSE
- return n;
- }
-
- first_file(filename, buffer, dta) /* set up FCB and get first file */
- char *filename, *buffer;
- struct EFCB *dta;
- { register char *s, *t;
- register int i, ret;
-
- for (s = filename; *s; s++) if (islower(*s)) *s = toupper(*s);
- #ifdef LARGEDATA
- ret = bdosx(SET_DTA, dta, 0); /* set up data transfer area */
- ret = bdosx(FIND_FRST, filename, _dir_mode_);
- #else
- ret = bdos(SET_DTA, dta, 0); /* set up data transfer area */
- ret = bdos(FIND_FRST, filename, _dir_mode_);
- #endif
- if (ret == NOTFOUND || ret == NOMORE) return NO;
- getname(buffer, dta); /* transfer the filename found to the caller */
- return YES;
- }
-
- next_file(buffer, dta) /* look up next file with the same attributes */
- char *buffer;
- struct EFCB *dta;
- { int ret;
-
- if (bdos(FIND_NEXT, 0, 0) == NOMORE) return NO;
- getname(buffer, dta);
- return YES;
- }
-
- getname(buffer, dta) /* transfer filename from dta to the buffer area */
- char *buffer;
- struct EFCB *dta;
- { strcpy(buffer, dta->file_name);
- }
-
-
- sel_disk(letter) /* make this disk the current one */
- char letter;
- { int ret;
-
- ret = bdos(SEL_DISK, letter - 'A', 0);
- return ret;
- }
-
- free_direct(names)
- char ***names;
- {
- int index;
-
- /*
- * Release each of the filename strings. There's room for
- * some twiddling here to avoid unnecessary strlen calls,
- * but this should be so rarely called that the overhead should
- * be acceptable. Feel free to change it, however, if you wish...
- */
- for(index = 0;(*names)[index] != (char *)NULL;index++)
- if(MEMFREE((*names)[index],(strlen((*names)[index])+1)))
- return(-1); /* Corrupted--don't trust anything */
-
- /* Now release the pointer structure */
- if(MEMFREE(*names,(index * sizeof(char *))))
- return(-1);
-
- /* Finally, remove any temptation to touch that released memory */
- *names = (char **)NULL;
-
- return(0);
- }
-
-
-