/
dgd-net/
dgd-net/doc/
dgd-net/doc/kfun/
dgd-net/src/host/unix/
dgd-net/src/kfun/
# define INCLUDE_FILE_IO
# include "dgd.h"
# include "str.h"
# include "array.h"
# include "object.h"
# include "xfloat.h"
# include "interpret.h"
# include "data.h"
# include "path.h"
# include "ed.h"
# include "call_out.h"
# include "comm.h"
# include "version.h"
# include "macro.h"
# include "token.h"
# include "ppcontrol.h"
# include "node.h"
# include "parser.h"
# include "compile.h"
# include "csupport.h"
# include "table.h"

typedef struct {
    char *name;		/* name of the option */
    short type;		/* option type */
    bool set;		/* TRUE if option is set */
    long low, high;	/* lower and higher bound, for numeric values */
    union {
	long num;	/* numeric value */
	char *str;	/* string value */
    } u;
} config;

static config conf[] = {
# define ARRAY_SIZE	0
				{ "array_size",		INT_CONST, FALSE,
							1000, 8192 },
# define AUTO_OBJECT	1
				{ "auto_object",	STRING_CONST, FALSE },
# define BINARY_PORT	2
				{ "binary_port",	INT_CONST, FALSE,
							1000 },
# define CACHE_SIZE	3
				{ "cache_size",		INT_CONST, FALSE,
							100, UINDEX_MAX },
# define CALL_OUTS	4
				{ "call_outs",		INT_CONST, FALSE,
							0, UINDEX_MAX },
# define CALL_STACK	5
				{ "call_stack",		INT_CONST, FALSE,
							10 },
# define CREATE		6
				{ "create",		STRING_CONST, FALSE },
# define DIRECTORY	7
				{ "directory",		STRING_CONST, FALSE },
# define DRIVER_OBJECT	8
				{ "driver_object",	STRING_CONST, FALSE },
# define DUMP_FILE	9
				{ "dump_file",		STRING_CONST, FALSE },
# define DYNAMIC_CHUNK	10
				{ "dynamic_chunk",	INT_CONST, FALSE },
# define ED_TMPFILE	11
				{ "ed_tmpfile",		STRING_CONST, FALSE },
# define EDITORS	12
				{ "editors",		INT_CONST, FALSE,
							1, 255 },
# define INCLUDE_DIRS	13
				{ "include_dirs",	'(', FALSE },
# define INCLUDE_FILE	14
				{ "include_file",	STRING_CONST, FALSE },
# define MAX_COST	15
				{ "max_cost",		INT_CONST, FALSE,
							100000L },
# define OBJECTS	16
				{ "objects",		INT_CONST, FALSE,
							100 },
# define RESERVED_CSTACK 17
				{ "reserved_cstack",	INT_CONST, FALSE,
							5 },
# define RESERVED_VSTACK 18
				{ "reserved_vstack",	INT_CONST, FALSE,
							20 },
# define SECTOR_SIZE	19
				{ "sector_size",	INT_CONST, FALSE,
							512, 8192 },
# define STATIC_CHUNK	20
				{ "static_chunk",	INT_CONST, FALSE },
# define SWAP_FILE	21
				{ "swap_file",		STRING_CONST, FALSE },
# define SWAP_FRAGMENT	22
				{ "swap_fragment",	INT_CONST, FALSE },
# define SWAP_SIZE	23
				{ "swap_size",		INT_CONST, FALSE,
							1024, UINDEX_MAX },
# define TELNET_PORT	24
				{ "telnet_port",	INT_CONST, FALSE,
							1000 },
# define TYPECHECKING	25
				{ "typechecking",	INT_CONST, FALSE,
							0, 1 },
# define USERS		26
				{ "users",		INT_CONST, FALSE,
							1, 255 },
# define VALUE_STACK	27
				{ "value_stack",	INT_CONST, FALSE,
							100 },
# define NR_OPTIONS	28
};


typedef struct {
    char fill;		/* filler */
    char c;		/* char */
} calign;

typedef struct {
    char fill;		/* filler */
    short s;		/* short */
} salign;

typedef struct {
    char fill;		/* filler */
    Int i;		/* Int */
} ialign;

typedef struct {
    char fill;		/* filler */
    long l;		/* long */
} lalign;

typedef struct {
    char fill;		/* filler */
    char *p;		/* char* */
} palign;

typedef struct {	/* struct align */
    char c;
} align;


static char header[18];	/* dump file header */
static Uint starttime;	/* start time */
static Uint elapsed;	/* elapsed time */
static Uint boottime;	/* boot time */

/*
 * NAME:	conf->dump()
 * DESCRIPTION:	dump system state on file
 */
void conf_dump()
{
    Uint t[4];
    int fd;

    fd = sw_dump(conf[DUMP_FILE].u.str);
    if (!kf_dump(fd)) {
	fatal("failed to dump kfun table");
    }
    if (!o_dump(fd)) {
	fatal("failed to dump object table");
    }
    if (!co_dump(fd)) {
	fatal("failed to dump callout table");
    }

    header[2] = conf[TYPECHECKING].u.num;
    t[0] = conf[SECTOR_SIZE].u.num;
    t[1] = P_time();
    t[2] = starttime;
    t[3] = elapsed + t[1] - boottime;

    lseek(fd, 0L, SEEK_SET);
    write(fd, header, sizeof(header));
    write(fd, (char *) t, sizeof(t));
    P_message("*** State dumped.\012");
}

/*
 * NAME:	conf->restore()
 * DESCRIPTION:	restore system stare from file
 */
static void conf_restore(fd)
int fd;
{
    char buffer[sizeof(header)];
    Uint t[4];

    if (read(fd, buffer, sizeof(header)) != sizeof(header) ||
	buffer[0] != 1 ||
	memcmp(buffer + 2, header + 2, sizeof(header) - 2) != 0 ||
	read(fd, t, sizeof(t)) != sizeof(t)) {
	fatal("bad or incompatible restore file header");
    }
    t[1] = P_time() - t[1];
    starttime = t[2];
    elapsed = t[3];
    sw_restore(fd, (int) t[0]);
    kf_restore(fd);
    o_restore(fd, t[1]);
    co_restore(fd, t[1]);
}

/*
 * NAME:	conferr()
 * DESCRIPTION:	cause a fatal error during the configuration phase
 */
static void conferr(err)
char *err;
{
    fatal("config file, line %u: %s", tk_line(), err);
}

# define MAX_DIRS	32

/*
 * NAME:	config->init()
 * DESCRIPTION:	initialize the driver
 */
void conf_init(configfile, dumpfile)
char *configfile, *dumpfile;
{
    static char *nodir[1], *dirs[MAX_DIRS], buffer[STRINGSZ];
    register char *p;
    register int h, l, m, c;
    FILE *fp;
    short s;
    Int i;
    int fd;
    calign cdummy;
    salign sdummy;
    ialign idummy;
    lalign ldummy;
    palign pdummy;

    if (!pp_init(configfile, nodir, 0)) {
	fatal("cannot open config file");
    }
    while ((c=pp_gettok()) != EOF) {
	if (c != IDENTIFIER) {
	    conferr("option expected");
	}

	l = 0;
	h = NR_OPTIONS;
	for (;;) {
	    c = strcmp(yytext, conf[m = (l + h) >> 1].name);
	    if (c == 0) {
		break;	/* found */
	    } else if (c < 0) {
		h = m;	/* search in lower half */
	    } else {
		l = m + 1;	/* search in upper half */
	    }
	    if (l >= h) {
		conferr("unknown option");
	    }
	}

	if (pp_gettok() != '=') {
	    conferr("'=' expected");
	}

	if ((c=pp_gettok()) != conf[m].type) {
	    if (c != INT_CONST && c != STRING_CONST && c != '(') {
		conferr("syntax error");
	    } else {
		conferr("bad value type");
	    }
	}

	switch (c) {
	case INT_CONST:
	    if (yylval.number < conf[m].low ||
		(conf[m].high != 0 && yylval.number > conf[m].high)) {
		conferr("int value out of range");
	    }
	    conf[m].u.num = yylval.number;
	    break;

	case STRING_CONST:
	    p = (m == AUTO_OBJECT || m == DRIVER_OBJECT || m == INCLUDE_FILE) ?
		 path_resolve(yytext) : yytext;
	    if (conf[m].u.str != (char *) NULL) {
		FREE(conf[m].u.str);
	    }
	    l = strlen(p);
	    if (l >= STRINGSZ) {
		l = STRINGSZ - 1;
		p[l] = '\0';
	    }
	    mstatic();
	    conf[m].u.str = strcpy(ALLOC(char, l + 1), p);
	    mdynamic();
	    break;

	case '(':
	    if (pp_gettok() != '{') {
		conferr("'{' expected");
	    }
	    l = 0;
	    for (;;) {
		if (pp_gettok() != STRING_CONST) {
		    conferr("string expected");
		}
		if (l == MAX_DIRS - 1) {
		    conferr("too many include directories");
		}
		if (dirs[l] != (char *) NULL) {
		    FREE(dirs[l]);
		}
		mstatic();
		dirs[l++] = strcpy(ALLOC(char, strlen(yytext) + 1), yytext);
		mdynamic();
		if ((c=pp_gettok()) == '}') {
		    break;
		}
		if (c != ',') {
		    conferr("',' expected");
		}
	    }
	    if (pp_gettok() != ')') {
		conferr("')' expected");
	    }
	    dirs[l] = (char *) NULL;
	    break;
	}
	conf[m].set = TRUE;
	if (pp_gettok() != ';') {
	    conferr("';' expected");
	}
    }

    for (l = 0; l < NR_OPTIONS; l++) {
	char buf[128];

	if (!conf[l].set) {
	    sprintf(buf, "unspecified option %s", conf[l].name);
	    conferr(buf);
	}
    }
    pp_clear();

    if (dumpfile != (char *) NULL) {
	fd = open(dumpfile, O_RDONLY | O_BINARY);
	if (fd < 0) {
	    fatal("cannot open restore file");
	}
    }

    /* change directory */
    if (chdir(conf[DIRECTORY].u.str) < 0) {
	fatal("bad base directory \"%s\"", conf[DIRECTORY].u.str);
    }

    mstatic();

    /* initialize strings */
    str_init();

    /* initialize arrays */
    arr_init((int) conf[ARRAY_SIZE].u.num);

    /* initialize object */
    o_init((int) conf[OBJECTS].u.num);

    /* initialize swap */
    sw_init(conf[SWAP_FILE].u.str,
	    (int) conf[SWAP_SIZE].u.num,
	    (int) conf[CACHE_SIZE].u.num,
	    (int) conf[SECTOR_SIZE].u.num);

    /* initalize editor */
    ed_init(conf[ED_TMPFILE].u.str,
	    (int) conf[EDITORS].u.num);

    /* initialize call_outs */
    co_init((uindex) conf[CALL_OUTS].u.num,
	    (int) conf[SWAP_FRAGMENT].u.num);

    /* initialize kfuns */
    kf_init();

    /* initialize compiler */
    c_init(conf[AUTO_OBJECT].u.str,
	   conf[DRIVER_OBJECT].u.str,
	   conf[INCLUDE_FILE].u.str,
	   dirs,
	   (int) conf[TYPECHECKING].u.num);

    /* initialize interpreter */
    i_init((int) conf[VALUE_STACK].u.num,
	   (int) conf[RESERVED_VSTACK].u.num,
	   (int) conf[CALL_STACK].u.num,
	   (int) conf[RESERVED_CSTACK].u.num,
	   conf[CREATE].u.str);

    mdynamic();

    /* initialize memory manager */
    minit((unsigned int) conf[STATIC_CHUNK].u.num,
    	  (unsigned int) conf[DYNAMIC_CHUNK].u.num);

    /* create status.h file */
    sprintf(buffer, "%s/status.h", dirs[0]);
    fp = fopen(path_file(path_resolve(buffer)), "w");
    if (fp == (FILE *) NULL) {
	fatal("cannot create \"%s\"", buffer);
    }
    P_fbinio(fp);

    fprintf(fp, "/*\012 * This file defines the fields of the array returned ");
    fprintf(fp, "by the\012 * status() kfun.  It is automatically generated ");
    fprintf(fp, "by DGD on startup.\012 */\012\012");
    fprintf(fp, "# define ST_VERSION\t0\t/* driver version */\012");

    fprintf(fp, "# define ST_STARTTIME\t1\t/* system start time */\012");
    fprintf(fp, "# define ST_BOOTTIME\t2\t/* system boot time */\012");
    fprintf(fp, "# define ST_UPTIME\t3\t/* system virtual uptime */\012");

    fprintf(fp, "# define ST_SWAPSIZE\t4\t/* # sectors on swap device */\012");
    fprintf(fp, "# define ST_SWAPUSED\t5\t/* # sectors allocated */\012");
    fprintf(fp, "# define ST_SECTORSIZE\t6\t/* size of swap sector */\012");
    fprintf(fp, "# define ST_SWAPRATE1\t7\t/* # objects swapped out per minute */\012");
    fprintf(fp, "# define ST_SWAPRATE5\t8\t/* # objects swapped out per five minutes */\012");

    fprintf(fp, "# define ST_SMEMSIZE\t9\t/* static memory allocated */\012");
    fprintf(fp, "# define ST_SMEMUSED\t10\t/* static memory in use */\012");
    fprintf(fp, "# define ST_DMEMSIZE\t11\t/* dynamic memory allocated */\012");
    fprintf(fp, "# define ST_DMEMUSED\t12\t/* dynamic memory in use */\012");

    fprintf(fp, "# define ST_OTABSIZE\t13\t/* object table size */\012");
    fprintf(fp, "# define ST_NOBJECTS\t14\t/* # objects in the system */\012");

    fprintf(fp, "# define ST_COTABSIZE\t15\t/* callouts table size */\012");
    fprintf(fp, "# define ST_NCOSHORT\t16\t/* # short-term callouts */\012");
    fprintf(fp, "# define ST_NCOLONG\t17\t/* # long-term callouts */\012");

    fprintf(fp, "# define ST_UTABSIZE\t18\t/* user table size */\012");
    fprintf(fp, "# define ST_ETABSIZE\t19\t/* editor table size */\012");

    fprintf(fp, "\012# define O_COMPILETIME\t0\t/* time of compilation */\012");
    fprintf(fp, "# define O_PROGSIZE\t1\t/* program size of object */\012");
    fprintf(fp, "# define O_DATASIZE\t2\t/* data size of object */\012");
    fprintf(fp, "# define O_NSECTORS\t3\t/* # sectors used by object */\012");
    fprintf(fp, "# define O_CALLOUTS\t4\t/* callouts in object */\012");
    fclose(fp);

    /* create type.h file */
    sprintf(buffer, "%s/type.h", dirs[0]);
    fp = fopen(path_file(path_resolve(buffer)), "w");
    if (fp == (FILE *) NULL) {
	fatal("cannot create \"%s\"", buffer);
    }
    P_fbinio(fp);

    fprintf(fp, "/*\012 * This file gives definitions for the value returned ");
    fprintf(fp, "by the\012 * typeof() kfun.  It is automatically generated ");
    fprintf(fp, "by DGD on startup.\012 */\012\012");
    fprintf(fp, "# define T_INT\t\t%d\012", T_INT);
    fprintf(fp, "# define T_FLOAT\t%d\012", T_FLOAT);
    fprintf(fp, "# define T_STRING\t%d\012", T_STRING);
    fprintf(fp, "# define T_OBJECT\t%d\012", T_OBJECT);
    fprintf(fp, "# define T_ARRAY\t%d\012", T_ARRAY);
    fprintf(fp, "# define T_MAPPING\t%d\012", T_MAPPING);
    fclose(fp);

    /* create limits.h file */
    sprintf(buffer, "%s/limits.h", dirs[0]);
    fp = fopen(path_file(path_resolve(buffer)), "w");
    if (fp == (FILE *) NULL) {
	fatal("cannot create \"%s\"", buffer);
    }
    P_fbinio(fp);

    fprintf(fp, "/*\012 * This file defines some basic sizes of datatypes and ");
    fprintf(fp, "resources.\012 * It is automatically generated by DGD on ");
    fprintf(fp, "startup.\012 */\012\012");
    fprintf(fp, "# define CHAR_BIT\t\t8\t\t/* # bits in character */\012");
    fprintf(fp, "# define CHAR_MIN\t\t0\t\t/* min character value */\012");
    fprintf(fp, "# define CHAR_MAX\t\t255\t\t/* max character value */\012\012");
    fprintf(fp, "# define INT_MIN\t\t0x80000000\t/* -2147483648 */\012");
    fprintf(fp, "# define INT_MAX\t\t2147483647\t/* max integer value */\012\012");
    fprintf(fp, "# define MAX_STRING_SIZE\t%u\t\t/* max string size */\012",
	    USHRT_MAX - sizeof(string));
    fprintf(fp, "# define MAX_ARRAY_SIZE\t\t%ld\t\t/* max array size */\012",
	    conf[ARRAY_SIZE].u.num);
    fprintf(fp, "# define MAX_MAPPING_SIZE\t%ld\t\t/* max mapping size */\012\012",
	    conf[ARRAY_SIZE].u.num);
    fprintf(fp, "# define MAX_EXEC_COST\t\t%ld\t\t/* max execution cost */\012",
	    conf[MAX_COST].u.num);
    fclose(fp);

    /* create float.h file */
    sprintf(buffer, "%s/float.h", dirs[0]);
    fp = fopen(path_file(path_resolve(buffer)), "w");
    if (fp == (FILE *) NULL) {
	fatal("cannot create \"%s\"", buffer);
    }
    P_fbinio(fp);

    fprintf(fp, "/*\012 * This file describes the floating point type. It is ");
    fprintf(fp, "automatically\012 * generated by DGD on startup.\012 */\012\012");
    fprintf(fp, "# define FLT_RADIX\t2\t\t\t/* binary */\012");
    fprintf(fp, "# define FLT_ROUNDS\t1\t\t\t/* round to nearest */\012");
    fprintf(fp, "# define FLT_EPSILON\t7.2759576142E-12\t/* smallest x: 1.0 + x != 1.0 */\012");
    fprintf(fp, "# define FLT_DIG\t10\t\t\t/* decimal digits of precision*/\012");
    fprintf(fp, "# define FLT_MANT_DIG\t36\t\t\t/* binary digits of precision */\012");
    fprintf(fp, "# define FLT_MIN\t2.22507385851E-308\t/* positive minimum */\012");
    fprintf(fp, "# define FLT_MIN_EXP\t(-1021)\t\t\t/* minimum binary exponent */\012");
    fprintf(fp, "# define FLT_MIN_10_EXP\t(-307)\t\t\t/* minimum decimal exponent */\012");
    fprintf(fp, "# define FLT_MAX\t1.79769313485E+308\t/* positive maximum */\012");
    fprintf(fp, "# define FLT_MAX_EXP\t1024\t\t\t/* maximum binary exponent */\012");
    fprintf(fp, "# define FLT_MAX_10_EXP\t308\t\t\t/* maximum decimal exponent */\012");
    fclose(fp);

    /* preload compiled objects */
    pc_preload(conf[AUTO_OBJECT].u.str, conf[DRIVER_OBJECT].u.str);

    /* initialize dumpfile header */
    s = 0x1234;
    i = 0x12345678L;
    header[0] = 1;				/* valid dump flag */
    header[1] = 0;				/* editor flag */
    header[2] = conf[TYPECHECKING].u.num;	/* global typechecking */
    header[3] = sizeof(uindex);			/* sizeof uindex */
    header[4] = sizeof(long);			/* sizeof long */
    header[5] = sizeof(char *);			/* sizeof char* */
    header[6] = ((char *) &s)[0];		/* 1 of 2 */
    header[7] = ((char *) &s)[1];		/* 2 of 2 */
    header[8] = ((char *) &i)[0];		/* 1 of 4 */
    header[9] = ((char *) &i)[1];		/* 2 of 4 */
    header[10] = ((char *) &i)[2];		/* 3 of 4 */
    header[11] = ((char *) &i)[3];		/* 4 of 4 */
    header[12] = (char *) &cdummy.c - (char *) &cdummy.fill; /* char align */
    header[13] = (char *) &sdummy.s - (char *) &sdummy.fill; /* short align */
    header[14] = (char *) &idummy.i - (char *) &idummy.fill; /* int align */
    header[15] = (char *) &ldummy.l - (char *) &ldummy.fill; /* long align */
    header[16] = (char *) &pdummy.p - (char *) &pdummy.fill; /* pointer align */
    header[17] = sizeof(align);				     /* struct align */

    starttime = boottime = P_time();
    i_set_cost((Int) conf[MAX_COST].u.num);
    if (dumpfile == (char *) NULL) {
	/*
	 * initialize mudlib
	 */
	call_driver_object("initialize", 0);
    } else {
	/*
	 * restore dump file
	 */
	conf_restore(fd);
	close(fd);
	call_driver_object("restored", 0);
    }
    i_del_value(sp++);

    /* initialize communications */
    mstatic();
    comm_init((int) conf[USERS].u.num,
	      (int) conf[TELNET_PORT].u.num,
	      (int) conf[BINARY_PORT].u.num);
    mdynamic();
}

/*
 * NAME:	config->basedir()
 * DESCRIPTION:	return the driver base directory
 */
char *conf_base_dir()
{
    return conf[DIRECTORY].u.str;
}

/*
 * NAME:	config->driver()
 * DESCRIPTION:	return the driver object name
 */
char *conf_driver()
{
    return conf[DRIVER_OBJECT].u.str;
}

/*
 * NAME:	config->exec_cost()
 * DESCRIPTION:	return the maximum execution cost
 */
Int conf_exec_cost()
{
    return conf[MAX_COST].u.num;
}

/*
 * NAME:	config->status()
 * DESCRIPTION:	return an array with information about resource usage
 */
array *conf_status()
{
    register value *v;
    array *a;
    char *version;
    allocinfo *mstat;
    uindex ncoshort, ncolong;

    a = arr_new(20L);
    v = a->elts;

    /* version */
    v->type = T_STRING;
    version = VERSION;
    str_ref((v++)->u.string = str_new(version, (long) strlen(version)));

    /* uptime */
    v->type = T_INT;
    (v++)->u.number = starttime;
    v->type = T_INT;
    (v++)->u.number = boottime;
    v->type = T_INT;
    (v++)->u.number = elapsed + P_time() - boottime;

    /* swap */
    v->type = T_INT;
    (v++)->u.number = conf[SWAP_SIZE].u.num;
    v->type = T_INT;
    (v++)->u.number = sw_count();
    v->type = T_INT;
    (v++)->u.number = conf[SECTOR_SIZE].u.num;
    v->type = T_INT;
    (v++)->u.number = co_swaprate1();
    v->type = T_INT;
    (v++)->u.number = co_swaprate5();

    /* memory */
    mstat = minfo();
    v->type = T_INT;
    (v++)->u.number = mstat->smemsize;
    v->type = T_INT;
    (v++)->u.number = mstat->smemused;
    v->type = T_INT;
    (v++)->u.number = mstat->dmemsize;
    v->type = T_INT;
    (v++)->u.number = mstat->dmemused;

    /* objects */
    v->type = T_INT;
    (v++)->u.number = conf[OBJECTS].u.num;
    v->type = T_INT;
    (v++)->u.number = o_count();

    /* callouts */
    co_info(&ncoshort, &ncolong);
    v->type = T_INT;
    (v++)->u.number = conf[CALL_OUTS].u.num;
    v->type = T_INT;
    (v++)->u.number = ncoshort;
    v->type = T_INT;
    (v++)->u.number = ncolong;

    /* users & editors */
    v->type = T_INT;
    (v++)->u.number = conf[USERS].u.num;
    v->type = T_INT;
    v->u.number = conf[EDITORS].u.num;

    return a;
}

/*
 * NAME:	config->object()
 * DESCRIPTION:	return resource usage of an object
 */
array *conf_object(obj)
object *obj;
{
    register control *ctrl;
    register value *v;
    array *clist, *a;
    dataspace *data;

    clist = co_list(obj);
    ctrl = o_control(obj);
    data = o_dataspace(obj);
    a = arr_new(5L);

    v = a->elts;
    v->type = T_INT;
    (v++)->u.number = ctrl->compiled;
    v->type = T_INT;
    (v++)->u.number = ctrl->ninherits * sizeof(dinherit) +
		      ctrl->progsize +
		      ctrl->nstrings * (long) sizeof(dstrconst) +
		      ctrl->strsize +
		      ctrl->nfuncdefs * sizeof(dfuncdef) +
		      ctrl->nvardefs * sizeof(dvardef) +
		      ctrl->nfuncalls * 2L +
		      ctrl->nsymbols * (long) sizeof(dsymbol);
    v->type = T_INT;
    (v++)->u.number = ctrl->nvariables * sizeof(value);
    v->type = T_INT;
    if (obj->flags & O_MASTER) {
	(v++)->u.number = ctrl->nsectors + data->nsectors;
    } else {
	(v++)->u.number = data->nsectors;
    }
    v->type = T_ARRAY;
    arr_ref(v->u.array = clist);

    return a;
}