Posix Directory Browsing API for Windows
Related Blog Items
- Pelt: Posix Wrapper for Windows Threads
- dlwrapper: POSIX Wrapper for Windows Dynamic Library Loading Calls
- c/c++ blogging - January 10, 2007
- gettimeofday function for windows
- A simple window application
APIs specified in POSIX for browsing through directories are implemneted in windows. This implementation is
a wrapper around windows native calls. Using these POSIX function calls, we can port programs related to
directory browsing and use these calls without learning windows specific calls.
Here are the POSIX APIs which I’ve implemented
opendir
closedir
readdir
rewinddir
seekdir
telldir
scandir
ftw
dirfd
stat
here is the dirent structure, in this structure, we are not going to use some members like d_ino, d_off, d_reclen.
struct dirent{ long d_ino; long d_off; unsigned short d_reclen; unsigned short d_namlen; char d_name[1]; };
Here is the structure used for implementing these posix calls, we should not directly access these members.
struct DIR { long handle; long int pos; struct _finddata_t info; struct dirent d_ent; };
here are the posix directory browsing APIs implementation
Opendir:
DIR *opendir(const char *name);
DESCRIPTION
The opendir() function opens a directory stream corresponding to the directory name, and returns a pointer to the directory stream. The stream is positioned at the first entry in the directory.
RETURN VALUE
The opendir() function returns a pointer to the directory stream. On error, NULL is returned, and errno is set appropriately.
ERRORS
EACCES
Permission denied.
EMFILE
Too many file descriptors in use by process.
ENFILE
Too many files are currently open in the system.
ENOENT
Directory does not exist, or name is an empty string.
ENOMEM
Insufficient memory to complete the operation.
ENOTDIR
name is not a directory.
NOTES
The underlying file descriptor of the directory stream can be obtained using dirfd(3).
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
IMPLEMENTATION
DIR *opendir(const char *name) { DIR *dir = NULL; const char *all; long len; if((NULL == name) || ('' == *name)) { errno = EINVAL; return dir; } len = strlen(name); all = strchr("/\", name[len - 1]) ? "*" : "/*"; len += strlen(all) + 1; dir = (DIR *) malloc((len*sizeof(char)) + sizeof(*dir)); if(NULL == dir) { errno = ENOMEM; return NULL; } dir->d_ent.d_namlen = len; dir->pos = 0; strcpy(dir->d_ent.d_name, name); strcat(dir->d_ent.d_name, all); dir->handle = (long) _findfirst(dir->d_ent.d_name, &dir->info); if(dir->handle == -1) { free(dir); dir = NULL; } return dir; }
Closedir:
int closedir(DIR *dir);
DESCRIPTION
The closedir() function closes the directory stream associated with dir. The directory stream descriptor dir is not available after this call.
RETURN VALUE
The closedir() function returns 0 on success. On error, -1 is returned, and errno is set appropriately.
ERRORS
EBADF
Invalid directory stream descriptor dir.
CONFORMING TO
SVr4, POSIX.1-2001, 4.3BSD
IMPLEMENTATION
int closedir(DIR *dir) { int result = -1; if(dir) { if(dir->handle != -1) { result = _findclose(dir->handle); } free(dir); } if(result == -1) { errno = EBADF; } return result; }
Readdir:
struct dirent *readdir(DIR *dir);
DESCRIPTION
The readdir() function returns a pointer to a dirent structure representing the next directory entry in the directory stream pointed to by dir. It returns NULL on reaching the end-of-file or if an error occurred.
According to POSIX, the dirent structure contains a field char d_name[] of unspecified size, with at most NAME_MAX characters preceding the terminating null byte. POSIX.1-2001 also documents the field ino_t d_ino as an XSI extension. Use of other fields will harm the portability of your programs.
The data returned by readdir() may be overwritten by subsequent calls to readdir() for the same directory stream.
RETURN VALUE
The readdir() function returns a pointer to a dirent structure, or NULL if an error occurs or end-of-file is reached. On error, errno is set appropriately.
ERRORS
EBADF
Invalid directory stream descriptor dir.
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001
IMPLEMENTATION
struct dirent *readdir(DIR *dir) { struct dirent *result = 0; if(dir && dir->handle != -1) { if(_findnext(dir->handle, &dir->info) != -1) { result = &dir->d_ent; strcpy(result->d_name, dir->info.name); dir->pos ++; } } else { errno = EBADF; } return result; }
Rewinddir:
void rewinddir(DIR *dir);
DESCRIPTION
The rewinddir() function resets the position of the directory stream dir to the beginning of the directory.
RETURN VALUE
The rewinddir() function returns no value.
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
IMPLEMENTATION:
void rewinddir(DIR *dir) { if(dir && dir->handle != -1) { _findclose(dir->handle); dir->handle = (long) _findfirst(dir->d_ent.d_name, &dir->info); dir->pos = 0; } else { errno = EBADF; } }
Dirfd:
int dirfd(DIR *dir);
DESCRIPTION
The function dirfd() returns the file descriptor associated with the directory stream dir.
This descriptor is the one used internally by the directory stream. As a result, it is only useful for functions which do not depend on or alter the file position, such as fstat(2) and fchdir(2). It will be automatically closed when closedir(3) is called.
ERRORS
On error -1 is returned.
NOTES
The prototype for dirfd() is only available if _BSD_SOURCE or _SVID_SOURCE is defined.
CONFORMING TO
This is a BSD extension, present in 4.3BSD-Reno, not in 4.2BSD. It is present in libc5 (since 5.1.2) and in glibc2. This function is under consideration for inclusion in a future version of POSIX.1.
IMPLEMENTATION:
int dirfd(DIR *dirp) { if (NULL == dirp) { return -1; } return dirp->handle; }
Seekdir:
void seekdir(DIR *dir, off_t offset);
DESCRIPTION
The seekdir() function sets the location in the directory stream from which the next readdir() call will start. seekdir() should be used with an offset returned by telldir().
RETURN VALUE
The seekdir() function returns no value.
CONFORMING TO
4.3BSD, POSIX.1-2001.
IMPLEMENTATION
void seekdir (DIR *dirp, long int pos) { long int i; if (NULL == dirp) { return; } if (pos<0) { pos = dirp->pos + pos; _findclose(dirp->handle); dirp->handle = (long) _findfirst(dirp->d_ent.d_name, &dirp->info); } for (i=0; ihandle, &dirp->info) != -1) { dirp->pos ++; } else { break; } } }
Telldir:
long int telldir(DIR *dir);
DESCRIPTION
The telldir() function returns the current location associated with the directory stream dir.
RETURN VALUE
On success, the telldir() function returns the current location in the directory stream. On error, -1 is returned, and errno is set appropriately.
ERRORS
EBADF
Invalid directory stream descriptor dir.
CONFORMING TO
4.3BSD, POSIX.1-2001.
IMPLEMENTATION
long int telldir (DIR *dirp) { if (NULL == dirp) { return -1; } return dirp->pos; }
Scandir:
int scandir(const char *dir, struct dirent ***namelist,
int(*filter)(const struct dirent *), int(*compar)(const struct dirent **, const struct dirent **));
int alphasort(const void *a, const void *b);
int versionsort(const void *a, const void *b);
DESCRIPTION
The scandir() function scans the directory dir, calling filter() on each directory entry. Entries for which filter() returns non-zero are stored in strings allocated via malloc(), sorted using qsort() with the comparison function compar(), and collected in array namelist which is allocated via malloc(). If filter is NULL, all entries are selected.
The alphasort() and versionsort() functions can be used as the comparison function compar(). The former sorts directory entries using strcoll(3), the latter using strverscmp(3) on the strings (*a)->d_name and (*b)->d_name.
RETURN VALUE
The scandir() function returns the number of directory entries selected or -1 if an error occurs.
The alphasort() and versionsort() functions return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
ERRORS
ENOMEM
Insufficient memory to complete the operation.
IMPLEMENTATION
int scandir(const char *dir, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { struct dirent *result = 0; struct dirent **namlist; int namelistlen = 0; int num_enrties= 0; DIR *dirp = opendir(dir); if (NULL == dirp) { return -1; } if(dirp && dirp->handle != -1) { while(_findnext(dirp->handle, &dirp->info) != -1) { num_enrties++; } } rewinddir(dirp); namlist = (struct dirent **) malloc( num_enrties * sizeof(struct dirent *)); if (NULL == namlist) { closedir(dirp); return namelistlen; } if(dirp && dirp->handle != -1) { while(_findnext(dirp->handle, &dirp->info) != -1) { result = (struct dirent *) malloc( sizeof(struct dirent) + strlen(dirp->info.name) + 1); strcpy(result->d_name, dirp->info.name); if (filter) { if (filter(result)) { namlist[namelistlen] = result; namelistlen++; } } else { namlist[namelistlen] = result; namelistlen++; } } } //qdirsort(namlist, namelistlen, compar); //todo *namelist = namlist; closedir(dirp); return namelistlen; }
Ftw:
int ftw(const char *dirpath,
int (*fn) (const char *fpath, const struct stat *sb,
int typeflag),
int nopenfd);
DESCRIPTION
ftw() walks through the directory tree that is located under the directory dirpath, and calls fn() once for each entry in the tree. By default, directories are handled before the files and subdirectories they contain (pre-order traversal).
To avoid using up all of the calling process?s file descriptors, nopenfd specifies the maximum number of directories that ftw() will hold open simultaneously. When the search depth exceeds this, ftw() will become slower because directories have to be closed and reopened. ftw() uses at most one file descriptor for each level in the directory tree.
For each entry found in the tree, ftw() calls fn() with three arguments: fpath, sb, and typeflag. fpath is the pathname of the entry relative to dirpath. sb is a pointer to the stat structure returned by a call to stat for fpath. typeflag is an integer that has one of the following values:
FTW_F
fpath is a normal file.
FTW_D
fpath is a directory.
FTW_DNR
fpath is a directory which can?t be read.
FTW_NS
The stat(2) call failed on fpath, which is not a symbolic link.
If fpath is a symbolic link and stat(2) failed, POSIX.1-2001 states that it is undefined whether FTW_NS or FTW_SL (see below) is passed in typeflag.
To stop the tree walk, fn() returns a non-zero value; this value will become the return value of ftw(). As long as fn() returns 0, ftw() will continue either until it has traversed the entire tree, in which case it will return zero, or until it encounters an error (such as a malloc(3) failure), in which case it will return -1.
Because ftw() uses dynamic data structures, the only safe way to exit out of a tree walk is to return a non-zero value from fn(). To allow a signal to terminate the walk without causing a memory leak, have the handler set a global flag that is checked by fn(). Don?t use longjmp(3) unless the program is going to terminate.
RETURN VALUE
These functions return 0 on success, and -1 if an error occurs.
If fn() returns non-zero, then the tree walk is terminated and the value returned by fn() is returned as the result of ftw() or nftw().
CONFORMING TO
POSIX.1-2001, SVr4, SUSv1.
IMPLEMENTATION
int ftw(const char *dirpath, int (*fn) (const char *fpath, const struct stat *sb, int typeflag), int nopenfd) { struct dirent *result = 0; struct dirent **namlist; int namelistlen = 0; int num_enrties= 0; struct stat sb; int typeflag = 0; DIR *dirp = opendir(dirpath); if (NULL == dirp) { return -1; } if (CallFn(fn, dirpath, dirp)) { closedir(dirp); return 0; } if(dirp && (dirp->handle != -1)) { while(_findnext(dirp->handle, &dirp->info) != -1) { if ((dirp->info.attrib & _A_SUBDIR) && (strcmp(dirp->info.name, ".")) && (strcmp(dirp->info.name, ".."))) { if (PreOrderTraversal(dirp, fn)>0) { break; } } else { if (CallFn(fn, dirp->info.name, dirp)) { break; } } } } closedir(dirp); return 0; }
Stat: (This is not related to directory browsing, but useful)
int stat(const char *path, struct stat *buf); int fstat(int filedes, struct stat *buf); int lstat(const char *path, struct stat *buf);
DESCRIPTION
These functions return information about a file. No permissions are required on the file itself, but ? in the case of stat() and lstat() ? execute (search) permission is required on all of the directories in path that lead to the file.
stat() stats the file pointed to by path and fills in buf.
lstat() is identical to stat(), except that if path is a symbolic link, then the link itself is stat-ed, not the file that it refers to.
fstat() is identical to stat(), except that the file to be stat-ed is specified by the file descriptor filedes.
All of these system calls return a stat structure, which contains the following fields:
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
The st_dev field describes the device on which this file resides.
The st_rdev field describes the device that this file (inode) represents.
The st_size field gives the size of the file (if it is a regular file or a symbolic link) in bytes. The size of a symlink is the length of the pathname it contains, without a trailing null byte.
The st_blocks field indicates the number of blocks allocated to the file, 512-byte units. (This may be smaller than st_size/512, for example, when the file has holes.)
The st_blksize field gives the ?preferred? blocksize for efficient file system I/O. (Writing to a file in smaller chunks may cause an inefficient read-modify-rewrite.)
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
ERRORS
EACCES
Search permission is denied for one of the directories in the path prefix of path. (See also path_resolution(2).)
EBADF
filedes is bad.
EFAULT
Bad address.
ELOOP
Too many symbolic links encountered while traversing the path.
ENAMETOOLONG
File name too long.
ENOENT
A component of the path path does not exist, or the path is an empty string.
ENOMEM
Out of memory (i.e. kernel memory).
ENOTDIR
A component of the path is not a directory.
CONFORMING TO
These system calls conform to SVr4, 4.3BSD, POSIX.1-2001.
IMPLEMENTATION
int stat(const char *path, struct stat *buf) { struct _finddata_t info; long handle; mode_t mode = 0; if(!buf || !path) return -1; handle = _findfirst(path, &info); _findclose(handle); buf->st_size = info.size; buf->st_atime = info.time_access; buf->st_ctime = info.time_create; /* | dir/regular | systemfile | readonly | write |*/ mode = (info.attrib & _A_SUBDIR) st_mode = mode; return 0; }
Please let me know your comments, and mail me bug reports.
here are the implementation files..
Popularity: 8%
You need to log on to convert this article into PDF
Related Blog Items - Pelt: Posix Wrapper for Windows Threads
- dlwrapper: POSIX Wrapper for Windows Dynamic Library Loading Calls
- c/c++ blogging - January 10, 2007
- gettimeofday function for windows
- A simple window application
Related Blog Items
- Pelt: Posix Wrapper for Windows Threads
- dlwrapper: POSIX Wrapper for Windows Dynamic Library Loading Calls
- c/c++ blogging - January 10, 2007
- gettimeofday function for windows
- A simple window application
Nice article, you made at least one happy guy posting these win32 APIs ;-)
February 29th, 2008 at 11:19 pm
thanks! i’m porting my app to Mac OSX. doing this I need to test some posix functionality in the original win32 environment. this helps a lot!
March 17th, 2008 at 11:37 pm