[AD] al_for_each_file and al_for_each_fs_entry |
[ Thread Index |
Date Index
| More lists.liballeg.org/allegro-developers Archives
]
As this was requested on the forum and several liked the idea, here is
a documented implementation of al_for_each_file and
al_for_each_fs_entry, with examples added to ex_dir.c.
I hope this is acceptable, if not I will improve it.
Kind regards,
Bjorn.
diff --git a/docs/src/refman/fshook.txt b/docs/src/refman/fshook.txt
index 1aaa5b2..81df80b 100644
--- a/docs/src/refman/fshook.txt
+++ b/docs/src/refman/fshook.txt
@@ -27,6 +27,29 @@ Filesystem modes/types
* ALLEGRO_FILEMODE_ISFILE - Regular file
* ALLEGRO_FILEMODE_ISDIR - Directory
+## API: ALLEGRO_FOR_EACH_FILE_FLAGS
+
+This enum contain flags that control the operation of [al_for_each_file]
+and [al_for_each_fs_entry].
+
+* ALLEGRO_FOR_EACH_FILE_RECURSE - Recurse into directories
+* ALLEGRO_FOR_EACH_FILE_BASENAME - Call the callback with the basename
+ of the file in stead of the full absolute path only for [al_for_each_file].
+* ALLEGRO_FOR_EACH_FILE_FILTER - Filter out files that don't match one of
+ [ALLEGRO_FILE_MODE] flags which should e binary orred with this flag.
+
+## API: ALLEGRO_FOR_EACH_FILE_RESULTS
+
+Thi enum contails the list of values that [al_for_each_file]
+and [al_for_each_fs_entry] and their callback functions may return.
+
+* ALLEGRO_FOR_EACH_FILE_ERROR - An error occured, iteration was halted.
+ Use [al_get_errno] to get an error code that details the problem. The error
+ code can be one of +ENOMEM+, +ENOTDIR+ or +ENOENT+.
+* ALLEGRO_FOR_EACH_FILE_STOP - Return this value from the callback to top
+ iteration without there being an error.
+* ALLEGRO_FOR_EACH_FILE_OK - Return this value to continue iterating.
+
## API: al_create_fs_entry
Creates an [ALLEGRO_FS_ENTRY] object pointing to path on the filesystem.
@@ -210,6 +233,109 @@ Returns the handle on success, NULL on error.
See also: [al_fopen]
+### API: al_for_each_fs_entry
+
+This convenience function looks up all entries under +dir+, and then
+will call the callback function +callback+ once iteratively for every filesystem
+entry in that directory. Note that the +.+ and +..+ directories will never show
+up, they are filtered away automatically.
+
+The +callback+ will be called with a +entry+ pointing to an [ALLEGRO_FS_ENTRY]
+that matches one file under +dir+, and the pointer +extra+ as it was passed
+to [al_for_each_fs_entry]. This +extra+ parameter is useful for storing
+results or hooking your own extra behavior to [al_for_each_fs_entry].
+
+When +callback+ returns +ALLEGRO_FOR_EACH_FILE_OK+ then iteration will contine
+with the next file system entry. However if +callback+ returns
++ALLEGRO_FOR_EACH_FILE_STOP+ or +ALLEGRO_FOR_EACH_FILE_ERROR+, then iteration
+will stop.
+
+Note that the +entry+ parameter of +callback+ will be destroyed automatically
+when +callback+ returns so if you want to keep it you will need to make
+a duplicate and store that somewhere yourself.
+
+The flags may be 0 or a combination of one or more [ALLEGRO_FOR_EACH_FILE_FLAGS]
+and [ALLEGRO_FILE_MODE] binary OR'd together.
+
+If the flags have +ALLEGRO_FOR_EACH_FILE_RECURSE+ set then
+[al_for_each_fs_entry] will recurse into all directories it finds
+in +dir+ and sub direcories of it.
+
+The flag +ALLEGRO_FOR_EACH_FILE_BASENAME+ has no effect for
+[al_for_each_fs_entry], it's for use by [al_for_each_file].
+
+If the flag ALLEGRO_FOR_EACH_FILE_FILTER is set, then you should binary OR it
+with one or more [ALLEGRO_FILE_MODE] flags. Filesystem entries of which the
+[al_get_fs_entry_mode] doesn't match the [ALLEGRO_FILE_MODE] flags ORrred into
++flags+ will be filtered out in that +callback+ will not be called for them.
+However, this has no effect on recursive iteration which will visit even
+directories that are being filtered out.
+
+This function returns +ALLEGRO_FOR_EACH_FILE_OK+ if it sucessfully
+iterated over all requested entries of +dir+. It returns
++ALLEGRO_FOR_EACH_FILE_STOP+ if the +callback+ requested to stop iteration
+by returning this value. If something went wrong along the way
++ALLEGRO_FOR_EACH_FILE_ERROR+ will be returned and [al_get_errno] will return
+one of ENOMEM, ENOENT or ENOTDIR to indicate more in detail what went wrong.
+
+See also: [al_for_each_filename]
+
+### API: al_for_each_file
+
+AL_FUNC(bool, al_for_each_file, (
+ const char *path,
+ bool (*callback)(const char * filename, int mode, void *extra),
+ int flags,
+ void *extra));
+
+
+This convenience function looks up all entries under the directory +dir+, and
+then will call the callback function +callback+ once iteratively for every
+filesystem entry in that directory. Note that the +.+ and +..+ directories will
+never show up, they are filtered away automatically.
+
+The +callback+ will be called with a +filename+ pointing to an
+string with the filename that matches one file under +dir+. The +callback+ a
+also receives a +mode+ parammeter which is the [al_get_fs_entry_name] of the
+corresponding file, and the pointer +extra+ as it was passed
+to [al_for_each_fs_entry]. This +extra+ parameter is useful for storing
+results or hooking your own extra behavior to [al_for_each_fs_entry].
+
+When +callback+ returns +ALLEGRO_FOR_EACH_FILE_OK+ then iteration will contine
+with the next file system entry. However if +callback+ returns
++ALLEGRO_FOR_EACH_FILE_STOP+ or +ALLEGRO_FOR_EACH_FILE_ERROR+, then iteration
+will stop.
+
+Note that the +filename+ parameter of +callback+ will be destroyed automatically
+when +callback+ returns so if you want to keep it you will need to make a
+duplicate and store that somewhere yourself.
+
+The flags may be 0 or a combination of one or more [ALLEGRO_FOR_EACH_FILE_FLAGS]
+and [ALLEGRO_FILE_MODE] binary OR'd together.
+
+If the flags have +ALLEGRO_FOR_EACH_FILE_RECURSE+ set then
+[al_for_each_file] will recurse into all directories it finds
+in +dir+ and sub direcories of it.
+
+If the flag +ALLEGRO_FOR_EACH_FILE_BASENAME+ is set, then filename will contain
+the basename of the file name, that is, the file name without the full
+directory.
+
+If the flag ALLEGRO_FOR_EACH_FILE_FILTER is set, then you should binary OR it
+with one or more [ALLEGRO_FILE_MODE] flags. Files of which the +mode+ doesn't
+match the [ALLEGRO_FILE_MODE] flags ORrred into +flags+ will be filtered out in
+that +callback+ will not be called for them. However, this has no effect on
+recursive iteration which will visit even directories that are being filtered
+out.
+
+This function returns +ALLEGRO_FOR_EACH_FILE_OK+ if it sucessfully
+iterated over all requested entries of +dir+. It returns
++ALLEGRO_FOR_EACH_FILE_STOP+ if the +callback+ requested to stop iteration
+by returning this value. If something went wrong along the way
++ALLEGRO_FOR_EACH_FILE_ERROR+ will be returned and [al_get_errno] will return
+one of ENOMEM, ENOENT or ENOTDIR to indicate more in detail what went wrong.
+
+See also: [al_for_each_fs_entry]
## Alternative filesystem functions
diff --git a/examples/ex_dir.c b/examples/ex_dir.c
index 32623d3..17d40cf 100644
--- a/examples/ex_dir.c
+++ b/examples/ex_dir.c
@@ -53,6 +53,37 @@ static void print_entry(ALLEGRO_FS_ENTRY *entry)
al_close_directory(entry);
}
+
+static bool print_directory_callback(const char * filename, int mode, void * extra) {
+ log_printf("%s: (%d) %s\n", (char *) extra, mode, filename);
+ return true;
+}
+
+static void print_directory(char * dirname)
+{
+ log_printf("\n------------------------------------\nExample of al_for_each_filename:\n\n");
+ al_for_each_file(dirname, print_directory_callback,
+ ALLEGRO_FOR_EACH_FILE_SHORT_NAME |
+ ALLEGRO_FILEMODE_ISFILE |
+ ALLEGRO_FOR_EACH_FILE_FILTER |
+ ALLEGRO_FOR_EACH_FILE_RECURSE,
+ (void*) dirname);
+}
+
+
+static bool print_fs_entry_callback(ALLEGRO_FS_ENTRY * entry, void * extra) {
+ (void) extra;
+ print_file(entry);
+ return true;
+}
+
+static void print_fs_entry(ALLEGRO_FS_ENTRY * dir)
+{
+ log_printf("\n------------------------------------\nExample of al_for_each_fs_entry:\n\n");
+ al_for_each_fs_entry(dir, print_fs_entry_callback, 0, (void*) al_get_fs_entry_name(dir));
+}
+
+
int main(int argc, char **argv)
{
int i;
@@ -62,7 +93,7 @@ int main(int argc, char **argv)
}
open_log_monospace();
- log_printf("%-36s %-6s %8s %8s %8s %8s\n",
+ log_printf("Example of filesystem entry functions:\n\n%-36s %-6s %8s %8s %8s %8s\n",
"name", "flags", "ctime", "mtime", "atime", "size");
log_printf(
"------------------------------------ "
@@ -75,13 +106,19 @@ int main(int argc, char **argv)
if (argc == 1) {
ALLEGRO_FS_ENTRY *entry = al_create_fs_entry("data");
print_entry(entry);
+ print_fs_entry(entry);
al_destroy_fs_entry(entry);
+ print_directory("data");
+ print_directory("data");
+ print_directory("data");
}
for (i = 1; i < argc; i++) {
ALLEGRO_FS_ENTRY *entry = al_create_fs_entry(argv[i]);
print_entry(entry);
+ print_fs_entry(entry);
al_destroy_fs_entry(entry);
+ print_directory(argv[i]);
}
close_log(true);
diff --git a/include/allegro5/fshook.h b/include/allegro5/fshook.h
index 004b0c4..9472bd2 100644
--- a/include/allegro5/fshook.h
+++ b/include/allegro5/fshook.h
@@ -115,6 +115,39 @@ AL_FUNC(bool, al_make_directory, (const char *path));
AL_FUNC(ALLEGRO_FILE *, al_open_fs_entry, (ALLEGRO_FS_ENTRY *e,
const char *mode));
+/* Utility functions and callbacks for them. */
+
+/* Enum: ALLEGRO_FOR_EACH_FILE_FLAGS
+ */
+typedef enum ALLEGRO_FOR_EACH_FILE_FLAGS
+{
+ ALLEGRO_FOR_EACH_FILE_RECURSE = 1 << 8,
+ ALLEGRO_FOR_EACH_FILE_SHORT_NAME = 1 << 9,
+ ALLEGRO_FOR_EACH_FILE_FILTER = 1 << 10,
+} ALLEGRO_FOR_EACH_FILE_FLAGS;
+
+
+/* Enum: ALLEGRO_FOR_EACH_FILE_RESULTS
+ */
+typedef enum ALLEGRO_FOR_EACH_FILE_RESULTS
+{
+ ALLEGRO_FOR_EACH_FILE_ERROR = -1,
+ ALLEGRO_FOR_EACH_FILE_STOP = 0,
+ ALLEGRO_FOR_EACH_FILE_OK = 1
+} ALLEGRO_FOR_EACH_FILE_FLAGS;
+
+
+AL_FUNC(int, al_for_each_fs_entry, (
+ ALLEGRO_FS_ENTRY *dir,
+ int (*callback)(ALLEGRO_FS_ENTRY *e, void *extra),
+ int flags,
+ void *extra));
+
+AL_FUNC(int, al_for_each_file, (
+ const char *path,
+ int (*callback)(const char * filename, int mode, void *extra),
+ int flags,
+ void *extra));
/* Thread-local state. */
AL_FUNC(const ALLEGRO_FS_INTERFACE *, al_get_fs_interface, (void));
diff --git a/src/fshook.c b/src/fshook.c
index 4cd6d69..ab3f25c 100644
--- a/src/fshook.c
+++ b/src/fshook.c
@@ -235,6 +235,128 @@ ALLEGRO_FILE *al_open_fs_entry(ALLEGRO_FS_ENTRY *e, const char *mode)
}
+/* Utility functions and callbacks for them. */
+
+
+/* Halper to handle a single entry of al_for_each_entry */
+static int al_for_each_fs_entry_handle_entry(
+ ALLEGRO_FS_ENTRY *entry,
+ int (*callback)(ALLEGRO_FS_ENTRY *e, void *extra),
+ int flags,
+ void *extra)
+{
+ int result;
+ int mode = al_get_fs_entry_mode(entry);
+ /* Handle recursion if requested. Do this before filtering so even
+ * filetered entries are recursed into. This also ensures depth-first
+ * recursion.
+ */
+ if (mode & ALLEGRO_FILEMODE_ISDIR) {
+ if (flags & ALLEGRO_FOR_EACH_FILE_RECURSE) {
+ result = al_for_each_fs_entry(entry, callback, flags, extra);
+ if (result < ALLEGRO_FOR_EACH_FILE_OK) {
+ return result;
+ }
+ }
+ }
+
+ /* Filter if requested. */
+ if (flags & ALLEGRO_FOR_EACH_FILE_FILTER) {
+ int filter = flags & 0xff; /* Low bits contain filter. */
+ if (!(filter & mode)) return true;
+ }
+
+ result = callback(entry, extra);
+
+ return result;
+}
+
+
+/* Function: al_for_each_fs_entry
+ */
+bool al_for_each_fs_entry (
+ ALLEGRO_FS_ENTRY *dir,
+ bool (*callback)(ALLEGRO_FS_ENTRY *e, void *extra),
+ int flags,
+ void *extra)
+{
+ ALLEGRO_FS_ENTRY * entry;
+ al_set_errno(0);
+
+ if (!al_open_directory(dir)) {
+ al_set_errno(ENOENT);
+ return ALLEGRO_FOR_EACH_FILE_ERROR;
+ }
+
+ entry = al_read_directory(dir);
+ while(entry) {
+ int result = al_for_each_fs_entry_handle_entry(entry, callback, flags, extra);
+ al_destroy_fs_entry(entry);
+ if (result < ALLEGRO_FOR_EACH_FILE_OK) {
+ return result;
+ }
+ entry = al_read_directory(dir);
+ }
+
+ return ALLEGRO_FOR_EACH_FILE_OK;
+}
+
+typedef struct ALLEGRO_FOR_EACH_FILE_INFO {
+ int (*callback)(const char * filename, int mode, void *extra);
+ int flags;
+ void * extra;
+ const char * path;
+} ALLEGRO_FOR_EACH_FILE_INFO;
+
+
+/* Helper callback that will map al_for_each_fs_entry to
+ * for each file. */
+static int al_for_each_file_callback_wrapper(ALLEGRO_FS_ENTRY * e, void * extra) {
+ ALLEGRO_FOR_EACH_FILE_INFO * info = extra;
+ int result;
+ const char * name = al_get_fs_entry_name(e);
+ int mode = al_get_fs_entry_mode(e);
+ if (info->flags & ALLEGRO_FOR_EACH_FILE_SHORT_NAME) {
+ ALLEGRO_PATH * path = al_create_path(name);
+ if (!path) {
+ al_set_errno(ENOMEM);
+ return AL_FOR_EACH_FILE_ERROR;
+ }
+ result = info->callback(al_get_path_basename(path), mode, info->extra);
+ al_destroy_path(path);
+ } else {
+ result = info->callback(name, mode, info->extra);
+ }
+ return result;
+}
+
+/* Function: al_for_each_file
+ */
+int al_for_each_file(
+ const char *path,
+ int (*callback)(const char * filename, int mode, void *extra),
+ int flags,
+ void *extra)
+{
+ ALLEGRO_FOR_EACH_FILE_INFO info;
+ ALLEGRO_FS_ENTRY * dir;
+ int result;
+ dir = al_create_fs_entry(path);
+ if (!dir) {
+ al_set_errno(ENOENT);
+ }
+ info.callback = callback;
+ info.extra = extra;
+ info.flags = flags;
+ info.path = path;
+ result = al_for_each_fs_entry(dir, al_for_each_file_callback_wrapper,
+ flags, &info);
+ al_destroy_fs_entry(dir);
+ return result;
+}
+
+
+
/*
* Local Variables:
* c-basic-offset: 3