/* boolexp.c */ #include "copyright.h" #include "config.h" #include <stdio.h> #ifdef STRING_H #include <string.h> #else #include <strings.h> #endif /* STRING_H */ #include <ctype.h> #include "teeny.h" /* * Routines for parsing, evaluating and displaying boolean expressions. */ /* * Opcodes for our little Boolean expression evaluator. */ #define STOP -1 #define AND -2 #define OR -3 #define NOT -4 #define OPEN -5 #define CLOSE -6 #define BADREF -7 #define MALE_LOCK -100 #define FEMALE_LOCK -101 #define NEUTER_LOCK -102 #define WIZARD_LOCK -200 #define ROBOT_LOCK -201 static char *get_tok(); static char *to_infix(); void parse_lock(); static int stack[64]; /* Expressions can't be too big. */ static int work[256]; static char toocomplex[] = "Lock is too complex\r\n"; static char badlock[] = "I don't understand that lock.\r\n"; /* * We use these structs, an array of them, to build parse trees from our * internal RPN locks so we can write them out in infix. */ struct tree { int dat; short left, right; /* Array offsets */ }; /* * Parses a boolean expression, stuffs it into a hunk of memory, and returns * a pointer to said hunk. Returns NULL if it can't cope. */ int * bool_parse(player, str) int player; char *str; { int wp, sp; int tok, *thelock; sp = 0; wp = 1; if (str == NULL) { notify_player(player, badlock); return (NULL); } do { str = get_tok(player, str, &tok); if (tok == BADREF) { notify_player(player, badlock); return (NULL); } switch (tok) { case OPEN: if (sp < 63) { stack[sp++] = OPEN; } else { notify_player(player, toocomplex); return (NULL); } break; case CLOSE: while (sp > 0 && stack[sp - 1] != OPEN && wp < 255) { work[wp++] = stack[--sp]; } if (sp == 0 || stack[sp - 1] != OPEN) { notify_player(player, "Too complex, or unbalanced parens.\r\n"); return (NULL); } sp--; break; case AND: if (sp > 0 && stack[sp - 1] == OR) { notify_player(player, badlock); return (NULL); } while (sp > 0 && stack[sp - 1] == NOT && wp < 255) { work[wp++] = stack[--sp]; } if (wp == 255) { notify_player(player, toocomplex); return (NULL); } if (sp < 63) { stack[sp++] = AND; } else { notify_player(player, toocomplex); return (NULL); } break; case OR: if (sp > 0 && (stack[sp - 1] == AND || stack[sp - 1] == NOT)) { notify_player(player, badlock); return (NULL); } while (sp > 0 && (stack[sp - 1] == NOT || stack[sp - 1] == AND) && wp < 255) { work[wp++] = stack[--sp]; } if (wp == 255) { notify_player(player, toocomplex); return (NULL); } if (sp < 63) { stack[sp++] = OR; } else { notify_player(player, toocomplex); return (NULL); } break; case NOT: if (sp < 63) { stack[sp++] = NOT; } else { notify_player(player, toocomplex); return (NULL); } break; case STOP: while (sp > 0 && wp < 255) { if (stack[sp - 1] != AND && stack[sp - 1] != OR && stack[sp - 1] != NOT) { notify_player(player, badlock); return (NULL); } work[wp++] = stack[--sp]; } if (wp == 255) { notify_player(player, toocomplex); return (NULL); } work[wp++] = STOP; break; default: work[wp++] = tok; break; } } while (tok != STOP); /* Stow it away somewhere. */ thelock = (int *) ty_malloc(sizeof(int) * wp, "bool_parse"); thelock[0] = --wp; while (wp > 0) { thelock[wp] = work[wp]; wp--; } return (thelock); } /* * Grabs the next token out a string, and returns a pointer to the next thing * in the string. */ static char * get_tok(player, p, tok) char *p; int *tok; { char *q, ch; int obj; while (*p && isspace(*p)) p++; switch (*p) { case '&': *tok = AND; break; case '|': *tok = OR; break; case '!': *tok = NOT; break; case '(': *tok = OPEN; break; case ')': *tok = CLOSE; break; case '\0': *tok = STOP; break; default: /* Snarf out an object name */ q = p; while (*p != '&' && *p != '|' && *p != '!' && *p != ')' && *p) { p++; } p--; while (isspace(*p)) p--; ch = p[1]; p[1] = '\0'; if (!strcasecmp(q, "sex:male")) { obj = MALE_LOCK; } else if (!strcasecmp(q, "sex:female")) { obj = FEMALE_LOCK; } else if (!strcasecmp(q, "sex:it") || !strcasecmp(q, "sex:neuter")) { obj = NEUTER_LOCK; } else if (!strcasecmp(q, "flag:wizard")) { obj = WIZARD_LOCK; } else if (!strcasecmp(q, "flag:robot")) { obj = ROBOT_LOCK; } else obj = resolve_object(player, q, 1); /* Allow *<playername> */ p[1] = ch; if (!exists_object(obj) && obj > -2) { *tok = BADREF; } else { *tok = obj; } } /* p points at the last char of the token. */ p++; return (p); } /* * Writes a boolean expression into a sized buffer. */ int bool_display(player, exp, buff, buffsiz) int player; int *exp; char *buff; int buffsiz; { struct tree trees[64]; int tp; char *p; int sp; int count; if (exp == NULL) { strcpy(buff, "*UNLOCKED*"); return (1); } tp = sp = 0; count = *exp++; while (*exp != STOP && count) { switch (*exp) { case AND: case OR: if (sp < 2 || tp > 63) { warning("bool_display", "lock bad or too big"); return (-1); } trees[tp].dat = *exp; trees[tp].left = (short) (stack[--sp]); trees[tp].right = (short) (stack[--sp]); stack[sp++] = tp++; break; case NOT: if (sp < 1 || tp > 63) { warning("bool_display", "lock bad or too big"); return (-1); } trees[tp].dat = NOT; trees[tp].right = (short) (stack[--sp]); stack[sp++] = tp++; break; default: if (sp > 255 || tp > 63) { warning("bool_display", "lock bad or too big"); return (-1); } trees[tp].dat = *exp; if (sp < 63) { stack[sp++] = tp++; } else { warning("to_infix", "Internal lock too complex!"); return (-1); } break; } exp++; } /* OK. We've built this parse tree thing. Now traverse the tree. */ if (sp != 1) { warning("bool_display", "bad internal boolean expression"); return (-1); } p = to_infix(player, trees, tp - 1, buff, buffsiz); *p = '\0'; return (0); } /* * Convert a tree to infix. Recursive as hell. */ static char * to_infix(player, trees, t, b, s) int player; struct tree *trees; int t; char *b; int s; { char *p; if (s < 5) { return (b); } switch (trees[t].dat) { case AND: *b++ = '('; p = to_infix(player, trees, trees[t].left, b, s); if (s - (p - b) < 6) { return (p); } *p++ = ' '; *p++ = '&'; *p++ = ' '; p = to_infix(player, trees, trees[t].right, p, s - (p - b)); *p++ = ')'; break; case OR: *b++ = '('; p = to_infix(player, trees, trees[t].left, b, s); if (s - (p - b) < 5) { return (p); } *p++ = ' '; *p++ = '|'; *p++ = ' '; p = to_infix(player, trees, trees[t].right, p, s - (p - b)); *p++ = ')'; break; case NOT: if (s < 4) { return (p); } *b++ = '!'; *b++ = '('; p = to_infix(player, trees, trees[t].right, b, s - 2); *p++ = ')'; break; case MALE_LOCK: if (s < 8) { return (b); } strcpy(b, "sex:male"); p = b + 8; break; case FEMALE_LOCK: if (s < 10) { return (b); } strcpy(b, "sex:female"); p = b + 10; break; case NEUTER_LOCK: if (s < 6) { return (b); } strcpy(b, "sex:it"); p = b + 6; break; case WIZARD_LOCK: if (s < 11) { return (b); } strcpy(b, "flag:WIZARD"); p = b + 11; break; case ROBOT_LOCK: if (s < 13) { return (b); } strcpy(b, "flag:ROBOT"); p = b + 13; break; default: if (s < 9) { return (b); } else { char *z; z = stuff_name(player, trees[t].dat); if (strlen(z) > (s - 1)) z[s - 2] = 0; /* just trunicate it */ (void) strcpy(b, z); p = b + strlen(z); } break; } return (p); } /* * Returns 1 if thing is locked against player, 0 if not. */ int islocked(player, thing, code) int player; int thing; int code; { int *exp; int sp, op1, op2; if ((code != LOCK) && (code != ELOCK)) { warning("islocked", "bad code"); return (0); } if (get_lock_elt(thing, code, &exp) == -1) { warning("islocked", "bad lock reference"); return (0); } if (exp == NULL) { /* No lock */ return (0); } /* Run down the expression until you hit a STOP */ exp++; /* Skip the count field */ sp = 0; while (*exp != STOP) { if (*exp < 0) { switch (*exp) { case NOT: op1 = stack[--sp]; stack[sp++] = !op1; break; case AND: op1 = stack[--sp]; op2 = stack[--sp]; stack[sp++] = op1 && op2; break; case OR: op1 = stack[--sp]; op2 = stack[--sp]; stack[sp++] = op1 || op2; break; case MALE_LOCK: stack[sp++] = (check_player_gender(player, MALE_LOCK) ? TRUE : FALSE); break; case FEMALE_LOCK: stack[sp++] = (check_player_gender(player, FEMALE_LOCK) ? TRUE : FALSE); break; case NEUTER_LOCK: stack[sp++] = (check_player_gender(player, NEUTER_LOCK) ? TRUE : FALSE); break; case WIZARD_LOCK: stack[sp++] = (iswiz(player) ? TRUE : FALSE); break; case ROBOT_LOCK: stack[sp++] = (isrobot(player) ? TRUE : FALSE); break; } } else { /* See if the player has this object */ if (sp < 63) { stack[sp++] = (player_has(player, *exp) ? TRUE : FALSE); } else { warning("islocked", "Internal lock too complex!"); return (1); } } exp++; } if (sp != 1) { warning("islocked", "bad internal boolean expression"); return (0); } return (!stack[0]); } /* * Basic tester, sees if a player is or has a specific object. */ int player_has(player, obj) int player; int obj; { int current; if (player == obj) { return (1); } /* Search the list */ if (get_int_elt(player, CONTENTS, ¤t) == -1) { warning("player_has", "bad contents ref on player"); return (0); } while (current != -1) { if (current == obj) { return (1); } if (get_int_elt(current, NEXT, ¤t) == -1) { warning("player_has", "bad NEXT ref"); return (0); } } /* Didn't have it, I guess... */ return (0); } static int check_player_gender(player, code) int player; int code; { int flags; if (get_int_elt(player, FLAGS, &flags) == -1) { warning("check_player_gender", "bad flags ref on player"); return (0); } switch (code) { case MALE_LOCK: return ((flags & GENDER_MALE) ? 1 : 0); break; case FEMALE_LOCK: return ((flags & GENDER_FEMALE) ? 1 : 0); break; case NEUTER_LOCK: return ((flags & GENDER_NEUTER) ? 1 : 0); break; default: warning("check_player_gender", "bad code"); return (0); } return (0); } /* @unlock, @lock moved here from buildcmds.c */ void parse_lock(str, obj, attr) char *str, **obj; int *attr; { char *p; *attr = LOCK; *obj = str; for (p = str; *p && *p != '/'; p++); if (*p == '/') *p++ = 0; /* term obj */ else /* obj is already terminated */ return; /* parse attr */ if (stringprefix(p, "ELOCK")) { *attr = ELOCK; return; } else if (stringprefix(p, "LOCK")) { *attr = LOCK; return; } else *attr = -1; } voidfunc do_unlock(player, arg) int player; char *arg; { int attr, obj; char *objname; if (!arg || !*arg) { notify_player(player, "Unlock what?\r\n"); return; } parse_lock(arg, &objname, &attr); if (attr == -1) { notify_player(player, "No such attribute.\r\n"); return; } if ((obj = resolve_object(player, objname, 0)) == -1) if ((obj = resolve_exit(player, objname)) == -1) { notify_player(player, "I don't see that here.\r\n"); return; } if (obj == -2) { notify_player(player, "I can't tell which one you mean.\r\n"); return; } if (!controls(player, obj)) { notify_player(player, "You can't unlock that!\r\n"); return; } if (set_lock_elt(obj, attr, (int *) NULL) == -1) { notify_bad(player); return; } notify_player(player, "Unlocked.\r\n"); } voidfunc do_lock(player, argone, argtwo) int player; char *argone; char *argtwo; { int obj, *exp, attr; char *objname; if (!argone || !*argone) { notify_player(player, "Lock what to what?\r\n"); return; } parse_lock(argone, &objname, &attr); if (attr == -1) { notify_player(player, "No such attribute.\r\n"); return; } if ((obj = resolve_object(player, objname, 0)) == -1) { if ((obj = resolve_exit(player, objname)) == -1) { notify_player(player, "I don't see that here.\r\n"); return; } } if (obj == -2) { notify_player(player, "I don't know which one you mean.\r\n"); return; } if (!controls(player, obj)) { notify_player(player, "You can't do that!\r\n"); return; } /* OK. We can mess with the lock on this thing. */ exp = bool_parse(player, argtwo); if (exp == NULL) return; /* bool_parse() already told the player off. */ if (set_lock_elt(obj, attr, exp) == -1) { notify_bad(player); return; } notify_player(player, "Locked.\r\n"); }