mux/src/game.cpp

Go to the documentation of this file.
00001 // game.cpp
00002 //
00003 // $Id: game.cpp,v 1.93 2006/03/12 22:45:04 sdennis Exp $
00004 //
00005 #include "copyright.h"
00006 #include "autoconf.h"
00007 #include "config.h"
00008 #include "externs.h"
00009 
00010 #include <sys/stat.h>
00011 #include <signal.h>
00012 
00013 #include "attrs.h"
00014 #include "command.h"
00015 #include "functions.h"
00016 #include "comsys.h"
00017 #include "file_c.h"
00018 #include "mguests.h"
00019 #include "muxcli.h"
00020 #include "pcre.h"
00021 #include "powers.h"
00022 #include "help.h"
00023 #ifdef REALITY_LVLS
00024 #include "levels.h"
00025 #endif /* REALITY_LVLS */
00026 
00027 void do_dump(dbref executor, dbref caller, dbref enactor, int key)
00028 {
00029     UNUSED_PARAMETER(caller);
00030     UNUSED_PARAMETER(enactor);
00031 
00032 #ifndef WIN32
00033     if (mudstate.dumping)
00034     {
00035         notify(executor, "Dumping in progress. Try again later.");
00036         return;
00037     }
00038 #endif
00039     notify(executor, "Dumping...");
00040     fork_and_dump(key);
00041 }
00042 
00043 // print out stuff into error file
00044 //
00045 void report(void)
00046 {
00047     STARTLOG(LOG_BUGS, "BUG", "INFO");
00048     log_text("Command: '");
00049     log_text(mudstate.debug_cmd);
00050     log_text("'");
00051     ENDLOG;
00052     if (Good_obj(mudstate.curr_executor))
00053     {
00054         STARTLOG(LOG_BUGS, "BUG", "INFO");
00055         log_text("Player: ");
00056         log_name_and_loc(mudstate.curr_executor);
00057         if (  mudstate.curr_enactor != mudstate.curr_executor
00058            && Good_obj(mudstate.curr_enactor))
00059         {
00060             log_text(" Enactor: ");
00061             log_name_and_loc(mudstate.curr_enactor);
00062         }
00063         ENDLOG;
00064     }
00065 }
00066 
00067 /* ----------------------------------------------------------------------
00068  * regexp_match: Load a regular expression match and insert it into
00069  * registers.
00070  */
00071 
00072 bool regexp_match
00073 (
00074     char *pattern,
00075     char *str,
00076     int case_opt,
00077     char *args[],
00078     int nargs
00079 )
00080 {
00081     int matches;
00082     int i;
00083     const char *errptr;
00084     int erroffset;
00085 
00086     /*
00087      * Load the regexp pattern. This allocates memory which must be
00088      * later freed. A free() of the regexp does free all structures
00089      * under it.
00090      */
00091 
00092     pcre *re;
00093     if (  MuxAlarm.bAlarmed
00094        || (re = pcre_compile(pattern, case_opt, &errptr, &erroffset, NULL)) == NULL)
00095     {
00096         /*
00097          * This is a matching error. We have an error message in
00098          * regexp_errbuf that we can ignore, since we're doing
00099          * command-matching.
00100          */
00101         return false;
00102     }
00103 
00104     // To capture N substrings, you need space for 3(N+1) offsets in the
00105     // offset vector. We'll allow 2N-1 substrings and possibly ignore some.
00106     //
00107     const int ovecsize = 6 * nargs;
00108     int *ovec = new int[ovecsize];
00109 
00110     /*
00111      * Now we try to match the pattern. The relevant fields will
00112      * automatically be filled in by this.
00113      */
00114     matches = pcre_exec(re, NULL, str, strlen(str), 0, 0, ovec, ovecsize);
00115     if (matches < 0)
00116     {
00117         delete [] ovec;
00118         MEMFREE(re);
00119         return false;
00120     }
00121 
00122     if (matches == 0)
00123     {
00124         // There were too many substring matches. See docs for
00125         // pcre_copy_substring().
00126         //
00127         matches = ovecsize / 3;
00128     }
00129 
00130     /*
00131      * Now we fill in our args vector. Note that in regexp matching,
00132      * 0 is the entire string matched, and the parenthesized strings
00133      * go from 1 to 9. We DO PRESERVE THIS PARADIGM, for consistency
00134      * with other languages.
00135      */
00136 
00137     for (i = 0; i < nargs; ++i)
00138     {
00139         args[i] = alloc_lbuf("regexp_match");
00140         if (pcre_copy_substring(str, ovec, matches, i,
00141                                 args[i], LBUF_SIZE) < 0)
00142         {
00143             free_lbuf(args[i]);
00144             args[i] = NULL;
00145         }
00146     }
00147 
00148     delete [] ovec;
00149     MEMFREE(re);
00150     return true;
00151 }
00152 
00153 /* ----------------------------------------------------------------------
00154  * atr_match: Check attribute list for wild card matches and queue them.
00155  */
00156 
00157 static int atr_match1
00158 (
00159     dbref thing,
00160     dbref parent,
00161     dbref player,
00162     char  type,
00163     char  *str,
00164     char  *raw_str,
00165     int   check_exclude,
00166     int   hash_insert
00167 )
00168 {
00169     // See if we can do it.  Silently fail if we can't.
00170     //
00171     if (!could_doit(player, parent, A_LUSE))
00172     {
00173         return -1;
00174     }
00175 
00176     int match = 0;
00177     if (  AMATCH_CMD == type
00178        && mudstate.bfNoCommands.IsSet(parent))
00179     {
00180         return match;
00181     }
00182     else if ( AMATCH_LISTEN == type
00183             && mudstate.bfNoListens.IsSet(parent))
00184     {
00185         return match;
00186     }
00187 
00188     bool bFoundCommands = false;
00189     bool bFoundListens  = false;
00190 
00191     char *as;
00192     atr_push();
00193     for (int atr = atr_head(parent, &as); atr; atr = atr_next(&as))
00194     {
00195         ATTR *ap = atr_num(atr);
00196 
00197         // Never check NOPROG attributes.
00198         //
00199         if (  !ap
00200            || (ap->flags & AF_NOPROG))
00201         {
00202             continue;
00203         }
00204 
00205         // We need to grab the attribute even before we know whether we'll use
00206         // it or not in order to maintain cached knowledge about ^-Commands
00207         // and $-Commands.
00208         //
00209         dbref aowner;
00210         int   aflags;
00211         char buff[LBUF_SIZE];
00212         atr_get_str(buff, parent, atr, &aowner, &aflags);
00213 
00214         if (aflags & AF_NOPROG)
00215         {
00216             continue;
00217         }
00218 
00219         char *s = NULL;
00220         if (  AMATCH_CMD    == buff[0]
00221            || AMATCH_LISTEN == buff[0])
00222         {
00223             s = strchr(buff+1, ':');
00224             if (s)
00225             {
00226                 if (AMATCH_CMD == buff[0])
00227                 {
00228                     bFoundCommands = true;
00229                 }
00230                 else
00231                 {
00232                     bFoundListens = true;
00233                 }
00234             }
00235         }
00236 
00237         // If we aren't the bottom level, check if we saw this attr
00238         // before. Also exclude it if the attribute type is PRIVATE.
00239         //
00240         if (  check_exclude
00241            && (  (ap->flags & AF_PRIVATE)
00242               || (aflags & AF_PRIVATE)
00243               || hashfindLEN(&(ap->number), sizeof(ap->number), &mudstate.parent_htab)))
00244         {
00245             continue;
00246         }
00247 
00248         // If we aren't the top level, remember this attr so we
00249         // exclude it from now on.
00250         //
00251         if (hash_insert)
00252         {
00253             hashaddLEN(&(ap->number), sizeof(ap->number), &atr, &mudstate.parent_htab);
00254         }
00255 
00256         // Check for the leadin character after excluding the attrib.
00257         // This lets non-command attribs on the child block commands
00258         // on the parent.
00259         //
00260         if (buff[0] != type)
00261         {
00262             continue;
00263         }
00264 
00265         // Was there a ':'?
00266         //
00267         if (!s)
00268         {
00269             continue;
00270         }
00271         *s++ = '\0';
00272 
00273         char *args[NUM_ENV_VARS];
00274         if (  (  0 != (aflags & AF_REGEXP)
00275             && regexp_match(buff + 1, (aflags & AF_NOPARSE) ? raw_str : str,
00276                 ((aflags & AF_CASE) ? 0 : PCRE_CASELESS), args, NUM_ENV_VARS))
00277            || (  0 == (aflags & AF_REGEXP)
00278               && wild(buff + 1, (aflags & AF_NOPARSE) ? raw_str : str,
00279                 args, NUM_ENV_VARS)))
00280         {
00281             match = 1;
00282             CLinearTimeAbsolute lta;
00283             wait_que(thing, player, player, false, lta, NOTHING, 0, s,
00284                 args, NUM_ENV_VARS, mudstate.global_regs);
00285 
00286             for (int i = 0; i < NUM_ENV_VARS; i++)
00287             {
00288                 if (args[i])
00289                 {
00290                     free_lbuf(args[i]);
00291                 }
00292             }
00293         }
00294     }
00295     atr_pop();
00296 
00297     if (bFoundCommands)
00298     {
00299         mudstate.bfNoCommands.Clear(parent);
00300         mudstate.bfCommands.Set(parent);
00301     }
00302     else
00303     {
00304         mudstate.bfCommands.Clear(parent);
00305         mudstate.bfNoCommands.Set(parent);
00306     }
00307 
00308     if (bFoundListens)
00309     {
00310         mudstate.bfNoListens.Clear(parent);
00311         mudstate.bfListens.Set(parent);
00312     }
00313     else
00314     {
00315         mudstate.bfListens.Clear(parent);
00316         mudstate.bfNoListens.Set(parent);
00317     }
00318     return match;
00319 }
00320 
00321 bool atr_match
00322 (
00323     dbref thing,
00324     dbref player,
00325     char  type,
00326     char  *str,
00327     char  *raw_str,
00328     bool check_parents
00329 )
00330 {
00331     int lev, result;
00332     bool exclude, insert;
00333     dbref parent;
00334 
00335     // If thing is halted or we are matching $-commands on a NO_COMMAND
00336     // object, don't check anything
00337     //
00338     if (  Halted(thing)
00339        || (  AMATCH_CMD == type
00340           && No_Command(thing)))
00341     {
00342         return false;
00343     }
00344 
00345     // If we're matching ^-commands, strip ANSI
00346     //
00347     if (AMATCH_LISTEN == type)
00348     {
00349         // Remember, strip_ansi returns a pointer to a static buffer
00350         // within itself.
00351         //
00352         size_t junk;
00353         str = strip_ansi(str, &junk);
00354     }
00355 
00356     // If not checking parents, just check the thing
00357     //
00358     bool match = false;
00359     if (!check_parents)
00360     {
00361         return (atr_match1(thing, thing, player, type, str, raw_str, false, false) > 0);
00362     }
00363 
00364     // Check parents, ignoring halted objects
00365     //
00366     exclude = false;
00367     insert = true;
00368     hashflush(&mudstate.parent_htab);
00369     ITER_PARENTS(thing, parent, lev)
00370     {
00371         if (!Good_obj(Parent(parent)))
00372         {
00373             insert = false;
00374         }
00375         result = atr_match1(thing, parent, player, type, str, raw_str,
00376             exclude, insert);
00377         if (result > 0)
00378         {
00379             match = true;
00380         }
00381         else if (result < 0)
00382         {
00383             return match;
00384         }
00385         exclude = true;
00386     }
00387     return match;
00388 }
00389 
00390 /* ---------------------------------------------------------------------------
00391  * notify_check: notifies the object #target of the message msg, and
00392  * optionally notify the contents, neighbors, and location also.
00393  */
00394 
00395 static bool check_filter(dbref object, dbref player, int filter, const char *msg)
00396 {
00397     int aflags;
00398     dbref aowner;
00399     char *buf, *nbuf, *cp, *dp, *str;
00400 
00401     buf = atr_pget(object, filter, &aowner, &aflags);
00402     if (!*buf)
00403     {
00404         free_lbuf(buf);
00405         return true;
00406     }
00407     char **preserve = NULL;
00408     int *preserve_len = NULL;
00409     preserve = PushPointers(MAX_GLOBAL_REGS);
00410     preserve_len = PushIntegers(MAX_GLOBAL_REGS);
00411     save_global_regs("check_filter_save", preserve, preserve_len);
00412     nbuf = dp = alloc_lbuf("check_filter");
00413     str = buf;
00414     mux_exec(nbuf, &dp, object, player, player,
00415              EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **)NULL, 0);
00416     *dp = '\0';
00417     dp = nbuf;
00418     free_lbuf(buf);
00419     restore_global_regs("check_filter_restore", preserve, preserve_len);
00420     PopIntegers(preserve_len, MAX_GLOBAL_REGS);
00421     PopPointers(preserve, MAX_GLOBAL_REGS);
00422 
00423     if (!(aflags & AF_REGEXP))
00424     {
00425         do
00426         {
00427             cp = parse_to(&dp, ',', EV_STRIP_CURLY);
00428             mudstate.wild_invk_ctr = 0;
00429             if (  MuxAlarm.bAlarmed
00430                || quick_wild(cp, msg))
00431             {
00432                 free_lbuf(nbuf);
00433                 return false;
00434             }
00435         } while (dp != NULL);
00436     }
00437     else
00438     {
00439         int case_opt = (aflags & AF_CASE) ? 0 : PCRE_CASELESS;
00440         do
00441         {
00442             int erroffset;
00443             const char *errptr;
00444             cp = parse_to(&dp, ',', EV_STRIP_CURLY);
00445             pcre *re;
00446             if (  !MuxAlarm.bAlarmed
00447                && (re = pcre_compile(cp, case_opt, &errptr, &erroffset, NULL)) != NULL)
00448             {
00449                 const int ovecsize = 33;
00450                 int ovec[ovecsize];
00451                 int matches = pcre_exec(re, NULL, msg, strlen(msg), 0, 0,
00452                     ovec, ovecsize);
00453                 if (0 <= matches)
00454                 {
00455                     MEMFREE(re);
00456                     free_lbuf(nbuf);
00457                     return false;
00458                 }
00459                 MEMFREE(re);
00460             }
00461         } while (dp != NULL);
00462     }
00463     free_lbuf(nbuf);
00464     return true;
00465 }
00466 
00467 static char *add_prefix(dbref object, dbref player, int prefix,
00468                         const char *msg, const char *dflt)
00469 {
00470     int aflags;
00471     dbref aowner;
00472     char *buf, *nbuf, *cp, *str;
00473 
00474     buf = atr_pget(object, prefix, &aowner, &aflags);
00475     if (!*buf)
00476     {
00477         cp = buf;
00478         safe_str(dflt, buf, &cp);
00479     }
00480     else
00481     {
00482         char **preserve = NULL;
00483         int *preserve_len = NULL;
00484         preserve = PushPointers(MAX_GLOBAL_REGS);
00485         preserve_len = PushIntegers(MAX_GLOBAL_REGS);
00486         save_global_regs("add_prefix_save", preserve, preserve_len);
00487 
00488         nbuf = cp = alloc_lbuf("add_prefix");
00489         str = buf;
00490         mux_exec(nbuf, &cp, object, player, player,
00491                  EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **)NULL, 0);
00492         free_lbuf(buf);
00493 
00494         restore_global_regs("add_prefix_restore", preserve, preserve_len);
00495         PopIntegers(preserve_len, MAX_GLOBAL_REGS);
00496         PopPointers(preserve, MAX_GLOBAL_REGS);
00497 
00498         buf = nbuf;
00499     }
00500     if (cp != buf)
00501     {
00502         safe_chr(' ', buf, &cp);
00503     }
00504     safe_str(msg, buf, &cp);
00505     *cp = '\0';
00506     return buf;
00507 }
00508 
00509 static char *dflt_from_msg(dbref sender, dbref sendloc)
00510 {
00511     char *tp, *tbuff;
00512 
00513     tp = tbuff = alloc_lbuf("notify_check.fwdlist");
00514     safe_str("From ", tbuff, &tp);
00515     if (Good_obj(sendloc))
00516     {
00517         safe_str(Name(sendloc), tbuff, &tp);
00518     }
00519     else
00520     {
00521         safe_str(Name(sender), tbuff, &tp);
00522     }
00523     safe_chr(',', tbuff, &tp);
00524     *tp = '\0';
00525     return tbuff;
00526 }
00527 
00528 /* Do HTML escaping, converting < to &lt;, etc.  'dest' needs to be
00529  * allocated & freed by the caller.
00530  *
00531  * If you're using this to append to a string, you can pass in the
00532  * safe_{str|chr} (char **) so we can just do the append directly,
00533  * saving you an alloc_lbuf()...free_lbuf().  If you want us to append
00534  * from the start of 'dest', just pass in a 0 for 'destp'.
00535  *
00536  * Returns 0 if the copy succeeded, 1 if it failed.
00537  */
00538 bool html_escape(const char *src, char *dest, char **destp)
00539 {
00540     const char *msg_orig;
00541     bool ret = false;
00542 
00543     if (destp == 0)
00544     {
00545         char *temp = dest;
00546         destp = &temp;
00547     }
00548 
00549     for (msg_orig = src; msg_orig && *msg_orig && !ret; msg_orig++)
00550     {
00551         char *p = *destp;
00552         switch (*msg_orig)
00553         {
00554         case '<':
00555             safe_str("&lt;", dest, destp);
00556             break;
00557 
00558         case '>':
00559             safe_str("&gt;", dest, destp);
00560             break;
00561 
00562         case '&':
00563             safe_str("&amp;", dest, destp);
00564             break;
00565 
00566         case '\"':
00567             safe_str("&quot;", dest, destp);
00568             break;
00569 
00570         default:
00571             safe_chr(*msg_orig, dest, destp);
00572             break;
00573         }
00574 
00575         // For <>&\, this may cause an extra loop around before it figures out that we are
00576         // out of buffer, but no harm is done in this, and the common case is a single character.
00577         //
00578         if (p == *destp)
00579         {
00580             ret = true;
00581         }
00582     }
00583     **destp = 0;
00584     return ret;
00585 }
00586 
00587 void notify_check(dbref target, dbref sender, const char *msg, int key)
00588 {
00589     // If speaker is invalid or message is empty, just exit.
00590     //
00591     if (  !Good_obj(target)
00592        || !msg
00593        || !*msg)
00594     {
00595         return;
00596     }
00597 
00598 #ifdef WOD_REALMS
00599     if ((key & MSG_OOC) == 0)
00600     {
00601         if ((key & MSG_SAYPOSE) != 0)
00602         {
00603             if (REALM_DO_HIDDEN_FROM_YOU == DoThingToThingVisibility(target, sender, ACTION_IS_TALKING))
00604             {
00605                 return;
00606             }
00607         }
00608         else
00609         {
00610             if (REALM_DO_HIDDEN_FROM_YOU == DoThingToThingVisibility(target, sender, ACTION_IS_MOVING))
00611             {
00612                 return;
00613             }
00614         }
00615     }
00616 #endif // WOD_REALMS
00617 
00618     // Enforce a recursion limit
00619     //
00620     mudstate.ntfy_nest_lev++;
00621     if (mudconf.ntfy_nest_lim <= mudstate.ntfy_nest_lev)
00622     {
00623         mudstate.ntfy_nest_lev--;
00624         return;
00625     }
00626 
00627     char *msg_ns, *mp, *tbuff, *tp, *buff;
00628     char *args[NUM_ENV_VARS];
00629     dbref aowner,  recip, obj;
00630     int i, nargs, aflags;
00631     FWDLIST *fp;
00632 
00633     // If we want NOSPOOF output, generate it.  It is only needed if we are
00634     // sending the message to the target object.
00635     //
00636     if (key & MSG_ME)
00637     {
00638         mp = msg_ns = alloc_lbuf("notify_check");
00639         if (  Nospoof(target)
00640            && target != sender
00641            && target != mudstate.curr_enactor
00642            && target != mudstate.curr_executor)
00643         {
00644             // I'd really like to use tprintf here but I can't because the
00645             // caller may have.  notify(target, tprintf(...)) is quite common
00646             // in the code.
00647             //
00648             tbuff = alloc_sbuf("notify_check.nospoof");
00649             safe_chr('[', msg_ns, &mp);
00650             safe_str(Name(sender), msg_ns, &mp);
00651             sprintf(tbuff, "(#%d)", sender);
00652             safe_str(tbuff, msg_ns, &mp);
00653 
00654             if (sender != Owner(sender))
00655             {
00656                 safe_chr('{', msg_ns, &mp);
00657                 safe_str(Name(Owner(sender)), msg_ns, &mp);
00658                 safe_chr('}', msg_ns, &mp);
00659             }
00660             if (sender != mudstate.curr_enactor)
00661             {
00662                 sprintf(tbuff, "<-(#%d)", mudstate.curr_enactor);
00663                 safe_str(tbuff, msg_ns, &mp);
00664             }
00665             safe_str("] ", msg_ns, &mp);
00666             free_sbuf(tbuff);
00667         }
00668         safe_str(msg, msg_ns, &mp);
00669         *mp = '\0';
00670     }
00671     else
00672     {
00673         msg_ns = NULL;
00674     }
00675 
00676     // msg contains the raw message, msg_ns contains the NOSPOOFed msg.
00677     //
00678     bool check_listens = !Halted(target);
00679     switch (Typeof(target))
00680     {
00681     case TYPE_PLAYER:
00682         if (key & MSG_ME)
00683         {
00684             if (key & MSG_HTML)
00685             {
00686                 raw_notify_html(target, msg_ns);
00687             }
00688             else
00689             {
00690                 if (Html(target))
00691                 {
00692                     char *msg_ns_escaped;
00693 
00694                     msg_ns_escaped = alloc_lbuf("notify_check_escape");
00695                     html_escape(msg_ns, msg_ns_escaped, 0);
00696                     raw_notify(target, msg_ns_escaped);
00697                     free_lbuf(msg_ns_escaped);
00698                 }
00699                 else
00700                 {
00701                     raw_notify(target, msg_ns);
00702                 }
00703             }
00704         }
00705         if (!mudconf.player_listen)
00706         {
00707             check_listens = false;
00708         }
00709 
00710         // FALLTHROUGH
00711 
00712     case TYPE_THING:
00713     case TYPE_ROOM:
00714 
00715         // If we're in a pipe, objects can receive raw_notify if
00716         // they're not a player. (players were already notified
00717         // above.
00718         //
00719         if (  mudstate.inpipe
00720            && !isPlayer(target))
00721         {
00722             raw_notify(target, msg_ns);
00723         }
00724 
00725         // Forward puppet message if it is for me.
00726         //
00727         bool has_neighbors = Has_location(target);
00728         dbref targetloc = where_is(target);
00729         bool is_audible = Audible(target);
00730 
00731         if ( (key & MSG_ME)
00732            && Puppet(target)
00733            && (target != Owner(target))
00734            && (  (key & MSG_PUP_ALWAYS)
00735               || (  targetloc != Location(Owner(target))
00736                  && targetloc != Owner(target))))
00737         {
00738             tp = tbuff = alloc_lbuf("notify_check.puppet");
00739             safe_str(Name(target), tbuff, &tp);
00740             safe_str("> ", tbuff, &tp);
00741             safe_str(msg_ns, tbuff, &tp);
00742             *tp = '\0';
00743             raw_notify(Owner(target), tbuff);
00744             free_lbuf(tbuff);
00745         }
00746 
00747         // Check for @Listen match if it will be useful.
00748         //
00749         bool pass_listen = false;
00750         nargs = 0;
00751         if (  check_listens
00752            && (key & (MSG_ME | MSG_INV_L))
00753            && H_Listen(target))
00754         {
00755             tp = atr_get(target, A_LISTEN, &aowner, &aflags);
00756             if (*tp && wild(tp, (char *)msg, args, NUM_ENV_VARS))
00757             {
00758                 for (nargs = NUM_ENV_VARS; nargs && (!args[nargs - 1] || !(*args[nargs - 1])); nargs--)
00759                 {
00760                     ; // Nothing
00761                 }
00762                 pass_listen = true;
00763             }
00764             free_lbuf(tp);
00765         }
00766 
00767         // If we matched the @listen or are monitoring, check the
00768         // USE lock.
00769         //
00770         bool pass_uselock = false;
00771         if (  (key & MSG_ME)
00772            && check_listens
00773            && (  pass_listen
00774               || Monitor(target)))
00775         {
00776             pass_uselock = could_doit(sender, target, A_LUSE);
00777         }
00778 
00779         // Process AxHEAR if we pass LISTEN, USElock and it's for me.
00780         //
00781         if (  (key & MSG_ME)
00782            && pass_listen
00783            && pass_uselock
00784            && mudstate.nHearNest <= 2)
00785         {
00786             mudstate.nHearNest++;
00787             if (sender != target)
00788             {
00789                 did_it(sender, target, 0, NULL, 0, NULL, A_AHEAR, args, nargs);
00790             }
00791             else
00792             {
00793                 did_it(sender, target, 0, NULL, 0, NULL, A_AMHEAR, args,
00794                     nargs);
00795             }
00796             did_it(sender, target, 0, NULL, 0, NULL, A_AAHEAR, args, nargs);
00797             mudstate.nHearNest--;
00798         }
00799 
00800         // Get rid of match arguments. We don't need them anymore.
00801         //
00802         if (pass_listen)
00803         {
00804             for (i = 0; i < nargs; i++)
00805             {
00806                 if (args[i] != NULL)
00807                 {
00808                     free_lbuf(args[i]);
00809                 }
00810             }
00811         }
00812 
00813         // Process ^-listens if for me, MONITOR, and we pass USElock.
00814         //
00815         if (  (key & MSG_ME)
00816            && pass_uselock
00817            && sender != target
00818            && Monitor(target))
00819         {
00820             atr_match(target, sender, AMATCH_LISTEN, (char *)msg, (char *)msg,
00821                 false);
00822         }
00823 
00824         // Deliver message to forwardlist members.
00825         //
00826         if ( (key & MSG_FWDLIST)
00827            && is_audible
00828            && check_filter(target, sender, A_FILTER, msg))
00829         {
00830             tbuff = dflt_from_msg(sender, target);
00831             buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
00832             free_lbuf(tbuff);
00833 
00834             fp = fwdlist_get(target);
00835             if (fp)
00836             {
00837                 for (i = 0; i < fp->count; i++)
00838                 {
00839                     recip = fp->data[i];
00840                     if (  !Good_obj(recip)
00841                        || recip == target)
00842                     {
00843                         continue;
00844                     }
00845                     notify_check(recip, sender, buff,
00846                              MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE);
00847                 }
00848             }
00849             free_lbuf(buff);
00850         }
00851 
00852         // Deliver message through audible exits.
00853         //
00854         if (key & MSG_INV_EXITS)
00855         {
00856             DOLIST(obj, Exits(target))
00857             {
00858                 recip = Location(obj);
00859                 if (  Audible(obj)
00860                    && (  recip != target
00861                       && check_filter(obj, sender, A_FILTER, msg)))
00862                 {
00863                     buff = add_prefix(obj, target, A_PREFIX, msg,
00864                         "From a distance,");
00865                     notify_check(recip, sender, buff,
00866                         MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE);
00867                     free_lbuf(buff);
00868                 }
00869             }
00870         }
00871 
00872         // Deliver message through neighboring audible exits.
00873         //
00874         if (  has_neighbors
00875            && (  (key & MSG_NBR_EXITS)
00876               || (  (key & MSG_NBR_EXITS_A)
00877                  && is_audible)))
00878         {
00879             // If from inside, we have to add the prefix string of
00880             // the container.
00881             //
00882             if (key & MSG_S_INSIDE)
00883             {
00884                 tbuff = dflt_from_msg(sender, target);
00885                 buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
00886                 free_lbuf(tbuff);
00887             }
00888             else
00889             {
00890                 buff = (char *)msg;
00891             }
00892 
00893             DOLIST(obj, Exits(Location(target)))
00894             {
00895                 recip = Location(obj);
00896                 if (  Good_obj(recip)
00897                    && Audible(obj)
00898                    && recip != targetloc
00899                    && recip != target
00900                    && check_filter(obj, sender, A_FILTER, msg))
00901                 {
00902                     tbuff = add_prefix(obj, target, A_PREFIX, buff,
00903                         "From a distance,");
00904                     notify_check(recip, sender, tbuff,
00905                         MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE);
00906                     free_lbuf(tbuff);
00907                 }
00908             }
00909             if (key & MSG_S_INSIDE)
00910             {
00911                 free_lbuf(buff);
00912             }
00913         }
00914 
00915         // Deliver message to contents.
00916         //
00917         if (  (  (key & MSG_INV)
00918               || (  (key & MSG_INV_L)
00919                  && pass_listen))
00920            && check_filter(target, sender, A_INFILTER, msg))
00921         {
00922             // Don't prefix the message if we were given the MSG_NOPREFIX key.
00923             //
00924             if (key & MSG_S_OUTSIDE)
00925             {
00926                 buff = add_prefix(target, sender, A_INPREFIX, msg, "");
00927             }
00928             else
00929             {
00930                 buff = (char *)msg;
00931             }
00932             DOLIST(obj, Contents(target))
00933             {
00934                 if (obj != target)
00935                 {
00936                     notify_check(obj, sender, buff,
00937                         MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | key & MSG_HTML);
00938                 }
00939             }
00940             if (key & MSG_S_OUTSIDE)
00941             {
00942                 free_lbuf(buff);
00943             }
00944         }
00945 
00946         // Deliver message to neighbors.
00947         //
00948         if (  has_neighbors
00949            && (  (key & MSG_NBR)
00950               || (  (key & MSG_NBR_A)
00951                  && is_audible
00952                  && check_filter(target, sender, A_FILTER, msg))))
00953         {
00954             if (key & MSG_S_INSIDE)
00955             {
00956                 tbuff = dflt_from_msg(sender, target);
00957                 buff = add_prefix(target, sender, A_PREFIX, msg, "");
00958                 free_lbuf(tbuff);
00959             }
00960             else
00961             {
00962                 buff = (char *)msg;
00963             }
00964             DOLIST(obj, Contents(targetloc))
00965             {
00966                 if (  obj != target
00967                    && obj != targetloc)
00968                 {
00969                     notify_check(obj, sender, buff,
00970                     MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE);
00971                 }
00972             }
00973             if (key & MSG_S_INSIDE)
00974             {
00975                 free_lbuf(buff);
00976             }
00977         }
00978 
00979         // Deliver message to container.
00980         //
00981         if (  has_neighbors
00982            && (  (key & MSG_LOC)
00983               || ( (key & MSG_LOC_A)
00984                  && is_audible
00985                  && check_filter(target, sender, A_FILTER, msg))))
00986         {
00987             if (key & MSG_S_INSIDE)
00988             {
00989                 tbuff = dflt_from_msg(sender, target);
00990                 buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
00991                 free_lbuf(tbuff);
00992             }
00993             else
00994             {
00995                 buff = (char *)msg;
00996             }
00997             notify_check(targetloc, sender, buff,
00998                 MSG_ME | MSG_F_UP | MSG_S_INSIDE);
00999             if (key & MSG_S_INSIDE)
01000             {
01001                 free_lbuf(buff);
01002             }
01003         }
01004     }
01005     if (msg_ns)
01006     {
01007         free_lbuf(msg_ns);
01008     }
01009     mudstate.ntfy_nest_lev--;
01010 }
01011 
01012 void notify_except(dbref loc, dbref player, dbref exception, const char *msg, int key)
01013 {
01014     dbref first;
01015 
01016     if (loc != exception)
01017     {
01018         notify_check(loc, player, msg, (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A | key));
01019     }
01020     DOLIST(first, Contents(loc))
01021     {
01022         if (first != exception)
01023         {
01024             notify_check(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | key));
01025         }
01026     }
01027 }
01028 
01029 void notify_except2(dbref loc, dbref player, dbref exc1, dbref exc2, const char *msg)
01030 {
01031     dbref first;
01032 
01033     if (  loc != exc1
01034        && loc != exc2)
01035     {
01036         notify_check(loc, player, msg, (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A));
01037     }
01038     DOLIST(first, Contents(loc))
01039     {
01040         if (  first != exc1
01041            && first != exc2)
01042         {
01043             notify_check(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE));
01044         }
01045     }
01046 }
01047 
01048 /* ----------------------------------------------------------------------
01049  * Reporting of CPU information.
01050  */
01051 
01052 static void report_timecheck
01053 (
01054     dbref player,
01055     bool yes_screen,
01056     bool yes_log,
01057     bool yes_clear
01058 )
01059 {
01060     int thing, obj_counted;
01061     CLinearTimeDelta ltdPeriod, ltdTotal;
01062     CLinearTimeAbsolute ltaNow;
01063     ltaNow.GetUTC();
01064     ltdPeriod = ltaNow - mudstate.cpu_count_from;
01065 
01066     if (  yes_log
01067        && (LOG_TIMEUSE & mudconf.log_options))
01068     {
01069         start_log("OBJ", "CPU");
01070         log_name(player);
01071         log_text(" checks object time use over ");
01072         log_number(ltdPeriod.ReturnSeconds());
01073         log_text(" seconds" ENDLINE);
01074     }
01075     else
01076     {
01077         yes_log = false;
01078         STARTLOG(LOG_ALWAYS, "WIZ", "TIMECHECK");
01079         log_name(player);
01080         log_text(" checks object time use over ");
01081         log_number(ltdPeriod.ReturnSeconds());
01082         log_text(" seconds");
01083         ENDLOG;
01084     }
01085 
01086     obj_counted = 0;
01087     ltdTotal.Set100ns(0);
01088 
01089     // Step through the db. Care only about the ones that are nonzero.
01090     //
01091     DO_WHOLE_DB(thing)
01092     {
01093         CLinearTimeDelta &ltd = db[thing].cpu_time_used;
01094         if (ltd.Return100ns())
01095         {
01096             ltdTotal += ltd;
01097             long used_msecs = ltd.ReturnMilliseconds();
01098             obj_counted++;
01099             if (yes_log)
01100             {
01101                 Log.tinyprintf("#%d\t%ld" ENDLINE, thing, used_msecs);
01102             }
01103             if (yes_screen)
01104             {
01105                 raw_notify(player, tprintf("#%d\t%ld", thing, used_msecs));
01106             }
01107             if (yes_clear)
01108             {
01109                 ltd.Set100ns(0);
01110             }
01111         }
01112     }
01113 
01114     long lTotal = ltdTotal.ReturnMilliseconds();
01115     long lPeriod = ltdPeriod.ReturnSeconds();
01116 
01117     if (yes_screen)
01118     {
01119         raw_notify(player,
01120             tprintf("Counted %d objects using %ld msecs over %d seconds.",
01121             obj_counted, lTotal, lPeriod));
01122     }
01123 
01124     if (yes_log)
01125     {
01126         Log.tinyprintf("Counted %d objects using %ld msecs over %d seconds.",
01127             obj_counted, lTotal, lPeriod);
01128         end_log();
01129     }
01130 
01131     if (yes_clear)
01132     {
01133         mudstate.cpu_count_from = ltaNow;
01134     }
01135 }
01136 
01137 void do_timecheck(dbref executor, dbref caller, dbref enactor, int key)
01138 {
01139     UNUSED_PARAMETER(caller);
01140     UNUSED_PARAMETER(enactor);
01141 
01142     bool yes_screen, yes_log, yes_clear;
01143 
01144     yes_screen = yes_log = yes_clear = false;
01145 
01146     if (key == 0)
01147     {
01148         // No switches, default to printing to screen and clearing counters.
01149         //
01150         yes_screen = true;
01151         yes_clear = true;
01152     }
01153     else
01154     {
01155         if (key & TIMECHK_RESET)
01156         {
01157             yes_clear = true;
01158         }
01159         if (key & TIMECHK_SCREEN)
01160         {
01161             yes_screen = true;
01162         }
01163         if (key & TIMECHK_LOG)
01164         {
01165             yes_log = true;
01166         }
01167     }
01168     report_timecheck(executor, yes_screen, yes_log, yes_clear);
01169 }
01170 
01171 void do_shutdown
01172 (
01173     dbref executor,
01174     dbref caller,
01175     dbref enactor,
01176     int   key,
01177     char *message
01178 )
01179 {
01180     UNUSED_PARAMETER(caller);
01181     UNUSED_PARAMETER(enactor);
01182 
01183     if (!Can_SiteAdmin(executor))
01184     {
01185         notify(executor, NOPERM_MESSAGE);
01186         return;
01187     }
01188 
01189     raw_broadcast(0, "GAME: Shutdown by %s", Name(Owner(executor)));
01190     STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN");
01191     log_text("Shutdown by ");
01192     log_name(executor);
01193     ENDLOG;
01194 
01195     STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN");
01196     log_text("Shutdown status: ");
01197     log_text(message);
01198     ENDLOG;
01199 
01200     int fd = open(mudconf.status_file, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);
01201     if (fd != -1)
01202     {
01203         write(fd, message, strlen(message));
01204         write(fd, ENDLINE, sizeof(ENDLINE)-1);
01205         DebugTotalFiles++;
01206         if (close(fd) == 0)
01207         {
01208             DebugTotalFiles--;
01209         }
01210     }
01211 
01212     // Do we perform a normal or an emergency shutdown? Normal
01213     // shutdown is handled by exiting the main loop in shovechars,
01214     // emergency shutdown is done here.
01215     //
01216     if (key & SHUTDN_PANIC)
01217     {
01218         // Close down the network interface.
01219         //
01220         emergency_shutdown();
01221 
01222         local_presync_database();
01223 
01224         // Close the attribute text db and dump the header db.
01225         //
01226 #ifndef MEMORY_BASED
0122