[A5] for_each_file - missing
PeKaGM

Hi to all.I'm trying to rewrite Allegro 4 code to Allegro 5 and I miss function - for_each_file - in A5. I found some routines on the net but they are not working. I have only little free time for programing so I can't study those routines for errors.Could someone tell me where can I find functional routine or some really simple way to replace function for_each_file in A5. I need to find all files in directory for example ".jpg" files and store their file names in a field of strings.Thank you very much.

Edgar Reynaldo

Basically you al_create_fs_entry a filesystem entry for your desired directory, then you al_open_directory the directory, al_read_directory it until there are no entries left, and then al_close_directory it.

Here is an example program that lists all the files in a given directory. You can specify the directory as a command line argument or else it will default to the current directory.

#SelectExpand
1 2#include "allegro5/allegro.h" 3 4#include <cstdio> 5#include <string> 6 7 8int main(int argc , char** argv) { 9 10 char* read_dir = 0; 11 bool free_dir = false; 12 bool close_dir = false; 13 14 15 if (!al_init()) {return 1;} 16 17 if (argc == 2) { 18 // Use first argument as read directory 19 read_dir = argv[1]; 20 } 21 else { 22 // Use current directory as read directory 23 read_dir = al_get_current_directory(); 24 free_dir = true; 25 } 26 27 ALLEGRO_FS_ENTRY* fs_dir_entry = al_create_fs_entry(read_dir); 28 29 30 unsigned int mode = al_get_fs_entry_mode(fs_dir_entry); 31 32 if (!(mode & ALLEGRO_FILEMODE_ISDIR)) { 33 printf("Invalid directory! (%s)\n" , read_dir); 34 } 35 36 if (!al_open_directory(fs_dir_entry)) { 37 printf("Could not open directory %s\n" , read_dir); 38 } 39 else { 40 /// Directory is now open 41 close_dir = true; 42 43 printf("Contents of directory %s\n" , read_dir); 44 45 // prime the while loop 46 ALLEGRO_FS_ENTRY* fs_entry = al_read_directory(fs_dir_entry); 47 48 // Loop until there are not more entries 49 while(fs_entry) { 50 51 // String manip to get short name of file/dir 52 std::string s = al_get_fs_entry_name(fs_entry); 53 std::string s2; 54 char path_sep = '\\'; 55 if (s.find_first_of('/') != std::string::npos) { 56 path_sep = '/'; 57 } 58 unsigned int last_separator = s.find_last_of(path_sep); 59 if (last_separator == std::string::npos) { 60 s2 = s; 61 } 62 else { 63 s2 = s.substr(s.find_last_of(path_sep) + 1); 64 } 65 66 // output short file/dir name 67 printf("%s\n" , s2.c_str()); 68 69 al_destroy_fs_entry(fs_entry); 70 71 fs_entry = al_read_directory(fs_dir_entry); 72 } 73 } 74 75 76 if (close_dir) { 77 al_close_directory(fs_dir_entry); 78 } 79 80 if (fs_dir_entry) { 81 al_destroy_fs_entry(fs_dir_entry); 82 fs_dir_entry = 0; 83 } 84 85 if (free_dir) { 86 al_free(read_dir); 87 read_dir = 0; 88 } 89 return 0; 90}

beoran

Hmm, seeing that the code to replicate for_each_file is relatively long, I'm in favor of adding an al_for_each_file() function.

Elias

beoran +1

Thomas Fjellstrom

He could also use the ALLEGRO_PATH api to handle paths and files. Would cut out a bit of that code.

Elias

He could also use the ALLEGRO_PATH api to handle paths and files. Would cut out a bit of that code.

Replace the loop with this then:

#SelectExpand
1while(fs_entry) { 2 ALLEGRO_PATH *path = al_create_path(al_get_fs_entry_name(fs_entry)) 3 /* Note: ownership of the strings stays with path */ 4 char const *name = al_get_path_filename(path) 5 if (not name[0]) { 6 /* Note: ownership of the strings stays with path */ 7 name = al_get_path_component(path, -1); 8 } 9 if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) { 10 bool is_dir = al_get_fs_entry_mode(fs_entry) & ALLEGRO_FILEMODE_ISDIR 11 12 printf("%s%s\n", name, is_dir ? "/" : ""); 13 } 14 /* Note: This also invalidates the name string. */ 15 al_destroy_path(path) 16 al_destroy_fs_entry(fs_entry); 17 fs_entry = al_read_directory(fs_dir_entry); 18}

I also added directory detection, but it's still kinda longish. The proposed change would look something like:

void callback(char const *filename, void *user) {
    printf("%s\n", filename);
}
al_for_each_file(read_dir, callback, int flags, user);

Which is much more simple :) It also handles common errors someone would make on implementing it on their own, like not ignoring the "." and ".." entries we have on some platforms and dealing with directories where al_get_path_filename fails.

Possible flags could be:
FULL_PATH - pass the full path to the callback not just the filename
RECURSIVE - also recurse into any directories found
INCLUDE_DIRECTORIES - also call the callback with directory names not just files

beoran

Actually I already made a patch, with two functions, documentation and a test. One function uses a callback with char * file names and the other with ALLEGRO_FS_ENTRY. :)

The flags parameter is a good idea though, I'll modify my patch to use that. I'll probably add the ALLEGRO_EACH_FILE_RECURSE, ALLEGRO_EACH_FILE_DIRECTORIES, ALLEGRO_EACH_FILE_DOTFILES and ALLEGRO_EACH_FILE_SHORT_PATH flags. The default call does not recurse, does not return directories, does return not dotfiles, and does return full file names, not partial ones, since full names are normally most useful.

Thread #614478. Printed from Allegro.cc