diff options
Diffstat (limited to 'src/filelist.c')
-rw-r--r-- | src/filelist.c | 431 |
1 files changed, 312 insertions, 119 deletions
diff --git a/src/filelist.c b/src/filelist.c index 5c0777d..3d9bcef 100644 --- a/src/filelist.c +++ b/src/filelist.c @@ -1,6 +1,7 @@ /* filelist.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2024 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -25,12 +26,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "feh.h" #include "filelist.h" +#include "signals.h" #include "options.h" +#ifdef HAVE_LIBCURL +#include <curl/curl.h> +#endif + gib_list *filelist = NULL; +gib_list *original_file_items = NULL; /* original file items from argv */ int filelist_len = 0; gib_list *current_file = NULL; -extern int errno; static gib_list *rm_filelist = NULL; @@ -47,7 +53,12 @@ feh_file *feh_file_new(char *filename) newfile->name = estrdup(s + 1); else newfile->name = estrdup(filename); + newfile->size = -1; + newfile->mtime = 0; newfile->info = NULL; +#ifdef HAVE_LIBEXIF + newfile->ed = NULL; +#endif return(newfile); } @@ -63,6 +74,10 @@ void feh_file_free(feh_file * file) free(file->caption); if (file->info) feh_file_info_free(file->info); +#ifdef HAVE_LIBEXIF + if (file->ed) + exif_data_unref(file->ed); +#endif free(file); return; } @@ -76,7 +91,6 @@ feh_file_info *feh_file_info_new(void) info->width = 0; info->height = 0; - info->size = 0; info->pixels = 0; info->has_alpha = 0; info->format = NULL; @@ -111,13 +125,81 @@ gib_list *feh_file_remove_from_list(gib_list * list, gib_list * l) return(gib_list_remove(list, l)); } +int file_selector_all(const struct dirent *unused __attribute__((unused))) +{ + return 1; +} + +static void feh_print_stat_error(char *path) +{ + if (opt.quiet) + return; + + switch (errno) { + case ENOENT: + case ENOTDIR: + weprintf("%s does not exist - skipping", path); + break; + case ELOOP: + weprintf("%s - too many levels of symbolic links - skipping", path); + break; + case EACCES: + weprintf("you don't have permission to open %s - skipping", path); + break; + case EOVERFLOW: + weprintf("Cannot open %s - EOVERFLOW.\n" + "Recompile with stat64=1 to fix this", path); + break; + default: + weprintf("couldn't open %s", path); + break; + } +} + +static void add_stdin_to_filelist(void) +{ + char buf[1024]; + size_t readsize; + char *sfn = estrjoin("_", "/tmp/feh_stdin", "XXXXXX", NULL); + int fd = mkstemp(sfn); + FILE *outfile; + + if (fd == -1) { + free(sfn); + weprintf("cannot read from stdin: mktemp:"); + return; + } + + outfile = fdopen(fd, "w"); + + if (outfile == NULL) { + free(sfn); + weprintf("cannot read from stdin: fdopen:"); + return; + } + + while ((readsize = fread(buf, sizeof(char), sizeof(buf), stdin)) > 0) { + if (fwrite(buf, sizeof(char), readsize, outfile) < readsize) { + free(sfn); + fclose(outfile); + return; + } + } + fclose(outfile); + + filelist = gib_list_add_front(filelist, feh_file_new(sfn)); + add_file_to_rm_filelist(sfn); + free(sfn); +} + + /* Recursive */ void add_file_to_filelist_recursively(char *origpath, unsigned char level) { struct stat st; char *path; - if (!origpath) + if (!origpath || *origpath == '\0') return; path = estrdup(origpath); @@ -131,15 +213,17 @@ void add_file_to_filelist_recursively(char *origpath, unsigned char level) if (path[len - 1] == '/') path[len - 1] = '\0'; - if ((!strncmp(path, "http://", 7)) - || (!strncmp(path, "https://", 8)) - || (!strncmp(path, "ftp://", 6))) { - /* Its a url */ + if (path_is_url(path)) { D(("Adding url %s to filelist\n", path)); filelist = gib_list_add_front(filelist, feh_file_new(path)); /* We'll download it later... */ free(path); return; + } else if ((len == 1) && (path[0] == '-')) { + D(("Adding temporary file for stdin (-) to filelist\n")); + add_stdin_to_filelist(); + free(path); + return; } else if (opt.filelistfile) { char *newpath = feh_absolute_path(path); @@ -150,33 +234,15 @@ void add_file_to_filelist_recursively(char *origpath, unsigned char level) errno = 0; if (stat(path, &st)) { - /* Display useful error message */ - switch (errno) { - case ENOENT: - case ENOTDIR: - if (!opt.quiet) - weprintf("%s does not exist - skipping", path); - break; - case ELOOP: - if (!opt.quiet) - weprintf("%s - too many levels of symbolic links - skipping", path); - break; - case EACCES: - if (!opt.quiet) - weprintf("you don't have permission to open %s - skipping", path); - break; - default: - if (!opt.quiet) - weprintf("couldn't open %s", path); - break; - } + feh_print_stat_error(path); free(path); return; } if ((S_ISDIR(st.st_mode)) && (level != FILELIST_LAST)) { - struct dirent *de; + struct dirent **de; DIR *dir; + int cnt, n; D(("It is a directory\n")); @@ -186,24 +252,35 @@ void add_file_to_filelist_recursively(char *origpath, unsigned char level) free(path); return; } - de = readdir(dir); - while (de != NULL) { - if (strcmp(de->d_name, ".") - && strcmp(de->d_name, "..")) { - char *newfile; - - newfile = estrjoin("", path, "/", de->d_name, NULL); - - /* This ensures we go down one level even if not fully recursive - - this way "feh some_dir" expands to some_dir's contents */ - if (opt.recursive) - add_file_to_filelist_recursively(newfile, FILELIST_CONTINUE); - else - add_file_to_filelist_recursively(newfile, FILELIST_LAST); - - free(newfile); + n = scandir(path, &de, file_selector_all, alphasort); + if (n < 0) { + switch (errno) { + case ENOMEM: + weprintf("Insufficient memory to scan directory %s:", path); + break; + default: + weprintf("Failed to scan directory %s:", path); } - de = readdir(dir); + } else { + for (cnt = 0; cnt < n; cnt++) { + if (strcmp(de[cnt]->d_name, ".") + && strcmp(de[cnt]->d_name, "..")) { + char *newfile; + + newfile = estrjoin("", path, "/", de[cnt]->d_name, NULL); + + /* This ensures we go down one level even if not fully recursive + - this way "feh some_dir" expands to some_dir's contents */ + if (opt.recursive) + add_file_to_filelist_recursively(newfile, FILELIST_CONTINUE); + else + add_file_to_filelist_recursively(newfile, FILELIST_LAST); + + free(newfile); + } + free(de[cnt]); + } + free(de); } closedir(dir); } else if (S_ISREG(st.st_mode)) { @@ -229,32 +306,49 @@ void delete_rm_files(void) return; } -gib_list *feh_file_info_preload(gib_list * list) +gib_list *feh_file_info_preload(gib_list * list, int load_images) { gib_list *l; feh_file *file = NULL; gib_list *remove_list = NULL; - if (opt.verbose) - fprintf(stdout, PACKAGE " - preloading...\n"); - for (l = list; l; l = l->next) { file = FEH_FILE(l->data); D(("file %p, file->next %p, file->name %s\n", l, l->next, file->name)); - if (feh_file_info_load(file, NULL)) { - D(("Failed to load file %p\n", file)); - remove_list = gib_list_add_front(remove_list, l); - if (opt.verbose) - feh_display_status('x'); - } else if (opt.verbose) - feh_display_status('.'); + if (load_images) { + if (feh_file_info_load(file, NULL)) { + D(("Failed to load file %p\n", file)); + remove_list = gib_list_add_front(remove_list, l); + if (opt.verbose) + feh_display_status('x'); + } else if (((unsigned int)file->info->width < opt.min_width) + || ((unsigned int)file->info->width > opt.max_width) + || ((unsigned int)file->info->height < opt.min_height) + || ((unsigned int)file->info->height > opt.max_height)) { + remove_list = gib_list_add_front(remove_list, l); + if (opt.verbose) + feh_display_status('s'); + } else if (opt.verbose) + feh_display_status('.'); + } else { + if (feh_file_stat(file)) { + D(("Failed to stat file %p\n", file)); + remove_list = gib_list_add_front(remove_list, l); + } + } + if (sig_exit) { + feh_display_status(0); + exit(sig_exit); + } } if (opt.verbose) - fprintf(stdout, "\n"); + feh_display_status(0); if (remove_list) { - for (l = remove_list; l; l = l->next) + for (l = remove_list; l; l = l->next) { + feh_file_free(FEH_FILE(((gib_list *) l->data)->data)); filelist = list = gib_list_remove(list, (gib_list *) l->data); + } gib_list_free(remove_list); } @@ -262,48 +356,39 @@ gib_list *feh_file_info_preload(gib_list * list) return(list); } -int feh_file_info_load(feh_file * file, Imlib_Image im) +int feh_file_stat(feh_file * file) { struct stat st; + + errno = 0; + if (stat(file->filename, &st)) { + feh_print_stat_error(file->filename); + return(1); + } + + file->mtime = st.st_mtime; + + file->size = st.st_size; + + return(0); +} + +int feh_file_info_load(feh_file * file, Imlib_Image im) +{ int need_free = 1; Imlib_Image im1; + if (feh_file_stat(file)) + return(1); + D(("im is %p\n", im)); if (im) need_free = 0; - errno = 0; - if (stat(file->filename, &st)) { - /* Display useful error message */ - switch (errno) { - case ENOENT: - case ENOTDIR: - if (!opt.quiet) - weprintf("%s does not exist - skipping", file->filename); - break; - case ELOOP: - if (!opt.quiet) - weprintf("%s - too many levels of symbolic links - skipping", file->filename); - break; - case EACCES: - if (!opt.quiet) - weprintf("you don't have permission to open %s - skipping", file->filename); - break; - default: - if (!opt.quiet) - weprintf("couldn't open %s ", file->filename); - break; - } - return(1); - } - if (im) im1 = im; - else if (!feh_load_image(&im1, file)) - return(1); - - if (!im1) + else if (!feh_load_image(&im1, file) || !im1) return(1); file->info = feh_file_info_new(); @@ -317,21 +402,59 @@ int feh_file_info_load(feh_file * file, Imlib_Image im) file->info->format = estrdup(gib_imlib_image_format(im1)); - file->info->size = st.st_size; - - if (need_free && im1) + if (need_free) gib_imlib_free_image_and_decache(im1); return(0); } +void feh_file_dirname(char *dst, feh_file * f, int maxlen) +{ + int n = strlen(f->filename) - strlen(f->name); + + /* Give up on long dirnames */ + if (n <= 0 || n >= maxlen) { + dst[0] = '\0'; + return; + } + + memcpy(dst, f->filename, n); + dst[n] = '\0'; +} + +static inline int strcmp_or_strverscmp(const char *s1, const char *s2) +{ + if (!opt.version_sort) + return(strcmp(s1, s2)); + else + return(strverscmp(s1, s2)); +} + int feh_cmp_filename(void *file1, void *file2) { - return(strcmp(FEH_FILE(file1)->filename, FEH_FILE(file2)->filename)); + return(strcmp_or_strverscmp(FEH_FILE(file1)->filename, FEH_FILE(file2)->filename)); } int feh_cmp_name(void *file1, void *file2) { - return(strcmp(FEH_FILE(file1)->name, FEH_FILE(file2)->name)); + return(strcmp_or_strverscmp(FEH_FILE(file1)->name, FEH_FILE(file2)->name)); +} + +int feh_cmp_dirname(void *file1, void *file2) +{ + char dir1[PATH_MAX], dir2[PATH_MAX]; + int cmp; + feh_file_dirname(dir1, FEH_FILE(file1), PATH_MAX); + feh_file_dirname(dir2, FEH_FILE(file2), PATH_MAX); + if ((cmp = strcmp_or_strverscmp(dir1, dir2)) != 0) + return(cmp); + return(feh_cmp_name(file1, file2)); +} + +/* Return -1 if file1 is _newer_ than file2 */ +int feh_cmp_mtime(void *file1, void *file2) +{ + /* gib_list_sort is not stable, so explicitly return 0 as -1 */ + return(FEH_FILE(file1)->mtime >= FEH_FILE(file2)->mtime ? -1 : 1); } int feh_cmp_width(void *file1, void *file2) @@ -351,7 +474,7 @@ int feh_cmp_pixels(void *file1, void *file2) int feh_cmp_size(void *file1, void *file2) { - return((FEH_FILE(file1)->info->size - FEH_FILE(file2)->info->size)); + return((FEH_FILE(file1)->size - FEH_FILE(file2)->size)); } int feh_cmp_format(void *file1, void *file2) @@ -361,10 +484,25 @@ int feh_cmp_format(void *file1, void *file2) void feh_prepare_filelist(void) { - if (opt.list || opt.customlist || (opt.sort > SORT_FILENAME) - || opt.preload) { + /* + * list and customlist mode as well as the somewhat more fancy sort modes + * need access to file infos. Preloading them is also useful for + * list/customlist as --min-dimension/--max-dimension may filter images + * which should not be processed. + * Finally, if --min-dimension/--max-dimension (-> opt.filter_by_dimensions) + * is set and we're in thumbnail mode, we need to filter images first so + * we can create a properly sized thumbnail list. + */ + if (opt.list || opt.preload || opt.customlist || (opt.sort >= SORT_WIDTH) + || (opt.filter_by_dimensions && (opt.index || opt.thumbs || opt.bgmode))) { /* For these sort options, we have to preload images */ - filelist = feh_file_info_preload(filelist); + filelist = feh_file_info_preload(filelist, TRUE); + if (!gib_list_length(filelist)) + show_mini_usage(); + } else if (opt.sort >= SORT_SIZE) { + /* For these sort options, we need stat(2) information on the files, + * but there is no need to load the images. */ + filelist = feh_file_info_preload(filelist, FALSE); if (!gib_list_length(filelist)) show_mini_usage(); } @@ -386,6 +524,12 @@ void feh_prepare_filelist(void) case SORT_FILENAME: filelist = gib_list_sort(filelist, feh_cmp_filename); break; + case SORT_DIRNAME: + filelist = gib_list_sort(filelist, feh_cmp_dirname); + break; + case SORT_MTIME: + filelist = gib_list_sort(filelist, feh_cmp_mtime); + break; case SORT_WIDTH: filelist = gib_list_sort(filelist, feh_cmp_width); break; @@ -419,7 +563,7 @@ int feh_write_filelist(gib_list * list, char *filename) FILE *fp; gib_list *l; - if (!list || !filename) + if (!list || !filename || !strcmp(filename, "/dev/stdin")) return(0); errno = 0; @@ -441,26 +585,39 @@ gib_list *feh_read_filelist(char *filename) FILE *fp; gib_list *list = NULL; char s[1024], s1[1024]; - Imlib_Image im1; + Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE; + Imlib_Image tmp_im; + struct stat st; + signed short tmp_conversion_timeout; if (!filename) return(NULL); - /* try and load the given filelist as an image, cowardly refuse to - * overwrite an image with a filelist. (requested by user who did feh -df * - * when he meant feh -dF *, as it overwrote the first image with the - * filelist). + /* + * feh_load_image will fail horribly if filename is not seekable */ - if (feh_load_image_char(&im1, filename)) { - weprintf( - "The file you specified as a filelist to read - %s - appears to be an image. Ignoring it (this is a common mistake).\n", - filename); - opt.filelistfile = NULL; - return(NULL); + tmp_conversion_timeout = opt.conversion_timeout; + opt.conversion_timeout = -1; + if (!stat(filename, &st) && S_ISREG(st.st_mode)) { + tmp_im = imlib_load_image_with_error_return(filename, &err); + if (err == IMLIB_LOAD_ERROR_NONE) { + gib_imlib_free_image_and_decache(tmp_im); + weprintf("Filelist file %s is an image, refusing to use it.\n" + "Did you mix up -f and -F?", filename); + opt.filelistfile = NULL; + return NULL; + } } + opt.conversion_timeout = tmp_conversion_timeout; errno = 0; - if ((fp = fopen(filename, "r")) == NULL) { + + if (!strcmp(filename, "/dev/stdin")) + fp = stdin; + else + fp = fopen(filename, "r"); + + if (fp == NULL) { /* return quietly, as it's okay to specify a filelist file that doesn't exist. In that case we create it on exit. */ return(NULL); @@ -476,7 +633,8 @@ gib_list *feh_read_filelist(char *filename) /* Add it to the new list */ list = gib_list_add_front(list, feh_file_new(s1)); } - fclose(fp); + if (strcmp(filename, "/dev/stdin")) + fclose(fp); return(list); } @@ -490,17 +648,19 @@ char *feh_absolute_path(char *path) if (!path) return(NULL); - if (path[0] == '/') + if (path[0] == '/' || path_is_url(path)) return(estrdup(path)); /* This path is not relative. We're gonna convert it, so that a filelist file can be saved anywhere and feh will still find the images */ D(("Need to convert %s to an absolute form\n", path)); - /* I SHOULD be able to just use a simple realpath() here, but dumb * + /* I SHOULD be able to just use a simple realpath() here, but dumb * old Solaris's realpath doesn't return an absolute path if the path you give it is relative. Linux and BSD get this right... */ - getcwd(cwd, sizeof(cwd)); - snprintf(temp, sizeof(temp), "%s/%s", cwd, path); + if (getcwd(cwd, sizeof(cwd)) == NULL) + eprintf("Cannot determine working directory:"); + if ((size_t) snprintf(temp, sizeof(temp), "%s/%s", cwd, path) >= sizeof(temp)) + eprintf("Absolute path for working directory was truncated"); if (realpath(temp, fullpath) != NULL) { ret = estrdup(fullpath); } else { @@ -510,16 +670,49 @@ char *feh_absolute_path(char *path) return(ret); } -void feh_save_filelist() +void feh_save_filelist(void) { char *tmpname; + char *base_dir = ""; - tmpname = feh_unique_filename("", "filelist"); + if (opt.output_dir) { + base_dir = estrjoin("", opt.output_dir, "/", NULL); + } - if (!opt.quiet) - printf("saving filelist to filename '%s'\n", tmpname); + tmpname = feh_unique_filename(base_dir, "filelist"); + + if (opt.output_dir) { + free(base_dir); + } + + if (opt.verbose) + fprintf(stderr, "saving filelist to filename '%s'\n", tmpname); feh_write_filelist(filelist, tmpname); free(tmpname); return; } + +#ifdef HAVE_LIBCURL + +char *feh_http_unescape(char *url) +{ + CURL *curl = curl_easy_init(); + if (!curl) { + return NULL; + } + char *tmp_url = curl_easy_unescape(curl, url, 0, NULL); + char *new_url = estrdup(tmp_url); + curl_free(tmp_url); + curl_easy_cleanup(curl); + return new_url; +} + +#else + +char *feh_http_unescape(char *url) +{ + return NULL; +} + +#endif |