*
* This is intended to be used as an alternative to functions like fl_directory_list(), giving more control over the recursion process.
*
- * When recursing the directory, except for the top directory, handle() is called after the recurse.path is updated (with both the before and after flags set).
- * For the top directory, handle() is called when the path is not updated (with the before and after flags set).
- *
* This function is designed and intended to be used on directories.
- * If depth is 0, the operations callacks are still called but are done at the top level.
- * Be sure to use the recurse.path_top when depth is 0 or when f_directory_recurse_do_flag_top_d is set (the recurse.path may have been altered during the recursion).
*
- * The action callback must set the error bit on recurse.state.status to ensure that the handle callbacks are called or not set the error bit to prevent this behavior.
+ * There are two callbacks directly used by this function:
+ * - action: This callback is for performing the directory action.
+ * - handle: This callback is for reacting to failures and other error/exceptional cases.
*
- * This exits on error if, after the handle callback is called, when the recurse.state.status still has the error bit set.
- * This allows for the caller to inform this function to effectively ignore any errors.
+ * The action() callback must set the error bit on recurse.state.status to trigger the appropriate handle() call.
*
- * This does not perform the operations on the top level directory when the max depth is set to 0.
- * Use the before and after flags to perform top level directory actions in this situation.
- * This is done so that the caller can better determine if top level directory actions must be performed before or after the recursed operations.
- * Be sure to add the appropriate before and after flags when calling this function in order to make this possible.
+ * This always exits on error if, after the handle() callback is called, the recurse.state.status still has the error bit set.
+ * The handle() gives the caller more power to do things like report errors or even ignore errors.
+ * When ignoring errors, be careful about modifications as some may put the project in an unusual state.
*
- * The recurse parameter may be NULL if parameter checking fails or may be NULL if no parameter checking is performed.
- * The caller should consider checking if recurse is NULL.
+ * The action() and handle() should check if the recurse is NULL (and any other appropraite NULL checks).
*
* General behavior flow:
- * 1. Check recurse.action existence (if not _di_level_1_parameter_checking_), calling recurse.handle with (f_directory_recurse_do_flag_top_d) on error.
- * 2. Check path.used, call recurse.handle with (f_directory_recurse_do_flag_path_d | f_directory_recurse_do_flag_top_d) on error.
- * 3. Prepare recurse.path, call recurse.handle with (f_directory_recurse_do_flag_path_d | f_directory_recurse_do_flag_top_d) on error.
- * 4. If recurse.flag has (f_directory_recurse_do_flag_top_before_e), call recurse.action with (f_directory_recurse_do_flag_top_before_e).
- * - Call recurse.handle with (f_directory_recurse_do_flag_top_before_e) on error.
- *
- * 5. If recurse.depth_max > 0, perform recursion.
- * 1. Load directory listing, call recurse.handle with (f_directory_recurse_do_flag_list_d | f_directory_recurse_do_flag_path_d) on error.
- * 2. If recurse.flag has (f_directory_recurse_do_flag_list_d), call recurse.action with (f_directory_recurse_do_flag_list_d).
- * - Call recurse.handle with (f_directory_recurse_do_flag_list_d) on error.
- *
- * 3. For each file type, except directory (optionally calling recurse->state.interrupt() at the top of each loop):
- * 1. Prepare list array, call recurse.handle with (f_directory_recurse_do_flag_directory_d) on error.
- * 2. If recurse.flag has (f_directory_recurse_do_flag_before_d), call recurse.action with (f_directory_recurse_do_flag_before_d | f_directory_recurse_do_flag_*_e, where * represents type).
- * - Call recurse.handle with (f_directory_recurse_do_flag_before_d | f_directory_recurse_do_flag_*_e, where * represents type) on error.
- *
- * 3. Call recurse.action with (f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_*_e, where * represents type).
- * - Call recurse.handle with (f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_*_e, where * represents type) on error.
- *
- * 4. If recurse.flag has (f_directory_recurse_do_flag_after_d), call recurse.action with (f_directory_recurse_do_flag_after_d | f_directory_recurse_do_flag_*_e, where * represents type).
- * - Call recurse.handle with (f_directory_recurse_do_flag_after_d | f_directory_recurse_do_flag_*_e, where * represents type) on error.
- *
- * 4. For each directory (optionally calling recurse->state.interrupt() at the top of each loop):
- * 1. Prepare list array, call recurse.handle with (f_directory_recurse_do_flag_directory_d) on error.
- * 2. Call recurse.action with (f_directory_recurse_do_flag_action_d| f_directory_recurse_do_flag_before_d | f_directory_recurse_do_flag_directory_d).
- * - Call recurse.handle with (f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_before_d | f_directory_recurse_do_flag_directory_d) on error.
- *
- * 3. If depth max is not reached, perform recursion.
- * 4. If depth max is reached, call recurse.action with (f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_directory_d).
- * - Call recurse.handle with (f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_directory_d) on error.
- *
- * 5. Call recurse.action with (f_directory_recurse_do_flag_after_d | f_directory_recurse_do_flag_directory_d).
- * - Call recurse.handle with (f_directory_recurse_do_flag_after_d | f_directory_recurse_do_flag_directory_d) on error.
- *
- * 6. Else if recurse.depth_max == 0, process directory, call recurse.handle with (f_directory_recurse_do_flag_top_d | f_directory_recurse_do_flag_directory_d) on error.
- * 1. Load directory listing, call recurse.handle with (f_directory_recurse_do_flag_list_d | f_directory_recurse_do_flag_path_d) on error.
- * 2. If recurse.flag has (f_directory_recurse_do_flag_list_d), call recurse.action with (f_directory_recurse_do_flag_list_d).
- * - Call recurse.handle with (f_directory_recurse_do_flag_list_d) on error.
- *
- * 3. Call recurse.action with (f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_directory_d | f_directory_recurse_do_flag_top_d).
- * - Call recurse.handle with (f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_directory_d | f_directory_recurse_do_flag_top_d) on error.
- *
- * 7. If recurse.flag has (f_directory_recurse_do_flag_top_after_e), call recurse.action with (f_directory_recurse_do_flag_top_after_e).
- * - Call recurse.handle with (f_directory_recurse_do_flag_top_after_e) on error.
+ * 1. Check recurse.action existence (if not _di_level_1_parameter_checking_), flag is f_directory_recurse_do_flag_none_d.
+ * 2. Check path.used and allocation recurse.path, flag is f_directory_recurse_do_flag_path_d (even if handle() changes error bit, if path.used is 0, then the function always returns here).
+ * 3. If recurse.depth < recurse.depth_max, then recurse into directory contents.
+ * 3.1 Open directory stream, flag is f_directory_recurse_do_flag_list_d.
+ * 3.2 Read directory stream, flag is f_directory_recurse_do_flag_path_list_d ('.' and '..' are skipped).
+ * 3.3 Allocate sub-path onto recurse.path, flag is f_directory_recurse_do_flag_path_d.
+ * 3.4 Process before state on path, flag is (type code of file/directory) | f_directory_recurse_do_flag_before_d.
+ * 3.5 Process action state on path, flag is (type code of file/directory) | f_directory_recurse_do_flag_action_d.
+ * 3.5.1 If type is directory (flag has f_directory_recurse_do_flag_directory_d), then increase recurse.depth and recurse (repeat step (3) for this sub-directory).
+ * 3.6 Process after state on path, flag is (type code of file/directory) | f_directory_recurse_do_flag_after_d.
+ * 3.7 Close directory stream, flag is f_directory_recurse_do_flag_clean_d | f_directory_recurse_do_flag_path_list_d.
+ * 4. Process top-level directory.
+ * 4.1 Process before state on path, flag is f_directory_recurse_do_flag_before_d | f_directory_recurse_do_flag_directory_d | recurse.flag.
+ * 4.2 Process action state on path, flag is f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_directory_d | recurse.flag.
+ * 4.3 Process after state on path, flag is f_directory_recurse_do_flag_after_d | f_directory_recurse_do_flag_directory_d | recurse.flag.
*
* @param path
* The directory file path.
#if !defined(_di_fl_directory_do_)
void private_fl_directory_do_recurse(f_directory_recurse_do_t * const recurse) {
+ f_directory_stream_t stream = f_directory_stream_t_initialize;
+
+ recurse->state.status = f_directory_stream_open(recurse->path, &stream);
+
+ if (F_status_is_error(recurse->state.status)) {
+ private_inline_fl_directory_do_handle(recurse, recurse->path, f_directory_recurse_do_flag_list_d);
+ if (F_status_is_error(recurse->state.status)) return;
+
+ if (recurse->state.status != F_done) {
+ recurse->state.status = F_okay;
+ }
+ }
+
const uint32_t flag_actions[] = {
recurse->flag & f_directory_recurse_do_flag_before_d ? f_directory_recurse_do_flag_before_d : 0,
f_directory_recurse_do_flag_action_d,
const f_number_unsigned_t used_original = recurse->path.used;
- f_directory_stream_t stream = f_directory_stream_t_initialize;
f_directory_entity_t entity = f_directory_entity_t_initialize;
f_string_static_t name = f_string_static_t_initialize;
uint8_t action = 0;
f_number_unsigned_t used_directory = 0;
- recurse->state.status = f_directory_stream_open(recurse->path, &stream);
-
- if (F_status_is_error(recurse->state.status)) {
- private_inline_fl_directory_do_handle(recurse, recurse->path, f_directory_recurse_do_flag_path_list_d);
- if (F_status_is_error(recurse->state.status)) return;
-
- if (recurse->state.status != F_done) {
- recurse->state.status = F_okay;
- }
- }
-
for (;;) {
if (recurse->state.interrupt) {
recurse->state.interrupt((void *) &recurse->state, (void *) recurse);
if (recurse->state.status == F_okay_eos) break;
if (F_status_is_error(recurse->state.status)) {
- private_inline_fl_directory_do_handle(recurse, recurse->path, f_directory_recurse_do_flag_action_d | f_directory_recurse_do_flag_path_list_d);
+ private_inline_fl_directory_do_handle(recurse, recurse->path, f_directory_recurse_do_flag_path_list_d);
if (F_status_is_error(recurse->state.status)) return;
if (recurse->state.status != F_done) {
recurse->path.string[recurse->path.used] = 0;
if (F_status_is_error(recurse->state.status)) {
- private_inline_fl_directory_do_handle(recurse, name, f_directory_recurse_do_flag_directory_d);
+ private_inline_fl_directory_do_handle(recurse, name, f_directory_recurse_do_flag_path_d);
if (F_status_is_error(recurse->state.status)) break;
if (recurse->state.status == F_break || recurse->state.status == F_done) break;
recurse->state.status = f_directory_stream_close(stream);
if (F_status_is_error(recurse->state.status)) {
- private_inline_fl_directory_do_handle(recurse, recurse->path, f_directory_recurse_do_flag_clean_d | f_directory_recurse_do_flag_path_list_d);
+ private_inline_fl_directory_do_handle(recurse, recurse->path, f_directory_recurse_do_flag_clean_list_d);
if (F_status_is_error(recurse->state.status)) return;
if (recurse->state.status != F_done) {