Allow to override documentation paths through options:
[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_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 void save_options(ConfOptions *options)
643 {
644         gchar *rc_path;
645
646         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
647         save_options_to(rc_path, options);
648         g_free(rc_path);
649 }
650
651
652
653 /*
654  *-----------------------------------------------------------------------------
655  * load configuration (public)
656  *-----------------------------------------------------------------------------
657  */
658
659 static gboolean is_numbered_option(const gchar *option, const gchar *prefix, gint *number)
660 {
661         gsize n;
662         gsize option_len = strlen(option);
663         gsize prefix_len = strlen(prefix);
664         
665         if (option_len <= prefix_len) return FALSE;
666         if (g_ascii_strncasecmp(option, prefix, prefix_len) != 0) return FALSE;
667
668         n = prefix_len;
669         while (g_ascii_isdigit(option[n])) n++;
670         if (n < option_len) return FALSE;
671         
672         if (number) *number = atoi(option + prefix_len);
673         return TRUE;
674 }
675
676 #define OPTION_READ_BUFFER_SIZE 1024
677
678 static gboolean load_options_from(const gchar *utf8_path, ConfOptions *options)
679 {
680         FILE *f;
681         gchar *rc_pathl;
682         gchar s_buf[OPTION_READ_BUFFER_SIZE];
683         gchar value_all[OPTION_READ_BUFFER_SIZE];
684         gchar *option;
685         gchar *value;
686         gint i;
687
688         rc_pathl = path_from_utf8(utf8_path);
689         f = fopen(rc_pathl,"r");
690         g_free(rc_pathl);
691         if (!f) return FALSE;
692
693         while (fgets(s_buf, sizeof(s_buf), f))
694                 {
695                 gchar *value_end;
696                 gchar *p = s_buf;
697
698                 /* skip empty lines and comments */
699                 while (g_ascii_isspace(*p)) p++;
700                 if (!*p || *p == '\n' || *p == '#') continue;
701
702                 /* parse option name */
703                 option = p;
704                 while (g_ascii_isalnum(*p) || *p == '_' || *p == '.') p++;
705                 if (!*p) continue;
706                 *p = '\0';
707                 p++;
708
709                 /* search for value start, name and value are normally separated by ': '
710                  * but we allow relaxed syntax here, so '=', ':=' or just a tab will work too */
711                 while (*p == ':' || g_ascii_isspace(*p) || *p == '=') p++;
712                 value = p;
713
714                 while (*p && !g_ascii_isspace(*p) && *p != '\n') p++;
715                 value_end = p; /* value part up to the first whitespace or end of line */
716                 while (*p != '\0') p++;
717                 memcpy(value_all, value, 1 + p - value);
718
719                 *value_end = '\0';
720
721 #define READ_BOOL(_name_) if (read_bool_option(f, option, #_name_, value, &options->_name_)) continue;
722 #define READ_INT(_name_) if (read_int_option(f, option, #_name_, value, &options->_name_)) continue;
723 #define READ_UINT(_name_) if (read_uint_option(f, option, #_name_, value, &options->_name_)) continue;
724 #define READ_INT_CLAMP(_name_, _min_, _max_) if (read_int_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
725 #define READ_UINT_CLAMP(_name_, _min_, _max_) if (read_uint_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
726 #define READ_INT_UNIT(_name_, _unit_) if (read_int_unit_option(f, option, #_name_, value, &options->_name_, _unit_)) continue;
727 #define READ_CHAR(_name_) if (read_char_option(f, option, #_name_, value_all, &options->_name_)) continue;
728 #define READ_COLOR(_name_) if (read_color_option(f, option, #_name_, value, &options->_name_)) continue;
729
730 #define COMPAT_READ_BOOL(_oldname_, _name_) if (read_bool_option(f, option, #_oldname_, value, &options->_name_)) continue;
731 #define COMPAT_READ_INT(_oldname_, _name_) if (read_int_option(f, option, #_oldname_, value, &options->_name_)) continue;
732 #define COMPAT_READ_UINT(_oldname_, _name_) if (read_uint_option(f, option, #_oldname_, value, &options->_name_)) continue;
733 #define COMPAT_READ_INT_CLAMP(_oldname_, _name_, _min_, _max_) if (read_int_option_clamp(f, option, #_oldname_, value, &options->_name_, _min_, _max_)) continue;
734 #define COMPAT_READ_INT_UNIT(_oldname_, _name_, _unit_) if (read_int_unit_option(f, option, #_oldname_, value, &options->_name_, _unit_)) continue;
735 #define COMPAT_READ_CHAR(_oldname_, _name_) if (read_char_option(f, option, #_oldname_, value_all, &options->_name_)) continue;
736 #define COMPAT_READ_COLOR(_oldname_, _name_) if (read_color_option(f, option, #_oldname_, value, &options->_name_)) continue;
737
738                 /* general options */
739                 READ_BOOL(show_icon_names);
740                 READ_BOOL(show_copy_path);
741
742                 READ_BOOL(tree_descend_subdirs);
743                 READ_BOOL(lazy_image_sync);
744                 READ_BOOL(update_on_time_change);
745
746                 READ_UINT_CLAMP(duplicates_similarity_threshold, 0, 100);
747
748                 READ_BOOL(progressive_key_scrolling);
749
750                 READ_BOOL(enable_metadata_dirs);
751                 READ_BOOL(save_metadata_in_image_file);
752
753                 READ_BOOL(mousewheel_scrolls);
754
755                 READ_INT(open_recent_list_maxsize);
756                 READ_INT(dnd_icon_size);
757                 READ_BOOL(place_dialogs_under_mouse);
758
759                 /* startup options */
760                 
761                 COMPAT_READ_BOOL(startup_path_enable, startup.restore_path); /* 2008/05/11 */
762                 READ_BOOL(startup.restore_path);
763
764                 READ_BOOL(startup.use_last_path);
765
766                 COMPAT_READ_CHAR(startup_path, startup.path); /* 2008/05/11 */
767                 READ_CHAR(startup.path);
768         
769                 /* layout options */
770
771                 READ_INT(layout.style);
772                 READ_CHAR(layout.order);
773                 
774                 COMPAT_READ_UINT(layout.view_as_icons, layout.file_view_type); /* 2008/05/03 */
775
776                 READ_UINT(layout.dir_view_type);
777                 READ_UINT(layout.file_view_type);
778                 READ_BOOL(layout.show_marks);
779                 READ_BOOL(layout.show_thumbnails);
780                 READ_BOOL(layout.show_directory_date);
781                 READ_CHAR(layout.home_path);
782
783                 /* window positions */
784
785                 READ_BOOL(layout.save_window_positions);
786
787                 READ_INT(layout.main_window.x);
788                 READ_INT(layout.main_window.y);
789                 READ_INT(layout.main_window.w);
790                 READ_INT(layout.main_window.h);
791                 READ_BOOL(layout.main_window.maximized);
792                 READ_INT(layout.main_window.hdivider_pos);
793                 READ_INT(layout.main_window.vdivider_pos);
794
795                 READ_INT(layout.float_window.x);
796                 READ_INT(layout.float_window.y);
797                 READ_INT(layout.float_window.w);
798                 READ_INT(layout.float_window.h);
799                 READ_INT(layout.float_window.vdivider_pos);
800         
801                 READ_INT(layout.properties_window.w);
802                 READ_INT(layout.properties_window.h);
803
804                 READ_BOOL(layout.tools_float);
805                 READ_BOOL(layout.tools_hidden);
806                 READ_BOOL(layout.tools_restore_state);
807                 READ_BOOL(layout.toolbar_hidden);
808
809                 /* panels */
810                 READ_BOOL(panels.exif.enabled);
811                 READ_INT_CLAMP(panels.exif.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
812                 READ_BOOL(panels.info.enabled);
813                 READ_INT_CLAMP(panels.info.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
814                 READ_BOOL(panels.sort.enabled);
815                 READ_INT(panels.sort.action_state);
816                 READ_INT(panels.sort.mode_state);
817                 READ_INT(panels.sort.selection_state);
818
819                 /* properties dialog options */
820                 READ_CHAR(properties.tabs_order);
821
822                 /* image options */
823                 if (g_ascii_strcasecmp(option, "image.zoom_mode") == 0)
824                         {
825                         if (g_ascii_strcasecmp(value, "original") == 0)
826                                 options->image.zoom_mode = ZOOM_RESET_ORIGINAL;
827                         else if (g_ascii_strcasecmp(value, "fit") == 0)
828                                 options->image.zoom_mode = ZOOM_RESET_FIT_WINDOW;
829                         else if (g_ascii_strcasecmp(value, "dont_change") == 0)
830                                 options->image.zoom_mode = ZOOM_RESET_NONE;
831                         continue;
832                         }
833                 READ_BOOL(image.zoom_2pass);
834                 READ_BOOL(image.zoom_to_fit_allow_expand);
835                 READ_BOOL(image.fit_window_to_image);
836                 READ_BOOL(image.limit_window_size);
837                 READ_INT(image.max_window_size);
838                 READ_BOOL(image.limit_autofit_size);
839                 READ_INT(image.max_autofit_size);
840                 READ_UINT_CLAMP(image.scroll_reset_method, 0, PR_SCROLL_RESET_COUNT - 1);
841                 READ_INT(image.tile_cache_max);
842                 READ_INT(image.image_cache_max);
843                 READ_UINT_CLAMP(image.zoom_quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
844                 READ_UINT_CLAMP(image.dither_quality, GDK_RGB_DITHER_NONE, GDK_RGB_DITHER_MAX);
845                 READ_INT(image.zoom_increment);
846                 READ_BOOL(image.enable_read_ahead);
847                 READ_BOOL(image.exif_rotate_enable);
848                 READ_BOOL(image.use_custom_border_color);
849                 READ_COLOR(image.border_color);
850                 READ_INT_CLAMP(image.read_buffer_size, IMAGE_LOADER_READ_BUFFER_SIZE_MIN, IMAGE_LOADER_READ_BUFFER_SIZE_MAX);
851                 READ_INT_CLAMP(image.idle_read_loop_count, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MIN, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MAX);
852
853
854                 /* thumbnails options */
855                 READ_INT_CLAMP(thumbnails.max_width, 16, 512);
856                 READ_INT_CLAMP(thumbnails.max_height, 16, 512);
857
858                 READ_BOOL(thumbnails.enable_caching);
859                 READ_BOOL(thumbnails.cache_into_dirs);
860                 READ_BOOL(thumbnails.fast);
861                 READ_BOOL(thumbnails.use_xvpics);
862                 READ_BOOL(thumbnails.spec_standard);
863                 READ_UINT_CLAMP(thumbnails.quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
864
865                 /* file sorting options */
866                 READ_UINT(file_sort.method);
867                 READ_BOOL(file_sort.ascending);
868                 READ_BOOL(file_sort.case_sensitive);
869
870                 /* file operations options */
871                 READ_BOOL(file_ops.enable_in_place_rename);
872                 READ_BOOL(file_ops.confirm_delete);
873                 READ_BOOL(file_ops.enable_delete_key);
874                 READ_BOOL(file_ops.safe_delete_enable);
875                 READ_CHAR(file_ops.safe_delete_path);
876                 READ_INT(file_ops.safe_delete_folder_maxsize);
877
878                 /* fullscreen options */
879                 READ_INT(fullscreen.screen);
880                 READ_BOOL(fullscreen.clean_flip);
881                 READ_BOOL(fullscreen.disable_saver);
882                 READ_BOOL(fullscreen.above);
883
884                 /* histogram */
885                 READ_UINT(histogram.last_channel_mode);
886                 READ_UINT(histogram.last_log_mode);
887
888                 /* image overlay */
889                 COMPAT_READ_UINT(image_overlay.common.enabled, image_overlay.common.state); /* 2008-05-12 */
890                 READ_UINT(image_overlay.common.state);
891                 COMPAT_READ_BOOL(fullscreen.show_info, image_overlay.common.show_at_startup); /* 2008-04-21 */
892                 READ_BOOL(image_overlay.common.show_at_startup);
893                 COMPAT_READ_CHAR(fullscreen.info, image_overlay.common.template_string); /* 2008-04-21 */
894                 READ_CHAR(image_overlay.common.template_string);
895
896                 READ_INT(image_overlay.common.x);
897                 READ_INT(image_overlay.common.y);
898
899
900                 /* slideshow options */
901                 READ_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
902                 READ_BOOL(slideshow.random);
903                 READ_BOOL(slideshow.repeat);
904
905                 /* collection options */
906
907                 READ_BOOL(collections.rectangular_selection);
908
909                 /* filtering options */
910
911                 READ_BOOL(file_filter.show_hidden_files);
912                 READ_BOOL(file_filter.show_dot_directory);
913                 READ_BOOL(file_filter.disable);
914
915                 if (g_ascii_strcasecmp(option, "file_filter.ext") == 0)
916                         {
917                         filter_parse(value_all);
918                         continue;
919                         }
920
921                 if (g_ascii_strcasecmp(option, "sidecar.ext") == 0)
922                         {
923                         sidecar_ext_parse(value_all, TRUE);
924                         continue;
925                         }
926
927                 /* Color Profiles */
928
929                 READ_BOOL(color_profile.enabled);
930                 READ_BOOL(color_profile.use_image);
931                 READ_INT(color_profile.input_type);
932
933                 if (is_numbered_option(option, "color_profile.input_file_", &i))
934                         {
935                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
936                                 {
937                                 i--;
938                                 read_char_option(f, option, option, value, &options->color_profile.input_file[i]);
939                                 }
940                         continue;
941                         }
942
943                 if (is_numbered_option(option, "color_profile.input_name_", &i))
944                         {
945                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
946                                 {
947                                 i--;
948                                 read_char_option(f, option, option, value, &options->color_profile.input_name[i]);
949                                 }
950                         continue;
951                         }
952
953                 READ_INT(color_profile.screen_type);
954                 READ_CHAR(color_profile.screen_file);
955
956                 /* Shell command */
957                 READ_CHAR(shell.path);
958                 READ_CHAR(shell.options);
959
960                 /* Helpers */
961                 READ_CHAR(helpers.html_browser.command_name);
962                 READ_CHAR(helpers.html_browser.command_line);
963
964                 /* External Programs */
965
966                 if (is_numbered_option(option, "external_", &i))
967                         {
968                         if (i > 0 && i <= GQ_EDITOR_SLOTS)
969                                 {
970                                 const gchar *ptr;
971
972                                 i--;
973                                 editor_set_name(i, quoted_value(value_all, &ptr));
974                                 editor_set_command(i, quoted_value(ptr, NULL));
975                                 }
976                         continue;
977                         }
978
979                 /* Exif */
980                 if (0 == g_ascii_strncasecmp(option, "exif.display.", 13))
981                         {
982                         for (i = 0; ExifUIList[i].key; i++)
983                                 if (0 == g_ascii_strcasecmp(option + 13, ExifUIList[i].key))
984                                         ExifUIList[i].current = strtol(value, NULL, 10);
985                         continue;
986                         }
987                 
988                 /* Documentation */
989                 READ_CHAR(documentation.helpdir);
990                 READ_CHAR(documentation.htmldir);
991                 
992                 }
993
994         fclose(f);
995         return TRUE;
996 }
997
998 void load_options(ConfOptions *options)
999 {
1000         gboolean success;
1001         gchar *rc_path;
1002
1003         if (isdir(GQ_SYSTEM_WIDE_DIR))
1004                 {
1005                 rc_path = g_build_filename(GQ_SYSTEM_WIDE_DIR, RC_FILE_NAME, NULL);
1006                 success = load_options_from(rc_path, options);
1007                 DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
1008                 g_free(rc_path);
1009                 }
1010         
1011         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
1012         success = load_options_from(rc_path, options);
1013         DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
1014         g_free(rc_path);
1015 }