From 8f71262a4cca45a476bc9efa35e89e72829ecdc1 Mon Sep 17 00:00:00 2001 From: Ari Johnson Date: Fri, 23 Feb 2007 03:58:29 +0000 Subject: [PATCH] Parser updates: ufun() refactoring and process_expression() changes to parse for regular expression pattern captures by default --- hdrs/externs.h | 14 +++ hdrs/parse.h | 7 +- src/funlist.c | 297 ++++++++++++++----------------------------------- src/funufun.c | 73 +++++------- src/game.c | 5 + src/parse.c | 38 +++++-- src/utils.c | 152 +++++++++++++++++++++++++ 7 files changed, 312 insertions(+), 274 deletions(-) diff --git a/hdrs/externs.h b/hdrs/externs.h index 937f1eb..03770eb 100644 --- a/hdrs/externs.h +++ b/hdrs/externs.h @@ -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); diff --git a/hdrs/parse.h b/hdrs/parse.h index 6be0dc7..6f425a0 100644 --- a/hdrs/parse.h +++ b/hdrs/parse.h @@ -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: */ diff --git a/src/funlist.c b/src/funlist.c index 18dbbe1..9d14a7a 100644 --- a/src/funlist.c +++ b/src/funlist.c @@ -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); diff --git a/src/funufun.c b/src/funufun.c index b816d9a..5ce90fa 100644 --- a/src/funufun.c +++ b/src/funufun.c @@ -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; } diff --git a/src/game.c b/src/game.c index 20b57df..f010f0d 100644 --- a/src/game.c +++ b/src/game.c @@ -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)) diff --git a/src/parse.c b/src/parse.c index 399f8a7..5f7c98a 100644 --- a/src/parse.c +++ b/src/parse.c @@ -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 */ diff --git a/src/utils.c b/src/utils.c index 0304238..f91f5dd 100644 --- a/src/utils.c +++ b/src/utils.c @@ -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 [/] 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. -- 2.30.2