%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% module #include "dk4conf.h" #include "dk4types.h" #if DK4_ON_WINDOWS #ifndef WINDOWS_H_INCLUDED #include #define WINDOWS_H_INCLUDED 1 #endif #endif #ifndef DK4APP_H_INCLUDED #include "dk4app.h" #endif #ifndef DK4CONST_H_INCLUDED #include "dk4const.h" #endif #ifndef DK4STATD_H_INCLUDED #include "dk4statd.h" #endif #ifndef DK4STAT_H_INCLUDED #include "dk4stat.h" #endif #ifndef DK4DIR_H_INCLUDED #include "dk4dir.h" #endif #ifndef DK4DIRA_H_INCLUDED #include "dk4dira.h" #endif #ifndef DK4MEM_H_INCLUDED #include "dk4mem.h" #endif #ifndef DK4MEMA_H_INCLUDED #include "dk4mema.h" #endif #ifndef DK4MPL_H_INCLUDED #include "dk4mpl.h" #endif #ifndef DK4STR_H_INCLUDED #include "dk4strd.h" #endif #ifndef DK4MAASZ_H_INCLUDED #include "dk4maasz.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_CHAR_SIZE > 1 #if DK4_HAVE_WCHAR_H #ifndef WCHAR_H_INCLUDED #include #define WCHAR_H_INCLUDED 1 #endif #endif #endif $!trace-include /** Information about failed removals of files or directories. */ enum { /** No errors yet. */ NE_NO_ERROR = 0, /** Failed to remove one file. */ NE_ONE_FILE_REMOVAL_FAILED , /** Failed to remove multiple files. */ NE_MULTIPLE_FILE_REMOVALS_FAILED , /** Failed to remove one directory. */ NE_ONE_DIRECTORY_REMOVAL_FAILED , /** Failed to remove multiple dirs. */ NE_MULTIPLE_DIRECTORY_REMOVALS_FAILED }; /** One node in tree to delete. */ typedef struct _dk4_del_tree_node_t_ { struct _dk4_del_tree_node_t_ *parent; /**< Parent node. */ dk4_dir_t *dir; /**< Directory contents. */ size_t dplen; /**< Directory path length. */ int ne; /**< Flag: Not empty. */ } dk4_del_tree_node_t; /** Job structure (information passed downwards). */ typedef struct { dkChar *sbptr; /**< Static buffer. */ dkChar *dbptr; /**< Dynamic buffer, may be NULL. */ size_t sbsz; /**< Static buffer size. */ size_t dbsz; /**< Dynamic buffer size. */ int result; /**< Suggested function result. */ } dk4_del_tree_job_t; #if DK4_ON_WINDOWS /* +++ Windows */ #if DK4_CHAR_SIZE > 1 /* +++ Windows, wchar_t */ /* --- Windows, wchar_t */ #else /* +++ Windows, char */ /* --- Windows, char */ #endif static int dk4app_del_tree_process_dir(const dkChar *dirname) { #if DK4_CHAR_SIZE > 1 WIN32_FIND_DATAW ff; #else WIN32_FIND_DATAA ff; #endif DWORD dwFileAttributes; HANDLE ha; int back = 1; #if DK4_CHAR_SIZE > 1 dwFileAttributes = GetFileAttributesW(dirname); #else dwFileAttributes = GetFileAttributesA(dirname); #endif if (INVALID_FILE_ATTRIBUTES == dwFileAttributes) { #if DK4_CHAR_SIZE > 1 ha = FindFirstFileW(dirname, &ff); #else ha = FindFirstFileA(dirname, &ff); #endif if (INVALID_HANDLE_VALUE != ha) { dwFileAttributes = ff.dwFileAttributes; FindClose(ha); } } if (INVALID_FILE_ATTRIBUTES != dwFileAttributes) { if (0 != (FILE_ATTRIBUTE_REPARSE_POINT & dwFileAttributes)) { #if DK4_CHAR_SIZE > 1 ha = FindFirstFileW(dirname, &ff); #else ha = FindFirstFileA(dirname, &ff); #endif if (INVALID_HANDLE_VALUE != ha) { if (0 != (FILE_ATTRIBUTE_REPARSE_POINT & ff.dwFileAttributes)) { if (IO_REPARSE_TAG_MOUNT_POINT == ff.dwReserved0) { back = 0; } if (IO_REPARSE_TAG_SYMLINK == ff.dwReserved0) { back = 0; } } FindClose(ha); } else { back = 0; } } } return back; } /* --- Windows */ #else /* +++ non-Windows */ #if DK4_CHAR_SIZE > 1 /* +++ non-Windows, wchar_t */ /* --- non-Windows, wchar_t */ #else /* +++ non-Windows, char */ /* --- non-Windows, char */ #endif /* --- non-Windows */ #endif #if DK4_HAVE_BACKSLASH_AS_SEP static const dkChar dk4appdeltree_fnsep[] = { dkT("\\") }; #else static const dkChar dk4appdeltree_fnsep[] = { dkT("/") }; #endif static void dk4app_del_tree_node_close(dk4_del_tree_node_t *ptr) { if (NULL != ptr) { ptr->parent = NULL; if (NULL != ptr->dir) { dk4dir_close(ptr->dir); ptr->dir = NULL; } ptr->dplen = 0; ptr->ne = 0; dk4mem_free(ptr); } } static dk4_del_tree_node_t * dk4app_del_tree_node_open( const dkChar *dn, dk4_del_tree_node_t *par, dk4_del_tree_job_t *job, dk4_app_t *app ) { dk4_er_t er; /* Error report */ const dkChar *fn; /* File name to process */ dkChar *nbptr; /* Buffer pointer */ dk4_del_tree_node_t *back = NULL; size_t lgt; /* Length of buffer */ #if VERSION_BEFORE_20150821 || TRACE_DEBUG int dfres; /* Result from file deletion */ #endif $? "+ dk4_del_tree_node_t \"%!ds\"", dn back = dk4mem_new_app(dk4_del_tree_node_t,1,app); if (NULL != back) { $? ". memory ok" back->parent = par; back->ne = NE_NO_ERROR; back->dplen = dk4str_len(dn); dk4error_init(&er); back->dir = dk4dir_open_app(dn, DK4_DIR_OPEN_DELTREE, app); if (NULL != back->dir) { $? ". dir open ok" dk4error_init(&er); lgt = dk4ma_size_t_add( dk4ma_size_t_add( back->dplen, dk4dir_get_max_entry_length(back->dir), &er ), 2, &er ); if (DK4_E_NONE == er.ec) { $? ". buffer length calculated" if ((lgt > job->sbsz) && (lgt > job->dbsz)) { $? ". NEED %u", (unsigned)lgt nbptr = dk4mem_new_app(dkChar,lgt,app); if (NULL != nbptr) { $? ". new buffer allocated" if (job->dbptr) { dk4mem_free(job->dbptr); $? ". RELEASE dbptr" } job->dbptr = nbptr; job->dbsz = lgt; $? ". ALLOC dbptr" } else { $? "! allocation" dk4app_del_tree_node_close(back); back = NULL; job->result = 0; } } if (NULL != back) { if ((NULL != job->dbptr) && (0 < job->dbsz)) { nbptr = job->dbptr; $? ". use dynamic buffer" lgt = job->dbsz; } else { $? ". use static buffer" nbptr = job->sbptr; lgt = job->sbsz; } $? ". process files" do { fn = dk4dir_next_file(back->dir); if (NULL != fn) { $? ". processing file \"%!ds\" \"%!ds\"", dn, fn if (dk4str_cpy_s(nbptr,lgt,dk4dir_get_path(back->dir),NULL)) { $? ". nbptr=\"%!ds\" dn=\"%!ds\"", nbptr, dk4dir_get_path(back->dir) if (dk4str_cat_s(nbptr, lgt, dk4appdeltree_fnsep, NULL)) { $? ". nbptr=\"%!ds\" dn=\"%!ds\"",nbptr,dk4dir_get_path(back->dir) if (dk4str_cat_s(nbptr, lgt, fn, NULL)) { $? ". nbptr=\"%!ds\" dn=\"%!ds\"",nbptr,dk4dir_get_path(back->dir) $? ". full path name \"%!ds\"", nbptr switch (back->ne) { case NE_NO_ERROR : { if (0 == dk4app_del_file(nbptr, app)) { back->ne = NE_ONE_FILE_REMOVAL_FAILED; job->result = 0; $? "! file deletion failed" } } break; case NE_ONE_FILE_REMOVAL_FAILED : { if (0 == dk4app_del_file(nbptr, NULL)) { back->ne = NE_MULTIPLE_FILE_REMOVALS_FAILED; /* ERROR: Further file removals failed */ dk4app_log_base3( app,DK4_LL_ERROR,94,95,dk4dir_get_path(back->dir) ); $? "! file deletion failed" } } break; default : { #if TRACE_DEBUG dfres = dk4app_del_file(nbptr, NULL); if (0 == dfres) { $? "! file deletion failed" } #else dk4app_del_file(nbptr, NULL); #endif } break; } } else { $? "! buffer too short" if (NE_ONE_FILE_REMOVAL_FAILED < back->ne) { back->ne = NE_ONE_FILE_REMOVAL_FAILED; } /* ERROR: Directory name too long */ dk4app_log_base3( app, DK4_LL_ERROR, 98, 99, dk4dir_get_path(back->dir) ); } } else { $? "! buffer too short" if (NE_ONE_FILE_REMOVAL_FAILED < back->ne) { back->ne = NE_ONE_FILE_REMOVAL_FAILED; } /* ERROR: Directory name too long */ dk4app_log_base3( app, DK4_LL_ERROR, 98, 99, dk4dir_get_path(back->dir) ); } } else { $? "! buffer too short" if (NE_ONE_FILE_REMOVAL_FAILED < back->ne) { back->ne = NE_ONE_FILE_REMOVAL_FAILED; } /* ERROR: Directory name too long */ dk4app_log_base3( app, DK4_LL_ERROR, 98, 99, dk4dir_get_path(back->dir) ); } } } while(NULL != fn); dk4dir_skip_files(back->dir); } else { $? "! allocation" } } else { $? "! overflow in buffer length calc" /* ERROR: Math error in size calculation */ dk4app_log_base1(app, DK4_LL_ERROR, 82); dk4app_del_tree_node_close(back); back = NULL; job->result = 0; } } else { $? "! dir open" dk4app_del_tree_node_close(back); back = NULL; job->result = 0; /* ERROR: Failed to open directory, already reported */ /* !!!!! TODO: Attempt to remove directory or file */ } } else { $? "! memory" } $? "- dk4_del_tree_node_t PTR=%d", TR_IPTR(back) return back; } static void dk4app_del_tree_debug_file_or_dir( dk4_app_t *app, const dkChar *fn, size_t ind ) { const dkChar *msg[3]; if ((NULL != app) && (NULL != fn)) { if (0 != dk4app_log_do(app, DK4_LL_DEBUG)) { if ((NULL != app->msg_debug) && (ind < app->sz_msg_debug)) { msg[0] = (app->msg_debug)[ind]; msg[1] = fn; msg[2] = NULL; dk4app_log_msg(app, DK4_LL_DEBUG, msg, 2); } } } } int dk4app_del_tree(const dkChar *fn, dk4_app_t *app) { dkChar sb[DK4_MAX_PATH]; dk4_del_tree_job_t job; /* Buffers */ dk4_del_tree_node_t *cn; /* Current node */ dk4_del_tree_node_t *nn; /* Next node */ dkChar *fnb; /* File name buffer */ const dkChar *dn; /* Directory name */ size_t fnbsz; /* File name buffer size */ int res; /* Operation result */ #if VERSION_BEFORE_20150821 || TRACE_DEBUG int dfres; /* Delete file result */ #endif int back = 0; $? "+ dk4app_del_tree \"%!ds\"", TR_DKSTR(fn) job.sbptr = sb; job.sbsz = DK4_SIZEOF(sb,dkChar); job.dbptr = NULL; job.dbsz = 0; job.result = 1; if (NULL != fn) { $? ". directory name ok" cn = dk4app_del_tree_node_open(fn, NULL, &job, app); if (NULL != cn) { $? ". node ok" while (NULL != cn) { $? ". start of loop" if ((NULL != job.dbptr) && (0 < job.dbsz)) { fnb = job.dbptr; fnbsz = job.dbsz; $? ". use dynamic buffer" } else { $? ". use static buffer" fnb = job.sbptr; fnbsz = job.sbsz; } dn = dk4dir_next_dir(cn->dir); if (NULL != dn) { $? ". subdirectory found" if (0 != dk4str_cpy_s(fnb, fnbsz, dk4dir_get_path(cn->dir), NULL)) { if (0 != dk4str_cat_s(fnb, fnbsz, dk4appdeltree_fnsep, NULL)) { if (0 != dk4str_cat_s(fnb, fnbsz, dn, NULL)) { $? ". fnb=%!ds",fnb #if DK4_ON_WINDOWS res = dk4app_del_tree_process_dir(fnb); #else res = 1; #endif if (0 != res) { $? ". directory processing" nn = dk4app_del_tree_node_open(fnb, cn, &job, app); if (NULL != nn) { cn = nn; } else { job.result = 0; } } else { $? ". no directory processing" switch (cn->ne) { case NE_NO_ERROR: case NE_ONE_FILE_REMOVAL_FAILED: case NE_MULTIPLE_FILE_REMOVALS_FAILED: { dk4app_del_tree_debug_file_or_dir(app, fnb, 27); if (0 == dk4app_del_dir(fnb, NULL)) { if (0 == dk4app_del_file(fnb, NULL)) { job.result = 0; cn->ne = NE_ONE_DIRECTORY_REMOVAL_FAILED; dk4app_log_base3(app, DK4_LL_ERROR, 66, 77, fnb); $? "! file deletion failed" } } } break; case NE_ONE_DIRECTORY_REMOVAL_FAILED: { dk4app_del_tree_debug_file_or_dir(app, fnb, 27); if (0 == dk4app_del_dir(fnb, NULL)) { if (0 == dk4app_del_file(fnb, NULL)) { job.result = 0; cn->ne = NE_MULTIPLE_DIRECTORY_REMOVALS_FAILED; dk4app_log_base3( app, DK4_LL_ERROR, 96, 97, dk4dir_get_path(cn->dir) ); $? "! file deletion failed" } } } break; default: { dk4app_del_tree_debug_file_or_dir(app, fnb, 27); if (0 == dk4app_del_dir(fnb, NULL)) { #if TRACE_DEBUG dfres = dk4app_del_file(fnb, NULL); if (0 == dfres) { $? "! file deletion failed" } #else dk4app_del_file(fnb, NULL); #endif } } break; } } } else { $? "! buffer too small" /* ERROR: Directory name too long! */ dk4app_log_base3(app, DK4_LL_ERROR, 98, 99, dn); job.result = 0; } } else { $? "! buffer too small" /* ERROR: Directory name too long! */ dk4app_log_base3(app,DK4_LL_ERROR,98,99,dk4dir_get_path(cn->dir)); job.result = 0; } } else { $? "! buffer too small" /* ERROR: Directory name too long! */ dk4app_log_base3(app,DK4_LL_ERROR,98,99,dk4dir_get_path(cn->dir)); job.result = 0; } } else { $? ". no more subdirectories" nn = cn->parent; if (NULL != nn) { $? ". propagate errors" switch (nn->ne) { case NE_NO_ERROR: case NE_ONE_FILE_REMOVAL_FAILED: case NE_MULTIPLE_FILE_REMOVALS_FAILED: { if (0 == dk4app_del_dir(dk4dir_get_path(cn->dir), app)) { job.result = 0; nn->ne = NE_ONE_DIRECTORY_REMOVAL_FAILED; } } break; case NE_ONE_DIRECTORY_REMOVAL_FAILED: { dk4app_del_tree_debug_file_or_dir( app, dk4dir_get_path(cn->dir), 25 ); if (0 == dk4app_del_dir(dk4dir_get_path(cn->dir), NULL)) { job.result = 0; nn->ne = NE_MULTIPLE_DIRECTORY_REMOVALS_FAILED; dk4app_log_base3( app, DK4_LL_ERROR, 96, 97, dk4dir_get_path(nn->dir) ); } } break; default: { dk4app_del_tree_debug_file_or_dir( app, dk4dir_get_path(cn->dir), 25 ); (void)dk4app_del_dir(dk4dir_get_path(cn->dir), NULL); } break; } } else { $? ". no parent directory" if (0 == dk4app_del_dir(dk4dir_get_path(cn->dir), app)) { job.result = 0; } } dk4app_del_tree_node_close(cn); cn = nn; } } } else { $? "! node" } } else { $? "! directory name" } if ((NULL != job.dbptr) && (0 < job.dbsz)) { dk4mem_free(job.dbptr); $? ". RELEASE dbptr" } back = job.result; $? "- dk4app_del_tree %d", back return back; }