/* $Id: gtkhackrc.c,v 1.12 2004/01/17 12:30:01 j_ali Exp $ */ /* Copyright (c) Slash'EM Development Team 2003 */ /* NetHack may be freely redistributed. See license for details. */ #include #include #include #ifdef WIN32 #include #endif #include #include "winGTK.h" #include "prxyclnt.h" /* * This is the version of the gtkhackrc file format only. It has no * relation to the version of gtkhack. Increase the minor version * number when the new file format can not be read by earlier versions * of gtkhack. Increase the major version number (and set minor to 0) * when the new version of gtkhack can not read earlier file formats. * * Note: adding new settings will not normally require changing the * file format version since gtkhack ignores (and preserves) settings * it does not recognize as long as they follow the supported syntax. */ #define GTKHACKRC_VER_MAJOR 0 #define GTKHACKRC_VER_MINOR 2 #define GTKHACKRC_VERSTR(major, minor) #major "." #minor #define GTKHACKRC_VERSTRING(major, minor) GTKHACKRC_VERSTR(major, minor) #define GTKHACKRC_VERSION GTKHACKRC_VERSTRING(GTKHACKRC_VER_MAJOR,\ GTKHACKRC_VER_MINOR) #define PARSE_OK 0 #define PARSE_FAIL 1 #define PARSE_ERROR 2 #ifndef SIZE #define SIZE(array) (sizeof(array)/sizeof(*(array))) #endif #define GTKHACKRC(scanner) ((struct gtkhackrc *)(scanner)->user_data) typedef struct gtkhackrc_variable { /* ident [.ident] ... */ int n_idents; gchar **idents; } GtkHackRcVariable; typedef struct gtkhackrc_value GtkHackRcValue; typedef struct gtkhackrc_valuelist { /* [value [,value] ...] */ int n_values; GtkHackRcValue **values; } GtkHackRcVList; typedef struct gtkhackrc_func { /* var([value [,value] ...]) */ GtkHackRcVariable var; GtkHackRcVList params; } GtkHackRcFunc; #define PARSE_VALUE_TYPE_NUMBER 0 #define PARSE_VALUE_TYPE_STRING 1 #define PARSE_VALUE_TYPE_VARIABLE 2 #define PARSE_VALUE_TYPE_FUNCTION 3 #define PARSE_VALUE_TYPE_RECORD 4 #define PARSE_VALUE_TYPE_VECTOR 5 #define PARSE_VALUE_TYPE_VALUE_LIST 6 #define PARSE_VALUE_TYPE_FREE -1 struct gtkhackrc_value { int type; union { long number; gchar *string; GtkHackRcVariable var; GtkHackRcFunc func; GtkHackRcVList record; GtkHackRcVList vector; GtkHackRcVList vlist; GtkHackRcValue *next; /* For free list */ } u; }; extern int check_rc_version(GScanner *scanner, GtkHackRcValue *value); extern void rc_window_position(GScanner *scanner, GtkHackRcVList *params, GtkHackRcValue *value); extern void rc_window_size(GScanner *scanner, GtkHackRcVList *params, GtkHackRcValue *value); extern void rc_map_font(GScanner *scanner, GtkHackRcValue *value); extern void rc_map_clip_dist2(GScanner *scanner, GtkHackRcValue *value); extern void rc_radar(GScanner *scanner, GtkHackRcValue *value); #ifdef GTKHACK extern void rc_connections(GScanner *scanner, GtkHackRcValue *value); extern void rc_default_connection(GScanner *scanner, GtkHackRcValue *value); #endif #ifdef GTK_PROXY extern void rc_proxy_cachedir(GScanner *scanner, GtkHackRcValue *value); #endif extern void rc_help_font(GScanner *scanner, GtkHackRcValue *value); #define GTKHACKRC_VARIABLE 0 #define GTKHACKRC_FUNCTION 1 typedef struct gtkhackrc_setting GtkHackRcSetting; struct gtkhackrc_setting { int type; const char *name; union { void (*dummy)(); /* For initialization prototyping */ void (*v)(GScanner *scanner, GtkHackRcValue *value); void (*f)(GScanner *scanner, GtkHackRcVList *params, GtkHackRcValue *value); } handler; } gtkhackrc_settings[] = { GTKHACKRC_FUNCTION, "window.position", rc_window_position, GTKHACKRC_FUNCTION, "window.size", rc_window_size, GTKHACKRC_VARIABLE, "map.font", rc_map_font, GTKHACKRC_VARIABLE, "map.clip_dist2", rc_map_clip_dist2, GTKHACKRC_VARIABLE, "radar", rc_radar, #ifdef GTKHACK GTKHACKRC_VARIABLE, "connections", rc_connections, GTKHACKRC_VARIABLE, "default_connection", rc_default_connection, #endif #ifdef GTK_PROXY GTKHACKRC_VARIABLE, "proxy.cachedir", rc_proxy_cachedir, #endif GTKHACKRC_VARIABLE, "help.font", rc_help_font, }; #define PARSE_ERROR_NONE 0 #define PARSE_ERROR_TOKEN 1 /* Expecting token ... */ #define PARSE_ERROR_RULE 2 /* Expecting rule ... */ #define PARSE_ERROR_RESOURCE 3 /* Not enough memory */ #define PARSE_ERROR_INCOMPATIBLE 4 /* Incompatible profile */ struct gtkhackrc { int error_type; union { GTokenType token; gchar *rule; } error; GtkHackRcValue *value; GtkHackRcValue *freelist; gchar *variable; /* Only valid in settings handler */ #ifdef WIN32 /* Only valid in nh_write_gtkhackrc */ HKEY key; #else FILE *fp; #endif }; /* Preserved for writing back to file */ static GSList *unrecognized_settings = NULL; #ifndef WIN32 static gboolean write_verspecific_file = FALSE; #endif static int action_setting(GScanner *scanner, GtkHackRcValue *lhs, GtkHackRcValue *rhs); GtkHackRcValue *new_value(GScanner *scanner, int type) { GtkHackRcValue *v = GTKHACKRC(scanner)->freelist; if (v) GTKHACKRC(scanner)->freelist = v->u.next; else v = g_malloc(sizeof(*v)); v->type = type; return v; } static void free_value(GScanner *scanner, GtkHackRcValue *value) { int i; if (value) { switch(value->type) { case PARSE_VALUE_TYPE_FREE: case PARSE_VALUE_TYPE_NUMBER: break; case PARSE_VALUE_TYPE_STRING: g_free(value->u.string); break; case PARSE_VALUE_TYPE_VARIABLE: for(i = value->u.var.n_idents - 1; i >= 0; i--) g_free(value->u.var.idents[i]); g_free(value->u.var.idents); break; case PARSE_VALUE_TYPE_FUNCTION: for(i = value->u.func.var.n_idents - 1; i >= 0; i--) g_free(value->u.func.var.idents[i]); g_free(value->u.func.var.idents); for(i = value->u.func.params.n_values - 1; i >= 0; i--) free_value(scanner, value->u.func.params.values[i]); g_free(value->u.func.params.values); break; case PARSE_VALUE_TYPE_RECORD: case PARSE_VALUE_TYPE_VECTOR: case PARSE_VALUE_TYPE_VALUE_LIST: for(i = value->u.vlist.n_values - 1; i >= 0; i--) free_value(scanner, value->u.vlist.values[i]); g_free(value->u.vlist.values); break; } value->type = PARSE_VALUE_TYPE_FREE; value->u.next = GTKHACKRC(scanner)->freelist; GTKHACKRC(scanner)->freelist = value; } } /* * Syntax of a GtkHack profile: * * profile : profile setting * | * ; * * setting : lval = value * ; * * lval : variable * | variable '(' ')' * | variable '(' value_list ')' * ; * * variable : identifier * | variable '.' identifier * ; * * value : number * | string * | lval * | record * | vector * ; * * record : '{' value_list '}' * ; * * vector : '[' value_list ']' * | '[' ']' * ; * * value_list : value_list ',' value * | value * ; */ static int parse_setting(GScanner *scanner, int failok); static int parse_lval(GScanner *scanner, int failok); static int parse_variable(GScanner *scanner, int failok); static int parse_value(GScanner *scanner, int failok); static int parse_record(GScanner *scanner, int failok); static int parse_vector(GScanner *scanner, int failok); static int parse_value_list(GScanner *scanner, int failok); static int match(GScanner *scanner, GTokenType token) { if (g_scanner_peek_next_token(scanner) == token) { g_scanner_get_next_token(scanner); return TRUE; } else return FALSE; } /* * Conventions of parse_ functions: * - Must only return PARSE_FAIL if failok set * - If PARSE_OK is returned, then value must be set accordingly * - If PARSE_FAIL is returned, then the read pointer and value * must be unchanged. */ static int parse_setting(GScanner *scanner, int failok) { int i, n, retval; gchar *name; GtkHackRcValue *lhs; GtkHackRcVariable *var; retval = parse_lval(scanner, failok); lhs = GTKHACKRC(scanner)->value; GTKHACKRC(scanner)->value = NULL; if (retval != PARSE_OK) { free_value(scanner, lhs); return retval; } if (!match(scanner, G_TOKEN_EQUAL_SIGN)) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_EQUAL_SIGN; free_value(scanner, lhs); return PARSE_ERROR; } retval = parse_value(scanner, FALSE); if (retval == PARSE_OK) retval = action_setting(scanner, lhs, GTKHACKRC(scanner)->value); free_value(scanner, lhs); free_value(scanner, GTKHACKRC(scanner)->value); GTKHACKRC(scanner)->value = NULL; return retval; } static int parse_lval(GScanner *scanner, int failok) { int retval; GtkHackRcValue *func; retval = parse_variable(scanner, failok); if (retval != PARSE_OK) return retval; if (match(scanner, G_TOKEN_LEFT_PAREN)) { func = GTKHACKRC(scanner)->value; GTKHACKRC(scanner)->value = NULL; func->type = PARSE_VALUE_TYPE_FUNCTION; func->u.func.var = func->u.var; retval = parse_value_list(scanner, TRUE); if (retval == PARSE_OK) { func->u.func.params = GTKHACKRC(scanner)->value->u.vlist; GTKHACKRC(scanner)->value->u.vlist.n_values = 0; GTKHACKRC(scanner)->value->u.vlist.values = NULL; free_value(scanner, GTKHACKRC(scanner)->value); } else if (retval == PARSE_FAIL) { func->u.func.params.n_values = 0; func->u.func.params.values = NULL; } GTKHACKRC(scanner)->value = func; if (retval != PARSE_ERROR) if (!match(scanner, G_TOKEN_RIGHT_PAREN)) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_RIGHT_PAREN; retval = PARSE_ERROR; } } return retval; } static int parse_variable(GScanner *scanner, int failok) { int n, matched; gchar **new; GtkHackRcValue *var; var = new_value(scanner, PARSE_VALUE_TYPE_VARIABLE); n = 0; var->u.var.idents = NULL; do { matched = match(scanner, G_TOKEN_IDENTIFIER); if (matched) { new = g_realloc(var->u.var.idents, ++n * sizeof(*new)); if (!new) { free_value(scanner, var); GTKHACKRC(scanner)->error_type = PARSE_ERROR_RESOURCE; return PARSE_ERROR; } var->u.var.idents = new; var->u.var.idents[n - 1] = g_strdup(g_scanner_cur_value(scanner).v_identifier); if (!var->u.var.idents[n - 1]) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_RESOURCE; return PARSE_ERROR; } } } while (matched && match(scanner, '.')); var->u.var.n_idents = n; if (!matched) free_value(scanner, var); else GTKHACKRC(scanner)->value = var; if (!failok && !matched) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_IDENTIFIER; return PARSE_ERROR; } else return matched ? PARSE_OK : PARSE_FAIL; } static int parse_value(GScanner *scanner, int failok) { int retval; GtkHackRcValue *var; retval = match(scanner, G_TOKEN_INT) ? PARSE_OK : PARSE_FAIL; if (retval == PARSE_OK) { var = new_value(scanner, PARSE_VALUE_TYPE_NUMBER); var->u.number = g_scanner_cur_value(scanner).v_int; } else { retval = match(scanner, G_TOKEN_STRING) ? PARSE_OK : PARSE_FAIL; if (retval == PARSE_OK) { var = new_value(scanner, PARSE_VALUE_TYPE_STRING); var->u.string = g_strdup(g_scanner_cur_value(scanner).v_string); } } if (retval == PARSE_OK) GTKHACKRC(scanner)->value = var; else retval = parse_lval(scanner, TRUE); if (retval == PARSE_FAIL) retval = parse_record(scanner, TRUE); if (retval == PARSE_FAIL) retval = parse_vector(scanner, TRUE); if (!failok && retval == PARSE_FAIL) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_RULE; GTKHACKRC(scanner)->error.rule = "value"; retval = PARSE_ERROR; } return retval; } static int parse_record(GScanner *scanner, int failok) { int retval; if (match(scanner, G_TOKEN_LEFT_CURLY)) { retval = parse_value_list(scanner, FALSE); if (retval == PARSE_OK) { GTKHACKRC(scanner)->value->type = PARSE_VALUE_TYPE_RECORD; if (!match(scanner, G_TOKEN_RIGHT_CURLY)) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_RIGHT_CURLY; retval = PARSE_ERROR; } } } else if (!failok) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_LEFT_CURLY; retval = PARSE_ERROR; } else retval = PARSE_FAIL; return retval; } static int parse_vector(GScanner *scanner, int failok) { int retval; if (match(scanner, G_TOKEN_LEFT_BRACE)) { retval = parse_value_list(scanner, TRUE); if (retval != PARSE_ERROR) if (match(scanner, G_TOKEN_RIGHT_BRACE)) { if (retval == PARSE_OK) GTKHACKRC(scanner)->value->type = PARSE_VALUE_TYPE_VECTOR; else { GTKHACKRC(scanner)->value = new_value(scanner, PARSE_VALUE_TYPE_VECTOR); GTKHACKRC(scanner)->value->u.vector.n_values = 0; GTKHACKRC(scanner)->value->u.vector.values = NULL; } retval = PARSE_OK; } else { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_RIGHT_BRACE; retval = PARSE_ERROR; } } else if (!failok) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_LEFT_BRACE; retval = PARSE_ERROR; } else retval = PARSE_FAIL; return retval; } static int parse_value_list(GScanner *scanner, int failok) { int n, retval; GtkHackRcValue *vl; GtkHackRcValue **new; vl = new_value(scanner, PARSE_VALUE_TYPE_VALUE_LIST); n = 0; vl->u.vlist.values = NULL; do { retval = parse_value(scanner, failok); if (retval == PARSE_OK) { new = g_realloc(vl->u.vlist.values, ++n * sizeof(*new)); if (!new) { free_value(scanner, vl); GTKHACKRC(scanner)->error_type = PARSE_ERROR_RESOURCE; return PARSE_ERROR; } vl->u.vlist.values = new; vl->u.vlist.values[n - 1] = GTKHACKRC(scanner)->value; GTKHACKRC(scanner)->value = NULL; failok = FALSE; } } while (retval == PARSE_OK && match(scanner, ',')); vl->u.vlist.n_values = n; if (retval != PARSE_OK) free_value(scanner, vl); else GTKHACKRC(scanner)->value = vl; if (!failok && retval == PARSE_FAIL) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_TOKEN; GTKHACKRC(scanner)->error.token = G_TOKEN_IDENTIFIER; retval = PARSE_ERROR; } return retval; } static GString *print_value_list(GtkHackRcVList *vl); static GString *print_variable(GtkHackRcVariable *var) { int i, n; GString *str; n = var->n_idents; for(i = 0; i < var->n_idents; i++) n += strlen(var->idents[i]); str = g_string_sized_new(n - 1); for(i = 0; i < var->n_idents; i++) { if (i) g_string_append_c(str, '.'); g_string_append(str, var->idents[i]); } return str; } static GString *print_value(GtkHackRcValue *var) { int i; GString *str, *vl; switch(var->type) { case PARSE_VALUE_TYPE_NUMBER: str = g_string_new(""); g_string_printf(str, "%ld", var->u.number); return str; case PARSE_VALUE_TYPE_STRING: str = g_string_new(""); g_string_printf(str, "\"%s\"", g_strescape(var->u.string, "")); return str; case PARSE_VALUE_TYPE_VARIABLE: return print_variable(&var->u.var); case PARSE_VALUE_TYPE_FUNCTION: str = print_variable(&var->u.func.var); g_string_append_c(str, '('); if (var->u.func.params.n_values) { vl = print_value_list(&var->u.func.params); g_string_append_len(str, vl->str, vl->len); g_string_free(vl, TRUE); } g_string_append_c(str, ')'); return str; case PARSE_VALUE_TYPE_RECORD: str = g_string_new("{"); vl = print_value_list(&var->u.record); g_string_append_len(str, vl->str, vl->len); g_string_free(vl, TRUE); g_string_append_c(str, '}'); return str; case PARSE_VALUE_TYPE_VECTOR: str = g_string_new("["); if (var->u.vector.n_values) { vl = print_value_list(&var->u.vector); g_string_append_len(str, vl->str, vl->len); g_string_free(vl, TRUE); } g_string_append_c(str, ']'); return str; case PARSE_VALUE_TYPE_VALUE_LIST: return print_value_list(&var->u.vlist); } } static GString *print_value_list(GtkHackRcVList *vl) { int i; GString *str, *v; str = print_value(vl->values[0]); for(i = 1; i < vl->n_values; i++) { g_string_append_c(str, ','); v = print_value(vl->values[i]); g_string_append_len(str, v->str, v->len); g_string_free(v, TRUE); } return str; } static int action_setting(GScanner *scanner, GtkHackRcValue *lhs, GtkHackRcValue *rhs) { int i, n, retval = 0; gchar *name; GString *str, *value; GtkHackRcVariable *var; { if (lhs->type == PARSE_VALUE_TYPE_VARIABLE) var = &lhs->u.var; else var = &lhs->u.func.var; n = var->n_idents; for(i = 0; i < var->n_idents; i++) n += strlen(var->idents[i]); name = g_malloc(n); if (!name) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_RESOURCE; retval = PARSE_ERROR; } else { *name = '\0'; for(i = n = 0; i < var->n_idents; i++) { if (i) name[n++] = '.'; strcpy(name + n, var->idents[i]); n += strlen(var->idents[i]); } GTKHACKRC(scanner)->variable = name; if (lhs->type == PARSE_VALUE_TYPE_VARIABLE) { for(i = 0; i < SIZE(gtkhackrc_settings); i++) if (gtkhackrc_settings[i].type == GTKHACKRC_VARIABLE && !strcmp(gtkhackrc_settings[i].name, name)) break; if (i < SIZE(gtkhackrc_settings)) gtkhackrc_settings[i].handler.v(scanner, rhs); else if (!strcmp(name, "version")) { i = -1; retval = check_rc_version(scanner, rhs); } #ifdef DEBUG else fprintf(stderr, "gtkhackrc: variable %s ignored\n", name); #endif } else { for(i = 0; i < SIZE(gtkhackrc_settings); i++) if (gtkhackrc_settings[i].type == GTKHACKRC_FUNCTION && !strcmp(gtkhackrc_settings[i].name, name)) break; if (i < SIZE(gtkhackrc_settings)) gtkhackrc_settings[i].handler.f(scanner, &lhs->u.func.params, rhs); #ifdef DEBUG else fprintf(stderr, "gtkhackrc: function %s ignored\n", name); #endif } g_free(name); if (i >= SIZE(gtkhackrc_settings)) { str = print_value(lhs); g_string_append(str, " = "); value = print_value(rhs); g_string_append_len(str, value->str, value->len); g_string_free(value, TRUE); unrecognized_settings = g_slist_append(unrecognized_settings, str->str); g_string_free(str, FALSE); } } } return retval; } static void clear_unrecognized() { if (unrecognized_settings) { g_slist_foreach(unrecognized_settings, (GFunc)g_free, NULL); g_slist_free(unrecognized_settings); unrecognized_settings = NULL; } } /* * Return TRUE if okay to write file on exit; FALSE if we failed to read * file due to lack of resources. */ #ifdef WIN32 static int nh_scan_gtkhackrc(GScanner *scanner) { int i, retval; struct gtkhackrc *rc = GTKHACKRC(scanner); GtkHackRcValue *v; HKEY key; DWORD type, no_values, value_len, data_len, value_count, data_count; char *s, *value, *data; int minor; for(minor = GTKHACKRC_VER_MINOR; minor >= 0; minor--) { s = g_strdup_printf("Software\\NetHack\\GtkHack\\%d.%d", GTKHACKRC_VER_MAJOR, minor); retval = RegOpenKeyEx(HKEY_CURRENT_USER, s, 0, KEY_READ, &key); g_free(s); if (retval == ERROR_SUCCESS) break; } if (minor < 0) return TRUE; if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL, &no_values, &value_len, &data_len, NULL, NULL) != ERROR_SUCCESS) { RegCloseKey(key); return TRUE; } value = malloc(value_len + 1); data = malloc(data_len + 1); if (!value || !data) { free(value); free(data); RegCloseKey(key); return FALSE; } clear_unrecognized(); for(i = no_values - 1; i >= 0; i--) { value_count = value_len + 1; data_count = data_len + 1; if (RegEnumValue(key, i, value, &value_count, NULL, &type, data, &data_count) == ERROR_SUCCESS && type == REG_SZ) { scanner->input_name = value; g_scanner_input_text(scanner, value, strlen(value)); retval = parse_lval(scanner, FALSE); if (retval == PARSE_OK) { v = GTKHACKRC(scanner)->value; GTKHACKRC(scanner)->value = NULL; g_scanner_input_text(scanner, data, strlen(data)); retval = parse_value(scanner, FALSE); } if (retval == PARSE_OK) retval = action_setting(scanner, v, GTKHACKRC(scanner)->value); if (retval == PARSE_ERROR) { switch (rc->error_type) { case PARSE_ERROR_TOKEN: g_scanner_get_next_token(scanner); g_scanner_unexp_token(scanner, rc->error.token, NULL, NULL, NULL, NULL, TRUE); break; case PARSE_ERROR_RULE: g_scanner_get_next_token(scanner); g_scanner_unexp_token(scanner, scanner->token == G_TOKEN_SYMBOL ? G_TOKEN_IDENTIFIER : G_TOKEN_SYMBOL, rc->error.rule, rc->error.rule, NULL, NULL, TRUE); break; case PARSE_ERROR_RESOURCE: g_scanner_error(scanner, "%s%s%s", "Resource failure", " - ", "Read of GtkHack profile aborted"); i = -1; break; } } } } free(value); free(data); RegCloseKey(key); return retval != PARSE_ERROR || rc->error_type != PARSE_ERROR_RESOURCE; } #else /* WIN32 */ static int nh_scan_gtkhackrc(GScanner *scanner) { int fd, retval; struct gtkhackrc *rc = GTKHACKRC(scanner); char *home, *file; home = getenv("HOME"); if (!home) return FALSE; /* Can't write file */ file = malloc(strlen(home) + 13 + strlen(GTKHACKRC_VERSION)); if (!file) return FALSE; sprintf(file, "%s/.gtkhackrc-" GTKHACKRC_VERSION, home); fd = open(file, O_RDONLY); if (fd < 0) { write_verspecific_file = FALSE; sprintf(file, "%s/.gtkhackrc", home); fd = open(file, O_RDONLY); if (fd < 0) { free(file); return TRUE; } } else write_verspecific_file = TRUE; scanner->input_name = file; g_scanner_input_file(scanner, fd); clear_unrecognized(); while((retval = parse_setting(scanner, TRUE)) == PARSE_OK) ; if (retval == PARSE_ERROR) { switch (rc->error_type) { case PARSE_ERROR_TOKEN: g_scanner_get_next_token(scanner); g_scanner_unexp_token(scanner, rc->error.token, NULL, NULL, NULL, "Read of GtkHack profile aborted", TRUE); break; case PARSE_ERROR_RULE: g_scanner_get_next_token(scanner); g_scanner_unexp_token(scanner, scanner->token == G_TOKEN_SYMBOL ? G_TOKEN_IDENTIFIER : G_TOKEN_SYMBOL, rc->error.rule, rc->error.rule, NULL, "Read of GtkHack profile aborted", TRUE); break; case PARSE_ERROR_RESOURCE: g_scanner_error(scanner, "%s%s%s", "Resource failure", " - ", "Read of GtkHack profile aborted"); break; case PARSE_ERROR_INCOMPATIBLE: g_scanner_error(scanner, "%s%s%s", "Incompatible profile", " - ", "Read of GtkHack profile aborted"); break; default: g_scanner_error(scanner, "%s%s%s", "Unknown error", " - ", "Read of GtkHack profile aborted"); break; } } close(fd); free(file); return retval != PARSE_ERROR || rc->error_type != PARSE_ERROR_RESOURCE; } #endif /* WIN32 */ int nh_read_gtkhackrc(void) { int retval; struct gtkhackrc rc; GScanner *scanner; GtkHackRcValue *v, *vn; scanner = g_scanner_new(NULL); if (!scanner) return -1; rc.error_type = PARSE_ERROR_NONE; rc.value = NULL; rc.freelist = NULL; scanner->user_data = (gpointer)&rc; scanner->config->scan_float = FALSE; /* Don't treat '.' as special */ /* * Don't update the file on exit if we failed to read it correctly * due to a (presumably temporary) resource failure. */ if (nh_scan_gtkhackrc(scanner)) atexit(nh_write_gtkhackrc); free_value(scanner, rc.value); for(v = rc.freelist; v; ) { vn = v->u.next; g_free(v); v = vn; } g_scanner_destroy(scanner); return 0; } void nh_gtkhackrc_store(struct gtkhackrc *rc, const char *fmt, ...) { #ifdef WIN32 gchar *value, *data, *s; #endif va_list args; va_start(args, fmt); #ifdef WIN32 value = g_strdup_vprintf(fmt, args); s = strchr(value, '='); if (s) { for(data = s + 1; *data == ' '; data++) ; for(s--; *s == ' '; s--) ; s[1] = '\0'; RegSetValueEx(rc->key, value, 0, REG_SZ, data, strlen(data) + 1); } free(value); #else vfprintf(rc->fp, fmt, args); putc('\n', rc->fp); #endif va_end(args); } void nh_write_gtkhackrc(void) { struct gtkhackrc rc; GSList *list; #ifdef WIN32 RegDeleteKey(HKEY_CURRENT_USER, "Software\\NetHack\\GtkHack\\" GTKHACKRC_VERSION); RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\NetHack\\GtkHack\\" GTKHACKRC_VERSION, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &rc.key, NULL); #else char *home, *file; home = getenv("HOME"); if (!home) return; if (write_verspecific_file) file = malloc(strlen(home) + 13 + strlen(GTKHACKRC_VERSION)); else file = malloc(strlen(home) + 12); if (!file) return; if (write_verspecific_file) sprintf(file, "%s/.gtkhackrc-" GTKHACKRC_VERSION, home); else sprintf(file, "%s/.gtkhackrc", home); rc.fp = fopen(file, "w"); if (!rc.fp) { perror(file); free(file); return; } fprintf(rc.fp, "# This file is generated by the Gtk interface of NetHack & friends.\n" "# Do not edit.\n" "\n" "version = \"" GTKHACKRC_VERSION "\"\n"); #endif nh_session_save(&rc); GTK_preferences_save(&rc); #ifdef GTKHACK GTK_connection_save(&rc); #endif #ifdef GTK_PROXY nh_proxy_cache_save(&rc); #endif nh_help_save(&rc); if (unrecognized_settings) for(list = unrecognized_settings; list; list = list->next) nh_gtkhackrc_store(&rc, "%s", (gchar *)list->data); #ifdef WIN32 RegCloseKey(rc.key); #else fclose(rc.fp); free(file); #endif } /* Helper functions for handlers to check the value being set and, for * functions, the parameters. */ int gtkhackrc_check_no(GScanner *scanner, GtkHackRcVList *params, const char *names, int mn, int mx) { if (params->n_values > mx) { g_scanner_warn(scanner, "too many %s to function `%s'", names, GTKHACKRC(scanner)->variable); return FALSE; } else if (params->n_values < mn) { g_scanner_warn(scanner, "too few %s to function `%s'", names, GTKHACKRC(scanner)->variable); return FALSE; } else return TRUE; } int gtkhackrc_check_type(GScanner *scanner, GtkHackRcValue *value, const char *name, int type) { char *types[]={"number", "string", "variable", "function", "record", "vector", "value list"}; if (value->type != type) { g_scanner_warn(scanner, "incompatible type for %s of `%s'", name, GTKHACKRC(scanner)->variable); g_scanner_warn(scanner, "(expected %s, got %s)", types[type], value->type >= 0 && value->type < SIZE(types) ? types[value->type] : "value of unknown type"); return FALSE; } else return TRUE; } int check_rc_version(GScanner *scanner, GtkHackRcValue *value) { int major, minor; if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_STRING)) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_INCOMPATIBLE; #ifndef WIN32 write_verspecific_file = TRUE; #endif return PARSE_ERROR; } if (sscanf(value->u.string, "%d.%d", &major, &minor) != 2) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_INCOMPATIBLE; #ifndef WIN32 write_verspecific_file = TRUE; #endif return PARSE_ERROR; } if (major != GTKHACKRC_VER_MAJOR || minor > GTKHACKRC_VER_MINOR) { GTKHACKRC(scanner)->error_type = PARSE_ERROR_INCOMPATIBLE; #ifndef WIN32 if (major >= GTKHACKRC_VER_MAJOR) write_verspecific_file = TRUE; #endif return PARSE_ERROR; } return 0; } void rc_window_position(GScanner *scanner, GtkHackRcVList *params, GtkHackRcValue *value) { if (!gtkhackrc_check_no(scanner, params, "arguments", 1, 1) || !gtkhackrc_check_type(scanner, params->values[0], "argument 1", PARSE_VALUE_TYPE_STRING) || !gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_RECORD) || !gtkhackrc_check_no(scanner, &value->u.record, "fields in value", 2, 2) || !gtkhackrc_check_type(scanner, value->u.record.values[0], "x- value", PARSE_VALUE_TYPE_NUMBER) || !gtkhackrc_check_type(scanner, value->u.record.values[1], "y- value", PARSE_VALUE_TYPE_NUMBER)) return; nh_session_set_geometry(params->values[0]->u.string, value->u.record.values[0]->u.number, value->u.record.values[1]->u.number, -1, -1); } void rc_window_size(GScanner *scanner, GtkHackRcVList *params, GtkHackRcValue *value) { if (!gtkhackrc_check_no(scanner, params, "arguments", 1, 1) || !gtkhackrc_check_type(scanner, params->values[0], "argument 1", PARSE_VALUE_TYPE_STRING) || !gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_RECORD) || !gtkhackrc_check_no(scanner, &value->u.record, "fields in value", 2, 2) || !gtkhackrc_check_type(scanner, value->u.record.values[0], "width", PARSE_VALUE_TYPE_NUMBER) || !gtkhackrc_check_type(scanner, value->u.record.values[1], "height", PARSE_VALUE_TYPE_NUMBER)) return; nh_session_set_geometry(params->values[0]->u.string, -1, -1, value->u.record.values[0]->u.number, value->u.record.values[1]->u.number); } void rc_map_font(GScanner *scanner, GtkHackRcValue *value) { if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_STRING)) return; nh_set_map_font(value->u.string); } void rc_map_clip_dist2(GScanner *scanner, GtkHackRcValue *value) { if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_NUMBER)) return; map_clip_dist2 = value->u.number; nh_map_check_visibility(); } void rc_radar(GScanner *scanner, GtkHackRcValue *value) { if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_NUMBER)) return; nh_radar_set_use(value->u.number); } #ifdef GTKHACK void rc_connections(GScanner *scanner, GtkHackRcValue *value) { int i, j, n, nflags; unsigned long flags; GtkHackRcValue *con, *flag; if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_VECTOR)) return; n = value->u.vector.n_values; for(i = 0; i < n; i++) { con = value->u.vector.values[i]; if (gtkhackrc_check_type(scanner, con, "connection", PARSE_VALUE_TYPE_RECORD) && gtkhackrc_check_no(scanner, &con->u.record, "fields in connection", 3, 4) && gtkhackrc_check_type(scanner, con->u.record.values[0], "name", PARSE_VALUE_TYPE_STRING) && gtkhackrc_check_type(scanner, con->u.record.values[1], "scheme", PARSE_VALUE_TYPE_STRING) && gtkhackrc_check_type(scanner, con->u.record.values[2], "address", PARSE_VALUE_TYPE_STRING)) { if (con->u.record.n_values < 4 || gtkhackrc_check_type(scanner, con->u.record.values[3], "flags", PARSE_VALUE_TYPE_VECTOR)) { flags = 0; if (con->u.record.n_values >= 4) { nflags = con->u.record.values[3]->u.vector.n_values; for(j = 0; j < nflags; j++) { flag = con->u.record.values[3]->u.vector.values[j]; if (gtkhackrc_check_type(scanner, flag, "flag", PARSE_VALUE_TYPE_STRING)) { if (!strcmp(flag->u.string, "synchronous")) flags |= PROXY_CLNT_SYNCHRONOUS; else if (!strcmp(flag->u.string, "logged")) flags |= PROXY_CLNT_LOGGED; } } } GTK_connection_add(con->u.record.values[0]->u.string, con->u.record.values[1]->u.string, con->u.record.values[2]->u.string, flags); } } } } void rc_default_connection(GScanner *scanner, GtkHackRcValue *value) { if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_STRING)) return; GTK_connection_set_default(value->u.string); } #endif #ifdef GTK_PROXY void rc_proxy_cachedir(GScanner *scanner, GtkHackRcValue *value) { if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_STRING)) return; nh_proxy_cache_set_dir(value->u.string); } #endif void rc_help_font(GScanner *scanner, GtkHackRcValue *value) { if (!gtkhackrc_check_type(scanner, value, "value", PARSE_VALUE_TYPE_STRING)) return; nh_set_help_font(value->u.string); }