%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file Directory traversal using char file names. CRT on Windows: Optional. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4ERROR_H_INCLUDED #include "dk4error.h" #endif #ifndef DK4STO_H_INCLUDED #include "dk4sto.h" #endif /** Directory structure. */ typedef struct { char *path; /**< Directory path. */ dk4_sto_t *s_dir; /**< Storage for directories. */ dk4_sto_it_t *i_dir; /**< Iterator for directories. */ dk4_sto_t *s_file; /**< Storage for non-directories. */ dk4_sto_it_t *i_file; /**< Iterator for non-directories. */ size_t maxlen; /**< Maximum entry length. */ } dk4_dir_c8_t; #ifdef __cplusplus extern "C" { #endif /** Open directory, create directory structure. @param path Directory path name. @param om Opening mode. @param erp Error report, may be NULL. @return Pointer to new directory structure on success, NULL on error. On success use dk4dir_c8_close() to destroy the directory structure. Error codes: - DK4_E_INVALID_ARGUMENTS
if path is NULL, - DK4_E_NOT_SUPPORTED
if no function to traverse directories was found during build process, - DK4_E_MEMORY_ALLOCATION_FAILED
if a memory allocation failed, - DK4_E_MATH_OVERFLOW
if a mathematical overflow occured in a size calculation, - DK4_E_BUFFER_TOO_SMALL
if one of the involved directory or file names is too long for an internal buffer, - DK4_E_OPENDIR_FAILED
with errno value in iDetails1 if the attempt to open the directory failed, - DK4_E_FINDFIRSTFILE_FAILED
with GetLastError() value in lDetails1 if the FindFirstFile() function failed on Windows, - DK4_E_SYSTEM
if the stat() or lstat() function failed for one of the directory items. */ dk4_dir_c8_t * dk4dir_c8_open(const char *path, int om, dk4_er_t *erp); /** Destroy directory structure. @param dptr Structure to destroy. */ void dk4dir_c8_close(dk4_dir_c8_t *dptr); /** Reset directory traversal. @param dptr Directory structure. */ void dk4dir_c8_reset(dk4_dir_c8_t *dptr); /** Retrieve directory path. @param dptr Directory structure. @return Directory path on success, NULL on error or if no directory was specified for a file name expansion. */ const char * dk4dir_c8_get_path(dk4_dir_c8_t *dptr); /** Retrieve next directory name. @param dptr Directory structure. @return Pointer to short name on success, NULL if there are no more directories. */ const char * dk4dir_c8_next_dir(dk4_dir_c8_t *dptr); /** Retrieve next file (non-directory) name. @param dptr Directory structure. @return Pointer to short name on success, NULL if there are no more files. */ const char * dk4dir_c8_next_file(dk4_dir_c8_t *dptr); /** Skip files (remove information about files, release memory). @param dptr Directory structure. */ void dk4dir_c8_skip_files(dk4_dir_c8_t *dptr); /** Compare two file or directory names. @param l Left name. @param r Right name. @param cr Comparison criteria (ignored). @return 1 if l>r, 0 if l=r, -1 if l #define WINDOWS_H_INCLUDED 1 #endif #endif #include "dk4dir08.h" #ifndef DK4DIR_H_INCLUDED #include "dk4dir.h" #endif #ifndef DK4STR8_H_INCLUDED #include "dk4str8.h" #endif #ifndef DK4STAT_H_INCLUDED #include "dk4stat.h" #endif #ifndef DK4STAT8_H_INCLUDED #include "dk4stat8.h" #endif #ifndef DK4MEM_H_INCLUDED #include "dk4mem.h" #endif #ifndef DK4MPL_H_INCLUDED #include "dk4mpl.h" #endif #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_DIRENT_H #ifndef DIRENT_H_INCLUDED #include #define DIRENT_H_INCLUDED 1 #endif #endif #if DK4_HAVE_ERRNO_H #ifndef ERRNO_H_INCLUDED #include #define ERRNO_H_INCLUDED 1 #endif #endif $!trace-include int dk4dir_c8_compare(const void *l, const void *r, int cr) { int back = 0; if (NULL != l) { if (NULL != r) { back = dk4str8_pathcmp((const char *)l, (const char *)r); } else { back = 1; } } else { if (NULL != r) { back = -1; } } return back; } static void dk4dir_c8_clean_storage(dk4_sto_t *s, dk4_sto_it_t *i) { char *n; if (NULL != s) { if (NULL != i) { dk4sto_it_reset(i); while (NULL != (n = (char *)dk4sto_it_next(i))) { dk4mem_free(n); } dk4sto_it_close(i); } dk4sto_close(s); } } void dk4dir_c8_close(dk4_dir_c8_t *dptr) { $? "+ dk4dir_c8_close" if (NULL != dptr) { dk4mem_release(dptr->path); dk4dir_c8_clean_storage(dptr->s_dir, dptr->i_dir); dptr->s_dir = NULL; dptr->i_dir = NULL; dk4dir_c8_clean_storage(dptr->s_file, dptr->i_file); dptr->s_file = NULL; dptr->i_file = NULL; dk4mem_free(dptr); } $? "- dk4dir_c8_close" } #if DK4_ON_WINDOWS || DK4_HAVE_DIRENT_H /** Fixed texts used by the module. */ static const char * const dk4dir_c8_kw[] = { $!string-table # # 0 Current directory # . # # 1 Parent directory # .. # # 2 Separator for non-Windows systems # / # # 3 Separator for Windows systems # \\ # # 4 Pattern for all files # * $!end }; static int dk4dir_c8_fill_directory(dk4_dir_c8_t *dptr, int om, dk4_er_t *erp) { #if DK4_ON_WINDOWS char pab[DK4_MAX_PATH]; /* Pattern buffer */ WIN32_FIND_DATAA ffdata; /* Find result */ HANDLE ffres; /* Result from FindFirstFile */ DWORD attr; /* File attributes */ char *nn; /* Copy of name, dyn */ size_t slen; /* Entry name length */ int isdir; /* Flag: Entry is directory */ int back = 0; $? "+ dk4dir_c8_fill_directory" if (dk4str8_cpy_s(pab, sizeof(pab), dptr->path, erp)) { if (dk4str8_cat_s(pab, sizeof(pab), dk4dir_c8_kw[3], erp)) { if (dk4str8_cat_s(pab, sizeof(pab), dk4dir_c8_kw[4], erp)) { ffres = FindFirstFileA(pab, &ffdata); if (INVALID_HANDLE_VALUE != ffres) { back = 1; do { if (0 != dk4str8_cmp(dk4dir_c8_kw[0], ffdata.cFileName)) { if (0 != dk4str8_cmp(dk4dir_c8_kw[1], ffdata.cFileName)) { slen = dk4str8_len(ffdata.cFileName); if (slen > dptr->maxlen) { dptr->maxlen = slen; } nn = dk4str8_dup(ffdata.cFileName, erp); if (NULL != nn) { isdir = 0; attr = ffdata.dwFileAttributes; if (0 != (FILE_ATTRIBUTE_DIRECTORY & attr)) { isdir = 1; if (0 != (FILE_ATTRIBUTE_REPARSE_POINT & attr)) { if (0 != (DK4_DIR_SYMLINK_DIR_AS_FILE & om)) { isdir = 0; } } } if (0 != isdir) { if (0 == dk4sto_add(dptr->s_dir, (void *)nn, erp)) { back = 0; dk4mem_free(nn); } } else { if (0 == dk4sto_add(dptr->s_file, (void *)nn, erp)) { back = 0; dk4mem_free(nn); } } } else { back = 0; } } } } while(FindNextFileA(ffres, &ffdata)); FindClose(ffres); } else { /* ERROR: FindFirstFile failed */ dk4error_set_ldetails( erp, DK4_E_FINDFIRSTFILE_FAILED, (long)((unsigned long)GetLastError()) ); } } } } $? "- dk4dir_c8_fill_directory %d", back return back; #else char fnb[DK4_MAX_PATH]; /* File name buffer */ dk4_stat_t stb; /* Stat buffer */ DIR *dir; /* Directory */ struct dirent *de; /* Directory entry */ char *nn; /* Dynamic copy of file name */ size_t slen; /* Entry name length */ int isdir = 0; /* Flag: Current item is directory */ int back = 0; $? "+ dk4dir_c8_fill_directory" errno = 0; dir = opendir(dptr->path); if (NULL != dir) { $? ". opendir" back = 1; do { de = readdir(dir); if (NULL != de) { if (0 < (slen = dk4str8_len(de->d_name))) { if (slen > dptr->maxlen) { dptr->maxlen = slen; } if (0 != dk4str8_cmp(dk4dir_c8_kw[0], de->d_name)) { if (0 != dk4str8_cmp(dk4dir_c8_kw[1], de->d_name)) { if (dk4str8_cpy_s(fnb, sizeof(fnb), dptr->path, erp)) { if (dk4str8_cat_s(fnb, sizeof(fnb), dk4dir_c8_kw[2], erp)) { if (dk4str8_cat_s(fnb, sizeof(fnb), de->d_name, erp)) { nn = dk4str8_dup(de->d_name, erp); if (NULL != nn) { isdir = 0; if (0 != dk4stat_c8(&stb, fnb, erp)) { isdir = dk4stat_is_directory(&stb, NULL); if (0 != isdir) { if (0 != (DK4_DIR_SYMLINK_DIR_AS_FILE & om)) { if (0 != dk4stat_l_c8(&stb, fnb, NULL)) { if (dk4stat_is_symlink(&stb, NULL)) { isdir = 0; } } } } } else { $? "! stat" #if 0 /* 2015-04-11 Dangling links are not directories. */ back = 0; #endif } if (0 != isdir) { if (0 == dk4sto_add(dptr->s_dir, (void *)nn, erp)) { back = 0; $? "! memory" dk4mem_free(nn); } } else { if (0 == dk4sto_add(dptr->s_file, (void *)nn, erp)) { back = 0; $? "! memory" dk4mem_free(nn); } } } else { $? "! memory" back = 0; } } else { $? "! buffer too small" back = 0; } } else { $? "! buffer too small" back = 0; } } else { $? "! buffer too small" back = 0; } } } } } } while(NULL != de); closedir(dir); } else { $? "! opendir" dk4error_set_idetails(erp, DK4_E_OPENDIR_FAILED, errno); } $? "- dk4dir_c8_fill_directory %d", back return back; #endif } #endif dk4_dir_c8_t * dk4dir_c8_open(const char *path, int om, dk4_er_t *erp) { dk4_dir_c8_t *back = NULL; int ok = 0; $? "+ dk4dir_c8_open \"%!8s\"", TR_8STR(path) if (NULL != path) { #if DK4_ON_WINDOWS || DK4_HAVE_DIRENT_H back = dk4mem_new(dk4_dir_c8_t, 1, erp); if (NULL != back) { back->path = NULL; back->s_dir = NULL; back->i_dir = NULL; back->s_file = NULL; back->i_file = NULL; back->path = dk4str8_dup(path, erp); back->maxlen = 0; if (NULL != back->path) { back->s_dir = dk4sto_open(erp); if (NULL != back->s_dir) { if (0 != (DK4_DIR_OPEN_SORTED & om)) { dk4sto_set_comp(back->s_dir, dk4dir_c8_compare, 0); } back->i_dir = dk4sto_it_open(back->s_dir, erp); if (NULL != back->i_dir) { back->s_file = dk4sto_open(erp); if (NULL != back->s_file) { if (0 != (DK4_DIR_OPEN_SORTED & om)) { dk4sto_set_comp(back->s_file, dk4dir_c8_compare, 0); } back->i_file = dk4sto_it_open(back->s_file, erp); if (NULL != back->i_file) { ok = dk4dir_c8_fill_directory(back, om, erp); } } } } } if (0 == ok) { dk4dir_c8_close(back); back = NULL; } } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dir_c8_open PTR=%d", TR_IPTR(back) return back; } void dk4dir_c8_reset(dk4_dir_c8_t *dptr) { $? "+ dk4dir_c8_reset" if (NULL != dptr) { if ((NULL != dptr->s_dir) && (NULL != dptr->i_dir)) { dk4sto_it_reset(dptr->i_dir); } if ((NULL != dptr->s_file) && (NULL != dptr->i_file)) { dk4sto_it_reset(dptr->i_file); } } $? "- dk4dir_c8_reset" } const char * dk4dir_c8_get_path(dk4_dir_c8_t *dptr) { const char *back = NULL; $? "+ dk4dir_c8_get_path" if (NULL != dptr) { back = (const char *)(dptr->path); } $? "- dk4dir_c8_get_path \"%!8s\"", TR_8STR(back) return back; } const char * dk4dir_c8_next_dir(dk4_dir_c8_t *dptr) { const char *back = NULL; $? "+ dk4dir_c8_next_dir" if (NULL != dptr) { if ((NULL != dptr->s_dir) && (NULL != dptr->i_dir)) { back = (const char *)dk4sto_it_next(dptr->i_dir); } } $? "- dk4dir_c8_next_dir \"%!8s\"", TR_8STR(back) return back; } const char * dk4dir_c8_next_file(dk4_dir_c8_t *dptr) { const char *back = NULL; $? "+ dk4dir_c8_next_file" if (NULL != dptr) { if ((NULL != dptr->s_file) && (NULL != dptr->i_file)) { back = (const char *)dk4sto_it_next(dptr->i_file); } } $? "- dk4dir_c8_next_file \"%!8s\"", TR_8STR(back) return back; } void dk4dir_c8_skip_files(dk4_dir_c8_t *dptr) { $? "+ dk4dir_c8_skip_files" if (NULL != dptr) { dk4dir_c8_clean_storage(dptr->s_file, dptr->i_file); dptr->s_file = NULL; dptr->i_file = NULL; } $? "- dk4dir_c8_skip_files" } size_t dk4dir_c8_get_max_entry_length(dk4_dir_c8_t *dptr) { size_t back = 0; $? "+ dk4dir_c8_get_max_entry_length" if (NULL != dptr) { back = dptr->maxlen; } $? "- dk4dir_c8_get_max_entry_length %lu", (unsigned long)back return back; }