/* * Code by Scott Bigham */ /* * Helper functions for "process_pref_file()" to evaluate conditional * expressions * * The following expression syntax is recognized: * * expr | expr Logical OR * expr & expr Logical AND * ! expr Logical NOT * ( expr ) Grouping * "string" True if player name exactly matches string * (case-sensitive) * token True if token matches (case-sensitive) the * player's race, class or sex or the three- * letter system identifier the Angband executable * was built with * 0 False * 1 True * * For instance, this expression is true on an X11 system for all male * characters not named Fred, and for all hobbit characters except rogues: * * (x11 | xaw) & ((!"Fred" & Male) | (Hobbit & !Rogue)) * * Input: * sp: pointer to current position in expression string * * Output: * 1 for true, 0 for false, <0 for error * * Should probably do: * SYSTEM="mac" & !(RACE="Gnome") */ /* Convenient macro to skip white space */ #define SKIP_WS \ do { while (**sp && isspace(**sp)) (*sp)++; } while (0) /* Forward declaration */ static int process_pref_file_or_expr(char **); /* * Handle 0, 1, token, !expr, (expr) and "playername" */ static int process_pref_file_primary_expr(char **sp) { int v = -5; char *s1, *s2; SKIP_WS; if (**sp == '0' || **sp == '1') { v = (**sp == '1'); (*sp)++; } else if (**sp == '(') { (*sp)++; v = process_pref_file_or_expr(sp); if (**sp == ')') (*sp)++; else v = -1; } else if (**sp == '!') { (*sp)++; v = process_pref_file_primary_expr(sp); v = (v < 0 ? v : !v); } else if (**sp == '"') { s1 = *sp + 1; s2 = s1 + 1; while (*s2 && *s2 != '"') s2++; if (*s2 != '"') v = -2; else v = !strncmp(s1, player_name, s2 - s1); *sp = s2 + 1; } else if (isalpha(**sp)) { s1 = s2 = *sp; while (isalnum(*s2) || *s2 == '-') s2++; v = (!strncmp(s1, ANGBAND_SYS, (s2 - s1)) || !strncmp(s1, rp_ptr->title, (s2 - s1)) || !strncmp(s1, cp_ptr->title, (s2 - s1)) || !strncmp(s1, sp_ptr->title, (s2 - s1))); *sp = s2; } else { v = -3; } return v; } /* * Handle expr & expr [ & expr ... ] */ static int process_pref_file_and_expr(char **sp) { int v, v2; v = process_pref_file_primary_expr(sp); if (v < 0) return v; SKIP_WS; while (**sp && **sp == '&') { (*sp)++; v2 = process_pref_file_primary_expr(sp); if (v2 < 0) return v2; v = (v && v2); SKIP_WS; } return v; } /* * Handle expr | expr [ | expr ... ] */ static int process_pref_file_or_expr(char **sp) { int v, v2; v = process_pref_file_and_expr(sp); if (v < 0) return v; SKIP_WS; while (**sp && **sp == '|') { (*sp)++; v2 = process_pref_file_and_expr(sp); if (v2 < 0) return v2; v = (v || v2); SKIP_WS; } return v; } static int process_pref_file_expr(char **sp) { int v; v = process_pref_file_or_expr(sp); SKIP_WS; if (**sp != '\0') v = -4; return v; } #undef SKIP_WS /* * Process the "user pref file" with the given name * * See the function above for a list of legal "commands". * * We also accept the special "?" and "%" directives, which * allow conditional evaluation and filename inclusion. */ errr process_pref_file(cptr name) { FILE *fp; char buf[1024]; int num = -1; errr err = 0; bool bypass = FALSE; /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_USER, name); /* Open the file */ fp = my_fopen(buf, "r"); /* No such file */ if (!fp) return (-1); /* Process the file */ while (0 == my_fgets(fp, buf, 1024)) { /* Count lines */ num++; /* Skip "empty" lines */ if (!buf[0]) continue; /* Skip "blank" lines */ if (isspace(buf[0])) continue; /* Skip comments */ if (buf[0] == '#') continue; /* Process "?:" */ if ((buf[0] == '?') && (buf[1] == ':')) { int v; char *s; /* Start */ s = buf + 2; /* Parse the expr */ v = process_pref_file_expr(&s); /* Check for error */ if (v < 0) { err = v; break; } /* Set flag */ bypass = !v; /* Continue */ continue; } /* Apply conditionals */ if (bypass) continue; /* Process "%:" */ if (buf[0] == '%') { /* Process that file if allowed */ (void)process_pref_file(buf + 2); /* Continue */ continue; } /* Process the line */ err = process_pref_file_aux(buf); /* Oops */ if (err) break; } /* Error */ if (err) { /* Useful error message */ msg_format("Error %d in line %d of file '%s'.", err, num, name); msg_format("Parsing '%s'", buf); } /* Close the file */ my_fclose(fp); /* Result */ return (err); }