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