/* predicates.c */ #include "copyright.h" #include <varargs.h> #include <ctype.h> #ifdef WANT_ANSI #ifdef __STDC__ #include <stddef.h> #include <stdlib.h> #endif /* __STDC__ */ #endif /* WANT_ANSI */ #include "mudconf.h" #include "config.h" #include "db.h" #include "interface.h" #include "externs.h" #include "match.h" #include "command.h" /* Wouldn't it be neat if this used varargs? */ #ifdef NEVER char *tprintf(format, a, b, c, d, e, f) char *format; int a, b, c, d, e, f; { static char buff[10000]; sprintf(buff, format, a, b, c, d, e, f); buff[9999] = 0; return (buff); } #else char *tprintf(va_alist) va_dcl { static char buff[10000]; va_list ap; char *format; va_start(ap); format = va_arg(ap, char *); vsprintf(buff, format, ap); va_end(ap); buff[9999] = '\0'; return buff; } #endif /* --------------------------------------------------------------------------- * insert_first, remove_first: Insert or remove objects from lists. */ dbref insert_first (dbref head, dbref thing) { s_Next(thing, head); return thing; } dbref remove_first (dbref head, dbref thing) { dbref prev; if (head == thing) return (Next(thing)); DOLIST(prev, head) { if (Next(prev) == thing) { s_Next(prev, Next(thing)); return head; } } return head; } /* --------------------------------------------------------------------------- * reverse_list: Reverse the order of members in a list. */ dbref reverse_list(dbref list) { dbref newlist, rest; newlist = NOTHING; while (list != NOTHING) { rest = Next(list); s_Next(list, newlist); newlist = list; list = rest; } return newlist; } /* --------------------------------------------------------------------------- * member - indicate if thing is in list */ int member(dbref thing, dbref list) { DOLIST(list, list) { if (list == thing) return 1; } return 0; } /* --------------------------------------------------------------------------- * is_number: see if string contains just a number. */ int is_number (char *str) { while (*str && isspace(*str)) str++; /* Leading spaces */ if (*str == '-') { /* Leading minus */ str++; if (!*str) return 0; /* but not if just a minus */ } while (*str && isdigit(*str)) str++; /* The number */ while (*str && isspace(*str)) str++; /* Leading spaces */ return (*str ? 0 : 1); } #ifndef STANDALONE int could_doit(dbref player, dbref thing, int locknum) { char *key; dbref aowner; int aflags, doit; /* no if puppet trys to get key */ if (Typeof(player) != TYPE_PLAYER) { if (IS(thing, TYPE_THING, THING_KEY) || IS(thing, TYPE_EXIT, EXIT_KEY)) return 0; } if (Typeof(thing) != TYPE_ROOM && Location(thing) == NOTHING) return 0; key = atr_get(thing, locknum, &aowner, &aflags); doit = eval_boolexp_atr(player, thing, key); free_lbuf(key); return doit; } int can_see(dbref player, dbref thing, int can_see_loc) { /* Sleeping players are invisible if configured */ if ((Typeof(thing) == TYPE_PLAYER) && !(Flags(thing) & (PLAYER_CONNECT|PUPPET)) && mudconf.dark_sleepers) { return 0; } /* You don't see yourself or exits */ if ((player == thing) || (Typeof(thing) == TYPE_EXIT)) { return 0; } /* If loc is not dark, you see it if it's not dark or you control it. * If loc is dark, you see it if you control it. Seeing your own * dark objects is controlled by mudconf.see_own_dark. */ if (can_see_loc) { return (!Dark(thing) || (mudconf.see_own_dark && Examinable(player, thing))); } else { return (mudconf.see_own_dark && Examinable(player, thing)); } } static int pay_quota(dbref who, int cost) { dbref aowner; int quota, aflags; char buf[20], *quota_str; /* determine quota */ quota = atoi(quota_str = atr_get(Owner(who), A_RQUOTA, &aowner, &aflags)); free_lbuf(quota_str); /* enough to build? Wizards always have enough. */ quota -= cost; if ((quota < 0) && !Wizard(who) && !Wizard(Owner(who))) return 0; /* dock the quota */ sprintf(buf, "%d", quota); atr_add_raw(who, A_RQUOTA, buf); return 1; } int canpayfees(dbref player, dbref who, int pennies, int quota) { if (!Wizard(who) && !Wizard(Owner(who)) && !Immortal(who) && !Immortal(Owner(who)) && (Pennies(Owner(who)) < pennies)) { if (player == who) { notify(player, tprintf("Sorry, you don't have enough %s.", mudconf.many_coins)); } else { notify(player, tprintf("Sorry, that player doesn't have enough %s.")); } return 0; } if(mudconf.quotas) { if (!pay_quota(who, quota)) { if (player == who) { notify(player, "Sorry, your building contract has run out."); } else { notify(player, "Sorry, that player's building contract has run out."); } return 0; } } payfor(who, pennies); return 1; } int payfor(dbref who, int cost) { dbref tmp; if (Wizard(who) || Wizard(Owner(who)) || Immortal(who) || Immortal(Owner(who))) { return 1; } who = Owner(who); if ((tmp = Pennies(who)) >= cost) { s_Pennies(who, tmp - cost); return 1; } return 0; } #endif /* STANDALONE */ void add_quota(dbref who, int payment) { dbref aowner; int aflags; char buf[20], *quota; quota = atr_get(who, A_RQUOTA, &aowner, &aflags); sprintf(buf, "%d", atoi(quota) + payment); free_lbuf(quota); atr_add_raw(who, A_RQUOTA, buf); } void giveto(dbref who, int pennies) { if (Wizard(who) || Wizard(Owner(who)) || Immortal(who) || Immortal(Owner(who))) { return; } who = Owner(who); s_Pennies(who, Pennies(who) + pennies); } int ok_name(const char *name) { const char *cp; for (cp=name; cp && *cp; cp++) if (!isprint(*cp)) return 0; return (name && *name && *name != LOOKUP_TOKEN && *name != NUMBER_TOKEN && *name != NOT_TOKEN && !index(name, ARG_DELIMITER) && !index(name, AND_TOKEN) && !index(name, OR_TOKEN) && string_compare(name, "me") && string_compare(name, "home") && string_compare(name, "here")); } int ok_player_name(const char *name) { const char *cp; if (!ok_name(name) || strlen(name) > PLAYER_NAME_LIMIT) return 0; for (cp=name; *cp; cp++) if (isspace(*cp)) return 0; return 1; } int ok_attr_name(const char *attrname) { const char *scan; if (!isalpha(*attrname)) return 0; for (scan=attrname; *scan; scan++) { if (isalnum(*scan)) continue; if (!(index("-_.@#$^&~=+", *scan))) return 0; } return 1; } int ok_password(const char *password) { const char *scan; if (*password == '\0') return 0; for (scan = password; *scan; scan++) { if (!(isprint(*scan) && !isspace(*scan))) { return 0; } } /* Needed. Change it if you like, but be sure yours is the same. */ if ((strlen(password) == 13) && (password[0] == 'X') && (password[1] == 'X')) return 0; return 1; } #ifndef STANDALONE /* --------------------------------------------------------------------------- * handle_ears: Generate the 'grows ears' and 'loses ears' messages. */ void handle_ears (dbref thing, int could_hear, int can_hear) { char *buff, *bp; if (!could_hear && can_hear) { buff = alloc_lbuf("handle_ears.grow"); strcpy(buff, Name(thing)); if (Typeof(thing) == TYPE_EXIT) { for (bp=buff; *bp && (*bp!=';'); bp++) ; *bp = '\0'; } if (Has_location(thing)) notify_all(Location(thing), thing, tprintf("%s grows ears and can now hear.", buff), 0); if (Has_contents(thing)) notify_all(thing, thing, tprintf("%s grows ears and can now hear.", buff), 0); free_lbuf(buff); } else if (could_hear && !can_hear) { buff = alloc_lbuf("handle_ears.lose"); strcpy(buff, Name(thing)); if (Typeof(thing) == TYPE_EXIT) { for (bp=buff; *bp && (*bp!=';'); bp++) ; *bp = '\0'; } if (Has_location(thing)) notify_all(Location(thing), thing, tprintf("%s loses its ears and becomes deaf.", buff), 0); if (Has_contents(thing)) notify_all(thing, thing, tprintf("%s loses its ears and becomes deaf.", buff), 0); free_lbuf(buff); } } /* for lack of better place the @switch code is here */ void do_switch (dbref player, dbref cause, int key, char *expr, char *args[], int nargs, char *cargs[], int ncargs) { int any, a; char *buff; any = 0; if (!expr || (nargs <= 0)) return; if (key == SWITCH_DEFAULT) { if (mudconf.switch_df_all) key = SWITCH_ANY; else key = SWITCH_ONE; } /* now try a wild card match of buff with stuff in coms */ for (a=0; (a<(nargs-1)) && args[a] && args[a+1]; a+=2) { buff = exec(player, cause, EV_STRIP|EV_FCHECK, args[a], cargs, ncargs); if (wild_match(buff, expr, (char **)NULL, 0)) { if ((any == 0) || (key == SWITCH_ANY)) wait_que(player, cause, RU_ARG1_COPY, 0, NOTHING, args[a+1], cargs, ncargs); any = 1; } free_lbuf(buff); } if ((a < nargs) && !any && args[a]) wait_que(player, cause, RU_ARG1_COPY, 0, NOTHING, args[a], cargs, ncargs); } /* --------------------------------------------------------------------------- * do_comment: Implement the @@ (comment) command. Very cpu-intensive :-) */ void do_comment (dbref player, dbref cause, int key) { } dbref is_possess(dbref player, char *arg1) { dbref result1, result; char *buff, *place, *temp, *s1, *d1; /* If no space in the string, forget it */ place = (char *)index(arg1, ' '); if (place == NULL) return NOTHING; /* Look for <name>' and <name>'s. Return failure if <name> is null. */ temp = place--; temp++; if (place == arg1) return NOTHING; if ((*place != 's') && (*place != 'S') && (*place != '\'')) return NOTHING; if (*place != '\'') { place--; if ((place == arg1) || (*place != '\'')) return NOTHING; } /* Copy the container name to a new buffer so we can terminate it */ buff = alloc_lbuf("is_posess"); for (s1=arg1,d1=buff; *s1&&(s1<place); *d1++=(*s1++)) ; *d1 = '\0'; /* Look for the container here and in our inventory */ init_match(player, buff, NOTYPE); match_neighbor(); match_possession(); result1 = match_result(); free_lbuf(buff); if (result1 == NOTHING || result1 == AMBIGUOUS) return NOTHING; /* If we don't control it and it is either dark or opaque, fail */ if ((Dark(result1) || (Flags(result1) & OPAQUE)) && !Controls(player, result1)) return NOTHING; /* Look for the object in the container */ init_match(result1, temp, NOTYPE); match_possession(); result = match_result(); if (result == AMBIGUOUS) result = NOTHING; return result; } #endif /* STANDALONE */ /* --------------------------------------------------------------------------- * where_is: Returns place where obj is linked into a list. * ie. location for players/things, source for exits, NOTHING for rooms. */ dbref where_is (dbref what) { dbref loc; if (!Good_obj(what)) return NOTHING; switch (Typeof(what)) { case TYPE_PLAYER: case TYPE_THING: loc = Location(what); break; case TYPE_EXIT: loc = Exits(what); break; default: loc = NOTHING; break; } return loc; } /* --------------------------------------------------------------------------- * nearby: Check if thing is nearby player (in inventory, in same room, or * IS the room. */ int nearby (dbref player, dbref thing) { int thing_loc, player_loc; if (!Good_obj(player) || !Good_obj(thing)) return 0; thing_loc = where_is(thing); if (thing_loc == player) return 1; player_loc = where_is(player); if ((thing_loc == player_loc) || (thing == player_loc)) return 1; return 0; } /* --------------------------------------------------------------------------- * next_exit: return next exit that is ok to see. */ dbref next_exit(dbref player, dbref this, int exam_here) { if (Typeof(this) == TYPE_ROOM) return NOTHING; if ((Typeof(this) != TYPE_EXIT) || exam_here) return this; while ((this != NOTHING) && (Flags(this) & DARK) && !Examinable(player, this)) this = Next(this); return this; } #ifndef STANDALONE /* --------------------------------------------------------------------------- * did_it: Do something with an object */ void did_it(dbref player, dbref thing, int what, const char *def, int owhat, const char *odef, int awhat, char *args[], int nargs) { char *d, *buff, *act, *charges; dbref loc, aowner; int num, aflags; /* message to player */ if (what) { if (*(d = atr_pget(thing, what, &aowner, &aflags))) { buff = exec(thing, player, EV_FIGNORE, d, args, nargs); notify(player, buff); free_lbuf(buff); } else if (def) { notify(player, def); } free_lbuf(d); } /* message to neighbors */ if (owhat && Has_location(player) && Good_obj(loc = Location(player))) { if (*(d = atr_pget(thing, owhat, &aowner, &aflags))) { buff = exec(thing, player, EV_FIGNORE, d, args, nargs); notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), buff), 1); free_lbuf(buff); } else if (odef) { notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), odef), 1); } free_lbuf(d); } /* do the action attribute */ if (awhat) { if (*(act = atr_pget(thing, awhat, &aowner, &aflags))) { charges = atr_pget(thing, A_CHARGES, &aowner, &aflags); if (*charges) { num = atoi(charges); if (num > 0) { buff = alloc_sbuf("did_it.charges"); sprintf(buff, "%d", num - 1); atr_add_raw(thing, A_CHARGES, buff); free_sbuf(buff); } else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner, &aflags))) { free_lbuf(act); act = buff; } else { free_lbuf(act); free_lbuf(buff); free_lbuf(charges); return; } } free_lbuf(charges); wait_que(thing, player, RU_ARG1_COPY, 0, NOTHING, act, args, nargs); } free_lbuf(act); } } /* --------------------------------------------------------------------------- * do_verb: Command interface to did_it. */ void do_verb (dbref player, dbref cause, int key, char *actor_str, char *args[], int nargs) { dbref actor; int what, owhat, awhat, nxargs; ATTR *ap; const char *whatd, *owhatd; char *xargs[10]; /* Look for the actor */ if (!actor_str || !*actor_str) { notify(player, "Nothing to do."); return; } actor = match_controlled(player, actor_str); if (!Good_obj(actor)) return; /* Get the invoker. Default to my invoker. Player must control. */ if ((nargs >= 1) && args[0] && *args[0]) { cause = match_controlled(player, args[0]); } else if (!Controls(player, cause)) { notify(player, "Permission denied."); return; } if (!Good_obj(cause)) return; what = 0; owhat = 0; awhat = 0; whatd = NULL; owhatd = NULL; nxargs = 0; /* Get invoker message attribute */ if (nargs >= 2) { ap = atr_str(args[1]); if (ap) what = ap->number; } /* Get invoker message default */ if ((nargs >= 3) && args[2] && *args[2]) { whatd = args[2]; } /* Get others message attribute */ if (nargs >= 4) { ap = atr_str(args[3]); if (ap) owhat = ap->number; } /* Get others message default */ if ((nargs >= 5) && args[4] && *args[4]) { owhatd = args[4]; } /* Get action attribute */ if (nargs >= 6) { ap = atr_str(args[5]); if (ap) awhat = ap->number; } /* Get arguments */ if (nargs >= 7) { parse_arglist(actor, cause, args[6], '\0', EV_STRIP_LS|EV_STRIP_TS, xargs, 10, (char **)NULL, 0); for (nxargs=0; (nxargs<10) && xargs[nxargs]; nxargs++) ; } /* Go do it */ did_it(cause, actor, what, whatd, owhat, owhatd, awhat, xargs, nxargs); } /* --------------------------------------------------------------------------- * Buffer pool routines - faster allocate/free for fixed-size buffers */ #define POOL_MAGICNUM 0xdeadbeef void pool_init(POOL *poolp, int poolsize) { poolp->pool_size = poolsize; poolp->free_head = NULL; poolp->chain_head = NULL; poolp->tot_alloc = 0; poolp->num_alloc = 0; poolp->max_alloc = 0; poolp->num_lost = 0; return; } #ifdef PARANOID_ALLOC static void pool_vfy (POOL *poolp, const char *tag) { POOLHDR *ph; for (ph=poolp->chain_head; ph; ph=ph->next) { if (ph->magicnum != POOL_MAGICNUM) { if (mudstate.logging == 0) { STARTLOG(LOG_ALWAYS,"BUG","ALLOC") sprintf(mudstate.buffer, "Buffer[%d] consistency check at %x, tag '%s'"; poolp->pool_size, ph, tag); log_text(mudstate.buffer); ENDLOG else { sprintf(mudstate.buffer, "***< Buffer[%d] consistency check at %x, tag '%s' >***"; poolp->pool_size, ph, tag); log_text(mudstate.buffer); } } } } void pool_check(const char *tag) { pool_vfy(&mudstate.lbuf_pool, tag); pool_vfy(&mudstate.mbuf_pool, tag); pool_vfy(&mudstate.sbuf_pool, tag); pool_vfy(&mudstate.bool_pool, tag); pool_vfy(&mudstate.desc_pool, tag); pool_vfy(&mudstate.qentry_pool, tag); } #endif char *pool_alloc(POOL *poolp, const char *tag) { int *p; char *h; POOLHDR *ph; #ifdef PARANOID_ALLOC pool_check(tag); #endif do { if (poolp->free_head == NULL) { h = (char *)malloc(poolp->pool_size + sizeof(POOLHDR)); if (h == NULL) abort(); ph = (POOLHDR *)h; ph->next = poolp->chain_head; ph->nxtfree = NULL; ph->magicnum = POOL_MAGICNUM; poolp->chain_head = ph; poolp->max_alloc++; p = (int *)(h + sizeof(POOLHDR)); *p = POOL_MAGICNUM; } else { ph = (POOLHDR *)(poolp->free_head); h = (char *)ph; poolp->free_head = ph->nxtfree; p = (int *)(h + sizeof(POOLHDR)); if (ph->magicnum != POOL_MAGICNUM) { /* New buffer is corrupted. Log it always. */ if (!mudstate.logging) { STARTLOG(LOG_ALWAYS,"BUG","ALLOC") sprintf(mudstate.buffer, "Alloc[%d] (tag %s) got corrupted buffer at %x, clearing freelist.", poolp->pool_size, tag, ph); log_text(mudstate.buffer); ENDLOG } else { sprintf(mudstate.buffer, "***< Alloc[%d] (tag %s) got corrupted buffer at %x, clearing freelist. >***", poolp->pool_size, tag, ph); log_text(mudstate.buffer); } /* Start a new free list and record stats */ p = NULL; poolp->free_head = NULL; poolp->num_lost += (poolp->tot_alloc - poolp->num_alloc); poolp->tot_alloc = poolp->num_alloc; } } } while (p == NULL); ph->buf_tag = (char *)tag; poolp->tot_alloc++; poolp->num_alloc++; /* Log if requested. Only try to log if we are not logging, because * there are often alloc/free pairs within a STARTLOG/ENDLOG pair, * and attemting to log these would generate a recursive logging * error. */ if (!mudstate.logging) { STARTLOG(LOG_ALLOCATE,"DBG","ALLOC") sprintf(mudstate.buffer, "Allocate[%d] at %x by %s", poolp->pool_size, ph, tag); log_text(mudstate.buffer); ENDLOG } /* If the buffer was modified after it was last freed, log it. */ if ((*p != POOL_MAGICNUM) && (!mudstate.logging)) { STARTLOG(LOG_PROBLEMS,"BUG","ALLOC") sprintf(mudstate.buffer, "Alloc[%d] (tag %s) buffer at %x modified after free.", poolp->pool_size, tag, ph); log_text(mudstate.buffer); ENDLOG } return (char *)p; } void pool_free(POOL *poolp, char **buf) { int *ibuf; char *h; POOLHDR *ph; ibuf = (int *)*buf; h = (char *)ibuf; h -= sizeof(POOLHDR); ph = (POOLHDR *)h; #ifdef PARANOID_ALLOC pool_check(ph->buf_tag); #endif *ibuf = POOL_MAGICNUM; /* Make sure the buffer header is good. If it isn't, log the error and * throw away the buffer. */ if (ph->magicnum != POOL_MAGICNUM) { if (!mudstate.logging) { STARTLOG(LOG_ALWAYS,"BUG","ALLOC") sprintf(mudstate.buffer, "Free[%d] buffer header corrupted at %x.", poolp->pool_size, ph); log_text(mudstate.buffer); ENDLOG } else { sprintf(mudstate.buffer, "***< Free[%d] buffer header corrupted at %x. >***", poolp->pool_size, ph); log_text(mudstate.buffer); } poolp->num_lost++; poolp->num_alloc--; poolp->tot_alloc--; return; } /* Log if requested. Only try to log if we are not logging, because * there are often alloc/free pairs within a STARTLOG/ENDLOG pair, * and attemting to log these would generate a recursive logging * error. */ if (!mudstate.logging) { STARTLOG(LOG_ALLOCATE,"DBG","ALLOC") sprintf(mudstate.buffer, "Free[%d] at %x (alloc by %s)", poolp->pool_size, ph, ph->buf_tag); log_text(mudstate.buffer); ENDLOG } /* Make sure we aren't freeing an already free buffer. If we are, * log an error, otherwise update the pool header and stats */ if (ph->buf_tag == NULL) { if (!mudstate.logging) { STARTLOG(LOG_BUGS,"BUG","ALLOC") sprintf(mudstate.buffer, "Free[%d] of already free block at %x", poolp->pool_size, ph); log_text(mudstate.buffer); ENDLOG } } else { ph->nxtfree = poolp->free_head; poolp->free_head = ph; poolp->num_alloc--; ph->buf_tag = NULL; } } static char *pool_stats(POOL *poolp, const char *text) { char *buf; buf = alloc_mbuf("pool_stats"); sprintf(buf, "%-15s %5d%9d%9d%9d%9d", text, poolp->pool_size, poolp->num_alloc, poolp->max_alloc, poolp->tot_alloc, poolp->num_lost); return buf; } static void pool_trace(dbref player, POOL *poolp, const char *text) { POOLHDR *ph; int numfree; numfree = 0; notify(player, tprintf("----- %s -----", text)); for (ph=poolp->chain_head; ph!=NULL; ph=ph->next) { if (ph->magicnum != POOL_MAGICNUM) { notify(player,"*** CORRUPTED BUFFER HEADER, ABORTING SCAN ***"); notify(player, tprintf("%d free %s (before corruption)", numfree, text)); return; } if (ph->buf_tag != NULL) notify(player, ph->buf_tag); else numfree++; } notify(player, tprintf("%d free %s", numfree, text)); } static void list_bufstat(dbref player, POOL *poolp, const char *pool_name) { char *buff; buff = pool_stats(poolp, pool_name); notify(player, buff); free_mbuf(buff); } void list_bufstats(dbref player) { notify(player, "Buffer Stats Size InUse Total Allocs Lost"); list_bufstat(player, &mudstate.lbuf_pool, "Lbufs"); list_bufstat(player, &mudstate.mbuf_pool, "Mbufs"); list_bufstat(player, &mudstate.sbuf_pool, "Sbufs"); list_bufstat(player, &mudstate.bool_pool, "Bools"); list_bufstat(player, &mudstate.desc_pool, "Descs"); list_bufstat(player, &mudstate.qentry_pool, "Qentries"); } void list_buftrace(dbref player) { pool_trace(player,&mudstate.lbuf_pool, "Lbufs"); pool_trace(player,&mudstate.mbuf_pool, "Mbufs"); pool_trace(player,&mudstate.sbuf_pool, "Sbufs"); pool_trace(player,&mudstate.bool_pool, "Bools"); pool_trace(player,&mudstate.desc_pool, "Descs"); pool_trace(player,&mudstate.qentry_pool, "Qentries"); } #endif /* STANDALONE */