tmi2/
tmi2/bin/
tmi2/etc/
tmi2/lib/
tmi2/lib/adm/
tmi2/lib/adm/daemons/languages/
tmi2/lib/adm/daemons/network/I3/
tmi2/lib/adm/daemons/virtual/template/
tmi2/lib/adm/obj/master/
tmi2/lib/adm/priv/
tmi2/lib/adm/shell/
tmi2/lib/adm/tmp/
tmi2/lib/cmds/
tmi2/lib/d/
tmi2/lib/d/Conf/
tmi2/lib/d/Conf/adm/
tmi2/lib/d/Conf/boards/
tmi2/lib/d/Conf/cmds/
tmi2/lib/d/Conf/data/
tmi2/lib/d/Conf/logs/
tmi2/lib/d/Conf/obj/
tmi2/lib/d/Conf/text/help/
tmi2/lib/d/Fooland/adm/
tmi2/lib/d/Fooland/data/
tmi2/lib/d/Fooland/data/attic/
tmi2/lib/d/Fooland/items/
tmi2/lib/d/TMI/
tmi2/lib/d/TMI/adm/
tmi2/lib/d/TMI/boards/
tmi2/lib/d/TMI/data/
tmi2/lib/d/TMI/rooms/
tmi2/lib/d/grid/
tmi2/lib/d/grid/adm/
tmi2/lib/d/grid/data/
tmi2/lib/d/std/
tmi2/lib/d/std/adm/
tmi2/lib/data/adm/
tmi2/lib/data/adm/daemons/
tmi2/lib/data/adm/daemons/doc_d/
tmi2/lib/data/adm/daemons/emoted/
tmi2/lib/data/adm/daemons/network/http/
tmi2/lib/data/adm/daemons/network/services/mail_q/
tmi2/lib/data/adm/daemons/network/smtp/
tmi2/lib/data/adm/daemons/news/archives/
tmi2/lib/data/attic/connection/
tmi2/lib/data/attic/user/
tmi2/lib/data/std/connection/b/
tmi2/lib/data/std/connection/l/
tmi2/lib/data/std/user/a/
tmi2/lib/data/std/user/b/
tmi2/lib/data/std/user/d/
tmi2/lib/data/std/user/f/
tmi2/lib/data/std/user/l/
tmi2/lib/data/std/user/x/
tmi2/lib/data/u/d/dm/working/doc_d/
tmi2/lib/data/u/l/leto/doc_d/
tmi2/lib/data/u/l/leto/smtp/
tmi2/lib/doc/
tmi2/lib/doc/driverdoc/applies/
tmi2/lib/doc/driverdoc/concepts/
tmi2/lib/doc/driverdoc/driver/
tmi2/lib/doc/driverdoc/efuns/arrays/
tmi2/lib/doc/driverdoc/efuns/buffers/
tmi2/lib/doc/driverdoc/efuns/compile/
tmi2/lib/doc/driverdoc/efuns/ed/
tmi2/lib/doc/driverdoc/efuns/floats/
tmi2/lib/doc/driverdoc/efuns/functions/
tmi2/lib/doc/driverdoc/efuns/general/
tmi2/lib/doc/driverdoc/efuns/numbers/
tmi2/lib/doc/driverdoc/efuns/parsing/
tmi2/lib/doc/driverdoc/lpc/constructs/
tmi2/lib/doc/driverdoc/lpc/preprocessor/
tmi2/lib/doc/driverdoc/lpc/types/
tmi2/lib/doc/driverdoc/platforms/
tmi2/lib/doc/mudlib/
tmi2/lib/ftp/
tmi2/lib/log/
tmi2/lib/obj/net/
tmi2/lib/obj/shells/
tmi2/lib/std/board/
tmi2/lib/std/body/
tmi2/lib/std/fun/
tmi2/lib/std/living/
tmi2/lib/std/object/
tmi2/lib/std/shop/
tmi2/lib/std/socket/
tmi2/lib/std/virtual/
tmi2/lib/student/
tmi2/lib/student/kalypso/
tmi2/lib/student/kalypso/armor/
tmi2/lib/student/kalypso/rooms/
tmi2/lib/student/kalypso/weapons/
tmi2/lib/u/l/leto/
tmi2/lib/u/l/leto/cmds/
tmi2/lib/www/errors/
tmi2/lib/www/gateways/
tmi2/lib/www/images/
tmi2/old/
tmi2/v21.7a10/
tmi2/v21.7a10/ChangeLog.old/
tmi2/v21.7a10/compat/simuls/
tmi2/v21.7a10/include/
tmi2/v21.7a10/testsuite/
tmi2/v21.7a10/testsuite/clone/
tmi2/v21.7a10/testsuite/command/
tmi2/v21.7a10/testsuite/data/
tmi2/v21.7a10/testsuite/etc/
tmi2/v21.7a10/testsuite/include/
tmi2/v21.7a10/testsuite/inherit/
tmi2/v21.7a10/testsuite/inherit/master/
tmi2/v21.7a10/testsuite/log/
tmi2/v21.7a10/testsuite/u/
tmi2/v21.7a10/tmp/
#include "std.h"
#include "lpc_incl.h"
#include "comm.h"
#include "regexp.h"
#include "backend.h"
#include "qsort.h"
#include "array.h"
#include "md.h"

/*
 * This file contains functions used to manipulate arrays.
 * Some of them are connected to efuns, and some are only used internally
 * by the MudOS driver.
 */

int num_arrays;
int total_array_size;

INLINE static int builtin_sort_array_cmp_fwd PROT((svalue_t *, svalue_t *));
INLINE static int builtin_sort_array_cmp_rev PROT((svalue_t *, svalue_t *));
INLINE static int sort_array_cmp PROT((svalue_t *, svalue_t *));
#ifndef NO_ENVIRONMENT
static int deep_inventory_count PROT((object_t *));
static void deep_inventory_collect PROT((object_t *, array_t *, int *));
#endif
INLINE static int alist_cmp PROT((svalue_t *, svalue_t *));

/*
 * Make an empty array for everyone to use, never to be deallocated.
 * It is cheaper to reuse it, than to use MALLOC() and allocate.
 */

static array_t the_null_array =
{
    1,				/* Ref count, which will ensure that it will
				 * never be deallocated */
#ifdef DEBUG
    1,                          /* extra ref */
#endif
    0,				/* size */
};

INLINE array_t *
       null_array()
{
    the_null_array.ref++;
    return &the_null_array;
}

/*
 * Allocate an array of size 'n'.
 */
array_t *allocate_array P1(int, n)
{
    array_t *p;

    if (n < 0 || n > max_array_size)
	error("Illegal array size.\n");
    if (n == 0) {
	return null_array();
    }
    num_arrays++;
    total_array_size += sizeof(array_t) + sizeof(svalue_t) *
	(n - 1);
    p = ALLOC_ARRAY(n);
    p->ref = 1;
    p->size = n;
#ifdef PACKAGE_MUDLIB_STATS
    if (current_object) {
	assign_stats(&p->stats, current_object);
	add_array_size(&p->stats, n);
    } else {
	null_stats(&p->stats);
    }
#endif
    while (n--)
	p->item[n] = const0;
    return p;
}

array_t *allocate_empty_array P1(int, n)
{
    array_t *p;
    
    if (n < 0 || n > max_array_size)
	error("Illegal array size.\n");
    if (!n) return null_array();
    num_arrays++;
    total_array_size += sizeof(array_t) + sizeof(svalue_t) * (n-1);
    p = ALLOC_ARRAY(n);
    p->ref = 1;
    p->size = n;
#ifdef PACKAGE_MUDLIB_STATS
    if (current_object) {
	assign_stats(&p->stats, current_object);
	add_array_size(&p->stats, n);
    } else {
	null_stats(&p->stats);
    }
#endif
    return p;
}

void dealloc_array P1(array_t *, p)
{
    int i;

    if (p == &the_null_array)
	return;

    for (i = p->size; i--;)
	free_svalue(&p->item[i], "free_array");
#ifdef PACKAGE_MUDLIB_STATS
    add_array_size(&p->stats, -((int)p->size));
#endif
    num_arrays--;
    total_array_size -= sizeof(array_t) + sizeof(svalue_t) *
	(p->size - 1);
    FREE((char *) p);
}

void free_array P1(array_t *, p)
{
    if (--(p->ref) > 0)
	return;

    dealloc_array(p);
}

void free_empty_array P1(array_t *, p)
{
    if ((--(p->ref) > 0) || (p == &the_null_array)) {
        return;
      }
#ifdef PACKAGE_MUDLIB_STATS
    add_array_size(&p->stats, -((int)p->size));
#endif
    num_arrays--;
    total_array_size -= sizeof(array_t) + sizeof(svalue_t) *
        (p->size - 1);
    FREE((char *) p);
}

array_t *explode_string P4(char *, str, int, slen, char *, del, int, len)
{
    char *p, *beg, *lastdel = (char *) NULL;
    int num, j, limit;
    array_t *ret;
    char *buff, *tmp;
    short sz;

    if (!slen)
	return null_array();

    /* return an array of length strlen(str) -w- one character per element */
    if (len == 0) {
	sz = 1;

	if (slen > max_array_size) {
	    slen = max_array_size;
	}
	ret = allocate_empty_array(slen);
	for (j = 0; j < slen; j++) {
	    ret->item[j].type = T_STRING;
	    ret->item[j].subtype = STRING_MALLOC;
	    ret->item[j].u.string = tmp = new_string(1, "explode_string: tmp");
	    tmp[0] = str[j];
	    tmp[1] = '\0';
	}
	return ret;
    }
    if (len == 1) {
	char delimeter;

	delimeter = *del;

	/*
	 * Skip leading 'del' strings, if any.
	 */
	while (*str == delimeter) {
	    str++;
	    slen--;
	    if (str[0] == '\0') {
		return null_array();
	    }
#ifdef SANE_EXPLODE_STRING
	    break;
#endif
	}

	/*
	 * Find number of occurences of the delimiter 'del'.
	 */
	for (p = str, num = 0; *p;) {
	    if (*p == delimeter) {
		num++;
		lastdel = p;
	    }
	    p++;
	}

	/*
	 * Compute number of array items. It is either number of delimiters,
	 * or, one more.
	 */
	limit = max_array_size;
	if (lastdel != (str + slen - 1)) {
	    num++;
	    limit--;
	}
	if (num > max_array_size) {
	    num = max_array_size;
	}
	ret = allocate_empty_array(num);
	for (p = str, beg = str, num = 0; *p && (num < limit);) {
	    if (*p == delimeter) {
		DEBUG_CHECK(num >= ret->size, "Index out of bounds in explode!\n");
		sz = p - beg;
		ret->item[num].type = T_STRING;
		ret->item[num].subtype = STRING_MALLOC;
		ret->item[num].u.string = buff = new_string(p - beg, "explode_string: buff");

		strncpy(buff, beg, p - beg);
		buff[p - beg] = '\0';
		num++;
		beg = ++p;
	    } else {
		p++;
	    }
	}

	/* Copy last occurence, if there was not a 'del' at the end. */
	if (*beg != '\0') {
	    ret->item[num].type = T_STRING;
	    ret->item[num].subtype = STRING_MALLOC;
	    ret->item[num].u.string = string_copy(beg, "explode_string: last, len == 1");
	}
	return ret;
    }				/* len == 1 */
    /*
     * Skip leading 'del' strings, if any.
     */
    while (strncmp(str, del, len) == 0) {
	str += len;
	slen -= len;
	if (str[0] == '\0') {
	    return null_array();
	}
#ifdef SANE_EXPLODE_STRING
	break;
#endif
    }

    /*
     * Find number of occurences of the delimiter 'del'.
     */
    for (p = str, num = 0; *p;) {
	if (strncmp(p, del, len) == 0) {
	    num++;
	    lastdel = p;
	    p += len;
	} else {
	    p++;
	}
    }

    /*
     * Compute number of array items. It is either number of delimiters, or,
     * one more.
     */
    if ((slen <= len) || (lastdel != (str + slen - len))) {
	num++;
    }
    if (num > max_array_size) {
	num = max_array_size;
    }
    ret = allocate_empty_array(num);
    limit = max_array_size - 1;	/* extra element can be added after loop */
    for (p = str, beg = str, num = 0; *p && (num < limit);) {
	if (strncmp(p, del, len) == 0) {
	    if (num >= ret->size)
		fatal("Index out of bounds in explode!\n");

	    ret->item[num].type = T_STRING;
	    ret->item[num].subtype = STRING_MALLOC;
	    ret->item[num].u.string = buff = new_string(p - beg, 
						     "explode_string: buff");

	    strncpy(buff, beg, p - beg);
	    buff[p - beg] = '\0';
	    num++;
	    beg = p + len;
	    p = beg;
	} else {
	    p++;
	}
    }

    /* Copy last occurence, if there was not a 'del' at the end. */
    if (*beg != '\0') {
	ret->item[num].type = T_STRING;
	ret->item[num].subtype = STRING_MALLOC;
	ret->item[num].u.string = string_copy(beg, "explode_string: last, len != 1");
    }
    return ret;
}

char *implode_string P3(array_t *, arr, char *, del, int, del_len)
{
    int size, i, num;
    char *p, *q;
    svalue_t *sv = arr->item;

    for (i = arr->size, size = 0, num = 0; i--;) {
	if (sv[i].type == T_STRING) {
	    size += SVALUE_STRLEN(&sv[i]);
	    num++;
	}
    }
    if (num == 0)
	return string_copy("", "implode_string");

    p = new_string(size + (num - 1) * del_len, "implode_string: p");
    q = p;
    for (i = 0, num = 0; i < arr->size; i++) {
	if (sv[i].type == T_STRING) {
	    if (num) {
		strncpy(p, del, del_len);
		p += del_len;
	    }
	    size = SVALUE_STRLEN(&sv[i]);
	    strncpy(p, sv[i].u.string, size);
	    p += size;
	    num++;
	}
    }
    *p = 0;
    return q;
}

void implode_array P4(funptr_t *, fp, array_t *, arr, 
		      svalue_t *, dest, int, first_on_stack) {
    int i = 0, n;
    svalue_t *v;

    if (first_on_stack) {
	if (!(n = arr->size)) {
	    *dest = *sp--;
	    return;
	}
    } else {
	if (!(n = arr->size)) {
	    *dest = const0;
	    return;
	} else if (n == 1) {
	    assign_svalue_no_free(dest, &arr->item[0]);
	    return;
	}
    }

    if (!first_on_stack)
	push_svalue(&arr->item[i++]);
	
    while (1) {
	push_svalue(&arr->item[i++]);
	v = call_function_pointer(fp, 2);
	if (!v) {
	    *dest = const0;
	    return;
	}
	if (i < n)
	    push_svalue(v);
	else
	    break;
    }
    assign_svalue_no_free(dest, v);
}

array_t *users()
{
    register object_t *ob;
    int i, j;
    int display_hidden = 0;
    array_t *ret;

    if (num_hidden > 0) {
	if (current_object->flags & O_HIDDEN) {
	    display_hidden = 1;
	} else {
	    display_hidden = valid_hide(current_object);
	}
    }
    ret = allocate_empty_array(num_user - (display_hidden ? 0 : num_hidden));
    for (i = j = 0; i < max_users; i++) {
	if (!all_users[i]) {
	    continue;
	}
	ob = all_users[i]->ob;
	if (!display_hidden && (ob->flags & O_HIDDEN)) {
	    continue;
	}
	ret->item[j].type = T_OBJECT;
	ret->item[j].u.ob = ob;
	add_ref(ob, "users");
	j++;
    }
    return ret;
}

/*
 * Slice of an array.
 * It now frees the passed array
 */
array_t *slice_array P3(array_t *, p, int, from, int, to)
{
    int cnt;
    svalue_t *sv1, *sv2;

    if (from < 0)
	from = 0;
    if (to >= p->size)
	to = p->size - 1;
    if (from > to) {
	free_array(p);
	return null_array();
    }

    if (!(--p->ref)){
#ifdef PACKAGE_MUDLIB_STATS
	add_array_size(&p->stats, -((int)p->size));
#endif
	total_array_size += (to - from + 1 - p->size) * sizeof(svalue_t);
	if (from) {
	    sv1 = p->item + from;
	    cnt = from;
	    while (cnt--) free_svalue(--sv1, "slice_array:2");
	    cnt = to - from + 1;
	    sv1 = p->item;
	    sv2 = p->item + from;
	    while (cnt--) *sv1++ = *sv2++;
	} else {
	    sv2 = p->item + to + 1;
	}
	cnt = (p->size - 1) - to;
	while (cnt--) free_svalue(sv2++, "slice_array:3");
	p = RESIZE_ARRAY(p, to-from+1);
#ifdef PACKAGE_MUDLIB_STATS
	if (current_object) {
	    assign_stats(&p->stats, current_object);
	    add_array_size(&p->stats, to - from + 1);
	} else null_stats(&p->stats);
#endif
	p->size = to-from+1;
	p->ref = 1;
	return p;
    } else {
	array_t *d;

	d = allocate_empty_array(to - from + 1);
	sv1 = d->item - from;
	sv2 = p->item;
	for (cnt = from; cnt <= to; cnt++)
	  assign_svalue_no_free(sv1 + cnt, sv2 + cnt);
	return d;
    }
}

/*
 * Copy of an array
 */
array_t *copy_array P1(array_t *, p)
{
    array_t *d;
    int n;
    svalue_t *sv1 = p->item, *sv2;

    d = allocate_empty_array(n = p->size);
    sv2 = d->item;
    while (n--)
	assign_svalue_no_free(sv2 + n, sv1 + n);
    return d;
}

#ifdef F_COMMANDS
array_t *commands P1(object_t *, ob)
{
    sentence_t *s;
    array_t *v, *p;
    int cnt = 0;
    svalue_t *sv;

    for (s = ob->sent; s && s->verb; s = s->next) {
	if (++cnt == max_array_size) break;
    }
    v = allocate_empty_array(cnt);
    sv = v->item;
    for (s = ob->sent; cnt-- && s && s->verb; s = s->next) {
	sv->type = T_ARRAY;
	(sv++)->u.arr = p = allocate_empty_array(4);
	p->item[0].type = T_STRING;
	p->item[0].u.string = ref_string(s->verb);	/* the verb is shared */
	p->item[0].subtype = STRING_SHARED;
	p->item[1].type = T_NUMBER;
	p->item[1].u.number = s->flags;
	p->item[2].type = T_OBJECT;
	p->item[2].u.ob = s->ob;
	p->item[3].type = T_STRING;
        if (s->flags & V_FUNCTION) {
            p->item[3].u.string = "<function>";
            p->item[3].subtype = STRING_CONSTANT;
        } else {
            p->item[3].u.string = ref_string(s->function.s);
            p->item[3].subtype = STRING_SHARED;
        }
	add_ref(s->ob, "commands");
    }
    return v;
}
#endif

/* EFUN: filter_array

   Runs all elements of an array through ob->func()
   and returns an array holding those elements that ob->func
   returned 1 for.
   */

#ifdef F_FILTER
void
filter_array P2(svalue_t *, arg, int, num_arg)
{
    array_t *vec = arg->u.arr, *r;
    int size;

    if ((size = vec->size) < 1) {
	pop_n_elems(num_arg - 1);
	return;
    }
    else {
	funptr_t *fp;
	object_t *ob = 0;
	char *func;

	char *flags = new_string(size, "TEMP: filter: flags");
	svalue_t *extra, *v;
	int res = 0, cnt, numex = 0;

	push_malloced_string(flags);

	if ((arg+1)->type == T_FUNCTION){
	    fp = (arg+1)->u.fp;
	    if (num_arg > 2) extra = arg+2, numex = num_arg - 2;
	} else {    
	    func = (arg+1)->u.string;
	    if (num_arg < 3) ob = current_object;
	    else {
		if ((arg+2)->type == T_OBJECT) ob = (arg+2)->u.ob;
		else if ((arg+2)->type == T_STRING){
		    if ((ob = find_object(arg[2].u.string)) && !object_visible(ob)) ob = 0;
		}
		if (!ob) bad_argument(arg+2, T_STRING | T_OBJECT, 3, F_FILTER);
		if (num_arg > 3) extra = arg + 3, numex = num_arg - 3;
	    }
	}

	for (cnt = 0; cnt < size; cnt++){
	    push_svalue(vec->item + cnt);
	    if (numex) push_some_svalues(extra, numex);
	    v = ob ? apply(func, ob, 1 + numex, ORIGIN_EFUN)
		: call_function_pointer(fp, 1 + numex);
	    if (!IS_ZERO(v)){
		flags[cnt] = 1;
		res++;
	    } else flags[cnt] = 0;
	}
	r = allocate_empty_array(res);
	if (res){
	    while (cnt--) {
		if (flags[cnt])
		    assign_svalue_no_free(&r->item[--res], vec->item+cnt);
	    }
	}

	FREE_MSTR(flags);
	sp--;
	pop_n_elems(num_arg - 1);
	free_array(vec);
	sp->u.arr = r;
    }
}

#ifdef FILTER_ARRAY
void c_filter_array P5(svalue *, ret, svalue *, s0, svalue *, s1, svalue *, s2, svalue *, s3)
{
    error("c_filter_array needs to be written.\n");
    /* TODO */
}
#endif
#endif

/* Unique maker

   These routines takes an array of objects and calls the function 'func'
   in them. The return values are used to decide which of the objects are
   unique. Then an array on the below form are returned:

   ({
   ({Same1:1, Same1:2, Same1:3, .... Same1:N }),
   ({Same2:1, Same2:2, Same2:3, .... Same2:N }),
   ({Same3:1, Same3:2, Same3:3, .... Same3:N }),
   ....
   ....
   ({SameM:1, SameM:2, SameM:3, .... SameM:N }),
   })

   i.e an array of arrays consisting of lists of objectpointers
   to all the nonunique objects for each unique set of objects.

   The basic purpose of this routine is to speed up the preparing of the
   array used for describing.

   */

/* nonstatic, is used in mappings too */
int sameval P2(svalue_t *, arg1, svalue_t *, arg2)
{
    DEBUG_CHECK(!arg1 || !arg2, "Null pointer passed to sameval.\n");

    switch (arg1->type | arg2->type) {
    case T_NUMBER:
	return arg1->u.number == arg2->u.number;
    case T_ARRAY:
    case T_CLASS:
	return arg1->u.arr == arg2->u.arr;
    case T_STRING:
	if (SVALUE_STRLEN_DIFFERS(arg1, arg2)) return 0;
	return !strcmp(arg1->u.string, arg2->u.string);
    case T_OBJECT:
	return arg1->u.ob == arg2->u.ob;
    case T_MAPPING:
	return arg1->u.map == arg2->u.map;
    case T_FUNCTION:
	return arg1->u.fp == arg2->u.fp;
    case T_REAL:
	return arg1->u.real == arg2->u.real;
    case T_BUFFER:
	return arg1->u.buf == arg2->u.buf;
    }
    return 0;
}

#ifdef F_UNIQUE_ARRAY

typedef struct unique_s {
    svalue_t mark;
    int count;
    struct unique_s *next;
    int *indices;
} unique_t;

typedef struct unique_list_s {
    unique_t *head;
    struct unique_list_s *next;
} unique_list_t;

static unique_list_t *g_u_list = 0;

void unique_array_error_handler PROT((void)) {
    unique_list_t *unlist = g_u_list;
    unique_t *uptr = unlist->head, *nptr;

    g_u_list = g_u_list->next;
    while (uptr) {
	nptr = uptr->next;
	FREE((char *) uptr->indices);
	free_svalue(&uptr->mark, "unique_array_error_handler");
	FREE((char *) uptr);
	uptr = nptr;
    }
    FREE((char *)unlist);
}

void f_unique_array PROT((void)){
    array_t *v, *ret;
    int size, i, numkeys = 0, *ind, num_arg = st_num_arg;
    svalue_t *skipval, *sv, *svp;
    unique_list_t *unlist;
    unique_t **head, *uptr, *nptr;
    funptr_t *fp = 0;
    char *func;

    size = (v = (sp - num_arg + 1)->u.arr)->size;
    if (!size) {
	if (num_arg == 3) free_svalue(sp--, "f_unique_array");
	free_svalue(sp--, "f_unique_array");
	return;
    }

    if (num_arg == 3) {
	skipval = sp;
	if ((sp-1)->type == T_FUNCTION) fp = (sp-1)->u.fp;
	else func = (sp-1)->u.string;
    }
    else {
	skipval = &const0;
	if (sp->type == T_FUNCTION) fp = sp->u.fp;
	else func = sp->u.string;
    }

    unlist = ALLOCATE(unique_list_t, TAG_TEMPORARY, "f_unique_array:1");
    unlist->next = g_u_list;
    unlist->head = 0;
    head = &unlist->head;
    g_u_list = unlist;

    (++sp)->type = T_ERROR_HANDLER;
    sp->u.error_handler = unique_array_error_handler;
    
    for (i = 0; i < size; i++) {
	if (fp) {
	    push_svalue(v->item + i);
	    sv = call_function_pointer(fp, 1);
	} else if ((v->item + i)->type == T_OBJECT) {
	    sv = apply(func, (v->item + i)->u.ob, 0, ORIGIN_EFUN);
	} else sv = 0;

	if (sv && !sameval(sv, skipval)) {
	    uptr = *head;
	    while (uptr) {
		if (sameval(sv, &uptr->mark)) {
		    uptr->indices = RESIZE(uptr->indices, uptr->count + 1, int,
					   TAG_TEMPORARY, "f_unique_array:2");
		    uptr->indices[uptr->count++] = i;
		    break;
		}
		uptr = uptr->next;
	    }
	    if (!uptr) {
		numkeys++;
		uptr = ALLOCATE(unique_t, TAG_TEMPORARY, "f_unique_array:3");
		uptr->indices = ALLOCATE(int, TAG_TEMPORARY, "f_unique_array:4");
		uptr->count = 1;
		uptr->indices[0] = i;
		uptr->next = *head;
		assign_svalue_no_free(&uptr->mark, sv);
		*head = uptr;
	    }
	}
    }

    ret = allocate_empty_array(numkeys);
    uptr = *head;
    svp = v->item;
    while (numkeys--) {
	nptr = uptr->next;
	(sv = ret->item + numkeys)->type = T_ARRAY;
	sv->u.arr = allocate_empty_array(i = uptr->count);
	skipval = sv->u.arr->item + i;
	ind = uptr->indices;
	while (i--) {
	    assign_svalue_no_free(--skipval, svp + ind[i]);
	}
	FREE((char *)ind);
	free_svalue(&uptr->mark, "f_unique_array");
	FREE((char *)uptr);
	uptr = nptr;
    }

    unlist = g_u_list->next;
    FREE((char *)g_u_list);
    g_u_list = unlist;
    sp--;
    pop_n_elems(num_arg - 1);
    free_array(v);
    sp->u.arr = ret;
}

/*
 * End of Unique maker
 *************************
 */
#endif

/* Concatenation of two arrays into one
 */
array_t *add_array P2(array_t *, p, array_t *, r)
{
    int cnt, res;
    array_t *d;		/* destination */

    /*
     * have to be careful with size zero arrays because they could be
     * the_null_array.  REALLOC(the_null_array, ...) is bad :(
     */
    if (p->size == 0) {
	p->ref--;
	return r->ref > 1 ? (r->ref--, copy_array(r)) : r;
    }
    if (r->size == 0) {
	r->ref--;
	return p->ref > 1 ? (p->ref--, copy_array(p)) : p;
    }

    res = p->size + r->size;
    if (res < 0 || res > max_array_size)
      error("result of array addition is greater than maximum array size.\n");

    /* x += x */
    if ((p == r) && (p->ref == 2)) {
	d = RESIZE_ARRAY(p, res);
        if (!d)
	    fatal("Out of memory.\n");
        /* copy myself */
	for (cnt = d->size; cnt--;)
	    assign_svalue_no_free(&d->item[--res], &d->item[cnt]);
        total_array_size += sizeof(svalue_t) * (d->size);
#ifdef PACKAGE_MUDLIB_STATS
	/* mudlib_stats stuff */
	if (current_object) {
	    assign_stats(&d->stats, current_object);
	    add_array_size(&d->stats, d->size);
	} else {
	    null_stats(&d->stats);
	}
#endif
        d->ref = 1;
        d->size <<= 1;
	
        return d;
    }
    
    /* transfer svalues for ref 1 target array */
    if (p->ref == 1) {
	/*
	 * realloc(p) to try extending block; this will save an
	 * allocate_array(), copying the svalues over, and free()'ing p
	 */
	d = RESIZE_ARRAY(p, res);
	if (!d)
	    fatal("Out of memory.\n");

	total_array_size += sizeof(svalue_t) * (r->size);
	/* d->ref = 1;     d is p, and p's ref was already one -Beek */
	d->size = res;

#ifdef PACKAGE_MUDLIB_STATS
	/* mudlib_stats stuff */
	if (current_object) {
	    assign_stats(&d->stats, current_object);
	    add_array_size(&d->stats, r->size);
	} else {
	    null_stats(&d->stats);
	}
#endif
    } else {
	d = allocate_empty_array(res);

	for (cnt = p->size; cnt--;)
	    assign_svalue_no_free(&d->item[cnt], &p->item[cnt]);
	p->ref--;
    }

    /* transfer svalues from ref 1 source array */
    if (r->ref == 1) {
	for (cnt = r->size; cnt--;)
	    d->item[--res] = r->item[cnt];
#ifdef PACKAGE_MUDLIB_STATS
	add_array_size(&r->stats, -((int)r->size));
#endif
	num_arrays--;
	total_array_size -= sizeof(array_t) +
	    sizeof(svalue_t) * (r->size - 1);
	FREE((char *) r);
    } else {
	for (cnt = r->size; cnt--;)
	    assign_svalue_no_free(&d->item[--res], &r->item[cnt]);
	r->ref--;
    }

    return d;
}

#ifndef NO_ENVIRONMENT
/* Returns an array of all objects contained in 'ob'
 */
array_t *all_inventory P2(object_t *, ob, int, override)
{
    array_t *d;
    object_t *cur;
    int cnt, res;
    int display_hidden;

    if (override) {
	display_hidden = 1;
    } else {
	display_hidden = -1;
    }
    cnt = 0;
    for (cur = ob->contains; cur; cur = cur->next_inv) {
	if (cur->flags & O_HIDDEN) {
	    if (display_hidden == -1) {
		display_hidden = valid_hide(current_object);
	    }
	    if (display_hidden)
		cnt++;
	} else
	    cnt++;
    }

    if (!cnt)
	return null_array();

    d = allocate_empty_array(cnt);
    cur = ob->contains;

    for (res = 0; res < cnt; res++) {
	if ((cur->flags & O_HIDDEN) && !display_hidden) {
	    cur = cur->next_inv;
	    res--;
	    continue;
	}
	d->item[res].type = T_OBJECT;
	d->item[res].u.ob = cur;
	add_ref(cur, "all_inventory");
	cur = cur->next_inv;
    }
    return d;
}
#endif

/* Runs all elements of an array through ob::func
   and replaces each value in arr by the value returned by ob::func
   */
#ifdef F_MAP
void
map_array P2(svalue_t *, arg, int, num_arg)
{
    array_t *arr = arg->u.arr;
    array_t *r;
    int size;

    if ((size = arr->size) < 1) r = null_array();
    else {
	funptr_t *fp = 0;
	int numex = 0, cnt;
	object_t *ob;
	svalue_t *extra, *v;
	char *func;

	r = allocate_array(size);

	(++sp)->type = T_ARRAY;
	sp->u.arr = r;

	if (arg[1].type == T_FUNCTION) {
	    fp = arg[1].u.fp;
	    if (num_arg > 2) extra = arg + 2, numex = num_arg - 2;
	}
	else {
	    func = arg[1].u.string;
	    if (num_arg < 3) ob = current_object;
	    else{
		if (arg[2].type == T_OBJECT) ob = arg[2].u.ob;
		else if (arg[2].type == T_STRING){
		    if ((ob = find_object(arg[2].u.string)) && !object_visible(ob))
			ob = 0;
		}
		if (num_arg > 3) extra = arg + 3, numex = num_arg - 3;
		if (!ob) error("Bad argument 3 to map_array.\n");
	    }
	}
	    
	for (cnt = 0; cnt < size; cnt++){
	    push_svalue(arr->item + cnt);
	    if (numex) push_some_svalues(extra, numex);
	    v = fp ? call_function_pointer(fp, numex + 1) : apply(func, ob, 1 + numex, ORIGIN_EFUN);
	    if (v) assign_svalue_no_free(&r->item[cnt], v);
	    else break;
	}
	sp--;
    }

    pop_n_elems(num_arg);
    (++sp)->type = T_ARRAY;
    sp->u.arr = r;
}

void
map_string P2(svalue_t *, arg, int, num_arg)
{
    char *arr = arg->u.string;
    char *p;
    funptr_t *fp = 0;
    int numex = 0;
    object_t *ob;
    svalue_t *extra, *v;
    char *func;

    /* get a modifiable string */
    /* do not use arg after this; it has been copied or freed.
       Put our result string on the stack where it belongs in case of an
       error (note it is also in the right spot for the return value).
     */
    unlink_string_svalue(arg);
    arr = arg->u.string;

    if (arg[1].type == T_FUNCTION) {
	fp = arg[1].u.fp;
	if (num_arg > 2) extra = arg + 2, numex = num_arg - 2;
    }
    else {
	func = arg[1].u.string;
	if (num_arg < 3) ob = current_object;
	else{
	    if (arg[2].type == T_OBJECT) ob = arg[2].u.ob;
	    else if (arg[2].type == T_STRING){
		if ((ob = find_object(arg[2].u.string)) && !object_visible(ob))
		    ob = 0;
	    }
	    if (num_arg > 3) extra = arg + 3, numex = num_arg - 3;
	    if (!ob) error("Bad argument 3 to map_string.\n");
	}
    }

    for (p = arr; *p; p++) {
	push_number((unsigned char)*p);
	if (numex) push_some_svalues(extra, numex);
	v = fp ? call_function_pointer(fp, numex + 1) : apply(func, ob, 1 + numex, ORIGIN_EFUN);
	/* no function or illegal return value is unaltered.
	 * Anyone got a better idea?  A few idea:
	 * (1) insert strings? - algorithm needs changing
	 * (2) ignore them? - again, could require a realloc, since size would
	 *                    change
	 * (3) become ' ' or something
	 */
	if (!v) break;
	if (v->type == T_NUMBER && v->u.number != 0) *p = ((unsigned char)(v->u.number));
    }

    pop_n_elems(num_arg - 1);
    /* return value on stack */
}
#endif

static funptr_t *sort_array_cmp_funp;
static object_t *sort_array_cmp_ob;
static char *sort_array_cmp_func;

#define COMPARE_NUMS(x,y) (x < y ? -1 : (x > y ? 1 : 0))

array_t *builtin_sort_array P2(array_t *, inlist, int, dir)
{
    quickSort((char *) inlist->item, inlist->size, sizeof(inlist->item),
	      (dir<0) ? builtin_sort_array_cmp_rev : builtin_sort_array_cmp_fwd);

    return inlist;
}

INLINE static int builtin_sort_array_cmp_fwd P2(svalue_t *, p1, svalue_t *, p2)
{
    switch(p1->type | p2->type){
	case T_STRING:
	{
	    return strcmp(p1->u.string, p2->u.string);
	}

	case T_NUMBER:
	{
	    return COMPARE_NUMS(p1->u.number, p2->u.number);
	}

	case T_REAL:
	{
	    return COMPARE_NUMS(p1->u.real, p2->u.real);
	}

	case T_ARRAY:
	{
	    array_t *v1 = p1->u.arr, *v2 = p2->u.arr;
	    if (!v1->size  || !v2->size)
		error("Illegal to have empty array in array for sort_array()\n");


	    switch(v1->item->type | v2->item->type){
		case T_STRING:
		{
		    return strcmp(v1->item->u.string, v2->item->u.string);
		}

		case T_NUMBER:
		{
		    return COMPARE_NUMS(v1->item->u.number, v2->item->u.number);
		}

		case T_REAL:
		{
		    return COMPARE_NUMS(v1->item->u.real, v2->item->u.real);
		}
		default:
		{
		    /* Temp. long err msg till I can think of a better one - Sym */
		    error("sort_array() cannot handle arrays of arrays whose 1st elems\naren't strings/ints/floats\n");
		}
	    }
	}

    }
    error("built-in sort_array() can only handle homogeneous arrays of strings/ints/floats/arrays\n");
    return 0;
}

INLINE static int builtin_sort_array_cmp_rev P2(svalue_t *, p1, svalue_t *, p2)
{
    switch(p1->type | p2->type){
        case T_STRING:
        {
            return strcmp(p2->u.string, p1->u.string);
	}

        case T_NUMBER:
        {
            return COMPARE_NUMS(p2->u.number, p1->u.number);
	}

        case T_REAL:
        {
            return COMPARE_NUMS(p2->u.real, p1->u.real);
	}

        case T_ARRAY:
        {
            array_t *v1 = p1->u.arr, *v2 = p2->u.arr;
            if (!v1->size  || !v2->size)
                error("Illegal to have empty array in array for sort_array()\n");


            switch(v1->item->type | v2->item->type){
                case T_STRING:
                {
                    return strcmp(v2->item->u.string, v1->item->u.string);
		}

                case T_NUMBER:
                {
                    return COMPARE_NUMS(v2->item->u.number, v1->item->u.number);
		}

                case T_REAL:
                {
                    return COMPARE_NUMS(v2->item->u.real, v1->item->u.real);
		}
                default:
		{
		    /* Temp. long err msg till I can think of a better one - Sym */
		    error("sort_array() cannot handle arrays of arrays whose 1st elems\naren't strings/ints/floats\n");
		}
	    }
	}

    }
    error("built-in sort_array() can only handle homogeneous arrays of strings/ints/floats/arrays\n");
    return 0;
}

INLINE static
int sort_array_cmp P2(svalue_t *, p1, svalue_t *, p2) {
    svalue_t *d;

    push_svalue(p1);
    push_svalue(p2);

    if (sort_array_cmp_funp){
	d = call_function_pointer(sort_array_cmp_funp, 2);
    } else {	
	if (sort_array_cmp_ob->flags & O_DESTRUCTED)
	    error("object used by sort_array destructed\n");
	d = apply(sort_array_cmp_func, sort_array_cmp_ob, 2, ORIGIN_EFUN);
    }

    if (!d || d->type != T_NUMBER) {
	return 0;
    } else {
	return d->u.number;
    }
}

#ifdef F_SORT_ARRAY
void
f_sort_array PROT((void))
{
    svalue_t *arg = sp - st_num_arg + 1;
    array_t *tmp = arg->u.arr;
    int num_arg = st_num_arg;

    check_for_destr(tmp);
    tmp = copy_array(tmp);

    switch(arg[1].type){
	case T_NUMBER:
        {
	    tmp = builtin_sort_array(tmp, arg[1].u.number);
	    break;
	}

	case T_FUNCTION:
        {
	    sort_array_cmp_funp = arg[1].u.fp;
	    quickSort((char *) tmp->item, tmp->size, sizeof(tmp->item), sort_array_cmp);
	    break;
	}

	case T_STRING:
        {
	    sort_array_cmp_funp = 0;
	    sort_array_cmp_ob = 0;
	    sort_array_cmp_func = (arg+1)->u.string;
	    if (num_arg == 2) {
		sort_array_cmp_ob = current_object;
		if (sort_array_cmp_ob->flags & O_DESTRUCTED) sort_array_cmp_ob = 0;
	    } else if (arg[2].type == T_OBJECT)
		sort_array_cmp_ob = arg[2].u.ob;
	    else if (arg[2].type == T_STRING) {
		sort_array_cmp_ob = find_object(arg[2].u.string);
		if (sort_array_cmp_ob && !object_visible(sort_array_cmp_ob)) sort_array_cmp_ob = 0;
	    }
	    if (!sort_array_cmp_ob) bad_arg(3, F_SORT_ARRAY);
	    quickSort((char *) tmp->item, tmp->size, sizeof(tmp->item), sort_array_cmp);
	    break;
	}

    }
    
    pop_n_elems(num_arg);
    (++sp)->type = T_ARRAY;
    sp->u.arr = tmp;
}
#endif

/*
 * deep_inventory()
 *
 * This function returns the recursive inventory of an object. The returned
 * array of objects is flat, ie there is no structure reflecting the
 * internal containment relations.
 *
 * This is Robocoder's deep_inventory().  It uses two passes in order to
 * avoid costly temporary arrays (allocating, copying, adding, freeing, etc).
 * The recursive call routines are:
 *    deep_inventory_count() and deep_inventory_collect()
 */
#ifndef NO_ENVIRONMENT
static int valid_hide_flag;

static int deep_inventory_count P1(object_t *, ob)
{
    object_t *cur;
    int cnt;

    cnt = 0;

    /* step through object's inventory and count visible objects */
    for (cur = ob->contains; cur; cur = cur->next_inv) {
	if (cur->flags & O_HIDDEN) {
	    if (!valid_hide_flag)
		valid_hide_flag = 1 +
		    (valid_hide(current_object) ? 1 : 0);
	    if (valid_hide_flag & 2) {
		cnt++;
		cnt += deep_inventory_count(cur);
	    }
	} else {
	    cnt++;
	    cnt += deep_inventory_count(cur);
	}
    }

    return cnt;
}

static void deep_inventory_collect P3(object_t *, ob, array_t *, inv, int *, i)
{
    object_t *cur;

    /* step through object's inventory and look for visible objects */
    for (cur = ob->contains; cur; cur = cur->next_inv) {
	if (cur->flags & O_HIDDEN) {
	    if (valid_hide_flag & 2) {
		inv->item[*i].type = T_OBJECT;
		inv->item[*i].u.ob = cur;
		(*i)++;
		add_ref(cur, "deep_inventory_collect");

		deep_inventory_collect(cur, inv, i);
	    }
	} else {
	    inv->item[*i].type = T_OBJECT;
	    inv->item[*i].u.ob = cur;
	    (*i)++;
	    add_ref(cur, "deep_inventory_collect");

	    deep_inventory_collect(cur, inv, i);
	}
    }
}

array_t *deep_inventory P2(object_t *, ob, int, take_top)
{
    array_t *dinv;
    int i;

    valid_hide_flag = 0;

    /*
     * count visible objects in an object's inventory, and in their
     * inventory, etc
     */
    i = deep_inventory_count(ob);
    if (take_top)
	i++;

    if (i == 0)
	return null_array();

    /*
     * allocate an array
     */
    dinv = allocate_empty_array(i);
    if (take_top) {
	dinv->item[0].type = T_OBJECT;
	dinv->item[0].u.ob = ob;
	add_ref(ob, "deep_inventory");
    }
    /*
     * collect visible inventory objects recursively
     */
    i = take_top;
    deep_inventory_collect(ob, dinv, &i);

    return dinv;
}
#endif

INLINE static int alist_cmp P2(svalue_t *, p1, svalue_t *, p2)
{
    register int d;

    if ((d = p1->u.number - p2->u.number))
	return d;
    if ((d = p1->type - p2->type))
	return d;
    return 0;
}

INLINE static svalue_t *alist_sort P1(array_t *, inlist) {
    int size, j, curix, parix, child1, child2, flag;
    svalue_t *sv_tab, *tmp, *table, *sv_ptr, val;
    char *str;

    if (!(size = inlist->size)) return (svalue_t *)NULL;
    if ((flag = (inlist->ref > 1))) {
	sv_tab = CALLOCATE(size, svalue_t, TAG_TEMPORARY, "alist_sort: sv_tab");
	sv_ptr = inlist->item;
	for (j = 0; j < size; j++) {
	    if (((tmp = (sv_ptr + j))->type == T_OBJECT) && (tmp->u.ob->flags & O_DESTRUCTED)) {
		free_object(tmp->u.ob, "alist_sort");
		sv_tab[j] = *tmp = const0;
	    } else if ((tmp->type == T_STRING) && !(tmp->subtype == STRING_SHARED)) {
		sv_tab[j].u.string = make_shared_string(tmp->u.string);
		(tmp = sv_tab + j)->subtype = STRING_SHARED;
		tmp->type = T_STRING;
	    } else assign_svalue_no_free(sv_tab + j, tmp);

	    if ((curix = j)) {
		val = *tmp;

		do {
		    parix = (curix - 1) >> 1;
		    if (alist_cmp(sv_tab + parix, sv_tab + curix) > 0) {
			sv_tab[curix] = sv_tab[parix];
			sv_tab[parix] = val;
		    }
		} while ((curix = parix));
	    }
	}
    } else {
	sv_tab = inlist->item;
	for (j = 0; j < size; j++){
	    if (((tmp = (sv_tab + j))->type == T_OBJECT) && (tmp->u.ob->flags & O_DESTRUCTED)) {
		free_object(tmp->u.ob, "alist_sort");
		*tmp = const0;
	    } else if ((tmp->type == T_STRING) && !(tmp->subtype == STRING_SHARED)) {
		str = make_shared_string(tmp->u.string);
		free_string_svalue(tmp);
		tmp->u.string = str;
		tmp->subtype = STRING_SHARED;
	    }

	    if ((curix = j)) {
		val = *tmp;
	    
		do {
		    parix = (curix - 1) >> 1;
		    if (alist_cmp(sv_tab + parix, sv_tab + curix) > 0) {
			sv_tab[curix] = sv_tab[parix];
			sv_tab[parix] = val;
		    }
		} while ((curix = parix));
	    }
	}
    }

    table = CALLOCATE(size, svalue_t, TAG_TEMPORARY, "alist_sort: table");

    for (j = 0; j < size; j++) {
	table[j] = sv_tab[0];

	for (curix = 0; ; ) {
	    child1 = (curix << 1) + 1;
	    child2 = child1 + 1;
	    
	    if (child2 < size && sv_tab[child2].type != T_INVALID &&
		(sv_tab[child1].type == T_INVALID ||
		 alist_cmp(sv_tab+child1, sv_tab+child2) > 0)) {
		child1 = child2;
	    }
	    if (child1 < size && sv_tab[child1].type != T_INVALID) {
		sv_tab[curix] = sv_tab[child1];
		curix = child1;
	    } else break;
	}

	sv_tab[curix].type = T_INVALID;
    }

    if (flag) FREE((char *)sv_tab);
    return table;
}

array_t *subtract_array P2(array_t *, minuend, array_t *, subtrahend) {
    array_t *difference;
    svalue_t *source, *dest, *svt;
    int i, size, o, d, l, h, msize;

    if (!(size = subtrahend->size)) {
	subtrahend->ref--;
	return minuend->ref > 1 ? (minuend->ref--, copy_array(minuend)) : minuend;
    }
    if (!(msize = minuend->size)) {
	free_array(subtrahend);
	return &the_null_array;
    }
    svt = alist_sort(subtrahend);
    difference = ALLOC_ARRAY(msize);
    for (source = minuend->item, dest = difference->item, i = msize;
	 i--; source++) {

	l = 0;
	o = (h = size - 1) >> 1;

	if ((source->type == T_OBJECT) && (source->u.ob->flags & O_DESTRUCTED)) {
	    free_object(source->u.ob, "subtract_array");
	    *source = const0;
	} else if ((source->type == T_STRING) && !(source->subtype == STRING_SHARED)) {
	    svalue_t stmp = {T_STRING, STRING_SHARED};
	    
	    if (!(stmp.u.string = findstring(source->u.string))){
	        assign_svalue_no_free(dest++, source);
		continue;
	    }
	    while ((d = alist_cmp(&stmp, svt + o))) {
		if (d < 0) h = o - 1;
		else l = o + 1;
		if (l > h){
		    assign_svalue_no_free(dest++, source);
		    break;
		}
		o = (l + h) >> 1;
	    }
	    continue;
	}
	
	while ((d = alist_cmp(source, svt + o))) {
	    if (d < 0) h = o - 1;
	    else l = o + 1;
	    if (l > h) {
		assign_svalue_no_free(dest++, source);
		break;
	    }
	    o = (l + h) >> 1;
	}

    }
    i = size;
    while (i--) free_svalue(svt + i, "subtract_array");
    FREE((char *) svt);
    if (subtrahend->ref > 1) {
	subtrahend->ref--;
    } else {
#ifdef PACKAGE_MUDLIB_STATS
	add_array_size(&subtrahend->stats, -size);
#endif
	num_arrays--;
	total_array_size -= sizeof(array_t) + sizeof(svalue_t) *
	    (size - 1);
	FREE((char *) subtrahend);
    }
    free_array(minuend);
    msize = dest - difference->item;
    if (!msize) {
	FREE((char *)difference);
	return null_array();
    }
    difference = RESIZE_ARRAY(difference, msize);
    difference->size = msize;
    difference->ref = 1;
    total_array_size += sizeof(array_t) + sizeof(svalue_t[1]) * (msize - 1);
    num_arrays++;
#ifdef PACKAGE_MUDLIB_STATS
    if (current_object) {
	assign_stats(&difference->stats, current_object);
	add_array_size(&difference->stats, msize);
    } else {
	null_stats(&difference->stats);
    }
#endif
    return difference;
}

array_t *intersect_array P2(array_t *, a1, array_t *, a2) {
    array_t *a3;
    int d, l, j, i, a1s = a1->size, a2s = a2->size, flag;
    svalue_t *svt_1, *ntab, *sv_tab, *sv_ptr, val, *tmp;
    int curix, parix, child1, child2;
    
    if (!a1s || !a2s) {
	free_array(a1);
	free_array(a2);
	return null_array();
    }

    svt_1 = alist_sort(a1);
    if ((flag = (a2->ref > 1))) {
	sv_tab = CALLOCATE(a2s, svalue_t, TAG_TEMPORARY, "intersect_array: sv2_tab");
	sv_ptr = a2->item;
	for (j = 0; j < a2s; j++) {
	    if (((tmp = (sv_ptr + j))->type == T_OBJECT) && (tmp->u.ob->flags & O_DESTRUCTED)) {
		free_object(tmp->u.ob, "intersect_array");
		sv_tab[j] = *tmp = const0;
	    } else if ((tmp->type == T_STRING) && !(tmp->subtype == STRING_SHARED)) {
		sv_tab[j].u.string = make_shared_string(tmp->u.string);
		(tmp = sv_tab + j)->subtype = STRING_SHARED;
		tmp->type = T_STRING;
	    } else assign_svalue_no_free(sv_tab + j, tmp);
	    
	    if ((curix = j)) {
		val = *tmp;

		do {
		    parix = (curix - 1) >> 1;
		    if (alist_cmp(sv_tab + parix, sv_tab + curix) > 0) {
			sv_tab[curix] = sv_tab[parix];
			sv_tab[parix] = val;
		    }
		} while ((curix = parix));
	    } 
	}
    } else {
	char *str;

	sv_tab = a2->item;
	for (j = 0; j < a2s; j++) {
	    if (((tmp = (sv_tab + j))->type == T_OBJECT) && (tmp->u.ob->flags & O_DESTRUCTED)) {
		free_object(tmp->u.ob, "alist_sort");
		*tmp = const0;
	    } else if ((tmp->type == T_STRING) && !(tmp->subtype == STRING_SHARED)) {
		str = make_shared_string(tmp->u.string);
		free_string_svalue(tmp);
		tmp->u.string = str;
		tmp->subtype = STRING_SHARED;
	    }

	    if ((curix = j)) {
		val = *tmp;

		do {
		    parix = (curix - 1) >> 1;
		    if (alist_cmp(sv_tab + parix, sv_tab + curix) > 0) {
			sv_tab[curix] = sv_tab[parix];
			sv_tab[parix] = val;
		    }
		} while ((curix = parix));
	    } 	    
	}
    }

    a3 = ALLOC_ARRAY(a2s);
    ntab = a3->item;

    l = i = 0;

    for (j = 0; j < a2s; j++) {
	val = sv_tab[0];

	while ((d = alist_cmp(&val, &svt_1[i])) > 0) {
	    if (++i >= a1s) goto settle_business;
	}

	if (!d) {
	    ntab[l++] = val;
	} else {
	    free_svalue(&val, "intersect_array");
	}

	for (curix = 0;; ) {
	    child1 = (curix << 1) + 1;
	    child2 = child1 + 1;
	    
	    if (child2 < a2s && sv_tab[child2].type != T_INVALID &&
		(sv_tab[child1].type == T_INVALID ||
		 alist_cmp(sv_tab + child1, sv_tab + child2) > 0)) {
		child1 = child2;
	    }
	    
	    if (child1 < a2s && sv_tab[child1].type != T_INVALID) {
		sv_tab[curix] = sv_tab[child1];
		curix = child1;
	    } else break;
	}
	
	sv_tab[curix].type = T_INVALID;
    }

 settle_business:
    
    curix = a2s;
    while (curix--){
	if (sv_tab[curix].type != T_INVALID) free_svalue(sv_tab + curix, "intersect_array:2");
    }

    i = a1s;
    while (i--) free_svalue(svt_1 + i, "intersect_array");
    FREE((char *)svt_1);

    if (a1->ref > 1) a1->ref--;
    else {
#ifdef PACKAGE_MUDLIB_STATS
	add_array_size(&a1->stats, -a1s);
#endif
	num_arrays--;
	total_array_size -= sizeof(array_t) + sizeof(svalue_t) * (a1s - 1);
	FREE((char *) a1);
    }
    
    if (flag) {
	a2->ref--;
	FREE((char *) sv_tab);
    } else {
#ifdef PACKAGE_MUDLIB_STATS
	add_array_size(&a2->stats, -a2s);
#endif
	num_arrays--;
	total_array_size -= sizeof(array_t) + sizeof(svalue_t) * (a2s - 1);
	FREE((char *) a2);
    }
    a3 = RESIZE_ARRAY(a3, l);
    a3->ref = 1;
    a3->size = l;
#ifdef PACKAGE_MUDLIB_STATS
    if (current_object) {
	assign_stats(&a3->stats, current_object);
	add_array_size(&a3->stats, l);
    } else null_stats(&a3->stats);
#endif
    total_array_size += sizeof(array_t) + (l-1) * sizeof(svalue_t);
    num_arrays++;
    return a3;
}

int match_single_regexp P2(char *, str, char *, pattern) {
    struct regexp *reg;
    int ret;
    
    regexp_user = EFUN_REGEXP;
    reg = regcomp((unsigned char *)pattern, 0);
    if (!reg) error(regexp_error);
    ret = regexec(reg, str);
    FREE((char *)reg);
    return ret;
}

array_t *match_regexp P3(array_t *, v, char *, pattern, int, flag) {
    struct regexp *reg;
    char *res;
    int num_match, size, match = !(flag & 2);
    array_t *ret;
    svalue_t *sv1, *sv2;

    regexp_user = EFUN_REGEXP;
    if (!(size = v->size)) return null_array();
    reg = regcomp((unsigned char *)pattern, 0);
    if (!reg) error(regexp_error);
    res = (char *)DMALLOC(size, TAG_TEMPORARY, "match_regexp: res");
    sv1 = v->item + size;
    num_match = 0;
    while (size--){
        if (!((--sv1)->type == T_STRING) || (regexec(reg, sv1->u.string) != match)){
            res[size] = 0;
	} else {
	    res[size] = 1;
	    num_match++;
	}
    }
    
    flag &= 1;
    ret = allocate_empty_array(num_match << flag);
    sv2 = ret->item + (num_match << flag);
    size = v->size;
    while (size--){
        if (res[size]) {
            if (flag) {
                (--sv2)->type = T_NUMBER;
                sv2->u.number = size + 1;
	    }
            (--sv2)->type = T_STRING;
            sv1 = v->item + size;
	    *sv2 = *sv1;
	    if (sv1->subtype & STRING_COUNTED) {
		ADD_STRING(COUNTED_STRLEN(sv1->u.string));
		COUNTED_REF(sv1->u.string)++;
	    }
            if (!--num_match) break;
	}
    }
    FREE(res);
    FREE((char *) reg);
    return ret;
}

/*
 * Returns a list of all inherited files.
 *
 * Must be fixed so that any number of files can be returned, now max 256
 * (Sounds like a contradiction to me /Lars).
 */
array_t *deep_inherit_list P1(object_t *, ob)
{
    array_t *ret;
    program_t *pr, *plist[256];
    int il, il2, next, cur;

    plist[0] = ob->prog;
    next = 1;
    cur = 0;

    for (; cur < next && next < 256; cur++) {
	pr = plist[cur];
	for (il2 = 0; il2 < (int) pr->num_inherited; il2++)
	    plist[next++] = pr->inherit[il2].prog;
    }

    next--;
    ret = allocate_empty_array(next);

    for (il = 0; il < next; il++) {
	pr = plist[il + 1];
	ret->item[il].type = T_STRING;
	ret->item[il].subtype = STRING_SHARED;
	ret->item[il].u.string =
	    make_shared_string(pr->name);
    }
    return ret;
}

/*
 * Returns a list of the immediate inherited files.
 *
 */
array_t *inherit_list P1(object_t *, ob)
{
    array_t *ret;
    program_t *pr, *plist[256];
    int il, il2, next, cur;

    plist[0] = ob->prog;
    next = 1;
    cur = 0;

    pr = plist[cur];
    for (il2 = 0; il2 < (int) pr->num_inherited; il2++) {
	plist[next++] = pr->inherit[il2].prog;
    }

    next--;			/* don't count the file itself */
    ret = allocate_empty_array(next);

    for (il = 0; il < next; il++) {
	pr = plist[il + 1];
	ret->item[il].type = T_STRING;
	ret->item[il].subtype = STRING_SHARED;
	ret->item[il].u.string = make_shared_string(pr->name);
    }
    return ret;
}

array_t *
       children P1(char *, str)
{
    int i, j;
    int t_sz;
    int sl, ol;
    object_t *ob;
    object_t **tmp_children;
    array_t *ret;
    int display_hidden;
    char tmpbuf[MAX_OBJECT_NAME_SIZE];

    display_hidden = -1;
    if (!strip_name(str, tmpbuf, sizeof tmpbuf))
	return null_array();

    sl = strlen(tmpbuf);

    if (!(tmp_children = (object_t **)
	  DMALLOC(sizeof(object_t *) * (t_sz = 50),
		  TAG_TEMPORARY, "children: tmp_children"))) 
	return null_array();	/* unable to malloc enough temp space */

    for (i = 0, ob = obj_list; ob; ob = ob->next_all) {
	ol = strlen(ob->name);
	if (((ol == sl) || ((ol > sl) && (ob->name[sl] == '#')))
	    && !strncmp(tmpbuf, ob->name, sl)) {
	    if (ob->flags & O_HIDDEN) {
		if (display_hidden == -1) {
		    display_hidden = valid_hide(current_object);
		}
		if (!display_hidden)
		    continue;
	    }
	    tmp_children[i] = ob;
	    if ((++i == t_sz) && (!(tmp_children
			= RESIZE(tmp_children, (t_sz += 50), object_t *,
				 TAG_TEMPORARY, "children: tmp_children: realloc")))) {
		/* unable to REALLOC more space 
		 * (tmp_children is now NULL) */
		return null_array();
	    }
	}
    }
    if (i > max_array_size) {
	i = max_array_size;
    }
    ret = allocate_empty_array(i);
    for (j = 0; j < i; j++) {
	ret->item[j].type = T_OBJECT;
	ret->item[j].u.ob = tmp_children[j];
	add_ref(tmp_children[j], "children");
    }
    FREE((void *) tmp_children);
    return ret;
}

#ifdef F_LIVINGS
array_t *livings()
{
    int nob, apply_valid_hide, hide_is_valid = 0;
    object_t *ob, **obtab;
    array_t *vec;

    nob = 0;
    apply_valid_hide = 1;

    obtab = CALLOCATE(max_array_size, object_t *, TAG_TEMPORARY, "livings");

    for (ob = obj_list; ob != NULL; ob = ob->next_all) {
	if ((ob->flags & O_ENABLE_COMMANDS) == 0)
	    continue;
	if (ob->flags & O_HIDDEN) {
	    if (apply_valid_hide) {
		apply_valid_hide = 0;
		hide_is_valid = valid_hide(current_object);
	    }
	    if (hide_is_valid)
		continue;
	}
	if (nob == max_array_size)
	    break;
	obtab[nob++] = ob;
    }

    vec = allocate_empty_array(nob);
    while (--nob >= 0) {
	vec->item[nob].type = T_OBJECT;
	vec->item[nob].u.ob = obtab[nob];
	add_ref(obtab[nob], "livings");
    }

    FREE(obtab);

    return vec;
}
#endif

#ifdef F_OBJECTS
void f_objects PROT((void))
{
    char *func;
    object_t *ob, **tmp;
    array_t *ret;
    funptr_t *f = 0;
    int display_hidden = 0, t_sz, i,j, num_arg = st_num_arg;
    svalue_t *v;

    if (!num_arg) func = 0;
    else if (sp->type == T_FUNCTION) f = sp->u.fp;
    else func = sp->u.string;
	
    if (!(tmp = (object_t **)new_string( (t_sz = 1000)*sizeof(object_t *),
					"TMP: objects: tmp")))
	fatal("Out of memory!\n");

    push_malloced_string((char *)tmp);

    for (i = 0, ob = obj_list; ob; ob = ob->next_all) {
	if (ob->flags & O_HIDDEN) {
	    if (!display_hidden)
		display_hidden = 1 + !!valid_hide(current_object);
	    if (!(display_hidden & 2))
		continue;
	}
	if (f) {
	    push_object(ob);
	    v = call_function_pointer(f, 1);
	    if (!v){
		FREE_MSTR((char *)tmp);
		sp--;
		free_svalue(sp,"f_objects");
		*sp = const0;
		return;
	    }
	    if (v->type == T_NUMBER && !v->u.number) continue;
	} else if (func){
	    push_object(ob);
	    v = apply(func, current_object, 1, ORIGIN_EFUN);
            if (!v){
                FREE_MSTR((char *)tmp);
                sp--;
                free_svalue(sp,"f_objects");
                *sp = const0;
                return;
	    }
            if ((v->type == T_NUMBER) && !v->u.number) continue;
	}

	tmp[i] = ob;
	if (++i == t_sz) {
	    if (!(tmp = (object_t **)extend_string((char *)tmp, (t_sz += 1000)*sizeof(object_t *))))
		fatal("Out of memory!\n");
	    else
		sp->u.string = (char *) tmp;
	}
    }
    if (i > max_array_size)
	i = max_array_size;
    ret = allocate_empty_array(i);
    for (j = 0; j < i; j++) {
	ret->item[j].type = T_OBJECT;
	ret->item[j].u.ob = tmp[j];
	add_ref(tmp[j], "objects");
    }

    FREE_MSTR((char *)tmp);
    sp--;
    pop_n_elems(num_arg);
    (++sp)->type = T_ARRAY;
    sp->u.arr = ret;
}
#endif

#ifdef F_REG_ASSOC
/*
 * write(sprintf("%O", reg_assoc("testhahatest", ({ "haha", "te" }),
 *              ({ 2,3 }), 4)));

 * --------
 * ({
 *   ({ "", "te", "st", "haha", "", "te", "st" }),
 *   ({  4,    3,    4,      2,  4,    3,   4  })
 * })
 *
 */
array_t *reg_assoc P4(char *, str, array_t *, pat, array_t *, tok, svalue_t *, def) {
    int i, size;
    char *tmp;
    array_t *ret;
    
    regexp_user = EFUN_REGEXP;
    if ((size = pat->size) != tok->size)
	error("Pattern and token array sizes must be identical.\n");
    
    for (i = 0; i < size; i++) {
	if (!(pat->item[i].type == T_STRING))
	    error("Non-string found in pattern array.\n");
    }
    
    ret = allocate_empty_array(2);
    
    if (size) {
	struct regexp **rgpp;
	struct reg_match {
 	    int tok_i;
	    char *begin, *end;
	    struct reg_match *next;
 	} *rmp = (struct reg_match *) 0, *rmph = (struct reg_match *) 0;
	int num_match = 0, length;
	svalue_t *sv1, *sv2, *sv;
 	int index;
	struct regexp *tmpreg;
 	char *laststart, *currstart;
 
	rgpp = CALLOCATE(size, struct regexp *, TAG_TEMPORARY, "reg_assoc : rgpp");
	for (i = 0; i < size; i++){
             if (!(rgpp[i] = regcomp((unsigned char *)pat->item[i].u.string, 0))){
		 while (i--)
		     FREE((char *)rgpp[i]);
		 FREE((char *) rgpp);
		 error(regexp_error);
	     }
 	}
 
         tmp = str;
         while (*tmp){
 
             /* Sigh - need a kludge here - Randor */
             /* In the future I may alter regexp.c to include branch info */
             /* so as to minimize checks here - Randor 5/30/94 */
 
 	    laststart = 0;
 	    index = -1;
 
	    for (i = 0; i < size; i++){
		if (regexec(tmpreg = rgpp[i], tmp)){
		    currstart = tmpreg->startp[0];
		    if (tmp == currstart){
			index = i;
			break;
 		    }
		    if (!laststart || currstart < laststart){
			laststart = currstart;
 			index = i;
 		    }
 		}
 	    }
 
            if (index >= 0){
		num_match++;
		if (rmp){
		    rmp->next = ALLOCATE(struct reg_match, 
					 TAG_TEMPORARY, "reg_assoc : rmp->next");
		    rmp = rmp->next;
 		}
		else rmph = rmp =
		    ALLOCATE(struct reg_match, TAG_TEMPORARY, "reg_assoc : rmp");
		tmpreg = rgpp[index];
		rmp->begin = tmpreg->startp[0];
		rmp->end = tmp = tmpreg->endp[0];
		rmp->tok_i = index; 
		rmp->next = (struct reg_match *) 0;
 	    }
	    else break;
 
	    /* The following is from regexplode, to prevent i guess infinite */
	    /* loops on "" patterns - Randor 5/29/94 */
	    if (rmp->begin == tmp && (!*++tmp)) break;
 	}
 
	sv = ret->item;
	sv->type = T_ARRAY;
	sv1 = (sv->u.arr = allocate_empty_array(2*num_match + 1))->item;
	
	sv++;
	sv->type = T_ARRAY;
	sv2 = (sv->u.arr = allocate_empty_array(2*num_match + 1))->item;
	
	rmp = rmph;
	
	tmp = str;
	
	while (num_match--) {
	    char *svtmp;
	    
	    length = rmp->begin - tmp;
	    sv1->type = T_STRING;
            sv1->subtype = STRING_MALLOC;
	    svtmp = sv1->u.string = new_string(length, "reg_assoc : sv1");
	    strncpy(svtmp, tmp, length);
	    svtmp[length] = 0;
	    sv1++;
	    assign_svalue_no_free(sv2++, def);
	    tmp += length;
	    length = rmp->end - rmp->begin;
	    sv1->type = T_STRING;
	    sv1->subtype = STRING_MALLOC;
	    svtmp = sv1->u.string = new_string(length, "reg_assoc : sv1");
	    strncpy(svtmp, tmp, length);
	    svtmp[length] = 0;
	    sv1++;
	    assign_svalue_no_free(sv2++, &tok->item[rmp->tok_i]);
	    tmp += length;
	    rmp = rmp->next;
	}
	sv1->type = T_STRING;
	sv1->subtype = STRING_MALLOC;
	sv1->u.string = string_copy(tmp, "reg_assoc");
	assign_svalue_no_free(sv2, def);
	for (i=0; i<size; i++)
	    FREE((char *)rgpp[i]);
	FREE((char *) rgpp);
	
	while ((rmp = rmph)) {
	    rmph = rmp->next;
	    FREE((char *) rmp);
 	}
	return ret;
    }
    else { /* Default match */
	svalue_t *temp;
	svalue_t *sv;
	
	(sv = ret->item)->type = T_ARRAY;
	temp = (sv->u.arr = allocate_empty_array(1))->item;
	temp->subtype = STRING_MALLOC;
	temp->type = T_STRING;
	temp->u.string = string_copy(str, "reg_assoc");
	sv = &ret->item[1];
	sv->type = T_ARRAY;
	assign_svalue_no_free((sv->u.arr = allocate_empty_array(1))->item, def);
	return ret;
    }
}
#endif