%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% header #include "dk3conf.h" #include "dk3types.h" #ifdef __cplusplus extern "C" { #endif /** Open a directory. @param dn Directory name. @param app Application structure for diagnostics, may be NULL. @return Pointer to directory structure on success, NULL on error. */ dk3_dir_t * dk3dir_open_app(dkChar const *dn, dk3_app_t *app); /** Open directory structure to expand a file name. @param fn File name. @param app Application structure for diagnostics, may be NULL. @return Pointer to directory structure on success, NULL on errror. */ dk3_dir_t * dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app); /** Close directory. @param dp Directory to close. */ void dk3dir_close(dk3_dir_t *dp); /** Get number of subdirectories. @param d Directory structure. @return Number of subdirectories. */ dk3_um_t dk3dir_get_number_of_directories(dk3_dir_t const *d); /** Get number of files. @param d Directory structure. @return Number of non-directory entries in directory. */ dk3_um_t dk3dir_get_number_of_files(dk3_dir_t const *d); /** Attempt to retrieve information about the next sub-directory. @param d Directory structure. @return 1 on success, 0 on error. */ int dk3dir_get_next_directory(dk3_dir_t *d); /** Attempt to retrieve information about the next non-sub-directory entry. @param d Directory structure. @return 1 on success, 0 on error. */ int dk3dir_get_next_file(dk3_dir_t *d); /** Retrieve short file name of entry found. May be used after a successful call to dk3dir_get_next_directory() or dk3dir_get_next_file() only. @param d Directory structure. @return Pointer to file name on success, NULL on error. */ dkChar const * dk3dir_get_shortname(dk3_dir_t const *d); /** Retrieve full file name of entry found. May be used after a successful call to dk3dir_get_next_directory() or dk3dir_get_next_file() only. @param d Directory structure. @return Pointer to file name on success, NULL on error. */ dkChar const * dk3dir_get_fullname(dk3_dir_t const *d); /** Retrieve file status information. May be used after a successful call to dk3dir_get_next_directory() or dk3dir_get_next_file() only. @param d Directory structure. @return Pointer to file name on success, NULL on error. */ dk3_stat_t const * dk3dir_get_stat(dk3_dir_t *d); /** Reset directory for new traversal. @param d Directory structure. */ void dk3dir_reset(dk3_dir_t *d); /** Retrieve directory name. @param dir Directory structure. @return Directory path name. */ dkChar const * dk3dir_get_directory_name(dk3_dir_t const *dir); /** Remove directory recursively, including all contents. @param dn Directory name. @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ int dk3dir_remove_app(dkChar const *dn, dk3_app_t *app); #ifdef __cplusplus } #endif %% module #include "dk3all.h" $!trace-include /* Error flags used in ec: 1 Failed to allocate memory for name to save. 2 Failed to save name to storage. 4 Directory name too long. 8 stat() failed for full name. 16 Failed to list directory contents. */ /** Keywords and texts used in this module. */ static dkChar const * const kw[] = { /* 0 */ dkT("."), /* 1 */ dkT(".."), #if DK3_ON_WINDOWS || DK3_HAVE_BACKSLASH /* 2 */ dkT("\\"), /* 3 */ dkT("\\*"), #else /* 2 */ dkT("/"), /* 3 */ dkT("/*"), #endif /* 4 */ dkT("*"), NULL }; /** Compare two directory entries by name. @param l Left name. @param r Right name. @param cr Comparison criteria (ignored). @return Comparison result. */ static int dk3dir_compare_by_name(void const *l, void const *r, int cr) { int back = 0; if(l) { if(r) { #if DK3_ON_WINDOWS || DK3_HAVE_FNCASEINS back = dk3str_casecmp((dkChar const *)l,(dkChar const *)r); #else back = dk3str_cmp((dkChar const *)l,(dkChar const *)r); #endif } else { back = 1; } } else { if(r) { back = -1; } } return back; } /** Release all directory entries in a storage. @param s Storage to clean up. @param i Iterator for \a s. */ static void dk3dir_release_entries(dk3_sto_t *s, dk3_sto_it_t *i) { dkChar *p = NULL; /* Current name to release. */ $? "+ dk3dir_release_entries" if(s) { if(i) { dk3sto_it_reset(i); while((p = (dkChar *)dk3sto_it_next(i)) != NULL) { $? ". release entry %s", p dk3_delete(p); } dk3sto_it_close(i); } dk3sto_close(s); } $? "- dk3dir_release_entries" } /** Close directory. @param dp Directory to close. */ void dk3dir_close(dk3_dir_t *dp) { $? "+ dk3dir_close" if(dp) { dk3dir_release_entries(dp->sdi, dp->idi); dk3dir_release_entries(dp->sfi, dp->ifi); dp->sdi = NULL; dp->sfi = NULL; dp->idi = NULL; dp->ifi = NULL; dk3_release(dp->dirname); dk3_release(dp->fullname); dp->app = NULL; dp->ndir = dp->nfi = DK3_UM_0; dk3_delete(dp); } $? "- dk3dir_close" } /** Create new dir structure. @param dn Directory name. @param app Application structure for diagnostics, may be NULL. @return Pointer to new structure on success, NULL on error. */ static dk3_dir_t * dk3dir_new(dkChar const *dn, dk3_app_t *app) { dk3_dir_t *back = NULL; int ok = 0; /* Flag: Success. */ $? "+ dk3dir_new \"%s\"", TR_STR(dn) back = dk3_new_app(dk3_dir_t,1,app); if(back) { back->sdi = NULL; back->sfi = NULL; back->idi = NULL; back->ifi = NULL; back->dirname = NULL; back->fullname = NULL; back->app = app; back->ndir = back->nfi = DK3_UM_0; back->sdi = dk3sto_open_app(app); if(back->sdi) { back->idi = dk3sto_it_open(back->sdi); if(back->idi) { back->sfi = dk3sto_open_app(app); if(back->sfi) { back->ifi = dk3sto_it_open(back->sfi); if(back->ifi) { dk3sto_set_comp(back->sdi,dk3dir_compare_by_name,0); dk3sto_set_comp(back->sfi,dk3dir_compare_by_name,0); back->dirname = dk3str_dup_app(dn, NULL); if(back->dirname) { back->fullname = dk3_new_app(dkChar,DK3_MAX_PATH,app); if(back->fullname) { ok = 1; } } } } } } if(!ok) { dk3dir_close(back); back = NULL; } } $? "- dk3dir_new %s", TR_PTR(back) return back; } dkChar const * dk3dir_get_directory_name(dk3_dir_t const *dir) { dkChar const *back = NULL; $? "+ dk3dir_get_directory_name" if(dir) { back = dir->dirname; } $? "- dk3dir_get_directory_name \"%s\"", TR_STR(back) return back; } void dk3dir_reset(dk3_dir_t *d) { $? "+ dk3dir_reset" if(d) { dk3sto_it_reset(d->idi); dk3sto_it_reset(d->ifi); d->shortname = NULL; } $? "- dk3dir_reset" } dk3_um_t dk3dir_get_number_of_directories(dk3_dir_t const *d) { dk3_um_t back = DK3_UM_0; if(d) { back = d->ndir; } $? "= dk3dir_get_number_of_directories %lu", (unsigned long)back return back; } dk3_um_t dk3dir_get_number_of_files(dk3_dir_t const *d) { dk3_um_t back = DK3_UM_0; if(d) { back = d->nfi; } $? "= dk3dir_get_number_of_files %lu", (unsigned long)back return back; } dkChar const * dk3dir_get_fullname(dk3_dir_t const *d) { dkChar const *back = NULL; $? "+ dk3dir_get_fullname" if(d) { back = (dkChar const *)(d->fullname); } $? "- dk3dir_get_fullname \"%s\"", TR_STR(back) return back; } dk3_stat_t const * dk3dir_get_stat(dk3_dir_t *d) { dk3_stat_t *back = NULL; $? "+ dk3dir_get_stat" if(d) { back = &(d->stb); } $? "- dk3dir_get_stat %s", TR_PTR(back) return back; } dkChar const * dk3dir_get_shortname(dk3_dir_t const *d) { dkChar const *back = NULL; $? "+ dk3dir_get_shortname" if(d) { back = (dkChar const *)(d->shortname); } $? "- dk3dir_get_shortname \"%s\"", TR_STR(back) return back; } /** Get next entry. @param d Directory structure. @param s Iterator for the storage. @param isdir Flag: Current entry is a directory. @return 1 on success, 0 on error. */ static int dk3dir_get_next_entry(dk3_dir_t *d, dk3_sto_it_t *s, int isdir) { int back = 0; int cc = 1; /* Flag: Can continue. */ size_t sz = 0; /* Directory name length. */ $? "+ dk3dir_get_next_entry dir=%d", isdir do { cc = 0; d->shortname = (dkChar *)dk3sto_it_next(s); if(d->shortname) { $? ". shortname = \"%s\"", d->shortname cc = 1; if(d->dirname) { if(dk3str_len(d->dirname)) { dk3str_cpy_not_overlapped(d->fullname, d->dirname); sz = dk3str_len(d->dirname); if(sz > 0) { if((d->dirname)[sz - 1] != DK3_CHAR_SEP) { dk3str_cat(d->fullname, kw[2]); } } else { dk3str_cat(d->fullname, kw[2]); } dk3str_cat(d->fullname, d->shortname); } else { dk3str_cpy_not_overlapped(d->fullname, d->shortname); } } else { dk3str_cpy_not_overlapped(d->fullname, d->shortname); } if(dk3sf_stat_app(&(d->stb), d->fullname, (dk3_app_t *)(d->app))) { switch(((d->stb).ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { if(isdir) { back = 1; cc = 0; } } break; default: { if(!isdir) { back = 1; cc = 0; } } break; } } } $? ". cc = %d shortname=\"%s\"", cc, TR_STR(d->shortname) } while(cc); $? "- dk3dir_get_next_entry %d", back return back; } int dk3dir_get_next_directory(dk3_dir_t *d) { int back = 0; $? "+ dk3dir_get_next_directory" if(d) { back = dk3dir_get_next_entry(d, d->idi, 1); } $? "- dk3dir_get_next_directory %d", back return back; } int dk3dir_get_next_file(dk3_dir_t *d) { int back = 0; $? "+ dk3dir_get_next_file" if(d) { back = dk3dir_get_next_entry(d, d->ifi, 0); } $? "- dk3dir_get_next_file %d", back return back; } /** Add a found item to a storage. @param s Storage to add item to. @param n Item name. @param c Pointer to variable to increase to. @param ec Pointer to error code variable. @param app Application structure for diagnostics. */ static void dk3dir_add(dk3_sto_t *s, dkChar const *n, dk3_um_t *c, int *ec, dk3_app_t *app) { dkChar *newname = NULL; /* New copy of name. */ dk3_um_t mu = DK3_UM_0; /* Number of entries. */ $? "+ dk3dir_add \"%s\"", TR_STR(n) newname = dk3str_dup_app(n,(((*ec) & 1) ? NULL : app)); if(newname) { if(dk3sto_add(s, newname)) { mu = *c; mu++; *c = mu; } else { dk3_delete(newname); *ec |= 2; $? "! failed to store name" } } else { *ec |= 1; $? "! failed to allocate name" } $? "- dk3dir_add" } #if DK3_ON_WINDOWS /* +++ Windows +++ */ #if DK3_CHAR_SIZE > 1 /* +++ Windows > 8 bit +++ */ #if VERSION_BEFORE_2012_04_13 dk3_dir_t * dk3dir_open_app(dkChar const *dn, dk3_app_t *app) { dk3_dir_t *back = NULL; int ec = 0; /* Error code. */ dkChar buffer[DK3_MAX_PATH]; /* Buffer for entry names. */ intptr_t ffres; /* Result of findfirst(). */ dkChar *namecopy = NULL; /* New copy of name. */ struct _wfinddata64_t ffdata; /* Find data. */ if(dn) { if((dk3str_len(kw[3]) + dk3str_len(dn)) < DK3_SIZEOF(buffer,dkChar)) { dk3str_cpy_not_overlapped(buffer, dn); dk3str_cat(buffer, kw[3]); back = dk3dir_new(dn, app); if(back) { ffres = _wfindfirst64(buffer, &ffdata); if(ffres != -1) { do { if(dk3str_cmp(ffdata.name, kw[0])) { if(dk3str_cmp(ffdata.name, kw[1])) { if((ffdata.attrib) & _A_SUBDIR) { dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app); } else { dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app); } } } } while(_wfindnext64(ffres, &ffdata) == 0); _findclose(ffres); } else { ec |= 16; if(app) { /* ERROR: Failed to open directory! */ dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn); } } } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } else { if(app) { /* ERROR: Directory name too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn); } } } return back; } dk3_dir_t * dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app) { dk3_dir_t *back = NULL; int ec = 0; /* Error code. */ dkChar mybuffer[DK3_MAX_PATH]; /* Buffer for entry names. */ dkChar myb2[2]; /* Search pattern. */ dkChar *ptr = NULL; /* Last separator. */ dkChar *namecopy = NULL; /* New copy of entry name. */ intptr_t ffres; /* Result from findfirst(). */ struct _wfinddata64_t ffdata; /* Find data. */ $? "+ dk3dir_fne_open_app \"%ls\"", fn if(fn) { if(dk3str_len(fn) < DK3_MAX_PATH) { dk3str_cpy_not_overlapped(mybuffer, fn); ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP); if(ptr) { *ptr = DK3_CHAR_0; back = dk3dir_new(mybuffer, app); *ptr = DK3_CHAR_SEP; } else { myb2[0] = DK3_CHAR_0; back = dk3dir_new(myb2, app); } if(back) { $? ". mybuffer = \"%ls\"", mybuffer ffres = _wfindfirst64(mybuffer, &ffdata); if(ffres != -1) { do { $? ". entry \"%ls\"", ffdata.name if(dk3str_cmp(ffdata.name, kw[0])) { $? ". comparison ok" if(dk3str_cmp(ffdata.name, kw[1])) { $? ". comparison ok" if((ffdata.attrib) & _A_SUBDIR) { $? ". sub directory" dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app); } else { $? ". regular file" dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app); } } } } while(_wfindnext64(ffres, &ffdata) == 0); _findclose(ffres); } else { ec |= 16; if(app) { /* ERROR: Failed to open directory! */ dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, mybuffer); } } } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } else { /* ERROR: Pattern too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn); } } $? "- dk3dir_fne_open_app %s", TR_PTR(back) return back; } #else static dk3_dir_t * dk3dir_my_open_app(dkChar const *dn, int usefne, dk3_app_t *app) { WIN32_FIND_DATAW wfd; /* Details for file. */ dkChar b1[DK3_MAX_PATH]; /* Directory base. */ dkChar b2[DK3_MAX_PATH]; /* Pattern. */ dkChar *p1; /* Separator. */ dk3_dir_t *back = NULL; HANDLE hSearch; /* File search handle. */ size_t sz; /* String length. */ int ok; /* Flag: No error yet. */ int ec; /* Error code. */ int isDir; /* Flag: Entry is dir. */ $? "+ dk3dir_my_open_app \"%s\"", dn ec = 0; if(dn) { ok = 0; if(usefne) { if(dk3str_len(dn) < DK3_SIZEOF(b1,dkChar)) { ok = 1; $? ". ok" dk3str_cpy_not_overlapped(b1, dn); dk3str_cpy_not_overlapped(b2, dn); p1 = dk3str_rchr(b1, DK3_CHAR_SEP); if(p1) { *p1 = dkT('\0'); } else { b1[0] = dkT('\0'); } } else { $? "! name too long" /* ERROR: Name too long! */ if(app) { dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, dn); } } } else { sz = dk3str_len(dn) + dk3str_len(kw[3]); if(sz < DK3_SIZEOF(b1,dkChar)) { ok = 1; $? ". ok" dk3str_cpy_not_overlapped(b1, dn); dk3str_cpy_not_overlapped(b2, dn); sz = dk3str_len(b2); if(sz > 1) { dk3str_cat(b2, kw[(DK3_CHAR_SEP == b2[sz - 1]) ? 4 : 3]); } else { dk3str_cat(b2, kw[3]); } } else { $? "! name too long" /* Name too long! */ if(app) { dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn); } } } if(ok) { $? ". b1=\"%s\" b2=\"%s\"", b1, b2 back = dk3dir_new(b1, app); if(back) { $? ". alloc ok" hSearch = FindFirstFileW(b2, &wfd); if(INVALID_HANDLE_VALUE != hSearch) { $? ". FIndFirstFile" do { isDir = 0; $? ". name=\"%s\"", wfd.cFileName if(dk3str_cmp(wfd.cFileName, kw[0])) { if(dk3str_cmp(wfd.cFileName, kw[1])) { $? ". name ok" if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) { isDir = 1; #if 0 if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_REPARSE_POINT) { switch(wfd.dwReserved0) { case IO_REPARSE_TAG_MOUNT_POINT: { isDir = 0; } break; } } #endif } if(isDir) { $? ". dir" dk3dir_add(back->sdi, wfd.cFileName, &(back->ndir), &ec, app); } else { $? ". no dir" dk3dir_add(back->sfi, wfd.cFileName, &(back->nfi), &ec, app); } } } } while(FindNextFileW(hSearch, &wfd)); FindClose(hSearch); } else { $? "! FindFirstFileW" /* ERROR: Failed to search for pattern! */ ec |= 16; if(app) { if(usefne) { dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, dn); } else { dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn); } } } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } else { $? "! alloc" } } } else { $? "! no dn" } $? "- dk3dir_my_open_app %s", TR_PTR(back) return back; } dk3_dir_t * dk3dir_open_app(dkChar const *dn, dk3_app_t *app) { dk3_dir_t *back; $? "+ dk3dir_open_app \"%s\"", dn back = dk3dir_my_open_app(dn, 0, app); $? "- dk3dir_open_app %s", TR_PTR(back) return back; } dk3_dir_t * dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app) { dk3_dir_t *back; $? "+ dk3dir_fne_open_app \"%s\"", fn back = dk3dir_my_open_app(fn, 1, app); $? "- dk3dir_fne_open_app %s", TR_PTR(back) return back; } #endif /* --- Windows > 8 bit --- */ #else /* +++ Windows, 8 bit +++ */ #if VERSION_BEFORE_2012_04_13 dk3_dir_t * dk3dir_open_app(dkChar const *dn, dk3_app_t *app) { dk3_dir_t *back = NULL; int ec = 0; /* Error code. */ dkChar buffer[DK3_MAX_PATH]; /* Buffer for entry names. */ intptr_t ffres; /* Result from findfirst(). */ dkChar *namecopy = NULL; /* New copy of entry name. */ struct __finddata64_t ffdata; /* Find data. */ if(dn) { if((dk3str_len(kw[3]) + dk3str_len(dn)) < DK3_SIZEOF(buffer,dkChar)) { dk3str_cpy_not_overlapped(buffer, dn); dk3str_cat(buffer, kw[3]); back = dk3dir_new(dn, app); if(back) { ffres = _findfirst64(buffer, &ffdata); if(ffres != -1) { do { if(dk3str_cmp(ffdata.name, kw[0])) { if(dk3str_cmp(ffdata.name, kw[1])) { if((ffdata.attrib) & _A_SUBDIR) { dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app); } else { dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app); } } } } while(_findnext64(ffres, &ffdata) == 0); _findclose(ffres); } else { ec |= 16; /* Findfirst failed */ if(app) { /* ERROR: Findfirst failed! */ dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn); } } } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } else { if(app) { /* ERROR: Directory name too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn); } } } return back; } dk3_dir_t * dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app) { dk3_dir_t *back = NULL; int ec = 0; /* Error code. */ dkChar mybuffer[DK3_MAX_PATH]; /* Buffer for entry name. */ dkChar myb2[2]; /* Buffer for pattern. */ dkChar *ptr = NULL; /* Last separator. */ dkChar *namecopy = NULL; /* New copy of entry name. */ intptr_t ffres; /* Result from findfirst(). */ struct __finddata64_t ffdata; /* Find data. */ $? "+ dk3dir_fne_open_app \"%s\"", fn if(fn) { if(dk3str_len(fn) < DK3_MAX_PATH) { dk3str_cpy_not_overlapped(mybuffer, fn); ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP); if(ptr) { *ptr = DK3_CHAR_0; back = dk3dir_new(mybuffer, app); *ptr = DK3_CHAR_SEP; } else { myb2[0] = DK3_CHAR_0; back = dk3dir_new(myb2, app); } if(back) { ffres = _findfirst64(mybuffer, &ffdata); if(ffres != -1) { do { if(dk3str_cmp(ffdata.name, kw[0])) { if(dk3str_cmp(ffdata.name, kw[1])) { if((ffdata.attrib) & _A_SUBDIR) { dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app); } else { dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app); } } } } while(_findnext64(ffres, &ffdata) == 0); _findclose(ffres); } else { ec |= 16; if(app) { /* ERROR: Failed to open directory! */ dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, mybuffer); } } } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } else { if(app) { /* ERROR: Pattern too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn); } } } $? "- dk3dir_fne_open_app %s", TR_PTR(back) return back; } #else static dk3_dir_t * dk3dir_my_open_app(dkChar const *dn, int usefne, dk3_app_t *app) { WIN32_FIND_DATAA wfd; /* Details for file. */ dkChar b1[DK3_MAX_PATH]; /* Directory base. */ dkChar b2[DK3_MAX_PATH]; /* Pattern. */ dkChar *p1; /* Separator. */ dk3_dir_t *back = NULL; HANDLE hSearch; /* File search handle. */ size_t sz; /* String length. */ int ok; /* Flag: No error yet. */ int ec; /* Error code. */ int isDir; /* Flag: Entry is dir. */ $? "+ dk3dir_my_open_app \"%s\"", dn ec = 0; if(dn) { ok = 0; if(usefne) { if(dk3str_len(dn) < DK3_SIZEOF(b1,dkChar)) { ok = 1; $? ". ok" dk3str_cpy_not_overlapped(b1, dn); dk3str_cpy_not_overlapped(b2, dn); p1 = dk3str_rchr(b1, DK3_CHAR_SEP); if(p1) { *p1 = dkT('\0'); } else { b1[0] = dkT('\0'); } } else { $? "! name too long" /* ERROR: Name too long! */ if(app) { dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, dn); } } } else { sz = dk3str_len(dn) + dk3str_len(kw[3]); if(sz < DK3_SIZEOF(b1,dkChar)) { ok = 1; $? ". ok" dk3str_cpy_not_overlapped(b1, dn); dk3str_cpy_not_overlapped(b2, dn); sz = dk3str_len(b2); if(sz > 1) { dk3str_cat(b2, kw[(DK3_CHAR_SEP == b2[sz - 1]) ? 4 : 3]); } else { dk3str_cat(b2, kw[3]); } } else { $? "! name too long" /* ERROR: Name too long! */ if(app) { dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn); } } } if(ok) { $? ". b1=\"%s\" b2=\"%s\"", b1, b2 back = dk3dir_new(b1, app); if(back) { $? ". alloc ok" hSearch = FindFirstFileA(b2, &wfd); if(INVALID_HANDLE_VALUE != hSearch) { $? ". FIndFirstFile" do { isDir = 0; $? ". name=\"%s\"", wfd.cFileName if(dk3str_cmp(wfd.cFileName, kw[0])) { if(dk3str_cmp(wfd.cFileName, kw[1])) { $? ". name ok" if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) { isDir = 1; #if 0 if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_REPARSE_POINT) { switch(wfd.dwReserved0) { case IO_REPARSE_TAG_MOUNT_POINT: { isDir = 0; } break; } } #endif } if(isDir) { $? ". dir" dk3dir_add(back->sdi, wfd.cFileName, &(back->ndir), &ec, app); } else { $? ". no dir" dk3dir_add(back->sfi, wfd.cFileName, &(back->nfi), &ec, app); } } } } while(FindNextFileA(hSearch, &wfd)); FindClose(hSearch); } else { $? "! FindFirstFileW" /* ERROR: Failed to search for pattern! */ ec |= 16; if(app) { if(usefne) { dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, dn); } else { dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn); } } } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } else { $? "! alloc" } } } else { $? "! no dn" } $? "- dk3dir_my_open_app %s", TR_PTR(back) return back; } dk3_dir_t * dk3dir_open_app(dkChar const *dn, dk3_app_t *app) { dk3_dir_t *back; $? "+ dk3dir_open_app \"%s\"", dn back = dk3dir_my_open_app(dn, 0, app); $? "- dk3dir_open_app %s", TR_PTR(back) return back; } dk3_dir_t * dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app) { dk3_dir_t *back; $? "+ dk3dir_fne_open_app \"%s\"", fn back = dk3dir_my_open_app(fn, 1, app); $? "- dk3dir_fne_open_app %s", TR_PTR(back) return back; } #endif /* --- Windows, 8 bit --- */ #endif /* --- Windows --- */ #else /* +++ non-Windows +++ */ #if DK3_CHAR_SIZE > 1 /* +++ non-Windows > 8 bit +++ */ #error "16/32-bit file names only available on Windows!" /* --- non-Windows > 8 bit --- */ #else /* +++ non-Windows, 8 bit +++ */ /** Function names used in error messages. */ static dkChar const * const dk3dir_function_names[] = { $!string-table macro=dkT # # 0 # opendir: $!end }; #if DK3_HAVE_OPENDIR && DK3_HAVE_READDIR && DK3_HAVE_CLOSEDIR dk3_dir_t * dk3dir_open_app(dkChar const *dn, dk3_app_t *app) { dk3_dir_t *back = NULL; int ec = 0; /* Error code. */ struct dirent *de = NULL; /* Directory entry from readdir(). */ DIR *d = NULL; /* Directory structure from opendir(). */ size_t sz = 0; /* Length of entire file name. */ size_t dns = 0; /* Directory name length. */ size_t kw2l = 0; /* Length of separator string. */ dk3_stat_t stb; /* Buffer for stat() result. */ $? "+ dk3dir_open_app" if(dn) { back = dk3dir_new(dn, app); if(back) { d = opendir(dn); if(d) { dns = dk3str_len(dn); kw2l = dk3str_len(kw[2]); while((de = readdir(d)) != NULL) { if(dk3str_cmp(de->d_name, kw[0])) { if(dk3str_cmp(de->d_name, kw[1])) { sz = dns + kw2l + dk3str_len(de->d_name); if(sz < DK3_MAX_PATH) { dk3str_cpy_not_overlapped(back->fullname, dn); dk3str_cat(back->fullname, kw[2]); dk3str_cat(back->fullname, de->d_name); if(dk3sf_stat_app(&stb, back->fullname, NULL)) { switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { dk3dir_add(back->sdi,de->d_name,&(back->ndir),&ec,app); } break; default: { dk3dir_add(back->sfi,de->d_name,&(back->nfi),&ec,app); } break; } } else { if(!(ec & 8)) { if(app) { /* Stat failed! */ dk3app_log_i3(app,DK3_LL_ERROR,61,62,back->fullname); } } ec |= 8; } } else { if(!(ec & 4)) { if(app) { /* Directory name too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn); } } ec |= 4; } } } } closedir(d); } else { ec |= 16; if(app) { /* Failed to open directory! */ dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn); dk3sf_report_errno(app, errno, dk3dir_function_names[0]); } } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } } $? "- dk3dir_open_app %s", TR_PTR(back) return back; } dk3_dir_t * dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app) { dk3_dir_t *back = NULL; int ec = 0; /* Error code. */ dk3_stat_t stb; /* Stat buffer. */ dkChar mybuffer[DK3_MAX_PATH]; /* Buffer for entire file name. */ dkChar myb2[2]; /* Pattern buffer. */ dkChar *ptr; /* Last separator in pattern. */ $? "+ dk3dir_fne_open_app \"%s\"", fn if(fn) { if(dk3str_len(fn) < DK3_MAX_PATH) { dk3str_cpy_not_overlapped(mybuffer, fn); if(dk3sf_stat_app(&stb, fn, app)) { ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP); if(ptr) { *(ptr++) = DK3_CHAR_0; back = dk3dir_new(mybuffer, app); } else { myb2[0] = DK3_CHAR_0; back = dk3dir_new(myb2, app); ptr = mybuffer; } if(back) { switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { dk3dir_add(back->sdi, ptr, &(back->ndir), &ec, app); } break; default: { dk3dir_add(back->sfi, ptr, &(back->nfi), &ec, app); } break; } if(ec) { if(3 & ec) { if(app) { dk3app_log_i1(app, DK3_LL_ERROR, 9); } } dk3dir_close(back); back = NULL; } } } } else { if(app) { /* Name too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn); } } } $? "- dk3dir_fne_open_app %s", TR_PTR(back) return back; } #else #error "No opendir()/readdir()/closedir() function set!" #endif /* --- non-Windows, 8 bit --- */ #endif /* --- non-Windows --- */ #endif /** Directory chain cell. */ struct _dk3_dir_cell_t_ { struct _dk3_dir_cell_t_ *parent; /**< Parent cell. */ dk3_dir_t *dir; /**< Directory structure. */ }; /** Directory chain cell. */ typedef struct _dk3_dir_cell_t_ DK3_DIR_CELL; /** Destroy chain cell, release memory. @param c Cell to destroy. */ static void dk3dir_cell_delete(DK3_DIR_CELL *c) { if(c) { if(c->dir) { dk3dir_close(c->dir); } c->dir = NULL; c->parent = NULL; dk3_delete(c); } } /** Create directory cell, allocate memory. @param n Directory name. @param p Parent cell in chain. @param app Application structure for diagnostics. @return Pointer to new cell on success, NULL on error. */ static DK3_DIR_CELL * dk3dir_cell_new(dkChar const *n, DK3_DIR_CELL *p, dk3_app_t *app) { DK3_DIR_CELL *back = NULL; back = dk3_new_app(DK3_DIR_CELL,1,app); if(back) { back->parent = p; back->dir = dk3dir_open_app(n, app); if(!(back->dir)) { dk3dir_cell_delete(back); back = NULL; } } return back; } #if DK3_ON_WINDOWS /** Remove a directory reparse point. @param dn Directory to remove. @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ static int dk3dir_remove_reparse_point(dkChar const *dn, dk3_app_t *app) { int back = 0; if(dk3sf_remove_dir_app(dn, app)) { back = 1; } else { if(dk3sf_remove_file_app(dn, app)) { back = 1; } } return back; } #endif int dk3dir_remove_app(dkChar const *dn, dk3_app_t *app) { int back = 0; dk3_stat_t stb; /* Stat buffer. */ dkChar const *en = NULL; /* Entry name. */ dkChar const *xdn; /* Current dir to delete. */ dk3_stat_t const *es = NULL; /* Entry stat buffer. */ DK3_DIR_CELL *ccell = NULL; /* Current cell. */ DK3_DIR_CELL *ncell = NULL; /* New cell or next cell. */ int ft; /* File type. */ if(dn) { if(dk3sf_stat_app(&stb, dn, app)) { if((stb.ft) & DK3_FT_SYMLINK) { /* symbolic link */ /* Removing symbolic link only, not target! */ back = dk3sf_remove_file_app(dn, app); } else { if(stb.ft == DK3_FT_DIRECTORY) { #if DK3_ON_WINDOWS if(0x00 == stb.cReparse) { #endif ccell = dk3dir_cell_new(dn, NULL, app); if(ccell) { back = 1; while(ccell) { if(dk3dir_get_next_directory(ccell->dir)) { en = dk3dir_get_fullname(ccell->dir); if(en) { es = dk3dir_get_stat(ccell->dir); if(es) { if((es->ft) & DK3_FT_SYMLINK) { if(!dk3sf_remove_file_app(en, app)) { back = 0; } } else { #if DK3_ON_WINDOWS if(0x00 == es->cReparse) { #endif ncell = dk3dir_cell_new(en, ccell, app); if(ncell) { ccell = ncell; } else { back = 0; } #if DK3_ON_WINDOWS } else { if(!dk3dir_remove_reparse_point(en, app)) { back = 0; } } #endif } } else { back = 0; /* BUG: Internal error. */ } } else { back = 0; /* BUG: Internal error. */ } } else { while(dk3dir_get_next_file(ccell->dir)) { ft = DK3_FT_REGULAR; en = dk3dir_get_fullname(ccell->dir); es = dk3dir_get_stat(ccell->dir); if(es) { ft = es->ft; } if(en) { if(!dk3sf_remove_file_app(en, app)) { back = 0; } } else { back = 0; /* BUG: Internal error. */ } } ncell = ccell->parent; if(ccell->dir) { xdn = dk3dir_get_directory_name(ccell->dir); if(xdn) { if(!dk3sf_remove_dir_app(xdn, app)) { back = 0; } } } dk3dir_cell_delete(ccell); ccell = ncell; } } } #if DK3_ON_WINDOWS } else { if(!dk3dir_remove_reparse_point(dn, app)) { back = 0; } } #endif } else { /* ERROR: Not a directory! */ dk3app_log_i3(app, DK3_LL_ERROR, 202, 203, dn); } } } } return back; } /* vim: set ai sw=2 : */