/* predicat.c */ #include "copyrite.h" #include "config.h" /* Predicates for testing various conditions */ #include <stdio.h> #ifdef I_STDARG #include <stdarg.h> #else #include <varargs.h> #endif #include <ctype.h> #ifdef I_STRING #include <string.h> #else #include <strings.h> #endif #ifdef I_SYS_TYPES #include <sys/types.h> #endif #ifdef I_SYS_TIME #include <sys/time.h> #else #include <time.h> #endif #ifdef I_STDLIB #include <stdlib.h> #endif #include "conf.h" #include "externs.h" #include "mushdb.h" #include "intrface.h" #include "globals.h" #include "match.h" #include "ansi.h" #include "parse.h" #include "dbdefs.h" #ifdef MEM_CHECK #include "memcheck.h" #endif #include "mymalloc.h" #include "confmagic.h" extern int first_free; /* free object list, from destroy.c */ extern int quick_wild _((const char *tstr, const char *dstr)); int could_doit _((dbref player, dbref thing)); void charge_action _((dbref player, dbref thing, const char *awhat)); void did_it _((dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc)); int can_see _((dbref player, dbref thing, int can_see_loc)); int controls _((dbref who, dbref what)); int can_pay_fees _((dbref who, int pennies)); void giveto _((dbref who, dbref pennies)); int payfor _((dbref who, int cost)); int forbidden_name _((const char *name)); int ok_name _((const char *name)); int ok_player_name _((const char *name)); int ok_password _((const char *password)); void sstrcat _((char *string, char *app)); void do_switch _((dbref player, char *expression, char **argv, dbref cause, int first)); dbref parse_match_possessive _((dbref player, const char *str)); void page_return _((dbref player, dbref target, const char *type, const char *message, const char *def)); dbref where_is _((dbref thing)); int nearby _((dbref obj1, dbref obj2)); void do_verb _((dbref player, dbref cause, char *arg1, char **argv)); char *grep_util _((dbref player, dbref thing, char *pattern, char *lookfor, int len, int insensitive)); static int grep_util_helper _((dbref player, dbref thing, char const *pattern, ATTR *atr, void *args)); static int grep_helper _((dbref player, dbref thing, char const *pattern, ATTR *atr, void *args)); void do_grep _((dbref player, char *obj, char *lookfor, int flag, int insensitive)); char *tprintf _((const char *fmt,...)); #ifdef I_STDARG char * tprintf(const char *fmt,...) #else char * tprintf(va_alist) va_dcl #endif { /* this is a generic function used to generate a format string */ static char buff[BUFFER_LEN * 3]; /* safety margin */ va_list args; #ifndef I_STDARG char *fmt; va_start(args); fmt = va_arg(args, char *); #else va_start(args, fmt); #endif (void) vsprintf(buff, fmt, args); buff[BUFFER_LEN - 1] = '\0'; va_end(args); return (buff); } int could_doit(player, thing) dbref player; dbref thing; { /* lock evaluation -- determines if player passes lock on thing, for * the purposes of picking up an object or moving through an exit */ if (Typeof(thing) != TYPE_ROOM && Location(thing) == NOTHING) return 0; return (eval_lock(player, thing, Basic_Lock)); } void charge_action(player, thing, awhat) dbref player; dbref thing; const char *awhat; { ATTR *d; ATTR *b; char tbuf[BUFFER_LEN]; char tbuf2[BUFFER_LEN]; int num; if (awhat && *awhat && (d = atr_get(thing, awhat))) { strcpy(tbuf, uncompress(d->value)); /* check if object has # of charges */ b = atr_get_noparent(thing, "CHARGES"); if (!b) { /* no charges set, just execute the action */ parse_que(thing, tbuf, player); return; } else { strcpy(tbuf2, uncompress(b->value)); num = atoi(tbuf2); if (num) { /* charges left, decrement and execute */ (void) atr_add(thing, "CHARGES", tprintf("%d", num - 1), db[b->creator].owner, NOTHING); parse_que(thing, tbuf, player); return; } else if (!(d = atr_get(thing, "RUNOUT"))) /* no charges left and no runout; do nothing */ return; /* no charges left, execute runout */ strcpy(tbuf, uncompress(d->value)); parse_que(thing, tbuf, player); } } } void did_it(player, thing, what, def, owhat, odef, awhat, loc) dbref player; dbref thing; const char *what; const char *def; const char *owhat; const char *odef; const char *awhat; dbref loc; { /* executes the @attr, @oattr, @aattr for a command - gives a message * to the enactor and others in the room with the enactor, and executes * an action. */ ATTR *d; char buff[BUFFER_LEN], *bp, *sp; char const *asave, *ap; int j; char *preserve[10]; int need_pres = 0; loc = (loc == NOTHING) ? db[player].location : loc; /* only give messages if the location is good */ if (GoodObject(loc)) { /* message to player */ if (what && *what) { d = atr_get(thing, what); if (d) { asave = safe_uncompress(d->value); ap = asave; bp = buff; process_expression(buff, &bp, &ap, thing, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; notify_by(thing,player,buff); free((Malloc_t) asave); } else if (def && *def) notify_by(thing,player, def); } /* message to neighbors */ if (!Dark(player)) { if (owhat && *owhat) { d = atr_get(thing, owhat); if (d) { if (!need_pres) { need_pres = 1; save_global_regs("did_it_save", preserve); } asave = safe_uncompress(d->value); ap = asave; bp = buff; safe_str(Name(player), buff, &bp); safe_chr(' ', buff, &bp); sp = bp; process_expression(buff, &bp, &ap, thing, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; if (bp != sp) notify_except2(Contents(loc), player, thing, buff); free((Malloc_t) asave); } else { if (odef && *odef) { notify_except2(db[loc].contents, player, thing, tprintf("%s %s", Name(player), odef)); } } } } } if (need_pres) restore_global_regs("did_it_save", preserve); for (j = 0; j < 10; j++) { wnxt[j] = NULL; rnxt[j] = NULL; } charge_action(player, thing, awhat); } dbref first_visible(player, thing) dbref player; dbref thing; { int lck = 0; int ldark; dbref loc; if (!(GoodObject(thing))) return NOTHING; loc = (Typeof(thing) == TYPE_EXIT) ? Source(thing) : Location(thing); ldark = (Typeof(loc) == TYPE_PLAYER) ? (Flags(loc) & OPAQUE) : (Flags(loc) & DARK); while (GoodObject(thing)) { if (Dark(thing) || (ldark && !Light(thing))) { if (!lck) { if (See_All(player) || (loc == player) || controls(player, loc)) return thing; lck = 1; } if (controls(player, thing)) return thing; } else { return thing; } thing = Next(thing); } return thing; } int can_see(player, thing, can_see_loc) dbref player; dbref thing; int can_see_loc; { /* * 1) your own body isn't listed in a 'look' 2) exits aren't listed in a * 'look' 3) unconnected (sleeping) players aren't listed in a 'look' */ if (player == thing || Typeof(thing) == TYPE_EXIT || ((Typeof(thing) == TYPE_PLAYER) && !IS(thing, TYPE_PLAYER, PLAYER_CONNECT))) return 0; /* if the room is lit, you can see any non-dark objects */ else if (can_see_loc) return (!Dark(thing)); /* otherwise room is dark and you can only see lit things */ else return (Light(thing) && !Dark(thing)); } int controls(who, what) dbref who; dbref what; { /* Wizard controls everything * owners control their stuff * something which is in the enterlock of a ZMO controls non-INHERIT * and non-player objects. * INHERIT checks between two objects are checked in the code for the * specific function in question (do_trigger, do_set, etc.) * Those who pass the enterlock of a ZoneMaster control his objects, * but not the ZoneMaster himself. */ if (!GoodObject(what)) return 0; if (God(what) && !God(who)) return 0; if (Wizard(who)) return 1; if (Wizard(what) || (Hasprivs(what) && !Hasprivs(who))) return 0; if (Owns(who, what) && (!Inherit(what) || Inherit(who))) return 1; if ((Zone(what) != NOTHING) && (Typeof(what) != TYPE_PLAYER) && !Inherit(what) && (eval_lock(who, Zone(what), Zone_Lock))) return 1; if (ZMaster(Owner(what)) && (Typeof(what) != TYPE_PLAYER) && (eval_lock(who, Owner(what), Zone_Lock))) return 1; return 0; } int can_pay_fees(who, pennies) dbref who; int pennies; { /* does who have enough pennies to pay for something, and if something * is being built, does who have enough quota? Wizards, roys * aren't subject to either. */ #ifdef QUOTA int pay_quota(); #endif /* QUOTA */ /* check database size -- EVERYONE is subject to this! */ if (DBTOP_MAX && (db_top >= DBTOP_MAX + 1) && (first_free == NOTHING)) { notify(who, "Sorry, there is no more room in the database."); return 0; } /* Can they afford it? */ if (!NoPay(who) && (Pennies(Owner(who)) < pennies)) { notify(who, tprintf("Sorry, you don't have enough %s.", MONIES)); return 0; } /* check building quota */ #ifdef QUOTA if (!NoQuota(who) && !pay_quota(who, QUOTA_COST)) { notify(who, "Sorry, your building quota has run out."); return 0; } #endif /* QUOTA */ /* charge */ payfor(who, pennies); return 1; } void giveto(who, pennies) dbref who; dbref pennies; { /* give who pennies */ /* wizards and royalty don't need pennies */ if (NoPay(who)) return; who = Owner(who); s_Pennies(who, Pennies(who) + pennies); } int payfor(who, cost) dbref who; int cost; { /* subtract cost from who's pennies */ dbref tmp; if (NoPay(who)) return 1; else if ((tmp = Pennies(Owner(who))) >= cost) { s_Pennies(Owner(who), tmp - cost); return 1; } else return 0; } #ifdef QUOTA int get_current_quota(who) dbref who; { /* figure out a player's quota. Add the RQUOTA attribute if he doesn't * have one already. This function returns the REMAINING quota, not * the TOTAL limit. */ ATTR *a; int i; int limit; int owned = 0; /* if he's got an RQUOTA attribute, his remaining quota is that */ a = atr_get_noparent(Owner(who), "RQUOTA"); if (a) return (atoi(uncompress(a->value))); /* else, count up his objects. If he has less than the START_QUOTA, * then his remaining quota is that minus his number of current objects. * Otherwise, it's his current number of objects. Add the attribute * if he doesn't have it. */ for (i = 0; i < db_top; i++) if (Owner(i) == Owner(who)) owned++; owned--; /* don't count the player himself */ if (owned <= START_QUOTA) limit = START_QUOTA - owned; else limit = owned; atr_add(Owner(who), "RQUOTA", tprintf("%d", limit), GOD, NOTHING); return (limit); } void change_quota(who, payment) dbref who; int payment; { /* add or subtract from quota */ /* wizards and royalty don't need a quota */ if (NoQuota(Owner(who))) return; atr_add(Owner(who), "RQUOTA", tprintf("%d", get_current_quota(who) + payment), GOD, NOTHING); } int pay_quota(who, cost) dbref who; int cost; { /* determine if we've got enough quota to pay for an object, * and, if so, return true, and subtract from the quota. */ int curr; /* wizards and royalty don't need a quota */ if (NoQuota(Owner(who))) return 1; /* figure out how much we have, and if it's big enough */ curr = get_current_quota(who); if (curr - cost < 0) /* not enough */ return 0; change_quota(who, -cost); return 1; } #endif /* QUOTA */ int forbidden_name(name) const char *name; { /* checks to see if name is in the forbidden names file */ char buf[BUFFER_LEN], *newlin, *ptr; FILE *fp; fp = fopen(NAMES_FILE, "r"); if (!fp) return 0; while (!feof(fp)) { fgets(buf, BUFFER_LEN, fp); /* step on the newline */ if ((newlin = (char *) index(buf, '\n')) != NULL) *newlin = '\0'; ptr = buf; if (name && ptr && quick_wild(ptr, name)) { fclose(fp); return 1; } } fclose(fp); return 0; } int ok_name(name) const char *name; { /* is name valid for an object? */ const char *p; /* No leading spaces */ if (isspace(*name)) return 0; /* only printable characters */ for (p = name; p && *p; p++) { if (!isprint(*p)) return 0; if ((*p == '[') || (*p == ']') || (*p == '%') || (*p == '\\')) return 0; } /* No trailing spaces */ p--; if (isspace(*p)) return 0; /* No magic cookies */ return (name && *name && *name != LOOKUP_TOKEN && *name != NUMBER_TOKEN && *name != NOT_TOKEN && !index(name, ARG_DELIMITER) && !index(name, AND_TOKEN) && !index(name, OR_TOKEN) && strcasecmp(name, "me") && strcasecmp(name, "home") && strcasecmp(name, "here")); } int ok_player_name(name) const char *name; { /* is name okay for a player? */ const char *scan, *good; if (!ok_name(name) || forbidden_name(name) || strlen(name) >= PLAYER_NAME_LIMIT) return 0; good = PLAYER_NAME_SPACES ? " `$_-.,'" : "`$_-.,'"; /* Make sure that the name contains legal characters only */ for (scan = name; scan && *scan; scan++) { if (isalpha(*scan) || isdigit(*scan)) continue; if (!index(good, *scan)) return 0; } return (lookup_player(name) == NOTHING); } int ok_password(password) const char *password; { /* is password an acceptable password? */ const char *scan; if (*password == '\0') return 0; for (scan = password; *scan; scan++) { if (!(isprint(*scan) && !isspace(*scan))) { return 0; } } return 1; } void sstrcat(string, app) char *string; char *app; { char *s; char tbuf1[BUFFER_LEN]; if ((strlen(app) + strlen(string)) >= BUFFER_LEN) return; sprintf(tbuf1, "%s", app); for (s = tbuf1; *s; s++) if ((*s == ',') || (*s == ';')) *s = ' '; strcat(string, tbuf1); } /* for lack of better place the @switch code is here */ void do_switch(player, expression, argv, cause, first) dbref player; char *expression; char **argv; dbref cause; int first; /* 0, match all, 1, match first */ { int any = 0, a; char buff[BUFFER_LEN], *bp; char const *ap; char *tbuf1; if (!argv[1]) return; /* set up environment for any spawned commands */ for (a = 0; a < 10; a++) { wnxt[a] = wenv[a]; rnxt[a] = renv[a]; } /* now try a wild card match of buff with stuff in coms */ for (a = 1; !(first && any) && (a < (MAX_ARG - 1)) && argv[a] && argv[a + 1]; a += 2) { /* eval expression */ ap = argv[a]; bp = buff; process_expression(buff, &bp, &ap, player, cause, cause, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; /* check for a match */ if (local_wild_match(buff, expression)) { any = 1; tbuf1 = replace_string("#$", expression, argv[a + 1]); parse_que(player, tbuf1, cause); free(tbuf1); #ifdef MEM_CHECK del_check("replace_string.buff"); #endif } } /* do default if nothing has been matched */ if ((a < MAX_ARG) && !any && argv[a]) { tbuf1 = replace_string("#$", expression, argv[a]); parse_que(player, tbuf1, cause); free(tbuf1); #ifdef MEM_CHECK del_check("replace_string.buff"); #endif } } dbref parse_match_possessive(player, str) dbref player; const char *str; { char *box; /* name of container */ char *obj; /* name of object */ dbref loc; /* dbref of container */ char name[BUFFER_LEN]; /* str would be destructively modified */ strcpy(name, str); box = name; /* check to see if we have an 's sequence */ if ((obj = (char *) index(name, '\'')) == NULL) return NOTHING; *obj++ = '\0'; /* terminate */ if ((*obj == '\0') || ((*obj != 's') && (*obj != 'S'))) return NOTHING; /* skip over the 's' and whitespace */ do { obj++; } while (isspace(*obj)); /* we already have a terminating null, so we're okay to just do matches */ loc = match_result(player, box, NOTYPE, MAT_NEIGHBOR | MAT_POSSESSION); if (!GoodObject(loc)) return NOTHING; /* now we match on the contents */ return (match_result(loc, obj, NOTYPE, MAT_POSSESSION)); } void page_return(player, target, type, message, def) dbref player; dbref target; const char *type; const char *message; const char *def; { /* code for auto-return page - HAVEN, IDLE, and AWAY messages */ ATTR *d; char buff[BUFFER_LEN], *bp; char const *asave, *ap; struct tm *ptr; time_t t; if (message && *message) { d = atr_get(target, message); if (d) { asave = safe_uncompress(d->value); ap = asave; bp = buff; process_expression(buff, &bp, &ap, target, player, player, PE_DEFAULT, PT_DEFAULT, NULL); *bp = '\0'; free((Malloc_t) asave); if (*buff) { t = time(NULL); ptr = (struct tm *) localtime(&t); notify(player, tprintf("%s message from %s: %s", type, db[target].name, buff)); if (!Haven(target)) notify(target, tprintf("[%d:%02d] %s message sent to %s.", MILITARY_TIME ? ptr->tm_hour : (ptr->tm_hour ? (ptr->tm_hour > 12 ? (ptr->tm_hour - 12) : ptr->tm_hour) : 12), ptr->tm_min, type, Name(player))); } } else if (def && *def) notify(player, def); } } dbref where_is(thing) dbref thing; { /* returns "real" location of object. This is the location for players * and things, source for exits, and NOTHING for rooms. */ if (!GoodObject(thing)) return NOTHING; switch (Typeof(thing)) { case TYPE_ROOM: return NOTHING; case TYPE_EXIT: return Home(thing); default: return Location(thing); } } int nearby(obj1, obj2) dbref obj1; dbref obj2; { /* returns 1 if obj1 is "nearby" object2. "Nearby" is defined as: * obj1 is in the same room as obj2, obj1 is being carried by * obj2, obj1 is carrying obj2. Returns 0 if object isn't nearby * or the input is invalid. */ dbref loc1, loc2; if (!GoodObject(obj1) || !GoodObject(obj2)) return 0; loc1 = where_is(obj1); if (loc1 == obj2) return 1; loc2 = where_is(obj2); if ((loc2 == obj1) || (loc2 == loc1)) return 1; return 0; } void do_verb(player, cause, arg1, argv) dbref player; dbref cause; char *arg1; char **argv; { /* user-defined verbs */ dbref victim; dbref actor; int i; /* find the object that we want to read the attributes off * (the object that was the victim of the command) */ /* our victim object can be anything */ victim = match_result(player, arg1, NOTYPE, MAT_EVERYTHING); if (victim == NOTHING) { notify(player, "What was the victim of the verb?"); return; } /* find the object that executes the action */ if (!argv || !argv[1] || !*argv[1]) { notify(player, "What do you want to do with the verb?"); return; } actor = match_result(player, argv[1], NOTYPE, MAT_NEAR_THINGS); if (actor == NOTHING) { notify(player, "What do you want to do the verb?"); return; } /* Control check is fascist. * First check: we don't want <actor> to do something involuntarily. * Both victim and actor have to be controlled by the thing which did * the @verb (for speed we do a WIZARD check first), or: cause controls * actor plus the second check is passed. * Second check: we need read access to the attributes. * Either the player controls victim or the player * must be priviledged, or the victim has to be VISUAL. */ if (!(Wizard(player) || (controls(player, victim) && controls(player, actor)) || ((controls(cause, actor) && Can_Examine(player, victim))))) { notify(player, "Permission denied."); return; } /* We're okay. Send out messages. */ did_it(actor, victim, upcasestr(argv[2]), argv[3], upcasestr(argv[4]), argv[5], NULL, Location(actor)); /* Now we copy our args into the stack, and do the command. */ for (i = 0; i < 10; i++) wnxt[i] = argv[i + 7]; charge_action(actor, victim, upcasestr(argv[6])); } struct guh_args { char *buff; char *bp; char *lookfor; int len; int insensitive; }; static int grep_util_helper(player, thing, pattern, atr, args) dbref player; dbref thing; char const *pattern; ATTR *atr; void *args; { struct guh_args *guh = args; int found; char *s; s = (char *) uncompress(AL_STR(atr)); /* warning: static */ found = 0; while (*s && !found) { if ((!guh->insensitive && !strncmp(guh->lookfor, s, guh->len)) || (guh->insensitive && !strncasecmp(guh->lookfor, s, guh->len))) found = 1; else s++; } if (found) { if (guh->bp != guh->buff) safe_chr(' ', guh->buff, &guh->bp); safe_str(AL_NAME(atr), guh->buff, &guh->bp); } return found; } char * grep_util(player, thing, pattern, lookfor, len, insensitive) dbref player; dbref thing; char *pattern; char *lookfor; int len; int insensitive; /* strlen(lookfor) */ /* 1 if case-insensitive grep, 0 if not */ { /* returns a list of attributes which match <pattern> on <thing> * whose contents have <lookfor> */ struct guh_args guh; guh.buff = (char *) malloc(BUFFER_LEN + 1); guh.bp = guh.buff; guh.lookfor = lookfor; guh.len = len; guh.insensitive = insensitive; (void) atr_iter_get(player, thing, pattern, grep_util_helper, &guh); *guh.bp = '\0'; return guh.buff; } struct gh_args { char *lookfor; int len; int insensitive; char repl[BUFFER_LEN]; int rlen; }; static int grep_helper(player, thing, pattern, atr, args) dbref player; dbref thing; char const *pattern; ATTR *atr; void *args; { struct gh_args *gh = args; int found; int d; char *s; char tbuf1[BUFFER_LEN]; found = 0; s = (char *) uncompress(AL_STR(atr)); /* warning: static */ for (d = 0; (d < BUFFER_LEN) && *s;) { if ((gh->insensitive && !strncasecmp(gh->lookfor, s, gh->len)) || (!gh->insensitive && !strncmp(gh->lookfor, s, gh->len))) { found = 1; if ((d + gh->rlen) < BUFFER_LEN) { strcpy(tbuf1 + d, gh->repl); d += gh->rlen; s += gh->len; } else tbuf1[d++] = *s++; } else tbuf1[d++] = *s++; } tbuf1[d++] = 0; /* if we got it, display it */ if (found) notify(player, tprintf("%s%s [#%d%s%s %s", ANSI_HILITE, AL_NAME(atr), Owner(AL_CREATOR(atr)), (AL_FLAGS(atr) & AF_LOCKED) ? "+]:" : "]:", ANSI_NORMAL, tbuf1)); return found; } void do_grep(player, obj, lookfor, flag, insensitive) dbref player; char *obj; char *lookfor; int flag; int insensitive; /* this is an UNPARSED arg */ /* 0 for just list, 1 for hilites */ /* 0 for case-sensitive, 1 if not */ { struct gh_args gh; dbref thing; char *pattern; int len; char *tp; /* check this first */ if (flag && !ShowAnsi(player)) { notify(player, "You must be set ANSI to use this option."); return; } if ((len = strlen(lookfor)) < 1) { notify(player, "What pattern do you want to grep for?"); return; } /* find the attribute pattern */ pattern = (char *) index(obj, '/'); if (!pattern) pattern = (char *) "*"; /* set it to global match */ else *pattern++ = '\0'; /* now we've got the object. match for it. */ if ((thing = noisy_match_result(player, obj, NOTYPE, MAT_EVERYTHING)) == NOTHING) return; if (!Can_Examine(player, thing)) { notify(player, "Permission denied."); return; } /* we can think of adding in the hilites like doing a find and replace */ sprintf(gh.repl, "%s%s%s", ANSI_HILITE, lookfor, ANSI_NORMAL); gh.rlen = strlen(gh.repl); if (flag) { gh.lookfor = lookfor; gh.len = len; gh.insensitive = insensitive; (void) atr_iter_get(player, thing, pattern, grep_helper, &gh); } else { tp = grep_util(player, thing, pattern, lookfor, len, insensitive); notify(player, tprintf("Matches of '%s' on %s(#%d): %s", lookfor, Name(thing), thing, tp)); free(tp); } }