├── .DS_Store ├── .gitignore ├── .luarc.json ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── build.lua ├── combustion-dev-2.rockspec ├── copy-to-lua.lua ├── src ├── compile.lua ├── executables │ ├── init.lua │ ├── loaders │ │ ├── self-extract │ │ │ ├── loader.lua │ │ │ ├── miniz.lua │ │ │ └── self-extract.c │ │ └── static │ │ │ ├── compat-5.3.c │ │ │ ├── compat-5.3.h │ │ │ ├── compat-53-c.lua │ │ │ ├── loader.lua │ │ │ ├── module-template.c │ │ │ ├── module-template.lua │ │ │ └── static.c │ ├── self-extract.lua │ └── static.lua ├── main.lua ├── miniz.c ├── miniz.h ├── utilities.lua └── zip.c └── test └── small.lua /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frityet/combustion/4294fca8c00de9b1979a9d0d9f393488f3208738/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | luarocks 2 | lua 3 | lua_modules/ 4 | .luarocks/ 5 | lsp/ 6 | build/ 7 | combust-out/ 8 | .cache/ 9 | .idea/ 10 | /luarocks 11 | /lua 12 | /lua_modules 13 | /.luarocks 14 | build/ 15 | build-test/ 16 | 17 | */.DS_Store 18 | 19 | **/*.o 20 | **/*.so 21 | **/*.dylib 22 | **/*.dll 23 | -------------------------------------------------------------------------------- /.luarc.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "Lua.workspace.checkThirdParty": false, 4 | "diagnostics.groupSeverity": { 5 | "ambiguity": "Error", 6 | "await": "Error", 7 | "codestyle": "Error", 8 | "duplicate": "Hint", 9 | "global": "Error", 10 | "luadoc": "Error", 11 | "redefined": "Information", 12 | "strict": "Error", 13 | "strong": "Error", 14 | "type-check": "Error", 15 | "unbalanced": "Error", 16 | "unused": "Hint", 17 | "conventions": "Error" 18 | }, 19 | "diagnostics.disable": [ 20 | "duplicate-doc-alias", 21 | "duplicate-doc-field" 22 | ], 23 | "runtime.path": [ 24 | "src/?.lua", 25 | "src/?/init.lua", 26 | "?.lua", 27 | "?/init.lua" 28 | ], 29 | "runtime.pathStrict": true, 30 | "runtime.version": "LuaJIT", 31 | "workspace.ignoreDir": [ 32 | ".vscode", 33 | "build" 34 | ], 35 | "workspace.library": [ 36 | "/Users/frityet/Library/Application Support/Code/User/globalStorage/sumneko.lua/addonManager/addons/argparse/module/library", 37 | "/Users/frityet/Library/Application Support/Code/User/globalStorage/sumneko.lua/addonManager/addons/penlight/module/library", 38 | "${3rd}/lfs/library", 39 | // "lua_modules/share/lua/5.1/", 40 | // "lua_modules/lib/lua/5.1/" 41 | ], 42 | "workspace.useGitIgnore": false 43 | } 44 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lua", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/src/main.lua", 12 | "path": "${workspaceFolder}/lua_modules/share/lua/5.1/?.lua;${workspaceFolder}/lua_modules/share/lua/5.1/?/init.lua;${workspaceFolder}/src/?.lua;${workspaceFolder}/src/?/init.lua", 13 | "cpath": "${workspaceFolder}/lua_modules/lib/lua/5.1/?.so", 14 | "env": { 15 | "LUAROCKS_SYSCONFDIR": "/usr/local/etc/luarocks" 16 | }, 17 | "luaVersion": "luajit", 18 | "stopOnEntry": false, 19 | "luaexe": "luajit", 20 | "console": "internalConsole", 21 | "arg": [ 22 | "--sources=./src/", 23 | ], 24 | "cwd": "${workspaceFolder}", 25 | }, 26 | { 27 | "type": "lldb", 28 | "request": "launch", 29 | "name": "Launch", 30 | "program": "${workspaceFolder}/build/bin/main", 31 | "args": [ 32 | "--sources=./src/", 33 | ], 34 | "cwd": "${workspaceFolder}", 35 | "terminal": "console" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "licenser.license": "MIT", 3 | "clangd.fallbackFlags": [ 4 | "-I../../../", 5 | "-I/usr/local/include/luajit-2.1" 6 | ], 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Amrit Bhogal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Combustion 2 | 3 | ```yaml 4 | Usage: combust [-h] [--completion {bash,zsh,fish}] [-o ] 5 | [--lua ] [--luac ] [--c-compiler ] 6 | [--linker ] [-e ] [-n ] [--graphical] [-v] 7 | [{self-extract}] [-S [] ...] 8 | [-L [] ...] 9 | [-l [] ...] 10 | [-R [] ...] 11 | [--cflags [] ...] 12 | [--ldflags [] ...] 13 | 14 | Pack your lua project, and all dependencies into a single self contained file. 15 | 16 | Arguments: 17 | {self-extract} The type of project to pack. (default: self-extract) 18 | 19 | Options: 20 | -h, --help Show this help message and exit. 21 | --completion {bash,zsh,fish} 22 | Output a shell completion script for the specified shell. 23 | -o , 24 | --output-dir 25 | The output directory to write to. (default: build) 26 | -S [] ..., 27 | --source-dirs [] ... 28 | The source directory to pack. (default: .) 29 | -L [] ..., 30 | --library-dirs [] ... 31 | Location of C libraries 32 | -l [] ..., 33 | --link [] ... 34 | Libraries to statically link 35 | -R [] ..., 36 | --resource-dirs [] ... 37 | Additional resources to pack. 38 | --lua Path to the lua executable (default: /usr/local/bin/lua) 39 | --luac Path to the lua compiler, must be compatable with the lua executable. (default: ) 40 | --c-compiler 41 | C compiler to use. (default: /usr/local/opt/llvm/bin/clang) 42 | --cflags [] ... 43 | Flags to pass to the C compiler. 44 | --linker Linker to use. (default: ) 45 | --ldflags [] ... 46 | Flags to pass to the linker. 47 | -e , The entry point of the project. (default: main.lua) 48 | --entry 49 | -n , The name of the project. (default: ) 50 | --name 51 | --graphical (Windows only) Create a application which does not spawn a console window 52 | -v, --verbose Print verbose output. 53 | 54 | https://github.com/Frityet/combustion 55 | 56 | ``` 57 | -------------------------------------------------------------------------------- /build.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local TEST_DIR = "build-test" 4 | 5 | local function execute(...) 6 | local ok, err = os.execute(table.concat({...}, " ")) 7 | 8 | if not ok then 9 | error(err) 10 | end 11 | 12 | return ok 13 | end 14 | 15 | execute("rm", "-rf", TEST_DIR) 16 | 17 | print("Copying contents of self-extract...") 18 | do 19 | ---@type string 20 | local selfextract do 21 | local f = assert(io.open("src/executables/loaders/self-extract/self-extract.c", "r")) 22 | selfextract = f:read("*a") 23 | f:close() 24 | end 25 | 26 | local selfextract_lua = assert(io.open("src/executables/loaders/self-extract/loader.lua", "w+")) 27 | selfextract_lua:write(string.format("return [[\n%s\n]]", selfextract)) 28 | selfextract_lua:close() 29 | end 30 | 31 | print("Copying contents of static...") 32 | do 33 | ---@type string 34 | local selfextract do 35 | local f = assert(io.open("src/executables/loaders/static/static.c", "r")) 36 | selfextract = f:read("*a") 37 | f:close() 38 | end 39 | 40 | local selfextract_lua = assert(io.open("src/executables/loaders/static/loader.lua", "w+")) 41 | selfextract_lua:write(string.format("return [[\n%s\n]]", selfextract)) 42 | selfextract_lua:close() 43 | 44 | ---@type string 45 | local template do 46 | local f = assert(io.open("src/executables/loaders/static/module-template.c", "r")) 47 | template = f:read("*a") 48 | f:close() 49 | end 50 | 51 | local template_lua = assert(io.open("src/executables/loaders/static/module-template.lua", "w+")) 52 | template_lua:write(string.format("return [[\n%s\n]]", template)) 53 | template_lua:close() 54 | end 55 | 56 | print("Building executables...") 57 | execute("luarocks", "--lua-version=5.1", "init") 58 | execute("./luarocks", "make") 59 | 60 | local only_copy = arg[1] == "--only-copy" 61 | 62 | if not only_copy then 63 | execute("./lua_modules/bin/combust", "-S", "src", "lua_modules/share/lua/5.1/", "-Llua_modules/lib/lua/5.1", "--lua=/usr/local/bin/luajit", "-v", "-o", "build", "--name=test") 64 | end 65 | 66 | if not only_copy and arg[1] == "test" then 67 | print("Testing binary") 68 | execute("./build/bin/test "..table.concat(arg, " ").." -o build/test --name=test") 69 | end 70 | -------------------------------------------------------------------------------- /combustion-dev-2.rockspec: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: lowercase-global 2 | package = "combustion" 3 | version = "dev-2" 4 | source = { 5 | url = "git://github.com/Frityet/combustion" 6 | } 7 | description = { 8 | summary = "Tool to pack any lua project (including dependencies) into a single executable", 9 | detailed = [[ 10 | Combustion is a tool to pack any lua project (including dependencies) into a single executable. 11 | It can be used to create a single executable for your lua project, or to create a self-extracting archive. 12 | 13 | Just define a `combust-config.lua` in the root of your project (example in repo) and run the combust executable! 14 | ]], 15 | homepage = "https://github.com/Frityet/combustion", 16 | license = "MIT/X11" 17 | } 18 | dependencies = { 19 | "luafilesystem", 20 | "penlight", 21 | "argparse", 22 | "lua ~> 5.1", 23 | } 24 | build = { 25 | type = "builtin", 26 | 27 | install = { 28 | bin = { 29 | ["combust"] = "src/main.lua" 30 | } 31 | }, 32 | 33 | modules = { 34 | ["utilities"] = "src/utilities.lua", 35 | ["compile"] = "src/compile.lua", 36 | 37 | ["executables"] = "src/executables/init.lua", 38 | 39 | ["executables.self-extract"] = "src/executables/self-extract.lua", 40 | ["executables.loaders.self-extract.loader"] = "src/executables/loaders/self-extract/loader.lua", 41 | ["executables.loaders.self-extract.miniz"] = "src/executables/loaders/self-extract/miniz.lua", 42 | 43 | ["executables.static"] = "src/executables/static.lua", 44 | ["executables.loaders.static.loader"] = "src/executables/loaders/static/loader.lua", 45 | ["executables.loaders.static.module-template"] = "src/executables/loaders/static/module-template.lua", 46 | ["executables.loaders.static.compat-53-c"] = "src/executables/loaders/static/compat-53-c.lua", 47 | ["zip"] = { 48 | sources = { "src/miniz.c", "src/zip.c" }, 49 | incdirs = { "src/" } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /copy-to-lua.lua: -------------------------------------------------------------------------------- 1 | local src, dest = arg[1], arg[2] 2 | if not src or not dest then error "Must have both arguments (src and dest) provided" end 3 | 4 | 5 | if arg[3] then 6 | local src, header = src, dest 7 | dest = arg[3] 8 | 9 | local src_contents = assert(io.open(src, "rb")) 10 | local header_contents = assert(io.open(header, "rb")) 11 | local out = assert(io.open(dest, "w+b")) 12 | 13 | out:write("return {\n header = [=[", header_contents:read("*a"), "\n]=],\n") 14 | out:write(" source = [=[\n", src_contents:read("*a"), "]=]\n}") 15 | 16 | return 17 | end 18 | 19 | local contents = assert(io.open(src, "rb")) 20 | local out = assert(io.open(dest, "w+b")) 21 | 22 | out:write("return [=[\n", contents:read("*a"), "\n]=]") 23 | 24 | contents:close() 25 | out:close() 26 | -------------------------------------------------------------------------------- /src/compile.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2023 Amrit Bhogal 2 | -- 3 | -- This software is released under the MIT License. 4 | -- https://opensource.org/licenses/MIT 5 | 6 | ---@class Combustion.BuildOptions 7 | ---@field lua Lua 8 | ---@field c_compiler string? 9 | ---@field cflags string[] 10 | ---@field ldflags string[] 11 | ---@field linker string? 12 | ---@field build_dir string 13 | ---@field bin_dir string 14 | ---@field lua_source_dir string 15 | ---@field lua_files string[] 16 | ---@field luac_objects string[] 17 | ---@field lib_dir string? 18 | ---@field c_libraries string[]? 19 | ---@field link string[]? 20 | ---@field lua_libdir string? 21 | ---@field lua_incdir string? 22 | ---@field resources_dir string? 23 | ---@field resources string[]? 24 | ---@field frameworks string[]? 25 | ---@field verbose boolean 26 | ---@field graphical boolean 27 | ---@field name string 28 | ---@field entry string 29 | 30 | local directory = require("pl.dir") 31 | local path = require("pl.path") 32 | local file = require("pl.file") 33 | local utilities = require("utilities") 34 | 35 | ---@param luac string 36 | ---@param src string 37 | ---@param dst string 38 | ---@return boolean ok, string? err 39 | local function compile_lua(luac, src, dst) 40 | if luac:find("luajit") then 41 | local ok, err = utilities.programs[luac] "-b"(src)(dst)() 42 | else 43 | local ok, err = utilities.programs[luac] "-o"(dst)(src)() 44 | if not ok then return false, err end 45 | end 46 | 47 | return true 48 | end 49 | 50 | ---@param arg Combustion.Options 51 | ---@return Combustion.BuildOptions options 52 | local function validate_arguments(arg) 53 | ---@type Combustion.BuildOptions 54 | ---@diagnostic disable-next-line: missing-fields 55 | local opts = {} 56 | 57 | local ok, err = directory.makepath(arg.output_dir) 58 | if not ok then error(err) end 59 | opts.build_dir = arg.output_dir 60 | 61 | --lua source dir 62 | opts.lua_source_dir = path.join(opts.build_dir, "lua") 63 | path.rmdir(opts.lua_source_dir) 64 | ok, err = directory.makepath(opts.lua_source_dir) 65 | if not ok then error(err) end 66 | 67 | opts.bin_dir = path.join(opts.build_dir, "bin") 68 | path.rmdir(opts.bin_dir) 69 | ok, err = directory.makepath(opts.bin_dir) 70 | if not ok then error(err) end 71 | 72 | if arg.library_dirs ~= nil then 73 | opts.lib_dir = path.join(opts.build_dir, "lib") 74 | path.rmdir(opts.lib_dir) 75 | ok, err = directory.makepath(opts.lib_dir) 76 | if not ok then error(err) end 77 | end 78 | 79 | if arg.resources_dirs ~= nil then 80 | opts.resources_dir = path.join(opts.build_dir, "resources") 81 | path.rmdir(opts.resources_dir) 82 | ok, err = directory.makepath(opts.resources_dir) 83 | if not ok then error(err) end 84 | end 85 | 86 | if arg.lua == nil then 87 | local lua = utilities.find_lua() 88 | if lua == nil then error("Could not find lua") end 89 | opts.lua = lua 90 | else 91 | local luaver, err = utilities.verify_lua(arg.lua) 92 | if not luaver then error(err) end 93 | opts.lua = { 94 | interpreter = arg.lua, 95 | version = luaver, 96 | compiler = "" 97 | } 98 | end 99 | 100 | if arg.luac == nil or arg.luac == "" then 101 | if opts.lua.version == "JIT" then 102 | opts.lua.compiler = "luajit" 103 | else 104 | ok, err = utilities.find_executable("luac"..(opts.lua.version == "Other" and "" or opts.lua.version)) 105 | if not ok then error(err) end 106 | opts.lua.compiler = ok 107 | end 108 | else 109 | opts.lua.compiler = arg.luac 110 | end 111 | 112 | if arg.c_compiler == nil then 113 | warning("No C compiler found, some features may not work") 114 | else 115 | opts.c_compiler = arg.c_compiler 116 | end 117 | 118 | if opts.c_compiler and not arg.linker or arg.linker == "" then 119 | opts.linker = arg.c_compiler 120 | elseif arg.linker then 121 | opts.linker = arg.linker 122 | end 123 | 124 | opts.cflags = arg.cflags or { utilities.is_gcc_like(opts.c_compiler) and "-Os" or "/O2" } 125 | opts.ldflags = arg.ldflags or {} 126 | opts.link = arg.link or nil 127 | opts.lua_libdir = arg.lua_libdir 128 | opts.lua_incdir = arg.lua_incdir 129 | 130 | opts.entry = arg.entry 131 | opts.verbose = arg.verbose 132 | opts.graphical = arg.graphical 133 | if arg.name == "" then 134 | opts.name = arg.entry:gsub(path.extension(arg.entry), "")..(utilities.platform == "Windows" and ".exe" or "") 135 | else 136 | opts.name = arg.name 137 | end 138 | 139 | return opts 140 | end 141 | 142 | ---@param arg Combustion.Options 143 | ---@return Combustion.BuildOptions 144 | return function (arg) 145 | local opts = validate_arguments(arg) 146 | 147 | --copy lua files by recursively copying the source directory preserving root 148 | ---@type { [string] : string[] } 149 | local srcs = {} 150 | for _, dir in ipairs(arg.source_dirs) do 151 | --bad hack, but if `dir` doesnt have a trailing slash, add it, depending on the OS 152 | if dir:sub(-1) ~= path.sep then 153 | dir = dir..path.sep 154 | end 155 | srcs[dir] = utilities.find(dir, ".lua") 156 | end 157 | 158 | opts.luac_objects = {} 159 | print("Compiling lua files with "..opts.lua.compiler.."...") 160 | for k, v in pairs(srcs) do 161 | --Compile each file, making sure that the directory structure is preserved 162 | --for example, `src/main.lua` -. `build/lua/src/main.lua` 163 | --`src/executables/self-extract.lua` -. `build/lua/src/executables/self-extract.lua` 164 | for _, file in ipairs(v) do 165 | local dest = path.join(opts.lua_source_dir, (path.relpath(file, k):gsub(k, ""))) 166 | 167 | local ok, err = directory.makepath(path.dirname(dest)) 168 | if not ok then error(err) end 169 | if opts.lua.version == "JIT" then 170 | print("$ "..opts.lua.compiler.." -b "..file.." "..dest) 171 | else 172 | print("$ "..opts.lua.compiler.." -o "..dest.." "..file) 173 | end 174 | ok = compile_lua(opts.lua.compiler, file, dest) 175 | if not ok then error("Failed to compile "..file) end 176 | 177 | opts.luac_objects[#opts.luac_objects + 1] = dest 178 | end 179 | end 180 | 181 | if arg.library_dirs ~= nil then 182 | --Do the same, but for C libraries (.so on *nix, .dll on windows) 183 | local libext do 184 | if utilities.os == "windows" then 185 | libext = ".dll" 186 | else 187 | libext = ".so" --technically macOS can also take .dylib, but I'm not going to bother with that 188 | end 189 | end 190 | 191 | local srcs = {} 192 | for _, dir in ipairs(arg.library_dirs) do 193 | if dir:sub(-1) ~= path.sep then 194 | dir = dir..path.sep 195 | end 196 | srcs[dir] = utilities.find(dir, libext) 197 | end 198 | 199 | print("Copying C libraries...") 200 | for k, v in pairs(srcs) do 201 | for _, lib in ipairs(v) do 202 | local dest = path.join(opts.lib_dir, (path.relpath(lib, k):gsub(k, ""))) 203 | 204 | local ok, err = directory.makepath(path.dirname(dest)) 205 | if not ok then error(err) end 206 | ok = file.copy(lib, dest, true) 207 | if not ok then error("Failed to copy "..lib) end 208 | end 209 | end 210 | end 211 | 212 | if arg.resources_dirs ~= nil then 213 | --Do the same, but for resources 214 | local srcs = {} 215 | for _, dir in ipairs(arg.resources_dirs) do 216 | if dir:sub(-1) ~= path.sep then 217 | dir = dir..path.sep 218 | end 219 | srcs[dir] = utilities.find(dir, "") 220 | end 221 | 222 | print("Copying resources...") 223 | for k, v in pairs(srcs) do 224 | for _, res in ipairs(v) do 225 | local dest = path.join(opts.resources_dir, (path.relpath(res, k):gsub(k, ""))) 226 | 227 | local ok, err = directory.makepath(path.dirname(dest)) 228 | if not ok then error(err) end 229 | ok = file.copy(res, dest, true) 230 | if not ok then error("Failed to copy "..res) end 231 | end 232 | end 233 | end 234 | 235 | return opts 236 | end 237 | -------------------------------------------------------------------------------- /src/executables/init.lua: -------------------------------------------------------------------------------- 1 | ---@type { [string] : fun(args: Combustion.BuildOptions) } 2 | local packages = { 3 | ["self-extract"] = require("executables.self-extract"), 4 | ["static"] = require("executables.static") 5 | } 6 | 7 | return packages 8 | -------------------------------------------------------------------------------- /src/executables/loaders/self-extract/loader.lua: -------------------------------------------------------------------------------- 1 | return [[ 2 | /** 3 | * Copyright (c) 2023 Amrit Bhogal 4 | * 5 | * This software is released under the MIT License. 6 | * https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "miniz.h" 17 | //Just here for intellisense, it will be replaced by the build script 18 | #if !defined(COMBUSTION_ENTRY) 19 | # define COMBUSTION_ENTRY "" 20 | #endif 21 | #define _STR(x) #x 22 | #define STR(x) _STR(x) 23 | #define LUA_BOOTSTRAP "package.path=\"%s/lua/?.lua;%s/lua/?/init.lua\"\n"\ 24 | "package.cpath=\"%s/lib/?"DLL_EXT"\"\n"\ 25 | "dofile(\"%s\")\n"\ 26 | "os.exit()" 27 | 28 | #if defined(_WIN32) 29 | # include 30 | # include 31 | # define mkdir(path, mode) _mkdir(path) 32 | # define getcwd(buf, size) _getcwd(buf, size) 33 | # define mkstemp(buf) _mktemp(buf) 34 | # define stat(path, buf) _stat(path, buf) 35 | # define spawnv(mode, path, argv) _spawnv(mode, path, argv) 36 | # define rmdir(path) _rmdir(path) 37 | # define PATH_SEPARATOR '\\' 38 | # define PATH_MAX MAX_PATH 39 | # define DLL_EXT ".dll" 40 | #else 41 | # include 42 | # include 43 | # include 44 | # define PATH_SEPARATOR '/' 45 | # if(defined(__linux__)) 46 | # include 47 | # else 48 | # include 49 | # endif 50 | # define DLL_EXT ".so" 51 | #endif 52 | 53 | extern uint8_t zipfile[]; 54 | extern size_t zipfile_size; 55 | 56 | static void perror_f(const char *fmt, ...) 57 | { 58 | va_list args; 59 | va_start(args, fmt); 60 | vfprintf(stderr, fmt, args); 61 | va_end(args); 62 | perror(""); 63 | } 64 | #define error(fmt, ...) do { perror_f("["__FILE__":"STR(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); exit(1); } while (0) 65 | 66 | bool debug_output; 67 | static void debug_f(const char *fmt, ...) 68 | { 69 | if (!debug_output) 70 | return; 71 | va_list args; 72 | va_start(args, fmt); 73 | vfprintf(stderr, fmt, args); 74 | va_end(args); 75 | } 76 | #define debug(fmt, ...) do { debug_f("["__FILE__":"STR(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); } while (0) 77 | 78 | 79 | /* recursive mkdir */ 80 | //Taken from https://gist.github.com/ChisholmKyle/0cbedcd3e64132243a39 81 | int mkdir_p(const char dir[static PATH_MAX]) { 82 | char tmp[PATH_MAX] = {0}; 83 | char *p = NULL; 84 | struct stat sb; 85 | size_t len; 86 | 87 | /* copy path */ 88 | len = strnlen (dir, PATH_MAX); 89 | if (len == 0 || len == PATH_MAX) { 90 | return -1; 91 | } 92 | memcpy (tmp, dir, len); 93 | tmp[len] = '\0'; 94 | 95 | /* remove trailing slash */ 96 | if(tmp[len - 1] == '/') { 97 | tmp[len - 1] = '\0'; 98 | } 99 | 100 | /* check if path exists and is a directory */ 101 | if (stat (tmp, &sb) == 0) { 102 | if (S_ISDIR (sb.st_mode)) { 103 | return 0; 104 | } 105 | } 106 | 107 | /* recursive mkdir */ 108 | for(p = tmp + 1; *p; p++) { 109 | if(*p == '/') { 110 | *p = 0; 111 | /* test path */ 112 | if (stat(tmp, &sb) != 0) { 113 | /* path does not exist - create directory */ 114 | if (mkdir(tmp, 0777) < 0) { 115 | return -1; 116 | } 117 | } else if (!S_ISDIR(sb.st_mode)) { 118 | /* not a directory */ 119 | return -1; 120 | } 121 | *p = '/'; 122 | } 123 | } 124 | /* test path */ 125 | if (stat(tmp, &sb) != 0) { 126 | /* path does not exist - create directory */ 127 | if (mkdir(tmp, 0777) < 0) { 128 | return -1; 129 | } 130 | } else if (!S_ISDIR(sb.st_mode)) { 131 | /* not a directory */ 132 | return -1; 133 | } 134 | return 0; 135 | } 136 | 137 | static char *dirname(char *path) 138 | { 139 | char *last_slash = strrchr(path, PATH_SEPARATOR); 140 | 141 | if (last_slash != NULL) 142 | *last_slash = '\0'; 143 | 144 | return path; 145 | } 146 | 147 | 148 | static int unzip_recursive(mz_zip_archive *zip, const char dir[static PATH_MAX]) 149 | { 150 | size_t num_files = mz_zip_reader_get_num_files(zip); 151 | for (size_t i = 0; i < num_files; i++) { 152 | mz_zip_archive_file_stat file_stat; 153 | if (!mz_zip_reader_file_stat(zip, i, &file_stat)) { 154 | error("Error getting file stat: %s\n", mz_zip_get_error_string(mz_zip_get_last_error(zip))); 155 | return 1; 156 | } 157 | 158 | //Get the path to the file in the zip, then create parent directories, then extract the file 159 | char outf[PATH_MAX] = {0}; 160 | snprintf(outf, PATH_MAX, "%s%c%s\n", dir, PATH_SEPARATOR, file_stat.m_filename); 161 | 162 | if (file_stat.m_is_directory) { 163 | if (mkdir_p(outf) != 0) { 164 | error("Error creating directory: %s\n", outf); 165 | return 1; 166 | } 167 | } else { 168 | //Mutates outf, but the filename is still there 169 | if (mkdir_p(dirname(outf)) != 0) { 170 | error("Error creating directory: %s\n", outf); 171 | return 1; 172 | } 173 | 174 | 175 | //So just remove the null terminator and add the filename back 176 | outf[strlen(outf)] = PATH_SEPARATOR; 177 | outf[strlen(outf) - 1] = '\0'; //For some reason the last char seems to be a newline? 178 | if (mz_zip_reader_extract_to_file(zip, i, outf, 0) != MZ_TRUE) { 179 | perror_f("Error extracting file: %s\n", outf); 180 | return 1; 181 | } 182 | } 183 | } 184 | return 0; 185 | } 186 | 187 | 188 | static int run_lua(const char tmpdir[const PATH_MAX], int argc, char *argv[static argc]) 189 | { 190 | #if defined(_WIN32) 191 | char lua_path[PATH_MAX] = {0}; 192 | snprintf(lua_path, PATH_MAX, "%s\\bin\\lua.exe", tmpdir); 193 | char entrypoint_path[PATH_MAX] = {0}; 194 | snprintf(entrypoint_path, PATH_MAX, "%s\\lua\\%s", tmpdir, STR(COMBUSTION_ENTRY)); 195 | 196 | char bootstrap[sizeof(LUA_BOOTSTRAP) + PATH_MAX * 4] = {0}; 197 | snprintf(bootstrap, sizeof(bootstrap), LUA_BOOTSTRAP, tmpdir, tmpdir, tmpdir, entrypoint_path); 198 | 199 | //argv[0] is the path to the lua interpreter, argv[1] is the path to the entrypoint file 200 | char **args = calloc(argc + 2 + 2, sizeof(char *)); 201 | memcpy(args, (char *[]) { 202 | lua_path, "-e", bootstrap, entrypoint_path 203 | }, 4 * sizeof(char *)); 204 | memcpy(args + 4, argv + 1, argc * sizeof(char *)); 205 | 206 | //Spawn the lua interpreter 207 | int ret = spawnv(P_WAIT, lua_path, args); 208 | free(args); 209 | return ret; 210 | #else 211 | char lua_path[PATH_MAX] = {0}; 212 | snprintf(lua_path, PATH_MAX, "%s/bin/lua", tmpdir); 213 | debug("Lua path: %s\n", lua_path); 214 | chmod(lua_path, 0755); //Make sure the lua interpreter is executable 215 | char entrypoint_path[PATH_MAX] = {0}; 216 | snprintf(entrypoint_path, PATH_MAX, "%s/lua/%s", tmpdir, STR(COMBUSTION_ENTRY)); 217 | debug("Entrypoint path: %s\n", entrypoint_path); 218 | 219 | char bootstrap[sizeof(LUA_BOOTSTRAP) + PATH_MAX * 4] = {0}; 220 | snprintf(bootstrap, sizeof(bootstrap), LUA_BOOTSTRAP, tmpdir, tmpdir, tmpdir, entrypoint_path); 221 | 222 | //argv[0] is the path to the lua interpreter, argv[1] is the path to the entrypoint file 223 | char **args = calloc(argc + 2 + 2, sizeof(char *)); 224 | memcpy(args, (char *[]) { 225 | lua_path, "-e", bootstrap, entrypoint_path 226 | }, 4 * sizeof(char *)); 227 | memcpy(args + 4, argv + 1, argc * sizeof(char *)); 228 | 229 | int ret = execv(lua_path, args); 230 | free(args); 231 | if (ret == -1) 232 | error("Error executing lua: %s\n", lua_path); 233 | 234 | return ret; 235 | #endif 236 | } 237 | 238 | unsigned long gettmpdir(char buf[static PATH_MAX]) 239 | { 240 | unsigned long id = 0; 241 | #if defined(_WIN32) 242 | id = GetCurrentProcessId(); 243 | DWORD len = GetTempPathA(PATH_MAX, buf); 244 | if (len == 0 || len > PATH_MAX) { 245 | error("Error getting temp path: %d\n", GetLastError()); 246 | return NULL; 247 | } 248 | #else 249 | id = getpid(); 250 | const char *tmpdir = NULL, **env_vars = NULL; 251 | for (env_vars = (const char *[]){ "TMPDIR", "TMP", "TEMP", "TEMPDIR", NULL }, tmpdir = getenv(*env_vars); 252 | tmpdir == NULL && *++env_vars; 253 | tmpdir = getenv(*env_vars)); 254 | 255 | if (tmpdir == NULL) { 256 | puts("Couldn't get with an env call"); 257 | snprintf(buf, PATH_MAX, "/tmpd"); //the d is a shitty workaround to me being too lazy to fix lines 264-270. :) 258 | tmpdir = buf; 259 | } 260 | #endif 261 | size_t len = strnlen(tmpdir, PATH_MAX); 262 | memcpy(buf, tmpdir, len); 263 | 264 | //Finally, append the id to the path 265 | snprintf(&buf[len - 1], PATH_MAX - len - 1, "%ccombustion-%lu", PATH_SEPARATOR, id); 266 | mkdir_p(buf); 267 | return id; 268 | } 269 | #if defined(WIN32_GRAPHICAL) 270 | static int main(int argc, const char *argv[const argc]); 271 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 272 | { 273 | int argc; 274 | char **argv = CommandLineToArgvA(GetCommandLineA(), &argc); 275 | return main(argc, argv); 276 | } 277 | static 278 | #endif 279 | //This program will be run when the executable is run, and it will unpack the zip file to tmpdir, and run the lua interpreter on the entrypoint file 280 | int main(int argc, char *argv[static argc]) 281 | { 282 | debug_output = getenv("COMBUSTION_LOADER_VERBOSE") != NULL; 283 | srand(time(NULL)); 284 | 285 | //Directory setup 286 | char cwd[PATH_MAX] = {0}, tmpdir[PATH_MAX] = {0}; 287 | getcwd(cwd, PATH_MAX); 288 | unsigned long id = gettmpdir(tmpdir); 289 | debug("tmpdir: %s (id: %ul)\n", tmpdir, id); 290 | 291 | mz_zip_archive zip_archive = {0}; 292 | 293 | #define ZIP_ERR(archive) (mz_zip_get_error_string(mz_zip_get_last_error(archive))) 294 | //Open the zip file 295 | if (!mz_zip_reader_init_mem(&zip_archive, zipfile, zipfile_size, 0)) { 296 | error("Error opening zip file: %s\n", ZIP_ERR(&zip_archive)); 297 | return EXIT_FAILURE; 298 | } 299 | 300 | //Extract the zip file, and create the directories preserving the file structure 301 | if (unzip_recursive(&zip_archive, tmpdir) != 0) { 302 | error("Error extracting zip file: %s\n", ZIP_ERR(&zip_archive)); 303 | return EXIT_FAILURE; 304 | } 305 | #undef ZIP_ERR 306 | //Close the zip file 307 | mz_zip_reader_end(&zip_archive); 308 | 309 | debug("Running lua with args: "); 310 | for (int i = 0; i < argc; i++) 311 | debug(" [%d] = \"%s\"\n", i, argv[i]); 312 | 313 | //Run the lua interpreter located in `tmpdir`/bin/lua[.exe] 314 | int ret = run_lua(tmpdir, argc, argv); 315 | rmdir(tmpdir); 316 | } 317 | 318 | ]] -------------------------------------------------------------------------------- /src/executables/loaders/self-extract/self-extract.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2023 Amrit Bhogal 3 | * 4 | * This software is released under the MIT License. 5 | * https://opensource.org/licenses/MIT 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "miniz.h" 16 | //Just here for intellisense, it will be replaced by the build script 17 | #if !defined(COMBUSTION_ENTRY) 18 | # define COMBUSTION_ENTRY "" 19 | #endif 20 | #define _STR(x) #x 21 | #define STR(x) _STR(x) 22 | #define LUA_BOOTSTRAP "package.path=\"%s/lua/?.lua;%s/lua/?/init.lua\"\n"\ 23 | "package.cpath=\"%s/lib/?"DLL_EXT"\"\n"\ 24 | "dofile(\"%s\")\n"\ 25 | "os.exit()" 26 | 27 | #if defined(_WIN32) 28 | # include 29 | # include 30 | # define mkdir(path, mode) _mkdir(path) 31 | # define getcwd(buf, size) _getcwd(buf, size) 32 | # define mkstemp(buf) _mktemp(buf) 33 | # define stat(path, buf) _stat(path, buf) 34 | # define spawnv(mode, path, argv) _spawnv(mode, path, argv) 35 | # define rmdir(path) _rmdir(path) 36 | # define PATH_SEPARATOR '\\' 37 | # define PATH_MAX MAX_PATH 38 | # define DLL_EXT ".dll" 39 | #else 40 | # include 41 | # include 42 | # include 43 | # define PATH_SEPARATOR '/' 44 | # if(defined(__linux__)) 45 | # include 46 | # else 47 | # include 48 | # endif 49 | # define DLL_EXT ".so" 50 | #endif 51 | 52 | extern uint8_t zipfile[]; 53 | extern size_t zipfile_size; 54 | 55 | static void perror_f(const char *fmt, ...) 56 | { 57 | va_list args; 58 | va_start(args, fmt); 59 | vfprintf(stderr, fmt, args); 60 | va_end(args); 61 | perror(""); 62 | } 63 | #define error(fmt, ...) do { perror_f("["__FILE__":"STR(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); exit(1); } while (0) 64 | 65 | bool debug_output; 66 | static void debug_f(const char *fmt, ...) 67 | { 68 | if (!debug_output) 69 | return; 70 | va_list args; 71 | va_start(args, fmt); 72 | vfprintf(stderr, fmt, args); 73 | va_end(args); 74 | } 75 | #define debug(fmt, ...) do { debug_f("["__FILE__":"STR(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); } while (0) 76 | 77 | 78 | /* recursive mkdir */ 79 | //Taken from https://gist.github.com/ChisholmKyle/0cbedcd3e64132243a39 80 | int mkdir_p(const char dir[static PATH_MAX]) { 81 | char tmp[PATH_MAX] = {0}; 82 | char *p = NULL; 83 | struct stat sb; 84 | size_t len; 85 | 86 | /* copy path */ 87 | len = strnlen (dir, PATH_MAX); 88 | if (len == 0 || len == PATH_MAX) { 89 | return -1; 90 | } 91 | memcpy (tmp, dir, len); 92 | tmp[len] = '\0'; 93 | 94 | /* remove trailing slash */ 95 | if(tmp[len - 1] == '/') { 96 | tmp[len - 1] = '\0'; 97 | } 98 | 99 | /* check if path exists and is a directory */ 100 | if (stat (tmp, &sb) == 0) { 101 | if (S_ISDIR (sb.st_mode)) { 102 | return 0; 103 | } 104 | } 105 | 106 | /* recursive mkdir */ 107 | for(p = tmp + 1; *p; p++) { 108 | if(*p == '/') { 109 | *p = 0; 110 | /* test path */ 111 | if (stat(tmp, &sb) != 0) { 112 | /* path does not exist - create directory */ 113 | if (mkdir(tmp, 0777) < 0) { 114 | return -1; 115 | } 116 | } else if (!S_ISDIR(sb.st_mode)) { 117 | /* not a directory */ 118 | return -1; 119 | } 120 | *p = '/'; 121 | } 122 | } 123 | /* test path */ 124 | if (stat(tmp, &sb) != 0) { 125 | /* path does not exist - create directory */ 126 | if (mkdir(tmp, 0777) < 0) { 127 | return -1; 128 | } 129 | } else if (!S_ISDIR(sb.st_mode)) { 130 | /* not a directory */ 131 | return -1; 132 | } 133 | return 0; 134 | } 135 | 136 | static char *dirname(char *path) 137 | { 138 | char *last_slash = strrchr(path, PATH_SEPARATOR); 139 | 140 | if (last_slash != NULL) 141 | *last_slash = '\0'; 142 | 143 | return path; 144 | } 145 | 146 | 147 | static int unzip_recursive(mz_zip_archive *zip, const char dir[static PATH_MAX]) 148 | { 149 | size_t num_files = mz_zip_reader_get_num_files(zip); 150 | for (size_t i = 0; i < num_files; i++) { 151 | mz_zip_archive_file_stat file_stat; 152 | if (!mz_zip_reader_file_stat(zip, i, &file_stat)) { 153 | error("Error getting file stat: %s\n", mz_zip_get_error_string(mz_zip_get_last_error(zip))); 154 | return 1; 155 | } 156 | 157 | //Get the path to the file in the zip, then create parent directories, then extract the file 158 | char outf[PATH_MAX] = {0}; 159 | snprintf(outf, PATH_MAX, "%s%c%s\n", dir, PATH_SEPARATOR, file_stat.m_filename); 160 | 161 | if (file_stat.m_is_directory) { 162 | if (mkdir_p(outf) != 0) { 163 | error("Error creating directory: %s\n", outf); 164 | return 1; 165 | } 166 | } else { 167 | //Mutates outf, but the filename is still there 168 | if (mkdir_p(dirname(outf)) != 0) { 169 | error("Error creating directory: %s\n", outf); 170 | return 1; 171 | } 172 | 173 | 174 | //So just remove the null terminator and add the filename back 175 | outf[strlen(outf)] = PATH_SEPARATOR; 176 | outf[strlen(outf) - 1] = '\0'; //For some reason the last char seems to be a newline? 177 | if (mz_zip_reader_extract_to_file(zip, i, outf, 0) != MZ_TRUE) { 178 | perror_f("Error extracting file: %s\n", outf); 179 | return 1; 180 | } 181 | } 182 | } 183 | return 0; 184 | } 185 | 186 | 187 | static int run_lua(const char tmpdir[const PATH_MAX], int argc, char *argv[static argc]) 188 | { 189 | #if defined(_WIN32) 190 | char lua_path[PATH_MAX] = {0}; 191 | snprintf(lua_path, PATH_MAX, "%s\\bin\\lua.exe", tmpdir); 192 | char entrypoint_path[PATH_MAX] = {0}; 193 | snprintf(entrypoint_path, PATH_MAX, "%s\\lua\\%s", tmpdir, STR(COMBUSTION_ENTRY)); 194 | 195 | char bootstrap[sizeof(LUA_BOOTSTRAP) + PATH_MAX * 4] = {0}; 196 | snprintf(bootstrap, sizeof(bootstrap), LUA_BOOTSTRAP, tmpdir, tmpdir, tmpdir, entrypoint_path); 197 | 198 | //argv[0] is the path to the lua interpreter, argv[1] is the path to the entrypoint file 199 | char **args = calloc(argc + 2 + 2, sizeof(char *)); 200 | memcpy(args, (char *[]) { 201 | lua_path, "-e", bootstrap, entrypoint_path 202 | }, 4 * sizeof(char *)); 203 | memcpy(args + 4, argv + 1, argc * sizeof(char *)); 204 | 205 | //Spawn the lua interpreter 206 | int ret = spawnv(P_WAIT, lua_path, args); 207 | free(args); 208 | return ret; 209 | #else 210 | char lua_path[PATH_MAX] = {0}; 211 | snprintf(lua_path, PATH_MAX, "%s/bin/lua", tmpdir); 212 | debug("Lua path: %s\n", lua_path); 213 | chmod(lua_path, 0755); //Make sure the lua interpreter is executable 214 | char entrypoint_path[PATH_MAX] = {0}; 215 | snprintf(entrypoint_path, PATH_MAX, "%s/lua/%s", tmpdir, STR(COMBUSTION_ENTRY)); 216 | debug("Entrypoint path: %s\n", entrypoint_path); 217 | 218 | char bootstrap[sizeof(LUA_BOOTSTRAP) + PATH_MAX * 4] = {0}; 219 | snprintf(bootstrap, sizeof(bootstrap), LUA_BOOTSTRAP, tmpdir, tmpdir, tmpdir, entrypoint_path); 220 | 221 | //argv[0] is the path to the lua interpreter, argv[1] is the path to the entrypoint file 222 | char **args = calloc(argc + 2 + 2, sizeof(char *)); 223 | memcpy(args, (char *[]) { 224 | lua_path, "-e", bootstrap, entrypoint_path 225 | }, 4 * sizeof(char *)); 226 | memcpy(args + 4, argv + 1, argc * sizeof(char *)); 227 | 228 | int ret = execv(lua_path, args); 229 | free(args); 230 | if (ret == -1) 231 | error("Error executing lua: %s\n", lua_path); 232 | 233 | return ret; 234 | #endif 235 | } 236 | 237 | unsigned long gettmpdir(char buf[static PATH_MAX]) 238 | { 239 | unsigned long id = 0; 240 | #if defined(_WIN32) 241 | id = GetCurrentProcessId(); 242 | DWORD len = GetTempPathA(PATH_MAX, buf); 243 | if (len == 0 || len > PATH_MAX) { 244 | error("Error getting temp path: %d\n", GetLastError()); 245 | return NULL; 246 | } 247 | #else 248 | id = getpid(); 249 | const char *tmpdir = NULL, **env_vars = NULL; 250 | for (env_vars = (const char *[]){ "TMPDIR", "TMP", "TEMP", "TEMPDIR", NULL }, tmpdir = getenv(*env_vars); 251 | tmpdir == NULL && *++env_vars; 252 | tmpdir = getenv(*env_vars)); 253 | 254 | if (tmpdir == NULL) { 255 | puts("Couldn't get with an env call"); 256 | snprintf(buf, PATH_MAX, "/tmpd"); //the d is a shitty workaround to me being too lazy to fix lines 264-270. :) 257 | tmpdir = buf; 258 | } 259 | #endif 260 | size_t len = strnlen(tmpdir, PATH_MAX); 261 | memcpy(buf, tmpdir, len); 262 | 263 | //Finally, append the id to the path 264 | snprintf(&buf[len - 1], PATH_MAX - len - 1, "%ccombustion-%lu", PATH_SEPARATOR, id); 265 | mkdir_p(buf); 266 | return id; 267 | } 268 | #if defined(WIN32_GRAPHICAL) 269 | static int main(int argc, const char *argv[const argc]); 270 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 271 | { 272 | int argc; 273 | char **argv = CommandLineToArgvA(GetCommandLineA(), &argc); 274 | return main(argc, argv); 275 | } 276 | static 277 | #endif 278 | //This program will be run when the executable is run, and it will unpack the zip file to tmpdir, and run the lua interpreter on the entrypoint file 279 | int main(int argc, char *argv[static argc]) 280 | { 281 | debug_output = getenv("COMBUSTION_LOADER_VERBOSE") != NULL; 282 | srand(time(NULL)); 283 | 284 | //Directory setup 285 | char cwd[PATH_MAX] = {0}, tmpdir[PATH_MAX] = {0}; 286 | getcwd(cwd, PATH_MAX); 287 | unsigned long id = gettmpdir(tmpdir); 288 | debug("tmpdir: %s (id: %ul)\n", tmpdir, id); 289 | 290 | mz_zip_archive zip_archive = {0}; 291 | 292 | #define ZIP_ERR(archive) (mz_zip_get_error_string(mz_zip_get_last_error(archive))) 293 | //Open the zip file 294 | if (!mz_zip_reader_init_mem(&zip_archive, zipfile, zipfile_size, 0)) { 295 | error("Error opening zip file: %s\n", ZIP_ERR(&zip_archive)); 296 | return EXIT_FAILURE; 297 | } 298 | 299 | //Extract the zip file, and create the directories preserving the file structure 300 | if (unzip_recursive(&zip_archive, tmpdir) != 0) { 301 | error("Error extracting zip file: %s\n", ZIP_ERR(&zip_archive)); 302 | return EXIT_FAILURE; 303 | } 304 | #undef ZIP_ERR 305 | //Close the zip file 306 | mz_zip_reader_end(&zip_archive); 307 | 308 | debug("Running lua with args: "); 309 | for (int i = 0; i < argc; i++) 310 | debug(" [%d] = \"%s\"\n", i, argv[i]); 311 | 312 | //Run the lua interpreter located in `tmpdir`/bin/lua[.exe] 313 | int ret = run_lua(tmpdir, argc, argv); 314 | rmdir(tmpdir); 315 | } 316 | -------------------------------------------------------------------------------- /src/executables/loaders/static/compat-5.3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "compat-5.3.h" 8 | 9 | /* don't compile it again if it already is included via compat53.h */ 10 | #ifndef COMPAT53_C_ 11 | #define COMPAT53_C_ 12 | 13 | 14 | 15 | /* definitions for Lua 5.1 only */ 16 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 17 | 18 | #ifndef COMPAT53_FOPEN_NO_LOCK 19 | # if defined(_MSC_VER) 20 | # define COMPAT53_FOPEN_NO_LOCK 1 21 | # else /* otherwise */ 22 | # define COMPAT53_FOPEN_NO_LOCK 0 23 | # endif /* VC++ only so far */ 24 | #endif /* No-lock fopen_s usage if possible */ 25 | 26 | #if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK 27 | # include 28 | #endif /* VC++ _fsopen for share-allowed file read */ 29 | 30 | #ifndef COMPAT53_HAVE_STRERROR_R 31 | # if (!defined(_WIN32)) && \ 32 | ((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ 33 | (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \ 34 | defined(__APPLE__)) 35 | # define COMPAT53_HAVE_STRERROR_R 1 36 | # else /* none of the defines matched: define to 0 */ 37 | # define COMPAT53_HAVE_STRERROR_R 0 38 | # endif /* have strerror_r of some form */ 39 | #endif /* strerror_r */ 40 | 41 | #ifndef COMPAT53_HAVE_STRERROR_S 42 | # if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \ 43 | defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__) 44 | # define COMPAT53_HAVE_STRERROR_S 1 45 | # else /* not VC++ or C11 */ 46 | # define COMPAT53_HAVE_STRERROR_S 0 47 | # endif /* strerror_s from VC++ or C11 */ 48 | #endif /* strerror_s */ 49 | 50 | #ifndef COMPAT53_LUA_FILE_BUFFER_SIZE 51 | # define COMPAT53_LUA_FILE_BUFFER_SIZE 4096 52 | #endif /* Lua File Buffer Size */ 53 | 54 | 55 | static char* compat53_strerror (int en, char* buff, size_t sz) { 56 | #if COMPAT53_HAVE_STRERROR_R 57 | /* use strerror_r here, because it's available on these specific platforms */ 58 | if (sz > 0) { 59 | buff[0] = '\0'; 60 | /* we don't care whether the GNU version or the XSI version is used: */ 61 | if (strerror_r(en, buff, sz)) { 62 | /* Yes, we really DO want to ignore the return value! 63 | * GCC makes that extra hard, not even a (void) cast will do. */ 64 | } 65 | if (buff[0] == '\0') { 66 | /* Buffer is unchanged, so we probably have called GNU strerror_r which 67 | * returned a static constant string. Chances are that strerror will 68 | * return the same static constant string and therefore be thread-safe. */ 69 | return strerror(en); 70 | } 71 | } 72 | return buff; /* sz is 0 *or* strerror_r wrote into the buffer */ 73 | #elif COMPAT53_HAVE_STRERROR_S 74 | /* for MSVC and other C11 implementations, use strerror_s since it's 75 | * provided by default by the libraries */ 76 | strerror_s(buff, sz, en); 77 | return buff; 78 | #else 79 | /* fallback, but strerror is not guaranteed to be threadsafe due to modifying 80 | * errno itself and some impls not locking a static buffer for it ... but most 81 | * known systems have threadsafe errno: this might only change if the locale 82 | * is changed out from under someone while this function is being called */ 83 | (void)buff; 84 | (void)sz; 85 | return strerror(en); 86 | #endif 87 | } 88 | 89 | 90 | COMPAT53_API int lua_absindex (lua_State *L, int i) { 91 | if (i < 0 && i > LUA_REGISTRYINDEX) 92 | i += lua_gettop(L) + 1; 93 | return i; 94 | } 95 | 96 | 97 | static void compat53_call_lua (lua_State *L, char const code[], size_t len, 98 | int nargs, int nret) { 99 | lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code); 100 | if (lua_type(L, -1) != LUA_TFUNCTION) { 101 | lua_pop(L, 1); 102 | if (luaL_loadbuffer(L, code, len, "=none")) 103 | lua_error(L); 104 | lua_pushvalue(L, -1); 105 | lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code); 106 | } 107 | lua_insert(L, -nargs-1); 108 | lua_call(L, nargs, nret); 109 | } 110 | 111 | 112 | static const char compat53_arith_code[] = 113 | "local op,a,b=...\n" 114 | "if op==0 then return a+b\n" 115 | "elseif op==1 then return a-b\n" 116 | "elseif op==2 then return a*b\n" 117 | "elseif op==3 then return a/b\n" 118 | "elseif op==4 then return a%b\n" 119 | "elseif op==5 then return a^b\n" 120 | "elseif op==6 then return -a\n" 121 | "end\n"; 122 | 123 | COMPAT53_API void lua_arith (lua_State *L, int op) { 124 | if (op < LUA_OPADD || op > LUA_OPUNM) 125 | luaL_error(L, "invalid 'op' argument for lua_arith"); 126 | luaL_checkstack(L, 5, "not enough stack slots"); 127 | if (op == LUA_OPUNM) 128 | lua_pushvalue(L, -1); 129 | lua_pushnumber(L, op); 130 | lua_insert(L, -3); 131 | compat53_call_lua(L, compat53_arith_code, 132 | sizeof(compat53_arith_code)-1, 3, 1); 133 | } 134 | 135 | 136 | static const char compat53_compare_code[] = 137 | "local a,b=...\n" 138 | "return a<=b\n"; 139 | 140 | COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) { 141 | int result = 0; 142 | switch (op) { 143 | case LUA_OPEQ: 144 | return lua_equal(L, idx1, idx2); 145 | case LUA_OPLT: 146 | return lua_lessthan(L, idx1, idx2); 147 | case LUA_OPLE: 148 | luaL_checkstack(L, 5, "not enough stack slots"); 149 | idx1 = lua_absindex(L, idx1); 150 | idx2 = lua_absindex(L, idx2); 151 | lua_pushvalue(L, idx1); 152 | lua_pushvalue(L, idx2); 153 | compat53_call_lua(L, compat53_compare_code, 154 | sizeof(compat53_compare_code)-1, 2, 1); 155 | result = lua_toboolean(L, -1); 156 | lua_pop(L, 1); 157 | return result; 158 | default: 159 | luaL_error(L, "invalid 'op' argument for lua_compare"); 160 | } 161 | return 0; 162 | } 163 | 164 | 165 | COMPAT53_API void lua_copy (lua_State *L, int from, int to) { 166 | int abs_to = lua_absindex(L, to); 167 | luaL_checkstack(L, 1, "not enough stack slots"); 168 | lua_pushvalue(L, from); 169 | lua_replace(L, abs_to); 170 | } 171 | 172 | 173 | COMPAT53_API void lua_len (lua_State *L, int i) { 174 | switch (lua_type(L, i)) { 175 | case LUA_TSTRING: 176 | lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); 177 | break; 178 | case LUA_TTABLE: 179 | if (!luaL_callmeta(L, i, "__len")) 180 | lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); 181 | break; 182 | case LUA_TUSERDATA: 183 | if (luaL_callmeta(L, i, "__len")) 184 | break; 185 | /* FALLTHROUGH */ 186 | default: 187 | luaL_error(L, "attempt to get length of a %s value", 188 | lua_typename(L, lua_type(L, i))); 189 | } 190 | } 191 | 192 | 193 | COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) { 194 | int abs_i = lua_absindex(L, i); 195 | lua_pushlightuserdata(L, (void*)p); 196 | lua_rawget(L, abs_i); 197 | return lua_type(L, -1); 198 | } 199 | 200 | COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) { 201 | int abs_i = lua_absindex(L, i); 202 | luaL_checkstack(L, 1, "not enough stack slots"); 203 | lua_pushlightuserdata(L, (void*)p); 204 | lua_insert(L, -2); 205 | lua_rawset(L, abs_i); 206 | } 207 | 208 | 209 | COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) { 210 | lua_Number n = lua_tonumber(L, i); 211 | if (isnum != NULL) { 212 | *isnum = (n != 0 || lua_isnumber(L, i)); 213 | } 214 | return n; 215 | } 216 | 217 | 218 | COMPAT53_API void luaL_checkversion (lua_State *L) { 219 | (void)L; 220 | } 221 | 222 | 223 | COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) { 224 | if (!lua_checkstack(L, sp+LUA_MINSTACK)) { 225 | if (msg != NULL) 226 | luaL_error(L, "stack overflow (%s)", msg); 227 | else { 228 | lua_pushliteral(L, "stack overflow"); 229 | lua_error(L); 230 | } 231 | } 232 | } 233 | 234 | 235 | COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) { 236 | int abs_i = lua_absindex(L, i); 237 | luaL_checkstack(L, 3, "not enough stack slots"); 238 | lua_pushstring(L, name); 239 | lua_gettable(L, abs_i); 240 | if (lua_istable(L, -1)) 241 | return 1; 242 | lua_pop(L, 1); 243 | lua_newtable(L); 244 | lua_pushstring(L, name); 245 | lua_pushvalue(L, -2); 246 | lua_settable(L, abs_i); 247 | return 0; 248 | } 249 | 250 | 251 | COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) { 252 | lua_Integer res = 0; 253 | int isnum = 0; 254 | luaL_checkstack(L, 1, "not enough stack slots"); 255 | lua_len(L, i); 256 | res = lua_tointegerx(L, -1, &isnum); 257 | lua_pop(L, 1); 258 | if (!isnum) 259 | luaL_error(L, "object length is not an integer"); 260 | return res; 261 | } 262 | 263 | 264 | COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { 265 | luaL_checkstack(L, nup+1, "too many upvalues"); 266 | for (; l->name != NULL; l++) { /* fill the table with given functions */ 267 | int i; 268 | lua_pushstring(L, l->name); 269 | for (i = 0; i < nup; i++) /* copy upvalues to the top */ 270 | lua_pushvalue(L, -(nup + 1)); 271 | lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ 272 | lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ 273 | } 274 | lua_pop(L, nup); /* remove upvalues */ 275 | } 276 | 277 | 278 | COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) { 279 | luaL_checkstack(L, 1, "not enough stack slots"); 280 | luaL_getmetatable(L, tname); 281 | lua_setmetatable(L, -2); 282 | } 283 | 284 | 285 | COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) { 286 | void *p = lua_touserdata(L, i); 287 | luaL_checkstack(L, 2, "not enough stack slots"); 288 | if (p == NULL || !lua_getmetatable(L, i)) 289 | return NULL; 290 | else { 291 | int res = 0; 292 | luaL_getmetatable(L, tname); 293 | res = lua_rawequal(L, -1, -2); 294 | lua_pop(L, 2); 295 | if (!res) 296 | p = NULL; 297 | } 298 | return p; 299 | } 300 | 301 | 302 | static int compat53_countlevels (lua_State *L) { 303 | lua_Debug ar; 304 | int li = 1, le = 1; 305 | /* find an upper bound */ 306 | while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } 307 | /* do a binary search */ 308 | while (li < le) { 309 | int m = (li + le)/2; 310 | if (lua_getstack(L, m, &ar)) li = m + 1; 311 | else le = m; 312 | } 313 | return le - 1; 314 | } 315 | 316 | static int compat53_findfield (lua_State *L, int objidx, int level) { 317 | if (level == 0 || !lua_istable(L, -1)) 318 | return 0; /* not found */ 319 | lua_pushnil(L); /* start 'next' loop */ 320 | while (lua_next(L, -2)) { /* for each pair in table */ 321 | if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ 322 | if (lua_rawequal(L, objidx, -1)) { /* found object? */ 323 | lua_pop(L, 1); /* remove value (but keep name) */ 324 | return 1; 325 | } 326 | else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */ 327 | lua_remove(L, -2); /* remove table (but keep name) */ 328 | lua_pushliteral(L, "."); 329 | lua_insert(L, -2); /* place '.' between the two names */ 330 | lua_concat(L, 3); 331 | return 1; 332 | } 333 | } 334 | lua_pop(L, 1); /* remove value */ 335 | } 336 | return 0; /* not found */ 337 | } 338 | 339 | static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) { 340 | int top = lua_gettop(L); 341 | lua_getinfo(L, "f", ar); /* push function */ 342 | lua_pushvalue(L, LUA_GLOBALSINDEX); 343 | if (compat53_findfield(L, top + 1, 2)) { 344 | lua_copy(L, -1, top + 1); /* move name to proper place */ 345 | lua_pop(L, 2); /* remove pushed values */ 346 | return 1; 347 | } 348 | else { 349 | lua_settop(L, top); /* remove function and global table */ 350 | return 0; 351 | } 352 | } 353 | 354 | static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) { 355 | if (*ar->namewhat != '\0') /* is there a name? */ 356 | lua_pushfstring(L, "function " LUA_QS, ar->name); 357 | else if (*ar->what == 'm') /* main? */ 358 | lua_pushliteral(L, "main chunk"); 359 | else if (*ar->what == 'C') { 360 | if (compat53_pushglobalfuncname(L, ar)) { 361 | lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); 362 | lua_remove(L, -2); /* remove name */ 363 | } 364 | else 365 | lua_pushliteral(L, "?"); 366 | } 367 | else 368 | lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); 369 | } 370 | 371 | #define COMPAT53_LEVELS1 12 /* size of the first part of the stack */ 372 | #define COMPAT53_LEVELS2 10 /* size of the second part of the stack */ 373 | 374 | COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, 375 | const char *msg, int level) { 376 | lua_Debug ar; 377 | int top = lua_gettop(L); 378 | int numlevels = compat53_countlevels(L1); 379 | int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0; 380 | if (msg) lua_pushfstring(L, "%s\n", msg); 381 | lua_pushliteral(L, "stack traceback:"); 382 | while (lua_getstack(L1, level++, &ar)) { 383 | if (level == mark) { /* too many levels? */ 384 | lua_pushliteral(L, "\n\t..."); /* add a '...' */ 385 | level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */ 386 | } 387 | else { 388 | lua_getinfo(L1, "Slnt", &ar); 389 | lua_pushfstring(L, "\n\t%s:", ar.short_src); 390 | if (ar.currentline > 0) 391 | lua_pushfstring(L, "%d:", ar.currentline); 392 | lua_pushliteral(L, " in "); 393 | compat53_pushfuncname(L, &ar); 394 | lua_concat(L, lua_gettop(L) - top); 395 | } 396 | } 397 | lua_concat(L, lua_gettop(L) - top); 398 | } 399 | 400 | 401 | COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { 402 | const char *serr = NULL; 403 | int en = errno; /* calls to Lua API may change this value */ 404 | char buf[512] = { 0 }; 405 | if (stat) { 406 | lua_pushboolean(L, 1); 407 | return 1; 408 | } 409 | else { 410 | lua_pushnil(L); 411 | serr = compat53_strerror(en, buf, sizeof(buf)); 412 | if (fname) 413 | lua_pushfstring(L, "%s: %s", fname, serr); 414 | else 415 | lua_pushstring(L, serr); 416 | lua_pushnumber(L, (lua_Number)en); 417 | return 3; 418 | } 419 | } 420 | 421 | 422 | static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) { 423 | if (mode && strchr(mode, modename[0]) == NULL) { 424 | lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode); 425 | return err; 426 | } 427 | return LUA_OK; 428 | } 429 | 430 | 431 | typedef struct { 432 | lua_Reader reader; 433 | void *ud; 434 | int has_peeked_data; 435 | const char *peeked_data; 436 | size_t peeked_data_size; 437 | } compat53_reader_data; 438 | 439 | 440 | static const char *compat53_reader (lua_State *L, void *ud, size_t *size) { 441 | compat53_reader_data *data = (compat53_reader_data *)ud; 442 | if (data->has_peeked_data) { 443 | data->has_peeked_data = 0; 444 | *size = data->peeked_data_size; 445 | return data->peeked_data; 446 | } else 447 | return data->reader(L, data->ud, size); 448 | } 449 | 450 | 451 | COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) { 452 | int status = LUA_OK; 453 | compat53_reader_data compat53_data = { 0, NULL, 1, 0, 0 }; 454 | compat53_data.reader = reader; 455 | compat53_data.ud = data; 456 | compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size)); 457 | if (compat53_data.peeked_data && compat53_data.peeked_data_size && 458 | compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */ 459 | status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); 460 | else 461 | status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); 462 | if (status != LUA_OK) 463 | return status; 464 | /* we need to call the original 5.1 version of lua_load! */ 465 | #undef lua_load 466 | return lua_load(L, compat53_reader, &compat53_data, source); 467 | #define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) 468 | } 469 | 470 | 471 | typedef struct { 472 | int n; /* number of pre-read characters */ 473 | FILE *f; /* file being read */ 474 | char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */ 475 | } compat53_LoadF; 476 | 477 | 478 | static const char *compat53_getF (lua_State *L, void *ud, size_t *size) { 479 | compat53_LoadF *lf = (compat53_LoadF *)ud; 480 | (void)L; /* not used */ 481 | if (lf->n > 0) { /* are there pre-read characters to be read? */ 482 | *size = lf->n; /* return them (chars already in buffer) */ 483 | lf->n = 0; /* no more pre-read characters */ 484 | } 485 | else { /* read a block from file */ 486 | /* 'fread' can return > 0 *and* set the EOF flag. If next call to 487 | 'compat53_getF' called 'fread', it might still wait for user input. 488 | The next check avoids this problem. */ 489 | if (feof(lf->f)) return NULL; 490 | *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ 491 | } 492 | return lf->buff; 493 | } 494 | 495 | 496 | static int compat53_errfile (lua_State *L, const char *what, int fnameindex) { 497 | char buf[512] = {0}; 498 | const char *serr = compat53_strerror(errno, buf, sizeof(buf)); 499 | const char *filename = lua_tostring(L, fnameindex) + 1; 500 | lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); 501 | lua_remove(L, fnameindex); 502 | return LUA_ERRFILE; 503 | } 504 | 505 | 506 | static int compat53_skipBOM (compat53_LoadF *lf) { 507 | const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ 508 | int c; 509 | lf->n = 0; 510 | do { 511 | c = getc(lf->f); 512 | if (c == EOF || c != *(const unsigned char *)p++) return c; 513 | lf->buff[lf->n++] = (char)c; /* to be read by the parser */ 514 | } while (*p != '\0'); 515 | lf->n = 0; /* prefix matched; discard it */ 516 | return getc(lf->f); /* return next character */ 517 | } 518 | 519 | 520 | /* 521 | ** reads the first character of file 'f' and skips an optional BOM mark 522 | ** in its beginning plus its first line if it starts with '#'. Returns 523 | ** true if it skipped the first line. In any case, '*cp' has the 524 | ** first "valid" character of the file (after the optional BOM and 525 | ** a first-line comment). 526 | */ 527 | static int compat53_skipcomment (compat53_LoadF *lf, int *cp) { 528 | int c = *cp = compat53_skipBOM(lf); 529 | if (c == '#') { /* first line is a comment (Unix exec. file)? */ 530 | do { /* skip first line */ 531 | c = getc(lf->f); 532 | } while (c != EOF && c != '\n'); 533 | *cp = getc(lf->f); /* skip end-of-line, if present */ 534 | return 1; /* there was a comment */ 535 | } 536 | else return 0; /* no comment */ 537 | } 538 | 539 | 540 | COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { 541 | compat53_LoadF lf; 542 | int status, readstatus; 543 | int c; 544 | int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ 545 | if (filename == NULL) { 546 | lua_pushliteral(L, "=stdin"); 547 | lf.f = stdin; 548 | } 549 | else { 550 | lua_pushfstring(L, "@%s", filename); 551 | #if defined(_MSC_VER) 552 | /* This code is here to stop a deprecation error that stops builds 553 | * if a certain macro is defined. While normally not caring would 554 | * be best, some header-only libraries and builds can't afford to 555 | * dictate this to the user. A quick check shows that fopen_s this 556 | * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET, 557 | * possibly even before that so we don't need to do any version 558 | * number checks, since this has been there since forever. */ 559 | 560 | /* TO USER: if you want the behavior of typical fopen_s/fopen, 561 | * which does lock the file on VC++, define the macro used below to 0 */ 562 | #if COMPAT53_FOPEN_NO_LOCK 563 | lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */ 564 | if (lf.f == NULL) 565 | return compat53_errfile(L, "open", fnameindex); 566 | #else /* use default locking version */ 567 | if (fopen_s(&lf.f, filename, "r") != 0) 568 | return compat53_errfile(L, "open", fnameindex); 569 | #endif /* Locking vs. No-locking fopen variants */ 570 | #else 571 | lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */ 572 | if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex); 573 | #endif 574 | } 575 | if (compat53_skipcomment(&lf, &c)) /* read initial portion */ 576 | lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ 577 | if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ 578 | #if defined(_MSC_VER) 579 | if (freopen_s(&lf.f, filename, "rb", lf.f) != 0) 580 | return compat53_errfile(L, "reopen", fnameindex); 581 | #else 582 | lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ 583 | if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex); 584 | #endif 585 | compat53_skipcomment(&lf, &c); /* re-read initial portion */ 586 | } 587 | if (c != EOF) 588 | lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */ 589 | status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode); 590 | readstatus = ferror(lf.f); 591 | if (filename) fclose(lf.f); /* close file (even in case of errors) */ 592 | if (readstatus) { 593 | lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ 594 | return compat53_errfile(L, "read", fnameindex); 595 | } 596 | lua_remove(L, fnameindex); 597 | return status; 598 | } 599 | 600 | 601 | COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) { 602 | int status = LUA_OK; 603 | if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) { 604 | status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); 605 | } 606 | else { 607 | status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); 608 | } 609 | if (status != LUA_OK) 610 | return status; 611 | return luaL_loadbuffer(L, buff, sz, name); 612 | } 613 | 614 | 615 | #if !defined(l_inspectstat) && \ 616 | (defined(unix) || defined(__unix) || defined(__unix__) || \ 617 | defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \ 618 | (defined(__APPLE__) && defined(__MACH__))) 619 | /* some form of unix; check feature macros in unistd.h for details */ 620 | # include 621 | /* check posix version; the relevant include files and macros probably 622 | * were available before 2001, but I'm not sure */ 623 | # if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L 624 | # include 625 | # define l_inspectstat(stat,what) \ 626 | if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ 627 | else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } 628 | # endif 629 | #endif 630 | 631 | /* provide default (no-op) version */ 632 | #if !defined(l_inspectstat) 633 | # define l_inspectstat(stat,what) ((void)0) 634 | #endif 635 | 636 | 637 | COMPAT53_API int luaL_execresult (lua_State *L, int stat) { 638 | const char *what = "exit"; 639 | if (stat == -1) 640 | return luaL_fileresult(L, 0, NULL); 641 | else { 642 | l_inspectstat(stat, what); 643 | if (*what == 'e' && stat == 0) 644 | lua_pushboolean(L, 1); 645 | else 646 | lua_pushnil(L); 647 | lua_pushstring(L, what); 648 | lua_pushinteger(L, stat); 649 | return 3; 650 | } 651 | } 652 | 653 | 654 | COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) { 655 | /* make it crash if used via pointer to a 5.1-style luaL_Buffer */ 656 | B->b.p = NULL; 657 | B->b.L = NULL; 658 | B->b.lvl = 0; 659 | /* reuse the buffer from the 5.1-style luaL_Buffer though! */ 660 | B->ptr = B->b.buffer; 661 | B->capacity = LUAL_BUFFERSIZE; 662 | B->nelems = 0; 663 | B->L2 = L; 664 | } 665 | 666 | 667 | COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) { 668 | if (B->capacity - B->nelems < s) { /* needs to grow */ 669 | char* newptr = NULL; 670 | size_t newcap = B->capacity * 2; 671 | if (newcap - B->nelems < s) 672 | newcap = B->nelems + s; 673 | if (newcap < B->capacity) /* overflow */ 674 | luaL_error(B->L2, "buffer too large"); 675 | newptr = (char*)lua_newuserdata(B->L2, newcap); 676 | memcpy(newptr, B->ptr, B->nelems); 677 | if (B->ptr != B->b.buffer) 678 | lua_replace(B->L2, -2); /* remove old buffer */ 679 | B->ptr = newptr; 680 | B->capacity = newcap; 681 | } 682 | return B->ptr+B->nelems; 683 | } 684 | 685 | 686 | COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) { 687 | memcpy(luaL_prepbuffsize(B, l), s, l); 688 | luaL_addsize(B, l); 689 | } 690 | 691 | 692 | COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) { 693 | size_t len = 0; 694 | const char *s = lua_tolstring(B->L2, -1, &len); 695 | if (!s) 696 | luaL_error(B->L2, "cannot convert value to string"); 697 | if (B->ptr != B->b.buffer) 698 | lua_insert(B->L2, -2); /* userdata buffer must be at stack top */ 699 | luaL_addlstring(B, s, len); 700 | lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1); 701 | } 702 | 703 | 704 | void luaL_pushresult (luaL_Buffer_53 *B) { 705 | lua_pushlstring(B->L2, B->ptr, B->nelems); 706 | if (B->ptr != B->b.buffer) 707 | lua_replace(B->L2, -2); /* remove userdata buffer */ 708 | } 709 | 710 | 711 | #endif /* Lua 5.1 */ 712 | 713 | 714 | 715 | /* definitions for Lua 5.1 and Lua 5.2 */ 716 | #if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502 717 | 718 | 719 | COMPAT53_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { 720 | #undef lua_pushlstring 721 | lua_pushlstring(L, len > 0 ? s : "", len); 722 | #define lua_pushlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _pushlstring_53) 723 | return lua_tostring(L, -1); 724 | } 725 | 726 | 727 | COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) { 728 | index = lua_absindex(L, index); 729 | lua_pushinteger(L, i); 730 | lua_gettable(L, index); 731 | return lua_type(L, -1); 732 | } 733 | 734 | 735 | #ifndef LUA_EXTRASPACE 736 | #define LUA_EXTRASPACE (sizeof(void*)) 737 | #endif 738 | 739 | COMPAT53_API void *lua_getextraspace (lua_State *L) { 740 | int is_main = 0; 741 | void *ptr = NULL; 742 | luaL_checkstack(L, 4, "not enough stack slots available"); 743 | lua_pushliteral(L, "__compat53_extraspace"); 744 | lua_pushvalue(L, -1); 745 | lua_rawget(L, LUA_REGISTRYINDEX); 746 | if (!lua_istable(L, -1)) { 747 | lua_pop(L, 1); 748 | lua_createtable(L, 0, 2); 749 | lua_createtable(L, 0, 1); 750 | lua_pushliteral(L, "k"); 751 | lua_setfield(L, -2, "__mode"); 752 | lua_setmetatable(L, -2); 753 | lua_pushvalue(L, -2); 754 | lua_pushvalue(L, -2); 755 | lua_rawset(L, LUA_REGISTRYINDEX); 756 | } 757 | lua_replace(L, -2); 758 | is_main = lua_pushthread(L); 759 | lua_rawget(L, -2); 760 | ptr = lua_touserdata(L, -1); 761 | if (!ptr) { 762 | lua_pop(L, 1); 763 | ptr = lua_newuserdata(L, LUA_EXTRASPACE); 764 | if (is_main) { 765 | memset(ptr, '\0', LUA_EXTRASPACE); 766 | lua_pushthread(L); 767 | lua_pushvalue(L, -2); 768 | lua_rawset(L, -4); 769 | lua_pushboolean(L, 1); 770 | lua_pushvalue(L, -2); 771 | lua_rawset(L, -4); 772 | } else { 773 | void* mptr = NULL; 774 | lua_pushboolean(L, 1); 775 | lua_rawget(L, -3); 776 | mptr = lua_touserdata(L, -1); 777 | if (mptr) 778 | memcpy(ptr, mptr, LUA_EXTRASPACE); 779 | else 780 | memset(ptr, '\0', LUA_EXTRASPACE); 781 | lua_pop(L, 1); 782 | lua_pushthread(L); 783 | lua_pushvalue(L, -2); 784 | lua_rawset(L, -4); 785 | } 786 | } 787 | lua_pop(L, 2); 788 | return ptr; 789 | } 790 | 791 | 792 | COMPAT53_API int lua_isinteger (lua_State *L, int index) { 793 | if (lua_type(L, index) == LUA_TNUMBER) { 794 | lua_Number n = lua_tonumber(L, index); 795 | lua_Integer i = lua_tointeger(L, index); 796 | if (i == n) 797 | return 1; 798 | } 799 | return 0; 800 | } 801 | 802 | 803 | COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) { 804 | int ok = 0; 805 | lua_Number n = lua_tonumberx(L, i, &ok); 806 | if (ok) { 807 | if (n == (lua_Integer)n) { 808 | if (isnum) 809 | *isnum = 1; 810 | return (lua_Integer)n; 811 | } 812 | } 813 | if (isnum) 814 | *isnum = 0; 815 | return 0; 816 | } 817 | 818 | 819 | static void compat53_reverse (lua_State *L, int a, int b) { 820 | for (; a < b; ++a, --b) { 821 | lua_pushvalue(L, a); 822 | lua_pushvalue(L, b); 823 | lua_replace(L, a); 824 | lua_replace(L, b); 825 | } 826 | } 827 | 828 | 829 | COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) { 830 | int n_elems = 0; 831 | idx = lua_absindex(L, idx); 832 | n_elems = lua_gettop(L)-idx+1; 833 | if (n < 0) 834 | n += n_elems; 835 | if ( n > 0 && n < n_elems) { 836 | luaL_checkstack(L, 2, "not enough stack slots available"); 837 | n = n_elems - n; 838 | compat53_reverse(L, idx, idx+n-1); 839 | compat53_reverse(L, idx+n, idx+n_elems-1); 840 | compat53_reverse(L, idx, idx+n_elems-1); 841 | } 842 | } 843 | 844 | 845 | COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) { 846 | luaL_checkstack(L, 1, "not enough stack slots available"); 847 | index = lua_absindex(L, index); 848 | lua_pushinteger(L, i); 849 | lua_insert(L, -2); 850 | lua_settable(L, index); 851 | } 852 | 853 | 854 | #if !defined(lua_str2number) 855 | # define lua_str2number(s, p) strtod((s), (p)) 856 | #endif 857 | 858 | COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) { 859 | char* endptr; 860 | lua_Number n = lua_str2number(s, &endptr); 861 | if (endptr != s) { 862 | while (*endptr != '\0' && isspace((unsigned char)*endptr)) 863 | ++endptr; 864 | if (*endptr == '\0') { 865 | lua_pushnumber(L, n); 866 | return endptr - s + 1; 867 | } 868 | } 869 | return 0; 870 | } 871 | 872 | 873 | COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { 874 | if (!luaL_callmeta(L, idx, "__tostring")) { 875 | int t = lua_type(L, idx), tt = 0; 876 | char const* name = NULL; 877 | switch (t) { 878 | case LUA_TNIL: 879 | lua_pushliteral(L, "nil"); 880 | break; 881 | case LUA_TSTRING: 882 | case LUA_TNUMBER: 883 | lua_pushvalue(L, idx); 884 | break; 885 | case LUA_TBOOLEAN: 886 | if (lua_toboolean(L, idx)) 887 | lua_pushliteral(L, "true"); 888 | else 889 | lua_pushliteral(L, "false"); 890 | break; 891 | default: 892 | tt = luaL_getmetafield(L, idx, "__name"); 893 | name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t); 894 | lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx)); 895 | if (tt != LUA_TNIL) 896 | lua_replace(L, -2); 897 | break; 898 | } 899 | } else { 900 | if (!lua_isstring(L, -1)) 901 | luaL_error(L, "'__tostring' must return a string"); 902 | } 903 | return lua_tolstring(L, -1, len); 904 | } 905 | 906 | 907 | COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, 908 | lua_CFunction openf, int glb) { 909 | luaL_checkstack(L, 3, "not enough stack slots available"); 910 | luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); 911 | if (lua_getfield(L, -1, modname) == LUA_TNIL) { 912 | lua_pop(L, 1); 913 | lua_pushcfunction(L, openf); 914 | lua_pushstring(L, modname); 915 | lua_call(L, 1, 1); 916 | lua_pushvalue(L, -1); 917 | lua_setfield(L, -3, modname); 918 | } 919 | if (glb) { 920 | lua_pushvalue(L, -1); 921 | lua_setglobal(L, modname); 922 | } 923 | lua_replace(L, -2); 924 | } 925 | 926 | 927 | #endif /* Lua 5.1 and 5.2 */ 928 | 929 | 930 | #endif /* COMPAT53_C_ */ 931 | 932 | 933 | /********************************************************************* 934 | * This file contains parts of Lua 5.2's and Lua 5.3's source code: 935 | * 936 | * Copyright (C) 1994-2014 Lua.org, PUC-Rio. 937 | * 938 | * Permission is hereby granted, free of charge, to any person obtaining 939 | * a copy of this software and associated documentation files (the 940 | * "Software"), to deal in the Software without restriction, including 941 | * without limitation the rights to use, copy, modify, merge, publish, 942 | * distribute, sublicense, and/or sell copies of the Software, and to 943 | * permit persons to whom the Software is furnished to do so, subject to 944 | * the following conditions: 945 | * 946 | * The above copyright notice and this permission notice shall be 947 | * included in all copies or substantial portions of the Software. 948 | * 949 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 950 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 951 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 952 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 953 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 954 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 955 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 956 | *********************************************************************/ 957 | 958 | -------------------------------------------------------------------------------- /src/executables/loaders/static/compat-5.3.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT53_H_ 2 | #define COMPAT53_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) 8 | extern "C" { 9 | #endif 10 | #include 11 | #include 12 | #include 13 | #if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) 14 | } 15 | #endif 16 | 17 | 18 | #undef COMPAT53_INCLUDE_SOURCE 19 | #if defined(COMPAT53_PREFIX) 20 | /* - change the symbol names of functions to avoid linker conflicts 21 | * - compat-5.3.c needs to be compiled (and linked) separately 22 | */ 23 | # if !defined(COMPAT53_API) 24 | # define COMPAT53_API extern 25 | # endif 26 | #else /* COMPAT53_PREFIX */ 27 | /* - make all functions static and include the source. 28 | * - compat-5.3.c doesn't need to be compiled (and linked) separately 29 | */ 30 | # define COMPAT53_PREFIX compat53 31 | # undef COMPAT53_API 32 | # if defined(__GNUC__) || defined(__clang__) 33 | # define COMPAT53_API __attribute__((__unused__)) static 34 | # else 35 | # define COMPAT53_API static 36 | # endif 37 | # define COMPAT53_INCLUDE_SOURCE 38 | #endif /* COMPAT53_PREFIX */ 39 | 40 | #define COMPAT53_CONCAT_HELPER(a, b) a##b 41 | #define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b) 42 | 43 | 44 | 45 | /* declarations for Lua 5.1 */ 46 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 47 | 48 | /* XXX not implemented: 49 | * lua_arith (new operators) 50 | * lua_upvalueid 51 | * lua_upvaluejoin 52 | * lua_version 53 | * lua_yieldk 54 | */ 55 | 56 | #ifndef LUA_OK 57 | # define LUA_OK 0 58 | #endif 59 | #ifndef LUA_OPADD 60 | # define LUA_OPADD 0 61 | #endif 62 | #ifndef LUA_OPSUB 63 | # define LUA_OPSUB 1 64 | #endif 65 | #ifndef LUA_OPMUL 66 | # define LUA_OPMUL 2 67 | #endif 68 | #ifndef LUA_OPDIV 69 | # define LUA_OPDIV 3 70 | #endif 71 | #ifndef LUA_OPMOD 72 | # define LUA_OPMOD 4 73 | #endif 74 | #ifndef LUA_OPPOW 75 | # define LUA_OPPOW 5 76 | #endif 77 | #ifndef LUA_OPUNM 78 | # define LUA_OPUNM 6 79 | #endif 80 | #ifndef LUA_OPEQ 81 | # define LUA_OPEQ 0 82 | #endif 83 | #ifndef LUA_OPLT 84 | # define LUA_OPLT 1 85 | #endif 86 | #ifndef LUA_OPLE 87 | # define LUA_OPLE 2 88 | #endif 89 | 90 | /* LuaJIT/Lua 5.1 does not have the updated 91 | * error codes for thread status/function returns (but some patched versions do) 92 | * define it only if it's not found 93 | */ 94 | #if !defined(LUA_ERRGCMM) 95 | /* Use + 2 because in some versions of Lua (Lua 5.1) 96 | * LUA_ERRFILE is defined as (LUA_ERRERR+1) 97 | * so we need to avoid it (LuaJIT might have something at this 98 | * integer value too) 99 | */ 100 | # define LUA_ERRGCMM (LUA_ERRERR + 2) 101 | #endif /* LUA_ERRGCMM define */ 102 | 103 | typedef size_t lua_Unsigned; 104 | 105 | typedef struct luaL_Buffer_53 { 106 | luaL_Buffer b; /* make incorrect code crash! */ 107 | char *ptr; 108 | size_t nelems; 109 | size_t capacity; 110 | lua_State *L2; 111 | } luaL_Buffer_53; 112 | #define luaL_Buffer luaL_Buffer_53 113 | 114 | /* In PUC-Rio 5.1, userdata is a simple FILE* 115 | * In LuaJIT, it's a struct where the first member is a FILE* 116 | * We can't support the `closef` member 117 | */ 118 | typedef struct luaL_Stream { 119 | FILE *f; 120 | } luaL_Stream; 121 | 122 | #define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex) 123 | COMPAT53_API int lua_absindex (lua_State *L, int i); 124 | 125 | #define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith) 126 | COMPAT53_API void lua_arith (lua_State *L, int op); 127 | 128 | #define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare) 129 | COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op); 130 | 131 | #define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy) 132 | COMPAT53_API void lua_copy (lua_State *L, int from, int to); 133 | 134 | #define lua_getuservalue(L, i) \ 135 | (lua_getfenv((L), (i)), lua_type((L), -1)) 136 | #define lua_setuservalue(L, i) \ 137 | (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i))) 138 | 139 | #define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len) 140 | COMPAT53_API void lua_len (lua_State *L, int i); 141 | 142 | #define lua_pushstring(L, s) \ 143 | (lua_pushstring((L), (s)), lua_tostring((L), -1)) 144 | 145 | #ifndef luaL_newlibtable 146 | # define luaL_newlibtable(L, l) \ 147 | (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1)) 148 | #endif 149 | #ifndef luaL_newlib 150 | # define luaL_newlib(L, l) \ 151 | (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l))) 152 | #endif 153 | 154 | #define lua_pushglobaltable(L) \ 155 | lua_pushvalue((L), LUA_GLOBALSINDEX) 156 | 157 | #define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp) 158 | COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p); 159 | 160 | #define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp) 161 | COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p); 162 | 163 | #define lua_rawlen(L, i) lua_objlen((L), (i)) 164 | 165 | #define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL) 166 | 167 | #define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx) 168 | COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum); 169 | 170 | #define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion) 171 | COMPAT53_API void luaL_checkversion (lua_State *L); 172 | 173 | #define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) 174 | COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode); 175 | 176 | #define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex) 177 | COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode); 178 | 179 | #define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx) 180 | COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); 181 | 182 | #define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53) 183 | COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg); 184 | 185 | #define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable) 186 | COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name); 187 | 188 | #define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len) 189 | COMPAT53_API lua_Integer luaL_len (lua_State *L, int i); 190 | 191 | #define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs) 192 | COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); 193 | 194 | #define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable) 195 | COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname); 196 | 197 | #define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata) 198 | COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname); 199 | 200 | #define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback) 201 | COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level); 202 | 203 | #define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult) 204 | COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname); 205 | 206 | #define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult) 207 | COMPAT53_API int luaL_execresult (lua_State *L, int stat); 208 | 209 | #define lua_callk(L, na, nr, ctx, cont) \ 210 | ((void)(ctx), (void)(cont), lua_call((L), (na), (nr))) 211 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ 212 | ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err))) 213 | 214 | #define lua_resume(L, from, nargs) \ 215 | ((void)(from), lua_resume((L), (nargs))) 216 | 217 | #define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53) 218 | COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B); 219 | 220 | #define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53) 221 | COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s); 222 | 223 | #define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53) 224 | COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l); 225 | 226 | #define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53) 227 | COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B); 228 | 229 | #define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53) 230 | COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B); 231 | 232 | #undef luaL_buffinitsize 233 | #define luaL_buffinitsize(L, B, s) \ 234 | (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s))) 235 | 236 | #undef luaL_prepbuffer 237 | #define luaL_prepbuffer(B) \ 238 | luaL_prepbuffsize((B), LUAL_BUFFERSIZE) 239 | 240 | #undef luaL_addchar 241 | #define luaL_addchar(B, c) \ 242 | ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \ 243 | ((B)->ptr[(B)->nelems++] = (c))) 244 | 245 | #undef luaL_addsize 246 | #define luaL_addsize(B, s) \ 247 | ((B)->nelems += (s)) 248 | 249 | #undef luaL_addstring 250 | #define luaL_addstring(B, s) \ 251 | luaL_addlstring((B), (s), strlen((s))) 252 | 253 | #undef luaL_pushresultsize 254 | #define luaL_pushresultsize(B, s) \ 255 | (luaL_addsize((B), (s)), luaL_pushresult((B))) 256 | 257 | #if defined(LUA_COMPAT_APIINTCASTS) 258 | #define lua_pushunsigned(L, n) \ 259 | lua_pushinteger((L), (lua_Integer)(n)) 260 | #define lua_tounsignedx(L, i, is) \ 261 | ((lua_Unsigned)lua_tointegerx((L), (i), (is))) 262 | #define lua_tounsigned(L, i) \ 263 | lua_tounsignedx((L), (i), NULL) 264 | #define luaL_checkunsigned(L, a) \ 265 | ((lua_Unsigned)luaL_checkinteger((L), (a))) 266 | #define luaL_optunsigned(L, a, d) \ 267 | ((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d))) 268 | #endif 269 | 270 | #endif /* Lua 5.1 only */ 271 | 272 | 273 | 274 | /* declarations for Lua 5.1 and 5.2 */ 275 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 276 | 277 | typedef int lua_KContext; 278 | 279 | typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx); 280 | 281 | #define lua_dump(L, w, d, s) \ 282 | ((void)(s), lua_dump((L), (w), (d))) 283 | 284 | #define lua_getfield(L, i, k) \ 285 | (lua_getfield((L), (i), (k)), lua_type((L), -1)) 286 | 287 | #define lua_gettable(L, i) \ 288 | (lua_gettable((L), (i)), lua_type((L), -1)) 289 | 290 | #define lua_pushlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _pushlstring_53) 291 | COMPAT53_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len); 292 | 293 | #define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti) 294 | COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i); 295 | 296 | #define lua_getextraspace COMPAT53_CONCAT(COMPAT53_PREFIX, _getextraspace) 297 | COMPAT53_API void *lua_getextraspace (lua_State *L); 298 | 299 | #define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger) 300 | COMPAT53_API int lua_isinteger (lua_State *L, int index); 301 | 302 | #define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53) 303 | COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum); 304 | 305 | #define lua_numbertointeger(n, p) \ 306 | ((*(p) = (lua_Integer)(n)), 1) 307 | 308 | #define lua_rawget(L, i) \ 309 | (lua_rawget((L), (i)), lua_type((L), -1)) 310 | 311 | #define lua_rawgeti(L, i, n) \ 312 | (lua_rawgeti((L), (i), (n)), lua_type((L), -1)) 313 | 314 | #define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate) 315 | COMPAT53_API void lua_rotate (lua_State *L, int idx, int n); 316 | 317 | #define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti) 318 | COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i); 319 | 320 | #define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber) 321 | COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s); 322 | 323 | #define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring) 324 | COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len); 325 | 326 | #define luaL_getmetafield(L, o, e) \ 327 | (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL) 328 | 329 | #define luaL_newmetatable(L, tn) \ 330 | (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0) 331 | 332 | #define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53) 333 | COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, 334 | lua_CFunction openf, int glb ); 335 | 336 | #endif /* Lua 5.1 and Lua 5.2 */ 337 | 338 | 339 | 340 | /* declarations for Lua 5.2 */ 341 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502 342 | 343 | /* XXX not implemented: 344 | * lua_isyieldable 345 | * lua_arith (new operators) 346 | * lua_pushfstring (new formats) 347 | */ 348 | 349 | #define lua_getglobal(L, n) \ 350 | (lua_getglobal((L), (n)), lua_type((L), -1)) 351 | 352 | #define lua_getuservalue(L, i) \ 353 | (lua_getuservalue((L), (i)), lua_type((L), -1)) 354 | 355 | #define lua_rawgetp(L, i, p) \ 356 | (lua_rawgetp((L), (i), (p)), lua_type((L), -1)) 357 | 358 | #define LUA_KFUNCTION(_name) \ 359 | static int (_name)(lua_State *L, int status, lua_KContext ctx); \ 360 | static int (_name ## _52)(lua_State *L) { \ 361 | lua_KContext ctx; \ 362 | int status = lua_getctx(L, &ctx); \ 363 | return (_name)(L, status, ctx); \ 364 | } \ 365 | static int (_name)(lua_State *L, int status, lua_KContext ctx) 366 | 367 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ 368 | lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52) 369 | 370 | #define lua_callk(L, na, nr, ctx, cont) \ 371 | lua_callk((L), (na), (nr), (ctx), cont ## _52) 372 | 373 | #define lua_yieldk(L, nr, ctx, cont) \ 374 | lua_yieldk((L), (nr), (ctx), cont ## _52) 375 | 376 | #ifdef lua_call 377 | # undef lua_call 378 | # define lua_call(L, na, nr) \ 379 | (lua_callk)((L), (na), (nr), 0, NULL) 380 | #endif 381 | 382 | #ifdef lua_pcall 383 | # undef lua_pcall 384 | # define lua_pcall(L, na, nr, err) \ 385 | (lua_pcallk)((L), (na), (nr), (err), 0, NULL) 386 | #endif 387 | 388 | #ifdef lua_yield 389 | # undef lua_yield 390 | # define lua_yield(L, nr) \ 391 | (lua_yieldk)((L), (nr), 0, NULL) 392 | #endif 393 | 394 | #endif /* Lua 5.2 only */ 395 | 396 | 397 | 398 | /* other Lua versions */ 399 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504 400 | 401 | # error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)" 402 | 403 | #endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */ 404 | 405 | 406 | 407 | /* helper macro for defining continuation functions (for every version 408 | * *except* Lua 5.2) */ 409 | #ifndef LUA_KFUNCTION 410 | #define LUA_KFUNCTION(_name) \ 411 | static int (_name)(lua_State *L, int status, lua_KContext ctx) 412 | #endif 413 | 414 | 415 | #if defined(COMPAT53_INCLUDE_SOURCE) 416 | # include "compat-5.3.c" 417 | #endif 418 | 419 | 420 | #endif /* COMPAT53_H_ */ 421 | 422 | -------------------------------------------------------------------------------- /src/executables/loaders/static/compat-53-c.lua: -------------------------------------------------------------------------------- 1 | return { 2 | header = [=[ 3 | #ifndef COMPAT53_H_ 4 | #define COMPAT53_H_ 5 | 6 | #include 7 | #include 8 | #include 9 | #if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) 10 | extern "C" { 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) 16 | } 17 | #endif 18 | 19 | 20 | #undef COMPAT53_INCLUDE_SOURCE 21 | #if defined(COMPAT53_PREFIX) 22 | /* - change the symbol names of functions to avoid linker conflicts 23 | * - compat-5.3.c needs to be compiled (and linked) separately 24 | */ 25 | # if !defined(COMPAT53_API) 26 | # define COMPAT53_API extern 27 | # endif 28 | #else /* COMPAT53_PREFIX */ 29 | /* - make all functions static and include the source. 30 | * - compat-5.3.c doesn't need to be compiled (and linked) separately 31 | */ 32 | # define COMPAT53_PREFIX compat53 33 | # undef COMPAT53_API 34 | # if defined(__GNUC__) || defined(__clang__) 35 | # define COMPAT53_API __attribute__((__unused__)) static 36 | # else 37 | # define COMPAT53_API static 38 | # endif 39 | # define COMPAT53_INCLUDE_SOURCE 40 | #endif /* COMPAT53_PREFIX */ 41 | 42 | #define COMPAT53_CONCAT_HELPER(a, b) a##b 43 | #define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b) 44 | 45 | 46 | 47 | /* declarations for Lua 5.1 */ 48 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 49 | 50 | /* XXX not implemented: 51 | * lua_arith (new operators) 52 | * lua_upvalueid 53 | * lua_upvaluejoin 54 | * lua_version 55 | * lua_yieldk 56 | */ 57 | 58 | #ifndef LUA_OK 59 | # define LUA_OK 0 60 | #endif 61 | #ifndef LUA_OPADD 62 | # define LUA_OPADD 0 63 | #endif 64 | #ifndef LUA_OPSUB 65 | # define LUA_OPSUB 1 66 | #endif 67 | #ifndef LUA_OPMUL 68 | # define LUA_OPMUL 2 69 | #endif 70 | #ifndef LUA_OPDIV 71 | # define LUA_OPDIV 3 72 | #endif 73 | #ifndef LUA_OPMOD 74 | # define LUA_OPMOD 4 75 | #endif 76 | #ifndef LUA_OPPOW 77 | # define LUA_OPPOW 5 78 | #endif 79 | #ifndef LUA_OPUNM 80 | # define LUA_OPUNM 6 81 | #endif 82 | #ifndef LUA_OPEQ 83 | # define LUA_OPEQ 0 84 | #endif 85 | #ifndef LUA_OPLT 86 | # define LUA_OPLT 1 87 | #endif 88 | #ifndef LUA_OPLE 89 | # define LUA_OPLE 2 90 | #endif 91 | 92 | /* LuaJIT/Lua 5.1 does not have the updated 93 | * error codes for thread status/function returns (but some patched versions do) 94 | * define it only if it's not found 95 | */ 96 | #if !defined(LUA_ERRGCMM) 97 | /* Use + 2 because in some versions of Lua (Lua 5.1) 98 | * LUA_ERRFILE is defined as (LUA_ERRERR+1) 99 | * so we need to avoid it (LuaJIT might have something at this 100 | * integer value too) 101 | */ 102 | # define LUA_ERRGCMM (LUA_ERRERR + 2) 103 | #endif /* LUA_ERRGCMM define */ 104 | 105 | typedef size_t lua_Unsigned; 106 | 107 | typedef struct luaL_Buffer_53 { 108 | luaL_Buffer b; /* make incorrect code crash! */ 109 | char *ptr; 110 | size_t nelems; 111 | size_t capacity; 112 | lua_State *L2; 113 | } luaL_Buffer_53; 114 | #define luaL_Buffer luaL_Buffer_53 115 | 116 | /* In PUC-Rio 5.1, userdata is a simple FILE* 117 | * In LuaJIT, it's a struct where the first member is a FILE* 118 | * We can't support the `closef` member 119 | */ 120 | typedef struct luaL_Stream { 121 | FILE *f; 122 | } luaL_Stream; 123 | 124 | #define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex) 125 | COMPAT53_API int lua_absindex (lua_State *L, int i); 126 | 127 | #define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith) 128 | COMPAT53_API void lua_arith (lua_State *L, int op); 129 | 130 | #define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare) 131 | COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op); 132 | 133 | #define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy) 134 | COMPAT53_API void lua_copy (lua_State *L, int from, int to); 135 | 136 | #define lua_getuservalue(L, i) \ 137 | (lua_getfenv((L), (i)), lua_type((L), -1)) 138 | #define lua_setuservalue(L, i) \ 139 | (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i))) 140 | 141 | #define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len) 142 | COMPAT53_API void lua_len (lua_State *L, int i); 143 | 144 | #define lua_pushstring(L, s) \ 145 | (lua_pushstring((L), (s)), lua_tostring((L), -1)) 146 | 147 | #ifndef luaL_newlibtable 148 | # define luaL_newlibtable(L, l) \ 149 | (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1)) 150 | #endif 151 | #ifndef luaL_newlib 152 | # define luaL_newlib(L, l) \ 153 | (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l))) 154 | #endif 155 | 156 | #define lua_pushglobaltable(L) \ 157 | lua_pushvalue((L), LUA_GLOBALSINDEX) 158 | 159 | #define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp) 160 | COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p); 161 | 162 | #define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp) 163 | COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p); 164 | 165 | #define lua_rawlen(L, i) lua_objlen((L), (i)) 166 | 167 | #define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL) 168 | 169 | #define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx) 170 | COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum); 171 | 172 | #define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion) 173 | COMPAT53_API void luaL_checkversion (lua_State *L); 174 | 175 | #define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) 176 | COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode); 177 | 178 | #define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex) 179 | COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode); 180 | 181 | #define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx) 182 | COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); 183 | 184 | #define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53) 185 | COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg); 186 | 187 | #define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable) 188 | COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name); 189 | 190 | #define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len) 191 | COMPAT53_API lua_Integer luaL_len (lua_State *L, int i); 192 | 193 | #define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs) 194 | COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); 195 | 196 | #define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable) 197 | COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname); 198 | 199 | #define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata) 200 | COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname); 201 | 202 | #define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback) 203 | COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level); 204 | 205 | #define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult) 206 | COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname); 207 | 208 | #define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult) 209 | COMPAT53_API int luaL_execresult (lua_State *L, int stat); 210 | 211 | #define lua_callk(L, na, nr, ctx, cont) \ 212 | ((void)(ctx), (void)(cont), lua_call((L), (na), (nr))) 213 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ 214 | ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err))) 215 | 216 | #define lua_resume(L, from, nargs) \ 217 | ((void)(from), lua_resume((L), (nargs))) 218 | 219 | #define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53) 220 | COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B); 221 | 222 | #define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53) 223 | COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s); 224 | 225 | #define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53) 226 | COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l); 227 | 228 | #define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53) 229 | COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B); 230 | 231 | #define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53) 232 | COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B); 233 | 234 | #undef luaL_buffinitsize 235 | #define luaL_buffinitsize(L, B, s) \ 236 | (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s))) 237 | 238 | #undef luaL_prepbuffer 239 | #define luaL_prepbuffer(B) \ 240 | luaL_prepbuffsize((B), LUAL_BUFFERSIZE) 241 | 242 | #undef luaL_addchar 243 | #define luaL_addchar(B, c) \ 244 | ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \ 245 | ((B)->ptr[(B)->nelems++] = (c))) 246 | 247 | #undef luaL_addsize 248 | #define luaL_addsize(B, s) \ 249 | ((B)->nelems += (s)) 250 | 251 | #undef luaL_addstring 252 | #define luaL_addstring(B, s) \ 253 | luaL_addlstring((B), (s), strlen((s))) 254 | 255 | #undef luaL_pushresultsize 256 | #define luaL_pushresultsize(B, s) \ 257 | (luaL_addsize((B), (s)), luaL_pushresult((B))) 258 | 259 | #if defined(LUA_COMPAT_APIINTCASTS) 260 | #define lua_pushunsigned(L, n) \ 261 | lua_pushinteger((L), (lua_Integer)(n)) 262 | #define lua_tounsignedx(L, i, is) \ 263 | ((lua_Unsigned)lua_tointegerx((L), (i), (is))) 264 | #define lua_tounsigned(L, i) \ 265 | lua_tounsignedx((L), (i), NULL) 266 | #define luaL_checkunsigned(L, a) \ 267 | ((lua_Unsigned)luaL_checkinteger((L), (a))) 268 | #define luaL_optunsigned(L, a, d) \ 269 | ((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d))) 270 | #endif 271 | 272 | #endif /* Lua 5.1 only */ 273 | 274 | 275 | 276 | /* declarations for Lua 5.1 and 5.2 */ 277 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 278 | 279 | typedef int lua_KContext; 280 | 281 | typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx); 282 | 283 | #define lua_dump(L, w, d, s) \ 284 | ((void)(s), lua_dump((L), (w), (d))) 285 | 286 | #define lua_getfield(L, i, k) \ 287 | (lua_getfield((L), (i), (k)), lua_type((L), -1)) 288 | 289 | #define lua_gettable(L, i) \ 290 | (lua_gettable((L), (i)), lua_type((L), -1)) 291 | 292 | #define lua_pushlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _pushlstring_53) 293 | COMPAT53_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len); 294 | 295 | #define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti) 296 | COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i); 297 | 298 | #define lua_getextraspace COMPAT53_CONCAT(COMPAT53_PREFIX, _getextraspace) 299 | COMPAT53_API void *lua_getextraspace (lua_State *L); 300 | 301 | #define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger) 302 | COMPAT53_API int lua_isinteger (lua_State *L, int index); 303 | 304 | #define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53) 305 | COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum); 306 | 307 | #define lua_numbertointeger(n, p) \ 308 | ((*(p) = (lua_Integer)(n)), 1) 309 | 310 | #define lua_rawget(L, i) \ 311 | (lua_rawget((L), (i)), lua_type((L), -1)) 312 | 313 | #define lua_rawgeti(L, i, n) \ 314 | (lua_rawgeti((L), (i), (n)), lua_type((L), -1)) 315 | 316 | #define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate) 317 | COMPAT53_API void lua_rotate (lua_State *L, int idx, int n); 318 | 319 | #define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti) 320 | COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i); 321 | 322 | #define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber) 323 | COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s); 324 | 325 | #define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring) 326 | COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len); 327 | 328 | #define luaL_getmetafield(L, o, e) \ 329 | (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL) 330 | 331 | #define luaL_newmetatable(L, tn) \ 332 | (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0) 333 | 334 | #define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53) 335 | COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, 336 | lua_CFunction openf, int glb ); 337 | 338 | #endif /* Lua 5.1 and Lua 5.2 */ 339 | 340 | 341 | 342 | /* declarations for Lua 5.2 */ 343 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502 344 | 345 | /* XXX not implemented: 346 | * lua_isyieldable 347 | * lua_arith (new operators) 348 | * lua_pushfstring (new formats) 349 | */ 350 | 351 | #define lua_getglobal(L, n) \ 352 | (lua_getglobal((L), (n)), lua_type((L), -1)) 353 | 354 | #define lua_getuservalue(L, i) \ 355 | (lua_getuservalue((L), (i)), lua_type((L), -1)) 356 | 357 | #define lua_rawgetp(L, i, p) \ 358 | (lua_rawgetp((L), (i), (p)), lua_type((L), -1)) 359 | 360 | #define LUA_KFUNCTION(_name) \ 361 | static int (_name)(lua_State *L, int status, lua_KContext ctx); \ 362 | static int (_name ## _52)(lua_State *L) { \ 363 | lua_KContext ctx; \ 364 | int status = lua_getctx(L, &ctx); \ 365 | return (_name)(L, status, ctx); \ 366 | } \ 367 | static int (_name)(lua_State *L, int status, lua_KContext ctx) 368 | 369 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ 370 | lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52) 371 | 372 | #define lua_callk(L, na, nr, ctx, cont) \ 373 | lua_callk((L), (na), (nr), (ctx), cont ## _52) 374 | 375 | #define lua_yieldk(L, nr, ctx, cont) \ 376 | lua_yieldk((L), (nr), (ctx), cont ## _52) 377 | 378 | #ifdef lua_call 379 | # undef lua_call 380 | # define lua_call(L, na, nr) \ 381 | (lua_callk)((L), (na), (nr), 0, NULL) 382 | #endif 383 | 384 | #ifdef lua_pcall 385 | # undef lua_pcall 386 | # define lua_pcall(L, na, nr, err) \ 387 | (lua_pcallk)((L), (na), (nr), (err), 0, NULL) 388 | #endif 389 | 390 | #ifdef lua_yield 391 | # undef lua_yield 392 | # define lua_yield(L, nr) \ 393 | (lua_yieldk)((L), (nr), 0, NULL) 394 | #endif 395 | 396 | #endif /* Lua 5.2 only */ 397 | 398 | 399 | 400 | /* other Lua versions */ 401 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504 402 | 403 | # error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)" 404 | 405 | #endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */ 406 | 407 | 408 | 409 | /* helper macro for defining continuation functions (for every version 410 | * *except* Lua 5.2) */ 411 | #ifndef LUA_KFUNCTION 412 | #define LUA_KFUNCTION(_name) \ 413 | static int (_name)(lua_State *L, int status, lua_KContext ctx) 414 | #endif 415 | 416 | 417 | #if defined(COMPAT53_INCLUDE_SOURCE) 418 | # include "compat-5.3.c" 419 | #endif 420 | 421 | 422 | #endif /* COMPAT53_H_ */ 423 | ]=], 424 | source = [=[ 425 | #include 426 | #include 427 | #include 428 | #include 429 | #include 430 | #include 431 | #include "compat-5.3.h" 432 | 433 | /* don't compile it again if it already is included via compat53.h */ 434 | #ifndef COMPAT53_C_ 435 | #define COMPAT53_C_ 436 | 437 | 438 | 439 | /* definitions for Lua 5.1 only */ 440 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 441 | 442 | #ifndef COMPAT53_FOPEN_NO_LOCK 443 | # if defined(_MSC_VER) 444 | # define COMPAT53_FOPEN_NO_LOCK 1 445 | # else /* otherwise */ 446 | # define COMPAT53_FOPEN_NO_LOCK 0 447 | # endif /* VC++ only so far */ 448 | #endif /* No-lock fopen_s usage if possible */ 449 | 450 | #if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK 451 | # include 452 | #endif /* VC++ _fsopen for share-allowed file read */ 453 | 454 | #ifndef COMPAT53_HAVE_STRERROR_R 455 | # if (!defined(_WIN32)) && \ 456 | ((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ 457 | (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \ 458 | defined(__APPLE__)) 459 | # define COMPAT53_HAVE_STRERROR_R 1 460 | # else /* none of the defines matched: define to 0 */ 461 | # define COMPAT53_HAVE_STRERROR_R 0 462 | # endif /* have strerror_r of some form */ 463 | #endif /* strerror_r */ 464 | 465 | #ifndef COMPAT53_HAVE_STRERROR_S 466 | # if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \ 467 | defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__) 468 | # define COMPAT53_HAVE_STRERROR_S 1 469 | # else /* not VC++ or C11 */ 470 | # define COMPAT53_HAVE_STRERROR_S 0 471 | # endif /* strerror_s from VC++ or C11 */ 472 | #endif /* strerror_s */ 473 | 474 | #ifndef COMPAT53_LUA_FILE_BUFFER_SIZE 475 | # define COMPAT53_LUA_FILE_BUFFER_SIZE 4096 476 | #endif /* Lua File Buffer Size */ 477 | 478 | 479 | static char* compat53_strerror (int en, char* buff, size_t sz) { 480 | #if COMPAT53_HAVE_STRERROR_R 481 | /* use strerror_r here, because it's available on these specific platforms */ 482 | if (sz > 0) { 483 | buff[0] = '\0'; 484 | /* we don't care whether the GNU version or the XSI version is used: */ 485 | if (strerror_r(en, buff, sz)) { 486 | /* Yes, we really DO want to ignore the return value! 487 | * GCC makes that extra hard, not even a (void) cast will do. */ 488 | } 489 | if (buff[0] == '\0') { 490 | /* Buffer is unchanged, so we probably have called GNU strerror_r which 491 | * returned a static constant string. Chances are that strerror will 492 | * return the same static constant string and therefore be thread-safe. */ 493 | return strerror(en); 494 | } 495 | } 496 | return buff; /* sz is 0 *or* strerror_r wrote into the buffer */ 497 | #elif COMPAT53_HAVE_STRERROR_S 498 | /* for MSVC and other C11 implementations, use strerror_s since it's 499 | * provided by default by the libraries */ 500 | strerror_s(buff, sz, en); 501 | return buff; 502 | #else 503 | /* fallback, but strerror is not guaranteed to be threadsafe due to modifying 504 | * errno itself and some impls not locking a static buffer for it ... but most 505 | * known systems have threadsafe errno: this might only change if the locale 506 | * is changed out from under someone while this function is being called */ 507 | (void)buff; 508 | (void)sz; 509 | return strerror(en); 510 | #endif 511 | } 512 | 513 | 514 | COMPAT53_API int lua_absindex (lua_State *L, int i) { 515 | if (i < 0 && i > LUA_REGISTRYINDEX) 516 | i += lua_gettop(L) + 1; 517 | return i; 518 | } 519 | 520 | 521 | static void compat53_call_lua (lua_State *L, char const code[], size_t len, 522 | int nargs, int nret) { 523 | lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code); 524 | if (lua_type(L, -1) != LUA_TFUNCTION) { 525 | lua_pop(L, 1); 526 | if (luaL_loadbuffer(L, code, len, "=none")) 527 | lua_error(L); 528 | lua_pushvalue(L, -1); 529 | lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code); 530 | } 531 | lua_insert(L, -nargs-1); 532 | lua_call(L, nargs, nret); 533 | } 534 | 535 | 536 | static const char compat53_arith_code[] = 537 | "local op,a,b=...\n" 538 | "if op==0 then return a+b\n" 539 | "elseif op==1 then return a-b\n" 540 | "elseif op==2 then return a*b\n" 541 | "elseif op==3 then return a/b\n" 542 | "elseif op==4 then return a%b\n" 543 | "elseif op==5 then return a^b\n" 544 | "elseif op==6 then return -a\n" 545 | "end\n"; 546 | 547 | COMPAT53_API void lua_arith (lua_State *L, int op) { 548 | if (op < LUA_OPADD || op > LUA_OPUNM) 549 | luaL_error(L, "invalid 'op' argument for lua_arith"); 550 | luaL_checkstack(L, 5, "not enough stack slots"); 551 | if (op == LUA_OPUNM) 552 | lua_pushvalue(L, -1); 553 | lua_pushnumber(L, op); 554 | lua_insert(L, -3); 555 | compat53_call_lua(L, compat53_arith_code, 556 | sizeof(compat53_arith_code)-1, 3, 1); 557 | } 558 | 559 | 560 | static const char compat53_compare_code[] = 561 | "local a,b=...\n" 562 | "return a<=b\n"; 563 | 564 | COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) { 565 | int result = 0; 566 | switch (op) { 567 | case LUA_OPEQ: 568 | return lua_equal(L, idx1, idx2); 569 | case LUA_OPLT: 570 | return lua_lessthan(L, idx1, idx2); 571 | case LUA_OPLE: 572 | luaL_checkstack(L, 5, "not enough stack slots"); 573 | idx1 = lua_absindex(L, idx1); 574 | idx2 = lua_absindex(L, idx2); 575 | lua_pushvalue(L, idx1); 576 | lua_pushvalue(L, idx2); 577 | compat53_call_lua(L, compat53_compare_code, 578 | sizeof(compat53_compare_code)-1, 2, 1); 579 | result = lua_toboolean(L, -1); 580 | lua_pop(L, 1); 581 | return result; 582 | default: 583 | luaL_error(L, "invalid 'op' argument for lua_compare"); 584 | } 585 | return 0; 586 | } 587 | 588 | 589 | COMPAT53_API void lua_copy (lua_State *L, int from, int to) { 590 | int abs_to = lua_absindex(L, to); 591 | luaL_checkstack(L, 1, "not enough stack slots"); 592 | lua_pushvalue(L, from); 593 | lua_replace(L, abs_to); 594 | } 595 | 596 | 597 | COMPAT53_API void lua_len (lua_State *L, int i) { 598 | switch (lua_type(L, i)) { 599 | case LUA_TSTRING: 600 | lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); 601 | break; 602 | case LUA_TTABLE: 603 | if (!luaL_callmeta(L, i, "__len")) 604 | lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); 605 | break; 606 | case LUA_TUSERDATA: 607 | if (luaL_callmeta(L, i, "__len")) 608 | break; 609 | /* FALLTHROUGH */ 610 | default: 611 | luaL_error(L, "attempt to get length of a %s value", 612 | lua_typename(L, lua_type(L, i))); 613 | } 614 | } 615 | 616 | 617 | COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) { 618 | int abs_i = lua_absindex(L, i); 619 | lua_pushlightuserdata(L, (void*)p); 620 | lua_rawget(L, abs_i); 621 | return lua_type(L, -1); 622 | } 623 | 624 | COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) { 625 | int abs_i = lua_absindex(L, i); 626 | luaL_checkstack(L, 1, "not enough stack slots"); 627 | lua_pushlightuserdata(L, (void*)p); 628 | lua_insert(L, -2); 629 | lua_rawset(L, abs_i); 630 | } 631 | 632 | 633 | COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) { 634 | lua_Number n = lua_tonumber(L, i); 635 | if (isnum != NULL) { 636 | *isnum = (n != 0 || lua_isnumber(L, i)); 637 | } 638 | return n; 639 | } 640 | 641 | 642 | COMPAT53_API void luaL_checkversion (lua_State *L) { 643 | (void)L; 644 | } 645 | 646 | 647 | COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) { 648 | if (!lua_checkstack(L, sp+LUA_MINSTACK)) { 649 | if (msg != NULL) 650 | luaL_error(L, "stack overflow (%s)", msg); 651 | else { 652 | lua_pushliteral(L, "stack overflow"); 653 | lua_error(L); 654 | } 655 | } 656 | } 657 | 658 | 659 | COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) { 660 | int abs_i = lua_absindex(L, i); 661 | luaL_checkstack(L, 3, "not enough stack slots"); 662 | lua_pushstring(L, name); 663 | lua_gettable(L, abs_i); 664 | if (lua_istable(L, -1)) 665 | return 1; 666 | lua_pop(L, 1); 667 | lua_newtable(L); 668 | lua_pushstring(L, name); 669 | lua_pushvalue(L, -2); 670 | lua_settable(L, abs_i); 671 | return 0; 672 | } 673 | 674 | 675 | COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) { 676 | lua_Integer res = 0; 677 | int isnum = 0; 678 | luaL_checkstack(L, 1, "not enough stack slots"); 679 | lua_len(L, i); 680 | res = lua_tointegerx(L, -1, &isnum); 681 | lua_pop(L, 1); 682 | if (!isnum) 683 | luaL_error(L, "object length is not an integer"); 684 | return res; 685 | } 686 | 687 | 688 | COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { 689 | luaL_checkstack(L, nup+1, "too many upvalues"); 690 | for (; l->name != NULL; l++) { /* fill the table with given functions */ 691 | int i; 692 | lua_pushstring(L, l->name); 693 | for (i = 0; i < nup; i++) /* copy upvalues to the top */ 694 | lua_pushvalue(L, -(nup + 1)); 695 | lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ 696 | lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ 697 | } 698 | lua_pop(L, nup); /* remove upvalues */ 699 | } 700 | 701 | 702 | COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) { 703 | luaL_checkstack(L, 1, "not enough stack slots"); 704 | luaL_getmetatable(L, tname); 705 | lua_setmetatable(L, -2); 706 | } 707 | 708 | 709 | COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) { 710 | void *p = lua_touserdata(L, i); 711 | luaL_checkstack(L, 2, "not enough stack slots"); 712 | if (p == NULL || !lua_getmetatable(L, i)) 713 | return NULL; 714 | else { 715 | int res = 0; 716 | luaL_getmetatable(L, tname); 717 | res = lua_rawequal(L, -1, -2); 718 | lua_pop(L, 2); 719 | if (!res) 720 | p = NULL; 721 | } 722 | return p; 723 | } 724 | 725 | 726 | static int compat53_countlevels (lua_State *L) { 727 | lua_Debug ar; 728 | int li = 1, le = 1; 729 | /* find an upper bound */ 730 | while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } 731 | /* do a binary search */ 732 | while (li < le) { 733 | int m = (li + le)/2; 734 | if (lua_getstack(L, m, &ar)) li = m + 1; 735 | else le = m; 736 | } 737 | return le - 1; 738 | } 739 | 740 | static int compat53_findfield (lua_State *L, int objidx, int level) { 741 | if (level == 0 || !lua_istable(L, -1)) 742 | return 0; /* not found */ 743 | lua_pushnil(L); /* start 'next' loop */ 744 | while (lua_next(L, -2)) { /* for each pair in table */ 745 | if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ 746 | if (lua_rawequal(L, objidx, -1)) { /* found object? */ 747 | lua_pop(L, 1); /* remove value (but keep name) */ 748 | return 1; 749 | } 750 | else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */ 751 | lua_remove(L, -2); /* remove table (but keep name) */ 752 | lua_pushliteral(L, "."); 753 | lua_insert(L, -2); /* place '.' between the two names */ 754 | lua_concat(L, 3); 755 | return 1; 756 | } 757 | } 758 | lua_pop(L, 1); /* remove value */ 759 | } 760 | return 0; /* not found */ 761 | } 762 | 763 | static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) { 764 | int top = lua_gettop(L); 765 | lua_getinfo(L, "f", ar); /* push function */ 766 | lua_pushvalue(L, LUA_GLOBALSINDEX); 767 | if (compat53_findfield(L, top + 1, 2)) { 768 | lua_copy(L, -1, top + 1); /* move name to proper place */ 769 | lua_pop(L, 2); /* remove pushed values */ 770 | return 1; 771 | } 772 | else { 773 | lua_settop(L, top); /* remove function and global table */ 774 | return 0; 775 | } 776 | } 777 | 778 | static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) { 779 | if (*ar->namewhat != '\0') /* is there a name? */ 780 | lua_pushfstring(L, "function " LUA_QS, ar->name); 781 | else if (*ar->what == 'm') /* main? */ 782 | lua_pushliteral(L, "main chunk"); 783 | else if (*ar->what == 'C') { 784 | if (compat53_pushglobalfuncname(L, ar)) { 785 | lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); 786 | lua_remove(L, -2); /* remove name */ 787 | } 788 | else 789 | lua_pushliteral(L, "?"); 790 | } 791 | else 792 | lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); 793 | } 794 | 795 | #define COMPAT53_LEVELS1 12 /* size of the first part of the stack */ 796 | #define COMPAT53_LEVELS2 10 /* size of the second part of the stack */ 797 | 798 | COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, 799 | const char *msg, int level) { 800 | lua_Debug ar; 801 | int top = lua_gettop(L); 802 | int numlevels = compat53_countlevels(L1); 803 | int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0; 804 | if (msg) lua_pushfstring(L, "%s\n", msg); 805 | lua_pushliteral(L, "stack traceback:"); 806 | while (lua_getstack(L1, level++, &ar)) { 807 | if (level == mark) { /* too many levels? */ 808 | lua_pushliteral(L, "\n\t..."); /* add a '...' */ 809 | level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */ 810 | } 811 | else { 812 | lua_getinfo(L1, "Slnt", &ar); 813 | lua_pushfstring(L, "\n\t%s:", ar.short_src); 814 | if (ar.currentline > 0) 815 | lua_pushfstring(L, "%d:", ar.currentline); 816 | lua_pushliteral(L, " in "); 817 | compat53_pushfuncname(L, &ar); 818 | lua_concat(L, lua_gettop(L) - top); 819 | } 820 | } 821 | lua_concat(L, lua_gettop(L) - top); 822 | } 823 | 824 | 825 | COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { 826 | const char *serr = NULL; 827 | int en = errno; /* calls to Lua API may change this value */ 828 | char buf[512] = { 0 }; 829 | if (stat) { 830 | lua_pushboolean(L, 1); 831 | return 1; 832 | } 833 | else { 834 | lua_pushnil(L); 835 | serr = compat53_strerror(en, buf, sizeof(buf)); 836 | if (fname) 837 | lua_pushfstring(L, "%s: %s", fname, serr); 838 | else 839 | lua_pushstring(L, serr); 840 | lua_pushnumber(L, (lua_Number)en); 841 | return 3; 842 | } 843 | } 844 | 845 | 846 | static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) { 847 | if (mode && strchr(mode, modename[0]) == NULL) { 848 | lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode); 849 | return err; 850 | } 851 | return LUA_OK; 852 | } 853 | 854 | 855 | typedef struct { 856 | lua_Reader reader; 857 | void *ud; 858 | int has_peeked_data; 859 | const char *peeked_data; 860 | size_t peeked_data_size; 861 | } compat53_reader_data; 862 | 863 | 864 | static const char *compat53_reader (lua_State *L, void *ud, size_t *size) { 865 | compat53_reader_data *data = (compat53_reader_data *)ud; 866 | if (data->has_peeked_data) { 867 | data->has_peeked_data = 0; 868 | *size = data->peeked_data_size; 869 | return data->peeked_data; 870 | } else 871 | return data->reader(L, data->ud, size); 872 | } 873 | 874 | 875 | COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) { 876 | int status = LUA_OK; 877 | compat53_reader_data compat53_data = { 0, NULL, 1, 0, 0 }; 878 | compat53_data.reader = reader; 879 | compat53_data.ud = data; 880 | compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size)); 881 | if (compat53_data.peeked_data && compat53_data.peeked_data_size && 882 | compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */ 883 | status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); 884 | else 885 | status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); 886 | if (status != LUA_OK) 887 | return status; 888 | /* we need to call the original 5.1 version of lua_load! */ 889 | #undef lua_load 890 | return lua_load(L, compat53_reader, &compat53_data, source); 891 | #define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) 892 | } 893 | 894 | 895 | typedef struct { 896 | int n; /* number of pre-read characters */ 897 | FILE *f; /* file being read */ 898 | char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */ 899 | } compat53_LoadF; 900 | 901 | 902 | static const char *compat53_getF (lua_State *L, void *ud, size_t *size) { 903 | compat53_LoadF *lf = (compat53_LoadF *)ud; 904 | (void)L; /* not used */ 905 | if (lf->n > 0) { /* are there pre-read characters to be read? */ 906 | *size = lf->n; /* return them (chars already in buffer) */ 907 | lf->n = 0; /* no more pre-read characters */ 908 | } 909 | else { /* read a block from file */ 910 | /* 'fread' can return > 0 *and* set the EOF flag. If next call to 911 | 'compat53_getF' called 'fread', it might still wait for user input. 912 | The next check avoids this problem. */ 913 | if (feof(lf->f)) return NULL; 914 | *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ 915 | } 916 | return lf->buff; 917 | } 918 | 919 | 920 | static int compat53_errfile (lua_State *L, const char *what, int fnameindex) { 921 | char buf[512] = {0}; 922 | const char *serr = compat53_strerror(errno, buf, sizeof(buf)); 923 | const char *filename = lua_tostring(L, fnameindex) + 1; 924 | lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); 925 | lua_remove(L, fnameindex); 926 | return LUA_ERRFILE; 927 | } 928 | 929 | 930 | static int compat53_skipBOM (compat53_LoadF *lf) { 931 | const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ 932 | int c; 933 | lf->n = 0; 934 | do { 935 | c = getc(lf->f); 936 | if (c == EOF || c != *(const unsigned char *)p++) return c; 937 | lf->buff[lf->n++] = (char)c; /* to be read by the parser */ 938 | } while (*p != '\0'); 939 | lf->n = 0; /* prefix matched; discard it */ 940 | return getc(lf->f); /* return next character */ 941 | } 942 | 943 | 944 | /* 945 | ** reads the first character of file 'f' and skips an optional BOM mark 946 | ** in its beginning plus its first line if it starts with '#'. Returns 947 | ** true if it skipped the first line. In any case, '*cp' has the 948 | ** first "valid" character of the file (after the optional BOM and 949 | ** a first-line comment). 950 | */ 951 | static int compat53_skipcomment (compat53_LoadF *lf, int *cp) { 952 | int c = *cp = compat53_skipBOM(lf); 953 | if (c == '#') { /* first line is a comment (Unix exec. file)? */ 954 | do { /* skip first line */ 955 | c = getc(lf->f); 956 | } while (c != EOF && c != '\n'); 957 | *cp = getc(lf->f); /* skip end-of-line, if present */ 958 | return 1; /* there was a comment */ 959 | } 960 | else return 0; /* no comment */ 961 | } 962 | 963 | 964 | COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { 965 | compat53_LoadF lf; 966 | int status, readstatus; 967 | int c; 968 | int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ 969 | if (filename == NULL) { 970 | lua_pushliteral(L, "=stdin"); 971 | lf.f = stdin; 972 | } 973 | else { 974 | lua_pushfstring(L, "@%s", filename); 975 | #if defined(_MSC_VER) 976 | /* This code is here to stop a deprecation error that stops builds 977 | * if a certain macro is defined. While normally not caring would 978 | * be best, some header-only libraries and builds can't afford to 979 | * dictate this to the user. A quick check shows that fopen_s this 980 | * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET, 981 | * possibly even before that so we don't need to do any version 982 | * number checks, since this has been there since forever. */ 983 | 984 | /* TO USER: if you want the behavior of typical fopen_s/fopen, 985 | * which does lock the file on VC++, define the macro used below to 0 */ 986 | #if COMPAT53_FOPEN_NO_LOCK 987 | lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */ 988 | if (lf.f == NULL) 989 | return compat53_errfile(L, "open", fnameindex); 990 | #else /* use default locking version */ 991 | if (fopen_s(&lf.f, filename, "r") != 0) 992 | return compat53_errfile(L, "open", fnameindex); 993 | #endif /* Locking vs. No-locking fopen variants */ 994 | #else 995 | lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */ 996 | if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex); 997 | #endif 998 | } 999 | if (compat53_skipcomment(&lf, &c)) /* read initial portion */ 1000 | lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ 1001 | if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ 1002 | #if defined(_MSC_VER) 1003 | if (freopen_s(&lf.f, filename, "rb", lf.f) != 0) 1004 | return compat53_errfile(L, "reopen", fnameindex); 1005 | #else 1006 | lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ 1007 | if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex); 1008 | #endif 1009 | compat53_skipcomment(&lf, &c); /* re-read initial portion */ 1010 | } 1011 | if (c != EOF) 1012 | lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */ 1013 | status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode); 1014 | readstatus = ferror(lf.f); 1015 | if (filename) fclose(lf.f); /* close file (even in case of errors) */ 1016 | if (readstatus) { 1017 | lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ 1018 | return compat53_errfile(L, "read", fnameindex); 1019 | } 1020 | lua_remove(L, fnameindex); 1021 | return status; 1022 | } 1023 | 1024 | 1025 | COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) { 1026 | int status = LUA_OK; 1027 | if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) { 1028 | status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); 1029 | } 1030 | else { 1031 | status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); 1032 | } 1033 | if (status != LUA_OK) 1034 | return status; 1035 | return luaL_loadbuffer(L, buff, sz, name); 1036 | } 1037 | 1038 | 1039 | #if !defined(l_inspectstat) && \ 1040 | (defined(unix) || defined(__unix) || defined(__unix__) || \ 1041 | defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \ 1042 | (defined(__APPLE__) && defined(__MACH__))) 1043 | /* some form of unix; check feature macros in unistd.h for details */ 1044 | # include 1045 | /* check posix version; the relevant include files and macros probably 1046 | * were available before 2001, but I'm not sure */ 1047 | # if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L 1048 | # include 1049 | # define l_inspectstat(stat,what) \ 1050 | if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ 1051 | else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } 1052 | # endif 1053 | #endif 1054 | 1055 | /* provide default (no-op) version */ 1056 | #if !defined(l_inspectstat) 1057 | # define l_inspectstat(stat,what) ((void)0) 1058 | #endif 1059 | 1060 | 1061 | COMPAT53_API int luaL_execresult (lua_State *L, int stat) { 1062 | const char *what = "exit"; 1063 | if (stat == -1) 1064 | return luaL_fileresult(L, 0, NULL); 1065 | else { 1066 | l_inspectstat(stat, what); 1067 | if (*what == 'e' && stat == 0) 1068 | lua_pushboolean(L, 1); 1069 | else 1070 | lua_pushnil(L); 1071 | lua_pushstring(L, what); 1072 | lua_pushinteger(L, stat); 1073 | return 3; 1074 | } 1075 | } 1076 | 1077 | 1078 | COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) { 1079 | /* make it crash if used via pointer to a 5.1-style luaL_Buffer */ 1080 | B->b.p = NULL; 1081 | B->b.L = NULL; 1082 | B->b.lvl = 0; 1083 | /* reuse the buffer from the 5.1-style luaL_Buffer though! */ 1084 | B->ptr = B->b.buffer; 1085 | B->capacity = LUAL_BUFFERSIZE; 1086 | B->nelems = 0; 1087 | B->L2 = L; 1088 | } 1089 | 1090 | 1091 | COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) { 1092 | if (B->capacity - B->nelems < s) { /* needs to grow */ 1093 | char* newptr = NULL; 1094 | size_t newcap = B->capacity * 2; 1095 | if (newcap - B->nelems < s) 1096 | newcap = B->nelems + s; 1097 | if (newcap < B->capacity) /* overflow */ 1098 | luaL_error(B->L2, "buffer too large"); 1099 | newptr = (char*)lua_newuserdata(B->L2, newcap); 1100 | memcpy(newptr, B->ptr, B->nelems); 1101 | if (B->ptr != B->b.buffer) 1102 | lua_replace(B->L2, -2); /* remove old buffer */ 1103 | B->ptr = newptr; 1104 | B->capacity = newcap; 1105 | } 1106 | return B->ptr+B->nelems; 1107 | } 1108 | 1109 | 1110 | COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) { 1111 | memcpy(luaL_prepbuffsize(B, l), s, l); 1112 | luaL_addsize(B, l); 1113 | } 1114 | 1115 | 1116 | COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) { 1117 | size_t len = 0; 1118 | const char *s = lua_tolstring(B->L2, -1, &len); 1119 | if (!s) 1120 | luaL_error(B->L2, "cannot convert value to string"); 1121 | if (B->ptr != B->b.buffer) 1122 | lua_insert(B->L2, -2); /* userdata buffer must be at stack top */ 1123 | luaL_addlstring(B, s, len); 1124 | lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1); 1125 | } 1126 | 1127 | 1128 | void luaL_pushresult (luaL_Buffer_53 *B) { 1129 | lua_pushlstring(B->L2, B->ptr, B->nelems); 1130 | if (B->ptr != B->b.buffer) 1131 | lua_replace(B->L2, -2); /* remove userdata buffer */ 1132 | } 1133 | 1134 | 1135 | #endif /* Lua 5.1 */ 1136 | 1137 | 1138 | 1139 | /* definitions for Lua 5.1 and Lua 5.2 */ 1140 | #if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502 1141 | 1142 | 1143 | COMPAT53_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { 1144 | #undef lua_pushlstring 1145 | lua_pushlstring(L, len > 0 ? s : "", len); 1146 | #define lua_pushlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _pushlstring_53) 1147 | return lua_tostring(L, -1); 1148 | } 1149 | 1150 | 1151 | COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) { 1152 | index = lua_absindex(L, index); 1153 | lua_pushinteger(L, i); 1154 | lua_gettable(L, index); 1155 | return lua_type(L, -1); 1156 | } 1157 | 1158 | 1159 | #ifndef LUA_EXTRASPACE 1160 | #define LUA_EXTRASPACE (sizeof(void*)) 1161 | #endif 1162 | 1163 | COMPAT53_API void *lua_getextraspace (lua_State *L) { 1164 | int is_main = 0; 1165 | void *ptr = NULL; 1166 | luaL_checkstack(L, 4, "not enough stack slots available"); 1167 | lua_pushliteral(L, "__compat53_extraspace"); 1168 | lua_pushvalue(L, -1); 1169 | lua_rawget(L, LUA_REGISTRYINDEX); 1170 | if (!lua_istable(L, -1)) { 1171 | lua_pop(L, 1); 1172 | lua_createtable(L, 0, 2); 1173 | lua_createtable(L, 0, 1); 1174 | lua_pushliteral(L, "k"); 1175 | lua_setfield(L, -2, "__mode"); 1176 | lua_setmetatable(L, -2); 1177 | lua_pushvalue(L, -2); 1178 | lua_pushvalue(L, -2); 1179 | lua_rawset(L, LUA_REGISTRYINDEX); 1180 | } 1181 | lua_replace(L, -2); 1182 | is_main = lua_pushthread(L); 1183 | lua_rawget(L, -2); 1184 | ptr = lua_touserdata(L, -1); 1185 | if (!ptr) { 1186 | lua_pop(L, 1); 1187 | ptr = lua_newuserdata(L, LUA_EXTRASPACE); 1188 | if (is_main) { 1189 | memset(ptr, '\0', LUA_EXTRASPACE); 1190 | lua_pushthread(L); 1191 | lua_pushvalue(L, -2); 1192 | lua_rawset(L, -4); 1193 | lua_pushboolean(L, 1); 1194 | lua_pushvalue(L, -2); 1195 | lua_rawset(L, -4); 1196 | } else { 1197 | void* mptr = NULL; 1198 | lua_pushboolean(L, 1); 1199 | lua_rawget(L, -3); 1200 | mptr = lua_touserdata(L, -1); 1201 | if (mptr) 1202 | memcpy(ptr, mptr, LUA_EXTRASPACE); 1203 | else 1204 | memset(ptr, '\0', LUA_EXTRASPACE); 1205 | lua_pop(L, 1); 1206 | lua_pushthread(L); 1207 | lua_pushvalue(L, -2); 1208 | lua_rawset(L, -4); 1209 | } 1210 | } 1211 | lua_pop(L, 2); 1212 | return ptr; 1213 | } 1214 | 1215 | 1216 | COMPAT53_API int lua_isinteger (lua_State *L, int index) { 1217 | if (lua_type(L, index) == LUA_TNUMBER) { 1218 | lua_Number n = lua_tonumber(L, index); 1219 | lua_Integer i = lua_tointeger(L, index); 1220 | if (i == n) 1221 | return 1; 1222 | } 1223 | return 0; 1224 | } 1225 | 1226 | 1227 | COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) { 1228 | int ok = 0; 1229 | lua_Number n = lua_tonumberx(L, i, &ok); 1230 | if (ok) { 1231 | if (n == (lua_Integer)n) { 1232 | if (isnum) 1233 | *isnum = 1; 1234 | return (lua_Integer)n; 1235 | } 1236 | } 1237 | if (isnum) 1238 | *isnum = 0; 1239 | return 0; 1240 | } 1241 | 1242 | 1243 | static void compat53_reverse (lua_State *L, int a, int b) { 1244 | for (; a < b; ++a, --b) { 1245 | lua_pushvalue(L, a); 1246 | lua_pushvalue(L, b); 1247 | lua_replace(L, a); 1248 | lua_replace(L, b); 1249 | } 1250 | } 1251 | 1252 | 1253 | COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) { 1254 | int n_elems = 0; 1255 | idx = lua_absindex(L, idx); 1256 | n_elems = lua_gettop(L)-idx+1; 1257 | if (n < 0) 1258 | n += n_elems; 1259 | if ( n > 0 && n < n_elems) { 1260 | luaL_checkstack(L, 2, "not enough stack slots available"); 1261 | n = n_elems - n; 1262 | compat53_reverse(L, idx, idx+n-1); 1263 | compat53_reverse(L, idx+n, idx+n_elems-1); 1264 | compat53_reverse(L, idx, idx+n_elems-1); 1265 | } 1266 | } 1267 | 1268 | 1269 | COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) { 1270 | luaL_checkstack(L, 1, "not enough stack slots available"); 1271 | index = lua_absindex(L, index); 1272 | lua_pushinteger(L, i); 1273 | lua_insert(L, -2); 1274 | lua_settable(L, index); 1275 | } 1276 | 1277 | 1278 | #if !defined(lua_str2number) 1279 | # define lua_str2number(s, p) strtod((s), (p)) 1280 | #endif 1281 | 1282 | COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) { 1283 | char* endptr; 1284 | lua_Number n = lua_str2number(s, &endptr); 1285 | if (endptr != s) { 1286 | while (*endptr != '\0' && isspace((unsigned char)*endptr)) 1287 | ++endptr; 1288 | if (*endptr == '\0') { 1289 | lua_pushnumber(L, n); 1290 | return endptr - s + 1; 1291 | } 1292 | } 1293 | return 0; 1294 | } 1295 | 1296 | 1297 | COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { 1298 | if (!luaL_callmeta(L, idx, "__tostring")) { 1299 | int t = lua_type(L, idx), tt = 0; 1300 | char const* name = NULL; 1301 | switch (t) { 1302 | case LUA_TNIL: 1303 | lua_pushliteral(L, "nil"); 1304 | break; 1305 | case LUA_TSTRING: 1306 | case LUA_TNUMBER: 1307 | lua_pushvalue(L, idx); 1308 | break; 1309 | case LUA_TBOOLEAN: 1310 | if (lua_toboolean(L, idx)) 1311 | lua_pushliteral(L, "true"); 1312 | else 1313 | lua_pushliteral(L, "false"); 1314 | break; 1315 | default: 1316 | tt = luaL_getmetafield(L, idx, "__name"); 1317 | name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t); 1318 | lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx)); 1319 | if (tt != LUA_TNIL) 1320 | lua_replace(L, -2); 1321 | break; 1322 | } 1323 | } else { 1324 | if (!lua_isstring(L, -1)) 1325 | luaL_error(L, "'__tostring' must return a string"); 1326 | } 1327 | return lua_tolstring(L, -1, len); 1328 | } 1329 | 1330 | 1331 | COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, 1332 | lua_CFunction openf, int glb) { 1333 | luaL_checkstack(L, 3, "not enough stack slots available"); 1334 | luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); 1335 | if (lua_getfield(L, -1, modname) == LUA_TNIL) { 1336 | lua_pop(L, 1); 1337 | lua_pushcfunction(L, openf); 1338 | lua_pushstring(L, modname); 1339 | lua_call(L, 1, 1); 1340 | lua_pushvalue(L, -1); 1341 | lua_setfield(L, -3, modname); 1342 | } 1343 | if (glb) { 1344 | lua_pushvalue(L, -1); 1345 | lua_setglobal(L, modname); 1346 | } 1347 | lua_replace(L, -2); 1348 | } 1349 | 1350 | 1351 | #endif /* Lua 5.1 and 5.2 */ 1352 | 1353 | 1354 | #endif /* COMPAT53_C_ */ 1355 | 1356 | 1357 | /********************************************************************* 1358 | * This file contains parts of Lua 5.2's and Lua 5.3's source code: 1359 | * 1360 | * Copyright (C) 1994-2014 Lua.org, PUC-Rio. 1361 | * 1362 | * Permission is hereby granted, free of charge, to any person obtaining 1363 | * a copy of this software and associated documentation files (the 1364 | * "Software"), to deal in the Software without restriction, including 1365 | * without limitation the rights to use, copy, modify, merge, publish, 1366 | * distribute, sublicense, and/or sell copies of the Software, and to 1367 | * permit persons to whom the Software is furnished to do so, subject to 1368 | * the following conditions: 1369 | * 1370 | * The above copyright notice and this permission notice shall be 1371 | * included in all copies or substantial portions of the Software. 1372 | * 1373 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1374 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1375 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1376 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 1377 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 1378 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 1379 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1380 | *********************************************************************/ 1381 | 1382 | 1383 | ]=] 1384 | } 1385 | -------------------------------------------------------------------------------- /src/executables/loaders/static/loader.lua: -------------------------------------------------------------------------------- 1 | return [[ 2 | /** 3 | * Copyright (c) 2023 Amrit Bhogal 4 | * 5 | * This software is released under the MIT License. 6 | * https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "compat-5.3.h" 13 | 14 | //Just here for intellisense, it will be replaced by the build script 15 | #if !defined(COMBUSTION_ENTRY) 16 | # define COMBUSTION_ENTRY ERROR_COMBUSTION_ENTRY_NOT_DEFINED 17 | # define COMBUSTION_MODULE_DEFINITIONS 18 | # define COMBUSTION_MODULE_LIST { "", ERROR_COMBUSTION_MODULE_LIST_NOT_DEFINED }, 19 | static int ERROR_COMBUSTION_MODULE_LIST_NOT_DEFINED(lua_State *lua) 20 | { 21 | return 0; 22 | } 23 | #endif 24 | 25 | #define _EVAL(x) x 26 | #define EVAL(...) _EVAL(__VA_ARGS__) 27 | 28 | #define CONCAT(x, y) EVAL(x##y) 29 | #define _STRINGIFY(x) #x 30 | #define STRINGIFY(...) _STRINGIFY(__VA_ARGS__) 31 | 32 | #define MODULE(x) CONCAT($internal_module$_, x) 33 | #define DECLARE_MODULE(x) __attribute__((used)) int MODULE(x)(lua_State *lua) 34 | 35 | static bool debug_output; 36 | static void debug_f(const char *fmt, ...) 37 | { 38 | if (!debug_output) 39 | return; 40 | va_list args; 41 | va_start(args, fmt); 42 | vfprintf(stderr, fmt, args); 43 | va_end(args); 44 | } 45 | #define debug(fmt, ...) do { debug_f("["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); } while (0) 46 | 47 | static void perror_f(lua_State *lua, const char *fmt, ...) 48 | { 49 | va_list args; 50 | va_start(args, fmt); 51 | static char buf[1024] = {0}; 52 | vsnprintf(buf, 1024, fmt, args); 53 | va_end(args); 54 | 55 | if (lua != NULL) 56 | luaL_error(lua, "%s: %s", buf, strerror(errno)); 57 | else 58 | perror(buf); 59 | } 60 | #define error(lua, fmt, ...) do { perror_f(lua, "["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); exit(1); } while (0) 61 | 62 | COMBUSTION_MODULE_DEFINITIONS 63 | static luaL_Reg MODULES[] = { 64 | COMBUSTION_MODULE_LIST 65 | {0} 66 | }; 67 | 68 | static int internal_module_searcher(lua_State *lua) 69 | { 70 | size_t nlen = 0; 71 | const char *name = luaL_checklstring(lua, 1, &nlen); 72 | const luaL_Reg *module = MODULES; 73 | for (; module->name; module++) { 74 | if (strncmp(module->name, name, nlen) == 0) { 75 | debug("Found module %s (match %s)\n", module->name, name); 76 | lua_pushcfunction(lua, module->func); 77 | return 1; 78 | } 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | static struct Arguments { 85 | const char *program_name, 86 | **arguments; 87 | size_t count; 88 | } arguments; 89 | 90 | static int setup_arg_table(lua_State *lua) 91 | { 92 | debug("Setting up arg table\n"); 93 | lua_newtable(lua); 94 | //make sure arg[0] is the program name 95 | lua_pushstring(lua, arguments.program_name); 96 | lua_rawseti(lua, -2, 0); 97 | 98 | for (size_t i = 0; i < arguments.count; i++) { 99 | debug("Setting arg[%zu] to %s\n", i + 1, arguments.arguments[i]); 100 | lua_pushstring(lua, arguments.arguments[i]); 101 | lua_rawseti(lua, -2, i + 1); 102 | } 103 | 104 | lua_setglobal(lua, "arg"); 105 | return 0; 106 | } 107 | 108 | #if defined(WIN32_GRAPHICAL) 109 | static int main(int argc, const char *argv[const argc]); 110 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 111 | { 112 | int argc; 113 | char **argv = CommandLineToArgvA(GetCommandLineA(), &argc); 114 | return main(argc, argv); 115 | } 116 | static 117 | #endif 118 | //This program will be run when the executable is run, and it will unpack the zip file to tmpdir, and run the lua interpreter on the entrypoint file 119 | int main(int argc, char *argv[static argc]) 120 | { 121 | arguments.program_name = argv[0]; 122 | arguments.arguments = (const char **)argv + 1; 123 | arguments.count = argc - 1; 124 | 125 | const char *dbg_env = getenv("COMBUSTION_LOADER_VERBOSE"); 126 | debug_output = dbg_env != NULL ? (dbg_env[0] == '1') : false; 127 | srand(time(NULL)); 128 | 129 | lua_State *lua = luaL_newstate(); 130 | if (lua == NULL) { 131 | error(NULL, "Failed to create new Lua state"); 132 | return 1; 133 | } 134 | 135 | //pause GC while we load the modules and install the searcher 136 | lua_gc(lua, LUA_GCSTOP, 0); 137 | { 138 | luaL_openlibs(lua); 139 | 140 | //Install the searcher, 141 | { 142 | debug("Installing searcher\n"); 143 | 144 | lua_getglobal(lua, LUA_LOADLIBNAME); 145 | if (!lua_istable(lua, -1)) { 146 | error(lua, "Failed to get "LUA_LOADLIBNAME" table!"); 147 | return 1; 148 | } 149 | 150 | const char *searcher = "searchers"; 151 | lua_getfield(lua, -1, searcher); 152 | if (!lua_istable(lua, -1)) { 153 | debug("Failed to get "LUA_LOADLIBNAME".searchers table, trying "LUA_LOADLIBNAME".loaders\n"); 154 | searcher = "loaders"; 155 | debug("Checking in "LUA_LOADLIBNAME".loaders (type %s)\n", lua_typename(lua, lua_type(lua, -2))); 156 | lua_getfield(lua, -2, searcher); 157 | if (!lua_istable(lua, -1)) { 158 | error(lua, "Failed to get "LUA_LOADLIBNAME".searchers or "LUA_LOADLIBNAME".loaders table!"); 159 | return 1; 160 | } 161 | 162 | debug("Found "LUA_LOADLIBNAME".%s\n", searcher); 163 | } 164 | 165 | lua_pushcfunction(lua, internal_module_searcher); 166 | lua_rawseti(lua, -2, lua_rawlen(lua, -2) + 1); 167 | 168 | lua_pop(lua, 2); 169 | 170 | debug("Installed searcher\n"); 171 | } 172 | 173 | 174 | lua_pushcfunction(lua, setup_arg_table); 175 | if (lua_pcall(lua, 0, 0, 0) != LUA_OK) { 176 | error(lua, "Failed to setup arg table"); 177 | return 1; 178 | } 179 | } 180 | lua_gc(lua, LUA_GCRESTART, -1); //https://github.com/LuaJIT/LuaJIT/blob/e826d0c101d750fac8334d71e221c50d8dbe236c/src/luajit.c#L535C2-L535C2 181 | 182 | extern int MODULE(COMBUSTION_ENTRY)(lua_State *L); 183 | 184 | debug("Executing entrypoint "STRINGIFY(MODULE(COMBUSTION_ENTRY))"\n"); 185 | // MODULE(COMBUSTION_ENTRY)(lua); 186 | lua_pushcfunction(lua, MODULE(COMBUSTION_ENTRY)); 187 | lua_call(lua, 0, 0); 188 | // int stat = lua_pcall(lua, 0, 1, 0); 189 | // if (stat != LUA_OK) { 190 | // error(lua, "Error executing entrypoint"); 191 | // return 1; 192 | // } 193 | 194 | debug("Program exited\n"); 195 | lua_close(lua); 196 | 197 | return 0; 198 | } 199 | 200 | ]] -------------------------------------------------------------------------------- /src/executables/loaders/static/module-template.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2023 Amrit Bhogal 3 | * 4 | * This software is released under the MIT License. 5 | * https://opensource.org/licenses/MIT 6 | */ 7 | 8 | #include 9 | 10 | #include "compat-5.3.h" 11 | 12 | #define _EVAL(x) x 13 | #define EVAL(...) _EVAL(__VA_ARGS__) 14 | 15 | #define CONCAT(x, y) EVAL(x##y) 16 | #define _STRINGIFY(x) #x 17 | #define STRINGIFY(...) _STRINGIFY(__VA_ARGS__) 18 | 19 | #define MODULE(x) CONCAT($internal_module$_, x) 20 | #define DECLARE_MODULE(x) __attribute__((used)) int MODULE(x)(lua_State *lua) 21 | 22 | // For clangd 23 | #if !defined(COMBUSTION_MODULE_SYMBOL) 24 | # define COMBUSTION_MODULE_SYMBOL ERROR__MODULE_SYMBOL_NOT_DEFINED 25 | # define COMBUSTION_MODULE_NAME ERROR__MODULE_NAME_NOT_DEFINED 26 | # define COMBUSTION_MODULE_BYTECODE_SYMBOL ERROR_MODULE_BYTECODE_SYMBOL_NOT_DEFINED 27 | # define COMBUSTION_MODULE_BYTECODE_SIZE 1 28 | #endif 29 | 30 | extern const char COMBUSTION_MODULE_BYTECODE_SYMBOL[COMBUSTION_MODULE_BYTECODE_SIZE]; 31 | 32 | static void debug_f(const char *fmt, ...) 33 | { 34 | va_list args; 35 | va_start(args, fmt); 36 | vfprintf(stderr, fmt, args); 37 | va_end(args); 38 | } 39 | #define debug(fmt, ...) do { debug_f("["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); } while (0) 40 | 41 | static void perror_f(const char *fmt, ...) 42 | { 43 | va_list args; 44 | va_start(args, fmt); 45 | static char buf[1024] = {0}; 46 | vsnprintf(buf, 1024, fmt, args); 47 | va_end(args); 48 | perror(buf); 49 | } 50 | #define error(fmt, ...) do { perror_f("["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); exit(1); } while (0) 51 | 52 | 53 | DECLARE_MODULE(COMBUSTION_MODULE_SYMBOL) 54 | { 55 | debug("Loading module %s (size: %zu, id: %s)\n", STRINGIFY(COMBUSTION_MODULE_NAME), COMBUSTION_MODULE_BYTECODE_SIZE, STRINGIFY(MODULE(COMBUSTION_MODULE_SYMBOL))); 56 | int stat = luaL_loadbuffer(lua, COMBUSTION_MODULE_BYTECODE_SYMBOL, COMBUSTION_MODULE_BYTECODE_SIZE, STRINGIFY(COMBUSTION_MODULE_NAME)); 57 | if (stat != LUA_OK) { 58 | error("Failed to load module %s: %s\n", STRINGIFY(COMBUSTION_MODULE_NAME), lua_tostring(lua, -1)); 59 | exit(1); 60 | } 61 | 62 | int ret = lua_pcall(lua, 0, LUA_MULTRET, 0); 63 | if (ret != LUA_OK) { 64 | error("Failed to load module %s: %s\n", STRINGIFY(COMBUSTION_MODULE_NAME), lua_tostring(lua, -1)); 65 | exit(1); 66 | } 67 | 68 | return 1; 69 | }; 70 | -------------------------------------------------------------------------------- /src/executables/loaders/static/module-template.lua: -------------------------------------------------------------------------------- 1 | return [[ 2 | /** 3 | * Copyright (c) 2023 Amrit Bhogal 4 | * 5 | * This software is released under the MIT License. 6 | * https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include 10 | 11 | #include "compat-5.3.h" 12 | 13 | #define _EVAL(x) x 14 | #define EVAL(...) _EVAL(__VA_ARGS__) 15 | 16 | #define CONCAT(x, y) EVAL(x##y) 17 | #define _STRINGIFY(x) #x 18 | #define STRINGIFY(...) _STRINGIFY(__VA_ARGS__) 19 | 20 | #define MODULE(x) CONCAT($internal_module$_, x) 21 | #define DECLARE_MODULE(x) __attribute__((used)) int MODULE(x)(lua_State *lua) 22 | 23 | // For clangd 24 | #if !defined(COMBUSTION_MODULE_SYMBOL) 25 | # define COMBUSTION_MODULE_SYMBOL ERROR__MODULE_SYMBOL_NOT_DEFINED 26 | # define COMBUSTION_MODULE_NAME ERROR__MODULE_NAME_NOT_DEFINED 27 | # define COMBUSTION_MODULE_BYTECODE_SYMBOL ERROR_MODULE_BYTECODE_SYMBOL_NOT_DEFINED 28 | # define COMBUSTION_MODULE_BYTECODE_SIZE 1 29 | #endif 30 | 31 | extern const char COMBUSTION_MODULE_BYTECODE_SYMBOL[COMBUSTION_MODULE_BYTECODE_SIZE]; 32 | 33 | static void debug_f(const char *fmt, ...) 34 | { 35 | va_list args; 36 | va_start(args, fmt); 37 | vfprintf(stderr, fmt, args); 38 | va_end(args); 39 | } 40 | #define debug(fmt, ...) do { debug_f("["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); } while (0) 41 | 42 | static void perror_f(lua_State *lua, const char *fmt, ...) 43 | { 44 | va_list args; 45 | va_start(args, fmt); 46 | static char buf[1024] = {0}; 47 | vsnprintf(buf, 1024, fmt, args); 48 | va_end(args); 49 | luaL_error(lua, "%s: %s", buf, strerror(errno)); 50 | } 51 | #define error(fmt, ...) do { perror_f(lua, "["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); exit(1); } while (0) 52 | 53 | 54 | DECLARE_MODULE(COMBUSTION_MODULE_SYMBOL) 55 | { 56 | debug("Loading module %s (size: %zu, id: %s)\n", STRINGIFY(COMBUSTION_MODULE_NAME), COMBUSTION_MODULE_BYTECODE_SIZE, STRINGIFY(MODULE(COMBUSTION_MODULE_SYMBOL))); 57 | int stat = luaL_loadbuffer(lua, COMBUSTION_MODULE_BYTECODE_SYMBOL, COMBUSTION_MODULE_BYTECODE_SIZE, STRINGIFY(COMBUSTION_MODULE_NAME)); 58 | if (stat != LUA_OK) { 59 | error("Failed to load module %s: %s\n", STRINGIFY(COMBUSTION_MODULE_NAME), lua_tostring(lua, -1)); 60 | exit(1); 61 | } 62 | 63 | int ret = lua_pcall(lua, 0, LUA_MULTRET, 0); 64 | if (ret != LUA_OK) { 65 | error("Failed to load module %s: %s\n", STRINGIFY(COMBUSTION_MODULE_NAME), lua_tostring(lua, -1)); 66 | exit(1); 67 | } 68 | 69 | return 1; 70 | }; 71 | 72 | ]] -------------------------------------------------------------------------------- /src/executables/loaders/static/static.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2023 Amrit Bhogal 3 | * 4 | * This software is released under the MIT License. 5 | * https://opensource.org/licenses/MIT 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "compat-5.3.h" 12 | 13 | //Just here for intellisense, it will be replaced by the build script 14 | #if !defined(COMBUSTION_ENTRY) 15 | # define COMBUSTION_ENTRY ERROR_COMBUSTION_ENTRY_NOT_DEFINED 16 | # define COMBUSTION_MODULE_DEFINITIONS 17 | # define COMBUSTION_MODULE_LIST { "", ERROR_COMBUSTION_MODULE_LIST_NOT_DEFINED }, 18 | static int ERROR_COMBUSTION_MODULE_LIST_NOT_DEFINED(lua_State *lua) 19 | { 20 | return 0; 21 | } 22 | #endif 23 | 24 | #define _EVAL(x) x 25 | #define EVAL(...) _EVAL(__VA_ARGS__) 26 | 27 | #define CONCAT(x, y) EVAL(x##y) 28 | #define _STRINGIFY(x) #x 29 | #define STRINGIFY(...) _STRINGIFY(__VA_ARGS__) 30 | 31 | #define MODULE(x) CONCAT($internal_module$_, x) 32 | #define DECLARE_MODULE(x) __attribute__((used)) int MODULE(x)(lua_State *lua) 33 | 34 | static bool debug_output; 35 | static void debug_f(const char *fmt, ...) 36 | { 37 | if (!debug_output) 38 | return; 39 | va_list args; 40 | va_start(args, fmt); 41 | vfprintf(stderr, fmt, args); 42 | va_end(args); 43 | } 44 | #define debug(fmt, ...) do { debug_f("["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); } while (0) 45 | 46 | static void perror_f(lua_State *lua, const char *fmt, ...) 47 | { 48 | va_list args; 49 | va_start(args, fmt); 50 | static char buf[1024] = {0}; 51 | vsnprintf(buf, 1024, fmt, args); 52 | va_end(args); 53 | 54 | if (lua != NULL) 55 | luaL_error(lua, "%s: %s", buf, strerror(errno)); 56 | else 57 | perror(buf); 58 | } 59 | #define error(lua, fmt, ...) do { perror_f(lua, "["__FILE__":"STRINGIFY(__LINE__)"] " fmt __VA_OPT__(,) __VA_ARGS__); exit(1); } while (0) 60 | 61 | COMBUSTION_MODULE_DEFINITIONS 62 | static luaL_Reg MODULES[] = { 63 | COMBUSTION_MODULE_LIST 64 | {0} 65 | }; 66 | 67 | static int internal_module_searcher(lua_State *lua) 68 | { 69 | size_t nlen = 0; 70 | const char *name = luaL_checklstring(lua, 1, &nlen); 71 | const luaL_Reg *module = MODULES; 72 | for (; module->name; module++) { 73 | if (strncmp(module->name, name, nlen) == 0) { 74 | debug("Found module %s (match %s)\n", module->name, name); 75 | lua_pushcfunction(lua, module->func); 76 | return 1; 77 | } 78 | } 79 | 80 | return 0; 81 | } 82 | 83 | static struct Arguments { 84 | const char *program_name, 85 | **arguments; 86 | size_t count; 87 | } arguments; 88 | 89 | static int setup_arg_table(lua_State *lua) 90 | { 91 | debug("Setting up arg table\n"); 92 | lua_newtable(lua); 93 | //make sure arg[0] is the program name 94 | lua_pushstring(lua, arguments.program_name); 95 | lua_rawseti(lua, -2, 0); 96 | 97 | for (size_t i = 0; i < arguments.count; i++) { 98 | debug("Setting arg[%zu] to %s\n", i + 1, arguments.arguments[i]); 99 | lua_pushstring(lua, arguments.arguments[i]); 100 | lua_rawseti(lua, -2, i + 1); 101 | } 102 | 103 | lua_setglobal(lua, "arg"); 104 | return 0; 105 | } 106 | 107 | #if defined(WIN32_GRAPHICAL) 108 | static int main(int argc, const char *argv[const argc]); 109 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 110 | { 111 | int argc; 112 | char **argv = CommandLineToArgvA(GetCommandLineA(), &argc); 113 | return main(argc, argv); 114 | } 115 | static 116 | #endif 117 | //This program will be run when the executable is run, and it will unpack the zip file to tmpdir, and run the lua interpreter on the entrypoint file 118 | int main(int argc, char *argv[static argc]) 119 | { 120 | arguments.program_name = argv[0]; 121 | arguments.arguments = (const char **)argv + 1; 122 | arguments.count = argc - 1; 123 | 124 | const char *dbg_env = getenv("COMBUSTION_LOADER_VERBOSE"); 125 | debug_output = dbg_env != NULL ? (dbg_env[0] == '1') : false; 126 | srand(time(NULL)); 127 | 128 | lua_State *lua = luaL_newstate(); 129 | if (lua == NULL) { 130 | error(NULL, "Failed to create new Lua state"); 131 | return 1; 132 | } 133 | 134 | //pause GC while we load the modules and install the searcher 135 | lua_gc(lua, LUA_GCSTOP, 0); 136 | { 137 | luaL_openlibs(lua); 138 | 139 | //Install the searcher, 140 | { 141 | debug("Installing searcher\n"); 142 | 143 | lua_getglobal(lua, LUA_LOADLIBNAME); 144 | if (!lua_istable(lua, -1)) { 145 | error(lua, "Failed to get "LUA_LOADLIBNAME" table!"); 146 | return 1; 147 | } 148 | 149 | const char *searcher = "searchers"; 150 | lua_getfield(lua, -1, searcher); 151 | if (!lua_istable(lua, -1)) { 152 | debug("Failed to get "LUA_LOADLIBNAME".searchers table, trying "LUA_LOADLIBNAME".loaders\n"); 153 | searcher = "loaders"; 154 | debug("Checking in "LUA_LOADLIBNAME".loaders (type %s)\n", lua_typename(lua, lua_type(lua, -2))); 155 | lua_getfield(lua, -2, searcher); 156 | if (!lua_istable(lua, -1)) { 157 | error(lua, "Failed to get "LUA_LOADLIBNAME".searchers or "LUA_LOADLIBNAME".loaders table!"); 158 | return 1; 159 | } 160 | 161 | debug("Found "LUA_LOADLIBNAME".%s\n", searcher); 162 | } 163 | 164 | lua_pushcfunction(lua, internal_module_searcher); 165 | lua_rawseti(lua, -2, lua_rawlen(lua, -2) + 1); 166 | 167 | lua_pop(lua, 2); 168 | 169 | debug("Installed searcher\n"); 170 | } 171 | 172 | 173 | lua_pushcfunction(lua, setup_arg_table); 174 | if (lua_pcall(lua, 0, 0, 0) != LUA_OK) { 175 | error(lua, "Failed to setup arg table"); 176 | return 1; 177 | } 178 | } 179 | lua_gc(lua, LUA_GCRESTART, -1); //https://github.com/LuaJIT/LuaJIT/blob/e826d0c101d750fac8334d71e221c50d8dbe236c/src/luajit.c#L535C2-L535C2 180 | 181 | extern int MODULE(COMBUSTION_ENTRY)(lua_State *L); 182 | 183 | debug("Executing entrypoint "STRINGIFY(MODULE(COMBUSTION_ENTRY))"\n"); 184 | // MODULE(COMBUSTION_ENTRY)(lua); 185 | lua_pushcfunction(lua, MODULE(COMBUSTION_ENTRY)); 186 | lua_call(lua, 0, 0); 187 | // int stat = lua_pcall(lua, 0, 1, 0); 188 | // if (stat != LUA_OK) { 189 | // error(lua, "Error executing entrypoint"); 190 | // return 1; 191 | // } 192 | 193 | debug("Program exited\n"); 194 | lua_close(lua); 195 | 196 | return 0; 197 | } 198 | -------------------------------------------------------------------------------- /src/executables/self-extract.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2023 Amrit Bhogal 2 | -- 3 | -- This software is released under the MIT License. 4 | -- https://opensource.org/licenses/MIT 5 | 6 | local path = require("pl.path") 7 | local directory = require("pl.dir") 8 | local file = require("pl.file") 9 | local utilities = require("utilities") 10 | 11 | ---@type fun(directory: string): string?, integer?, string? 12 | local zip = require("zip") 13 | 14 | ---@param opt Combustion.BuildOptions 15 | return function (opt) 16 | if not opt.c_compiler then error("No C compiler specified") end 17 | 18 | ---@param contents string 19 | ---@param ... string 20 | ---@return { to: fun(self, to: string): boolean, string? } 21 | local function compile(contents, ...) 22 | local cflags = table.concat({...}, " ") 23 | return { 24 | to = function (_, to) 25 | local cmd = string.format("%s %s -x c -c -o %s -", opt.c_compiler, cflags, to) 26 | print("$ "..cmd) 27 | local proc = io.popen(cmd, "w") 28 | if not proc then return false, "Could not open pipe to "..cmd end 29 | proc:write(contents) 30 | 31 | local ok, err = proc:close() 32 | if not ok then return false, err end 33 | 34 | return true 35 | end 36 | } 37 | end 38 | 39 | ---@class LinkResult 40 | ---@field private libs string[]? 41 | ---@field to fun(self, to: string): boolean, string? 42 | ---@field with fun(self, libs: string[]?): LinkResult 43 | 44 | ---@param files string[] 45 | ---@return LinkResult 46 | local function link(files) 47 | return { 48 | with = function(self, libs) 49 | self.libs = libs 50 | return self 51 | end, 52 | 53 | to = function (self, to) 54 | local linker = utilities.programs[assert(opt.linker)] 55 | for _, file in ipairs(files) do 56 | linker = linker(file) 57 | end 58 | 59 | linker = linker "-o" (to) "-L" (opt.lib_dir or "./") 60 | if self.libs ~= nil then 61 | linker = linker("-l"..table.concat(self.libs, " -l")) 62 | end 63 | 64 | local out, err = linker() 65 | print(string.format("$ %s %s -o %s %s", opt.linker, table.concat(files, " "), to, "-l"..table.concat(self.libs or {}, " -l"))) 66 | if not out then return false, err end 67 | return true 68 | end 69 | } 70 | end 71 | 72 | local bin = path.join(opt.bin_dir, opt.name) 73 | local obj_dir = path.join(opt.build_dir, "obj") 74 | path.rmdir(obj_dir) 75 | file.delete(bin) 76 | local ok, err = file.copy(opt.lua.interpreter, bin, true) 77 | if not ok then error(err) end 78 | 79 | --Copy the lua interpreter to the bin directory 80 | file.copy(opt.lua.interpreter, path.join(opt.bin_dir, "lua"..(utilities.platform == "Windows" and ".exe" or "")), false) 81 | 82 | local ok, err = directory.makepath(obj_dir) 83 | if not ok then error(err) end 84 | 85 | ok, err = directory.makepath(opt.bin_dir) 86 | if not ok then error(err) end 87 | 88 | ---@type string[] 89 | local objects = {} 90 | 91 | local contents, size, err = zip(opt.build_dir) 92 | if not contents then error(err) end 93 | 94 | --First, we need to compile miniz by copying the header into the "obj" directory and then compiling the source file 95 | local miniz = require("executables.loaders.self-extract.miniz") 96 | local miniz_header_path = path.join(obj_dir, "miniz.h") 97 | 98 | ok = file.write(miniz_header_path, miniz.header) 99 | if not ok then error("Could not write miniz header to "..miniz_header_path) end 100 | 101 | table.insert(objects, path.join(obj_dir, "miniz.o")) 102 | ok, err = compile(miniz.source, "-I"..obj_dir, table.unpack(opt.cflags)) 103 | :to(objects[1]) 104 | if not ok then error(err) end 105 | 106 | --Now, we need to compile the self-extracting executable "loader" 107 | local loader_source = require("executables.loaders.self-extract.loader") 108 | table.insert(objects, path.join(obj_dir, "loader.o")) 109 | ok, err = compile(loader_source, table.unpack(opt.cflags), 110 | "-I"..obj_dir, 111 | "-DCOMBUSTION_ENTRY=\""..opt.entry.."\"", 112 | ((opt.graphical and utilities.platform == "Windows") and "-DWIN32_GRAPHICAL" or "")) 113 | :to(objects[2]) 114 | if not ok then error(err) end 115 | 116 | --Finally, we need to compile the bin2c result of the zip file 117 | local data = utilities.bin2c("zipfile", contents, size) 118 | table.insert(objects, path.join(obj_dir, "zipfile.o")) 119 | ok, err = compile(data, "-I"..obj_dir, table.unpack(opt.cflags)) 120 | :to(objects[3]) 121 | if not ok then error(err) end 122 | 123 | --Now, we need to link the object files together 124 | ok, err = link(objects) 125 | :with(opt.link) 126 | :to(bin) 127 | 128 | path.rmdir(obj_dir) 129 | end 130 | -------------------------------------------------------------------------------- /src/executables/static.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2023 Amrit Bhogal 2 | -- 3 | -- This software is released under the MIT License. 4 | -- https://opensource.org/licenses/MIT 5 | 6 | local path = require("pl.path") 7 | local directory = require("pl.dir") 8 | local file = require("pl.file") 9 | local tablex = require("pl.tablex") 10 | local utilities = require("utilities") 11 | 12 | ---@param opt Combustion.BuildOptions 13 | return function (opt) 14 | if not opt.c_compiler then error("No C compiler specified") end 15 | 16 | opt.cflags = opt.cflags or { utilities.is_gcc_like(opt.c_compiler) and "-Os" or "/O2" } 17 | 18 | ---@type { [string] : integer } 19 | local running_compiles = {} 20 | ---@param options { contents: string, cflags: string[]?, defines: { [string] : string }?, to: string, is_file: boolean } 21 | local function compile(options) 22 | local defines_str = "" 23 | for k, v in pairs(options.defines or {}) do 24 | v = v:gsub('"', '\\"') 25 | defines_str = defines_str.."-D"..k.."=\""..v.."\" " 26 | 27 | options.cflags[#options.cflags+1] = defines_str 28 | end 29 | 30 | --disable warnings 31 | options.cflags[#options.cflags+1] = utilities.is_gcc_like(opt.c_compiler) and "-w" or "/w" 32 | 33 | local cflags = table.concat(options.cflags or {}, " ") 34 | 35 | 36 | local cmd = string.format("%s %s -x c -c -o %s -", opt.c_compiler, cflags, options.to) 37 | if options.is_file then 38 | cmd = string.format("%s %s -c -o %s %s", opt.c_compiler, cflags, options.to, options.contents) 39 | end 40 | 41 | -- if posix, then fork off in order to compile 42 | -- if jit and jit.os ~= "Windows" then 43 | -- local ffi = require("ffi") 44 | -- ffi.cdef [[ 45 | -- int fork(void); 46 | -- int execlp(const char *file, const char *arg, ...); 47 | -- int getpid(void); 48 | -- ]] 49 | 50 | -- local pid = ffi.C.fork() 51 | -- if pid == 0 then 52 | -- --child 53 | -- print("("..ffi.C.getpid()..") Compiling to "..options.to.."...") 54 | -- ffi.C.execlp("/bin/sh", "/bin/sh", "-c", cmd) 55 | -- os.exit(1) 56 | -- else 57 | -- running_compiles[options.to] = pid 58 | -- end 59 | 60 | -- return true 61 | -- end 62 | 63 | print("$ "..cmd) 64 | local proc = io.popen(cmd, "w") 65 | if not proc then return false, "Could not open pipe to "..cmd end 66 | if not options.is_file then 67 | proc:write(options.contents) 68 | end 69 | 70 | local ok, err = proc:close() 71 | if not ok then return false, err end 72 | 73 | return true 74 | end 75 | 76 | ---@param options { input: string[], flags: string[]?, to: string } 77 | ---@return boolean ok, string? err 78 | local function link(options) 79 | local cmd = string.format("%s %s -o %s %s", opt.linker, table.concat(options.flags or {}, " "), options.to, table.concat(options.input, " ")) 80 | print("$ "..cmd) 81 | local proc = io.popen(cmd, "w") 82 | if not proc then return false, "Could not open pipe to "..cmd end 83 | 84 | local ok, err = proc:close() 85 | if not ok then return false, err end 86 | 87 | return true 88 | end 89 | 90 | local bin = path.join(opt.bin_dir, opt.name) 91 | local obj_dir = path.join(opt.build_dir, "obj") 92 | path.rmdir(obj_dir) 93 | file.delete(bin) 94 | assert(directory.makepath(obj_dir)) 95 | 96 | --first, copy compat53 to the obj dir, needed for compiling modules 97 | local compat53 = require("executables.loaders.static.compat-53-c") 98 | assert(file.write(path.join(obj_dir, "compat-5.3.h"), compat53.header)) 99 | assert(file.write(path.join(obj_dir, "compat-5.3.c"), compat53.source)) 100 | 101 | local template = require("executables.loaders.static.module-template") 102 | local template_path = path.join(obj_dir, "module-template.c") 103 | assert(file.write(template_path, template)) 104 | 105 | 106 | opt.cflags[#opt.cflags+1] = "-I"..obj_dir 107 | opt.cflags[#opt.cflags+1] = "-I"..opt.lua_incdir 108 | -- opt.cflags[#opt.cflags+1] = "-g" 109 | -- opt.cflags[#opt.cflags+1] = "-Og" 110 | 111 | ---@type string? 112 | local entry_symbol = nil 113 | 114 | require("jit.p").start("fa", "performance.log") 115 | ---{ [name with all the '.'s] : path } 116 | ---@type { [string] : { hexname: string, luac_object: string, module_object: string, module_symbol: string } } 117 | local modules = {} 118 | --compile all the modules, use the `executables.loaders.static.module-template` with different defines 119 | for _, module in ipairs(opt.luac_objects) do 120 | local cflags = tablex.copy(opt.cflags) 121 | local mangled_name = ("lua_%s"):format(utilities.hex_encode(module)) 122 | local sym, symsize = utilities.bin2c(mangled_name.."_luac", file.read(module)) 123 | local luac_objc = path.join(obj_dir, mangled_name..".luac.o") 124 | 125 | if module:find(opt.lua_source_dir, 1, true) == 1 then 126 | module = module:sub(#opt.lua_source_dir + 1) 127 | --remove leading slash if it exists 128 | if module:sub(1, 1) == "/" or module:sub(1, 1) == "\\" then module = module:sub(2) end 129 | if module == opt.entry then 130 | print("Found entry point: "..module) 131 | entry_symbol = mangled_name 132 | end 133 | end 134 | 135 | --first, compile the module into an object 136 | assert(compile { 137 | contents = sym, 138 | cflags = cflags, 139 | to = luac_objc, 140 | is_file = false 141 | }) 142 | 143 | --the path as if it would be `require`d lua, for example `my/module.lua` would be `my.module`, and `my/module/init.lua` would also be `my.module` 144 | --the common base path for all files is `opt.lua_source_dir` 145 | local modulepath do 146 | local src_dir = opt.lua_source_dir 147 | if src_dir:sub(-1) ~= "/" then 148 | src_dir = src_dir .. "/" 149 | end 150 | 151 | -- Remove the lua_source_dir prefix if it exists 152 | if module:find(opt.lua_source_dir, 1, true) == 1 then 153 | module = module:sub(#opt.lua_source_dir + 1) 154 | end 155 | 156 | modulepath = module:gsub("/", "."):gsub("\\", ".") 157 | 158 | modulepath = modulepath:gsub("%.lua$", "") 159 | modulepath = modulepath:gsub("%.init$", "") 160 | 161 | if modulepath:sub(1, 1) == "." then modulepath = modulepath:sub(2) end 162 | end 163 | 164 | local mod_obj = path.join(obj_dir, mangled_name..".o") 165 | assert(compile { 166 | contents = template_path, 167 | cflags = cflags, 168 | to = mod_obj, 169 | is_file = true, 170 | 171 | defines = { 172 | ["COMBUSTION_MODULE_SYMBOL"] = mangled_name, 173 | ["COMBUSTION_MODULE_NAME"] = modulepath, 174 | ["COMBUSTION_MODULE_BYTECODE_SYMBOL"] = mangled_name.."_luac", 175 | ["COMBUSTION_MODULE_BYTECODE_SIZE"] = tostring(symsize) 176 | } 177 | }) 178 | 179 | modules[modulepath] = { 180 | hexname = mangled_name, 181 | luac_object = luac_objc, 182 | module_object = mod_obj, 183 | module_symbol = mangled_name 184 | } 185 | end 186 | require("jit.p").stop() 187 | 188 | if not entry_symbol then error("Could not find entry point "..opt.entry) end 189 | 190 | --Compile the entry point 191 | --write it to a file in the obj dir, we want it to be a file so that `__FILE__` and `__LINE__` work 192 | local entry_source_path = path.join(obj_dir, "combustion-entry.c") 193 | assert(file.write(entry_source_path, require("executables.loaders.static.loader"))) 194 | 195 | --first, we need to make the module list, in the format of `{ "name", module_symbol }, { "name2", module_symbol2 }, ...` 196 | 197 | local module_list = "" 198 | for pathname, module in pairs(modules) do 199 | module_list = module_list..string.format('{ "%s", MODULE(%s) }, ', pathname, module.hexname) 200 | end 201 | 202 | local module_definition_template = "extern DECLARE_MODULE(%s);" 203 | local module_definitions = "" 204 | for _, module in pairs(modules) do 205 | module_definitions = module_definitions..string.format(module_definition_template, module.hexname).." " 206 | end 207 | 208 | local entry_path = path.join(obj_dir, "combustion-entry.o") 209 | print("Compiling with entry "..opt.entry.." into "..entry_path..", entry symbol: "..entry_symbol) 210 | local cflags = tablex.copy(opt.cflags) 211 | cflags[#cflags+1] = "-I"..obj_dir 212 | compile { 213 | contents = entry_source_path, 214 | cflags = cflags, 215 | to = entry_path, 216 | is_file = true, 217 | 218 | defines = { 219 | ["COMBUSTION_ENTRY"] = entry_symbol, 220 | ["COMBUSTION_MODULE_LIST"] = module_list, 221 | ["COMBUSTION_MODULE_DEFINITIONS"] = module_definitions 222 | } 223 | } 224 | 225 | local objects = {} 226 | for _, module in pairs(modules) do 227 | objects[#objects+1] = module.luac_object 228 | objects[#objects+1] = module.module_object 229 | end 230 | 231 | --link with lua or luajit depending on the luac version 232 | local ldflags = { "-L"..opt.lua_libdir, (opt.lua.version == "JIT" and "-lluajit" or "-llua") } 233 | for _, lib in ipairs(opt.link or {}) do 234 | ldflags[#ldflags+1] = "-l"..lib 235 | end 236 | 237 | -- if jit and jit.os ~= "Windows" then 238 | -- local ffi = require("ffi") 239 | -- ffi.cdef [[ 240 | -- int waitpid(int pid, int *status, int options); 241 | -- int kill(int pid, int sig); 242 | -- ]] 243 | 244 | -- for module, pid in pairs(running_compiles) do 245 | -- print("Waiting for "..module.." to compile ("..pid..")...") 246 | -- local status = ffi.new("int[1]") 247 | -- ffi.C.waitpid(pid, status, 0) 248 | 249 | -- if status[0] ~= 0 then 250 | -- error("Child compiler process "..pid.." exited with status "..status[0]) 251 | -- end 252 | 253 | -- --after we are done, kill the child process 254 | -- local SIGKILL = 9 255 | -- ffi.C.kill(pid, SIGKILL) 256 | -- end 257 | -- end 258 | 259 | print("Linking...") 260 | assert(link { 261 | input = { entry_path, unpack(objects) }, 262 | to = bin, 263 | flags = ldflags 264 | }) 265 | 266 | --copy shared objects 267 | end 268 | -------------------------------------------------------------------------------- /src/main.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2023 Amrit Bhogal 2 | -- 3 | -- This software is released under the MIT License. 4 | -- https://opensource.org/licenses/MIT 5 | 6 | 7 | ---Command line arguments 8 | ---@class Combustion.Options 9 | ---@field type "self-extract"|"app"|"directory" 10 | ---@field output_dir string 11 | ---@field source_dirs string[] 12 | ---@field library_dirs string[] 13 | ---@field link string[] 14 | ---@field lua_incdir string? 15 | ---@field lua_libdir string? 16 | ---@field resources_dirs string[] 17 | ---@field lua string? 18 | ---@field luac string? 19 | ---@field entry string 20 | ---@field name string 21 | ---@field c_compiler string 22 | ---@field cflags string[]? 23 | ---@field ldflags string[]? 24 | ---@field linker string 25 | ---@field verbose boolean 26 | ---@field graphical boolean 27 | 28 | local argparse = require("argparse") 29 | local pretty = require("pl.pretty") 30 | local path = require("pl.path") 31 | local tablex = require("pl.tablex") 32 | 33 | local utilities = require("utilities") 34 | local compile = require("compile") 35 | local executables = require("executables") 36 | 37 | local lprint, lerror = print, error 38 | unpack = unpack or table.unpack 39 | 40 | 41 | ---Regular print can't print tables, and pprint has quotations around strings, so this is the best solution 42 | ---@param ... any 43 | function print(...) 44 | local a = {...} 45 | if #a < 1 then return 46 | elseif #a == 1 then 47 | if type(a[1]) ~= "table" then lprint(tostring(a[1])) else print(pretty.write(a[1])) end 48 | else print(pretty.write(a)) end 49 | end 50 | 51 | function error(msg, n) 52 | if type(msg) == "table" then msg = pretty.write(msg) 53 | elseif type(msg) ~= "string" then msg = tostring(msg) end 54 | return lerror("\n\x1b[31m"..msg.."\x1b[0m", n) 55 | end 56 | 57 | ---@diagnostic disable-next-line: lowercase-global 58 | function warning(msg) 59 | if type(msg) == "table" then msg = pretty.write(msg) 60 | elseif type(msg) ~= "string" then msg = tostring(msg) end 61 | return lprint("\n\x1b[33m"..msg.."\x1b[0m") 62 | end 63 | 64 | local parser = argparse() { 65 | name = "combust", 66 | description = "Pack your lua project, and all dependencies into a single self contained file.", 67 | epilog = "https://github.com/Frityet/combustion" 68 | } 69 | 70 | parser:add_complete() 71 | 72 | local t_choices = tablex.keys(require("executables")) 73 | if t_choices[1] == "source" or t_choices[1] == "header" then error({"ERROR ERROR ERROR", t_choices}) end 74 | 75 | parser:option("-t --type", "The type of project to pack.") 76 | :args(1) 77 | :choices(t_choices) 78 | :default "self-extract" 79 | 80 | 81 | --Options 82 | parser:option("-o --output-dir", "The output directory to write to.") 83 | :args(1) 84 | :default "build" 85 | :convert(path.abspath) 86 | 87 | parser:option("-S --source-dirs", "The source directories to pack.") 88 | :args "+" 89 | :default "." 90 | :convert(function (f) 91 | if not path.isdir(f) then error("Source directory "..f.." does not exist.") end 92 | return path.abspath(f) 93 | end) 94 | 95 | parser:option("-L --library-dirs", "Locations of C libraries") 96 | :args "+" 97 | :convert(function (f) 98 | if not path.isdir(f) then error("Library directory "..f.." does not exist.") end 99 | return path.abspath(f) 100 | end) 101 | 102 | parser:option("-l --link", "Libraries to link with") 103 | :args "+" 104 | :default { "c", "m" } 105 | 106 | parser:option("--lua-incdir", "Location of lua headers (required for `static` build)") 107 | :args(1) 108 | :convert(function (f) 109 | if not path.isdir(f) then error("Lua include directory "..f.." does not exist.") end 110 | return path.abspath(f) 111 | end) 112 | 113 | parser:option("--lua-libdir", "Location of liblua (required for `static` build)") 114 | :args(1) 115 | :convert(function (f) 116 | if not path.isdir(f) then error("Lua library directory "..f.." does not exist.") end 117 | return path.abspath(f) 118 | end) 119 | 120 | 121 | parser:option("-R --resource-dirs", "Additional resources to pack.") 122 | :args "+" 123 | :convert(function (f) 124 | if not path.isdir(f) then error("Resource directory "..f.." does not exist.") end 125 | return path.abspath(f) 126 | end) 127 | 128 | parser:option("--lua", "Path to the lua executable") 129 | :args(1) 130 | :default(utilities.find_lua().interpreter) 131 | :convert(function (f) 132 | if not path.isfile(f) then error("Lua executable "..f.." does not exist.") end 133 | local lua, err = utilities.verify_lua(f) 134 | if not lua then error(err) end 135 | 136 | return path.abspath(f) 137 | end) 138 | 139 | parser:option("--luac", "Path to the lua compiler, must be compatable with the lua executable.") 140 | :args(1) 141 | :default("") 142 | :convert(function (f) 143 | if f == "" then return f end 144 | if not path.isfile(f) then error("Lua compiler "..f.." does not exist.") end 145 | local lua, err = utilities.verify_lua(f) 146 | if not lua then error(err) end 147 | 148 | return path.abspath(f) 149 | end) 150 | 151 | parser:option("--c-compiler", "C compiler to use.") 152 | :args(1) 153 | :default((function () 154 | local cc = os.getenv("CC") 155 | if not cc then 156 | local cc, err = utilities.find_executable("cc") 157 | if not cc then 158 | for _, name in ipairs { "gcc", "clang" } do 159 | cc, err = utilities.find_executable(name) 160 | if cc then break end 161 | end 162 | end 163 | end 164 | 165 | if not cc then 166 | warning("No C compiler found. Some features may not work.") 167 | else 168 | cc = utilities.find_executable(cc) 169 | end 170 | 171 | return cc 172 | end)()) 173 | :convert(function (f) 174 | local cc, err = utilities.find_executable(f) 175 | if not cc then error(err) end 176 | return cc 177 | end) 178 | 179 | parser:option("--cflags", "Flags to pass to the C compiler.") 180 | :args "+" 181 | 182 | parser:option("--linker", "Linker to use.") 183 | :args(1) 184 | :default "" 185 | 186 | parser:option("--ldflags", "Flags to pass to the linker.") 187 | :args "+" 188 | :default { "-flto" } 189 | 190 | 191 | parser:option("-e --entry", "The entry point of the project.") 192 | :args(1) 193 | :default "main.lua" 194 | 195 | 196 | parser:option("-n --name", "The name of the project.") 197 | :args(1) 198 | :default "" 199 | 200 | parser:flag("--graphical", "(Windows only) Create a application which does not spawn a console window") 201 | :default(false) 202 | 203 | parser:flag("-v --verbose", "Print verbose output.") 204 | :default(false) 205 | 206 | ---@type Combustion.Options 207 | local cli_opts = parser:parse() 208 | 209 | if not cli_opts.verbose then 210 | print = function (...) end 211 | end 212 | 213 | local opts = compile(cli_opts) 214 | 215 | print("Options:") 216 | print(opts) 217 | 218 | executables[cli_opts.type](opts) 219 | 220 | print("\x1b[32mSuccess!\x1b[0m") 221 | -------------------------------------------------------------------------------- /src/utilities.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2023 Amrit Bhogal 2 | -- 3 | -- This software is released under the MIT License. 4 | -- https://opensource.org/licenses/MIT 5 | 6 | local export = {} 7 | 8 | ---@alias Platform "Windows"|"Linux"|"OSX"|string 9 | 10 | ---@alias LuaVersion "5.1"|"5.2"|"5.3"|"5.4"|"JIT"|string 11 | 12 | ---@class Lua 13 | ---@field version LuaVersion 14 | ---@field interpreter string 15 | ---@field compiler string 16 | 17 | local stringx = require("pl.stringx") 18 | local path = require("pl.path") 19 | string.buffer = require("string.buffer") 20 | 21 | ---@type LuaFileSystem 22 | local lfs = require("lfs") 23 | 24 | ---@type { [string] : FunctionArgument } 25 | export.programs = setmetatable({}, { 26 | __index = function(_, prog) 27 | local exec = prog 28 | local function arg(x) 29 | if x == nil then 30 | local proc, err = io.popen(exec, "r") 31 | if not proc then return nil, err end 32 | local contents = proc:read("*a") 33 | proc:close() 34 | contents = stringx.strip(contents) 35 | return contents 36 | else 37 | exec = exec.." "..tostring(x) 38 | return arg 39 | end 40 | end 41 | 42 | return arg 43 | end 44 | }) 45 | 46 | ---@type Platform 47 | export.platform = "Other" 48 | do 49 | if jit then 50 | export.platform = require("ffi").os 51 | else 52 | if package.config:sub(1, 1) == '\\' then 53 | export.platform = "Windows" 54 | else 55 | --uname time 56 | local uname, err = export.programs["uname"]() 57 | if not uname then 58 | export.platform = "Other" 59 | warning("Could not determine platform: "..err) 60 | else 61 | if uname == "Linux" then 62 | export.platform = "Linux" 63 | elseif uname == "Darwin" then 64 | export.platform = "OSX" 65 | else 66 | export.platform = uname 67 | end 68 | end 69 | end 70 | end 71 | end 72 | 73 | ---@param name string 74 | ---@return string? executable, string? err 75 | function export.find_executable(name) 76 | local path = os.getenv("PATH") 77 | if not path then return nil, "Could not find PATH environment variable" end 78 | if export.platform == "Windows" then 79 | local paths = stringx.split(path, ";") 80 | for _, path in ipairs(paths) do 81 | local file = path.."\\"..name..".exe" 82 | if lfs.attributes(file, "mode") == "file" then return file 83 | else return nil, "Could not find executable "..name end 84 | end 85 | else 86 | --Use "which" 87 | local res, err = export.programs["which"](name)() 88 | if res then return res 89 | else return nil, err end 90 | end 91 | end 92 | 93 | ---@alias FunctionArgument (fun(str: string): FunctionArgument) | fun(): string?, string? 94 | 95 | ---@return Lua? info, string? err 96 | function export.find_lua() 97 | ---@type Lua 98 | ---@diagnostic disable-next-line: missing-fields 99 | local luainfo = {} 100 | 101 | for _, name in ipairs { "", "5.1", "5.2", "5.3", "5.4", "jit" } do 102 | local lua, err = export.find_executable("lua"..name) 103 | if lua then 104 | luainfo.interpreter = lua 105 | if name == "" then 106 | local ver_str, err = export.programs[luainfo.interpreter] "-v"() 107 | if not ver_str then return nil, err end 108 | 109 | local ver = ver_str:match("Lua (%d+%.%d+)") 110 | if ver and ver == "5.1" or ver == "5.2" or ver == "5.3" or ver == "5.4" then 111 | luainfo.version = ver 112 | else 113 | luainfo.version = ver_str 114 | end 115 | else 116 | luainfo.version = name --[[@as LuaVersion]] 117 | end 118 | break 119 | end 120 | end 121 | 122 | if not luainfo.interpreter then return nil, "Could not find Lua interpreter" end 123 | 124 | luainfo.interpreter = export.find_executable(luainfo.interpreter) --[[@as string]] 125 | 126 | if luainfo.version == "JIT" then 127 | luainfo.compiler = luainfo.interpreter 128 | else 129 | local luac, err = export.find_executable("luac"..luainfo.version) 130 | if not luac then return nil, err end 131 | luainfo.compiler = luac 132 | end 133 | 134 | return luainfo, nil 135 | end 136 | 137 | 138 | ---@param lua string 139 | ---@return LuaVersion? version, string? err 140 | function export.verify_lua(lua) 141 | local verstr, err = export.programs[lua] "-v"() 142 | if not verstr then return nil, err end 143 | 144 | local version = verstr:match("Lua (%d+%.%d+)") 145 | if not version then 146 | version = verstr:match("LuaJIT (%d+%.%d+)") 147 | if not version then version = verstr 148 | else version = "JIT" end 149 | end 150 | 151 | return version, nil 152 | end 153 | 154 | ---@param dir string 155 | ---@param filetype string 156 | ---@return string[]? files, string? err 157 | function export.find(dir, filetype) 158 | local files = {} 159 | for dirent in lfs.dir(dir) do 160 | if dirent == "." or dirent == ".." then goto next end 161 | local relpath = path.join(dir, dirent) 162 | 163 | if path.isfile(relpath) and path.extension(relpath) == filetype then 164 | table.insert(files, relpath) 165 | elseif path.isdir(relpath) then 166 | local subfiles, err = export.find(relpath, filetype) 167 | if not subfiles then return nil, err end 168 | for _, file in ipairs(subfiles) do 169 | table.insert(files, file) 170 | end 171 | end 172 | 173 | ::next:: 174 | end 175 | 176 | return files, nil 177 | end 178 | 179 | 180 | ---@param cc string 181 | ---@return boolean 182 | function export.is_gcc_like(cc) 183 | return not not export.programs[cc] "--version"() 184 | end 185 | 186 | ---@param symname string 187 | ---@param data string 188 | ---@param size number? 189 | ---@return string, number size 190 | function export.bin2c(symname, data, size) 191 | size = size or #data 192 | local buf = string.buffer.new((size * 8) + string.len("#include \n\nconst unsigned char "..symname.."[] = {\n};\nconst size_t "..symname.."_size = "..size..";\n")) 193 | 194 | buf:putf("#include \n\nconst unsigned char %s[] = {\n", symname) 195 | for i = 1, #data do 196 | buf:putf("%d,", data:byte(i)) 197 | if i % 16 == 0 then buf:putf("\n") end 198 | end 199 | buf:putf("\n};\nconst size_t %s_size = %d;\n", symname, size) 200 | 201 | return buf:tostring(), size 202 | end 203 | 204 | ---Bin2C that works on non-luajit, so no `string.buffer` 205 | ---@param symname string 206 | ---@param data string 207 | ---@param size number? 208 | ---@return string 209 | function export.bin2c_portable(symname, data, size) 210 | size = size or #data 211 | local buf = {} 212 | 213 | table.insert(buf, "#include \n\nconst unsigned char "..symname.."[] = {\n") 214 | for i = 1, #data do 215 | table.insert(buf, data:byte(i)..",") 216 | if i % 16 == 0 then table.insert(buf, "\n") end 217 | end 218 | table.insert(buf, "\n};\nconst size_t "..symname.."_size = "..size..";\n") 219 | 220 | return table.concat(buf) 221 | end 222 | 223 | ---@brief hash string 224 | ---@param str string 225 | ---@return number 226 | function export.hash_portable(str) 227 | -- see: https://stackoverflow.com/a/7666577 228 | local hash = 5381 229 | for i = 1, #str do 230 | hash = (bit.lshift(hash, 5) + hash) + string.byte(string.sub(str, i, i)) 231 | end 232 | return hash 233 | end 234 | 235 | ---@brief hash string 236 | ---@param str string 237 | ---@return number 238 | function export.hash(str) 239 | local ffi = require("ffi") 240 | -- see: https://stackoverflow.com/a/7666577 241 | local hash = ffi.new("uint32_t", 5381) 242 | for i = 1, #str do 243 | ---@diagnostic disable-next-line: param-type-mismatch 244 | hash = (bit.lshift(hash, 5) + hash) + string.byte(string.sub(str, i, i)) 245 | end 246 | return assert(tonumber(hash)) 247 | end 248 | 249 | ---@brief unhash number 250 | ---@param hash number 251 | ---@return string 252 | function export.unhash_portable(hash) 253 | local str = "" 254 | while hash > 0 do 255 | str = str..string.char(hash % 256) 256 | hash = math.floor(hash / 256) 257 | end 258 | return str 259 | end 260 | 261 | ---Turns a string into a series of hex bytes, i,e "Hello" -> "48656c6c6f" as a string 262 | ---@param string string 263 | ---@return string 264 | function export.hex_encode(string) 265 | local hex = "" 266 | for i = 1, #string do 267 | hex = hex..string.format("%02X", string:byte(i)) 268 | end 269 | return hex 270 | end 271 | 272 | return export 273 | -------------------------------------------------------------------------------- /src/zip.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2023 Amrit Bhogal 3 | * 4 | * This software is released under the MIT License. 5 | * https://opensource.org/licenses/MIT 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "miniz.h" 17 | 18 | #if defined(_WIN32) 19 | # include 20 | # include 21 | # define mkdir(path, mode) _mkdir(path) 22 | # define getcwd(buf, size) _getcwd(buf, size) 23 | # define mkstemp(buf) _mktemp(buf) 24 | # define stat(path, buf) _stat(path, buf 25 | # define PATH_SEPARATOR '\\' 26 | # define PATH_MAX MAX_PATH 27 | #else 28 | # include 29 | # include 30 | # include 31 | # include 32 | # define PATH_SEPARATOR '/' 33 | # if(defined(__linux__)) 34 | # include 35 | # else 36 | # include 37 | # endif 38 | #endif 39 | 40 | 41 | 42 | 43 | static int zip(lua_State *L), add_files_to_zip(mz_zip_archive *zip, const char *dir, const char *prefix); 44 | 45 | /* 46 | ---Recursively creates a zip file from a directory, preserving the directory structure 47 | ---@param dir string The directory to compress 48 | ---@return string? contents of the zip size_t? The size of the zip file, string? The error message 49 | function zip(dir) end 50 | */ 51 | static int zip(lua_State *L) 52 | { 53 | const char *dir = luaL_checkstring(L, 1); 54 | char errbuf[512] = {0}; 55 | struct stat st = {0}; 56 | if (stat(dir, &st) == -1) { 57 | lua_pushnil(L); 58 | lua_pushnil(L); 59 | snprintf(errbuf, sizeof(errbuf) - 1, "Failed to stat directory: %s", dir); 60 | lua_pushstring(L, errbuf); 61 | return 3; 62 | } else if (!(st.st_mode & S_IFDIR)) { 63 | lua_pushnil(L); 64 | lua_pushnil(L); 65 | snprintf(errbuf, sizeof(errbuf) - 1, "Not a directory: %s", dir); 66 | lua_pushstring(L, errbuf); 67 | return 3; 68 | } 69 | 70 | mz_zip_archive zip = {0}; 71 | if (!mz_zip_writer_init_heap(&zip, 0, 0)) { 72 | lua_pushnil(L); 73 | lua_pushnil(L); 74 | snprintf(errbuf, sizeof(errbuf) - 1, "Failed to initialize zip archive\nReason: %s", mz_zip_get_error_string(mz_zip_get_last_error(&zip))); 75 | lua_pushstring(L, errbuf); 76 | return 3; 77 | } 78 | 79 | if (add_files_to_zip(&zip, dir, ".") != 0) { 80 | mz_zip_writer_end(&zip); 81 | lua_pushnil(L); 82 | lua_pushnil(L); 83 | snprintf(errbuf, sizeof(errbuf) - 1, "Failed to add files to zip archive\nReason: %s", mz_zip_get_error_string(mz_zip_get_last_error(&zip))); 84 | lua_pushstring(L, errbuf); 85 | return 3; 86 | } 87 | 88 | char *zip_data = NULL; 89 | size_t zip_size = 0; 90 | if (!mz_zip_writer_finalize_heap_archive(&zip, (void **)&zip_data, &zip_size)) { 91 | mz_zip_writer_end(&zip); 92 | lua_pushnil(L); 93 | lua_pushnil(L); 94 | snprintf(errbuf, sizeof(errbuf) - 1, "Failed to finalize zip archive\nReason: %s", mz_zip_get_error_string(mz_zip_get_last_error(&zip))); 95 | lua_pushstring(L, errbuf); 96 | return 3; 97 | } 98 | 99 | mz_zip_writer_end(&zip); 100 | lua_pushlstring(L, zip_data, zip_size); 101 | lua_pushinteger(L, zip_size); 102 | mz_free(zip_data); 103 | return 2; 104 | } 105 | 106 | static int add_files_to_zip(mz_zip_archive *zip, const char *dir, const char *prefix) 107 | { 108 | #if defined(_WIN32) 109 | char filepath[PATH_MAX] = {0}, subprefix[PATH_MAX] = {0}; 110 | 111 | WIN32_FIND_DATA find_data; 112 | HANDLE find_handle = INVALID_HANDLE_VALUE; 113 | 114 | // Build a wildcard pattern for the files in the directory 115 | snprintf(filepath, sizeof(filepath) - 1, "%s\\*", dir); 116 | 117 | // Find the first file in the directory 118 | find_handle = FindFirstFile(filepath, &find_data); 119 | if (find_handle == INVALID_HANDLE_VALUE) { 120 | return -1; 121 | } 122 | 123 | // Iterate over all files in the directory 124 | do { 125 | if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { 126 | continue; 127 | } 128 | 129 | // Build the full path to the file 130 | snprintf(filepath, sizeof(filepath) - 1, "%s\\%s", dir, find_data.cFileName); 131 | 132 | // Build the zip path for the file 133 | snprintf(subprefix, sizeof(subprefix) - 1, "%s\\%s", prefix, find_data.cFileName); 134 | 135 | if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 136 | // Recursively add files in subdirectories 137 | if (add_files_to_zip(zip, filepath, subprefix) != 0) { 138 | FindClose(find_handle); 139 | return -1; 140 | } 141 | } else { 142 | // Add regular files to the zip 143 | if (!mz_zip_writer_add_file(zip, subprefix, filepath, NULL, 0, MZ_BEST_COMPRESSION)) { 144 | FindClose(find_handle); 145 | return -1; 146 | } 147 | } 148 | } while (FindNextFile(find_handle, &find_data) != 0); 149 | 150 | FindClose(find_handle); 151 | return 0; 152 | #else 153 | DIR *dp = opendir(dir); 154 | if (dp == NULL) return -1; 155 | 156 | 157 | struct dirent *ep; 158 | char filepath[512] = {0}; 159 | while ((ep = readdir(dp)) != NULL) { 160 | if (ep->d_type == DT_DIR) { 161 | if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) 162 | continue; 163 | 164 | snprintf(filepath, sizeof(filepath) - 1, "%s/%s", dir, ep->d_name); 165 | char subprefix[512] = {0}; 166 | snprintf(subprefix, sizeof(subprefix) - 1, "%s/%s", prefix, ep->d_name); 167 | if (add_files_to_zip(zip, filepath, subprefix) != 0) 168 | return 1; 169 | 170 | } else if (ep->d_type == DT_REG) { 171 | snprintf(filepath, sizeof(filepath) - 1, "%s/%s", dir, ep->d_name); 172 | char zippath[512] = {0}; 173 | snprintf(zippath, sizeof(zippath) - 1, "%s/%s", prefix, ep->d_name); 174 | if (!mz_zip_writer_add_file(zip, zippath, filepath, NULL, 0, MZ_BEST_COMPRESSION)) 175 | return 1; 176 | 177 | } 178 | } 179 | 180 | closedir(dp); 181 | return 0; 182 | } 183 | #endif 184 | 185 | int luaopen_zip(lua_State *L) 186 | { 187 | lua_pushcfunction(L, zip); 188 | return 1; 189 | } 190 | -------------------------------------------------------------------------------- /test/small.lua: -------------------------------------------------------------------------------- 1 | local tablex = require("pl.tablex") 2 | 3 | print 'Small test of combustion' 4 | 5 | print(package.path, package.cpath) 6 | 7 | print("Arguments:") 8 | 9 | for i, v in ipairs(arg) do 10 | print(i, v) 11 | end 12 | 13 | print("Env") 14 | 15 | for k, v in pairs(_G) do 16 | print(k, v) 17 | end 18 | --------------------------------------------------------------------------------