The LUA language

My first contact with the LUA as a programming language was during my practical placement at the Logic IO company in Horsens. The implementation of LUA language for their embedded devices was the main part of my job. Below I show a simple example of a LUA program.
The program reads an input file byte by byte, it converts the read value to the ASCII and save the representation into a new txt-file. It is easy now to manipulate the values for instance by XOR-ing them with a secret code.

lua_zip

This piece of data represents a ZIP file as showed in the binary form. A smart eye will recognize the first two bytes [PK], which means a package file. A binary form is not the ASCII file. It is a binary file where each byte can be in range of 0-255, whereas in the ASCII file letters are defined by an ASCII table.

lua_asc

This screen shows the content of an encrypted file. Even the content of the ASCII file looks equal the content of the ZIP package, but there are two different files! The ZIP file is ready to expand. The ASCII is nothing more then a readable txt-file. Period.

lua_asc_bin

This screen shows the content of an encrypted file. The ASCII letters are showed as bytes. The string "50" is represented by to bytes: 5 and 0, which in turn means 0x35 and 0x30 (in hexadecimal notation). This file is byte-equal to the file showed above, but not equal to the very first on the top.

The whole LUA listing code:

LUA3d

---Returns a string representation of a hex dump of a string (containing binary bytes even zero)

function hexdump(s)

  local manLine=""       --human readable format of the current line

  local hexLine=""       --hexadecimal representation of the current line

  local address=0        --the address where the current line starts

  local LINE_LENGTH=16   --how many characters per line?

  local ADDRESS_LENGTH=4 --how many characters for the address part?

  local ret=""


  if not hex then

    hex={}

    local digit={ [0] = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" }

    for i = 0, 15 do

      for j = 0, 15 do

        hex[i * 16 + j] = digit[i] .. digit[j]

      end

    end

  end


  for i = 1, s:len() do

    local ch = s:sub(i, i)

    if ch:find("%c") then

      ch="."

    end           --if ch is a control character, assign some default value to it


    manLine = manLine .. ch

    hexLine = hexLine .. hex[s:byte(i)] .. " "

    if (i % LINE_LENGTH) == 0 or i == s:len() then

      -- print(string.format("%04u | %-48s | %s", address, hexLine, manLine))

      -- print(string.format("%04u | %-48s", address, hexLine))

      myString = string.format("%-48s \n", hexLine)

      ret = ret .. myString

      manLine, hexLine = "", ""

      address = i

    end

  end


  return ret

end



--Test code


local myHexDump = hexdump("hello! how are you?\tI didn't know you are here! Salutation!\n\n\nHahaha!:D")


print(myHexDump)



output:

68 65 6C 6C 6F 21 20 68 6F 77 20 61 72 65 20 79 

6F 75 3F 09 49 20 64 69 64 6E 27 74 20 6B 6E 6F 

77 20 79 6F 75 20 61 72 65 20 68 65 72 65 21 20 

53 61 6C 75 74 61 74 69 6F 6E 21 0A 0A 0A 48 61 

68 61 68 61 21 3A 44 


To implement LUA in C-code one has to understand the way a stack is working. Because all parameters are passing through the stack to the C-level. As LUA is not type oriented, and the 'C' requires types, thus it is a bit challenging to understand the parameters to handle them in the C-code.

LUA implementation in C:

C_SOURCE_FILE

/***************************** C SOURCE FILE *********************************

**

**  Project:    Firmware - LUA interface for serial communication

**  Filename:   SERIALLUAIF.C

**  Version:    1.05

**  Date:       (pk)

**

*****************************************************************************/

// z:\rtcu_arm7\src\vcom\makefile to compile..

// about stack read in refman5.pdf p. 24

#define DEBMSG_ENABLE 1


#include <armx.h>

#include <string.h>


#include <errno.h>

#include <stdarg.h>

#include <stdio.h>

#include <stdlib.h>


// #define serlib_c

#define LUA_LIB


// this is not a name of struct, and will be printed within *.lua code

#define cfMetaTab "lio.ser.serdev"

#define SERSTAT_INFRAME       0x01

#define SERSTAT_STUFF         0x02


#include <lua.h>

#include <lauxlib.h>


#if defined(TRG_ARM002)

#define SERPORT_MAX         1

#elif defined(TRG_ARM004)

#define SERPORT_MAX         2

#elif defined(TRG_ARM005)

#define SERPORT_MAX         0

#elif defined(TRG_ARM006)

#define SERPORT_MAX         0

#elif defined(TRG_ARM009)

#define SERPORT_MAX         2

#elif defined(TRG_ARM022)

#define SERPORT_MAX         2

#elif defined(TRG_ARM092)

#define SERPORT_MAX         2

#else

#error NO TARGET or UNSUPPORTED TARGET!

#endif


#define READ_BUFFER_SIZE 512 // define how much it can read data at a time


typedef struct port_data {

       int8 handle;

       int32 baud;

       int8 bit;

       int8 parity;

       int8 stop;

       int8 handshake;

       int8 rs485;

} port_data;


typedef struct {

       // Var:

       int16 nextin;

       int8 status;


       // Input:

       int8 port;

       int8 enable;

#ifdef TRG_ARM7

       int8 alignment;

#endif

       uint8* frame;

       int16 maxsize;

       uint8 sof;

       uint8 eof;

       uint8 stuffch;


       // Output:

       int8 ready;

       int16 size;


}ALIGN_ATTR tdefserframereceiver;


static port_data* check_serdev(lua_State *L, int i) {

       return (port_data*) luaL_checkudata(L, i, cfMetaTab);

}


static int getInt32FromTable(lua_State *L, int index, const char * key,

               int32 * val) {

       lua_getfield(L, index, key);

       if (lua_isnil(L, -1)) {

               lua_pop(L, 1);

               return 0;

       }

       *val = lua_tointeger(L, -1);

       lua_pop(L, 1);

       return 0;

}

static int getInt8FromTable(lua_State *L, int index, const char * key,

               int8 * val) {

       lua_getfield(L, index, key);

       if (lua_isnil(L, -1)) {

               lua_pop(L, 1);

               return 0;

       }

       *val = lua_tointeger(L, -1);

       lua_pop(L, 1);

       return 0;

}


static const char * getStringFromTable(lua_State *L, int index,

               const char * key) {

       lua_getfield(L, index, key);

       if (lua_isnil(L, -1)) {

               lua_pop(L, 1);

               return 0;

       }

       const char *rtn;

       rtn = lua_tostring(L, -1); // if error then returns NULL

       lua_pop(L, 1);


       return rtn;

}


static int getBooleanFromTable(lua_State *L, int index, const char * key,

               int8 * val) {

       lua_getfield(L, index, key);

       if (lua_isnil(L, -1)) {

               lua_pop(L, 1);

               return 0;

       }

       *val = lua_toboolean(L, -1);

       lua_pop(L, 1);

       return 0;

}


// Fill out the config-table with parameters from table.

// Used by luaSerConfigure() and luaSerOpen()

// Input parameters:

// L: stack, pos: position on stack

// Return values: always 0

static int32 setConfig(lua_State *L, port_data* port) {


       const char *fcArray[3] = { "none", "rtscts", "xon/xoff" };

       const char *pArray[3] = { "none", "even", "odd" };


       int const tabOnStack = -1;

       int i;


       // this is a table, but we have userdata not a simple table!

       getInt32FromTable(L, tabOnStack, "baudRate", &port->baud);

       getInt8FromTable(L, tabOnStack, "numDataBits", &port->bit);

       getInt8FromTable(L, tabOnStack, "numStopBits", &port->stop);

       getBooleanFromTable(L, tabOnStack, "rs485", &port->rs485);

       const char* handshake = getStringFromTable(L, tabOnStack, "flowControl");


       for (i = 0; i < 3; i++) {

               if (stricmp(handshake, fcArray[i]) == 0) {

                       port->handshake = i;

                       break;

               }

       }


       const char* parity = getStringFromTable(L, tabOnStack, "parity");

       for (i = 0; i < 3; i++) {

               if (stricmp(parity, pArray[i]) == 0) {

                       port->parity = i;

                       break;

               }

       }


       return 0;

}


static int luaSerSetConfigTable(lua_State *L) {

       port_data* port = check_serdev(L, 1);

       int status;

       if (lua_gettop(L) > 1) { //Additional parameter

               luaL_checktype(L, 2, LUA_TTABLE);

               if ((status = setConfig(L, port)) != 0) {

                       return status;

               }

       }

       lua_pushstring(L, "ok");

       return 1;

}


// in LUA: serial.open(port, config)

// Parameters

// #integer port :

// #config config : An optional #config table.

//        Return value: #serdev: Serial device - userdata with functions etc.

static int luaSerOpen(lua_State *L) {


       int port;

       struct port_data* port_data;

       int use_config = 0;

       deb_printf("LUA", "luaSerOpen()");

       // displayStack(L);


       // This function allocates a new block of memory with the given size,

       // pushes onto the stack a new full userdata with the block address,

       // and returns this address.

       // luaL_checkint does not change the stack

       port_data = lua_newuserdata(L, sizeof(struct port_data));

       port = luaL_checkint(L, 1);                // take the first parameter and save it


       if (lua_gettop(L) > 1) {                //Additional parameter

               luaL_checktype(L, 2, LUA_TTABLE);

               use_config = 1;

       }


       deb_printf("luaSerOpen()", "Opening a connection on port: %d", port);

       port_data->baud = 9600;

       port_data->bit = 8;

       port_data->parity = 0;

       port_data->stop = 1;

       port_data->rs485 = 0;

       port_data->handshake = 0;


       if (use_config) {

               int status;

               lua_pushvalue(L, 2); // make a copy of the config

               if ((status = setConfig(L, port_data)) != 0) {

                       return status;

               }

               lua_pop(L, 1); // pop the copy of the config


       } else {

               deb_printf("luaSerOpen()",

               "Second parameter is not a table. Default values will be used.");

       }


       deb_printf("LUA", "Open: %d, %d, %d", port, port_data->baud,

                       port_data->bit);

       port_data->handle = vcomOpen(port, port_data->baud, port_data->bit,

                       port_data->parity, port_data->stop, port_data->rs485, NULL);

       if (port_data->handle < 0) {

               lua_pushnil(L);

               lua_pushnumber(L, port_data->handle);

               return 2;

       }

       luaL_getmetatable(L, cfMetaTab);

       // Pushes onto the stack the metatable "configMetatable"

       //        displayStack():3         LUA_TTABLE         -1

       //        displayStack():2         LUA_TUSERDATA  -2        

// returns this userdata

       //        displayStack():1         LUA_TNUMBER         -3  1


       lua_setmetatable(L, -2);

       // Pops a table from the stack and sets it as the new metatable

       // for the value at the given index.

       // displayStack():2         LUA_TUSERDATA         -1

       // displayStack():1         LUA_TNUMBER         -2         1


       return 1;

}


static int luaSerClose(lua_State *L) {


       struct port_data * port = check_serdev(L, 1);


       int8 rc;


       // Close device

       rc = vcomClose(port->handle);

       // 0: - Success // -1: - Client not found.

       if (rc != 0) {

               deb_printf("Error: luaSerClose()", "vcomClose():rc: %d \r\n", rc);

               lua_pushnil(L);

               lua_pushnumber(L, rc); /* push result */

               return 2;

       }


       // Completed

       lua_pushliteral(L, "ok");

       return 1;

}


#if 0

static int luaSerSendChar(lua_State *L) {


       deb_printf("luaSerSendChar()", " my stack: ");

//        displayStack(L);


       int8 serport;


       const char * mychar = lua_tostring(L, 1);

       char ch = mychar[0];

       configPtr cfPtr = serialPtrGet(L, 1);


       // Var

       int32 rc;

       serport = cfPtr->port;// just added


       // Validate

       if (serport < 0 || serport > SERPORT_MAX) {

               deb_printf("Error: luaSerSendChar()",

"serport out of range: %d \r\n", serport);

               lua_pushnumber(L, 0); /* push result */

               return 1;

       }


       // Write char

       rc = vcomWrite(serport, (unsigned char*) &ch, 1);

       //    Return:  0 - Success. //  -1 - Client not found.

       if (rc != 0) {

               deb_printf("Error: luaSerSendChar()", "vcomWrite():rc: %d \r\n", rc);

               lua_pushnil(L);

               lua_pushnumber(L, rc); /* push result */

               return 2;

       }


// Completed

       lua_pushliteral( L, "ok");

       return 1;

}

#endif


static int luaSerWrite(lua_State *L) {

       deb_printf("luaSerWrite()", " my stack: ");

//        displayStack(L);

       size_t len;

       port_data* cfPtr = check_serdev(L, 1);

       const char * Data = luaL_tolstring(L, 2, &len);


       int32 rc;


// Validate

       int8 serport = cfPtr->handle;


       if (serport < 0 || serport > SERPORT_MAX) {

               deb_printf("Error: luaSerWrite()", "serport out of range: %d \r\n",

               serport);

               lua_pushnumber(L, serport); /* push result */

               return 1;

       }

       deb_printf("LUA", "Write string (%d), %s", len, Data);


// Write string

       // when serport + 1 write() returns nil, when serport - 1 also nil

       rc = vcomWrite(serport, (unsigned char*) Data, len);

       if (rc != 0) {

               deb_printf("Error: luaSerWrite()", "vcomWrite():rc: %d \r\n", rc);

               lua_pushnil(L);

               lua_pushnumber(L, rc); /* push result */

               return 2;

       }


// Completed

       lua_pushliteral(L, "ok");

       return 1;


}


// Flushes pending data

// to check the flush command I need first the size of the buffer

static int luaSerFlush(lua_State *L) {

       struct port_data* cfPtr = check_serdev(L, 1);

       int8 serport = cfPtr->handle;

       if (serport < 0 || serport > SERPORT_MAX) {

               lua_pushnil(L);

               lua_pushstring(L, "Error: luaSerFlush(). Illegal serial device.");

               return 2; // Validate

       }


// Var

       int32 rc;


// Flush receive buffer

       rc = vcomSetRegister(serport, VCOM_REG_RCVBUF, 0);

       if (rc != 0) {

               deb_printf("Error: luaSerFlush()", "vcomSetRegister():rc: %d \r\n", rc);


               lua_pushnil(L);

               lua_pushnumber(L, rc); /* push result */

               return 2;

       }


// Completed

       lua_pushliteral(L, "ok");

       return 1;

}


// Return values

// 1.#boolean: state

// 2.#nil: Illegal #serdev

// the RTS and CTS are turned off and on from alternate ends to control data flow,

// for instance when a buffer is almost full.

static int luaSerGetCTS(lua_State *L) {

       int32 rc;

       struct port_data* cfPtr = check_serdev(L, 1);


       // SerGetCTS

       int8 serport = cfPtr->handle;

       if (serport < 0 || serport > SERPORT_MAX) {

               lua_pushnil(L);

               lua_pushstring(L, "Error: luaSerGetCTS(). Illegal serial device.");

               return 2; // Validate

       }

       rc = vcomGetRegister(serport, VCOM_REG_UART_CTS);

       lua_pushnumber(L, rc); /* push result */

       return 1;

}


// serialvplif.c line: 440

//        serdev:setRTS(state)

//        Parameter

//        #boolean state :


//        Return values

//        1.#integer: 0 for ok

//        2.#nil: Illegal #serdev

static int luaSerSetRTS(lua_State *L) {

       int32 rc;

       struct port_data* cfPtr = check_serdev(L, 1);

       int state = lua_toboolean(L, 2);


       // SerSetRTS

       int8 serport = cfPtr->handle;

       if (serport < 0 || serport > SERPORT_MAX) {

               lua_pushnil(L);

               lua_pushstring(L, "Error: luaSerSetRTS(). Illegal serial device.");

               return 2;

       }


       // bool myState = lua_toboolean(L, 1);

       //cfPtr->state = lua_toboolean(L, 1);

       rc = vcomSetRegister(serport, VCOM_REG_UART_RTS, state);

       lua_pushnumber(L, 0); /* push result */

       return 1;


}


static int luaSerGC(lua_State *L) {

       struct port_data* cfPtr = check_serdev(L, 1);

       vcomClose(cfPtr->handle);

       return 0;

}


static int luaSerToString(lua_State *L) {

       struct port_data* port_data;

       port_data = check_serdev(L, 1); // get the address of userdata

       lua_pushfstring(L,

       cfMetaTab " (%d, %d, %d, %d, %d, %d, %d)", port_data->handle,

                       port_data->baud, port_data->handshake, port_data->bit,

                       port_data->stop, port_data->parity, port_data->rs485);

       return 1;

}


// luaL_Reg to struct z dwoma polami: const char *name i lua_CFunction func;

// https://github.com/brimworks/lua-ev

// http://stackoverflow.com/questions/12058186/how-do-i-extend-lua-with-a-static-c-library


LUALIB_API int luaopen_serlib(lua_State *L) {

       // metamethods

       static const luaL_Reg MetaMap[] =

                       {

                               { "__tostring", luaSerToString },

                               { "__gc", luaSerGC },

                               { "bufferLevel", NULL },

                               { "close", luaSerClose },

                               { "configure", luaSerSetConfigTable },

                               { "flush", luaSerFlush },

                               { "frameConfig", NULL },

                               { "getCTS", luaSerGetCTS },

                               { "read", NULL },

                               { "setRTS", luaSerSetRTS },

                               { "write", luaSerWrite },

                               { NULL, NULL }

                       };


       static const luaL_Reg Map[] = { { "open", luaSerOpen }, { NULL, NULL } };


       lua_pop(L, 1);

       luaL_newlib(L, Map);


       luaL_newmetatable(L, cfMetaTab); // Stack: MyLib meta

       lua_pushvalue(L, -1); // Push copy of metatable

       lua_setfield(L, -2, "__index"); // Stack: MyLib meta

       luaL_setfuncs(L, MetaMap, 0);


       lua_pop(L, 1);

       /* remove _PRELOAD table */


       return 1;

}