/* cyber.c
 *      - square@cco.caltech.edu
 * adopted from the "old" tracer in lpmud < 2.x
 * - it can now support many-arguments calling (up to 6)
 * - argument could be any type, even array and map! (well, by a little trick)
 * - for <item>, file representation could have wildcards, as long as
 *   it is a unique file representation.
 * - the Recipient <item> of Call could be an array of objects
 *   (again by using wildcards)
 * - It's incorporated into lsc, becoming a programmable tracer
 *   (THE ULTIMATE CYBER EYE!)
 */
#include <mudlib.h>
/*
#include <lsc.h>
*/
#include "/u/s/square/lsc/lsc.h"
inherit OBJECT ;
/*
inherit LSC ;
*/
inherit "u/s/square/lsc/lsc2";

#define MAXARGS 6
string * query_list;
string * efun_list;

mixed * Parse_List(string str);
mixed parse_object_list(string str);
void assign(mixed var,mixed val);
static void disp(object ob);
int Call(string str);
string * init_efun();
print_ret(mixed ret, int layer);

string repeat_str(string ch, int n) {
	return sprintf("%'"+ch+"':*s",n,"");
}

string format_mixed(mixed data, int len) {
        int n;
        string tmp;

	if (undefinedp(data)) {
		tmp = "<null>";
		n = strlen(tmp);
		if (n>len) return repeat_str("*",len);
		return sprintf("%|:*s", len, tmp);
	}

        if (!data || intp(data) ) {
                tmp = data+" ";
                n = strlen(tmp);
                if (n>len) return repeat_str("*",len);
                return sprintf("%:*s", len, tmp);
        }

        if (stringp(data)) {
                n = strlen(data);
                if (n>len) return data[0..len-4]+" ..";
                return sprintf("%-:*s",len,data);
        }

        if (objectp(data)) {
                tmp = file_name(data);
                n = strlen(tmp);
                if (n>len) return tmp[0..len-4]+" ..";
                return sprintf("%-:*s",len,tmp);
        }

	if (mapp(data)) {
		tmp = "<mapp>";
                n = strlen(tmp);
                if (n>len) return repeat_str("*",len);
                return sprintf("%|:*s", len, tmp);
	}

	if (pointerp(data)) {
		tmp = "<array>";
                n = strlen(tmp);
                if (n>len) return repeat_str("*",len);
                return sprintf("%|:*s", len, tmp);
        }

        /* error */
        return repeat_str("X",len);
}

int Clean(string str) {
	object *oblist;
	mixed ob;
	int i,n;
	int all_done;

	all_done = 0;

	if (!str)
		return 0;
	ob = parse_object_list(str);
	if (!ob || !objectp(ob))
		return 0;
	while(!all_done) {
		all_done = 1;
		oblist = all_inventory(ob);
		n = sizeof(oblist);
		for(i=0;i<n;i++) {
			if (query_ip_number(oblist[i]))
				continue;
			oblist[i]->remove();
			all_done = 0;
		}
	}
	write("Ok.\n");
	return 1;
}

int Destruct(string str) {
	mixed mark;

	if (!str)
		return 0;
	mark = parse_object_list(str);
	if (!mark || !objectp(mark))
		return 0;
	mark->remove();
	if (mark) destruct(mark);
	write("Ok.\n");
	return 1;
}

int Trans(string str) {
	mixed mark;

	if (!str)
		return 0;
	mark = parse_object_list(str);
	if (!mark || !objectp(mark))
		return 0;
	mark->move(environment(this_player()));
	write("Ok.\n");
	return 1;
}

string *init_query() {
	if (query_list)
		return query_list;
	query_list = ({
		    "name",
		    "ghost","npc","wizard",
		    "email",
		    "hit_points", "max_hp", 
		    "spell_points", "max_sp",
		    "time_to_heal",
		    "race",
		    "armor_class",
		    "wealth",
		    "stat",
		    "weapon1", "weapon2",	
		    "armor",
		    "bulk", "mass", "capacity",
		    "invis",
		    "title", "type", "value",
		    "damage", "weapon",
		    "gender",
	}
	);
	return query_list;
}


static object find_item(object prev, string str) {
	object ob;
	string tmp;
	int num;

	if (str == "here")
		return environment(this_player());
	if (str=="me")
		return this_player();
	if (str=="EYE")
		return this_object();
	if (str == "^")
		return environment(prev);
	if (sscanf(str, "@%s", tmp) == 1)
		return find_living(tmp);
	if (sscanf(str, "*%s", tmp) == 1)
		return find_player(tmp);
	if (sscanf(str, "/%s", tmp) == 1 ||
	    sscanf(str, "~%s", tmp) == 1) {
		string *matched, file;
		int n;
		if (str[0]=='/' && (ob=find_object(str))) return ob;
		if (sscanf(str,"%s#%d",file,n)==2) {
			matched = (string *)this_player()->wild_card(file+".c");
			if (sizeof(matched)!=1) {
				write("Ambiguous\n");
				return 0;
			}
			return find_object(matched[0][0..strlen(matched[0])-3]+
				"#"+n);
		}
		matched=(string *) this_player()->wild_card(str+".c");
		if (sizeof(matched)!=1) {
			write("Ambiguous.\n");
			return 0;
		}
		call_other(matched[0],"??");
		return find_object(matched[0]);
	}
	if (sscanf(str, "$%d", num) == 1) {
		object * u;
		u = users();
		write("size: " + sizeof(u) + "\n");
		if (num >= sizeof(u) || num < 0)
			return 0;
		return u[num - 1];
	}
	if (sscanf(str, "$%s", tmp) == 1) {
		return _varlist[tmp];
	}
	if (prev == 0)
		prev = environment(this_player());
	if (sscanf(str, "\"%s\"", tmp) == 1) {
		ob = first_inventory(prev);
		while(ob && (string) ob->query("short") != tmp) {
			ob = next_inventory(ob);
		}
		return ob;
	}
	if (sscanf(str, "#%d", num) == 1) {
		if (prev == 0)
			return 0;
		ob = first_inventory(prev);
		while(num > 1) {
			num -= 1;
			if (ob == 0)
				return 0;
			ob = next_inventory(ob);
		}
		return ob;
	}

	/* this part is adopted from the code of huthar@tmi */

	if(ob = present(str, prev)) return ob;
	tmp = resolv_path( (string)this_player()->get_path(), str);
	if(file_size(tmp+".c") < 0)
		return 0;
	call_other(tmp,"???"); /* Force load */
	return find_object(tmp);

}

mixed parse_object_list(string str) {
	string tmp, rest;
	object prev;

	prev = environment(this_player());
	while(prev && str) {
		if (sscanf(str, "%s:%s", tmp, rest) == 2) {
			prev = find_item(prev, tmp);
			str = rest;
			continue;
		}
		prev = find_item(prev, str);
		break;
	}
	assign("$", prev);
	if (objectp(prev))
		disp(prev);
	return prev;
}

static void disp(object ob) {
	write(file_name(ob) + "\n");
}

int Goto(string str) {
	mixed mark;

	if (!str)
		return 0;
	mark = parse_object_list(str);
	if (!mark || !objectp(mark))
		return 0;
	say((string)this_player()->query_mmout()+".\n");
	this_player()->move(mark);
	say((string)this_player()->query_mmin()+".\n");
	write("Ok.\n");
	return 1;
}

int Efun(string str) {
	string *words;
	if (!str) return 0;
	words = explode(str+" "," ");
	if (member_array(words[0],efun_list)!=-1) {
		words[0] = capitalize(words[0]);
		return Call( "EYE "+implode(words," ") );
	}
	write("Efun "+words[0]+" is not supported\n");
	return 1;
}

int In(string str) {
	string path, cmd;
	object old_ob;
	mixed ob;
	if (!str)
		return 0;
	if (sscanf(str, "%s %s", path, cmd) != 2)
		return 0;
	ob = parse_object_list(path);
	if (!ob || !objectp(ob))
		return 0;
	old_ob = environment(this_player());
	this_player()->move(ob);
	seteuid(getuid(this_player()));
	this_player()->force_me(cmd);
	this_player()->move(old_ob);
	return 1;
}

int Anew(string str) {
	string file, error;
	int num;
	object newob, original;

	if (sscanf(file_name(this_object()),"%s#%d",file,num)!=2) return 0;
	seteuid(getuid(this_player()));
	original = find_object(file);
	if (original) destruct(original);
	error = catch(call_other(file,"???"));
	if (error) {
		write(error+"\n");
		return 1;
	}
	newob = new(file);
	newob->move(this_player());
	write("The cyber refreshes itself.\n");
	::remove();
	return 1;
}


int move(object dest) {
	if (wizardp(dest)) return ::move(dest);
	return 1;
}

void init() {
	if (!wizardp(this_player())) destruct(this_object());
	add_action("help", "help");
	add_action("Dump", "Dump");
	add_action("Destruct", "Destruct");
	add_action("Call", "Call");
	/* Tell could be accomplished by
	   Call obj catch_tell ... */
	add_action("Trans", "Trans");
	add_action("Set", "Set");
	add_action("Goto", "Goto");
	add_action("In", "In");
	add_action("Clean", "Clean");
	add_action("Efun", "Efun");
	add_action("Extract", "Extract");
	add_action("Anew", "Anew");
	add_action("compile", "Run");
	add_action("compile", ">");
	add_action("lighton","light");
}

int help(string str) {
	if (str == "eye") {
		write("Commands available:\n\n");
		write("Goto <item>, Dump [<item> [list]],\n"+
			"Destruct <item>, Trans <item>, In <item> command,\n"+
			"Clean <item>, Anew,\n"+
			"Call <item> <function> [arg1] [arg2] ...\n"+
			"Set <var_name> <arg>\n"+
			"Efun <efun_name> [arg1] [arg2]...\n"+
			"Extract <varname> <from> [to]\n"+
			"Run <LSC language> (or use '>' instead of 'Run'\n");
		write("DO 'help eye commands|item|arg' FOR MORE INFORMATION\n");
		return 1;
	}
	if (str == "eye funcation" || str == "eye <function>") {
		write("see 'help eye Call'\n");
		return 1;
	}
	if (str == "eye [arg]" || str == "eye arg" ||
	    str == "eye argument") {
		write("arguments are in the form:\n"+
			"   *<item>,\n"+
			"   string_with_no_space,\n"+
			"   \"string with spaces\",\n"+
			"   integer, or\n"+
			"   { argument_1 argument_2 ... }\n");
		return 1;
	}
	if (str == "eye item" || str == "eye <item>" ) {
	      write("An item is a list separated by ':'. An item in that e\n");
		write("@name\tName of a player or monster.\n");
		write("*name\tName of player only. Can be invisible.\n");
		write("/obj or ~obj\tfile name of an object.\n");
		write("   IMPORTANT: file name can have \"#%d\""+
			" and wildcards \"*\".\n");
		write("$var\tContents of a variable.\n");
		write("   ($$ will give last item used)\n");
		write("$num\tPlayer number 'num' in users() array.\n");
		write("here\tThis room.\n");
		write("#num\tThe 'num'th inventory inside this room or\n");
		write("   the item surrounding it by a ':'\n");
		write("id\tName of an item.\n");
		write("me\tYourself.\n");

		return 1;
	}
	if (str == "eye Call") {
		write("e.g:\n"+
		   "Call *square:#1 testing_function 1 \"hello world\"\n"+
		   "  This command looks for the first inventory inside\n"+
		   "  the player named 'square' and call the function\n"+
		   "  'testing_function' inside it with arguments: integer 1\n"+
		   "  and string \"hello world\"\n");
		write("Call *square:#1 move **deadman\n"+
		  "  This command calls the move() function of square's\n"+
		  "  1st inventory with the argument: object (deadman's obj)\n"+		  "   [NOTE: this normally teleports the thing to deadman's\n"+
		  "    inventory]\n");
		write("Call ~sq*/c* foobar {1 2 \"three four\"}\n"+
		  "  This command calls ALL objects with matched filename\n"+
		  "  EXCEPT cloned objects (with \"#dddd\" suffix)\n"+
		  "  Though not recommended for massive calling, it may come\n"+
		  "  in handy when you're dealing with long filenames\n");
		return 1;
	}
	if (str == "eye Dump") {
		write("e.g:\n"+
		  "Dump me list\n"+
		  "  This display every inventory of the command giver.\n"+
		  "Dump\n"+
		  "  This shows the variables remembered by the eye.\n");
		return 1;
	}
	if (str == "eye Destruct") {
		write("Destruct object specified by <item>.\n"+
			"See also 'help eye item'\n");
		return 1;
	}
	if (str == "eye Goto") {
		write("Goto the <item>'s inventory.\n"+
			"See also 'help eye item'\n");
		return 1;
	}
	if (str == "eye Trans") {
		write("Summon the <item> to your environment.\n"+
			"See also 'help eye item'\n");
		return 1;
	}
	if (str == "eye In") {
		write("Perform the <command> inside <item>.\n"+
			"See also 'help eye item'\n");
		return 1;
	}
	if (str == "eye Clean") {
		write("Destruct all non-interactive objs inside <item>.\n"+
			"See also 'help eye item'\n");
		return 1;
	}
	if (str == "eye Set") {
		write("e.g:\n"+
		  "Set one 1\n"+
		  "Set home *~square/workroom\n"+
		  "  This 2 commands set up 2 variables,'one' and 'home'\n"+
		  "  So, when you need to do something like:\n"+
		  "     Call dummy_object test_function 1 *~square/workroom\n"+
		  "  you can as well do this:\n"+
		  "     Call dummy_object test_function $one $home\n");
		return 1;
	}
	if (str == "eye Efun") {
		int i,n;
		n = sizeof(efun_list);
		write("Efun is just like Call, but it calls efuns\n"+
			"instead of object-declared functions.\n"+
			"FUNCTIONS SUPPORTED RIGHT NOW:\n");
		for(i=0;i<n;i++) {
			write(efun_list[i]+"\n");
		}       
		return 1;
	}
	if (str == "eye Extract") {
		write("Manipulate array variables\n");
		return 1;
	}
	if (str == "eye Run" || str == "eye >") {
		write("e.g:\n"+
		"Run /! {dup dup 1 le {pop pop 1} {1 sub ! mul} ifelse } def\n"+
		"  This defines a recursive factorial function, so you may\n"+
		"  then have:\n"+
		"Run 4 ! say\n"+
		"  which tells you the result to be 24\n");
		return 1;
	}
	return 0;
}

varargs int Dump(string str) {
	int tmp, i,j,n;
	object tmpob;
	string flag, path, * ky;
	string tmpstr;
	string qu;
	mixed ob, tmpmixed;

	if (!str) {
		write("All variables:\n");
		ky = keys(_varlist);
		n = sizeof(ky);
		for (i=0;i<n;i++) {
			printf("%-:10s %s",ky[i],
			    format_mixed(_varlist[ky[i]],30));
			if (objectp(_varlist[ky[i]])) {
				write(format_mixed(getuid(_varlist[ky[i]]),
					15)+" "+
				    format_mixed(geteuid(_varlist[ky[i]]),15) );
			}
			write("\n");
		}
		return 1;
	}

	if (sscanf(str, "%s %s", path, flag) != 2)
		path = str;
	ob = parse_object_list(path);
	if (!ob) return 0;
	if (!objectp(ob) ) {
		print_ret(ob,0);
		return 1;
	}
	if (flag == "list") {
		ob = first_inventory(ob);
		if (!ob) {
			write("Nothing in it\n");
			return 1;
		}
		write(format_mixed("#",3)+format_mixed("file name",30) +
		    format_mixed("short desc",20)+format_mixed("uid",10)+
		    format_mixed("euid",10)+"\n");
		while(ob) {
			i += 1;
			write( format_mixed(i + ":", 3) );
			write( format_mixed(ob, 30) );
			write( format_mixed((string)
				ob->query("short"), 20));
			write( format_mixed( getuid(ob),10)+
			    format_mixed(geteuid(ob),10) +"\n" );
			ob = next_inventory(ob);
		}
		return 1;
	}
	write(file_name(ob));
	write(":\n");
	write((string) ob->query("short") );
	if (living(ob))
		write(" (living) ");
	if (tmpstr = query_ip_number(ob))
		write("(interactive) '" + query_ip_number(ob) + "' ");
	write("\n");
	if (tmpstr)
		write("query_idle:\t\t" + query_idle(ob) +"@"
			+query_ip_name(ob)+"\n");
	tmpob = query_snoop(ob);
	if (tmpob)
		write("Snooped by " + format_mixed(tmpob,20) + "\n");
	for(i=0;i<sizeof(query_list);i++) {
		tmpmixed = (mixed) ob->query(query_list[i]);
		if (!undefinedp(tmpmixed)) {
			write(format_mixed(query_list[i]+":",25));
			print_ret(tmpmixed,1);
		}
	}
	return 1;
}

void call_usage() {
	write("Usage: Call <item> <func> [*item|[\"]string[\"]|integer] ...\n");
}

mixed * Parse_Arguments(string str) {
	int i,j, part;
	mixed * ret;
	string tmp1, tmp2, current, rest;

	if (!str) return ({});
	while(sscanf(str," %s",str));
	if (str=="") return ({});

	if (str[0]=='\"') {
		for(j=1;j<strlen(str);j++) {
			if(str[j]=='\\'&& j<strlen(str)-1 ) {
				tmp1=j>1?str[0..j-1]:"";
				tmp2=j<strlen(str)-2?str[j+2..strlen(str)-1]:"";
				if(str[j+1]=='n') {
					str=tmp1+"\n"+tmp2;
				} else if(str[j+1]=='t') {
					str=tmp1+"\t"+tmp2;
				} else {
					str = tmp1+str[j+1..j+1]+tmp2;
				}
				continue;
			}
			if (str[j]=='\"') {
				if(j==1) ret=({""});
				else ret=({str[1..j-1]});
				if(j==strlen(str)-1) {
					str="";
				} else {
					str=str[j+1..strlen(str)-1];
				}
				return ret+Parse_Arguments(str);
			}
		}
		write("Cyber eye: incomplete string input\n");
		return 0;
	}
	if (str[0]=='{') { /*array*/
		part = Look_for_char(str,'{','}');
		if (part==-1) {
			write("Cyber eye: incomplete array input\n");
			return 0;
		}
		if (part==1) ret=({ ({}) });
		else ret = ({ Parse_Arguments(str[1..part-1]) });
		if (part==strlen(str)-1) {
			rest="";
		} else {
			rest=str[part+1..strlen(str)-1];
		}

		return ret + Parse_Arguments(rest);
	}
	if (sscanf(str,"%s %s",current, rest)!=2) {
		current = str;
		rest = "";
	}
	if (sscanf(current,"%d", i)) return ({ i }) + Parse_Arguments(rest);

	if (strlen(current)>2 &&
	    sscanf(current,"*%s",tmp1))
		return ({ parse_object_list(tmp1) })+Parse_Arguments(rest);
	return ({ current })+Parse_Arguments(rest);

}

int Call(string str) {
	string item, with, rest, argstr, tmp;
	mixed * ob;
	mixed * args;
	int i,m,n;
	int ret;
	if (!str ||
	    !sscanf(str,"%s %s", item, rest) ||
	    !rest) {
		call_usage();
		return 1;
	}

	seteuid(getuid(this_player()));
	sscanf(rest, "%s %s", with, argstr);
	if (!argstr || argstr=="")
		with = rest;

	if (!with) {
		call_usage();
		return 1;
	}

	ob = Parse_List(item) - ({0});
	if (!(m=sizeof(ob))) {
		write("Can't find target.\n");
		return 1;
	}

	args = Parse_Arguments(argstr) ;
	if (!args) return 1;
	n = sizeof(args);
	if (n<MAXARGS) args = args + allocate(MAXARGS-n);

	write("Processing object list of size "+m+".\n");
	write("Function: "+with+"(");
	for(i=0;i<n;i++) {
		if (i) write(",");
		if (stringp(args[i])) write("\""+args[i]+"\"");
		else if (pointerp(args[i])) write(dump_variable(args[i]));
		else if (objectp(args[i])) write(file_name(args[i]));
		else write(args[i]);
	}
	write(")\n");

	for(i=0;i<m;i++) {
		ret = (mixed) call_other(ob[i], with, args[0], args[1],
			 args[2], args[3], args[4], args[5]);
		if ( ( objectp(ob[i]) && !function_exists(with,ob[i])) ||
		     ( stringp(ob[i]) && (ob[i]=find_object(ob[i])) &&
			ob[i] && !function_exists(with,ob[i])) )
		{
			write("Warning: Function "+with+" does not exist in ");
			write(file_name(ob[i]));
			write("\n");
			if (ret) {
				write("Diagnosis: object shadowed => ");
				print_ret(ret,0);
			}
		}
		else {
			write("=> ");
			print_ret(ret,0);
		}
	}
	assign("ret", ret);
	return 1;
}

mixed * Parse_List(string str) {
	string tmp1, tmp2, tail, *filelist;
	int tmp3;
	object list;
	int i,n;
	if ((str[0]=='/'||str[0]=='~') && !sscanf(str,"%s:%s",tmp1,tmp2) &&
	    !sscanf(str,"%s#%d",tmp1,tmp3) &&
		sscanf(str,"%s*%s",tmp1,tmp2) ) {
		n=strlen(str);
		tail=str[(n-2)..(n-1)];
		if (tail!=".c" && tail!="*c" && str[n-1]!='*' )
			str+=".c";
		filelist= (string *)this_player()->wild_card(str);
		print_ret(filelist,0);
		return filelist;
	}
	return ({
		parse_object_list(str)	  }
	);
}

print_ret(mixed ret,int layer) {
	int i,n;
	mixed * k;
	if (layer) {
		printf("%-:*s",layer*2," ");
	}
	if (undefinedp(ret)) write("<NULL>\n");
	else
	if (intp(ret))
		write(ret + "\n");
	else if (pointerp(ret)) {
		n=sizeof(ret);
		write("Array of size " + n + "\n");
		for(i=0;i<n;i++) print_ret(ret[i],layer+1);
	}
	else if (stringp(ret))
		write("\"" + ret + "\"\n");
	else if (objectp(ret)) {
		write("Obj:");
		write(format_mixed(ret,25)+
		    format_mixed((string)ret->query("short"),20) +
		    format_mixed(getuid(ret),7)+format_mixed(geteuid(ret),7));
		write("\n");
	}
	else if (mapp(ret)) {
		k=keys(ret);
		n = sizeof(k);
		write("Mapping of size "+n+"\n");
		for(i=0;i<n;i++) {
			write("		     "[0..(layer*2+1)]);
			write(k[i]);
			write(":");
			print_ret(ret[k[i]],layer+1);
		}
	}
}

int Set(string str) {
	object ob;
	mixed * args;
	string item, var;
	int n;

	if (!str)
		return 0;
	if (sscanf(str, "%s %s", var, item) != 2)
		return 0;
	args = Parse_Arguments(item);
	if (!args) return 1;
	n = sizeof(args);
	if (n!=1) {
		write("Can't assign more than 1 values");
		return 1;
	}
	assign(var, args[0]);
	write("Ok.\n");
	return 1;
}

void assign(mixed var, mixed val) {
	_varlist[var]=val;
}

create() {
	set("id", ({
		"eye", "trace", "tracer", "cyber eye", "cyber"	  }
	) );
	set("short","Cyber Eye");
	set("long","@@query_long");
	query_list = init_query();
	efun_list = init_efun();
	LSC_create();
	Reset();
}

string query_long() {
	int i,n,stat;
	string statstr;

	n = number_of_children();
	write("You check the current status of the cyber eye:\n");
	if (_status==IDLE) statstr="Idle";
        else if (_status==RUNNING) statstr="Running";
        else if (_status==INCOMPLETE_INPUT) statstr="Incomplete input";
        else if (_status==PAUSED) statstr="Paused";
        else if (_status==ERROR) statstr="Error";

	printf("Status: %s, Stack size: %d\nInput:%-:70O\n", statstr,
		_stack_ptr, _pending_input);
	if (n) {
		printf("Background job:(%d in total)\n",n);
		printf("%:3s %|:35s %|:15s %:3s %-:20s\n",
			"#", "OBJ", "id", "Stk", "Input");
		for(i=0;i<n;i++) {
			if (_children[i])
			   printf("%:3d %-:35O %-:15s %:3d %-:20s\n",
				i, _children[i], 
				(string)_children[i]->query_process_id(),
				(int) _children[i]->query_stack_ptr(),
				(string) _children[i]->query_input() );
		}
	}
	return "Type \"help eye\" for how to use this cyber eye.\n";
}

void self_destruct() {
	tell_object(environment(this_object()),
	    "The Cyber Eye suddenly gets warm.\n");
	call_out("self_destruct2", 2);
}

void self_destruct2() {
	tell_object(environment(this_object()),
	    "The Cyber Eye disappears in a flash of light.\n");
	destruct(this_object());
}

/* fool-proof query_auto_load(). It works everywhere. */
string query_auto_load() {
	string full, file;
	int num;
	full = file_name(this_object());
	if (sscanf(full,"%s#%d", file, num)) full = file;
	return full+":0";
}

init_arg(string arg) {
	return;
}

string *init_efun() {
	if (efun_list) return efun_list;
	efun_list = ({
		"users",
		"dump_variable",
		"resolv_path",
		"children",
		"sprintf",
		"call_out_info",
		"inherit_list",
	}
	);
	return efun_list;
}

Users() {
	return users();
}

string Dump_variable(mixed data) {
	return dump_variable(data);
}

string Resolv_path(string curr, string new) {
	return resolv_path(curr, new);
}

int Extract(string str) {
	mixed * args;
	string var;
	int i,j,n,element;

	element = 0;
	if (!str)
		return 0;
	if (sscanf(str, "%s %d %d", var, i,j) != 3) {
		if (sscanf(str, "%s %d", var,i)==2) {
			j=i;
			element = 1;
		} else {
			write("usage: Extract <varname> <from> [to]\n");
			return 1;
		}
	}
	args = _varlist[var];
	if (!pointerp(args)) {
		write("variable "+var+" is not an array\n");
		return 1;
	}
	n = sizeof(args);
	if (!n) {
		write("Empty Array\n");
		return 1;
	}
	if (j<i || i<0 || j>=n) {
		write("Out of range\n");
		return 1;
	}

	if (element) assign("$", args[i]);
	else assign("$", args[i..j]);
	write("Ok.\n");
	return 1;
}

int lighton(string str) {
	if (str=="on") set_light(1);
	if (str=="off") set_light(-1);
	return 1;
}

object * Children(string ob) {
	return children(ob);
}

string Sprintf(string fmt, mixed a1,mixed a2,mixed a3,mixed a4, mixed a5) {
#ifdef SECURE
      write("secure!\n");
#endif
	return sprintf(fmt,a1,a2,a3,a4,a5);
}

mixed * Call_out_info() { return call_out_info(); }

string * Inherit_list(object ob) { return inherit_list(ob); }

remove() {
	:: remove();
}

int compile(string str) {
	Compile(str);
	return 1;
}

int _cmd() {
        mixed i;
	object env;
	i = Pop();
        if (!Is_String(i)) {
                error("_cmd: Illegal argument");
                return -1;
        }
        i = Parse_String(i);
	env = environment(this_object());
	if (!env) {
		::remove();
		return -1;
	}
        environment()->force_me(i);
	_count = 0;
}

int _say() {
	mixed i;
        object env, ob;
        string out;

        i = Pop();
        env = environment(this_object());
        if (!env) return 0;
        _counter = 0;
        if (Is_String(i)) {
                out = Parse_String(i);
        }
        else if (Is_Object(i)) {
                ob = find_object(i[2..strlen(i)-1]);
                if (!ob) out = "<null object>";
                else out = file_name(ob);
        }
        else if (objectp(i)) {
                if (!i) out = "<null object>";
                else out = file_name(i);
        }
        else if (intp(i)) {
                out = ""+i;
        }
        else if (pointerp(i)) {
                out = Restore_Array(i);
        }
        if (out) {
		tell_object(env, "cyber eye: "+out+"\n");
                return 0;
        }

        return 0;
}

error(string str) {
	tell_object(environment(this_object()), "CYBER EYE ERROR: "+str+"\n");
	_status = ERROR;
}