Fix some more build warnings
[gliv.git] / src / rcfile.c
blob6fd683ed42619241c790e86ca1ca88dc26b6154e
1 /*
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() */
30 #include "gliv.h"
31 #include "rcfile.h"
32 #include "large_files.h"
33 #include "options.h"
34 #include "messages.h"
35 #include "actions.h"
37 #ifndef HAVE_GETDELIM
38 #include "../lib/getdelim.h"
39 #endif
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 = {
47 /* Default options */
48 .fullscreen = FALSE,
49 .maximize = FALSE,
50 .scaledown = FALSE,
51 .menu_bar = TRUE,
52 .status_bar = TRUE,
53 .scrollbars = TRUE,
54 .zoom_pointer = FALSE,
55 .alpha_checks = TRUE,
56 .dither = FALSE,
57 .force = FALSE,
58 .build_menus = TRUE,
59 .mipmap = FALSE,
60 .mnemonics = FALSE,
61 .loop = FALSE,
62 .one_image = FALSE,
63 .delay = 0,
64 .history_size = 1000,
65 .thumbnails = TRUE,
66 .resize_win = TRUE,
67 .start_show = FALSE,
68 .confirm_quit = TRUE,
69 .save_quit = TRUE,
70 .recursive = FALSE,
71 .transitions = TRUE,
72 .keep_transfo = FALSE,
73 .opengl_errors = FALSE,
74 .filtering = TRUE,
75 .duration = 10,
76 .fps = 100,
77 .thumb_width = 128,
78 .thumb_height = 64,
79 .notice_time = 500,
80 .trans_time = 500,
81 .initial_pos = POSITION_CENTER,
82 .bg_col = {0, 0, 0, 0},
83 .alpha1 = {0, 0x6666, 0x6666, 0x6666},
84 .alpha2 = {0, 0x9999, 0x9999, 0x9999}
87 typedef struct {
88 const gchar *name;
89 gpointer option;
90 const gchar *comment;
91 const gboolean is_bool;
92 } option_struct;
94 /* *INDENT-OFF* */
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 }
142 /* *INDENT-ON* */
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)
160 /* First time */
161 filename = g_build_filename(g_get_home_dir(), ".glivrc", NULL);
163 return filename;
166 /*** Loading options. ***/
168 /* The hash table is the link between an option name and its value. */
169 static void init_hash_table(void)
171 gint i;
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,
177 &option_names[i]);
180 /* Processes spaces and '#'. */
181 static gchar *clean_str(const gchar * str)
183 gchar *new_str;
184 gchar *ptr;
186 new_str = g_new(gchar, strlen(str) + 1);
188 for (ptr = new_str; *str != '\n' && *str != '\0'; str++) {
189 if (*str == ' ' || *str == '\t')
190 continue;
192 if (*str == '#')
193 break;
195 *ptr = *str;
196 ptr++;
198 *ptr = '\0';
200 return new_str;
203 static void process_line(const gchar * line)
205 gchar **res;
206 option_struct *opt;
207 gchar *clean;
209 if (*line == '\n' || *line == '\0' || *line == '#')
210 /* Skip this line. */
211 return;
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]);
222 if (opt != NULL) {
223 /* Option found. */
225 if (opt->is_bool) {
226 gboolean *bool;
228 bool = (gboolean *) opt->option;
229 *bool = g_strcasecmp(res[1], "true") ? FALSE : TRUE;
230 } else {
231 /* opt->is_bool == FALSE */
232 gint *value;
234 value = (gint *) opt->option;
235 *value = (gint) g_strtod(res[1], NULL);
237 } else
238 g_printerr(_("Unknown option in configuration file: %s\n"), res[0]);
239 } else
240 g_printerr(_("Parse error in configuration file: %s\n"), clean);
242 g_free(clean);
243 g_strfreev(res);
246 #ifndef SYSCONFDIR
247 #define SYSCONFDIR "/etc"
248 #endif
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" };
255 gint i;
257 if (func(filename))
258 return filename;
260 filename = user_glivrc();
261 if (func(filename))
262 return filename;
264 for (i = 0; i < G_N_ELEMENTS(system_wide); i++) {
265 if (func(system_wide[i]))
266 return system_wide[i];
269 return NULL;
272 static gboolean load_rc_file(const gchar * filename)
274 FILE *file;
275 gchar *line = NULL;
276 size_t nb = 0;
278 if (filename == NULL || !g_file_test(filename, G_FILE_TEST_IS_REGULAR))
279 return FALSE;
281 file = fopen(filename, "r");
282 if (file == NULL) {
283 perror(filename);
284 return FALSE;
287 while (getdelim(&line, &nb, '\n', file) >= 0) {
288 if (g_str_equal(line, SEPARATOR_ACCELERATORS) ||
289 g_str_equal(line, SEPARATOR_ACTIONS))
290 break;
292 process_line(line);
295 g_free(line);
296 fclose(file);
297 return TRUE;
300 options_struct *load_rc(gboolean default_file, const gchar * filename)
302 if (default_file == FALSE && filename == NULL) {
303 opts.save_quit = FALSE;
304 return &opts;
307 init_hash_table();
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);
314 return &opts;
317 static void process_action_line(const gchar * line)
319 static gchar *action_name = NULL;
320 const gchar *str = line;
321 gint length;
322 gboolean is_action_name;
324 if (*line == '\n' || *line == '\0' || *line == '#')
325 /* Skip this line. */
326 return;
328 if (g_str_has_prefix(line, "action_name") == FALSE &&
329 g_str_has_prefix(line, "action_command") == FALSE)
330 return;
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')
335 str++;
337 if (*str != '=')
338 /* Invalid line. */
339 return;
341 do {
342 str++;
343 } while (*str == ' ' || *str == '\t');
345 length = strlen(str);
346 while (length > 0 && str[length - 1] == '\n')
347 length--;
349 if (is_action_name) {
350 g_free(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);
357 g_free(action_name);
358 g_free(action_command);
359 action_name = NULL;
363 static void load_actions_file(const gchar * filename)
365 FILE *file;
366 gchar *line = NULL;
367 size_t nb = 0;
369 if (filename == NULL)
370 return;
372 file = fopen(filename, "r");
373 if (file == NULL) {
374 perror(filename);
375 return;
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) {
383 g_free(line);
384 fclose(file);
385 return;
388 while (getdelim(&line, &nb, '\n', file) >= 0) {
389 if (g_str_equal(line, SEPARATOR_ACCELERATORS) ||
390 g_str_equal(line, SEPARATOR_ACTIONS))
391 break;
393 process_action_line(line);
396 g_free(line);
397 fclose(file);
400 void load_actions(void)
402 load_actions_file(read_config_file);
405 static void load_accelerators_file(const gchar * filename)
407 FILE *file;
408 gchar *line = NULL;
409 size_t nb = 0;
410 off_t position;
411 gint fd;
413 if (filename == NULL)
414 return;
416 file = fopen(filename, "r");
417 if (file == NULL) {
418 perror(filename);
419 return;
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) {
427 g_free(line);
428 fclose(file);
429 return;
432 g_free(line);
434 position = ftello(file);
435 fd = fileno(file);
436 if (lseek(fd, position, SEEK_SET) < 0) {
437 perror("lseek");
438 fclose(file);
439 return;
442 gtk_accel_map_load_fd(fd);
444 fclose(file);
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)
457 gint i, value;
459 fputs(option_names[index].name, f);
461 for (i = strlen(option_names[index].name); i < MAX_OPT_LEN; i++)
462 fputc(' ', f);
464 value = *((gint *) option_names[index].option);
466 if (option_names[index].is_bool)
467 fprintf(f, " = %s\n\n", value ? "True" : "False");
468 else
469 fprintf(f, " = %d\n\n", value);
472 static gboolean write_rc(const gchar * filename)
474 FILE *file;
475 gint i;
477 if (filename == NULL)
478 return FALSE;
480 file = fopen(filename, "w");
481 if (file == NULL) {
482 perror(filename);
483 return FALSE;
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);
500 write_actions(file);
502 /* The separation between actions and keyboard accelerators. */
503 fprintf(file, "%s\n", SEPARATOR_ACCELERATORS);
504 fflush(file);
506 gtk_accel_map_save_fd(fileno(file));
508 fclose(file);
509 return TRUE;
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)
527 gchar *dirname;
528 gboolean res;
530 if (file == NULL)
531 return FALSE;
533 if (g_file_test(file, G_FILE_TEST_IS_DIR))
534 return FALSE;
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);
541 g_free(dirname);
543 return res;
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();
554 return NULL;