mux/src/functions.cpp

Go to the documentation of this file.
00001 // functions.cpp -- MUX function handlers.
00002 //
00003 // $Id: functions.cpp,v 1.173 2007/04/14 04:57:05 sdennis Exp $
00004 //
00005 // MUX 2.4
00006 // Copyright (C) 1998 through 2005 Solid Vertical Domains, Ltd. All
00007 // rights not explicitly given are reserved.
00008 //
00009 #include "copyright.h"
00010 #include "autoconf.h"
00011 #include "config.h"
00012 #include "externs.h"
00013 
00014 #include "ansi.h"
00015 #include "attrs.h"
00016 #include "command.h"
00017 #include "functions.h"
00018 #include "funmath.h"
00019 #include "interface.h"
00020 #include "misc.h"
00021 #include "pcre.h"
00022 #ifdef REALITY_LVLS
00023 #include "levels.h"
00024 #endif /* REALITY_LVLS */
00025 
00026 UFUN *ufun_head;
00027 
00028 SEP sepSpace = { 1, " " };
00029 
00030 // Trim off leading and trailing spaces if the separator char is a
00031 // space -- known length version.
00032 //
00033 char *trim_space_sep_LEN(char *str, int nStr, SEP *sep, int *nTrim)
00034 {
00035     if (  sep->n != 1
00036        || sep->str[0] != ' ')
00037     {
00038         *nTrim = nStr;
00039         return str;
00040     }
00041 
00042     // Advance over leading spaces.
00043     //
00044     char *pBegin = str;
00045     char *pEnd = str + nStr - 1;
00046     while (*pBegin == ' ')
00047     {
00048         pBegin++;
00049     }
00050 
00051     // Advance over trailing spaces.
00052     //
00053     for (; pEnd > pBegin && *pEnd == ' '; pEnd--)
00054     {
00055         // Nothing.
00056     }
00057     pEnd++;
00058 
00059     *pEnd = '\0';
00060     *nTrim = pEnd - pBegin;
00061     return pBegin;
00062 }
00063 
00064 
00065 // Trim off leading and trailing spaces if the separator char is a space.
00066 //
00067 char *trim_space_sep(char *str, SEP *sep)
00068 {
00069     if (  sep->n != 1
00070        || sep->str[0] != ' ')
00071     {
00072         return str;
00073     }
00074     while (*str == ' ')
00075     {
00076         str++;
00077     }
00078     char *p;
00079     for (p = str; *p; p++)
00080     {
00081         // Nothing.
00082     }
00083     for (p--; p > str && *p == ' '; p--)
00084     {
00085         // Nothing.
00086     }
00087     p++;
00088     *p = '\0';
00089     return str;
00090 }
00091 
00092 // next_token: Point at start of next token in string -- known length
00093 // version.
00094 //
00095 static char *next_token_LEN(char *str, int *nStr, SEP *psep)
00096 {
00097     char *pBegin = str;
00098     if (psep->n == 1)
00099     {
00100         while (  *pBegin != '\0'
00101               && *pBegin != psep->str[0])
00102         {
00103             pBegin++;
00104         }
00105         if (!*pBegin)
00106         {
00107             *nStr = 0;
00108             return NULL;
00109         }
00110         pBegin++;
00111         if (psep->str[0] == ' ')
00112         {
00113             while (*pBegin == ' ')
00114             {
00115                 pBegin++;
00116             }
00117         }
00118     }
00119     else
00120     {
00121         char *p = strstr(pBegin, psep->str);
00122         if (p)
00123         {
00124             pBegin = p + psep->n;
00125         }
00126         else
00127         {
00128             *nStr = 0;
00129             return NULL;
00130         }
00131     }
00132     *nStr -= pBegin - str;
00133     return pBegin;
00134 }
00135 
00136 // next_token: Point at start of next token in string
00137 //
00138 char *next_token(char *str, SEP *psep)
00139 {
00140     if (psep->n == 1)
00141     {
00142         while (  *str != '\0'
00143               && *str != psep->str[0])
00144         {
00145             str++;
00146         }
00147         if (!*str)
00148         {
00149             return NULL;
00150         }
00151         str++;
00152         if (psep->str[0] == ' ')
00153         {
00154             while (*str == ' ')
00155             {
00156                 str++;
00157             }
00158         }
00159     }
00160     else
00161     {
00162         char *p = strstr(str, psep->str);
00163         if (p)
00164         {
00165             str = p + psep->n;
00166         }
00167         else
00168         {
00169             return NULL;
00170         }
00171     }
00172     return str;
00173 }
00174 
00175 // split_token: Get next token from string as null-term string. String is
00176 // destructively modified -- known length version.
00177 //
00178 static char *split_token_LEN(char **sp, int *nStr, SEP *psep, int *nToken)
00179 {
00180     char *str = *sp;
00181     char *save = str;
00182     if (!str)
00183     {
00184         *nStr = 0;
00185         *sp = NULL;
00186         *nToken = 0;
00187         return NULL;
00188     }
00189 
00190     if (psep->n == 1)
00191     {
00192         // Advance over token
00193         //
00194         while (  *str
00195               && *str != psep->str[0])
00196         {
00197             str++;
00198         }
00199         *nToken = str - save;
00200 
00201         if (*str)
00202         {
00203             *str++ = '\0';
00204             if (psep->str[0] == ' ')
00205             {
00206                 while (*str == ' ')
00207                 {
00208                     str++;
00209                 }
00210             }
00211             *nStr -= str - save;
00212         }
00213         else
00214         {
00215             *nStr = 0;
00216             str = NULL;
00217         }
00218     }
00219     else
00220     {
00221         char *p = strstr(str, psep->str);
00222         if (p)
00223         {
00224             *p = '\0';
00225             str = p + psep->n;
00226         }
00227         else
00228         {
00229             str = NULL;
00230         }
00231     }
00232     *sp = str;
00233     return save;
00234 }
00235 
00236 // split_token: Get next token from string as null-term string.  String is
00237 // destructively modified.
00238 //
00239 char *split_token(char **sp, SEP *psep)
00240 {
00241     char *str = *sp;
00242     char *save = str;
00243 
00244     if (!str)
00245     {
00246         *sp = NULL;
00247         return NULL;
00248     }
00249     if (psep->n == 1)
00250     {
00251         while (  *str
00252               && *str != psep->str[0])
00253         {
00254             str++;
00255         }
00256         if (*str)
00257         {
00258             *str++ = '\0';
00259             if (psep->str[0] == ' ')
00260             {
00261                 while (*str == ' ')
00262                 {
00263                     str++;
00264                 }
00265             }
00266         }
00267         else
00268         {
00269             str = NULL;
00270         }
00271     }
00272     else
00273     {
00274         char *p = strstr(str, psep->str);
00275         if (p)
00276         {
00277             *p = '\0';
00278             str = p + psep->n;
00279         }
00280         else
00281         {
00282             str = NULL;
00283         }
00284     }
00285     *sp = str;
00286     return save;
00287 }
00288 
00289 /* ---------------------------------------------------------------------------
00290  * List management utilities.
00291  */
00292 
00293 #define ASCII_LIST      1
00294 #define NUMERIC_LIST    2
00295 #define DBREF_LIST      4
00296 #define FLOAT_LIST      8
00297 #define CI_ASCII_LIST   16
00298 #define ALL_LIST        (ASCII_LIST|NUMERIC_LIST|DBREF_LIST|FLOAT_LIST)
00299 
00300 static int autodetect_list(char *ptrs[], int nitems)
00301 {
00302     int could_be = ALL_LIST;
00303     for (int i = 0; i < nitems; i++)
00304     {
00305         char *p = ptrs[i];
00306         if (p[0] != NUMBER_TOKEN)
00307         {
00308             could_be &= ~DBREF_LIST;
00309         }
00310         if (  (could_be & DBREF_LIST)
00311            && !is_integer(p+1, NULL))
00312         {
00313             could_be &= ~(DBREF_LIST|NUMERIC_LIST|FLOAT_LIST);
00314         }
00315         if (  (could_be & FLOAT_LIST)
00316            && !is_real(p))
00317         {
00318             could_be &= ~(NUMERIC_LIST|FLOAT_LIST);
00319         }
00320         if (  (could_be & NUMERIC_LIST)
00321            && !is_integer(p, NULL))
00322         {
00323             could_be &= ~NUMERIC_LIST;
00324         }
00325 
00326         if (could_be == ASCII_LIST)
00327         {
00328             return ASCII_LIST;
00329         }
00330     }
00331     if (could_be & NUMERIC_LIST)
00332     {
00333         return NUMERIC_LIST;
00334     }
00335     else if (could_be & FLOAT_LIST)
00336     {
00337         return FLOAT_LIST;
00338     }
00339     else if (could_be & DBREF_LIST)
00340     {
00341         return DBREF_LIST;
00342     }
00343     return ASCII_LIST;
00344 }
00345 
00346 static int get_list_type
00347 (
00348     char *fargs[],
00349     int nfargs,
00350     int type_pos,
00351     char *ptrs[],
00352     int nitems
00353 )
00354 {
00355     if (nfargs >= type_pos)
00356     {
00357         switch (mux_tolower(*fargs[type_pos - 1]))
00358         {
00359         case 'd':
00360             return DBREF_LIST;
00361         case 'n':
00362             return NUMERIC_LIST;
00363         case 'f':
00364             return FLOAT_LIST;
00365         case 'i':
00366             return CI_ASCII_LIST;
00367         case '\0':
00368             return autodetect_list(ptrs, nitems);
00369         default:
00370             return ASCII_LIST;
00371         }
00372     }
00373     return autodetect_list(ptrs, nitems);
00374 }
00375 
00376 int list2arr(char *arr[], int maxlen, char *list, SEP *psep)
00377 {
00378     list = trim_space_sep(list, psep);
00379     if (list[0] == '\0')
00380     {
00381         return 0;
00382     }
00383     char *p = split_token(&list, psep);
00384     int i;
00385     for (i = 0; p && i < maxlen; i++, p = split_token(&list, psep))
00386     {
00387         arr[i] = p;
00388     }
00389     return i;
00390 }
00391 
00392 void arr2list(char *arr[], int alen, char *list, char **bufc, SEP *psep)
00393 {
00394     int i;
00395     for (i = 0; i < alen-1; i++)
00396     {
00397         safe_str(arr[i], list, bufc);
00398         print_sep(psep, list, bufc);
00399     }
00400     if (alen)
00401     {
00402         safe_str(arr[i], list, bufc);
00403     }
00404 }
00405 
00406 static int dbnum(char *dbr)
00407 {
00408     if (dbr[0] != '#' || dbr[1] == '\0')
00409     {
00410         return 0;
00411     }
00412     else
00413     {
00414         return mux_atol(dbr + 1);
00415     }
00416 }
00417 
00418 /* ---------------------------------------------------------------------------
00419  * nearby_or_control: Check if player is near or controls thing
00420  */
00421 
00422 static bool nearby_or_control(dbref player, dbref thing)
00423 {
00424     if (!Good_obj(player) || !Good_obj(thing))
00425     {
00426         return false;
00427     }
00428     if (Controls(player, thing))
00429     {
00430         return true;
00431     }
00432     if (!nearby(player, thing))
00433     {
00434         return false;
00435     }
00436     return true;
00437 }
00438 
00439 /* ---------------------------------------------------------------------------
00440  * delim_check: obtain delimiter
00441  */
00442 bool delim_check
00443 (
00444     char *buff, char **bufc,
00445     dbref executor, dbref caller, dbref enactor,
00446     char *fargs[], int nfargs,
00447     char *cargs[], int ncargs,
00448     int sep_arg, SEP *sep, int dflags
00449 )
00450 {
00451     bool bSuccess = true;
00452     if (sep_arg <= nfargs)
00453     {
00454         // First, we decide whether to evalute fargs[sep_arg-1] or not.
00455         //
00456         char *tstr = fargs[sep_arg-1];
00457         int tlen = strlen(tstr);
00458 
00459         if (tlen <= 1)
00460         {
00461             dflags &= ~DELIM_EVAL;
00462         }
00463         if (dflags & DELIM_EVAL)
00464         {
00465             char *str = tstr;
00466             char *bp = tstr = alloc_lbuf("delim_check");
00467             mux_exec(tstr, &bp, executor, caller, enactor,
00468                      EV_EVAL | EV_FCHECK, &str, cargs, ncargs);
00469             *bp = '\0';
00470             tlen = bp - tstr;
00471         }
00472 
00473         // Regardless of evaulation or no, tstr contains what we need to
00474         // look at, and tlen is the length of this string.
00475         //
00476         if (tlen == 1)
00477         {
00478             sep->n      = 1;
00479             memcpy(sep->str, tstr, tlen+1);
00480         }
00481         else if (tlen == 0)
00482         {
00483             sep->n      = 1;
00484             memcpy(sep->str, " ", 2);
00485         }
00486         else if (  tlen == 2
00487                 && (dflags & DELIM_NULL)
00488                 && memcmp(tstr, NULL_DELIM_VAR, 2) == 0)
00489         {
00490             sep->n      = 0;
00491             sep->str[0] = '\0';
00492         }
00493         else if (  tlen == 2
00494                 && (dflags & DELIM_EVAL)
00495                 && memcmp(tstr, "\r\n", 2) == 0)
00496         {
00497             sep->n      = 2;
00498             memcpy(sep->str, "\r\n", 3);
00499         }
00500         else if (dflags & DELIM_STRING)
00501         {
00502             if (tlen <= MAX_SEP_LEN)
00503             {
00504                 sep->n = tlen;
00505                 memcpy(sep->str, tstr, tlen);
00506                 sep->str[sep->n] = '\0';
00507             }
00508             else
00509             {
00510                 safe_str("#-1 SEPARATOR IS TOO LARGE", buff, bufc);
00511                 bSuccess = false;
00512             }
00513         }
00514         else
00515         {
00516             safe_str("#-1 SEPARATOR MUST BE ONE CHARACTER", buff, bufc);
00517             bSuccess = false;
00518         }
00519 
00520         // Clean up the evaluation buffer.
00521         //
00522         if (dflags & DELIM_EVAL)
00523         {
00524             free_lbuf(tstr);
00525         }
00526     }
00527     else if (!(dflags & DELIM_INIT))
00528     {
00529         sep->n      = 1;
00530         memcpy(sep->str, " ", 2);
00531     }
00532     return bSuccess;
00533 }
00534 
00535 /* ---------------------------------------------------------------------------
00536  * fun_words: Returns number of words in a string.
00537  * Added 1/28/91 Philip D. Wasson
00538  */
00539 
00540 int countwords(char *str, SEP *psep)
00541 {
00542     int n;
00543 
00544     str = trim_space_sep(str, psep);
00545     if (!*str)
00546     {
00547         return 0;
00548     }
00549     for (n = 0; str; str = next_token(str, psep), n++)
00550     {
00551         ; // Nothing.
00552     }
00553     return n;
00554 }
00555 
00556 static FUNCTION(fun_words)
00557 {
00558     if (nfargs == 0)
00559     {
00560         safe_chr('0', buff, bufc);
00561         return;
00562     }
00563 
00564     SEP sep;
00565     if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
00566     {
00567         return;
00568     }
00569 
00570     safe_ltoa(countwords(strip_ansi(fargs[0]), &sep), buff, bufc);
00571 }
00572 
00573 /* ---------------------------------------------------------------------------
00574  * fun_flags: Returns the flags on an object or an object's attribute.
00575  * Because @switch is case-insensitive, not quite as useful as it could be.
00576  */
00577 
00578 static FUNCTION(fun_flags)
00579 {
00580     UNUSED_PARAMETER(caller);
00581     UNUSED_PARAMETER(nfargs);
00582     UNUSED_PARAMETER(cargs);
00583     UNUSED_PARAMETER(ncargs);
00584 
00585     dbref it;
00586     ATTR  *pattr;
00587     if (parse_attrib(executor, fargs[0], &it, &pattr))
00588     {
00589         if (  pattr
00590            && See_attr(executor, it, pattr))
00591         {
00592             dbref aowner;
00593             int   aflags;
00594             atr_pget_info(it, pattr->number, &aowner, &aflags);
00595             char xbuf[11];
00596             decode_attr_flags(aflags, xbuf);
00597             safe_str(xbuf, buff, bufc);
00598         }
00599     }
00600     else
00601     {
00602         it = match_thing_quiet(executor, fargs[0]);
00603         if (!Good_obj(it))
00604         {
00605             safe_match_result(it, buff, bufc);
00606             return;
00607         }
00608         if (  mudconf.pub_flags
00609            || Examinable(executor, it)
00610            || it == enactor)
00611         {
00612             char *buff2 = decode_flags(executor, &(db[it].fs));
00613             safe_str(buff2, buff, bufc);
00614             free_sbuf(buff2);
00615         }
00616         else
00617         {
00618             safe_noperm(buff, bufc);
00619         }
00620     }
00621 }
00622 
00623 /* ---------------------------------------------------------------------------
00624  * fun_rand: Return a random number from 0 to arg1-1
00625  */
00626 
00627 static FUNCTION(fun_rand)
00628 {
00629     UNUSED_PARAMETER(executor);
00630     UNUSED_PARAMETER(caller);
00631     UNUSED_PARAMETER(enactor);
00632     UNUSED_PARAMETER(cargs);
00633     UNUSED_PARAMETER(ncargs);
00634 
00635     int nDigits;
00636     switch (nfargs)
00637     {
00638     case 1:
00639         if (is_integer(fargs[0], &nDigits))
00640         {
00641             int num = mux_atol(fargs[0]);
00642             if (num < 1)
00643             {
00644                 safe_chr('0', buff, bufc);
00645             }
00646             else
00647             {
00648                 safe_ltoa(RandomINT32(0, num-1), buff, bufc);
00649             }
00650         }
00651         else
00652         {
00653             safe_str("#-1 ARGUMENT MUST BE INTEGER", buff, bufc);
00654         }
00655         break;
00656 
00657     case 2:
00658         if (  is_integer(fargs[0], &nDigits)
00659            && is_integer(fargs[1], &nDigits))
00660         {
00661             int lower = mux_atol(fargs[0]);
00662             int higher = mux_atol(fargs[1]);
00663             if (  lower <= higher
00664                && (unsigned int)(higher-lower) <= INT32_MAX_VALUE)
00665             {
00666                 safe_ltoa(RandomINT32(lower, higher), buff, bufc);
00667             }
00668             else
00669             {
00670                 safe_range(buff, bufc);
00671             }
00672         }
00673         else
00674         {
00675             safe_str("#-1 ARGUMENT MUST BE INTEGER", buff, bufc);
00676         }
00677         break;
00678     }
00679 }
00680 
00681 // ---------------------------------------------------------------------------
00682 // fun_time:
00683 //
00684 // With no arguments, it returns local time in the 'Ddd Mmm DD HH:MM:SS YYYY'
00685 // format.
00686 //
00687 // If an argument is provided, "utc" gives a UTC time string, and "local"
00688 // gives the local time string.
00689 //
00690 static FUNCTION(fun_time)
00691 {
00692     UNUSED_PARAMETER(executor);
00693     UNUSED_PARAMETER(caller);
00694     UNUSED_PARAMETER(enactor);
00695     UNUSED_PARAMETER(cargs);
00696     UNUSED_PARAMETER(ncargs);
00697 
00698     CLinearTimeAbsolute ltaNow;
00699     if (  nfargs == 0
00700        || mux_stricmp("utc", fargs[0]) != 0)
00701     {
00702         ltaNow.GetLocal();
00703     }
00704     else
00705     {
00706         ltaNow.GetUTC();
00707     }
00708     int nPrecision = 0;
00709     if (nfargs == 2)
00710     {
00711         nPrecision = mux_atol(fargs[1]);
00712     }
00713     char *temp = ltaNow.ReturnDateString(nPrecision);
00714     safe_str(temp, buff, bufc);
00715 }
00716 
00717 // ---------------------------------------------------------------------------
00718 // fun_secs.
00719 //
00720 // With no arguments, it returns seconds since Jan 01 00:00:00 1970 UTC not
00721 // counting leap seconds.
00722 //
00723 // If an argument is provided, "utc" gives UTC seconds, and "local" gives
00724 // an integer which corresponds to a local time string. It is not useful
00725 // as a count, but it can be given to convsecs(secs(),raw) to get the
00726 // corresponding time string.
00727 //
00728 static FUNCTION(fun_secs)
00729 {
00730     UNUSED_PARAMETER(executor);
00731     UNUSED_PARAMETER(caller);
00732     UNUSED_PARAMETER(enactor);
00733     UNUSED_PARAMETER(cargs);
00734     UNUSED_PARAMETER(ncargs);
00735 
00736     CLinearTimeAbsolute ltaNow;
00737     if (  nfargs == 0
00738        || mux_stricmp("local", fargs[0]) != 0)
00739     {
00740         ltaNow.GetUTC();
00741     }
00742     else
00743     {
00744         ltaNow.GetLocal();
00745     }
00746     int nPrecision = 0;
00747     if (nfargs == 2)
00748     {
00749         nPrecision = mux_atol(fargs[1]);
00750     }
00751     safe_str(ltaNow.ReturnSecondsString(nPrecision), buff, bufc);
00752 }
00753 
00754 // ---------------------------------------------------------------------------
00755 // fun_convsecs.
00756 //
00757 // With one arguments, it converts seconds from Jan 01 00:00:00 1970 UTC to a
00758 // local time string in the 'Ddd Mmm DD HH:MM:SS YYYY' format.
00759 //
00760 // If a second argument is given, it is the <zonename>:
00761 //
00762 //   local - indicates that a conversion for timezone/DST of the server should
00763 //           be applied (default if no second argument is given).
00764 //
00765 //   utc   - indicates that no timezone/DST conversions should be applied.
00766 //           This is useful to give a unique one-to-one mapping between an
00767 //           integer and it's corresponding text-string.
00768 //
00769 static FUNCTION(fun_convsecs)
00770 {
00771     UNUSED_PARAMETER(executor);
00772     UNUSED_PARAMETER(caller);
00773     UNUSED_PARAMETER(enactor);
00774     UNUSED_PARAMETER(cargs);
00775     UNUSED_PARAMETER(ncargs);
00776 
00777     CLinearTimeAbsolute lta;
00778     if (lta.SetSecondsString(fargs[0]))
00779     {
00780         if (  nfargs == 1
00781            || mux_stricmp("utc", fargs[1]) != 0)
00782         {
00783             lta.UTC2Local();
00784         }
00785         int nPrecision = 0;
00786         if (nfargs == 3)
00787         {
00788             nPrecision = mux_atol(fargs[2]);
00789         }
00790         char *temp = lta.ReturnDateString(nPrecision);
00791         safe_str(temp, buff, bufc);
00792     }
00793     else
00794     {
00795         safe_str("#-1 INVALID DATE", buff, bufc);
00796     }
00797 }
00798 
00799 // ---------------------------------------------------------------------------
00800 // fun_convtime.
00801 //
00802 // With one argument, it converts a local time string in the format
00803 //'[Ddd] Mmm DD HH:MM:SS YYYY' to a count of seconds from Jan 01 00:00:00 1970
00804 // UTC.
00805 //
00806 // If a second argument is given, it is the <zonename>:
00807 //
00808 //   local - indicates that the given time string is for the local timezone
00809 //           local DST adjustments (default if no second argument is given).
00810 //
00811 //   utc   - indicates that no timezone/DST conversions should be applied.
00812 //           This is useful to give a unique one-to-one mapping between an
00813 //           integer and it's corresponding text-string.
00814 //
00815 // This function returns -1 if there was a problem parsing the time string.
00816 //
00817 static FUNCTION(fun_convtime)
00818 {
00819     UNUSED_PARAMETER(executor);
00820     UNUSED_PARAMETER(caller);
00821     UNUSED_PARAMETER(enactor);
00822     UNUSED_PARAMETER(cargs);
00823     UNUSED_PARAMETER(ncargs);
00824 
00825     CLinearTimeAbsolute lta;
00826     bool bZoneSpecified = false;
00827     if (  lta.SetString(fargs[0])
00828        || ParseDate(lta, fargs[0], &bZoneSpecified))
00829     {
00830         if (  !bZoneSpecified
00831            && (  nfargs == 1
00832               || mux_stricmp("utc", fargs[1]) != 0))
00833         {
00834             lta.Local2UTC();
00835         }
00836         int nPrecision = 0;
00837         if (nfargs == 3)
00838         {
00839             nPrecision = mux_atol(fargs[2]);
00840         }
00841         safe_str(lta.ReturnSecondsString(nPrecision), buff, bufc);
00842     }
00843     else
00844     {
00845         safe_str("#-1 INVALID DATE", buff, bufc);
00846     }
00847 }
00848 
00849 /*
00850  * ---------------------------------------------------------------------------
00851  * * fun_starttime: What time did this system last reboot?
00852  */
00853 
00854 static FUNCTION(fun_starttime)
00855 {
00856     UNUSED_PARAMETER(executor);
00857     UNUSED_PARAMETER(caller);
00858     UNUSED_PARAMETER(enactor);
00859     UNUSED_PARAMETER(fargs);
00860     UNUSED_PARAMETER(nfargs);
00861     UNUSED_PARAMETER(cargs);
00862     UNUSED_PARAMETER(ncargs);
00863 
00864     char *temp = mudstate.start_time.ReturnDateString();
00865     safe_str(temp, buff, bufc);
00866 }
00867 
00868 
00869 // fun_timefmt
00870 //
00871 // timefmt(<format>[, <secs>])
00872 //
00873 // If <secs> isn't given, the current time is used. Escape sequences
00874 // in <format> are expanded out.
00875 //
00876 // All escape sequences start with a $. Any unrecognized codes or other
00877 // text will be returned unchanged.
00878 //
00879 static const char *DayOfWeekStringLong[7] =
00880 {
00881     "Sunday",
00882     "Monday",
00883     "Tuesday",
00884     "Wednesday",
00885     "Thursday",
00886     "Friday",
00887     "Saturday"
00888 };
00889 
00890 static const char *MonthTableLong[] =
00891 {
00892     "January",
00893     "February",
00894     "March",
00895     "April",
00896     "May",
00897     "June",
00898     "July",
00899     "August",
00900     "September",
00901     "October",
00902     "November",
00903     "December"
00904 };
00905 
00906 static const int Map24to12[24] =
00907 {
00908     12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
00909     12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
00910 };
00911 
00912 static FUNCTION(fun_timefmt)
00913 {
00914     UNUSED_PARAMETER(executor);
00915     UNUSED_PARAMETER(caller);
00916     UNUSED_PARAMETER(enactor);
00917     UNUSED_PARAMETER(cargs);
00918     UNUSED_PARAMETER(ncargs);
00919 
00920     CLinearTimeAbsolute lta, ltaUTC;
00921     if (nfargs == 2)
00922     {
00923         ltaUTC.SetSecondsString(fargs[1]);
00924     }
00925     else
00926     {
00927         ltaUTC.GetUTC();
00928     }
00929     lta = ltaUTC;
00930     lta.UTC2Local();
00931 
00932     FIELDEDTIME ft;
00933     lta.ReturnFields(&ft);
00934 
00935     // Calculate Time Zone Info
00936     //
00937     CLinearTimeDelta ltd = lta - ltaUTC;
00938     int iTZSecond = ltd.ReturnSeconds();
00939     int iTZSign;
00940     if (iTZSecond < 0)
00941     {
00942         iTZSign = '-';
00943         iTZSecond = -iTZSecond;
00944     }
00945     else
00946     {
00947         iTZSign = '+';
00948     }
00949     int iTZHour = iTZSecond / 3600;
00950     iTZSecond %= 3600;
00951     int iTZMinute = iTZSecond/60;
00952     int iHour12 = Map24to12[ft.iHour];
00953 
00954     // Calculate Monday and Sunday-oriented week numbers.
00955     //
00956     int iWeekOfYearSunday = (ft.iDayOfYear-ft.iDayOfWeek+6)/7;
00957     int iDayOfWeekMonday  = (ft.iDayOfWeek == 0)?7:ft.iDayOfWeek;
00958     int iWeekOfYearMonday = (ft.iDayOfYear-iDayOfWeekMonday+7)/7;
00959 
00960     // Calculate ISO Week and Year.  Remember that the ISO Year can be the
00961     // same, one year ahead, or one year behind of the Gregorian Year.
00962     //
00963     int iYearISO = ft.iYear;
00964     int iWeekISO = 0;
00965     int iTemp = 0;
00966     if (  ft.iMonth == 12
00967        && 35 <= 7 + ft.iDayOfMonth - iDayOfWeekMonday)
00968     {
00969         iYearISO++;
00970         iWeekISO = 1;
00971     }
00972     else if (  ft.iMonth == 1
00973             && ft.iDayOfMonth <= 3
00974             && (iTemp = 7 - ft.iDayOfMonth + iDayOfWeekMonday) >= 11)
00975     {
00976         iYearISO--;
00977         if (  iTemp == 11
00978            || (  iTemp == 12
00979               && isLeapYear(iYearISO)))
00980         {
00981             iWeekISO = 53;
00982         }
00983         else
00984         {
00985             iWeekISO = 52;
00986         }
00987     }
00988     else
00989     {
00990         iWeekISO = (7 + ft.iDayOfYear - iDayOfWeekMonday)/7;
00991         if (4 <= (7 + ft.iDayOfYear - iDayOfWeekMonday)%7)
00992         {
00993             iWeekISO++;
00994         }
00995     }
00996 
00997     char *q;
00998     char *p = fargs[0];
00999     while ((q = strchr(p, '$')) != NULL)
01000     {
01001         size_t nLen = q - p;
01002         safe_copy_buf(p, nLen, buff, bufc);
01003         p = q;
01004 
01005         // Now, p points to a '$'.
01006         //
01007         p++;
01008 
01009         // Handle modifiers
01010         //
01011         int  iOption = 0;
01012         int ch = *p++;
01013         if (ch == '#' || ch == 'E' || ch == 'O')
01014         {
01015             iOption = ch;
01016             ch = *p++;
01017         }
01018 
01019         // Handle format letter.
01020         //
01021         switch (ch)
01022         {
01023         case 'a': // $a - Abbreviated weekday name
01024             safe_str(DayOfWeekString[ft.iDayOfWeek], buff, bufc);
01025             break;
01026 
01027         case 'A': // $A - Full weekday name
01028             safe_str(DayOfWeekStringLong[ft.iDayOfWeek], buff, bufc);
01029             break;
01030 
01031         case 'b': // $b - Abbreviated month name
01032         case 'h':
01033             safe_str(monthtab[ft.iMonth-1], buff, bufc);
01034             break;
01035 
01036         case 'B': // $B - Full month name
01037             safe_str(MonthTableLong[ft.iMonth-1], buff, bufc);
01038             break;
01039 
01040         case 'c': // $c - Date and time
01041             if (iOption == '#')
01042             {
01043                 // Long version.
01044                 //
01045                 safe_tprintf_str(buff, bufc, "%s, %s %d, %d, %02d:%02d:%02d",
01046                     DayOfWeekStringLong[ft.iDayOfWeek],
01047                     MonthTableLong[ft.iMonth-1],
01048                     ft.iDayOfMonth, ft.iYear, ft.iHour, ft.iMinute,
01049                     ft.iSecond);
01050             }
01051             else
01052             {
01053                 safe_str(lta.ReturnDateString(7), buff, bufc);
01054             }
01055             break;
01056 
01057         case 'C': // $C - The century (year/100).
01058             safe_tprintf_str(buff, bufc, "%d", ft.iYear / 100);
01059             break;
01060 
01061         case 'd': // $d - Day of Month as decimal number (1-31)
01062             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d",
01063                 ft.iDayOfMonth);
01064             break;
01065 
01066         case 'x': // $x - Date
01067             if (iOption == '#')
01068             {
01069                 safe_tprintf_str(buff, bufc, "%s, %s %d, %d",
01070                     DayOfWeekStringLong[ft.iDayOfWeek],
01071                     MonthTableLong[ft.iMonth-1],
01072                     ft.iDayOfMonth, ft.iYear);
01073                 break;
01074             }
01075 
01076             // FALL THROUGH
01077 
01078         case 'D': // $D - Equivalent to %m/%d/%y
01079             safe_tprintf_str(buff, bufc, "%02d/%02d/%02d", ft.iMonth,
01080                 ft.iDayOfMonth, ft.iYear % 100);
01081             break;
01082 
01083         case 'e': // $e - Like $d, the day of the month as a decimal number,
01084                   // but a leading zero is replaced with a space.
01085             safe_tprintf_str(buff, bufc, "%2d", ft.iDayOfMonth);
01086             break;
01087 
01088         case 'F': // $F - The ISO 8601 formated date.
01089             safe_tprintf_str(buff, bufc, "%d-%02d-%02d", ft.iYear, ft.iMonth,
01090                 ft.iDayOfMonth);
01091             break;
01092 
01093         case 'g': // $g - Like $G, two-digit ISO 8601 year.
01094             safe_tprintf_str(buff, bufc, "%02d", iYearISO%100);
01095             break;
01096 
01097         case 'G': // $G - The ISO 8601 year.
01098             safe_tprintf_str(buff, bufc, "%04d", iYearISO);
01099             break;
01100 
01101         case 'H': // $H - Hour of the 24-hour day.
01102             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d", ft.iHour);
01103             break;
01104 
01105         case 'I': // $I - Hour of the 12-hour day
01106             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d", iHour12);
01107             break;
01108 
01109         case 'j': // $j - Day of the year.
01110             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%03d",
01111                 ft.iDayOfYear);
01112             break;
01113 
01114         case 'k': // $k - Hour of the 24-hour day. Pad with a space.
01115             safe_tprintf_str(buff, bufc, "%2d", ft.iHour);
01116             break;
01117 
01118         case 'l': // $l - Hour of the 12-hour clock. Pad with a space.
01119             safe_tprintf_str(buff, bufc, "%2d", iHour12);
01120             break;
01121 
01122         case 'm': // $m - Month of the year
01123             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d",
01124                 ft.iMonth);
01125             break;
01126 
01127         case 'M': // $M - Minutes after the hour
01128             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d",
01129                 ft.iMinute);
01130             break;
01131 
01132         case 'n': // $n - Newline.
01133             safe_str("\r\n", buff, bufc);
01134             break;
01135 
01136         case 'p': // $p - AM/PM
01137             safe_str((ft.iHour < 12)?"AM":"PM", buff, bufc);
01138             break;
01139 
01140         case 'P': // $p - am/pm
01141             safe_str((ft.iHour < 12)?"am":"pm", buff, bufc);
01142             break;
01143 
01144         case 'r': // $r - Equivalent to $I:$M:$S $p
01145             safe_tprintf_str(buff, bufc,
01146                 (iOption=='#')?"%d:%02d:%02d %s":"%02d:%02d:%02d %s",
01147                 iHour12, ft.iMinute, ft.iSecond,
01148                 (ft.iHour<12)?"AM":"PM");
01149             break;
01150 
01151         case 'R': // $R - Equivalent to $H:$M
01152             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d:%02d":"%02d:%02d",
01153                 ft.iHour, ft.iMinute);
01154             break;
01155 
01156         case 's': // $s - Number of seconds since the epoch.
01157             safe_str(ltaUTC.ReturnSecondsString(7), buff, bufc);
01158             break;
01159 
01160         case 'S': // $S - Seconds after the minute
01161             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d",
01162                 ft.iSecond);
01163             break;
01164 
01165         case 't':
01166             safe_chr('\t', buff, bufc);
01167             break;
01168 
01169         case 'X': // $X - Time
01170         case 'T': // $T - Equivalent to $H:$M:$S
01171             safe_tprintf_str(buff, bufc,
01172                 (iOption=='#')?"%d:%02d:%02d":"%02d:%02d:%02d",
01173                 ft.iHour, ft.iMinute, ft.iSecond);
01174             break;
01175 
01176         case 'u': // $u - Day of the Week, range 1 to 7. Monday = 1.
01177             safe_ltoa(iDayOfWeekMonday, buff, bufc);
01178             break;
01179 
01180         case 'U': // $U - Week of the year from 1st Sunday
01181             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d",
01182                 iWeekOfYearSunday);
01183             break;
01184 
01185         case 'V': // $V - ISO 8601:1988 week number.
01186             safe_tprintf_str(buff, bufc, "%02d", iWeekISO);
01187             break;
01188 
01189         case 'w': // $w - Day of the week. 0 = Sunday
01190             safe_ltoa(ft.iDayOfWeek, buff, bufc);
01191             break;
01192 
01193         case 'W': // $W - Week of the year from 1st Monday
01194             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d",
01195                 iWeekOfYearMonday);
01196             break;
01197 
01198         case 'y': // $y - Two-digit year
01199             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%02d",
01200                 ft.iYear % 100);
01201             break;
01202 
01203         case 'Y': // $Y - All-digit year
01204             safe_tprintf_str(buff, bufc, (iOption=='#')?"%d":"%04d",
01205                 ft.iYear);
01206             break;
01207 
01208         case 'z': // $z - Time zone
01209             safe_tprintf_str(buff, bufc, "%c%02d%02d", iTZSign, iTZHour,
01210                 iTZMinute);
01211             break;
01212 
01213         case 'Z': // $Z - Time zone name
01214             // TODO
01215             break;
01216 
01217         case '$': // $$
01218             safe_chr(ch, buff, bufc);
01219             break;
01220 
01221         default:
01222             safe_chr('$', buff, bufc);
01223             p = q + 1;
01224             break;
01225         }
01226     }
01227     safe_str(p, buff, bufc);
01228 }
01229 
01230 /*
01231  * ---------------------------------------------------------------------------
01232  * * fun_get, fun_get_eval: Get attribute from object.
01233  */
01234 #define GET_GET     1
01235 #define GET_XGET    2
01236 #define GET_EVAL    4
01237 #define GET_GEVAL   8
01238 
01239 static void get_handler(char *buff, char **bufc, dbref executor, char *fargs[], int key)
01240 {
01241     bool bFreeBuffer = false;
01242     char *pRefAttrib = fargs[0];
01243 
01244     if (  key == GET_XGET
01245        || key == GET_EVAL)
01246     {
01247         pRefAttrib = alloc_lbuf("get_handler");
01248         char *bufp = pRefAttrib;
01249         safe_tprintf_str(pRefAttrib, &bufp, "%s/%s", fargs[0], fargs[1]);
01250         bFreeBuffer = true;
01251     }
01252     dbref thing;
01253     ATTR *pattr;
01254     bool bNoMatch = !parse_attrib(executor, pRefAttrib, &thing, &pattr);
01255     if (bFreeBuffer)
01256     {
01257         free_lbuf(pRefAttrib);
01258     }
01259 
01260     if (bNoMatch)
01261     {
01262         safe_nomatch(buff, bufc);
01263         return;
01264     }
01265 
01266     if (!pattr)
01267     {
01268         return;
01269     }
01270 
01271     if (  (pattr->flags & AF_IS_LOCK)
01272        || !bCanReadAttr(executor, thing, pattr, true))
01273     {
01274         safe_noperm(buff, bufc);
01275         return;
01276     }
01277 
01278     dbref aowner;
01279     int   aflags;
01280     size_t nLen = 0;
01281     char *atr_gotten = atr_pget_LEN(thing, pattr->number, &aowner, &aflags, &nLen);
01282 
01283     if (  key == GET_EVAL
01284        || key == GET_GEVAL)
01285     {
01286         char *str = atr_gotten;
01287         mux_exec(buff, bufc, thing, executor, executor, EV_FIGNORE | EV_EVAL,
01288                     &str, (char **)NULL, 0);
01289     }
01290     else
01291     {
01292         if (nLen)
01293         {
01294             safe_copy_buf(atr_gotten, nLen, buff, bufc);
01295         }
01296     }
01297     free_lbuf(atr_gotten);
01298 }
01299 
01300 static FUNCTION(fun_get)
01301 {
01302     UNUSED_PARAMETER(caller);
01303     UNUSED_PARAMETER(enactor);
01304     UNUSED_PARAMETER(nfargs);
01305     UNUSED_PARAMETER(cargs);
01306     UNUSED_PARAMETER(ncargs);
01307 
01308     get_handler(buff, bufc, executor, fargs, GET_GET);
01309 }
01310 
01311 static FUNCTION(fun_xget)
01312 {
01313     UNUSED_PARAMETER(caller);
01314     UNUSED_PARAMETER(enactor);
01315     UNUSED_PARAMETER(nfargs);
01316     UNUSED_PARAMETER(cargs);
01317     UNUSED_PARAMETER(ncargs);
01318 
01319     if (!*fargs[0] || !*fargs[1])
01320     {
01321         return;
01322     }
01323 
01324     get_handler(buff, bufc, executor, fargs, GET_XGET);
01325 }
01326 
01327 
01328 static FUNCTION(fun_get_eval)
01329 {
01330     UNUSED_PARAMETER(caller);
01331     UNUSED_PARAMETER(enactor);
01332     UNUSED_PARAMETER(nfargs);
01333     UNUSED_PARAMETER(cargs);
01334     UNUSED_PARAMETER(ncargs);
01335 
01336     get_handler(buff, bufc, executor, fargs, GET_GEVAL);
01337 }
01338 
01339 static FUNCTION(fun_subeval)
01340 {
01341     UNUSED_PARAMETER(nfargs);
01342     UNUSED_PARAMETER(cargs);
01343     UNUSED_PARAMETER(ncargs);
01344 
01345     char *str = fargs[0];
01346     mux_exec(buff, bufc, executor, caller, enactor,
01347              EV_EVAL|EV_NO_LOCATION|EV_NOFCHECK|EV_FIGNORE|EV_NO_COMPRESS,
01348              &str, (char **)NULL, 0);
01349 }
01350 
01351 static FUNCTION(fun_eval)
01352 {
01353     UNUSED_PARAMETER(cargs);
01354     UNUSED_PARAMETER(ncargs);
01355 
01356     if (nfargs == 1)
01357     {
01358         char *str = fargs[0];
01359         mux_exec(buff, bufc, executor, caller, enactor, EV_EVAL, &str,
01360                  (char **)NULL, 0);
01361         return;
01362     }
01363     if (!*fargs[0] || !*fargs[1])
01364     {
01365         return;
01366     }
01367 
01368     get_handler(buff, bufc, executor, fargs, GET_EVAL);
01369 }
01370 
01371 /*
01372  * ---------------------------------------------------------------------------
01373  * * fun_u and fun_ulocal:  Call a user-defined function.
01374  */
01375 
01376 static void do_ufun(char *buff, char **bufc, dbref executor, dbref caller,
01377             dbref enactor,
01378             char *fargs[], int nfargs,
01379             char *cargs[], int ncargs,
01380             bool is_local)
01381 {
01382     UNUSED_PARAMETER(caller);
01383     UNUSED_PARAMETER(cargs);
01384     UNUSED_PARAMETER(ncargs);
01385 
01386     char *atext;
01387     dbref thing;
01388     if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
01389     {
01390         return;
01391     }
01392 
01393     // If we're evaluating locally, preserve the global registers.
01394     //
01395     char **preserve = NULL;
01396     int *preserve_len = NULL;
01397     if (is_local)
01398     {
01399         preserve = PushPointers(MAX_GLOBAL_REGS);
01400         preserve_len = PushIntegers(MAX_GLOBAL_REGS);
01401         save_global_regs("fun_ulocal_save", preserve, preserve_len);
01402     }
01403 
01404     // Evaluate it using the rest of the passed function args.
01405     //
01406     char *str = atext;
01407     mux_exec(buff, bufc, thing, executor, enactor, EV_FCHECK | EV_EVAL,
01408         &str, &(fargs[1]), nfargs - 1);
01409     free_lbuf(atext);
01410 
01411     // If we're evaluating locally, restore the preserved registers.
01412     //
01413     if (is_local)
01414     {
01415         restore_global_regs("fun_ulocal_restore", preserve, preserve_len);
01416         PopIntegers(preserve_len, MAX_GLOBAL_REGS);
01417         PopPointers(preserve, MAX_GLOBAL_REGS);
01418     }
01419 }
01420 
01421 static FUNCTION(fun_u)
01422 {
01423     do_ufun(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs,
01424             ncargs, false);
01425 }
01426 
01427 static FUNCTION(fun_ulocal)
01428 {
01429     do_ufun(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs,
01430             ncargs, true);
01431 }
01432 
01433 /*
01434  * ---------------------------------------------------------------------------
01435  * * fun_parent: Get parent of object.
01436  */
01437 
01438 static FUNCTION(fun_parent)
01439 {
01440     UNUSED_PARAMETER(caller);
01441     UNUSED_PARAMETER(nfargs);
01442     UNUSED_PARAMETER(cargs);
01443     UNUSED_PARAMETER(ncargs);
01444 
01445     dbref it = match_thing_quiet(executor, fargs[0]);
01446     if (!Good_obj(it))
01447     {
01448         safe_match_result(it, buff, bufc);
01449         return;
01450     }
01451     if (  Examinable(executor, it)
01452        || it == enactor)
01453     {
01454         safe_tprintf_str(buff, bufc, "#%d", Parent(it));
01455     }
01456     else
01457     {
01458         safe_noperm(buff, bufc);
01459     }
01460 }
01461 
01462 /*
01463  * ---------------------------------------------------------------------------
01464  * * fun_mid: mid(foobar,2,3) returns oba
01465  */
01466 
01467 static FUNCTION(fun_mid)
01468 {
01469     UNUSED_PARAMETER(executor);
01470     UNUSED_PARAMETER(caller);
01471     UNUSED_PARAMETER(enactor);
01472     UNUSED_PARAMETER(nfargs);
01473     UNUSED_PARAMETER(cargs);
01474     UNUSED_PARAMETER(ncargs);
01475 
01476     // Initial checks for iPosition0 [0,LBUF_SIZE), nLength [0,LBUF_SIZE),
01477     // and iPosition1 [0,LBUF_SIZE).
01478     //
01479     int iPosition0 = mux_atol(fargs[1]);
01480     int nLength = mux_atol(fargs[2]);
01481     if (nLength < 0)
01482     {
01483         iPosition0 += nLength;
01484         nLength = -nLength;
01485     }
01486 
01487     if (iPosition0 < 0)
01488     {
01489         iPosition0 = 0;
01490     }
01491     else if (LBUF_SIZE-1 < iPosition0)
01492     {
01493         iPosition0 = LBUF_SIZE-1;
01494     }
01495 
01496     // At this point, iPosition0, nLength are reasonable numbers which may
01497     // -still- not refer to valid data in the string.
01498     //
01499     struct ANSI_In_Context aic;
01500     ANSI_String_In_Init(&aic, fargs[0], ANSI_ENDGOAL_NORMAL);
01501     int nDone;
01502     ANSI_String_Skip(&aic, iPosition0, &nDone);
01503     if (nDone < iPosition0)
01504     {
01505         return;
01506     }
01507 
01508     struct ANSI_Out_Context aoc;
01509     int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
01510     ANSI_String_Out_Init(&aoc, *bufc, nBufferAvailable, nLength, ANSI_ENDGOAL_NORMAL);
01511     ANSI_String_Copy(&aoc, &aic, nLength);
01512     int nSize = ANSI_String_Finalize(&aoc, &nDone);
01513     *bufc += nSize;
01514 }
01515 
01516 // ---------------------------------------------------------------------------
01517 // fun_right: right(foobar,2) returns ar
01518 // ---------------------------------------------------------------------------
01519 
01520 static FUNCTION(fun_right)
01521 {
01522     UNUSED_PARAMETER(executor);
01523     UNUSED_PARAMETER(caller);
01524     UNUSED_PARAMETER(enactor);
01525     UNUSED_PARAMETER(nfargs);
01526     UNUSED_PARAMETER(cargs);
01527     UNUSED_PARAMETER(ncargs);
01528 
01529     // nLength on [0,LBUF_SIZE).
01530     //
01531     long   lLength = mux_atol(fargs[1]);
01532     size_t nLength;
01533     if (lLength < 0)
01534     {
01535         safe_range(buff, bufc);
01536         return;
01537     }
01538     else if (LBUF_SIZE-1 < lLength)
01539     {
01540         nLength = LBUF_SIZE-1;
01541     }
01542     else
01543     {
01544         nLength = lLength;
01545     }
01546 
01547     // iPosition1 on [0,LBUF_SIZE)
01548     //
01549     size_t iPosition1 = strlen(strip_ansi(fargs[0]));
01550 
01551     // iPosition0 on [0,LBUF_SIZE)
01552     //
01553     size_t iPosition0;
01554     if (iPosition1 <= nLength)
01555     {
01556         iPosition0 = 0;
01557     }
01558     else
01559     {
01560         iPosition0 = iPosition1 - nLength;
01561     }
01562 
01563     // At this point, iPosition0, nLength, and iPosition1 are reasonable
01564     // numbers which may -still- not refer to valid data in the string.
01565     //
01566     struct ANSI_In_Context aic;
01567     ANSI_String_In_Init(&aic, fargs[0], ANSI_ENDGOAL_NORMAL);
01568     int nDone;
01569     ANSI_String_Skip(&aic, iPosition0, &nDone);
01570     if ((size_t)nDone < iPosition0)
01571     {
01572         return;
01573     }
01574 
01575     struct ANSI_Out_Context aoc;
01576     int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
01577     ANSI_String_Out_Init(&aoc, *bufc, nBufferAvailable, nLength, ANSI_ENDGOAL_NORMAL);
01578     ANSI_String_Copy(&aoc, &aic, nLength);
01579     int nSize = ANSI_String_Finalize(&aoc, &nDone);
01580     *bufc += nSize;
01581 }
01582 
01583 /*
01584  * ---------------------------------------------------------------------------
01585  * * fun_first: Returns first word in a string
01586  */
01587 
01588 static FUNCTION(fun_first)
01589 {
01590     // If we are passed an empty arglist return a null string.
01591     //
01592     if (nfargs == 0)
01593     {
01594         return;
01595     }
01596 
01597     SEP sep;
01598     if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
01599     {
01600         return;
01601     }
01602 
01603     char *s = trim_space_sep(fargs[0], &sep);
01604     char *first = split_token(&s, &sep);
01605     if (first)
01606     {
01607         safe_str(first, buff, bufc);
01608     }
01609 }
01610 
01611 /*
01612  * ---------------------------------------------------------------------------
01613  * * fun_rest: Returns all but the first word in a string
01614  */
01615 
01616 
01617 static FUNCTION(fun_rest)
01618 {
01619     // If we are passed an empty arglist return a null string.
01620     //
01621     if (nfargs == 0)
01622     {
01623         return;
01624     }
01625 
01626     SEP sep;
01627     if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
01628     {
01629         return;
01630     }
01631 
01632     char *s = trim_space_sep(fargs[0], &sep);
01633     split_token(&s, &sep);
01634     if (s)
01635     {
01636         safe_str(s, buff, bufc);
01637     }
01638 }
01639 
01640 /*
01641  * ---------------------------------------------------------------------------
01642  * * fun_v: Function form of %-substitution
01643  */
01644 
01645 static FUNCTION(fun_v)
01646 {
01647     UNUSED_PARAMETER(nfargs);
01648 
01649     dbref aowner;
01650     int aflags;
01651     char *sbuf, *sbufc, *tbuf, *str;
01652     ATTR *ap;
01653 
01654     tbuf = fargs[0];
01655     if (mux_AttrNameInitialSet(tbuf[0]) && tbuf[1])
01656     {
01657         // Fetch an attribute from me. First see if it exists,
01658         // returning a null string if it does not.
01659         //
01660         ap = atr_str(fargs[0]);
01661         if (!ap)
01662         {
01663             return;
01664         }
01665 
01666         // If we can access it, return it, otherwise return a null
01667         // string.
01668         //
01669         size_t nLen;
01670         tbuf = atr_pget_LEN(executor, ap->number, &aowner, &aflags, &nLen);
01671         if (See_attr(executor, executor, ap))
01672         {
01673             safe_copy_buf(tbuf, nLen, buff, bufc);
01674         }
01675         free_lbuf(tbuf);
01676         return;
01677     }
01678 
01679     // Not an attribute, process as %<arg>
01680     //
01681     sbuf = alloc_sbuf("fun_v");
01682     sbufc = sbuf;
01683     safe_sb_chr('%', sbuf, &sbufc);
01684     safe_sb_str(fargs[0], sbuf, &sbufc);
01685     *sbufc = '\0';
01686     str = sbuf;
01687     mux_exec(buff, bufc, executor, caller, enactor, EV_EVAL|EV_FIGNORE, &str,
01688              cargs, ncargs);
01689     free_sbuf(sbuf);
01690 }
01691 
01692 /*
01693  * ---------------------------------------------------------------------------
01694  * * fun_s: Force substitution to occur.
01695  */
01696 
01697 static FUNCTION(fun_s)
01698 {
01699     UNUSED_PARAMETER(nfargs);
01700 
01701     char *str = fargs[0];
01702     mux_exec(buff, bufc, executor, caller, enactor, EV_FIGNORE | EV_EVAL, &str,
01703              cargs, ncargs);
01704 }
01705 
01706 /*
01707  * ---------------------------------------------------------------------------
01708  * * fun_con: Returns first item in contents list of object/room
01709  */
01710 
01711 static FUNCTION(fun_con)
01712 {
01713     UNUSED_PARAMETER(caller);
01714     UNUSED_PARAMETER(nfargs);
01715     UNUSED_PARAMETER(cargs);
01716     UNUSED_PARAMETER(ncargs);
01717 
01718     dbref it = match_thing_quiet(executor, fargs[0]);
01719     if (!Good_obj(it))
01720     {
01721         safe_match_result(it, buff, bufc);
01722     }
01723     else if (Has_contents(it))
01724     {
01725         if (  Examinable(executor, it)
01726            || where_is(executor) == it
01727            || it == enactor)
01728         {
01729             safe_tprintf_str(buff, bufc, "#%d", Contents(it));
01730         }
01731         else
01732         {
01733             safe_noperm(buff, bufc);
01734         }
01735     }
01736     else
01737     {
01738         safe_nothing(buff, bufc);
01739     }
01740 }
01741 
01742 /*
01743  * ---------------------------------------------------------------------------
01744  * * fun_exit: Returns first exit in exits list of room.
01745  */
01746 
01747 static FUNCTION(fun_exit)
01748 {
01749     UNUSED_PARAMETER(caller);
01750     UNUSED_PARAMETER(enactor);
01751     UNUSED_PARAMETER(nfargs);
01752     UNUSED_PARAMETER(cargs);
01753     UNUSED_PARAMETER(ncargs);
01754 
01755     dbref it = match_thing_quiet(executor, fargs[0]);
01756     if (!Good_obj(it))
01757     {
01758         safe_match_result(it, buff, bufc);
01759     }
01760     else if (  Has_exits(it)
01761             && Good_obj(Exits(it)))
01762     {
01763         int key = 0;
01764         if (Examinable(executor, it))
01765         {
01766             key |= VE_LOC_XAM;
01767         }
01768         if (Dark(it))
01769         {
01770             key |= VE_LOC_DARK;
01771         }
01772         dbref exit;
01773         DOLIST(exit, Exits(it))
01774         {
01775             if (exit_visible(exit, executor, key))
01776             {
01777                 safe_tprintf_str(buff, bufc, "#%d", exit);
01778                 return;
01779             }
01780         }
01781         safe_notfound(buff, bufc);
01782     }
01783     else
01784     {
01785         safe_nothing(buff, bufc);
01786     }
01787 }
01788 
01789 /*
01790  * ---------------------------------------------------------------------------
01791  * * fun_next: return next thing in contents or exits chain
01792  */
01793 
01794 static FUNCTION(fun_next)
01795 {
01796     UNUSED_PARAMETER(caller);
01797     UNUSED_PARAMETER(enactor);
01798     UNUSED_PARAMETER(nfargs);
01799     UNUSED_PARAMETER(cargs);
01800     UNUSED_PARAMETER(ncargs);
01801 
01802     dbref it = match_thing_quiet(executor, fargs[0]);
01803     if (!Good_obj(it))
01804     {
01805         safe_match_result(it, buff, bufc);
01806     }
01807     else if (Has_siblings(it))
01808     {
01809         dbref loc = where_is(it);
01810         bool ex_here = Good_obj(loc) ? Examinable(executor, loc) : false;
01811         if (  ex_here
01812            || loc == executor
01813            || loc == where_is(executor))
01814         {
01815             if (!isExit(it))
01816             {
01817                 safe_tprintf_str(buff, bufc, "#%d", Next(it));
01818             }
01819             else
01820             {
01821                 int key = 0;
01822                 if (ex_here)
01823                 {
01824                     key |= VE_LOC_XAM;
01825                 }
01826                 if (Dark(loc))
01827                 {
01828                     key |= VE_LOC_DARK;
01829                 }
01830                 dbref exit;
01831                 DOLIST(exit, it)
01832                 {
01833                     if (  exit != it
01834                        && exit_visible(exit, executor, key))
01835                     {
01836                         safe_tprintf_str(buff, bufc, "#%d", exit);
01837                         return;
01838                     }
01839                 }
01840                 safe_notfound(buff, bufc);
01841             }
01842         }
01843         else
01844         {
01845             safe_noperm(buff, bufc);
01846         }
01847     }
01848     else
01849     {
01850         safe_nothing(buff, bufc);
01851     }
01852 }
01853 
01854 /*
01855  * ---------------------------------------------------------------------------
01856  * * fun_loc: Returns the location of something
01857  */
01858 
01859 static FUNCTION(fun_loc)
01860 {
01861     UNUSED_PARAMETER(caller);
01862     UNUSED_PARAMETER(nfargs);
01863     UNUSED_PARAMETER(cargs);
01864     UNUSED_PARAMETER(ncargs);
01865 
01866     dbref it = match_thing_quiet(executor, fargs[0]);
01867     if (!Good_obj(it))
01868     {
01869         safe_match_result(it, buff, bufc);
01870     }
01871     else if (locatable(executor, it, enactor))
01872     {
01873         safe_tprintf_str(buff, bufc, "#%d", Location(it));
01874     }
01875     else
01876     {
01877         safe_nothing(buff, bufc);
01878     }
01879 }
01880 
01881 /*
01882  * ---------------------------------------------------------------------------
01883  * * fun_where: Returns the "true" location of something
01884  */
01885 
01886 static FUNCTION(fun_where)
01887 {
01888     UNUSED_PARAMETER(caller);
01889     UNUSED_PARAMETER(nfargs);
01890     UNUSED_PARAMETER(cargs);
01891     UNUSED_PARAMETER(ncargs);
01892 
01893     dbref it = match_thing_quiet(executor, fargs[0]);
01894     if (!Good_obj(it))
01895     {
01896         safe_match_result(it, buff, bufc);
01897     }
01898     else if (locatable(executor, it, enactor))
01899     {
01900         safe_tprintf_str(buff, bufc, "#%d", where_is(it));
01901     }
01902     else
01903     {
01904         safe_nothing(buff, bufc);
01905     }
01906 }
01907 
01908 /*
01909  * ---------------------------------------------------------------------------
01910  * * fun_rloc: Returns the recursed location of something (specifying #levels)
01911  */
01912 
01913 static FUNCTION(fun_rloc)
01914 {
01915     UNUSED_PARAMETER(caller);
01916     UNUSED_PARAMETER(nfargs);
01917     UNUSED_PARAMETER(cargs);
01918     UNUSED_PARAMETER(ncargs);
01919 
01920     int levels = mux_atol(fargs[1]);
01921     if (levels > mudconf.ntfy_nest_lim)
01922     {
01923         levels = mudconf.ntfy_nest_lim;
01924     }
01925 
01926     dbref it = match_thing_quiet(executor, fargs[0]);
01927     if (!Good_obj(it))
01928     {
01929         safe_match_result(it, buff, bufc);
01930     }
01931     else if (locatable(executor, it, enactor))
01932     {
01933         for (int i = 0; i < levels; i++)
01934         {
01935             if (  Good_obj(it)
01936                && (  isExit(it)
01937                   || Has_location(it)))
01938             {
01939                 it = Location(it);
01940             }
01941             else
01942             {
01943                 break;
01944             }
01945         }
01946         safe_tprintf_str(buff, bufc, "#%d", it);
01947     }
01948     else
01949     {
01950         safe_nothing(buff, bufc);
01951     }
01952 }
01953 
01954 /*
01955  * ---------------------------------------------------------------------------
01956  * * fun_room: Find the room an object is ultimately in.
01957  */
01958 
01959 static FUNCTION(fun_room)
01960 {
01961     UNUSED_PARAMETER(caller);
01962     UNUSED_PARAMETER(nfargs);
01963     UNUSED_PARAMETER(cargs);
01964     UNUSED_PARAMETER(ncargs);
01965 
01966     dbref it = match_thing_quiet(executor, fargs[0]);
01967     if (!Good_obj(it))
01968     {
01969         safe_match_result(it, buff, bufc);
01970     }
01971     else if (locatable(executor, it, enactor))
01972     {
01973         int count;
01974         for (count = mudconf.ntfy_nest_lim; count > 0; count--)
01975         {
01976             it = Location(it);
01977             if (!Good_obj(it))
01978             {
01979                 break;
01980             }
01981             if (isRoom(it))
01982             {
01983                 safe_tprintf_str(buff, bufc, "#%d", it);
01984                 return;
01985             }
01986         }
01987         safe_nothing(buff, bufc);
01988     }
01989     else if (isRoom(it))
01990     {
01991         safe_tprintf_str(buff, bufc, "#%d", it);
01992     }
01993     else
01994     {
01995         safe_nothing(buff, bufc);
01996     }
01997 }
01998 
01999 /*
02000  * ---------------------------------------------------------------------------
02001  * * fun_owner: Return the owner of an object.
02002  */
02003 
02004 static FUNCTION(fun_owner)
02005 {
02006     UNUSED_PARAMETER(caller);
02007     UNUSED_PARAMETER(enactor);
02008     UNUSED_PARAMETER(nfargs);
02009     UNUSED_PARAMETER(cargs);
02010     UNUSED_PARAMETER(ncargs);
02011 
02012     dbref it;
02013     ATTR *pattr;
02014     if (parse_attrib(executor, fargs[0], &it, &pattr))
02015     {
02016         if (  !pattr
02017            || !See_attr(executor, it, pattr))
02018         {
02019             safe_nothing(buff, bufc);
02020             return;
02021         }
02022         else
02023         {
02024             dbref aowner;
02025             int   aflags;
02026             atr_pget_info(it, pattr->number, &aowner, &aflags);
02027             it = aowner;
02028         }
02029     }
02030     else
02031     {
02032         it = match_thing_quiet(executor, fargs[0]);
02033         if (!Good_obj(it))
02034         {
02035             safe_match_result(it, buff, bufc);
02036             return;
02037         }
02038         it = Owner(it);
02039     }
02040     safe_tprintf_str(buff, bufc, "#%d", it);
02041 }
02042 
02043 /*
02044  * ---------------------------------------------------------------------------
02045  * * fun_controls: Does x control y?
02046  */
02047 
02048 static FUNCTION(fun_controls)
02049 {
02050     UNUSED_PARAMETER(caller);
02051     UNUSED_PARAMETER(enactor);
02052     UNUSED_PARAMETER(nfargs);
02053     UNUSED_PARAMETER(cargs);
02054     UNUSED_PARAMETER(ncargs);
02055 
02056     dbref x = match_thing_quiet(executor, fargs[0]);
02057     if (!Good_obj(x))
02058     {
02059         safe_match_result(x, buff, bufc);
02060         safe_str(" (ARG1)", buff, bufc);
02061         return;
02062     }
02063     dbref y = match_thing_quiet(executor, fargs[1]);
02064     if (!Good_obj(y))
02065     {
02066         safe_match_result(x, buff, bufc);
02067         safe_str(" (ARG2)", buff, bufc);
02068         return;
02069     }
02070     safe_bool(Controls(x,y), buff, bufc);
02071 }
02072 
02073 /*
02074  * ---------------------------------------------------------------------------
02075  * * fun_fullname: Return the fullname of an object (good for exits)
02076  */
02077 
02078 static FUNCTION(fun_fullname)
02079 {
02080     UNUSED_PARAMETER(caller);
02081     UNUSED_PARAMETER(enactor);
02082     UNUSED_PARAMETER(nfargs);
02083     UNUSED_PARAMETER(cargs);
02084     UNUSED_PARAMETER(ncargs);
02085 
02086     dbref it = match_thing_quiet(executor, fargs[0]);
02087     if (!Good_obj(it))
02088     {
02089         safe_match_result(it, buff, bufc);
02090         return;
02091     }
02092     if (!mudconf.read_rem_name)
02093     {
02094         if (  !nearby_or_control(executor, it)
02095            && !isPlayer(it))
02096         {
02097             safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc);
02098             return;
02099         }
02100     }
02101     safe_str(Name(it), buff, bufc);
02102 }
02103 
02104 /*
02105  * ---------------------------------------------------------------------------
02106  * * fun_name: Return the name of an object
02107  */
02108 
02109 static FUNCTION(fun_name)
02110 {
02111     UNUSED_PARAMETER(caller);
02112     UNUSED_PARAMETER(enactor);
02113     UNUSED_PARAMETER(nfargs);
02114     UNUSED_PARAMETER(cargs);
02115     UNUSED_PARAMETER(ncargs);
02116 
02117     dbref it = match_thing_quiet(executor, fargs[0]);
02118     if (!Good_obj(it))
02119     {
02120         safe_match_result(it, buff, bufc);
02121         return;
02122     }
02123     if (!mudconf.read_rem_name)
02124     {
02125         if (  !nearby_or_control(executor, it)
02126            && !isPlayer(it)
02127            && !Long_Fingers(executor))
02128         {
02129             safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc);
02130             return;
02131         }
02132     }
02133     char *temp = *bufc;
02134     safe_str(Name(it), buff, bufc);
02135     if (isExit(it))
02136     {
02137         char *s;
02138         for (s = temp; (s != *bufc) && (*s != ';'); s++)
02139         {
02140             // Do nothing
02141             //
02142             ;
02143         }
02144         if (*s == ';')
02145         {
02146             *bufc = s;
02147         }
02148     }
02149 }
02150 
02151 /*
02152  * ---------------------------------------------------------------------------
02153  * * fun_match, fun_strmatch: Match arg2 against each word of arg1 returning
02154  * * index of first match, or against the whole string.
02155  */
02156 
02157 static FUNCTION(fun_match)
02158 {
02159     SEP sep;
02160     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
02161     {
02162         return;
02163     }
02164 
02165     // Check each word individually, returning the word number of the first
02166     // one that matches.  If none match, return 0.
02167     //
02168     int wcount = 1;
02169     char *s = trim_space_sep(fargs[0], &sep);
02170     do {
02171         char *r = split_token(&s, &sep);
02172         mudstate.wild_invk_ctr = 0;
02173         if (quick_wild(fargs[1], r))
02174         {
02175             safe_ltoa(wcount, buff, bufc);
02176             return;
02177         }
02178         wcount++;
02179     } while (s);
02180     safe_chr('0', buff, bufc);
02181 }
02182 
02183 static FUNCTION(fun_strmatch)
02184 {
02185     UNUSED_PARAMETER(executor);
02186     UNUSED_PARAMETER(caller);
02187     UNUSED_PARAMETER(enactor);
02188     UNUSED_PARAMETER(nfargs);
02189     UNUSED_PARAMETER(cargs);
02190     UNUSED_PARAMETER(ncargs);
02191 
02192     // Check if we match the whole string.  If so, return 1.
02193     //
02194     mudstate.wild_invk_ctr = 0;
02195     bool cc = quick_wild(fargs[1], fargs[0]);
02196     safe_bool(cc, buff, bufc);
02197 }
02198 
02199 /*
02200  * ---------------------------------------------------------------------------
02201  * * fun_extract: extract words from string:
02202  * * extract(foo bar baz,1,2) returns 'foo bar'
02203  * * extract(foo bar baz,2,1) returns 'bar'
02204  * * extract(foo bar baz,2,2) returns 'bar baz'
02205  * *
02206  * * Now takes optional separator extract(foo-bar-baz,1,2,-) returns 'foo-bar'
02207  */
02208 
02209 static FUNCTION(fun_extract)
02210 {
02211     SEP sep;
02212     if (!OPTIONAL_DELIM(4, sep, DELIM_DFLT|DELIM_STRING))
02213     {
02214         return;
02215     }
02216 
02217     SEP osep = sep;
02218     if (!OPTIONAL_DELIM(5, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
02219     {
02220         return;
02221     }
02222 
02223     int start = mux_atol(fargs[1]);
02224     int len = mux_atol(fargs[2]);
02225 
02226     if (  start < 1
02227        || len < 1)
02228     {
02229         return;
02230     }
02231 
02232     // Skip to the start of the string to save.
02233     //
02234     start--;
02235     char *s = trim_space_sep(fargs[0], &sep);
02236     while (  start
02237           && s)
02238     {
02239         s = next_token(s, &sep);
02240         start--;
02241     }
02242 
02243     // If we ran of the end of the string, return nothing.
02244     //
02245     if (!s || !*s)
02246     {
02247         return;
02248     }
02249 
02250     // Count off the words in the string to save.
02251     //
02252     bool bFirst = true;
02253     while (  len
02254           && s)
02255     {
02256         char *t = split_token(&s, &sep);
02257         if (!bFirst)
02258         {
02259             print_sep(&osep, buff, bufc);
02260         }
02261         else
02262         {
02263             bFirst = false;
02264         }
02265         safe_str(t, buff, bufc);
02266         len--;
02267     }
02268 }
02269 
02270 // xlate() controls the subtle definition of a softcode boolean.
02271 //
02272 bool xlate(char *arg)
02273 {
02274     const char *p = arg;
02275     if (p[0] == '#')
02276     {
02277         if (p[1] == '-')
02278         {
02279             // '#-...' is false. This includes '#-0000' and '#-ABC'.
02280             // This cases are unlikely in practice. We can always come back
02281             // and cover these.
02282             //
02283             return false;
02284         }
02285         return true;
02286     }
02287 
02288     PARSE_FLOAT_RESULT pfr;
02289     if (ParseFloat(&pfr, p))
02290     {
02291         // Examine whether number was a zero value.
02292         //
02293         if (pfr.iString)
02294         {
02295             // This covers NaN, +Inf, -Inf, and Ind.
02296             //
02297             return false;
02298         }
02299 
02300         // We can ignore leading sign, exponent sign, and exponent as 0, -0,
02301         // and +0. Also, 0E+100 and 0.0e-100 are all zero.
02302         //
02303         // However, we need to cover 00000.0 and 0.00000 cases.
02304         //
02305         while (pfr.nDigitsA--)
02306         {
02307             if (*pfr.pDigitsA != '0')
02308             {
02309                 return true;
02310             }
02311             pfr.pDigitsA++;
02312         }
02313         while (pfr.nDigitsB--)
02314         {
02315             if (*pfr.pDigitsB != '0')
02316             {
02317                 return true;
02318             }
02319             pfr.pDigitsB++;
02320         }
02321         return false;
02322     }
02323     while (mux_isspace(*p))
02324     {
02325         p++;
02326     }
02327     if (p[0] == '\0')
02328     {
02329         return false;
02330     }
02331     return true;
02332 }
02333 
02334 /* ---------------------------------------------------------------------------
02335  * fun_index:  like extract(), but it works with an arbitrary separator.
02336  * index(a b | c d e | f g h | i j k, |, 2, 1) => c d e
02337  * index(a b | c d e | f g h | i j k, |, 2, 2) => c d e | f g h
02338  */
02339 
02340 static FUNCTION(fun_index)
02341 {
02342     UNUSED_PARAMETER(executor);
02343     UNUSED_PARAMETER(caller);
02344     UNUSED_PARAMETER(enactor);
02345     UNUSED_PARAMETER(nfargs);
02346     UNUSED_PARAMETER(cargs);
02347     UNUSED_PARAMETER(ncargs);
02348 
02349     int start, end;
02350     char c, *s, *p;
02351 
02352     s = fargs[0];
02353     c = *fargs[1];
02354     start = mux_atol(fargs[2]);
02355     end = mux_atol(fargs[3]);
02356 
02357     if ((start < 1) || (end < 1) || (*s == '\0'))
02358     {
02359         return;
02360     }
02361     if (c == '\0')
02362     {
02363         c = ' ';
02364     }
02365 
02366     // Move s to point to the start of the item we want.
02367     //
02368     start--;
02369     while (start && s && *s)
02370     {
02371         if ((s = strchr(s, c)) != NULL)
02372         {
02373             s++;
02374         }
02375         start--;
02376     }
02377 
02378     // Skip over just spaces.
02379     //
02380     while (s && (*s == ' '))
02381     {
02382         s++;
02383     }
02384     if (!s || !*s)
02385     {
02386         return;
02387     }
02388 
02389     // Figure out where to end the string.
02390     //
02391     p = s;
02392     while (end && p && *p)
02393     {
02394         if ((p = strchr(p, c)) != NULL)
02395         {
02396             if (--end == 0)
02397             {
02398                 do {
02399                     p--;
02400                 } while ((*p == ' ') && (p > s));
02401                 *(++p) = '\0';
02402                 safe_str(s, buff, bufc);
02403                 return;
02404             }
02405             else
02406             {
02407                 p++;
02408             }
02409         }
02410     }
02411 
02412     // if we've gotten this far, we've run off the end of the string.
02413     //
02414     safe_str(s, buff, bufc);
02415 }
02416 
02417 
02418 static FUNCTION(fun_cat)
02419 {
02420     UNUSED_PARAMETER(executor);
02421     UNUSED_PARAMETER(caller);
02422     UNUSED_PARAMETER(enactor);
02423     UNUSED_PARAMETER(cargs);
02424     UNUSED_PARAMETER(ncargs);
02425 
02426     if (nfargs)
02427     {
02428         safe_str(fargs[0], buff, bufc);
02429         for (int i = 1; i < nfargs; i++)
02430         {
02431             safe_chr(' ', buff, bufc);
02432             safe_str(fargs[i], buff, bufc);
02433         }
02434     }
02435 }
02436 
02437 static FUNCTION(fun_version)
02438 {
02439     UNUSED_PARAMETER(executor);
02440     UNUSED_PARAMETER(caller);
02441     UNUSED_PARAMETER(enactor);
02442     UNUSED_PARAMETER(fargs);
02443     UNUSED_PARAMETER(nfargs);
02444     UNUSED_PARAMETER(cargs);
02445     UNUSED_PARAMETER(ncargs);
02446 
02447     safe_str(mudstate.version, buff, bufc);
02448 }
02449 
02450 static FUNCTION(fun_strlen)
02451 {
02452     UNUSED_PARAMETER(executor);
02453     UNUSED_PARAMETER(caller);
02454     UNUSED_PARAMETER(enactor);
02455     UNUSED_PARAMETER(cargs);
02456     UNUSED_PARAMETER(ncargs);
02457 
02458     size_t n = 0;
02459     if (nfargs >= 1)
02460     {
02461         strip_ansi(fargs[0], &n);
02462     }
02463     safe_ltoa(n, buff, bufc);
02464 }
02465 
02466 static FUNCTION(fun_strmem)
02467 {
02468     UNUSED_PARAMETER(executor);
02469     UNUSED_PARAMETER(caller);
02470     UNUSED_PARAMETER(enactor);
02471     UNUSED_PARAMETER(cargs);
02472     UNUSED_PARAMETER(ncargs);
02473 
02474    size_t n = 0;
02475    if (nfargs >= 1)
02476    {
02477        n = strlen(fargs[0]);
02478    }
02479    safe_ltoa(n, buff, bufc);
02480 }
02481 
02482 static FUNCTION(fun_num)
02483 {
02484     UNUSED_PARAMETER(caller);
02485     UNUSED_PARAMETER(enactor);
02486     UNUSED_PARAMETER(nfargs);
02487     UNUSED_PARAMETER(cargs);
02488     UNUSED_PARAMETER(ncargs);
02489 
02490     safe_tprintf_str(buff, bufc, "#%d", match_thing_quiet(executor, fargs[0]));
02491 }
02492 
02493 static void internalPlayerFind
02494 (
02495     char* buff,
02496     char** bufc,
02497     dbref player,
02498     char* name,
02499     int bVerifyPlayer
02500 )
02501 {
02502     dbref thing;
02503     if (*name == '#')
02504     {
02505         thing = match_thing_quiet(player, name);
02506         if (bVerifyPlayer)
02507         {
02508             if (!Good_obj(thing))
02509             {
02510                 safe_match_result(thing, buff, bufc);
02511                 return;
02512             }
02513             if (!isPlayer(thing))
02514             {
02515                 safe_nomatch(buff, bufc);
02516                 return;
02517             }
02518         }
02519     }
02520     else
02521     {
02522         char *nptr = name;
02523         if (*nptr == '*')
02524         {
02525             // Start with the second character in the name string.
02526             //
02527             nptr++;
02528         }
02529         thing = lookup_player(player, nptr, true);
02530         if (  (!Good_obj(thing))
02531            || (!isPlayer(thing) && bVerifyPlayer))
02532         {
02533             safe_nomatch(buff, bufc);
02534             return;
02535         }
02536     }
02537     ITL pContext;
02538     ItemToList_Init(&pContext, buff, bufc, '#');
02539     ItemToList_AddInteger(&pContext, thing);
02540     ItemToList_Final(&pContext);
02541 }
02542 
02543 
02544 static FUNCTION(fun_pmatch)
02545 {
02546     UNUSED_PARAMETER(caller);
02547     UNUSED_PARAMETER(enactor);
02548     UNUSED_PARAMETER(nfargs);
02549     UNUSED_PARAMETER(cargs);
02550     UNUSED_PARAMETER(ncargs);
02551 
02552     internalPlayerFind(buff, bufc, executor, fargs[0], true);
02553 }
02554 
02555 static FUNCTION(fun_pfind)
02556 {
02557     UNUSED_PARAMETER(caller);
02558     UNUSED_PARAMETER(enactor);
02559     UNUSED_PARAMETER(nfargs);
02560     UNUSED_PARAMETER(cargs);
02561     UNUSED_PARAMETER(ncargs);
02562 
02563     internalPlayerFind(buff, bufc, executor, fargs[0], false);
02564 }
02565 
02566 /*
02567  * ---------------------------------------------------------------------------
02568  * * fun_comp: string compare.
02569  */
02570 
02571 static FUNCTION(fun_comp)
02572 {
02573     UNUSED_PARAMETER(executor);
02574     UNUSED_PARAMETER(caller);
02575     UNUSED_PARAMETER(enactor);
02576     UNUSED_PARAMETER(nfargs);
02577     UNUSED_PARAMETER(cargs);
02578     UNUSED_PARAMETER(ncargs);
02579 
02580     int x;
02581 
02582     x = strcmp(fargs[0], fargs[1]);
02583     if (x < 0)
02584     {
02585         safe_str("-1", buff, bufc);
02586     }
02587     else
02588     {
02589         safe_bool((x != 0), buff, bufc);
02590     }
02591 }
02592 
02593 #if defined(WOD_REALMS) || defined(REALITY_LVLS)
02594 static FUNCTION(fun_cansee)
02595 {
02596     UNUSED_PARAMETER(caller);
02597     UNUSED_PARAMETER(enactor);
02598     UNUSED_PARAMETER(cargs);
02599     UNUSED_PARAMETER(ncargs);
02600 
02601     dbref looker = match_thing_quiet(executor, fargs[0]);
02602     if (!Good_obj(looker))
02603     {
02604         safe_match_result(looker, buff, bufc);
02605         safe_str(" (LOOKER)", buff, bufc);
02606         return;
02607     }
02608     dbref lookee = match_thing_quiet(executor, fargs[1]);
02609     if (!Good_obj(lookee))
02610     {
02611         safe_match_result(looker, buff, bufc);
02612         safe_str(" (LOOKEE)", buff, bufc);
02613         return;
02614     }
02615     int mode;
02616     if (nfargs == 3)
02617     {
02618         mode = mux_atol(fargs[2]);
02619         switch (mode)
02620         {
02621         case ACTION_IS_STATIONARY:
02622         case ACTION_IS_MOVING:
02623         case ACTION_IS_TALKING:
02624             break;
02625 
02626         default:
02627             mode = ACTION_IS_STATIONARY;
02628             break;
02629         }
02630     }
02631     else
02632     {
02633         mode = ACTION_IS_STATIONARY;
02634     }
02635 
02636     // Do it.
02637     //
02638     int Realm_Do = DoThingToThingVisibility(looker, lookee, mode);
02639     bool bResult = false;
02640     if ((Realm_Do & REALM_DO_MASK) != REALM_DO_HIDDEN_FROM_YOU)
02641     {
02642 #ifdef REALITY_LVLS
02643         bResult = (!Dark(lookee) && IsReal(looker, lookee));
02644 #else
02645         bResult = !Dark(lookee);
02646 #endif /* REALITY_LVLS */
02647     }
02648     safe_bool(bResult, buff, bufc);
02649 }
02650 #endif
02651 
02652 /*
02653  * ---------------------------------------------------------------------------
02654  * * fun_lcon: Return a list of contents.
02655  */
02656 
02657 static FUNCTION(fun_lcon)
02658 {
02659     UNUSED_PARAMETER(caller);
02660     UNUSED_PARAMETER(nfargs);
02661     UNUSED_PARAMETER(cargs);
02662     UNUSED_PARAMETER(ncargs);
02663 
02664     dbref it = match_thing_quiet(executor, fargs[0]);
02665     if (!Good_obj(it))
02666     {
02667         safe_match_result(it, buff, bufc);
02668         return;
02669     }
02670     if (!Has_contents(it))
02671     {
02672         safe_nothing(buff, bufc);
02673         return;
02674     }
02675     if (  Examinable(executor, it)
02676        || Location(executor) == it
02677        || it == enactor)
02678     {
02679         dbref thing;
02680         ITL pContext;
02681         ItemToList_Init(&pContext, buff, bufc, '#');
02682         DOLIST(thing, Contents(it))
02683         {
02684 #ifdef WOD_REALMS
02685             int iRealmAction = DoThingToThingVisibility(executor, thing,
02686                 ACTION_IS_STATIONARY);
02687             if (iRealmAction != REALM_DO_HIDDEN_FROM_YOU)
02688             {
02689 #endif
02690                 if (  Can_Hide(thing)
02691                    && Hidden(thing)
02692                    && !See_Hidden(executor))
02693                 {
02694                     continue;
02695                 }
02696 
02697                 if (!ItemToList_AddInteger(&pContext, thing))
02698                 {
02699                     break;
02700                 }
02701 #ifdef WOD_REALMS
02702             }
02703 #endif
02704         }
02705         ItemToList_Final(&pContext);
02706     }
02707     else
02708     {
02709         safe_noperm(buff, bufc);
02710     }
02711 }
02712 
02713 /*
02714  * ---------------------------------------------------------------------------
02715  * * fun_lexits: Return a list of exits.
02716  */
02717 
02718 static FUNCTION(fun_lexits)
02719 {
02720     UNUSED_PARAMETER(caller);
02721     UNUSED_PARAMETER(nfargs);
02722     UNUSED_PARAMETER(cargs);
02723     UNUSED_PARAMETER(ncargs);
02724 
02725     dbref it = match_thing_quiet(executor, fargs[0]);
02726     if (!Good_obj(it))
02727     {
02728         safe_match_result(it, buff, bufc);
02729         return;
02730     }
02731     if (!Has_exits(it))
02732     {
02733         safe_nothing(buff, bufc);
02734         return;
02735     }
02736     bool bExam = Examinable(executor, it);
02737     if (  !bExam
02738        && where_is(executor) != it
02739        && it != enactor)
02740     {
02741         safe_noperm(buff, bufc);
02742         return;
02743     }
02744 
02745     // Return info for all parent levels.
02746     //
02747     bool bDone = false;
02748     dbref parent;
02749     int lev;
02750     ITL pContext;
02751     ItemToList_Init(&pContext, buff, bufc, '#');
02752     ITER_PARENTS(it, parent, lev)
02753     {
02754         // Look for exits at each level.
02755         //
02756         if (!Has_exits(parent))
02757         {
02758             continue;
02759         }
02760         int key = 0;
02761         if (Examinable(executor, parent))
02762         {
02763             key |= VE_LOC_XAM;
02764         }
02765         if (Dark(parent))
02766         {
02767             key |= VE_LOC_DARK;
02768         }
02769         if (Dark(it))
02770         {
02771             key |= VE_BASE_DARK;
02772         }
02773 
02774         dbref thing;
02775         DOLIST(thing, Exits(parent))
02776         {
02777             if (  exit_visible(thing, executor, key)
02778                && !ItemToList_AddInteger(&pContext, thing))
02779             {
02780                 bDone = true;
02781                 break;
02782             }
02783         }
02784         if (bDone)
02785         {
02786             break;
02787         }
02788     }
02789     ItemToList_Final(&pContext);
02790 }
02791 
02792 // ---------------------------------------------------------------------------
02793 // fun_entrances: Search database for entrances (inverse of exits).
02794 //
02795 FUNCTION(fun_entrances)
02796 {
02797     UNUSED_PARAMETER(caller);
02798     UNUSED_PARAMETER(enactor);
02799     UNUSED_PARAMETER(cargs);
02800     UNUSED_PARAMETER(ncargs);
02801 
02802     char *p;
02803     dbref i;
02804 
02805     dbref low_bound = 0;
02806     if (3 <= nfargs)
02807     {
02808         p = fargs[2];
02809         if (NUMBER_TOKEN == p[0])
02810         {
02811             p++;
02812         }
02813         i = mux_atol(p);
02814         if (Good_dbref(i))
02815         {
02816             low_bound = i;
02817         }
02818     }
02819 
02820     dbref high_bound = mudstate.db_top - 1;
02821     if (4 == nfargs)
02822     {
02823         p = fargs[3];
02824         if (NUMBER_TOKEN == p[0])
02825         {
02826             p++;
02827         }
02828         i = mux_atol(p);
02829         if (Good_dbref(i))
02830         {
02831             high_bound = i;
02832         }
02833     }
02834 
02835     bool find_ex = false;
02836     bool find_th = false;
02837     bool find_pl = false;
02838     bool find_rm = false;
02839 
02840     if (2 <= nfargs)
02841     {
02842         for (p = fargs[1]; *p; p++)
02843         {
02844             switch(mux_toupper(*p))
02845             {
02846             case 'A':
02847                 find_ex = find_th = find_pl = find_rm = true;
02848                 break;
02849 
02850             case 'E':
02851                 find_ex = true;
02852                 break;
02853 
02854             case 'T':
02855                 find_th = true;
02856                 break;
02857 
02858             case 'P':
02859                 find_pl = true;
02860                 break;
02861 
02862             case 'R':
02863                 find_rm = true;
02864                 break;
02865 
02866             default:
02867                 safe_str("#-1 INVALID TYPE", buff, bufc);
02868                 return;
02869             }
02870         }
02871     }
02872 
02873     if (!(find_ex || find_th || find_pl || find_rm))
02874     {
02875         find_ex = find_th = find_pl = find_rm = true;
02876     }
02877 
02878     dbref thing;
02879     if (  nfargs == 0
02880        || *fargs[0] == '\0')
02881     {
02882         if (Has_location(executor))
02883         {
02884             thing = Location(executor);
02885         }
02886         else
02887         {
02888             thing = executor;
02889         }
02890         if (!Good_obj(thing))
02891         {
02892             safe_nothing(buff, bufc);
02893             return;
02894         }
02895     }
02896     else
02897     {
02898         init_match(executor, fargs[0], NOTYPE);
02899         match_everything(MAT_EXIT_PARENTS);
02900         thing = noisy_match_result();
02901         if (!Good_obj(thing))
02902         {
02903             safe_nothing(buff, bufc);
02904             return;
02905         }
02906     }
02907 
02908     if (!payfor(executor, mudconf.searchcost))
02909     {
02910         notify(executor, tprintf("You don't have enough %s.",
02911             mudconf.many_coins));
02912         safe_nothing(buff, bufc);
02913         return;
02914     }
02915 
02916     int control_thing = Examinable(executor, thing);
02917     ITL itl;
02918     ItemToList_Init(&itl, buff, bufc, '#');
02919     for (i = low_bound; i <= high_bound; i++)
02920     {
02921         if (  control_thing
02922            || Examinable(executor, i))
02923         {
02924             if (  (  find_ex
02925                   && isExit(i)
02926                   && Location(i) == thing)
02927                || (  find_rm
02928                   && isRoom(i)
02929                   && Dropto(i) == thing)
02930                || (  find_th
02931                   && isThing(i)
02932                   && Home(i) == thing)
02933                || (  find_pl
02934                   && isPlayer(i)
02935                   && Home(i) == thing))
02936             {
02937                 if (!ItemToList_AddInteger(&itl, i))
02938                 {
02939                     break;
02940                 }
02941             }
02942         }
02943     }
02944     ItemToList_Final(&itl);
02945 }
02946 
02947 /*
02948  * --------------------------------------------------------------------------
02949  * * fun_home: Return an object's home
02950  */
02951 
02952 static FUNCTION(fun_home)
02953 {
02954     UNUSED_PARAMETER(caller);
02955     UNUSED_PARAMETER(enactor);
02956     UNUSED_PARAMETER(nfargs);
02957     UNUSED_PARAMETER(cargs);
02958     UNUSED_PARAMETER(ncargs);
02959 
02960     dbref it = match_thing_quiet(executor, fargs[0]);
02961     if (!Good_obj(it))
02962     {
02963         safe_match_result(it, buff, bufc);
02964     }
02965     else if (!Examinable(executor, it))
02966     {
02967         safe_noperm(buff, bufc);
02968     }
02969     else if (Has_home(it))
02970     {
02971         safe_tprintf_str(buff, bufc, "#%d", Home(it));
02972     }
02973     else if (Has_dropto(it))
02974     {
02975         safe_tprintf_str(buff, bufc, "#%d", Dropto(it));
02976     }
02977     else if (isExit(it))
02978     {
02979         safe_tprintf_str(buff, bufc, "#%d", where_is(it));
02980     }
02981     else
02982     {
02983         safe_nothing(buff, bufc);
02984     }
02985 }
02986 
02987 /*
02988  * ---------------------------------------------------------------------------
02989  * * fun_money: Return an object's value
02990  */
02991 
02992 static FUNCTION(fun_money)
02993 {
02994     UNUSED_PARAMETER(caller);
02995     UNUSED_PARAMETER(enactor);
02996     UNUSED_PARAMETER(nfargs);
02997     UNUSED_PARAMETER(cargs);
02998     UNUSED_PARAMETER(ncargs);
02999 
03000     dbref it = match_thing_quiet(executor, fargs[0]);
03001     if (!Good_obj(it))
03002     {
03003         safe_match_result(it, buff, bufc);
03004         return;
03005     }
03006     if (Examinable(executor, it))
03007     {
03008         safe_ltoa(Pennies(it), buff, bufc);
03009     }
03010     else
03011     {
03012         safe_noperm(buff, bufc);
03013     }
03014 }
03015 
03016 /*
03017  * ---------------------------------------------------------------------------
03018  * * fun_pos: Find a word in a string
03019  */
03020 
03021 static FUNCTION(fun_pos)
03022 {
03023     UNUSED_PARAMETER(executor);
03024     UNUSED_PARAMETER(caller);
03025     UNUSED_PARAMETER(enactor);
03026     UNUSED_PARAMETER(nfargs);
03027     UNUSED_PARAMETER(cargs);
03028     UNUSED_PARAMETER(ncargs);
03029 
03030     // Strip ANSI from pattern and save.
03031     //
03032     // Note: We need to save it because the next call to strip_ansi()
03033     // will overwrite the prior result.  Also, we save the pattern
03034     // instead of the source because the the pattern will tend to be
03035     // smaller (i.e., on average, fewer bytes to move).
03036     //
03037     size_t nPat = 0;
03038     char aPatBuf[LBUF_SIZE];
03039     char *pPatStrip = strip_ansi(fargs[0], &nPat);
03040     memcpy(aPatBuf, pPatStrip, nPat);
03041 
03042     // Strip ANSI from source.
03043     //
03044     size_t nSrc;
03045     char *pSrc = strip_ansi(fargs[1], &nSrc);
03046 
03047     // Search for pattern string inside source string.
03048     //
03049     int i = -1;
03050     if (nPat == 1)
03051     {
03052         // We can optimize the single-character case.
03053         //
03054         char *p = strchr(pSrc, aPatBuf[0]);
03055         if (p)
03056         {
03057             i = p - pSrc + 1;
03058         }
03059     }
03060     else if (nPat > 1)
03061     {
03062         // We have a multi-byte pattern.
03063         //
03064         i = BMH_StringSearch(nPat, aPatBuf, nSrc, pSrc)+1;
03065     }
03066 
03067     if (i > 0)
03068     {
03069         safe_ltoa(i, buff, bufc);
03070     }
03071     else
03072     {
03073         safe_nothing(buff, bufc);
03074     }
03075 }
03076 
03077 /* ---------------------------------------------------------------------------
03078  * fun_lpos: Find all occurrences of a character in a string, and return
03079  * a space-separated list of the positions, starting at 0. i.e.,
03080  * lpos(a-bc-def-g,-) ==> 1 4 8
03081  */
03082 
03083 static FUNCTION(fun_lpos)
03084 {
03085     UNUSED_PARAMETER(executor);
03086     UNUSED_PARAMETER(caller);
03087     UNUSED_PARAMETER(enactor);
03088     UNUSED_PARAMETER(nfargs);
03089     UNUSED_PARAMETER(cargs);
03090     UNUSED_PARAMETER(ncargs);
03091 
03092     if (*fargs[0] == '\0')
03093     {
03094         return;
03095     }
03096 
03097     char c = *fargs[1];
03098     if (!c)
03099     {
03100         c = ' ';
03101     }
03102 
03103     int i;
03104     char *bb_p = *bufc;
03105     char *s = strip_ansi(fargs[0]);
03106     for (i = 0; *s; i++, s++)
03107     {
03108         if (*s == c)
03109         {
03110             if (*bufc != bb_p)
03111             {
03112                 safe_chr(' ', buff, bufc);
03113             }
03114             safe_ltoa(i, buff, bufc);
03115         }
03116     }
03117 }
03118 
03119 /*
03120  * ---------------------------------------------------------------------------
03121  * * ldelete: Remove a word from a string by place
03122  * *  ldelete(<list>,<position>[,<separator>])
03123  * *
03124  * * insert: insert a word into a string by place
03125  * *  insert(<list>,<position>,<new item> [,<separator>])
03126  * *
03127  * * replace: replace a word into a string by place
03128  * *  replace(<list>,<position>,<new item>[,<separator>])
03129  */
03130 
03131 #define IF_DELETE   0
03132 #define IF_REPLACE  1
03133 #define IF_INSERT   2
03134 
03135 static void do_itemfuns(char *buff, char **bufc, char *str, int el,
03136                         char *word, SEP *psep, int flag)
03137 {
03138     int ct;
03139     char *sptr, *iptr, *eptr;
03140     int slen = 0, ilen = 0, elen = 0;
03141     bool overrun;
03142 
03143     // If passed a null string return an empty string, except that we
03144     // are allowed to append to a null string.
03145     //
03146     if (  (  !str
03147           || !*str)
03148        && (  flag != IF_INSERT
03149           || el != 1))
03150     {
03151         return;
03152     }
03153     int nStr = strlen(str);
03154 
03155     // We can't fiddle with anything before the first position.
03156     //
03157     if (el < 1)
03158     {
03159         safe_copy_buf(str, nStr, buff, bufc);
03160         return;
03161     }
03162 
03163     // Split the list up into 'before', 'target', and 'after' chunks
03164     // pointed to by sptr, iptr, and eptr respectively.
03165     //
03166     if (el == 1)
03167     {
03168         // No 'before' portion, just split off element 1
03169         //
03170         sptr = NULL;
03171         slen = 0;
03172         if (!str || !*str)
03173         {
03174             eptr = NULL;
03175             iptr = NULL;
03176         }
03177         else
03178         {
03179             eptr = trim_space_sep_LEN(str, nStr, psep, &elen);
03180             iptr = split_token_LEN(&eptr, &elen, psep, &ilen);
03181         }
03182     }
03183     else
03184     {
03185         // Break off 'before' portion.
03186         //
03187         sptr = eptr = trim_space_sep_LEN(str, nStr, psep, &elen);
03188         overrun = true;
03189         for (  ct = el;
03190                ct > 2 && eptr;
03191                eptr = next_token_LEN(eptr, &elen, psep), ct--)
03192         {
03193             // Nothing
03194         }
03195         if (eptr)
03196         {
03197             // Note: We are using (iptr,ilen) temporarily. It
03198             // doesn't represent the 'target' word, but the
03199             // the last token in the 'before' portion.
03200             //
03201             overrun = false;
03202             iptr = split_token_LEN(&eptr, &elen, psep, &ilen);
03203             slen = (iptr - sptr) + ilen;
03204         }
03205 
03206         // If we didn't make it to the target element, just return
03207         // the string. Insert is allowed to continue if we are exactly
03208         // at the end of the string, but replace and delete are not.
03209         //
03210         if (!(  eptr
03211              || (  flag == IF_INSERT
03212                 && !overrun)))
03213         {
03214             safe_copy_buf(str, nStr, buff, bufc);
03215             return;
03216         }
03217 
03218         // Split the 'target' word from the 'after' portion.
03219         //
03220         if (eptr)
03221         {
03222             iptr = split_token_LEN(&eptr, &elen, psep, &ilen);
03223         }
03224         else
03225         {
03226             iptr = NULL;
03227             ilen = 0;
03228         }
03229     }
03230 
03231     switch (flag)
03232     {
03233     case IF_DELETE:
03234 
03235         // deletion
03236         //
03237         if (sptr)
03238         {
03239             safe_copy_buf(sptr, slen, buff, bufc);
03240             if (eptr)
03241             {
03242                 safe_chr(psep->str[0], buff, bufc);
03243             }
03244         }
03245         if (eptr)
03246         {
03247             safe_copy_buf(eptr, elen, buff, bufc);
03248         }
03249         break;
03250 
03251     case IF_REPLACE:
03252 
03253         // replacing.
03254         //
03255         if (sptr)
03256         {
03257             safe_copy_buf(sptr, slen, buff, bufc);
03258             safe_chr(psep->str[0], buff, bufc);
03259         }
03260         safe_str(word, buff, bufc);
03261         if (eptr)
03262         {
03263             safe_chr(psep->str[0], buff, bufc);
03264             safe_copy_buf(eptr, elen, buff, bufc);
03265         }
03266         break;
03267 
03268     case IF_INSERT:
03269 
03270         // Insertion.
03271         //
03272         if (sptr)
03273         {
03274             safe_copy_buf(sptr, slen, buff, bufc);
03275             safe_chr(psep->str[0], buff, bufc);
03276         }
03277         safe_str(word, buff, bufc);
03278         if (iptr)
03279         {
03280             safe_chr(psep->str[0], buff, bufc);
03281             safe_copy_buf(iptr, ilen, buff, bufc);
03282         }
03283         if (eptr)
03284         {
03285             safe_chr(psep->str[0], buff, bufc);
03286             safe_copy_buf(eptr, elen, buff, bufc);
03287         }
03288         break;
03289     }
03290 }
03291 
03292 
03293 static FUNCTION(fun_ldelete)
03294 {
03295     SEP sep;
03296     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
03297     {
03298         return;
03299     }
03300 
03301     // Delete a word at position X of a list.
03302     //
03303     do_itemfuns(buff, bufc, fargs[0], mux_atol(fargs[1]), NULL, &sep, IF_DELETE);
03304 }
03305 
03306 static FUNCTION(fun_replace)
03307 {
03308     SEP sep;
03309     if (!OPTIONAL_DELIM(4, sep, DELIM_DFLT|DELIM_STRING))
03310     {
03311         return;
03312     }
03313 
03314     // Replace a word at position X of a list.
03315     //
03316     do_itemfuns(buff, bufc, fargs[0], mux_atol(fargs[1]), fargs[2], &sep, IF_REPLACE);
03317 }
03318 
03319 static FUNCTION(fun_insert)
03320 {
03321     SEP sep;
03322     if (!OPTIONAL_DELIM(4, sep, DELIM_DFLT|DELIM_STRING))
03323     {
03324         return;
03325     }
03326 
03327     // Insert a word at position X of a list.
03328     //
03329     do_itemfuns(buff, bufc, fargs[0], mux_atol(fargs[1]), fargs[2], &sep, IF_INSERT);
03330 }
03331 
03332 /*
03333  * ---------------------------------------------------------------------------
03334  * * fun_remove: Remove a word from a string
03335  */
03336 
03337 static FUNCTION(fun_remove)
03338 {
03339     SEP sep;
03340     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
03341     {
03342         return;
03343     }
03344     SEP osep = sep;
03345     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
03346     {
03347         return;
03348     }
03349 
03350     char *s, *sp, *word;
03351     bool first, found;
03352 
03353     if (strstr(fargs[1], sep.str))
03354     {
03355         safe_str("#-1 CAN ONLY REMOVE ONE ELEMENT", buff, bufc);
03356         return;
03357     }
03358     s = fargs[0];
03359     word = fargs[1];
03360 
03361     // Walk through the string copying words until (if ever) we get to
03362     // one that matches the target word.
03363     //
03364     sp = s;
03365     found = false;
03366     first = true;
03367     while (s)
03368     {
03369         sp = split_token(&s, &sep);
03370         if (  found
03371            || strcmp(sp, word) != 0)
03372         {
03373             if (!first)
03374             {
03375                 print_sep(&osep, buff, bufc);
03376             }
03377             else
03378             {
03379                 first = false;
03380             }
03381             safe_str(sp, buff, bufc);
03382         }
03383         else
03384         {
03385             found = true;
03386         }
03387     }
03388 }
03389 
03390 /*
03391  * ---------------------------------------------------------------------------
03392  * * fun_member: Is a word in a string
03393  */
03394 
03395 static FUNCTION(fun_member)
03396 {
03397     SEP sep;
03398     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
03399     {
03400         return;
03401     }
03402 
03403     int wcount;
03404     char *r, *s;
03405 
03406     wcount = 1;
03407     s = trim_space_sep(fargs[0], &sep);
03408     do
03409     {
03410         r = split_token(&s, &sep);
03411         if (!strcmp(fargs[1], r))
03412         {
03413             safe_ltoa(wcount, buff, bufc);
03414             return;
03415         }
03416         wcount++;
03417     } while (s);
03418     safe_chr('0', buff, bufc);
03419 }
03420 
03421 // fun_secure: This function replaces any character in the set
03422 // '%$\[](){},;' with a space. It handles ANSI by not replacing
03423 // the '[' character within an ANSI sequence.
03424 //
03425 static FUNCTION(fun_secure)
03426 {
03427     UNUSED_PARAMETER(executor);
03428     UNUSED_PARAMETER(caller);
03429     UNUSED_PARAMETER(enactor);
03430     UNUSED_PARAMETER(nfargs);
03431     UNUSED_PARAMETER(cargs);
03432     UNUSED_PARAMETER(ncargs);
03433 
03434     char *pString = fargs[0];
03435     int nString = strlen(pString);
03436 
03437     while (nString)
03438     {
03439         int nTokenLength0;
03440         int nTokenLength1;
03441         int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1);
03442 
03443         if (iType == TOKEN_TEXT_ANSI)
03444         {
03445             // Process TEXT portion (pString, nTokenLength0).
03446             //
03447             nString -= nTokenLength0;
03448             while (nTokenLength0--)
03449             {
03450                 if (mux_issecure(*pString))
03451                 {
03452                     safe_chr(' ', buff, bufc);
03453                 }
03454                 else
03455                 {
03456                     safe_chr(*pString, buff, bufc);
03457                 }
03458                 pString++;
03459             }
03460             nTokenLength0 = nTokenLength1;
03461         }
03462 
03463         if (nTokenLength0)
03464         {
03465             // Process ANSI portion (pString, nTokenLength0).
03466             //
03467             safe_copy_buf(pString, nTokenLength0, buff, bufc);
03468             pString += nTokenLength0;
03469             nString -= nTokenLength0;
03470         }
03471     }
03472 }
03473 
03474 // fun_escape: This function prepends a '\' to the beginning of a
03475 // string and before any character which occurs in the set '%\[]{};,()^$'.
03476 // It handles ANSI by not treating the '[' character within an ANSI
03477 // sequence as a special character.
03478 //
03479 static FUNCTION(fun_escape)
03480 {
03481     UNUSED_PARAMETER(executor);
03482     UNUSED_PARAMETER(caller);
03483     UNUSED_PARAMETER(enactor);
03484     UNUSED_PARAMETER(fargs);
03485     UNUSED_PARAMETER(nfargs);
03486     UNUSED_PARAMETER(cargs);
03487     UNUSED_PARAMETER(ncargs);
03488 
03489     char *pString = fargs[0];
03490     int nString = strlen(pString);
03491 
03492     while (nString)
03493     {
03494         int nTokenLength0;
03495         int nTokenLength1;
03496         int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1);
03497 
03498         if (iType == TOKEN_TEXT_ANSI)
03499         {
03500             // Process TEXT portion (pString, nTokenLength0).
03501             //
03502             nString -= nTokenLength0;
03503             while (nTokenLength0--)
03504             {
03505                 if (  mux_isescape(*pString)
03506                    || pString == fargs[0])
03507                 {
03508                     safe_chr('\\', buff, bufc);
03509                 }
03510                 safe_chr(*pString, buff, bufc);
03511                 pString++;
03512             }
03513             nTokenLength0 = nTokenLength1;
03514         }
03515 
03516         if (nTokenLength0)
03517         {
03518             // Process ANSI portion (pString, nTokenLength0).
03519             //
03520             safe_copy_buf(pString, nTokenLength0, buff, bufc);
03521             pString += nTokenLength0;
03522             nString -= nTokenLength0;
03523         }
03524     }
03525 }
03526 
03527 /*
03528  * Take a character position and return which word that char is in.
03529  * * wordpos(<string>, <charpos>)
03530  */
03531 static FUNCTION(fun_wordpos)
03532 {
03533     SEP sep;
03534     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
03535     {
03536         return;
03537     }
03538 
03539     unsigned int charpos = mux_atol(fargs[1]);
03540     char *cp = fargs[0];
03541     size_t ncp = strlen(cp);
03542     if (  charpos > 0
03543        && charpos <= ncp)
03544     {
03545         int ncp_trimmed;
03546         char *tp = &(cp[charpos - 1]);
03547         cp = trim_space_sep_LEN(cp, ncp, &sep, &ncp_trimmed);
03548         char *xp = split_token(&cp, &sep);
03549 
03550         int i;
03551         for (i = 1; xp; i++)
03552         {
03553             if (tp < xp + strlen(xp))
03554             {
03555                 break;
03556             }
03557             xp = split_token(&cp, &sep);
03558         }
03559         safe_ltoa(i, buff, bufc);
03560         return;
03561     }
03562     safe_nothing(buff, bufc);
03563 }
03564 
03565 static FUNCTION(fun_type)
03566 {
03567     UNUSED_PARAMETER(caller);
03568     UNUSED_PARAMETER(enactor);
03569     UNUSED_PARAMETER(nfargs);
03570     UNUSED_PARAMETER(cargs);
03571     UNUSED_PARAMETER(ncargs);
03572 
03573     dbref it = match_thing_quiet(executor, fargs[0]);
03574     if (!Good_obj(it))
03575     {
03576         safe_match_result(it, buff, bufc);
03577         return;
03578     }
03579     switch (Typeof(it))
03580     {
03581     case TYPE_ROOM:
03582         safe_str("ROOM", buff, bufc);
03583         break;
03584     case TYPE_EXIT:
03585         safe_str("EXIT", buff, bufc);
03586         break;
03587     case TYPE_PLAYER:
03588         safe_str("PLAYER", buff, bufc);
03589         break;
03590     case TYPE_THING:
03591         safe_str("THING", buff, bufc);
03592         break;
03593     default:
03594         safe_str("#-1 ILLEGAL TYPE", buff, bufc);
03595     }
03596 }
03597 
03598 typedef struct
03599 {
03600     const char *pName;
03601     int  iMask;
03602 } ATR_HAS_FLAG_ENTRY;
03603 
03604 static ATR_HAS_FLAG_ENTRY atr_has_flag_table[] =
03605 {
03606     { "dark",       AF_DARK    },
03607     { "wizard",     AF_WIZARD  },
03608     { "hidden",     AF_MDARK   },
03609     { "html",       AF_HTML    },
03610     { "locked",     AF_LOCK    },
03611     { "no_command", AF_NOPROG  },
03612     { "no_parse",   AF_NOPARSE },
03613     { "regexp",     AF_REGEXP  },
03614     { "god",        AF_GOD     },
03615     { "visual",     AF_VISUAL  },
03616     { "no_inherit", AF_PRIVATE },
03617     { "const",      AF_CONST   },
03618     { NULL,         0          }
03619 };
03620 
03621 static bool atr_has_flag
03622 (
03623     dbref player,
03624     dbref thing,
03625     ATTR* pattr,
03626     dbref aowner,
03627     int   aflags,
03628     const char *flagname
03629 )
03630 {
03631     UNUSED_PARAMETER(aowner);
03632 
03633     if (See_attr(player, thing, pattr))
03634     {
03635         ATR_HAS_FLAG_ENTRY *pEntry = atr_has_flag_table;
03636         while (pEntry->pName)
03637         {
03638             if (string_prefix(pEntry->pName, flagname))
03639             {
03640                 return ((aflags & (pEntry->iMask)) ? true : false);
03641             }
03642             pEntry++;
03643         }
03644     }
03645     return false;
03646 }
03647 
03648 static FUNCTION(fun_hasflag)
03649 {
03650     UNUSED_PARAMETER(caller);
03651     UNUSED_PARAMETER(nfargs);
03652     UNUSED_PARAMETER(cargs);
03653     UNUSED_PARAMETER(ncargs);
03654 
03655     dbref it;
03656     ATTR *pattr;
03657 
03658     if (parse_attrib(executor, fargs[0], &it, &pattr))
03659     {
03660         if (  !pattr
03661            || !See_attr(executor, it, pattr))
03662         {
03663             safe_notfound(buff, bufc);
03664         }
03665         else
03666         {
03667             int aflags;
03668             dbref aowner;
03669             atr_pget_info(it, pattr->number, &aowner, &aflags);
03670             bool cc = atr_has_flag(executor, it, pattr, aowner, aflags, fargs[1]);
03671             safe_bool(cc, buff, bufc);
03672         }
03673     }
03674     else
03675     {
03676         it = match_thing_quiet(executor, fargs[0]);
03677         if (!Good_obj(it))
03678         {
03679             safe_match_result(it, buff, bufc);
03680         }
03681         else if (  mudconf.pub_flags
03682                 || Examinable(executor, it)
03683                 || it == enactor)
03684         {
03685             bool cc = has_flag(executor, it, fargs[1]);
03686             safe_bool(cc, buff, bufc);
03687         }
03688         else
03689         {
03690             safe_noperm(buff, bufc);
03691         }
03692     }
03693 }
03694 
03695 static FUNCTION(fun_haspower)
03696 {
03697     UNUSED_PARAMETER(caller);
03698     UNUSED_PARAMETER(nfargs);
03699     UNUSED_PARAMETER(cargs);
03700     UNUSED_PARAMETER(ncargs);
03701 
03702     dbref it = match_thing_quiet(executor, fargs[0]);
03703     if (!Good_obj(it))
03704     {
03705         safe_match_result(it, buff, bufc);
03706         return;
03707     }
03708     if (  mudconf.pub_flags
03709        || Examinable(executor, it)
03710        || it == enactor)
03711     {
03712         safe_bool(has_power(executor, it, fargs[1]), buff, bufc);
03713     }
03714     else
03715     {
03716         safe_noperm(buff, bufc);
03717     }
03718 }
03719 
03720 #ifdef REALITY_LVLS
03721 static FUNCTION(fun_hasrxlevel)
03722 {
03723     dbref it;
03724     RLEVEL rl;
03725 
03726     it = match_thing(executor, fargs[0]);
03727     if (!Good_obj(it))
03728     {
03729         safe_str("#-1 NOT FOUND", buff, bufc);
03730         return;
03731     }
03732     rl = find_rlevel(fargs[1]);
03733     if (!rl)
03734     {
03735         safe_str("#-1 INVALID RLEVEL", buff, bufc);
03736         return;
03737     }
03738     if (Examinable(executor, it))
03739     {
03740         if ((RxLevel(it) & rl) == rl)
03741         {
03742             safe_chr('1', buff, bufc);
03743         }
03744         else
03745         {
03746             safe_chr('0', buff, bufc);
03747         }
03748    }
03749    else
03750    {
03751         safe_str("#-1 PERMISSION DENIED", buff, bufc);
03752    }
03753 }
03754 
03755 static FUNCTION(fun_hastxlevel)
03756 {
03757     dbref it;
03758     RLEVEL rl;
03759 
03760     it = match_thing(executor, fargs[0]);
03761     if (!Good_obj(it))
03762     {
03763         safe_str("#-1 NOT FOUND", buff, bufc);
03764         return;
03765     }
03766     rl = find_rlevel(fargs[1]);
03767     if (!rl)
03768     {
03769         safe_str("#-1 INVALID RLEVEL", buff, bufc);
03770         return;
03771     }
03772     if (Examinable(executor, it))
03773     {
03774         if ((TxLevel(it) & rl) == rl)
03775         {
03776             safe_chr('1', buff, bufc);
03777         }
03778         else
03779         {
03780              safe_chr('0', buff, bufc);
03781         }
03782    }
03783    else
03784    {
03785         safe_str("#-1 PERMISSION DENIED", buff, bufc);
03786    }
03787 }
03788 
03789 static FUNCTION(fun_listrlevels)
03790 {
03791    int i, add_space, cmp_x, cmp_y, cmp_z;
03792 
03793    cmp_x = sizeof(mudconf.reality_level);
03794    cmp_y = sizeof(mudconf.reality_level[0]);
03795    if ( cmp_y == 0 )
03796       cmp_z = 0;
03797    else
03798       cmp_z = cmp_x / cmp_y;
03799    if ( mudconf.no_levels < 1 ) {
03800       safe_str("#-1 NO REALITY LEVELS DEFINED", buff, bufc);
03801    } else {
03802       for (add_space = i = 0; (i < mudconf.no_levels) && (i < cmp_z); ++i) {
03803          if(add_space)
03804             safe_chr(' ', buff, bufc);
03805          safe_str(mudconf.reality_level[i].name, buff, bufc);
03806          add_space = 1;
03807       }
03808    }
03809 }
03810 
03811 static FUNCTION(fun_rxlevel)
03812 {
03813     dbref it;
03814     char levelbuff[2048];
03815     int i;
03816     RLEVEL lev;
03817 
03818     it = match_thing(executor, fargs[0]);
03819     if (!Good_obj(it))
03820     {
03821         safe_str("#-1 NOT FOUND", buff, bufc);
03822         return;
03823     }
03824     if (Examinable(executor, it))
03825     {
03826         lev = RxLevel(it);
03827         levelbuff[0]='\0';
03828         for(i = 0; i < mudconf.no_levels; ++i)
03829             if((lev & mudconf.reality_level[i].value) == mudconf.reality_level[i].value)
03830             {
03831                 strcat(levelbuff, mudconf.reality_level[i].name);
03832                 strcat(levelbuff, " ");
03833             }
03834         safe_tprintf_str(buff, bufc, "%s", levelbuff);
03835     }
03836     else
03837         safe_str("#-1 PERMISSION DENIED", buff, bufc);
03838 }
03839 
03840 static FUNCTION(fun_txlevel)
03841 {
03842     dbref it;
03843     char levelbuff[2048];
03844     int i;
03845     RLEVEL lev;
03846 
03847     it = match_thing(executor, fargs[0]);
03848     if (!Good_obj(it))
03849     {
03850         safe_str("#-1 NOT FOUND", buff, bufc);
03851         return;
03852     }
03853     if (Examinable(executor, it))
03854     {
03855         lev = TxLevel(it);
03856         levelbuff[0]='\0';
03857         for(i = 0; i < mudconf.no_levels; ++i)
03858             if((lev & mudconf.reality_level[i].value) == mudconf.reality_level[i].value)
03859             {
03860                 strcat(levelbuff, mudconf.reality_level[i].name);
03861                 strcat(levelbuff, " ");
03862             }
03863         safe_tprintf_str(buff, bufc, "%s", levelbuff);
03864     }
03865     else
03866         safe_str("#-1 PERMISSION DENIED", buff, bufc);
03867 }
03868 #endif /* REALITY_LVLS */
03869 
03870 static FUNCTION(fun_powers)
03871 {
03872     UNUSED_PARAMETER(caller);
03873     UNUSED_PARAMETER(nfargs);
03874     UNUSED_PARAMETER(cargs);
03875     UNUSED_PARAMETER(ncargs);
03876 
03877     dbref it = match_thing_quiet(executor, fargs[0]);
03878     if (!Good_obj(it))
03879     {
03880         safe_match_result(it, buff, bufc);
03881         return;
03882     }
03883     if (  mudconf.pub_flags
03884        || Examinable(executor, it)
03885        || it == enactor)
03886     {
03887         char *buf = powers_list(executor, it);
03888         safe_str(buf, buff, bufc);
03889         free_lbuf(buf);
03890     }
03891     else
03892     {
03893         safe_noperm(buff, bufc);
03894     }
03895 }
03896 
03897 static FUNCTION(fun_delete)
03898 {
03899     UNUSED_PARAMETER(executor);
03900     UNUSED_PARAMETER(caller);
03901     UNUSED_PARAMETER(enactor);
03902     UNUSED_PARAMETER(nfargs);
03903     UNUSED_PARAMETER(cargs);
03904     UNUSED_PARAMETER(ncargs);
03905 
03906     char *s = fargs[0];
03907     int iStart = mux_atol(fargs[1]);
03908     int nChars = mux_atol(fargs[2]);
03909     int nLen = strlen(s);
03910 
03911     int iEnd;
03912     if (0 <= nChars)
03913     {
03914         iEnd = iStart + nChars;
03915     }
03916     else
03917     {
03918         iEnd = iStart;
03919         iStart = iEnd + nChars;
03920     }
03921 
03922     // Are we deleting anything at all?
03923     //
03924     if (  iEnd <= 0
03925        || nLen <= iStart)
03926     {
03927         if (nLen)
03928         {
03929             safe_copy_buf(s, nLen, buff, bufc);
03930         }
03931         return;
03932     }
03933 
03934     if (iStart < 0) iStart = 0;
03935     if (nLen < iEnd) iEnd = nLen;
03936 
03937     // ASSERT: Now [iStart,iEnd) exist somewhere within the the string
03938     // [s,nLen).
03939     //
03940     if (iStart)
03941     {
03942         safe_copy_buf(s, iStart, buff, bufc);
03943     }
03944     if (iEnd < nLen)
03945     {
03946         safe_copy_buf(s + iEnd, nLen - iEnd, buff, bufc);
03947     }
03948 }
03949 
03950 static FUNCTION(fun_lock)
03951 {
03952     UNUSED_PARAMETER(caller);
03953     UNUSED_PARAMETER(enactor);
03954     UNUSED_PARAMETER(nfargs);
03955     UNUSED_PARAMETER(cargs);
03956     UNUSED_PARAMETER(ncargs);
03957 
03958     dbref it, aowner;
03959     int aflags;
03960     ATTR *pattr;
03961     struct boolexp *pBoolExp;
03962 
03963     // Parse the argument into obj + lock
03964     //
03965     if (!get_obj_and_lock(executor, fargs[0], &it, &pattr, buff, bufc))
03966     {
03967         return;
03968     }
03969 
03970     // Get the attribute and decode it if we can read it
03971     //
03972     char *tbuf = atr_get(it, pattr->number, &aowner, &aflags);
03973     if (bCanReadAttr(executor, it, pattr, false))
03974     {
03975         pBoolExp = parse_boolexp(executor, tbuf, true);
03976         free_lbuf(tbuf);
03977         tbuf = unparse_boolexp_function(executor, pBoolExp);
03978         free_boolexp(pBoolExp);
03979         safe_str(tbuf, buff, bufc);
03980     }
03981     else
03982     {
03983         free_lbuf(tbuf);
03984     }
03985 }
03986 
03987 static FUNCTION(fun_elock)
03988 {
03989     UNUSED_PARAMETER(caller);
03990     UNUSED_PARAMETER(nfargs);
03991     UNUSED_PARAMETER(cargs);
03992     UNUSED_PARAMETER(ncargs);
03993 
03994     dbref it, aowner;
03995     int aflags;
03996     ATTR *pattr;
03997     struct boolexp *pBoolExp;
03998 
03999     // Parse lock supplier into obj + lock.
04000     //
04001     if (!get_obj_and_lock(executor, fargs[0], &it, &pattr, buff, bufc))
04002     {
04003         return;
04004     }
04005     else if (!locatable(executor, it, enactor))
04006     {
04007         safe_nothing(buff, bufc);
04008     }
04009 
04010     // Get the victim and ensure we can do it.
04011     //
04012     dbref victim = match_thing_quiet(executor, fargs[1]);
04013     if (!Good_obj(victim))
04014     {
04015         safe_match_result(victim, buff, bufc);
04016     }
04017     else if (!locatable(executor, victim, enactor))
04018     {
04019         safe_nothing(buff, bufc);
04020     }
04021     else if (  nearby_or_control(executor, victim)
04022             || nearby_or_control(executor, it))
04023     {
04024         char *tbuf = atr_get(it, pattr->number, &aowner, &aflags);
04025         if (  pattr->number == A_LOCK
04026            || bCanReadAttr(executor, it, pattr, false))
04027         {
04028             pBoolExp = parse_boolexp(executor, tbuf, true);
04029             safe_bool(eval_boolexp(victim, it, it, pBoolExp), buff, bufc);
04030             free_boolexp(pBoolExp);
04031         }
04032         else
04033         {
04034             safe_chr('0', buff, bufc);
04035         }
04036         free_lbuf(tbuf);
04037     }
04038     else
04039     {
04040         safe_str("#-1 TOO FAR AWAY", buff, bufc);
04041     }
04042 }
04043 
04044 /* ---------------------------------------------------------------------------
04045  * fun_lwho: Return list of connected users.
04046  */
04047 
04048 static FUNCTION(fun_lwho)
04049 {
04050     UNUSED_PARAMETER(caller);
04051     UNUSED_PARAMETER(enactor);
04052     UNUSED_PARAMETER(cargs);
04053     UNUSED_PARAMETER(ncargs);
04054 
04055     bool bPorts = false;
04056     if (nfargs == 1)
04057     {
04058         bPorts = xlate(fargs[0]);
04059         if (  bPorts
04060            && !Wizard(executor))
04061         {
04062             safe_noperm(buff, bufc);
04063             return;
04064         }
04065     }
04066     make_ulist(executor, buff, bufc, bPorts);
04067 }
04068 
04069 // ---------------------------------------------------------------------------
04070 // fun_lports: Return list of ports of connected users.
04071 // ---------------------------------------------------------------------------
04072 
04073 static FUNCTION(fun_lports)
04074 {
04075     UNUSED_PARAMETER(caller);
04076     UNUSED_PARAMETER(enactor);
04077     UNUSED_PARAMETER(fargs);
04078     UNUSED_PARAMETER(nfargs);
04079     UNUSED_PARAMETER(cargs);
04080     UNUSED_PARAMETER(ncargs);
04081 
04082     make_port_ulist(executor, buff, bufc);
04083 }
04084 
04085 /* ---------------------------------------------------------------------------
04086  * fun_nearby: Return whether or not obj1 is near obj2.
04087  */
04088 
04089 static FUNCTION(fun_nearby)
04090 {
04091     UNUSED_PARAMETER(caller);
04092     UNUSED_PARAMETER(enactor);
04093     UNUSED_PARAMETER(nfargs);
04094     UNUSED_PARAMETER(cargs);
04095     UNUSED_PARAMETER(ncargs);
04096 
04097     dbref obj1 = match_thing_quiet(executor, fargs[0]);
04098     if (!Good_obj(obj1))
04099     {
04100         safe_match_result(obj1, buff, bufc);
04101         safe_str(" (ARG1)", buff, bufc);
04102         return;
04103     }
04104     dbref obj2 = match_thing_quiet(executor, fargs[1]);
04105     if (!Good_obj(obj2))
04106     {
04107         safe_match_result(obj2, buff, bufc);
04108         safe_str(" (ARG2)", buff, bufc);
04109         return;
04110     }
04111     bool bResult = (  (  nearby_or_control(executor, obj1)
04112                       || nearby_or_control(executor, obj2))
04113                       && nearby(obj1, obj2));
04114     safe_bool(bResult, buff, bufc);
04115 }
04116 
04117 /*
04118  * ---------------------------------------------------------------------------
04119  * * fun_obj, fun_poss, and fun_subj: perform pronoun sub for object.
04120  */
04121 
04122 static void process_sex(dbref player, char *what, const char *token, char *buff, char **bufc)
04123 {
04124     dbref it = match_thing_quiet(player, strip_ansi(what));
04125     if (!Good_obj(it))
04126     {
04127         safe_match_result(it, buff, bufc);
04128         return;
04129     }
04130     if (  !isPlayer(it)
04131        && !nearby_or_control(player, it))
04132     {
04133         safe_nomatch(buff, bufc);
04134     }
04135     else
04136     {
04137         char *str = (char *)token;
04138         mux_exec(buff, bufc, it, it, it, EV_EVAL, &str, (char **)NULL, 0);
04139     }
04140 }
04141 
04142 static FUNCTION(fun_obj)
04143 {
04144     UNUSED_PARAMETER(caller);
04145     UNUSED_PARAMETER(enactor);
04146     UNUSED_PARAMETER(nfargs);
04147     UNUSED_PARAMETER(cargs);
04148     UNUSED_PARAMETER(ncargs);
04149 
04150     process_sex(executor, fargs[0], "%o", buff, bufc);
04151 }
04152 
04153 static FUNCTION(fun_poss)
04154 {
04155     UNUSED_PARAMETER(caller);
04156     UNUSED_PARAMETER(enactor);
04157     UNUSED_PARAMETER(nfargs);
04158     UNUSED_PARAMETER(cargs);
04159     UNUSED_PARAMETER(ncargs);
04160 
04161     process_sex(executor, fargs[0], "%p", buff, bufc);
04162 }
04163 
04164 static FUNCTION(fun_subj)
04165 {
04166     UNUSED_PARAMETER(caller);
04167     UNUSED_PARAMETER(enactor);
04168     UNUSED_PARAMETER(nfargs);
04169     UNUSED_PARAMETER(cargs);
04170     UNUSED_PARAMETER(ncargs);
04171 
04172     process_sex(executor, fargs[0], "%s", buff, bufc);
04173 }
04174 
04175 static FUNCTION(fun_aposs)
04176 {
04177     UNUSED_PARAMETER(caller);
04178     UNUSED_PARAMETER(enactor);
04179     UNUSED_PARAMETER(nfargs);
04180     UNUSED_PARAMETER(cargs);
04181     UNUSED_PARAMETER(ncargs);
04182 
04183     process_sex(executor, fargs[0], "%a", buff, bufc);
04184 }
04185 
04186 /*
04187  * ---------------------------------------------------------------------------
04188  * * fun_mudname: Return the name of the mud.
04189  */
04190 
04191 static FUNCTION(fun_mudname)
04192 {
04193     UNUSED_PARAMETER(executor);
04194     UNUSED_PARAMETER(caller);
04195     UNUSED_PARAMETER(enactor);
04196     UNUSED_PARAMETER(fargs);
04197     UNUSED_PARAMETER(nfargs);
04198     UNUSED_PARAMETER(cargs);
04199     UNUSED_PARAMETER(ncargs);
04200 
04201     safe_str(mudconf.mud_name, buff, bufc);
04202 }
04203 
04204 // ---------------------------------------------------------------------------
04205 // fun_connrecord: Return the record number of connected players.
04206 // ---------------------------------------------------------------------------
04207 
04208 static FUNCTION(fun_connrecord)
04209 {
04210     UNUSED_PARAMETER(executor);
04211     UNUSED_PARAMETER(caller);
04212     UNUSED_PARAMETER(enactor);
04213     UNUSED_PARAMETER(fargs);
04214     UNUSED_PARAMETER(nfargs);
04215     UNUSED_PARAMETER(cargs);
04216     UNUSED_PARAMETER(ncargs);
04217 
04218     safe_ltoa(mudstate.record_players, buff, bufc);
04219 }
04220 
04221 // ---------------------------------------------------------------------------
04222 // fun_fcount: Return the current function invocation counter.
04223 // ---------------------------------------------------------------------------
04224 
04225 FUNCTION(fun_fcount)
04226 {
04227     UNUSED_PARAMETER(executor);
04228     UNUSED_PARAMETER(caller);
04229     UNUSED_PARAMETER(enactor);
04230     UNUSED_PARAMETER(fargs);
04231     UNUSED_PARAMETER(nfargs);
04232     UNUSED_PARAMETER(cargs);
04233     UNUSED_PARAMETER(ncargs);
04234 
04235     safe_ltoa(mudstate.func_invk_ctr, buff, bufc);
04236 }
04237 
04238 // ---------------------------------------------------------------------------
04239 // fun_fdepth: Return the current function nesting depth.
04240 // ---------------------------------------------------------------------------
04241 
04242 FUNCTION(fun_fdepth)
04243 {
04244     UNUSED_PARAMETER(executor);
04245     UNUSED_PARAMETER(caller);
04246     UNUSED_PARAMETER(enactor);
04247     UNUSED_PARAMETER(fargs);
04248     UNUSED_PARAMETER(nfargs);
04249     UNUSED_PARAMETER(cargs);
04250     UNUSED_PARAMETER(ncargs);
04251 
04252     safe_ltoa(mudstate.func_nest_lev, buff, bufc);
04253 }
04254 
04255 // ---------------------------------------------------------------------------
04256 // fun_ctime: Return the value of an object's CREATED attribute.
04257 // ---------------------------------------------------------------------------
04258 
04259 static FUNCTION(fun_ctime)
04260 {
04261     UNUSED_PARAMETER(caller);
04262     UNUSED_PARAMETER(enactor);
04263     UNUSED_PARAMETER(cargs);
04264     UNUSED_PARAMETER(ncargs);
04265 
04266     dbref thing;
04267     if (nfargs == 1)
04268     {
04269         thing = match_thing_quiet(executor, fargs[0]);
04270     }
04271     else
04272     {
04273         thing = executor;
04274     }
04275     if (!Good_obj(thing))
04276     {
04277         safe_match_result(thing, buff, bufc);
04278     }
04279     else if (Examinable(executor, thing))
04280     {
04281         safe_str(atr_get_raw(thing, A_CREATED), buff, bufc);
04282     }
04283     else
04284     {
04285         safe_noperm(buff, bufc);
04286     }
04287 }
04288 
04289 // ---------------------------------------------------------------------------
04290 // fun_mtime: Return the value of an object's Modified attribute.
04291 // ---------------------------------------------------------------------------
04292 
04293 static FUNCTION(fun_mtime)
04294 {
04295     UNUSED_PARAMETER(caller);
04296     UNUSED_PARAMETER(enactor);
04297     UNUSED_PARAMETER(cargs);
04298     UNUSED_PARAMETER(ncargs);
04299 
04300     dbref thing;
04301     if (nfargs == 1)
04302     {
04303         thing = match_thing_quiet(executor, fargs[0]);
04304     }
04305     else
04306     {
04307         thing = executor;
04308     }
04309     if (!Good_obj(thing))
04310     {
04311         safe_match_result(thing, buff, bufc);
04312     }
04313     else if (Examinable(executor, thing))
04314     {
04315         safe_str(atr_get_raw(thing, A_MODIFIED), buff, bufc);
04316     }
04317     else
04318     {
04319         safe_noperm(buff, bufc);
04320     }
04321 }
04322 
04323 // ---------------------------------------------------------------------------
04324 // fun_moniker: Return the value of an object's @moniker attribute.
04325 // ---------------------------------------------------------------------------
04326 static FUNCTION(fun_moniker)
04327 {
04328     UNUSED_PARAMETER(caller);
04329     UNUSED_PARAMETER(enactor);
04330     UNUSED_PARAMETER(cargs);
04331     UNUSED_PARAMETER(ncargs);
04332 
04333     dbref thing;
04334     if (nfargs == 1)
04335     {
04336         thing = match_thing_quiet(executor, fargs[0]);
04337     }
04338     else
04339     {
04340         thing = executor;
04341     }
04342     if (!Good_obj(thing))
04343     {
04344         safe_match_result(thing, buff, bufc);
04345         return;
04346     }
04347     safe_str(Moniker(thing), buff, bufc);
04348 }
04349 
04350 static void ANSI_TransformTextWithTable
04351 (
04352     char *buff,
04353     char **bufc,
04354     char *pString,
04355     const unsigned char xfrmTable[256])
04356 {
04357     int   nString = strlen(pString);
04358     char *pBuffer = *bufc;
04359     int   nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
04360     while (nString)
04361     {
04362         int nTokenLength0;
04363         int nTokenLength1;
04364         int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1);
04365 
04366         if (iType == TOKEN_TEXT_ANSI)
04367         {
04368             // Determine how much to move.
04369             //
04370             int nMove = nTokenLength0;
04371             if (nMove > nBufferAvailable)
04372             {
04373                 nMove = nBufferAvailable;
04374             }
04375             nBufferAvailable -= nMove;
04376 
04377             // Update pointers and counts.
04378             //
04379             char *p = pString;
04380             nString -= nTokenLength0;
04381             pString += nTokenLength0;
04382 
04383             // Transform and Move text.
04384             //
04385             while (nMove--)
04386             {
04387                 *pBuffer++ = xfrmTable[(unsigned char)*p++];
04388             }
04389 
04390             // Determine whether to move the ANSI part.
04391             //
04392             if (nTokenLength1)
04393             {
04394                 if (nTokenLength1 <= nBufferAvailable)
04395                 {
04396                     memcpy(pBuffer, pString, nTokenLength1);
04397                     pBuffer += nTokenLength1;
04398                     nBufferAvailable -= nTokenLength1;
04399                 }
04400                 nString -= nTokenLength1;
04401                 pString += nTokenLength1;
04402             }
04403         }
04404         else
04405         {
04406             // TOKEN_ANSI
04407             //
04408             // Determine whether to move the ANSI part.
04409             //
04410             if (nTokenLength0 <= nBufferAvailable)
04411             {
04412                 memcpy(pBuffer, pString, nTokenLength0);
04413                 pBuffer += nTokenLength0;
04414                 nBufferAvailable -= nTokenLength0;
04415             }
04416             nString -= nTokenLength0;
04417             pString += nTokenLength0;
04418         }
04419     }
04420     *pBuffer = '\0';
04421     *bufc = pBuffer;
04422 }
04423 
04424 /*
04425  * ---------------------------------------------------------------------------
04426  * * fun_lcstr, fun_ucstr, fun_capstr: Lowercase, uppercase, or capitalize str.
04427  */
04428 
04429 static FUNCTION(fun_lcstr)
04430 {
04431     UNUSED_PARAMETER(executor);
04432     UNUSED_PARAMETER(caller);
04433     UNUSED_PARAMETER(enactor);
04434     UNUSED_PARAMETER(nfargs);
04435     UNUSED_PARAMETER(cargs);
04436     UNUSED_PARAMETER(ncargs);
04437 
04438     ANSI_TransformTextWithTable(buff, bufc, fargs[0], mux_tolower);
04439 }
04440 
04441 static FUNCTION(fun_ucstr)
04442 {
04443     UNUSED_PARAMETER(executor);
04444     UNUSED_PARAMETER(caller);
04445     UNUSED_PARAMETER(enactor);
04446     UNUSED_PARAMETER(nfargs);
04447     UNUSED_PARAMETER(cargs);
04448     UNUSED_PARAMETER(ncargs);
04449 
04450     ANSI_TransformTextWithTable(buff, bufc, fargs[0], mux_toupper);
04451 }
04452 
04453 static FUNCTION(fun_capstr)
04454 {
04455     UNUSED_PARAMETER(executor);
04456     UNUSED_PARAMETER(caller);
04457     UNUSED_PARAMETER(enactor);
04458     UNUSED_PARAMETER(nfargs);
04459     UNUSED_PARAMETER(cargs);
04460     UNUSED_PARAMETER(ncargs);
04461 
04462     char *pString = fargs[0];
04463     char *pBuffer = *bufc;
04464     int nString = strlen(pString);
04465     nString = safe_copy_buf(pString, nString, buff, bufc);
04466 
04467     // Find the first text character in (nString, pBuffer).
04468     //
04469     while (nString)
04470     {
04471         int nTokenLength0;
04472         int nTokenLength1;
04473         int iType = ANSI_lex(nString, pBuffer, &nTokenLength0, &nTokenLength1);
04474         if (iType == TOKEN_TEXT_ANSI)
04475         {
04476             *pBuffer = mux_toupper(*pBuffer);
04477             return;
04478         }
04479         else
04480         {
04481             // iType == TOKEN_ANSI
04482             //
04483             pBuffer += nTokenLength0;
04484             nString -= nTokenLength0;
04485         }
04486     }
04487 }
04488 
04489 /*
04490  * ---------------------------------------------------------------------------
04491  * * fun_lnum: Return a list of numbers.
04492  */
04493 static FUNCTION(fun_lnum)
04494 {
04495     SEP sep;
04496     if (  nfargs == 0
04497        || !OPTIONAL_DELIM(3, sep, DELIM_NULL|DELIM_CRLF|DELIM_STRING))
04498     {
04499         return;
04500     }
04501 
04502     int bot = 0, top;
04503     if (nfargs == 1)
04504     {
04505         top = mux_atol(fargs[0]) - 1;
04506         if (top < 0)
04507         {
04508             return;
04509         }
04510     }
04511     else
04512     {
04513         bot = mux_atol(fargs[0]);
04514         top = mux_atol(fargs[1]);
04515     }
04516 
04517     int i;
04518     if (bot == top)
04519     {
04520         safe_ltoa(bot, buff, bufc);
04521     }
04522     else if (bot < top)
04523     {
04524         safe_ltoa(bot, buff, bufc);
04525         for (i = bot+1; i <= top; i++)
04526         {
04527             print_sep(&sep, buff, bufc);
04528             char *p = *bufc;
04529             safe_ltoa(i, buff, bufc);
04530             if (p == *bufc) return;
04531         }
04532     }
04533     else if (top < bot)
04534     {
04535         safe_ltoa(bot, buff, bufc);
04536         for (i = bot-1; i >= top; i--)
04537         {
04538             print_sep(&sep, buff, bufc);
04539             char *p = *bufc;
04540             safe_ltoa(i, buff, bufc);
04541             if (p == *bufc)
04542             {
04543                 return;
04544             }
04545         }
04546     }
04547 }
04548 
04549 /* ---------------------------------------------------------------------------
04550  * fun_lattr, fun_lattrp: Return list of attributes I can see on the object.
04551  */
04552 
04553 static void lattr_handler(char *buff, char **bufc, dbref executor, char *fargs[],
04554                    bool bCheckParent)
04555 {
04556     dbref thing;
04557     int ca;
04558     bool bFirst;
04559 
04560     // Check for wildcard matching.  parse_attrib_wild checks for read
04561     // permission, so we don't have to.  Have p_a_w assume the
04562     // slash-star if it is missing.
04563     //
04564     bFirst = true;
04565     olist_push();
04566     if (parse_attrib_wild(executor, fargs[0], &thing, bCheckParent, false, true))
04567     {
04568         for (ca = olist_first(); ca != NOTHING; ca = olist_next())
04569         {
04570             ATTR *pattr = atr_num(ca);
04571             if (pattr)
04572             {
04573                 if (!bFirst)
04574                 {
04575                     safe_chr(' ', buff, bufc);
04576                 }
04577                 bFirst = false;
04578                 safe_str(pattr->name, buff, bufc);
04579             }
04580         }
04581     }
04582     else
04583     {
04584         safe_nomatch(buff, bufc);
04585     }
04586     olist_pop();
04587 }
04588 
04589 static FUNCTION(fun_lattr)
04590 {
04591     UNUSED_PARAMETER(caller);
04592     UNUSED_PARAMETER(enactor);
04593     UNUSED_PARAMETER(nfargs);
04594     UNUSED_PARAMETER(cargs);
04595     UNUSED_PARAMETER(ncargs);
04596 
04597     lattr_handler(buff, bufc, executor, fargs, false);
04598 }
04599 
04600 static FUNCTION(fun_lattrp)
04601 {
04602     UNUSED_PARAMETER(caller);
04603     UNUSED_PARAMETER(enactor);
04604     UNUSED_PARAMETER(nfargs);
04605     UNUSED_PARAMETER(cargs);
04606     UNUSED_PARAMETER(ncargs);
04607 
04608     lattr_handler(buff, bufc, executor, fargs, true);
04609 }
04610 // ---------------------------------------------------------------------------
04611 // fun_attrcnt: Return number of attributes I can see on the object.
04612 // ---------------------------------------------------------------------------
04613 
04614 static FUNCTION(fun_attrcnt)
04615 {
04616     UNUSED_PARAMETER(caller);
04617     UNUSED_PARAMETER(enactor);
04618     UNUSED_PARAMETER(nfargs);
04619     UNUSED_PARAMETER(cargs);
04620     UNUSED_PARAMETER(ncargs);
04621 
04622     dbref thing;
04623     int ca, count = 0;
04624 
04625     // Mechanism from lattr.
04626     //
04627     olist_push();
04628     if (parse_attrib_wild(executor, fargs[0], &thing, false, false, true))
04629     {
04630         for (ca = olist_first(); ca != NOTHING; ca = olist_next())
04631         {
04632             ATTR *pattr = atr_num(ca);
04633             if (pattr)
04634             {
04635                 count++;
04636             }
04637         }
04638         safe_ltoa(count, buff, bufc);
04639     }
04640     else
04641     {
04642         safe_nomatch(buff, bufc);
04643     }
04644     olist_pop();
04645 }
04646 
04647 /*
04648  * ---------------------------------------------------------------------------
04649  * * fun_reverse, fun_revwords: Reverse things.
04650  */
04651 
04652 static void mux_memrevcpy(char *dest, char *src, unsigned int n)
04653 {
04654     dest += n - 1;
04655     while (n--)
04656     {
04657         *dest-- = *src++;
04658     }
04659 }
04660 
04661 typedef void MEMXFORM(char *dest, char *src, unsigned int n);
04662 static void ANSI_TransformTextReverseWithFunction
04663 (
04664     char *buff,
04665     char **bufc,
04666     char *pString,
04667     MEMXFORM *pfMemXForm
04668 )
04669 {
04670     // Bounds checking.
04671     //
04672     unsigned int nString = strlen(pString);
04673     char *pBuffer = *bufc;
04674     unsigned int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
04675     if (nString > nBufferAvailable)
04676     {
04677         nString = nBufferAvailable;
04678         pString[nString] = '\0';
04679     }
04680 
04681     // How it's done: We have a source string (pString, nString) and a
04682     // destination buffer (pBuffer, nString) of equal size.
04683     //
04684     // We recognize (ANSI,TEXT) phrases and place them at the end of
04685     // destination buffer working our way to the front. The ANSI part
04686     // is left inviolate, but the text part is reversed.
04687     //
04688     // In this way, (ANSI1,TEXT1)(ANSI2,TEXT2) is traslated into
04689     // (ANSI2,reverse(TEST2))(ANSI1,reverse(TEXT1)).
04690     //
04691     // TODO: Do not reverse the CRLF in the text part either.
04692     //
04693     int  nANSI = 0;
04694     char *pANSI = pString;
04695     pBuffer += nString;
04696     *bufc = pBuffer;
04697     **bufc = '\0';
04698     while (nString)
04699     {
04700         int nTokenLength0;
04701         int nTokenLength1;
04702         int iType = ANSI_lex(nString, pString, &nTokenLength0, &nTokenLength1);
04703 
04704         if (iType == TOKEN_TEXT_ANSI)
04705         {
04706             // (ANSI,TEXT) is given by (nANSI, nTokenLength0)
04707             //
04708             pBuffer -= nANSI + nTokenLength0;
04709             memcpy(pBuffer, pANSI, nANSI);
04710             pfMemXForm(pBuffer+nANSI, pString, nTokenLength0);
04711 
04712             // Adjust pointers and counts.
04713             //
04714             nString -= nTokenLength0;
04715             pString += nTokenLength0;
04716             pANSI = pString;
04717             nANSI = 0;
04718 
04719             nTokenLength0 = nTokenLength1;
04720         }
04721         // TOKEN_ANSI
04722         //
04723         nString -= nTokenLength0;
04724         pString += nTokenLength0;
04725         nANSI   += nTokenLength0;
04726     }
04727 
04728     // Copy the last ANSI part (if present). It will be overridden by
04729     // ANSI further on, but we need to fill up the space. Code
04730     // elsewhere will compact it before it's sent to the client.
04731     //
04732     pBuffer -= nANSI;
04733     memcpy(pBuffer, pANSI, nANSI);
04734 }
04735 
04736 static FUNCTION(fun_reverse)
04737 {
04738     UNUSED_PARAMETER(executor);
04739     UNUSED_PARAMETER(caller);
04740     UNUSED_PARAMETER(enactor);
04741     UNUSED_PARAMETER(nfargs);
04742     UNUSED_PARAMETER(cargs);
04743     UNUSED_PARAMETER(ncargs);
04744 
04745     ANSI_TransformTextReverseWithFunction(buff, bufc, fargs[0], mux_memrevcpy);
04746 }
04747 
04748 static char ReverseWordsInText_Seperator;
04749 
04750 static void ReverseWordsInText(char *dest, char *src, unsigned int n)
04751 {
04752     char chSave = src[n];
04753     src[n] = '\0';
04754     dest += n;
04755     while (n)
04756     {
04757         char *pWord = strchr(src, ReverseWordsInText_Seperator);
04758         int nLen;
04759         if (pWord)
04760         {
04761             nLen = (pWord - src);
04762         }
04763         else
04764         {
04765             nLen = n;
04766         }
04767         dest -= nLen;
04768         memcpy(dest, src, nLen);
04769         src += nLen;
04770         n -= nLen;
04771         if (pWord)
04772         {
04773             dest--;
04774             *dest = ReverseWordsInText_Seperator;
04775             src++;
04776             n--;
04777         }
04778     }
04779     src[n] = chSave;
04780 }
04781 
04782 static FUNCTION(fun_revwords)
04783 {
04784     // If we are passed an empty arglist return a null string.
04785     //
04786     if (nfargs == 0)
04787     {
04788         return;
04789     }
04790     SEP sep;
04791     if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT))
04792     {
04793         return;
04794     }
04795     ReverseWordsInText_Seperator = sep.str[0];
04796     ANSI_TransformTextReverseWithFunction(buff, bufc, fargs[0], ReverseWordsInText);
04797 }
04798 
04799 /*
04800  * ---------------------------------------------------------------------------
04801  * * fun_after, fun_before: Return substring after or before a specified string.
04802  */
04803 
04804 static FUNCTION(fun_after)
04805 {
04806     UNUSED_PARAMETER(executor);
04807     UNUSED_PARAMETER(caller);
04808     UNUSED_PARAMETER(enactor);
04809     UNUSED_PARAMETER(cargs);
04810     UNUSED_PARAMETER(ncargs);
04811 
04812     char *mp;
04813     int mlen;
04814 
04815     // Sanity-check arg1 and arg2.
04816     //
04817     char *bp = fargs[0];
04818     if (nfargs > 1)
04819     {
04820         mp = fargs[1];
04821         mlen = strlen(mp);
04822     }
04823     else
04824     {
04825         mp = " ";
04826         mlen = 1;
04827     }
04828 
04829     if (  mlen == 1
04830        && *mp == ' ')
04831     {
04832         bp = trim_space_sep(bp, &sepSpace);
04833     }
04834 
04835     // Look for the target string.
04836     //
04837     int nText = strlen(bp);
04838     int i = BMH_StringSearch(mlen, mp, nText, bp);
04839     if (i >= 0)
04840     {
04841         // Yup, return what follows.
04842         //
04843         bp += i + mlen;
04844         safe_copy_buf(bp, nText-i-mlen, buff, bufc);
04845     }
04846     //
04847     // Ran off the end without finding it.
04848 }
04849 
04850 static FUNCTION(fun_before)
04851 {
04852     UNUSED_PARAMETER(executor);
04853     UNUSED_PARAMETER(caller);
04854     UNUSED_PARAMETER(enactor);
04855     UNUSED_PARAMETER(cargs);
04856     UNUSED_PARAMETER(ncargs);
04857 
04858     char *mp, *ip;
04859     int mlen;
04860 
04861     // Sanity-check arg1 and arg2.
04862     //
04863     char *bp = fargs[0];
04864     if (nfargs > 1)
04865     {
04866         mp = fargs[1];
04867         mlen = strlen(mp);
04868     }
04869     else
04870     {
04871         mp = " ";
04872         mlen = 1;
04873     }
04874 
04875     if (  mlen == 1
04876        && *mp == ' ')
04877     {
04878         bp = trim_space_sep(bp, &sepSpace);
04879     }
04880 
04881     ip = bp;
04882 
04883     // Look for the target string.
04884     //
04885     int i = BMH_StringSearch(mlen, mp, strlen(bp), bp);
04886     if (i >= 0)
04887     {
04888         // Yup, return what follows.
04889         //
04890         safe_copy_buf(ip, i, buff, bufc);
04891         return;
04892     }
04893     // Ran off the end without finding it.
04894     //
04895     safe_str(ip, buff, bufc);
04896 }
04897 
04898 /*
04899  * ---------------------------------------------------------------------------
04900  * * fun_search: Search the db for things, returning a list of what matches
04901  */
04902 
04903 static FUNCTION(fun_search)
04904 {
04905     UNUSED_PARAMETER(cargs);
04906     UNUSED_PARAMETER(ncargs);
04907 
04908     char *pArg = NULL;
04909     if (nfargs != 0)
04910     {
04911         pArg = fargs[0];
04912     }
04913 
04914     // Set up for the search.  If any errors, abort.
04915     //
04916     SEARCH searchparm;
04917     if (!search_setup(executor, pArg, &searchparm))
04918     {
04919         safe_str("#-1 ERROR DURING SEARCH", buff, bufc);
04920         return;
04921     }
04922 
04923     // Do the search and report the results.
04924     //
04925     olist_push();
04926     search_perform(executor, caller, enactor, &searchparm);
04927     dbref thing;
04928     ITL pContext;
04929     ItemToList_Init(&pContext, buff, bufc, '#');
04930     for (thing = olist_first(); thing != NOTHING; thing = olist_next())
04931     {
04932         if (!ItemToList_AddInteger(&pContext, thing))
04933         {
04934             break;
04935         }
04936     }
04937     ItemToList_Final(&pContext);
04938     olist_pop();
04939 }
04940 
04941 /*
04942  * ---------------------------------------------------------------------------
04943  * * fun_stats: Get database size statistics.
04944  */
04945 
04946 static FUNCTION(fun_stats)
04947 {
04948     UNUSED_PARAMETER(caller);
04949     UNUSED_PARAMETER(enactor);
04950     UNUSED_PARAMETER(cargs);
04951     UNUSED_PARAMETER(ncargs);
04952 
04953     dbref who;
04954 
04955     if (  nfargs == 0
04956        || (!fargs[0])
04957        || !*fargs[0]
04958        || !string_compare(fargs[0], "all"))
04959     {
04960         who = NOTHING;
04961     }
04962     else
04963     {
04964         who = lookup_player(executor, fargs[0], true);
04965         if (who == NOTHING)
04966         {
04967             safe_str("#-1 PLAYER NOT FOUND", buff, bufc);
04968             return;
04969         }
04970     }
04971     STATS statinfo;
04972     if (!get_stats(executor, who, &statinfo))
04973     {
04974         safe_str("#-1 ERROR GETTING STATS", buff, bufc);
04975         return;
04976     }
04977     safe_tprintf_str(buff, bufc, "%d %d %d %d %d %d", statinfo.s_total, statinfo.s_rooms,
04978             statinfo.s_exits, statinfo.s_things, statinfo.s_players, statinfo.s_garbage);
04979 }
04980 
04981 /*
04982  * ---------------------------------------------------------------------------
04983  * * fun_merge:  given two strings and a character, merge the two strings
04984  * *   by replacing characters in string1 that are the same as the given
04985  * *   character by the corresponding character in string2 (by position).
04986  * *   The strings must be of the same length.
04987  */
04988 
04989 static FUNCTION(fun_merge)
04990 {
04991     UNUSED_PARAMETER(executor);
04992     UNUSED_PARAMETER(caller);
04993     UNUSED_PARAMETER(enactor);
04994     UNUSED_PARAMETER(nfargs);
04995     UNUSED_PARAMETER(cargs);
04996     UNUSED_PARAMETER(ncargs);
04997 
04998     char *str, *rep;
04999     char c;
05000 
05001     // Do length checks first.
05002     //
05003     size_t n0 = strlen(fargs[0]);
05004     size_t n1 = strlen(fargs[1]);
05005     if (n0 != n1)
05006     {
05007         safe_str("#-1 STRING LENGTHS MUST BE EQUAL", buff, bufc);
05008         return;
05009     }
05010     if (strlen(fargs[2]) > 1)
05011     {
05012         safe_str("#-1 TOO MANY CHARACTERS", buff, bufc);
05013         return;
05014     }
05015 
05016     // Find the character to look for. null character is considered a
05017     // space.
05018     //
05019     if (!*fargs[2])
05020         c = ' ';
05021     else
05022         c = *fargs[2];
05023 
05024     // Walk strings, copy from the appropriate string.
05025     //
05026     for (str = fargs[0], rep = fargs[1];
05027          *str && *rep && ((*bufc - buff) < (LBUF_SIZE-1));
05028          str++, rep++, (*bufc)++)
05029     {
05030         if (*str == c)
05031             **bufc = *rep;
05032         else
05033             **bufc = *str;
05034     }
05035     return;
05036 }
05037 
05038 /* ---------------------------------------------------------------------------
05039  * fun_splice: similar to MERGE(), eplaces by word instead of by character.
05040  */
05041 
05042 static FUNCTION(fun_splice)
05043 {
05044     SEP sep;
05045     if (!OPTIONAL_DELIM(4, sep, DELIM_DFLT|DELIM_STRING))
05046     {
05047         return;
05048     }
05049     SEP osep = sep;
05050     if (!OPTIONAL_DELIM(5, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
05051     {
05052         return;
05053     }
05054 
05055     // Length checks.
05056     //
05057     if (countwords(fargs[2], &sep) > 1)
05058     {
05059         safe_str("#-1 TOO MANY WORDS", buff, bufc);
05060         return;
05061     }
05062     int words = countwords(fargs[0], &sep);
05063     if (words != countwords(fargs[1], &sep))
05064     {
05065         safe_str("#-1 NUMBER OF WORDS MUST BE EQUAL", buff, bufc);
05066         return;
05067     }
05068 
05069     // Loop through the two lists.
05070     //
05071     char *p1 = fargs[0];
05072     char *q1 = fargs[1];
05073     char *p2, *q2;
05074     bool first = true;
05075     int i;
05076     for (i = 0; i < words; i++)
05077     {
05078         p2 = split_token(&p1, &sep);
05079         q2 = split_token(&q1, &sep);
05080         if (!first)
05081         {
05082             print_sep(&osep, buff, bufc);
05083         }
05084         if (strcmp(p2, fargs[2]) == 0)
05085         {
05086             safe_str(q2, buff, bufc); // replace
05087         }
05088         else
05089         {
05090             safe_str(p2, buff, bufc); // copy
05091         }
05092         first = false;
05093     }
05094 }
05095 
05096 /*---------------------------------------------------------------------------
05097  * fun_repeat: repeats a string
05098  */
05099 
05100 static FUNCTION(fun_repeat)
05101 {
05102     UNUSED_PARAMETER(executor);
05103     UNUSED_PARAMETER(caller);
05104     UNUSED_PARAMETER(enactor);
05105     UNUSED_PARAMETER(nfargs);
05106     UNUSED_PARAMETER(cargs);
05107     UNUSED_PARAMETER(ncargs);
05108 
05109     int times = mux_atol(fargs[1]);
05110     if (times < 1 || *fargs[0] == '\0')
05111     {
05112         // Legal but no work to do.
05113         //
05114         return;
05115     }
05116     else if (times == 1)
05117     {
05118         // It turns into a string copy.
05119         //
05120         safe_str(fargs[0], buff, bufc);
05121     }
05122     else
05123     {
05124         int len = strlen(fargs[0]);
05125         if (len == 1)
05126         {
05127             // It turns into a memset.
05128             //
05129             safe_fill(buff, bufc, *fargs[0], times);
05130         }
05131         else
05132         {
05133             int nSize = len*times;
05134             if (  times > LBUF_SIZE - 1
05135                || nSize > LBUF_SIZE - 1)
05136             {
05137                 safe_str("#-1 STRING TOO LONG", buff, bufc);
05138             }
05139             else
05140             {
05141                 int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
05142                 if (nSize > nBufferAvailable)
05143                 {
05144                     nSize = nBufferAvailable;
05145                 }
05146                 int nFullCopies = nSize / len;
05147                 int nPartial = nSize - nFullCopies * len;
05148                 while (nFullCopies--)
05149                 {
05150                     memcpy(*bufc, fargs[0], len);
05151                     *bufc += len;
05152                 }
05153                 if (nPartial)
05154                 {
05155                     memcpy(*bufc, fargs[0], nPartial);
05156                     *bufc += nPartial;
05157                 }
05158             }
05159         }
05160     }
05161 }
05162 
05163 /*
05164  * ---------------------------------------------------------------------------
05165  * * fun_iter: Make list from evaluating arg2 with each member of arg1.
05166  * * NOTE: This function expects that its arguments have not been evaluated.
05167  */
05168 
05169 static FUNCTION(fun_iter)
05170 {
05171     // Optional Input Delimiter.
05172     //
05173     SEP sep;
05174     if (!OPTIONAL_DELIM(3, sep, DELIM_EVAL|DELIM_STRING))
05175     {
05176         return;
05177     }
05178 
05179     // Optional Output Delimiter.
05180     //
05181     SEP osep;
05182     if (!OPTIONAL_DELIM(4, osep, DELIM_EVAL|DELIM_NULL|DELIM_CRLF|DELIM_STRING))
05183     {
05184         return;
05185     }
05186 
05187     char *curr = alloc_lbuf("fun_iter");
05188     char *dp = curr;
05189     char *str = fargs[0];
05190     mux_exec(curr, &dp, executor, caller, enactor,
05191         EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05192     *dp = '\0';
05193     int ncp;
05194     char *cp = trim_space_sep_LEN(curr, dp-curr, &sep, &ncp);
05195     if (!*cp)
05196     {
05197         free_lbuf(curr);
05198         return;
05199     }
05200     bool first = true;
05201     int number = 0;
05202     bool bLoopInBounds = (  0 <= mudstate.in_loop
05203                          && mudstate.in_loop < MAX_ITEXT);
05204     if (bLoopInBounds)
05205     {
05206         mudstate.itext[mudstate.in_loop] = NULL;
05207         mudstate.inum[mudstate.in_loop] = number;
05208     }
05209     mudstate.in_loop++;
05210     while (  cp
05211           && mudstate.func_invk_ctr < mudconf.func_invk_lim
05212           && !MuxAlarm.bAlarmed)
05213     {
05214         if (!first)
05215         {
05216             print_sep(&osep, buff, bufc);
05217         }
05218         first = false;
05219         number++;
05220         char *objstring = split_token(&cp, &sep);
05221         if (bLoopInBounds)
05222         {
05223             mudstate.itext[mudstate.in_loop-1] = objstring;
05224             mudstate.inum[mudstate.in_loop-1]  = number;
05225         }
05226         char *buff2 = replace_tokens(fargs[1], objstring, mux_ltoa_t(number),
05227             NULL);
05228         str = buff2;
05229         mux_exec(buff, bufc, executor, caller, enactor,
05230             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05231         free_lbuf(buff2);
05232     }
05233     mudstate.in_loop--;
05234     if (bLoopInBounds)
05235     {
05236         mudstate.itext[mudstate.in_loop] = NULL;
05237         mudstate.inum[mudstate.in_loop] = 0;
05238     }
05239     free_lbuf(curr);
05240 }
05241 
05242 static void iter_value(char *buff, char **bufc, char *fargs[], int nfargs, bool bWhich)
05243 {
05244     int number = 0;
05245     if (nfargs > 0)
05246     {
05247         number = mux_atol(fargs[0]);
05248         if (number < 0)
05249         {
05250             number = 0;
05251         }
05252     }
05253 
05254     number++;
05255     int val = mudstate.in_loop - number;
05256     if (  0 <= val
05257        && val < MAX_ITEXT)
05258     {
05259         if (bWhich)
05260         {
05261             safe_ltoa(mudstate.inum[val], buff, bufc);
05262         }
05263         else
05264         {
05265             safe_str(mudstate.itext[val], buff, bufc);
05266         }
05267     }
05268 }
05269 
05270 static FUNCTION(fun_itext)
05271 {
05272     UNUSED_PARAMETER(executor);
05273     UNUSED_PARAMETER(caller);
05274     UNUSED_PARAMETER(enactor);
05275     UNUSED_PARAMETER(cargs);
05276     UNUSED_PARAMETER(ncargs);
05277 
05278     iter_value(buff, bufc, fargs, nfargs, false);
05279 }
05280 
05281 static FUNCTION(fun_inum)
05282 {
05283     UNUSED_PARAMETER(executor);
05284     UNUSED_PARAMETER(caller);
05285     UNUSED_PARAMETER(enactor);
05286     UNUSED_PARAMETER(cargs);
05287     UNUSED_PARAMETER(ncargs);
05288 
05289     iter_value(buff, bufc, fargs, nfargs, true);
05290 }
05291 
05292 static FUNCTION(fun_list)
05293 {
05294     SEP sep;
05295     if (!OPTIONAL_DELIM(3, sep, DELIM_EVAL|DELIM_STRING))
05296     {
05297         return;
05298     }
05299 
05300     char *objstring, *result, *str;
05301 
05302     char *curr = alloc_lbuf("fun_list");
05303     char *dp   = curr;
05304     str = fargs[0];
05305     mux_exec(curr, &dp, executor, caller, enactor,
05306         EV_TOP | EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05307     *dp = '\0';
05308 
05309     int ncp;
05310     char *cp = trim_space_sep_LEN(curr, dp-curr, &sep, &ncp);
05311     if (!*cp)
05312     {
05313         free_lbuf(curr);
05314         return;
05315     }
05316     int number = 0;
05317     bool bLoopInBounds = (  0 <= mudstate.in_loop
05318                          && mudstate.in_loop < MAX_ITEXT);
05319     if (bLoopInBounds)
05320     {
05321         mudstate.itext[mudstate.in_loop] = NULL;
05322         mudstate.inum[mudstate.in_loop] = number;
05323     }
05324     mudstate.in_loop++;
05325     while (  cp
05326           && mudstate.func_invk_ctr < mudconf.func_invk_lim
05327           && !MuxAlarm.bAlarmed)
05328     {
05329         number++;
05330         objstring = split_token(&cp, &sep);
05331         if (bLoopInBounds)
05332         {
05333             mudstate.itext[mudstate.in_loop-1] = objstring;
05334             mudstate.inum[mudstate.in_loop-1]  = number;
05335         }
05336         char *buff2 = replace_tokens(fargs[1], objstring, mux_ltoa_t(number),
05337             NULL);
05338         dp = result = alloc_lbuf("fun_list.2");
05339         str = buff2;
05340         mux_exec(result, &dp, executor, caller, enactor,
05341             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05342         *dp = '\0';
05343         free_lbuf(buff2);
05344         notify(enactor, result);
05345         free_lbuf(result);
05346     }
05347     mudstate.in_loop--;
05348     if (bLoopInBounds)
05349     {
05350         mudstate.itext[mudstate.in_loop] = NULL;
05351         mudstate.inum[mudstate.in_loop] = 0;
05352     }
05353     free_lbuf(curr);
05354 }
05355 
05356 static FUNCTION(fun_ilev)
05357 {
05358     UNUSED_PARAMETER(executor);
05359     UNUSED_PARAMETER(caller);
05360     UNUSED_PARAMETER(enactor);
05361     UNUSED_PARAMETER(fargs);
05362     UNUSED_PARAMETER(nfargs);
05363     UNUSED_PARAMETER(cargs);
05364     UNUSED_PARAMETER(ncargs);
05365 
05366     safe_ltoa(mudstate.in_loop-1, buff, bufc);
05367 }
05368 
05369 /* ---------------------------------------------------------------------------
05370  * fun_fold: Iteratively eval an attrib with a list of arguments and an
05371  *           optional base case.  With no base case, the first list element is
05372  *           passed as %0 and the second is %1.  The attrib is then evaluated
05373  *           with these args, the result is then used as %0 and the next arg
05374  *           is %1 and so it goes as long as there are elements left in the
05375  *           list. The optional base case gives the user a nice starting point.
05376  *
05377  *      > &REP_NUM object=[%0][repeat(%1,%1)]
05378  *      > say fold(OBJECT/REP_NUM,1 2 3 4 5,->)
05379  *      You say, "->122333444455555"
05380  *
05381  * NOTE: To use added list separator, you must use base case!
05382  */
05383 
05384 static FUNCTION(fun_fold)
05385 {
05386     SEP sep;
05387     if (!OPTIONAL_DELIM(4, sep, DELIM_DFLT|DELIM_STRING))
05388     {
05389         return;
05390     }
05391 
05392     char *atext;
05393     dbref thing;
05394     if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
05395     {
05396         return;
05397     }
05398 
05399     // Evaluate it using the rest of the passed function args.
05400     //
05401     char *curr = fargs[1];
05402     char *cp = curr;
05403     char *atextbuf = alloc_lbuf("fun_fold");
05404     strcpy(atextbuf, atext);
05405 
05406     char *result, *bp, *str, *clist[2];
05407 
05408     // May as well handle first case now.
05409     //
05410     if ( nfargs >= 3
05411        && fargs[2])
05412     {
05413         clist[0] = fargs[2];
05414         clist[1] = split_token(&cp, &sep);
05415         result = bp = alloc_lbuf("fun_fold");
05416         str = atextbuf;
05417         mux_exec(result, &bp, thing, executor, enactor,
05418             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, clist, 2);
05419         *bp = '\0';
05420     }
05421     else
05422     {
05423         clist[0] = split_token(&cp, &sep);
05424         clist[1] = split_token(&cp, &sep);
05425         result = bp = alloc_lbuf("fun_fold");
05426         str = atextbuf;
05427         mux_exec(result, &bp, thing, executor, enactor,
05428             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, clist, 2);
05429         *bp = '\0';
05430     }
05431 
05432     char *rstore = result;
05433     result = alloc_lbuf("fun_fold");
05434 
05435     while (  cp
05436           && mudstate.func_invk_ctr < mudconf.func_invk_lim
05437           && !MuxAlarm.bAlarmed)
05438     {
05439         clist[0] = rstore;
05440         clist[1] = split_token(&cp, &sep);
05441         strcpy(atextbuf, atext);
05442         bp = result;
05443         str = atextbuf;
05444         mux_exec(result, &bp, thing, executor, enactor,
05445             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, clist, 2);
05446         *bp = '\0';
05447         strcpy(rstore, result);
05448     }
05449     free_lbuf(result);
05450     safe_str(rstore, buff, bufc);
05451     free_lbuf(rstore);
05452     free_lbuf(atext);
05453     free_lbuf(atextbuf);
05454 }
05455 
05456 // Taken from PennMUSH with permission.
05457 //
05458 // itemize(<list>[,<delim>[,<conjunction>[,<punctuation>]]])
05459 // It takes the elements of list and:
05460 //  If there's just one, returns it.
05461 //  If there's two, returns <e1> <conjunction> <e2>
05462 //  If there's >2, returns <e1><punc> <e2><punc> ... <conjunction> <en>
05463 // Default <conjunction> is "and", default punctuation is ","
05464 //
05465 static FUNCTION(fun_itemize)
05466 {
05467     SEP sep;
05468     if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
05469     {
05470         return;
05471     }
05472 
05473     const char *lconj = "and";
05474     if (nfargs > 2)
05475     {
05476         lconj = fargs[2];
05477     }
05478     const char *punc = ",";
05479     if (nfargs > 3)
05480     {
05481         punc = fargs[3];
05482     }
05483 
05484     int pos = 1;
05485     char *cp = trim_space_sep(fargs[0], &sep);
05486     char *word = split_token(&cp, &sep);
05487     while (cp && *cp)
05488     {
05489         pos++;
05490         safe_str(word, buff, bufc);
05491         char *nextword = split_token(&cp, &sep);
05492 
05493         if (!cp || !*cp)
05494         {
05495             // We're at the end.
05496             //
05497             if (pos >= 3)
05498             {
05499                 safe_str(punc, buff, bufc);
05500             }
05501             safe_chr(' ', buff, bufc);
05502             safe_str(lconj, buff, bufc);
05503         }
05504         else
05505         {
05506             safe_str(punc, buff, bufc);
05507         }
05508         safe_chr(' ', buff, bufc);
05509 
05510         word = nextword;
05511     }
05512     safe_str(word, buff, bufc);
05513 }
05514 
05515 // ---------------------------------------------------------------------------
05516 // fun_choose: Weighted random choice from a list.
05517 //             choose(<list of items>,<list of weights>,<input delim>)
05518 //
05519 static FUNCTION(fun_choose)
05520 {
05521     SEP isep;
05522     if (!OPTIONAL_DELIM(3, isep, DELIM_DFLT|DELIM_STRING))
05523     {
05524         return;
05525     }
05526 
05527     char *elems[LBUF_SIZE/2], *weights[LBUF_SIZE/2];
05528     int n_elems = list2arr(elems, LBUF_SIZE/2, fargs[0], &isep);
05529     int n_weights = list2arr(weights, LBUF_SIZE/2, fargs[1], &sepSpace);
05530 
05531     if (n_elems != n_weights)
05532     {
05533         safe_str("#-1 LISTS MUST BE OF EQUAL SIZE", buff, bufc);
05534         return;
05535     }
05536 
05537     // Calculate the the breakpoints, not the weights themselves.
05538     //
05539     int i;
05540     int sum = 0;
05541     int ip[LBUF_SIZE/2];
05542     for (i = 0; i < n_weights; i++)
05543     {
05544         int num = mux_atol(weights[i]);
05545         if (num < 0)
05546         {
05547             num = 0;
05548         }
05549         if (num == 0)
05550         {
05551             ip[i] = 0;
05552         }
05553         else
05554         {
05555             int sum_next = sum + num;
05556             if (sum_next < sum)
05557             {
05558                 safe_str("#-1 OVERFLOW", buff, bufc);
05559                 return;
05560             }
05561             sum = sum_next;
05562             ip[i] = sum;
05563         }
05564     }
05565 
05566     INT32 num = RandomINT32(0, sum-1);
05567 
05568     for (i = 0; i < n_weights; i++)
05569     {
05570         if (  ip[i] != 0
05571            && num < ip[i])
05572         {
05573             safe_str(elems[i], buff, bufc);
05574             break;
05575         }
05576     }
05577 }
05578 
05579 /* ---------------------------------------------------------------------------
05580  * fun_filter: Iteratively perform a function with a list of arguments and
05581  *             return the arg, if the function evaluates to true using the arg.
05582  *
05583  *      > &IS_ODD object=mod(%0,2)
05584  *      > say filter(object/is_odd,1 2 3 4 5)
05585  *      You say, "1 3 5"
05586  *      > say filter(object/is_odd,1-2-3-4-5,-)
05587  *      You say, "1-3-5"
05588  *
05589  *  NOTE:  If you specify a separator, it is used to delimit the returned list.
05590  */
05591 
05592 static void filter_handler(char *buff, char **bufc, dbref executor, dbref enactor,
05593                     char *fargs[], SEP *psep, SEP *posep, bool bBool)
05594 {
05595     char *atext;
05596     dbref thing;
05597     if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
05598     {
05599         return;
05600     }
05601 
05602     // Now iteratively eval the attrib with the argument list.
05603     //
05604     char *curr = trim_space_sep(fargs[1], psep);
05605     char *cp = curr;
05606     char *atextbuf = alloc_lbuf("fun_filter");
05607     char *result = alloc_lbuf("fun_filter");
05608     bool bFirst = true;
05609     while (  cp
05610           && mudstate.func_invk_ctr < mudconf.func_invk_lim
05611           && !MuxAlarm.bAlarmed)
05612     {
05613         char *objstring = split_token(&cp, psep);
05614         strcpy(atextbuf, atext);
05615         char *bp = result;
05616         char *str = atextbuf;
05617         mux_exec(result, &bp, thing, executor, enactor,
05618             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &objstring, 1);
05619         *bp = '\0';
05620 
05621         if (  (  bBool
05622               && xlate(result))
05623            || (  !bBool
05624               && result[0] == '1'
05625               && result[1] == '\0'))
05626         {
05627             if (!bFirst)
05628             {
05629                 print_sep(posep, buff, bufc);
05630             }
05631             safe_str(objstring, buff, bufc);
05632             bFirst = false;
05633         }
05634     }
05635     free_lbuf(result);
05636     free_lbuf(atext);
05637     free_lbuf(atextbuf);
05638 }
05639 
05640 static FUNCTION(fun_filter)
05641 {
05642     SEP sep;
05643     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
05644     {
05645         return;
05646     }
05647     SEP osep = sep;
05648     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
05649     {
05650         return;
05651     }
05652     filter_handler(buff, bufc, executor, enactor, fargs, &sep, &osep, false);
05653 }
05654 
05655 static FUNCTION(fun_filterbool)
05656 {
05657     SEP sep;
05658     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
05659     {
05660         return;
05661     }
05662     SEP osep = sep;
05663     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
05664     {
05665         return;
05666     }
05667     filter_handler(buff, bufc, executor, enactor, fargs, &sep, &osep, true);
05668 }
05669 
05670 /* ---------------------------------------------------------------------------
05671  * fun_map: Iteratively evaluate an attribute with a list of arguments.
05672  *
05673  *      > &DIV_TWO object=fdiv(%0,2)
05674  *      > say map(1 2 3 4 5,object/div_two)
05675  *      You say, "0.5 1 1.5 2 2.5"
05676  *      > say map(object/div_two,1-2-3-4-5,-)
05677  *      You say, "0.5-1-1.5-2-2.5"
05678  */
05679 
05680 static FUNCTION(fun_map)
05681 {
05682     SEP sep;
05683     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
05684     {
05685         return;
05686     }
05687 
05688     SEP osep = sep;
05689     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
05690     {
05691         return;
05692     }
05693 
05694     char *atext;
05695     dbref thing;
05696     if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
05697     {
05698         return;
05699     }
05700 
05701     // Now process the list one element at a time.
05702     //
05703     char *cp = trim_space_sep(fargs[1], &sep);
05704     char *atextbuf = alloc_lbuf("fun_map");
05705     bool first = true;
05706     char *objstring, *str;
05707     while (  cp
05708           && mudstate.func_invk_ctr < mudconf.func_invk_lim
05709           && !MuxAlarm.bAlarmed)
05710     {
05711         if (!first)
05712         {
05713             print_sep(&osep, buff, bufc);
05714         }
05715         first = false;
05716         objstring = split_token(&cp, &sep);
05717         strcpy(atextbuf, atext);
05718         str = atextbuf;
05719         mux_exec(buff, bufc, thing, executor, enactor,
05720             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &objstring, 1);
05721     }
05722     free_lbuf(atext);
05723     free_lbuf(atextbuf);
05724 }
05725 
05726 /*
05727  * ---------------------------------------------------------------------------
05728  * * fun_edit: Edit text.
05729  */
05730 
05731 static FUNCTION(fun_edit)
05732 {
05733     UNUSED_PARAMETER(executor);
05734     UNUSED_PARAMETER(caller);
05735     UNUSED_PARAMETER(enactor);
05736     UNUSED_PARAMETER(nfargs);
05737     UNUSED_PARAMETER(cargs);
05738     UNUSED_PARAMETER(ncargs);
05739 
05740     char *tstr;
05741 
05742     edit_string(strip_ansi(fargs[0]), &tstr, fargs[1], fargs[2]);
05743     safe_str(tstr, buff, bufc);
05744     free_lbuf(tstr);
05745 }
05746 
05747 /* ---------------------------------------------------------------------------
05748  * fun_locate: Search for things with the perspective of another obj.
05749  */
05750 
05751 static FUNCTION(fun_locate)
05752 {
05753     UNUSED_PARAMETER(caller);
05754     UNUSED_PARAMETER(enactor);
05755     UNUSED_PARAMETER(nfargs);
05756     UNUSED_PARAMETER(cargs);
05757     UNUSED_PARAMETER(ncargs);
05758 
05759     bool check_locks, verbose, multiple;
05760     dbref thing, what;
05761     char *cp;
05762 
05763     int pref_type = NOTYPE;
05764     check_locks = verbose = multiple = false;
05765 
05766     // Find the thing to do the looking, make sure we control it.
05767     //
05768     if (See_All(executor))
05769     {
05770         thing = match_thing_quiet(executor, fargs[0]);
05771     }
05772     else
05773     {
05774         thing = match_controlled_quiet(executor, fargs[0]);
05775     }
05776     if (!Good_obj(thing))
05777     {
05778         safe_match_result(thing, buff, bufc);
05779         return;
05780     }
05781 
05782     // Get pre- and post-conditions and modifiers
05783     //
05784     for (cp = fargs[2]; *cp; cp++)
05785     {
05786         switch (*cp)
05787         {
05788         case 'E':
05789             pref_type = TYPE_EXIT;
05790             break;
05791         case 'L':
05792             check_locks = true;
05793             break;
05794         case 'P':
05795             pref_type = TYPE_PLAYER;
05796             break;
05797         case 'R':
05798             pref_type = TYPE_ROOM;
05799             break;
05800         case 'T':
05801             pref_type = TYPE_THING;
05802             break;
05803         case 'V':
05804             verbose = true;
05805             break;
05806         case 'X':
05807             multiple = true;
05808             break;
05809         }
05810     }
05811 
05812     // Set up for the search
05813     //
05814     if (check_locks)
05815     {
05816         init_match_check_keys(thing, fargs[1], pref_type);
05817     }
05818     else
05819     {
05820         init_match(thing, fargs[1], pref_type);
05821     }
05822 
05823     // Search for each requested thing
05824     //
05825     for (cp = fargs[2]; *cp; cp++)
05826     {
05827         switch (*cp)
05828         {
05829         case 'a':
05830             match_absolute();
05831             break;
05832         case 'c':
05833             match_carried_exit_with_parents();
05834             break;
05835         case 'e':
05836             match_exit_with_parents();
05837             break;
05838         case 'h':
05839             match_here();
05840             break;
05841         case 'i':
05842             match_possession();
05843             break;
05844         case 'm':
05845             match_me();
05846             break;
05847         case 'n':
05848             match_neighbor();
05849             break;
05850         case 'p':
05851             match_player();
05852             break;
05853         case '*':
05854             match_everything(MAT_EXIT_PARENTS);
05855             break;
05856         }
05857     }
05858 
05859     // Get the result and return it to the caller
05860     //
05861     if (multiple)
05862     {
05863         what = last_match_result();
05864     }
05865     else
05866     {
05867         what = match_result();
05868     }
05869 
05870     if (verbose)
05871     {
05872         (void)match_status(executor, what);
05873     }
05874 
05875     safe_tprintf_str(buff, bufc, "#%d", what);
05876 }
05877 
05878 static void switch_handler
05879 (
05880     char *buff, char **bufc,
05881     dbref executor, dbref caller, dbref enactor,
05882     char *fargs[], int nfargs,
05883     char *cargs[], int ncargs,
05884     bool bSwitch
05885 )
05886 {
05887     // Evaluate the target in fargs[0].
05888     //
05889     char *mbuff = alloc_lbuf("fun_switch");
05890     char *bp = mbuff;
05891     char *str = fargs[0];
05892     mux_exec(mbuff, &bp, executor, caller, enactor,
05893         EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05894     *bp = '\0';
05895 
05896     char *tbuff = alloc_lbuf("fun_switch.2");
05897 
05898     // Loop through the patterns looking for a match.
05899     //
05900     int i;
05901     for (i = 1;  i < nfargs-1
05902               && fargs[i]
05903               && fargs[i+1]
05904               && !MuxAlarm.bAlarmed; i += 2)
05905     {
05906         bp = tbuff;
05907         str = fargs[i];
05908         mux_exec(tbuff, &bp, executor, caller, enactor,
05909             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05910         *bp = '\0';
05911 
05912         if (bSwitch ? wild_match(tbuff, mbuff) : strcmp(tbuff, mbuff) == 0)
05913         {
05914             free_lbuf(tbuff);
05915             tbuff = replace_tokens(fargs[i+1], NULL, NULL, mbuff);
05916             free_lbuf(mbuff);
05917             str = tbuff;
05918             mux_exec(buff, bufc, executor, caller, enactor,
05919                 EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05920             free_lbuf(tbuff);
05921             return;
05922         }
05923     }
05924     free_lbuf(tbuff);
05925 
05926     // Nope, return the default if there is one.
05927     //
05928     if (  i < nfargs
05929        && fargs[i])
05930     {
05931         tbuff = replace_tokens(fargs[i], NULL, NULL, mbuff);
05932         str = tbuff;
05933         mux_exec(buff, bufc, executor, caller, enactor,
05934             EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
05935         free_lbuf(tbuff);
05936     }
05937     free_lbuf(mbuff);
05938 }
05939 
05940 /* ---------------------------------------------------------------------------
05941  * fun_switch: Return value based on pattern matching (ala @switch)
05942  * NOTE: This function expects that its arguments have not been evaluated.
05943  */
05944 
05945 static FUNCTION(fun_switch)
05946 {
05947     switch_handler
05948     (
05949         buff, bufc,
05950         executor, caller, enactor,
05951         fargs, nfargs,
05952         cargs, ncargs,
05953         true
05954     );
05955 }
05956 
05957 static FUNCTION(fun_case)
05958 {
05959     switch_handler
05960     (
05961         buff, bufc,
05962         executor, caller, enactor,
05963         fargs, nfargs,
05964         cargs, ncargs,
05965         false
05966     );
05967 }
05968 
05969 /*
05970  * ---------------------------------------------------------------------------
05971  * * fun_space: Make spaces.
05972  */
05973 
05974 static FUNCTION(fun_space)
05975 {
05976     UNUSED_PARAMETER(executor);
05977     UNUSED_PARAMETER(caller);
05978     UNUSED_PARAMETER(enactor);
05979     UNUSED_PARAMETER(cargs);
05980     UNUSED_PARAMETER(ncargs);
05981 
05982     // Validate request.
05983     //
05984     int num;
05985     if (nfargs == 0 || *fargs[0] == '\0')
05986     {
05987         num = 1;
05988     }
05989     else
05990     {
05991         num = mux_atol(fargs[0]);
05992         if (num == 0)
05993         {
05994             // If 'space(0)', 'space(00)', ..., then allow num == 0,
05995             // otherwise, we force to num to be 1.
05996             //
05997             if (!is_integer(fargs[0], NULL))
05998             {
05999                 num = 1;
06000             }
06001         }
06002         else if (num < 0)
06003         {
06004             num = 0;
06005         }
06006 
06007     }
06008     safe_fill(buff, bufc, ' ', num);
06009 }
06010 
06011 static FUNCTION(fun_height)
06012 {
06013     UNUSED_PARAMETER(caller);
06014     UNUSED_PARAMETER(enactor);
06015     UNUSED_PARAMETER(nfargs);
06016     UNUSED_PARAMETER(cargs);
06017     UNUSED_PARAMETER(ncargs);
06018 
06019     long nHeight = 24;
06020     if (is_rational(fargs[0]))
06021     {
06022         SOCKET s = mux_atol(fargs[0]);
06023         DESC *d;
06024         DESC_ITER_CONN(d)
06025         {
06026             if (d->descriptor == s)
06027             {
06028                 nHeight = d->height;
06029                 break;
06030             }
06031         }
06032     }
06033     else
06034     {
06035         char *pTargetName = fargs[0];
06036         if ('*' == *pTargetName)
06037         {
06038             pTargetName++;
06039         }
06040         dbref target = lookup_player(executor, pTargetName, true);
06041         if (Good_obj(target))
06042         {
06043             if (  executor == target
06044                || See_All(executor))
06045             {
06046                 nHeight = fetch_height(target);
06047             }
06048         }
06049     }
06050     safe_ltoa(nHeight, buff, bufc);
06051 }
06052 
06053 
06054 static FUNCTION(fun_width)
06055 {
06056     UNUSED_PARAMETER(caller);
06057     UNUSED_PARAMETER(enactor);
06058     UNUSED_PARAMETER(nfargs);
06059     UNUSED_PARAMETER(cargs);
06060     UNUSED_PARAMETER(ncargs);
06061 
06062     long nWidth = 24;
06063     if (is_rational(fargs[0]))
06064     {
06065         SOCKET s = mux_atol(fargs[0]);
06066         DESC *d;
06067         DESC_ITER_CONN(d)
06068         {
06069             if (d->descriptor == s)
06070             {
06071                 nWidth = d->width;
06072                 break;
06073             }
06074         }
06075     }
06076     else
06077     {
06078         char *pTargetName = fargs[0];
06079         if ('*' == *pTargetName)
06080         {
06081             pTargetName++;
06082         }
06083         dbref target = lookup_player(executor, pTargetName, true);
06084         if (Good_obj(target))
06085         {
06086             if (  executor == target
06087                || See_All(executor))
06088             {
06089                 nWidth = fetch_width(target);
06090             }
06091         }
06092     }
06093     safe_ltoa(nWidth, buff, bufc);
06094 }
06095 
06096 /*
06097  * ---------------------------------------------------------------------------
06098  * * fun_idle, fun_conn: return seconds idle or connected.
06099  */
06100 
06101 static FUNCTION(fun_idle)
06102 {
06103     UNUSED_PARAMETER(caller);
06104     UNUSED_PARAMETER(enactor);
06105     UNUSED_PARAMETER(nfargs);
06106     UNUSED_PARAMETER(cargs);
06107     UNUSED_PARAMETER(ncargs);
06108 
06109     long nIdle = -1;
06110     if (is_rational(fargs[0]))
06111     {
06112         SOCKET s = mux_atol(fargs[0]);
06113         bool bFound = false;
06114         DESC *d;
06115         CLinearTimeAbsolute ltaNow;
06116         ltaNow.GetUTC();
06117         DESC_ITER_CONN(d)
06118         {
06119             if (d->descriptor == s)
06120             {
06121                 bFound = true;
06122                 break;
06123             }
06124         }
06125         if (  bFound
06126            && (  d->player == executor
06127               || Wizard_Who(executor)))
06128         {
06129             CLinearTimeDelta ltdResult = ltaNow - d->last_time;
06130             nIdle = ltdResult.ReturnSeconds();
06131         }
06132     }
06133     else
06134     {
06135         char *pTargetName = fargs[0];
06136         if (*pTargetName == '*')
06137         {
06138             pTargetName++;
06139         }
06140         dbref target = lookup_player(executor, pTargetName, true);
06141         if (  Good_obj(target)
06142            && (  !Hidden(target)
06143               || See_Hidden(executor)))
06144         {
06145             nIdle = fetch_idle(target);
06146         }
06147     }
06148     safe_ltoa(nIdle, buff, bufc);
06149 }
06150 
06151 static FUNCTION(fun_conn)
06152 {
06153     UNUSED_PARAMETER(caller);
06154     UNUSED_PARAMETER(enactor);
06155     UNUSED_PARAMETER(nfargs);
06156     UNUSED_PARAMETER(cargs);
06157     UNUSED_PARAMETER(ncargs);
06158 
06159     long nConnected = -1;
06160     if (is_rational(fargs[0]))
06161     {
06162         SOCKET s = mux_atol(fargs[0]);
06163         bool bFound = false;
06164         DESC *d;
06165         CLinearTimeAbsolute ltaNow;
06166         ltaNow.GetUTC();
06167         DESC_ITER_CONN(d)
06168         {
06169             if (d->descriptor == s)
06170             {
06171                 bFound = true;
06172                 break;
06173             }
06174         }
06175         if (  bFound
06176            && (  d->player == executor
06177               || Wizard_Who(executor)))
06178         {
06179             CLinearTimeDelta ltdResult = ltaNow - d->connected_at;
06180             nConnected = ltdResult.ReturnSeconds();
06181         }
06182     }
06183     else
06184     {
06185         char *pTargetName = fargs[0];
06186         if (*pTargetName == '*')
06187         {
06188             pTargetName++;
06189         }
06190         dbref target = lookup_player(executor, pTargetName, true);
06191         if (  Good_obj(target)
06192            && (  !Hidden(target)
06193               || See_Hidden(executor)))
06194         {
06195             nConnected = fetch_connect(target);
06196         }
06197     }
06198     safe_ltoa(nConnected, buff, bufc);
06199 }
06200 
06201 /*
06202  * ---------------------------------------------------------------------------
06203  * * fun_sort: Sort lists.
06204  */
06205 
06206 typedef struct f_record
06207 {
06208     double data;
06209     char *str;
06210 } f_rec;
06211 
06212 typedef struct i_record
06213 {
06214     long data;
06215     char *str;
06216 } i_rec;
06217 
06218 typedef struct i64_record
06219 {
06220     INT64 data;
06221     char *str;
06222 } i64_rec;
06223 
06224 static int DCL_CDECL a_comp(const void *s1, const void *s2)
06225 {
06226     return strcmp(*(char **)s1, *(char **)s2);
06227 }
06228 
06229 static int DCL_CDECL a_casecomp(const void *s1, const void *s2)
06230 {
06231     return mux_stricmp(*(char **)s1, *(char **)s2);
06232 }
06233 
06234 static int DCL_CDECL f_comp(const void *s1, const void *s2)
06235 {
06236     if (((f_rec *) s1)->data > ((f_rec *) s2)->data)
06237     {
06238         return 1;
06239     }
06240     else if (((f_rec *) s1)->data < ((f_rec *) s2)->data)
06241     {
06242         return -1;
06243     }
06244     return 0;
06245 }
06246 
06247 static int DCL_CDECL i_comp(const void *s1, const void *s2)
06248 {
06249     if (((i_rec *) s1)->data > ((i_rec *) s2)->data)
06250     {
06251         return 1;
06252     }
06253     else if (((i_rec *) s1)->data < ((i_rec *) s2)->data)
06254     {
06255         return -1;
06256     }
06257     return 0;
06258 }
06259 
06260 static int DCL_CDECL i64_comp(const void *s1, const void *s2)
06261 {
06262     if (((i64_rec *) s1)->data > ((i64_rec *) s2)->data)
06263     {
06264         return 1;
06265     }
06266     else if (((i64_rec *) s1)->data < ((i64_rec *) s2)->data)
06267     {
06268         return -1;
06269     }
06270     return 0;
06271 }
06272 
06273 static void do_asort(char *s[], int n, int sort_type)
06274 {
06275     int i;
06276     f_rec *fp;
06277     i_rec *ip;
06278     i64_rec *i64p;
06279 
06280     switch (sort_type)
06281     {
06282     case ASCII_LIST:
06283         qsort(s, n, sizeof(char *), a_comp);
06284         break;
06285 
06286     case NUMERIC_LIST:
06287         i64p = (i64_rec *) MEMALLOC(n * sizeof(i64_rec));
06288         ISOUTOFMEMORY(i64p);
06289         for (i = 0; i < n; i++)
06290         {
06291             i64p[i].str = s[i];
06292             i64p[i].data = mux_atoi64(s[i]);
06293         }
06294         qsort(i64p, n, sizeof(i64_rec), i64_comp);
06295         for (i = 0; i < n; i++)
06296         {
06297             s[i] = i64p[i].str;
06298         }
06299         MEMFREE(i64p);
06300         i64p = NULL;
06301         break;
06302 
06303     case DBREF_LIST:
06304         ip = (i_rec *) MEMALLOC(n * sizeof(i_rec));
06305         ISOUTOFMEMORY(ip);
06306         for (i = 0; i < n; i++)
06307         {
06308             ip[i].str = s[i];
06309             ip[i].data = dbnum(s[i]);
06310         }
06311         qsort(ip, n, sizeof(i_rec), i_comp);
06312         for (i = 0; i < n; i++)
06313         {
06314             s[i] = ip[i].str;
06315         }
06316         MEMFREE(ip);
06317         ip = NULL;
06318         break;
06319 
06320     case FLOAT_LIST:
06321         fp = (f_rec *) MEMALLOC(n * sizeof(f_rec));
06322         ISOUTOFMEMORY(fp);
06323         for (i = 0; i < n; i++)
06324         {
06325             fp[i].str = s[i];
06326             fp[i].data = mux_atof(s[i], false);
06327         }
06328         qsort(fp, n, sizeof(f_rec), f_comp);
06329         for (i = 0; i < n; i++)
06330         {
06331             s[i] = fp[i].str;
06332         }
06333         MEMFREE(fp);
06334         fp = NULL;
06335         break;
06336 
06337     case CI_ASCII_LIST:
06338         qsort(s, n, sizeof(char *), a_casecomp);
06339         break;
06340     }
06341 }
06342 
06343 static FUNCTION(fun_sort)
06344 {
06345     SEP sep;
06346     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
06347     {
06348         return;
06349     }
06350 
06351     SEP osep = sep;
06352     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
06353     {
06354         return;
06355     }
06356 
06357     char *ptrs[LBUF_SIZE / 2];
06358 
06359     // Convert the list to an array.
06360     //
06361     char *list = alloc_lbuf("fun_sort");
06362     strcpy(list, fargs[0]);
06363     int nitems = list2arr(ptrs, LBUF_SIZE / 2, list, &sep);
06364     int sort_type = get_list_type(fargs, nfargs, 2, ptrs, nitems);
06365     do_asort(ptrs, nitems, sort_type);
06366     arr2list(ptrs, nitems, buff, bufc, &osep);
06367     free_lbuf(list);
06368 }
06369 
06370 /* ---------------------------------------------------------------------------
06371  * fun_setunion, fun_setdiff, fun_setinter: Set management.
06372  */
06373 
06374 #define SET_UNION     1
06375 #define SET_INTERSECT 2
06376 #define SET_DIFF      3
06377 
06378 static void handle_sets
06379 (
06380     char *fargs[],
06381     char *buff,
06382     char **bufc,
06383     int  oper,
06384     SEP  *psep,
06385     SEP  *posep
06386 )
06387 {
06388     char *ptrs1[LBUF_SIZE], *ptrs2[LBUF_SIZE];
06389     int val;
06390 
06391     char *list1 = alloc_lbuf("fun_setunion.1");
06392     strcpy(list1, fargs[0]);
06393     int n1 = list2arr(ptrs1, LBUF_SIZE, list1, psep);
06394     do_asort(ptrs1, n1, ASCII_LIST);
06395 
06396     char *list2 = alloc_lbuf("fun_setunion.2");
06397     strcpy(list2, fargs[1]);
06398     int n2 = list2arr(ptrs2, LBUF_SIZE, list2, psep);
06399     do_asort(ptrs2, n2, ASCII_LIST);
06400 
06401     int i1 = 0;
06402     int i2 = 0;
06403     char *oldp = NULL;
06404     bool bFirst = true;
06405 
06406     switch (oper)
06407     {
06408     case SET_UNION:
06409 
06410         // Copy elements common to both lists.
06411         //
06412         // Handle case of two identical single-element lists.
06413         //
06414         if (  n1 == 1
06415            && n2 == 1
06416            && strcmp(ptrs1[0], ptrs2[0]) == 0)
06417         {
06418             safe_str(ptrs1[0], buff, bufc);
06419             break;
06420         }
06421 
06422         // Process until one list is empty.
06423         //
06424         while (  i1 < n1
06425               && i2 < n2)
06426         {
06427             // Skip over duplicates.
06428             //
06429             if (  i1 > 0
06430                || i2 > 0)
06431             {
06432                 while (  i1 < n1
06433                       && oldp
06434                       && strcmp(ptrs1[i1], oldp) == 0)
06435                 {
06436                     i1++;
06437                 }
06438                 while (  i2 < n2
06439                       && oldp
06440                       && strcmp(ptrs2[i2], oldp) == 0)
06441                 {
06442                     i2++;
06443                 }
06444             }
06445 
06446             // Compare and copy.
06447             //
06448             if (  i1 < n1
06449                && i2 < n2)
06450             {
06451                 if (!bFirst)
06452                 {
06453                     print_sep(posep, buff, bufc);
06454                 }
06455                 bFirst = false;
06456                 if (strcmp(ptrs1[i1], ptrs2[i2]) < 0)
06457                 {
06458                     oldp = ptrs1[i1];
06459                     safe_str(ptrs1[i1], buff, bufc);
06460                     i1++;
06461                 }
06462                 else
06463                 {
06464                     oldp = ptrs2[i2];
06465                     safe_str(ptrs2[i2], buff, bufc);
06466                     i2++;
06467                 }
06468             }
06469         }
06470 
06471         // Copy rest of remaining list, stripping duplicates.
06472         //
06473         for (; i1 < n1; i1++)
06474         {
06475             if (  !oldp
06476                || strcmp(oldp, ptrs1[i1]) != 0)
06477             {
06478                 if (!bFirst)
06479                 {
06480                     print_sep(posep, buff, bufc);
06481                 }
06482                 bFirst = false;
06483                 oldp = ptrs1[i1];
06484                 safe_str(ptrs1[i1], buff, bufc);
06485             }
06486         }
06487         for (; i2 < n2; i2++)
06488         {
06489             if (  !oldp
06490                || strcmp(oldp, ptrs2[i2]) != 0)
06491             {
06492                 if (!bFirst)
06493                 {
06494                     print_sep(posep, buff, bufc);
06495                 }
06496                 bFirst = false;
06497                 oldp = ptrs2[i2];
06498                 safe_str(ptrs2[i2], buff, bufc);
06499             }
06500         }
06501         break;
06502 
06503     case SET_INTERSECT:
06504 
06505         // Copy elements not in both lists.
06506         //
06507         while (  i1 < n1
06508               && i2 < n2)
06509         {
06510             val = strcmp(ptrs1[i1], ptrs2[i2]);
06511             if (!val)
06512             {
06513                 // Got a match, copy it.
06514                 //
06515                 if (!bFirst)
06516                 {
06517                     print_sep(posep, buff, bufc);
06518                 }
06519                 bFirst = false;
06520                 oldp = ptrs1[i1];
06521                 safe_str(ptrs1[i1], buff, bufc);
06522                 i1++;
06523                 i2++;
06524                 while (  i1 < n1
06525                       && strcmp(ptrs1[i1], oldp) == 0)
06526                 {
06527                     i1++;
06528                 }
06529                 while (  i2 < n2
06530                       && strcmp(ptrs2[i2], oldp) == 0)
06531                 {
06532                     i2++;
06533                 }
06534             }
06535             else if (val < 0)
06536             {
06537                 i1++;
06538             }
06539             else
06540             {
06541                 i2++;
06542             }
06543         }
06544         break;
06545 
06546     case SET_DIFF:
06547 
06548         // Copy elements unique to list1.
06549         //
06550         while (  i1 < n1
06551               && i2 < n2)
06552         {
06553             val = strcmp(ptrs1[i1], ptrs2[i2]);
06554             if (!val)
06555             {
06556                 // Got a match, increment pointers.
06557                 //
06558                 oldp = ptrs1[i1];
06559                 while (  i1 < n1
06560                       && strcmp(ptrs1[i1], oldp) == 0)
06561                 {
06562                     i1++;
06563                 }
06564                 while (  i2 < n2
06565                       && strcmp(ptrs2[i2], oldp) == 0)
06566                 {
06567                     i2++;
06568                 }
06569             }
06570             else if (val < 0)
06571             {
06572                 // Item in list1 not in list2, copy.
06573                 //
06574                 if (!bFirst)
06575                 {
06576                     print_sep(posep, buff, bufc);
06577                 }
06578                 bFirst = false;
06579                 safe_str(ptrs1[i1], buff, bufc);
06580                 oldp = ptrs1[i1];
06581                 i1++;
06582                 while (  i1 < n1
06583                       && strcmp(ptrs1[i1], oldp) == 0)
06584                 {
06585                     i1++;
06586                 }
06587             }
06588             else
06589             {
06590                 // Item in list2 but not in list1, discard.
06591                 //
06592                 oldp = ptrs2[i2];
06593                 i2++;
06594                 while (  i2 < n2
06595                       && strcmp(ptrs2[i2], oldp) == 0)
06596                 {
06597                     i2++;
06598                 }
06599             }
06600         }
06601 
06602         // Copy remainder of list1.
06603         //
06604         while (i1 < n1)
06605         {
06606             if (!bFirst)
06607             {
06608                 print_sep(posep, buff, bufc);
06609             }
06610             bFirst = false;
06611             safe_str(ptrs1[i1], buff, bufc);
06612             oldp = ptrs1[i1];
06613             i1++;
06614             while (  i1 < n1
06615                   && strcmp(ptrs1[i1], oldp) == 0)
06616             {
06617                 i1++;
06618             }
06619         }
06620     }
06621     free_lbuf(list1);
06622     free_lbuf(list2);
06623 }
06624 
06625 static FUNCTION(fun_setunion)
06626 {
06627     SEP sep;
06628     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
06629     {
06630         return;
06631     }
06632 
06633     SEP osep = sep;
06634     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
06635     {
06636         return;
06637     }
06638     handle_sets(fargs, buff, bufc, SET_UNION, &sep, &osep);
06639 }
06640 
06641 static FUNCTION(fun_setdiff)
06642 {
06643     SEP sep;
06644     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
06645     {
06646         return;
06647     }
06648 
06649     SEP osep = sep;
06650     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
06651     {
06652         return;
06653     }
06654     handle_sets(fargs, buff, bufc, SET_DIFF, &sep, &osep);
06655 }
06656 
06657 static FUNCTION(fun_setinter)
06658 {
06659     SEP sep;
06660     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
06661     {
06662         return;
06663     }
06664 
06665     SEP osep = sep;
06666     if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_INIT|DELIM_STRING))
06667     {
06668         return;
06669     }
06670     handle_sets(fargs, buff, bufc, SET_INTERSECT, &sep, &osep);
06671 }
06672 
06673 /* ---------------------------------------------------------------------------
06674  * rjust, ljust, center: Justify or center text, specifying fill character.
06675  */
06676 #define CJC_CENTER 0
06677 #define CJC_LJUST  1
06678 #define CJC_RJUST  2
06679 
06680 static void centerjustcombo
06681 (
06682     int iType,
06683     char *buff,
06684     char **bufc,
06685     char *fargs[],
06686     int nfargs
06687 )
06688 {
06689     // Width must be a number.
06690     //
06691     if (!is_integer(fargs[1], NULL))
06692     {
06693         return;
06694     }
06695     int width = mux_atol(fargs[1]);
06696     if (width <= 0 || LBUF_SIZE <= width)
06697     {
06698         safe_range(buff, bufc);
06699         return;
06700     }
06701 
06702     // Determine string to pad with.
06703     //
06704     int  vwPad = 0;
06705     int  nPad = -1;
06706     char aPad[SBUF_SIZE];
06707     struct ANSI_In_Context  aic;
06708     struct ANSI_Out_Context aoc;
06709     if (nfargs == 3 && *fargs[2])
06710     {
06711         char *p = RemoveSetOfCharacters(fargs[2], "\r\n\t");
06712         ANSI_String_In_Init(&aic, p, ANSI_ENDGOAL_NORMAL);
06713         ANSI_String_Out_Init(&aoc, aPad, sizeof(aPad), sizeof(aPad), ANSI_ENDGOAL_LEAK);
06714         ANSI_String_Copy(&aoc, &aic, sizeof(aPad));
06715         nPad = ANSI_String_Finalize(&aoc, &vwPad);
06716     }
06717     if (nPad <= 0)
06718     {
06719         aPad[0] = ' ';
06720         aPad[1] = '\0';
06721         nPad    = 1;
06722         vwPad   = 1;
06723     }
06724 
06725     int  vwStr;
06726     char aStr[LBUF_SIZE];
06727     int nStr = ANSI_TruncateToField(fargs[0], sizeof(aStr), aStr,
06728         width, &vwStr, ANSI_ENDGOAL_NORMAL);
06729 
06730     // If the visual width of the text fits exactly into the field,
06731     // then we are done. ANSI_TruncateToField insures that it's
06732     // never larger.
06733     //
06734     if (vwStr == width)
06735     {
06736         safe_copy_buf(aStr, nStr, buff, bufc);
06737         return;
06738     }
06739 
06740     int vwLeading = 0;
06741     if (iType == CJC_CENTER)
06742     {
06743         vwLeading = (width - vwStr)/2;
06744     }
06745     else if (iType == CJC_RJUST)
06746     {
06747         vwLeading = width - vwStr;
06748     }
06749     int vwTrailing      = width - vwLeading - vwStr;
06750 
06751     // Shortcut this function if nPad == 1 (i.e., the padding is a single
06752     // character).
06753     //
06754     if (nPad == 1 && vwPad == 1)
06755     {
06756         safe_fill(buff, bufc, aPad[0], vwLeading);
06757         safe_copy_buf(aStr, nStr, buff, bufc);
06758         safe_fill(buff, bufc, aPad[0], vwTrailing);
06759         return;
06760     }
06761 
06762 
06763     // Calculate the necessary info about the leading padding.
06764     // The origin on the padding is at byte 0 at beginning of the
06765     // field (this may cause mis-syncronization on the screen if
06766     // the same background padding string is used on several lines
06767     // with each idented from column 0 by a different amount.
06768     // There is nothing center() can do about this issue. You are
06769     // on your own.
06770     //
06771     // Padding is repeated nLeadFull times and then a partial string
06772     // of vwLeadPartial visual width is tacked onto the end.
06773     //
06774     // vwLeading == nLeadFull * vwPad + vwLeadPartial
06775     //
06776     int nLeadFull     = 0;
06777     int vwLeadPartial = 0;
06778     if (vwLeading)
06779     {
06780         nLeadFull     = vwLeading / vwPad;
06781         vwLeadPartial = vwLeading - nLeadFull * vwPad;
06782     }
06783 
06784     // Calculate the necessary info about the trailing padding.
06785     //
06786     // vwTrailing == vwTrailPartial0 + nTrailFull * vwPad
06787     //             + vwTrailPartial1
06788     //
06789     int vwTrailSkip0    = 0;
06790     int vwTrailPartial0 = 0;
06791     int nTrailFull      = 0;
06792     int vwTrailPartial1 = 0;
06793     if (vwTrailing)
06794     {
06795         vwTrailSkip0    = (vwLeading + vwStr) % vwPad;
06796         vwTrailPartial0 = 0;
06797         if (vwTrailSkip0)
06798         {
06799             vwTrailPartial0 = vwPad - vwTrailSkip0;
06800             if (vwTrailing < vwTrailPartial0)
06801             {
06802                 vwTrailPartial0 = vwTrailing;
06803                 vwTrailing = 0;
06804             }
06805             else
06806             {
06807                 vwTrailing -= vwTrailPartial0;
06808             }
06809         }
06810         nTrailFull      = vwTrailing / vwPad;
06811         vwTrailPartial1 = vwTrailing - nTrailFull * vwPad;
06812     }
06813 
06814     int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
06815     ANSI_String_Out_Init(&aoc, *bufc, nBufferAvailable,
06816         LBUF_SIZE-1, ANSI_ENDGOAL_NORMAL);
06817     int    vwDone;
06818 
06819     // Output the runs of full leading padding.
06820     //
06821     int i, n;
06822     for (i = 0; i < nLeadFull; i++)
06823     {
06824         ANSI_String_In_Init(&aic, aPad, ANSI_ENDGOAL_NORMAL);
06825         ANSI_String_Copy(&aoc, &aic, vwPad);
06826     }
06827 
06828     // Output the partial leading padding segment.
06829     //
06830     if (vwLeadPartial > 0)
06831     {
06832         ANSI_String_In_Init(&aic, aPad, ANSI_ENDGOAL_NORMAL);
06833         ANSI_String_Copy(&aoc, &aic, vwLeadPartial);
06834     }
06835 
06836     // Output the main string to be centered.
06837     //
06838     if (nStr > 0)
06839     {
06840         ANSI_String_In_Init(&aic, aStr, ANSI_ENDGOAL_NORMAL);
06841         ANSI_String_Copy(&aoc, &aic, LBUF_SIZE-1);
06842     }
06843 
06844     // Output the first partial trailing padding segment.
06845     //
06846     if (vwTrailPartial0 > 0)
06847     {
06848         ANSI_String_In_Init(&aic, aPad, ANSI_ENDGOAL_NORMAL);
06849         ANSI_String_Skip(&aic, vwTrailSkip0, &vwDone);
06850         ANSI_String_Copy(&aoc, &aic, vwTrailPartial0);
06851     }
06852 
06853     // Output the runs of full trailing padding.
06854     //
06855     for (i = 0; i < nTrailFull; i++)
06856     {
06857         ANSI_String_In_Init(&aic, aPad, ANSI_ENDGOAL_NORMAL);
06858         ANSI_String_Copy(&aoc, &aic, vwPad);
06859     }
06860 
06861     // Output the second partial trailing padding segment.
06862     //
06863     if (vwTrailPartial1 > 0)
06864     {
06865         ANSI_String_In_Init(&aic, aPad, ANSI_ENDGOAL_NORMAL);
06866         ANSI_String_Copy(&aoc, &aic, vwTrailPartial1);
06867     }
06868 
06869     n = ANSI_String_Finalize(&aoc, &vwDone);
06870     *bufc += n;
06871 }
06872 
06873 static FUNCTION(fun_ljust)
06874 {
06875     UNUSED_PARAMETER(executor);
06876     UNUSED_PARAMETER(caller);
06877     UNUSED_PARAMETER(enactor);
06878     UNUSED_PARAMETER(cargs);
06879     UNUSED_PARAMETER(ncargs);
06880 
06881     centerjustcombo(CJC_LJUST, buff, bufc, fargs, nfargs);
06882 }
06883 
06884 static FUNCTION(fun_rjust)
06885 {
06886     UNUSED_PARAMETER(executor);
06887     UNUSED_PARAMETER(caller);
06888     UNUSED_PARAMETER(enactor);
06889     UNUSED_PARAMETER(cargs);
06890     UNUSED_PARAMETER(ncargs);
06891 
06892     centerjustcombo(CJC_RJUST, buff, bufc, fargs, nfargs);
06893 }
06894 
06895 static FUNCTION(fun_center)
06896 {
06897     UNUSED_PARAMETER(executor);
06898     UNUSED_PARAMETER(caller);
06899     UNUSED_PARAMETER(enactor);
06900     UNUSED_PARAMETER(cargs);
06901     UNUSED_PARAMETER(ncargs);
06902 
06903     centerjustcombo(CJC_CENTER, buff, bufc, fargs, nfargs);
06904 }
06905 
06906 /* ---------------------------------------------------------------------------
06907  * setq, setr, r: set and read global registers.
06908  */
06909 
06910 static FUNCTION(fun_setq)
06911 {
06912     UNUSED_PARAMETER(executor);
06913     UNUSED_PARAMETER(caller);
06914     UNUSED_PARAMETER(enactor);
06915     UNUSED_PARAMETER(nfargs);
06916     UNUSED_PARAMETER(cargs);
06917     UNUSED_PARAMETER(ncargs);
06918 
06919     int regnum = mux_RegisterSet[(unsigned char)fargs[0][0]];
06920     if (  regnum < 0
06921        || regnum >= MAX_GLOBAL_REGS
06922        || fargs[0][1] != '\0')
06923     {
06924         safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc);
06925     }
06926     else
06927     {
06928         if (!mudstate.global_regs[regnum])
06929         {
06930             mudstate.global_regs[regnum] = alloc_lbuf("fun_setq");
06931         }
06932         int n = strlen(fargs[1]);
06933         memcpy(mudstate.global_regs[regnum], fargs[1], n+1);
06934         mudstate.glob_reg_len[regnum] = n;
06935     }
06936 }
06937 
06938 static FUNCTION(fun_setr)
06939 {
06940     UNUSED_PARAMETER(executor);
06941     UNUSED_PARAMETER(caller);
06942     UNUSED_PARAMETER(enactor);
06943     UNUSED_PARAMETER(nfargs);
06944     UNUSED_PARAMETER(cargs);
06945     UNUSED_PARAMETER(ncargs);
06946 
06947     int regnum = mux_RegisterSet[(unsigned char)fargs[0][0]];
06948     if (  regnum < 0
06949        || regnum >= MAX_GLOBAL_REGS
06950        || fargs[0][1] != '\0')
06951     {
06952         safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc);
06953     }
06954     else
06955     {
06956         if (!mudstate.global_regs[regnum])
06957         {
06958             mudstate.global_regs[regnum] = alloc_lbuf("fun_setq");
06959         }
06960         int n = strlen(fargs[1]);
06961         memcpy(mudstate.global_regs[regnum], fargs[1], n+1);
06962         mudstate.glob_reg_len[regnum] = n;
06963         safe_copy_buf(fargs[1], n, buff, bufc);
06964     }
06965 }
06966 
06967 static FUNCTION(fun_r)
06968 {
06969     UNUSED_PARAMETER(executor);
06970     UNUSED_PARAMETER(caller);
06971     UNUSED_PARAMETER(enactor);
06972     UNUSED_PARAMETER(nfargs);
06973     UNUSED_PARAMETER(cargs);
06974     UNUSED_PARAMETER(ncargs);
06975 
06976     int regnum = mux_RegisterSet[(unsigned char)fargs[0][0]];
06977     if (  regnum < 0
06978        || regnum >= MAX_GLOBAL_REGS
06979        || fargs[0][1] != '\0')
06980     {
06981         safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc);
06982     }
06983     else if (mudstate.global_regs[regnum])
06984     {
06985         safe_copy_buf(mudstate.global_regs[regnum],
06986             mudstate.glob_reg_len[regnum], buff, bufc);
06987     }
06988 }
06989 
06990 /* ---------------------------------------------------------------------------
06991  * isdbref: is the argument a valid dbref?
06992  */
06993 
06994 static FUNCTION(fun_isdbref)
06995 {
06996     UNUSED_PARAMETER(executor);
06997     UNUSED_PARAMETER(caller);
06998     UNUSED_PARAMETER(enactor);
06999     UNUSED_PARAMETER(nfargs);
07000     UNUSED_PARAMETER(cargs);
07001     UNUSED_PARAMETER(ncargs);
07002 
07003     bool bResult = false;
07004 
07005     char *p = fargs[0];
07006     if (NUMBER_TOKEN == p[0])
07007     {
07008         p++;
07009         dbref dbitem = parse_dbref(p);
07010         bResult = Good_obj(dbitem);
07011     }
07012     safe_bool(bResult, buff, bufc);
07013 }
07014 
07015 /* ---------------------------------------------------------------------------
07016  * trim: trim off unwanted white space.
07017  */
07018 
07019 static char* trim_fast_left(char* str, char delim)
07020 {
07021     // We assume delim is never '\0'
07022     //
07023     while (*str == delim)
07024     {
07025         str++;
07026     }
07027     return str;
07028 }
07029 
07030 static void trim_fast_right(char* str, char delim)
07031 {
07032     // We assume delim is never '\0'
07033     //
07034     char* last = NULL;
07035     while (*str)
07036     {
07037         if (*str != delim)
07038         {
07039             last = str;
07040         }
07041         str++;
07042     }
07043 
07044     if (last == NULL)
07045     {
07046         return;
07047     }
07048 
07049     *(last+1) = '\0';
07050 }
07051 
07052 static char* trim_left(char* str, SEP* sep)
07053 {
07054     if (1 == sep->n)
07055     {
07056         return trim_fast_left(str, sep->str[0]);
07057     }
07058     int cycle = 0;
07059     int max = sep->n;
07060     char* base = str-1;
07061     for ( ; *str == sep->str[cycle]; str++)
07062     {
07063         if (max <= ++cycle)
07064         {
07065             cycle = 0;
07066             base = str;
07067         }
07068     }
07069     return base+1;
07070 }
07071 
07072 static void trim_right(char* str, SEP* sep)
07073 {
07074     if (1 == sep->n)
07075     {
07076         trim_fast_right(str,sep->str[0]);
07077         return;
07078     }
07079 
07080     int cycle = sep->n - 1;
07081     int max = sep->n - 1;
07082     int n = strlen(str);
07083     int base = n;
07084     n--;
07085     for ( ; n >= 0 && str[n] == sep->str[cycle]; n--)
07086     {
07087         if (--cycle < 0)
07088         {
07089             cycle = max;
07090             base = n;
07091         }
07092     }
07093     *(str+base) = '\0';
07094 }
07095 
07096 static FUNCTION(fun_trim)
07097 {
07098     SEP sep;
07099     if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
07100     {
07101         return;
07102     }
07103 
07104 #define TRIM_LEFT  1
07105 #define TRIM_RIGHT 2
07106 
07107     int trim;
07108     if (nfargs >= 2)
07109     {
07110         switch (mux_tolower(*fargs[1]))
07111         {
07112         case 'l':
07113             trim = TRIM_LEFT;
07114             break;
07115 
07116         case 'r':
07117             trim = TRIM_RIGHT;
07118             break;
07119 
07120         default:
07121             trim = TRIM_LEFT|TRIM_RIGHT;
07122             break;
07123         }
07124     }
07125     else
07126     {
07127         trim = TRIM_LEFT|TRIM_RIGHT;
07128     }
07129 
07130     char* str;
07131     if (trim & TRIM_LEFT)
07132     {
07133         str = trim_left(fargs[0],&sep);
07134     }
07135     else
07136     {
07137         str = fargs[0];
07138     }
07139 
07140     if (trim & TRIM_RIGHT)
07141     {
07142         trim_right(str,&sep);
07143     }
07144     safe_str(str,buff,bufc);
07145 }
07146 
07147 static FUNCTION(fun_config)
07148 {
07149     UNUSED_PARAMETER(caller);
07150     UNUSED_PARAMETER(enactor);
07151     UNUSED_PARAMETER(cargs);
07152     UNUSED_PARAMETER(ncargs);
07153 
07154     if (nfargs == 1)
07155     {
07156         cf_display(executor, fargs[0], buff, bufc);
07157     }
07158     else
07159     {
07160         cf_list(executor, buff, bufc);
07161     }
07162 }
07163 
07164 // ---------------------------------------------------------------------------
07165 // fun_bittype adapted from RhostMUSH. Used with permission.
07166 //
07167 static int return_bit(dbref player)
07168 {
07169    if (God(player))
07170       return 7;
07171    // 6 is Rhost Immortal. We don't have an equivalent (yet?).
07172    if (Wizard(player))
07173       return 5;
07174    if (Royalty(player))
07175       return 4;
07176    if (Staff(player) || Builder(player))
07177       return 3;
07178    if (Head(player) || Immortal(player))
07179       return 2;
07180    if (!(Uninspected(player) || Guest(player)))
07181       return 1;
07182    return 0;
07183 }
07184 
07185 static FUNCTION(fun_bittype)
07186 {
07187     UNUSED_PARAMETER(caller);
07188     UNUSED_PARAMETER(enactor);
07189     UNUSED_PARAMETER(cargs);
07190     UNUSED_PARAMETER(ncargs);
07191 
07192     dbref target;
07193     if (nfargs == 1)
07194     {
07195         target = match_thing(executor, fargs[0]);
07196     }
07197     else
07198     {
07199         target = executor;
07200     }
07201     if (!Good_obj(target))
07202     {
07203         return;
07204     }
07205     safe_ltoa(return_bit(target), buff, bufc);
07206 }
07207 
07208 static FUNCTION(fun_error)
07209 {
07210     UNUSED_PARAMETER(executor);
07211     UNUSED_PARAMETER(cargs);
07212     UNUSED_PARAMETER(ncargs);
07213 
07214     if (  Good_obj(mudconf.global_error_obj)
07215        && !Going(mudconf.global_error_obj))
07216     {
07217         dbref aowner;
07218         int aflags;
07219         char *errtext = atr_get(mudconf.global_error_obj, A_VA, &aowner, &aflags);
07220         char *errbuff = alloc_lbuf("process_command.error_msg");
07221         char *errbufc = errbuff;
07222         char *str = errtext;
07223         if (nfargs == 1)
07224         {
07225             char *arg = fargs[0];
07226             mux_exec(errbuff, &errbufc, mudconf.global_error_obj, caller, enactor,
07227                 EV_TOP | EV_EVAL | EV_FCHECK | EV_STRIP_CURLY, &str, &arg, 1);
07228             *errbufc = '\0';
07229         }
07230         else
07231         {
07232             mux_exec(errbuff, &errbufc, mudconf.global_error_obj, caller, enactor,
07233                 EV_TOP | EV_EVAL | EV_FCHECK | EV_STRIP_CURLY, &str, (char **)NULL, 0);
07234             *errbufc = '\0';
07235         }
07236         safe_str(errbuff, buff, bufc);
07237         free_lbuf(errtext);
07238         free_lbuf(errbuff);
07239     }
07240     else
07241     {
07242         safe_str("Huh?  (Type \"help\" for help.)", buff, bufc);
07243     }
07244 }
07245 
07246 static FUNCTION(fun_strip)
07247 {
07248     UNUSED_PARAMETER(executor);
07249     UNUSED_PARAMETER(caller);
07250     UNUSED_PARAMETER(enactor);
07251     UNUSED_PARAMETER(cargs);
07252     UNUSED_PARAMETER(ncargs);
07253 
07254     if (fargs[0][0] == '\0')
07255     {
07256         return;
07257     }
07258     size_t n;
07259     char  *p = strip_ansi(fargs[0], &n);
07260     if (  nfargs < 2
07261        || fargs[1][0] == '\0')
07262     {
07263         safe_copy_buf(p, n, buff, bufc);
07264         return;
07265     }
07266     char *pInput = alloc_lbuf("fun_strip.1");
07267     memcpy(pInput, p, n+1);
07268     p = strip_ansi(fargs[1], &n);
07269     safe_str(RemoveSetOfCharacters(pInput, p), buff, bufc);
07270     free_lbuf(pInput);
07271 }
07272 
07273 #define DEFAULT_WIDTH 78
07274 static char *expand_tabs(const char *str)
07275 {
07276     static char tbuf1[LBUF_SIZE];
07277     char *bp = tbuf1;
07278 
07279     if (str)
07280     {
07281         unsigned int n = 0;
07282         bool ansi = false;
07283 
07284         for (unsigned int i = 0; str[i]; i++)
07285         {
07286             switch (str[i])
07287             {
07288             case '\t':
07289                 safe_fill(tbuf1, &bp, ' ', 8 - n % 8);
07290                 continue;
07291             case '\r':
07292                 // FALL THROUGH
07293             case '\n':
07294                 n = 0;
07295                 break;
07296             case ESC_CHAR:
07297                 ansi = true;
07298                 break;
07299             case ANSI_ATTR_CMD:
07300                 if (ansi)
07301                 {
07302                     ansi = false;
07303                 }
07304                 else
07305                 {
07306                     n++;
07307                 }
07308                 break;
07309             case BEEP_CHAR:
07310                 break;
07311             default:
07312                 if (!ansi)
07313                 {
07314                     n++;
07315                 }
07316             }
07317             safe_chr(str[i], tbuf1, &bp);
07318         }
07319     }
07320     *bp = '\0';
07321     return tbuf1;
07322 }
07323 
07324 static int wraplen(char *str, const int nWidth, bool &newline)
07325 {
07326     const int length = strlen(str);
07327     newline = false;
07328     if (length <= nWidth)
07329     {
07330         /* Find the first return char
07331         * so %r will not mess with any alignment
07332         * functions.
07333         */
07334         for (int i = 0; i < length; i++)
07335         {
07336             if (  str[i] == '\n'
07337                || str[i] == '\r')
07338             {
07339                 newline = true;
07340                 return i+2;
07341             }
07342         }
07343         return length;
07344     }
07345 
07346     /* Find the first return char
07347     * so %r will not mess with any alignment
07348     * functions.
07349     */
07350     for (int i = 0; i < nWidth; i++)
07351     {
07352         if (  str[i] == '\n'
07353            || str[i] == '\r')
07354         {
07355             newline = true;
07356             return i+2;
07357         }
07358     }
07359 
07360     /* No return char was found. Now
07361     * find the last space in str.
07362     */
07363     int maxlen = nWidth;
07364     while (str[maxlen] != ' ' && maxlen > 0)
07365     {
07366         maxlen--;
07367     }
07368     if (str[maxlen] != ' ')
07369     {
07370         maxlen = nWidth;
07371     }
07372     return (maxlen ? maxlen : -1);
07373 }
07374 
07375 static FUNCTION(fun_wrap)
07376 {
07377     UNUSED_PARAMETER(executor);
07378     UNUSED_PARAMETER(caller);
07379     UNUSED_PARAMETER(enactor);
07380     UNUSED_PARAMETER(cargs);
07381     UNUSED_PARAMETER(ncargs);
07382 
07383     // ARG 2: Width. Default: 78.
07384     //
07385     int nWidth = DEFAULT_WIDTH;
07386     if (  nfargs >= 2
07387        && fargs[1][0])
07388     {
07389         nWidth = mux_atol(fargs[1]);
07390         if (  nWidth < 1
07391            || nWidth >= LBUF_SIZE)
07392         {
07393             safe_range(buff, bufc);
07394             return;
07395         }
07396     }
07397 
07398     // ARG 3: Justification. Default: Left.
07399     //
07400     int iJustKey = CJC_LJUST;
07401     if (  nfargs >= 3
07402        && fargs[2][0])
07403     {
07404         char cJust = mux_toupper(fargs[2][0]);
07405         switch (cJust)
07406         {
07407         case 'L':
07408             iJustKey = CJC_LJUST;
07409             break;
07410         case 'R':
07411             iJustKey = CJC_RJUST;
07412             break;
07413         case 'C':
07414             iJustKey = CJC_CENTER;
07415             break;
07416         default:
07417             safe_str("#-1 INVALID JUSTIFICATION SPECIFIED", buff, bufc);
07418             return;
07419         }
07420     }
07421 
07422     // ARG 4: Left padding. Default: blank.
07423     //
07424     char *pLeft = NULL;
07425     if (  nfargs >= 4
07426        && fargs[3][0])
07427     {
07428         pLeft = fargs[3];
07429     }
07430 
07431     // ARG 5: Right padding. Default: blank.
07432     //
07433     char *pRight = NULL;
07434     if (  nfargs >= 5
07435        && fargs[4][0])
07436     {
07437         pRight = fargs[4];
07438     }
07439 
07440     // ARG 6: Hanging indent. Default: 0.
07441     //
07442     int nHanging = 0;
07443     if (  nfargs >= 6
07444        && fargs[5][0])
07445     {
07446         nHanging = mux_atol(fargs[5]);
07447     }
07448 
07449     // ARG 7: Output separator. Default: line break.
07450     //
07451     char *pOSep = "\r\n";
07452     if (  nfargs >= 7
07453        && fargs[6][0])
07454     {
07455         if (!strcmp(fargs[6], "@@"))
07456         {
07457             pOSep = NULL;
07458         }
07459         else
07460         {
07461             pOSep = fargs[6];
07462         }
07463     }
07464 
07465     // ARG 8: First line width. Default: same as arg 2.
07466     int nFirstWidth = nWidth;
07467     if (  nfargs >= 8
07468        && fargs[7][0])
07469     {
07470         nFirstWidth = mux_atol(fargs[7]);
07471         if (  nFirstWidth < 1
07472            || nFirstWidth >= LBUF_SIZE)
07473         {
07474             safe_range(buff, bufc);
07475             return;
07476         }
07477     }
07478 
07479     char *str = alloc_lbuf("fun_mywrap.str");
07480     char *tstr = alloc_lbuf("fun_mywrap.str2");
07481     strcpy(tstr, expand_tabs(fargs[0]));
07482     strcpy(str,strip_ansi(tstr));
07483     int nLength = 0;
07484     bool newline = false;
07485     char *jargs[2];
07486     struct ANSI_In_Context aic;
07487     struct ANSI_Out_Context aoc;
07488     char *mbufc;
07489     char *mbuf = mbufc = alloc_lbuf("fun_mywrap.out");
07490     int nBufferAvailable, nSize;
07491     int nDone;
07492     int i = 0;
07493 
07494     while (str[i])
07495     {
07496         nLength = wraplen(str + i, i == 0 ? nFirstWidth : nWidth, newline);
07497         mbufc = mbuf;
07498 
07499         ANSI_String_In_Init(&aic, tstr, ANSI_ENDGOAL_NORMAL);
07500         ANSI_String_Skip(&aic, i, &nDone);
07501         if (nDone < i || nLength <= 0)
07502         {
07503             break;
07504         }
07505         if (i > 0)
07506         {
07507             safe_str(pOSep, buff, bufc);
07508             if (nHanging > 0)
07509             {
07510                 safe_fill(buff, bufc, ' ', nHanging);
07511             }
07512         }
07513         nBufferAvailable = LBUF_SIZE - (mbufc - mbuf) - 1;
07514         ANSI_String_Out_Init(&aoc, mbufc, nBufferAvailable, nLength-(newline ? 2 : 0), ANSI_ENDGOAL_NORMAL);
07515         ANSI_String_Copy(&aoc, &aic, nLength-(newline ? 2 : 0));
07516         nSize = ANSI_String_Finalize(&aoc, &nDone);
07517         mbufc += nSize;
07518 
07519         jargs[0] = mbuf;
07520         jargs[1] = mux_ltoa_t(i == 0 ? nFirstWidth : nWidth);
07521         safe_str(pLeft,buff,bufc);
07522         centerjustcombo(iJustKey, buff, bufc, jargs, 2);
07523         safe_str(pRight, buff, bufc);
07524 
07525         i += nLength;
07526         if (str[i] == ' ' && str[i+1] != ' ')
07527         {
07528             i++;
07529         }
07530     }
07531     free_lbuf(mbuf);
07532     free_lbuf(str);
07533     free_lbuf(tstr);
07534 }
07535 
07536 typedef struct
07537 {
07538     int  iBase;
07539     char chLetter;
07540     int  nName;
07541     char *pName;
07542 
07543 } RADIX_ENTRY;
07544 
07545 #define N_RADIX_ENTRIES 7
07546 static const RADIX_ENTRY reTable[N_RADIX_ENTRIES] =
07547 {
07548     { 31556926, 'y', 4, "year"   },  // Average solar year.
07549     {  2629743, 'M', 5, "month"  },  // Average month.
07550     {   604800, 'w', 4, "week"   },  // 7 days.
07551     {    86400, 'd', 3, "day"    },
07552     {     3600, 'h', 4, "hour"   },
07553     {       60, 'm', 6, "minute" },
07554     {        1, 's', 6, "second" }
07555 };
07556 
07557 #define IYEARS   0
07558 #define IMONTHS  1
07559 #define IWEEKS   2
07560 #define IDAYS    3
07561 #define IHOURS   4
07562 #define IMINUTES 5
07563 #define ISECONDS 6
07564 
07565 // This routine supports most of the time formats using the above
07566 // table.
07567 //
07568 static void GeneralTimeConversion
07569 (
07570     char *Buffer,
07571     long Seconds,
07572     int iStartBase,
07573     int iEndBase,
07574     bool bSingleTerm,
07575     bool bNames
07576 )
07577 {
07578     if (Seconds < 0)
07579     {
07580         Seconds = 0;
07581     }
07582 
07583     char *p = Buffer;
07584     int iValue;
07585 
07586     for (int i = iStartBase; i <= iEndBase; i++)
07587     {
07588         if (reTable[i].iBase <= Seconds || i == iEndBase)
07589         {
07590 
07591             // Division and remainder.
07592             //
07593             iValue = Seconds/reTable[i].iBase;
07594             Seconds -= iValue * reTable[i].iBase;
07595 
07596             if (iValue != 0 || i == iEndBase)
07597             {
07598                 if (p != Buffer)
07599                 {
07600                     *p++ = ' ';
07601                 }
07602                 p += mux_ltoa(iValue, p);
07603                 if (bNames)
07604                 {
07605                     // Use the names with the correct pluralization.
07606                     //
07607                     *p++ = ' ';
07608                     memcpy(p, reTable[i].pName, reTable[i].nName);
07609                     p += reTable[i].nName;
07610                     if (iValue != 1)
07611                     {
07612                         // More than one or zero.
07613                         //
07614                         *p++ = 's';
07615                     }
07616                 }
07617                 else
07618                 {
07619                     *p++ = reTable[i].chLetter;
07620                 }
07621             }
07622             if (bSingleTerm)
07623             {
07624                 break;
07625             }
07626         }
07627     }
07628     *p++ = '\0';
07629 }
07630 
07631 // These buffers are used by:
07632 //
07633 //     digit_format  (23 bytes) uses TimeBuffer80,
07634 //     time_format_1 (12 bytes) uses TimeBuffer80,
07635 //     time_format_2 (17 bytes) uses TimeBuffer64,
07636 //     expand_time   (33 bytes) uses TimeBuffer64,
07637 //     write_time    (69 bytes) uses TimeBuffer80.
07638 //
07639 // time_format_1 and time_format_2 are called from within the same
07640 // printf, so they must use different buffers.
07641 //
07642 // We pick 64 as a round number.
07643 //
07644 static char TimeBuffer64[64];
07645 static char TimeBuffer80[80];
07646 
07647 // Show time in days, hours, and minutes
07648 //
07649 // 2^63/86400 is 1.07E14 which is at most 15 digits.
07650 // '(15)d (2):(2)\0' is at most 23 characters.
07651 //
07652 static const char *digit_format(int Seconds)
07653 {
07654     if (Seconds < 0)
07655     {
07656         Seconds = 0;
07657     }
07658 
07659     // We are showing the time in minutes. 59s --> 0m
07660     //
07661 
07662     // Divide the time down into days, hours, and minutes.
07663     //
07664     int Days = Seconds / 86400;
07665     Seconds -= Days * 86400;
07666 
07667     int Hours = Seconds / 3600;
07668     Seconds -= Hours * 3600;
07669 
07670     int Minutes = Seconds / 60;
07671 
07672     if (Days > 0)
07673     {
07674         sprintf(TimeBuffer80, "%dd %02d:%02d", Days, Hours, Minutes);
07675     }
07676     else
07677     {
07678         sprintf(TimeBuffer80, "%02d:%02d", Hours, Minutes);
07679     }
07680     return TimeBuffer80;
07681 }
07682 
07683 // Show time in one of the following formats limited by a width of 8, 9, 10,
07684 // or 11 places and depending on the value to display:
07685 //
07686 // Width:8
07687 //         Z9:99            0 to         86,399
07688 //      9d 99:99       86,400 to        863,999
07689 //      ZZ9d 99h      864,000 to     86,396,459
07690 //      ZZZ9w 9d   86,396,460 to  6,047,913,659
07691 //
07692 // Width:9
07693 //         Z9:99            0 to         86,399
07694 //     Z9d 99:99       86,400 to      8,639,999
07695 //     ZZZ9d 99h    8,640,000 to    863,996,459
07696 //      ZZZ9w 9d  863,996,460 to  6,047,913,659
07697 //
07698 // Width:10
07699 //         Z9:99            0 to         86,399
07700 //    ZZ9d 99:99       86,400 to     86,399,999
07701 //    ZZZZ9d 99h   86,400,000 to  8,639,996,459
07702 //
07703 // Width:11
07704 //         Z9:99            0 to         86,399
07705 //   ZZZ9d 99:99       86,400 to    863,999,999
07706 //   ZZZZZ9d 99h  864,000,000 to 86,399,996,459
07707 //
07708 static int tf1_width_table[4][3] =
07709 {
07710     { 86399,    863999,  86396459, },
07711     { 86399,   8639999, 863996459, },
07712     { 86399,  86399999,   INT_MAX, },
07713     { 86399, 863999999,   INT_MAX, }
07714 };
07715 
07716 static struct
07717 {
07718     char *specs[4];
07719     int  div[3];
07720 } tf1_case_table[4] =
07721 {
07722     {
07723         { "   %2d:%02d", "    %2d:%02d", "     %2d:%02d", "      %2d:%02d" },
07724         { 3600, 60, 1 }
07725     },
07726     {
07727         { "%dd %02d:%02d", "%2dd %02d:%02d", "%3dd %02d:%02d", "%4dd %02d:%02d" },
07728         { 86400, 3600, 60 }
07729     },
07730     {
07731         { "%3dd %02dh", "%4dd %02dh", "%5dd %02dh", "%6dd %02dh" },
07732         { 86400, 3600, 1 }
07733     },
07734     {
07735         { "%4dw %d", "%4dw %d", "", "" },
07736         { 604800, 86400, 1 }
07737     }
07738 };
07739 
07740 const char *time_format_1(int Seconds, size_t maxWidth)
07741 {
07742     if (Seconds < 0)
07743     {
07744         Seconds = 0;
07745     }
07746 
07747     if (  maxWidth < 8
07748        || 12 < maxWidth)
07749     {
07750         strcpy(TimeBuffer80, "???");
07751         return TimeBuffer80;
07752     }
07753     int iWidth = maxWidth - 8;
07754 
07755     int iCase = 0;
07756     while (  iCase < 3
07757           && tf1_width_table[iWidth][iCase] < Seconds)
07758     {
07759         iCase++;
07760     }
07761 
07762     int i, n[3];
07763     for (i = 0; i < 3; i++)
07764     {
07765         n[i] = Seconds / tf1_case_table[iCase].div[i];
07766         Seconds -= n[i] *tf1_case_table[iCase].div[i];
07767     }
07768     sprintf(TimeBuffer80, tf1_case_table[iCase].specs[iWidth], n[0], n[1], n[2]);
07769     return TimeBuffer80;
07770 }
07771 
07772 // Show time in days, hours, minutes, or seconds.
07773 //
07774 const char *time_format_2(int Seconds)
07775 {
07776     // 2^63/86400 is 1.07E14 which is at most 15 digits.
07777     // '(15)d\0' is at most 17 characters.
07778     //
07779     GeneralTimeConversion(TimeBuffer64, Seconds, IYEARS, ISECONDS, true, false);
07780     return TimeBuffer64;
07781 }
07782 
07783 // Del's added functions for dooferMUX ! :)
07784 // D.Piper (del@doofer.org) 1997 & 2000
07785 //
07786 
07787 // expand_time - Written (short) time format.
07788 //
07789 static const char *expand_time(int Seconds)
07790 {
07791     // 2^63/2592000 is 3558399705577 which is at most 13 digits.
07792     // '(13)M (1)w (1)d (2)h (2)m (2)s\0' is at most 33 characters.
07793     //
07794     GeneralTimeConversion(TimeBuffer64, Seconds, IMONTHS, ISECONDS, false, false);
07795     return TimeBuffer64;
07796 }
07797 
07798 // write_time - Written (long) time format.
07799 //
07800 static const char *write_time(int Seconds)
07801 {
07802     // 2^63/2592000 is 3558399705577 which is at most 13 digits.
07803     // '(13) months (1) weeks (1) days (2) hours (2) minutes (2) seconds\0' is
07804     // at most 69 characters.
07805     //
07806     GeneralTimeConversion(TimeBuffer80, Seconds, IMONTHS, ISECONDS, false, true);
07807     return TimeBuffer80;
07808 }
07809 
07810 // digittime - Digital format time ([(days)d]HH:MM) from given
07811 // seconds. D.Piper - May 1997 & April 2000
07812 //
07813 static FUNCTION(fun_digittime)
07814 {
07815     UNUSED_PARAMETER(executor);
07816     UNUSED_PARAMETER(caller);
07817     UNUSED_PARAMETER(enactor);
07818     UNUSED_PARAMETER(nfargs);
07819     UNUSED_PARAMETER(cargs);
07820     UNUSED_PARAMETER(ncargs);
07821 
07822     int tt = mux_atol(fargs[0]);
07823     safe_str(digit_format(tt), buff, bufc);
07824 }
07825 
07826 // singletime - Single element time from given seconds.
07827 // D.Piper - May 1997 & April 2000
07828 //
07829 static FUNCTION(fun_singletime)
07830 {
07831     UNUSED_PARAMETER(executor);
07832     UNUSED_PARAMETER(caller);
07833     UNUSED_PARAMETER(enactor);
07834     UNUSED_PARAMETER(nfargs);
07835     UNUSED_PARAMETER(cargs);
07836     UNUSED_PARAMETER(ncargs);
07837 
07838     int tt = mux_atol(fargs[0]);
07839     safe_str(time_format_2(tt), buff, bufc);
07840 }
07841 
07842 // exptime - Written (short) time from given seconds
07843 // D.Piper - May 1997 & April 2000
07844 //
07845 static FUNCTION(fun_exptime)
07846 {
07847     UNUSED_PARAMETER(executor);
07848     UNUSED_PARAMETER(caller);
07849     UNUSED_PARAMETER(enactor);
07850     UNUSED_PARAMETER(nfargs);
07851     UNUSED_PARAMETER(cargs);
07852     UNUSED_PARAMETER(ncargs);
07853 
07854     int tt = mux_atol(fargs[0]);
07855     safe_str(expand_time(tt), buff, bufc);
07856 }
07857 
07858 // writetime - Written (long) time from given seconds
07859 // D.Piper - May 1997 & April 2000
07860 //
07861 static FUNCTION(fun_writetime)
07862 {
07863     UNUSED_PARAMETER(executor);
07864     UNUSED_PARAMETER(caller);
07865     UNUSED_PARAMETER(enactor);
07866     UNUSED_PARAMETER(nfargs);
07867     UNUSED_PARAMETER(cargs);
07868     UNUSED_PARAMETER(ncargs);
07869 
07870     int tt = mux_atol(fargs[0]);
07871     safe_str(write_time(tt), buff, bufc);
07872 }
07873 
07874 // cmds - Return player command count (Wizard_Who OR Self ONLY)
07875 // D.Piper - May 1997
07876 //
07877 static FUNCTION(fun_cmds)
07878 {
07879     UNUSED_PARAMETER(caller);
07880     UNUSED_PARAMETER(enactor);
07881     UNUSED_PARAMETER(nfargs);
07882     UNUSED_PARAMETER(cargs);
07883     UNUSED_PARAMETER(ncargs);
07884 
07885     long nCmds = -1;
07886     if (is_rational(fargs[0]))
07887     {
07888         SOCKET s = mux_atol(fargs[0]);
07889         bool bFound = false;
07890         DESC *d;
07891         DESC_ITER_CONN(d)
07892         {
07893             if (d->descriptor == s)
07894             {
07895                 bFound = true;
07896                 break;
07897             }
07898         }
07899         if (  bFound
07900            && (  d->player == executor
07901               || Wizard_Who(executor)))
07902         {
07903             nCmds = d->command_count;
07904         }
07905     }
07906     else
07907     {
07908         dbref target = lookup_player(executor, fargs[0], true);
07909         if (  Good_obj(target)
07910            && Connected(target)
07911            && (  Wizard_Who(executor)
07912               || Controls(executor, target)))
07913         {
07914             nCmds = fetch_cmds(target);
07915         }
07916     }
07917     safe_ltoa(nCmds, buff, bufc);
07918 }
07919 
07920 // startsecs - Time the MUX was started, in seconds
07921 // D.Piper - May 1997 & April 2000
07922 //
07923 static FUNCTION(fun_startsecs)
07924 {
07925     UNUSED_PARAMETER(executor);
07926     UNUSED_PARAMETER(caller);
07927     UNUSED_PARAMETER(enactor);
07928     UNUSED_PARAMETER(fargs);
07929     UNUSED_PARAMETER(nfargs);
07930     UNUSED_PARAMETER(cargs);
07931     UNUSED_PARAMETER(ncargs);
07932 
07933     CLinearTimeAbsolute lta;
07934     lta = mudstate.start_time;
07935     lta.Local2UTC();
07936     safe_str(lta.ReturnSecondsString(), buff, bufc);
07937 }
07938 
07939 // conntotal - Return player's total online time to the MUX
07940 // (including their current connection). D.Piper - May 1997
07941 //
07942 static FUNCTION(fun_conntotal)
07943 {
07944     UNUSED_PARAMETER(caller);
07945     UNUSED_PARAMETER(enactor);
07946     UNUSED_PARAMETER(nfargs);
07947     UNUSED_PARAMETER(cargs);
07948     UNUSED_PARAMETER(ncargs);
07949 
07950     dbref target = lookup_player(executor, fargs[0], true);
07951     if (Good_obj(target))
07952     {
07953         long TotalTime = fetch_totaltime(target);
07954         if (Connected(target))
07955         {
07956             TotalTime += fetch_connect(target);
07957         }
07958         safe_ltoa(TotalTime, buff, bufc);
07959     }
07960     else
07961     {
07962         safe_str("#-1 PLAYER NOT FOUND", buff, bufc);
07963     }
07964 }
07965 
07966 // connmax - Return player's longest session to the MUX
07967 // (including the current one). D.Piper - May 1997
07968 //
07969 static FUNCTION(fun_connmax)
07970 {
07971     UNUSED_PARAMETER(caller);
07972     UNUSED_PARAMETER(enactor);
07973     UNUSED_PARAMETER(nfargs);
07974     UNUSED_PARAMETER(cargs);
07975     UNUSED_PARAMETER(ncargs);
07976 
07977     dbref target = lookup_player(executor, fargs[0], true);
07978     if (Good_obj(target))
07979     {
07980         long Longest = fetch_longestconnect(target);
07981         long Current = fetch_connect(target);
07982         if (Longest < Current)
07983         {
07984             Longest = Current;
07985         }
07986         safe_ltoa(Longest, buff, bufc);
07987     }
07988     else
07989     {
07990         safe_str("#-1 PLAYER NOT FOUND", buff, bufc);
07991     }
07992 }
07993 
07994 // connlast - Return player's last connection time to the MUX
07995 // D.Piper - May 1997
07996 //
07997 static FUNCTION(fun_connlast)
07998 {
07999     UNUSED_PARAMETER(caller);
08000     UNUSED_PARAMETER(enactor);
08001     UNUSED_PARAMETER(nfargs);
08002     UNUSED_PARAMETER(cargs);
08003     UNUSED_PARAMETER(ncargs);
08004 
08005     dbref target = lookup_player(executor, fargs[0], true);
08006     if (Good_obj(target))
08007     {
08008         safe_ltoa(fetch_lastconnect(target), buff, bufc);
08009     }
08010     else
08011     {
08012         safe_str("#-1 PLAYER NOT FOUND", buff, bufc);
08013     }
08014 }
08015 
08016 // connnum - Return the total number of sessions this player has had
08017 // to the MUX (including any current ones). D.Piper - May 1997
08018 //
08019 static FUNCTION(fun_connnum)
08020 {
08021     UNUSED_PARAMETER(caller);
08022     UNUSED_PARAMETER(enactor);
08023     UNUSED_PARAMETER(nfargs);
08024     UNUSED_PARAMETER(cargs);
08025     UNUSED_PARAMETER(ncargs);
08026 
08027     dbref target = lookup_player(executor, fargs[0], true);
08028     if (Good_obj(target))
08029     {
08030         long NumConnections = fetch_numconnections(target);
08031         if (Connected(target))
08032         {
08033             NumConnections += fetch_session(target);
08034         }
08035         safe_ltoa(NumConnections, buff, bufc);
08036     }
08037     else
08038     {
08039         safe_str("#-1 PLAYER NOT FOUND", buff, bufc);
08040     }
08041 }
08042 
08043 // connleft - Return when a player last logged off the MUX as
08044 // UTC seconds. D.Piper - May 1997
08045 //
08046 static FUNCTION(fun_connleft)
08047 {
08048     UNUSED_PARAMETER(caller);
08049     UNUSED_PARAMETER(enactor);
08050     UNUSED_PARAMETER(nfargs);
08051     UNUSED_PARAMETER(cargs);
08052     UNUSED_PARAMETER(ncargs);
08053 
08054     dbref target = lookup_player(executor, fargs[0], true);
08055     if (Good_obj(target))
08056     {
08057         CLinearTimeAbsolute cl = fetch_logouttime(target);
08058         safe_str(cl.ReturnSecondsString(7), buff, bufc);
08059     }
08060     else
08061     {
08062         safe_str("#-1 PLAYER NOT FOUND", buff, bufc);
08063     }
08064 }
08065 
08066 // lattrcmds - Output a list of all attributes containing $ commands.
08067 // Altered from lattr(). D.Piper - May 1997 & April 2000
08068 //
08069 static FUNCTION(fun_lattrcmds)
08070 {
08071     UNUSED_PARAMETER(caller);
08072     UNUSED_PARAMETER(enactor);
08073     UNUSED_PARAMETER(nfargs);
08074     UNUSED_PARAMETER(cargs);
08075     UNUSED_PARAMETER(ncargs);
08076 
08077     // Check for wildcard matching.  parse_attrib_wild checks for read
08078     // permission, so we don't have to.  Have p_a_w assume the
08079     // slash-star if it is missing.
08080     //
08081     olist_push();
08082     dbref thing;
08083     if (parse_attrib_wild(executor, fargs[0], &thing, false, false, true))
08084     {
08085         bool isFirst = true;
08086         char *buf = alloc_lbuf("fun_lattrcmds");
08087         for (int ca = olist_first(); ca != NOTHING; ca = olist_next())
08088         {
08089             ATTR *pattr = atr_num(ca);
08090             if (pattr)
08091             {
08092                 dbref aowner;
08093                 int   aflags;
08094                 atr_get_str(buf, thing, pattr->number, &aowner, &aflags);
08095                 if (buf[0] == '$')
08096                 {
08097                     if (!isFirst)
08098                     {
08099                         safe_chr(' ', buff, bufc);
08100                     }
08101                     isFirst = false;
08102                     safe_str(pattr->name, buff, bufc);
08103                 }
08104             }
08105         }
08106         free_lbuf(buf);
08107     }
08108     else
08109     {
08110         safe_nomatch(buff, bufc);
08111     }
08112     olist_pop();
08113 }
08114 
08115 // lcmds - Output a list of all $ commands on an object.
08116 // Altered from MUX lattr(). D.Piper - May 1997 & April 2000
08117 // Modified to handle spaced commands and ^-listens - July 2001 (Ash)
08118 // Applied patch and code reviewed - February 2002 (Stephen)
08119 //
08120 static FUNCTION(fun_lcmds)
08121 {
08122     SEP sep;
08123     if (!OPTIONAL_DELIM(2, sep, DELIM_NULL|DELIM_CRLF|DELIM_STRING))
08124     {
08125         return;
08126     }
08127 
08128     // Check to see what type of command matching we will do. '$' commands
08129     // or '^' listens.  We default with '$' commands.
08130     //
08131     char cmd_type = '$';
08132     if (  nfargs == 3
08133         && (  *fargs[2] == '$'
08134            || *fargs[2] == '^'))
08135     {
08136         cmd_type = *fargs[2];
08137     }
08138 
08139     // Check for wildcard matching.  parse_attrib_wild checks for read
08140     // permission, so we don't have to.  Have p_a_w assume the
08141     // slash-star if it is missing.
08142     //
08143     olist_push();
08144     dbref thing;
08145     if (parse_attrib_wild(executor, fargs[0], &thing, false, false, true))
08146     {
08147         bool isFirst = true;
08148         char *buf = alloc_lbuf("fun_lattrcmds");
08149         dbref aowner;
08150         int   aflags;
08151         for (int ca = olist_first(); ca != NOTHING; ca = olist_next())
08152         {
08153             ATTR *pattr = atr_num(ca);
08154             if (pattr)
08155             {
08156                 atr_get_str(buf, thing, pattr->number, &aowner, &aflags);
08157                 if (buf[0] == cmd_type)
08158                 {
08159                     bool isFound = false;
08160                     char *c_ptr = buf+1;
08161 
08162                     // If there is no characters between the '$' or '^' and the
08163                     // ':' it's not a valid command, so skip it.
08164                     //
08165                     if (*c_ptr != ':')
08166                     {
08167                         int isEscaped = false;
08168                         while (*c_ptr && !isFound)
08169                         {
08170                             // We need to check if the ':' in the command is
08171                             // escaped.
08172                             //
08173                             if (*c_ptr == '\\')
08174                             {
08175                                 isEscaped = !isEscaped;
08176                             }
08177                             else if (*c_ptr == ':' && !isEscaped)
08178                             {
08179                                 isFound = true;
08180                                 *c_ptr = '\0';
08181                             }
08182                             else if (*c_ptr != '\\' && isEscaped)
08183                             {
08184                                 isEscaped = false;
08185                             }
08186                             c_ptr++;
08187                         }
08188                     }
08189 
08190                     // We don't want to bother printing out the $command
08191                     // if it doesn't have a matching ':'.  It isn't a valid
08192                     // command then.
08193                     //
08194                     if (isFound)
08195                     {
08196                         if (!isFirst)
08197                         {
08198                             print_sep(&sep, buff, bufc);
08199                         }
08200 
08201                         mux_strlwr(buf);
08202                         safe_str(buf+1, buff, bufc);
08203 
08204                         isFirst = false;
08205                     }
08206                 }
08207             }
08208         }
08209         free_lbuf(buf);
08210     }
08211     else
08212     {
08213         safe_nomatch(buff, bufc);
08214     }
08215     olist_pop();
08216 }
08217 
08218 // lflags - List flags as names - (modified from 'flag_description()' and
08219 // MUX flags(). D.Piper - May 1997 & May 2000
08220 //
08221 static FUNCTION(fun_lflags)
08222 {
08223     UNUSED_PARAMETER(caller);
08224     UNUSED_PARAMETER(enactor);
08225     UNUSED_PARAMETER(nfargs);
08226     UNUSED_PARAMETER(cargs);
08227     UNUSED_PARAMETER(ncargs);
08228 
08229     dbref target = match_thing_quiet(executor, fargs[0]);
08230     if (!Good_obj(target))
08231     {
08232         safe_match_result(target, buff, bufc);
08233         return;
08234     }
08235     bool bFirst = true;
08236     if (  mudconf.pub_flags
08237        || Examinable(executor, target))
08238     {
08239         FLAGNAMEENT *fp;
08240         for (fp = gen_flag_names; fp->flagname; fp++)
08241         {
08242             if (!fp->bPositive)
08243             {
08244                 continue;
08245             }
08246             FLAGBITENT *fbe = fp->fbe;
08247             if (db[target].fs.word[fbe->flagflag] & fbe->flagvalue)
08248             {
08249                 if (  (  (fbe->listperm & CA_STAFF)
08250                       && !Staff(executor))
08251                    || (  (fbe->listperm & CA_ADMIN)
08252                       && !WizRoy(executor))
08253                    || (  (fbe->listperm & CA_WIZARD)
08254                       && !Wizard(executor))
08255                    || (  (fbe->listperm & CA_GOD)
08256                       && !God(executor)))
08257                 {
08258                     continue;
08259                 }
08260                 if (  isPlayer(target)
08261                    && (fbe->flagvalue == CONNECTED)
08262                    && (fbe->flagflag == FLAG_WORD2)
08263                    && Hidden(target)
08264                    && !See_Hidden(executor))
08265                 {
08266                     continue;
08267                 }
08268 
08269                 if (!bFirst)
08270                 {
08271                     safe_chr(' ', buff, bufc);
08272                 }
08273                 bFirst = false;
08274 
08275                 safe_str(fp->flagname, buff, bufc);
08276             }
08277         }
08278     }
08279     else
08280     {
08281         safe_noperm(buff, bufc);
08282     }
08283 }
08284 
08285 // ---------------------------------------------------------------------------
08286 // fun_art:
08287 //
08288 // Accepts a single argument. Based on the rules specified in the config
08289 // parameters article_regexp and article_exception it determines whether the
08290 // word should use 'an' or 'a' as its article.
08291 //
08292 // By default if a word matches the regexp specified in article_regexp then
08293 // this function will return 'an', otherwise it will return 'a'. If, however
08294 // the word also matches one of the specified article_exception regexp's
08295 // will return the given article.
08296 //
08297 
08298 static FUNCTION(fun_art)
08299 {
08300     UNUSED_PARAMETER(executor);
08301     UNUSED_PARAMETER(caller);
08302     UNUSED_PARAMETER(enactor);
08303     UNUSED_PARAMETER(nfargs);
08304     UNUSED_PARAMETER(cargs);
08305     UNUSED_PARAMETER(ncargs);
08306 
08307     const int ovecsize = 33;
08308     int ovec[ovecsize];
08309 
08310     // Drop the input string into lower case.
08311     //
08312     mux_strlwr(fargs[0]);
08313 
08314     // Search for exceptions.
08315     //
08316     ArtRuleset *arRule = mudconf.art_rules;
08317 
08318     while (arRule != NULL)
08319     {
08320         pcre* reRuleRegexp = (pcre *) arRule->m_pRegexp;
08321         pcre_extra* reRuleStudy = (pcre_extra *) arRule->m_pRegexpStudy;
08322 
08323         if (  !MuxAlarm.bAlarmed
08324            && pcre_exec(reRuleRegexp, reRuleStudy, fargs[0], strlen(fargs[0]),
08325                 0, 0, ovec, ovecsize) > 0)
08326         {
08327             safe_str(arRule->m_bUseAn ? "an" : "a", buff, bufc);
08328             return;
08329         }
08330 
08331         arRule = arRule->m_pNextRule;
08332     }
08333 
08334     // Default to 'a'.
08335     //
08336     safe_str( "a", buff, bufc);
08337 }
08338 
08339 // ---------------------------------------------------------------------------
08340 // fun_ord:
08341 //
08342 // Takes a single character and returns the corresponding ordinal of its
08343 // position in the character set.
08344 //
08345 static FUNCTION(fun_ord)
08346 {
08347     UNUSED_PARAMETER(executor);
08348     UNUSED_PARAMETER(caller);
08349     UNUSED_PARAMETER(enactor);
08350     UNUSED_PARAMETER(nfargs);
08351     UNUSED_PARAMETER(cargs);
08352     UNUSED_PARAMETER(ncargs);
08353 
08354     size_t n;
08355     char *p  = strip_ansi(fargs[0], &n);
08356     if (n == 1)
08357     {
08358         unsigned char ch = p[0];
08359         safe_ltoa(ch, buff, bufc);
08360     }
08361     else
08362     {
08363         safe_str("#-1 FUNCTION EXPECTS ONE CHARACTER", buff, bufc);
08364     }
08365 }
08366 
08367 // ---------------------------------------------------------------------------
08368 // fun_chr:
08369 //
08370 // Takes an integer and returns the corresponding character from the character
08371 // set.
08372 //
08373 static FUNCTION(fun_chr)
08374 {
08375     UNUSED_PARAMETER(executor);
08376     UNUSED_PARAMETER(caller);
08377     UNUSED_PARAMETER(enactor);
08378     UNUSED_PARAMETER(nfargs);
08379     UNUSED_PARAMETER(cargs);
08380     UNUSED_PARAMETER(ncargs);
08381 
08382     if (!is_integer(fargs[0], NULL))
08383     {
08384         safe_str("#-1 ARGUMENT MUST BE A NUMBER", buff, bufc);
08385         return;
08386     }
08387     int ch = mux_atol(fargs[0]);
08388     if (  ch < 0
08389        || (int) UCHAR_MAX < ch)
08390     {
08391         safe_str("#-1 THIS ISN'T UNICODE", buff, bufc);
08392     }
08393     else if (mux_isprint(ch))
08394     {
08395         safe_chr(ch, buff, bufc);
08396     }
08397     else
08398     {
08399         safe_str("#-1 UNPRINTABLE CHARACTER", buff, bufc);
08400     }
08401 }
08402 
08403 // ---------------------------------------------------------------------------
08404 // fun_stripaccents:
08405 //
08406 static FUNCTION(fun_stripaccents)
08407 {
08408     UNUSED_PARAMETER(executor);
08409     UNUSED_PARAMETER(caller);
08410     UNUSED_PARAMETER(enactor);
08411     UNUSED_PARAMETER(nfargs);
08412     UNUSED_PARAMETER(cargs);
08413     UNUSED_PARAMETER(ncargs);
08414 
08415     size_t nLen;
08416     char *p = strip_accents(fargs[0], &nLen);
08417     safe_copy_buf(p, nLen, buff, bufc);
08418 }
08419 
08420 // Base Letter: AaCcEeIiNnOoUuYy?!<>sPpD
08421 //
08422 static const unsigned char AccentCombo1[256] =
08423 {
08424 //  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
08425 //
08426     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
08427     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
08428     0,18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 2
08429     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,19, 0,20,17,  // 3
08430     0, 1, 0, 3,24, 5, 0, 0, 0, 7, 0, 0, 0, 0, 9,11,  // 4
08431    22, 0, 0, 0, 0,13, 0, 0, 0,15, 0, 0, 0, 0, 0, 0,  // 5
08432     0, 2, 0, 4, 0, 6, 0, 0, 0, 8, 0, 0, 0, 0,10,12,  // 6
08433    23, 0, 0,21, 0,14, 0, 0, 0,16, 0, 0, 0, 0, 0, 0,  // 7
08434 
08435     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
08436     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
08437     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
08438     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
08439     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
08440     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
08441     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
08442     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
08443 };
08444 
08445 // Accent:      `'^~:o,u"B|-&Ee
08446 //
08447 static const unsigned char AccentCombo2[256] =
08448 {
08449 //  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
08450 //
08451     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0
08452     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 1
08453     0, 0, 9, 0, 0, 0,13, 2, 0, 0, 0, 0, 7,12, 0, 0,  // 2
08454     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0,  // 3
08455     0, 0,10, 0, 0,14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 4
08456     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,  // 5
08457     1, 0, 0, 0, 0,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,  // 6
08458     0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0,11, 0, 4, 0,  // 7
08459 
08460     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 8
08461     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 9
08462     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // A
08463     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // B
08464     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // C
08465     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // D
08466     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // E
08467     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   // F
08468 };
08469 
08470 static const unsigned char AccentCombo3[24][16] =
08471 {
08472     //  0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15
08473     //        `     '     ^     ~     :     o     ,     u     "     B     |     -     &     E     e
08474     //
08475     {  0x00, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x00 }, //  1 'A'
08476     {  0x00, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6 }, //  2 'a'
08477     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //  3 'C'
08478     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //  4 'c'
08479     {  0x00, 0xC8, 0xC9, 0xCA, 0x00, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //  5 'E'
08480     {  0x00, 0xE8, 0xE9, 0xEA, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //  6 'e'
08481     {  0x00, 0xCC, 0xCD, 0xCE, 0x00, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //  7 'I'
08482     {  0x00, 0xEC, 0xED, 0xEE, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //  8 'i'
08483 
08484     {  0x00, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //  9 'N'
08485     {  0x00, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 10 'n'
08486     {  0x00, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 11 'O'
08487     {  0x00, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00 }, // 12 'o'
08488     {  0x00, 0xD9, 0xDA, 0xDB, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 13 'U'
08489     {  0x00, 0xF9, 0xFA, 0xFB, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 14 'u'
08490     {  0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 15 'Y'
08491     {  0x00, 0x00, 0xFD, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 16 'y'
08492 
08493     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 17 '?'
08494     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 18 '!'
08495     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 19 '<'
08496     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 20 '>'
08497     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 21 's'
08498     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x00 }, // 22 'P'
08499     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00 }, // 23 'p'
08500     {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00 }  // 24 'D'
08501 };
08502 
08503 // ---------------------------------------------------------------------------
08504 // fun_accent:
08505 //
08506 static FUNCTION(fun_accent)
08507 {
08508     UNUSED_PARAMETER(executor);
08509     UNUSED_PARAMETER(caller);
08510     UNUSED_PARAMETER(enactor);
08511     UNUSED_PARAMETER(nfargs);
08512     UNUSED_PARAMETER(cargs);
08513     UNUSED_PARAMETER(ncargs);
08514 
08515     size_t n = strlen(fargs[0]);
08516     if (n != strlen(fargs[1]))
08517     {
08518         safe_str("#-1 STRING LENGTHS MUST BE EQUAL", buff, bufc);
08519         return;
08520     }
08521 
08522     const unsigned char *p = (unsigned char *)fargs[0];
08523     const unsigned char *q = (unsigned char *)fargs[1];
08524 
08525     while (*p)
08526     {
08527         unsigned char ch = '\0';
08528         unsigned char ch0 = AccentCombo1[*p];
08529         if (ch0)
08530         {
08531             unsigned char ch1 = AccentCombo2[*q];
08532             if (ch1)
08533             {
08534                 ch  = AccentCombo3[ch0-1][ch1];
08535             }
08536         }
08537         if (!mux_isprint(ch))
08538         {
08539             ch = *p;
08540         }
08541         safe_chr(ch, buff, bufc);
08542 
08543         p++;
08544         q++;
08545     }
08546 }
08547 
08548 // ----------------------------------------------------------------------------
08549 // flist: List of existing functions in alphabetical order.
08550 //
08551 //   Name          Handler      # of args   min #    max #   flags  permissions
08552 //                               to parse  of args  of args
08553 //
08554 static FUN builtin_function_list[] =
08555 {
08556     {"@@",          fun_null,             1, 1,       1, FN_NOEVAL, CA_PUBLIC},
08557     {"ABS",         fun_abs,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08558     {"ACCENT",      fun_accent,     MAX_ARG, 2,       2,         0, CA_PUBLIC},
08559     {"ACOS",        fun_acos,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08560     {"ADD",         fun_add,        MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08561     {"AFTER",       fun_after,      MAX_ARG, 1,       2,         0, CA_PUBLIC},
08562     {"ALPHAMAX",    fun_alphamax,   MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08563     {"ALPHAMIN",    fun_alphamin,   MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08564     {"AND",         fun_and,        MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08565     {"ANDBOOL",     fun_andbool,    MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08566     {"ANDFLAGS",    fun_andflags,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08567     {"ANSI",        fun_ansi,       MAX_ARG, 2, MAX_ARG,         0, CA_PUBLIC},
08568     {"APOSS",       fun_aposs,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08569     {"ART",         fun_art,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08570     {"ASIN",        fun_asin,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08571     {"ATAN",        fun_atan,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08572     {"ATTRCNT",     fun_attrcnt,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08573     {"BAND",        fun_band,       MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08574     {"BEEP",        fun_beep,       MAX_ARG, 0,       0,         0, CA_WIZARD},
08575     {"BEFORE",      fun_before,     MAX_ARG, 1,       2,         0, CA_PUBLIC},
08576     {"BITTYPE",     fun_bittype,    MAX_ARG, 0,       1,         0, CA_PUBLIC},
08577     {"BNAND",       fun_bnand,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08578     {"BOR",         fun_bor,        MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08579     {"BXOR",        fun_bxor,       MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08580     {"CAND",        fun_cand,       MAX_ARG, 0, MAX_ARG, FN_NOEVAL, CA_PUBLIC},
08581     {"CANDBOOL",    fun_candbool,   MAX_ARG, 0, MAX_ARG, FN_NOEVAL, CA_PUBLIC},
08582 #if defined(WOD_REALMS) || defined(REALITY_LVLS)
08583     {"CANSEE",      fun_cansee,     MAX_ARG, 2,       3,         0, CA_PUBLIC},
08584 #endif
08585     {"CAPSTR",      fun_capstr,           1, 1,       1,         0, CA_PUBLIC},
08586     {"CASE",        fun_case,       MAX_ARG, 2, MAX_ARG, FN_NOEVAL, CA_PUBLIC},
08587     {"CAT",         fun_cat,        MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08588     {"CEIL",        fun_ceil,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08589     {"CEMIT",       fun_cemit,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08590     {"CENTER",      fun_center,     MAX_ARG, 2,       3,         0, CA_PUBLIC},
08591     {"CHANNELS",    fun_channels,   MAX_ARG, 0,       1,         0, CA_PUBLIC},
08592     {"CHILDREN",    fun_children,   MAX_ARG, 1,       1,         0, CA_PUBLIC},
08593     {"CHOOSE",      fun_choose,     MAX_ARG, 2,       3,         0, CA_PUBLIC},
08594     {"CHR",         fun_chr,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08595     {"CMDS",        fun_cmds,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08596     {"COLUMNS",     fun_columns,    MAX_ARG, 2,       4,         0, CA_PUBLIC},
08597     {"COMALIAS",    fun_comalias,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08598     {"COMP",        fun_comp,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08599     {"COMTITLE",    fun_comtitle,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08600     {"CON",         fun_con,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08601     {"CONFIG",      fun_config,     MAX_ARG, 0,       1,         0, CA_PUBLIC},
08602     {"CONN",        fun_conn,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08603     {"CONNLAST",    fun_connlast,   MAX_ARG, 1,       1,         0, CA_PUBLIC},
08604     {"CONNLEFT",    fun_connleft,   MAX_ARG, 1,       1,         0, CA_PUBLIC},
08605     {"CONNMAX",     fun_connmax,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08606     {"CONNNUM",     fun_connnum,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08607     {"CONNRECORD",  fun_connrecord, MAX_ARG, 0,       0,         0, CA_PUBLIC},
08608     {"CONNTOTAL",   fun_conntotal,  MAX_ARG, 1,       1,         0, CA_PUBLIC},
08609     {"CONTROLS",    fun_controls,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08610     {"CONVSECS",    fun_convsecs,   MAX_ARG, 1,       3,         0, CA_PUBLIC},
08611     {"CONVTIME",    fun_convtime,   MAX_ARG, 1,       3,         0, CA_PUBLIC},
08612     {"COR",         fun_cor,        MAX_ARG, 0, MAX_ARG, FN_NOEVAL, CA_PUBLIC},
08613     {"CORBOOL",     fun_corbool,    MAX_ARG, 0, MAX_ARG, FN_NOEVAL, CA_PUBLIC},
08614     {"COS",         fun_cos,        MAX_ARG, 1,       2,         0, CA_PUBLIC},
08615     {"CRC32",       fun_crc32,      MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08616     {"CREATE",      fun_create,     MAX_ARG, 2,       3,         0, CA_PUBLIC},
08617     {"CTIME",       fun_ctime,      MAX_ARG, 0,       1,         0, CA_PUBLIC},
08618     {"CTU",         fun_ctu,        MAX_ARG, 3,       3,         0, CA_PUBLIC},
08619     {"CWHO",        fun_cwho,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08620     {"DEC",         fun_dec,        MAX_ARG, 0,       1,         0, CA_PUBLIC},
08621     {"DECRYPT",     fun_decrypt,    MAX_ARG, 2,       2,         0, CA_PUBLIC},
08622     {"DEFAULT",     fun_default,    MAX_ARG, 2,       2, FN_NOEVAL, CA_PUBLIC},
08623     {"DELETE",      fun_delete,     MAX_ARG, 3,       3,         0, CA_PUBLIC},
08624     {"DIE",         fun_die,        MAX_ARG, 2,       3,         0, CA_PUBLIC},
08625     {"DIGITTIME",   fun_digittime,  MAX_ARG, 1,       1,         0, CA_PUBLIC},
08626     {"DIST2D",      fun_dist2d,     MAX_ARG, 4,       4,         0, CA_PUBLIC},
08627     {"DIST3D",      fun_dist3d,     MAX_ARG, 6,       6,         0, CA_PUBLIC},
08628     {"DOING",       fun_doing,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08629     {"DUMPING",     fun_dumping,    MAX_ARG, 0,       0,         0, CA_PUBLIC},
08630     {"E",           fun_e,          MAX_ARG, 0,       0,         0, CA_PUBLIC},
08631     {"EDEFAULT",    fun_edefault,   MAX_ARG, 2,       2, FN_NOEVAL, CA_PUBLIC},
08632     {"EDIT",        fun_edit,       MAX_ARG, 3,       3,         0, CA_PUBLIC},
08633     {"ELEMENTS",    fun_elements,   MAX_ARG, 2,       4,         0, CA_PUBLIC},
08634     {"ELOCK",       fun_elock,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08635     {"EMIT",        fun_emit,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08636     {"EMPTY",       fun_empty,      MAX_ARG, 0,       1,         0, CA_PUBLIC},
08637     {"ENCRYPT",     fun_encrypt,    MAX_ARG, 2,       2,         0, CA_PUBLIC},
08638     {"ENTRANCES",   fun_entrances,  MAX_ARG, 0,       4,         0, CA_PUBLIC},
08639     {"EQ",          fun_eq,         MAX_ARG, 2,       2,         0, CA_PUBLIC},
08640     {"ERROR",       fun_error,            1, 0,       1,         0, CA_PUBLIC},
08641     {"ESCAPE",      fun_escape,           1, 1,       1,         0, CA_PUBLIC},
08642     {"EVAL",        fun_eval,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08643     {"EXIT",        fun_exit,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08644     {"EXP",         fun_exp,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08645     {"EXPTIME",     fun_exptime,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08646     {"EXTRACT",     fun_extract,    MAX_ARG, 3,       5,         0, CA_PUBLIC},
08647     {"FCOUNT",      fun_fcount,     MAX_ARG, 0,       0,         0, CA_PUBLIC},
08648     {"FDEPTH",      fun_fdepth,     MAX_ARG, 0,       0,         0, CA_PUBLIC},
08649     {"FDIV",        fun_fdiv,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08650     {"FILTER",      fun_filter,     MAX_ARG, 2,       4,         0, CA_PUBLIC},
08651     {"FILTERBOOL",  fun_filterbool, MAX_ARG, 2,       4,         0, CA_PUBLIC},
08652     {"FINDABLE",    fun_findable,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08653     {"FIRST",       fun_first,      MAX_ARG, 0,       2,         0, CA_PUBLIC},
08654     {"FLAGS",       fun_flags,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08655     {"FLOOR",       fun_floor,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08656     {"FLOORDIV",    fun_floordiv,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08657     {"FMOD",        fun_fmod,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08658     {"FOLD",        fun_fold,       MAX_ARG, 2,       4,         0, CA_PUBLIC},
08659     {"FOREACH",     fun_foreach,    MAX_ARG, 2,       4,         0, CA_PUBLIC},
08660     {"FULLNAME",    fun_fullname,   MAX_ARG, 1,       1,         0, CA_PUBLIC},
08661     {"GET",         fun_get,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08662     {"GET_EVAL",    fun_get_eval,   MAX_ARG, 1,       1,         0, CA_PUBLIC},
08663     {"GRAB",        fun_grab,       MAX_ARG, 2,       3,         0, CA_PUBLIC},
08664     {"GRABALL",     fun_graball,    MAX_ARG, 2,       4,         0, CA_PUBLIC},
08665     {"GREP",        fun_grep,       MAX_ARG, 3,       3,         0, CA_PUBLIC},
08666     {"GREPI",       fun_grepi,      MAX_ARG, 3,       3,         0, CA_PUBLIC},
08667     {"GT",          fun_gt,         MAX_ARG, 2,       2,         0, CA_PUBLIC},
08668     {"GTE",         fun_gte,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08669     {"HASATTR",     fun_hasattr,    MAX_ARG, 2,       2,         0, CA_PUBLIC},
08670     {"HASATTRP",    fun_hasattrp,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08671     {"HASFLAG",     fun_hasflag,    MAX_ARG, 2,       2,         0, CA_PUBLIC},
08672     {"HASPOWER",    fun_haspower,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08673     {"HASQUOTA",    fun_hasquota,   MAX_ARG, 2,       3,         0, CA_PUBLIC},
08674 #ifdef REALITY_LVLS
08675     {"HASRXLEVEL",  fun_hasrxlevel, MAX_ARG, 2,       2,         0, CA_PUBLIC},
08676     {"HASTXLEVEL",  fun_hastxlevel, MAX_ARG, 2,       2,         0, CA_PUBLIC},
08677 #endif /* REALITY_LVLS */
08678     {"HASTYPE",     fun_hastype,    MAX_ARG, 2,       2,         0, CA_PUBLIC},
08679     {"HEIGHT",      fun_height,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08680     {"HOME",        fun_home,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08681     {"HOST",        fun_host,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08682     {"IABS",        fun_iabs,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08683     {"IADD",        fun_iadd,       MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08684     {"IDIV",        fun_idiv,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08685     {"IDLE",        fun_idle,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08686     {"IF",          fun_ifelse,     MAX_ARG, 2,       3, FN_NOEVAL, CA_PUBLIC},
08687     {"IFELSE",      fun_ifelse,     MAX_ARG, 3,       3, FN_NOEVAL, CA_PUBLIC},
08688     {"ILEV",        fun_ilev,       MAX_ARG, 0,       0,         0, CA_PUBLIC},
08689     {"IMUL",        fun_imul,       MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08690     {"INC",         fun_inc,        MAX_ARG, 0,       1,         0, CA_PUBLIC},
08691     {"INDEX",       fun_index,      MAX_ARG, 4,       4,         0, CA_PUBLIC},
08692     {"INSERT",      fun_insert,     MAX_ARG, 3,       4,         0, CA_PUBLIC},
08693     {"INUM",        fun_inum,       MAX_ARG, 0,       1,         0, CA_PUBLIC},
08694     {"INZONE",      fun_inzone,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08695     {"ISDBREF",     fun_isdbref,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08696     {"ISIGN",       fun_isign,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08697     {"ISINT",       fun_isint,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08698     {"ISNUM",       fun_isnum,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08699     {"ISRAT",       fun_israt,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08700     {"ISUB",        fun_isub,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08701     {"ISWORD",      fun_isword,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08702     {"ITEMIZE",     fun_itemize,    MAX_ARG, 1,       4,         0, CA_PUBLIC},
08703     {"ITEMS",       fun_items,      MAX_ARG, 0,       1,         0, CA_PUBLIC},
08704     {"ITER",        fun_iter,       MAX_ARG, 2,       4, FN_NOEVAL, CA_PUBLIC},
08705     {"ITEXT",       fun_itext,      MAX_ARG, 0,       1,         0, CA_PUBLIC},
08706     {"LADD",        fun_ladd,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08707     {"LAND",        fun_land,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08708     {"LAST",        fun_last,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08709     {"LATTR",       fun_lattr,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08710     {"LATTRCMDS",   fun_lattrcmds,  MAX_ARG, 1,       1,         0, CA_PUBLIC},
08711     {"LATTRP",      fun_lattrp,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08712 #ifdef REALITY_LVLS
08713     {"LISTRLEVELS", fun_listrlevels, MAX_ARG, 0,       0,         0, CA_PUBLIC},
08714 #endif
08715     {"LCMDS",       fun_lcmds,      MAX_ARG, 1,       3,         0, CA_PUBLIC},
08716     {"LCON",        fun_lcon,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08717     {"LCSTR",       fun_lcstr,            1, 1,       1,         0, CA_PUBLIC},
08718     {"LDELETE",     fun_ldelete,    MAX_ARG, 2,       3,         0, CA_PUBLIC},
08719     {"LEXITS",      fun_lexits,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08720     {"LFLAGS",      fun_lflags,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08721     {"LINK",        fun_link,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08722     {"LIST",        fun_list,       MAX_ARG, 2,       3, FN_NOEVAL, CA_PUBLIC},
08723     {"LIT",         fun_lit,              1, 1,       1, FN_NOEVAL, CA_PUBLIC},
08724     {"LJUST",       fun_ljust,      MAX_ARG, 2,       3,         0, CA_PUBLIC},
08725     {"LN",          fun_ln,         MAX_ARG, 1,       1,         0, CA_PUBLIC},
08726     {"LNUM",        fun_lnum,       MAX_ARG, 0,       3,         0, CA_PUBLIC},
08727     {"LOC",         fun_loc,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08728     {"LOCALIZE",    fun_localize,   MAX_ARG, 1,       1, FN_NOEVAL, CA_PUBLIC},
08729     {"LOCATE",      fun_locate,     MAX_ARG, 3,       3,         0, CA_PUBLIC},
08730     {"LOCK",        fun_lock,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08731     {"LOG",         fun_log,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08732     {"LOR",         fun_lor,        MAX_ARG, 0,       2,         0, CA_PUBLIC},
08733     {"LPARENT",     fun_lparent,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08734     {"LPORTS",      fun_lports,     MAX_ARG, 0,       0,         0, CA_WIZARD},
08735     {"LPOS",        fun_lpos,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08736     {"LRAND",       fun_lrand,      MAX_ARG, 3,       4,         0, CA_PUBLIC},
08737     {"LROOMS",      fun_lrooms,     MAX_ARG, 1,       3,         0, CA_PUBLIC},
08738     {"LSTACK",      fun_lstack,     MAX_ARG, 0,       1,         0, CA_PUBLIC},
08739     {"LT",          fun_lt,         MAX_ARG, 2,       2,         0, CA_PUBLIC},
08740     {"LTE",         fun_lte,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08741     {"LWHO",        fun_lwho,       MAX_ARG, 0,       1,         0, CA_PUBLIC},
08742     {"MAIL",        fun_mail,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08743     {"MAILFROM",    fun_mailfrom,   MAX_ARG, 1,       2,         0, CA_PUBLIC},
08744     {"MAP",         fun_map,        MAX_ARG, 2,       4,         0, CA_PUBLIC},
08745     {"MATCH",       fun_match,      MAX_ARG, 2,       3,         0, CA_PUBLIC},
08746     {"MATCHALL",    fun_matchall,   MAX_ARG, 2,       3,         0, CA_PUBLIC},
08747     {"MAX",         fun_max,        MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08748     {"MEMBER",      fun_member,     MAX_ARG, 2,       3,         0, CA_PUBLIC},
08749     {"MERGE",       fun_merge,      MAX_ARG, 3,       3,         0, CA_PUBLIC},
08750     {"MID",         fun_mid,        MAX_ARG, 3,       3,         0, CA_PUBLIC},
08751     {"MIN",         fun_min,        MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08752     {"MIX",         fun_mix,        MAX_ARG, 3,      12,         0, CA_PUBLIC},
08753     {"MOD",         fun_mod,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08754     {"MONEY",       fun_money,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08755     {"MONIKER",     fun_moniker,    MAX_ARG, 0,       1,         0, CA_PUBLIC},
08756     {"MOTD",        fun_motd,       MAX_ARG, 0,       0,         0, CA_PUBLIC},
08757     {"MTIME",       fun_mtime,      MAX_ARG, 0,       1,         0, CA_PUBLIC},
08758     {"MUDNAME",     fun_mudname,    MAX_ARG, 0,       0,         0, CA_PUBLIC},
08759     {"MUL",         fun_mul,        MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08760     {"MUNGE",       fun_munge,      MAX_ARG, 3,       4,         0, CA_PUBLIC},
08761     {"NAME",        fun_name,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08762     {"NEARBY",      fun_nearby,     MAX_ARG, 2,       2,         0, CA_PUBLIC},
08763     {"NEQ",         fun_neq,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08764     {"NEXT",        fun_next,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08765     {"NOT",         fun_not,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08766     {"NULL",        fun_null,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08767     {"NUM",         fun_num,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08768     {"OBJ",         fun_obj,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08769     {"OBJEVAL",     fun_objeval,    MAX_ARG, 2,       2, FN_NOEVAL, CA_PUBLIC},
08770     {"OBJMEM",      fun_objmem,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08771     {"OEMIT",       fun_oemit,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08772     {"OR",          fun_or,         MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08773     {"ORBOOL",      fun_orbool,     MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08774     {"ORD",         fun_ord,        MAX_ARG, 1,       1,         0, CA_PUBLIC},
08775     {"ORFLAGS",     fun_orflags,    MAX_ARG, 2,       2,         0, CA_PUBLIC},
08776     {"OWNER",       fun_owner,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08777     {"PACK",        fun_pack,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08778     {"PARENT",      fun_parent,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08779     {"PARSE",       fun_iter,       MAX_ARG, 2,       4, FN_NOEVAL, CA_PUBLIC},
08780     {"PEEK",        fun_peek,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08781     {"PEMIT",       fun_pemit,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08782     {"PFIND",       fun_pfind,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08783     {"PI",          fun_pi,         MAX_ARG, 0,       0,         0, CA_PUBLIC},
08784     {"PICKRAND",    fun_pickrand,   MAX_ARG, 0,       2,         0, CA_PUBLIC},
08785     {"PLAYMEM",     fun_playmem,    MAX_ARG, 0,       1,         0, CA_PUBLIC},
08786     {"PMATCH",      fun_pmatch,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08787     {"POLL",        fun_poll,       MAX_ARG, 0,       0,         0, CA_PUBLIC},
08788     {"POP",         fun_pop,        MAX_ARG, 0,       2,         0, CA_PUBLIC},
08789     {"PORTS",       fun_ports,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08790     {"POS",         fun_pos,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08791     {"POSS",        fun_poss,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08792     {"POWER",       fun_power,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08793     {"POWERS",      fun_powers,     MAX_ARG, 1,       1,         0, CA_PUBLIC},
08794     {"PUSH",        fun_push,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08795     {"R",           fun_r,          MAX_ARG, 1,       1,         0, CA_PUBLIC},
08796     {"RAND",        fun_rand,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08797     {"REGMATCH",    fun_regmatch,   MAX_ARG, 2,       3,         0, CA_PUBLIC},
08798     {"REGMATCHI",   fun_regmatchi,  MAX_ARG, 2,       3,         0, CA_PUBLIC},
08799     {"REGRAB",      fun_regrab,     MAX_ARG, 2,       3,         0, CA_PUBLIC},
08800     {"REGRABALL",   fun_regraball,  MAX_ARG, 2,       3,         0, CA_PUBLIC},
08801     {"REGRABALLI",  fun_regraballi, MAX_ARG, 2,       3,         0, CA_PUBLIC},
08802     {"REGRABI",     fun_regrabi,    MAX_ARG, 2,       3,         0, CA_PUBLIC},
08803     {"REMAINDER",   fun_remainder,  MAX_ARG, 2,       2,         0, CA_PUBLIC},
08804     {"REMIT",       fun_remit,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08805     {"REMOVE",      fun_remove,     MAX_ARG, 2,       4,         0, CA_PUBLIC},
08806     {"REPEAT",      fun_repeat,     MAX_ARG, 2,       2,         0, CA_PUBLIC},
08807     {"REPLACE",     fun_replace,    MAX_ARG, 3,       4,         0, CA_PUBLIC},
08808     {"REST",        fun_rest,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08809     {"REVERSE",     fun_reverse,          1, 1,       1,         0, CA_PUBLIC},
08810     {"REVWORDS",    fun_revwords,   MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08811     {"RIGHT",       fun_right,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08812     {"RJUST",       fun_rjust,      MAX_ARG, 2,       3,         0, CA_PUBLIC},
08813     {"RLOC",        fun_rloc,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08814     {"ROMAN",       fun_roman,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08815     {"ROOM",        fun_room,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08816     {"ROUND",       fun_round,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08817 #ifdef REALITY_LVLS
08818     {"RXLEVEL",     fun_rxlevel,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08819 #endif /* REALITY_LVLS */
08820     {"S",           fun_s,                1, 1,       1,         0, CA_PUBLIC},
08821     {"SCRAMBLE",    fun_scramble,   MAX_ARG, 1,       1,         0, CA_PUBLIC},
08822     {"SEARCH",      fun_search,           1, 0,       1,         0, CA_PUBLIC},
08823     {"SECS",        fun_secs,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08824     {"SECURE",      fun_secure,           1, 1,       1,         0, CA_PUBLIC},
08825     {"SET",         fun_set,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08826     {"SETDIFF",     fun_setdiff,    MAX_ARG, 2,       4,         0, CA_PUBLIC},
08827     {"SETINTER",    fun_setinter,   MAX_ARG, 2,       4,         0, CA_PUBLIC},
08828     {"SETQ",        fun_setq,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08829     {"SETR",        fun_setr,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08830     {"SETUNION",    fun_setunion,   MAX_ARG, 2,       4,         0, CA_PUBLIC},
08831     {"SHA1",        fun_sha1,             1, 0,       1,         0, CA_PUBLIC},
08832     {"SHL",         fun_shl,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08833     {"SHR",         fun_shr,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08834     {"SHUFFLE",     fun_shuffle,    MAX_ARG, 1,       3,         0, CA_PUBLIC},
08835     {"SIGN",        fun_sign,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08836     {"SIN",         fun_sin,        MAX_ARG, 1,       2,         0, CA_PUBLIC},
08837     {"SINGLETIME",  fun_singletime, MAX_ARG, 1,       1,         0, CA_PUBLIC},
08838     {"SORT",        fun_sort,       MAX_ARG, 1,       4,         0, CA_PUBLIC},
08839     {"SORTBY",      fun_sortby,     MAX_ARG, 2,       4,         0, CA_PUBLIC},
08840     {"SPACE",       fun_space,      MAX_ARG, 0,       1,         0, CA_PUBLIC},
08841     {"SPELLNUM",    fun_spellnum,   MAX_ARG, 1,       1,         0, CA_PUBLIC},
08842     {"SPLICE",      fun_splice,     MAX_ARG, 3,       5,         0, CA_PUBLIC},
08843     {"SQRT",        fun_sqrt,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08844     {"SQUISH",      fun_squish,     MAX_ARG, 0,       2,         0, CA_PUBLIC},
08845     {"STARTSECS",   fun_startsecs,  MAX_ARG, 0,       0,         0, CA_PUBLIC},
08846     {"STARTTIME",   fun_starttime,  MAX_ARG, 0,       0,         0, CA_PUBLIC},
08847     {"STATS",       fun_stats,      MAX_ARG, 0,       1,         0, CA_PUBLIC},
08848     {"STRCAT",      fun_strcat,     MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08849     {"STRIP",       fun_strip,      MAX_ARG, 1,       2,         0, CA_PUBLIC},
08850     {"STRIPACCENTS",fun_stripaccents, MAX_ARG, 1,     1,         0, CA_PUBLIC},
08851     {"STRIPANSI",   fun_stripansi,  MAX_ARG, 1,       1,         0, CA_PUBLIC},
08852     {"STRLEN",      fun_strlen,           1, 0,       1,         0, CA_PUBLIC},
08853     {"STRMATCH",    fun_strmatch,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08854     {"STRMEM",      fun_strmem,           1, 0,       1,         0, CA_PUBLIC},
08855     {"STRTRUNC",    fun_strtrunc,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08856     {"SUB",         fun_sub,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08857     {"SUBEVAL",     fun_subeval,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08858     {"SUBJ",        fun_subj,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08859     {"SWITCH",      fun_switch,     MAX_ARG, 2, MAX_ARG, FN_NOEVAL, CA_PUBLIC},
08860     {"T",           fun_t,                1, 0,       1,         0, CA_PUBLIC},
08861     {"TABLE",       fun_table,      MAX_ARG, 1,       6,         0, CA_PUBLIC},
08862     {"TAN",         fun_tan,        MAX_ARG, 1,       2,         0, CA_PUBLIC},
08863     {"TEL",         fun_tel,        MAX_ARG, 2,       2,         0, CA_PUBLIC},
08864     {"TEXTFILE",    fun_textfile,   MAX_ARG, 2,       2,         0, CA_PUBLIC},
08865     {"TIME",        fun_time,       MAX_ARG, 0,       2,         0, CA_PUBLIC},
08866     {"TIMEFMT",     fun_timefmt,    MAX_ARG, 1,       2,         0, CA_PUBLIC},
08867     {"TRANSLATE",   fun_translate,  MAX_ARG, 2,       2,         0, CA_PUBLIC},
08868     {"TRIM",        fun_trim,       MAX_ARG, 1,       3,         0, CA_PUBLIC},
08869     {"TRUNC",       fun_trunc,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08870 #ifdef REALITY_LVLS
08871     {"TXLEVEL",     fun_txlevel,    MAX_ARG, 1,       1,         0, CA_PUBLIC},
08872 #endif /* REALITY_LVLS */
08873     {"TYPE",        fun_type,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08874     {"U",           fun_u,          MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08875     {"UCSTR",       fun_ucstr,            1, 1,       1,         0, CA_PUBLIC},
08876     {"UDEFAULT",    fun_udefault,   MAX_ARG, 2, MAX_ARG, FN_NOEVAL, CA_PUBLIC},
08877     {"ULOCAL",      fun_ulocal,     MAX_ARG, 1, MAX_ARG,         0, CA_PUBLIC},
08878     {"UNPACK",      fun_unpack,     MAX_ARG, 1,       2,         0, CA_PUBLIC},
08879     {"V",           fun_v,          MAX_ARG, 1,       1,         0, CA_PUBLIC},
08880     {"VADD",        fun_vadd,       MAX_ARG, 2,       4,         0, CA_PUBLIC},
08881     {"VALID",       fun_valid,      MAX_ARG, 2,       2,         0, CA_PUBLIC},
08882     {"VCROSS",      fun_vcross,     MAX_ARG, 2,       4,         0, CA_PUBLIC},
08883     {"VDIM",        fun_words,      MAX_ARG, 0,       2,         0, CA_PUBLIC},
08884     {"VDOT",        fun_vdot,       MAX_ARG, 2,       4,         0, CA_PUBLIC},
08885     {"VERSION",     fun_version,    MAX_ARG, 0,       0,         0, CA_PUBLIC},
08886     {"VISIBLE",     fun_visible,    MAX_ARG, 2,       2,         0, CA_PUBLIC},
08887     {"VMAG",        fun_vmag,       MAX_ARG, 1,       2,         0, CA_PUBLIC},
08888     {"VMUL",        fun_vmul,       MAX_ARG, 2,       4,         0, CA_PUBLIC},
08889     {"VSUB",        fun_vsub,       MAX_ARG, 2,       4,         0, CA_PUBLIC},
08890     {"VUNIT",       fun_vunit,      MAX_ARG, 1,       2,         0, CA_PUBLIC},
08891     {"WHERE",       fun_where,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08892     {"WIDTH",       fun_width,      MAX_ARG, 1,       1,         0, CA_PUBLIC},
08893     {"WORDPOS",     fun_wordpos,    MAX_ARG, 2,       3,         0, CA_PUBLIC},
08894     {"WORDS",       fun_words,      MAX_ARG, 0,       2,         0, CA_PUBLIC},
08895     {"WRAP",        fun_wrap,       MAX_ARG, 1,       8,         0, CA_PUBLIC},
08896     {"WRITETIME",   fun_writetime,  MAX_ARG, 1,       1,         0, CA_PUBLIC},
08897     {"XGET",        fun_xget,       MAX_ARG, 2,       2,         0, CA_PUBLIC},
08898     {"XOR",         fun_xor,        MAX_ARG, 0, MAX_ARG,         0, CA_PUBLIC},
08899     {"ZFUN",        fun_zfun,       MAX_ARG, 2,      11,         0, CA_PUBLIC},
08900     {"ZONE",        fun_zone,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08901     {"ZWHO",        fun_zwho,       MAX_ARG, 1,       1,         0, CA_PUBLIC},
08902     {NULL,          NULL,           MAX_ARG, 0,       0,         0, 0}
08903 };
08904 
08905 void function_add(FUN *fp)
08906 {
08907     char *buff = alloc_sbuf("init_functab");
08908     char *bp = buff;
08909     safe_sb_str(fp->name, buff, &bp);
08910     *bp = '\0';
08911     mux_strlwr(buff);
08912     hashaddLEN(buff, strlen(buff), fp, &mudstate.func_htab);
08913     free_sbuf(buff);
08914 }
08915 
08916 void functions_add(FUN funlist[])
08917 {
08918     char *buff = alloc_sbuf("init_functab");
08919     for (FUN *fp = funlist; fp->name; fp++)
08920     {
08921         char *bp = buff;
08922         safe_sb_str(fp->name, buff, &bp);
08923         *bp = '\0';
08924         mux_strlwr(buff);
08925         hashaddLEN(buff, strlen(buff), fp, &mudstate.func_htab);
08926     }
08927     free_sbuf(buff);
08928 }
08929 
08930 void init_functab(void)
08931 {
08932     functions_add(builtin_function_list);
08933     ufun_head = NULL;
08934 }
08935 
08936 void do_function
08937 (
08938     dbref executor,
08939     dbref caller,
08940     dbref enactor,
08941     int   key,
08942     int   nargs,
08943     char *fname,
08944     char *target
08945 )
08946 {
08947     UNUSED_PARAMETER(caller);
08948     UNUSED_PARAMETER(enactor);
08949     UNUSED_PARAMETER(nargs);
08950 
08951     UFUN *ufp, *ufp2;
08952     ATTR *ap;
08953 
08954     if ((key & FN_LIST) || !fname || *fname == '\0')
08955     {
08956         notify(executor, tprintf("%-28s   %-8s  %-30s Flgs",
08957             "Function Name", "DBref#", "Attribute"));
08958         notify(executor, tprintf("%28s   %8s  %30s %4s",
08959             "----------------------------", "--------",
08960             "------------------------------", " -- "));
08961 
08962         int count = 0;
08963         for (ufp2 = ufun_head; ufp2; ufp2 = ufp2->next)
08964         {
08965             const char *pName = "(WARNING: Bad Attribute Number)";
08966             ap = atr_num(ufp2->atr);
08967             if (ap)
08968             {
08969                 pName = ap->name;
08970             }
08971             notify(executor, tprintf("%-28.28s   #%-7d  %-30.30s  %c%c",
08972                 ufp2->name, ufp2->obj, pName, ((ufp2->flags & FN_PRIV) ? 'W' : '-'),
08973                 ((ufp2->flags & FN_PRES) ? 'p' : '-')));
08974             count++;
08975         }
08976 
08977         notify(executor, tprintf("%28s   %8s  %30s %4s",
08978             "----------------------------", "--------",
08979             "------------------------------", " -- "));
08980 
08981         notify(executor, tprintf("Total User-Defined Functions: %d", count));
08982         return;
08983     }
08984 
08985     char *np, *bp;
08986     ATTR *pattr;
08987     dbref obj;
08988 
08989     // Make a local uppercase copy of the function name.
08990     //
08991     bp = np = alloc_sbuf("add_user_func");
08992     safe_sb_str(fname, np, &bp);
08993     *bp = '\0';
08994     mux_strlwr(np);
08995 
08996     // Verify that the function doesn't exist in the builtin table.
08997     //
08998     if (hashfindLEN(np, strlen(np), &mudstate.func_htab) != NULL)
08999     {
09000         notify_quiet(executor, "Function already defined in builtin function table.");
09001         free_sbuf(np);
09002         return;
09003     }
09004 
09005     // Make sure the target object exists.
09006     //
09007     if (!parse_attrib(executor, target, &obj, &pattr))
09008     {
09009         notify_quiet(executor, NOMATCH_MESSAGE);
09010         free_sbuf(np);
09011         return;
09012     }
09013 
09014 
09015     // Make sure the attribute exists.
09016     //
09017     if (!pattr)
09018     {
09019         notify_quiet(executor, "No such attribute.");
09020         free_sbuf(np);
09021         return;
09022     }
09023 
09024     // Make sure attribute is readably by me.
09025     //
09026     if (!See_attr(executor, obj, pattr))
09027     {
09028         notify_quiet(executor, NOPERM_MESSAGE);
09029         free_sbuf(np);
09030         return;
09031     }
09032 
09033     // Privileged functions require you control the obj.
09034     //
09035     if ((key & FN_PRIV) && !Controls(executor, obj))
09036     {
09037         notify_quiet(executor, NOPERM_MESSAGE);
09038         free_sbuf(np);
09039         return;
09040     }
09041 
09042     // See if function already exists.  If so, redefine it.
09043     //
09044     ufp = (UFUN *) hashfindLEN(np, strlen(np), &mudstate.ufunc_htab);
09045 
09046     if (!ufp)
09047     {
09048         ufp = (UFUN *) MEMALLOC(sizeof(UFUN));
09049         ISOUTOFMEMORY(ufp);
09050         ufp->name = StringClone(np);
09051         mux_strupr(ufp->name);
09052         ufp->obj = obj;
09053         ufp->atr = pattr->number;
09054         ufp->perms = CA_PUBLIC;
09055         ufp->next = NULL;
09056         if (!ufun_head)
09057         {
09058             ufun_head = ufp;
09059         }
09060         else
09061         {
09062             for (ufp2 = ufun_head; ufp2->next; ufp2 = ufp2->next)
09063             {
09064                 // Nothing
09065                 ;
09066             }
09067             ufp2->next = ufp;
09068         }
09069         hashaddLEN(np, strlen(np), ufp, &mudstate.ufunc_htab);
09070     }
09071     ufp->obj = obj;
09072     ufp->atr = pattr->number;
09073     ufp->flags = key;
09074     free_sbuf(np);
09075     if (!Quiet(executor))
09076     {
09077         notify_quiet(executor, tprintf("Function %s defined.", fname));
09078     }
09079 }
09080 
09081 // ---------------------------------------------------------------------------
09082 // list_functable: List available functions.
09083 //
09084 void list_functable(dbref player)
09085 {
09086     char *buff = alloc_lbuf("list_functable");
09087     char *bp = buff;
09088 
09089     safe_str("Functions:", buff, &bp);
09090 
09091     FUN *fp;
09092     for (fp = builtin_function_list; fp->name && bp < buff + (LBUF_SIZE-1); fp++)
09093     {
09094         if (check_access(player, fp->perms))
09095         {
09096             safe_chr(' ', buff, &bp);
09097             safe_str(fp->name, buff, &bp);
09098         }
09099     }
09100     *bp = '\0';
09101     notify(player, buff);
09102 
09103     bp = buff;
09104     safe_str("User-Functions:", buff, &bp);
09105 
09106     UFUN *ufp;
09107     for (ufp = ufun_head; ufp && bp < buff + (LBUF_SIZE-1); ufp = ufp->next)
09108     {
09109         if (check_access(player, ufp->perms))
09110         {
09111             safe_chr(' ', buff, &bp);
09112             safe_str(ufp->name, buff, &bp);
09113         }
09114     }
09115     *bp = '\0';
09116     notify(player, buff);
09117     free_lbuf(buff);
09118 }
09119 
09120 
09121 /* ---------------------------------------------------------------------------
09122  * cf_func_access: set access on functions
09123  */
09124 
09125 CF_HAND(cf_func_access)
09126 {
09127     UNUSED_PARAMETER(vp);
09128 
09129     char *ap;
09130     for (ap = str; *ap && !mux_isspace(*ap); ap++)
09131     {
09132         *ap = mux_tolower(*ap); // Nothing.
09133     }
09134     int nstr = ap - str;
09135 
09136     if (*ap)
09137     {
09138         *ap++ = '\0';
09139     }
09140 
09141     FUN *fp = (FUN *)hashfindLEN(str, nstr, &mudstate.func_htab);
09142     if (fp)
09143     {
09144         return cf_modify_bits(&fp->perms, ap, pExtra, nExtra, player, cmd);
09145     }
09146     UFUN *ufp = (UFUN *)hashfindLEN(str, nstr, &mudstate.ufunc_htab);
09147     if (ufp)
09148     {
09149         return cf_modify_bits(&ufp->perms, ap, pExtra, nExtra, player, cmd);
09150     }
09151     cf_log_notfound(player, cmd, "Function", str);
09152     return -1;
09153 }

Generated on Mon May 28 04:40:09 2007 for MUX by  doxygen 1.4.7