Make better matching between options types in options.h and the rest of the code.
[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 static 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_SEPARATOR();
393
394         WRITE_BOOL(layout.save_window_positions);
395         WRITE_SEPARATOR();
396
397         WRITE_INT(layout.main_window.x);
398         WRITE_INT(layout.main_window.y);
399         WRITE_INT(layout.main_window.w);
400         WRITE_INT(layout.main_window.h);
401         WRITE_BOOL(layout.main_window.maximized);
402         WRITE_INT(layout.main_window.hdivider_pos);
403         WRITE_INT(layout.main_window.vdivider_pos);
404         WRITE_SEPARATOR();
405
406         WRITE_INT(layout.float_window.x);
407         WRITE_INT(layout.float_window.y);
408         WRITE_INT(layout.float_window.w);
409         WRITE_INT(layout.float_window.h);
410         WRITE_INT(layout.float_window.vdivider_pos);
411         WRITE_SEPARATOR();
412
413         WRITE_INT(layout.properties_window.w);
414         WRITE_INT(layout.properties_window.h);
415         WRITE_SEPARATOR();
416
417         WRITE_BOOL(layout.tools_float);
418         WRITE_BOOL(layout.tools_hidden);
419         WRITE_BOOL(layout.tools_restore_state);
420         WRITE_SEPARATOR();
421
422         WRITE_BOOL(layout.toolbar_hidden);
423
424         WRITE_SUBTITLE("Panels Options");
425
426         WRITE_BOOL(panels.exif.enabled);
427         WRITE_INT(panels.exif.width);
428         WRITE_BOOL(panels.info.enabled);
429         WRITE_INT(panels.info.width);
430         WRITE_BOOL(panels.sort.enabled);
431         WRITE_INT(panels.sort.action_state);
432         WRITE_INT(panels.sort.mode_state);
433         WRITE_INT(panels.sort.selection_state);
434
435         WRITE_SUBTITLE("Properties dialog Options");
436         WRITE_CHAR(properties.tabs_order);
437
438         WRITE_SUBTITLE("Image Options");
439
440         secure_fprintf(ssi, "# image.zoom_mode possible values are:\n"
441                             "#   original\n"
442                             "#   fit\n"
443                             "#   dont_change\n");
444         secure_fprintf(ssi, "image.zoom_mode: ");
445         if (options->image.zoom_mode == ZOOM_RESET_ORIGINAL)
446                 secure_fprintf(ssi, "original\n");
447         else if (options->image.zoom_mode == ZOOM_RESET_FIT_WINDOW)
448                 secure_fprintf(ssi, "fit\n");
449         else if (options->image.zoom_mode == ZOOM_RESET_NONE)
450                 secure_fprintf(ssi, "dont_change\n");
451         WRITE_SEPARATOR();
452         WRITE_BOOL(image.zoom_2pass);
453         WRITE_BOOL(image.zoom_to_fit_allow_expand);
454         WRITE_UINT(image.zoom_quality);
455         WRITE_INT(image.zoom_increment);
456         WRITE_BOOL(image.fit_window_to_image);
457         WRITE_BOOL(image.limit_window_size);
458         WRITE_INT(image.max_window_size);
459         WRITE_BOOL(image.limit_autofit_size);
460         WRITE_INT(image.max_autofit_size);
461         WRITE_UINT(image.scroll_reset_method);
462         WRITE_INT(image.tile_cache_max);
463         WRITE_INT(image.image_cache_max);
464         WRITE_UINT(image.dither_quality);
465         WRITE_BOOL(image.enable_read_ahead);
466         WRITE_BOOL(image.exif_rotate_enable);
467         WRITE_BOOL(image.use_custom_border_color);
468         WRITE_COLOR(image.border_color);
469         WRITE_INT(image.read_buffer_size);
470         WRITE_INT(image.idle_read_loop_count);
471
472         WRITE_SUBTITLE("Thumbnails Options");
473
474         WRITE_INT(thumbnails.max_width);
475         WRITE_INT(thumbnails.max_height);
476         WRITE_BOOL(thumbnails.enable_caching);
477         WRITE_BOOL(thumbnails.cache_into_dirs);
478         WRITE_BOOL(thumbnails.fast);
479         WRITE_BOOL(thumbnails.use_xvpics);
480         WRITE_BOOL(thumbnails.spec_standard);
481         WRITE_UINT(thumbnails.quality);
482
483
484         WRITE_SUBTITLE("File sorting Options");
485
486         WRITE_INT(file_sort.method);
487         WRITE_BOOL(file_sort.ascending);
488         WRITE_BOOL(file_sort.case_sensitive);
489
490
491         WRITE_SUBTITLE("Fullscreen Options");
492
493         WRITE_INT(fullscreen.screen);
494         WRITE_BOOL(fullscreen.clean_flip);
495         WRITE_BOOL(fullscreen.disable_saver);
496         WRITE_BOOL(fullscreen.above);
497
498
499         WRITE_SUBTITLE("Histogram Options");
500         WRITE_UINT(histogram.last_channel_mode);
501         WRITE_UINT(histogram.last_log_mode);
502
503
504         WRITE_SUBTITLE("Image Overlay Options");
505         WRITE_UINT(image_overlay.common.state);
506         WRITE_BOOL(image_overlay.common.show_at_startup);
507         WRITE_CHAR(image_overlay.common.template_string);
508         WRITE_SEPARATOR();
509
510         secure_fprintf(ssi, "# these are relative positions:\n");
511         secure_fprintf(ssi, "# x >= 0: |x| pixels from left border\n");
512         secure_fprintf(ssi, "# x < 0 : |x| pixels from right border\n");
513         secure_fprintf(ssi, "# y >= 0: |y| pixels from top border\n");
514         secure_fprintf(ssi, "# y < 0 : |y| pixels from bottom border\n");
515         WRITE_INT(image_overlay.common.x);
516         WRITE_INT(image_overlay.common.y);
517
518
519         WRITE_SUBTITLE("Slideshow Options");
520
521         WRITE_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
522         WRITE_BOOL(slideshow.random);
523         WRITE_BOOL(slideshow.repeat);
524
525
526         WRITE_SUBTITLE("Collection Options");
527
528         WRITE_BOOL(collections.rectangular_selection);
529
530
531         WRITE_SUBTITLE("Filtering Options");
532
533         WRITE_BOOL(file_filter.show_hidden_files);
534         WRITE_BOOL(file_filter.show_dot_directory);
535         WRITE_BOOL(file_filter.disable);
536         WRITE_SEPARATOR();
537
538         filter_write_list(ssi);
539
540
541         WRITE_SUBTITLE("Sidecars Options");
542
543         sidecar_ext_write(ssi);
544
545
546         WRITE_SUBTITLE("Color Profiles");
547
548 #ifndef HAVE_LCMS
549         secure_fprintf(ssi, "# NOTICE: %s was not built with support for color profiles,\n"
550                             "#         color profile options will have no effect.\n\n", GQ_APPNAME);
551 #endif
552
553         WRITE_BOOL(color_profile.enabled);
554         WRITE_BOOL(color_profile.use_image);
555         WRITE_INT(color_profile.input_type);
556         WRITE_SEPARATOR();
557
558         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
559                 {
560                 gchar *buf;
561
562                 buf = g_strdup_printf("color_profile.input_file_%d", i + 1);
563                 write_char_option(ssi, buf, options->color_profile.input_file[i]);
564                 g_free(buf);
565
566                 buf = g_strdup_printf("color_profile.input_name_%d", i + 1);
567                 write_char_option(ssi, buf, options->color_profile.input_name[i]);
568                 g_free(buf);
569                 }
570
571         WRITE_SEPARATOR();
572         WRITE_INT(color_profile.screen_type);
573         WRITE_CHAR(color_profile.screen_file);
574
575
576         WRITE_SUBTITLE("Shell command");
577         WRITE_CHAR(shell.path);
578         WRITE_CHAR(shell.options);
579
580
581         WRITE_SUBTITLE("External Programs");
582         secure_fprintf(ssi, "# Maximum of %d programs (external_1 through external_%d)\n", GQ_EDITOR_GENERIC_SLOTS, GQ_EDITOR_GENERIC_SLOTS);
583         secure_fprintf(ssi, "# external_%d through external_%d are used for file ops\n", GQ_EDITOR_GENERIC_SLOTS + 1, GQ_EDITOR_SLOTS);
584         secure_fprintf(ssi, "# format: external_n: \"menu name\" \"command line\"\n\n");
585
586         for (i = 0; i < GQ_EDITOR_SLOTS; i++)
587                 {
588                 if (i == GQ_EDITOR_GENERIC_SLOTS) secure_fputc(ssi, '\n');
589                 gchar *qname = escquote_value(options->editor[i].name);
590                 gchar *qcommand = escquote_value(options->editor[i].command);
591                 secure_fprintf(ssi, "external_%d: %s %s\n", i+1, qname, qcommand);
592                 g_free(qname);
593                 g_free(qcommand);
594                 }
595
596
597         WRITE_SUBTITLE("Exif Options");
598         secure_fprintf(ssi, "# Display: 0: never\n"
599                             "#          1: if set\n"
600                             "#          2: always\n\n");
601         for (i = 0; ExifUIList[i].key; i++)
602                 {
603                 secure_fprintf(ssi, "exif.display.");
604                 write_int_option(ssi, (gchar *)ExifUIList[i].key, ExifUIList[i].current);
605                 }
606
607         WRITE_SEPARATOR();
608         WRITE_SEPARATOR();
609
610         secure_fprintf(ssi, "######################################################################\n");
611         secure_fprintf(ssi, "#                         end of config file                         #\n");
612         secure_fprintf(ssi, "######################################################################\n");
613
614
615         if (secure_close(ssi))
616                 {
617                 log_printf(_("error saving config file: %s\nerror: %s\n"), utf8_path,
618                            secsave_strerror(secsave_errno));
619                 return FALSE;
620                 }
621
622         return TRUE;
623 }
624
625 void save_options(ConfOptions *options)
626 {
627         gchar *rc_path;
628
629         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
630         save_options_to(rc_path, options);
631         g_free(rc_path);
632 }
633
634
635
636 /*
637  *-----------------------------------------------------------------------------
638  * load configuration (public)
639  *-----------------------------------------------------------------------------
640  */
641
642 static gboolean is_numbered_option(const gchar *option, const gchar *prefix, gint *number)
643 {
644         gsize n;
645         gsize option_len = strlen(option);
646         gsize prefix_len = strlen(prefix);
647         
648         if (option_len <= prefix_len) return FALSE;
649         if (g_ascii_strncasecmp(option, prefix, prefix_len) != 0) return FALSE;
650
651         n = prefix_len;
652         while (g_ascii_isdigit(option[n])) n++;
653         if (n < option_len) return FALSE;
654         
655         if (number) *number = atoi(option + prefix_len);
656         return TRUE;
657 }
658
659 #define OPTION_READ_BUFFER_SIZE 1024
660
661 static gboolean load_options_from(const gchar *utf8_path, ConfOptions *options)
662 {
663         FILE *f;
664         gchar *rc_pathl;
665         gchar s_buf[OPTION_READ_BUFFER_SIZE];
666         gchar value_all[OPTION_READ_BUFFER_SIZE];
667         gchar *option;
668         gchar *value;
669         gint i;
670
671         rc_pathl = path_from_utf8(utf8_path);
672         f = fopen(rc_pathl,"r");
673         g_free(rc_pathl);
674         if (!f) return FALSE;
675
676         while (fgets(s_buf, sizeof(s_buf), f))
677                 {
678                 gchar *value_end;
679                 gchar *p = s_buf;
680
681                 /* skip empty lines and comments */
682                 while (g_ascii_isspace(*p)) p++;
683                 if (!*p || *p == '\n' || *p == '#') continue;
684
685                 /* parse option name */
686                 option = p;
687                 while (g_ascii_isalnum(*p) || *p == '_' || *p == '.') p++;
688                 if (!*p) continue;
689                 *p = '\0';
690                 p++;
691
692                 /* search for value start, name and value are normally separated by ': '
693                  * but we allow relaxed syntax here, so '=', ':=' or just a tab will work too */
694                 while (*p == ':' || g_ascii_isspace(*p) || *p == '=') p++;
695                 value = p;
696
697                 while (*p && !g_ascii_isspace(*p) && *p != '\n') p++;
698                 value_end = p; /* value part up to the first whitespace or end of line */
699                 while (*p != '\0') p++;
700                 memcpy(value_all, value, 1 + p - value);
701
702                 *value_end = '\0';
703
704 #define READ_BOOL(_name_) if (read_bool_option(f, option, #_name_, value, &options->_name_)) continue;
705 #define READ_INT(_name_) if (read_int_option(f, option, #_name_, value, &options->_name_)) continue;
706 #define READ_UINT(_name_) if (read_uint_option(f, option, #_name_, value, &options->_name_)) continue;
707 #define READ_INT_CLAMP(_name_, _min_, _max_) if (read_int_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
708 #define READ_UINT_CLAMP(_name_, _min_, _max_) if (read_uint_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
709 #define READ_INT_UNIT(_name_, _unit_) if (read_int_unit_option(f, option, #_name_, value, &options->_name_, _unit_)) continue;
710 #define READ_CHAR(_name_) if (read_char_option(f, option, #_name_, value_all, &options->_name_)) continue;
711 #define READ_COLOR(_name_) if (read_color_option(f, option, #_name_, value, &options->_name_)) continue;
712
713 #define COMPAT_READ_BOOL(_oldname_, _name_) if (read_bool_option(f, option, #_oldname_, value, &options->_name_)) continue;
714 #define COMPAT_READ_INT(_oldname_, _name_) if (read_int_option(f, option, #_oldname_, value, &options->_name_)) continue;
715 #define COMPAT_READ_UINT(_oldname_, _name_) if (read_uint_option(f, option, #_oldname_, value, &options->_name_)) continue;
716 #define COMPAT_READ_INT_CLAMP(_oldname_, _name_, _min_, _max_) if (read_int_option_clamp(f, option, #_oldname_, value, &options->_name_, _min_, _max_)) continue;
717 #define COMPAT_READ_INT_UNIT(_oldname_, _name_, _unit_) if (read_int_unit_option(f, option, #_oldname_, value, &options->_name_, _unit_)) continue;
718 #define COMPAT_READ_CHAR(_oldname_, _name_) if (read_char_option(f, option, #_oldname_, value_all, &options->_name_)) continue;
719 #define COMPAT_READ_COLOR(_oldname_, _name_) if (read_color_option(f, option, #_oldname_, value, &options->_name_)) continue;
720
721                 /* general options */
722                 READ_BOOL(show_icon_names);
723                 READ_BOOL(show_copy_path);
724
725                 READ_BOOL(tree_descend_subdirs);
726                 READ_BOOL(lazy_image_sync);
727                 READ_BOOL(update_on_time_change);
728
729                 READ_UINT_CLAMP(duplicates_similarity_threshold, 0, 100);
730
731                 READ_BOOL(progressive_key_scrolling);
732
733                 READ_BOOL(enable_metadata_dirs);
734                 READ_BOOL(save_metadata_in_image_file);
735
736                 READ_BOOL(mousewheel_scrolls);
737
738                 READ_INT(open_recent_list_maxsize);
739                 READ_INT(dnd_icon_size);
740                 READ_BOOL(place_dialogs_under_mouse);
741
742                 /* startup options */
743                 
744                 COMPAT_READ_BOOL(startup_path_enable, startup.restore_path); /* 2008/05/11 */
745                 READ_BOOL(startup.restore_path);
746
747                 READ_BOOL(startup.use_last_path);
748
749                 COMPAT_READ_CHAR(startup_path, startup.path); /* 2008/05/11 */
750                 READ_CHAR(startup.path);
751         
752                 /* layout options */
753
754                 READ_INT(layout.style);
755                 READ_CHAR(layout.order);
756                 
757                 COMPAT_READ_UINT(layout.view_as_icons, layout.file_view_type); /* 2008/05/03 */
758
759                 READ_UINT(layout.dir_view_type);
760                 READ_UINT(layout.file_view_type);
761                 READ_BOOL(layout.show_marks);
762                 READ_BOOL(layout.show_thumbnails);
763                 READ_BOOL(layout.show_directory_date);
764
765                 /* window positions */
766
767                 READ_BOOL(layout.save_window_positions);
768
769                 READ_INT(layout.main_window.x);
770                 READ_INT(layout.main_window.y);
771                 READ_INT(layout.main_window.w);
772                 READ_INT(layout.main_window.h);
773                 READ_BOOL(layout.main_window.maximized);
774                 READ_INT(layout.main_window.hdivider_pos);
775                 READ_INT(layout.main_window.vdivider_pos);
776
777                 READ_INT(layout.float_window.x);
778                 READ_INT(layout.float_window.y);
779                 READ_INT(layout.float_window.w);
780                 READ_INT(layout.float_window.h);
781                 READ_INT(layout.float_window.vdivider_pos);
782         
783                 READ_INT(layout.properties_window.w);
784                 READ_INT(layout.properties_window.h);
785
786                 READ_BOOL(layout.tools_float);
787                 READ_BOOL(layout.tools_hidden);
788                 READ_BOOL(layout.tools_restore_state);
789                 READ_BOOL(layout.toolbar_hidden);
790
791                 /* panels */
792                 READ_BOOL(panels.exif.enabled);
793                 READ_INT_CLAMP(panels.exif.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
794                 READ_BOOL(panels.info.enabled);
795                 READ_INT_CLAMP(panels.info.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
796                 READ_BOOL(panels.sort.enabled);
797                 READ_INT(panels.sort.action_state);
798                 READ_INT(panels.sort.mode_state);
799                 READ_INT(panels.sort.selection_state);
800
801                 /* properties dialog options */
802                 READ_CHAR(properties.tabs_order);
803
804                 /* image options */
805                 if (g_ascii_strcasecmp(option, "image.zoom_mode") == 0)
806                         {
807                         if (g_ascii_strcasecmp(value, "original") == 0)
808                                 options->image.zoom_mode = ZOOM_RESET_ORIGINAL;
809                         else if (g_ascii_strcasecmp(value, "fit") == 0)
810                                 options->image.zoom_mode = ZOOM_RESET_FIT_WINDOW;
811                         else if (g_ascii_strcasecmp(value, "dont_change") == 0)
812                                 options->image.zoom_mode = ZOOM_RESET_NONE;
813                         continue;
814                         }
815                 READ_BOOL(image.zoom_2pass);
816                 READ_BOOL(image.zoom_to_fit_allow_expand);
817                 READ_BOOL(image.fit_window_to_image);
818                 READ_BOOL(image.limit_window_size);
819                 READ_INT(image.max_window_size);
820                 READ_BOOL(image.limit_autofit_size);
821                 READ_INT(image.max_autofit_size);
822                 READ_UINT_CLAMP(image.scroll_reset_method, 0, PR_SCROLL_RESET_COUNT - 1);
823                 READ_INT(image.tile_cache_max);
824                 READ_INT(image.image_cache_max);
825                 READ_UINT_CLAMP(image.zoom_quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
826                 READ_UINT_CLAMP(image.dither_quality, GDK_RGB_DITHER_NONE, GDK_RGB_DITHER_MAX);
827                 READ_INT(image.zoom_increment);
828                 READ_BOOL(image.enable_read_ahead);
829                 READ_BOOL(image.exif_rotate_enable);
830                 READ_BOOL(image.use_custom_border_color);
831                 READ_COLOR(image.border_color);
832                 READ_INT_CLAMP(image.read_buffer_size, IMAGE_LOADER_READ_BUFFER_SIZE_MIN, IMAGE_LOADER_READ_BUFFER_SIZE_MAX);
833                 READ_INT_CLAMP(image.idle_read_loop_count, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MIN, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MAX);
834
835
836                 /* thumbnails options */
837                 READ_INT_CLAMP(thumbnails.max_width, 16, 512);
838                 READ_INT_CLAMP(thumbnails.max_height, 16, 512);
839
840                 READ_BOOL(thumbnails.enable_caching);
841                 READ_BOOL(thumbnails.cache_into_dirs);
842                 READ_BOOL(thumbnails.fast);
843                 READ_BOOL(thumbnails.use_xvpics);
844                 READ_BOOL(thumbnails.spec_standard);
845                 READ_UINT_CLAMP(thumbnails.quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
846
847                 /* file sorting options */
848                 READ_UINT(file_sort.method);
849                 READ_BOOL(file_sort.ascending);
850                 READ_BOOL(file_sort.case_sensitive);
851
852                 /* file operations options */
853                 READ_BOOL(file_ops.enable_in_place_rename);
854                 READ_BOOL(file_ops.confirm_delete);
855                 READ_BOOL(file_ops.enable_delete_key);
856                 READ_BOOL(file_ops.safe_delete_enable);
857                 READ_CHAR(file_ops.safe_delete_path);
858                 READ_INT(file_ops.safe_delete_folder_maxsize);
859
860                 /* fullscreen options */
861                 READ_INT(fullscreen.screen);
862                 READ_BOOL(fullscreen.clean_flip);
863                 READ_BOOL(fullscreen.disable_saver);
864                 READ_BOOL(fullscreen.above);
865
866                 /* histogram */
867                 READ_UINT(histogram.last_channel_mode);
868                 READ_UINT(histogram.last_log_mode);
869
870                 /* image overlay */
871                 COMPAT_READ_UINT(image_overlay.common.enabled, image_overlay.common.state); /* 2008-05-12 */ 
872                 READ_UINT(image_overlay.common.state);
873                 COMPAT_READ_BOOL(fullscreen.show_info, image_overlay.common.show_at_startup); /* 2008-04-21 */
874                 READ_BOOL(image_overlay.common.show_at_startup);
875                 COMPAT_READ_CHAR(fullscreen.info, image_overlay.common.template_string); /* 2008-04-21 */
876                 READ_CHAR(image_overlay.common.template_string);
877
878                 READ_INT(image_overlay.common.x);
879                 READ_INT(image_overlay.common.y);
880
881
882                 /* slideshow options */
883                 READ_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
884                 READ_BOOL(slideshow.random);
885                 READ_BOOL(slideshow.repeat);
886
887                 /* collection options */
888
889                 READ_BOOL(collections.rectangular_selection);
890
891                 /* filtering options */
892
893                 READ_BOOL(file_filter.show_hidden_files);
894                 READ_BOOL(file_filter.show_dot_directory);
895                 READ_BOOL(file_filter.disable);
896
897                 if (g_ascii_strcasecmp(option, "file_filter.ext") == 0)
898                         {
899                         filter_parse(value_all);
900                         continue;
901                         }
902
903                 if (g_ascii_strcasecmp(option, "sidecar.ext") == 0)
904                         {
905                         sidecar_ext_parse(value_all, TRUE);
906                         continue;
907                         }
908
909                 /* Color Profiles */
910
911                 READ_BOOL(color_profile.enabled);
912                 READ_BOOL(color_profile.use_image);
913                 READ_INT(color_profile.input_type);
914
915                 if (is_numbered_option(option, "color_profile.input_file_", &i))
916                         {
917                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
918                                 {
919                                 i--;
920                                 read_char_option(f, option, option, value, &options->color_profile.input_file[i]);
921                                 }
922                         continue;
923                         }
924
925                 if (is_numbered_option(option, "color_profile.input_name_", &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_name[i]);
931                                 }
932                         continue;
933                         }
934
935                 READ_INT(color_profile.screen_type);
936                 READ_CHAR(color_profile.screen_file);
937
938                 /* Shell command */
939                 READ_CHAR(shell.path);
940                 READ_CHAR(shell.options);
941
942                 /* External Programs */
943
944                 if (is_numbered_option(option, "external_", &i))
945                         {
946                         if (i > 0 && i <= GQ_EDITOR_SLOTS)
947                                 {
948                                 const gchar *ptr;
949
950                                 i--;
951                                 editor_set_name(i, quoted_value(value_all, &ptr));
952                                 editor_set_command(i, quoted_value(ptr, NULL));
953                                 }
954                         continue;
955                         }
956
957                 /* Exif */
958                 if (0 == g_ascii_strncasecmp(option, "exif.display.", 13))
959                         {
960                         for (i = 0; ExifUIList[i].key; i++)
961                                 if (0 == g_ascii_strcasecmp(option + 13, ExifUIList[i].key))
962                                         ExifUIList[i].current = strtol(value, NULL, 10);
963                         continue;
964                         }
965                 }
966
967         fclose(f);
968         return TRUE;
969 }
970
971 void load_options(ConfOptions *options)
972 {
973         gboolean success;
974         gchar *rc_path;
975
976         if (isdir(GQ_SYSTEM_WIDE_DIR))
977                 {
978                 rc_path = g_build_filename(GQ_SYSTEM_WIDE_DIR, RC_FILE_NAME, NULL);
979                 success = load_options_from(rc_path, options);
980                 DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
981                 g_free(rc_path);
982                 }
983         
984         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
985         success = load_options_from(rc_path, options);
986         DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
987         g_free(rc_path);
988 }