| /* |
| |
| Implementation of POSIX directory browsing functions and types for Win32. |
| |
| Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) |
| History: Created March 1997. Updated June 2003. |
| Rights: See end of file. |
| |
| */ |
| |
| #include "win32-dirent.h" |
| #include <errno.h> |
| #include <io.h> /* _findfirst and _findnext set errno iff they return -1 */ |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #ifdef __cplusplus |
| extern "C" |
| { |
| #endif |
| |
| struct DIR |
| { |
| long handle; /* -1 for failed rewind */ |
| struct _finddata_t info; |
| struct dirent result; /* d_name null iff first time */ |
| char *name; /* null-terminated char string */ |
| }; |
| |
| DIR *opendir(const char *name) |
| { |
| DIR *dir = 0; |
| |
| if(name && name[0]) |
| { |
| size_t base_length = strlen(name); |
| const char *all = /* search pattern must end with suitable wildcard */ |
| strchr("/\\", name[base_length - 1]) ? "*" : "/*"; |
| |
| if((dir = (DIR *) malloc(sizeof *dir)) != 0 && |
| (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) |
| { |
| strcat(strcpy(dir->name, name), all); |
| |
| if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1) |
| { |
| dir->result.d_name = 0; |
| } |
| else /* rollback */ |
| { |
| free(dir->name); |
| free(dir); |
| dir = 0; |
| } |
| } |
| else /* rollback */ |
| { |
| free(dir); |
| dir = 0; |
| errno = ENOMEM; |
| } |
| } |
| else |
| { |
| errno = EINVAL; |
| } |
| |
| return dir; |
| } |
| |
| int closedir(DIR *dir) |
| { |
| int result = -1; |
| |
| if(dir) |
| { |
| if(dir->handle != -1) |
| { |
| result = _findclose(dir->handle); |
| } |
| |
| free(dir->name); |
| free(dir); |
| } |
| |
| if(result == -1) /* map all errors to EBADF */ |
| { |
| errno = EBADF; |
| } |
| |
| return result; |
| } |
| |
| struct dirent *readdir(DIR *dir) |
| { |
| struct dirent *result = 0; |
| |
| if(dir && dir->handle != -1) |
| { |
| if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) |
| { |
| result = &dir->result; |
| result->d_name = dir->info.name; |
| } |
| } |
| else |
| { |
| errno = EBADF; |
| } |
| |
| return result; |
| } |
| |
| void rewinddir(DIR *dir) |
| { |
| if(dir && dir->handle != -1) |
| { |
| _findclose(dir->handle); |
| dir->handle = (long) _findfirst(dir->name, &dir->info); |
| dir->result.d_name = 0; |
| } |
| else |
| { |
| errno = EBADF; |
| } |
| } |
| |
| // helper for scandir below |
| static void scandir_free_dir_entries(struct dirent*** namelist, int entries) { |
| int i; |
| if (!*namelist) return; |
| for (i = 0; i < entries; ++i) { |
| free((*namelist)[i]); |
| } |
| free(*namelist); |
| *namelist = 0; |
| } |
| |
| // returns the number of directory entries select or -1 if an error occurs |
| int scandir( |
| const char* dir, |
| struct dirent*** namelist, |
| int(*filter)(const struct dirent*), |
| int(*compar)(const void*, const void*) |
| ) { |
| int entries = 0; |
| int max_entries = 512; // assume 2*512 = 1024 entries (used for allocation) |
| DIR* d; |
| |
| *namelist = 0; |
| |
| // open directory |
| d = opendir(dir); |
| if (!d) return -1; |
| |
| // iterate |
| while (1) { |
| struct dirent* ent = readdir(d); |
| if (!ent) break; |
| |
| // add if no filter or filter returns non-zero |
| if (filter && (0 == filter(ent))) continue; |
| |
| // resize our buffer if there is not enough room |
| if (!*namelist || entries >= max_entries) { |
| struct dirent** new_entries; |
| |
| max_entries *= 2; |
| new_entries = (struct dirent **)realloc(*namelist, max_entries); |
| if (!new_entries) { |
| scandir_free_dir_entries(namelist, entries); |
| closedir(d); |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| *namelist = new_entries; |
| } |
| |
| // allocate new entry |
| (*namelist)[entries] = (struct dirent *)malloc(sizeof(struct dirent) + strlen(ent->d_name) + 1); |
| if (!(*namelist)[entries]) { |
| scandir_free_dir_entries(namelist, entries); |
| closedir(d); |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| // copy entry info |
| *(*namelist)[entries] = *ent; |
| |
| // and then we tack the string onto the end |
| { |
| char* dest = (char*)((*namelist)[entries]) + sizeof(struct dirent); |
| strcpy(dest, ent->d_name); |
| (*namelist)[entries]->d_name = dest; |
| } |
| |
| ++entries; |
| } |
| |
| // sort |
| if (*namelist && compar) qsort(*namelist, entries, sizeof((*namelist)[0]), compar); |
| |
| return entries; |
| } |
| |
| int alphasort(const void* lhs, const void* rhs) { |
| const struct dirent* lhs_ent = *(struct dirent**)lhs; |
| const struct dirent* rhs_ent = *(struct dirent**)rhs; |
| return _strcmpi(lhs_ent->d_name, rhs_ent->d_name); |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| /* |
| |
| Copyright Kevlin Henney, 1997, 2003. All rights reserved. |
| |
| Permission to use, copy, modify, and distribute this software and its |
| documentation for any purpose is hereby granted without fee, provided |
| that this copyright and permissions notice appear in all copies and |
| derivatives. |
| |
| This software is supplied "as is" without express or implied warranty. |
| |
| But that said, if there are any problems please get in touch. |
| |
| */ |