summaryrefslogblamecommitdiffstats
path: root/mbbsd/bbslua.c
blob: 0fb4ac54d76de2457bfe567b2000d9caf63336c5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                          

                                     








                                                                          
                   

                                                                          


                                              
                                                   

                                              


                                                                          

                                   



                                         
                                                                          




                                                                          













                                                                          







                                      












                                        
















                                         















































                                                   








                                         
                                                          






















                                                                                      
           
                      

                         
                                  




                                       


                 
 
           
                       







                                          
                  






                                           
                                   







                                                  
                      






                                       
                                  



                                       




                              
                      











                                       



















                                                           














                                                            






                                                                          
                                      
                                          
                                     




                                           
                                            
                                            
                                            

                                            


                                            
                           

                                           

                                        

                                             


























                                                



                                                  


                                                             
                                   











                                                                       
 
              
                                           



























                                                                            





                               
























                                                              


                                 
         
                                                              
                              




















                                                                                          
         






                                 


                 




                                  



                           
                                       







                                          
                                      

                         
 
                    
                        
                           

                                                


                                                     
                              


                   



                                                         
                                                             
                             

                         
 
                      
                                          
                                       



                                                                                             
 
                                  
                                                                            
 
                            
                
                                                                      
 
                                                                   
         


                                     
                             
 
                                                          
                                                                   
                 


                                        

                 








                                                         



                                          

                                                                                






                 
//////////////////////////////////////////////////////////////////////////
// BBS Lua Project
//
// Author: Hung-Te Lin(piaip), Jan. 2008. 
// <piaip@csie.ntu.edu.tw>
// This source is released in MIT License.
//
// TODO:
//  1. add quick key/val conversion
//  2. add key values (UP/DOWN/...)
//  3. remove i/o libraries [done]
//  4. add system break key (Ctrl-C?)
//////////////////////////////////////////////////////////////////////////

#include "bbs.h"

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

//////////////////////////////////////////////////////////////////////////
// CONST DEFINITION
//////////////////////////////////////////////////////////////////////////

#define BBSLUA_VERSION_MAJOR    (0)
#define BBSLUA_VERSION_MINOR    (1)
#define BBSLUA_VERSION_STR      "0.01"
#define BBSLUA_SIGNATURE        "--#BBSLUA"
#define BBSLUA_EOFSIGNATURE     "--\n"

//////////////////////////////////////////////////////////////////////////
// CONFIGURATION VARIABLES
//////////////////////////////////////////////////////////////////////////
#define BLAPI_PROTO     int

#define BLCONF_EXEC_COUNT   (1000)
#define BLCONF_PEEK_TIME    (0.1)
#define BLCONF_BREAK_KEY    Ctrl('C')

//////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
//////////////////////////////////////////////////////////////////////////
static int abortBBSLua = 0;

//////////////////////////////////////////////////////////////////////////
// BBSLUA API IMPLEMENTATION
//////////////////////////////////////////////////////////////////////////

BLAPI_PROTO
bl_getyx(lua_State* L)
{
    int y, x;
    getyx(&y, &x);
    lua_pushinteger(L, y);
    lua_pushinteger(L, x);
    return 2;
}

BLAPI_PROTO
bl_getmaxyx(lua_State* L)
{
    lua_pushinteger(L, t_lines);
    lua_pushinteger(L, t_columns);
    return 2;
}

BLAPI_PROTO
bl_move(lua_State* L)
{
    int n = lua_gettop(L);
    int y = 0, x = 0;
    if (n > 0)
        y = lua_tointeger(L, 1);
    if (n > 1)
        x = lua_tointeger(L, 2);
    move(y, x);
    return 0;
}

BLAPI_PROTO
bl_moverel(lua_State* L)
{
    int n = lua_gettop(L);
    int y = 0, x = 0;
    getyx(&y, &x);
    if (n > 0)
        y += lua_tointeger(L, 1);
    if (n > 1)
        x += lua_tointeger(L, 2);
    move(y, x);
    getyx(&y, &x);
    lua_pushinteger(L, y);
    lua_pushinteger(L, x);
    return 2;
}

BLAPI_PROTO
bl_clear(lua_State* L)
{
    clear();
    return 0;
}

BLAPI_PROTO
bl_clrtoeol(lua_State* L)
{
    clrtoeol();
    return 0;
}

BLAPI_PROTO
bl_clrtobot(lua_State* L)
{
    clrtobot();
    return 0;
}

BLAPI_PROTO
bl_refresh(lua_State* L)
{
    refresh();
    return 0;
}

BLAPI_PROTO
bl_redrawwin(lua_State* L)
{
    redrawwin();
    return 0;
}

BLAPI_PROTO
bl_addstr(lua_State* L)
{
    int n = lua_gettop(L);
    int i = 1;
    for (i = 1; i <= n; i++)
    {
        const char *s = lua_tostring(L, i);
        if(s)
            outs(s);
    }
    return 0;
}

void
bl_k2s(lua_State* L, int v)
{
    if (v <= 0)
        lua_pushnil(L);
    else if (v == KEY_TAB)
        lua_pushstring(L, "TAB");
    else if (v == '\b' || v == 0x7F)
        lua_pushstring(L, "BS");
    else if (v == '\n' || v == '\r' || v == Ctrl('M'))
        lua_pushstring(L, "ENTER");
    else if (v < ' ')
        lua_pushfstring(L, "^%c", v-1+'A');
    else if (v < 0x100)
        lua_pushfstring(L, "%c", v);
    else if (v >= KEY_F1 && v <= KEY_F12)
        lua_pushfstring(L, "F%d", v - KEY_F1 +1);
    else switch(v)
    {
        case KEY_UP:    lua_pushstring(L, "UP");    break;
        case KEY_DOWN:  lua_pushstring(L, "DOWN");  break;
        case KEY_RIGHT: lua_pushstring(L, "RIGHT"); break;
        case KEY_LEFT:  lua_pushstring(L, "LEFT");  break;
        case KEY_HOME:  lua_pushstring(L, "HOME");  break;
        case KEY_END:   lua_pushstring(L, "END");   break;
        case KEY_INS:   lua_pushstring(L, "INS");   break;
        case KEY_DEL:   lua_pushstring(L, "DEL");   break;
        case KEY_PGUP:  lua_pushstring(L, "PGUP");  break;
        case KEY_PGDN:  lua_pushstring(L, "PGDN");  break;
        default:        lua_pushnil(L);             break;
    }
}

BLAPI_PROTO
bl_getch(lua_State* L)
{
    int c = igetch();
    if (c == BLCONF_BREAK_KEY)
    {
        abortBBSLua = 1;
        return lua_yield(L, 0);
    }
    bl_k2s(L, c);
    return 1;
}


BLAPI_PROTO
bl_getstr(lua_State* L)
{
    int y, x;
    char buf[PATHLEN] = "";
    int len = 2, echo = 1;
    int n = lua_gettop(L);

    if (n > 0)
        len = lua_tointeger(L, 1);
    if (n > 1)
        echo = lua_tointeger(L, 2);

    if (len < 2)
        len = 2;
    if (len >= sizeof(buf))
        len = sizeof(buf)-1;

    // TODO process Ctrl-C here
    getyx(&y, &x);
    len = getdata(y, x, NULL, buf, len, echo);
    if (len)
        lua_pushstring(L, buf);
    return len ? 1 : 0;
}

BLAPI_PROTO
bl_pause(lua_State* L)
{
    int n = lua_gettop(L);
    const char *s = NULL;
    if (n > 0)
        s = lua_tostring(L, 1);

    n = vmsg(s);
    if (n == BLCONF_BREAK_KEY)
    {
        abortBBSLua = 1;
        return lua_yield(L, 0);
    }
    lua_pushinteger(L, n);
    return 1;
}

BLAPI_PROTO
bl_title(lua_State* L)
{
    int n = lua_gettop(L);
    const char *s = NULL;
    if (n > 0)
        s = lua_tostring(L, 1);
    if (s == NULL)
        return 0;

    stand_title(s);
    return 0;
}

BLAPI_PROTO
bl_ansi_color(lua_State *L)
{
    char buf[PATHLEN] = ESC_STR "[";
    char *p = buf + strlen(buf);
    int i = 1;
    int n = lua_gettop(L);
    if (n >= 10) n = 10;
    for (i = 1; i <= n; i++)
    {
        if (i > 1) *p++ = ';';
        sprintf(p, "%d", (int)lua_tointeger(L, i));
        p += strlen(p);
    }
    *p++ = 'm';
    *p   = 0;
    lua_pushstring(L, buf);
    return 1;
}

BLAPI_PROTO
bl_attrset(lua_State *L)
{
    char buf[PATHLEN] = ESC_STR "[";
    char *p = buf + strlen(buf);
    int i = 1;
    int n = lua_gettop(L);
    for (i = 1; i <= n; i++)
    {
        sprintf(p, "%dm",(int)lua_tointeger(L, i)); 
        outs(buf);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////
// BBSLUA LIBRARY
//////////////////////////////////////////////////////////////////////////

static const struct luaL_reg lib_bbslua [] = {
    /* curses output */
    { "getyx",      bl_getyx },
    { "getmaxyx",   bl_getmaxyx },
    { "move",       bl_move },
    { "moverel",    bl_moverel },
    { "clear",      bl_clear },
    { "clrtoeol",   bl_clrtoeol },
    { "clrtobot",   bl_clrtobot },
    { "refresh",    bl_refresh },
    { "redrawwin",  bl_redrawwin },
    // { "addch",       bl_addstr },
    { "addstr",     bl_addstr },
    // { "outc",        bl_addstr },
    { "outs",       bl_addstr },
    /* input */
    { "getch",      bl_getch },
    { "getdata",    bl_getstr },
    { "getstr",     bl_getstr },
    /* BBS utilities */
    { "pause",      bl_pause },
    { "title",      bl_title },
    /* ANSI helpers */
    { "ANSI_COLOR", bl_ansi_color },
    { "color",      bl_attrset },
    { "attrset",    bl_attrset },
    { NULL, NULL},
};

static const luaL_Reg mylualibs[] = {
  {"", luaopen_base},

  // {LUA_LOADLIBNAME, luaopen_package},
  {LUA_TABLIBNAME, luaopen_table},
  // {LUA_IOLIBNAME, luaopen_io},
  // {LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_MATHLIBNAME, luaopen_math},
  // {LUA_DBLIBNAME, luaopen_debug},

  {NULL, NULL}
};


LUALIB_API void myluaL_openlibs (lua_State *L) {
  const luaL_Reg *lib = mylualibs;
  for (; lib->func; lib++) {
    lua_pushcfunction(L, lib->func);
    lua_pushstring(L, lib->name);
    lua_call(L, 1, 0);
  }
}

static void
bbsluaRegConst(lua_State *L, const char *globName)
{
    lua_getglobal(L, globName);
    lua_pushstring(L, "ESC"); lua_pushstring(L, ESC_STR);
    lua_settable(L, -3);

    lua_getglobal(L, globName);
    lua_pushstring(L, "ANSI_RESET"); lua_pushstring(L, ANSI_RESET);
    lua_settable(L, -3);

}

static void
bbsluaHook(lua_State *L, lua_Debug* ar)
{
    // vmsg("bbslua HOOK!");
    if (ar->event == LUA_HOOKCOUNT)
        lua_yield(L, 0);
}

static char * 
bbslua_attach(const char *fpath, int *plen)
{
    struct stat st;
    int fd = open(fpath, O_RDONLY, 0600);
    char *buf = NULL;

    *plen = 0;

    if (fd < 0) return buf;
    if (fstat(fd, &st) || ((*plen = st.st_size) < 1) || S_ISDIR(st.st_mode))
    {
        close(fd);
        return buf;
    }
    *plen = *plen +1;

    buf = mmap(NULL, *plen, PROT_READ, MAP_SHARED, fd, 0);
    close(fd);

    if (buf == NULL || buf == MAP_FAILED) 
    {
        *plen = 0;
        return  NULL;
    }

    madvise(buf, *plen, MADV_SEQUENTIAL);
    return buf;
}

static void
bbslua_detach(char *p, int len)
{
    munmap(p, len);
}

int
bbslua_detect_range(char **pbs, char **pbe)
{
    int szsig = strlen(BBSLUA_SIGNATURE),
        szeofsig = strlen(BBSLUA_EOFSIGNATURE);
    char *bs, *be, *ps, *pe;

    bs = ps = *pbs;
    be = pe = *pbe;

    // find start
    while (ps + szsig < pe)
    {
        if (strncmp(ps, BBSLUA_SIGNATURE, szsig) == 0)
            break;
        // else, skip to next line
        while (ps + szsig < pe && *ps++ != '\n');
    }

    if (!(ps + szsig < pe))
        return 0;

    *pbs = ps;
    *pbe = be;

    // find tail by SIGNATURE
    pe = ps + 1;
    while (pe + szsig < be)
    {
        if (strncmp(pe, BBSLUA_SIGNATURE, szsig) == 0)
            break;
        // else, skip to next line
        while (pe + szsig < be && *pe++ != '\n');
    }

    if (pe + szsig < be)
    {
        // found sig, end at such line.
        pe--;
        *pbe = pe;
    } else {
        // search by eof-sig
        pe = be - szeofsig-2;
        while (pe > ps)
        {
            if (pe+2 + szeofsig < be &&
                    strncmp(pe+2, BBSLUA_EOFSIGNATURE, szeofsig) == 0)
                break;
            while (pe > ps && *pe-- != '\n');
        }
        if (pe > ps)
            *pbe = pe+2;
    }

    // prevent trailing zeros
    pe = *pbe;
    while (pe > ps && !*pe)
        pe--;
    *pbe = pe;

    return 1;
}

int
bbslua(const char *fpath)
{
    int r = 0;
    lua_State *L = lua_open();
    char *bs, *ps, *pe;
    int sz = 0;

    // detect file
    bs = bbslua_attach(fpath, &sz);
    if (!bs)
        return 0;
    ps = bs;
    pe = ps + sz;

    if(!bbslua_detect_range(&ps, &pe))
    {
        // not detected
        bbslua_detach(bs, sz);
        return 0;
    }

    // load file
    abortBBSLua = 0;
    myluaL_openlibs(L);
    luaL_openlib(L,   "bbs", lib_bbslua, 0);
    bbsluaRegConst(L, "bbs");
    r = luaL_loadbuffer(L, ps, pe-ps, "BBS-Lua");
    
    // unmap
    bbslua_detach(bs, sz);

    if (r != 0)
    {
        const char *errmsg = lua_tostring(L, -1);
        move(b_lines-3, 0); clrtobot();
        outs("\n");
        outs(errmsg);
        vmsg("BBS-Lua 錯誤: 請通知作者修正程式碼。");
        lua_close(L);
        return 0;
    }

    // prompt user
    grayout(0, b_lines, GRAYOUT_DARK);
    move(b_lines-2, 0); clrtobot();
    prints("\n" ANSI_COLOR(1;33;41) "%-*s" ANSI_RESET,
            t_columns-1,
            "請按任意鍵開始執行 BBS-Lua 程式。執行中可隨時按下 Ctrl-C 強制中斷。"
            );

    setutmpmode(UMODE_BBSLUA);
    vmsg(" BBS-Lua v" BBSLUA_VERSION_STR " (" __DATE__ " " __TIME__")");

    // ready for running
    clear();
    lua_sethook(L, bbsluaHook, LUA_MASKCOUNT, BLCONF_EXEC_COUNT );

    while (!abortBBSLua && (r = lua_resume(L, 0)) == LUA_YIELD)
    {
        if (input_isfull())
            drop_input();

        // refresh();

        // check if input key is system break key.
        if (peek_input(BLCONF_PEEK_TIME, BLCONF_BREAK_KEY))
        {
            drop_input();
            abortBBSLua = 1;
            break;
        }
    }

    if (r != 0)
    {
        const char *errmsg = lua_tostring(L, -1);
        move(b_lines-3, 0); clrtobot();
        outs("\n");
        outs(errmsg);
    }

    lua_close(L);

    grayout(0, b_lines, GRAYOUT_DARK);
    move(b_lines, 0); clrtoeol();
    vmsgf("BBS-Lua 執行結束%s。", 
            abortBBSLua ? " (使用者中斷)" : r ? " (程式錯誤)" : "");
    clear();

    return 0;
}


// vim:ts=4:sw=4