Parser updates: ufun() refactoring and process_expression() changes to parse for...
authorAri Johnson <ari@cobramush.org>
Fri, 23 Feb 2007 03:58:29 +0000 (03:58 +0000)
committerAri Johnson <ari@cobramush.org>
Fri, 23 Feb 2007 03:58:29 +0000 (03:58 +0000)
hdrs/externs.h
hdrs/parse.h
src/funlist.c
src/funufun.c
src/game.c
src/parse.c
src/utils.c

index 937f1ebc0ef3ffd76210cc8427e1de74d4164e66..03770ebd9078d913144433dbf4e8c2c4166ba2cf 100644 (file)
@@ -210,6 +210,9 @@ struct eval_context {
   char ucom[BUFFER_LEN];      /**< evaluated command */
   int break_called;           /**< Has the break command been called? */
   char break_replace[BUFFER_LEN];  /**< What to replace the break with */
+  int re_subpatterns;        /**< The number of re subpatterns */
+  int *re_offsets;           /**< The offsets for the subpatterns */
+  char *re_from;             /**< The positions of the subpatterns */
 };
 
 typedef struct eval_context EVAL_CONTEXT;
@@ -560,6 +563,17 @@ extern int safe_ansi_string2(ansi_string *as, size_t start, size_t len, char *bu
     extern void parse_anon_attrib(dbref player, char *str, dbref *thing,
                                  ATTR **attrib);
     extern void free_anon_attrib(ATTR *attrib);
+    typedef struct _ufun_attrib {
+      dbref thing;
+      char contents[BUFFER_LEN];
+      int pe_flags;
+      char *errmess;
+    } ufun_attrib;
+    extern int fetch_ufun_attrib(char *attrname, dbref executor,
+                                ufun_attrib * ufun, int accept_lambda);
+    extern int call_ufun(ufun_attrib * ufun, char **wenv_args, int wenv_argc,
+                        char *ret, dbref executor, dbref enactor,
+                        PE_Info * pe_info);
     extern int member(dbref thing, dbref list);
     extern int recursive_member(dbref disallow, dbref from, int count);
     extern dbref remove_first(dbref first, dbref what);
index 6be0dc74c13fc8ba0c793335cf91d30f291a479b..6f425a039b8221bdade266738133b85024a91365 100644 (file)
@@ -183,7 +183,9 @@ int process_expression(char *buff, char **bp, char const **str,
 #define PE_DEBUG                0x00000400
 
 #define PE_DEFAULT (PE_COMPRESS_SPACES | PE_STRIP_BRACES | \
-                    PE_EVALUATE | PE_FUNCTION_CHECK)
+                    PE_DOLLAR | PE_EVALUATE | PE_FUNCTION_CHECK)
+#define PE_UDEFAULT (PE_COMPRESS_SPACES | PE_STRIP_BRACES | \
+                    PE_EVALUATE | PE_FUNCTION_CHECK)
 
 /* PE_COMPRESS_SPACES strips leading and trailing spaces, and reduces sets
  * of internal spaces to one space.
@@ -209,6 +211,9 @@ int process_expression(char *buff, char **bp, char const **str,
  * PE_DEFAULT is the most commonly used set of flags, normally sufficient
  * for calls to process_expression().
  *
+ * PE_UDEFAULT is PE_DEFAULT without PE_DOLLAR, intended for use in
+ * calling attributes (via u(), mix, step, etc)
+ *
  *
  * tflags consists of one or more of the following termination flags:
  */
index 18dbbe1e429a2d6b21d3ce9df7b2d330a7332e98..9d14a7a834cbe3aa9ced8496e38355c2f30fb22d 100644 (file)
@@ -423,89 +423,48 @@ FUNCTION(fun_fold)
    * can provide a starting point.
    */
 
-  dbref thing;
-  ATTR *attrib;
-  char const *ap;
-  char *abuf, *result, *rp, *rsave;
+  ufun_attrib ufun;
   char *cp;
-  char *tptr[2];
+  char *wenv[2];
   char sep;
   int funccount, per;
-  int pe_flags = PE_DEFAULT;
+  char base[BUFFER_LEN];
+  char result[BUFFER_LEN];
 
   if (!delim_check(buff, bp, nargs, args, 4, &sep))
     return;
 
-  /* find our object and attribute */
-  parse_anon_attrib(executor, args[0], &thing, &attrib);
-
-  if (!GoodObject(thing) || !attrib || !Can_Read_Attr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
+  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1))
     return;
-  }
-  if (!CanEvalAttr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
-    return;
-  }
-
-  /* Now we can go to work */
-  if (AF_Debug(attrib))
-    pe_flags |= PE_DEBUG;
-  result = (char *) mush_malloc(BUFFER_LEN, "string");
-  rsave = (char *) mush_malloc(BUFFER_LEN, "string");
-  if (!result || !rsave)
-    mush_panic("Unable to allocate memory in fun_fold");
-
-  abuf = safe_atr_value(attrib);
-
-  /* save our stack */
-  tptr[0] = global_eval_context.wenv[0];
-  tptr[1] = global_eval_context.wenv[1];
 
   cp = args[1];
 
   /* If we have three or more arguments, the third one is the base case */
   if (nargs >= 3) {
-    global_eval_context.wenv[0] = args[2];
-    global_eval_context.wenv[1] = split_token(&cp, sep);
+    strncpy(base, args[2], BUFFER_LEN);
   } else {
-    global_eval_context.wenv[0] = split_token(&cp, sep);
-    global_eval_context.wenv[1] = split_token(&cp, sep);
+    strncpy(base, split_token(&cp, sep), BUFFER_LEN);
   }
-  rp = result;
-  ap = abuf;
-  process_expression(result, &rp, &ap, thing, executor, enactor,
-                    pe_flags, PT_DEFAULT, pe_info);
-  *rp = '\0';
-  strcpy(rsave, result);
+  wenv[0] = base;
+  wenv[1] = split_token(&cp, sep);
+
+  call_ufun(&ufun, wenv, 2, result, executor, enactor, pe_info);
+
+  strncpy(base, result, BUFFER_LEN);
+
   funccount = pe_info->fun_invocations;
 
   /* handle the rest of the cases */
   while (cp && *cp) {
-    global_eval_context.wenv[0] = rsave;
-    global_eval_context.wenv[1] = split_token(&cp, sep);
-    rp = result;
-    ap = abuf;
-    per = process_expression(result, &rp, &ap, thing, executor, enactor,
-                            pe_flags, PT_DEFAULT, pe_info);
-    *rp = '\0';
+    wenv[1] = split_token(&cp, sep);
+    per = call_ufun(&ufun, wenv, 2, result, executor, enactor, pe_info);
     if (per || (pe_info->fun_invocations >= FUNCTION_LIMIT &&
-               pe_info->fun_invocations == funccount &&
-               !strcmp(rsave, result)))
+               pe_info->fun_invocations == funccount && !strcmp(base, result)))
       break;
     funccount = pe_info->fun_invocations;
-    strcpy(rsave, result);
+    strcpy(base, result);
   }
-  safe_str(rsave, buff, bp);
-
-  /* restore the stack */
-  global_eval_context.wenv[0] = tptr[0];
-  global_eval_context.wenv[1] = tptr[1];
-
-  free((Malloc_t) abuf);
-  mush_free((Malloc_t) result, "string");
-  mush_free((Malloc_t) rsave, "string");
-  free_anon_attrib(attrib);
+  safe_str(base, buff, bp);
 }
 
 /* ARGSUSED */
@@ -567,62 +526,37 @@ FUNCTION(fun_filter)
    * of the list for which the function evaluates to 1.
    */
 
-  dbref thing;
-  ATTR *attrib;
-  char const *ap;
-  char *abuf, result[BUFFER_LEN], *rp;
+  ufun_attrib ufun;
+  char result[BUFFER_LEN];
   char *cp;
-  char *tptr;
+  char *wenv[1];
   char sep;
   int first;
   int check_bool = 0;
   int funccount;
   char *osep, osepd[2] = { '\0', '\0' };
-  int pe_flags = PE_DEFAULT;
 
   if (!delim_check(buff, bp, nargs, args, 3, &sep))
     return;
 
-  if (nargs == 4)
-    osep = args[3];
-  else {
-    osepd[0] = sep;
-    osep = osepd;
-  }
+  osepd[0] = sep;
+  osep = (nargs >= 4) ? args[3] : osepd;
 
   if (strcmp(called_as, "FILTERBOOL") == 0)
     check_bool = 1;
 
   /* find our object and attribute */
-  parse_anon_attrib(executor, args[0], &thing, &attrib);
-
-  if (!GoodObject(thing) || !attrib || !Can_Read_Attr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
+  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1))
     return;
-  }
-  if (!CanEvalAttr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
-    return;
-  }
-
-  if (AF_Debug(attrib))
-    pe_flags |= PE_DEBUG;
-
-  abuf = safe_atr_value(attrib);
-
-  tptr = global_eval_context.wenv[0];
 
+  /* Go through each argument */
   cp = trim_space_sep(args[1], sep);
   first = 1;
   funccount = pe_info->fun_invocations;
   while (cp && *cp) {
-    global_eval_context.wenv[0] = split_token(&cp, sep);
-    ap = abuf;
-    rp = result;
-    if (process_expression(result, &rp, &ap, thing, executor, enactor,
-                          pe_flags, PT_DEFAULT, pe_info))
+    wenv[0] = split_token(&cp, sep);
+    if (call_ufun(&ufun, wenv, 1, result, executor, enactor, pe_info))
       break;
-    *rp = '\0';
     if ((check_bool == 0)
        ? (*result == '1' && *(result + 1) == '\0')
        : parse_boolean(result)) {
@@ -630,7 +564,7 @@ FUNCTION(fun_filter)
        first = 0;
       else
        safe_str(osep, buff, bp);
-      safe_str(global_eval_context.wenv[0], buff, bp);
+      safe_str(wenv[0], buff, bp);
     }
     /* Can't do *bp == oldbp like in all the others, because bp might not
      * move even when not full, if one of the list elements is null and
@@ -639,11 +573,6 @@ FUNCTION(fun_filter)
       break;
     funccount = pe_info->fun_invocations;
   }
-
-  global_eval_context.wenv[0] = tptr;
-
-  free((Malloc_t) abuf);
-  free_anon_attrib(attrib);
 }
 
 /* ARGSUSED */
@@ -2884,78 +2813,50 @@ FUNCTION(fun_map)
    * This function takes delimiters.
    */
 
-  dbref thing;
-  ATTR *attrib;
-  char const *ap;
-  char *asave, *lp;
-  char *tptr[2];
+  ufun_attrib ufun;
+  char *lp;
+  char *wenv[2];
   char place[16];
   int placenr = 1;
   char sep;
   int funccount;
-  char *oldbp;
   char *osep, osepd[2] = { '\0', '\0' };
-  int pe_flags = PE_DEFAULT;
+  char rbuff[BUFFER_LEN];
 
   if (!delim_check(buff, bp, nargs, args, 3, &sep))
     return;
 
-  if (nargs == 4)
-    osep = args[3];
-  else {
-    osepd[0] = sep;
-    osep = osepd;
-  }
+  osepd[0] = sep;
+  osep = (nargs >= 4) ? args[3] : osepd;
 
   lp = trim_space_sep(args[1], sep);
   if (!*lp)
     return;
 
-  /* find our object and attribute */
-  parse_anon_attrib(executor, args[0], &thing, &attrib);
-  if (!GoodObject(thing) || !attrib || !Can_Read_Attr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
-    return;
-  }
-  if (!CanEvalAttr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
+  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1))
     return;
-  }
-  if (AF_Debug(attrib))
-    pe_flags |= PE_DEBUG;
 
   strcpy(place, "1");
-  asave = safe_atr_value(attrib);
 
-  /* save our stack */
-  tptr[0] = global_eval_context.wenv[0];
-  tptr[1] = global_eval_context.wenv[1];
-  global_eval_context.wenv[1] = place;
+  /* Build our %0 args */
+  wenv[0] = split_token(&lp, sep);
+  wenv[1] = place;
 
-  global_eval_context.wenv[0] = split_token(&lp, sep);
-  ap = asave;
-  process_expression(buff, bp, &ap, thing, executor, enactor,
-                    pe_flags, PT_DEFAULT, pe_info);
-  oldbp = *bp;
+  call_ufun(&ufun, wenv, 2, rbuff, executor, enactor, pe_info);
   funccount = pe_info->fun_invocations;
+  safe_str(rbuff, buff, bp);
   while (lp) {
     safe_str(osep, buff, bp);
     strcpy(place, unparse_integer(++placenr));
-    global_eval_context.wenv[0] = split_token(&lp, sep);
-    ap = asave;
-    if (process_expression(buff, bp, &ap, thing, executor, enactor,
-                          pe_flags, PT_DEFAULT, pe_info))
+    wenv[0] = split_token(&lp, sep);
+
+    if (call_ufun(&ufun, wenv, 2, rbuff, executor, enactor, pe_info))
       break;
+    safe_str(rbuff, buff, bp);
     if (*bp == (buff + BUFFER_LEN - 1) && pe_info->fun_invocations == funccount)
       break;
-    oldbp = *bp;
     funccount = pe_info->fun_invocations;
   }
-
-  free((Malloc_t) asave);
-  free_anon_attrib(attrib);
-  global_eval_context.wenv[0] = tptr[0];
-  global_eval_context.wenv[1] = tptr[1];
 }
 
 
@@ -2967,17 +2868,15 @@ FUNCTION(fun_mix)
    * This function takes delimiters.
    */
 
-  dbref thing;
-  ATTR *attrib;
-  char const *ap;
-  char *asave, *lp[10];
-  char *tptr[10];
+  ufun_attrib ufun;
+  char rbuff[BUFFER_LEN];
+  char *lp[10];
+  char *list[10];
   char sep;
   int funccount;
   int n;
   int lists, words;
-  char *oldbp;
-  int pe_flags = PE_DEFAULT;
+  int first = 1;
 
   if (nargs > 3) {             /* Last arg must be the delimiter */
     n = nargs;
@@ -2994,69 +2893,31 @@ FUNCTION(fun_mix)
     lp[n] = trim_space_sep(args[n + 1], sep);
 
   /* find our object and attribute */
-  parse_anon_attrib(executor, args[0], &thing, &attrib);
-  if (!GoodObject(thing) || !attrib || !Can_Read_Attr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
-    return;
-  }
-  if (!CanEvalAttr(executor, thing, attrib)) {
-    free_anon_attrib(attrib);
+  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1))
     return;
-  }
-  if (AF_Debug(attrib))
-    pe_flags |= PE_DEBUG;
-
-  asave = safe_atr_value(attrib);
 
-  /* save our stack */
-  save_global_env("fun_mix", tptr);
-
-  words = 0;
-  for (n = 0; n < 10; n++) {
-    if ((n < lists) && lp[n] && *lp[n]) {
-      global_eval_context.wenv[n] = split_token(&lp[n], sep);
-      if (global_eval_context.wenv[n])
-       words++;
-    } else
-      global_eval_context.wenv[n] = NULL;
-  }
-  if (words == 0) {
-    restore_global_env("fun_mix", tptr);
-    free((Malloc_t) asave);
-    free_anon_attrib(attrib);
-    return;
-  }
-  ap = asave;
-  process_expression(buff, bp, &ap, thing, executor, enactor,
-                    pe_flags, PT_DEFAULT, pe_info);
-  oldbp = *bp;
-  funccount = pe_info->fun_invocations;
+  first = 0;
   while (1) {
     words = 0;
-    for (n = 0; n < 10; n++) {
-      if ((n < lists) && lp[n] && *lp[n]) {
-       global_eval_context.wenv[n] = split_token(&lp[n], sep);
-       if (global_eval_context.wenv[n])
+    for (n = 0; n < lists; n++) {
+      if (lp[n] && *lp[n]) {
+       list[n] = split_token(&lp[n], sep);
+       if (list[n])
          words++;
-      } else
-       global_eval_context.wenv[n] = NULL;
+      } else {
+       list[n] = NULL;
+      }
     }
-    if (words == 0)
-      break;
-    safe_chr(sep, buff, bp);
-    ap = asave;
-    if (process_expression(buff, bp, &ap, thing, executor, enactor,
-                          pe_flags, PT_DEFAULT, pe_info))
-      break;
-    if (*bp == (buff + BUFFER_LEN - 1) && pe_info->fun_invocations == funccount)
-      break;
-    oldbp = *bp;
+    if (!words)
+      return;
+    if (first)
+      first = 0;
+    else
+      safe_chr(sep, buff, bp);
     funccount = pe_info->fun_invocations;
+    call_ufun(&ufun, list, lists, rbuff, executor, enactor, pe_info);
+    safe_str(rbuff, buff, bp);
   }
-
-  free((Malloc_t) asave);
-  free_anon_attrib(attrib);
-  restore_global_env("fun_mix", tptr);
 }
 
 /* ARGSUSED */
@@ -3168,9 +3029,6 @@ FUNCTION(fun_table)
 
 /* string, regexp, replacement string. Acts like sed or perl's s///g,
 //with an ig version */
-int re_subpatterns = -1;  /**< Number of subpatterns in regexp */
-int *re_offsets;         /**< Array of offsets to subpatterns */
-char *re_from = NULL;    /**< Pointer to last match position */
 FUNCTION(fun_regreplace)
 {
   pcre *re;
@@ -3185,10 +3043,17 @@ FUNCTION(fun_regreplace)
   char abuf[BUFFER_LEN], *abp;
   char prebuf[BUFFER_LEN], *prep;
   char postbuf[BUFFER_LEN], *postp;
-
   int flags = 0, all = 0, match_offset = 0, len, funccount;
   int i;
 
+  int old_re_subpatterns;
+  int *old_re_offsets;
+  char *old_re_from;
+
+  old_re_subpatterns = global_eval_context.re_subpatterns;
+  old_re_offsets = global_eval_context.re_offsets;
+  old_re_from = global_eval_context.re_from;
+
   if (called_as[strlen(called_as) - 1] == 'I')
     flags = PCRE_CASELESS;
 
@@ -3263,9 +3128,9 @@ FUNCTION(fun_regreplace)
 
       /* Now copy in the replacement, putting in captured sub-expressions */
       obp = args[i + 1];
-      re_from = prebuf;
-      re_offsets = offsets;
-      re_subpatterns = subpatterns;
+      global_eval_context.re_from = prebuf;
+      global_eval_context.re_offsets = offsets;
+      global_eval_context.re_subpatterns = subpatterns;
       process_expression(postbuf, &postp, &obp, executor, caller, enactor,
                         PE_DEFAULT | PE_DOLLAR, PT_DEFAULT, pe_info);
       if ((*bp == (buff + BUFFER_LEN - 1))
@@ -3293,9 +3158,9 @@ FUNCTION(fun_regreplace)
     if (study)
       mush_free((Malloc_t) study, "pcre.extra");
 
-    re_offsets = NULL;
-    re_subpatterns = -1;
-    re_from = NULL;
+    global_eval_context.re_offsets = old_re_offsets;
+    global_eval_context.re_subpatterns = old_re_subpatterns;
+    global_eval_context.re_from = old_re_from;
   }
 
   safe_str(postbuf, buff, bp);
index b816d9a2b08e012648fd3ae77c7a1455511be3f0..5ce90fa718dbde34fc88f73074dc289de4c5bb0e 100644 (file)
@@ -148,31 +148,22 @@ do_userfn(char *buff, char **bp, dbref obj, ATTR *attrib, int nargs,
 /* ARGSUSED */
 FUNCTION(fun_ufun)
 {
-  ATTR *attrib;
-  dbref obj;
+  char rbuff[BUFFER_LEN];
+  ufun_attrib ufun;
 
   BEGINOOREF_L
 
-  /* find the user function attribute */
-  parse_attrib(executor, args[0], &obj, &attrib);
-  if (!GoodObject(obj)) {
-    safe_str(T("#-1 INVALID OBJECT"), buff, bp);
-    goto ufun_done;
-  }
-  if (attrib && Can_Read_Attr(executor, obj, attrib)) {
-    if (!CanEvalAttr(executor, obj, attrib)) {
-      safe_str(T(e_perm), buff, bp);
-      goto ufun_done;
-    }
-    do_userfn(buff, bp, obj, attrib, nargs - 1, args + 1, executor, caller,
-             enactor, pe_info);
-    goto ufun_done;
-  } else if (attrib || !Can_Examine(executor, obj)) {
-    safe_str(T(e_atrperm), buff, bp);
-    goto ufun_done;
+  if (!fetch_ufun_attrib(args[0], executor, &ufun, 0)) {
+    safe_str(T(ufun.errmess), buff, bp);
+    return;
   }
-ufun_done:
+
+  call_ufun(&ufun, args + 1, nargs - 1, rbuff, executor, enactor, pe_info);
+
+  safe_str(rbuff, buff, bp);
+
   ENDOOREF_L
+
   return;
 }
 
@@ -182,37 +173,27 @@ FUNCTION(fun_ulocal)
   /* Like fun_ufun, but saves the state of the q0-q9 registers
    * when called
    */
-  ATTR *attrib;
-  dbref obj;
   char *preserve[NUMQ];
+  char rbuff[BUFFER_LEN];
+  ufun_attrib ufun;
 
-  BEGINOOREF_L
-
-  /* find the user function attribute */
-  parse_attrib(executor, args[0], &obj, &attrib);
-  if (!GoodObject(obj)) {
-    safe_str(T("#-1 INVALID OBJECT"), buff, bp);
-    ENDOOREF_L
-    return;
-  }
-  if (attrib && Can_Read_Attr(executor, obj, attrib)) {
-    if (!CanEvalAttr(executor, obj, attrib)) {
-      safe_str(T(e_perm), buff, bp);
-      ENDOOREF_L
-      return;
-    }
-    save_global_regs("ulocal.save", preserve);
-    do_userfn(buff, bp, obj, attrib, nargs - 1, args + 1, executor, caller,
-             enactor, pe_info);
-    restore_global_regs("ulocal.save", preserve);
-    ENDOOREF_L
-    return;
-  } else if (attrib || !Can_Examine(executor, obj)) {
-    safe_str(T(e_atrperm), buff, bp);
-    ENDOOREF_L
+  if (!fetch_ufun_attrib(args[0], executor, &ufun, 0)) {
+    safe_str(T(ufun.errmess), buff, bp);
     return;
   }
+
+  BEGINOOREF_L
+
+  /* Save global regs */
+  save_global_regs("ulocal.save", preserve);
+
+  call_ufun(&ufun, args + 1, nargs - 1, rbuff, executor, enactor, pe_info);
+  safe_str(rbuff, buff, bp);
+
+  restore_global_regs("ulocal.save", preserve);
+
   ENDOOREF_L
+
   return;
 }
 
index 20b57dff2ee26459dda75f2040286cbf30d36fab..f010f0dc7761c8adb3a14bf68c7963e037edcd58 100644 (file)
@@ -709,6 +709,11 @@ do_restart(void)
   for (j = 0; j < NUMQ; j++)
     global_eval_context.rnxt[j] = NULL;
 
+  /* Initialize the regexp patterns to nothing */
+  global_eval_context.re_subpatterns = -1;
+  global_eval_context.re_offsets = NULL;
+  global_eval_context.re_from = NULL;
+
   for (thing = 0; thing < db_top; thing++) {
     if (Name(thing) == NULL) {
       if (IsGarbage(thing))
index 399f8a79bb3f53072acc772ec46b09be85159d89..5f7c98aab6d5fbb4fabf834db42261ee07cfa0f1 100644 (file)
@@ -39,9 +39,9 @@ extern int inum, inum_limit;
 extern char *iter_rep[];
 int global_fun_invocations;
 int global_fun_recursions;
-extern int re_subpatterns;
-extern int *re_offsets;
-extern char *re_from;
+/* extern int re_subpatterns; */
+/* extern int *re_offsets; */
+/* extern char *re_from; */
 extern sig_atomic_t cpu_time_limit_hit;
 extern int cpu_limit_warning_sent;
 extern int iter_break;
@@ -652,10 +652,8 @@ process_expression(char *buff, char **bp, char const **str,
       }
       break;
     case '$':                  /* Dollar subs for regedit() */
-      if ((eflags & (PE_DOLLAR | PE_EVALUATE)) != (PE_DOLLAR | PE_EVALUATE)) {
-       safe_chr('$', buff, bp);
-       (*str)++;
-      } else {
+      if ((eflags & (PE_DOLLAR | PE_EVALUATE)) == (PE_DOLLAR | PE_EVALUATE) &&
+         global_eval_context.re_subpatterns >= 0) {
        char obuf[BUFFER_LEN];
        int p = 0;
 
@@ -668,16 +666,34 @@ process_expression(char *buff, char **bp, char const **str,
            p *= 10;
            p += **str - '0';
            (*str)++;
+           if (isdigit((unsigned char) **str)) {
+             /* More than 100. Treat this as literal. */
+             safe_chr('$', buff, bp);
+             safe_number(p, buff, bp);
+           }
          }
-       } else
+       } else {
+         safe_chr('$', buff, bp);
          break;
+       }
 
-       if (p >= re_subpatterns || re_offsets == NULL || re_from == NULL)
+       if (p >= global_eval_context.re_subpatterns ||
+           global_eval_context.re_offsets == NULL ||
+           global_eval_context.re_from == NULL) {
+         /* It's out of bounds, return */
+         safe_chr('$', buff, bp);
+         safe_number(p, buff, bp);
          break;
+       }
 
-       pcre_copy_substring(re_from, re_offsets, re_subpatterns, p, obuf,
-                           BUFFER_LEN);
+       pcre_copy_substring(global_eval_context.re_from,
+                           global_eval_context.re_offsets,
+                           global_eval_context.re_subpatterns,
+                           p, obuf, BUFFER_LEN);
        safe_str(obuf, buff, bp);
+      } else {
+       safe_chr('$', buff, bp);
+       (*str)++;
       }
       break;
     case '%':                  /* Percent substitutions */
index 0304238af9db8baff69e247cf8a2834e40da6b6d..f91f5dd3f6e8434e91c2bd1f39f96aa09772968e 100644 (file)
@@ -42,6 +42,7 @@
 #include "flags.h"
 #include "dbdefs.h"
 #include "attrib.h"
+#include "parse.h"
 #include "lock.h"
 #include "confmagic.h"
 
@@ -168,6 +169,157 @@ free_anon_attrib(ATTR *attrib)
   }
 }
 
+/** Given an attribute [<object>/]<name> pair (which may include #lambda),
+ * fetch its value, owner (thing), and pe_flags, and store in the struct
+ * pointed to by ufun
+ */
+int 
+fetch_ufun_attrib(char *attrname, dbref executor, ufun_attrib * ufun,
+                 int accept_lambda)
+{ 
+  ATTR *attrib;
+  dbref thing;
+  int pe_flags = PE_UDEFAULT;
+    
+  if (!ufun)
+    return 0;          /* We should never NOT receive a ufun. */
+  ufun->errmess = (char *) "";
+    
+  /* find our object and attribute */
+  if (accept_lambda) {
+    parse_anon_attrib(executor, attrname, &thing, &attrib);
+  } else {
+    parse_attrib(executor, attrname, &thing, &attrib);
+  }
+
+  /* Is it valid? */
+  if (!GoodObject(thing)) {
+    ufun->errmess = (char *) "#-1 INVALID OBJECT";
+    free_anon_attrib(attrib);
+    return 0;
+  } else if (!attrib) {
+    ufun->contents[0] = '\0';
+    ufun->thing = thing;
+    ufun->pe_flags = pe_flags;
+    free_anon_attrib(attrib);
+    return 1;
+  } else if (!Can_Read_Attr(executor, thing, attrib)) {
+    ufun->errmess = e_atrperm;
+    free_anon_attrib(attrib);
+    return 0;
+  }
+    
+  /* Can we evaluate it? */
+  if (!CanEvalAttr(executor, thing, attrib)) {
+    ufun->errmess = e_perm;
+    free_anon_attrib(attrib);
+    return 0;
+  } 
+    
+  /* DEBUG attributes */
+  if (AF_Debug(attrib))
+    pe_flags |= PE_DEBUG;
+    
+  /* Populate the ufun object */
+  strncpy(ufun->contents, atr_value(attrib), BUFFER_LEN);
+  ufun->thing = thing;
+  ufun->pe_flags = pe_flags;
+  
+  /* Cleanup */
+  free_anon_attrib(attrib);
+    
+  /* We're good */
+  return 1;
+}
+
+/** Given a ufun, executor, enactor, PE_Info, and arguments for %0-%9,
+ * call the ufun with appropriate permissions on values given for
+ * wenv_args. The value returned is stored in the buffer pointed to
+ * by retval, if given.
+ * \param ufun The ufun_attrib that was initialized by fetch_ufun_attrib
+ * \param wenv_args An array of string values for global_eval_context.wenv
+ * \param wenv_argc The number of wenv args to use.
+ * \param ret If desired, a pointer to a buffer in which the results
+ * of the process_expression are stored in.
+ * \param executor The executor.
+ * \param enactor The enactor.
+ * \param pe_info The pe_info passed to the FUNCTION
+ * \retval 0 success
+ * \retval 1 process_expression failed. (CPU time limit)
+ */
+int
+call_ufun(ufun_attrib * ufun, char **wenv_args, int wenv_argc, char *ret,
+         dbref executor, dbref enactor, PE_Info * pe_info)   
+{
+  char rbuff[BUFFER_LEN];
+  char *rp;
+  char *old_wenv[10];
+  int old_args;
+  int i;
+  int pe_ret;
+  char const *ap;
+
+  int old_re_subpatterns;
+  int *old_re_offsets;
+  char *old_re_from;
+
+  old_re_subpatterns = global_eval_context.re_subpatterns;
+  old_re_offsets = global_eval_context.re_offsets;
+  old_re_from = global_eval_context.re_from;
+
+  /* Make sure we have a ufun first */
+  if (!ufun)
+    return 1;
+
+  /* If the user doesn't care about the return of the expression,
+   * then use our own rbuff.
+   */
+  if (!ret)
+    ret = rbuff;
+  rp = ret;
+
+  for (i = 0; i < wenv_argc; i++) {
+    old_wenv[i] = global_eval_context.wenv[i];
+    global_eval_context.wenv[i] = wenv_args[i];
+  }
+  for (; i < 10; i++) {
+    old_wenv[i] = global_eval_context.wenv[i];
+    global_eval_context.wenv[i] = NULL;
+  }
+
+  /* Set all the regexp patterns to NULL so they are not
+   * propogated */
+  global_eval_context.re_subpatterns = -1;
+  global_eval_context.re_offsets = NULL;
+  global_eval_context.re_from = NULL;
+
+  /* And now, make the call! =) */
+  if (pe_info) {
+    old_args = pe_info->arg_count;
+    pe_info->arg_count = wenv_argc;
+  }
+
+  ap = ufun->contents;
+  pe_ret = process_expression(ret, &rp, &ap, ufun->thing, executor,
+                             enactor, ufun->pe_flags, PT_DEFAULT, pe_info);
+  *rp = '\0';
+
+  /* Restore the old wenv */
+  for (i = 0; i < 10; i++) {
+    global_eval_context.wenv[i] = old_wenv[i];
+  }
+  if (pe_info) {
+    pe_info->arg_count = old_args;
+  }
+
+  /* Restore regexp patterns */
+  global_eval_context.re_offsets = old_re_offsets;
+  global_eval_context.re_subpatterns = old_re_subpatterns;
+  global_eval_context.re_from = old_re_from;
+
+  return pe_ret;
+}
+
 /** Given an exit, find the room that is its source through brute force.
  * This is used in pathological cases where the exit's own source
  * element is invalid.