fbmuck-6.05/auto/
fbmuck-6.05/contrib/jresolver/
fbmuck-6.05/contrib/jresolver/org/
fbmuck-6.05/contrib/jresolver/org/fuzzball/
fbmuck-6.05/docs/devel/
fbmuck-6.05/game/
fbmuck-6.05/game/logs/
fbmuck-6.05/game/muf/
fbmuck-6.05/scripts/
fbmuck-6.05/src_docs/
/* Primitives package */

#include "copyright.h"
#include "config.h"

#include <sys/types.h>
#include <stdio.h>
#include <time.h>
#include "db.h"
#include "mcp.h"
#include "inst.h"
#include "externs.h"
#include "match.h"
#include "interface.h"
#include "params.h"
#include "tune.h"
#include "fbstrings.h"
#include "interp.h"
#include "mcpgui.h"
#include "array.h"
#include "mufevent.h"

static struct inst *oper1, *oper2, *oper3, *oper4;
static int result;


struct mcp_muf_context {
	dbref prog;
};


void
muf_mcp_context_cleanup(void *context)
{
	struct mcp_muf_context* mmc = (struct mcp_muf_context*)context;

	free(mmc);
}


void
muf_mcp_callback(McpFrame * mfr, McpMesg * mesg, McpVer version, void *context)
{
	struct mcp_muf_context* mmc = (struct mcp_muf_context*)context;
	dbref obj = mmc->prog;
	struct mcp_binding *ptr;
	McpArg *arg;
	char *pkgname = mesg->package;
	char *msgname = mesg->mesgname;
	dbref user;
	int descr;

	descr = mcpframe_to_descr(mfr);
	user = mcpframe_to_user(mfr);

	for (ptr = PROGRAM_MCPBINDS(obj); ptr; ptr = ptr->next) {
		if (ptr->pkgname && ptr->msgname) {
			if (!string_compare(ptr->pkgname, pkgname)) {
				if (!string_compare(ptr->msgname, msgname)) {
					break;
				}
			}
		}
	}

	if (ptr) {
		struct frame *tmpfr;
		stk_array *argarr;
		struct inst argname, argval, argpart;

		tmpfr = interp(descr, user, getloc(user), obj, -1, PREEMPT, STD_REGUID, 0);
		if (tmpfr) {
			oper1 = tmpfr->argument.st + --tmpfr->argument.top;
			CLEAR(oper1);
			argarr = new_array_dictionary();
			for (arg = mesg->args; arg; arg = arg->next) {
				if (!arg->value) {
					argval.type = PROG_STRING;
					argval.data.string = NULL;
				} else if (!arg->value->next) {
					argval.type = PROG_STRING;
					argval.data.string = alloc_prog_string(arg->value->value);
				} else {
					McpArgPart *partptr;
					int count = 0;

					argname.type = PROG_INTEGER;
					argval.type = PROG_ARRAY;
					argval.data.array =
							new_array_packed(mcp_mesg_arg_linecount(mesg, arg->name));

					for (partptr = arg->value; partptr; partptr = partptr->next) {
						argname.data.number = count++;
						argpart.type = PROG_STRING;
						argpart.data.string = alloc_prog_string(partptr->value);
						array_setitem(&argval.data.array, &argname, &argpart);
						CLEAR(&argpart);
					}
				}
				argname.type = PROG_STRING;
				argname.data.string = alloc_prog_string(arg->name);
				array_setitem(&argarr, &argname, &argval);
				CLEAR(&argname);
				CLEAR(&argval);
			}
			push(tmpfr->argument.st, &(tmpfr->argument.top), PROG_INTEGER, MIPSCAST & descr);
			push(tmpfr->argument.st, &(tmpfr->argument.top), PROG_ARRAY, MIPSCAST argarr);
			tmpfr->pc = ptr->addr;
			interp_loop(user, obj, tmpfr, 0);
		}
	}
}


struct mcpevent_context {
	int pid;
};


void
mcpevent_context_cleanup(void *context)
{
	struct mcpevent_context* mec = (struct mcpevent_context*)context;

	free(mec);
}


void
muf_mcp_event_callback(McpFrame * mfr, McpMesg * mesg, McpVer version, void *context)
{
	struct mcpevent_context* mec = (struct mcpevent_context*)context;
	int destpid = mec->pid;
	struct frame* destfr = timequeue_pid_frame(destpid);
	int descr = mcpframe_to_descr(mfr);
	char *pkgname = mesg->package;
	char *msgname = mesg->mesgname;
	McpArg *arg;

	if (destfr) {
		char buf[BUFFER_LEN];
		stk_array *argarr;
		stk_array *contarr;
		struct inst argname, argval, argpart;

		argarr = new_array_dictionary();
		for (arg = mesg->args; arg; arg = arg->next) {
			if (!arg->value) {
				argval.type = PROG_ARRAY;
				argval.data.array = NULL;
			} else {
				McpArgPart *partptr;
				int count = 0;

				argname.type = PROG_INTEGER;
				argval.type = PROG_ARRAY;
				argval.data.array =
						new_array_packed(mcp_mesg_arg_linecount(mesg, arg->name));

				for (partptr = arg->value; partptr; partptr = partptr->next) {
					argname.data.number = count++;
					argpart.type = PROG_STRING;
					argpart.data.string = alloc_prog_string(partptr->value);
					array_setitem(&argval.data.array, &argname, &argpart);
					CLEAR(&argpart);
				}
			}
			argname.type = PROG_STRING;
			argname.data.string = alloc_prog_string(arg->name);
			array_setitem(&argarr, &argname, &argval);
			CLEAR(&argname);
			CLEAR(&argval);
		}

		contarr = new_array_dictionary();
		array_set_strkey_intval(&contarr, "descr", descr);
		array_set_strkey_strval(&contarr, "package", pkgname);
		array_set_strkey_strval(&contarr, "message", msgname);

		argname.type = PROG_STRING;
		argname.data.string = alloc_prog_string("args");
		argval.type = PROG_ARRAY;
		argval.data.array = argarr;
		array_setitem(&contarr, &argname, &argval);
		CLEAR(&argname);
		CLEAR(&argval);

		argval.type = PROG_ARRAY;
		argval.data.array = contarr;
		if (msgname && *msgname) {
			snprintf(buf, sizeof(buf), "MCP.%.128s-%.128s", pkgname, msgname);
		} else {
			snprintf(buf, sizeof(buf), "MCP.%.128s", pkgname);
		}
		muf_event_add(destfr, buf, &argval, 0);
		CLEAR(&argval);
	}
}


int
stuff_dict_in_mesg(stk_array* arr, McpMesg* msg)
{
	struct inst argname, *argval;
	char buf[64];
	int result;
	
	result = array_first(arr, &argname);
	while (result) {
		if (argname.type != PROG_STRING) {
			CLEAR(&argname);
			return -1;
		}
		if (!argname.data.string || !*argname.data.string->data) {
			CLEAR(&argname);
			return -2;
		}
		argval = array_getitem(arr, &argname);
		switch (argval->type) {
		case PROG_ARRAY:{
				struct inst subname, *subval;
				int contd = array_first(argval->data.array, &subname);

				mcp_mesg_arg_remove(msg, argname.data.string->data);
				while (contd) {
					subval = array_getitem(argval->data.array, &subname);
					switch (subval->type) {
					case PROG_STRING:
						mcp_mesg_arg_append(msg, argname.data.string->data,
											DoNullInd(subval->data.string));
						break;
					case PROG_INTEGER:
						snprintf(buf, sizeof(buf), "%d", subval->data.number);
						mcp_mesg_arg_append(msg, argname.data.string->data, buf);
						break;
					case PROG_OBJECT:
						snprintf(buf, sizeof(buf), "#%d", subval->data.number);
						mcp_mesg_arg_append(msg, argname.data.string->data, buf);
						break;
					case PROG_FLOAT:
						snprintf(buf, sizeof(buf), "%.15g", subval->data.fnumber);
						mcp_mesg_arg_append(msg, argname.data.string->data, buf);
						break;
					default:
						CLEAR(&argname);
						return -3;
					}
					contd = array_next(argval->data.array, &subname);
				}
				break;
			}
		case PROG_STRING:
			mcp_mesg_arg_remove(msg, argname.data.string->data);
			mcp_mesg_arg_append(msg, argname.data.string->data,
								DoNullInd(argval->data.string));
			break;
		case PROG_INTEGER:
			snprintf(buf, sizeof(buf), "%d", argval->data.number);
			mcp_mesg_arg_remove(msg, argname.data.string->data);
			mcp_mesg_arg_append(msg, argname.data.string->data, buf);
			break;
		case PROG_OBJECT:
			snprintf(buf, sizeof(buf), "#%d", argval->data.number);
			mcp_mesg_arg_remove(msg, argname.data.string->data);
			mcp_mesg_arg_append(msg, argname.data.string->data, buf);
			break;
		case PROG_FLOAT:
			snprintf(buf, sizeof(buf), "%.15g", argval->data.fnumber);
			mcp_mesg_arg_remove(msg, argname.data.string->data);
			mcp_mesg_arg_append(msg, argname.data.string->data, buf);
			break;
		default:
			CLEAR(&argname);
			return -4;
		}
		result = array_next(arr, &argname);
	}
	return 0;
}


void
prim_mcp_register(PRIM_PROTOTYPE)
{
	char *pkgname;
	McpVer vermin, vermax;
	struct mcp_muf_context *mmc;

	CHECKOP(3);
	oper3 = POP();
	oper2 = POP();
	oper1 = POP();

	/* oper1  oper2   oper3  */
	/* pkgstr minver  maxver */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("Package name string expected. (1)");
	if (oper2->type != PROG_FLOAT)
		abort_interp("Floating point minimum version number expected. (2)");
	if (oper3->type != PROG_FLOAT)
		abort_interp("Floating point maximum version number expected. (3)");
	if (!oper1->data.string || !*oper1->data.string->data)
		abort_interp("Package name cannot be a null string. (1)");

	pkgname = oper1->data.string->data;
	vermin.vermajor = (int) oper2->data.fnumber;
	vermin.verminor = (int) (oper2->data.fnumber * 1000) % 1000;
	vermax.vermajor = (int) oper3->data.fnumber;
	vermax.verminor = (int) (oper3->data.fnumber * 1000) % 1000;

	mmc = (struct mcp_muf_context*)malloc(sizeof(struct mcp_muf_context));
	mmc->prog = program;

	mcp_package_register(pkgname, vermin, vermax, muf_mcp_callback, (void *) mmc, muf_mcp_context_cleanup);

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
}



void
prim_mcp_register_event(PRIM_PROTOTYPE)
{
	char *pkgname;
	McpVer vermin, vermax;
	struct mcpevent_context *mec;


	CHECKOP(3);
	oper3 = POP();
	oper2 = POP();
	oper1 = POP();

	/* oper1  oper2   oper3  */
	/* pkgstr minver  maxver */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("Package name string expected. (1)");
	if (oper2->type != PROG_FLOAT)
		abort_interp("Floating point minimum version number expected. (2)");
	if (oper3->type != PROG_FLOAT)
		abort_interp("Floating point maximum version number expected. (3)");
	if (!oper1->data.string || !*oper1->data.string->data)
		abort_interp("Package name cannot be a null string. (1)");

	pkgname = oper1->data.string->data;
	vermin.vermajor = (int) oper2->data.fnumber;
	vermin.verminor = (int) (oper2->data.fnumber * 1000) % 1000;
	vermax.vermajor = (int) oper3->data.fnumber;
	vermax.verminor = (int) (oper3->data.fnumber * 1000) % 1000;

	mec = (struct mcpevent_context*)malloc(sizeof(struct mcpevent_context));
	mec->pid = fr->pid;

	mcp_package_register(pkgname, vermin, vermax, muf_mcp_event_callback, (void *)mec, mcpevent_context_cleanup);

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
}



void
prim_mcp_supports(PRIM_PROTOTYPE)
{
	char *pkgname;
	int descr;
	McpVer ver;
	McpFrame *mfr;
	double fver;

	CHECKOP(2);
	oper2 = POP();
	oper1 = POP();

	/* oper1 oper2  */
	/* descr pkgstr */

	if (oper1->type != PROG_INTEGER)
		abort_interp("Integer descriptor number expected. (1)");
	if (oper2->type != PROG_STRING)
		abort_interp("Package name string expected. (2)");
	if (!oper2->data.string || !*oper2->data.string->data)
		abort_interp("Package name cannot be a null string. (2)");

	pkgname = oper2->data.string->data;
	descr = oper1->data.number;

	fver = 0.0;
	mfr = descr_mcpframe(descr);
	if (mfr) {
		ver = mcp_frame_package_supported(mfr, pkgname);
		fver = ver.vermajor + (ver.verminor / 1000.0);
	}

	CLEAR(oper1);
	CLEAR(oper2);

	PushFloat(fver);
}



void
prim_mcp_bind(PRIM_PROTOTYPE)
{
	char *pkgname, *msgname;
	struct mcp_binding *ptr;

	CHECKOP(3);
	oper3 = POP();
	oper2 = POP();
	oper1 = POP();

	/* oper1  oper2  oper3 */
	/* pkgstr msgstr address */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("Package name string expected. (1)");
	if (oper2->type != PROG_STRING)
		abort_interp("Message name string expected. (2)");
	if (oper3->type != PROG_ADD)
		abort_interp("Function address expected. (3)");
	if (!oper1->data.string || !*oper1->data.string->data)
		abort_interp("Package name cannot be a null string. (1)");
	if (oper3->data.addr->progref >= db_top ||
		oper3->data.addr->progref < 0 || (Typeof(oper3->data.addr->progref) != TYPE_PROGRAM)) {
		abort_interp("Invalid address. (3)");
	}
	if (program != oper3->data.addr->progref) {
		abort_interp("Destination address outside current program. (3)");
	}

	pkgname = oper1->data.string->data;
	msgname = oper2->data.string ? oper2->data.string->data : "";

	for (ptr = PROGRAM_MCPBINDS(program); ptr; ptr = ptr->next) {
		if (ptr->pkgname && ptr->msgname) {
			if (!string_compare(ptr->pkgname, pkgname)) {
				if (!string_compare(ptr->msgname, msgname)) {
					break;
				}
			}
		}
	}
	if (!ptr) {
		ptr = (struct mcp_binding *) malloc(sizeof(struct mcp_binding));

		ptr->pkgname = (char *) malloc(strlen(pkgname) + 1);
		strcpy(ptr->pkgname, pkgname);
		ptr->msgname = (char *) malloc(strlen(msgname) + 1);
		strcpy(ptr->msgname, msgname);

		ptr->next = PROGRAM_MCPBINDS(program);
		PROGRAM_SET_MCPBINDS(program, ptr);
	}
	ptr->addr = oper3->data.addr->data;

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
}


void
prim_mcp_send(PRIM_PROTOTYPE)
{
	char *pkgname, *msgname;
	int descr;
	McpFrame *mfr;
	stk_array *arr;

	CHECKOP(4);
	oper3 = POP();
	oper2 = POP();
	oper1 = POP();
	oper4 = POP();

	/* oper4 oper1  oper2  oper3 */
	/* descr pkgstr msgstr argsarray */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper4->type != PROG_INTEGER)
		abort_interp("Integer descriptor number expected. (1)");
	if (oper1->type != PROG_STRING)
		abort_interp("Package name string expected. (2)");
	if (!oper1->data.string || !*oper1->data.string->data)
		abort_interp("Package name cannot be a null string. (2)");
	if (oper2->type != PROG_STRING)
		abort_interp("Message name string expected. (3)");
	if (oper3->type != PROG_ARRAY)
		abort_interp("Arguments dictionary expected. (4)");

	pkgname = oper1->data.string->data;
	msgname = oper2->data.string ? oper2->data.string->data : "";
	arr = oper3->data.array;
	descr = oper4->data.number;

	mfr = descr_mcpframe(descr);
	if (mfr) {
		McpMesg msg;
		McpVer ver = mcp_frame_package_supported(mfr, pkgname);

		if (ver.verminor == 0 && ver.vermajor == 0)
			abort_interp("MCP package not supported by that descriptor.");

		mcp_mesg_init(&msg, pkgname, msgname);

		result = stuff_dict_in_mesg(arr, &msg);
		if (result) {
			mcp_mesg_clear(&msg);
			switch (result) {
				case -1:
					abort_interp("Args dictionary can only have string keys. (4)");
					break;
				case -2:
					abort_interp("Args dictionary cannot have a null string key. (4)");
					break;
				case -3:
					abort_interp("Unsupported value type in list value. (4)");
					break;
				case -4:
					abort_interp("Unsupported value type in args dictionary. (4)");
					break;
			}
		}
		
		mcp_frame_output_mesg(mfr, &msg);
		mcp_mesg_clear(&msg);
	}

	CLEAR(oper1);
	CLEAR(oper2);
}



void
fbgui_muf_event_cb(GUI_EVENT_CB_ARGS)
{
	char buf[BUFFER_LEN];
	const char *name;
	struct frame *fr = (struct frame *) context;
	struct inst temp;
	struct inst temp1;
	struct inst temp2;
	stk_array *nu;
	int i;
	int lines;

	nu = new_array_dictionary();
	name = GuiValueFirst(dlogid);
	while (name) {
		lines = gui_value_linecount(dlogid, name);

		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string(name);

		temp2.type = PROG_ARRAY;
		temp2.data.array = new_array_packed(lines);

		for (i = 0; i < lines; i++) {
			struct inst temp3;
			array_data temp4;

			temp3.type = PROG_INTEGER;
			temp3.data.number = i;

			temp4.type = PROG_STRING;
			temp4.data.string = alloc_prog_string(gui_value_get(dlogid, name, i));

			array_setitem(&temp2.data.array, &temp3, &temp4);
			CLEAR(&temp3);
			CLEAR(&temp4);
		}

		array_setitem(&nu, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);

		name = GuiValueNext(dlogid, name);
	}

	temp.type = PROG_ARRAY;
	temp.data.array = new_array_dictionary();

	array_set_strkey_intval(&temp.data.array, "dismissed", did_dismiss);
	array_set_strkey_intval(&temp.data.array, "descr", descr);
	array_set_strkey_strval(&temp.data.array, "dlogid", dlogid);
	array_set_strkey_strval(&temp.data.array, "id", id);
	array_set_strkey_strval(&temp.data.array, "event", event);

	temp2.type = PROG_ARRAY;
	temp2.data.array = nu;
	array_set_strkey(&temp.data.array, "values", &temp2);
	CLEAR(&temp2);

	lines = mcp_mesg_arg_linecount(msg, "data");
	if (lines > 0) {
		temp2.type = PROG_ARRAY;
		temp2.data.array = new_array_packed(lines);
		for (i = 0; i < lines; i++) {
			struct inst temp3;
			array_data temp4;

			temp3.type = PROG_INTEGER;
			temp3.data.number = i;

			temp4.type = PROG_STRING;
			temp4.data.string = alloc_prog_string(mcp_mesg_arg_getline(msg, "data", i));

			array_setitem(&temp2.data.array, &temp3, &temp4);
			CLEAR(&temp4);
		}
		array_set_strkey(&temp.data.array, "data", &temp2);
		CLEAR(&temp2);
	}

	/*
	if (did_dismiss) {
		muf_dlog_remove(fr, dlogid);
	}
	*/

	snprintf(buf, sizeof(buf), "GUI.%s", dlogid);
	muf_event_add(fr, buf, &temp, 0);
	CLEAR(&temp);
}



void
fbgui_muf_error_cb(GUI_ERROR_CB_ARGS)
{
	char buf[BUFFER_LEN];
	struct frame *fr = (struct frame *) context;
	struct inst temp;

	temp.type = PROG_ARRAY;
	temp.data.array = new_array_dictionary();

	array_set_strkey_intval(&temp.data.array, "descr",  descr);
	array_set_strkey_strval(&temp.data.array, "dlogid", dlogid);
	if (id) {
		array_set_strkey_strval(&temp.data.array, "id", id);
	}
	array_set_strkey_strval(&temp.data.array, "errcode", errcode);
	array_set_strkey_strval(&temp.data.array, "errtext", errtext);

	snprintf(buf, sizeof(buf), "GUI.%s", dlogid);
	muf_event_add(fr, buf, &temp, 0);
	CLEAR(&temp);
}



void
prim_gui_available(PRIM_PROTOTYPE)
{
	McpVer ver;
	double fver;

	CHECKOP(1);
	oper1 = POP();				/* descr */

	if (oper1->type != PROG_INTEGER)
		abort_interp("Integer descriptor number expected. (1)");

	ver = GuiVersion(oper1->data.number);
	fver = ver.vermajor + (ver.verminor / 1000.0);

	CLEAR(oper1);

	PushFloat(fver);
}



void
prim_gui_dlog_create(PRIM_PROTOTYPE)
{
	const char *dlogid = NULL;
	char *title = NULL;
	char *wintype = NULL;
	stk_array *arr = NULL;
	McpMesg msg;
	McpFrame *mfr;

	CHECKOP(4);
	oper4 = POP();				/* arr  args */
	oper3 = POP();				/* str  title */
	oper2 = POP();				/* str  type */
	oper1 = POP();				/* int  descr */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_INTEGER)
		abort_interp("Integer descriptor number expected. (1)");
	if (!GuiSupported(oper1->data.number))
		abort_interp("The MCP GUI package is not supported for this connection.");
	if (oper2->type != PROG_STRING)
		abort_interp("Window type string expected. (3)");
	if (oper3->type != PROG_STRING)
		abort_interp("Window title string expected. (3)");
	if (oper4->type != PROG_ARRAY)
		abort_interp("Window options array expected. (4)");

	title = oper3->data.string ? oper3->data.string->data : "";
	wintype = oper2->data.string ? oper2->data.string->data : "simple";
	arr = oper4->data.array;
	mfr = descr_mcpframe(oper1->data.number);

	if (!mfr)
		abort_interp("Invalid descriptor number. (1)");

	dlogid = gui_dlog_alloc(oper1->data.number, fbgui_muf_event_cb,
							fbgui_muf_error_cb, fr);
	mcp_mesg_init(&msg, GUI_PACKAGE, "dlog-create");
	mcp_mesg_arg_append(&msg, "title", title);

	result = stuff_dict_in_mesg(arr, &msg);
	if (result) {
		mcp_mesg_clear(&msg);
		switch (result) {
			case -1:
				abort_interp("Args dictionary can only have string keys. (4)");
				break;
			case -2:
				abort_interp("Args dictionary cannot have a null string key. (4)");
				break;
			case -3:
				abort_interp("Unsupported value type in list value. (4)");
				break;
			case -4:
				abort_interp("Unsupported value type in args dictionary. (4)");
				break;
		}
	}
	
	mcp_mesg_arg_remove(&msg, "type");
	mcp_mesg_arg_append(&msg, "type", wintype);
	mcp_mesg_arg_remove(&msg, "dlogid");
	mcp_mesg_arg_append(&msg, "dlogid", dlogid);

	mcp_frame_output_mesg(mfr, &msg);
	mcp_mesg_clear(&msg);
	muf_dlog_add(fr, dlogid);

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	CLEAR(oper4);

	PushString(dlogid);
}



void
prim_gui_dlog_show(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();				/* str  dlogid */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("String dialog ID expected.");
	if (!oper1->data.string || !oper1->data.string->data[0])
		abort_interp("Invalid dialog ID.");

	result = GuiShow(oper1->data.string->data);
	if (result == EGUINOSUPPORT)
		abort_interp("GUI not available.  Internal error.");
	if (result == EGUINODLOG)
		abort_interp("Invalid dialog ID.");

	CLEAR(oper1);
}


void
prim_gui_dlog_close(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();				/* str  dlogid */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("String dialog ID expected.");
	if (!oper1->data.string || !oper1->data.string->data[0])
		abort_interp("Invalid dialog ID.");

	result = GuiClose(oper1->data.string->data);
	if (result == EGUINOSUPPORT)
		abort_interp("Internal error: GUI not available.");
	if (result == EGUINODLOG)
		abort_interp("Invalid dialog ID.");

	muf_dlog_remove(fr, oper1->data.string->data);

	CLEAR(oper1);
}



void
prim_gui_ctrl_create(PRIM_PROTOTYPE)
{
	int vallines = 0;
	char **vallist = NULL;
	char *dlogid = NULL;
	char *ctrlid = NULL;
	char *ctrltype = NULL;
	char *valname = NULL;
	stk_array *arr;
	McpMesg msg;
	McpFrame *mfr;
	int descr;
	int i;
	char cmdname[64];

	CHECKOP(4);
	oper4 = POP();				/* dict args */
	oper3 = POP();				/* str  ctrlid */
	oper2 = POP();				/* str  type */
	oper1 = POP();				/* str  dlogid */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("Dialog ID string expected. (1)");
	if (!oper1->data.string || !oper1->data.string->data[0])
		abort_interp("Invalid dialog ID. (1)");

	if (oper2->type != PROG_STRING)
		abort_interp("Control type string expected. (2)");
	if (!oper2->data.string || !oper2->data.string->data[0])
		abort_interp("Invalid control type. (2)");

	if (oper3->type != PROG_STRING)
		abort_interp("Control ID string expected. (3)");

	if (oper4->type != PROG_ARRAY)
		abort_interp("Dictionary of arguments expected. (4)");

	dlogid = oper1->data.string->data;
	ctrltype = oper2->data.string->data;
	ctrlid = oper3->data.string ? oper3->data.string->data : NULL;
	arr = oper4->data.array;

	descr = gui_dlog_get_descr(dlogid);
	mfr = descr_mcpframe(descr);

	if (!mfr)
		abort_interp("No such dialog currently exists. (1)");

	if (!GuiSupported(descr))
		abort_interp("Internal error: The given dialog's descriptor doesn't support the GUI package. (1)");

	snprintf(cmdname, sizeof(cmdname), "ctrl-%.55s", ctrltype);
	mcp_mesg_init(&msg, GUI_PACKAGE, cmdname);

	result = stuff_dict_in_mesg(arr, &msg);
	if (result) {
		mcp_mesg_clear(&msg);
		switch (result) {
			case -1:
				abort_interp("Args dictionary can only have string keys. (4)");
				break;
			case -2:
				abort_interp("Args dictionary cannot have a null string key. (4)");
				break;
			case -3:
				abort_interp("Unsupported value type in list value. (4)");
				break;
			case -4:
				abort_interp("Unsupported value type in args dictionary. (4)");
				break;
		}
	}
	
	vallines = mcp_mesg_arg_linecount(&msg, "value");
	valname = mcp_mesg_arg_getline(&msg, "valname", 0);
	if (!valname || !*valname) {
		valname = ctrlid;
	}
	if (valname && vallines > 0) {
		vallist = (char**)malloc(sizeof(char*) * vallines);
		for (i = 0; i < vallines; i++)
			vallist[i] = mcp_mesg_arg_getline(&msg, "value", i);
		gui_value_set_local(dlogid, valname, vallines, (const char**)vallist);
		free(vallist);
	}

	mcp_mesg_arg_remove(&msg, "dlogid");
	mcp_mesg_arg_append(&msg, "dlogid", dlogid);

	mcp_mesg_arg_remove(&msg, "id");
	mcp_mesg_arg_append(&msg, "id", ctrlid);

	mcp_frame_output_mesg(mfr, &msg);
	mcp_mesg_clear(&msg);

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	CLEAR(oper4);
}


void
prim_gui_ctrl_command(PRIM_PROTOTYPE)
{
	char *dlogid = NULL;
	char *ctrlid = NULL;
	char *ctrlcmd = NULL;
	stk_array *arr;
	McpMesg msg;
	McpFrame *mfr;
	int descr;

	CHECKOP(4);
	oper4 = POP();				/* dict args */
	oper2 = POP();				/* str  command */
	oper3 = POP();				/* str  ctrlid */
	oper1 = POP();				/* str  dlogid */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("Dialog ID string expected. (1)");
	if (!oper1->data.string || !*oper1->data.string->data)
		abort_interp("Non-null dialog ID string expected. (1)");

	if (oper3->type != PROG_STRING)
		abort_interp("Control ID string expected. (2)");
	if (!oper3->data.string || !*oper3->data.string->data)
		abort_interp("Non-null control ID string expected. (2)");

	if (oper2->type != PROG_STRING)
		abort_interp("Control command string expected. (3)");
	if (!oper2->data.string || !*oper2->data.string->data)
		abort_interp("Non-null control command string expected. (3)");

	if (oper4->type != PROG_ARRAY)
		abort_interp("Dictionary of arguments expected. (4)");

	dlogid = oper1->data.string->data;
	ctrlcmd = oper2->data.string->data;
	ctrlid = oper3->data.string ? oper3->data.string->data : NULL;
	arr = oper4->data.array;

	descr = gui_dlog_get_descr(dlogid);
	mfr = descr_mcpframe(descr);

	if (!mfr)
		abort_interp("No such dialog currently exists. (1)");

	if (!GuiSupported(descr))
		abort_interp("Internal error: The given dialog's descriptor doesn't support the GUI package. (1)");

	mcp_mesg_init(&msg, GUI_PACKAGE, "ctrl-command");

	result = stuff_dict_in_mesg(arr, &msg);
	if (result) {
		mcp_mesg_clear(&msg);
		switch (result) {
			case -1:
				abort_interp("Args dictionary can only have string keys. (4)");
				break;
			case -2:
				abort_interp("Args dictionary cannot have a null string key. (4)");
				break;
			case -3:
				abort_interp("Unsupported value type in list value. (4)");
				break;
			case -4:
				abort_interp("Unsupported value type in args dictionary. (4)");
				break;
		}
	}
	
	mcp_mesg_arg_remove(&msg, "dlogid");
	mcp_mesg_arg_append(&msg, "dlogid", dlogid);

	mcp_mesg_arg_remove(&msg, "id");
	mcp_mesg_arg_append(&msg, "id", ctrlid);

	mcp_mesg_arg_remove(&msg, "command");
	mcp_mesg_arg_append(&msg, "command", ctrlcmd);

	mcp_frame_output_mesg(mfr, &msg);
	mcp_mesg_clear(&msg);

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	CLEAR(oper4);
}


void
prim_gui_value_set(PRIM_PROTOTYPE)
{
	int count;
	int i;
	char buf[BUFFER_LEN];
	char *name;
	char *dlogid;
	char *value;
	char **valarray;
	struct inst temp1;
	array_data *temp2;

	CHECKOP(3);
	oper3 = POP();				/* str value  */
	oper2 = POP();				/* str ctrlid */
	oper1 = POP();				/* str dlogid */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper3->type != PROG_STRING && oper3->type != PROG_ARRAY)
		abort_interp("String or string list control value expected. (3)");

	if (oper2->type != PROG_STRING)
		abort_interp("String control ID expected. (2)");
	if (!oper2->data.string || !oper2->data.string->data[0])
		abort_interp("Invalid dialog ID. (2)");

	if (oper1->type != PROG_STRING)
		abort_interp("String dialog ID expected. (1)");
	if (!oper1->data.string || !oper1->data.string->data[0])
		abort_interp("Invalid dialog ID. (1)");

	dlogid = oper1->data.string->data;
	name = oper2->data.string->data;

	if (oper3->type == PROG_STRING) {
		count = 1;
		valarray = (char **) malloc(sizeof(char *) * count);

		value = oper3->data.string ? oper3->data.string->data : "";
		valarray[0] = (char *) malloc(sizeof(char) * strlen(value) + 1);

		strcpy(valarray[0], value);
	} else {
		count = array_count(oper3->data.array);
		valarray = (char **) malloc(sizeof(char *) * count);

		for (i = 0; i < count; i++) {
			temp1.type = PROG_INTEGER;
			temp1.data.number = i;

			temp2 = array_getitem(oper3->data.array, &temp1);
			if (!temp2) {
				break;
			}
			switch (temp2->type) {
			case PROG_STRING:
				value = DoNullInd(temp2->data.string);
				break;
			case PROG_INTEGER:
				snprintf(buf, sizeof(buf), "%d", temp2->data.number);
				value = buf;
				break;
			case PROG_OBJECT:
				snprintf(buf, sizeof(buf), "#%d", temp2->data.number);
				value = buf;
				break;
			case PROG_FLOAT:
				snprintf(buf, sizeof(buf), "%.15g", temp2->data.fnumber);
				value = buf;
				break;
			default:
				while (i-- > 0) {
					free(valarray[i]);
				}
				free(valarray);
				abort_interp("Unsupported value type in list value. (3)");
			}
			valarray[i] = (char *) malloc(sizeof(char) * strlen(value) + 1);

			strcpy(valarray[i], value);
		}
	}

	GuiSetVal(dlogid, name, count, (const char**)valarray);

	while (count-- > 0) {
		free(valarray[count]);
	}
	free(valarray);

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
}


void
prim_gui_values_get(PRIM_PROTOTYPE)
{
	const char *name;
	char *dlogid;
	stk_array *nu;
	struct inst temp1;
	array_data temp2;

	CHECKOP(1);
	oper1 = POP();				/* str  dlogid */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("String dialog ID expected.");
	if (!oper1->data.string || !oper1->data.string->data[0])
		abort_interp("Invalid dialog ID.");

	dlogid = oper1->data.string->data;
	nu = new_array_dictionary();
	name = GuiValueFirst(oper1->data.string->data);
	while (name) {
		int i;
		int lines = gui_value_linecount(dlogid, name);

		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string(name);

		temp2.type = PROG_ARRAY;
		temp2.data.array = new_array_packed(lines);

		for (i = 0; i < lines; i++) {
			struct inst temp3;
			array_data temp4;

			temp3.type = PROG_INTEGER;
			temp3.data.number = i;

			temp4.type = PROG_STRING;
			temp4.data.string = alloc_prog_string(gui_value_get(dlogid, name, i));

			array_setitem(&temp2.data.array, &temp3, &temp4);
			CLEAR(&temp3);
			CLEAR(&temp4);
		}
		array_setitem(&nu, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);

		name = GuiValueNext(dlogid, name);
	}

	CLEAR(oper1);
	PushArrayRaw(nu);
}


void
prim_gui_value_get(PRIM_PROTOTYPE)
{
	char *dlogid;
	char *ctrlid;
	stk_array *nu;
	struct inst temp1;
	array_data temp2;
	int lines;
	int i;

	CHECKOP(2);
	oper2 = POP();				/* str  ctrlid */
	oper1 = POP();				/* str  dlogid */

	if (mlev < tp_mcp_muf_mlev)
		abort_interp("Permission denied.");
	if (oper1->type != PROG_STRING)
		abort_interp("String dialog ID expected. (1)");
	if (!oper1->data.string || !*oper1->data.string->data)
		abort_interp("Non-null string dialog ID expected. (1)");

	if (oper2->type != PROG_STRING)
		abort_interp("String control ID expected. (2)");
	if (!oper2->data.string || !*oper2->data.string->data)
		abort_interp("Non-null string control ID expected. (2)");

	dlogid = oper1->data.string->data;
	ctrlid = oper2->data.string->data;

	lines = gui_value_linecount(dlogid, ctrlid);
	nu = new_array_packed(lines);

	for (i = 0; i < lines; i++) {
		temp1.type = PROG_INTEGER;
		temp1.data.number = i;

		temp2.type = PROG_STRING;
		temp2.data.string = alloc_prog_string(gui_value_get(dlogid, ctrlid, i));

		array_setitem(&nu, &temp1, &temp2);

		CLEAR(&temp1);
		CLEAR(&temp2);
	}

	CLEAR(oper1);
	CLEAR(oper2);
	PushArrayRaw(nu);
}