home *** CD-ROM | disk | FTP | other *** search
- /*
- * dirent.c - POSIX directory access routines for MS-DOS and OS/2
- *
- * Author: Frank Whaley (few@well.sf.ca.us)
- *
- * Copyright Frank Whaley 1992. All rights reserved.
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without fee,
- * provided that the above copyright notice appears in all copies of the
- * source code. The name of the author may not be used to endorse or
- * promote products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * CAVEATS:
- * The associated 'dirent.h' file should be copied into your system
- * include directory if the '#include <dirent.h>' syntax will be used,
- * otherwise a '-I.' switch must be added to command lines.
- *
- * This code was originally developed with Turbo C, and continues to
- * use TC's function and structure names. Numerous macros make the
- * code palatable to MSC 5.1/6.0 for MS-DOS and OS/2. The macros
- * depend on four defines: __TURBOC__, __MSC__, __MSDOS__, and __OS2__.
- * The TC and BC compilers provide __TURBOC__ and __MSDOS__ as
- * appropriate; MSC doesn't provide any of these flags so they must
- * be given on the command line. Sample commands for building test
- * programs (see '#ifdef TEST' below):
- * tcc -DTEST dirent.c
- * cl -DTEST -D__MSC__ -D__MSDOS__ dirent.c
- * cl -Lp -DTEST -D__MSC__ -D__OS2__ dirent.c
- *
- * This code reads an entire directory into memory, and thus is not
- * a good choice for scanning very large directories.
- *
- * POSIX requires that the rewinddir() function re-scan the directory,
- * so this code must preserve the original directory name. If the
- * name given is a relative path (".", "..", etc.) and the current
- * directory is changed, moved, or deleted between opendir() and
- * rewinddir(), a different directory will be scanned by rewinddir().
- * The directory name could be qualified by opendir(), but this process
- * yields unusable names for network drives.
- *
- * This code provides only file names, as that is all that is required
- * by POSIX. Considerable other information is available from the
- * MS-DOS and OS/2 directory search functions. This package should not
- * be considered as a general-purpose directory scanner, but rather as
- * a tool to simplify porting other programs.
- */
-
- #include <dirent.h>
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-
- #ifdef __TURBOC__
- # include <alloc.h>
- # include <dir.h>
- # include <mem.h>
- typedef struct ffblk FIND_T;
- # define findclose(f)
- # define FA_RDONLY 0x01
- # define FA_HIDDEN 0x02
- # define FA_SYSTEM 0x04
- # define FA_LABEL 0x08
- # define FA_DIREC 0x10
- # define FA_ARCH 0x20
- #endif
-
- #if defined(__MSC__) && defined(__MSDOS__)
- # include <dos.h>
- # include <malloc.h>
- # include <memory.h>
- typedef struct find_t FIND_T;
- # define findfirst(n,f,a) _dos_findfirst(n,a,f)
- # define findnext(f) _dos_findnext(f)
- # define findclose(f)
- # define ff_name name
- # define FA_RDONLY _A_RDONLY
- # define FA_HIDDEN _A_HIDDEN
- # define FA_SYSTEM _A_SYSTEM
- # define FA_LABEL _A_VOLID
- # define FA_DIREC _A_SUBDIR
- # define FA_ARCH _A_ARCH
- #endif
-
- #if defined(__MSC__) && defined(__OS2__)
- # define INCL_DOS
- # include <os2.h>
- # include <malloc.h>
- # include <memory.h>
- typedef struct {
- HDIR ft_handle;
- FILEFINDBUF ft_ffb;
- int ft_count;
- } FIND_T;
- # define findfirst(n,f,a) \
- ( \
- (f)->ft_handle = HDIR_CREATE, \
- (f)->ft_count = 1, \
- DosFindFirst(n, &(f)->ft_handle, a, &(f)->ft_ffb, \
- sizeof((f)->ft_ffb), &(f)->ft_count, 0L \
- ) \
- )
- # define findnext(f) \
- DosFindNext( \
- (f)->ft_handle, &(f)->ft_ffb, \
- sizeof((f)->ft_ffb), &(f)->ft_count \
- )
- # define findclose(f) DosFindClose((f)->ft_handle);
- # define ff_name ft_ffb.achName
- # define FA_RDONLY 0x01
- # define FA_HIDDEN 0x02
- # define FA_SYSTEM 0x04
- # define FA_LABEL 0x08
- # define FA_DIREC 0x10
- # define FA_ARCH 0x20
- #endif
-
- /* mask for all interesting files */
- #define ALL (FA_RDONLY+FA_HIDDEN+FA_SYSTEM+FA_DIREC)
-
- struct dirnames {
- struct dirnames *next; /* next name in directory */
- char name[1]; /* null terminated name; may be larger */
- };
-
- struct __DIRENT {
- struct dirnames *names; /* list of names */
- struct dirnames const *current; /* position (0 if at directory end) */
- char path[1]; /* null terminated path name; may be larger */
- };
-
- static char const pattern[] = "\\*.*";
-
- /* forward declarations */
- static int loadDir(DIR *dir);
- static void freenames(DIR *dir);
-
- /* Open the directory NAME for reading. */
- DIR *
- opendir(char const *name)
- {
- DIR *dir;
-
- if (!*name) {
- errno = ENOENT;
- return 0;
- }
-
- if (!(dir = malloc(sizeof(DIR) + strlen(name) + sizeof(pattern)-1))) {
- errno = ENOMEM;
- return 0;
- }
-
- strcpy(dir->path, name);
- if (loadDir(dir) != 0) {
- int e = errno;
- free(dir);
- errno = e;
- return 0;
- }
-
- return dir;
- }
-
- /* Close the directory DIR. */
- int
- closedir(DIR *dir)
- {
- freenames(dir);
- free(dir);
- return 0;
- }
-
- /* Yield the next entry in the directory DIR. */
- struct dirent *
- readdir(DIR *dir)
- {
- struct dirnames const *c = dir->current;
- if (!c)
- return 0;
- dir->current = c->next;
- return (struct dirent *) c->name;
- }
-
- /* Rewind the directory DIR; this requires rereading it. */
- void
- rewinddir(DIR *dir)
- {
- freenames(dir);
- loadDir(dir);
- }
-
- /* Load the directory DIR from disk. */
- static int
- loadDir(DIR *dir)
- {
- char *path = dir->path, *pathend = path + strlen(path);
- int mode;
- FIND_T ff;
- struct stat statb;
-
- /* Is it a directory? */
- if (stat(path, &statb) != 0)
- return -1;
- if ((statb.st_mode & S_IFMT) != S_IFDIR) {
- errno = ENOTDIR;
- return -1;
- }
-
- /* Build pattern string. */
- strcpy(pathend, pattern + !!strchr("\\/:", pathend[-1]));
-
- if (findfirst(path, &ff, ALL) == 0) {
- struct dirnames **np = &dir->names;
- do {
- /* Add name if not "." or ".." */
- if (ff.ff_name[0]!='.' ||
- ff.ff_name[1] && (ff.ff_name[1]!='.'||ff.ff_name[2])
- ) {
- struct dirnames *p = malloc(
- sizeof(struct dirnames)+strlen(ff.ff_name)
- );
- if (!p) {
- freenames(dir);
- errno = ENOMEM;
- *pathend = 0;
- return -1;
- }
- strcpy(p->name, ff.ff_name);
- p->next = 0;
- *np = p;
- np = &p->next;
- }
- } while (findnext(&ff) == 0);
- }
- findclose(&ff);
- dir->current = dir->names;
- *pathend = 0;
- return 0;
- }
-
- /* Free all names in the directory DIR. */
- static void
- freenames(DIR *dir)
- {
- struct dirnames *o = dir->names, *n;
- for (o = dir->names; o; o = n) {
- n = o->next;
- free(o);
- }
- }
-
- #ifdef TEST
- int
- main(int argc, char *argv[])
- {
- DIR *dir;
- struct dirent const *d;
- long pos;
-
- /* Check arguments. */
- if (argc != 2) {
- fprintf(stderr, "Usage: dirent <directory>\n");
- return 1;
- }
-
- /* Try to open the given directory. */
- if (!(dir = opendir(argv[1]))) {
- fprintf(stderr, "cannot open %s\n", argv[1]);
- return 1;
- }
-
- /* Walk the directory once forward. */
- while ((d = readdir(dir)))
- printf("%s\n", d->d_name);
-
- /* Rewind. */
- rewinddir(dir);
-
- /* Scan to the end again. */
- while ((d = readdir(dir)))
- continue;
-
- /* Close and exit. */
- printf("closedir() returns %d\n", closedir(dir));
- return 0;
- }
- #endif
-