From ecca345f3ea63dfe85759311cea90227bdc4384a Mon Sep 17 00:00:00 2001 From: "Dmitrij D. Czarkoff" Date: Tue, 12 Jan 2016 21:01:30 +0100 Subject: Loading image data from standard input This commit changes processing of arguments: * When called without arguments (with or without flags), imv reads list of files from standard input. * When called with "-" among arguments, read image data from standard input. --- doc/imv.1 | 24 ++++++++++++++++----- src/loader.c | 57 ++++++++++++++++++++++++++++++++++++++++++-------- src/loader.h | 6 +++++- src/main.c | 68 +++++++++++++++++++++++++++++++++++++++++------------------- src/util.c | 29 +++++++++++++++++++++++++- src/util.h | 3 +++ 6 files changed, 150 insertions(+), 37 deletions(-) diff --git a/doc/imv.1 b/doc/imv.1 index cbd5c68..d883611 100644 --- a/doc/imv.1 +++ b/doc/imv.1 @@ -21,12 +21,8 @@ It supports a wide variety of image file formats, including animated gif files. .Nm reloads the current image if it detects changes to the file. .Pp -When run with argument -.Sq - , .Nm -reads the list of images from standard input. -.Pp -The options are as follows: +accepts following flags: .Bl -tag -width Ds .It Fl a Default to showing images at actual size. @@ -66,6 +62,17 @@ Setting this to zero disables slideshow mode, which is the default. .It Fl u Use nearest neighbour resampling. Recommended for pixel art. .El +.Ss Reading from standard input +When run with argument +.Sq - , +.Nm +reads image from standard input. +Argument +.Sq - +may occur among other arguments, but only once. +.Pp +If no arguments supplied, reads list of files from standard input. +.Pp .Sh CONTROLS .Bl -tag -width Ds .It Aq Cm left mouse button @@ -111,6 +118,13 @@ Increase slideshow delay by one second .It Cm T Decrease slideshow delay by one second. When delay is zero, slideshow mode is disabled. +.Sh EXAMPLES +Load all files from directory +.Pa dir : +.Pp +.Dl $ ls dir | imv +or +.Dl $ ls dir | xargs imv .Sh LEGAL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software diff --git a/src/loader.c b/src/loader.c index 71c0e84..1250289 100644 --- a/src/loader.c +++ b/src/loader.c @@ -17,6 +17,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "loader.h" #include "texture.h" +#include #include #include #include @@ -67,7 +68,8 @@ void imv_destroy_loader(struct imv_loader *ldr) } } -void imv_loader_load_path(struct imv_loader *ldr, const char *path) +void imv_loader_load(struct imv_loader *ldr, const char *path, + const void *buffer, const size_t buffer_size) { /* cancel existing thread if already running */ if(ldr->bg_thread) { @@ -80,6 +82,12 @@ void imv_loader_load_path(struct imv_loader *ldr, const char *path) free(ldr->path); } ldr->path = strdup(path); + if (strncmp(path, "-", 2) == 0) { + ldr->buffer = (BYTE *)buffer; + ldr->buffer_size = buffer_size; + } else if (ldr->fi_buffer != NULL) { + FreeImage_CloseMemory(ldr->fi_buffer); + } pthread_create(&ldr->bg_thread, NULL, &bg_new_img, ldr); pthread_mutex_unlock(&ldr->lock); } @@ -149,16 +157,31 @@ static void *bg_new_img(void *data) block_usr1_signal(); struct imv_loader *ldr = data; + char path[PATH_MAX] = "-"; pthread_mutex_lock(&ldr->lock); - char *path = strdup(ldr->path); + int from_stdin = !strncmp(path, ldr->path, 2); + if(!from_stdin) { + (void)snprintf(path, PATH_MAX, "%s", ldr->path); + } pthread_mutex_unlock(&ldr->lock); - FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(path, 0); - + FREE_IMAGE_FORMAT fmt; + if (from_stdin) { + pthread_mutex_lock(&ldr->lock); + ldr->fi_buffer = FreeImage_OpenMemory(ldr->buffer, ldr->buffer_size); + fmt = FreeImage_GetFileTypeFromMemory(ldr->fi_buffer, 0); + pthread_mutex_unlock(&ldr->lock); + } else { + fmt = FreeImage_GetFileType(path, 0); + } if(fmt == FIF_UNKNOWN) { + if (from_stdin) { + pthread_mutex_lock(&ldr->lock); + FreeImage_CloseMemory(ldr->fi_buffer); + pthread_mutex_unlock(&ldr->lock); + } error_occurred(ldr); - free(path); return NULL; } @@ -169,12 +192,18 @@ static void *bg_new_img(void *data) int raw_frame_time = 100; /* default to 100 */ if(fmt == FIF_GIF) { - mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path, + if(from_stdin) { + pthread_mutex_lock(&ldr->lock); + mbmp = FreeImage_LoadMultiBitmapFromMemory(FIF_GIF, ldr->fi_buffer, + GIF_LOAD256); + pthread_mutex_unlock(&ldr->lock); + } else { + mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path, /* don't create file */ 0, /* read only */ 1, /* keep in memory */ 1, /* flags */ GIF_LOAD256); - free(path); + } if(!mbmp) { error_occurred(ldr); return NULL; @@ -198,10 +227,20 @@ static void *bg_new_img(void *data) } else { /* Future TODO: If we load image line-by-line we could stop loading large * ones before wasting much more time/memory on them. */ - FIBITMAP *image = FreeImage_Load(fmt, path, 0); - free(path); + FIBITMAP *image; + if(from_stdin) { + pthread_mutex_lock(&ldr->lock); + image = FreeImage_LoadFromMemory(fmt, ldr->fi_buffer, 0); + pthread_mutex_unlock(&ldr->lock); + } else { + image = FreeImage_Load(fmt, path, 0); + } if(!image) { error_occurred(ldr); + pthread_mutex_lock(&ldr->lock); + FreeImage_CloseMemory(ldr->fi_buffer); + ldr->fi_buffer = NULL; + pthread_mutex_unlock(&ldr->lock); return NULL; } diff --git a/src/loader.h b/src/loader.h index 65c8689..6e20349 100644 --- a/src/loader.h +++ b/src/loader.h @@ -28,6 +28,9 @@ struct imv_loader { pthread_mutex_t lock; pthread_t bg_thread; char *path; + BYTE *buffer; + size_t buffer_size; + FIMEMORY *fi_buffer; FIBITMAP *out_bmp; int out_is_new_image; char *out_err; @@ -48,7 +51,8 @@ void imv_init_loader(struct imv_loader *img); void imv_destroy_loader(struct imv_loader *img); /* Asynchronously load the given file */ -void imv_loader_load_path(struct imv_loader *ldr, const char *path); +void imv_loader_load(struct imv_loader *ldr, const char *path, + const void *buffer, const size_t buffer_size); /* Returns 1 if image data is available. 0 if not. Caller is responsible for * cleaning up the data returned. Each image is only returned once. diff --git a/src/main.c b/src/main.c index f3c197b..7ba55ec 100644 --- a/src/main.c +++ b/src/main.c @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include #include +#include #include "loader.h" #include "texture.h" @@ -33,7 +34,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. struct { int fullscreen; - int sin; + int stdin_list; int recursive; int actual; int nearest_neighbour; @@ -49,7 +50,7 @@ struct { const char *font; } g_options = { .fullscreen = 0, - .sin = 0, + .stdin_list = 0, .recursive = 0, .actual = 0, .nearest_neighbour = 0, @@ -72,7 +73,7 @@ static void print_usage(const char* name) "Usage: %s [-rfaudlh] [-n ] [-b BG] [-e FONT:SIZE] [-t SECONDS] [-] [images...]\n" "\n" "Flags:\n" - " -: Read paths from stdin. One path per line.\n" + " -: Read image from stdin. One path per line.\n" " -r: Recursively search input paths.\n" " -f: Start in fullscreen mode\n" " -a: Default to images' actual size\n" @@ -137,8 +138,8 @@ static void parse_args(int argc, char** argv) switch(o) { case 'f': g_options.fullscreen = 1; break; case 'i': - g_options.sin = 1; - fprintf(stderr, "Warning: '-i' is deprecated. Just use '-' instead.\n"); + g_options.stdin_list = 1; + fprintf(stderr, "Warning: '-i' is deprecated. No flag is needed.\n"); break; case 'r': g_options.recursive = 1; break; case 'a': g_options.actual = 1; break; @@ -193,30 +194,22 @@ static void parse_args(int argc, char** argv) int main(int argc, char** argv) { - if(argc < 2) { - print_usage(argv[0]); - exit(1); - } - struct imv_navigator nav; imv_navigator_init(&nav); /* parse any command line options given */ parse_args(argc, argv); - /* handle any image paths given as arguments */ - for(int i = optind; i < argc; ++i) { - /* special case: '-' is actually an option */ - if(!strcmp("-",argv[i])) { - g_options.sin = 1; - continue; - } - /* add the given path to the list to load */ - imv_navigator_add(&nav, argv[i], g_options.recursive); + argc -= optind; + argv += optind; + + /* if no names are given, expect them on stdin */ + if(argc == 0) { + g_options.stdin_list = 1; } /* if the user asked us to load paths from stdin, now is the time */ - if(g_options.sin) { + if(g_options.stdin_list) { char buf[PATH_MAX]; while(fgets(buf, sizeof(buf), stdin)) { size_t len = strlen(buf); @@ -229,6 +222,30 @@ int main(int argc, char** argv) } } + void *stdin_buffer = NULL; + size_t stdin_buffer_size = 0; + int stdin_error = 0; + + /* handle any image paths given as arguments */ + for(int i = 0; i < argc; ++i) { + /* special case: '-' is actually an option */ + if(!strcmp("-",argv[i])) { + if (stdin_buffer) { + fprintf(stderr, "Can't read from stdin twice\n"); + exit(1); + } + stdin_buffer_size = read_from_stdin(&stdin_buffer); + if (stdin_buffer_size == 0) { + perror(NULL); + continue; /* we can't recover from the freed buffer, just ignore it */ + } + stdin_error = errno; + errno = 0; /* clear errno */ + } + /* add the given path to the list to load */ + imv_navigator_add(&nav, argv[i], g_options.recursive); + } + /* if we weren't given any paths we have nothing to view. exit */ if(!imv_navigator_selection(&nav)) { fprintf(stderr, "No input files. Exiting.\n"); @@ -435,6 +452,15 @@ int main(int argc, char** argv) char *err_path = imv_loader_get_error(&ldr); if(err_path) { imv_navigator_remove(&nav, err_path); + if (strncmp(err_path, "-", 2) == 0) { + free(stdin_buffer); + stdin_buffer_size = 0; + if (stdin_error != 0) { + errno = stdin_error; + perror("Failed to load image from standard input"); + errno = 0; + } + } free(err_path); } @@ -451,7 +477,7 @@ int main(int argc, char** argv) nav.cur_path + 1, nav.num_paths, current_path); imv_viewport_set_title(&view, title); - imv_loader_load_path(&ldr, current_path); + imv_loader_load(&ldr, current_path, stdin_buffer, stdin_buffer_size); view.playing = 1; } diff --git a/src/util.c b/src/util.c index dac23d4..0a15238 100644 --- a/src/util.c +++ b/src/util.c @@ -16,9 +16,36 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "util.h" - +#include +#include +#include #include +size_t read_from_stdin(void **buffer) { + size_t len = 0; + ssize_t r; + size_t step = 1024; /* Arbitrary value of 1 KiB */ + void *p; + + errno = 0; /* clear errno */ + + for(*buffer = NULL; (*buffer = realloc((p = *buffer), len + step)); + len += (size_t)r) { + if((r = read(STDIN_FILENO, (uint8_t *)*buffer + len, step)) <= 0) { + break; + } + } + + /* realloc(3) leaves old buffer allocated in case of error */ + if(*buffer == NULL && p != NULL) { + int save = errno; + free(p); + errno = save; + len = 0; + } + return len; +} + TTF_Font *load_font(const char *font_spec) { int font_size; diff --git a/src/util.h b/src/util.h index 3625a6c..09d5554 100644 --- a/src/util.h +++ b/src/util.h @@ -21,6 +21,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include +/* Read binary data from stdin into buffer */ +size_t read_from_stdin(void **buffer); + /* Creates a new SDL_Texture* containing a chequeboard texture */ SDL_Texture *create_chequered(SDL_Renderer *renderer); -- cgit v1.2.3