📄 smurfcfg.c
字号:
/*================================================================== * smurfcfg.c - Smurf preference config loading routines * * Based on gtkrc.c in the GTK source "GTK - The GIMP Toolkit" * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * Smurf Sound Font Editor * Copyright (C) 1999-2001 Josh Green * * 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 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 or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green <jgreen@users.sourceforge.net> * Smurf homepage: http://smurf.sourceforge.net *==================================================================*/#include "config.h"#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <glib.h>#include "smurfcfg.h"#include "util.h"#include "i18n.h"/* global data */gboolean smurfcfg_error;static const GScannerConfig smurfcfg_scanner_config = { " \t\n", /* cset_skip_characters */ G_CSET_a_2_z "_" G_CSET_A_2_Z, /* cset_identifier_first */ G_CSET_a_2_z "_-0123456789" G_CSET_A_2_Z, /* cset_identifier_nth */ "#\n", /* cpair_comment_single */ FALSE, /* case_sensitive */ TRUE, /* skip_comment_multi */ TRUE, /* skip_comment_single */ TRUE, /* scan_comment_multi */ TRUE, /* scan_identifier */ FALSE, /* scan_identifier_1char */ FALSE, /* scan_identifier_NULL */ TRUE, /* scan_symbols */ TRUE, /* scan_binary */ TRUE, /* scan_octal */ TRUE, /* scan_float */ TRUE, /* scan_hex */ TRUE, /* scan_hex_dollar */ TRUE, /* scan_string_sq */ TRUE, /* scan_string_dq */ TRUE, /* numbers_2_int */ FALSE, /* int_2_float */ FALSE, /* identifier_2_string */ TRUE, /* char_2_token */ TRUE, /* symbol_2_token */ FALSE, /* scope_0_fallback */};static struct{ gchar *name; /* the variable name */ guint type; /* the variable type (GTokenType) */ GTokenValue def; /* default value of variable */ GTokenValue val; /* the current variable value */} symbols[] = {/* VERSION is saved to both config files */ {"version", G_TOKEN_STRING, {VERSION}, {NULL} },/* state information (saved into a seperate file on exit, smurf_state.cfg) */ {"win_xpos", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"win_ypos", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"win_width", G_TOKEN_INT, {GINT_TO_POINTER (620)}, {NULL} }, {"win_height", G_TOKEN_INT, {GINT_TO_POINTER (440)}, {NULL} }, {"treewin_width", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"treewin_height", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"tips_position", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} },/* dummy value for SMURFCFG_STATE_LAST separator */ {"dummy", G_TOKEN_NONE, {NULL}, {NULL} },/* Smurf preferences (requires manual saving by user, saved to smurf.cfg) *//* paths */ {"def_sfont_path", G_TOKEN_STRING, {""}, {NULL} }, {"def_sample_path", G_TOKEN_STRING, {""}, {NULL} }, {"vbnk_search_path",G_TOKEN_STRING, {""}, {NULL} }, {"quit_confirm", G_TOKEN_INT, {GINT_TO_POINTER (SMURFCFG_QUIT_CONFIRM_ALWAYS)}, {NULL} }, {"disable_splash", G_TOKEN_INT, {GINT_TO_POINTER (FALSE)}, {NULL} },/* Smurfy tips */ {"tips_enabled", G_TOKEN_INT, {GINT_TO_POINTER (TRUE)}, {NULL} },/* window geometry preferences */ {"save_win_geom", G_TOKEN_INT, {GINT_TO_POINTER (TRUE)}, {NULL} }, {"set_win_pos", G_TOKEN_INT, {GINT_TO_POINTER (FALSE)}, {NULL} }, {"set_pane_size", G_TOKEN_INT, {GINT_TO_POINTER (FALSE)}, {NULL} },/* sound font preferences */ {"temp_audible_bank", G_TOKEN_INT, {GINT_TO_POINTER (127)}, {NULL} }, {"temp_audible_preset", G_TOKEN_INT, {GINT_TO_POINTER (127)}, {NULL} }, /* defaults to "AUTO" mode */ {"wavetbl_sam_caching", G_TOKEN_INT, {GINT_TO_POINTER (2)}, {NULL} }, {"sam_sfspec", G_TOKEN_INT, {GINT_TO_POINTER (TRUE)}, {NULL} }, {"sam_minloop", G_TOKEN_INT, {GINT_TO_POINTER (32)}, {NULL} }, {"sam_minlooppad", G_TOKEN_INT, {GINT_TO_POINTER (8)}, {NULL} }, {"sam_buf_waste", G_TOKEN_INT, {GINT_TO_POINTER (4)}, {NULL} }, {"sam_ch1_postfix", G_TOKEN_STRING, {"_L"}, {NULL} }, {"sam_ch2_postfix", G_TOKEN_STRING, {"_R"}, {NULL} },/* drivers */ {"wavetbl_driver", G_TOKEN_STRING, {"AUTO"}, {NULL} }, {"wavetbl_ossdev", G_TOKEN_STRING, {"/dev/sequencer"}, {NULL} }, {"seq_driver", G_TOKEN_STRING, {"AUTO"}, {NULL} }, {"seq_alsaclient",G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"seq_alsaport", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"midi_driver", G_TOKEN_STRING, {"AUTO"}, {NULL} }, {"midi_alsa_inclient", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"midi_alsa_inport", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"midi_alsa_outclient", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"midi_alsa_outport", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"midi_alsacard", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} }, {"midi_alsadevice", G_TOKEN_INT, {GINT_TO_POINTER (0)}, {NULL} },/* color preferences */ {"velbar_scolor", G_TOKEN_STRING, {"#000040"}, {NULL} }, {"velbar_ecolor", G_TOKEN_STRING, {"#0000FF"}, {NULL} },/* piano keyboard to note mapping */ {"piano_lowoctkeys", G_TOKEN_STRING, {""}, {NULL} }, {"piano_hioctkeys", G_TOKEN_STRING, {""}, {NULL} },};static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);gboolean smurfcfg_up2date;/* prototypes */static gint smurfcfg_load_vars (gchar *fname, gboolean reset_vars);static void smurfcfg_parse_err (GScanner * scanner, gchar * msg);static void smurfcfg_next_line (GScanner * scanner);static gint smurfcfg_save_vars (gchar *fname, gint first, gint last);static gchar *smurfcfg_type_string (guint token);/* functions */gintsmurfcfg_load (void) /* load smurf.cfg file */{ return (smurfcfg_load_vars ("smurf.cfg", TRUE));}gintsmurfcfg_load_state (void) /* load smurf_state.cfg file, no reset vars */{ return (smurfcfg_load_vars ("smurf_state.cfg", FALSE));}/* load a smurf configuration file into variables */static gintsmurfcfg_load_vars (gchar *fname, gboolean reset_vars){ gint fh; GScanner *scanner; guint ndx, token; guint i; gchar *s; if (reset_vars) { /* reset config variables to default values */ ndx = SMURFCFG_LAST - SMURFCFG_INVALID - 1; for (i = 0; i < ndx; i++) { if (symbols[i].type == G_TOKEN_STRING) symbols[i].val.v_string = g_strdup (symbols[i].def.v_string); else memcpy (&symbols[i].val, &symbols[i].def, sizeof (GTokenValue)); } } s = g_strconcat (g_get_home_dir (), "/.smurf/", fname, NULL); if ((fh = open (s, O_RDONLY)) == -1) { g_free (s); return (logit (LogWarn, _("Could not load ~/.smurf/%s"), fname)); } g_free (s); logit (LogInfo, _("Parsing ~/.smurf/%s.."), fname); smurfcfg_error = FALSE; scanner = g_scanner_new ((GScannerConfig *) & smurfcfg_scanner_config); g_scanner_input_file (scanner, fh); scanner->input_name = fname; /* load symbols into scanner */ g_scanner_freeze_symbol_table (scanner); for (i = 0; i < n_symbols; i++) g_scanner_add_symbol (scanner, symbols[i].name, GINT_TO_POINTER (i + SMURFCFG_INVALID + 1)); g_scanner_thaw_symbol_table (scanner); /* loop over each "variable = value" */ while (g_scanner_peek_next_token (scanner) != G_TOKEN_EOF) { /* get variable token */ token = g_scanner_get_next_token (scanner); if (token <= SMURFCFG_INVALID || token >= SMURFCFG_LAST) { smurfcfg_parse_err (scanner, _("Invalid config variable name")); smurfcfg_next_line (scanner); continue; } /* index into symbol table */ ndx = token - SMURFCFG_INVALID - 1; /* get equals token '=' */ token = g_scanner_get_next_token (scanner); if (token == G_TOKEN_EOF) { /* end of file reached? */ smurfcfg_parse_err (scanner, _("Unexpected end of file")); break; } if (token != G_TOKEN_EQUAL_SIGN) { /* make sure its an '=' sign */ smurfcfg_parse_err (scanner, _("Unexpected token while looking for '='")); smurfcfg_next_line (scanner); continue; } /* get value */ token = g_scanner_get_next_token (scanner); if (token == G_TOKEN_EOF) { smurfcfg_parse_err (scanner, _("Unexpected end of file")); break; /* end of file? */ } /* value is of correct type? */ if (token != symbols[ndx].type) { /* %s is the type of the value (i.e. string, integer, etc) */ s = g_strdup_printf (_("Unexpected value while looking for a '%s'"), smurfcfg_type_string (symbols[ndx].type)); smurfcfg_parse_err (scanner, s); g_free (s); smurfcfg_next_line (scanner); continue; } /* save the value */ switch (token) { case G_TOKEN_STRING: symbols[ndx].val.v_string = g_strdup (scanner->value.v_string); break; default: memcpy (&symbols[ndx].val, &scanner->value, sizeof (GTokenValue)); break; } } if (!smurfcfg_error) logit (LogInfo, _("Finished parsing %s"), fname); else logit (LogInfo, _("Finished parsing %s, some lines were discarded"), fname); g_scanner_destroy (scanner); smurfcfg_up2date = TRUE; return (OK);}static voidsmurfcfg_parse_err (GScanner * scanner, gchar * msg){ smurfcfg_error = TRUE; logit (LogFubar, _("Parse error on line %d: %s"), scanner->line, msg);}/* advance to next line of input file */static voidsmurfcfg_next_line (GScanner * scanner){ gint line; line = g_scanner_cur_line (scanner); while ( g_scanner_peek_next_token (scanner) != G_TOKEN_EOF) { if (scanner->next_line > line) break; g_scanner_get_next_token (scanner); }}/* saves preference variables */gintsmurfcfg_save (void){ gint retval; retval = smurfcfg_save_vars ("smurf.cfg", SMURFCFG_STATE_LAST + 1, SMURFCFG_LAST - 1); if (retval) smurfcfg_up2date = TRUE; return (retval);}/* saves state variables */gintsmurfcfg_save_state (void){ return (smurfcfg_save_vars ("smurf_state.cfg", SMURFCFG_VERSION + 1, SMURFCFG_STATE_LAST - 1));}static gintsmurfcfg_save_vars (gchar *fname, gint first, gint last){ FILE *fd; gchar *s, *s2; gint i; struct stat st; GTokenValue tokval; s = g_strconcat (g_get_home_dir (), "/.smurf", NULL); if (stat (s, &st) == -1) /* check if ~/.smurf exists */ { if (mkdir (s, 0755) == -1) /* nope: make the directory */ { g_free (s); return (logit (LogFubar | LogErrno, _("Failed to create ~/.smurf directory"))); } } s2 = g_strconcat (s, "/", fname, NULL); g_free (s); if (!(fd = fopen (s2, "w"))) /* open config file for writing */ { g_free (s2); return (logit (LogFubar | LogErrno, _("Failed to open ~/.smurf/%s for writing"), fname)); } g_free (s2); /* set Smurf version config var to current version */ tokval.v_string = VERSION; smurfcfg_set_val (SMURFCFG_VERSION, &tokval); /* This is a comment at the top of the smurf.cfg file, leave '#'s intact */ if (fputs (_("#\n# Smurf Sound Font Editor configuration file," " overwritten apon exit\n#\n"), fd) < 0) { fclose (fd); return (logit (LogFubar | LogErrno, _("Failed to write to ~/.smurf/%s"), fname)); } last -= SMURFCFG_VERSION; /* convert to array index */ /* write specified config vars to file ("first" to "last") */ i = 0; do { switch (symbols[i].type) { case G_TOKEN_STRING: s = g_strdup_printf ("\"%s\"", symbols[i].val.v_string); break; case G_TOKEN_INT: s = g_strdup_printf ("%d", (int) symbols[i].val.v_int); break; case G_TOKEN_FLOAT: s = g_strdup_printf ("%f", symbols[i].val.v_float); break; default: s = g_strdup (""); break; } s2 = g_strdup_printf ("%s = %s\n", symbols[i].name, s); g_free (s); if (!safe_fwrite (s2, strlen (s2), fd)) { g_free (s2); fclose (fd); return (logit (LogFubar, _("Failed to write to ~/.smurf/%s"), fname)); } g_free (s2); if (i == 0) i = first - SMURFCFG_VERSION; else i++; } while (i <= last); fclose (fd); return (OK);}GTokenValue *smurfcfg_get_val (guint var){ g_return_val_if_fail (var > SMURFCFG_INVALID && var < SMURFCFG_LAST, NULL); return (&symbols[var - SMURFCFG_INVALID - 1].val);}gintsmurfcfg_get_int (guint var){ GTokenValue *val; val = smurfcfg_get_val (var); if (!val) return (-1); return (val->v_int);}gchar *smurfcfg_get_str (guint var){ GTokenValue *val; val = smurfcfg_get_val (var); if (!val) return (NULL); return (val->v_string);}voidsmurfcfg_set_val (guint var, GTokenValue * value){ guint ndx; g_return_if_fail (var > SMURFCFG_INVALID && var < SMURFCFG_LAST); ndx = var - SMURFCFG_INVALID - 1; if (symbols[ndx].type == G_TOKEN_STRING) /* variable type is string? */ { /* current string value is not the same as new string? */ if (strcmp (symbols[ndx].val.v_string, value->v_string) != 0) { g_free (symbols[ndx].val.v_string); symbols[ndx].val.v_string = g_strdup (value->v_string); smurfcfg_up2date = FALSE; } } else if (symbols[ndx].type == G_TOKEN_INT) /* variable type is integer? */ { if (symbols[ndx].val.v_int != value->v_int) /* value changed? */ { symbols[ndx].val.v_int = value->v_int; smurfcfg_up2date = FALSE; } }}/* parses a hash color "#RRGGBB" and saves RGB components into array return: OK/FAIL */gintsmurfcfg_parse_hashcolor (gchar * clr, guchar * rgb){ gint i; if (*clr++ != '#') return (FAIL); for (i = 0; i < 3; i++) { if (*clr >= '0' && *clr <= '9') rgb[i] = (*clr - '0') << 4; else if (*clr >= 'A' && *clr <= 'F') rgb[i] = (*clr - 'A' + 0xA) << 4; else if (*clr >= 'a' && *clr <= 'F') rgb[i] = (*clr - 'a' + 0xA) << 4; else return (FAIL); clr++; if (*clr >= '0' && *clr <= '9') rgb[i] |= (*clr - '0'); else if (*clr >= 'A' && *clr <= 'F') rgb[i] |= (*clr - 'A' + 0xA); else if (*clr >= 'a' && *clr <= 'F') rgb[i] |= (*clr - 'a' + 0xA); else return (FAIL); clr++; } return (OK);}static gchar *smurfcfg_type_string (guint token){ switch (token) { case G_TOKEN_STRING: return (_("string")); case G_TOKEN_INT: return (_("integer")); case G_TOKEN_FLOAT: return (_("float")); default: /* "Unknown" as refering to an unknown data type */ return (_("unknown")); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -