/* Primitives Package */
#include "copyright.h"
#include "config.h"
#include <sys/types.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include "db.h"
#include "tune.h"
#include "inst.h"
#include "externs.h"
#include "match.h"
#include "interface.h"
#include "params.h"
#include "fbstrings.h"
#include "interp.h"
#include "props.h"
#include "dbsearch.h"
static struct inst *oper1, *oper2, *oper3, *oper4;
static struct inst temp1, temp2;
static int result;
static dbref ref;
static char buf[BUFFER_LEN];
void
prim_array_make(PRIM_PROTOTYPE)
{
stk_array *nu;
int i;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_INTEGER)
abort_interp("Invalid item count.");
result = oper1->data.number;
if (result < 0)
abort_interp("Item count must be non-negative.");
CLEAR(oper1);
nargs = 0;
if (*top < result)
abort_interp("Stack underflow.");
temp1.type = PROG_INTEGER;
temp1.line = 0;
nu = new_array_packed(result);
for (i = result; i-- > 0;) {
temp1.data.number = i;
oper1 = POP();
array_setitem(&nu, &temp1, oper1);
CLEAR(oper1);
}
PushArrayRaw(nu);
}
void
prim_array_make_dict(PRIM_PROTOTYPE)
{
stk_array *nu;
int i;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_INTEGER)
abort_interp("Invalid item count.");
result = oper1->data.number;
if (result < 0)
abort_interp("Item count must be positive.");
CLEAR(oper1);
nargs = 0;
if (*top < (2 * result))
abort_interp("Stack underflow.");
nargs = 2;
nu = new_array_dictionary();
for (i = result; i-- > 0;) {
oper1 = POP(); /* val */
oper2 = POP(); /* key */
if (oper2->type != PROG_INTEGER && oper2->type != PROG_STRING) {
array_free(nu);
abort_interp("Keys must be integers or strings.");
}
array_setitem(&nu, oper2, oper1);
CLEAR(oper1);
CLEAR(oper2);
}
PushArrayRaw(nu);
}
void
prim_array_explode(PRIM_PROTOTYPE)
{
stk_array *arr;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
result = array_count(oper1->data.array);
CHECKOFLOW(((2 * result) + 1));
copyinst(oper1, &temp2);
arr = temp2.data.array;
CLEAR(oper1);
if (array_first(arr, &temp1)) {
do {
copyinst(&temp1, &arg[((*top)++)]);
oper2 = array_getitem(arr, &temp1);
copyinst(oper2, &arg[((*top)++)]);
oper2 = NULL;
} while (array_next(arr, &temp1));
}
CLEAR(&temp2);
PushInt(result);
}
void
prim_array_vals(PRIM_PROTOTYPE)
{
stk_array *arr;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
copyinst(oper1, &temp2);
arr = temp2.data.array;
result = array_count(arr);
CHECKOFLOW((result + 1));
CLEAR(oper1);
if (array_first(arr, &temp1)) {
do {
oper2 = array_getitem(arr, &temp1);
copyinst(oper2, &arg[((*top)++)]);
oper2 = NULL;
} while (array_next(arr, &temp1));
}
CLEAR(&temp2);
PushInt(result);
}
void
prim_array_keys(PRIM_PROTOTYPE)
{
stk_array *arr;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
copyinst(oper1, &temp2);
arr = temp2.data.array;
result = array_count(arr);
CHECKOFLOW((result + 1));
CLEAR(oper1);
if (array_first(arr, &temp1)) {
do {
copyinst(&temp1, &arg[((*top)++)]);
} while (array_next(arr, &temp1));
}
CLEAR(&temp2);
PushInt(result);
}
void
prim_array_count(PRIM_PROTOTYPE)
{
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
result = array_count(oper1->data.array);
CLEAR(oper1);
PushInt(result);
}
void
prim_array_first(PRIM_PROTOTYPE)
{
CHECKOP(1);
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
temp1.type = PROG_INTEGER;
temp1.data.number = 0;
result = array_first(oper1->data.array, &temp1);
CLEAR(oper1);
if (result) {
copyinst(&temp1, &arg[((*top)++)]);
} else {
result = 0;
PushInt(result);
}
PushInt(result);
}
void
prim_array_last(PRIM_PROTOTYPE)
{
CHECKOP(1);
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
temp1.type = PROG_INTEGER;
temp1.data.number = 0;
result = array_last(oper1->data.array, &temp1);
CLEAR(oper1);
if (result) {
copyinst(&temp1, &arg[((*top)++)]);
} else {
result = 0;
PushInt(result);
}
PushInt(result);
}
void
prim_array_prev(PRIM_PROTOTYPE)
{
CHECKOP(2);
oper1 = POP(); /* ??? previous index */
oper2 = POP(); /* arr Array */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array.(1)");
copyinst(oper1, &temp1);
result = array_prev(oper2->data.array, &temp1);
CLEAR(oper1);
CLEAR(oper2);
if (result) {
copyinst(&temp1, &arg[((*top)++)]);
CLEAR(&temp1);
} else {
result = 0;
PushInt(result);
}
PushInt(result);
}
void
prim_array_next(PRIM_PROTOTYPE)
{
CHECKOP(2);
oper1 = POP(); /* ??? previous index */
oper2 = POP(); /* arr Array */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array.(1)");
copyinst(oper1, &temp1);
result = array_next(oper2->data.array, &temp1);
CLEAR(oper1);
CLEAR(oper2);
if (result) {
copyinst(&temp1, &arg[((*top)++)]);
CLEAR(&temp1);
} else {
result = 0;
PushInt(result);
}
PushInt(result);
}
void
prim_array_getitem(PRIM_PROTOTYPE)
{
struct inst *in;
struct inst temp;
CHECKOP(2);
oper1 = POP(); /* ??? index */
oper2 = POP(); /* arr Array */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
in = array_getitem(oper2->data.array, oper1);
/* copy data to a temp inst before clearing the containing array */
if (in) {
copyinst(in, &temp);
} else {
temp.type = PROG_INTEGER;
temp.data.number = 0;
}
CLEAR(oper1);
CLEAR(oper2);
/* copy data to stack, then clear temp inst */
copyinst(&temp, &arg[(*top)++]);
CLEAR(&temp);
}
void
prim_array_setitem(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(3);
oper1 = POP(); /* ??? index to store at */
oper2 = POP(); /* arr Array to store in */
oper3 = POP(); /* ??? item to store */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (3)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (2)");
result = array_setitem(&oper2->data.array, oper1, oper3);
if (result < 0)
abort_interp("Index out of array bounds. (3)");
nu = oper2->data.array;
oper2->data.array = NULL;
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
PushArrayRaw(nu);
}
void
prim_array_appenditem(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(2);
oper2 = POP(); /* arr Array to store in */
oper1 = POP(); /* ??? item to store */
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (2)");
if (oper2->data.array && oper2->data.array->type != ARRAY_PACKED)
abort_interp("Argument must be a list type array. (2)");
result = array_appenditem(&oper2->data.array, oper1);
if (result == -1)
abort_interp("Internal Error!");
nu = oper2->data.array;
oper2->data.array = NULL;
CLEAR(oper1);
CLEAR(oper2);
PushArrayRaw(nu);
}
void
prim_array_insertitem(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(3);
oper1 = POP(); /* ??? index to store at */
oper2 = POP(); /* arr Array to store in */
oper3 = POP(); /* ??? item to store */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (3)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (2)");
result = array_insertitem(&oper2->data.array, oper1, oper3);
if (result < 0)
abort_interp("Index out of array bounds. (3)");
nu = oper2->data.array;
oper2->data.array = NULL;
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
PushArrayRaw(nu);
}
void
prim_array_getrange(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(3);
oper1 = POP(); /* int range end item */
oper2 = POP(); /* int range start item */
oper3 = POP(); /* arr Array */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (3)");
if (oper2->type != PROG_INTEGER && oper2->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper3->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
nu = array_getrange(oper3->data.array, oper2, oper1);
if (!nu)
nu = new_array_packed(0);
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
PushArrayRaw(nu);
}
void
prim_array_setrange(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(3);
oper1 = POP(); /* arr array to insert */
oper2 = POP(); /* ??? starting pos for lists */
oper3 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (3)");
if (oper2->type != PROG_INTEGER && oper2->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper3->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
result = array_setrange(&oper3->data.array, oper2, oper1->data.array);
if (result < 0)
abort_interp("Index out of array bounds. (2)");
nu = oper3->data.array;
oper3->data.array = NULL;;
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
PushArrayRaw(nu);
}
void
prim_array_insertrange(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(3);
oper1 = POP(); /* arr array to insert */
oper2 = POP(); /* ??? starting pos for lists */
oper3 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (3)");
if (oper2->type != PROG_INTEGER && oper2->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper3->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
result = array_insertrange(&oper3->data.array, oper2, oper1->data.array);
if (result < 0)
abort_interp("Index out of array bounds. (2)");
nu = oper3->data.array;
oper3->data.array = NULL;
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
PushArrayRaw(nu);
}
void
prim_array_delitem(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(2);
oper1 = POP(); /* int item to delete */
oper2 = POP(); /* arr Array */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
result = array_delitem(&oper2->data.array, oper1);
if (result < 0)
abort_interp("Bad array index specified.");
nu = oper2->data.array;
oper2->data.array = NULL;
CLEAR(oper1);
CLEAR(oper2);
PushArrayRaw(nu);
}
void
prim_array_delrange(PRIM_PROTOTYPE)
{
stk_array *nu;
CHECKOP(3);
oper1 = POP(); /* int range end item */
oper2 = POP(); /* int range start item */
oper3 = POP(); /* arr Array */
if (oper1->type != PROG_INTEGER && oper1->type != PROG_STRING)
abort_interp("Argument not an integer or string. (3)");
if (oper2->type != PROG_INTEGER && oper2->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper3->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
result = array_delrange(&oper3->data.array, oper2, oper1);
if (result < 0)
abort_interp("Bad array range specified.");
nu = oper3->data.array;
oper3->data.array = NULL;
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
PushArrayRaw(nu);
}
void
prim_array_cut(PRIM_PROTOTYPE)
{
stk_array *nu1 = NULL;
stk_array *nu2 = NULL;
struct inst temps;
struct inst tempc;
struct inst tempe;
CHECKOP(2);
oper2 = POP(); /* int position */
oper1 = POP(); /* arr Array */
if (oper2->type != PROG_INTEGER && oper2->type != PROG_STRING)
abort_interp("Argument not an integer or string. (2)");
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
temps.type = PROG_INTEGER;
temps.data.number = 0;
result = array_first(oper1->data.array, &temps);
if (result) {
copyinst(oper2, &tempc);
result = array_prev(oper1->data.array, &tempc);
if (result) {
nu1 = array_getrange(oper1->data.array, &temps, &tempc);
CLEAR(&tempc);
}
result = array_last(oper1->data.array, &tempe);
if (result) {
nu2 = array_getrange(oper1->data.array, oper2, &tempe);
CLEAR(&tempe);
}
CLEAR(&temps);
}
if (!nu1)
nu1 = new_array_packed(0);
if (!nu2)
nu2 = new_array_packed(0);
CLEAR(oper1);
CLEAR(oper2);
PushArrayRaw(nu1);
PushArrayRaw(nu2);
}
/*\
|*| Eeep!
\*/
void
prim_array_n_union(PRIM_PROTOTYPE)
{
stk_array *new_union;
stk_array *new_mash;
int num_arrays;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_INTEGER)
abort_interp("Invalid item count.");
result = oper1->data.number;
if (result < 0)
abort_interp("Item count must be positive.");
CLEAR(oper1);
nargs = 0;
if (*top < result)
abort_interp("Stack underflow.");
if (result > 0) {
new_mash = new_array_dictionary();
for (num_arrays = 0; num_arrays < result; num_arrays++) {
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_ARRAY) {
array_free(new_mash);
abort_interp("Argument not an array.");
}
array_mash(oper1->data.array, &new_mash, 1);
CLEAR(oper1);
}
new_union = array_demote_only(new_mash, 1);
array_free(new_mash);
} else {
new_union = new_array_packed(0);
}
PushArrayRaw(new_union);
}
void
prim_array_n_intersection(PRIM_PROTOTYPE)
{
stk_array *new_union;
stk_array *new_mash;
stk_array *temp_mash;
int num_arrays;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_INTEGER)
abort_interp("Invalid item count.");
result = oper1->data.number;
if (result < 0)
abort_interp("Item count must be positive.");
CLEAR(oper1);
nargs = 0;
if (*top < result)
abort_interp("Stack underflow.");
if (result > 0) {
new_mash = new_array_dictionary();
for (num_arrays = 0; num_arrays < result; num_arrays++) {
oper1 = POP();
if (oper1->type != PROG_ARRAY) {
array_free(new_mash);
abort_interp("Argument not an array.");
}
temp_mash = new_array_dictionary();
array_mash(oper1->data.array, &temp_mash, 1);
CLEAR(oper1);
new_union = array_demote_only(temp_mash, 1);
array_free(temp_mash);
PushArrayRaw(new_union);
oper1 = POP();
array_mash(oper1->data.array, &new_mash, 1);
CLEAR(oper1);
}
new_union = array_demote_only(new_mash, result);
array_free(new_mash);
} else {
new_union = new_array_packed(0);
}
PushArrayRaw(new_union);
}
void
prim_array_n_difference(PRIM_PROTOTYPE)
{
stk_array *new_union;
stk_array *new_mash;
int num_arrays;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_INTEGER)
abort_interp("Invalid item count.");
result = oper1->data.number;
if (result < 0)
abort_interp("Item count must be positive.");
CLEAR(oper1);
nargs = 0;
if (*top < result)
abort_interp("Stack underflow.");
if (result > 0) {
new_mash = new_array_dictionary();
oper1 = POP();
if (oper1->type != PROG_ARRAY) {
array_free(new_mash);
abort_interp("Argument not an array.");
}
array_mash(oper1->data.array, &new_mash, 1);
CLEAR(oper1);
for (num_arrays = 1; num_arrays < result; num_arrays++) {
oper1 = POP();
if (oper1->type != PROG_ARRAY) {
array_free(new_mash);
abort_interp("Argument not an array.");
}
array_mash(oper1->data.array, &new_mash, -1);
CLEAR(oper1);
}
new_union = array_demote_only(new_mash, 1);
array_free(new_mash);
} else {
new_union = new_array_packed(0);
}
PushArrayRaw(new_union);
}
void
prim_array_notify(PRIM_PROTOTYPE)
{
stk_array *strarr;
stk_array *refarr;
struct inst *oper1=NULL, *oper2=NULL, *oper3=NULL, *oper4=NULL;
struct inst temp1, temp2;
CHECKOP(2);
oper2 = POP();
oper1 = POP();
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_ARRAY)
abort_interp("Argument not an array of dbrefs. (2)");
if (!array_is_homogenous(oper2->data.array, PROG_OBJECT))
abort_interp("Argument not an array of dbrefs. (2)");
strarr = oper1->data.array;
refarr = oper2->data.array;
if (array_first(strarr, &temp2)) {
do {
oper4 = array_getitem(strarr, &temp2);
if (tp_force_mlev1_name_notify && mlev < 2) {
prefix_message(buf, DoNullInd(oper4->data.string), NAME(player), BUFFER_LEN, 1);
}
else
{
/* TODO: Is there really a reason to make a copy? */
strcpy(buf, DoNullInd(oper4->data.string));
}
if (array_first(refarr, &temp1)) {
do {
oper3 = array_getitem(refarr, &temp1);
notify_listeners(player, program, oper3->data.objref,
getloc(oper3->data.objref), buf, 1);
oper3 = NULL;
} while (array_next(refarr, &temp1));
}
oper4 = NULL;
} while (array_next(strarr, &temp2));
}
CLEAR(oper1);
CLEAR(oper2);
}
void
prim_array_reverse(PRIM_PROTOTYPE)
{
stk_array *arr;
stk_array *nu;
int i;
CHECKOP(1);
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
arr = oper1->data.array;
if (arr->type != ARRAY_PACKED)
abort_interp("Argument must be a list type array.");
result = array_count(arr);
nu = new_array_packed(result);
temp1.type = PROG_INTEGER;
temp2.type = PROG_INTEGER;
for (i = 0; i < result; i++) {
temp1.data.number = i;
temp2.data.number = (result - i) - 1;
array_setitem(&nu, &temp1, array_getitem(arr, &temp2));
}
CLEAR(oper1);
PushArrayRaw(nu);
}
static int sortflag_caseinsens = 0;
static int sortflag_descending = 0;
static struct inst* sortflag_index = NULL;
int
sortcomp_generic(const void* x, const void* y)
{
struct inst* a;
struct inst* b;
if (!sortflag_descending) {
a = *(struct inst**)x;
b = *(struct inst**)y;
} else {
a = *(struct inst**)y;
b = *(struct inst**)x;
}
if (sortflag_index) {
/* This should only be set if comparators are all arrays. */
a = array_getitem(a->data.array, sortflag_index);
b = array_getitem(b->data.array, sortflag_index);
if (!a && !b) {
return 0;
} else if (!a) {
return -1;
} else if (!b) {
return 1;
}
}
return (array_idxcmp_case(a, b, (sortflag_caseinsens? 0 : 1)));
}
int
sortcomp_shuffle(const void* x, const void* y)
{
return (((RANDOM() >> 8) % 5) - 2);
}
/* Sort types:
* 1: case, ascending
* 2: nocase, ascending
* 3: case, descending
* 4: nocase, descending
* 5: randomize
*/
void
prim_array_sort(PRIM_PROTOTYPE)
{
stk_array *arr;
stk_array *nu;
int count, i;
int (*comparator)(const void*, const void*);
struct inst** tmparr = NULL;
CHECKOP(2);
oper2 = POP(); /* int sort_type */
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
arr = oper1->data.array;
if (arr->type != ARRAY_PACKED)
abort_interp("Argument must be a list type array. (1)");
if (oper2->type != PROG_INTEGER)
abort_interp("Expected integer argument to specify sort type. (2)");
temp1.type = PROG_INTEGER;
count = array_count(arr);
nu = new_array_packed(count);
tmparr = (struct inst**)malloc(count * sizeof(struct inst*));
for (i = 0; i < count; i++) {
temp1.data.number = i;
tmparr[i] = array_getitem(arr, &temp1);
}
/* WORK: if we go multithreaded, we'll need to lock a mutex here. */
/* Share this mutex with ARRAY_SORT_INDEXED. */
sortflag_caseinsens = (oper2->data.number & SORTTYPE_CASEINSENS) ? 1 : 0;
sortflag_descending = (oper2->data.number & SORTTYPE_DESCENDING) ? 1 : 0;
sortflag_index = NULL;
if ((oper2->data.number & SORTTYPE_SHUFFLE)) {
comparator = sortcomp_shuffle;
} else {
comparator = sortcomp_generic;
}
qsort(tmparr, count, sizeof(struct inst*), comparator);
/* WORK: if we go multithreaded, the mutex should be released here. */
/* Share this mutex with ARRAY_SORT_INDEXED. */
for (i = 0; i < count; i++) {
temp1.data.number = i;
array_setitem(&nu, &temp1, tmparr[i]);
}
free(tmparr);
CLEAR(oper1);
CLEAR(oper2);
PushArrayRaw(nu);
}
/* Sort types:
* 1: case, ascending
* 2: nocase, ascending
* 3: case, descending
* 4: nocase, descending
* 5: randomize
*/
void
prim_array_sort_indexed(PRIM_PROTOTYPE)
{
stk_array *arr;
stk_array *nu;
int count, i;
int (*comparator)(const void*, const void*);
struct inst** tmparr = NULL;
CHECKOP(3);
oper3 = POP(); /* idx index_key */
oper2 = POP(); /* int sort_type */
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
arr = oper1->data.array;
if (arr->type != ARRAY_PACKED)
abort_interp("Argument must be a list type array. (1)");
if (!array_is_homogenous(arr, PROG_ARRAY))
abort_interp("Argument must be a list array of arrays. (1)");
if (oper2->type != PROG_INTEGER)
abort_interp("Expected integer argument to specify sort type. (2)");
if (oper3->type != PROG_INTEGER && oper3->type != PROG_STRING)
abort_interp("Index argument not an integer or string. (3)");
temp1.type = PROG_INTEGER;
count = array_count(arr);
nu = new_array_packed(count);
tmparr = (struct inst**)malloc(count * sizeof(struct inst*));
for (i = 0; i < count; i++) {
temp1.data.number = i;
tmparr[i] = array_getitem(arr, &temp1);
}
/* WORK: if we go multithreaded, we'll need to lock a mutex here. */
/* Share this mutex with ARRAY_SORT. */
sortflag_caseinsens = (oper2->data.number & SORTTYPE_CASEINSENS) ? 1 : 0;
sortflag_descending = (oper2->data.number & SORTTYPE_DESCENDING) ? 1 : 0;
sortflag_index = oper3;
if ((oper2->data.number & SORTTYPE_SHUFFLE)) {
comparator = sortcomp_shuffle;
} else {
comparator = sortcomp_generic;
}
qsort(tmparr, count, sizeof(struct inst*), comparator);
/* WORK: if we go multithreaded, the mutex should be released here. */
/* Share this mutex with ARRAY_SORT. */
for (i = 0; i < count; i++) {
temp1.data.number = i;
array_setitem(&nu, &temp1, tmparr[i]);
}
free(tmparr);
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
PushArrayRaw(nu);
}
void
prim_array_get_propdirs(PRIM_PROTOTYPE)
{
stk_array *nu;
char propname[BUFFER_LEN];
char dir[BUFFER_LEN];
PropPtr propadr, pptr;
PropPtr prptr;
int count = 0;
int len;
/* dbref strPropDir -- array */
CHECKOP(2);
oper2 = POP();
oper1 = POP();
if (mlev < 3)
abort_interp("Permission denied.");
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required. (1)");
if (!valid_object(oper1))
abort_interp("Invalid dbref. (1)");
if (oper2->type != PROG_STRING)
abort_interp("String required. (2)");
ref = oper1->data.objref;
strcpy(dir, DoNullInd(oper2->data.string));
if (!*dir)
strcpy(dir, "/");
len = strlen(dir) - 1;
if (len > 0 && dir[len] == PROPDIR_DELIMITER)
dir[len] = '\0';
nu = new_array_packed(0);
propadr = first_prop(ref, dir, &pptr, propname);
while (propadr) {
snprintf(buf, sizeof(buf), "%s%c%s", dir, PROPDIR_DELIMITER, propname);
if (prop_read_perms(ProgUID, ref, buf, mlev)) {
prptr = get_property(ref, buf);
if (prptr) {
#ifdef DISKBASE
propfetch(ref, prptr);
#endif
if (PropDir(prptr)) {
if (count >= 511) {
array_free(nu);
abort_interp("Too many propdirs to put in an array!");
}
array_set_intkey_strval(&nu, count++, propname);
}
}
}
propadr = next_prop(pptr, propadr, propname);
}
CLEAR(oper1);
CLEAR(oper2);
PushArrayRaw(nu);
}
void
prim_array_get_propvals(PRIM_PROTOTYPE)
{
stk_array *nu;
char propname[BUFFER_LEN];
char dir[BUFFER_LEN];
PropPtr propadr, pptr;
PropPtr prptr;
int count = 0;
/* dbref strPropDir -- array */
CHECKOP(2);
oper2 = POP();
oper1 = POP();
if (mlev < 3)
abort_interp("Permission denied.");
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required. (1)");
if (!valid_object(oper1))
abort_interp("Invalid dbref. (1)");
if (oper2->type != PROG_STRING)
abort_interp("String required. (2)");
ref = oper1->data.objref;
strcpy(dir, DoNullInd(oper2->data.string));
if (!*dir)
strcpy(dir, "/");
nu = new_array_dictionary();
propadr = first_prop(ref, dir, &pptr, propname);
while (propadr) {
snprintf(buf, sizeof(buf), "%s%c%s", dir, PROPDIR_DELIMITER, propname);
if (prop_read_perms(ProgUID, ref, buf, mlev)) {
prptr = get_property(ref, buf);
if (prptr) {
int goodflag = 1;
#ifdef DISKBASE
propfetch(ref, prptr);
#endif
switch (PropType(prptr)) {
case PROP_STRTYP:
temp2.type = PROG_STRING;
temp2.data.string = alloc_prog_string(uncompress(PropDataStr(prptr)));
break;
case PROP_LOKTYP:
temp2.type = PROG_LOCK;
if (PropFlags(prptr) & PROP_ISUNLOADED) {
temp2.data.lock = TRUE_BOOLEXP;
} else {
temp2.data.lock = PropDataLok(prptr);
if (temp2.data.lock != TRUE_BOOLEXP) {
temp2.data.lock = copy_bool(temp2.data.lock);
}
}
break;
case PROP_REFTYP:
temp2.type = PROG_OBJECT;
temp2.data.number = PropDataRef(prptr);
break;
case PROP_INTTYP:
temp2.type = PROG_INTEGER;
temp2.data.number = PropDataVal(prptr);
break;
case PROP_FLTTYP:
temp2.type = PROG_FLOAT;
temp2.data.fnumber = PropDataFVal(prptr);
break;
default:
goodflag = 0;
break;
}
if (goodflag) {
if (count++ >= 511) {
array_free(nu);
abort_interp("Too many properties to put in an array!");
}
temp1.type = PROG_STRING;
temp1.data.string = alloc_prog_string(propname);
array_setitem(&nu, &temp1, &temp2);
CLEAR(&temp1);
CLEAR(&temp2);
}
}
}
propadr = next_prop(pptr, propadr, propname);
}
PushArrayRaw(nu);
}
void
prim_array_get_proplist(PRIM_PROTOTYPE)
{
stk_array *nu;
const char *strval;
char dir[BUFFER_LEN];
char propname[BUFFER_LEN];
PropPtr prptr;
int count = 1;
int maxcount;
/* dbref strPropDir -- array */
CHECKOP(2);
oper2 = POP();
oper1 = POP();
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required. (1)");
if (!valid_object(oper1))
abort_interp("Invalid dbref. (1)");
if (oper2->type != PROG_STRING)
abort_interp("String required. (2)");
ref = oper1->data.objref;
strcpy(dir, DoNullInd(oper2->data.string));
if (!*dir)
strcpy(dir, "/");
snprintf(propname, sizeof(propname), "%s#", dir);
maxcount = get_property_value(ref, propname);
if (!maxcount) {
strval = get_property_class(ref, propname);
if (strval) {
strval = uncompress(strval);
if (strval && number(strval)) {
maxcount = atoi(strval);
}
}
if (!maxcount) {
snprintf(propname, sizeof(propname), "%s%c#", dir, PROPDIR_DELIMITER);
maxcount = get_property_value(ref, propname);
if (!maxcount) {
strval = get_property_class(ref, propname);
strval = uncompress(strval);
if (strval && number(strval)) {
maxcount = atoi(strval);
}
}
}
}
nu = new_array_packed(0);
while (maxcount > 0) {
snprintf(propname, sizeof(propname), "%s#%c%d", dir, PROPDIR_DELIMITER, count);
prptr = get_property(ref, propname);
if (!prptr) {
snprintf(propname, sizeof(propname), "%s%c%d", dir, PROPDIR_DELIMITER, count);
prptr = get_property(ref, propname);
if (!prptr) {
snprintf(propname, sizeof(propname), "%s%d", dir, count);
prptr = get_property(ref, propname);
}
}
if (maxcount > 1023) {
maxcount = 1023;
}
if (maxcount) {
if (count > maxcount)
break;
} else if (!prptr) {
break;
}
if (prop_read_perms(ProgUID, ref, propname, mlev)) {
if (!prptr) {
temp2.type = PROG_INTEGER;
temp2.data.number = 0;
} else {
#ifdef DISKBASE
propfetch(ref, prptr);
#endif
switch (PropType(prptr)) {
case PROP_STRTYP:
temp2.type = PROG_STRING;
temp2.data.string = alloc_prog_string(uncompress(PropDataStr(prptr)));
break;
case PROP_LOKTYP:
temp2.type = PROG_LOCK;
if (PropFlags(prptr) & PROP_ISUNLOADED) {
temp2.data.lock = TRUE_BOOLEXP;
} else {
temp2.data.lock = PropDataLok(prptr);
if (temp2.data.lock != TRUE_BOOLEXP) {
temp2.data.lock = copy_bool(temp2.data.lock);
}
}
break;
case PROP_REFTYP:
temp2.type = PROG_OBJECT;
temp2.data.number = PropDataRef(prptr);
break;
case PROP_INTTYP:
temp2.type = PROG_INTEGER;
temp2.data.number = PropDataVal(prptr);
break;
case PROP_FLTTYP:
temp2.type = PROG_FLOAT;
temp2.data.fnumber = PropDataFVal(prptr);
break;
default:
temp2.type = PROG_INTEGER;
temp2.data.number = 0;
break;
}
}
array_appenditem(&nu, &temp2);
CLEAR(&temp2);
} else {
break;
}
count++;
}
PushArrayRaw(nu);
}
void
prim_array_put_propvals(PRIM_PROTOTYPE)
{
stk_array *arr;
char propname[BUFFER_LEN];
char dir[BUFFER_LEN];
PData propdat;
/* dbref strPropDir array -- */
CHECKOP(3);
oper3 = POP();
oper2 = POP();
oper1 = POP();
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required. (1)");
if (!valid_object(oper1))
abort_interp("Invalid dbref. (1)");
if (oper2->type != PROG_STRING)
abort_interp("String required. (2)");
if (oper3->type != PROG_ARRAY)
abort_interp("Array required. (3)");
ref = oper1->data.objref;
strcpy(dir, DoNullInd(oper2->data.string));
arr = oper3->data.array;
if (array_first(arr, &temp1)) {
do {
oper4 = array_getitem(arr, &temp1);
switch (temp1.type) {
case PROG_STRING:
snprintf(propname, sizeof(propname), "%s%c%s", dir, PROPDIR_DELIMITER,
DoNullInd(temp1.data.string));
break;
case PROG_INTEGER:
snprintf(propname, sizeof(propname), "%s%c%d", dir, PROPDIR_DELIMITER, temp1.data.number);
break;
case PROG_FLOAT:
snprintf(propname, sizeof(propname), "%s%c%.15g", dir, PROPDIR_DELIMITER, temp1.data.fnumber);
if (!strchr(propname, '.') && !strchr(propname, 'n') && !strchr(propname, 'e')) {
strcatn(propname, sizeof(propname), ".0");
}
break;
default:
*propname = '\0';
}
if (!prop_write_perms(ProgUID, ref, propname, mlev))
abort_interp("Permission denied while trying to set protected property.");
switch (oper4->type) {
case PROG_STRING:
propdat.flags = PROP_STRTYP;
propdat.data.str = oper4->data.string ? oper4->data.string->data : 0;
break;
case PROG_INTEGER:
propdat.flags = PROP_INTTYP;
propdat.data.val = oper4->data.number;
break;
case PROG_FLOAT:
propdat.flags = PROP_FLTTYP;
propdat.data.fval = oper4->data.fnumber;
break;
case PROG_OBJECT:
propdat.flags = PROP_REFTYP;
propdat.data.ref = oper4->data.objref;
break;
case PROG_LOCK:
propdat.flags = PROP_LOKTYP;
propdat.data.lok = copy_bool(oper4->data.lock);
break;
default:
*propname = '\0';
}
if (*propname) {
set_property(ref, propname, &propdat);
}
} while (array_next(arr, &temp1));
}
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
}
void
prim_array_put_proplist(PRIM_PROTOTYPE)
{
stk_array *arr;
char propname[BUFFER_LEN];
char dir[BUFFER_LEN];
PData propdat;
const char *fmtin;
char *fmtout;
int dirlen;
int count;
/* dbref strPropDir array -- */
CHECKOP(3);
oper3 = POP();
oper2 = POP();
oper1 = POP();
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required. (1)");
if (!valid_object(oper1))
abort_interp("Invalid dbref. (1)");
if (oper2->type != PROG_STRING)
abort_interp("String required. (2)");
if (oper3->type != PROG_ARRAY)
abort_interp("Array required. (3)");
if (oper3->data.array && oper3->data.array->type != ARRAY_PACKED)
abort_interp("Argument must be a list type array. (3)");
ref = oper1->data.objref;
strcpy(dir, DoNullInd(oper2->data.string));
arr = oper3->data.array;
dirlen = strlen(dir);
fmtout = propname;
fmtin = tp_proplist_counter_fmt;
while (*fmtin) {
if (*fmtin == 'P') {
if ((fmtout + dirlen) - propname > sizeof(propname))
break;
strcpy(fmtout, dir);
fmtout = &fmtout[strlen(fmtout)];
} else {
*fmtout++ = *fmtin;
}
fmtin++;
}
*fmtout++ = '\0';
if (!prop_write_perms(ProgUID, ref, propname, mlev))
abort_interp("Permission denied while trying to set protected property.");
if (tp_proplist_int_counter) {
propdat.flags = PROP_INTTYP;
propdat.data.val = array_count(arr);
} else {
snprintf(buf, sizeof(buf), "%d", array_count(arr));
propdat.flags = PROP_STRTYP;
propdat.data.str = buf;
}
set_property(ref, propname, &propdat);
if (array_first(arr, &temp1)) {
do {
oper4 = array_getitem(arr, &temp1);
fmtout = propname;
fmtin = tp_proplist_entry_fmt;
while (*fmtin) {
if (*fmtin == 'N') {
if ((fmtout + 18) - propname > sizeof(propname))
break;
snprintf(fmtout, sizeof(propname) - (fmtout - propname), "%d", temp1.data.number + 1);
fmtout = &fmtout[strlen(fmtout)];
} else if (*fmtin == 'P') {
if ((fmtout + dirlen) - propname > sizeof(propname))
break;
strcpy(fmtout, dir);
fmtout = &fmtout[strlen(fmtout)];
} else {
*fmtout++ = *fmtin;
}
fmtin++;
}
*fmtout++ = '\0';
if (!prop_write_perms(ProgUID, ref, propname, mlev))
abort_interp("Permission denied while trying to set protected property.");
switch (oper4->type) {
case PROG_STRING:
propdat.flags = PROP_STRTYP;
propdat.data.str = oper4->data.string ? oper4->data.string->data : 0;
break;
case PROG_INTEGER:
propdat.flags = PROP_INTTYP;
propdat.data.val = oper4->data.number;
break;
case PROG_FLOAT:
propdat.flags = PROP_FLTTYP;
propdat.data.fval = oper4->data.fnumber;
break;
case PROG_OBJECT:
propdat.flags = PROP_REFTYP;
propdat.data.ref = oper4->data.objref;
break;
case PROG_LOCK:
propdat.flags = PROP_LOKTYP;
propdat.data.lok = copy_bool(oper4->data.lock);
break;
default:
propdat.flags = PROP_INTTYP;
propdat.data.val = 0;
}
set_property(ref, propname, &propdat);
} while (array_next(arr, &temp1));
}
count = temp1.data.number;
for (;;) {
count++;
fmtout = propname;
fmtin = tp_proplist_entry_fmt;
while (*fmtin) {
if (*fmtin == 'N') {
if ((fmtout + 18) - propname > sizeof(propname))
break;
snprintf(fmtout, sizeof(propname) - (fmtout - propname), "%d", count + 1);
fmtout = &fmtout[strlen(fmtout)];
} else if (*fmtin == 'P') {
if ((fmtout + dirlen) - propname > sizeof(propname))
break;
strcpy(fmtout, dir);
fmtout = &fmtout[strlen(fmtout)];
} else {
*fmtout++ = *fmtin;
}
fmtin++;
}
*fmtout++ = '\0';
if (get_property(ref, propname)) {
remove_property(ref, propname);
} else {
break;
}
}
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
}
void
prim_array_get_reflist(PRIM_PROTOTYPE)
{
stk_array *nu;
const char *rawstr;
char dir[BUFFER_LEN];
int count = 0;
/* dbref strPropDir -- array */
CHECKOP(2);
oper2 = POP();
oper1 = POP();
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required. (1)");
if (!valid_object(oper1))
abort_interp("Invalid dbref. (1)");
if (oper2->type != PROG_STRING)
abort_interp("String required. (2)");
if (!oper2->data.string)
abort_interp("Non-null string required. (2)");
ref = oper1->data.objref;
strcpy(dir, oper2->data.string->data);
if (!prop_read_perms(ProgUID, ref, dir, mlev))
abort_interp("Permission denied.");
nu = new_array_packed(0);
rawstr = get_property_class(ref, dir);
rawstr = uncompress(rawstr);
if (rawstr) {
while (isspace(*rawstr))
rawstr++;
while (*rawstr) {
if (*rawstr == '#')
rawstr++;
if (!isdigit(*rawstr) && (*rawstr != '-'))
break;
result = atoi(rawstr);
while (*rawstr && !isspace(*rawstr))
rawstr++;
while (isspace(*rawstr))
rawstr++;
temp1.type = PROG_INTEGER;
temp1.data.number = count;
temp2.type = PROG_OBJECT;
temp2.data.number = result;
array_setitem(&nu, &temp1, &temp2);
count++;
CLEAR(&temp1);
CLEAR(&temp2);
}
}
PushArrayRaw(nu);
}
void
prim_array_put_reflist(PRIM_PROTOTYPE)
{
stk_array *arr;
char buf2[BUFFER_LEN];
char dir[BUFFER_LEN];
char *out;
PData propdat;
int len;
/* dbref strPropDir array -- */
CHECKOP(3);
oper3 = POP();
oper2 = POP();
oper1 = POP();
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required. (1)");
if (!valid_object(oper1))
abort_interp("Invalid dbref. (1)");
if (oper2->type != PROG_STRING)
abort_interp("String required. (2)");
if (!oper2->data.string)
abort_interp("Non-null string required. (2)");
if (oper3->type != PROG_ARRAY)
abort_interp("Argument must be a list array of dbrefs. (3)");
if (oper3->data.array && oper3->data.array->type != ARRAY_PACKED)
abort_interp("Argument must be a list array of dbrefs. (3)");
if (!array_is_homogenous(oper3->data.array, PROG_OBJECT))
abort_interp("Argument must be a list array of dbrefs. (3)");
ref = oper1->data.objref;
strcpy(dir, DoNullInd(oper2->data.string));
arr = oper3->data.array;
buf[0] = '\0';
if (!prop_write_perms(ProgUID, ref, dir, mlev))
abort_interp("Permission denied.");
out = buf;
if (array_first(arr, &temp1)) {
do {
oper4 = array_getitem(arr, &temp1);
len = snprintf(buf2, sizeof(buf2), "#%d", oper4->data.objref);
if (len == -1) {
buf2[sizeof(buf2)-1] = '\0';
len = sizeof(buf2) - 1;
}
if (out + len - buf >= BUFFER_LEN - 3)
abort_interp("Operation would result in string length overflow.");
if (*buf)
*out++ = ' ';
strcpy(out, buf2);
out += len;
} while (array_next(arr, &temp1));
}
remove_property(ref, dir);
propdat.flags = PROP_STRTYP;
propdat.data.str = buf;
set_property(ref, dir, &propdat);
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
}
void
prim_array_findval(PRIM_PROTOTYPE)
{
struct inst *in;
stk_array *arr;
stk_array *nu;
CHECKOP(2);
oper2 = POP(); /* ??? index */
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
nu = new_array_packed(0);
arr = oper1->data.array;
if (array_first(arr, &temp1)) {
do {
in = array_getitem(arr, &temp1);
if (!array_idxcmp(in, oper2)) {
array_appenditem(&nu, &temp1);
}
} while (array_next(arr, &temp1));
}
CLEAR(oper2);
CLEAR(oper1);
PushArrayRaw(nu);
}
void
prim_array_compare(PRIM_PROTOTYPE)
{
struct inst *val1;
struct inst *val2;
stk_array *arr1;
stk_array *arr2;
int res1, res2;
CHECKOP(2);
oper2 = POP(); /* arr Array */
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (2)");
arr1 = oper1->data.array;
arr2 = oper2->data.array;
res1 = array_first(arr1, &temp1);
res2 = array_first(arr2, &temp2);
if (!res1 && !res2) {
result = 0;
} else if (!res1) {
result = -1;
} else if (!res2) {
result = 1;
} else {
do {
result = array_idxcmp(&temp1, &temp2);
if (result) break;
val1 = array_getitem(arr1, &temp1);
val2 = array_getitem(arr2, &temp2);
result = array_idxcmp(val1, val2);
if (result) break;
res1 = array_next(arr1, &temp1);
res2 = array_next(arr2, &temp2);
} while (res1 && res2);
if (!res1 && !res2) {
result = 0;
} else if (!res1) {
result = -1;
} else if (!res2) {
result = 1;
}
}
CLEAR(oper2);
CLEAR(oper1);
PushInt(result);
}
void
prim_array_matchkey(PRIM_PROTOTYPE)
{
struct inst *in;
stk_array *arr;
stk_array *nu;
CHECKOP(2);
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)");
nu = new_array_dictionary();
arr = oper1->data.array;
if (array_first(arr, &temp1)) {
do {
if (temp1.type == PROG_STRING) {
if (equalstr(DoNullInd(oper2->data.string), DoNullInd(temp1.data.string))) {
in = array_getitem(arr, &temp1);
array_setitem(&nu, &temp1, in);
}
}
} while (array_next(arr, &temp1));
}
CLEAR(oper2);
CLEAR(oper1);
PushArrayRaw(nu);
}
void
prim_array_matchval(PRIM_PROTOTYPE)
{
struct inst *in;
stk_array *arr;
stk_array *nu;
CHECKOP(2);
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)");
nu = new_array_dictionary();
arr = oper1->data.array;
if (array_first(arr, &temp1)) {
do {
in = array_getitem(arr, &temp1);
if (in->type == PROG_STRING) {
if (equalstr(DoNullInd(oper2->data.string), DoNullInd(in->data.string))) {
array_setitem(&nu, &temp1, in);
}
}
} while (array_next(arr, &temp1));
}
CLEAR(oper2);
CLEAR(oper1);
PushArrayRaw(nu);
}
void
prim_array_extract(PRIM_PROTOTYPE)
{
struct inst *in;
struct inst *key;
stk_array *arr;
stk_array *karr;
stk_array *nu;
CHECKOP(2);
oper2 = POP(); /* arr indexes */
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (2)");
nu = new_array_dictionary();
arr = oper1->data.array;
karr = oper2->data.array;
if (array_first(karr, &temp1)) {
do {
key = array_getitem(karr, &temp1);
if (key) {
in = array_getitem(arr, key);
if (in) {
array_setitem(&nu, key, in);
}
}
} while (array_next(karr, &temp1));
}
CLEAR(oper2);
CLEAR(oper1);
PushArrayRaw(nu);
}
void
prim_array_excludeval(PRIM_PROTOTYPE)
{
struct inst *in;
stk_array *arr;
stk_array *nu;
CHECKOP(2);
oper2 = POP(); /* ??? index */
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
nu = new_array_packed(0);
arr = oper1->data.array;
if (array_first(arr, &temp1)) {
do {
in = array_getitem(arr, &temp1);
if (array_idxcmp(in, oper2)) {
array_appenditem(&nu, &temp1);
}
} while (array_next(arr, &temp1));
}
CLEAR(oper2);
CLEAR(oper1);
PushArrayRaw(nu);
}
void
prim_array_join(PRIM_PROTOTYPE)
{
struct inst *in;
stk_array *arr;
char outbuf[BUFFER_LEN];
char* ptr;
const char* text;
char* delim;
int tmplen;
int done;
int first_item;
CHECKOP(2);
oper2 = POP(); /* str joinstr */
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)");
arr = oper1->data.array;
delim = DoNullInd(oper2->data.string);
ptr = outbuf;
*outbuf = '\0';
first_item = 1;
done = !array_first(arr, &temp1);
while (!done) {
in = array_getitem(arr, &temp1);
switch (in->type) {
case PROG_STRING:
text = DoNullInd(in->data.string);
break;
case PROG_INTEGER:
snprintf(buf, sizeof(buf), "%d", in->data.number);
text = buf;
break;
case PROG_OBJECT:
snprintf(buf, sizeof(buf), "#%d", in->data.number);
text = buf;
break;
case PROG_FLOAT:
snprintf(buf, sizeof(buf), "%.15g", in->data.fnumber);
if (!strchr(buf, '.') && !strchr(buf, 'n') && !strchr(buf, 'e')) {
strcatn(buf, sizeof(buf), ".0");
}
text = buf;
break;
case PROG_LOCK:
text = unparse_boolexp(ProgUID, in->data.lock, 1);
break;
default:
text = "<UNSUPPORTED>";
break;
}
if ( first_item ) {
first_item = 0;
} else {
tmplen = strlen(delim);
if (tmplen > BUFFER_LEN - (ptr - outbuf) - 1)
abort_interp("Operation would result in overflow.");
strcpy(ptr, delim);
ptr += tmplen;
}
tmplen = strlen(text);
if (tmplen > BUFFER_LEN - (ptr - outbuf) - 1)
abort_interp("Operation would result in overflow.");
strcpy(ptr, text);
ptr += tmplen;
done = !array_next(arr, &temp1);
}
CLEAR(oper2);
CLEAR(oper1);
PushString(outbuf);
}
void
prim_array_interpret(PRIM_PROTOTYPE)
{
struct inst *in;
stk_array *arr;
char outbuf[BUFFER_LEN];
char *ptr;
const char *text;
/* char *delim; */
int tmplen;
int done;
CHECKOP(1);
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
arr = oper1->data.array;
ptr = outbuf;
*outbuf = '\0';
done = !array_first(arr, &temp1);
while (!done) {
in = array_getitem(arr, &temp1);
switch (in->type) {
case PROG_STRING:
text = DoNullInd(in->data.string);
break;
case PROG_INTEGER:
snprintf(buf, sizeof(buf), "%d", in->data.number);
text = buf;
break;
case PROG_OBJECT:
if (in->data.objref == NOTHING) {
text = "*NOTHING*";
break;
}
if (in->data.objref == AMBIGUOUS) {
text = "*AMBIGUOUS*";
break;
}
if (in->data.objref == HOME) {
text = "*HOME*";
break;
}
if (in->data.number < HOME) {
text = "*INVALID*";
break;
}
if (in->data.number >= db_top) {
text = "*INVALID*";
break;
}
snprintf(buf, sizeof(buf), "%s", NAME(in->data.number));
text = buf;
break;
case PROG_FLOAT:
snprintf(buf, sizeof(buf), "%.15g", in->data.fnumber);
if (!strchr(buf, '.') && !strchr(buf, 'n') && !strchr(buf, 'e')) {
strcatn(buf, sizeof(buf), ".0");
}
text = buf;
break;
case PROG_LOCK:
text = unparse_boolexp(ProgUID, in->data.lock, 1);
break;
default:
text = "<UNSUPPORTED>";
break;
}
tmplen = strlen(text);
if (tmplen > BUFFER_LEN - (ptr - outbuf) - 1) {
strncpy(ptr, text, BUFFER_LEN - (ptr - outbuf) - 1);
outbuf[BUFFER_LEN - 1] = '\0';
break;
} else {
strcpy(ptr, text);
ptr += tmplen;
}
done = !array_next(arr, &temp1);
}
CLEAR(oper1);
PushString(outbuf);
}
void
prim_array_pin(PRIM_PROTOTYPE)
{
stk_array *arr;
stk_array *nu;
CHECKOP(1);
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
arr = oper1->data.array;
if (!arr) {
nu = new_array_packed(0);
} else if (arr->links > 1) {
nu = array_decouple(arr);
} else {
arr->links++;
nu = arr;
}
array_set_pinned(nu, 1);
CLEAR(oper1);
PushArrayRaw(nu);
}
void
prim_array_unpin(PRIM_PROTOTYPE)
{
stk_array *arr;
CHECKOP(1);
oper1 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array.");
arr = oper1->data.array;
if (arr) {
arr->links++;
array_set_pinned(arr, 0);
}
CLEAR(oper1);
PushArrayRaw(arr);
}
void
prim_array_get_ignorelist(PRIM_PROTOTYPE)
{
stk_array *nu;
const char *rawstr;
int count = 0;
CHECKOP(1);
oper1 = POP();
if (oper1->type != PROG_OBJECT)
abort_interp("Dbref required.");
if (!valid_object(oper1))
abort_interp("Invalid dbref.");
if (mlev < 3)
abort_interp("Permission denied.");
ref = OWNER(oper1->data.objref);
CLEAR(oper1);
nu = new_array_packed(0);
if (tp_ignore_support)
{
rawstr = get_property_class(ref, IGNORE_PROP);
rawstr = uncompress(rawstr);
if (rawstr) {
while (isspace(*rawstr))
rawstr++;
while (*rawstr) {
if (*rawstr == '#')
rawstr++;
if (!isdigit(*rawstr))
break;
result = atoi(rawstr);
while (*rawstr && !isspace(*rawstr))
rawstr++;
while (isspace(*rawstr))
rawstr++;
temp1.type = PROG_INTEGER;
temp1.data.number = count;
temp2.type = PROG_OBJECT;
temp2.data.number = result;
array_setitem(&nu, &temp1, &temp2);
count++;
CLEAR(&temp1);
CLEAR(&temp2);
}
}
}
PushArrayRaw(nu);
}
void
prim_array_nested_get(PRIM_PROTOTYPE)
{
struct inst *idx;
struct inst *dat;
struct inst temp;
stk_array *idxarr;
int i, idxcnt;
CHECKOP(2);
oper1 = POP(); /* arr IndexList */
oper2 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array of indexes. (2)");
if (!oper1->data.array || oper1->data.array->type == ARRAY_DICTIONARY)
abort_interp("Argument not an array of indexes. (2)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
idxarr = oper1->data.array;
idxcnt = array_count(idxarr);
dat = oper2;
for (i = 0; dat && i < idxcnt; i++) {
temp1.type = PROG_INTEGER;
temp1.data.number = i;
idx = array_getitem(idxarr, &temp1);
if (idx->type != PROG_INTEGER && idx->type != PROG_STRING)
abort_interp("Argument not an array of indexes. (2)");
if (dat->type != PROG_ARRAY)
abort_interp("Mid-level nested item was not an array. (1)");
dat = array_getitem(dat->data.array, idx);
}
/* copy data to a temp inst before clearing the containing array */
if (dat) {
copyinst(dat, &temp);
} else {
temp.type = PROG_INTEGER;
temp.data.number = 0;
}
CLEAR(oper1);
CLEAR(oper2);
/* copy data to stack, then clear temp inst */
copyinst(&temp, &arg[(*top)++]);
CLEAR(&temp);
}
void
prim_array_nested_set(PRIM_PROTOTYPE)
{
struct inst nest[16];
struct inst *idx = NULL;
struct inst *dat;
struct inst temp;
stk_array *arr;
stk_array *idxarr;
int i, idxcnt;
CHECKOP(3);
oper1 = POP(); /* arr IndexList */
oper2 = POP(); /* arr Array */
oper3 = POP(); /* ??? Value */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array of indexes. (3)");
if (!oper1->data.array || oper1->data.array->type == ARRAY_DICTIONARY)
abort_interp("Argument not an array of indexes. (3)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (2)");
idxarr = oper1->data.array;
idxcnt = array_count(idxarr);
if (idxcnt > sizeof(nest) / sizeof(struct inst))
abort_interp("Nesting would be too deep. (3)");
if (idxcnt == 0) {
copyinst(oper2, &nest[0]);
} else {
dat = oper2;
for (i = 0; i < idxcnt; i++) {
copyinst(dat, &nest[i]);
temp.type = PROG_INTEGER;
temp.data.number = i;
idx = array_getitem(idxarr, &temp);
if (idx->type != PROG_INTEGER && idx->type != PROG_STRING)
abort_interp("Argument not an array of indexes. (3)");
if (i < idxcnt - 1) {
dat = array_getitem(nest[i].data.array, idx);
if (dat) {
if (dat->type != PROG_ARRAY)
abort_interp("Mid-level nested item was not an array. (2)");
} else {
if (idx->type == PROG_INTEGER && idx->data.number == 0) {
arr = new_array_packed(1);
} else {
arr = new_array_dictionary();
}
arr->links = 0;
temp.type = PROG_ARRAY;
temp.data.array = arr;
dat = &temp;
}
}
}
array_setitem(&nest[idxcnt-1].data.array, idx, oper3);
for (i = idxcnt - 1; i--> 0;) {
temp.type = PROG_INTEGER;
temp.data.number = i;
idx = array_getitem(idxarr, &temp);
array_setitem(&nest[i].data.array, idx, &nest[i+1]);
CLEAR(&nest[i+1]);
}
}
CLEAR(oper1);
CLEAR(oper2);
CLEAR(oper3);
/* copy data to stack, then clear temp inst */
copyinst(&nest[0], &arg[(*top)++]);
CLEAR(&nest[0]);
}
void
prim_array_nested_del(PRIM_PROTOTYPE)
{
struct inst nest[32];
struct inst *idx = NULL;
struct inst *dat;
struct inst temp;
stk_array *idxarr;
int i, idxcnt;
CHECKOP(2);
oper1 = POP(); /* arr IndexList */
oper2 = POP(); /* arr Array */
if (oper1->type != PROG_ARRAY)
abort_interp("Argument not an array of indexes. (2)");
if (!oper1->data.array || oper1->data.array->type == ARRAY_DICTIONARY)
abort_interp("Argument not an array of indexes. (2)");
if (oper2->type != PROG_ARRAY)
abort_interp("Argument not an array. (1)");
idxarr = oper1->data.array;
idxcnt = array_count(idxarr);
if (idxcnt > sizeof(nest) / sizeof(struct inst))
abort_interp("Nesting would be too deep. (2)");
if (idxcnt == 0) {
copyinst(oper2, &nest[0]);
} else {
int doneearly = 0;
dat = oper2;
for (i = 0; i < idxcnt; i++) {
copyinst(dat, &nest[i]);
temp.type = PROG_INTEGER;
temp.data.number = i;
idx = array_getitem(idxarr, &temp);
if (idx->type != PROG_INTEGER && idx->type != PROG_STRING)
abort_interp("Argument not an array of indexes. (2)");
if (i < idxcnt - 1) {
dat = array_getitem(nest[i].data.array, idx);
if (dat) {
if (dat->type != PROG_ARRAY)
abort_interp("Mid-level nested item was not an array. (1)");
} else {
doneearly = 1;
break;
}
}
}
if (!doneearly) {
array_delitem(&nest[idxcnt-1].data.array, idx);
for (i = idxcnt - 1; i--> 0;) {
temp.type = PROG_INTEGER;
temp.data.number = i;
idx = array_getitem(idxarr, &temp);
array_setitem(&nest[i].data.array, idx, &nest[i+1]);
CLEAR(&nest[i+1]);
}
}
}
CLEAR(oper1);
CLEAR(oper2);
/* copy data to stack, then clear temp inst */
copyinst(&nest[0], &arg[(*top)++]);
CLEAR(&nest[0]);
}
void
prim_array_filter_flags(PRIM_PROTOTYPE)
{
struct flgchkdat check;
stk_array *nw, *arr;
struct inst *in;
CHECKOP(2);
oper2 = POP(); /* str:flags */
oper1 = POP(); /* arr:refs */
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)");
arr = oper1->data.array;
nw = new_array_packed(0);
init_checkflags(player, DoNullInd(oper2->data.string), &check);
if (array_first(arr, &temp1)) {
do {
in = array_getitem(arr, &temp1);
if (valid_object(in) && checkflags(in->data.objref, check))
array_appenditem(&nw, in);
} while (array_next(arr, &temp1));
}
CLEAR(oper1);
CLEAR(oper2);
PushArrayRaw(nw);
}