musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/*
 * MusicMUD - Lua Interface Module
 * Copyright (C) 2002-2003 Paul Lettington
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <signal.h>
#include <sys/time.h>
#include <setjmp.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "musicmud.h"
#include "pflags.h"
#include "pflagnames.h"
#include "flags.h"
#include "flagnames.h"
#include "events.h"
#include "hooks.h"
#include "verbs.h"
#include "misc.h"
#include "State.h"
#include "eq.h"
#include "death.h"
#include "colour.h"

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "../lua/src/lstate.h"
#include "lauxlib.h"
}

#include "luaif.h"

extern MudObject *worn_on(MudObject *by, const char *what, int wol);

int LUA_TRACE=0;
string LTRACE_ZONE="";

bool lua_tracing() {
  if (LUA_TRACE==3) {
    if (is_player(qui))
      return 1;
    const char *z = qui?qui->get("zone"):"";
    if (z && streq(z, LTRACE_ZONE.c_str()))
      return 1;
  }

  if (LUA_TRACE==2 || (LUA_TRACE==1 && is_player(qui)))
    return 1;

  return 0;
}

#define MODULE "lua"

sigjmp_buf env;
lua_State *L;

int objecttag=0;
int flagtabletag=0;
int privtabletag=0;
int questtabletag=0;
int minitabletag=0;
int rotag=0;
int sectag=0;

int stacksize=0;

void do_init();

vector<string> timerobjects;

int luadotrap(const char*, MudObject*, MudObject*, MudObject*, MudObject*, 
	      const char*, MudObject*ta=0);
void lua_verb(MudObject *who, int argc, const char **argv);

void lua_timer() {
	time_t now = time(NULL);
	vector<string>::iterator iter = timerobjects.begin();
	while(iter != timerobjects.end()) {
		MudObject *ob = planet->get(iter->c_str());
		if(!ob) {
			iter = timerobjects.erase(iter);
			continue;
		}
		int removed=0;
		int left=0;
	restart:
		Object::strit st = ob->strs.begin();
		while(st != ob->strs.end()) {
			const char *key = st->first.c_str();
			if(strlen(key)>11 && !strncmp(key,"!lua.timer.",11)) {
				char * timestring = strdup(key+11);
				strtok(timestring,".");
				if(atoi(timestring) <= now) {
					luadotrap( key, ob, ob, NULL, 
						   NULL,
						   timestring);
					removed++;
					ob->unset(key);
					st = ob->strs.begin();
					goto restart;
				} else {
				  	left++; 
				}
				free(timestring);
			}
			st++;
		}
		if(!left) {
			iter = timerobjects.erase(iter);
			continue;
		}
		iter++;
	}
}

static int luaerror(lua_State *L)
{
		log(PFL_SEELUASPAM,0,"lua", "error: %s", escape_colour(lua_tostring(L,1), 0).c_str());
		return 0;
}

void do_cleanup() {
	if(!L) {
		log(PFL_SEEINFO,0,"lua","nothing to clean up");
	} else {
		lua_close(L);
		//log(PFL_SEEINFO,0,"lua","cleaned up");
	}
	LUA_DOTRAP=0;
	LUA_TIMERHOOK=0;
	LUA_VERB=0;
	stacksize=-1;
}


#define SEC_UNTRUSTED 0
#define SEC_TESTTRUSTED 1
#define SEC_APPROVED 2

const char *sec_as=0;
int sec_pr=SEC_APPROVED;
MudObject *sec_pl=0;
MudObject *sec_o1=0;
MudObject *sec_o2=0;
MudObject *sec_o3=0;
MudObject *sec_on=0;
const char *sec_txt=0;
const char *sec_prop=0;
const char **sec_argv=0;
int sec_argc=0;
Divert *sec_div=0;

class Sec {
  const char *my_as;
  int my_pr;
  MudObject *my_pl, *my_o1, *my_o2, *my_o3;
  MudObject *my_on;
  const char *my_txt, *my_prop;
  int my_argc;
  const char **my_argv;
  Divert *my_div;
public:
  Sec(const char *as, int pr, MudObject *pl, MudObject *o1, MudObject *o2,
      MudObject *o3, const char *txt, const char *prop, MudObject *on, 
      int argc=0, const char **argv=0) : 

    my_as(sec_as), my_pr(sec_pr), my_pl(sec_pl), my_o1(sec_o1), 
    my_o2(sec_o2), my_o3(sec_o3), my_on(sec_on), my_txt(sec_txt), 
    my_prop(sec_prop), my_argc(sec_argc), my_argv(sec_argv), my_div(sec_div) {
    sec_as = as;
    sec_pr = pr;
    sec_pl = pl;
    sec_o1 = o1;
    sec_o2 = o2;
    sec_o3 = o3;
    sec_on = on;
    sec_txt = txt;
    sec_prop = prop;
    sec_argc = argc;
    sec_argv = argv;
    sec_div = 0;
  }
  ~Sec() {
    sec_as = my_as;
    sec_pr = my_pr;
    sec_pl = my_pl;
    sec_o1 = my_o1;
    sec_o2 = my_o2;
    sec_o3 = my_o3;
    sec_txt = my_txt;
    sec_prop = my_prop;
    sec_argc = my_argc;
    sec_argv = my_argv;
    if (sec_div)
      delete sec_div;
    sec_div = my_div;
  }
};

void ldivert(MudObject *who, const char *s)
{
  if (sec_div || !who || !s)
    return;

  sec_div = new Divert(who, s);
}

void lundivert(MudObject *who, const char *s)
{
  if (!sec_div)
    return;

  delete sec_div;
  sec_div = 0;
}

bool writeaccess(MudObject *what)
{
  if (sec_pr==SEC_APPROVED)
    return 1;

  if (sec_pr==SEC_TESTTRUSTED)
    return 1;

  if (sec_as) {
    if (!what->get("zone"))
      return 1;
    MudObject *z=getzone(what->get("zone"));
    if (!z)
      return 1;
    const char *author = z->get("author");
    if (!author)
      return 1;
    char owners[4096];
    strcpy(owners, author);
    char *user = strtok(owners, ",");
    while (user) {
      if (streq(user, sec_as)) {
	return true;
      }
      user = strtok(0, ",");
    }    
    return 0;
  }
  return 1;
}

int luadotrap(const char *trig, MudObject *who, MudObject *o1, 
	      MudObject *o2, MudObject *o3, const char *txt, MudObject *ta) {
	string property,disabled;
	string vals;
	const char *value;

	int r, q=1;

	if (!trig) return 2;
	if (!who) return 2;

	if(!L) return 2;

 lookup:
	if(strncmp(trig,"lua.",4)==0 || strncmp(trig,"!lua.",5)==0)
		property="";
	else
		property = "lua.";
	property += trig;

	if (!ta)
	  ta = o1;

	value = (ta?ta:o1)->get(property.c_str());
	
	if (!value) {
	  NewWorld w;
	  w.add(ta);
	  MudObject *tat = ta;
	  MudObject *prev;
	  while (prev=tat,tat=tat->get_object("treatas")) {
	    {
	      if (!tat)
		break;

	      if (const char *v=tat->get(property.c_str())) {
		value = v;
		ta = tat;
		break;
	      }

	      if (prev==tat)
		break;

	      if (w.get(tat->id)) {
		log(PFL_SEEINFO,0,"lua","treatas loop from %s (%s)",o1->id, tat->id);
		return 0;
	      }
	      w.add(tat);
	    }
	  }
	}

	if (!ta) ta = o1;

	if(!value) {
	  return 2;
	}

	const char *as = 0;
	string ass;
	int pv=0;

	if (value[0]==',') {
	  ass = value+1;
	  ass = ass.substr(0, ass.find(','));
	  if(q) pv = atoi(strchr(value+1, ',')+1);
	  value = strchr(value, ';');
	  if (!value || !*value)
	    return 2;
	  value++;
	  if (!value || !*value)
	    return 2;
	  if(q) as = ass.c_str();
	}

	if (q && value[0]=='>') {
	  q--;
	  trig = value+1;
	  char *sp=NULL;
	  if((sp = strstr(trig, " "))) {
	    char temp[4096];
	    int slen = sp-trig; /* hooray for evil hacks */
	    strncpy(temp,trig,slen);
	    temp[slen]='\0';
	    MudObject *tempta = planet->get(temp);
	    if(tempta) {
	      ta=tempta;
	      trig=sp+1;
	    }
	    //log(PFL_SEEINFO,0,"lua","dotrap > : temp=%s, trig=%s",temp,trig);
	  }
	    
	  goto lookup;
	}

	if (value[0]=='#') {
	  char *tvalue = strdup(value);

	  char *spc=strchr(tvalue, ' ');
	  char *nl=strchr(tvalue, '\n');

	  char *first = spc;
	  if (nl && (!spc || (nl < spc))) {
	    first = nl;
	  }
	  if (first) {
	    *first = 0;
	    first++;
	  }

	  string prop = tvalue+1;
	  string nv = "";	  
	  MudObject *ifrom = ta;
	  string from;

	  if (prop.find(':')!=string::npos) {
	    from = prop.substr(prop.find(':'));
	    prop = prop.substr(0, prop.find(':')-1);
	    
	    ifrom = planet->get(from.c_str());
	  }

	  if (ifrom) {
	    if (const char *nva=ifrom->get(prop.c_str())) {
	      nv += nva;
	    } else {
	      log(PFL_SEEINFO,0,"lua", "missing include (%s.%s) in %s.%s", ifrom->id, prop.c_str(), ta->id, property.c_str());
	    }
	  } else {
	    log(PFL_SEEINFO,0,"lua", "missing include (%s.%s) in %s.%s", from.c_str(), prop.c_str(), ta->id, property.c_str());
	  }
	  
	  nv += "\n";
	  nv += first;
	  vals = nv;
	  value = vals.c_str();
	  free(tvalue);
	}

	Sec securinfo(as, pv, who, o1, o2, o3, txt, property.c_str(), ta);

	disabled = property + ".disabled";
	if((ta)->get(disabled.c_str()))
	{
		/* do we want a log message? could be too spammy if a common trap overran */
		log(PFL_SEELUASPAM,0,"lua","%s::%s (%s) is disabled, not running",
				(ta)->id, property.c_str(), o1->id);
		return 2;
	}

	if (lua_tracing()) {
	  if (ta==o1 || !ta)
	    log(PFL_SEELUASPAM,0,"lua", "found %s on %s", property.c_str(), o1->id);
	  else
	    log(PFL_SEELUASPAM,0,"lua", "found %s on %s for %s", property.c_str(), ta->id, o1->id);
	}

	//L->maxopcount=1000000;
	//if (pv==2) L->maxopcount=1000000; /* trusted code */
	
	int noprevioustimer=0;
	if(L->opcount == 0) {
		noprevioustimer=1;
	}

	if(L->maxopcount && L->opcount > L->maxopcount) {
		log(PFL_SEELUASPAM,0,"lua","%s::%s i'm exiting as well, but not disabling myself", (ta)->id,property.c_str());
		if(noprevioustimer)
			L->opcount=0;
		return 2;
	}

	int toreturn=-1;
	int oldtop=lua_gettop(L);

	r=lua_dostring(L,value);

	//if (LUA_TRACE==2 || (LUA_TRACE==1 && is_player(qui))) {
	if(lua_tracing()) {
		log(PFL_SEELUASPAM,0,"lua", "%s::%s opcount: %d",(ta)->id, property.c_str(), L->opcount);
	}

	if(L->maxopcount && L->opcount > L->maxopcount) {
		log(PFL_SEELUASPAM,0,"lua","%s::%s took too long, disabling. (opcount=%d)", (ta)->id,property.c_str(),L->opcount);
		ta->setf(disabled.c_str(),"over-ran at time %lu", time(NULL));
		if(noprevioustimer) L->opcount=0;
		return 2;
	}
	
	if(noprevioustimer)
		L->opcount=0;
	if(r) {
		log(PFL_SEELUASPAM,0,"lua", "%s::%s returned %d",o1->id,property.c_str(),r);
		toreturn=2;
	}
	int newtop = lua_gettop(L);
	if(oldtop==newtop)
	{
		toreturn=0; /* nothing returned */
	} else {
		if(lua_isnumber(L,newtop))
		{
			toreturn = (int) lua_tonumber(L,newtop);
		} else {
			toreturn=0;
			if (lua_isstring(L,newtop)) {
			  who->set("!msg.me", lua_tostring(L, newtop));
			}
			if (newtop == oldtop+4) {
			  if (lua_isstring(L,newtop-3)) {
			    who->set("!msg.me", lua_tostring(L, newtop-3));
			  }
			  if (l_isobject(L,newtop-2)) {
			    who->set("!msg.prop", l_getobject(L, newtop-2)->id);
			  }
			  if (lua_isstring(L,newtop-1)) {
			    who->set("!msg.target", lua_tostring(L, newtop-1));
			  }
			  if (l_isobject(L,newtop)) {
			    who->set("!msg.tprop", l_getobject(L, newtop)->id);
			  }
			}
			if (newtop == oldtop+3) {
			  if (lua_isstring(L,newtop-2)) {
			    who->set("!msg.me", lua_tostring(L, newtop-2));
			  }
			  if (l_isobject(L,newtop-1)) {
			    who->set("!msg.prop", l_getobject(L, newtop-1)->id);
			  }
			  if (lua_isstring(L,newtop)) {
			    who->set("!msg.target", lua_tostring(L, newtop));
			  }
			}
			if (newtop == oldtop+2) {
			  if (lua_isstring(L,newtop-1)) {
			    who->set("!msg.me", lua_tostring(L, newtop-1));
			  }
			  if (l_isobject(L,newtop)) {
			    who->set("!msg.prop", l_getobject(L, newtop)->id);
			  }
			}
		}
		lua_settop(L,oldtop);
	} 

	//log(PFL_SEELUASPAM,0,"lua","stack: %d/%d, opcount: %d/%d",lua_gettop(L),lua_stackspace(L),L->opcount,L->maxopcount);
	if(toreturn!=-1)
		return toreturn;
	return 2;
}



void removetimer(const char *code)
{
  const char *k = strchr(code, ':');
  if (!k)
    return;
  
  string oid = string(code, k-code);
  string tid = string(k+1);

  if (strncmp(tid.c_str(), "!lua.timer.", 11))
    return;

  MudObject *who = planet->get(oid);
  if (who && who->get(tid.c_str())) {
    who->unset(tid.c_str());
  }
}

string addtimer(MudObject *obj, const char *code, int when)
{
  string randomcrap;

  for(int i=0;i<10;i++)
    randomcrap += ('a' + random_number(26));

  string key = ssprintf("!lua.timer.%u.%s",(unsigned int)time(NULL) + (int)lua_tonumber(L,3), randomcrap.c_str());
  string value = ssprintf(",%s,%u;%s" , sec_as, sec_pr, lua_tostring(L,2));
  obj->set(key, value);

  int found = 0;
  iforeach(iter, timerobjects) {
    if (planet->get(iter->c_str())==obj)
      found = 1;
  }

  if (!found) 
    timerobjects.push_back(obj->id);

  return ssprintf("%s:%s", obj->id, key.c_str());
}

int l_getprops(lua_State *L)
{
  if (lua_gettop(L)!=1 && lua_gettop(L)!=2) {
    lua_error(L, "wrong number of arguments to getprops()");
  }

  MudObject *obj = l_getobject(L, 1);
  if (!obj) {
    lua_error(L, "expected an object for arg 1 of getprops()");
  }

  const char *pref = 0;

  if (lua_gettop(L)==2)
    if (lua_isstring(L, 2))
      pref = lua_tostring(L, 2);
    else
      if (!lua_isnil(L, 2))
	lua_error(L, "expected a string or nil for arg 1 of getprops()");

  lua_newtable(L);

  iforeach(i, obj->strs)
    {
      if (pref && strncmp(i->first.c_str(), pref, strlen(pref))!=0)
	  continue;

      lua_pushstring(L, i->first.c_str());
      lua_pushstring(L, i->second.c_str());
      lua_settable(L, -3);
    }

  if (obj->id && (!pref || strncmp("id", pref, strlen(pref))==0)) {
    lua_pushstring(L, "id");
    lua_pushstring(L, obj->id);
    lua_settable(L, -3);
  }

  iforeach(i, obj->ints)
    {
      if (pref && strncmp(i->first.c_str(), pref, strlen(pref))!=0)
	  continue;

      lua_pushstring(L, i->first.c_str());
      lua_pushnumber(L, i->second);
      lua_settable(L, -3);
    }

  return 1;
}

int l_set(lua_State *L)
{
  MudObject *a;
  const char *key;
  if(lua_gettop(L)!=3)
    lua_error(L,"wrong number of arguments to set()");
  if(!l_isobject(L,1)) lua_error(L, "expected an object for arg 1 of set");
  if(!lua_isstring(L,2)) lua_error(L,"expected string for arg 2 of set");
  a= (MudObject*) l_getobject(L,1);
  key = lua_tostring(L,2);
  if (strncmp(key, "lua.", 4)==0 || strncmp(key, "!lua.",5)==0) {
    lua_error(L,"you can't set lua.* or !lua.*");	  
  }
  
  if(!a)
    lua_error(L,"set() no such object");
  
  if(!writeaccess(a))
    lua_error(L,"permission violation in set");
  
  switch(lua_type(L,3)) {
  case LUA_TUSERDATA:
    if(lua_tag(L,3)==objecttag) {
      MudObject *b = (MudObject*) lua_touserdata(L,3);
      a->set(key, b->id);
      
      if (lua_tracing()) 
	log(PFL_SEELUASPAM, 0, "lua", "set(%s, %s, %s)", esc(a).c_str(), esc(key).c_str(), esc(b).c_str());
      
    } else {
      lua_error(L,"bad 3rd argument to set()");
    }
    break;
  case LUA_TNUMBER:
    {
      int num = (int)lua_tonumber(L,3);
      a->set(key, num);

      if (lua_tracing())
	log(PFL_SEELUASPAM, 0, "lua", "set(%s, %s, %i)", esc(a).c_str(), esc(key).c_str(), num);
      break;
    }
  case LUA_TSTRING:
    {
      const char *str = lua_tostring(L, 3);
      a->set(key, str);

      if (lua_tracing())
	log(PFL_SEELUASPAM, 0, "lua", "set(%s, %s, %s)", esc(a).c_str(), esc(key).c_str(), esc(str).c_str());
      break;
    }
  default:
    lua_error(L,"bad 3rd argument to set()");
  }
  
  
  return 1;
}

void l_pushworld(lua_State *L, World<MudObject> *world)
{
  lua_newtable(L);
  MudObject *child;
  int i;
  foreach(world, child, i)
    {
      lua_pushnumber(L,i+1);
      l_pushobject(L,child);
      lua_settable(L,-3);
    }
}

int l_flagtableget(lua_State *L)
{
	const char *key;
	int key2;
	if(lua_gettop(L)!=2)
		lua_error(L,"wrong number of arguments to flagtableget");
	if(lua_type(L,1)!=LUA_TTABLE || lua_tag(L,1)!=flagtabletag)
		lua_error(L,"bad argument to flagtableget");
	switch(lua_type(L,2))
	{
		case LUA_TSTRING:
			key = lua_tostring(L,2);
			if(strcmp(key,"n")==0)
			{
				lua_pushnumber(L,FL_MAX);
				return 1;
			}
			for(int i=0;i<FL_MAX;i++) {
				if(strcasecmp(flag_names[i],key)==0)
				{	
					lua_pushnumber(L,i+1);
					return 1;
				}
			}
			lua_error(L,"key not found in flag table");
			lua_pushnil(L);
			return 1;
			break;
		case LUA_TNUMBER:
			key2 = (int)lua_tonumber(L,2);
			if(key2>=0 && key2<FL_MAX)
			{
				lua_pushstring(L,flag_names[key2-1]);
				return 1;
			} else {
				lua_pushnil(L);
				return 1;
			}
			break;
		default:
			lua_error(L,"flagtableget: that was not a number or a string");
	}
	return 0;
}

int l_privtableget(lua_State *L)
{
	const char *key;
	int key2;
	if(lua_gettop(L)!=2)
		lua_error(L,"wrong number of arguments to privtableget");
	if(lua_type(L,1)!=LUA_TTABLE || lua_tag(L,1)!=privtabletag)
		lua_error(L,"bad argument to privtableget");
	switch(lua_type(L,2))
	{
		case LUA_TSTRING:
			key = lua_tostring(L,2);
			if(strcmp(key,"n")==0)
			{
				lua_pushnumber(L,PFL_MAX);
				return 1;
			}
			for(int i=PFL_NONE;i<PFL_MAX;i++) {
				if(strcasecmp(priv_names[i],key)==0)
				{	
					lua_pushnumber(L,i+1);
					return 1;
				}
			}
			lua_error(L,"key not found in priv table");
			lua_pushnil(L);
			return 1;
			break;
		case LUA_TNUMBER:
			key2 = (int)lua_tonumber(L,2);
			if(key2>=0 && key2<FL_MAX)
			{
				lua_pushstring(L, priv_names[key2-1]);
				return 1;
			} else {
				lua_pushnil(L);
				return 1;
			}
			break;
		default:
			lua_error(L,"privtableget: that was not a number or a string");
	}
	return 0;
}


bool l_isobject(lua_State *L, int idx)
{
  if(lua_tag(L, idx)== objecttag)
    return 1;

  if (lua_isstring(L, idx)) {
    const char *s = lua_tostring(L, idx);
    if (s && planet->get(s)) return 1;
  }

  return 0;
}

bool l_isnilobject(lua_State *L, int idx)
{
  if(lua_tag(L, idx)== objecttag)
    return 1;

  if (lua_isstring(L, idx)) {
    const char *s = lua_tostring(L, idx);
    if (s && planet->get(s)) return 1;
  }

  if (lua_isnil(L, idx))
    return 1;

  return 0;
}

MudObject *getobjbyname(const char *what, MudObject *holo);

MudObject *l_getobject(lua_State *L, int idx)
{
  if(lua_tag(L, idx)== objecttag)
    return (MudObject*)lua_touserdata(L, idx);

  if (lua_isstring(L, idx))
    if (const char *s = lua_tostring(L, idx))
      return planet->get(s);

  return 0;
}

void l_pushobject(lua_State *l, MudObject *o)
{
  if (o)
    lua_pushusertag(L, o, objecttag);
  else
    lua_pushnil(L);
}

int l_questtableget(lua_State *L)
{
	if(lua_gettop(L)!=2)
		lua_error(L,"wrong number of arguments to questtableget");
	if(lua_type(L,1)!=LUA_TTABLE || lua_tag(L,1)!=questtabletag || !lua_isstring(L,2))
		lua_error(L,"bad argument to questtableget");
	const char *key = lua_tostring(L,2);
	MudObject *qu = find_questob(key);
	if (qu) {
	  l_pushobject(L,qu);
	  return 1;
	}
	lua_error(L,"key not found in quest table");
	lua_pushnil(L);
	return 1;
}

int l_minitableget(lua_State *L)
{
	if(lua_gettop(L)!=2)
		lua_error(L,"wrong number of arguments to minitableget");
	if(lua_type(L,1)!=LUA_TTABLE || lua_tag(L,1)!=minitabletag || !lua_isstring(L,2))
		lua_error(L,"bad argument to minitableget");
	const char *key = lua_tostring(L,2);
	MudObject *min = planet->get("mini_zone");
	MudObject *o;
	int i;
	if (min) {
	  foreach(min->children, o, i) {
	    if (!o->get("mname"))
	      continue;
	    if (!strcasecmp(o->get("mname"), key)) {
	      l_pushobject(L, o);
	      return 1;
	    }
	  }
	}
	lua_error(L,"key not found in mini table");
	lua_pushnil(L);
	return 1;
}

int l_flagtableset(lua_State *L)
{
	lua_error(L,"You can't create new flags, silly");
	return 0;
}

int l_globalset(lua_State *L)
{
	log(PFL_SEELUASPAM,0,"lua","You can't set global variables: %s",lua_tostring(L,-3));
	lua_pushnil(L);
	return 1;
}

void do_flagload(lua_State *L)
{
	lua_pushcfunction(L,l_flagtableget);
	lua_settagmethod(L,flagtabletag,"gettable");
	lua_pushcfunction(L,l_flagtableset);
	lua_settagmethod(L,flagtabletag,"settable");
	lua_newtable(L);
	lua_settag(L,flagtabletag);
	lua_setglobal(L,"flag");

	lua_pushcfunction(L,l_privtableget);
	lua_settagmethod(L,privtabletag,"gettable");
	lua_pushcfunction(L,l_flagtableset);
	lua_settagmethod(L,privtabletag,"settable");
	lua_newtable(L);
	lua_settag(L,privtabletag);
	lua_setglobal(L,"priv");


	lua_pushcfunction(L,l_questtableget);
	lua_settagmethod(L,questtabletag,"gettable");
	lua_pushcfunction(L,l_flagtableset);
	lua_settagmethod(L,questtabletag,"settable");
	lua_newtable(L);
	lua_settag(L,questtabletag);
	lua_setglobal(L,"quest");



	lua_pushcfunction(L,l_minitableget);
	lua_settagmethod(L,minitabletag,"gettable");
	lua_pushcfunction(L,l_flagtableset);
	lua_settagmethod(L,minitabletag,"settable");
	lua_newtable(L);
	lua_settag(L,minitabletag);
	lua_setglobal(L,"mini");
}

/* Based on trace-globals.lua in the lua-4.0.1 distribution
 * Instead of a normal global variable "foo", "foo" would be a table with a 
 * special tag. The actual data is stored in "foo.value"
 */
int l_ro_setnew(lua_State *L)
{
	//log(PFL_NONE,0,"lua", "ro_setnew: %s",lua_tostring(L,-3));

	lua_getglobals(L);
	lua_pushvalue(L,-4); /* first of 3 passed parameters (name) */
	lua_newtable(L);
	lua_pushstring(L,"value"); /* push the key */
	lua_pushvalue(L,-5); /* push the value */
	lua_settable(L,-3); /* put the key and the value in the new table */
	lua_settag(L,rotag);
	lua_rawset(L,-3); /* push our new table into the globals */
	lua_pop(L,1); /* clean the globals table off the stack */

	lua_pushnil(L);
	return 1;
}

int l_ro_setnew2(lua_State *L)
{
	log(PFL_SEELUASPAM,0,"lua", "ro_setnew2: You can't create new global variable: %s",lua_tostring(L,-3));
	lua_pushnil(L);
	return 1;
}

int l_ro_setglobal(lua_State *L)
{
	log(PFL_SEELUASPAM,0,"lua", "ro_setglobal: You can't overwrite global variable: %s",lua_tostring(L,-3));
	lua_pushnil(L);
	return 1;
}

int l_ro_getglobal(lua_State *L)
{
	lua_pushstring(L,"value");
	lua_gettable(L,-2); /* -2 being the last passed argument, which is the
			       value of the global variable (i.e., our table) */
	return 1;
}

/* mark any future global variables as read only */
void ro_setup(lua_State *L)
{
	lua_pushcfunction(L,l_ro_getglobal);
	lua_settagmethod(L,rotag,"getglobal");

	lua_pushcfunction(L,l_ro_setglobal);
	lua_settagmethod(L,rotag,"setglobal");

	lua_pushcfunction(L,l_ro_setnew);
	lua_settagmethod(L,LUA_TNIL,"setglobal");
}

/* prevent the creating of any more global variables */
void ro_final(lua_State *L)
{
	lua_pushcfunction(L,l_ro_setnew2);
	lua_settagmethod(L,LUA_TNIL,"setglobal");
}

int l_sec_setglobal(lua_State *L)
{
	lua_pushstring(L,"You can't change ");
	lua_pushvalue(L,-4);
	lua_pushstring(L,", silly");
	lua_concat(L,3);
	lua_error(L,lua_tostring(L,-1));
	return 0;
}

int l_sec_getglobal(lua_State *L)
{
	const char *varname;
	varname = lua_tostring(L,1);
	if(strcasecmp(varname,"pl")==0)
	{
		if(sec_pl) l_pushobject(L,sec_pl);
		else lua_pushnil(L);
	} else if(strcasecmp(varname,"o1")==0)
	{
		if(sec_o1) l_pushobject(L,sec_o1);
		else lua_pushnil(L);
	} else if(strcasecmp(varname,"o2")==0)
	{
		if(sec_o2) l_pushobject(L,sec_o2);
		else lua_pushnil(L);
	} else if(strcasecmp(varname,"o3")==0)
	{
		if(sec_o3) l_pushobject(L,sec_o3);
		else lua_pushnil(L);
	} else if(strcasecmp(varname,"txt")==0)
	{
		if(sec_txt) lua_pushstring(L,sec_txt);
		else lua_pushnil(L);
	} else if(strcasecmp(varname,"arg")==0) {
		lua_newtable(L);
		for (int i=0;i<sec_argc;i++) {
		  lua_pushnumber(L, i);
		  lua_pushstring(L, sec_argv[i]);
		  lua_settable(L, -3);
		}
	}
	else
	{
		lua_pushnil(L);
	}
	return 1;
}

void do_secload(lua_State *L)
{
	lua_getglobals(L);
	
	lua_pushstring(L,"pl");
	lua_pushusertag(L, NULL, sectag);
	lua_rawset(L,-3);
	
	lua_pushstring(L,"o1");
	lua_pushusertag(L, NULL, sectag);
	lua_rawset(L,-3);
	
	lua_pushstring(L,"o2");
	lua_pushusertag(L, NULL, sectag);
	lua_rawset(L,-3);
	
	lua_pushstring(L,"o3");
	lua_pushusertag(L, NULL, sectag);
	lua_rawset(L,-3);
	
	lua_pushstring(L,"txt");
	lua_pushusertag(L, NULL, sectag);
	lua_rawset(L,-3);
	
	lua_pushstring(L,"arg");
	lua_pushusertag(L, NULL, sectag);
	lua_rawset(L,-3);
	
	lua_pop(L,1);

	lua_pushcfunction(L,l_sec_getglobal);
	lua_settagmethod(L,sectag,"getglobal");

	lua_pushcfunction(L,l_sec_setglobal);
	lua_settagmethod(L,sectag,"setglobal");
}


void do_init() {
	L=lua_open(300);
	if(!L) {
		log(PFL_SEEINFO,0,"lua","Lua is borken");
	} else {
		//log(PFL_NONE,0,"lua","started");
		stacksize = lua_stackspace(L);
		LUA_DOTRAP = luadotrap;
		LUA_TIMERHOOK = lua_timer;
		LUA_VERB = lua_verb;
		objecttag = lua_newtag(L);
		flagtabletag = lua_newtag(L);
		privtabletag = lua_newtag(L);
		questtabletag = lua_newtag(L);
		minitabletag = lua_newtag(L);
		rotag = lua_newtag(L);
		sectag = lua_newtag(L);
		ro_setup(L); /* this must be called _before_ any functions are defined */
		lua_baselibopen(L);
		lua_strlibopen(L);
		lua_mathlibopen(L);
		do_flagload(L);
		do_secload(L);
		lua_register(L,"_ERRORMESSAGE",luaerror);
		lua_register(L,"set",l_set);
		lua_register(L,"getprops",l_getprops);

		autodefine(L);

		lua_dofile(L,"data/init.lua");
		ro_final(L); /* this must be called after all functions are defined */
		L->opcount=0;
		L->maxopcount=1000000;
	}
}
 
bool verb_lstats(MudObject *who, int, const char **)
{
	who->printf("Lua is%s running. Stack: %d/%d. Opcount: %d/%d.\n",
			(L?"":" not"),
			(L?lua_stackspace(L):-1),
			stacksize,
			L->opcount,
			L->maxopcount);
	return true;
}

bool verb_ltrace(MudObject *who, int argc, const char **argv)
{
  int all=0, zone=0;
  if(argc==2 && !strcmp("all",argv[1]))
    all=1;
  if(argc==3 && streq("zone",argv[1])) {
    zone=1;
  }
  if(zone && LUA_TRACE!=3) {
    LUA_TRACE = 3;
    LTRACE_ZONE = argv[2];
    log(PFL_SEELUASPAM, 0, "lua", "Tracing players and everything with zone %s", argv[2]);
    return true;
   } if(all && LUA_TRACE!=2) {
    LUA_TRACE = 2;
    log(PFL_SEELUASPAM, 0, "lua", "Tracing everything. This may get spammy.");
    return true;
  } else
    LUA_TRACE = !LUA_TRACE;
  log(PFL_SEELUASPAM, 0, "lua", "Tracing %s.", LUA_TRACE?"players only":"turned off");
  return true;
}

bool verb_lua(MudObject *who, int argc, const char *argv[]) {
  Divert d(who, "lua");

  World<MudObject> *blah = planet;
    if (argc>1 && zones->get(argv[1])) {
      blah = zones->get(argv[1])->children;
    }  
    MudObject *o;
    int i;
    
    foreach_alpha(blah, o, i) {
      MudObject::strit it = o->strs.begin();
      while (it != o->strs.end()) {
	if (strncmp(it->first.c_str(), "lua.", 4)==0) {
	  who->printf("%30s %20s %s\n", o->id, it->first.c_str()+4,
		      o->get(ssprintf("%s.disabled", it->first.c_str()).c_str())?
		      "Disabled":"");
	}
	it++;
      }
    }
    return true;
}

bool verb_export(MudObject *who, int argc, const char *argv[]) {
  World<MudObject> *blah = planet;

    if (argc>1 && zones->get(argv[1])) {
      blah = zones->get(argv[1])->children;
    } else {
      who->printf("Export what?\n");
      return true;
    }
    MudObject *o;
    int i;
    
    string fn = argv[1];
    fn += ".lua";

    XFILE *f = xopen("data/converted", fn.c_str(), "w");
    if (!f) {
      return true;
    }
    
    foreach_alpha(blah, o, i) {
      MudObject::strit it = o->strs.begin();
      while (it != o->strs.end()) {
	if (strncmp(it->first.c_str(), "lua.", 4)==0) {
	  fprintf(f, "desc %s %s\n", o->id, it->first.c_str());
	  const char *l = it->second.c_str();
	  if (l[0]==',') { l = strchr(l, ';'); if (l) l++; }
	  fprintf(f, "%s\n", l);
	  fprintf(f, ".\n");
	}
	it++;
      }
    }
    xclose(f);

    return true;
}


bool verb_import(MudObject *who, int argc, const char *argv[]) {
  if (!is_player(who)) {
    return true;
  }
  Player *p = (Player *)who;

  if (argc<2) {
    who->printf("Import what?\n");
    return true;
  }

  string fn = argv[1];
  fn += ".lua";

  XFILE *f = xopen("data/converted", fn.c_str(), "r");
  if (!f) {
    who->printf("Can't find that to import.\n");
    return true;
  }

  while (1) {
    char bar[1000];
    fgets(bar, 1000, f);
    if (feof(f))
      break;

    if (strchr(bar, '\n')) {
      *strchr(bar, '\n')=0;
    }

    who->printf("%s\n", bar);

    if (p->get_state())
      p->get_state()->invoke(p, bar);
  }

  xclose(f);

  return true;
}

void lua_verb(MudObject *who, int argc, const char **argv)
{
  string v = "verb:";
  v += argv[0];
  string st = the_rest(argc, argv, 1);
  Sec s("@musicmud", 2, who, NULL, NULL, NULL, 
	st.c_str(), v.c_str(), NULL, argc, argv);
  string f = DATA_VERBS "/";
  f += argv[0];
  int oldt = lua_gettop(L);

  int prev = L->opcount == 0;

  lua_dofile(L, f.c_str());
  if (oldt != lua_gettop(L))
    lua_settop(L, oldt);

  if(L->maxopcount && L->opcount > L->maxopcount) {
	  log(PFL_SEELUASPAM,0,"lua", "verb %s overran!", argv[0]);
	  // should think about disabling it as well
  }

  if (prev)
    L->opcount = 0;

  return;
}

bool verb_eval(MudObject *who, int argc, const char **argv) {
  Sec s("@musicmud", 2, who, NULL, NULL, NULL, NULL, "eval", NULL, 0, NULL);

  int oldt = lua_gettop(L);
  int prev = L->opcount == 0;

  lua_dostring(L, the_rest(argc, argv, 1).c_str());

  if(L->maxopcount && L->opcount > L->maxopcount) {
	  log(PFL_SEELUASPAM,0,"lua", "verb %s overran!", argv[0]);
	  // should think about disabling it as well
  }

  if (prev)
    L->opcount = 0;
  if (oldt != lua_gettop(L))
    lua_settop(L, oldt);

  return true;
}


#define CLEANUP do_cleanup();

#include "verbmodule.h"

void startup() {

  do_init();

  AUTO_VERB(eval, 3, 0, PFL_CODER);

  AUTO_VERB(lstats, 3, 0, PFL_CODER);
  AUTO_VERB(lua, 3, 0, PFL_CODER);
  AUTO_VERB(import, 6, 0, PFL_CODER);
  AUTO_VERB(export, 6, 0, PFL_CODER);
  AUTO_VERB(ltrace, 5, 0, PFL_CODER);  
}