/*
** Compat-5.1
** Copyright Kepler Project 2004-2006 (http://www.keplerproject.org/compat)
** $Id: compat-5.1.c,v 1.13 2006/02/20 21:12:47 carregal Exp $
*/

#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "compat-5.1.h"

static void getfield(lua_State *L, int idx, const char *name) {
    const char *end = strchr(name, '.');
    lua_pushvalue(L, idx);
    while (end) {
        lua_pushlstring(L, name, end - name);
        lua_gettable(L, -2);
        lua_remove(L, -2);
        if (lua_isnil(L, -1)) return;
        name = end+1;
        end = strchr(name, '.');
    }
    lua_pushstring(L, name);
    lua_gettable(L, -2);
    lua_remove(L, -2);
}

static void setfield(lua_State *L, int idx, const char *name) {
    const char *end = strchr(name, '.');
    lua_pushvalue(L, idx);
    while (end) {
        lua_pushlstring(L, name, end - name);
        lua_gettable(L, -2);
        /* create table if not found */
        if (lua_isnil(L, -1)) {
            lua_pop(L, 1);
            lua_newtable(L);
            lua_pushlstring(L, name, end - name);
            lua_pushvalue(L, -2);
            lua_settable(L, -4);
        }
        lua_remove(L, -2);
        name = end+1;
        end = strchr(name, '.');
    }
    lua_pushstring(L, name);
    lua_pushvalue(L, -3);
    lua_settable(L, -3);
    lua_pop(L, 2);
}

LUALIB_API void luaL_module(lua_State *L, const char *libname,
                              const luaL_reg *l, int nup) {
  if (libname) {
    getfield(L, LUA_GLOBALSINDEX, libname);  /* check whether lib already exists */
    if (lua_isnil(L, -1)) { 
      int env, ns;
      lua_pop(L, 1); /* get rid of nil */
      lua_pushliteral(L, "require");
      lua_gettable(L, LUA_GLOBALSINDEX); /* look for require */
      lua_getfenv(L, -1); /* getfenv(require) */
      lua_remove(L, -2); /* remove function require */
      env = lua_gettop(L);

      lua_newtable(L); /* create namespace for lib */
      ns = lua_gettop(L);
      getfield(L, env, "package.loaded"); /* get package.loaded table */
      if (lua_isnil(L, -1)) { /* create package.loaded table */
          lua_pop(L, 1); /* remove previous result */
          lua_newtable(L);
          lua_pushvalue(L, -1);
          setfield(L, env, "package.loaded");
      }
      else if (!lua_istable(L, -1))
        luaL_error(L, "name conflict for library `%s'", libname);
      lua_pushstring(L, libname);
      lua_pushvalue(L, ns); 
      lua_settable(L, -3); /* package.loaded[libname] = ns */
      lua_pop(L, 1); /* get rid of package.loaded table */
      lua_pushvalue(L, ns); /* copy namespace */
      setfield(L, LUA_GLOBALSINDEX, libname);
      lua_remove (L, env); /* remove env */
    }
    lua_insert(L, -(nup+1));  /* move library table to below upvalues */
  }
  for (; l->name; l++) {
    int i;
    lua_pushstring(L, l->name);
    for (i=0; i<nup; i++)  /* copy upvalues to the top */
      lua_pushvalue(L, -(nup+1));
    lua_pushcclosure(L, l->func, nup);
    lua_settable(L, -(nup+3));
  }
  lua_pop(L, nup);  /* remove upvalues */
}