diff --git a/Makefile b/Makefile index d4fd0fd..ca6cd1b 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,10 @@ OPTIM ?= 2 CFLAGS = -g -O$(OPTIM) -I./src/ -D_GNU_SOURCE=1 -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DPATCHLEVEL=$(PATCHLEVEL) LIBS = -lm +### lua +CFLAGS += $(shell pkg-config --cflags lua) +LIBS += $(shell pkg-config --libs lua) + ### debugger CFLAGS += $(shell pkg-config --cflags readline) LIBS += $(shell pkg-config --libs readline) diff --git a/dist/config.lua b/dist/config.lua new file mode 100644 index 0000000..65272fc --- /dev/null +++ b/dist/config.lua @@ -0,0 +1,18 @@ +-- Configuration file for x48ng + +-- local os = os + +rom = "/usr/share/x48ng/ROMs/gxrom-r" + +ram = config_dir .. "/ram" +state = config_dir .. "/state" +port1 = config_dir .. "/port1" +port2 = config_dir .. "/port2" + +-- serial_line = "/dev/tty0" + +debugger = true + +throttle = false + +frontend = "sdl" diff --git a/src/runtime_options.c b/src/runtime_options.c index b3bb8fd..3f621f3 100644 --- a/src/runtime_options.c +++ b/src/runtime_options.c @@ -5,9 +5,14 @@ #include #include #include +#include +#include #include +#include +#include + #include "runtime_options.h" char* progname = "x48ng"; @@ -23,6 +28,7 @@ int resetOnStartup = 0; char* serialLine = "/dev/ttyS0"; char* configDir = ".x48ng"; +char* config_file = "config.lua"; char* romFileName = "rom"; char* ramFileName = "ram"; char* stateFileName = "hp48"; @@ -63,12 +69,127 @@ char* largeFont = "-*-fixed-medium-r-normal-*-20-*-*-*-*-*-iso8859-1"; char* connFont = "-*-fixed-medium-r-normal-*-12-*-*-*-*-*-iso8859-1"; char normalized_config_path[ MAX_LENGTH_FILENAME ]; +char normalized_config_file[ MAX_LENGTH_FILENAME ]; char normalized_rom_path[ MAX_LENGTH_FILENAME ]; char normalized_ram_path[ MAX_LENGTH_FILENAME ]; char normalized_state_path[ MAX_LENGTH_FILENAME ]; char normalized_port1_path[ MAX_LENGTH_FILENAME ]; char normalized_port2_path[ MAX_LENGTH_FILENAME ]; +/* https://boston.conman.org/2023/09/29.1 */ +lua_State* gL; + +bool config_read( const char* conf ) +{ + int rc; + + assert( conf != NULL ); + + /*--------------------------------------------------- + ; Create the Lua state, which includes NO predefined + ; functions or values. This is literally an empty + ; slate. + ;----------------------------------------------------*/ + + gL = luaL_newstate(); + if ( gL == NULL ) { + fprintf( stderr, "cannot create Lua state" ); + return false; + } + + /*----------------------------------------------------- + ; For the truly paranoid about sandboxing, enable the + ; following code, which removes the string library, + ; which some people find problematic to leave un-sand- + ; boxed. But in my opinion, if you are worried about + ; such attacks in a configuration file, you have bigger + ; security issues to worry about than this. + ;------------------------------------------------------*/ + +#ifdef PARANOID + lua_pushliteral( gL, "x" ); + lua_pushnil( gL ); + lua_setmetatable( gL, -2 ); + lua_pop( gL, 1 ); +#endif + + /*----------------------------------------------------- + ; Lua 5.2+ can restrict scripts to being text only, + ; to avoid a potential problem with loading pre-compiled + ; Lua scripts that may have malformed Lua VM code that + ; could possibly lead to an exploit, but again, if you + ; have to worry about that, you have bigger security + ; issues to worry about. But in any case, here I'm + ; restricting the file to "text" only. + ;------------------------------------------------------*/ + + rc = luaL_loadfilex( gL, conf, "t" ); + if ( rc != LUA_OK ) { + fprintf( stderr, "Lua error: (%d) %s", rc, lua_tostring( gL, -1 ) ); + return false; + } + + rc = lua_pcall( gL, 0, 0, 0 ); + if ( rc != LUA_OK ) { + fprintf( stderr, "Lua error: (%d) %s", rc, lua_tostring( gL, -1 ) ); + return false; + } + + /*-------------------------------------------- + ; the Lua state gL contains our configuration, + ; we can now query it for values + ;---------------------------------------------*/ + lua_getglobal( gL, "rom" ); + romFileName = lua_tostring( gL, -1 ); + fprintf( stderr, "config.rom = %s\n", romFileName ); + + lua_getglobal( gL, "ram" ); + ramFileName = lua_tostring( gL, -1 ); + fprintf( stderr, "config.ram = %s\n", ramFileName ); + + lua_getglobal( gL, "state" ); + stateFileName = lua_tostring( gL, -1 ); + fprintf( stderr, "config.state = %s\n", stateFileName ); + + lua_getglobal( gL, "port1" ); + port1FileName = lua_tostring( gL, -1 ); + fprintf( stderr, "config.port1 = %s\n", port1FileName ); + + lua_getglobal( gL, "port2" ); + port2FileName = lua_tostring( gL, -1 ); + fprintf( stderr, "config.port2 = %s\n", port2FileName ); + + lua_getglobal( gL, "serial_line" ); + serialLine = lua_tostring( gL, -1 ); + fprintf( stderr, "config.serial_line = %s\n", serialLine ); + + lua_getglobal( gL, "debugger" ); + useDebugger = lua_toboolean( gL, -1 ); + fprintf( stderr, "config.debugger = %i\n", useDebugger ); + + lua_getglobal( gL, "throttle" ); + throttle = lua_toboolean( gL, -1 ); + fprintf( stderr, "config.throttle = %i\n", throttle ); + + lua_getglobal( gL, "frontend" ); + const char* config_lua__frontend = lua_tostring( gL, -1 ); + fprintf( stderr, "config.frontend = %s\n", config_lua__frontend ); +#ifdef HAS_X11 + if ( strcmp( config_lua__frontend, "x11" ) == 0 ) + frontend_type = FRONTEND_X11; + else +#endif +#ifdef HAS_SDL + if ( strcmp( config_lua__frontend, "sdl" ) == 0 ) + frontend_type = FRONTEND_SDL; + else +#endif + if ( strcmp( config_lua__frontend, "text" ) == 0 ) + frontend_type = FRONTEND_TEXT; + + return true; +} + void get_absolute_config_dir( char* source, char* dest ) { char* home; @@ -97,10 +218,19 @@ void get_absolute_config_dir( char* source, char* dest ) strcat( dest, "/" ); } -static inline void normalize_filenames( void ) +static inline void normalize_filename( const char* orig, char* dest ) +{ + if ( orig[ 0 ] == '/' ) + strcpy( dest, "" ); + else + strcpy( dest, normalized_config_path ); + strcat( dest, orig ); +} + +int normalized_config_path_exist = 1; +static inline void normalize_config_dir( void ) { struct stat st; - int normalized_config_path_exist = 1; get_absolute_config_dir( configDir, normalized_config_path ); if ( verbose ) @@ -109,6 +239,16 @@ static inline void normalize_filenames( void ) if ( stat( normalized_config_path, &st ) == -1 ) if ( errno == ENOENT ) normalized_config_path_exist = 0; +} + +static inline void normalize_filenames( void ) +{ + normalize_filename( config_file, normalized_config_file ); + + normalize_filename( ramFileName, normalized_ram_path ); + normalize_filename( stateFileName, normalized_state_path ); + normalize_filename( port1FileName, normalized_port1_path ); + normalize_filename( port2FileName, normalized_port2_path ); if ( romFileName[ 0 ] == '/' ) strcpy( normalized_rom_path, "" ); @@ -122,30 +262,6 @@ static inline void normalize_filenames( void ) strcpy( normalized_rom_path, normalized_config_path ); } strcat( normalized_rom_path, romFileName ); - - if ( ramFileName[ 0 ] == '/' ) - strcpy( normalized_ram_path, "" ); - else - strcpy( normalized_ram_path, normalized_config_path ); - strcat( normalized_ram_path, ramFileName ); - - if ( stateFileName[ 0 ] == '/' ) - strcpy( normalized_state_path, "" ); - else - strcpy( normalized_state_path, normalized_config_path ); - strcat( normalized_state_path, stateFileName ); - - if ( port1FileName[ 0 ] == '/' ) - strcpy( normalized_port1_path, "" ); - else - strcpy( normalized_port1_path, normalized_config_path ); - strcat( normalized_port1_path, port1FileName ); - - if ( port2FileName[ 0 ] == '/' ) - strcpy( normalized_port2_path, "" ); - else - strcpy( normalized_port2_path, normalized_config_path ); - strcat( normalized_port2_path, port2FileName ); } int parse_args( int argc, char* argv[] ) @@ -153,9 +269,10 @@ int parse_args( int argc, char* argv[] ) int option_index; int c = '?'; - char* optstring = "c:S:u:hvVtsirT"; + char* optstring = "c:hvVtsirT"; static struct option long_options[] = { - {"config-dir", required_argument, NULL, 1000 }, + {"config", required_argument, NULL, 'c' }, + { "config-dir", required_argument, NULL, 1000 }, { "rom", required_argument, NULL, 1010 }, { "ram", required_argument, NULL, 1011 }, { "state", required_argument, NULL, 1012 }, @@ -273,6 +390,9 @@ int parse_args( int argc, char* argv[] ) fprintf( stdout, "%s %d.%d.%d\n", progname, VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL ); exit( 0 ); break; + case 'c': + config_file = optarg; + break; case 1000: configDir = optarg; break; @@ -346,6 +466,16 @@ int parse_args( int argc, char* argv[] ) fprintf( stderr, "\n" ); } + normalize_config_dir(); + + /* read config.lua */ + /* TODO: handle having no config_file */ + /* TODO: command-line options should have priority over config file's values */ + /* TODO: handle config_file being absolute or relative */ + normalize_filename( config_file, normalized_config_file ); + if ( !config_read( normalized_config_file ) ) + exit( 1 ); + normalize_filenames(); if ( verbose ) { diff --git a/src/runtime_options.h b/src/runtime_options.h index 7dc1335..bd80cd6 100644 --- a/src/runtime_options.h +++ b/src/runtime_options.h @@ -25,6 +25,7 @@ extern int frontend_type; extern char* serialLine; extern char* configDir; +extern char* config_file; extern char* romFileName; extern char* ramFileName; extern char* stateFileName; @@ -55,6 +56,7 @@ extern char* connFont; #define MAX_LENGTH_FILENAME 2048 extern char normalized_config_path[ MAX_LENGTH_FILENAME ]; +extern char normalized_config_file[ MAX_LENGTH_FILENAME ]; extern char normalized_rom_path[ MAX_LENGTH_FILENAME ]; extern char normalized_ram_path[ MAX_LENGTH_FILENAME ]; extern char normalized_state_path[ MAX_LENGTH_FILENAME ];