1 /* options.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 <strings.h>
28 #include "feh.h"
29 #include "filelist.h"
30 #include "options.h"
31 
32 static void check_options(void);
33 static void feh_getopt_theme(int argc, char **argv);
34 static void feh_parse_option_array(int argc, char **argv, int finalrun);
35 static void feh_check_theme_options(char **argv);
36 static void feh_parse_options_from_string(char *opts);
37 static void feh_load_options_for_theme(char *theme);
38 static void show_usage(void);
39 static void show_version(void);
40 static char *theme;
41 
42 fehoptions opt;
43 
init_parse_options(int argc,char ** argv)44 void init_parse_options(int argc, char **argv)
45 {
46 	/* TODO: sort these to match declaration of __fehoptions */
47 
48 	/* For setting the command hint on X windows */
49 	cmdargc = argc;
50 	cmdargv = argv;
51 
52 	/* Set default options */
53 	memset(&opt, 0, sizeof(fehoptions));
54 	opt.display = 1;
55 	opt.aspect = 1;
56 	opt.slideshow_delay = 0.0;
57 	opt.conversion_timeout = -1;
58 	opt.thumb_w = 60;
59 	opt.thumb_h = 60;
60 	opt.thumb_redraw = 10;
61 	opt.scroll_step = 20;
62 	opt.menu_font = estrdup(DEFAULT_MENU_FONT);
63 	opt.font = NULL;
64 	opt.max_height = opt.max_width = UINT_MAX;
65 
66 	opt.start_list_at = NULL;
67 	opt.jump_on_resort = 1;
68 
69 	opt.screen_clip = 1;
70 	opt.cache_size = 4;
71 #ifdef HAVE_LIBXINERAMA
72 	/* if we're using xinerama, then enable it by default */
73 	opt.xinerama = 1;
74 	opt.xinerama_index = -1;
75 #endif				/* HAVE_LIBXINERAMA */
76 #ifdef HAVE_INOTIFY
77 	opt.auto_reload = 1;
78 #endif				/* HAVE_INOTIFY */
79 	opt.use_conversion_cache = 1;
80 
81 	feh_getopt_theme(argc, argv);
82 
83 	D(("About to check for theme configuration\n"));
84 	feh_check_theme_options(argv);
85 
86 	D(("About to parse commandline options\n"));
87 	/* Parse the cmdline args */
88 	feh_parse_option_array(argc, argv, 1);
89 
90 	/* If we have a filelist to read, do it now */
91 	if (opt.filelistfile) {
92 		/* joining two reverse-sorted lists in this manner works nicely for us
93 		   here, as files specified on the commandline end up at the *end* of
94 		   the combined filelist, in the specified order. */
95 		D(("About to load filelist from file\n"));
96 		filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile));
97 	}
98 
99 	D(("Options parsed\n"));
100 
101 	filelist_len = gib_list_length(filelist);
102 	if (!filelist_len)
103 		show_mini_usage();
104 
105 	check_options();
106 
107 	feh_prepare_filelist();
108 	return;
109 }
110 
feh_check_theme_options(char ** argv)111 static void feh_check_theme_options(char **argv)
112 {
113 	if (!theme) {
114 		/* This prevents screw up when running src/feh or ./feh */
115 		char *pos = strrchr(argv[0], '/');
116 
117 		if (pos)
118 			theme = estrdup(pos + 1);
119 		else
120 			theme = estrdup(argv[0]);
121 	}
122 	D(("Theme name is %s\n", theme));
123 
124 	feh_load_options_for_theme(theme);
125 
126 	free(theme);
127 	return;
128 }
129 
feh_load_options_for_theme(char * theme)130 static void feh_load_options_for_theme(char *theme)
131 {
132 	FILE *fp = NULL;
133 	char *home = getenv("HOME");
134 	char *rcpath = NULL;
135 	char *oldrcpath = NULL;
136 	char *confbase = getenv("XDG_CONFIG_HOME");
137 	// s, s1 and s2 must always have identical size
138 	char s[1024], s1[1024], s2[1024];
139 	int cont = 0;
140 	int bspos;
141 
142 	if (confbase)
143 		rcpath = estrjoin("/", confbase, "feh/themes", NULL);
144 	else if (home)
145 		rcpath = estrjoin("/", home, ".config/feh/themes", NULL);
146 	else {
147 		weprintf("You have no HOME, cannot read configuration");
148 		return;
149 	}
150 
151 	oldrcpath = estrjoin("/", home, ".fehrc", NULL);
152 
153 	fp = fopen(rcpath, "r");
154 
155 	free(rcpath);
156 
157 	if (!fp && ((fp = fopen(oldrcpath, "r")) != NULL))
158 		weprintf("The theme config file was moved from ~/.fehrc to "
159 			"~/.config/feh/themes. Run\n"
160 			"    mkdir -p ~/.config/feh; mv ~/.fehrc ~/.config/feh/themes\n"
161 			"to fix this.");
162 
163 	free(oldrcpath);
164 
165 	if (!fp && ((fp = fopen("/etc/feh/themes", "r")) == NULL))
166 		return;
167 
168 	/* Oooh. We have an options file :) */
169 	for (; fgets(s, sizeof(s), fp);) {
170 		s1[0] = '\0';
171 		s2[0] = '\0';
172 
173 		if (cont) {
174 			/*
175 			 * fgets ensures that s contains no more than 1023 characters
176 			 * (+ 1 null byte)
177 			 */
178 			sscanf(s, " %[^\n]\n", (char *) &s2);
179 			if (!*s2)
180 				break;
181 			D(("Got continued options %s\n", s2));
182 		} else {
183 			/*
184 			 * fgets ensures that s contains no more than 1023 characters
185 			 * (+ 1 null byte)
186 			 */
187 			sscanf(s, "%s %[^\n]\n", (char *) &s1, (char *) &s2);
188 			if (!(*s1) || (!*s2) || (*s1 == '\n') || (*s1 == '#')) {
189 				cont = 0;
190 				continue;
191 			}
192 			D(("Got theme/options pair %s/%s\n", s1, s2));
193 		}
194 
195 		if (!strcmp(s1, theme) || cont) {
196 
197 			bspos = strlen(s2)-1;
198 
199 			if (s2[bspos] == '\\') {
200 				D(("Continued line\n"));
201 				s2[bspos] = '\0';
202 				cont = 1;
203 				/* A trailing whitespace confuses the option parser */
204 				if (bspos && (s2[bspos-1] == ' '))
205 					s2[bspos-1] = '\0';
206 			} else
207 				cont = 0;
208 
209 			D(("A match. Using options %s\n", s2));
210 			feh_parse_options_from_string(s2);
211 
212 			if (!cont)
213 				break;
214 		}
215 	}
216 	fclose(fp);
217 	return;
218 }
219 
220 /* FIXME This function is a crufty bitch ;) */
feh_parse_options_from_string(char * opts)221 static void feh_parse_options_from_string(char *opts)
222 {
223 	char *list[sizeof(char *) * 64];
224 	int num = 0;
225 	char *s;
226 	char *t;
227 	char last = 0;
228 	char inquote = 0;
229 	int i = 0;
230 
231 	/* So we don't reinvent the wheel (not again, anyway), we use the
232 	   getopt_long function to do this parsing as well. This means it has to
233 	   look like the real argv ;) */
234 
235 	list[num++] = estrdup(PACKAGE);
236 
237 	for (s = opts, t = opts;; t++) {
238 
239 		if (num > 64)
240 			eprintf(PACKAGE " does not support more than 64 words per "
241 					"theme definition.\n Please shorten your lines.");
242 
243 		if ((*t == ' ') && !inquote) {
244 			*t = '\0';
245 			num++;
246 
247 			list[num - 1] = feh_string_normalize(s);
248 			s = t + 1;
249 		} else if (*t == '\0') {
250 			num++;
251 
252 			list[num - 1] = feh_string_normalize(s);
253 			break;
254 		} else if ((*t == inquote) && (last != '\\')) {
255 			inquote = 0;
256 		} else if (((*t == '\"') || (*t == '\'')) && (last != '\\') && !inquote)
257 			inquote = *t;
258 		last = *t;
259 	}
260 
261 	feh_parse_option_array(num, list, 0);
262 
263 	for (i = 0; i < num; i++)
264 		if (list[i])
265 			free(list[i]);
266 	return;
267 }
268 
feh_string_normalize(char * str)269 char *feh_string_normalize(char *str)
270 {
271 	char ret[4096];
272 	char *s;
273 	int i = 0;
274 	char last = 0;
275 
276 	D(("normalizing %s\n", str));
277 	ret[0] = '\0';
278 
279 	for (s = str;; s++) {
280 		if (*s == '\0')
281 			break;
282 		else if ((*s == '\"') && (last == '\\'))
283 			ret[i++] = '\"';
284 		else if ((*s == '\"') && (last == 0));
285 		else if ((*s == '\'') && (last == '\\'))
286 			ret[i++] = '\'';
287 		else if ((*s == '\'') && (last == 0));
288 		else if ((*s == ' ') && (last == '\\'))
289 			ret[i++] = ' ';
290 		else
291 			ret[i++] = *s;
292 
293 		last = *s;
294 	}
295 	if (i && ((ret[i - 1] == '\"') || (ret[i - 1] == '\'')))
296 		ret[i - 1] = '\0';
297 	else
298 		ret[i] = '\0';
299 	D(("normalized to %s\n", ret));
300 
301 	return(estrdup(ret));
302 }
303 
feh_getopt_theme(int argc,char ** argv)304 static void feh_getopt_theme(int argc, char **argv)
305 {
306 	static char stropts[] = "-T:";
307 	static struct option lopts[] = {
308 		{"theme", 1, 0, 'T'},
309 		{0, 0, 0, 0}
310 	};
311 	int optch = 0, cmdx = 0;
312 
313 	opterr = 0;
314 
315 	while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) {
316 		if (optch == 'T')
317 			theme = estrdup(optarg);
318 	}
319 
320 	opterr = 1;
321 	optind = 0;
322 }
323 
feh_parse_option_array(int argc,char ** argv,int finalrun)324 static void feh_parse_option_array(int argc, char **argv, int finalrun)
325 {
326 	int discard;
327 	static char stropts[] =
328 		"a:A:b:B:C:dD:e:E:f:Fg:GhH:iIj:J:kK:lL:mM:nNo:O:pPqrR:sS:tT:uUvVwW:xXy:YzZ"
329 		".@:^:~:|:+:<:>:";
330 
331 	/* (*name, has_arg, *flag, val) See: struct option in getopts.h */
332 	static struct option lopts[] = {
333 		{"debug"         , 0, 0, '+'},
334 		{"scale-down"    , 0, 0, '.'},
335 		{"max-dimension" , 1, 0, '<'},
336 		{"min-dimension" , 1, 0, '>'},
337 		{"title-font"    , 1, 0, '@'},
338 		{"action"        , 1, 0, 'A'},
339 		{"image-bg"      , 1, 0, 'B'},
340 		{"fontpath"      , 1, 0, 'C'},
341 		{"slideshow-delay",1, 0, 'D'},
342 		{"thumb-height"  , 1, 0, 'E'},
343 		{"full-screen"   , 0, 0, 'F'}, /* deprecated */
344 		{"fullscreen"    , 0, 0, 'F'},
345 		{"draw-actions"  , 0, 0, 'G'},
346 		{"limit-height"  , 1, 0, 'H'},
347 		{"fullindex"     , 0, 0, 'I'},
348 		{"thumb-redraw"  , 1, 0, 'J'},
349 		{"caption-path"  , 1, 0, 'K'},
350 		{"customlist"    , 1, 0, 'L'},
351 		{"menu-font"     , 1, 0, 'M'},
352 		{"no-menus"      , 0, 0, 'N'},
353 		{"output-only"   , 1, 0, 'O'},
354 		{"cache-thumbnails", 0, 0, 'P'},
355 		{"reload"        , 1, 0, 'R'},
356 		{"sort"          , 1, 0, 'S'},
357 		{"theme"         , 1, 0, 'T'},
358 		{"loadable"      , 0, 0, 'U'},
359 		{"verbose"       , 0, 0, 'V'},
360 		{"limit-width"   , 1, 0, 'W'},
361 		{"ignore-aspect" , 0, 0, 'X'},
362 		{"hide-pointer"  , 0, 0, 'Y'},
363 		{"auto-zoom"     , 0, 0, 'Z'},
364 		{"title"         , 1, 0, '^'},
365 		{"alpha"         , 1, 0, 'a'},
366 		{"bg"            , 1, 0, 'b'},
367 		{"draw-filename" , 0, 0, 'd'},
368 		{"font"          , 1, 0, 'e'},
369 		{"filelist"      , 1, 0, 'f'},
370 		{"geometry"      , 1, 0, 'g'},
371 		{"help"          , 0, 0, 'h'},
372 		{"index"         , 0, 0, 'i'},
373 		{"output-dir"    , 1, 0, 'j'},
374 		{"keep-http"     , 0, 0, 'k'},
375 		{"list"          , 0, 0, 'l'},
376 		{"montage"       , 0, 0, 'm'},
377 		{"reverse"       , 0, 0, 'n'},
378 		{"output"        , 1, 0, 'o'},
379 		{"preload"       , 0, 0, 'p'},
380 		{"quiet"         , 0, 0, 'q'},
381 		{"recursive"     , 0, 0, 'r'},
382 		{"stretch"       , 0, 0, 's'},
383 		{"thumbnails"    , 0, 0, 't'},
384 		{"unloadable"    , 0, 0, 'u'},
385 		{"version"       , 0, 0, 'v'},
386 		{"multiwindow"   , 0, 0, 'w'},
387 		{"borderless"    , 0, 0, 'x'},
388 		{"thumb-width"   , 1, 0, 'y'},
389 		{"randomize"     , 0, 0, 'z'},
390 		{"start-at"      , 1, 0, '|'},
391 		{"thumb-title"   , 1, 0, '~'},
392 		{"bg-tile"       , 0, 0, 200},
393 		{"bg-center"     , 0, 0, 201},
394 		{"bg-scale"      , 0, 0, 202},
395 		{"zoom"          , 1, 0, 205},
396 		{"no-screen-clip", 0, 0, 206},
397 		{"index-info"    , 1, 0, 207},
398 		{"magick-timeout", 1, 0, 208},
399 		{"action1"       , 1, 0, 209},
400 		{"action2"       , 1, 0, 210},
401 		{"action3"       , 1, 0, 211},
402 		{"action4"       , 1, 0, 212},
403 		{"action5"       , 1, 0, 213},
404 		{"action6"       , 1, 0, 214},
405 		{"action7"       , 1, 0, 215},
406 		{"action8"       , 1, 0, 216},
407 		{"action9"       , 1, 0, 217},
408 		{"bg-fill"       , 0, 0, 218},
409 		{"bg-max"        , 0, 0, 219},
410 		{"no-jump-on-resort", 0, 0, 220},
411 		{"edit"          , 0, 0, 221},
412 #ifdef HAVE_LIBEXIF
413 		{"draw-exif"     , 0, 0, 223},
414 		{"auto-rotate"   , 0, 0, 242},
415 #endif
416 		{"no-xinerama"   , 0, 0, 225},
417 		{"draw-tinted"   , 0, 0, 229},
418 		{"info"          , 1, 0, 234},
419 		{"force-aliasing", 0, 0, 235},
420 		{"no-fehbg"      , 0, 0, 236},
421 		{"keep-zoom-vp"  , 0, 0, 237},
422 		{"scroll-step"   , 1, 0, 238},
423 		{"xinerama-index", 1, 0, 239},
424 		{"insecure"      , 0, 0, 240},
425 		{"no-recursive"  , 0, 0, 241},
426 		{"cache-size"    , 1, 0, 243},
427 		{"on-last-slide" , 1, 0, 244},
428 		{"conversion-timeout" , 1, 0, 245},
429 		{"version-sort"  , 0, 0, 246},
430 		{"offset"        , 1, 0, 247},
431 #ifdef HAVE_INOTIFY
432 		{"auto-reload"   , 0, 0, 248},
433 #endif
434 		{"class"         , 1, 0, 249},
435 		{"no-conversion-cache", 0, 0, 250},
436 		{0, 0, 0, 0}
437 	};
438 	int optch = 0, cmdx = 0;
439 
440 	while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) {
441 		D(("Got option, getopt calls it %d, or %c\n", optch, optch));
442 		switch (optch) {
443 		case 0:
444 			break;
445 		case '+':
446 			opt.debug = 1;
447 			break;
448 		case '<':
449 			opt.filter_by_dimensions = 1;
450 			XParseGeometry(optarg, &discard, &discard, &opt.max_width, &opt.max_height);
451 			if (opt.max_width == 0)
452 				opt.max_width = UINT_MAX;
453 			if (opt.max_height == 0)
454 				opt.max_height = UINT_MAX;
455 			break;
456 		case '>':
457 			opt.filter_by_dimensions = 1;
458 			XParseGeometry(optarg, &discard, &discard, &opt.min_width, &opt.min_height);
459 			break;
460 		case '.':
461 			opt.scale_down = 1;
462 			break;
463 		case '@':
464 			opt.title_font = estrdup(optarg);
465 			break;
466 		case 'A':
467 			opt.actions[0] = estrdup(optarg);
468 			break;
469 		case 'B':
470 			opt.image_bg = estrdup(optarg);
471 			break;
472 		case 'C':
473 			D(("adding fontpath %s\n", optarg));
474 			imlib_add_path_to_font_path(optarg);
475 			break;
476 		case 'D':
477 			opt.slideshow_delay = atof(optarg);
478 			if (opt.slideshow_delay < 0.0) {
479 				opt.slideshow_delay *= (-1);
480 				opt.paused = 1;
481 			} else {
482 				opt.paused = 0;
483 			}
484 			break;
485 		case 'E':
486 			opt.thumb_h = atoi(optarg);
487 			break;
488 		case 'F':
489 			opt.full_screen = 1;
490 			break;
491 		case 'G':
492 			opt.draw_actions = 1;
493 			break;
494 		case 'H':
495 			opt.limit_h = atoi(optarg);
496 			break;
497 		case 'I':
498 			opt.index = 1;
499 			opt.index_info = estrdup("%n\n%S\n%wx%h");
500 			break;
501 		case 'J':
502 			opt.thumb_redraw = atoi(optarg);
503 			break;
504 		case 'K':
505 			opt.caption_path = estrdup(optarg);
506 			break;
507 		case 'L':
508 			opt.customlist = estrdup(optarg);
509 			opt.display = 0;
510 			break;
511 		case 'M':
512 			free(opt.menu_font);
513 			opt.menu_font = estrdup(optarg);
514 			break;
515 		case 'N':
516 			opt.no_menus = 1;
517 			break;
518 		case 'O':
519 			opt.output = 1;
520 			opt.output_file = estrdup(optarg);
521 			opt.display = 0;
522 			break;
523 		case 'P':
524 			opt.cache_thumbnails = 1;
525 			break;
526 		case 'R':
527 			opt.reload = atof(optarg);
528 			opt.use_conversion_cache = 0;
529 #ifdef HAVE_INOTIFY
530 			opt.auto_reload = 0;
531 #endif
532 			break;
533 		case 'S':
534 			if (!strcasecmp(optarg, "name"))
535 				opt.sort = SORT_NAME;
536 			else if (!strcasecmp(optarg, "filename"))
537 				opt.sort = SORT_FILENAME;
538 			else if (!strcasecmp(optarg, "dirname"))
539 				opt.sort = SORT_DIRNAME;
540 			else if (!strcasecmp(optarg, "mtime"))
541 				opt.sort = SORT_MTIME;
542 			else if (!strcasecmp(optarg, "width"))
543 				opt.sort = SORT_WIDTH;
544 			else if (!strcasecmp(optarg, "height"))
545 				opt.sort = SORT_HEIGHT;
546 			else if (!strcasecmp(optarg, "pixels"))
547 				opt.sort = SORT_PIXELS;
548 			else if (!strcasecmp(optarg, "size"))
549 				opt.sort = SORT_SIZE;
550 			else if (!strcasecmp(optarg, "format"))
551 				opt.sort = SORT_FORMAT;
552 			else {
553 				weprintf("Unrecognised sort mode \"%s\". Defaulting to "
554 						"sort by filename", optarg);
555 				opt.sort = SORT_FILENAME;
556 			}
557 			if (opt.randomize) {
558 				weprintf("commandline contains --randomize and --sort. "
559 						"--randomize has been unset");
560 				opt.randomize = 0;
561 			}
562 			break;
563 		case 'T':
564 			theme = estrdup(optarg);
565 			break;
566 		case 'U':
567 			opt.loadables = 1;
568 			opt.display = 0;
569 			break;
570 		case 'V':
571 			opt.verbose = 1;
572 			break;
573 		case 'W':
574 			opt.limit_w = atoi(optarg);
575 			break;
576 		case 'X':
577 			opt.aspect = 0;
578 			break;
579 		case 'Y':
580 			opt.hide_pointer = 1;
581 			break;
582 		case 'Z':
583 			opt.zoom_mode = ZOOM_MODE_MAX;
584 			break;
585 		case '^':
586 			opt.title = estrdup(optarg);
587 			break;
588 		case 'a':
589 			opt.alpha = 1;
590 			opt.alpha_level = 255 - atoi(optarg);
591 			break;
592 		case 'b':
593 			opt.bg = 1;
594 			opt.bg_file = estrdup(optarg);
595 			break;
596 		case 'd':
597 			opt.draw_filename = 1;
598 			break;
599 		case 'e':
600 			opt.font = estrdup(optarg);
601 			break;
602 		case 'f':
603 			if (!strcmp(optarg, "-"))
604 				opt.filelistfile = estrdup("/dev/stdin");
605 			else
606 				opt.filelistfile = estrdup(optarg);
607 			break;
608 		case 'g':
609 			opt.geom_enabled = 1;
610 			opt.geom_flags = XParseGeometry(optarg, &opt.geom_x,
611 					&opt.geom_y, &opt.geom_w, &opt.geom_h);
612 			break;
613 		case 'h':
614 			show_usage();
615 			break;
616 		case 'i':
617 			opt.index = 1;
618 			opt.index_info = estrdup("%n");
619 			break;
620 		case 'j':
621 			opt.output_dir = estrdup(optarg);
622 			break;
623 		case 'k':
624 			opt.keep_http = 1;
625 			break;
626 		case 'l':
627 			opt.list = 1;
628 			opt.display = 0;
629 			break;
630 		case 'm':
631 			opt.index = 1;
632 			break;
633 		case 'n':
634 			opt.reverse = 1;
635 			break;
636 		case 'o':
637 			opt.output = 1;
638 			opt.output_file = estrdup(optarg);
639 			break;
640 		case 'p':
641 			opt.preload = 1;
642 			break;
643 		case 'q':
644 			opt.quiet = 1;
645 			break;
646 		case 'r':
647 			opt.recursive = 1;
648 			break;
649 		case 's':
650 			opt.stretch = 1;
651 			break;
652 		case 't':
653 			opt.thumbs = 1;
654 			opt.index_info = estrdup("%n");
655 			break;
656 		case 'u':
657 			opt.unloadables = 1;
658 			opt.display = 0;
659 			break;
660 		case 'v':
661 			show_version();
662 			break;
663 		case 'w':
664 			opt.multiwindow = 1;
665 			break;
666 		case 'x':
667 			opt.borderless = 1;
668 			break;
669 		case 'y':
670 			opt.thumb_w = atoi(optarg);
671 			break;
672 		case 'z':
673 			opt.randomize = 1;
674 			if (opt.sort != SORT_NONE) {
675 				weprintf("commandline contains --sort and --randomize. "
676 						"--sort has been unset");
677 				opt.sort = SORT_NONE;
678 			}
679 			break;
680 		case '|':
681 			opt.start_list_at = estrdup(optarg);
682 			break;
683 		case '~':
684 			opt.thumb_title = estrdup(optarg);
685 			break;
686 		case 200:
687 			opt.bgmode = BG_MODE_TILE;
688 			break;
689 		case 201:
690 			opt.bgmode = BG_MODE_CENTER;
691 			break;
692 		case 202:
693 			opt.bgmode = BG_MODE_SCALE;
694 			break;
695 		case 205:
696 			if (!strcmp("fill", optarg))
697 				opt.zoom_mode = ZOOM_MODE_FILL;
698 			else if (!strcmp("max", optarg))
699 				opt.zoom_mode = ZOOM_MODE_MAX;
700 			else
701 				opt.default_zoom = atoi(optarg);
702 			break;
703 		case 206:
704 			opt.screen_clip = 0;
705 			break;
706 		case 207:
707 			opt.index_info = estrdup(optarg);
708 			break;
709 		case 208:
710 			weprintf("--magick-timeout is deprecated, please use --conversion-timeout instead");
711 			opt.conversion_timeout = atoi(optarg);
712 			break;
713 		case 209:
714 			opt.actions[1] = estrdup(optarg);
715 			break;
716 		case 210:
717 			opt.actions[2] = estrdup(optarg);
718 			break;
719 		case 211:
720 			opt.actions[3] = estrdup(optarg);
721 			break;
722 		case 212:
723 			opt.actions[4] = estrdup(optarg);
724 			break;
725 		case 213:
726 			opt.actions[5] = estrdup(optarg);
727 			break;
728 		case 214:
729 			opt.actions[6] = estrdup(optarg);
730 			break;
731 		case 215:
732 			opt.actions[7] = estrdup(optarg);
733 			break;
734 		case 216:
735 			opt.actions[8] = estrdup(optarg);
736 			break;
737 		case 217:
738 			opt.actions[9] = estrdup(optarg);
739 			break;
740 		case 218:
741 			opt.bgmode = BG_MODE_FILL;
742 			break;
743 		case 219:
744 			opt.bgmode = BG_MODE_MAX;
745 			break;
746 		case 220:
747 			opt.jump_on_resort = 0;
748 			break;
749 		case 221:
750 			opt.edit = 1;
751 			break;
752 #ifdef HAVE_LIBEXIF
753 		case 223:
754 			opt.draw_exif = 1;
755 			break;
756 		case 242:
757 			opt.auto_rotate = 1;
758 			break;
759 #endif
760 		case 225:
761 			opt.xinerama = 0;
762 			break;
763 		case 229:
764 			opt.text_bg = TEXT_BG_TINTED;
765 			break;
766 		case 234:
767 			opt.info_cmd = estrdup(optarg);
768 			if (opt.info_cmd[0] == ';') {
769 				opt.draw_info = 0;
770 				opt.info_cmd++;
771 			} else {
772 				opt.draw_info = 1;
773 			}
774 			break;
775 		case 235:
776 			opt.force_aliasing = 1;
777 			break;
778 		case 236:
779 			opt.no_fehbg = 1;
780 			break;
781 		case 237:
782 			opt.keep_zoom_vp = 1;
783 			break;
784 		case 238:
785 			opt.scroll_step = atoi(optarg);
786 			break;
787 		case 239:
788 			opt.xinerama_index = atoi(optarg);
789 			break;
790 		case 240:
791 			opt.insecure_ssl = 1;
792 			break;
793 		case 241:
794 			opt.recursive = 0;
795 			break;
796 		case 243:
797 			opt.cache_size = atoi(optarg);
798 			if (opt.cache_size < 0)
799 				opt.cache_size = 0;
800 			if (opt.cache_size > 2048)
801 				opt.cache_size = 2048;
802 			break;
803 		case 244:
804 			if (!strcmp(optarg, "quit")) {
805 				opt.on_last_slide = ON_LAST_SLIDE_QUIT;
806 			} else if (!strcmp(optarg, "hold")) {
807 				opt.on_last_slide = ON_LAST_SLIDE_HOLD;
808 			} else if (!strcmp(optarg, "resume")) {
809 				opt.on_last_slide = ON_LAST_SLIDE_RESUME;
810 			} else {
811 				weprintf("Unrecognized on-last-slide action \"%s\"."
812 						"Supported actions: hold, resume, quit\n", optarg);
813 			}
814 			break;
815 		case 245:
816 			opt.conversion_timeout = atoi(optarg);
817 			break;
818 		case 246:
819 			opt.version_sort = 1;
820 			break;
821 		case 247:
822 			opt.offset_flags = XParseGeometry(optarg, &opt.offset_x,
823 					&opt.offset_y, (unsigned int *)&discard, (unsigned int *)&discard);
824 			break;
825 #ifdef HAVE_INOTIFY
826 		case 248:
827 			opt.auto_reload = 1;
828 			break;
829 #endif
830 		case 249:
831 			opt.x11_class = estrdup(optarg);
832 			break;
833 		case 250:
834 			opt.use_conversion_cache = 0;
835 			break;
836 		default:
837 			break;
838 		}
839 	}
840 
841 	/* Now the leftovers, which must be files */
842 	if (optind < argc) {
843 		while (optind < argc) {
844 			if (opt.reload)
845 				original_file_items = gib_list_add_front(original_file_items, estrdup(argv[optind]));
846 			/* If recursive is NOT set, but the only argument is a directory
847 			   name, we grab all the files in there, but not subdirs */
848 			add_file_to_filelist_recursively(argv[optind++], FILELIST_FIRST);
849 		}
850 	}
851 	else if (finalrun && !opt.filelistfile && !opt.bgmode) {
852 		/*
853 		 * if --start-at is a non-local URL (i.e., does not start with file:///),
854 		 * behave as if "feh URL" was called (there is no directory we can load)
855 		 */
856 		if (opt.start_list_at && path_is_url(opt.start_list_at) && (strlen(opt.start_list_at) <= 8 || strncmp(opt.start_list_at, "file:///", 8) != 0)) {
857 			add_file_to_filelist_recursively(opt.start_list_at, FILELIST_FIRST);
858 		} else if (opt.start_list_at && strrchr(opt.start_list_at, '/')) {
859 			if (strlen(opt.start_list_at) > 8 && strncmp(opt.start_list_at, "file:///", 8) == 0) {
860 				char *start_at_path = estrdup(opt.start_list_at + 7);
861 				free(opt.start_list_at);
862 				opt.start_list_at = start_at_path;
863 			}
864 			char *target_directory = estrdup(opt.start_list_at);
865 			char *filename_start = strrchr(target_directory, '/');
866 			if (filename_start) {
867 				*filename_start = '\0';
868 			}
869 			add_file_to_filelist_recursively(target_directory, FILELIST_FIRST);
870 			free(target_directory);
871 		} else {
872 			add_file_to_filelist_recursively(".", FILELIST_FIRST);
873 		}
874 	}
875 
876 	/* So that we can safely be called again */
877 	optind = 0;
878 	return;
879 }
880 
check_options(void)881 static void check_options(void)
882 {
883 	int i;
884 	char *endptr;
885 
886 	for (i = 0; i < 10; i++) {
887 		if (opt.actions[i] && !opt.hold_actions[i] && (opt.actions[i][0] == ';')) {
888 			opt.hold_actions[i] = 1;
889 			opt.actions[i] = opt.actions[i] + 1;
890 		}
891 		opt.action_titles[i] = opt.actions[i];
892 		if (opt.actions[i] && (opt.actions[i][0] == '[')) {
893 			if (((endptr = strchr(opt.actions[i], ']')) != NULL)
894 					&& (opt.actions[i][1] != ' ')) {
895 				opt.action_titles[i] = opt.actions[i] + 1;
896 				opt.actions[i] = endptr + 1;
897 				*endptr = 0;
898 			}
899 		}
900 	}
901 
902 	if (opt.full_screen && opt.multiwindow) {
903 		eprintf("You cannot combine --fullscreen with --multiwindow");
904 	}
905 
906 	if (opt.list && (opt.multiwindow || opt.index)) {
907 		eprintf("You cannot combine --list with other modes");
908 	}
909 
910 	if (opt.loadables && opt.unloadables) {
911 		eprintf("You cannot combine --loadable with --unloadable");
912 	}
913 
914 	return;
915 }
916 
show_version(void)917 static void show_version(void)
918 {
919 	puts(PACKAGE " version " VERSION);
920 	puts("Compile-time switches: "
921 
922 #ifdef HAVE_LIBCURL
923 		"curl "
924 #endif
925 
926 #ifdef DEBUG
927 		"debug "
928 #endif
929 
930 #ifdef HAVE_LIBEXIF
931 		"exif "
932 #endif
933 
934 #ifdef HAVE_INOTIFY
935 		"inotify "
936 #endif
937 
938 #ifdef INCLUDE_HELP
939 		"help "
940 #endif
941 
942 #if _FILE_OFFSET_BITS == 64
943 		"stat64 "
944 #endif
945 
946 #ifdef HAVE_STRVERSCMP
947          "verscmp "
948 #endif
949 
950 #ifdef HAVE_LIBXINERAMA
951 		"xinerama "
952 #endif
953 
954 	);
955 
956 	exit(0);
957 }
958 
show_mini_usage(void)959 void show_mini_usage(void)
960 {
961 	fputs(PACKAGE ": No loadable images specified.\n"
962 #ifdef INCLUDE_HELP
963 		"See '" PACKAGE " --help' or 'man " PACKAGE "' for detailed usage information\n",
964 #else
965 		"See 'man " PACKAGE "' for detailed usage information\n",
966 #endif
967 		stderr);
968 	exit(1);
969 }
970 
show_usage(void)971 static void show_usage(void)
972 {
973 	fputs(
974 #ifdef INCLUDE_HELP
975 #include "help.inc"
976 #else
977 	"See 'man " PACKAGE "'\n"
978 #endif
979 	, stdout);
980 	exit(0);
981 }
982