/* * config_file.c - manages config file access. * part of galculator * (c) 2002-2007 Simon Floery (chimaira@users.sf.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* to add a new preference, add it to - s_preferences in config_file.h - #define DEFAULTS in config_file.h - to config_file_get_default_prefs - to struct prefs_list (increase array size!) */ #include #include #include /* i18n */ #include #define _(String) gettext (String) #define gettext_noop(String) String #define N_(String) gettext_noop (String) #include "config_file.h" #include "general_functions.h" s_preferences prefs; static s_prefs_entry prefs_list[53] = { {"display_bkg_color", &(prefs.bkg_color), STRING, "prefs_bkg_color_button", set_button_color}, {"display_result_font", &(prefs.result_font), STRING, "prefs_result_font", set_button_font}, {"display_result_color", &(prefs.result_color), STRING, "prefs_result_color_button", set_button_color}, {"display_stack_font", &(prefs.stack_font), STRING, "prefs_stack_font", set_button_font}, {"display_stack_color", &(prefs.stack_color), STRING, "prefs_stack_color_button", set_button_color}, {"display_module_font", &(prefs.mod_font), STRING, "prefs_mod_font", set_button_font}, {"display_module_active_color", &(prefs.act_mod_color), STRING, "prefs_act_mod_color_button", set_button_color}, {"display_module_inactive_color", &(prefs.inact_mod_color), STRING, "prefs_inact_mod_color_button", set_button_color}, {"display_module_number", &(prefs.vis_number), BOOLEAN, "prefs_vis_number", set_checkbutton}, /*10*/ {"display_module_angle", &(prefs.vis_angle), BOOLEAN, "prefs_vis_angle", set_checkbutton}, {"display_module_notation", &(prefs.vis_notation), BOOLEAN, "prefs_vis_notation", set_checkbutton}, {"display_module_arith", &(prefs.vis_arith), BOOLEAN, "prefs_vis_arith", set_checkbutton}, {"display_module_open", &(prefs.vis_bracket), BOOLEAN, "prefs_vis_bracket", set_checkbutton}, {"custom_button_font", &(prefs.custom_button_font), BOOLEAN, "prefs_custom_button_font", set_checkbutton}, {"button_font", &(prefs.button_font), STRING, "prefs_button_font", set_button_font}, {"button_width", &(prefs.button_width), INTEGER, "prefs_button_width", set_spinbutton}, {"button_height", &(prefs.button_height), INTEGER, "prefs_button_height", set_spinbutton}, {"function_button_group", &(prefs.vis_funcs), BOOLEAN, NULL, NULL}, {"dispctrl_button_group", &(prefs.vis_dispctrl), BOOLEAN, NULL, NULL}, /*20*/ {"logic_button_group", &(prefs.vis_logic), BOOLEAN, NULL, NULL}, {"standard_button_group", &(prefs.vis_standard), BOOLEAN, NULL, NULL}, {"mode", &(prefs.mode), INTEGER, NULL, NULL}, {"dec_sep", &(prefs.dec_sep), BOOLEAN, "prefs_dec_sep", set_checkbutton}, {"dec_sep_length", &(prefs.dec_sep_length), INTEGER, "prefs_dec_sep_length", set_spinbutton}, {"dec_sep_char", &(prefs.dec_sep_char), STRING, "prefs_dec_sep_char", set_entry}, {"hex_bits", &(prefs.hex_bits), INTEGER, "prefs_hex_bits", set_spinbutton}, {"hex_signed", &(prefs.hex_signed), BOOLEAN, "prefs_hex_signed", set_checkbutton}, {"hex_sep", &(prefs.hex_sep), BOOLEAN, "prefs_hex_sep", set_checkbutton}, {"hex_sep_length", &(prefs.hex_sep_length), INTEGER, "prefs_hex_sep_length", set_spinbutton}, /*30*/ {"hex_sep_char", &(prefs.hex_sep_char), STRING, "prefs_hex_sep_char", set_entry}, {"oct_bits", &(prefs.oct_bits), INTEGER, "prefs_oct_bits", set_spinbutton}, {"oct_signed", &(prefs.oct_signed), BOOLEAN, "prefs_oct_signed", set_checkbutton}, {"oct_sep", &(prefs.oct_sep), BOOLEAN, "prefs_oct_sep", set_checkbutton}, {"oct_sep_length", &(prefs.oct_sep_length), INTEGER, "prefs_oct_sep_length", set_spinbutton}, {"oct_sep_char", &(prefs.oct_sep_char), STRING, "prefs_oct_sep_char", set_entry}, {"bin_bits", &(prefs.bin_bits), INTEGER, "prefs_bin_bits", set_spinbutton}, {"bin_signed", &(prefs.bin_signed), BOOLEAN, "prefs_bin_signed", set_checkbutton}, {"bin_fixed", &(prefs.bin_fixed), BOOLEAN, "prefs_bin_fixed", set_checkbutton}, {"bin_length", &(prefs.bin_length), INTEGER, "prefs_bin_length", set_spinbutton}, /*40*/ {"bin_sep", &(prefs.bin_sep), BOOLEAN, "prefs_bin_sep", set_checkbutton}, {"bin_sep_length", &(prefs.bin_sep_length), INTEGER, "prefs_bin_sep_length", set_spinbutton}, {"bin_sep_char", &(prefs.bin_sep_char), STRING, "prefs_bin_sep_char", set_entry}, {"default_number_base", &(prefs.def_number), INTEGER, NULL, NULL}, {"default_angle_base", &(prefs.def_angle), INTEGER, NULL, NULL}, {"default_notation_mode", &(prefs.def_notation), INTEGER, NULL, NULL}, {"stack_size", &(prefs.stack_size), INTEGER, NULL, set_stacksize}, {"remembers_display", &(prefs.rem_display), BOOLEAN, "prefs_rem_display", set_checkbutton}, {"remembered_valuex", &(prefs.rem_valuex), STRING, NULL, NULL}, {"remembered_valuey", &(prefs.rem_valuey), STRING, NULL, NULL}, /*50*/ {"remembered_valuez", &(prefs.rem_valuez), STRING, NULL, NULL}, {"remembered_valuet", &(prefs.rem_valuet), STRING, NULL, NULL}, {"show_menu_bar", &(prefs.show_menu), BOOLEAN, "prefs_show_menu", set_checkbutton}, {NULL, NULL, 0, NULL, NULL} }; static char *prefs_list_old_entries[3] = {"show_status_bar", "remembered_value", NULL}; static s_constant *cf_constant=NULL; static s_user_function *cf_user_function=NULL; /* * config_file_get_default_prefs - initialize ALL members of the given s_preferences * structure with default values. */ static void config_file_get_default_prefs (s_preferences *this_prefs) { /* 1st pref page */ this_prefs->bkg_color = g_strdup (DEFAULT_BKG_COLOR); this_prefs->result_font = g_strdup (DEFAULT_RESULT_FONT); this_prefs->result_color = g_strdup (DEFAULT_RESULT_COLOR); this_prefs->stack_font = g_strdup (DEFAULT_STACK_FONT); this_prefs->stack_color = g_strdup (DEFAULT_STACK_COLOR); this_prefs->mod_font = g_strdup (DEFAULT_MOD_FONT); this_prefs->act_mod_color = g_strdup (DEFAULT_ACT_MOD_COLOR); this_prefs->inact_mod_color = g_strdup (DEFAULT_INACT_MOD_COLOR); this_prefs->vis_number = DEFAULT_VIS_NUMBER; this_prefs->vis_angle = DEFAULT_VIS_ANGLE; this_prefs->vis_notation = DEFAULT_VIS_NOTATION; this_prefs->vis_arith = DEFAULT_VIS_ARITH; this_prefs->vis_bracket = DEFAULT_VIS_BRACKET; /* 2nd pref page */ this_prefs->custom_button_font = DEFAULT_CUSTOM_BUTTON_FONT; this_prefs->button_font = g_strdup (DEFAULT_BUTTON_FONT); this_prefs->button_width = DEFAULT_BUTTON_WIDTH; this_prefs->button_height = DEFAULT_BUTTON_HEIGHT; this_prefs->vis_funcs = DEFAULT_VIS_FUNCS; this_prefs->vis_dispctrl = DEFAULT_VIS_DISPCTRL; this_prefs->vis_logic = DEFAULT_VIS_LOGIC; this_prefs->vis_standard = DEFAULT_VIS_STANDARD; this_prefs->mode = DEFAULT_MODE; /* 3rd pref page */ /* constants - handled different */ /* 4th pref page */ this_prefs->dec_sep = DEFAULT_DEC_SEP; this_prefs->dec_sep_char = g_strdup (DEFAULT_DEC_SEP_CHAR); this_prefs->dec_sep_length = DEFAULT_DEC_SEP_LENGTH; this_prefs->hex_bits = DEFAULT_HEX_BITS; this_prefs->hex_signed = DEFAULT_HEX_SIGNED; this_prefs->hex_sep = DEFAULT_HEX_SEP; this_prefs->hex_sep_char = g_strdup (DEFAULT_HEX_SEP_CHAR); this_prefs->hex_sep_length = DEFAULT_HEX_SEP_LENGTH; this_prefs->oct_bits = DEFAULT_OCT_BITS; this_prefs->oct_signed = DEFAULT_OCT_SIGNED; this_prefs->oct_sep = DEFAULT_OCT_SEP; this_prefs->oct_sep_char = g_strdup (DEFAULT_OCT_SEP_CHAR); this_prefs->oct_sep_length = DEFAULT_OCT_SEP_LENGTH; this_prefs->bin_bits = DEFAULT_BIN_BITS; this_prefs->bin_signed = DEFAULT_BIN_SIGNED; this_prefs->bin_fixed = DEFAULT_BIN_FIXED; this_prefs->bin_length = DEFAULT_BIN_LENGTH; this_prefs->bin_sep = DEFAULT_BIN_SEP; this_prefs->bin_sep_char = g_strdup (DEFAULT_BIN_SEP_CHAR); this_prefs->bin_sep_length = DEFAULT_BIN_SEP_LENGTH; /* 5rd pref page */ this_prefs->stack_size = DEFAULT_STACK_SIZE; this_prefs->def_number = DEFAULT_NUMBER; this_prefs->def_angle = DEFAULT_ANGLE; this_prefs->def_notation = DEFAULT_NOTATION; this_prefs->rem_display = DEFAULT_REM_DISPLAY; this_prefs->rem_valuex = g_strdup (DEFAULT_REM_VALUEX); this_prefs->rem_valuey = g_strdup (DEFAULT_REM_VALUEY); this_prefs->rem_valuez = g_strdup (DEFAULT_REM_VALUEZ); this_prefs->rem_valuet = g_strdup (DEFAULT_REM_VALUET); this_prefs->show_menu = DEFAULT_SHOW_MENU; } /* * config_file_get_default_consts - fill in default constants, which are not * saved to configuration file. sync with counter inits in write and set_consts! */ static void config_file_get_default_consts (s_constant **consts) { *consts = (s_constant *) malloc (3 * sizeof (s_constant)); (*consts)[0].desc = g_strdup (_("Pi")); (*consts)[0].name = g_strdup (_("pi")); (*consts)[0].value = g_strdup_printf ("%.11f", G_PI); (*consts)[1].desc = g_strdup (_("Euler's Number")); (*consts)[1].name = g_strdup (_("e")); (*consts)[1].value = g_strdup_printf ("%.11f", G_E); (*consts)[2].name = NULL; } /* * config_file_get_default_user_functions - fill in default user functions */ static void config_file_get_default_user_functions (s_user_function **this_user_funcs) { *this_user_funcs = (s_user_function *) malloc (4 * sizeof (s_user_function)); (*this_user_funcs)[0].name = g_strdup ("abs"); (*this_user_funcs)[0].variable = g_strdup ("x"); (*this_user_funcs)[0].expression = g_strdup_printf ("sqrt(x^2)"); (*this_user_funcs)[1].name = g_strdup ("sign"); (*this_user_funcs)[1].variable = g_strdup ("x"); (*this_user_funcs)[1].expression = g_strdup_printf ("x/abs(x)"); (*this_user_funcs)[2].name = g_strdup ("cot"); (*this_user_funcs)[2].variable = g_strdup ("x"); (*this_user_funcs)[2].expression = g_strdup_printf ("cos(x)/sin(x)"); (*this_user_funcs)[3].name = NULL; } /* * config_file_set_prefs - find the key in the s_prefs_entry struct to retrieve * the variable to set. The way to set this variable is given by key_type. * used by config_file_read. */ void config_file_set_prefs (char *key, char *value) { int *int_var, counter=0, old_counter=0; char **string_var, *end_ptr; gboolean *bool_var; void *this_var; while (prefs_list[counter].key != NULL) { if (g_ascii_strcasecmp (key, prefs_list[counter].key) == 0) break; counter++; } if (prefs_list[counter].key == NULL) { while (prefs_list_old_entries[old_counter] != NULL) { if (g_ascii_strcasecmp (key, prefs_list_old_entries[old_counter]) == 0) break; old_counter++; } if (prefs_list_old_entries[old_counter] == NULL) \ fprintf (stderr, _("[%s] configuration file: ignoring unknown entry %s=%s. %s\n"), PACKAGE, key, value, BUG_REPORT); return; } this_var = prefs_list[counter].variable; switch (prefs_list[counter].key_type) { case STRING: string_var = this_var; g_free (*string_var); /* version 1.2.4 introduced strings enbodied by pairs of * \"'s. So remove those if they exist. */ if ((value[0] == '\"') && (value[strlen(value)-1] == '\"')) { value++; value[strlen(value)-1]='\0'; } *string_var = g_strdup (value); break; case BOOLEAN: bool_var = this_var; if (g_ascii_strcasecmp (value, "true") == 0) *bool_var = TRUE; else if (g_ascii_strcasecmp (value, "false") == 0) *bool_var = FALSE; else fprintf (stderr, _("[%s] configuration file: %s has to be TRUE or FALSE. Using defaults. %s\n"), PACKAGE, key, BUG_REPORT); break; case INTEGER: int_var = this_var; *int_var = (int) g_ascii_strtod (value, &end_ptr); if (*end_ptr != '\0') fprintf (stderr, _("[%s] configuration file: failed to convert %s to a number properly. Have you changed your locales? %s\n"), PACKAGE, value, BUG_REPORT); break; default: fprintf (stderr, _("[%s] configuration file: ignoring unknown key_type in config structure. %s\n"), PACKAGE, BUG_REPORT); } } /* * config_file_get_mode - if the line is enclosed by [...] and the enclosed * string is known, change to that mode. */ int config_file_get_mode (char *line, char *filename, int old_mode) { int len; line = g_strstrip(line); len = strlen(line); if ((line[0] == '[') && (line[len - 1] == ']')) { if (strcmp (line, SECTION_GENERAL) == 0) return GENERAL; else if (strcmp (line, SECTION_CONSTANTS) == 0) return CONSTANTS; else if (strcmp (line, SECTION_USER_FUNCTIONS) == 0) return USER_FUNCTIONS; else fprintf (stderr, _("[%s] found unknown section %s in \ configuration file %s. Using preceding section.\n"), PACKAGE, line, filename); } return old_mode; } /* * config_file_set_constants - parses line and fills into the s_constants array. */ void config_file_set_constants (char *line) { char *desc, *name, *value; static int nr_consts=0; desc = line; name = strchr (line, ':'); if (name == NULL) return; *name = '\0'; name++; value = strchr (name, '='); if (value == NULL) return; *value = '\0'; value++; desc = g_strstrip(desc); name = g_strstrip(name); value = g_strstrip(value); /* allowing desc and name to be "" */ if (strlen(value) == 0) return; nr_consts++; cf_constant = (s_constant *) realloc (cf_constant, (nr_consts + 1) * sizeof(s_constant)); cf_constant[nr_consts-1].desc = g_strdup (desc); cf_constant[nr_consts-1].name = g_strdup (name); cf_constant[nr_consts-1].value = g_strdup (value); /* keep it NULL terminated */ cf_constant[nr_consts].name = NULL; } /* config_file_set_user_functions * */ void config_file_set_user_functions (char *line) { char *name, *variable, *expression; static int nr_user_functions=0; name = line; variable = strchr (line, '('); if (variable == NULL) return; *variable = '\0'; variable++; expression = strchr (variable, ')'); if (expression == NULL) return; *expression = '\0'; expression++; expression = strchr (expression, '='); if (expression == NULL) return; expression++; name = g_strstrip(name); variable = g_strstrip(variable); expression = g_strstrip(expression); /* allowing name to be "" */ if ((strlen(variable) == 0) || (strlen(expression) == 0)) return; nr_user_functions++; cf_user_function = (s_user_function *) realloc (cf_user_function, (nr_user_functions + 1) * sizeof(s_user_function)); cf_user_function[nr_user_functions-1].name = g_strdup (name); cf_user_function[nr_user_functions-1].variable = g_strdup (variable); cf_user_function[nr_user_functions-1].expression = g_strdup (expression); /* keep it NULL terminated */ cf_user_function[nr_user_functions].name = NULL; } /* * config_file_read - open/read/close. values are saved to global variable prefs! * policy: if there is no constants section, pi and e are added as default * constants. of there is the section headline, nothing is added. */ s_preferences config_file_read (char *filename) { char line[MAX_FILE_LINE_LENGTH], *key, *value; FILE *this_file; int mode=GENERAL; gboolean have_const_section=FALSE, have_user_function_section=FALSE; this_file = fopen (filename, "r"); config_file_get_default_prefs (&prefs); cf_constant = (s_constant *) malloc (sizeof(s_constant)); cf_constant->desc = NULL; if (this_file != NULL) { while (fgets (line, MAX_FILE_LINE_LENGTH, this_file) != NULL) { if (line[0] != '#') { mode = config_file_get_mode (line, filename, mode); switch (mode) { case GENERAL: key = line; value = strchr (line, '='); if (value == NULL) break; *value = '\0'; value++; key = g_strstrip(key); value = g_strstrip(value); config_file_set_prefs (key, value); break; case CONSTANTS: have_const_section = TRUE; config_file_set_constants (line); break; case USER_FUNCTIONS: have_user_function_section = TRUE; config_file_set_user_functions (line); break; } } } fclose (this_file); } else fprintf (stderr, _("[%s] configuration file: couldn't open configuration file %s for reading. \ Nothing to worry about if you are starting %s for the first time. Using defaults.\n"), PACKAGE, filename, PACKAGE); if (have_const_section == FALSE) config_file_get_default_consts (&cf_constant); if (have_user_function_section == FALSE) config_file_get_default_user_functions (&cf_user_function); return prefs; } /* * config_file_write - open/write/close. */ void config_file_write (char *filename, s_preferences this_prefs, s_constant *this_constants, s_user_function *this_user_functions) { int *int_var, counter=0; char **string_var; gboolean *bool_var; void *this_var; char *line=NULL; FILE *this_file; cf_constant = this_constants; cf_user_function = this_user_functions; this_file = fopen (filename, "w+"); /* overwrite local prefs memory with supplied prefs. probably ugly. */ prefs = this_prefs; if (this_file != NULL) { fprintf (this_file, "\n%s\n\n", SECTION_GENERAL); while (prefs_list[counter].key != NULL) { this_var = prefs_list[counter].variable; switch (prefs_list[counter].key_type) { case STRING: string_var = this_var; line = g_strdup_printf ("%s=\"%s\"\n", prefs_list[counter].key, *string_var); break; case BOOLEAN: bool_var = this_var; if (*bool_var == TRUE) line = g_strdup_printf ("%s=true\n", prefs_list[counter].key); else if (*bool_var == FALSE) line = g_strdup_printf ("%s=false\n", prefs_list[counter].key); else fprintf (stderr, _("[%s] configuration file: strange boolean when writing. Skipping this key. %s\n"), PACKAGE, BUG_REPORT); break; case INTEGER: int_var = this_var; line = g_strdup_printf ("%s=%i\n", prefs_list[counter].key, *int_var); break; default: line = g_strdup_printf ("#%s=???\n", prefs_list[counter].key); fprintf (stderr, _("[%s] configuration file: ignoring unknown \"key_type\" in \"config_structure\". %s\n"), PACKAGE, BUG_REPORT); } fputs (line, this_file); g_free (line); counter ++; } counter = 0; fprintf (this_file, "\n%s\n\n", SECTION_CONSTANTS); while (cf_constant[counter].name != NULL) { fprintf (this_file, "%s:%s=%s\n", cf_constant[counter].desc, cf_constant[counter].name, cf_constant[counter].value); counter++; } counter = 0; fprintf (this_file, "\n%s\n\n", SECTION_USER_FUNCTIONS); while (cf_user_function[counter].name != NULL) { fprintf (this_file, "%s(%s)=%s\n", cf_user_function[counter].name, cf_user_function[counter].variable, cf_user_function[counter].expression); counter++; } fclose (this_file); } else fprintf (stderr, _("[%s] configuration file: couldn't save/write to configuration file %s.\n"), PACKAGE, filename); } s_prefs_entry *config_file_get_prefs_list() { return prefs_list; } s_constant *config_file_get_constants() { return cf_constant; } s_user_function *config_file_get_user_functions() { return cf_user_function; }