diff --git a/demux/demux_playlist.c b/demux/demux_playlist.c index 6b6b2be144..a99d8be7c8 100644 --- a/demux/demux_playlist.c +++ b/demux/demux_playlist.c @@ -336,10 +336,27 @@ static bool same_st(struct stat *st1, struct stat *st2) return st1->st_dev == st2->st_dev && st1->st_ino == st2->st_ino; } +struct pl_dir_entry { + char *path; + char *name; + struct stat st; + bool is_dir; +}; + +static int cmp_dir_entry(const void *a, const void *b) +{ + struct pl_dir_entry *a_entry = (struct pl_dir_entry*) a; + struct pl_dir_entry *b_entry = (struct pl_dir_entry*) b; + if (a_entry->is_dir == b_entry->is_dir) { + return mp_natural_sort_cmp(a_entry->name, b_entry->name); + } else { + return a_entry->is_dir ? 1 : -1; + } +} + // Return true if this was a readable directory. static bool scan_dir(struct pl_parser *p, char *path, - struct stat *dir_stack, int num_dir_stack, - char ***files, int *num_files) + struct stat *dir_stack, int num_dir_stack) { if (strlen(path) >= 8192 || num_dir_stack == MAX_DIR_STACK) return false; // things like mount bind loops @@ -350,7 +367,11 @@ static bool scan_dir(struct pl_parser *p, char *path, return false; } + struct pl_dir_entry *dir_entries = NULL; + int num_dir_entries = 0; + int path_len = strlen(path); int dir_mode = p->opts->dir_mode; + struct dirent *ep; while ((ep = readdir(dp))) { if (ep->d_name[0] == '.') @@ -362,7 +383,7 @@ static bool scan_dir(struct pl_parser *p, char *path, char *file = mp_path_join(p, path, ep->d_name); struct stat st; - if (dir_mode != DIR_LAZY && stat(file, &st) == 0 && S_ISDIR(st.st_mode)) { + if (stat(file, &st) == 0 && S_ISDIR(st.st_mode)) { if (dir_mode != DIR_IGNORE) { for (int n = 0; n < num_dir_stack; n++) { if (same_st(&dir_stack[n], &st)) { @@ -371,23 +392,33 @@ static bool scan_dir(struct pl_parser *p, char *path, } } - dir_stack[num_dir_stack] = st; - scan_dir(p, file, dir_stack, num_dir_stack + 1, files, num_files); + struct pl_dir_entry d = {file, &file[path_len], st, true}; + MP_TARRAY_APPEND(p, dir_entries, num_dir_entries, d); } } else { - MP_TARRAY_APPEND(p, *files, *num_files, file); + struct pl_dir_entry f = {file, &file[path_len], .is_dir = false}; + MP_TARRAY_APPEND(p, dir_entries, num_dir_entries, f); } skip: ; } - closedir(dp); - return true; -} -static int cmp_filename(const void *a, const void *b) -{ - return mp_natural_sort_cmp(*(char **)a, *(char **)b); + if (dir_entries) + qsort(dir_entries, num_dir_entries, sizeof(dir_entries[0]), cmp_dir_entry); + + for (int n = 0; n < num_dir_entries; n++) { + if (dir_mode == DIR_RECURSIVE && dir_entries[n].is_dir) { + dir_stack[num_dir_stack] = dir_entries[n].st; + char *file = dir_entries[n].path; + scan_dir(p, file, dir_stack, num_dir_stack + 1); + } + else { + playlist_add_file(p->pl, dir_entries[n].path); + } + } + + return true; } static int parse_dir(struct pl_parser *p) @@ -401,21 +432,13 @@ static int parse_dir(struct pl_parser *p) if (!path) return -1; - char **files = NULL; - int num_files = 0; struct stat dir_stack[MAX_DIR_STACK]; - scan_dir(p, path, dir_stack, 0, &files, &num_files); - - if (files) - qsort(files, num_files, sizeof(files[0]), cmp_filename); - - for (int n = 0; n < num_files; n++) - playlist_add_file(p->pl, files[n]); + scan_dir(p, path, dir_stack, 0); p->add_base = false; - return num_files > 0 ? 0 : -1; + return p->pl->num_entries > 0 ? 0 : -1; } #define MIME_TYPES(...) \