/* 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 #ifdef GENDER_BOOL_LOCK #define MALE_LOCK -100 #define FEMALE_LOCK -101 #define NEUTER_LOCK -102 #endif /* GENDER_BOOL_LOCK */ static char *get_tok(); static char *to_infix(); 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; 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'; #ifdef GENDER_BOOL_LOCK if (!strcmp(q, "sex:male")) { obj = MALE_LOCK; } else if (!strcmp(q, "sex:female")) { obj = FEMALE_LOCK; } else if (!strcmp(q, "sex:it") || !strcmp(q, "sex:neuter")) { obj = NEUTER_LOCK; } else #endif /* GENDER_BOOL_LOCK */ obj = resolve_object(player, q, 1); /* Allow *<playername> */ p[1] = ch; if (obj == -1) { *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 (0); } tp = sp = 0; count = *exp++; while (*exp != STOP && count) { switch (*exp) { case AND: case OR: if (sp < 2 || tp > 63) { log_error("bool_display, player was #%d\n", player); 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, ret; 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; #ifdef GENDER_BOOL_LOCK 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; #endif /* GENDER_BOOL_LOCK */ default: if (s < 9) { return (b); } ret = stuff_name(player, trees[t].dat, b, s - 1); if (ret == -1) { strcpy(b, "<unknown>"); p = b + 9; } else { p = b + ret; } break; } return (p); } /* * Returns 1 if thing is locked against player, 0 if not. */ int islocked(player, thing) int player; int thing; { int *exp; int sp, op1, op2; int ret; if (ret = isflaglocked(player, thing)) return (ret); if (get_lock_elt(thing, LOCK, &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; #ifdef GENDER_BOOL_LOCK 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; #endif /* GENDER_BOOL_LOCK */ } } 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) { log_error("islocked: player = %d, thing = %d\n", player, thing); 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); } #ifdef GENDER_BOOL_LOCK static int check_player_gender(player, code) int player; int code; { char *gender; if (get_str_elt(player, GENDER, &gender) == -1) { warning("check_player_gender", "bad gender ref on player"); return (0); } if (gender == NULL) return (0); switch (code) { case MALE_LOCK: if (*gender == 'm' || *gender == 'M') { return (1); } else { return (0); } break; case FEMALE_LOCK: if (*gender == 'f' || *gender == 'F') { return (1); } else { return (0); } break; case NEUTER_LOCK: if (*gender == 'i' || *gender == 'I' || *gender == 'n' || *gender == 'N') { return (1); } else { return (0); } break; default: warning("check_player_gender", "bad code"); return (0); } return (0); } #endif /* GENDER_BOOL_LOCK */ int isflaglocked(player, thing) int player; int thing; { if (ishaven(thing) && ishaven(player)) { return 1; } else { if (isexit(thing) && istemple(thing) && istemple(player)) { return 1; } else { if (iswiz(thing) && !iswiz(player)) { return 1; } else { return 0; } } } }