2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@gmail.com>
21 /**********************
22 * Configuration file *
23 **********************/
25 #include <unistd.h> /* R_OK */
26 #include <stdio.h> /* FILE, f*(), perror(), getdelim() */
27 #include <string.h> /* strlen(), memcpy() */
28 #include <fcntl.h> /* open(), O_RDONLY, close(), lseek() */
32 #include "large_files.h"
38 #include "../lib/getdelim.h"
41 static GHashTable
*table
= NULL
;
42 static gchar
*read_config_file
= NULL
;
43 static gchar
*write_config_file
= NULL
;
45 /* The link between options and the rcfile. */
46 static options_struct opts
= {
54 .zoom_pointer
= FALSE
,
72 .keep_transfo
= FALSE
,
73 .opengl_errors
= FALSE
,
81 .initial_pos
= POSITION_CENTER
,
82 .bg_col
= {0, 0, 0, 0},
83 .alpha1
= {0, 0x6666, 0x6666, 0x6666},
84 .alpha2
= {0, 0x9999, 0x9999, 0x9999}
91 const gboolean is_bool
;
95 static option_struct option_names
[] = {
96 /* To fill the hash table and the configuration file. */
97 { "full-screen", &opts
.fullscreen
, N_("Start in full screen mode"), 1 },
98 { "maximize", &opts
.maximize
, N_("Maximize small images"), 1 },
99 { "scale-down", &opts
.scaledown
, N_("Scale down large images"), 1 },
100 { "menu", &opts
.menu_bar
, N_("Display the menu bar"), 1 },
101 { "info", &opts
.status_bar
, N_("Display info about the image"), 1 },
102 { "scrollbars", &opts
.scrollbars
, N_("Display scrollbars"), 1 },
103 { "zoom-pointer", &opts
.zoom_pointer
, N_("Zoom centered on pointer"), 1 },
104 { "alpha-checks", &opts
.alpha_checks
, N_("Alpha checks in the background"), 1 },
105 { "dither", &opts
.dither
, N_("Dithering"), 1 },
106 { "force-load", &opts
.force
, N_("Try to load every file"), 1 },
107 { "build-menus", &opts
.build_menus
, N_("Build images menus at startup"), 1 },
108 { "mipmap", &opts
.mipmap
, N_("Build mipmaps"), 1 },
109 { "mnemonics", &opts
.mnemonics
, N_("Make mnemonics for images menus"),1 },
110 { "loop", &opts
.loop
, N_("Make the slide show loop"), 1 },
111 { "one_image", &opts
.one_image
, N_("Keep only one image in memory"), 1 },
112 { "thumbnails", &opts
.thumbnails
, N_("Show thumbnails in images menus"),1 },
113 { "start_show", &opts
.start_show
, N_("Start with the slide show"), 1 },
114 { "resize_win", &opts
.resize_win
, N_("Automatic window resizing"), 1 },
115 { "confirm_quit", &opts
.confirm_quit
, N_("Confirm before quitting"), 1 },
116 { "save_quit", &opts
.save_quit
, N_("Save options when quitting"), 1 },
117 { "transitions", &opts
.transitions
, N_("Enable transitions"), 1 },
118 { "recursive", &opts
.recursive
, N_("Recursive directory traversal"), 1 },
119 { "keep_transfo", &opts
.keep_transfo
, N_("Keep transfo between images"), 1 },
120 { "opengl-errors",&opts
.opengl_errors
,N_("Check for OpenGL errors"), 1 },
121 { "filtering", &opts
.filtering
, N_("Enable OpenGL filtering"), 1 },
122 { "fps", &opts
.fps
, N_("Maximum framerate"), 0 },
123 { "delay", &opts
.delay
, N_("Delay before hiding the cursor"), 0 },
124 { "history", &opts
.history_size
, N_("History length"), 0 },
125 { "slide-show", &opts
.duration
, N_("Delay between images"), 0 },
126 { "thumb-width", &opts
.thumb_width
, N_("Thumbnail width"), 0 },
127 { "thumb-height", &opts
.thumb_height
, N_("Thumbnail height"), 0 },
128 { "notice-time", &opts
.notice_time
, N_("Last/First image notice time"), 0 },
129 { "trans-time", &opts
.trans_time
, N_("Transition duration"), 0 },
130 { "initial_pos", &opts
.initial_pos
, N_("Initial image position"), 0 },
131 { "bg_col_red", &opts
.bg_col
.red
, N_("background: red channel"), 0 },
132 { "bg_col_green", &opts
.bg_col
.green
, N_("background: green channel"), 0 },
133 { "bg_col_blue", &opts
.bg_col
.blue
, N_("background: blue channel"), 0 },
134 { "alpha1_red", &opts
.alpha1
.red
, N_("alpha1 tile: red channel"), 0 },
135 { "alpha1_green", &opts
.alpha1
.green
, N_("alpha1 tile: green channel"), 0 },
136 { "alpha1_blue", &opts
.alpha1
.blue
, N_("alpha1 tile: blue channel"), 0 },
137 { "alpha2_red", &opts
.alpha2
.red
, N_("alpha2 tile: red channel"), 0 },
138 { "alpha2_green", &opts
.alpha2
.green
, N_("alpha2 tile: green channel"), 0 },
139 { "alpha2_blue", &opts
.alpha2
.blue
, N_("alpha2 tile: blue channel"), 0 },
140 { NULL
, NULL
, NULL
, 0 }
145 * Maximum length of the option names, currently
146 * it is strlen("opengl-errors") == 13.
147 * Used to indent the option file.
149 #define MAX_OPT_LEN 13
151 /* Between the options and the keyboard accelerators. */
152 #define SEPARATOR_ACCELERATORS "==========\n"
153 #define SEPARATOR_ACTIONS "========== Actions\n"
155 static const gchar
*user_glivrc(void)
157 static const gchar
*filename
= NULL
;
159 if (filename
== NULL
)
161 filename
= g_build_filename(g_get_home_dir(), ".glivrc", NULL
);
166 /*** Loading options. ***/
168 /* The hash table is the link between an option name and its value. */
169 static void init_hash_table(void)
173 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
175 for (i
= 0; option_names
[i
].name
!= NULL
; i
++)
176 g_hash_table_insert(table
, (gchar
*) option_names
[i
].name
,
180 /* Processes spaces and '#'. */
181 static gchar
*clean_str(const gchar
* str
)
186 new_str
= g_new(gchar
, strlen(str
) + 1);
188 for (ptr
= new_str
; *str
!= '\n' && *str
!= '\0'; str
++) {
189 if (*str
== ' ' || *str
== '\t')
203 static void process_line(const gchar
* line
)
209 if (*line
== '\n' || *line
== '\0' || *line
== '#')
210 /* Skip this line. */
213 clean
= clean_str(line
);
215 /* res[0]: option name ; res[1]: value */
216 res
= g_strsplit(clean
, "=", 2);
218 if (res
[0] != NULL
&& res
[1] != NULL
&& res
[2] == NULL
) {
219 /* No error during split. */
220 opt
= g_hash_table_lookup(table
, res
[0]);
228 bool = (gboolean
*) opt
->option
;
229 *bool = g_strcasecmp(res
[1], "true") ? FALSE
: TRUE
;
231 /* opt->is_bool == FALSE */
234 value
= (gint
*) opt
->option
;
235 *value
= (gint
) g_strtod(res
[1], NULL
);
238 g_printerr(_("Unknown option in configuration file: %s\n"), res
[0]);
240 g_printerr(_("Parse error in configuration file: %s\n"), clean
);
247 #define SYSCONFDIR "/etc"
250 typedef gboolean(*rcfile_func
) (const gchar
* filename
);
252 static const gchar
*foreach_rcfile(rcfile_func func
, const gchar
* filename
)
254 gchar
*system_wide
[] = { SYSCONFDIR
"/glivrc", "/etc/glivrc" };
260 filename
= user_glivrc();
264 for (i
= 0; i
< G_N_ELEMENTS(system_wide
); i
++) {
265 if (func(system_wide
[i
]))
266 return system_wide
[i
];
272 static gboolean
load_rc_file(const gchar
* filename
)
278 if (filename
== NULL
|| !g_file_test(filename
, G_FILE_TEST_IS_REGULAR
))
281 file
= fopen(filename
, "r");
287 while (getdelim(&line
, &nb
, '\n', file
) >= 0) {
288 if (g_str_equal(line
, SEPARATOR_ACCELERATORS
) ||
289 g_str_equal(line
, SEPARATOR_ACTIONS
))
300 options_struct
*load_rc(gboolean default_file
, const gchar
* filename
)
302 if (default_file
== FALSE
&& filename
== NULL
) {
303 opts
.save_quit
= FALSE
;
309 read_config_file
= g_strdup(foreach_rcfile(load_rc_file
, filename
));
310 write_config_file
= g_strdup(filename
);
312 g_hash_table_destroy(table
);
317 static void process_action_line(const gchar
* line
)
319 static gchar
*action_name
= NULL
;
320 const gchar
*str
= line
;
322 gboolean is_action_name
;
324 if (*line
== '\n' || *line
== '\0' || *line
== '#')
325 /* Skip this line. */
328 if (g_str_has_prefix(line
, "action_name") == FALSE
&&
329 g_str_has_prefix(line
, "action_command") == FALSE
)
332 is_action_name
= g_str_has_prefix(line
, "action_name");
333 str
+= is_action_name
? strlen("action_name") : strlen("action_command");
334 while (*str
== ' ' || *str
== '\t')
343 } while (*str
== ' ' || *str
== '\t');
345 length
= strlen(str
);
346 while (length
> 0 && str
[length
- 1] == '\n')
349 if (is_action_name
) {
351 action_name
= g_strndup(str
, length
);
353 } else if (action_name
!= NULL
) {
354 gchar
*action_command
= g_strndup(str
, length
);
356 add_action(action_name
, action_command
);
358 g_free(action_command
);
363 static void load_actions_file(const gchar
* filename
)
369 if (filename
== NULL
)
372 file
= fopen(filename
, "r");
378 /* Search the actions separator. */
379 while (getdelim(&line
, &nb
, '\n', file
) != -1 &&
380 g_str_equal(line
, SEPARATOR_ACTIONS
) == FALSE
);
382 if (g_str_equal(line
, SEPARATOR_ACTIONS
) == FALSE
) {
388 while (getdelim(&line
, &nb
, '\n', file
) >= 0) {
389 if (g_str_equal(line
, SEPARATOR_ACCELERATORS
) ||
390 g_str_equal(line
, SEPARATOR_ACTIONS
))
393 process_action_line(line
);
400 void load_actions(void)
402 load_actions_file(read_config_file
);
405 static void load_accelerators_file(const gchar
* filename
)
413 if (filename
== NULL
)
416 file
= fopen(filename
, "r");
422 /* Search the accelerators separator. */
423 while (getdelim(&line
, &nb
, '\n', file
) != -1 &&
424 g_str_equal(line
, SEPARATOR_ACCELERATORS
) == FALSE
);
426 if (g_str_equal(line
, SEPARATOR_ACCELERATORS
) == FALSE
) {
434 position
= ftello(file
);
436 if (lseek(fd
, position
, SEEK_SET
) < 0) {
442 gtk_accel_map_load_fd(fd
);
447 void load_accelerators(void)
449 load_accelerators_file(read_config_file
);
453 /*** Saving options. ***/
455 static void write_option_line(FILE * f
, gint index
)
459 fputs(option_names
[index
].name
, f
);
461 for (i
= strlen(option_names
[index
].name
); i
< MAX_OPT_LEN
; i
++)
464 value
= *((gint
*) option_names
[index
].option
);
466 if (option_names
[index
].is_bool
)
467 fprintf(f
, " = %s\n\n", value
? "True" : "False");
469 fprintf(f
, " = %d\n\n", value
);
472 static gboolean
write_rc(const gchar
* filename
)
477 if (filename
== NULL
)
480 file
= fopen(filename
, "w");
486 fprintf(file
, _("# Configuration file for GLiv %s\n\n"), VERSION
);
487 fputs(_("# Option names are case sensitive.\n"), file
);
488 fputs(_("# Option values are case insensitive.\n\n"), file
);
490 for (i
= 0; option_names
[i
].name
!= NULL
; i
++) {
491 /* The comment line. */
492 fprintf(file
, "# %s\n", _(option_names
[i
].comment
));
494 /* The option line. */
495 write_option_line(file
, i
);
498 /* The separation between options and actions. */
499 fprintf(file
, "%s\n", SEPARATOR_ACTIONS
);
502 /* The separation between actions and keyboard accelerators. */
503 fprintf(file
, "%s\n", SEPARATOR_ACCELERATORS
);
506 gtk_accel_map_save_fd(fileno(file
));
512 void save_rc(options_struct
* opt
)
514 memcpy(&opts
, opt
, sizeof(options_struct
));
516 write_rc(get_write_config_file());
519 const gchar
*get_read_config_file(void)
521 return read_config_file
;
524 /* Yes it's racy, but there's nothing wrong in this case */
525 static gboolean
can_write_config_file(const gchar
* file
)
533 if (g_file_test(file
, G_FILE_TEST_IS_DIR
))
536 if (g_file_test(file
, G_FILE_TEST_EXISTS
))
537 return !access(file
, W_OK
);
539 dirname
= g_path_get_dirname(file
);
540 res
= g_file_test(dirname
, G_FILE_TEST_IS_DIR
) && !access(dirname
, W_OK
);
546 const gchar
*get_write_config_file(void)
548 if (can_write_config_file(write_config_file
))
549 return write_config_file
;
551 if (can_write_config_file(user_glivrc()))
552 return user_glivrc();