"Fossies" - the Fresh Open Source Software Archive

Member "feh-3.4.1/src/imlib.c" (29 May 2020, 43616 Bytes) of package /linux/privat/feh-3.4.1.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "imlib.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.4_vs_3.4.1.

    1 /* imlib.c
    2 
    3 Copyright (C) 1999-2003 Tom Gilbert.
    4 Copyright (C) 2010-2020 Daniel Friesel.
    5 
    6 Permission is hereby granted, free of charge, to any person obtaining a copy
    7 of this software and associated documentation files (the "Software"), to
    8 deal in the Software without restriction, including without limitation the
    9 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   10 sell copies of the Software, and to permit persons to whom the Software is
   11 furnished to do so, subject to the following conditions:
   12 
   13 The above copyright notice and this permission notice shall be included in
   14 all copies of the Software and its documentation and acknowledgment shall be
   15 given in the documentation and software packages that this Software was
   16 used.
   17 
   18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   21 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   22 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   24 
   25 */
   26 
   27 #include "feh.h"
   28 #include "filelist.h"
   29 #include "signals.h"
   30 #include "winwidget.h"
   31 #include "options.h"
   32 
   33 #include <sys/types.h>
   34 #include <sys/socket.h>
   35 #include <netinet/in.h>
   36 #include <arpa/inet.h>
   37 #include <netdb.h>
   38 
   39 #ifdef HAVE_LIBCURL
   40 #include <curl/curl.h>
   41 #endif
   42 
   43 #ifdef HAVE_LIBEXIF
   44 #include "exif.h"
   45 #endif
   46 
   47 Display *disp = NULL;
   48 Visual *vis = NULL;
   49 Screen *scr = NULL;
   50 Colormap cm;
   51 int depth;
   52 Atom wmDeleteWindow;
   53 XContext xid_context = 0;
   54 Window root = 0;
   55 
   56 /* Xinerama support */
   57 #ifdef HAVE_LIBXINERAMA
   58 XineramaScreenInfo *xinerama_screens = NULL;
   59 int xinerama_screen;
   60 int num_xinerama_screens;
   61 #endif              /* HAVE_LIBXINERAMA */
   62 
   63 gib_hash* conversion_cache = NULL;
   64 
   65 int childpid = 0;
   66 
   67 static int feh_file_is_raw(char *filename);
   68 static char *feh_http_load_image(char *url);
   69 static char *feh_dcraw_load_image(char *filename);
   70 static char *feh_magick_load_image(char *filename);
   71 
   72 #ifdef HAVE_LIBXINERAMA
   73 void init_xinerama(void)
   74 {
   75     if (opt.xinerama && XineramaIsActive(disp)) {
   76         int major, minor, px, py, i;
   77 
   78         /* discarded */
   79         Window dw;
   80         int di;
   81         unsigned int du;
   82 
   83         XineramaQueryVersion(disp, &major, &minor);
   84         xinerama_screens = XineramaQueryScreens(disp, &num_xinerama_screens);
   85 
   86         if (opt.xinerama_index >= 0)
   87             xinerama_screen = opt.xinerama_index;
   88         else {
   89             xinerama_screen = 0;
   90             XQueryPointer(disp, root, &dw, &dw, &px, &py, &di, &di, &du);
   91             for (i = 0; i < num_xinerama_screens; i++) {
   92                 if (XY_IN_RECT(px, py,
   93                             xinerama_screens[i].x_org,
   94                             xinerama_screens[i].y_org,
   95                             xinerama_screens[i].width,
   96                             xinerama_screens[i].height)) {
   97                     xinerama_screen = i;
   98                     break;
   99                 }
  100             }
  101         }
  102     }
  103 }
  104 #endif              /* HAVE_LIBXINERAMA */
  105 
  106 void init_imlib_fonts(void)
  107 {
  108     /* Set up the font stuff */
  109     imlib_add_path_to_font_path(".");
  110     imlib_add_path_to_font_path(PREFIX "/share/feh/fonts");
  111 
  112     return;
  113 }
  114 
  115 void init_x_and_imlib(void)
  116 {
  117     disp = XOpenDisplay(NULL);
  118     if (!disp)
  119         eprintf("Can't open X display. It *is* running, yeah?");
  120     vis = DefaultVisual(disp, DefaultScreen(disp));
  121     depth = DefaultDepth(disp, DefaultScreen(disp));
  122     cm = DefaultColormap(disp, DefaultScreen(disp));
  123     root = RootWindow(disp, DefaultScreen(disp));
  124     scr = ScreenOfDisplay(disp, DefaultScreen(disp));
  125     xid_context = XUniqueContext();
  126 
  127 #ifdef HAVE_LIBXINERAMA
  128     init_xinerama();
  129 #endif              /* HAVE_LIBXINERAMA */
  130 
  131     imlib_context_set_display(disp);
  132     imlib_context_set_visual(vis);
  133     imlib_context_set_colormap(cm);
  134     imlib_context_set_color_modifier(NULL);
  135     imlib_context_set_progress_function(NULL);
  136     imlib_context_set_operation(IMLIB_OP_COPY);
  137     wmDeleteWindow = XInternAtom(disp, "WM_DELETE_WINDOW", False);
  138 
  139     imlib_set_cache_size(opt.cache_size * 1024 * 1024);
  140 
  141     return;
  142 }
  143 
  144 int feh_should_ignore_image(Imlib_Image * im)
  145 {
  146     if (opt.filter_by_dimensions) {
  147         unsigned int w = gib_imlib_image_get_width(im);
  148         unsigned int h = gib_imlib_image_get_height(im);
  149         if (w < opt.min_width || w > opt.max_width || h < opt.min_height || h > opt.max_height) {
  150             return 1;
  151         }
  152     }
  153     return 0;
  154 }
  155 
  156 int feh_load_image_char(Imlib_Image * im, char *filename)
  157 {
  158     feh_file *file;
  159     int i;
  160 
  161     file = feh_file_new(filename);
  162     i = feh_load_image(im, file);
  163     feh_file_free(file);
  164     return(i);
  165 }
  166 
  167 void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)
  168 {
  169     if (err == IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS)
  170         eprintf("%s - Out of file descriptors while loading", file);
  171     else if (!opt.quiet || w) {
  172         switch (err) {
  173         case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
  174             im_weprintf(w, "%s - File does not exist", file);
  175             break;
  176         case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY:
  177             im_weprintf(w, "%s - Directory specified for image filename", file);
  178             break;
  179         case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ:
  180             im_weprintf(w, "%s - No read access", file);
  181             break;
  182         case IMLIB_LOAD_ERROR_UNKNOWN:
  183         case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
  184             im_weprintf(w, "%s - No Imlib2 loader for that file format", file);
  185             break;
  186         case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
  187             im_weprintf(w, "%s - Path specified is too long", file);
  188             break;
  189         case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
  190             im_weprintf(w, "%s - Path component does not exist", file);
  191             break;
  192         case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
  193             im_weprintf(w, "%s - Path component is not a directory", file);
  194             break;
  195         case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
  196             im_weprintf(w, "%s - Path points outside address space", file);
  197             break;
  198         case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
  199             im_weprintf(w, "%s - Too many levels of symbolic links", file);
  200             break;
  201         case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
  202             im_weprintf(w, "While loading %s - Out of memory", file);
  203             break;
  204         case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
  205             im_weprintf(w, "%s - Cannot write to directory", file);
  206             break;
  207         case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE:
  208             im_weprintf(w, "%s - Cannot write - out of disk space", file);
  209             break;
  210         default:
  211             im_weprintf(w, "While loading %s - Unknown error (%d)",
  212                     file, err);
  213             break;
  214         }
  215     }
  216 }
  217 
  218 int feh_load_image(Imlib_Image * im, feh_file * file)
  219 {
  220     Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE;
  221     enum { SRC_IMLIB, SRC_HTTP, SRC_MAGICK, SRC_DCRAW } image_source = SRC_IMLIB;
  222     char *tmpname = NULL;
  223     char *real_filename = NULL;
  224 
  225     D(("filename is %s, image is %p\n", file->filename, im));
  226 
  227     if (!file || !file->filename)
  228         return 0;
  229 
  230     if (path_is_url(file->filename)) {
  231         image_source = SRC_HTTP;
  232 
  233         if ((tmpname = feh_http_load_image(file->filename)) == NULL)
  234             err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST;
  235     }
  236     else if (opt.conversion_timeout >= 0 && feh_file_is_raw(file->filename)) {
  237         image_source = SRC_DCRAW;
  238         tmpname = feh_dcraw_load_image(file->filename);
  239         if (!tmpname)
  240             err = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT;
  241     }
  242     else
  243         *im = imlib_load_image_with_error_return(file->filename, &err);
  244 
  245     if (opt.conversion_timeout >= 0 && (
  246             (err == IMLIB_LOAD_ERROR_UNKNOWN) ||
  247             (err == IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT))) {
  248         image_source = SRC_MAGICK;
  249         tmpname = feh_magick_load_image(file->filename);
  250     }
  251 
  252     if (tmpname) {
  253         *im = imlib_load_image_with_error_return(tmpname, &err);
  254         if (!err && im) {
  255             real_filename = file->filename;
  256             file->filename = tmpname;
  257 
  258             /*
  259              * feh does not associate a non-native image with its temporary
  260              * filename and may delete the temporary file right after loading.
  261              * To ensure that it is still aware of image size, dimensions, etc.,
  262              * file_info is preloaded here. To avoid a memory leak when loading
  263              * a non-native file multiple times in a slideshow, the file_info
  264              * struct is freed first. If file->info is not set, feh_file_info_free
  265              * is a no-op.
  266              */
  267             feh_file_info_free(file->info);
  268             feh_file_info_load(file, *im);
  269 
  270             file->filename = real_filename;
  271 #ifdef HAVE_LIBEXIF
  272             file->ed = exif_get_data(tmpname);
  273 #endif
  274         }
  275         if (!opt.use_conversion_cache && ((image_source != SRC_HTTP) || !opt.keep_http))
  276             unlink(tmpname);
  277         // keep_http already performs an add_file_to_rm_filelist call
  278         else if (opt.use_conversion_cache && !opt.keep_http)
  279             // add_file_to_rm_filelist duplicates tmpname
  280             add_file_to_rm_filelist(tmpname);
  281 
  282         if (image_source != SRC_HTTP && !opt.use_conversion_cache)
  283             free(tmpname);
  284     }
  285 
  286     if ((err) || (!im)) {
  287         if (opt.verbose && !opt.quiet) {
  288             fputs("\n", stderr);
  289             reset_output = 1;
  290         }
  291         feh_imlib_print_load_error(file->filename, NULL, err);
  292         D(("Load *failed*\n"));
  293         return(0);
  294     }
  295 
  296     /*
  297      * By default, Imlib2 unconditionally loads a cached file without checking
  298      * if it was modified on disk. However, feh (or rather its users) should
  299      * expect image changes to appear at the next reload. So we tell Imlib2 to
  300      * always check the file modification time and only use a cached image if
  301      * the mtime was not changed. The performance penalty is usually negligible.
  302      */
  303     imlib_context_set_image(*im);
  304     imlib_image_set_changes_on_disk();
  305 
  306 #ifdef HAVE_LIBEXIF
  307     int orientation = 0;
  308     ExifData *exifData = exif_data_new_from_file(file->filename);
  309     if (exifData) {
  310         ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
  311         ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
  312         if (exifEntry && opt.auto_rotate)
  313             orientation = exif_get_short(exifEntry->data, byteOrder);
  314     }
  315     file->ed = exifData;
  316 
  317     if (orientation == 2)
  318         gib_imlib_image_flip_horizontal(*im);
  319     else if (orientation == 3)
  320         gib_imlib_image_orientate(*im, 2);
  321     else if (orientation == 4)
  322         gib_imlib_image_flip_vertical(*im);
  323     else if (orientation == 5) {
  324         gib_imlib_image_orientate(*im, 3);
  325         gib_imlib_image_flip_vertical(*im);
  326     }
  327     else if (orientation == 6)
  328         gib_imlib_image_orientate(*im, 1);
  329     else if (orientation == 7) {
  330         gib_imlib_image_orientate(*im, 3);
  331         gib_imlib_image_flip_horizontal(*im);
  332     }
  333     else if (orientation == 8)
  334         gib_imlib_image_orientate(*im, 3);
  335 #endif
  336 
  337     D(("Loaded ok\n"));
  338     return(1);
  339 }
  340 
  341 void feh_reload_image(winwidget w, int resize, int force_new)
  342 {
  343     char *new_title;
  344     int len;
  345     Imlib_Image tmp;
  346     int old_w, old_h;
  347 
  348     if (!w->file) {
  349         im_weprintf(w, "couldn't reload, this image has no file associated with it.");
  350         winwidget_render_image(w, 0, 0);
  351         return;
  352     }
  353 
  354     D(("resize %d, force_new %d\n", resize, force_new));
  355 
  356     free(FEH_FILE(w->file->data)->caption);
  357     FEH_FILE(w->file->data)->caption = NULL;
  358 
  359     len = strlen(w->name) + sizeof("Reloading: ") + 1;
  360     new_title = emalloc(len);
  361     snprintf(new_title, len, "Reloading: %s", w->name);
  362     winwidget_rename(w, new_title);
  363     free(new_title);
  364 
  365     old_w = gib_imlib_image_get_width(w->im);
  366     old_h = gib_imlib_image_get_height(w->im);
  367 
  368     /*
  369      * If we don't free the old image before loading the new one, Imlib2's
  370      * caching will get in our way.
  371      * However, if --reload is used (force_new == 0), we want to continue if
  372      * the new image cannot be loaded, so we must not free the old image yet.
  373      */
  374     if (force_new)
  375         winwidget_free_image(w);
  376 
  377     // if it's an external image, our own cache will also get in your way
  378     char *sfn;
  379     if (opt.use_conversion_cache && conversion_cache && (sfn = gib_hash_get(conversion_cache, FEH_FILE(w->file->data)->filename)) != NULL) {
  380         free(sfn);
  381         gib_hash_set(conversion_cache, FEH_FILE(w->file->data)->filename, NULL);
  382     }
  383 
  384     if ((feh_load_image(&tmp, FEH_FILE(w->file->data))) == 0) {
  385         if (force_new)
  386             eprintf("failed to reload image\n");
  387         else {
  388             im_weprintf(w, "Couldn't reload image. Is it still there?");
  389             winwidget_render_image(w, 0, 0);
  390         }
  391         return;
  392     }
  393 
  394     if (!resize && ((old_w != gib_imlib_image_get_width(tmp)) ||
  395             (old_h != gib_imlib_image_get_height(tmp))))
  396         resize = 1;
  397 
  398     if (!force_new)
  399         winwidget_free_image(w);
  400 
  401     w->im = tmp;
  402     winwidget_reset_image(w);
  403 
  404     w->mode = MODE_NORMAL;
  405     if ((w->im_w != gib_imlib_image_get_width(w->im))
  406         || (w->im_h != gib_imlib_image_get_height(w->im)))
  407         w->had_resize = 1;
  408     if (w->has_rotated) {
  409         Imlib_Image temp;
  410 
  411         temp = gib_imlib_create_rotated_image(w->im, 0.0);
  412         w->im_w = gib_imlib_image_get_width(temp);
  413         w->im_h = gib_imlib_image_get_height(temp);
  414         gib_imlib_free_image_and_decache(temp);
  415     } else {
  416         w->im_w = gib_imlib_image_get_width(w->im);
  417         w->im_h = gib_imlib_image_get_height(w->im);
  418     }
  419     winwidget_render_image(w, resize, 0);
  420 
  421     return;
  422 }
  423 
  424 static int feh_file_is_raw(char *filename)
  425 {
  426     childpid = fork();
  427     if (childpid == -1) {
  428         perror("fork");
  429         return 0;
  430     }
  431 
  432     if (childpid == 0) {
  433         if (opt.quiet) {
  434             int devnull = open("/dev/null", O_WRONLY);
  435             dup2(devnull, 1);
  436             dup2(devnull, 2);
  437         }
  438         execlp("dcraw", "dcraw", "-i", filename, NULL);
  439         _exit(1);
  440     } else {
  441         int status;
  442         do {
  443             waitpid(childpid, &status, WUNTRACED);
  444             if (WIFEXITED(status)) {
  445                 return !WEXITSTATUS(status);
  446             }
  447         } while (!WIFEXITED(status) && !WIFSIGNALED(status));
  448     }
  449 
  450     return 0;
  451 }
  452 
  453 static char *feh_dcraw_load_image(char *filename)
  454 {
  455     char *basename;
  456     char *tmpname;
  457     char *sfn;
  458     int fd = -1;
  459 
  460     if (opt.use_conversion_cache) {
  461         if (!conversion_cache)
  462             conversion_cache = gib_hash_new();
  463         if ((sfn = gib_hash_get(conversion_cache, filename)) != NULL)
  464             return sfn;
  465     }
  466 
  467     basename = strrchr(filename, '/');
  468 
  469     if (basename == NULL)
  470         basename = filename;
  471     else
  472         basename++;
  473 
  474     tmpname = feh_unique_filename("/tmp/", basename);
  475 
  476     if (strlen(tmpname) > (NAME_MAX-6))
  477         tmpname[NAME_MAX-7] = '\0';
  478 
  479     sfn = estrjoin("_", tmpname, "XXXXXX", NULL);
  480     free(tmpname);
  481 
  482     fd = mkstemp(sfn);
  483 
  484     if (fd == -1) {
  485         free(sfn);
  486         return NULL;
  487     }
  488 
  489     childpid = fork();
  490     if (childpid == -1) {
  491         weprintf("%s: Can't load with dcraw. Fork failed:", filename);
  492         unlink(sfn);
  493         free(sfn);
  494         close(fd);
  495         return NULL;
  496     } else if (childpid == 0) {
  497 
  498         close(1);
  499         dup(fd);
  500         close(fd);
  501 
  502         alarm(opt.conversion_timeout);
  503         execlp("dcraw", "dcraw", "-c", "-e", filename, NULL);
  504         _exit(1);
  505     }
  506 
  507     int status;
  508     waitpid(-1, &status, 0);
  509     if (WIFSIGNALED(status)) {
  510         unlink(sfn);
  511         free(sfn);
  512         sfn = NULL;
  513         if (!opt.quiet)
  514             weprintf("%s - Conversion took too long, skipping", filename);
  515     }
  516 
  517     if ((sfn != NULL) && opt.use_conversion_cache)
  518         gib_hash_set(conversion_cache, filename, sfn);
  519 
  520     return sfn;
  521 }
  522 
  523 static char *feh_magick_load_image(char *filename)
  524 {
  525     char *argv_fn;
  526     char *basename;
  527     char *tmpname;
  528     char *sfn;
  529     char tempdir[] = "/tmp/.feh-magick-tmp-XXXXXX";
  530     int fd = -1, devnull = -1;
  531     int status;
  532     char created_tempdir = 0;
  533 
  534     if (opt.use_conversion_cache) {
  535         if (!conversion_cache)
  536             conversion_cache = gib_hash_new();
  537         if ((sfn = gib_hash_get(conversion_cache, filename)) != NULL)
  538             return sfn;
  539     }
  540 
  541     basename = strrchr(filename, '/');
  542 
  543     if (basename == NULL)
  544         basename = filename;
  545     else
  546         basename++;
  547 
  548     tmpname = feh_unique_filename("/tmp/", basename);
  549 
  550     if (strlen(tmpname) > (NAME_MAX-6))
  551         tmpname[NAME_MAX-7] = '\0';
  552 
  553     sfn = estrjoin("_", tmpname, "XXXXXX", NULL);
  554     free(tmpname);
  555 
  556     fd = mkstemp(sfn);
  557 
  558     if (fd == -1) {
  559         free(sfn);
  560         return NULL;
  561     }
  562 
  563     /*
  564      * We could use png:fd:(whatever mkstemp returned) as target filename
  565      * for convert, but this seems to be broken in some ImageMagick versions.
  566      * So we resort to png:(sfn) instead.
  567      */
  568     argv_fn = estrjoin(":", "png", sfn, NULL);
  569 
  570     /*
  571      * By default, ImageMagick saves (occasionally lots of) temporary files
  572      * in /tmp. It doesn't remove them if it runs into a timeout and is killed
  573      * by us, no matter whether we use SIGINT, SIGTERM or SIGKILL. So, unless
  574      * MAGICK_TMPDIR has already been set by the user, we create our own
  575      * temporary directory for ImageMagick and remove its contents at the end of
  576      * this function.
  577      */
  578     if (getenv("MAGICK_TMPDIR") == NULL) {
  579         if (mkdtemp(tempdir) == NULL) {
  580             weprintf("%s: ImageMagick may leave temporary files in /tmp. mkdtemp failed:", filename);
  581         } else {
  582             created_tempdir = 1;
  583         }
  584     }
  585 
  586     if ((childpid = fork()) < 0) {
  587         weprintf("%s: Can't load with imagemagick. Fork failed:", filename);
  588         unlink(sfn);
  589         free(sfn);
  590         sfn = NULL;
  591     }
  592     else if (childpid == 0) {
  593 
  594         devnull = open("/dev/null", O_WRONLY);
  595         dup2(devnull, 0);
  596         if (opt.quiet) {
  597             /* discard convert output */
  598             dup2(devnull, 1);
  599             dup2(devnull, 2);
  600         }
  601 
  602         /*
  603          * convert only accepts SIGINT via killpg, a normal kill doesn't work
  604          */
  605         setpgid(0, 0);
  606 
  607         if (created_tempdir) {
  608             // no error checking - this is a best-effort code path
  609             setenv("MAGICK_TMPDIR", tempdir, 0);
  610         }
  611 
  612         execlp("convert", "convert", filename, argv_fn, NULL);
  613         _exit(1);
  614     }
  615     else {
  616         alarm(opt.conversion_timeout);
  617         waitpid(childpid, &status, 0);
  618         kill(childpid, SIGKILL);
  619         if (opt.conversion_timeout > 0 && !alarm(0)) {
  620             unlink(sfn);
  621             free(sfn);
  622             sfn = NULL;
  623 
  624             if (!opt.quiet) {
  625                 weprintf("%s: Conversion took too long, skipping", filename);
  626             }
  627         }
  628         close(fd);
  629         childpid = 0;
  630     }
  631 
  632     if (created_tempdir) {
  633         DIR *dir;
  634         struct dirent *de;
  635         if ((dir = opendir(tempdir)) == NULL) {
  636             weprintf("%s: Cannot remove temporary ImageMagick files from %s:", filename, tempdir);
  637         } else {
  638             while ((de = readdir(dir)) != NULL) {
  639                 if (de->d_name[0] != '.') {
  640                     char *temporary_file_name = estrjoin("/", tempdir, de->d_name, NULL);
  641                     /*
  642                      * We assume that ImageMagick only creates temporary files and
  643                      * not directories.
  644                      */
  645                     if (unlink(temporary_file_name) == -1) {
  646                         weprintf("unlink %s:", temporary_file_name);
  647                     }
  648                     free(temporary_file_name);
  649                 }
  650             }
  651             if (rmdir(tempdir) == -1) {
  652                 weprintf("rmdir %s:", tempdir);
  653             }
  654         }
  655         closedir(dir);
  656     }
  657 
  658     free(argv_fn);
  659 
  660     if ((sfn != NULL) && opt.use_conversion_cache)
  661         gib_hash_set(conversion_cache, filename, sfn);
  662 
  663     return sfn;
  664 }
  665 
  666 #ifdef HAVE_LIBCURL
  667 
  668 #if LIBCURL_VERSION_NUM >= 0x072000 /* 07.32.0 */
  669 static int curl_quit_function(void *clientp,  curl_off_t dltotal,  curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
  670 #else
  671 static int curl_quit_function(void *clientp,  double dltotal,  double dlnow, double ultotal, double ulnow)
  672 #endif
  673 {
  674     // ignore "unused parameter" warnings
  675     (void)clientp;
  676     (void)dltotal;
  677     (void)dlnow;
  678     (void)ultotal;
  679     (void)ulnow;
  680     if (sig_exit) {
  681         /*
  682          * The user wants to quit feh. Tell libcurl to abort the transfer and
  683          * return control to the main loop, where we can quit gracefully.
  684          */
  685         return 1;
  686     }
  687     return 0;
  688 }
  689 
  690 static char *feh_http_load_image(char *url)
  691 {
  692     CURL *curl;
  693     CURLcode res;
  694     char *sfn;
  695     FILE *sfp;
  696     int fd = -1;
  697     char *ebuff;
  698     char *tmpname;
  699     char *basename;
  700     char *path = NULL;
  701 
  702     if (opt.use_conversion_cache) {
  703         if (!conversion_cache)
  704             conversion_cache = gib_hash_new();
  705         if ((sfn = gib_hash_get(conversion_cache, url)) != NULL)
  706             return sfn;
  707     }
  708 
  709     if (opt.keep_http) {
  710         if (opt.output_dir)
  711             path = opt.output_dir;
  712         else
  713             path = "";
  714     } else
  715         path = "/tmp/";
  716 
  717     curl = curl_easy_init();
  718     if (!curl) {
  719         weprintf("open url: libcurl initialization failure");
  720         return NULL;
  721     }
  722 
  723     basename = strrchr(url, '/') + 1;
  724     tmpname = feh_unique_filename(path, basename);
  725 
  726     if (strlen(tmpname) > (NAME_MAX-6))
  727         tmpname[NAME_MAX-7] = '\0';
  728 
  729     sfn = estrjoin("_", tmpname, "XXXXXX", NULL);
  730     free(tmpname);
  731 
  732     fd = mkstemp(sfn);
  733     if (fd != -1) {
  734         sfp = fdopen(fd, "w+");
  735         if (sfp != NULL) {
  736 #ifdef DEBUG
  737             curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  738 #endif
  739             /*
  740              * Do not allow requests to take longer than 30 minutes.
  741              * This should be sufficiently high to accomodate use cases with
  742              * unusually high latencies, while at the sime time avoiding
  743              * feh hanging indefinitely in unattended slideshows.
  744              */
  745             curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1800);
  746             curl_easy_setopt(curl, CURLOPT_URL, url);
  747             curl_easy_setopt(curl, CURLOPT_WRITEDATA, sfp);
  748             ebuff = emalloc(CURL_ERROR_SIZE);
  749             curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ebuff);
  750             curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
  751             curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
  752 #if LIBCURL_VERSION_NUM >= 0x072000 /* 07.32.0 */
  753             curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, curl_quit_function);
  754 #else
  755             curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_quit_function);
  756 #endif
  757             curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
  758             if (opt.insecure_ssl) {
  759                 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
  760                 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
  761             } else if (getenv("CURL_CA_BUNDLE") != NULL) {
  762                 // Allow the user to specify custom CA certificates.
  763                 curl_easy_setopt(curl, CURLOPT_CAINFO,
  764                         getenv("CURL_CA_BUNDLE"));
  765             }
  766 
  767             res = curl_easy_perform(curl);
  768             curl_easy_cleanup(curl);
  769             if (res != CURLE_OK) {
  770                 if (res != CURLE_ABORTED_BY_CALLBACK) {
  771                     weprintf("open url: %s", ebuff);
  772                 }
  773                 unlink(sfn);
  774                 close(fd);
  775                 free(sfn);
  776                 sfn = NULL;
  777             }
  778 
  779             free(ebuff);
  780             fclose(sfp);
  781             if (opt.use_conversion_cache)
  782                 gib_hash_set(conversion_cache, url, sfn);
  783             return sfn;
  784         } else {
  785             weprintf("open url: fdopen failed:");
  786             unlink(sfn);
  787             free(sfn);
  788             close(fd);
  789         }
  790     } else {
  791         weprintf("open url: mkstemp failed:");
  792         free(sfn);
  793     }
  794     curl_easy_cleanup(curl);
  795     return NULL;
  796 }
  797 
  798 #else               /* HAVE_LIBCURL */
  799 
  800 char *feh_http_load_image(char *url)
  801 {
  802     weprintf(
  803         "Cannot load image %s\nPlease recompile feh with libcurl support",
  804         url
  805     );
  806     return NULL;
  807 }
  808 
  809 #endif              /* HAVE_LIBCURL */
  810 
  811 void feh_imlib_image_fill_text_bg(Imlib_Image im, int w, int h)
  812 {
  813     gib_imlib_image_set_has_alpha(im, 1);
  814 
  815     imlib_context_set_blend(0);
  816 
  817     if (opt.text_bg == TEXT_BG_CLEAR)
  818         gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 0);
  819     else
  820         gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 127);
  821 
  822     imlib_context_set_blend(1);
  823 }
  824 
  825 static Imlib_Font feh_load_font(winwidget w)
  826 {
  827     static Imlib_Font fn = NULL;
  828 
  829     if (opt.font)
  830         fn = gib_imlib_load_font(opt.font);
  831 
  832     if (!fn) {
  833         if (w && w->full_screen)
  834             fn = gib_imlib_load_font(DEFAULT_FONT_BIG);
  835         else
  836             fn = gib_imlib_load_font(DEFAULT_FONT);
  837     }
  838 
  839     if (!fn) {
  840         eprintf("Couldn't load font to draw a message");
  841     }
  842 
  843     return fn;
  844 }
  845 
  846 
  847 void feh_draw_zoom(winwidget w)
  848 {
  849     static Imlib_Font fn = NULL;
  850     int tw = 0, th = 0;
  851     Imlib_Image im = NULL;
  852     char buf[100];
  853 
  854     if (!w->im)
  855         return;
  856 
  857     fn = feh_load_font(w);
  858 
  859     snprintf(buf, sizeof(buf), "%.0f%%, %dx%d", w->zoom * 100,
  860             (int) (w->im_w * w->zoom), (int) (w->im_h * w->zoom));
  861 
  862     /* Work out how high the font is */
  863     gib_imlib_get_text_size(fn, buf, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
  864 
  865     tw += 3;
  866     th += 3;
  867     im = imlib_create_image(tw, th);
  868     if (!im)
  869         eprintf("Couldn't create image. Out of memory?");
  870 
  871     feh_imlib_image_fill_text_bg(im, tw, th);
  872 
  873     gib_imlib_text_draw(im, fn, NULL, 2, 2, buf, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
  874     gib_imlib_text_draw(im, fn, NULL, 1, 1, buf, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
  875     gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - th, 1, 1, 0);
  876     gib_imlib_free_image_and_decache(im);
  877     return;
  878 }
  879 
  880 void im_weprintf(winwidget w, char *fmt, ...)
  881 {
  882     va_list args;
  883     char *errstr = emalloc(1024);
  884 
  885     fflush(stdout);
  886     fputs(PACKAGE " WARNING: ", stderr);
  887 
  888     va_start(args, fmt);
  889     vsnprintf(errstr, 1024, fmt, args);
  890     va_end(args);
  891 
  892     if (w)
  893         w->errstr = errstr;
  894 
  895     fputs(errstr, stderr);
  896     if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':')
  897         fprintf(stderr, " %s", strerror(errno));
  898     fputs("\n", stderr);
  899     if (!w)
  900         free(errstr);
  901 }
  902 
  903 
  904 void feh_draw_errstr(winwidget w)
  905 {
  906     static Imlib_Font fn = NULL;
  907     int tw = 0, th = 0;
  908     Imlib_Image im = NULL;
  909 
  910     if (!w->im)
  911         return;
  912 
  913     fn = feh_load_font(NULL);
  914 
  915     /* Work out how high the font is */
  916     gib_imlib_get_text_size(fn, w->errstr, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
  917 
  918     tw += 3;
  919     th += 3;
  920     im = imlib_create_image(tw, th);
  921     if (!im)
  922         eprintf("Couldn't create errstr image. Out of memory?");
  923 
  924     feh_imlib_image_fill_text_bg(im, tw, th);
  925 
  926     gib_imlib_text_draw(im, fn, NULL, 2, 2, w->errstr, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
  927     gib_imlib_text_draw(im, fn, NULL, 1, 1, w->errstr, IMLIB_TEXT_TO_RIGHT, 255, 0, 0, 255);
  928     free(w->errstr);
  929     w->errstr = NULL;
  930     gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - th, 1, 1, 0);
  931     gib_imlib_free_image_and_decache(im);
  932 }
  933 
  934 void feh_draw_filename(winwidget w)
  935 {
  936     static Imlib_Font fn = NULL;
  937     int tw = 0, th = 0, nw = 0;
  938     Imlib_Image im = NULL;
  939     char *s = NULL;
  940     int len = 0;
  941 
  942     if ((!w->file) || (!FEH_FILE(w->file->data))
  943             || (!FEH_FILE(w->file->data)->filename))
  944         return;
  945 
  946     fn = feh_load_font(w);
  947 
  948     /* Work out how high the font is */
  949     gib_imlib_get_text_size(fn, FEH_FILE(w->file->data)->filename, NULL, &tw,
  950             &th, IMLIB_TEXT_TO_RIGHT);
  951 
  952     if (gib_list_length(filelist) > 1) {
  953         len = snprintf(NULL, 0, "%d of %d",  gib_list_length(filelist),
  954                 gib_list_length(filelist)) + 1;
  955         s = emalloc(len);
  956         if (w->file)
  957             snprintf(s, len, "%d of %d", gib_list_num(filelist, w->file) +
  958                     1, gib_list_length(filelist));
  959         else
  960             snprintf(s, len, "%d of %d", gib_list_num(filelist, current_file) +
  961                     1, gib_list_length(filelist));
  962 
  963         gib_imlib_get_text_size(fn, s, NULL, &nw, NULL, IMLIB_TEXT_TO_RIGHT);
  964 
  965         if (nw > tw)
  966             tw = nw;
  967     }
  968 
  969     tw += 3;
  970     th += 3;
  971     im = imlib_create_image(tw, 2 * th);
  972     if (!im)
  973         eprintf("Couldn't create image. Out of memory?");
  974 
  975     feh_imlib_image_fill_text_bg(im, tw, 2 * th);
  976 
  977     gib_imlib_text_draw(im, fn, NULL, 2, 2, FEH_FILE(w->file->data)->filename,
  978             IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
  979     gib_imlib_text_draw(im, fn, NULL, 1, 1, FEH_FILE(w->file->data)->filename,
  980             IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
  981 
  982     if (s) {
  983         gib_imlib_text_draw(im, fn, NULL, 2, th + 1, s, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
  984         gib_imlib_text_draw(im, fn, NULL, 1, th, s, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
  985         free(s);
  986     }
  987 
  988     gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, 0, 1, 1, 0);
  989 
  990     gib_imlib_free_image_and_decache(im);
  991     return;
  992 }
  993 
  994 #ifdef HAVE_LIBEXIF
  995 void feh_draw_exif(winwidget w)
  996 {
  997     static Imlib_Font fn = NULL;
  998     int width = 0, height = 0, line_width = 0, line_height = 0;
  999     Imlib_Image im = NULL;
 1000     int no_lines = 0, i;
 1001     int pos = 0;
 1002     int pos2 = 0;
 1003     char info_line[256];
 1004     char *info_buf[128];
 1005     char buffer[EXIF_MAX_DATA];
 1006 
 1007     if ( (!w->file) || (!FEH_FILE(w->file->data))
 1008              || (!FEH_FILE(w->file->data)->filename) )
 1009     {
 1010         return;
 1011     }
 1012 
 1013 
 1014     buffer[0] = '\0';
 1015     exif_get_info(FEH_FILE(w->file->data)->ed, buffer, EXIF_MAX_DATA);
 1016 
 1017     fn = feh_load_font(w);
 1018 
 1019     if (buffer[0] == '\0')
 1020     {
 1021         snprintf(buffer, EXIF_MAX_DATA, "%s", estrdup("Failed to run exif command"));
 1022         gib_imlib_get_text_size(fn, buffer, NULL, &width, &height, IMLIB_TEXT_TO_RIGHT);
 1023         info_buf[no_lines] = estrdup(buffer);
 1024         no_lines++;
 1025     }
 1026     else
 1027     {
 1028 
 1029         while ( (no_lines < 128) && (pos < EXIF_MAX_DATA) )
 1030         {
 1031             /* max 128 lines */
 1032             pos2 = 0;
 1033             while ( pos2 < 256 ) /* max 256 chars per line */
 1034             {
 1035                 if ( (buffer[pos] != '\n')
 1036                       && (buffer[pos] != '\0') )
 1037                 {
 1038                 info_line[pos2] = buffer[pos];
 1039               }
 1040               else if ( buffer[pos] == '\0' )
 1041               {
 1042                 pos = EXIF_MAX_DATA; /* all data seen */
 1043                 info_line[pos2] = '\0';
 1044                 }
 1045               else
 1046               {
 1047                 info_line[pos2] = '\0'; /* line finished, continue with next line*/
 1048 
 1049                 pos++;
 1050                 break;
 1051               }
 1052 
 1053                pos++;
 1054                pos2++;
 1055             }
 1056 
 1057             gib_imlib_get_text_size(fn, info_line, NULL, &line_width,
 1058                               &line_height, IMLIB_TEXT_TO_RIGHT);
 1059 
 1060             if (line_height > height)
 1061                 height = line_height;
 1062             if (line_width > width)
 1063                 width = line_width;
 1064             info_buf[no_lines] = estrdup(info_line);
 1065 
 1066             no_lines++;
 1067         }
 1068     }
 1069 
 1070     if (no_lines == 0)
 1071         return;
 1072 
 1073     height *= no_lines;
 1074     width += 4;
 1075 
 1076     im = imlib_create_image(width, height);
 1077     if (!im)
 1078     {
 1079         eprintf("Couldn't create image. Out of memory?");
 1080     }
 1081 
 1082     feh_imlib_image_fill_text_bg(im, width, height);
 1083 
 1084     for (i = 0; i < no_lines; i++)
 1085     {
 1086         gib_imlib_text_draw(im, fn, NULL, 2, (i * line_height) + 2,
 1087                 info_buf[i], IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
 1088         gib_imlib_text_draw(im, fn, NULL, 1, (i * line_height) + 1,
 1089                 info_buf[i], IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
 1090 
 1091     }
 1092 
 1093     gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - height, 1, 1, 0);
 1094 
 1095     gib_imlib_free_image_and_decache(im);
 1096     return;
 1097 
 1098 }
 1099 #endif
 1100 
 1101 void feh_draw_info(winwidget w)
 1102 {
 1103     static Imlib_Font fn = NULL;
 1104     int width = 0, height = 0, line_width = 0, line_height = 0;
 1105     Imlib_Image im = NULL;
 1106     int no_lines = 0, i;
 1107     char *info_cmd;
 1108     char info_line[256];
 1109     char *info_buf[128];
 1110     FILE *info_pipe;
 1111 
 1112     if ((!w->file) || (!FEH_FILE(w->file->data))
 1113             || (!FEH_FILE(w->file->data)->filename))
 1114         return;
 1115 
 1116     fn = feh_load_font(w);
 1117 
 1118     info_cmd = feh_printf(opt.info_cmd, FEH_FILE(w->file->data), w);
 1119 
 1120     info_pipe = popen(info_cmd, "r");
 1121 
 1122     if (!info_pipe) {
 1123         info_buf[0] = estrdup("Failed to run info command");
 1124         gib_imlib_get_text_size(fn, info_buf[0], NULL, &width, &height, IMLIB_TEXT_TO_RIGHT);
 1125         no_lines = 1;
 1126     }
 1127     else {
 1128         while ((no_lines < 128) && fgets(info_line, 256, info_pipe)) {
 1129             if (info_line[strlen(info_line)-1] == '\n')
 1130                 info_line[strlen(info_line)-1] = '\0';
 1131 
 1132             gib_imlib_get_text_size(fn, info_line, NULL, &line_width,
 1133                     &line_height, IMLIB_TEXT_TO_RIGHT);
 1134 
 1135             if (line_height > height)
 1136                 height = line_height;
 1137             if (line_width > width)
 1138                 width = line_width;
 1139 
 1140             info_buf[no_lines] = estrdup(info_line);
 1141 
 1142             no_lines++;
 1143         }
 1144         pclose(info_pipe);
 1145     }
 1146 
 1147     if (no_lines == 0)
 1148         return;
 1149 
 1150     height *= no_lines;
 1151     width += 4;
 1152 
 1153     im = imlib_create_image(width, height);
 1154     if (!im)
 1155         eprintf("Couldn't create image. Out of memory?");
 1156 
 1157     feh_imlib_image_fill_text_bg(im, width, height);
 1158 
 1159     for (i = 0; i < no_lines; i++) {
 1160         gib_imlib_text_draw(im, fn, NULL, 2, (i * line_height) + 2,
 1161                 info_buf[i], IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
 1162         gib_imlib_text_draw(im, fn, NULL, 1, (i * line_height) + 1,
 1163                 info_buf[i], IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
 1164 
 1165         free(info_buf[i]);
 1166     }
 1167 
 1168     gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0,
 1169             w->h - height, 1, 1, 0);
 1170 
 1171     gib_imlib_free_image_and_decache(im);
 1172     return;
 1173 }
 1174 
 1175 char *build_caption_filename(feh_file * file, short create_dir)
 1176 {
 1177     char *caption_filename;
 1178     char *s, *dir, *caption_dir;
 1179     struct stat cdir_stat;
 1180     s = strrchr(file->filename, '/');
 1181     if (s) {
 1182         dir = estrdup(file->filename);
 1183         s = strrchr(dir, '/');
 1184         *s = '\0';
 1185     } else {
 1186         dir = estrdup(".");
 1187     }
 1188 
 1189     caption_dir = estrjoin("/", dir, opt.caption_path, NULL);
 1190 
 1191     D(("dir %s, cp %s, cdir %s\n", dir, opt.caption_path, caption_dir))
 1192 
 1193     if (stat(caption_dir, &cdir_stat) == -1) {
 1194         if (!create_dir)
 1195             return NULL;
 1196         if (mkdir(caption_dir, 0755) == -1)
 1197             eprintf("Failed to create caption directory %s:", caption_dir);
 1198     } else if (!S_ISDIR(cdir_stat.st_mode))
 1199         eprintf("Caption directory (%s) exists, but is not a directory.",
 1200             caption_dir);
 1201 
 1202     free(caption_dir);
 1203 
 1204     caption_filename = estrjoin("", dir, "/", opt.caption_path, "/", file->name, ".txt", NULL);
 1205     free(dir);
 1206     return caption_filename;
 1207 }
 1208 
 1209 void feh_draw_caption(winwidget w)
 1210 {
 1211     static Imlib_Font fn = NULL;
 1212     int tw = 0, th = 0, ww, hh;
 1213     int x, y;
 1214     Imlib_Image im = NULL;
 1215     char *p;
 1216     gib_list *lines, *l;
 1217     static gib_style *caption_style = NULL;
 1218     feh_file *file;
 1219 
 1220     if (!w->file) {
 1221         return;
 1222     }
 1223     file = FEH_FILE(w->file->data);
 1224     if (!file->filename) {
 1225         return;
 1226     }
 1227 
 1228     if (!file->caption) {
 1229         char *caption_filename;
 1230         caption_filename = build_caption_filename(file, 0);
 1231         if (caption_filename)
 1232             /* read caption from file */
 1233             file->caption = ereadfile(caption_filename);
 1234         else
 1235             file->caption = estrdup("");
 1236         free(caption_filename);
 1237     }
 1238 
 1239     if (file->caption == NULL) {
 1240         /* caption file is not there, we want to cache that, otherwise we'll stat
 1241          * the damn file every time we render the image. Reloading an image will
 1242          * always cause the caption to be reread though so we're safe to do so.
 1243          * (Before this bit was added, when zooming a captionless image with
 1244          * captions enabled, the captions file would be stat()d like 30 times a
 1245          * second) - don't forget this function is called from
 1246          * winwidget_render_image().
 1247          */
 1248         file->caption = estrdup("");
 1249     }
 1250 
 1251     if (*(file->caption) == '\0' && !w->caption_entry)
 1252         return;
 1253 
 1254     caption_style = gib_style_new("caption");
 1255     caption_style->bits = gib_list_add_front(caption_style->bits,
 1256         gib_style_bit_new(0, 0, 0, 0, 0, 0));
 1257     caption_style->bits = gib_list_add_front(caption_style->bits,
 1258         gib_style_bit_new(1, 1, 0, 0, 0, 255));
 1259 
 1260     fn = feh_load_font(w);
 1261 
 1262     if (*(file->caption) == '\0') {
 1263         p = estrdup("Caption entry mode - Hit ESC to cancel");
 1264         lines = feh_wrap_string(p, w->w, fn, NULL);
 1265         free(p);
 1266     } else
 1267         lines = feh_wrap_string(file->caption, w->w, fn, NULL);
 1268 
 1269     if (!lines)
 1270         return;
 1271 
 1272     /* Work out how high/wide the caption is */
 1273     l = lines;
 1274     while (l) {
 1275         p = (char *) l->data;
 1276         gib_imlib_get_text_size(fn, p, caption_style, &ww, &hh, IMLIB_TEXT_TO_RIGHT);
 1277         if (ww > tw)
 1278             tw = ww;
 1279         th += hh;
 1280         if (l->next)
 1281             th += 1;    /* line spacing */
 1282         l = l->next;
 1283     }
 1284 
 1285     /* we don't want the caption overlay larger than our window */
 1286     if (th > w->h)
 1287         th = w->h;
 1288     if (tw > w->w)
 1289         tw = w->w;
 1290 
 1291     im = imlib_create_image(tw, th);
 1292     if (!im)
 1293         eprintf("Couldn't create image. Out of memory?");
 1294 
 1295     feh_imlib_image_fill_text_bg(im, tw, th);
 1296 
 1297     l = lines;
 1298     y = 0;
 1299     while (l) {
 1300         p = (char *) l->data;
 1301         gib_imlib_get_text_size(fn, p, caption_style, &ww, &hh, IMLIB_TEXT_TO_RIGHT);
 1302         x = (tw - ww) / 2;
 1303         if (w->caption_entry && (*(file->caption) == '\0'))
 1304             gib_imlib_text_draw(im, fn, caption_style, x, y, p,
 1305                 IMLIB_TEXT_TO_RIGHT, 255, 255, 127, 255);
 1306         else if (w->caption_entry)
 1307             gib_imlib_text_draw(im, fn, caption_style, x, y, p,
 1308                 IMLIB_TEXT_TO_RIGHT, 255, 255, 0, 255);
 1309         else
 1310             gib_imlib_text_draw(im, fn, caption_style, x, y, p,
 1311                 IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
 1312 
 1313         y += hh + 1;    /* line spacing */
 1314         l = l->next;
 1315     }
 1316 
 1317     gib_imlib_render_image_on_drawable(w->bg_pmap, im, (w->w - tw) / 2, w->h - th, 1, 1, 0);
 1318     gib_imlib_free_image_and_decache(im);
 1319     gib_list_free_and_data(lines);
 1320     return;
 1321 }
 1322 
 1323 unsigned char reset_output = 0;
 1324 
 1325 void feh_display_status(char stat)
 1326 {
 1327     static int i = 0;
 1328     static int init_len = 0;
 1329     int j = 0;
 1330 
 1331     D(("filelist %p, filelist->next %p\n", filelist, filelist->next));
 1332 
 1333     if (!stat) {
 1334         putc('\n', stderr);
 1335         init_len = 0;
 1336         i = 0;
 1337         return;
 1338     }
 1339 
 1340     if (!init_len)
 1341         init_len = gib_list_length(filelist);
 1342 
 1343     if (i) {
 1344         if (reset_output) {
 1345             /* There's just been an error message. Unfortunate ;) */
 1346             for (j = 0; j < (((i % 50) + ((i % 50) / 10)) + 7); j++)
 1347                 putc(' ', stderr);
 1348         }
 1349 
 1350         if (!(i % 50)) {
 1351             int len = gib_list_length(filelist);
 1352 
 1353             fprintf(stderr, " %5d/%d (%d)\n[%3d%%] ",
 1354                     i, init_len, len, ((int) ((float) i / init_len * 100)));
 1355 
 1356         } else if ((!(i % 10)) && (!reset_output))
 1357             putc(' ', stderr);
 1358 
 1359         reset_output = 0;
 1360     } else
 1361         fputs("[  0%] ", stderr);
 1362 
 1363     fprintf(stderr, "%c", stat);
 1364     fflush(stderr);
 1365     i++;
 1366     return;
 1367 }
 1368 
 1369 void feh_edit_inplace(winwidget w, int op)
 1370 {
 1371     int tmp;
 1372     Imlib_Image old = NULL;
 1373     Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE;
 1374     if (!w->file || !w->file->data || !FEH_FILE(w->file->data)->filename)
 1375         return;
 1376 
 1377     if (!opt.edit) {
 1378         imlib_context_set_image(w->im);
 1379         if (op == INPLACE_EDIT_FLIP)
 1380             imlib_image_flip_vertical();
 1381         else if (op == INPLACE_EDIT_MIRROR)
 1382             imlib_image_flip_horizontal();
 1383         else {
 1384             imlib_image_orientate(op);
 1385             tmp = w->im_w;
 1386             w->im_w = w->im_h;
 1387             w->im_h = tmp;
 1388             if (FEH_FILE(w->file->data)->info) {
 1389                 FEH_FILE(w->file->data)->info->width = w->im_w;
 1390                 FEH_FILE(w->file->data)->info->height = w->im_h;
 1391             }
 1392         }
 1393         winwidget_render_image(w, 1, 0);
 1394         return;
 1395     }
 1396 
 1397     // Imlib2 <= 1.5 returns "jpeg", Imlib2 >= 1.6 uses "jpg"
 1398     if ((!strcmp(gib_imlib_image_format(w->im), "jpeg")
 1399                 || !strcmp(gib_imlib_image_format(w->im), "jpg"))
 1400             && !path_is_url(FEH_FILE(w->file->data)->filename)) {
 1401         feh_edit_inplace_lossless(w, op);
 1402         feh_reload_image(w, 1, 1);
 1403         return;
 1404     }
 1405 
 1406     old = imlib_load_image_with_error_return(FEH_FILE(w->file->data)->filename, &err);
 1407 
 1408     if ((old != NULL) && (err == IMLIB_LOAD_ERROR_NONE)) {
 1409         imlib_context_set_image(old);
 1410         if (op == INPLACE_EDIT_FLIP)
 1411             imlib_image_flip_vertical();
 1412         else if (op == INPLACE_EDIT_MIRROR)
 1413             imlib_image_flip_horizontal();
 1414         else
 1415             imlib_image_orientate(op);
 1416         gib_imlib_save_image_with_error_return(old,
 1417             FEH_FILE(w->file->data)->filename, &err);
 1418         gib_imlib_free_image(old);
 1419         if (err)
 1420             feh_imlib_print_load_error(FEH_FILE(w->file->data)->filename,
 1421                 w, err);
 1422         feh_reload_image(w, 1, 1);
 1423     } else {
 1424         /*
 1425          * Image was opened using curl/magick or has been deleted after
 1426          * opening it
 1427          */
 1428         imlib_context_set_image(w->im);
 1429         if (op == INPLACE_EDIT_FLIP)
 1430             imlib_image_flip_vertical();
 1431         else if (op == INPLACE_EDIT_MIRROR)
 1432             imlib_image_flip_horizontal();
 1433         else {
 1434             imlib_image_orientate(op);
 1435             tmp = w->im_w;
 1436             w->im_w = w->im_h;
 1437             w->im_h = tmp;
 1438             if (FEH_FILE(w->file->data)->info) {
 1439                 FEH_FILE(w->file->data)->info->width = w->im_w;
 1440                 FEH_FILE(w->file->data)->info->height = w->im_h;
 1441             }
 1442         }
 1443         im_weprintf(w, "unable to edit in place. Changes have not been saved.");
 1444         winwidget_render_image(w, 1, 0);
 1445     }
 1446 
 1447     return;
 1448 }
 1449 
 1450 gib_list *feh_wrap_string(char *text, int wrap_width, Imlib_Font fn, gib_style * style)
 1451 {
 1452     gib_list *ll, *lines = NULL, *list = NULL, *words;
 1453     gib_list *l = NULL;
 1454     char delim[2] = { '\n', '\0' };
 1455     int w, line_width;
 1456     int tw, th;
 1457     char *p, *pp;
 1458     char *line = NULL;
 1459     char *temp;
 1460     int space_width = 0, m_width = 0, t_width = 0, new_width = 0;
 1461 
 1462     lines = gib_string_split(text, delim);
 1463 
 1464     if (wrap_width) {
 1465         gib_imlib_get_text_size(fn, "M M", style, &t_width, NULL, IMLIB_TEXT_TO_RIGHT);
 1466         gib_imlib_get_text_size(fn, "M", style, &m_width, NULL, IMLIB_TEXT_TO_RIGHT);
 1467         space_width = t_width - (2 * m_width);
 1468         w = wrap_width;
 1469         l = lines;
 1470         while (l) {
 1471             line_width = 0;
 1472             p = (char *) l->data;
 1473             /* quick check to see if whole line fits okay */
 1474             gib_imlib_get_text_size(fn, p, style, &tw, &th, IMLIB_TEXT_TO_RIGHT);
 1475             if (tw <= w) {
 1476                 list = gib_list_add_end(list, estrdup(p));
 1477             } else if (strlen(p) == 0) {
 1478                 list = gib_list_add_end(list, estrdup(""));
 1479             } else if (!strcmp(p, " ")) {
 1480                 list = gib_list_add_end(list, estrdup(" "));
 1481             } else {
 1482                 words = gib_string_split(p, " ");
 1483                 if (words) {
 1484                     ll = words;
 1485                     while (ll) {
 1486                         pp = (char *) ll->data;
 1487                         if (strcmp(pp, " ")) {
 1488                             gib_imlib_get_text_size
 1489                                 (fn, pp, style, &tw, &th, IMLIB_TEXT_TO_RIGHT);
 1490                             if (line_width == 0)
 1491                                 new_width = tw;
 1492                             else
 1493                                 new_width = line_width + space_width + tw;
 1494                             if (new_width <= w) {
 1495                                 /* add word to line */
 1496                                 if (line) {
 1497                                     int len;
 1498 
 1499                                     len = strlen(line)
 1500                                         + strlen(pp)
 1501                                         + 2;
 1502                                     temp = emalloc(len);
 1503                                     snprintf(temp, len, "%s %s", line, pp);
 1504                                     free(line);
 1505                                     line = temp;
 1506                                 } else {
 1507                                     line = estrdup(pp);
 1508                                 }
 1509                                 line_width = new_width;
 1510                             } else if (line_width == 0) {
 1511                                 /* can't fit single word in :/
 1512                                    increase width limit to width of word
 1513                                    and jam the bastard in anyhow */
 1514                                 w = tw;
 1515                                 line = estrdup(pp);
 1516                                 line_width = new_width;
 1517                             } else {
 1518                                 /* finish this line, start next and add word there */
 1519                                 if (line) {
 1520                                     list = gib_list_add_end(list, estrdup(line));
 1521                                     free(line);
 1522                                     line = NULL;
 1523                                 }
 1524                                 line = estrdup(pp);
 1525                                 line_width = tw;
 1526                             }
 1527                         }
 1528                         ll = ll->next;
 1529                     }
 1530                     if (line) {
 1531                         /* finish last line */
 1532                         list = gib_list_add_end(list, estrdup(line));
 1533                         free(line);
 1534                         line = NULL;
 1535                     }
 1536                     gib_list_free_and_data(words);
 1537                 }
 1538             }
 1539             l = l->next;
 1540         }
 1541         gib_list_free_and_data(lines);
 1542         lines = list;
 1543     }
 1544     return lines;
 1545 }
 1546 
 1547 void feh_edit_inplace_lossless(winwidget w, int op)
 1548 {
 1549     char *filename = FEH_FILE(w->file->data)->filename;
 1550     int len = strlen(filename) + 1;
 1551     char *file_str = emalloc(len);
 1552     int pid, status;
 1553     int devnull = -1;
 1554     char op_name[]  = "rotate";     /* message */
 1555     char op_op[]    = "-rotate";    /* jpegtran option */
 1556     char op_value[] = "horizontal"; /* jpegtran option's value */
 1557 
 1558     if (op == INPLACE_EDIT_FLIP) {
 1559         sprintf(op_name,  "flip");
 1560         sprintf(op_op,    "-flip");
 1561         sprintf(op_value, "vertical");
 1562     } else if (op == INPLACE_EDIT_MIRROR) {
 1563         sprintf(op_name,  "mirror");
 1564         sprintf(op_op,    "-flip");
 1565     } else
 1566         snprintf(op_value, 4, "%d", 90 * op);
 1567 
 1568     snprintf(file_str, len, "%s", filename);
 1569 
 1570     if ((pid = fork()) < 0) {
 1571         im_weprintf(w, "lossless %s: fork failed:", op_name);
 1572         free(file_str);
 1573         return;
 1574     }
 1575     else if (pid == 0) {
 1576 
 1577         execlp("jpegtran", "jpegtran", "-copy", "all", op_op, op_value,
 1578                 "-outfile", file_str, file_str, NULL);
 1579 
 1580         weprintf("lossless %s: Is 'jpegtran' installed? Failed to exec:", op_name);
 1581         _exit(1);
 1582     }
 1583     else {
 1584         waitpid(pid, &status, 0);
 1585 
 1586         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 1587             im_weprintf(w,
 1588                     "lossless %s: Got exitcode %d from jpegtran."
 1589                     " Commandline was: "
 1590                     "jpegtran -copy all %s %s -outfile %s %s",
 1591                     op_name, status >> 8, op_op, op_value, file_str, file_str);
 1592             free(file_str);
 1593             return;
 1594         }
 1595     }
 1596     if ((pid = fork()) < 0) {
 1597         im_weprintf(w, "lossless %s: fork failed while updating EXIF tags:", op_name);
 1598     }
 1599     else if (pid == 0) {
 1600 
 1601         /* discard normal output */
 1602         devnull = open("/dev/null", O_WRONLY);
 1603         dup2(devnull, 1);
 1604 
 1605         execlp("jpegexiforient", "jpegexiforient", "-1", file_str, NULL);
 1606         weprintf("lossless %s: Failed to exec jpegexiforient:", op_name);
 1607         _exit(1);
 1608     }
 1609     else {
 1610         waitpid(pid, &status, 0);
 1611 
 1612         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 1613             im_weprintf(w,
 1614                     "lossless %s: Failed to update EXIF orientation tag:"
 1615                     " jpegexiforient returned %d",
 1616                     op_name, status >> 8);
 1617         }
 1618     }
 1619     free(file_str);
 1620 }
 1621 
 1622 void feh_draw_actions(winwidget w)
 1623 {
 1624     static Imlib_Font fn = NULL;
 1625     int tw = 0, th = 0;
 1626     int th_offset = 0;
 1627     int max_tw = 0;
 1628     int line_th = 0;
 1629     Imlib_Image im = NULL;
 1630     int i = 0;
 1631     int num_actions = 0;
 1632     int cur_action = 0;
 1633     char index[3];
 1634     char *line;
 1635 
 1636     /* Count number of defined actions. This method sucks a bit since it needs
 1637      * to be changed if the number of actions changes, but at least it doesn't
 1638      * miss actions 2 to 9 if action1 isn't defined
 1639      */
 1640     for (i = 0; i < 10; i++) {
 1641         if (opt.actions[i])
 1642             num_actions++;
 1643     }
 1644 
 1645     if (num_actions == 0)
 1646         return;
 1647 
 1648     if ((!w->file) || (!FEH_FILE(w->file->data))
 1649             || (!FEH_FILE(w->file->data)->filename))
 1650         return;
 1651 
 1652     fn = feh_load_font(w);
 1653 
 1654     gib_imlib_get_text_size(fn, "defined actions:", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
 1655 /* Check for the widest line */
 1656     max_tw = tw;
 1657 
 1658     for (i = 0; i < 10; i++) {
 1659         if (opt.actions[i]) {
 1660             line = emalloc(strlen(opt.action_titles[i]) + 5);
 1661             strcpy(line, "0: ");
 1662             line = strcat(line, opt.action_titles[i]);
 1663             gib_imlib_get_text_size(fn, line, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT);
 1664             free(line);
 1665             if (tw > max_tw)
 1666                 max_tw = tw;
 1667         }
 1668     }
 1669 
 1670     tw = max_tw;
 1671     tw += 3;
 1672     th += 3;
 1673     line_th = th;
 1674     th = (th * num_actions) + line_th;
 1675 
 1676     /* This depends on feh_draw_filename internals...
 1677      * should be fixed some time
 1678      */
 1679     if (opt.draw_filename)
 1680         th_offset = line_th * 2;
 1681 
 1682     im = imlib_create_image(tw, th);
 1683     if (!im)
 1684         eprintf("Couldn't create image. Out of memory?");
 1685 
 1686     feh_imlib_image_fill_text_bg(im, tw, th);
 1687 
 1688     gib_imlib_text_draw(im, fn, NULL, 2, 2, "defined actions:", IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
 1689     gib_imlib_text_draw(im, fn, NULL, 1, 1, "defined actions:", IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
 1690 
 1691     for (i = 0; i < 10; i++) {
 1692         if (opt.action_titles[i]) {
 1693             cur_action++;
 1694             line = emalloc(strlen(opt.action_titles[i]) + 5);
 1695             sprintf(index, "%d", i);
 1696             strcpy(line, index);
 1697             strcat(line, ": ");
 1698             strcat(line, opt.action_titles[i]);
 1699 
 1700             gib_imlib_text_draw(im, fn, NULL, 2,
 1701                     (cur_action * line_th) + 2, line,
 1702                     IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
 1703             gib_imlib_text_draw(im, fn, NULL, 1,
 1704                     (cur_action * line_th) + 1, line,
 1705                     IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
 1706             free(line);
 1707         }
 1708     }
 1709 
 1710     gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, 0 + th_offset, 1, 1, 0);
 1711 
 1712     gib_imlib_free_image_and_decache(im);
 1713     return;
 1714 }