/*********************************************************************** * Copyright (c) 2009, Secure Endpoints Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * **********************************************************************/ #include #include #include #include #include #include "dirent.h" #ifndef _WIN32 #error Only implemented for Win32 #endif struct _dirent_dirinfo { int magic; long n_entries; long nc_entries; long cursor; struct dirent **entries; }; #define DIRINFO_MAGIC 0xf8c0639d #define IS_DP(p) ((p) && ((DIR *)(p))->magic == DIRINFO_MAGIC) #define INITIAL_ENTRIES 16 /** * Create a filespec for use with _findfirst() using a path spec * * If the last component of the path spec contains wildcards, we let * it be. If the last component doesn't end with a slash, we add one. */ static const char * filespec_from_dir_path(const char * path, char * buffer, size_t cch_buffer) { char *comp, *t; size_t pos; int found_sep = 0; if (strcpy_s(buffer, cch_buffer, path) != 0) return NULL; comp = strrchr(buffer, '\\'); if (comp == NULL) comp = buffer; else found_sep = 1; t = strrchr(comp, '/'); if (t != NULL) { comp = t; found_sep = 1; } if (found_sep) comp++; pos = strcspn(comp, "*?"); if (comp[pos] != '\0') return buffer; /* We don't append a slash if pos == 0 because that changes the * meaning: * * "*.*" is all files in the current directory. * "\*.*" is all files in the root directory of the current drive. */ if (pos > 0 && comp[pos - 1] != '\\' && comp[pos - 1] != '/') { strcat_s(comp, cch_buffer - (comp - buffer), "\\"); } strcat_s(comp, cch_buffer - (comp - buffer), "*.*"); return buffer; } ROKEN_LIB_FUNCTION DIR * ROKEN_LIB_CALL opendir(const char * path) { DIR * dp; struct _finddata_t fd; intptr_t fd_handle; const char *filespec; char path_buffer[1024]; memset(&fd, 0, sizeof(fd)); filespec = filespec_from_dir_path(path, path_buffer, sizeof(path_buffer)/sizeof(char)); if (filespec == NULL) return NULL; fd_handle = _findfirst(filespec, &fd); if (fd_handle == -1) return NULL; dp = malloc(sizeof(*dp)); if (dp == NULL) goto done; memset(dp, 0, sizeof(*dp)); dp->magic = DIRINFO_MAGIC; dp->cursor = 0; dp->n_entries = 0; dp->nc_entries = INITIAL_ENTRIES; dp->entries = calloc(dp->nc_entries, sizeof(dp->entries[0])); if (dp->entries == NULL) { closedir(dp); dp = NULL; goto done; } do { size_t len = strlen(fd.name); struct dirent * e; if (dp->n_entries == dp->nc_entries) { struct dirent ** ne; dp->nc_entries *= 2; ne = realloc(dp->entries, sizeof(dp->entries[0]) * dp->nc_entries); if (ne == NULL) { closedir(dp); dp = NULL; goto done; } dp->entries = ne; } e = malloc(sizeof(*e) + len * sizeof(char)); if (e == NULL) { closedir(dp); dp = NULL; goto done; } e->d_ino = 0; /* no inodes :( */ strcpy_s(e->d_name, len + 1, fd.name); dp->entries[dp->n_entries++] = e; } while (_findnext(fd_handle, &fd) == 0); done: if (fd_handle != -1) _findclose(fd_handle); return dp; } ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL closedir(DIR * dp) { if (!IS_DP(dp)) return EINVAL; if (dp->entries) { long i; for (i=0; i < dp->n_entries; i++) { free(dp->entries[i]); } free(dp->entries); } free(dp); return 0; } ROKEN_LIB_FUNCTION struct dirent * ROKEN_LIB_CALL readdir(DIR * dp) { if (!IS_DP(dp) || dp->cursor < 0 || dp->cursor >= dp->n_entries) return NULL; return dp->entries[dp->cursor++]; } ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rewinddir(DIR * dp) { if (IS_DP(dp)) dp->cursor = 0; } ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL seekdir(DIR * dp, long offset) { if (IS_DP(dp) && offset >= 0 && offset < dp->n_entries) dp->cursor = offset; } ROKEN_LIB_FUNCTION long ROKEN_LIB_CALL telldir(DIR * dp) { return dp->cursor; }