mux/src/predicates.cpp

Go to the documentation of this file.
00001 // predicates.cpp
00002 //
00003 // $Id: predicates.cpp,v 1.78 2007/04/14 04:57:05 sdennis Exp $
00004 //
00005 
00006 #include "copyright.h"
00007 #include "autoconf.h"
00008 #include "config.h"
00009 #include "externs.h"
00010 
00011 #include <signal.h>
00012 
00013 #include "ansi.h"
00014 #include "attrs.h"
00015 #include "command.h"
00016 #include "interface.h"
00017 #include "powers.h"
00018 #ifdef REALITY_LVLS
00019 #include "levels.h"
00020 #endif /* REALITY_LVLS */
00021 
00022 char * DCL_CDECL tprintf(const char *fmt,...)
00023 {
00024     static char buff[LBUF_SIZE];
00025     va_list ap;
00026     va_start(ap, fmt);
00027     mux_vsnprintf(buff, LBUF_SIZE, fmt, ap);
00028     va_end(ap);
00029     return buff;
00030 }
00031 
00032 void DCL_CDECL safe_tprintf_str(char *str, char **bp, const char *fmt,...)
00033 {
00034     va_list ap;
00035     va_start(ap, fmt);
00036     size_t nAvailable = LBUF_SIZE - (*bp - str);
00037     size_t len = mux_vsnprintf(*bp, (int)nAvailable, fmt, ap);
00038     va_end(ap);
00039     *bp += len;
00040 }
00041 
00042 /* ---------------------------------------------------------------------------
00043  * insert_first, remove_first: Insert or remove objects from lists.
00044  */
00045 
00046 dbref insert_first(dbref head, dbref thing)
00047 {
00048     s_Next(thing, head);
00049     return thing;
00050 }
00051 
00052 dbref remove_first(dbref head, dbref thing)
00053 {
00054     if (head == thing)
00055     {
00056         return Next(thing);
00057     }
00058 
00059     dbref prev;
00060 
00061     DOLIST(prev, head)
00062     {
00063         if (Next(prev) == thing)
00064         {
00065             s_Next(prev, Next(thing));
00066             return head;
00067         }
00068     }
00069     return head;
00070 }
00071 
00072 /* ---------------------------------------------------------------------------
00073  * reverse_list: Reverse the order of members in a list.
00074  */
00075 
00076 dbref reverse_list(dbref list)
00077 {
00078     dbref newlist, rest;
00079 
00080     newlist = NOTHING;
00081     while (list != NOTHING)
00082     {
00083         rest = Next(list);
00084         s_Next(list, newlist);
00085         newlist = list;
00086         list = rest;
00087     }
00088     return newlist;
00089 }
00090 
00091 /* ---------------------------------------------------------------------------
00092  * member - indicate if thing is in list
00093  */
00094 
00095 bool member(dbref thing, dbref list)
00096 {
00097     DOLIST(list, list)
00098     {
00099         if (list == thing)
00100         {
00101             return true;
00102         }
00103     }
00104     return false;
00105 }
00106 
00107 bool could_doit(dbref player, dbref thing, int locknum)
00108 {
00109     if (thing == HOME)
00110     {
00111         return true;
00112     }
00113 
00114     // If nonplayer tries to get key, then no.
00115     //
00116     if (  !isPlayer(player)
00117        && Key(thing))
00118     {
00119         return false;
00120     }
00121     if (Pass_Locks(player))
00122     {
00123         return true;
00124     }
00125 
00126     dbref aowner;
00127     int   aflags;
00128     char *key = atr_get(thing, locknum, &aowner, &aflags);
00129     bool doit = eval_boolexp_atr(player, thing, thing, key);
00130     free_lbuf(key);
00131     return doit;
00132 }
00133 
00134 bool can_see(dbref player, dbref thing, bool can_see_loc)
00135 {
00136     // Don't show if all the following apply: Sleeping players should not be
00137     // seen.  The thing is a disconnected player.  The player is not a
00138     // puppet.
00139     //
00140     if (  mudconf.dark_sleepers
00141        && isPlayer(thing)
00142        && !Connected(thing)
00143        && !Puppet(thing))
00144     {
00145         return false;
00146     }
00147 
00148     // You don't see yourself or exits.
00149     //
00150     if (  player == thing
00151        || isExit(thing))
00152     {
00153         return false;
00154     }
00155 
00156     // If loc is not dark, you see it if it's not dark or you control it.  If
00157     // loc is dark, you see it if you control it.  Seeing your own dark
00158     // objects is controlled by mudconf.see_own_dark.  In dark locations, you
00159     // also see things that are LIGHT and !DARK.
00160     //
00161     if (can_see_loc)
00162     {
00163 #ifdef REALITY_LVLS
00164        return ((!Dark(thing) && IsReal(player, thing)) ||
00165 #else
00166         return (!Dark(thing) ||
00167 #endif /* REALITY_LVLS */
00168             (mudconf.see_own_dark && MyopicExam(player, thing)));
00169     }
00170     else
00171     {
00172 #ifdef REALITY_LVLS
00173         return ((Light(thing) && !Dark(thing) && IsReal(player, thing)) ||
00174 #else
00175         return ((Light(thing) && !Dark(thing)) ||
00176 #endif /* REALITY_LVLS */
00177             (mudconf.see_own_dark && MyopicExam(player, thing)));
00178     }
00179 }
00180 
00181 static bool pay_quota(dbref who, int cost)
00182 {
00183     // If no cost, succeed
00184     //
00185     if (cost <= 0)
00186     {
00187         return true;
00188     }
00189 
00190     // determine quota
00191     //
00192     dbref aowner;
00193     int aflags;
00194     char *quota_str = atr_get(Owner(who), A_RQUOTA, &aowner, &aflags);
00195     int quota = mux_atol(quota_str);
00196     free_lbuf(quota_str);
00197 
00198     // enough to build?  Wizards always have enough.
00199     //
00200     quota -= cost;
00201     if (  quota < 0
00202        && !Free_Quota(who)
00203        && !Free_Quota(Owner(who)))
00204     {
00205         return false;
00206     }
00207 
00208     // Dock the quota.
00209     //
00210     char buf[20];
00211     mux_ltoa(quota, buf);
00212     atr_add_raw(Owner(who), A_RQUOTA, buf);
00213 
00214     return true;
00215 }
00216 
00217 bool canpayfees(dbref player, dbref who, int pennies, int quota)
00218 {
00219     if (  !Wizard(who)
00220        && !Wizard(Owner(who))
00221        && !Free_Money(who)
00222        && !Free_Money(Owner(who))
00223        && (Pennies(Owner(who)) < pennies))
00224     {
00225         if (player == who)
00226         {
00227             notify(player, tprintf("Sorry, you don't have enough %s.",
00228                        mudconf.many_coins));
00229         }
00230         else
00231         {
00232             notify(player, tprintf("Sorry, that player doesn't have enough %s.",
00233                 mudconf.many_coins));
00234         }
00235         return false;
00236     }
00237     if (mudconf.quotas)
00238     {
00239         if (!pay_quota(who, quota))
00240         {
00241             if (player == who)
00242             {
00243                 notify(player, "Sorry, your building contract has run out.");
00244             }
00245             else
00246             {
00247                 notify(player,
00248                     "Sorry, that player's building contract has run out.");
00249             }
00250             return false;
00251         }
00252     }
00253     payfor(who, pennies);
00254     return true;
00255 }
00256 
00257 bool payfor(dbref who, int cost)
00258 {
00259     if (  Wizard(who)
00260        || Wizard(Owner(who))
00261        || Free_Money(who)
00262        || Free_Money(Owner(who)))
00263     {
00264         return true;
00265     }
00266     who = Owner(who);
00267     int tmp;
00268     if ((tmp = Pennies(who)) >= cost)
00269     {
00270         s_Pennies(who, tmp - cost);
00271         return true;
00272     }
00273     return false;
00274 }
00275 
00276 void add_quota(dbref who, int payment)
00277 {
00278     dbref aowner;
00279     int aflags;
00280     char buf[20];
00281 
00282     char *quota = atr_get(who, A_RQUOTA, &aowner, &aflags);
00283     mux_ltoa(mux_atol(quota) + payment, buf);
00284     free_lbuf(quota);
00285     atr_add_raw(who, A_RQUOTA, buf);
00286 }
00287 
00288 void giveto(dbref who, int pennies)
00289 {
00290     if (  Wizard(who)
00291        || Wizard(Owner(who))
00292        || Free_Money(who)
00293        || Free_Money(Owner(who)))
00294     {
00295         return;
00296     }
00297     who = Owner(who);
00298     s_Pennies(who, Pennies(who) + pennies);
00299 }
00300 
00301 // The following function validates that the object names (which will be
00302 // used for things and rooms, but not for players or exits) and generates
00303 // a canonical form of that name (with optimized ANSI).
00304 //
00305 char *MakeCanonicalObjectName(const char *pName, int *pnName, bool *pbValid)
00306 {
00307     static char Buf[MBUF_SIZE];
00308 
00309     *pnName = 0;
00310     *pbValid = false;
00311 
00312     if (!pName)
00313     {
00314         return NULL;
00315     }
00316 
00317     // Build up what the real name would be. If we pass all the
00318     // checks, this is what we will return as a result.
00319     //
00320     int nVisualWidth;
00321     int nBuf = ANSI_TruncateToField(pName, sizeof(Buf), Buf, MBUF_SIZE,
00322         &nVisualWidth, ANSI_ENDGOAL_NORMAL);
00323 
00324     // Disallow pure ANSI names. There must be at least -something-
00325     // visible.
00326     //
00327     if (nVisualWidth <= 0)
00328     {
00329         return NULL;
00330     }
00331 
00332     // Get the stripped version (Visible parts without color info).
00333     //
00334     size_t nStripped;
00335     char *pStripped = strip_ansi(Buf, &nStripped);
00336 
00337     // Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE
00338     // as the first character, or SPACE as the last character
00339     //
00340     if (  strchr("*!#", *pStripped)
00341        || mux_isspace(pStripped[0])
00342        || mux_isspace(pStripped[nStripped-1]))
00343     {
00344         return NULL;
00345     }
00346 
00347     // Only printable characters besides ARG_DELIMITER, AND_TOKEN,
00348     // and OR_TOKEN are allowed.
00349     //
00350     for (unsigned int i = 0; i < nStripped; i++)
00351     {
00352         if (!mux_ObjectNameSet(pStripped[i]))
00353         {
00354             return NULL;
00355         }
00356     }
00357 
00358     // Special names are specifically dis-allowed.
00359     //
00360     if (  (nStripped == 2 && memcmp("me", pStripped, 2) == 0)
00361        || (nStripped == 4 && (  memcmp("home", pStripped, 4) == 0
00362                              || memcmp("here", pStripped, 4) == 0)))
00363     {
00364         return NULL;
00365     }
00366 
00367     *pnName = nBuf;
00368     *pbValid = true;
00369     return Buf;
00370 }
00371 
00372 // The following function validates exit names.
00373 //
00374 char *MakeCanonicalExitName(const char *pName, int *pnName, bool *pbValid)
00375 {
00376     static char Buf[MBUF_SIZE];
00377     static char Out[MBUF_SIZE];
00378 
00379     *pnName = 0;
00380     *pbValid = false;
00381 
00382     if (!pName)
00383     {
00384         return NULL;
00385     }
00386 
00387     // Build the non-ANSI version so that we can parse for semicolons
00388     // safely.
00389     //
00390     char *pStripped = strip_ansi(pName);
00391     char *pBuf = Buf;
00392     safe_mb_str(pStripped, Buf, &pBuf);
00393     *pBuf = '\0';
00394 
00395     size_t nBuf = pBuf - Buf;
00396     pBuf = Buf;
00397 
00398     bool bHaveDisplay = false;
00399 
00400     char *pOut = Out;
00401 
00402     for (; nBuf;)
00403     {
00404         // Build (q,n) as the next segment.  Leave the the remaining segments as
00405         // (pBuf,nBuf).
00406         //
00407         char *q = strchr(pBuf, ';');
00408         size_t n;
00409         if (q)
00410         {
00411             *q = '\0';
00412             n = q - pBuf;
00413             q = pBuf;
00414             pBuf += n + 1;
00415             nBuf -= n + 1;
00416         }
00417         else
00418         {
00419             n = nBuf;
00420             q = pBuf;
00421             pBuf += nBuf;
00422             nBuf = 0;
00423         }
00424 
00425         if (bHaveDisplay)
00426         {
00427             // We already have the displayable name. We don't allow ANSI in
00428             // any segment but the first, so we can pull them directly from
00429             // the stripped buffer.
00430             //
00431             int  nN;
00432             bool bN;
00433             char *pN = MakeCanonicalObjectName(q, &nN, &bN);
00434             if (  bN
00435                && nN < MBUF_SIZE - (pOut - Out) - 1)
00436             {
00437                 safe_mb_chr(';', Out, &pOut);
00438                 safe_mb_str(pN, Out, &pOut);
00439             }
00440         }
00441         else
00442         {
00443             // We don't have the displayable name, yet. We know where the next
00444             // semicolon occurs, so we limit the visible width of the
00445             // truncation to that.  We should be picking up all the visible
00446             // characters leading up to the semicolon, but not including the
00447             // semi-colon.
00448             //
00449             int vw;
00450             ANSI_TruncateToField(pName, sizeof(Out), Out, n, &vw,
00451                 ANSI_ENDGOAL_NORMAL);
00452 
00453             // vw should always be equal to n, but we'll just make sure.
00454             //
00455             if ((size_t)vw == n)
00456             {
00457                 int  nN;
00458                 bool bN;
00459                 char *pN = MakeCanonicalObjectName(Out, &nN, &bN);
00460                 if (  bN
00461                    && nN <= MBUF_SIZE - 1)
00462                 {
00463                     safe_mb_str(pN, Out, &pOut);
00464                     bHaveDisplay = true;
00465                 }
00466             }
00467         }
00468     }
00469     if (bHaveDisplay)
00470     {
00471         *pnName = pOut - Out;
00472         *pbValid = true;
00473         *pOut = '\0';
00474         return Out;
00475     }
00476     else
00477     {
00478         return NULL;
00479     }
00480 }
00481 
00482 // The following function validates the player name. ANSI is not
00483 // allowed in player names. However, a player name must satisfy
00484 // the requirements of a regular name as well.
00485 //
00486 bool ValidatePlayerName(const char *pName)
00487 {
00488     if (!pName)
00489     {
00490         return false;
00491     }
00492     size_t nName = strlen(pName);
00493 
00494     // Verify that name is not empty, but not too long, either.
00495     //
00496     if (  nName <= 0
00497        || PLAYER_NAME_LIMIT <= nName)
00498     {
00499         return false;
00500     }
00501 
00502     // Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE
00503     // as the first character, or SPACE as the last character
00504     //
00505     if (  strchr("*!#", *pName)
00506        || mux_isspace(pName[0])
00507        || mux_isspace(pName[nName-1]))
00508     {
00509         return false;
00510     }
00511 
00512     if (  mudstate.bStandAlone
00513        || mudconf.name_spaces)
00514     {
00515         mux_PlayerNameSet[(unsigned char)' '] = 1;
00516     }
00517     else
00518     {
00519         mux_PlayerNameSet[(unsigned char)' '] = 0;
00520     }
00521 
00522     // Only printable characters besides ARG_DELIMITER, AND_TOKEN,
00523     // and OR_TOKEN are allowed.
00524     //
00525     for (unsigned int i = 0; i < nName; i++)
00526     {
00527         if (!mux_PlayerNameSet(pName[i]))
00528         {
00529             return false;
00530         }
00531     }
00532 
00533     // Special names are specifically dis-allowed.
00534     //
00535     if (  (nName == 2 && memcmp("me", pName, 2) == 0)
00536        || (nName == 4 && (  memcmp("home", pName, 4) == 0
00537                          || memcmp("here", pName, 4) == 0)))
00538     {
00539         return false;
00540     }
00541     return true;
00542 }
00543 
00544 bool ok_password(const char *password, const char **pmsg)
00545 {
00546     *pmsg = NULL;
00547 
00548     if (*password == '\0')
00549     {
00550         *pmsg = "Null passwords are not allowed.";
00551         return false;
00552     }
00553 
00554     const char *scan;
00555     int num_upper = 0;
00556     int num_special = 0;
00557     int num_lower = 0;
00558 
00559     for (scan = password; *scan; scan++)
00560     {
00561         if (  !mux_isprint(*scan)
00562            || mux_isspace(*scan))
00563         {
00564             *pmsg = "Illegal character in password.";
00565             return false;
00566         }
00567         if (mux_isupper(*scan))
00568         {
00569             num_upper++;
00570         }
00571         else if (mux_islower(*scan))
00572         {
00573             num_lower++;
00574         }
00575         else if (  *scan != '\''
00576                 && *scan != '-')
00577         {
00578             num_special++;
00579         }
00580     }
00581 
00582     if (  !mudstate.bStandAlone
00583        && mudconf.safer_passwords)
00584     {
00585         if (num_upper < 1)
00586         {
00587             *pmsg = "The password must contain at least one capital letter.";
00588             return false;
00589         }
00590         if (num_lower < 1)
00591         {
00592             *pmsg = "The password must contain at least one lowercase letter.";
00593             return false;
00594         }
00595         if (num_special < 1)
00596         {
00597             *pmsg = "The password must contain at least one number or a symbol other than the apostrophe or dash.";
00598             return false;
00599         }
00600     }
00601     return true;
00602 }
00603 
00604 /* ---------------------------------------------------------------------------
00605  * handle_ears: Generate the 'grows ears' and 'loses ears' messages.
00606  */
00607 
00608 void handle_ears(dbref thing, bool could_hear, bool can_hear)
00609 {
00610     char *buff, *bp;
00611     int gender;
00612     static const char *poss[5] =
00613     {"", "its", "her", "his", "their"};
00614 
00615     if (could_hear != can_hear)
00616     {
00617         buff = alloc_lbuf("handle_ears");
00618         strcpy(buff, Name(thing));
00619         if (isExit(thing))
00620         {
00621             for (bp = buff; *bp && *bp != ';'; bp++)
00622             {
00623                 ; // Nothing.
00624             }
00625             *bp = '\0';
00626         }
00627         gender = get_gender(thing);
00628 
00629         if (can_hear)
00630         {
00631             notify_check(thing, thing,
00632                          tprintf("%s grow%s ears and can now hear.",
00633                                  buff, (gender == 4) ? "" : "s"),
00634                          (MSG_ME | MSG_NBR | MSG_LOC | MSG_INV));
00635         }
00636         else
00637         {
00638             notify_check(thing, thing,
00639                          tprintf("%s lose%s %s ears and become%s deaf.",
00640                                  buff, (gender == 4) ? "" : "s",
00641                                  poss[gender], (gender == 4) ? "" : "s"),
00642                          (MSG_ME | MSG_NBR | MSG_LOC | MSG_INV));
00643         }
00644         free_lbuf(buff);
00645     }
00646 }
00647 
00648 // For lack of better place the @switch code is here.
00649 //
00650 void do_switch
00651 (
00652     dbref player,
00653     dbref caller,
00654     dbref enactor,
00655     int   key,
00656     char *expr,
00657     char *args[],
00658     int   nargs,
00659     char *cargs[],
00660     int   ncargs
00661 )
00662 {
00663     if (  !expr
00664        || nargs <= 0)
00665     {
00666         return;
00667     }
00668 
00669     if (key == SWITCH_DEFAULT)
00670     {
00671         if (mudconf.switch_df_all)
00672         {
00673             key = SWITCH_ANY;
00674         }
00675         else
00676         {
00677             key = SWITCH_ONE;
00678         }
00679     }
00680 
00681     // Now try a wild card match of buff with stuff in coms.
00682     //
00683     bool any = false;
00684     int a;
00685     char *buff, *bp, *str;
00686     buff = bp = alloc_lbuf("do_switch");
00687     CLinearTimeAbsolute lta;
00688     for (  a = 0;
00689               a < nargs - 1
00690            && args[a]
00691            && args[a + 1];
00692            a += 2)
00693     {
00694         bp = buff;
00695         str = args[a];
00696         mux_exec(buff, &bp, player, caller, enactor, EV_FCHECK | EV_EVAL | EV_TOP,
00697             &str, cargs, ncargs);
00698         *bp = '\0';
00699         if (wild_match(buff, expr))
00700         {
00701             char *tbuf = replace_tokens(args[a+1], NULL, NULL, expr);
00702             wait_que(player, caller, enactor, false, lta, NOTHING, 0,
00703                 tbuf, cargs, ncargs, mudstate.global_regs);
00704             free_lbuf(tbuf);
00705             if (key == SWITCH_ONE)
00706             {
00707                 free_lbuf(buff);
00708                 return;
00709             }
00710             any = true;
00711         }
00712     }
00713     free_lbuf(buff);
00714     if (  a < nargs
00715        && !any
00716        && args[a])
00717     {
00718         char *tbuf = replace_tokens(args[a], NULL, NULL, expr);
00719         wait_que(player, caller, enactor, false, lta, NOTHING, 0, tbuf,
00720             cargs, ncargs, mudstate.global_regs);
00721         free_lbuf(tbuf);
00722     }
00723 }
00724 
00725 // Also for lack of better place the @ifelse code is here.
00726 // Idea for @ifelse from ChaoticMUX.
00727 //
00728 void do_if
00729 (
00730     dbref player,
00731     dbref caller,
00732     dbref enactor,
00733     int   key,
00734     char *expr,
00735     char *args[],
00736     int   nargs,
00737     char *cargs[],
00738     int   ncargs
00739 )
00740 {
00741     UNUSED_PARAMETER(key);
00742 
00743     if (  !expr
00744        || nargs <= 0)
00745     {
00746         return;
00747     }
00748 
00749     char *buff, *bp;
00750     CLinearTimeAbsolute lta;
00751     buff = bp = alloc_lbuf("do_if");
00752 
00753     mux_exec(buff, &bp, player, caller, enactor, EV_FCHECK | EV_EVAL | EV_TOP,
00754         &expr, cargs, ncargs);
00755     *bp = '\0';
00756 
00757     int a = !xlate(buff);
00758     free_lbuf(buff);
00759 
00760     if (a < nargs)
00761     {
00762         wait_que(player, caller, enactor, false, lta, NOTHING, 0, args[a],
00763             cargs, ncargs, mudstate.global_regs);
00764     }
00765 }
00766 
00767 void do_addcommand
00768 (
00769     dbref player,
00770     dbref caller,
00771     dbref enactor,
00772     int   key,
00773     int   nargs,
00774     char *name,
00775     char *command
00776 )
00777 {
00778     UNUSED_PARAMETER(caller);
00779     UNUSED_PARAMETER(enactor);
00780     UNUSED_PARAMETER(key);
00781 
00782     // Validate command name.
00783     //
00784     char *pName = NULL;
00785     if (1 <= nargs)
00786     {
00787         char *pStripped = strip_ansi(name);
00788         pName = RemoveSetOfCharacters(pStripped, "\r\n\t ");
00789         mux_strlwr(pName);
00790     }
00791     if (  !pName
00792        || pName[0] == '\0'
00793        || (  pName[0] == '_'
00794           && pName[1] == '_'))
00795     {
00796         notify(player, "That is not a valid command name.");
00797         return;
00798     }
00799 
00800     // Validate object/attribute.
00801     //
00802     dbref thing;
00803     ATTR *pattr;
00804     if (  !parse_attrib(player, command, &thing, &pattr)
00805        || !pattr)
00806     {
00807         notify(player, "No such attribute.");
00808         return;
00809     }
00810     if (!See_attr(player, thing, pattr))
00811     {
00812         notify(player, NOPERM_MESSAGE);
00813         return;
00814     }
00815 
00816     CMDENT *old = (CMDENT *)hashfindLEN(pName, strlen(pName),
00817         &mudstate.command_htab);
00818 
00819     CMDENT *cmd;
00820     ADDENT *add, *nextp;
00821 
00822     if (  old
00823        && (old->callseq & CS_ADDED))
00824     {
00825         // Don't allow the same (thing,atr) in the list.
00826         //
00827         for (nextp = old->addent; nextp != NULL; nextp = nextp->next)
00828         {
00829             if (  nextp->thing == thing
00830                && nextp->atr == pattr->number)
00831             {
00832                 notify(player, tprintf("%s already added.", pName));
00833                 return;
00834             }
00835         }
00836 
00837         // Otherwise, add another (thing,atr) to the list.
00838         //
00839         add = (ADDENT *)MEMALLOC(sizeof(ADDENT));
00840         ISOUTOFMEMORY(add);
00841         add->thing = thing;
00842         add->atr = pattr->number;
00843         add->name = StringClone(pName);
00844         add->next = old->addent;
00845         old->addent = add;
00846     }
00847     else
00848     {
00849         if (old)
00850         {
00851             // Delete the old built-in (which will later be added back as
00852             // __name).
00853             //
00854             hashdeleteLEN(pName, strlen(pName), &mudstate.command_htab);
00855         }
00856 
00857         cmd = (CMDENT *)MEMALLOC(sizeof(CMDENT));
00858         ISOUTOFMEMORY(cmd);
00859         cmd->cmdname = StringClone(pName);
00860         cmd->switches = NULL;
00861         cmd->perms = 0;
00862         cmd->extra = 0;
00863         if (  old
00864            && (old->callseq & CS_LEADIN))
00865         {
00866             cmd->callseq = CS_ADDED|CS_ONE_ARG|CS_LEADIN;
00867         }
00868         else
00869         {
00870             cmd->callseq = CS_ADDED|CS_ONE_ARG;
00871         }
00872         cmd->hookmask = 0;
00873         add = (ADDENT *)MEMALLOC(sizeof(ADDENT));
00874         ISOUTOFMEMORY(add);
00875         add->thing = thing;
00876         add->atr = pattr->number;
00877         add->name = StringClone(pName);
00878         add->next = NULL;
00879         cmd->addent = add;
00880 
00881         hashaddLEN(pName, strlen(pName), cmd, &mudstate.command_htab);
00882 
00883         if (  old
00884            && strcmp(pName, old->cmdname) == 0)
00885         {
00886             // We are @addcommand'ing over a built-in command by its
00887             // unaliased name, therefore, we want to re-target all the
00888             // aliases.
00889             //
00890             char *p = tprintf("__%s", pName);
00891             hashdeleteLEN(p, strlen(p), &mudstate.command_htab);
00892             hashreplall(old, cmd, &mudstate.command_htab);
00893             hashaddLEN(p, strlen(p), old, &mudstate.command_htab);
00894         }
00895     }
00896 
00897     // We reset the one letter commands here so you can overload them.
00898     //
00899     set_prefix_cmds();
00900     notify(player, tprintf("Command %s added.", pName));
00901 }
00902 
00903 void do_listcommands(dbref player, dbref caller, dbref enactor, int key,
00904                      char *name)
00905 {
00906     UNUSED_PARAMETER(caller);
00907     UNUSED_PARAMETER(enactor);
00908     UNUSED_PARAMETER(key);
00909 
00910     CMDENT *old;
00911     ADDENT *nextp;
00912     bool didit = false;
00913 
00914     // Let's make this case insensitive...
00915     //
00916     mux_strlwr(name);
00917 
00918     if (*name)
00919     {
00920         old = (CMDENT *)hashfindLEN(name, strlen(name), &mudstate.command_htab);
00921 
00922         if (  old
00923            && (old->callseq & CS_ADDED))
00924         {
00925             // If it's already found in the hash table, and it's being added
00926             // using the same object and attribute...
00927             //
00928             for (nextp = old->addent; nextp != NULL; nextp = nextp->next)
00929             {
00930                 ATTR *ap = (ATTR *)atr_num(nextp->atr);
00931                 const char *pName = "(WARNING: Bad Attribute Number)";
00932                 if (ap)
00933                 {
00934                     pName = ap->name;
00935                 }
00936                 notify(player, tprintf("%s: #%d/%s", nextp->name, nextp->thing, pName));
00937             }
00938         }
00939         else
00940         {
00941             notify(player, tprintf("%s not found in command table.",name));
00942         }
00943         return;
00944     }
00945     else
00946     {
00947         char *pKeyName;
00948         int  nKeyName;
00949         for (old = (CMDENT *)hash_firstkey(&mudstate.command_htab, &nKeyName, &pKeyName);
00950              old != NULL;
00951              old = (CMDENT *)hash_nextkey(&mudstate.command_htab, &nKeyName, &pKeyName))
00952         {
00953             if (old->callseq & CS_ADDED)
00954             {
00955                 pKeyName[nKeyName] = '\0';
00956                 for (nextp = old->addent; nextp != NULL; nextp = nextp->next)
00957                 {
00958                     if (strcmp(pKeyName, nextp->name) != 0)
00959                     {
00960                         continue;
00961                     }
00962                     ATTR *ap = (ATTR *)atr_num(nextp->atr);
00963                     const char *pName = "(WARNING: Bad Attribute Number)";
00964                     if (ap)
00965                     {
00966                         pName = ap->name;
00967                     }
00968                     notify(player, tprintf("%s: #%d/%s", nextp->name,
00969                         nextp->thing, pName));
00970                     didit = true;
00971                 }
00972             }
00973         }
00974     }
00975     if (!didit)
00976     {
00977         notify(player, "No added commands found in command table.");
00978     }
00979 }
00980 
00981 void do_delcommand
00982 (
00983     dbref player,
00984     dbref caller,
00985     dbref enactor,
00986     int   key,
00987     int   nargs,
00988     char *name,
00989     char *command
00990 )
00991 {
00992     UNUSED_PARAMETER(caller);
00993     UNUSED_PARAMETER(enactor);
00994     UNUSED_PARAMETER(key);
00995     UNUSED_PARAMETER(nargs);
00996 
00997     if (!*name)
00998     {
00999         notify(player, "Sorry.");
01000         return;
01001     }
01002 
01003     dbref thing = NOTHING;
01004     int atr = NOTHING;
01005     ATTR *pattr;
01006     if (*command)
01007     {
01008         if (  !parse_attrib(player, command, &thing, &pattr)
01009            || !pattr)
01010         {
01011             notify(player, "No such attribute.");
01012             return;
01013         }
01014         if (!See_attr(player, thing, pattr))
01015         {
01016             notify(player, NOPERM_MESSAGE);
01017             return;
01018         }
01019         atr = pattr->number;
01020     }
01021 
01022     // Let's make this case insensitive...
01023     //
01024     mux_strlwr(name);
01025 
01026     CMDENT *old, *cmd;
01027     ADDENT *prev = NULL, *nextp;
01028     size_t nName = strlen(name);
01029     old = (CMDENT *)hashfindLEN(name, nName, &mudstate.command_htab);
01030 
01031     if (  old
01032        && (old->callseq & CS_ADDED))
01033     {
01034         char *p__Name = tprintf("__%s", name);
01035         size_t n__Name = strlen(p__Name);
01036 
01037         if (command[0] == '\0')
01038         {
01039             // Delete all @addcommand'ed associations with the given name.
01040             //
01041             for (prev = old->addent; prev != NULL; prev = nextp)
01042             {
01043                 nextp = prev->next;
01044                 MEMFREE(prev->name);
01045                 prev->name = NULL;
01046                 MEMFREE(prev);
01047                 prev = NULL;
01048             }
01049             hashdeleteLEN(name, nName, &mudstate.command_htab);
01050             cmd = (CMDENT *)hashfindLEN(p__Name, n__Name, &mudstate.command_htab);
01051             if (cmd)
01052             {
01053                 hashaddLEN(cmd->cmdname, strlen(cmd->cmdname), cmd,
01054                     &mudstate.command_htab);
01055                 if (strcmp(name, cmd->cmdname) != 0)
01056                 {
01057                     hashaddLEN(name, nName, cmd, &mudstate.command_htab);
01058                 }
01059 
01060                 hashdeleteLEN(p__Name, n__Name, &mudstate.command_htab);
01061                 hashaddLEN(p__Name, n__Name, cmd, &mudstate.command_htab);
01062                 hashreplall(old, cmd, &mudstate.command_htab);
01063             }
01064             else
01065             {
01066                 // TODO: Delete everything related to 'old'.
01067                 //
01068             }
01069             MEMFREE(old->cmdname);
01070             old->cmdname = NULL;
01071             MEMFREE(old);
01072             old = NULL;
01073             set_prefix_cmds();
01074             notify(player, "Done.");
01075         }
01076         else
01077         {
01078             // Remove only the (name,thing,atr) association.
01079             //
01080             for (nextp = old->addent; nextp != NULL; nextp = nextp->next)
01081             {
01082                 if (  nextp->thing == thing
01083                    && nextp->atr == atr)
01084                 {
01085                     MEMFREE(nextp->name);
01086                     nextp->name = NULL;
01087                     if (!prev)
01088                     {
01089                         if (!nextp->next)
01090                         {
01091                             hashdeleteLEN(name, nName, &mudstate.command_htab);
01092                             cmd = (CMDENT *)hashfindLEN(p__Name, n__Name,
01093                                 &mudstate.command_htab);
01094                             if (cmd)
01095                             {
01096                                 hashaddLEN(cmd->cmdname, strlen(cmd->cmdname),
01097                                     cmd, &mudstate.command_htab);
01098                                 if (strcmp(name, cmd->cmdname) != 0)
01099                                 {
01100                                     hashaddLEN(name, nName, cmd,
01101                                         &mudstate.command_htab);
01102                                 }
01103 
01104                                 hashdeleteLEN(p__Name, n__Name,
01105                                     &mudstate.command_htab);
01106                                 hashaddLEN(p__Name, n__Name, cmd,
01107                                     &mudstate.command_htab);
01108                                 hashreplall(old, cmd,
01109                                     &mudstate.command_htab);
01110                             }
01111                             MEMFREE(old->cmdname);
01112                             old->cmdname = NULL;
01113                             MEMFREE(old);
01114                             old = NULL;
01115                         }
01116                         else
01117                         {
01118                             old->addent = nextp->next;
01119                             MEMFREE(nextp);
01120                             nextp = NULL;
01121                         }
01122                     }
01123                     else
01124                     {
01125                         prev->next = nextp->next;
01126                         MEMFREE(nextp);
01127                         nextp = NULL;
01128                     }
01129                     set_prefix_cmds();
01130                     notify(player, "Done.");
01131                     return;
01132                 }
01133                 prev = nextp;
01134             }
01135             notify(player, "Command not found in command table.");
01136         }
01137     }
01138     else
01139     {
01140         notify(player, "Command not found in command table.");
01141     }
01142 }
01143 
01144 /*
01145  * @prog 'glues' a user's input to a command. Once executed, the first string
01146  * input from any of the doers's logged in descriptors, will go into
01147  * A_PROGMSG, which can be substituted in <command> with %0. Commands already
01148  * queued by the doer will be processed normally.
01149  */
01150 
01151 void handle_prog(DESC *d, char *message)
01152 {
01153     // Allow the player to pipe a command while in interactive mode.
01154     //
01155     if (*message == '|')
01156     {
01157         do_command(d, message + 1);
01158 
01159         if (d->program_data != NULL)
01160         {
01161             queue_string(d, tprintf("%s>%s ", ANSI_HILITE, ANSI_NORMAL));
01162 
01163             if (OPTION_YES == UsState(d, TELNET_EOR))
01164             {
01165                 // Use telnet protocol's EOR command to show prompt.
01166                 //
01167                 const char aEOR[2] = { NVT_IAC, NVT_EOR };
01168                 queue_write_LEN(d, aEOR, sizeof(aEOR));
01169             }
01170             else if (OPTION_YES != UsState(d, TELNET_SGA))
01171             {
01172                 // Use telnet protocol's GOAHEAD command to show prompt.
01173                 //
01174                 const char aGoAhead[2] = { NVT_IAC, NVT_GA };
01175                 queue_write_LEN(d, aGoAhead, sizeof(aGoAhead));
01176             }
01177         }
01178         return;
01179     }
01180     dbref aowner;
01181     int aflags, i;
01182     char *cmd = atr_get(d->player, A_PROGCMD, &aowner, &aflags);
01183     CLinearTimeAbsolute lta;
01184     wait_que(d->program_data->wait_enactor, d->player, d->player, false, lta,
01185         NOTHING, 0, cmd, (char **)&message, 1,
01186         (char **)d->program_data->wait_regs);
01187 
01188     // First, set 'all' to a descriptor we find for this player.
01189     //
01190     DESC *all = (DESC *)hashfindLEN(&(d->player), sizeof(d->player), &mudstate.desc_htab) ;
01191 
01192     if (all && all->program_data)
01193     {
01194         for (i = 0; i < MAX_GLOBAL_REGS; i++)
01195         {
01196             if (all->program_data->wait_regs[i])
01197             {
01198                 free_lbuf(all->program_data->wait_regs[i]);
01199                 all->program_data->wait_regs[i] = NULL;
01200             }
01201         }
01202 
01203         MEMFREE(all->program_data);
01204         all->program_data = NULL;
01205 
01206         // Set info for all player descriptors to NULL
01207         //
01208         DESC_ITER_PLAYER(d->player, all)
01209             all->program_data = NULL;
01210     }
01211     atr_clr(d->player, A_PROGCMD);
01212     free_l