These functions should help make appending interactive or user-data for directory path building easier.
}
#endif // _di_fl_directory_list_
+#ifndef _di_fl_directory_path_pop_
+ f_return_status fl_directory_path_pop(f_string_static *path) {
+ #ifndef _di_level_0_parameter_checking_
+ if (path->used > path->size) return F_status_set_error(F_parameter);
+ #endif // _di_level_0_parameter_checking_
+
+ if (path->used == 0) {
+ return F_data_not;
+ }
+
+ bool null_terminated = path->string[path->used] == 0;
+ bool first_nulless = F_false;
+
+ f_string_length i = path->used - 1;
+ f_string_length j = 0;
+
+ f_status status = F_none;
+
+ for (; i > 0; i--) {
+ if (path->string[i] == 0) continue;
+
+ status = f_utf_is_control(path->string + i, path->used - i);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ if (first_nulless) {
+ if (path->string[i] == f_path_separator[0]) {
+ if (null_terminated) {
+ path->string[i + 1] = 0;
+ path->used = i + 2;
+ }
+ else {
+ path->used = i + 1;
+ }
+
+ return F_none;
+ }
+ }
+ else {
+ first_nulless = F_true;
+
+ for (j = i; j > 0; j--) {
+ if (path->string[j] == 0) continue;
+
+ status = f_utf_is_control(path->string + j, path->used - j);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ if (path->string[j] != f_path_separator[0]) {
+ i = j + 1;
+ break;
+ }
+ } // for
+ }
+ } // for
+
+ path->used = 0;
+ return F_none;
+ }
+#endif // _di_fl_directory_path_pop_
+
+#ifndef _di_fl_directory_path_push_
+ f_return_status fl_directory_path_push(const f_string source, f_string_length length, f_string_dynamic *destination) {
+ #ifndef _di_level_0_parameter_checking_
+ if (destination->used > destination->size) return F_status_set_error(F_parameter);
+ #endif // _di_level_0_parameter_checking_
+
+ if (length == 0) {
+ return F_data_not;
+ }
+
+ return private_fl_directory_path_push(source, length, destination);
+ }
+#endif // _di_fl_directory_path_push_
+
+#ifndef _di_fl_directory_path_push_dynamic_
+ f_return_status fl_directory_path_push_dynamic(const f_string_static source, f_string_dynamic *destination) {
+ #ifndef _di_level_0_parameter_checking_
+ if (source.used > source.size) return F_status_set_error(F_parameter);
+ if (destination->used > destination->size) return F_status_set_error(F_parameter);
+ #endif // _di_level_0_parameter_checking_
+
+ if (source.used == 0) {
+ return F_data_not;
+ }
+
+ return private_fl_directory_path_push(source.string, source.used, destination);
+ }
+#endif // _di_fl_directory_path_push_dynamic_
+
#ifdef __cplusplus
} // extern "C"
#endif
#include <level_0/memory.h>
#include <level_0/string.h>
#include <level_0/type.h>
-#include <level_0/file.h>
+#include <level_0/utf.h>
#include <level_0/directory.h>
+#include <level_0/file.h>
+#include <level_0/path.h>
#ifdef __cplusplus
extern "C" {
extern f_return_status fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), f_directory_listing *listing);
#endif // _di_fl_directory_list_
+/**
+ * Append a path string onto the destination path.
+ *
+ * This ensures that there is a trailing '/' after pop.
+ * This ignores control characters.
+ * This does not dynamically reallocate the string.
+ *
+ * @param path
+ * The path to remove a single directory.
+ * This will only be NULL terminated if path string is already NULL terminated.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not if path.used is 0.
+ * F_parameter (with error bit) if a parameter is invalid.
+ */
+#ifndef _di_fl_directory_path_pop_
+ extern f_return_status fl_directory_path_pop(f_string_static *path);
+#endif // _di_fl_directory_path_pop_
+
+/**
+ * Append a path string onto the destination path.
+ *
+ * This ensures that there is a leading and trailing '/' from source.
+ * This ignores control characters.
+ *
+ * @param source
+ * The path to append onto the destination.
+ * This need not be NULL terminated.
+ * @param length
+ * The length of the string.
+ * Must not exceed length of source.
+ * @param destination
+ * The destination path to push the path part onto.
+ * Any terminating NULLs at the end of the destination string are removed before appending.
+ * This will only be NULL terminated if destination string is already NULL terminated.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not if length is 0.
+ * F_parameter (with error bit) if a parameter is invalid.
+ */
+#ifndef _di_fl_directory_path_push_
+ extern f_return_status fl_directory_path_push(const f_string source, f_string_length length, f_string_dynamic *destination);
+#endif // _di_fl_directory_path_push_
+
+/**
+ * Append a dynamic path string onto the destination path.
+ *
+ * This ensures that there is a leading and trailing '/' from source.
+ * This ignores control characters.
+ *
+ * @param source
+ * The path to append onto the destination.
+ * This need not be NULL terminated.
+ * @param destination
+ * The destination path to push the path part onto.
+ * Any terminating NULLs at the end of the destination string are removed before appending.
+ * This will only be NULL terminated if destination string is already NULL terminated.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not if source.used is 0.
+ * F_parameter (with error bit) if a parameter is invalid.
+ */
+#ifndef _di_fl_directory_path_push_dynamic_
+ extern f_return_status fl_directory_path_push_dynamic(const f_string_static source, f_string_dynamic *destination);
+#endif // _di_fl_directory_path_push_dynamic_
+
#ifdef __cplusplus
} // extern "C"
#endif
}
#endif // !defined(_di_fl_directory_list_)
+#if !defined(_di_fl_directory_path_push_) || !defined(_di_fl_directory_path_push_dynamic_)
+ f_return_status private_fl_directory_path_push(const f_string source, const f_string_length length, f_string_dynamic *destination) {
+ bool terminated_null = F_false;
+ bool separator_prepend = F_false;
+ bool separator_append = F_false;
+
+ f_string_length total = 0;
+ f_string_length start = 0;
+ f_string_length length_truncated = length;
+ f_status status = F_none;
+
+ {
+ f_string_length i = 0;
+ f_string_length j = 0;
+
+ if (destination->used > 0) {
+ if (destination->string[destination->used - 1] == 0) {
+ terminated_null = F_true;
+ total = 1;
+
+ destination->used--;
+ }
+
+ for (i = destination->used - 1; i > 0; i--) {
+ if (destination->string[i] == 0) continue;
+
+ status = f_utf_is_control(destination->string + i, destination->used - i);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ if (destination->string[i] == f_path_separator[0]) {
+ if (i - 1 > 0) {
+ for (j = i - 1; j > 0; j--) {
+ if (destination->string[j] == 0) continue;
+
+ status = f_utf_is_control(destination->string + j, destination->used - j);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ if (destination->string[j] == f_path_separator[0]) {
+ destination->used = j + 1;
+ }
+ else {
+ break;
+ }
+ } // for
+ }
+ }
+ else {
+ separator_prepend = F_true;
+ total++;
+ }
+
+ break;
+ } // for
+
+ if (destination->used > 0 && i == 0) {
+ if (destination->string[0] != 0 && destination->string[0] != f_path_separator[0]) {
+ separator_prepend = F_true;
+ total++;
+ }
+ }
+ }
+
+ for (i = length - 1; i > 0; i--) {
+ if (source[i] == 0) continue;
+
+ status = f_utf_is_control(source + i, length - i);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ if (source[i] == f_path_separator[0]) {
+ if (!separator_prepend && destination->used > 0) {
+ destination->used--;
+ }
+
+ if (i - 1 > 0) {
+ for (j = i - 1; j > 0; j--) {
+ if (source[j] == 0) continue;
+
+ status = f_utf_is_control(source + j, length - j);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ if (source[j] == f_path_separator[0]) {
+ length_truncated = j + 1;
+ }
+ else {
+ break;
+ }
+ } // for
+ }
+ }
+ else {
+ separator_append = F_true;
+ total++;
+ }
+
+ break;
+ } // for
+
+ if (i == 0 && source[0] != f_path_separator[0]) {
+ separator_append = F_true;
+ total++;
+ }
+
+ for (i = 0; i < length_truncated; i++) {
+ if (source[i] == 0) continue;
+
+ status = f_utf_is_control(source + i, length - i);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ start = i;
+
+ if (source[0] == f_path_separator[0]) {
+ if (i + 1 < length_truncated) {
+ for (j = i + 1; j < length_truncated; j++) {
+ if (source[j] == 0) continue;
+
+ status = f_utf_is_control(source + j, length - j);
+ if (status == F_true) continue;
+
+ if (F_status_is_error(status)) {
+ if (F_status_set_fine(status) == F_incomplete_utf) continue;
+
+ return status;
+ }
+
+ if (source[j] == f_path_separator[0]) {
+ start = j;
+ }
+ else {
+ break;
+ }
+ } // for
+ }
+ }
+
+ break;
+ } // for
+
+ total += length_truncated - start;
+
+ if (destination->used + total > destination->size) {
+ if (destination->used + total > f_string_length_size) {
+ return F_status_set_error(F_string_too_large);
+ }
+
+ f_macro_string_dynamic_resize(status, (*destination), destination->used + total);
+ if (F_status_is_error(status)) return status;
+ }
+ }
+
+ if (separator_prepend) {
+ destination->string[destination->used] = f_path_separator[0];
+ destination->used++;
+ total--;
+ }
+
+ if (length_truncated - start > 0) {
+ memcpy(destination->string + destination->used, source + start, length_truncated - start);
+ }
+
+ destination->used += total;
+
+ if (separator_append) {
+ if (terminated_null) {
+ destination->string[destination->used - 2] = f_path_separator[0];
+ destination->string[destination->used - 1] = 0;
+ }
+ else {
+ destination->string[destination->used - 1] = f_path_separator[0];
+ }
+ }
+ else if (terminated_null) {
+ destination->string[destination->used - 1] = 0;
+ }
+
+ return F_none;
+ }
+#endif // !defined(_di_fl_directory_path_push_) || !defined(_di_fl_directory_path_push_dynamic_)
+
#ifdef __cplusplus
} // extern "C"
#endif
extern f_return_status private_fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), f_directory_listing *listing) f_gcc_attribute_visibility_internal;
#endif // !defined(_di_fl_directory_list_)
+/**
+ * Private implementation of fl_directory_path_push().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param source
+ * The path to append onto the destination.
+ * This need not be NULL terminated.
+ * @param length
+ * The length of the string.
+ * Must not exceed length of source.
+ * @param destination
+ * The destination path to push the path part onto.
+ * Any terminating NULLs at the end of the destination string are removed before appending.
+ * This will only be NULL terminated if destination string is already NULL terminated.
+ *
+ * @return
+ * F_none on success.
+ * F_data_not if length is 0.
+ * F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_directory_path_push()
+ * @see fl_directory_path_push_dynamic()
+ */
+#if !defined(_di_fl_directory_path_push_) || !defined(_di_fl_directory_path_push_dynamic_)
+ extern f_return_status private_fl_directory_path_push(const f_string source, const f_string_length length, f_string_dynamic *destination) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_directory_path_push_) || !defined(_di_fl_directory_path_push_dynamic_)
+
#ifdef __cplusplus
} // extern "C"
#endif
f_status
f_memory
f_string
-f_file
+f_utf
f_directory
+f_file
+f_path
build_compiler gcc
build_linker ar
build_libraries -lc
-build_libraries_fll -lf_file -lf_directory -lf_memory
+build_libraries_fll -lf_file -lf_directory -lf_path -lf_utf -lf_memory
build_sources_library directory.c private-directory.c
-build_sources_program
+build_sources_program
build_sources_headers directory.h
build_sources_bash
build_sources_settings