/* walkdb.c -- Support for commands that walk the entire db */ #include "autoconf.h" #include "copyright.h" #ifndef lint static char *RCSid = "$Id: walkdb.c,v 1.8 1995/03/21 00:01:41 ambar Exp $"; USE(RCSid); #endif #include "interface.h" #include "match.h" #include "command.h" #include "flags.h" #include "misc.h" /* Bind occurances of the universal var in ACTION to ARG, then run ACTION. Cmds run in low-prio Q after a 1 sec delay for the first one. */ static void bind_and_queue(player, cause, action, argstr, cargs, ncargs, number) dbref player, cause; int ncargs; char *action, *argstr, *cargs[]; { char *command, *command2; /* allocated by replace_string */ char tbuf[8]; command = replace_string(BOUND_VAR, argstr, action); ltos(tbuf, number); command2 = replace_string(LISTPLACE_VAR, tbuf, command); wait_que(player, cause, 0, NOTHING, command2, cargs, ncargs, mudstate.global_regs); free_lbuf(command); free_lbuf(command2); } /* New @dolist. i.e.: * @dolist #12 #34 #45 #123 #34644=@emit [name(##)] * * New switches added 12/92, /space (default) delimits list using spaces, * and /delimit allows specification of a delimiter. * * Additional switch /notify queues a '@notify me' at the end of it. */ void do_dolist(player, cause, key, list, command, cargs, ncargs) dbref player, cause; int key, ncargs; char *list, *command, *cargs[]; { char *tbuf, *curr, *objstring, delimiter = ' '; int number = 0; if (!list || *list == '\0') { notify(player, "That's terrific, but what should I do with the list?"); return; } curr = list; if (key & DOLIST_DELIMIT) { char *tempstr; if (strlen((tempstr = parse_to(&curr, ' ', EV_STRIP))) > 1) { notify(player, "The delimiter must be a single character!"); return; } delimiter = *tempstr; } while (curr && *curr) { while (*curr == delimiter) curr++; if (*curr) { number++; objstring = parse_to(&curr, delimiter, EV_STRIP); bind_and_queue(player, cause, command, objstring, cargs, ncargs, number); } } if (key & DOLIST_NOTIFY) { tbuf = alloc_lbuf("dolist.notify_cmd"); strcpy(tbuf, (char *) "@notify me"); wait_que(player, cause, 0, NOTHING, tbuf, cargs, ncargs, mudstate.global_regs); free_lbuf(tbuf); } } /* Regular @find command */ void do_find(player, cause, key, name) dbref player, cause; int key; char *name; { dbref i, low_bound, high_bound; char *buff; if (!payfor(player, mudconf.searchcost)) { notify_quiet(player, tprintf("You don't have enough %s.", mudconf.many_coins)); return; } parse_range(&name, &low_bound, &high_bound); for (i = low_bound; i <= high_bound; i++) { if ((i % 100) == 0) cache_reset(0); if (!isExit(i) && controls(player, i) && (!*name || string_match(Name(i), name))) { buff = unparse_object(player, i, 0); notify(player, buff); free_lbuf(buff); } } notify(player, "***End of List***"); } /* --------------------------------------------------------------------------- * get_stats, do_stats: Get counts of items in the db. */ int get_stats(player, who, info) dbref player, who; STATS *info; { dbref i; info->s_total = 0; info->s_rooms = 0; info->s_exits = 0; info->s_things = 0; info->s_players = 0; info->s_garbage = 0; /* Do we have permission? */ if (Good_obj(who) && !Controls(player, who)) { notify(player, "Permission denied."); return 0; } /* Can we afford it? */ if (!payfor(player, mudconf.searchcost)) { notify(player, tprintf("You don't have enough %s.", mudconf.many_coins)); return 0; } DO_WHOLE_DB(i) { if ((who == NOTHING) || (who == Owner(i))) { info->s_total++; if (Going(i) && (!isRoom(i))) { info->s_garbage++; continue; } switch (Typeof(i)) { case TYPE_ROOM: info->s_rooms++; break; case TYPE_EXIT: info->s_exits++; break; case TYPE_THING: info->s_things++; break; case TYPE_PLAYER: info->s_players++; break; default: info->s_garbage++; } } } return 1; } void do_stats(player, cause, key, name) dbref player, cause; int key; char *name; /* reworked by R'nice */ { dbref owner; STATS statinfo; switch (key) { case STAT_ALL: owner = NOTHING; break; case STAT_ME: owner = Owner(player); break; case STAT_PLAYER: if (!(name && *name)) { notify(player, tprintf("The universe contains %d objects (next free is #%d).", mudstate.db_top, ((mudstate.freelist == NOTHING) ? mudstate.db_top : mudstate.freelist))); return; } owner = lookup_player(player, name, 1); if (owner == NOTHING) { notify(player, "Not found."); return; } break; default: notify(player, "Illegal combination of switches."); return; } if (!get_stats(player, owner, &statinfo)) return; notify(player, tprintf("%d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)", statinfo.s_total, statinfo.s_rooms, statinfo.s_exits, statinfo.s_things, statinfo.s_players, statinfo.s_garbage)); #ifdef TEST_MALLOC if (Wizard(player)) notify(player, tprintf("Malloc count = %d.", malloc_count)); #endif /* TEST_MALLOC */ #ifdef MSTATS if (Wizard(player)) { struct mstats_value mval; int i; extern unsigned int malloc_sbrk_used, malloc_sbrk_unused; extern int nmal, nfre; extern struct mstats_value malloc_stats(); extern int malloc_mem_used(), malloc_mem_free(); notify(player, tprintf("Sbrk Unused: %d -- Sbrk Used: %d", malloc_sbrk_unused, malloc_sbrk_used)); notify(player, tprintf("Raw malloc cnt: %d -- Raw free cnt: %d", nmal, nfre)); for (i = 0; i < 15; i++) { mval = malloc_stats(i); notify(player, tprintf("Blocksz: %d -- Free: %d -- Used: %d", mval.blocksize, mval.nfree, mval.nused)); } notify(player, tprintf("Mem used: %d -- Mem free: %d", malloc_mem_used(), malloc_mem_free())); } #endif /* MSTATS */ } void do_chownall(player, cause, key, from, to) dbref player, cause; int key; char *from, *to; { int count; dbref victim, recipient; init_match(player, from, TYPE_PLAYER); match_absolute(); match_player(); match_neighbor(); if ((victim = noisy_match_result()) == NOTHING) return; if ((to != NULL) && *to) { init_match(player, to, TYPE_PLAYER); match_absolute(); match_player(); match_neighbor(); if ((recipient = noisy_match_result()) == NOTHING) return; } else { recipient = player; } count = chown_all(victim, recipient); if (!Quiet(player)) { notify(player, tprintf("%d objects @chowned.", count)); } } int chown_all(from_player, to_player) dbref from_player, to_player; { register int i, count = 0, q_t = 0, q_p = 0, q_r = 0, q_e = 0; if (!isPlayer(from_player)) from_player = Owner(from_player); if (!isPlayer(to_player)) to_player = Owner(to_player); DO_WHOLE_DB(i) { if ((Owner(i) == from_player) && (Owner(i) != i)) { switch (Typeof(i)) { case TYPE_PLAYER: s_Owner(i, i); q_p += mudconf.player_quota; break; case TYPE_THING: if (Going(i)) break; s_Owner(i, to_player); q_t += mudconf.thing_quota; break; case TYPE_ROOM: s_Owner(i, to_player); q_r += mudconf.room_quota; break; case TYPE_EXIT: s_Owner(i, to_player); q_e += mudconf.exit_quota; break; default: s_Owner(i, to_player); } s_Flags(i, (Flags(i) & ~(CHOWN_OK | INHERIT)) | HALT); count++; } } payfees(to_player, 0, q_p, TYPE_PLAYER); payfees(from_player, 0, -q_p, TYPE_PLAYER); payfees(to_player, 0, q_r, TYPE_ROOM); payfees(from_player, 0, -q_r, TYPE_ROOM); payfees(to_player, 0, q_e, TYPE_EXIT); payfees(from_player, 0, -q_e, TYPE_EXIT); payfees(to_player, 0, q_t, TYPE_THING); payfees(from_player, 0, -q_t, TYPE_THING); return count; } #define ANY_OWNER -2 void er_mark_disabled(player) dbref player; { notify(player, "The mark commands are not allowed while DB cleaning is enabled."); notify(player, "Use the '@disable cleaning' command to disable automatic cleaning."); notify(player, "Remember to '@unmark_all' before re-enabling automatic cleaning."); } /* --------------------------------------------------------------------------- * do_search: Walk the db reporting various things (or setting/clearing * mark bits) */ int search_setup(player, searchfor, parm) dbref player; char *searchfor; SEARCH *parm; { char *pname, *searchtype, *t; int err; /* Crack arg into <pname> <type>=<targ>,<low>,<high> */ pname = parse_to(&searchfor, '=', EV_STRIP_TS); if (!pname || !*pname) { pname = (char *) "me"; } else for (t = pname; *t; t++) { if (isupper(*t)) *t = tolower(*t); } if (searchfor && *searchfor) { searchtype = (char *) rindex(pname, ' '); if (searchtype) { *searchtype++ = '\0'; } else { searchtype = pname; pname = (char *) ""; } } else { searchtype = (char *) ""; } /* If the player name is quoted, strip the quotes */ if (*pname == '\"') { err = strlen(pname) - 1; if (pname[err] == '"') { pname[err] = '\0'; pname++; } } /* Strip any range arguments */ parse_range(&searchfor, &parm->low_bound, &parm->high_bound); /* set limits on who we search */ parm->s_owner = Owner(player); parm->s_wizard = Wizard(player); parm->s_rst_owner = NOTHING; if (!*pname) { parm->s_rst_owner = parm->s_wizard ? ANY_OWNER : player; } else if (pname[0] == '#') { parm->s_rst_owner = atoi(&pname[1]); if (!Good_obj(parm->s_rst_owner)) parm->s_rst_owner = NOTHING; else if (!isPlayer(parm->s_rst_owner)) parm->s_rst_owner = NOTHING; } else if (strcmp(pname, "me") == 0) { parm->s_rst_owner = player; } else { parm->s_rst_owner = lookup_player(player, pname, 1); } if (parm->s_rst_owner == NOTHING) { notify(player, tprintf("%s: No such player", pname)); return 0; } /* set limits on what we search for */ err = 0; parm->s_rst_name = NULL; parm->s_rst_eval = NULL; parm->s_rst_type = NOTYPE; parm->s_parent = NOTHING; parm->s_fset.word1 = 0; parm->s_fset.word2 = 0; switch (searchtype[0]) { case '\0': /* the no class requested class :) */ break; case 'e': if (string_prefix("exits", searchtype)) { parm->s_rst_name = searchfor; parm->s_rst_type = TYPE_EXIT; } else if (string_prefix("evaluate", searchtype)) { parm->s_rst_eval = searchfor; } else if (string_prefix("eplayer", searchtype)) { parm->s_rst_type = TYPE_PLAYER; parm->s_rst_eval = searchfor; } else if (string_prefix("eroom", searchtype)) { parm->s_rst_type = TYPE_ROOM; parm->s_rst_eval = searchfor; } else if (string_prefix("eobject", searchtype)) { parm->s_rst_type = TYPE_THING; parm->s_rst_eval = searchfor; } else if (string_prefix("ething", searchtype)) { parm->s_rst_type = TYPE_THING; parm->s_rst_eval = searchfor; } else if (string_prefix("eexit", searchtype)) { parm->s_rst_type = TYPE_EXIT; parm->s_rst_eval = searchfor; } else { err = 1; } break; case 'f': if (string_prefix("flags", searchtype)) { /* convert_flags ignores previous values of flag_mask * and s_rst_type while setting them */ if (!convert_flags(player, searchfor, &parm->s_fset, &parm->s_rst_type)) return 0; } else { err = 1; } break; case 'n': if (string_prefix("name", searchtype)) { parm->s_rst_name = searchfor; } else { err = 1; } break; case 'o': if (string_prefix("objects", searchtype)) { parm->s_rst_name = searchfor; parm->s_rst_type = TYPE_THING; } else { err = 1; } break; case 'p': if (string_prefix("players", searchtype)) { parm->s_rst_name = searchfor; parm->s_rst_type = TYPE_PLAYER; if (!*pname) parm->s_rst_owner = ANY_OWNER; } else if (string_prefix("parent", searchtype)) { parm->s_parent = match_controlled(player, searchfor); if (!Good_obj(parm->s_parent)) return 0; if (!*pname) parm->s_rst_owner = ANY_OWNER; } else { err = 1; } break; case 'r': if (string_prefix("rooms", searchtype)) { parm->s_rst_name = searchfor; parm->s_rst_type = TYPE_ROOM; } else { err = 1; } break; case 't': if (string_prefix("type", searchtype)) { if (searchfor[0] == '\0') break; if (string_prefix("rooms", searchfor)) parm->s_rst_type = TYPE_ROOM; else if (string_prefix("exits", searchfor)) parm->s_rst_type = TYPE_EXIT; else if (string_prefix("objects", searchfor)) parm->s_rst_type = TYPE_THING; else if (string_prefix("things", searchfor)) parm->s_rst_type = TYPE_THING; else if (string_prefix("players", searchfor)) { parm->s_rst_type = TYPE_PLAYER; if (!*pname) parm->s_rst_owner = ANY_OWNER; } else { notify(player, tprintf("%s: unknown type", searchfor)); return 0; } } else if (string_prefix("things", searchtype)) { parm->s_rst_name = searchfor; parm->s_rst_type = TYPE_THING; } else { err = 1; } break; default: err = 1; } if (err) { notify(player, tprintf("%s: unknown class", searchtype)); return 0; } /* Make sure player is authorized to do the search */ if (!parm->s_wizard && (parm->s_rst_type != TYPE_PLAYER) && (parm->s_rst_owner != player) && (parm->s_rst_owner != ANY_OWNER)) { notify(player, "You need a search warrant to do that!"); return 0; } /* make sure player has money to do the search */ if (!payfor(player, mudconf.searchcost)) { notify(player, tprintf("You don't have enough %s to search. (You need %d)", mudconf.many_coins, mudconf.searchcost)); return 0; } return 1; } void search_perform(player, cause, parm) dbref player, cause; SEARCH *parm; { FLAG thing1flags, thing2flags; dbref thing; char *buff, *buff2, *result; int save_invk_ctr; buff = alloc_sbuf("search_perform.num"); olist_init(); save_invk_ctr = mudstate.func_invk_ctr; for (thing = parm->low_bound; thing <= parm->high_bound; thing++) { if ((thing % 100) == 0) { cache_reset(0); } mudstate.func_invk_ctr = save_invk_ctr; /* Check for matching type */ if ((parm->s_rst_type != NOTYPE) && (parm->s_rst_type != Typeof(thing))) continue; /* Check for matching owner */ if ((parm->s_rst_owner != ANY_OWNER) && (parm->s_rst_owner != Owner(thing))) continue; /* Toss out destroyed things */ thing1flags = Flags(thing); if (isThing(thing) && (thing1flags & GOING)) continue; /* Check for matching parent */ if ((parm->s_parent != NOTHING) && (parm->s_parent != Parent(thing))) continue; /* Check for matching flags */ thing2flags = Flags2(thing); if ((thing1flags & parm->s_fset.word1) != parm->s_fset.word1) continue; if ((thing2flags & parm->s_fset.word2) != parm->s_fset.word2) continue; /* Check for matching name */ if (parm->s_rst_name != NULL) { if (!string_prefix(Name(thing), parm->s_rst_name)) continue; } /* Check for successful evaluation */ if (parm->s_rst_eval != NULL) { *buff = '#'; ltos(&buff[1], thing); buff2 = replace_string(BOUND_VAR, buff, parm->s_rst_eval); result = exec(player, cause, EV_FCHECK | EV_EVAL | EV_NOTRACE, buff2, (char **) NULL, 0); free_lbuf(buff2); if (!*result || !xlate(result)) { free_lbuf(result); continue; } free_lbuf(result); } /* It passed everything. Amazing. */ olist_add(thing); } free_sbuf(buff); mudstate.func_invk_ctr = save_invk_ctr; } static void search_mark(player, key) dbref player; int key; { dbref thing; int nchanged, is_marked; nchanged = 0; for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { is_marked = Marked(thing); /* Don't bother checking if marking and already marked * (or if unmarking and not marked) */ if (((key == SRCH_MARK) && is_marked) || ((key == SRCH_UNMARK) && !is_marked)) continue; /* Toggle the mark bit and update the counters */ if (key == SRCH_MARK) { Mark(thing); nchanged++; } else { Unmark(thing); nchanged++; } } notify(player, tprintf("%d objects %smarked", nchanged, ((key == SRCH_MARK) ? "" : "un"))); return; } void do_search(player, cause, key, arg) dbref player, cause; int key; char *arg; { int flag, destitute; int rcount, ecount, tcount, pcount; char *buff, *outbuf, *bp; dbref thing, from, to; SEARCH searchparm; if ((key != SRCH_SEARCH) && (mudconf.control_flags & CF_DBCHECK)) { er_mark_disabled(player); return; } if (!search_setup(player, arg, &searchparm)) return; search_perform(player, cause, &searchparm); destitute = 1; /* If we are doing a @mark command, handle that here. */ if (key != SRCH_SEARCH) { search_mark(player, key); return; } outbuf = alloc_lbuf("do_search.outbuf"); rcount = ecount = tcount = pcount = 0; /* room search */ if (searchparm.s_rst_type == TYPE_ROOM || searchparm.s_rst_type == NOTYPE) { flag = 1; for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { if (!isRoom(thing)) continue; if (flag) { flag = 0; destitute = 0; notify(player, "\nROOMS:"); } buff = unparse_object(player, thing, 0); notify(player, buff); free_lbuf(buff); rcount++; } } /* exit search */ if (searchparm.s_rst_type == TYPE_EXIT || searchparm.s_rst_type == NOTYPE) { flag = 1; for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { if (!isExit(thing)) continue; if (flag) { flag = 0; destitute = 0; notify(player, "\nEXITS:"); } from = Exits(thing); to = Location(thing); bp = outbuf; buff = unparse_object(player, thing, 0); safe_str(buff, outbuf, &bp); free_lbuf(buff); safe_str((char *) " [from ", outbuf, &bp); buff = unparse_object(player, from, 0); safe_str(((from == NOTHING) ? "NOWHERE" : buff), outbuf, &bp); free_lbuf(buff); safe_str((char *) " to ", outbuf, &bp); buff = unparse_object(player, to, 0); safe_str(((to == NOTHING) ? "NOWHERE" : buff), outbuf, &bp); free_lbuf(buff); safe_chr(']', outbuf, &bp); notify(player, outbuf); ecount++; } } /* object search */ if (searchparm.s_rst_type == TYPE_THING || searchparm.s_rst_type == NOTYPE) { flag = 1; for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { if (!isThing(thing)) continue; if (flag) { flag = 0; destitute = 0; notify(player, "\nOBJECTS:"); } bp = outbuf; buff = unparse_object(player, thing, 0); safe_str(buff, outbuf, &bp); free_lbuf(buff); safe_str((char *) " [owner: ", outbuf, &bp); buff = unparse_object(player, Owner(thing), 0); safe_str(buff, outbuf, &bp); free_lbuf(buff); safe_chr(']', outbuf, &bp); notify(player, outbuf); tcount++; } } /* player search */ if (searchparm.s_rst_type == TYPE_PLAYER || searchparm.s_rst_type == NOTYPE) { flag = 1; for (thing = olist_first(); thing != NOTHING; thing = olist_next()) { if (!isPlayer(thing)) continue; if (flag) { flag = 0; destitute = 0; notify(player, "\nPLAYERS:"); } bp = outbuf; buff = unparse_object(player, thing, 0); safe_str(buff, outbuf, &bp); free_lbuf(buff); if (searchparm.s_wizard) { safe_str((char *) " [location: ", outbuf, &bp); buff = unparse_object(player, Location(thing), 0); safe_str(buff, outbuf, &bp); free_lbuf(buff); safe_chr(']', outbuf, &bp); } notify(player, outbuf); pcount++; } } /* if nothing found matching search criteria */ if (destitute) { notify(player, "Nothing found."); } else { sprintf(outbuf, "\nFound: Rooms...%d Exits...%d Objects...%d Players...%d", rcount, ecount, tcount, pcount); notify(player, outbuf); } free_lbuf(outbuf); olist_init(); } /* --------------------------------------------------------------------------- * do_markall: set or clear the mark bits of all objects in the db. */ void do_markall(player, cause, key) dbref player, cause; int key; { int i; if (mudconf.control_flags & CF_DBCHECK) { er_mark_disabled(player); return; } if (key == MARK_SET) Mark_all(i); else if (key == MARK_CLEAR) Unmark_all(i); if (!Quiet(player)) notify(player, "Done."); } /* --------------------------------------------------------------------------- * do_apply_marked: Perform a command for each marked obj in the db. */ void do_apply_marked(player, cause, key, command, cargs, ncargs) dbref player, cause; int key, ncargs; char *command, *cargs[]; { char *buff; int i; int number = 0; if (mudconf.control_flags & CF_DBCHECK) { er_mark_disabled(player); return; } buff = alloc_sbuf("do_apply_marked"); DO_WHOLE_DB(i) { if (Marked(i)) { *buff = '#'; ltos(&buff[1], i); number++; bind_and_queue(player, cause, command, buff, cargs, ncargs, number); } } free_sbuf(buff); if (!Quiet(player)) notify(player, "Done."); } /* --------------------------------------------------------------------------- * olist_init, olist_add, olist_first, olist_next: Object list management * routines. */ /* olist_init: Clear and initialize the object list */ void NDECL(olist_init) { OBLOCK *op, *onext; for (op = mudstate.olist_head; op != NULL; op = onext) { onext = op->next; free_lbuf(op); } mudstate.olist_head = NULL; mudstate.olist_tail = NULL; mudstate.olist_cblock = NULL; mudstate.olist_count = 0; mudstate.olist_citm = 0; } /* olist_add: Add an entry to the object list */ void olist_add(item) dbref item; { OBLOCK *op; if (!mudstate.olist_head) { op = (OBLOCK *) alloc_lbuf("olist_add.first"); mudstate.olist_head = mudstate.olist_tail = op; mudstate.olist_count = 0; op->next = NULL; } else if (mudstate.olist_count >= OBLOCK_SIZE) { op = (OBLOCK *) alloc_lbuf("olist_add.next"); mudstate.olist_tail->next = op; mudstate.olist_tail = op; mudstate.olist_count = 0; op->next = NULL; } else { op = mudstate.olist_tail; } op->data[mudstate.olist_count++] = item; } /* olist_first: Return the first entry in the object list */ dbref NDECL(olist_first) { if (!mudstate.olist_head) return NOTHING; if ((mudstate.olist_head == mudstate.olist_tail) && (mudstate.olist_count == 0)) return NOTHING; mudstate.olist_cblock = mudstate.olist_head; mudstate.olist_citm = 0; return mudstate.olist_cblock->data[mudstate.olist_citm++]; } dbref NDECL(olist_next) { dbref thing; if (!mudstate.olist_cblock) return NOTHING; if ((mudstate.olist_cblock == mudstate.olist_tail) && (mudstate.olist_citm >= mudstate.olist_count)) return NOTHING; thing = mudstate.olist_cblock->data[mudstate.olist_citm++]; if (mudstate.olist_citm >= OBLOCK_SIZE) { mudstate.olist_cblock = mudstate.olist_cblock->next; mudstate.olist_citm = 0; } return thing; }