Move save/load_options() to options.[ch].
[geeqie.git] / src / rcfile.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include <glib/gstdio.h>
14 #include <errno.h>
15
16 #include "main.h"
17 #include "rcfile.h"
18
19 #include "bar_exif.h"
20 #include "editors.h"
21 #include "filefilter.h"
22 #include "pixbuf-renderer.h"
23 #include "secure_save.h"
24 #include "slideshow.h"
25 #include "ui_fileops.h"
26
27
28 /*
29  *-----------------------------------------------------------------------------
30  * line write/parse routines (private)
31  *-----------------------------------------------------------------------------
32  */
33
34 /*
35    returns text without quotes or NULL for empty or broken string
36    any text up to first '"' is skipped
37    tail is set to point at the char after the second '"'
38    or at the ending \0
39
40 */
41
42 gchar *quoted_value(const gchar *text, const gchar **tail)
43 {
44         const gchar *ptr;
45         gint c = 0;
46         gint l = strlen(text);
47         gchar *retval = NULL;
48
49         if (tail) *tail = text;
50
51         if (l == 0) return retval;
52
53         while (c < l && text[c] != '"') c++;
54         if (text[c] == '"')
55                 {
56                 gint e;
57                 c++;
58                 ptr = text + c;
59                 e = c;
60                 while (e < l)
61                         {
62                         if (text[e-1] != '\\' && text[e] == '"') break;
63                         e++;
64                         }
65                 if (text[e] == '"')
66                         {
67                         if (e - c > 0)
68                                 {
69                                 gchar *substring = g_strndup(ptr, e - c);
70
71                                 if (substring)
72                                         {
73                                         retval = g_strcompress(substring);
74                                         g_free(substring);
75                                         }
76                                 }
77                         }
78                 if (tail) *tail = text + e + 1;
79                 }
80         else
81                 /* for compatibility with older formats (<0.3.7)
82                  * read a line without quotes too */
83                 {
84                 c = 0;
85                 while (c < l && text[c] != '\n' && !g_ascii_isspace(text[c])) c++;
86                 if (c != 0)
87                         {
88                         retval = g_strndup(text, c);
89                         }
90                 if (tail) *tail = text + c;
91                 }
92
93         return retval;
94 }
95
96 gchar *escquote_value(const gchar *text)
97 {
98         gchar *e;
99
100         if (!text) return g_strdup("\"\"");
101
102         e = g_strescape(text, "");
103         if (e)
104                 {
105                 gchar *retval = g_strdup_printf("\"%s\"", e);
106                 g_free(e);
107                 return retval;
108                 }
109         return g_strdup("\"\"");
110 }
111
112 static void write_char_option(SecureSaveInfo *ssi, gchar *label, gchar *text)
113 {
114         gchar *escval = escquote_value(text);
115
116         secure_fprintf(ssi, "%s: %s\n", label, escval);
117         g_free(escval);
118 }
119
120 static gboolean read_char_option(FILE *f, gchar *option, gchar *label, gchar *value, gchar **text)
121 {
122         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
123         if (!text) return FALSE;
124
125         g_free(*text);
126         *text = quoted_value(value, NULL);
127         return TRUE;
128 }
129
130 /* Since gdk_color_to_string() is only available since gtk 2.12
131  * here is an equivalent stub function. */
132 static gchar *color_to_string(GdkColor *color)
133 {
134         return g_strdup_printf("#%04X%04X%04X", color->red, color->green, color->blue);
135 }
136
137 static void write_color_option(SecureSaveInfo *ssi, gchar *label, GdkColor *color)
138 {
139         if (color)
140                 {
141                 gchar *colorstring = color_to_string(color);
142
143                 write_char_option(ssi, label, colorstring);
144                 g_free(colorstring);
145                 }
146         else
147                 secure_fprintf(ssi, "%s: \n", label);
148 }
149
150 static gboolean read_color_option(FILE *f, gchar *option, gchar *label, gchar *value, GdkColor *color)
151 {
152         gchar *colorstr;
153         
154         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
155         if (!color) return FALSE;
156
157         colorstr = quoted_value(value, NULL);
158         if (!colorstr) return FALSE;
159         gdk_color_parse(colorstr, color);
160         g_free(colorstr);
161         return TRUE;
162 }
163
164 static void write_int_option(SecureSaveInfo *ssi, gchar *label, gint n)
165 {
166         secure_fprintf(ssi, "%s: %d\n", label, n);
167 }
168
169 static gboolean read_int_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n)
170 {
171         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
172         if (!n) return FALSE;
173
174         if (g_ascii_isdigit(value[0]) || (value[0] == '-' && g_ascii_isdigit(value[1])))
175                 {
176                 *n = strtol(value, NULL, 10);
177                 }
178         else
179                 {
180                 if (g_ascii_strcasecmp(value, "true") == 0)
181                         *n = 1;
182                 else
183                         *n = 0;
184                 }
185
186         return TRUE;
187 }
188
189 static void write_uint_option(SecureSaveInfo *ssi, gchar *label, guint n)
190 {
191         secure_fprintf(ssi, "%s: %u\n", label, n);
192 }
193
194 static gboolean read_uint_option(FILE *f, gchar *option, gchar *label, gchar *value, guint *n)
195 {
196         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
197         if (!n) return FALSE;
198
199         if (g_ascii_isdigit(value[0]))
200                 {
201                 *n = strtoul(value, NULL, 10);
202                 }
203         else
204                 {
205                 if (g_ascii_strcasecmp(value, "true") == 0)
206                         *n = 1;
207                 else
208                         *n = 0;
209                 }
210         
211         return TRUE;
212 }
213
214 static gboolean read_uint_option_clamp(FILE *f, gchar *option, gchar *label, gchar *value, guint *n, guint min, guint max)
215 {
216         gboolean ret;
217
218         ret = read_uint_option(f, option, label, value, n);
219         if (ret) *n = CLAMP(*n, min, max);
220
221         return ret;
222 }
223
224
225 static gboolean read_int_option_clamp(FILE *f, gchar *option, gchar *label, gchar *value, gint *n, gint min, gint max)
226 {
227         gboolean ret;
228
229         ret = read_int_option(f, option, label, value, n);
230         if (ret) *n = CLAMP(*n, min, max);
231
232         return ret;
233 }
234
235 static void write_int_unit_option(SecureSaveInfo *ssi, gchar *label, gint n, gint subunits)
236 {
237         gint l, r;
238
239         if (subunits > 0)
240                 {
241                 l = n / subunits;
242                 r = n % subunits;
243                 }
244         else
245                 {
246                 l = n;
247                 r = 0;
248                 }
249
250         secure_fprintf(ssi, "%s: %d.%d\n", label, l, r);
251 }
252
253 static gboolean read_int_unit_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n, gint subunits)
254 {
255         gint l, r;
256         gchar *ptr;
257
258         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
259         if (!n) return FALSE;
260
261         ptr = value;
262         while (*ptr != '\0' && *ptr != '.') ptr++;
263         if (*ptr == '.')
264                 {
265                 *ptr = '\0';
266                 l = strtol(value, NULL, 10);
267                 *ptr = '.';
268                 ptr++;
269                 r = strtol(ptr, NULL, 10);
270                 }
271         else
272                 {
273                 l = strtol(value, NULL, 10);
274                 r = 0;
275                 }
276
277         *n = l * subunits + r;
278
279         return TRUE;
280 }
281
282 static void write_bool_option(SecureSaveInfo *ssi, gchar *label, gint n)
283 {
284         secure_fprintf(ssi, "%s: ", label);
285         if (n) secure_fprintf(ssi, "true\n"); else secure_fprintf(ssi, "false\n");
286 }
287
288 static gboolean read_bool_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n)
289 {
290         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
291         if (!n) return FALSE;
292
293         if (g_ascii_strcasecmp(value, "true") == 0 || atoi(value) != 0)
294                 *n = TRUE;
295         else
296                 *n = FALSE;
297
298         return TRUE;
299 }
300
301
302 /*
303  *-----------------------------------------------------------------------------
304  * save configuration (public)
305  *-----------------------------------------------------------------------------
306  */
307
308 gboolean save_options_to(const gchar *utf8_path, ConfOptions *options)
309 {
310         SecureSaveInfo *ssi;
311         gchar *rc_pathl;
312         gint i;
313
314         rc_pathl = path_from_utf8(utf8_path);
315         ssi = secure_open(rc_pathl);
316         g_free(rc_pathl);
317         if (!ssi)
318                 {
319                 log_printf(_("error saving config file: %s\n"), utf8_path);
320                 return FALSE;
321                 }
322
323 #define WRITE_BOOL(_name_) write_bool_option(ssi, #_name_, options->_name_)
324 #define WRITE_INT(_name_) write_int_option(ssi, #_name_, options->_name_)
325 #define WRITE_UINT(_name_) write_uint_option(ssi, #_name_, options->_name_)
326 #define WRITE_INT_UNIT(_name_, _unit_) write_int_unit_option(ssi, #_name_, options->_name_, _unit_)
327 #define WRITE_CHAR(_name_) write_char_option(ssi, #_name_, options->_name_)
328 #define WRITE_COLOR(_name_) write_color_option(ssi, #_name_, &options->_name_)
329
330 #define WRITE_SEPARATOR() secure_fputc(ssi, '\n')
331 #define WRITE_SUBTITLE(_title_) secure_fprintf(ssi, "\n\n##### "_title_" #####\n\n")
332
333         secure_fprintf(ssi, "######################################################################\n");
334         secure_fprintf(ssi, "# %30s config file      version %-10s #\n", GQ_APPNAME, VERSION);
335         secure_fprintf(ssi, "######################################################################\n");
336         WRITE_SEPARATOR();
337
338         secure_fprintf(ssi, "# Note: This file is autogenerated. Options can be changed here,\n");
339         secure_fprintf(ssi, "#       but user comments and formatting will be lost.\n");
340         WRITE_SEPARATOR();
341
342         WRITE_SUBTITLE("General Options");
343
344         WRITE_BOOL(show_icon_names);
345         WRITE_BOOL(show_copy_path);
346         WRITE_SEPARATOR();
347
348         WRITE_BOOL(tree_descend_subdirs);
349         WRITE_BOOL(lazy_image_sync);
350         WRITE_BOOL(update_on_time_change);
351         WRITE_SEPARATOR();
352
353         WRITE_BOOL(progressive_key_scrolling);
354         WRITE_BOOL(enable_metadata_dirs);
355         WRITE_BOOL(save_metadata_in_image_file);
356
357         WRITE_UINT(duplicates_similarity_threshold);
358         WRITE_SEPARATOR();
359
360         WRITE_BOOL(mousewheel_scrolls);
361         WRITE_INT(open_recent_list_maxsize);
362         WRITE_INT(dnd_icon_size);
363         WRITE_BOOL(place_dialogs_under_mouse);
364
365
366         WRITE_SUBTITLE("Startup Options");
367
368         WRITE_BOOL(startup.restore_path);
369         WRITE_BOOL(startup.use_last_path);
370         WRITE_CHAR(startup.path);
371
372
373         WRITE_SUBTITLE("File operations Options");
374
375         WRITE_BOOL(file_ops.enable_in_place_rename);
376         WRITE_BOOL(file_ops.confirm_delete);
377         WRITE_BOOL(file_ops.enable_delete_key);
378         WRITE_BOOL(file_ops.safe_delete_enable);
379         WRITE_CHAR(file_ops.safe_delete_path);
380         WRITE_INT(file_ops.safe_delete_folder_maxsize);
381
382
383         WRITE_SUBTITLE("Layout Options");
384
385         WRITE_INT(layout.style);
386         WRITE_CHAR(layout.order);
387         WRITE_UINT(layout.dir_view_type);
388         WRITE_UINT(layout.file_view_type);
389         WRITE_BOOL(layout.show_marks);
390         WRITE_BOOL(layout.show_thumbnails);
391         WRITE_BOOL(layout.show_directory_date);
392         WRITE_CHAR(layout.home_path);
393         WRITE_SEPARATOR();
394
395         WRITE_BOOL(layout.save_window_positions);
396         WRITE_SEPARATOR();
397
398         WRITE_INT(layout.main_window.x);
399         WRITE_INT(layout.main_window.y);
400         WRITE_INT(layout.main_window.w);
401         WRITE_INT(layout.main_window.h);
402         WRITE_BOOL(layout.main_window.maximized);
403         WRITE_INT(layout.main_window.hdivider_pos);
404         WRITE_INT(layout.main_window.vdivider_pos);
405         WRITE_SEPARATOR();
406
407         WRITE_INT(layout.float_window.x);
408         WRITE_INT(layout.float_window.y);
409         WRITE_INT(layout.float_window.w);
410         WRITE_INT(layout.float_window.h);
411         WRITE_INT(layout.float_window.vdivider_pos);
412         WRITE_SEPARATOR();
413
414         WRITE_INT(layout.properties_window.w);
415         WRITE_INT(layout.properties_window.h);
416         WRITE_SEPARATOR();
417
418         WRITE_BOOL(layout.tools_float);
419         WRITE_BOOL(layout.tools_hidden);
420         WRITE_BOOL(layout.tools_restore_state);
421         WRITE_SEPARATOR();
422
423         WRITE_BOOL(layout.toolbar_hidden);
424
425         WRITE_SUBTITLE("Panels Options");
426
427         WRITE_BOOL(panels.exif.enabled);
428         WRITE_INT(panels.exif.width);
429         WRITE_BOOL(panels.info.enabled);
430         WRITE_INT(panels.info.width);
431         WRITE_BOOL(panels.sort.enabled);
432         WRITE_INT(panels.sort.action_state);
433         WRITE_INT(panels.sort.mode_state);
434         WRITE_INT(panels.sort.selection_state);
435
436         WRITE_SUBTITLE("Properties dialog Options");
437         WRITE_CHAR(properties.tabs_order);
438
439         WRITE_SUBTITLE("Image Options");
440
441         secure_fprintf(ssi, "# image.zoom_mode possible values are:\n"
442                             "#   original\n"
443                             "#   fit\n"
444                             "#   dont_change\n");
445         secure_fprintf(ssi, "image.zoom_mode: ");
446         switch (options->image.zoom_mode)
447         {
448         case ZOOM_RESET_ORIGINAL: secure_fprintf(ssi, "original\n"); break;
449         case ZOOM_RESET_FIT_WINDOW: secure_fprintf(ssi, "fit\n"); break;
450         case ZOOM_RESET_NONE: secure_fprintf(ssi, "dont_change\n"); break;
451         }
452         WRITE_SEPARATOR();
453         WRITE_BOOL(image.zoom_2pass);
454         WRITE_BOOL(image.zoom_to_fit_allow_expand);
455         WRITE_UINT(image.zoom_quality);
456         WRITE_INT(image.zoom_increment);
457         WRITE_BOOL(image.fit_window_to_image);
458         WRITE_BOOL(image.limit_window_size);
459         WRITE_INT(image.max_window_size);
460         WRITE_BOOL(image.limit_autofit_size);
461         WRITE_INT(image.max_autofit_size);
462         WRITE_UINT(image.scroll_reset_method);
463         WRITE_INT(image.tile_cache_max);
464         WRITE_INT(image.image_cache_max);
465         WRITE_UINT(image.dither_quality);
466         WRITE_BOOL(image.enable_read_ahead);
467         WRITE_BOOL(image.exif_rotate_enable);
468         WRITE_BOOL(image.use_custom_border_color);
469         WRITE_COLOR(image.border_color);
470         WRITE_INT(image.read_buffer_size);
471         WRITE_INT(image.idle_read_loop_count);
472
473         WRITE_SUBTITLE("Thumbnails Options");
474
475         WRITE_INT(thumbnails.max_width);
476         WRITE_INT(thumbnails.max_height);
477         WRITE_BOOL(thumbnails.enable_caching);
478         WRITE_BOOL(thumbnails.cache_into_dirs);
479         WRITE_BOOL(thumbnails.fast);
480         WRITE_BOOL(thumbnails.use_xvpics);
481         WRITE_BOOL(thumbnails.spec_standard);
482         WRITE_UINT(thumbnails.quality);
483
484
485         WRITE_SUBTITLE("File sorting Options");
486
487         WRITE_INT(file_sort.method);
488         WRITE_BOOL(file_sort.ascending);
489         WRITE_BOOL(file_sort.case_sensitive);
490
491
492         WRITE_SUBTITLE("Fullscreen Options");
493
494         WRITE_INT(fullscreen.screen);
495         WRITE_BOOL(fullscreen.clean_flip);
496         WRITE_BOOL(fullscreen.disable_saver);
497         WRITE_BOOL(fullscreen.above);
498
499
500         WRITE_SUBTITLE("Histogram Options");
501         WRITE_UINT(histogram.last_channel_mode);
502         WRITE_UINT(histogram.last_log_mode);
503
504
505         WRITE_SUBTITLE("Image Overlay Options");
506         WRITE_UINT(image_overlay.common.state);
507         WRITE_BOOL(image_overlay.common.show_at_startup);
508         WRITE_CHAR(image_overlay.common.template_string);
509         WRITE_SEPARATOR();
510
511         secure_fprintf(ssi, "# these are relative positions:\n");
512         secure_fprintf(ssi, "# x >= 0: |x| pixels from left border\n");
513         secure_fprintf(ssi, "# x < 0 : |x| pixels from right border\n");
514         secure_fprintf(ssi, "# y >= 0: |y| pixels from top border\n");
515         secure_fprintf(ssi, "# y < 0 : |y| pixels from bottom border\n");
516         WRITE_INT(image_overlay.common.x);
517         WRITE_INT(image_overlay.common.y);
518
519
520         WRITE_SUBTITLE("Slideshow Options");
521
522         WRITE_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
523         WRITE_BOOL(slideshow.random);
524         WRITE_BOOL(slideshow.repeat);
525
526
527         WRITE_SUBTITLE("Collection Options");
528
529         WRITE_BOOL(collections.rectangular_selection);
530
531
532         WRITE_SUBTITLE("Filtering Options");
533
534         WRITE_BOOL(file_filter.show_hidden_files);
535         WRITE_BOOL(file_filter.show_dot_directory);
536         WRITE_BOOL(file_filter.disable);
537         WRITE_SEPARATOR();
538
539         filter_write_list(ssi);
540
541
542         WRITE_SUBTITLE("Sidecars Options");
543
544         sidecar_ext_write(ssi);
545
546
547         WRITE_SUBTITLE("Color Profiles");
548
549 #ifndef HAVE_LCMS
550         secure_fprintf(ssi, "# NOTICE: %s was not built with support for color profiles,\n"
551                             "#         color profile options will have no effect.\n\n", GQ_APPNAME);
552 #endif
553
554         WRITE_BOOL(color_profile.enabled);
555         WRITE_BOOL(color_profile.use_image);
556         WRITE_INT(color_profile.input_type);
557         WRITE_SEPARATOR();
558
559         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
560                 {
561                 gchar *buf;
562
563                 buf = g_strdup_printf("color_profile.input_file_%d", i + 1);
564                 write_char_option(ssi, buf, options->color_profile.input_file[i]);
565                 g_free(buf);
566
567                 buf = g_strdup_printf("color_profile.input_name_%d", i + 1);
568                 write_char_option(ssi, buf, options->color_profile.input_name[i]);
569                 g_free(buf);
570                 }
571
572         WRITE_SEPARATOR();
573         WRITE_INT(color_profile.screen_type);
574         WRITE_CHAR(color_profile.screen_file);
575
576
577         WRITE_SUBTITLE("Shell command");
578         WRITE_CHAR(shell.path);
579         WRITE_CHAR(shell.options);
580
581
582         WRITE_SUBTITLE("Helpers");
583         secure_fprintf(ssi, "# Html browser\n");
584         secure_fprintf(ssi, "# command_name is: the binary's name to look for in the path\n");
585         secure_fprintf(ssi, "# If command_name is empty, the program will try various common html browsers\n");
586         secure_fprintf(ssi, "# command_line is:\n");
587         secure_fprintf(ssi, "# \"\" (empty string)  = execute binary with html file path as command line\n");
588         secure_fprintf(ssi, "# \"string\"           = execute string and use results for command line\n");
589         secure_fprintf(ssi, "# \"!string\"          = use text following ! as command line, replacing optional %%s with html file path\n");
590         WRITE_CHAR(helpers.html_browser.command_name);
591         WRITE_CHAR(helpers.html_browser.command_line);
592
593
594         WRITE_SUBTITLE("External Programs");
595         secure_fprintf(ssi, "# Maximum of %d programs (external_1 through external_%d)\n", GQ_EDITOR_GENERIC_SLOTS, GQ_EDITOR_GENERIC_SLOTS);
596         secure_fprintf(ssi, "# external_%d through external_%d are used for file ops\n", GQ_EDITOR_GENERIC_SLOTS + 1, GQ_EDITOR_SLOTS);
597         secure_fprintf(ssi, "# format: external_n: \"menu name\" \"command line\"\n\n");
598
599         for (i = 0; i < GQ_EDITOR_SLOTS; i++)
600                 {
601                 if (i == GQ_EDITOR_GENERIC_SLOTS) secure_fputc(ssi, '\n');
602                 gchar *qname = escquote_value(options->editor[i].name);
603                 gchar *qcommand = escquote_value(options->editor[i].command);
604                 secure_fprintf(ssi, "external_%d: %s %s\n", i+1, qname, qcommand);
605                 g_free(qname);
606                 g_free(qcommand);
607                 }
608
609
610         WRITE_SUBTITLE("Exif Options");
611         secure_fprintf(ssi, "# Display: 0: never\n"
612                             "#          1: if set\n"
613                             "#          2: always\n\n");
614         for (i = 0; ExifUIList[i].key; i++)
615                 {
616                 secure_fprintf(ssi, "exif.display.");
617                 write_int_option(ssi, (gchar *)ExifUIList[i].key, ExifUIList[i].current);
618                 }
619
620         WRITE_SUBTITLE("Documentation Options");
621         WRITE_CHAR(documentation.helpdir);
622         WRITE_CHAR(documentation.htmldir);
623
624         WRITE_SEPARATOR();
625         WRITE_SEPARATOR();
626
627         secure_fprintf(ssi, "######################################################################\n");
628         secure_fprintf(ssi, "#                         end of config file                         #\n");
629         secure_fprintf(ssi, "######################################################################\n");
630
631
632         if (secure_close(ssi))
633                 {
634                 log_printf(_("error saving config file: %s\nerror: %s\n"), utf8_path,
635                            secsave_strerror(secsave_errno));
636                 return FALSE;
637                 }
638
639         return TRUE;
640 }
641
642
643
644
645 /*
646  *-----------------------------------------------------------------------------
647  * load configuration (public)
648  *-----------------------------------------------------------------------------
649  */
650
651 static gboolean is_numbered_option(const gchar *option, const gchar *prefix, gint *number)
652 {
653         gsize n;
654         gsize option_len = strlen(option);
655         gsize prefix_len = strlen(prefix);
656         
657         if (option_len <= prefix_len) return FALSE;
658         if (g_ascii_strncasecmp(option, prefix, prefix_len) != 0) return FALSE;
659
660         n = prefix_len;
661         while (g_ascii_isdigit(option[n])) n++;
662         if (n < option_len) return FALSE;
663         
664         if (number) *number = atoi(option + prefix_len);
665         return TRUE;
666 }
667
668 #define OPTION_READ_BUFFER_SIZE 1024
669
670 gboolean load_options_from(const gchar *utf8_path, ConfOptions *options)
671 {
672         FILE *f;
673         gchar *rc_pathl;
674         gchar s_buf[OPTION_READ_BUFFER_SIZE];
675         gchar value_all[OPTION_READ_BUFFER_SIZE];
676         gchar *option;
677         gchar *value;
678         gint i;
679
680         rc_pathl = path_from_utf8(utf8_path);
681         f = fopen(rc_pathl,"r");
682         g_free(rc_pathl);
683         if (!f) return FALSE;
684
685         while (fgets(s_buf, sizeof(s_buf), f))
686                 {
687                 gchar *value_end;
688                 gchar *p = s_buf;
689
690                 /* skip empty lines and comments */
691                 while (g_ascii_isspace(*p)) p++;
692                 if (!*p || *p == '\n' || *p == '#') continue;
693
694                 /* parse option name */
695                 option = p;
696                 while (g_ascii_isalnum(*p) || *p == '_' || *p == '.') p++;
697                 if (!*p) continue;
698                 *p = '\0';
699                 p++;
700
701                 /* search for value start, name and value are normally separated by ': '
702                  * but we allow relaxed syntax here, so '=', ':=' or just a tab will work too */
703                 while (*p == ':' || g_ascii_isspace(*p) || *p == '=') p++;
704                 value = p;
705
706                 while (*p && !g_ascii_isspace(*p) && *p != '\n') p++;
707                 value_end = p; /* value part up to the first whitespace or end of line */
708                 while (*p != '\0') p++;
709                 memcpy(value_all, value, 1 + p - value);
710
711                 *value_end = '\0';
712
713 #define READ_BOOL(_name_) if (read_bool_option(f, option, #_name_, value, &options->_name_)) continue;
714 #define READ_INT(_name_) if (read_int_option(f, option, #_name_, value, &options->_name_)) continue;
715 #define READ_UINT(_name_) if (read_uint_option(f, option, #_name_, value, &options->_name_)) continue;
716 #define READ_INT_CLAMP(_name_, _min_, _max_) if (read_int_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
717 #define READ_UINT_CLAMP(_name_, _min_, _max_) if (read_uint_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
718 #define READ_INT_UNIT(_name_, _unit_) if (read_int_unit_option(f, option, #_name_, value, &options->_name_, _unit_)) continue;
719 #define READ_CHAR(_name_) if (read_char_option(f, option, #_name_, value_all, &options->_name_)) continue;
720 #define READ_COLOR(_name_) if (read_color_option(f, option, #_name_, value, &options->_name_)) continue;
721
722 #define COMPAT_READ_BOOL(_oldname_, _name_) if (read_bool_option(f, option, #_oldname_, value, &options->_name_)) continue;
723 #define COMPAT_READ_INT(_oldname_, _name_) if (read_int_option(f, option, #_oldname_, value, &options->_name_)) continue;
724 #define COMPAT_READ_UINT(_oldname_, _name_) if (read_uint_option(f, option, #_oldname_, value, &options->_name_)) continue;
725 #define COMPAT_READ_INT_CLAMP(_oldname_, _name_, _min_, _max_) if (read_int_option_clamp(f, option, #_oldname_, value, &options->_name_, _min_, _max_)) continue;
726 #define COMPAT_READ_INT_UNIT(_oldname_, _name_, _unit_) if (read_int_unit_option(f, option, #_oldname_, value, &options->_name_, _unit_)) continue;
727 #define COMPAT_READ_CHAR(_oldname_, _name_) if (read_char_option(f, option, #_oldname_, value_all, &options->_name_)) continue;
728 #define COMPAT_READ_COLOR(_oldname_, _name_) if (read_color_option(f, option, #_oldname_, value, &options->_name_)) continue;
729
730                 /* general options */
731                 READ_BOOL(show_icon_names);
732                 READ_BOOL(show_copy_path);
733
734                 READ_BOOL(tree_descend_subdirs);
735                 READ_BOOL(lazy_image_sync);
736                 READ_BOOL(update_on_time_change);
737
738                 READ_UINT_CLAMP(duplicates_similarity_threshold, 0, 100);
739
740                 READ_BOOL(progressive_key_scrolling);
741
742                 READ_BOOL(enable_metadata_dirs);
743                 READ_BOOL(save_metadata_in_image_file);
744
745                 READ_BOOL(mousewheel_scrolls);
746
747                 READ_INT(open_recent_list_maxsize);
748                 READ_INT(dnd_icon_size);
749                 READ_BOOL(place_dialogs_under_mouse);
750
751                 /* startup options */
752                 
753                 COMPAT_READ_BOOL(startup_path_enable, startup.restore_path); /* 2008/05/11 */
754                 READ_BOOL(startup.restore_path);
755
756                 READ_BOOL(startup.use_last_path);
757
758                 COMPAT_READ_CHAR(startup_path, startup.path); /* 2008/05/11 */
759                 READ_CHAR(startup.path);
760         
761                 /* layout options */
762
763                 READ_INT(layout.style);
764                 READ_CHAR(layout.order);
765                 
766                 COMPAT_READ_UINT(layout.view_as_icons, layout.file_view_type); /* 2008/05/03 */
767
768                 READ_UINT(layout.dir_view_type);
769                 READ_UINT(layout.file_view_type);
770                 READ_BOOL(layout.show_marks);
771                 READ_BOOL(layout.show_thumbnails);
772                 READ_BOOL(layout.show_directory_date);
773                 READ_CHAR(layout.home_path);
774
775                 /* window positions */
776
777                 READ_BOOL(layout.save_window_positions);
778
779                 READ_INT(layout.main_window.x);
780                 READ_INT(layout.main_window.y);
781                 READ_INT(layout.main_window.w);
782                 READ_INT(layout.main_window.h);
783                 READ_BOOL(layout.main_window.maximized);
784                 READ_INT(layout.main_window.hdivider_pos);
785                 READ_INT(layout.main_window.vdivider_pos);
786
787                 READ_INT(layout.float_window.x);
788                 READ_INT(layout.float_window.y);
789                 READ_INT(layout.float_window.w);
790                 READ_INT(layout.float_window.h);
791                 READ_INT(layout.float_window.vdivider_pos);
792         
793                 READ_INT(layout.properties_window.w);
794                 READ_INT(layout.properties_window.h);
795
796                 READ_BOOL(layout.tools_float);
797                 READ_BOOL(layout.tools_hidden);
798                 READ_BOOL(layout.tools_restore_state);
799                 READ_BOOL(layout.toolbar_hidden);
800
801                 /* panels */
802                 READ_BOOL(panels.exif.enabled);
803                 READ_INT_CLAMP(panels.exif.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
804                 READ_BOOL(panels.info.enabled);
805                 READ_INT_CLAMP(panels.info.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
806                 READ_BOOL(panels.sort.enabled);
807                 READ_INT(panels.sort.action_state);
808                 READ_INT(panels.sort.mode_state);
809                 READ_INT(panels.sort.selection_state);
810
811                 /* properties dialog options */
812                 READ_CHAR(properties.tabs_order);
813
814                 /* image options */
815                 if (g_ascii_strcasecmp(option, "image.zoom_mode") == 0)
816                         {
817                         if (g_ascii_strcasecmp(value, "original") == 0)
818                                 options->image.zoom_mode = ZOOM_RESET_ORIGINAL;
819                         else if (g_ascii_strcasecmp(value, "fit") == 0)
820                                 options->image.zoom_mode = ZOOM_RESET_FIT_WINDOW;
821                         else if (g_ascii_strcasecmp(value, "dont_change") == 0)
822                                 options->image.zoom_mode = ZOOM_RESET_NONE;
823                         continue;
824                         }
825                 READ_BOOL(image.zoom_2pass);
826                 READ_BOOL(image.zoom_to_fit_allow_expand);
827                 READ_BOOL(image.fit_window_to_image);
828                 READ_BOOL(image.limit_window_size);
829                 READ_INT(image.max_window_size);
830                 READ_BOOL(image.limit_autofit_size);
831                 READ_INT(image.max_autofit_size);
832                 READ_UINT_CLAMP(image.scroll_reset_method, 0, PR_SCROLL_RESET_COUNT - 1);
833                 READ_INT(image.tile_cache_max);
834                 READ_INT(image.image_cache_max);
835                 READ_UINT_CLAMP(image.zoom_quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
836                 READ_UINT_CLAMP(image.dither_quality, GDK_RGB_DITHER_NONE, GDK_RGB_DITHER_MAX);
837                 READ_INT(image.zoom_increment);
838                 READ_BOOL(image.enable_read_ahead);
839                 READ_BOOL(image.exif_rotate_enable);
840                 READ_BOOL(image.use_custom_border_color);
841                 READ_COLOR(image.border_color);
842                 READ_INT_CLAMP(image.read_buffer_size, IMAGE_LOADER_READ_BUFFER_SIZE_MIN, IMAGE_LOADER_READ_BUFFER_SIZE_MAX);
843                 READ_INT_CLAMP(image.idle_read_loop_count, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MIN, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MAX);
844
845
846                 /* thumbnails options */
847                 READ_INT_CLAMP(thumbnails.max_width, 16, 512);
848                 READ_INT_CLAMP(thumbnails.max_height, 16, 512);
849
850                 READ_BOOL(thumbnails.enable_caching);
851                 READ_BOOL(thumbnails.cache_into_dirs);
852                 READ_BOOL(thumbnails.fast);
853                 READ_BOOL(thumbnails.use_xvpics);
854                 READ_BOOL(thumbnails.spec_standard);
855                 READ_UINT_CLAMP(thumbnails.quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
856
857                 /* file sorting options */
858                 READ_UINT(file_sort.method);
859                 READ_BOOL(file_sort.ascending);
860                 READ_BOOL(file_sort.case_sensitive);
861
862                 /* file operations options */
863                 READ_BOOL(file_ops.enable_in_place_rename);
864                 READ_BOOL(file_ops.confirm_delete);
865                 READ_BOOL(file_ops.enable_delete_key);
866                 READ_BOOL(file_ops.safe_delete_enable);
867                 READ_CHAR(file_ops.safe_delete_path);
868                 READ_INT(file_ops.safe_delete_folder_maxsize);
869
870                 /* fullscreen options */
871                 READ_INT(fullscreen.screen);
872                 READ_BOOL(fullscreen.clean_flip);
873                 READ_BOOL(fullscreen.disable_saver);
874                 READ_BOOL(fullscreen.above);
875
876                 /* histogram */
877                 READ_UINT(histogram.last_channel_mode);
878                 READ_UINT(histogram.last_log_mode);
879
880                 /* image overlay */
881                 COMPAT_READ_UINT(image_overlay.common.enabled, image_overlay.common.state); /* 2008-05-12 */
882                 READ_UINT(image_overlay.common.state);
883                 COMPAT_READ_BOOL(fullscreen.show_info, image_overlay.common.show_at_startup); /* 2008-04-21 */
884                 READ_BOOL(image_overlay.common.show_at_startup);
885                 COMPAT_READ_CHAR(fullscreen.info, image_overlay.common.template_string); /* 2008-04-21 */
886                 READ_CHAR(image_overlay.common.template_string);
887
888                 READ_INT(image_overlay.common.x);
889                 READ_INT(image_overlay.common.y);
890
891
892                 /* slideshow options */
893                 READ_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
894                 READ_BOOL(slideshow.random);
895                 READ_BOOL(slideshow.repeat);
896
897                 /* collection options */
898
899                 READ_BOOL(collections.rectangular_selection);
900
901                 /* filtering options */
902
903                 READ_BOOL(file_filter.show_hidden_files);
904                 READ_BOOL(file_filter.show_dot_directory);
905                 READ_BOOL(file_filter.disable);
906
907                 if (g_ascii_strcasecmp(option, "file_filter.ext") == 0)
908                         {
909                         filter_parse(value_all);
910                         continue;
911                         }
912
913                 if (g_ascii_strcasecmp(option, "sidecar.ext") == 0)
914                         {
915                         sidecar_ext_parse(value_all, TRUE);
916                         continue;
917                         }
918
919                 /* Color Profiles */
920
921                 READ_BOOL(color_profile.enabled);
922                 READ_BOOL(color_profile.use_image);
923                 READ_INT(color_profile.input_type);
924
925                 if (is_numbered_option(option, "color_profile.input_file_", &i))
926                         {
927                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
928                                 {
929                                 i--;
930                                 read_char_option(f, option, option, value, &options->color_profile.input_file[i]);
931                                 }
932                         continue;
933                         }
934
935                 if (is_numbered_option(option, "color_profile.input_name_", &i))
936                         {
937                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
938                                 {
939                                 i--;
940                                 read_char_option(f, option, option, value, &options->color_profile.input_name[i]);
941                                 }
942                         continue;
943                         }
944
945                 READ_INT(color_profile.screen_type);
946                 READ_CHAR(color_profile.screen_file);
947
948                 /* Shell command */
949                 READ_CHAR(shell.path);
950                 READ_CHAR(shell.options);
951
952                 /* Helpers */
953                 READ_CHAR(helpers.html_browser.command_name);
954                 READ_CHAR(helpers.html_browser.command_line);
955
956                 /* External Programs */
957
958                 if (is_numbered_option(option, "external_", &i))
959                         {
960                         if (i > 0 && i <= GQ_EDITOR_SLOTS)
961                                 {
962                                 const gchar *ptr;
963
964                                 i--;
965                                 editor_set_name(i, quoted_value(value_all, &ptr));
966                                 editor_set_command(i, quoted_value(ptr, NULL));
967                                 }
968                         continue;
969                         }
970
971                 /* Exif */
972                 if (0 == g_ascii_strncasecmp(option, "exif.display.", 13))
973                         {
974                         for (i = 0; ExifUIList[i].key; i++)
975                                 if (0 == g_ascii_strcasecmp(option + 13, ExifUIList[i].key))
976                                         ExifUIList[i].current = strtol(value, NULL, 10);
977                         continue;
978                         }
979                 
980                 /* Documentation */
981                 READ_CHAR(documentation.helpdir);
982                 READ_CHAR(documentation.htmldir);
983                 
984                 }
985
986         fclose(f);
987         return TRUE;
988 }