/*\ |*| array_mash |*| Takes the lists of values from the first array and |*| uses each value as a key in the second array. For |*| each key, the passed "change_by" value is applied to |*| any existing value. If the value does not exist, it |*| is set. |*| This will be the core of the different/union/intersection |*| code. |*| Of course, this is going to absolutely blow chunks when |*| passed an array with value types that can't be used as |*| key values in an array. Blast. That may be infeasible |*| regardless, though. \*/ void array_mash(stk_array *arr_in, stk_array **mash, int value) { int still_values = 0; array_iter current_key; array_iter *current_keyval; array_iter *current_value; array_data temp_value; if (NULL == arr_in || NULL == mash || NULL == *mash) return; still_values = array_first(arr_in, ¤t_key); while (still_values) { current_keyval = array_getitem(arr_in, ¤t_key); current_value = array_getitem(*mash, current_keyval); if (NULL != current_value) { if (PROG_INTEGER == current_value->type) { copyinst(current_value, &temp_value); temp_value.data.number += value; array_setitem(mash, current_keyval, &temp_value); } } else { temp_value.type = PROG_INTEGER; temp_value.data.number = value; array_insertitem(mash, current_keyval, &temp_value); } still_values = array_next(arr_in, ¤t_key); } }
int array_setrange(stk_array **harr, array_iter *start, stk_array *inarr) { stk_array *arr; array_iter idx; if (!harr || !*harr) { return -1; } arr = *harr; if (!inarr || !inarr->items) { return arr->items; } switch (arr->type) { case ARRAY_PACKED:{ if (!start) { return -1; } if (start->type != PROG_INTEGER) { return -1; } if (start->data.number < 0 || start->data.number > arr->items) { return -1; } if (arr->links > 1 && !arr->pinned) { arr->links--; arr = *harr = array_decouple(arr); } if (array_first(inarr, &idx)) { do { array_setitem(&arr, start, array_getitem(inarr, &idx)); start->data.number++; } while (array_next(inarr, &idx)); } return arr->items; break; } case ARRAY_DICTIONARY:{ if (arr->links > 1 && !arr->pinned) { arr->links--; arr = *harr = array_decouple(arr); } if (array_first(inarr, &idx)) { do { array_setitem(&arr, &idx, array_getitem(inarr, &idx)); } while (array_next(inarr, &idx)); } return arr->items; break; } default: break; } return -1; }
stk_array * array_promote(stk_array *arr) { stk_array *new2; int i; array_iter idx; if (!arr) { return NULL; } if (arr->type != ARRAY_PACKED) { return NULL; } new2 = new_array_dictionary(); idx.type = PROG_INTEGER; for (i = 0; i < arr->items; i++) { idx.data.number = i; array_setitem(&new2, &idx, array_getitem(arr, &idx)); } array_free(arr); return new2; }
/*\ |*| array_demote_only |*| array demote discards the values of a dictionary, and |*| returns a packed list of the keys. |*| (Useful because keys are ordered and unique, presumably.) |*| (This allows the keys to be abused as sets.) \*/ stk_array * array_demote_only(stk_array *arr, int threshold) { stk_array *demoted_array = NULL; int items_left = 0; array_iter current_key; array_iter *current_value; array_iter new_index; if (!arr || ARRAY_DICTIONARY != arr->type) return NULL; new_index.type = PROG_INTEGER;; new_index.data.number = 0; demoted_array = new_array_packed(0); items_left = array_first(arr, ¤t_key); while (items_left) { current_value = array_getitem(arr, ¤t_key); if (PROG_INTEGER == current_value->type && current_value->data.number >= threshold) { array_insertitem(&demoted_array, &new_index, ¤t_key); new_index.data.number++; } items_left = array_next(arr, ¤t_key); } return demoted_array; }
void prim_foriter(PRIM_PROTOTYPE) { CHECKOP(0); if (!fr->fors.st) abort_interp("Internal error; FOR stack underflow."); if (fr->fors.st->end.type == PROG_ARRAY) { stk_array *arr = fr->fors.st->end.data.array; if (fr->fors.st->didfirst) { result = array_next(arr, &fr->fors.st->cur); } else { result = array_first(arr, &fr->fors.st->cur); fr->fors.st->didfirst = 1; } if (result) { array_data *val = array_getitem(arr, &fr->fors.st->cur); if (val) { CHECKOFLOW(2); PushInst(&fr->fors.st->cur); /* push key onto stack */ PushInst(val); /* push value onto stack */ tmp = 1; /* tell following IF to NOT branch out of loop */ } else { tmp = 0; /* tell following IF to branch out of loop */ } } else { fr->fors.st->cur.type = PROG_INTEGER; fr->fors.st->cur.line = 0; fr->fors.st->cur.data.number = 0; tmp = 0; /* tell following IF to branch out of loop */ } } else { int cur = fr->fors.st->cur.data.number; int end = fr->fors.st->end.data.number; tmp = fr->fors.st->step > 0; if (tmp) tmp = !(cur > end); else tmp = !(cur < end); if (tmp) { CHECKOFLOW(1); result = cur; fr->fors.st->cur.data.number += fr->fors.st->step; PushInt(result); } } CHECKOFLOW(1); PushInt(tmp); }
int array_is_homogenous(stk_array *arr, int typ) { array_iter idx; array_data *dat; int failedflag = 0; if (array_first(arr, &idx)) { do { dat = array_getitem(arr, &idx); if (dat->type != typ) { failedflag = 1; } } while (array_next(arr, &idx)); } return (!failedflag); }
stk_array * array_decouple(stk_array *arr) { stk_array *new2; if (!arr) { return NULL; } new2 = new_array(); new2->pinned = arr->pinned; new2->type = arr->type; switch (arr->type) { case ARRAY_PACKED:{ int i; new2->items = arr->items; new2->data.packed = (array_data *) malloc(sizeof(array_data) * arr->items); for (i = arr->items; i-- > 0;) { copyinst(&arr->data.packed[i], &new2->data.packed[i]); } return new2; break; } case ARRAY_DICTIONARY:{ array_iter idx; array_data *val; if (array_first(arr, &idx)) { do { val = array_getitem(arr, &idx); array_setitem(&new2, &idx, val); } while (array_next(arr, &idx)); } return new2; break; } default: break; } return NULL; }
const char * array_get_intkey_strval(stk_array *arr, int key) { struct inst ikey; array_data *value; ikey.type = PROG_INTEGER; ikey.data.number = key; value = array_getitem(arr, &ikey); CLEAR(&ikey); if (!value || value->type != PROG_STRING) { return NULL; } else if (!value->data.string) { return ""; } else { return value->data.string->data; } }
void prim_array_regfilter_prop(PRIM_PROTOTYPE) { char buf[BUFFER_LEN]; struct inst *in; stk_array *arr; stk_array *nu; char* prop; const char* ptr; muf_re* re; int flags; int matchcnt = 0; const char* errstr = NULL; CHECKOP(4); oper4 = POP(); /* int pcreflags */ oper3 = POP(); /* str pattern */ oper2 = POP(); /* str propname */ oper1 = POP(); /* refarr Array */ if (oper1->type != PROG_ARRAY) abort_interp("Argument not an array. (1)"); if (!array_is_homogenous(oper1->data.array, PROG_OBJECT)) abort_interp("Argument not an array of dbrefs. (1)"); if (oper2->type != PROG_STRING || !oper2->data.string) abort_interp("Argument not a non-null string. (2)"); if (oper3->type != PROG_STRING) abort_interp("Argument not a string pattern. (3)"); if (oper4->type != PROG_INTEGER) abort_interp("Non-integer argument (4)"); ptr = oper2->data.string->data; while ((ptr = index(ptr, PROPDIR_DELIMITER))) if (!(*(++ptr))) abort_interp("Cannot access a propdir directly."); nu = new_array_packed(0); arr = oper1->data.array; flags = PCRE_NO_AUTO_CAPTURE; if (oper4->data.number & MUF_RE_ICASE) flags |= PCRE_CASELESS; if (oper4->data.number & MUF_RE_EXTENDED) flags |= PCRE_EXTENDED; re = regmatch_re_get(oper3->data.string, flags, &errstr); if (errstr) abort_interp(errstr) if (re && !re->extra && array_count(arr) > 2) { /* This pattern is getting used 3 or more times, let's study it. A null * return is okay, that just means there's nothing to optimize. */ re->extra = pcre_study(re->re, 0, &errstr); if (errstr) abort_interp(errstr); } prop = (char *) DoNullInd(oper2->data.string); if (array_first(arr, &temp1)) { do { in = array_getitem(arr, &temp1); if (valid_object(in)) { ref = in->data.objref; CHECKREMOTE(ref); if (prop_read_perms(ProgUID, ref, prop, mlev)) { ptr = get_property_class(ref, prop); if (ptr) strcpy(buf, ptr); else strcpy(buf, ""); if ((matchcnt = regmatch_exec(re, buf)) < 0) { if (matchcnt != PCRE_ERROR_NOMATCH) abort_interp(muf_re_error(matchcnt)); } else { array_appenditem(&nu, in); } } } } while (array_next(arr, &temp1)); } CLEAR(oper4); CLEAR(oper3); CLEAR(oper2); CLEAR(oper1); PushArrayRaw(nu); }
void prim_array_regmatchval(PRIM_PROTOTYPE) { struct inst *in; stk_array *arr; stk_array *nw; muf_re* re; char* text; int flags; int matchcnt = 0; const char* errstr = NULL; CHECKOP(3); oper3 = POP(); /* int pcreflags */ oper2 = POP(); /* str pattern */ oper1 = POP(); /* arr Array */ if (oper1->type != PROG_ARRAY) abort_interp("Argument not an array. (1)"); if (oper2->type != PROG_STRING) abort_interp("Argument not a string pattern. (2)"); if (oper3->type != PROG_INTEGER) abort_interp("Non-integer argument (3)"); flags = PCRE_NO_AUTO_CAPTURE; if (oper3->data.number & MUF_RE_ICASE) flags |= PCRE_CASELESS; if (oper3->data.number & MUF_RE_EXTENDED) flags |= PCRE_EXTENDED; re = regmatch_re_get(oper2->data.string, flags, &errstr); if (errstr) abort_interp(errstr) nw = new_array_dictionary(); arr = oper1->data.array; if (re && !re->extra && array_count(arr) > 2) { /* This pattern is getting used 3 or more times, let's study it. A null * return is okay, that just means there's nothing to optimize. */ re->extra = pcre_study(re->re, 0, &errstr); if (errstr) abort_interp(errstr); } if (array_first(arr, &temp1)) { do { in = array_getitem(arr, &temp1); if (in->type == PROG_STRING) { text = (char *)DoNullInd(in->data.string); if ((matchcnt = regmatch_exec(re, text)) < 0) { if (matchcnt != PCRE_ERROR_NOMATCH) abort_interp(muf_re_error(matchcnt)); } else { array_setitem(&nw, &temp1, in); } } else if (in->type == PROG_OBJECT) { text = (char *) NAME(in->data.objref); if ((matchcnt = regmatch_exec(re, text)) < 0) { if (matchcnt != PCRE_ERROR_NOMATCH) abort_interp(muf_re_error(matchcnt)); } else { array_setitem(&nw, &temp1, in); } } } while (array_next(arr, &temp1)); } CLEAR(oper3); CLEAR(oper2); CLEAR(oper1); PushArrayRaw(nw); }
void prim_array_regsub(PRIM_PROTOTYPE) { struct inst *in; stk_array *arr; stk_array *nw; int matches[MATCH_ARR_SIZE]; int flags = 0; char* write_ptr = buf; int write_left = BUFFER_LEN - 1; muf_re* re; char* text; char* textstart; const char* errstr; int matchcnt, len; CHECKOP(4); oper4 = POP(); /* int:Flags */ oper3 = POP(); /* str:Replace */ oper2 = POP(); /* str:Pattern */ oper1 = POP(); /* str:Text */ if (oper1->type != PROG_ARRAY) abort_interp("Argument not an array of strings. (1)"); if (!array_is_homogenous(oper1->data.array, PROG_STRING)) abort_interp("Argument not an array of strings. (1)"); if (oper2->type != PROG_STRING) abort_interp("Non-string argument (2)"); if (oper3->type != PROG_STRING) abort_interp("Non-string argument (3)"); if (oper4->type != PROG_INTEGER) abort_interp("Non-integer argument (4)"); if (!oper2->data.string) abort_interp("Empty string argument (2)"); if (oper4->data.number & MUF_RE_ICASE) flags |= PCRE_CASELESS; if (oper4->data.number & MUF_RE_EXTENDED) flags |= PCRE_EXTENDED; if ((re = muf_re_get(oper2->data.string, flags, &errstr)) == NULL) abort_interp(errstr); nw = new_array_dictionary(); arr = oper1->data.array; if (!re->extra && ((oper4->data.number & MUF_RE_ALL ) || array_count(arr) > 2)) { /* Study the pattern if the user requested recursive substitution, or * if the input array contains at least three items. */ re->extra = pcre_study(re->re, 0, &errstr); if (errstr) abort_interp(errstr); } if (array_first(arr, &temp1)) { do { write_ptr = buf; write_left = BUFFER_LEN - 1; in = array_getitem(arr, &temp1); textstart = text = (char *)DoNullInd(in->data.string); len = strlen(textstart); while((*text != '\0') && (write_left > 0)) { if ((matchcnt = pcre_exec(re->re, re->extra, textstart, len, text-textstart, 0, matches, MATCH_ARR_SIZE)) < 0) { if (matchcnt != PCRE_ERROR_NOMATCH) { abort_interp(muf_re_error(matchcnt)); } while((write_left > 0) && (*text != '\0')) { *write_ptr++ = *text++; write_left--; } break; } else { int allstart = matches[0]; int allend = matches[1]; int substart = -1; int subend = -1; char* read_ptr = (char *)DoNullInd(oper3->data.string); int count; for(count = allstart-(text-textstart); (write_left > 0) && (*text != '\0') && (count > 0); count--) { *write_ptr++ = *text++; write_left--; } while((write_left > 0) && (*read_ptr != '\0')) { if (*read_ptr == '\\') { if (!isdigit(*(++read_ptr))) { *write_ptr++ = *read_ptr++; write_left--; } else { int idx = (*read_ptr++) - '0'; if ((idx < 0) || (idx >= matchcnt)) { abort_interp("Invalid \\subexp in substitution string. (3)"); } substart = matches[idx*2]; subend = matches[idx*2+1]; if ((substart >= 0) && (subend >= 0) && (substart < len)) { char* ptr = &textstart[substart]; count = subend - substart; if (count > write_left) { abort_interp("Operation would result in overflow"); } for(; (write_left > 0) && (count > 0) && (*ptr != '\0'); count--) { *write_ptr++ = *ptr++; write_left--; } } } } else { *write_ptr++ = *read_ptr++; write_left--; } } for(count = allend - allstart; (*text != '\0') && (count > 0); count--) text++; if (allstart == allend && *text) { *write_ptr++ = *text++; write_left--; } } if ((oper4->data.number & MUF_RE_ALL) == 0) { while((write_left > 0) && (*text != '\0')) { *write_ptr++ = *text++; write_left--; } break; } } if (*text != '\0') abort_interp("Operation would result in overflow"); *write_ptr = '\0'; temp2.type = PROG_STRING; temp2.data.string = alloc_prog_string(buf); array_setitem(&nw, &temp1, &temp2); CLEAR(&temp2); } while (array_next(arr, &temp1)); } CLEAR(oper4); CLEAR(oper3); CLEAR(oper2); CLEAR(oper1); PushArrayRaw(nw); }
int array_delrange(stk_array **harr, array_iter *start, array_iter *end) { stk_array *arr; array_data *itm; int sidx, eidx, totsize; array_iter idx; array_iter didx; if (!harr || !*harr) { return -1; } arr = *harr; switch (arr->type) { case ARRAY_PACKED:{ if (start->type != PROG_INTEGER) { return -1; } if (end->type != PROG_INTEGER) { return -1; } if (arr->items == 0) { /* nothing to do here */ return 0; } sidx = start->data.number; eidx = end->data.number; if (sidx < 0) { sidx = 0; } else if (sidx >= arr->items) { return -1; } if (eidx >= arr->items) { eidx = arr->items - 1; } else if (eidx < 0) { return -1; } if (sidx > eidx) { return -1; } if (arr->links > 1 && !arr->pinned) { arr->links--; arr = *harr = array_decouple(arr); } start->data.number = sidx; end->data.number = eidx; copyinst(end, &idx); copyinst(start, &didx); idx.data.number += 1; while (idx.data.number < arr->items) { itm = array_getitem(arr, &idx); // plug memory leak -davin if (arr->data.packed[didx.data.number].type != PROG_CLEARED ) { CLEAR(&arr->data.packed[didx.data.number]); } copyinst(itm, &arr->data.packed[didx.data.number]); CLEAR(itm); idx.data.number++; didx.data.number++; } arr->items -= (eidx - sidx + 1); totsize = (arr->items) ? arr->items : 1; arr->data.packed = (array_data *) realloc(arr->data.packed, sizeof(array_data) * totsize); return arr->items; break; } case ARRAY_DICTIONARY:{ array_tree *s; array_tree *e; s = array_tree_find(arr->data.dict, start); if (!s) { s = array_tree_next_node(arr->data.dict, start); if (!s) { return arr->items; } } e = array_tree_find(arr->data.dict, end); if (!e) { e = array_tree_prev_node(arr->data.dict, end); if (!e) { return arr->items; } } if (array_tree_compare(&s->key, &e->key, 0, 0, 0) > 0) { return arr->items; } if (arr->links > 1 && !arr->pinned) { arr->links--; arr = *harr = array_decouple(arr); } copyinst(&s->key, &idx); while (s && array_tree_compare(&s->key, &e->key, 0, 0, 0) <= 0) { arr->data.dict = array_tree_delete(&s->key, arr->data.dict); arr->items--; s = array_tree_next_node(arr->data.dict, &idx); } CLEAR(&idx); return arr->items; break; } default: break; } return -1; }
int array_insertrange(stk_array **harr, array_iter *start, stk_array *inarr) { stk_array *arr; array_data *itm; array_iter idx; array_iter didx; if (!harr || !*harr) { return -1; } arr = *harr; if (!inarr || !inarr->items) { return arr->items; } switch (arr->type) { case ARRAY_PACKED:{ if (!start) { return -1; } if (start->type != PROG_INTEGER) { return -1; } if (start->data.number < 0 || start->data.number > arr->items) { return -1; } if (arr->links > 1 && !arr->pinned) { arr->links--; arr = *harr = array_decouple(arr); } arr->data.packed = (array_data *) realloc(arr->data.packed, sizeof(array_data) * (arr->items + inarr->items)); copyinst(start, &idx); copyinst(start, &didx); idx.data.number = arr->items - 1; didx.data.number = arr->items + inarr->items - 1; while (idx.data.number >= start->data.number) { itm = array_getitem(arr, &idx); copyinst(itm, &arr->data.packed[didx.data.number]); CLEAR(itm); idx.data.number--; didx.data.number--; } if (array_first(inarr, &idx)) { do { itm = array_getitem(inarr, &idx); copyinst(itm, &arr->data.packed[start->data.number]); start->data.number++; } while (array_next(inarr, &idx)); } arr->items += inarr->items; return arr->items; break; } case ARRAY_DICTIONARY:{ if (arr->links > 1 && !arr->pinned) { arr->links--; arr = *harr = array_decouple(arr); } if (array_first(inarr, &idx)) { do { array_setitem(&arr, &idx, array_getitem(inarr, &idx)); } while (array_next(inarr, &idx)); } return arr->items; break; } default: break; } return -1; }
stk_array * array_getrange(stk_array *arr, array_iter *start, array_iter *end) { stk_array *new2; array_data *tmp; int sidx, eidx; if (!arr) { return NULL; } switch (arr->type) { case ARRAY_PACKED:{ array_iter idx; array_iter didx; if (start->type != PROG_INTEGER) { return NULL; } if (end->type != PROG_INTEGER) { return NULL; } sidx = start->data.number; eidx = end->data.number; if (sidx < 0) { sidx = 0; } else if (sidx > arr->items) { return NULL; } if (eidx >= arr->items) { eidx = arr->items - 1; } else if (eidx < 0) { return NULL; } if (sidx > eidx) { return NULL; } idx.type = PROG_INTEGER; idx.data.number = sidx; didx.type = PROG_INTEGER; didx.data.number = 0; new2 = new_array_packed(eidx - sidx + 1); while (idx.data.number <= eidx) { tmp = array_getitem(arr, &idx); if (!tmp) break; array_setitem(&new2, &didx, tmp); didx.data.number++; idx.data.number++; } return new2; break; } case ARRAY_DICTIONARY:{ stk_array *new2; array_tree *s; array_tree *e; new2 = new_array_dictionary(); s = array_tree_find(arr->data.dict, start); if (!s) { s = array_tree_next_node(arr->data.dict, start); if (!s) { return new2; } } e = array_tree_find(arr->data.dict, end); if (!e) { e = array_tree_prev_node(arr->data.dict, end); if (!e) { return new2; } } if (array_tree_compare(&s->key, &e->key, 0, 0, 0) > 0) { return new2; } while (s) { array_setitem(&new2, &s->key, &s->data); if (s == e) break; s = array_tree_next_node(arr->data.dict, &s->key); } return new2; break; } default: break; } return NULL; }
void prim_array_filter_prop(PRIM_PROTOTYPE) { char pattern[BUFFER_LEN]; char tname[BUFFER_LEN]; struct inst *in; struct inst temp1; stk_array *arr; stk_array *nu; char* prop; int len; CHECKOP(3); oper3 = POP(); /* str pattern */ oper2 = POP(); /* str propname */ oper1 = POP(); /* refarr Array */ if (oper1->type != PROG_ARRAY) abort_interp("Argument not an array. (1)"); if (!array_is_homogenous(oper1->data.array, PROG_OBJECT)) abort_interp("Argument not an array of dbrefs. (1)"); if (oper2->type != PROG_STRING || !oper2->data.string) abort_interp("Argument not a non-null string. (2)"); if (oper3->type != PROG_STRING) abort_interp("Argument not a string pattern. (3)"); len = oper2->data.string ? oper2->data.string->length : 0; strcpyn(tname, sizeof(tname), DoNullInd(oper2->data.string)); while (len-- > 0 && tname[len] == PROPDIR_DELIMITER) { tname[len] = '\0'; } nu = new_array_packed(0); arr = oper1->data.array; prop = tname; strcpyn(pattern, sizeof(pattern), DoNullInd(oper3->data.string)); if (array_first(arr, &temp1)) { do { in = array_getitem(arr, &temp1); if (valid_object(in)) { ref = in->data.objref; CHECKREMOTE(ref); if (prop_read_perms(ProgUID, ref, prop, mlev)) { PropPtr pptr = get_property(ref, prop); if (pptr) { switch(PropType(pptr)) { case PROP_STRTYP: strncpy(buf, PropDataStr(pptr), BUFFER_LEN); break; case PROP_LOKTYP: if (PropFlags(pptr) & PROP_ISUNLOADED) { strncpy(buf, "*UNLOCKED*", BUFFER_LEN); } else { strncpy(buf, unparse_boolexp(ProgUID, PropDataLok(pptr), 0), BUFFER_LEN); } break; case PROP_REFTYP: snprintf(buf, BUFFER_LEN, "#%i", PropDataRef(pptr)); break; case PROP_INTTYP: snprintf(buf, BUFFER_LEN, "%i", PropDataVal(pptr)); break; case PROP_FLTTYP: snprintf(buf, BUFFER_LEN, "%g", PropDataFVal(pptr)); break; default: strncpy(buf, "", BUFFER_LEN); break; } } else strncpy(buf, "", BUFFER_LEN); if (equalstr(pattern, buf)) { array_appenditem(&nu, in); } } } } while (array_next(arr, &temp1)); } CLEAR(oper3); CLEAR(oper2); CLEAR(oper1); PushArrayRaw(nu); }
void prim_parsepropex(PRIM_PROTOTYPE) { struct inst* oper1 = NULL; /* prevents reentrancy issues! */ struct inst* oper2 = NULL; /* prevents reentrancy issues! */ struct inst* oper3 = NULL; /* prevents reentrancy issues! */ struct inst* oper4 = NULL; /* prevents reentrancy issues! */ stk_array* vars; const char* mpi; char* str = 0; array_iter idx; extern int varc; /* from msgparse.c */ int mvarcnt = 0; char* buffers = NULL; int novars; int hashow = 0; int i; int len; char tname[BUFFER_LEN]; char buf[BUFFER_LEN]; CHECKOP(4); oper4 = POP(); /* int:Private */ oper3 = POP(); /* dict:Vars */ oper2 = POP(); /* str:Prop */ oper1 = POP(); /* ref:Object */ if (mlev < 3) abort_interp("Mucker level 3 or greater required."); if (oper1->type != PROG_OBJECT) abort_interp("Non-object argument. (1)"); if (oper2->type != PROG_STRING) abort_interp("Non-string argument. (2)"); if (oper3->type != PROG_ARRAY) abort_interp("Non-array argument. (3)"); if (oper3->data.array && (oper3->data.array->type != ARRAY_DICTIONARY)) abort_interp("Dictionary array expected. (3)"); if (oper4->type != PROG_INTEGER) abort_interp("Non-integer argument. (4)"); if (!valid_object(oper1)) abort_interp("Invalid object. (1)"); if (!oper2->data.string) abort_interp("Empty string argument. (2)"); if ((oper4->data.number != 0) && (oper4->data.number != 1)) abort_interp("Integer of 0 or 1 expected. (4)"); CHECKREMOTE(oper1->data.objref); if (!prop_read_perms(ProgUID, oper1->data.objref, oper2->data.string->data, mlev)) abort_interp("Permission denied."); len = oper2->data.string->length; strcpyn(tname, sizeof(tname), oper2->data.string->data); while (len-- > 0 && tname[len] == PROPDIR_DELIMITER) { tname[len] = '\0'; } mpi = get_property_class(oper1->data.objref, tname); vars = oper3->data.array; novars = array_count(vars); if (check_mvar_overflow(novars)) abort_interp("Out of MPI variables. (3)"); if (array_first(vars, &idx)) { do { array_data* val = array_getitem(vars, &idx); if (idx.type != PROG_STRING) { CLEAR(&idx); abort_interp("Only string keys supported. (3)"); } if (idx.data.string == NULL) { CLEAR(&idx); abort_interp("Empty string keys not supported. (3)"); } if (strlen(idx.data.string->data) > MAX_MFUN_NAME_LEN) { CLEAR(&idx); abort_interp("Key too long to be an MPI variable. (3)"); } switch(val->type) { case PROG_INTEGER: case PROG_FLOAT: case PROG_OBJECT: case PROG_STRING: case PROG_LOCK: break; default: CLEAR(&idx); abort_interp("Only integer, float, dbref, string and lock values supported. (3)"); break; } if (string_compare(idx.data.string->data, "how") == 0) hashow = 1; } while(array_next(vars, &idx)); } if (mpi && *mpi) { if (novars > 0) { mvarcnt = varc; if ((buffers = (char*)malloc(novars * BUFFER_LEN)) == NULL) abort_interp("Out of memory."); if (array_first(vars, &idx)) { i = 0; do { char* var_buf = buffers + (i++ * BUFFER_LEN); array_data* val; val = array_getitem(vars, &idx); switch(val->type) { case PROG_INTEGER: snprintf(var_buf, BUFFER_LEN, "%i", val->data.number); break; case PROG_FLOAT: snprintf(var_buf, BUFFER_LEN, "%g", val->data.fnumber); break; case PROG_OBJECT: snprintf(var_buf, BUFFER_LEN, "#%i", val->data.objref); break; case PROG_STRING: strncpy(var_buf, DoNullInd(val->data.string), BUFFER_LEN); break; case PROG_LOCK: strncpy(var_buf, unparse_boolexp(ProgUID, val->data.lock, 1), BUFFER_LEN); break; default: var_buf[0] = '\0'; break; } var_buf[BUFFER_LEN - 1] = '\0'; new_mvar(idx.data.string->data, var_buf); } while(array_next(vars, &idx)); } } result = 0; if (oper4->data.number) result |= MPI_ISPRIVATE; if (Prop_Blessed(oper1->data.objref, oper2->data.string->data)) result |= MPI_ISBLESSED; if (hashow) result |= MPI_NOHOW; str = do_parse_mesg(fr->descr, player, oper1->data.objref, mpi, "(parsepropex)", buf, sizeof(buf), result); if (novars > 0) { if (array_first(vars, &idx)) { i = 0; do { char* var_buf = buffers + (i++ * BUFFER_LEN); struct inst temp; temp.type = PROG_STRING; temp.data.string = alloc_prog_string(var_buf); array_setitem(&vars, &idx, &temp); CLEAR(&temp); } while(array_next(vars, &idx)); } free(buffers); varc = mvarcnt; } } oper3->data.array = NULL; CLEAR(oper1); CLEAR(oper2); CLEAR(oper3); CLEAR(oper4); PushArrayRaw(vars); PushString(str); }