├── samples ├── test.app │ ├── add │ │ ├── b.lua │ │ ├── a.lua │ │ └── init.lua │ ├── greetings.txt │ ├── sonnet-133.txt │ ├── main.lua │ └── utils.lua ├── Makefile ├── repl.app │ ├── main.lua │ ├── repl.lua │ └── utils.lua └── winsvc.app │ └── main.lua ├── .gitattributes ├── lgtm.yml ├── .gitignore ├── deps ├── lpeg.cmake ├── lua-zlib.cmake ├── zlib.cmake ├── lrexlib.cmake ├── pcre2.cmake ├── lua-openssl.cmake ├── openssl.cmake └── bit.c ├── packaging ├── linux-run.sh └── linux-build.sh ├── .gitmodules ├── src ├── winsvcaux.h ├── winsvc.h ├── luvi_renamed.c ├── luvi_compat.c ├── luvi.c ├── luvi.h ├── winsvcaux.c ├── lua │ ├── luvipath.lua │ ├── init.lua │ └── luvibundle.lua ├── main.c ├── lenv.c ├── snapshot.c └── lminiz.c ├── cmake └── Modules │ ├── FindLibuv.cmake │ ├── FindPCRE2.cmake │ ├── FindLuaJIT.cmake │ ├── FindLuv.cmake │ ├── LuaAddExecutable.cmake │ └── luac.lua ├── CMakeLists.txt ├── Makefile ├── .github └── workflows │ └── ci.yml ├── README.md ├── LICENSE.txt └── CHANGELOG.md /samples/test.app/add/b.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bat eol=crlf 2 | -------------------------------------------------------------------------------- /samples/test.app/add/a.lua: -------------------------------------------------------------------------------- 1 | return "a" 2 | -------------------------------------------------------------------------------- /samples/test.app/greetings.txt: -------------------------------------------------------------------------------- 1 | Hello Luvly World! 2 | -------------------------------------------------------------------------------- /lgtm.yml: -------------------------------------------------------------------------------- 1 | path_classifiers: 2 | library: 3 | - deps 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | luvi-src.tar.gz 3 | luvi.exe 4 | VERSION 5 | -------------------------------------------------------------------------------- /samples/test.app/add/init.lua: -------------------------------------------------------------------------------- 1 | return function (a, b) 2 | return a + b 3 | end 4 | 5 | -------------------------------------------------------------------------------- /samples/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: repl test 3 | 4 | %.zip: %.app %.app/main.lua 5 | cd $< && zip -r -9 ../$@ . && cd .. 6 | 7 | %: %.zip 8 | make -C .. luvi 9 | cat ../luvi $< > $@ 10 | chmod +x $@ 11 | 12 | clean: 13 | rm -f repl repl.zip test test.zip 14 | -------------------------------------------------------------------------------- /deps/lpeg.cmake: -------------------------------------------------------------------------------- 1 | set(LPEGLIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/lpeg" CACHE PATH "Path to lpeg") 2 | 3 | add_library(lpeglib STATIC 4 | ${LPEGLIB_DIR}/lpcap.c 5 | ${LPEGLIB_DIR}/lpcode.c 6 | ${LPEGLIB_DIR}/lpcset.c 7 | ${LPEGLIB_DIR}/lpprint.c 8 | ${LPEGLIB_DIR}/lptree.c 9 | ${LPEGLIB_DIR}/lpvm.c 10 | ) 11 | 12 | list(APPEND LUVI_LIBRARIES lpeglib) 13 | list(APPEND LUVI_DEFINITIONS WITH_LPEG=1) 14 | -------------------------------------------------------------------------------- /deps/lua-zlib.cmake: -------------------------------------------------------------------------------- 1 | include(deps/zlib.cmake) 2 | 3 | set(LUA_ZLIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/lua-zlib" CACHE STRING "Path to lua-zlib") 4 | 5 | add_library(lua_zlib STATIC 6 | ${LUA_ZLIB_DIR}/lua_zlib.c 7 | ${LUA_ZLIB_DIR}/zlib.def 8 | ) 9 | 10 | target_include_directories(lua_zlib PUBLIC ${ZLIB_INCLUDE_DIR}) 11 | target_link_libraries(lua_zlib ${ZLIB_LIBRARIES}) 12 | 13 | list(APPEND LUVI_LIBRARIES lua_zlib ${ZLIB_LIBRARIES}) 14 | list(APPEND LUVI_DEFINITIONS WITH_ZLIB=1) 15 | -------------------------------------------------------------------------------- /samples/repl.app/main.lua: -------------------------------------------------------------------------------- 1 | local uv = require('uv') 2 | local bundle = require('luvi').bundle 3 | 4 | -- Register two libraries as modules 5 | bundle.register("utils", "utils.lua") 6 | bundle.register("repl", "repl.lua") 7 | 8 | local utils = require('utils') 9 | local repl = require('repl') 10 | 11 | local stdin = utils.stdin 12 | local stdout = utils.stdout 13 | 14 | local c = utils.color 15 | local greeting = "Welcome to the " .. c("err") .. "L" .. c("quotes") .. "uv" .. c("table") .. "i" .. c() .. " repl!" 16 | repl(stdin, stdout, uv, utils, greeting) 17 | 18 | uv.run("default") 19 | -------------------------------------------------------------------------------- /deps/zlib.cmake: -------------------------------------------------------------------------------- 1 | if (WithSharedZLIB) 2 | find_package(ZLIB REQUIRED) 3 | 4 | message("Enabling Shared ZLIB") 5 | message("ZLIB_INCLUDE_DIR: ${ZLIB_INCLUDE_DIR}") 6 | message("ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") 7 | else (WithSharedZLIB) 8 | message("Enabling Static ZLIB") 9 | add_subdirectory(deps/zlib) 10 | 11 | set(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib ${CMAKE_BINARY_DIR}/deps/zlib) 12 | set(ZLIB_LIBRARIES zlibstatic) 13 | 14 | message("ZLIB_INCLUDE_DIR: ${ZLIB_INCLUDE_DIR}") 15 | message("ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") 16 | endif (WithSharedZLIB) 17 | -------------------------------------------------------------------------------- /packaging/linux-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xeuo pipefail 4 | 5 | LIBC=$1 6 | ARCH=$2 7 | 8 | shift 2 9 | 10 | if [ x$LIBC == xglibc ]; then 11 | container_host="quay.io/pypa/manylinux2014_$ARCH" 12 | else 13 | container_host="quay.io/pypa/musllinux_1_2_$ARCH" 14 | fi 15 | 16 | # try to pull the image 3 times, because quay.io sometimes fails 17 | for i in 1 2 3; do 18 | docker pull $container_host && break 19 | done 20 | 21 | docker run --rm \ 22 | -v "$GITHUB_WORKSPACE":"/github/workspace" \ 23 | -w /github/workspace \ 24 | $container_host \ 25 | /bin/bash packaging/linux-build.sh $ARCH $@ 26 | -------------------------------------------------------------------------------- /deps/lrexlib.cmake: -------------------------------------------------------------------------------- 1 | include(deps/pcre2.cmake) 2 | 3 | set(LREXLIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/lrexlib" CACHE PATH "Path to lrexlib") 4 | 5 | add_library(lrexlib STATIC 6 | ${LREXLIB_DIR}/src/common.c 7 | ${LREXLIB_DIR}/src/pcre2/lpcre2.c 8 | ${LREXLIB_DIR}/src/pcre2/lpcre2_f.c 9 | ) 10 | 11 | target_include_directories(lrexlib PUBLIC ${PCRE2_INCLUDE_DIR}) 12 | target_link_libraries(lrexlib ${PCRE2_LIBRARIES}) 13 | target_compile_definitions(lrexlib PRIVATE 14 | LUA_COMPAT_APIINTCASTS 15 | VERSION="2.8.0") 16 | 17 | list(APPEND LUVI_LIBRARIES lrexlib ${PCRE2_LIBRARIES}) 18 | list(APPEND LUVI_DEFINITIONS WITH_PCRE2=1) 19 | -------------------------------------------------------------------------------- /samples/test.app/sonnet-133.txt: -------------------------------------------------------------------------------- 1 | Beshrew that heart that makes my heart to groan 2 | For that deep wound it gives my friend and me; 3 | Is't not enough to torture me alone, 4 | But slave to slavery my sweet'st friend must be? 5 | Me from my self thy cruel eye hath taken, 6 | And my next self thou harder hast engrossed, 7 | Of him, my self, and thee I am forsaken, 8 | A torment thrice three-fold thus to be crossed: 9 | Prison my heart in thy steel bosom's ward, 10 | But then my friend's heart let my poor heart bail, 11 | Whoe'er keeps me, let my heart be his guard, 12 | Thou canst not then use rigour in my gaol. 13 | And yet thou wilt, for I being pent in thee, 14 | Perforce am thine and all that is in me. 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/lua-openssl"] 2 | path = deps/lua-openssl 3 | url = https://github.com/zhaozg/lua-openssl.git 4 | [submodule "deps/luv"] 5 | path = deps/luv 6 | url = https://github.com/luvit/luv.git 7 | [submodule "deps/zlib"] 8 | path = deps/zlib 9 | url = https://github.com/madler/zlib.git 10 | [submodule "deps/lua-zlib"] 11 | path = deps/lua-zlib 12 | url = https://github.com/brimworks/lua-zlib.git 13 | [submodule "deps/lrexlib"] 14 | path = deps/lrexlib 15 | url = https://github.com/rrthomas/lrexlib.git 16 | [submodule "deps/lpeg"] 17 | path = deps/lpeg 18 | url = https://github.com/luvit/lpeg.git 19 | [submodule "deps/pcre2"] 20 | path = deps/pcre2 21 | url = https://github.com/PCRE2Project/pcre2 22 | [submodule "deps/miniz"] 23 | path = deps/miniz 24 | url = https://github.com/richgel999/miniz 25 | -------------------------------------------------------------------------------- /src/winsvcaux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Luvit Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef __WINSVCAUX_H__ 18 | #define __WINSVCAUX_H__ 19 | 20 | LUALIB_API int luaopen_winsvcaux(lua_State *L); 21 | 22 | #endif /* __WINSVCAUX_H__ */ 23 | 24 | -------------------------------------------------------------------------------- /src/winsvc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Luvit Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef __WINSVC_H__ 18 | #define __WINSVC_H__ 19 | 20 | #define WINSVC_VERSION "1.0.0" 21 | 22 | LUALIB_API int luaopen_winsvc(lua_State *L); 23 | 24 | #endif /* __WINSVC_H__ */ 25 | 26 | -------------------------------------------------------------------------------- /src/luvi_renamed.c: -------------------------------------------------------------------------------- 1 | #include "windows.h" 2 | #include "delayimp.h" 3 | FARPROC WINAPI LoadFailureHook(unsigned dliNotify, PDelayLoadInfo pdli); 4 | #if _MSC_FULL_VER >= 190024210 // MSVC 2015 Update 3 5 | #ifndef DELAYIMP_INSECURE_WRITABLE_HOOKS 6 | const 7 | #endif 8 | #endif 9 | PfnDliHook __pfnDliFailureHook2 = LoadFailureHook; 10 | 11 | 12 | FARPROC WINAPI LoadFailureHook(unsigned dliNotify, PDelayLoadInfo pdli) 13 | { 14 | if (dliNotify == dliFailLoadLib) { 15 | if (_stricmp("luvi.exe", pdli->szDll) == 0) { 16 | TCHAR name[MAX_PATH + 1]; 17 | DWORD ret = GetModuleFileName(NULL, name, MAX_PATH + 1); 18 | if (ret > 0) { 19 | HMODULE module = LoadLibrary(name); 20 | if (module) { 21 | return (FARPROC)module; 22 | } 23 | } 24 | } 25 | } 26 | return 0; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /packaging/linux-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xeuo pipefail 4 | 5 | ARCH=$1 6 | BUILD_TYPE=$2 7 | LUA_ENGINE=$3 8 | 9 | NPROCS=$(grep -c ^processor /proc/cpuinfo) 10 | 11 | if which yum; then 12 | if [ "$ARCH" != "i686" ]; then 13 | yum install -y epel-release 14 | yum install -y cmake3 15 | else # the version of cmake install is too old, and cmake3 is not available for i686 16 | yum install -y openssl-devel 17 | 18 | curl -fLO https://github.com/Kitware/CMake/releases/download/v3.22.3/cmake-3.22.3.tar.gz 19 | tar -zxf cmake-3.22.3.tar.gz 20 | cd cmake-3.22.3 21 | ./bootstrap --parallel=$NPROCS 22 | make -j$NPROCS 23 | make install 24 | cd .. 25 | fi 26 | yum install -y perl-core 27 | else 28 | apk add cmake 29 | fi 30 | 31 | git config --global --add safe.directory /github/workspace 32 | git config --global --add safe.directory /github/workspace/deps/luv/deps/luajit 33 | 34 | WITH_OPENSSL_ASM=ON 35 | if [ "$ARCH" == "i686" ]; then 36 | WITH_OPENSSL_ASM=OFF 37 | fi 38 | 39 | make $BUILD_TYPE WITH_LUA_ENGINE=$LUA_ENGINE WITH_OPENSSL_ASM=$WITH_OPENSSL_ASM 40 | make 41 | make test 42 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibuv.cmake: -------------------------------------------------------------------------------- 1 | #[=======================================================================[.rst: 2 | FindLibuv 3 | -------- 4 | 5 | Find the native libuv headers and libraries. 6 | 7 | Result Variables 8 | ^^^^^^^^^^^^^^^^ 9 | 10 | This module defines the following variables: 11 | 12 | ``LIBUV_FOUND`` 13 | "True" if ``libuv`` found. 14 | 15 | ``LIBUV_INCLUDE_DIRS`` 16 | where to find ``uv.h``, etc. 17 | 18 | ``LIBUV_LIBRARIES`` 19 | List of libraries when using ``uv``. 20 | 21 | #]=======================================================================] 22 | 23 | include(FindPackageHandleStandardArgs) 24 | 25 | find_package(PkgConfig QUIET) 26 | if(PKG_CONFIG_FOUND) 27 | pkg_check_modules(PC_UV QUIET libuv) 28 | endif() 29 | 30 | find_path(UV_INCLUDE_DIR 31 | NAMES uv.h 32 | HINTS ${PC_UV_INCLUDE_DIRS}) 33 | mark_as_advanced(UV_INCLUDE_DIR) 34 | 35 | find_library(UV_LIBRARY 36 | NAMES uv 37 | HINTS ${PC_UV_LIBRARY_DIRS}) 38 | mark_as_advanced(UV_LIBRARY) 39 | 40 | find_package_handle_standard_args(Libuv 41 | REQUIRED_VARS UV_INCLUDE_DIR UV_LIBRARY) 42 | 43 | if (LIBUV_FOUND) # Set the output variables 44 | set(LIBUV_LIBRARIES ${UV_LIBRARY}) 45 | set(LIBUV_INCLUDE_DIRS ${UV_INCLUDE_DIR}) 46 | endif () 47 | -------------------------------------------------------------------------------- /deps/pcre2.cmake: -------------------------------------------------------------------------------- 1 | if (WithSharedPCRE2) 2 | find_package(PCRE2 REQUIRED) 3 | 4 | message("Enabling Shared PCRE2") 5 | message("PCRE2_INCLUDE_DIR: ${PCRE2_INCLUDE_DIR}") 6 | message("PCRE2_LIBRARIES: ${PCRE2_LIBRARIES}") 7 | 8 | add_compile_definitions(PCRE2_CODE_UNIT_WIDTH=8) 9 | else (WithSharedPCRE2) 10 | message("Enabling Static PCRE2") 11 | 12 | set(PCRE2_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/pcre2" CACHE STRING "Path to pcre2") 13 | 14 | set(PCRE2_BUILD_PCRE2GREP OFF CACHE BOOL "Build pcre2grep") 15 | set(PCRE2_BUILD_TESTS OFF CACHE BOOL "Build tests") 16 | set(PCRE2_STATIC_RUNTIME ON CACHE BOOL "Use static runtime") 17 | set(PCRE2_SUPPORT_LIBBZ2 OFF CACHE BOOL "Support libbz2") 18 | set(PCRE2_SUPPORT_LIBZ OFF CACHE BOOL "Support libz") 19 | set(PCRE2_SUPPORT_LIBEDIT OFF CACHE BOOL "Support libedit") 20 | set(PCRE2_SUPPORT_LIBREADLINE OFF CACHE BOOL "Support libreadline") 21 | 22 | add_compile_definitions(PCRE2_CODE_UNIT_WIDTH=8) 23 | add_compile_definitions(PCRE2_STATIC) 24 | 25 | add_subdirectory(deps/pcre2) 26 | 27 | set(PCRE2_INCLUDE_DIR ${PCRE2_HEADERS}) 28 | set(PCRE2_LIBRARIES pcre2-8-static) 29 | 30 | message("PCRE2_INCLUDE_DIR: ${PCRE2_INCLUDE_DIR}") 31 | message("PCRE2_LIBRARIES: ${PCRE2_LIBRARIES}") 32 | endif (WithSharedPCRE2) 33 | -------------------------------------------------------------------------------- /cmake/Modules/FindPCRE2.cmake: -------------------------------------------------------------------------------- 1 | #[=======================================================================[.rst: 2 | FindPCRE2 3 | -------- 4 | 5 | Find the native pcre2 (specifically the 8-bit version) headers and libraries. 6 | 7 | Result Variables 8 | ^^^^^^^^^^^^^^^^ 9 | 10 | This module defines the following variables: 11 | 12 | ``PCRE2_FOUND`` 13 | "True" if ``pcre2-8`` found. 14 | 15 | ``PCRE2_INCLUDE_DIRS`` 16 | where to find ``pcre2.h``, etc. 17 | 18 | ``PCRE2_LIBRARIES`` 19 | List of libraries when using ``pcre2-8``. 20 | 21 | #]=======================================================================] 22 | 23 | include(FindPackageHandleStandardArgs) 24 | 25 | find_package(PkgConfig QUIET) 26 | if(PKG_CONFIG_FOUND) 27 | pkg_check_modules(PC_PCRE2 QUIET libpcre2-8) 28 | endif() 29 | 30 | find_path(PCRE2_INCLUDE_DIR 31 | NAMES pcre2.h 32 | HINTS ${PC_PCRE2_INCLUDE_DIRS}) 33 | mark_as_advanced(PCRE2_INCLUDE_DIR) 34 | 35 | find_library(PCRE2_LIBRARY 36 | NAMES pcre2-8 37 | HINTS ${PC_PCRE2_LIBRARY_DIRS}) 38 | mark_as_advanced(PCRE2_LIBRARY) 39 | 40 | find_package_handle_standard_args(PCRE2 41 | REQUIRED_VARS PCRE2_INCLUDE_DIR PCRE2_LIBRARY) 42 | 43 | if (PCRE2_FOUND) # Set the output variables 44 | set(PCRE2_LIBRARIES ${PCRE2_LIBRARY}) 45 | set(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR}) 46 | endif () 47 | -------------------------------------------------------------------------------- /cmake/Modules/FindLuaJIT.cmake: -------------------------------------------------------------------------------- 1 | #[=======================================================================[.rst: 2 | FindLuajit 3 | -------- 4 | 5 | Find the native luajit headers and libraries. 6 | 7 | Result Variables 8 | ^^^^^^^^^^^^^^^^ 9 | 10 | This module defines the following variables: 11 | 12 | ``LUAJIT_FOUND`` 13 | "True" if ``luajit`` found. 14 | 15 | ``LUAJIT_INCLUDE_DIRS`` 16 | where to find ``lua.h``, etc. 17 | 18 | ``LUAJIT_LIBRARIES`` 19 | List of libraries when using ``luajit``. 20 | 21 | #]=======================================================================] 22 | 23 | include(FindPackageHandleStandardArgs) 24 | 25 | find_package(PkgConfig QUIET) 26 | if(PKG_CONFIG_FOUND) 27 | pkg_check_modules(PC_LUAJIT QUIET luajit) 28 | endif() 29 | 30 | find_path(LUAJIT_INCLUDE_DIR 31 | NAMES lua.h 32 | HINTS ${PC_LUAJIT_INCLUDE_DIRS} 33 | PATH_SUFFIXES luajit-2.0) 34 | mark_as_advanced(LUAJIT_INCLUDE_DIR) 35 | 36 | find_library(LUAJIT_LIBRARY 37 | NAMES luajit-5.1 38 | HINTS ${PC_LUAJIT_LIBRARY_DIRS} 39 | PATH_SUFFIXES luajit-2.0) 40 | mark_as_advanced(LUAJIT_LIBRARY) 41 | 42 | find_package_handle_standard_args(LuaJIT 43 | REQUIRED_VARS LUAJIT_INCLUDE_DIR LUAJIT_LIBRARY) 44 | 45 | if (LUAJIT_FOUND) # Set the output variables 46 | set(LUAJIT_LIBRARIES ${LUAJIT_LIBRARY}) 47 | set(LUAJIT_INCLUDE_DIRS ${LUAJIT_INCLUDE_DIR}) 48 | endif () 49 | -------------------------------------------------------------------------------- /cmake/Modules/FindLuv.cmake: -------------------------------------------------------------------------------- 1 | #[=======================================================================[.rst: 2 | FindLuv 3 | -------- 4 | 5 | Find the native luv headers and libraries. 6 | 7 | Result Variables 8 | ^^^^^^^^^^^^^^^^ 9 | 10 | This module defines the following variables: 11 | 12 | ``LUV_FOUND`` 13 | "True" if ``luv`` found. 14 | 15 | ``LUV_INCLUDE_DIRS`` 16 | where to find ``luv.h``, etc. 17 | 18 | ``LUV_LIBRARIES`` 19 | List of libraries when using ``luv``. 20 | 21 | #]=======================================================================] 22 | 23 | include(FindPackageHandleStandardArgs) 24 | 25 | find_package(PkgConfig QUIET) 26 | if(PKG_CONFIG_FOUND) 27 | pkg_check_modules(PC_LUV QUIET libluv) 28 | if (NOT PC_LUV_FOUND) 29 | pkg_check_modules(PC_LUV QUIET lua5.1-luv) 30 | endif () 31 | endif() 32 | 33 | find_path(LUV_INCLUDE_DIR 34 | NAMES luv.h 35 | HINTS ${PC_LUV_INCLUDE_DIRS} 36 | PATH_SUFFIXES luv lua5.1/luv) 37 | mark_as_advanced(LUV_INCLUDE_DIR) 38 | 39 | find_library(LUV_LIBRARY 40 | NAMES luv lua5.1-luv 41 | HINTS ${PC_LUV_LIBRARY_DIRS}) 42 | mark_as_advanced(LUV_LIBRARY) 43 | 44 | find_package_handle_standard_args(Luv 45 | REQUIRED_VARS LUV_INCLUDE_DIR LUV_LIBRARY) 46 | 47 | if (LUV_FOUND) # Set the output variables 48 | set(LUV_LIBRARIES ${LUV_LIBRARY}) 49 | set(LUV_INCLUDE_DIRS ${LUV_INCLUDE_DIR}) 50 | endif () 51 | -------------------------------------------------------------------------------- /src/luvi_compat.c: -------------------------------------------------------------------------------- 1 | #define lstrlib_c 2 | #define ltablib_c 3 | #define lutf8lib_c 4 | 5 | #include "luvi.h" 6 | 7 | #if (LUA_VERSION_NUM < 503) 8 | #include "compat-5.3.h" 9 | #include "compat-5.3.c" 10 | 11 | #include "lprefix.h" 12 | #include "lstrlib.c" 13 | #include "ltablib.c" 14 | #include "lutf8lib.c" 15 | #endif 16 | 17 | #if (LUA_VERSION_NUM == 501) 18 | #ifndef LUA_UTF8LIBNAME 19 | #define LUA_UTF8LIBNAME "utf8" 20 | #endif 21 | 22 | #ifndef UTF8PATT_501 23 | #define UTF8PATT_501 "[%z\x01-\x7F\xC2-\xF4][\x80-\xBF]*" 24 | #endif 25 | #endif 26 | 27 | #ifdef WITH_PLAIN_LUA 28 | #include "../deps/bit.c" 29 | #endif 30 | 31 | void luvi_openlibs(lua_State *L) { 32 | luaL_openlibs(L); 33 | #if (LUA_VERSION_NUM < 503) 34 | { 35 | static luaL_Reg const funcs[] = { 36 | { "pack", str_pack }, 37 | { "packsize", str_packsize }, 38 | { "unpack", str_unpack }, 39 | { NULL, NULL } 40 | }; 41 | 42 | #if (LUA_VERSION_NUM > 501) 43 | luaL_newlib(L, funcs); 44 | #else 45 | luaL_register(L, LUA_STRLIBNAME, funcs); 46 | #endif 47 | } 48 | lua_pop(L, 1); 49 | 50 | luaL_requiref(L, LUA_UTF8LIBNAME, luaopen_utf8, 1); 51 | 52 | #if (LUA_VERSION_NUM == 501) 53 | lua_pushlstring(L, UTF8PATT_501, sizeof(UTF8PATT_501)/sizeof(char) - 1); 54 | lua_setfield(L, -2, "charpattern"); 55 | #endif 56 | lua_pop(L, 1); 57 | 58 | #endif 59 | } 60 | -------------------------------------------------------------------------------- /src/luvi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The Luvit Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include "./luvi.h" 19 | 20 | LUALIB_API int luaopen_luvi(lua_State *L) { 21 | #if defined(WITH_OPENSSL) || defined(WITH_PCRE2) 22 | char buffer[1024]; 23 | #endif 24 | lua_newtable(L); 25 | #ifdef LUVI_VERSION 26 | lua_pushstring(L, ""LUVI_VERSION""); 27 | lua_setfield(L, -2, "version"); 28 | #endif 29 | lua_newtable(L); 30 | #ifdef WITH_OPENSSL 31 | snprintf(buffer, sizeof(buffer), "%s, lua-openssl %s", 32 | SSLeay_version(SSLEAY_VERSION), LOPENSSL_VERSION); 33 | lua_pushstring(L, buffer); 34 | lua_setfield(L, -2, "ssl"); 35 | #endif 36 | #ifdef WITH_PCRE2 37 | pcre2_config(PCRE2_CONFIG_VERSION, buffer); 38 | lua_pushstring(L, buffer); 39 | lua_setfield(L, -2, "rex"); 40 | #endif 41 | #ifdef WITH_ZLIB 42 | lua_pushstring(L, zlibVersion()); 43 | lua_setfield(L, -2, "zlib"); 44 | #endif 45 | #ifdef WITH_WINSVC 46 | lua_pushstring(L, WINSVC_VERSION); 47 | lua_setfield(L, -2, "winsvc"); 48 | #endif 49 | lua_pushstring(L, uv_version_string()); 50 | lua_setfield(L, -2, "libuv"); 51 | lua_setfield(L, -2, "options"); 52 | return 1; 53 | } 54 | -------------------------------------------------------------------------------- /cmake/Modules/LuaAddExecutable.cmake: -------------------------------------------------------------------------------- 1 | # Added LUAJIT_ADD_EXECUTABLE Ryan Phillips 2 | # This CMakeLists.txt has been first taken from LuaDist 3 | # Copyright (C) 2007-2011 LuaDist. 4 | # Created by Peter Drahoš 5 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 6 | # Debugged and (now seriously) modified by Ronan Collobert, for Torch7 7 | # Stripped-down and modified by Jörg Krause , for 8 | # The Luvit Authors 9 | 10 | macro(LUA_ADD_EXECUTABLE target) 11 | set(LUA_COMMAND luajit) 12 | set(LUA_COMMAND_ARGS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/luac.lua") 13 | if (WITH_LUA_ENGINE STREQUAL Lua) 14 | set(LUA_COMMAND lua) 15 | endif () 16 | 17 | if ($ENV{LUA_PATH}) 18 | set(LUA_COMMAND ${CMAKE_COMMAND} -E env LUA_PATH=$ENV{LUA_PATH} -- ${LUA_COMMAND}) 19 | endif () 20 | 21 | set(target_sources) 22 | 23 | foreach(source_file ${ARGN}) 24 | get_filename_component(source_extension ${source_file} EXT) 25 | get_filename_component(source_name ${source_file} NAME_WE) 26 | 27 | if (${source_extension} STREQUAL ".lua") 28 | set(generated_file "${CMAKE_BINARY_DIR}/compiled_lua/${source_name}_${target}_generated.c") 29 | if (NOT IS_ABSOLUTE ${source_file}) 30 | set(source_file "${CMAKE_CURRENT_SOURCE_DIR}/${source_file}") 31 | endif () 32 | 33 | add_custom_command( 34 | OUTPUT ${generated_file} 35 | MAIN_DEPENDENCY ${source_file} 36 | COMMAND ${LUA_COMMAND} 37 | ARGS ${LUA_COMMAND_ARGS} ${source_file} ${generated_file} 38 | COMMENT "Building Lua ${source_file}: ${generated_file}" 39 | ) 40 | 41 | get_filename_component(basedir ${generated_file} PATH) 42 | file(MAKE_DIRECTORY ${basedir}) 43 | 44 | list(APPEND target_sources ${generated_file}) 45 | else () 46 | list(APPEND target_sources ${source_file}) 47 | endif () 48 | endforeach() 49 | 50 | add_executable(${target} ${target_sources}) 51 | endmacro(LUA_ADD_EXECUTABLE target) 52 | -------------------------------------------------------------------------------- /src/luvi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Luvit Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #ifndef LUVI_H 18 | #define LUVI_H 19 | 20 | #include "lua.h" 21 | #include "lualib.h" 22 | #include "lauxlib.h" 23 | #include "uv.h" 24 | #include "luv.h" 25 | #ifndef WITH_PLAIN_LUA 26 | #include "luajit.h" 27 | #endif 28 | 29 | #include 30 | #include 31 | #ifdef _WIN32 32 | #include 33 | #include 34 | #else 35 | #include 36 | #include 37 | #endif 38 | 39 | #if (LUA_VERSION_NUM < 503) 40 | #include "compat-5.3.h" 41 | #ifndef WITH_PLAIN_LUA 42 | #undef luaL_traceback /* luajit has its own version */ 43 | #endif 44 | #endif 45 | 46 | #ifdef WITH_OPENSSL 47 | #include "openssl.h" 48 | #endif 49 | #ifdef WITH_PCRE2 50 | #include "pcre2.h" 51 | int luaopen_rex_pcre2(lua_State* L); 52 | #endif 53 | #ifdef WITH_ZLIB 54 | #include "zlib.h" 55 | LUALIB_API int luaopen_zlib(lua_State * const L); 56 | #endif 57 | #ifdef WITH_WINSVC 58 | #include "winsvc.h" 59 | #include "winsvcaux.h" 60 | #endif 61 | #ifdef WITH_LPEG 62 | int luaopen_lpeg(lua_State* L); 63 | LUALIB_API int luaopen_re(lua_State *L); 64 | #endif 65 | #ifdef WITH_PLAIN_LUA 66 | LUALIB_API int luaopen_bit(lua_State *L); 67 | #endif 68 | #ifdef WITH_LJ_VMDEF 69 | LUALIB_API int luaopen_vmdef(lua_State *L); 70 | #endif 71 | 72 | void luvi_openlibs(lua_State *L); 73 | 74 | LUALIB_API int luaopen_init(lua_State *L); 75 | LUALIB_API int luaopen_luvibundle(lua_State *L); 76 | LUALIB_API int luaopen_luvipath(lua_State *L); 77 | #endif 78 | 79 | -------------------------------------------------------------------------------- /src/winsvcaux.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The Luvit Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | #define LUA_LIB 18 | #include "luvi.h" 19 | #if (LUA_VERSION_NUM < 503) 20 | #include "compat-5.3.h" 21 | #endif 22 | 23 | #include 24 | 25 | static int lua_GetModuleFileName(lua_State *L) 26 | { 27 | HMODULE handle = lua_touserdata(L, 1); 28 | TCHAR name[MAX_PATH + 1]; 29 | DWORD ret = GetModuleFileName(handle, name, MAX_PATH + 1); 30 | if (ret > 0) 31 | { 32 | lua_pushstring(L, name); 33 | return 1; 34 | } 35 | lua_pushnil(L); 36 | lua_pushinteger(L, GetLastError()); 37 | return 2; 38 | } 39 | 40 | static int lua_GetErrorString(lua_State *L) 41 | { 42 | DWORD err = luaL_checkinteger(L, 1); 43 | LPTSTR lpMsgBuf = NULL; 44 | 45 | DWORD len = FormatMessage( 46 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 47 | FORMAT_MESSAGE_FROM_SYSTEM | 48 | FORMAT_MESSAGE_IGNORE_INSERTS, 49 | NULL, 50 | err, 51 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 52 | (LPTSTR)&lpMsgBuf, 53 | 0, NULL); 54 | 55 | if (len) { 56 | if (len > 2) { 57 | // strip \r\n 58 | lpMsgBuf[len - 2] = '\0'; 59 | } 60 | lua_pushstring(L, lpMsgBuf); 61 | LocalFree(lpMsgBuf); 62 | return 1; 63 | } 64 | lua_pushnil(L); 65 | lua_pushinteger(L, GetLastError()); 66 | return 2; 67 | } 68 | 69 | static const luaL_Reg winsvcauxlib[] = { 70 | { "GetModuleFileName", lua_GetModuleFileName }, 71 | { "GetErrorString", lua_GetErrorString }, 72 | { NULL, NULL } 73 | }; 74 | 75 | /* 76 | ** Open Windows Service Aux library 77 | */ 78 | LUALIB_API int luaopen_winsvcaux(lua_State *L) { 79 | luaL_newlib(L, winsvcauxlib); 80 | return 1; 81 | } 82 | -------------------------------------------------------------------------------- /cmake/Modules/luac.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Copyright 2014 The Luvit Authors. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | s 16 | ]] 17 | local src, gen = ... 18 | 19 | local chunk = assert(loadfile(src, nil, '@'..src)) 20 | local bytecode = string.dump(chunk) 21 | 22 | local function basename(name) 23 | return name:match("([^/\\]+)$") 24 | end 25 | 26 | local function basename_noext(name) 27 | return basename(name):match("(.*)%..+") 28 | end 29 | 30 | local function write_chunk(s) 31 | local t = { "{\n " }; 32 | local cc = 7 33 | for i = 1, #s do 34 | local c = string.byte(s, i, i) 35 | local ss = (" 0x%X"):format(c) 36 | if cc + #ss > 77 then 37 | t[#t+1] = "\n " 38 | t[#t+1] = ss 39 | cc = 7 + #ss 40 | if i ~= #s then 41 | t[#t+1] = "," 42 | cc = cc + 1 43 | end 44 | else 45 | t[#t+1] = ss 46 | cc = cc + #ss 47 | if i ~= #s then 48 | t[#t+1] = "," 49 | cc = cc + 1 50 | end 51 | end 52 | end 53 | t[#t+1] = "\n }" 54 | return (table.concat(t)) 55 | end 56 | 57 | local function W(...) 58 | io.write(...) 59 | return W 60 | end 61 | 62 | io.output(gen) 63 | W [[ 64 | /* generated source for Lua codes */ 65 | 66 | #ifndef LUA_LIB 67 | # define LUA_LIB 68 | #endif 69 | #include 70 | #include 71 | 72 | LUALIB_API int luaopen_]](basename_noext(src))[[(lua_State *L) { 73 | size_t len = ]](#bytecode)[[; 74 | const char chunk[] = ]](write_chunk(bytecode))[[; 75 | 76 | if (luaL_loadbuffer(L, chunk, len, "]](basename(src))[[") != 0) 77 | lua_error(L); 78 | lua_insert(L, 1); 79 | lua_call(L, lua_gettop(L)-1, LUA_MULTRET); 80 | return lua_gettop(L); 81 | } 82 | 83 | ]] 84 | io.close() 85 | 86 | 87 | -------------------------------------------------------------------------------- /deps/lua-openssl.cmake: -------------------------------------------------------------------------------- 1 | include(deps/openssl.cmake) 2 | 3 | set(LUA_OPENSSL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/lua-openssl" CACHE PATH "Path to lua-openssl") 4 | 5 | include_directories( 6 | ${CMAKE_BINARY_DIR}/include 7 | ${LUA_OPENSSL_DIR}/deps/auxiliar 8 | ${LUA_OPENSSL_DIR}/deps/lua-compat 9 | ${LUA_OPENSSL_DIR}/src 10 | ) 11 | 12 | if(WIN32) 13 | add_compile_definitions(WIN32_LEAN_AND_MEAN) 14 | add_compile_definitions(_CRT_SECURE_NO_WARNINGS) 15 | else() 16 | find_package(Threads) 17 | endif() 18 | 19 | add_library(lua_openssl STATIC 20 | ${LUA_OPENSSL_DIR}/deps/auxiliar/auxiliar.c 21 | ${LUA_OPENSSL_DIR}/deps/auxiliar/subsidiar.c 22 | ${LUA_OPENSSL_DIR}/src/asn1.c 23 | ${LUA_OPENSSL_DIR}/src/bio.c 24 | ${LUA_OPENSSL_DIR}/src/callback.c 25 | ${LUA_OPENSSL_DIR}/src/cipher.c 26 | ${LUA_OPENSSL_DIR}/src/cms.c 27 | ${LUA_OPENSSL_DIR}/src/compat.c 28 | ${LUA_OPENSSL_DIR}/src/crl.c 29 | ${LUA_OPENSSL_DIR}/src/csr.c 30 | ${LUA_OPENSSL_DIR}/src/dh.c 31 | ${LUA_OPENSSL_DIR}/src/digest.c 32 | ${LUA_OPENSSL_DIR}/src/dsa.c 33 | ${LUA_OPENSSL_DIR}/src/ec.c 34 | ${LUA_OPENSSL_DIR}/src/engine.c 35 | ${LUA_OPENSSL_DIR}/src/mac.c 36 | ${LUA_OPENSSL_DIR}/src/hmac.c 37 | ${LUA_OPENSSL_DIR}/src/kdf.c 38 | ${LUA_OPENSSL_DIR}/src/lbn.c 39 | ${LUA_OPENSSL_DIR}/src/lhash.c 40 | ${LUA_OPENSSL_DIR}/src/misc.c 41 | ${LUA_OPENSSL_DIR}/src/ocsp.c 42 | ${LUA_OPENSSL_DIR}/src/oids.txt 43 | ${LUA_OPENSSL_DIR}/src/openssl.c 44 | ${LUA_OPENSSL_DIR}/src/ots.c 45 | ${LUA_OPENSSL_DIR}/src/param.c 46 | ${LUA_OPENSSL_DIR}/src/pkcs12.c 47 | ${LUA_OPENSSL_DIR}/src/pkcs7.c 48 | ${LUA_OPENSSL_DIR}/src/pkey.c 49 | ${LUA_OPENSSL_DIR}/src/private.h 50 | ${LUA_OPENSSL_DIR}/src/rsa.c 51 | ${LUA_OPENSSL_DIR}/src/sk.h 52 | ${LUA_OPENSSL_DIR}/src/srp.c 53 | ${LUA_OPENSSL_DIR}/src/ssl.c 54 | ${LUA_OPENSSL_DIR}/src/th-lock.c 55 | ${LUA_OPENSSL_DIR}/src/util.c 56 | ${LUA_OPENSSL_DIR}/src/x509.c 57 | ${LUA_OPENSSL_DIR}/src/xattrs.c 58 | ${LUA_OPENSSL_DIR}/src/xexts.c 59 | ${LUA_OPENSSL_DIR}/src/xname.c 60 | ${LUA_OPENSSL_DIR}/src/xalgor.c 61 | ${LUA_OPENSSL_DIR}/src/xstore.c 62 | ) 63 | 64 | target_include_directories(lua_openssl PUBLIC ${OPENSSL_INCLUDE_DIR}) 65 | target_link_libraries(lua_openssl ${OPENSSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 66 | 67 | list(APPEND LUVI_LIBRARIES lua_openssl ${OPENSSL_LIBRARIES}) 68 | list(APPEND LUVI_DEFINITIONS WITH_OPENSSL=1) 69 | -------------------------------------------------------------------------------- /samples/repl.app/repl.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Copyright 2014 The Luvit Authors. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS-IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | --]] 18 | 19 | return function (stdin, stdout, uv, utils, greeting) 20 | 21 | local loadstring = loadstring or load 22 | 23 | local print = function(...) 24 | uv.write(stdout, table.concat({...}, "\t") .. "\n") 25 | end 26 | 27 | if greeting then print(greeting) end 28 | 29 | local c = utils.color 30 | 31 | local function gatherResults(success, ...) 32 | local n = select('#', ...) 33 | return success, { n = n, ... } 34 | end 35 | 36 | local function printResults(results) 37 | for i = 1, results.n do 38 | results[i] = utils.dump(results[i]) 39 | end 40 | print(table.concat(results, '\t')) 41 | end 42 | 43 | local buffer = '' 44 | 45 | local function evaluateLine(line) 46 | if line == "<3\n" then 47 | print("I " .. c("Bred") .. "♥" .. c() .. " you too!") 48 | return '>' 49 | end 50 | local chunk = buffer .. line 51 | local f, err = loadstring('return ' .. chunk, 'REPL') -- first we prefix return 52 | 53 | if not f then 54 | f, err = loadstring(chunk, 'REPL') -- try again without return 55 | end 56 | 57 | if f then 58 | buffer = '' 59 | local success, results = gatherResults(xpcall(f, debug.traceback)) 60 | 61 | if success then 62 | -- successful call 63 | if results.n > 0 then 64 | printResults(results) 65 | end 66 | else 67 | -- error 68 | print(results[1]) 69 | end 70 | else 71 | 72 | if err and err:match "''$" then 73 | -- Lua expects some more input; stow it away for next time 74 | buffer = chunk .. '\n' 75 | return '>>' 76 | else 77 | print(err) 78 | buffer = '' 79 | end 80 | end 81 | 82 | return '>' 83 | end 84 | 85 | local function displayPrompt(prompt) 86 | uv.write(stdout, prompt .. ' ') 87 | end 88 | 89 | displayPrompt '>' 90 | 91 | uv.read_start(stdin, function (err, line) 92 | assert(not err, err) 93 | if line then 94 | local prompt = evaluateLine(line) 95 | displayPrompt(prompt) 96 | else 97 | uv.close(stdin) 98 | end 99 | end) 100 | 101 | end 102 | -------------------------------------------------------------------------------- /src/lua/luvipath.lua: -------------------------------------------------------------------------------- 1 | local luvi = require('luvi') 2 | local getPrefix, splitPath, joinParts 3 | 4 | local isWindows 5 | if pcall(require, 'jit') then 6 | isWindows = require('jit').os == "Windows" 7 | else 8 | isWindows = not not (package.config:sub(1, 1) == "\\" or package.path:find("\\", 1, true) or package.cpath:find("\\", 1, true)) 9 | end 10 | 11 | if isWindows then 12 | -- Windows aware path utilities 13 | function getPrefix(path) 14 | return path:match("^%a:\\") or 15 | path:match("^/") or 16 | path:match("^\\+") 17 | end 18 | function splitPath(path) 19 | local parts = {} 20 | for part in string.gmatch(path, '([^/\\]+)') do 21 | table.insert(parts, part) 22 | end 23 | return parts 24 | end 25 | function joinParts(prefix, parts, i, j) 26 | if not prefix then 27 | return table.concat(parts, '/', i, j) 28 | elseif prefix ~= '/' then 29 | return prefix .. table.concat(parts, '\\', i, j) 30 | else 31 | return prefix .. table.concat(parts, '/', i, j) 32 | end 33 | end 34 | else 35 | -- Simple optimized versions for UNIX systems 36 | function getPrefix(path) 37 | return path:match("^/") 38 | end 39 | function splitPath(path) 40 | local parts = {} 41 | for part in string.gmatch(path, '([^/]+)') do 42 | table.insert(parts, part) 43 | end 44 | return parts 45 | end 46 | function joinParts(prefix, parts, i, j) 47 | if prefix then 48 | return prefix .. table.concat(parts, '/', i, j) 49 | end 50 | return table.concat(parts, '/', i, j) 51 | end 52 | end 53 | 54 | local function pathJoin(...) 55 | local inputs = {...} 56 | local l = #inputs 57 | 58 | -- Find the last segment that is an absolute path 59 | -- Or if all are relative, prefix will be nil 60 | local i = l 61 | local prefix 62 | while true do 63 | prefix = getPrefix(inputs[i]) 64 | if prefix or i <= 1 then break end 65 | i = i - 1 66 | end 67 | 68 | -- If there was one, remove its prefix from its segment 69 | if prefix then 70 | inputs[i] = inputs[i]:sub(#prefix) 71 | end 72 | 73 | -- Split all the paths segments into one large list 74 | local parts = {} 75 | while i <= l do 76 | local sub = splitPath(inputs[i]) 77 | for j = 1, #sub do 78 | parts[#parts + 1] = sub[j] 79 | end 80 | i = i + 1 81 | end 82 | 83 | -- Evaluate special segments in reverse order. 84 | local skip = 0 85 | local reversed = {} 86 | for idx = #parts, 1, -1 do 87 | local part = parts[idx] 88 | if part ~= '.' then 89 | if part == '..' then 90 | skip = skip + 1 91 | elseif skip > 0 then 92 | skip = skip - 1 93 | else 94 | reversed[#reversed + 1] = part 95 | end 96 | end 97 | end 98 | 99 | -- Reverse the list again to get the correct order 100 | parts = reversed 101 | for idx = 1, #parts / 2 do 102 | local j = #parts - idx + 1 103 | parts[idx], parts[j] = parts[j], parts[idx] 104 | end 105 | 106 | local path = joinParts(prefix, parts) 107 | return path 108 | end 109 | 110 | -- Legacy path exports 111 | luvi.path = { 112 | join = pathJoin, 113 | getPrefix = getPrefix, 114 | splitPath = splitPath, 115 | joinparts = joinParts, 116 | } 117 | 118 | return { 119 | isWindows = isWindows, 120 | getPrefix = getPrefix, 121 | splitPath = splitPath, 122 | joinParts = joinParts, 123 | pathJoin = pathJoin, 124 | } 125 | -------------------------------------------------------------------------------- /deps/openssl.cmake: -------------------------------------------------------------------------------- 1 | if (WithSharedOpenSSL) 2 | find_package(OpenSSL REQUIRED) 3 | 4 | message("Enabling Shared OpenSSL") 5 | message("OPENSSL_INCLUDE_DIR: ${OPENSSL_INCLUDE_DIR}") 6 | message("OPENSSL_LIBRARIES: ${OPENSSL_LIBRARIES}") 7 | else (WithSharedOpenSSL) 8 | message("Enabling Static OpenSSL") 9 | 10 | execute_process( 11 | COMMAND openssl info -configdir 12 | OUTPUT_VARIABLE OPENSSL_CONFIG_DIR 13 | OUTPUT_STRIP_TRAILING_WHITESPACE 14 | ) 15 | 16 | set(OPENSSL_CONFIG_OPTIONS no-tests no-module no-shared no-pinshared no-makedepend --prefix=${CMAKE_BINARY_DIR}) 17 | if (OPENSSL_CONFIG_DIR) 18 | message("Using existing OpenSSL configuration directory: ${OPENSSL_CONFIG_DIR}") 19 | set(OPENSSL_CONFIG_OPTIONS ${OPENSSL_CONFIG_OPTIONS} --openssldir=${OPENSSL_CONFIG_DIR}) 20 | endif () 21 | 22 | if (WithOpenSSLASM) 23 | enable_language(ASM) 24 | if (MSVC) 25 | enable_language(ASM_NASM) 26 | endif () 27 | else () 28 | set(OPENSSL_CONFIG_OPTIONS no-asm ${OPENSSL_CONFIG_OPTIONS}) 29 | endif () 30 | 31 | set(OPENSSL_CONFIGURE_TARGET) 32 | set(OPENSSL_BUILD_COMMAND make) 33 | if (WIN32) 34 | if (MSVC) 35 | set(OPENSSL_CONFIGURE_TARGET VC-WIN32) 36 | if ("${CMAKE_VS_PLATFORM_NAME}" MATCHES "x64") 37 | set(OPENSSL_CONFIGURE_TARGET VC-WIN64A) 38 | endif () 39 | set(OPENSSL_BUILD_COMMAND nmake) 40 | elseif (MINGW) 41 | set(OPENSSL_CONFIGURE_TARGET mingw) 42 | if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") 43 | set(OPENSSL_CONFIGURE_TARGET mingw64) 44 | endif () 45 | 46 | set(OPENSSL_BUILD_COMMAND mingw32-make) 47 | else () 48 | # TODO: Add support for other Windows compilers 49 | message(FATAL_ERROR "This platform does not support building OpenSSL") 50 | endif () 51 | endif () 52 | 53 | message("OPENSSL_CONFIGURE_TARGET: ${OPENSSL_CONFIGURE_TARGET}") 54 | message("OPENSSL_CONFIG_OPTIONS: ${OPENSSL_CONFIG_OPTIONS}") 55 | message("OPENSSL_BUILD_COMMAND: ${OPENSSL_BUILD_COMMAND}") 56 | include(FetchContent) 57 | 58 | FetchContent_Declare(openssl 59 | URL https://github.com/openssl/openssl/releases/download/openssl-3.4.0/openssl-3.4.0.tar.gz 60 | URL_HASH SHA256=e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf 61 | ) 62 | 63 | FetchContent_MakeAvailable(openssl) 64 | FetchContent_GetProperties(openssl) 65 | 66 | set(OPENSSL_ROOT_DIR ${openssl_SOURCE_DIR}) 67 | 68 | # Configure OpenSSL 69 | 70 | execute_process( 71 | COMMAND perl Configure ${OPENSSL_CONFIGURE_TARGET} ${OPENSSL_CONFIG_OPTIONS} 72 | WORKING_DIRECTORY ${OPENSSL_ROOT_DIR} 73 | RESULT_VARIABLE result 74 | ) 75 | 76 | if (result) 77 | message(FATAL_ERROR "Failed to configure OpenSSL") 78 | endif () 79 | 80 | execute_process( 81 | COMMAND perl configdata.pm --dump 82 | WORKING_DIRECTORY ${OPENSSL_ROOT_DIR} 83 | ) 84 | 85 | if (MSVC) 86 | set(OPENSSL_LIB_CRYPTO ${OPENSSL_ROOT_DIR}/libcrypto.lib) 87 | set(OPENSSL_LIB_SSL ${OPENSSL_ROOT_DIR}/libssl.lib) 88 | else () 89 | set(OPENSSL_LIB_CRYPTO ${OPENSSL_ROOT_DIR}/libcrypto.a) 90 | set(OPENSSL_LIB_SSL ${OPENSSL_ROOT_DIR}/libssl.a) 91 | endif () 92 | 93 | # Build OpenSSL 94 | 95 | add_custom_target(openssl-build 96 | COMMAND ${OPENSSL_BUILD_COMMAND} 97 | BYPRODUCTS ${OPENSSL_LIB_CRYPTO} ${OPENSSL_LIB_SSL} 98 | WORKING_DIRECTORY ${OPENSSL_ROOT_DIR} 99 | USES_TERMINAL 100 | ) 101 | 102 | # Define OpenSSL libraries 103 | 104 | add_library(openssl_ssl STATIC IMPORTED) 105 | set_target_properties(openssl_ssl PROPERTIES IMPORTED_LOCATION ${OPENSSL_LIB_SSL}) 106 | add_dependencies(openssl_ssl openssl-build) 107 | 108 | add_library(openssl_crypto STATIC IMPORTED) 109 | set_target_properties(openssl_crypto PROPERTIES IMPORTED_LOCATION ${OPENSSL_LIB_CRYPTO}) 110 | add_dependencies(openssl_ssl openssl-build) 111 | 112 | set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include) 113 | set(OPENSSL_LIBRARIES openssl_ssl openssl_crypto) 114 | 115 | if (WIN32) 116 | set(OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES} crypt32) 117 | endif () 118 | 119 | message("OPENSSL_INCLUDE_DIR: ${OPENSSL_INCLUDE_DIR}") 120 | message("OPENSSL_LIBRARIES: ${OPENSSL_LIBRARIES}") 121 | endif (WithSharedOpenSSL) 122 | -------------------------------------------------------------------------------- /src/lua/init.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Copyright 2014 The Luvit Authors. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS-IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | --]] 18 | 19 | local uv = require('uv') 20 | local luvi = require('luvi') 21 | local miniz = require('miniz') 22 | 23 | local luviBundle = require('luvibundle') 24 | local commonBundle = luviBundle.commonBundle 25 | local makeBundle = luviBundle.makeBundle 26 | local buildBundle = luviBundle.buildBundle 27 | 28 | local function generateOptionsString() 29 | local s = {} 30 | for k, v in pairs(luvi.options) do 31 | if type(v) == 'boolean' then 32 | table.insert(s, k) 33 | else 34 | table.insert(s, string.format("%s: %s", k, v)) 35 | end 36 | end 37 | return table.concat(s, "\n") 38 | end 39 | 40 | local commands = { 41 | ["-o"] = "output", 42 | ["--output"] = "output", 43 | ["-m"] = "main", 44 | ["--main"] = "main", 45 | ["-v"] = "version", 46 | ["--version"] = "version", 47 | ["-h"] = "help", 48 | ["--help"] = "help", 49 | ["--compile"] = "compile", 50 | ["--force"] = "force", 51 | ["-s"] = "strip", 52 | ["--strip"] = "strip" 53 | } 54 | 55 | local function version(args) 56 | print(string.format("%s %s", args[0], luvi.version)) 57 | print(generateOptionsString()) 58 | end 59 | 60 | local function help(args) 61 | 62 | local usage = [[ 63 | Usage: $(LUVI) bundle+ [options] [-- extra args] 64 | 65 | bundle Path to directory or zip file containing bundle source. 66 | `bundle` can be specified multiple times to layer bundles 67 | on top of each other. 68 | --version Show luvi version and compiled in options. 69 | --output target Build a luvi app by zipping the bundle and inserting luvi. 70 | --main path Specify a custom main bundle path (normally main.lua) 71 | --compile Compile Lua code into bytecode before bundling. 72 | --strip Compile Lua code and strip debug info. 73 | --force Ignore errors when compiling Lua code. 74 | --help Show this help file. 75 | -- All args after this go to the luvi app itself. 76 | 77 | Examples: 78 | 79 | # Run an app from disk, but pass in arguments 80 | $(LUVI) path/to/app -- app args 81 | 82 | # Run from a app zip 83 | $(LUVI) path/to/app.zip 84 | 85 | # Run an app that layers on top of luvit 86 | $(LUVI) path/to/app path/to/luvit 87 | 88 | # Bundle an app with luvi to create standalone 89 | $(LUVI) path/to/app -o target 90 | ./target some args 91 | 92 | # Run unit tests for a luvi app using custom main 93 | $(LUVI) path/to/app -m tests/run.lua 94 | ]] 95 | print((string.gsub(usage, "%$%(LUVI%)", args[0]))) 96 | end 97 | 98 | local EXIT_SUCCESS = 0 99 | 100 | return function(args) 101 | 102 | -- First check for a bundled zip file appended to the executable 103 | local path = uv.exepath() 104 | local zip = miniz.new_reader(path) 105 | if zip then 106 | return commonBundle({path}, nil, args) 107 | end 108 | 109 | -- Parse the arguments 110 | local bundles = { } 111 | local options = {} 112 | local appArgs = { [0] = args[0] } 113 | 114 | local key 115 | for i = 1, #args do 116 | local arg = args[i] 117 | if arg == "--" then 118 | if #bundles == 0 then 119 | i = i + 1 120 | bundles[1] = args[i] 121 | end 122 | for j = i + 1, #args do 123 | appArgs[#appArgs + 1] = args[j] 124 | end 125 | break 126 | elseif key then 127 | options[key] = arg 128 | key = nil 129 | else 130 | local command = commands[arg] 131 | if options[command] then 132 | error("Duplicate flags: " .. command) 133 | end 134 | if command == "output" or command == "main" then 135 | key = command 136 | elseif command then 137 | options[command] = true 138 | else 139 | if arg:sub(1, 1) == "-" then 140 | error("Unknown flag: " .. arg) 141 | end 142 | bundles[#bundles + 1] = arg 143 | end 144 | end 145 | end 146 | 147 | if key then 148 | error("Missing value for option: " .. key) 149 | end 150 | 151 | -- Show help and version by default 152 | if #bundles == 0 and not options.version and not options.help then 153 | options.version = true 154 | options.help = true 155 | end 156 | 157 | if options.version then 158 | version(args) 159 | end 160 | if options.help then 161 | help(args) 162 | end 163 | 164 | -- Don't run app when printing version or help 165 | if options.version or options.help then return EXIT_SUCCESS end 166 | 167 | -- Build the app if output is given 168 | if options.output then 169 | return buildBundle(options, makeBundle(bundles)) 170 | end 171 | 172 | -- Run the luvi app with the extra args 173 | return commonBundle(bundles, options.main, appArgs) 174 | 175 | end 176 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The Luvit Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #define LUA_LIB 19 | #include "luvi.h" 20 | #include "luv.h" 21 | #include "lenv.c" 22 | #include "luvi.c" 23 | 24 | #include "snapshot.c" 25 | 26 | int luaopen_miniz(lua_State *L); 27 | 28 | #ifdef WITH_CUSTOM 29 | int luvi_custom(lua_State* L); 30 | #endif 31 | 32 | static int luvi_traceback(lua_State *L) { 33 | if (!lua_isstring(L, 1)) /* 'message' not a string? */ 34 | return 1; /* keep it intact */ 35 | luaL_traceback(L, L, lua_tostring(L, -1), 1); 36 | return 1; 37 | } 38 | 39 | static lua_State* vm_acquire(){ 40 | lua_State*L = luaL_newstate(); 41 | if (L == NULL) 42 | return L; 43 | 44 | // Ensure that the version of lua interpreter is compatible with the version luvi was compiled with. 45 | #ifdef WITH_PLAIN_LUA 46 | #if (LUA_VERSION_NUM >= 502) 47 | luaL_checkversion(L); 48 | #endif 49 | #else 50 | #ifndef LUAJIT_MISSING_VERSION_SYM // debian patches luajit to remove this symbol, so we can't check it. 51 | LUAJIT_VERSION_SYM(); 52 | #endif 53 | #endif 54 | 55 | // Add in the lua standard and compat libraries 56 | luvi_openlibs(L); 57 | 58 | // Get package.loaded so that we can load modules 59 | lua_getglobal(L, "package"); 60 | lua_getfield(L, -1, "loaded"); 61 | 62 | // load luv into uv in advance so that the metatables for async work. 63 | luaL_requiref(L, "uv", luaopen_luv, 0); 64 | lua_setfield(L, -2, "luv"); 65 | 66 | #ifdef WITH_PLAIN_LUA 67 | luaL_requiref(L, "bit", luaopen_bit, 1); 68 | lua_pop(L, 1); 69 | #endif 70 | 71 | // remove package.loaded 72 | lua_remove(L, -1); 73 | 74 | // Get package.preload so we can store builtins in it. 75 | lua_getfield(L, -1, "preload"); 76 | lua_remove(L, -2); // Remove package 77 | 78 | lua_pushcfunction(L, luaopen_env); 79 | lua_setfield(L, -2, "env"); 80 | 81 | lua_pushcfunction(L, luaopen_miniz); 82 | lua_setfield(L, -2, "miniz"); 83 | 84 | lua_pushcfunction(L, luaopen_snapshot); 85 | lua_setfield(L, -2, "snapshot"); 86 | 87 | #ifdef WITH_LPEG 88 | lua_pushcfunction(L, luaopen_lpeg); 89 | lua_setfield(L, -2, "lpeg"); 90 | lua_pushcfunction(L, luaopen_re); 91 | lua_setfield(L, -2, "re"); 92 | #endif 93 | 94 | #ifdef WITH_PCRE2 95 | lua_pushcfunction(L, luaopen_rex_pcre2); 96 | lua_pushvalue(L, -1); 97 | lua_setfield(L, -3, "rex_pcre2"); 98 | lua_setfield(L, -2, "rex"); 99 | #endif 100 | 101 | #ifdef WITH_OPENSSL 102 | // Store openssl module definition at preload.openssl 103 | lua_pushcfunction(L, luaopen_openssl); 104 | lua_setfield(L, -2, "openssl"); 105 | #endif 106 | 107 | #ifdef WITH_ZLIB 108 | // Store zlib module definition at preload.zlib 109 | lua_pushcfunction(L, luaopen_zlib); 110 | lua_setfield(L, -2, "zlib"); 111 | #endif 112 | 113 | #ifdef WITH_WINSVC 114 | // Store luvi module definition at preload.winsvc 115 | lua_pushcfunction(L, luaopen_winsvc); 116 | lua_setfield(L, -2, "winsvc"); 117 | lua_pushcfunction(L, luaopen_winsvcaux); 118 | lua_setfield(L, -2, "winsvcaux"); 119 | #endif 120 | 121 | // Store luvi module definition at preload.luvi 122 | lua_pushcfunction(L, luaopen_luvi); 123 | lua_setfield(L, -2, "luvi"); 124 | 125 | lua_pushcfunction(L, luaopen_init); 126 | lua_setfield(L, -2, "init"); 127 | lua_pushcfunction(L, luaopen_luvibundle); 128 | lua_setfield(L, -2, "luvibundle"); 129 | lua_pushcfunction(L, luaopen_luvipath); 130 | lua_setfield(L, -2, "luvipath"); 131 | 132 | #ifdef WITH_LJ_VMDEF 133 | lua_pushcfunction(L, luaopen_vmdef); 134 | lua_setfield(L, -2, "jit.vmdef"); 135 | #endif 136 | 137 | #ifdef WITH_CUSTOM 138 | luvi_custom(L); 139 | #endif 140 | return L; 141 | } 142 | 143 | static void vm_release(lua_State*L) { 144 | lua_close(L); 145 | } 146 | 147 | int main(int argc, char* argv[] ) { 148 | 149 | lua_State* L; 150 | int index; 151 | int res; 152 | int errfunc; 153 | 154 | // Hooks in libuv that need to be done in main. 155 | argv = uv_setup_args(argc, argv); 156 | 157 | luv_set_thread_cb(vm_acquire, vm_release); 158 | // Create the lua state. 159 | L = vm_acquire(); 160 | if (L == NULL) { 161 | fprintf(stderr, "luaL_newstate has failed\n"); 162 | return 1; 163 | } 164 | 165 | /* push debug function */ 166 | lua_pushcfunction(L, luvi_traceback); 167 | errfunc = lua_gettop(L); 168 | 169 | // Load the init.lua script 170 | if (luaL_loadstring(L, "return require('init')(...)")) { 171 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 172 | vm_release(L); 173 | return -1; 174 | } 175 | 176 | // Pass the command-line arguments to init as a zero-indexed table 177 | lua_createtable (L, argc, 0); 178 | for (index = 0; index < argc; index++) { 179 | lua_pushstring(L, argv[index]); 180 | lua_rawseti(L, -2, index); 181 | } 182 | 183 | // Start the main script. 184 | if (lua_pcall(L, 1, 1, errfunc)) { 185 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 186 | vm_release(L); 187 | return -1; 188 | } 189 | 190 | // Use the return value from the script as process exit code. 191 | res = 0; 192 | if (lua_type(L, -1) == LUA_TNUMBER) { 193 | res = (int)lua_tointeger(L, -1); 194 | } 195 | vm_release(L); 196 | return res; 197 | } 198 | -------------------------------------------------------------------------------- /deps/bit.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Lua BitOp -- a bit operations library for Lua 5.1/5.2. 3 | ** http://bitop.luajit.org/ 4 | ** 5 | ** Copyright (C) 2008-2012 Mike Pall. All rights reserved. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining 8 | ** a copy of this software and associated documentation files (the 9 | ** "Software"), to deal in the Software without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Software, and to 12 | ** permit persons to whom the Software is furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be 16 | ** included in all copies or substantial portions of the Software. 17 | ** 18 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | ** 26 | ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] 27 | */ 28 | 29 | #define LUA_BITOP_VERSION "1.0.2" 30 | 31 | #define LUA_LIB 32 | #include "lua.h" 33 | #include "lauxlib.h" 34 | 35 | #ifdef _MSC_VER 36 | /* MSVC is stuck in the last century and doesn't have C99's stdint.h. */ 37 | typedef __int32 int32_t; 38 | typedef unsigned __int32 uint32_t; 39 | typedef unsigned __int64 uint64_t; 40 | #else 41 | #include 42 | #endif 43 | 44 | typedef int32_t SBits; 45 | typedef uint32_t UBits; 46 | 47 | typedef union { 48 | lua_Number n; 49 | #ifdef LUA_NUMBER_DOUBLE 50 | uint64_t b; 51 | #else 52 | UBits b; 53 | #endif 54 | } BitNum; 55 | 56 | /* Convert argument to bit type. */ 57 | static UBits barg(lua_State *L, int idx) 58 | { 59 | BitNum bn; 60 | UBits b; 61 | #if LUA_VERSION_NUM < 502 62 | bn.n = lua_tonumber(L, idx); 63 | #elif LUA_VERSION_NUM >= 503 64 | (void)bn; 65 | b = (UBits)luaL_checkinteger(L, idx); 66 | #else 67 | bn.n = luaL_checknumber(L, idx); 68 | #if defined(LUA_NUMBER_DOUBLE) 69 | bn.n += 6755399441055744.0; /* 2^52+2^51 */ 70 | #ifdef SWAPPED_DOUBLE 71 | b = (UBits)(bn.b >> 32); 72 | #else 73 | b = (UBits)bn.b; 74 | #endif 75 | #elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \ 76 | defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \ 77 | defined(LUA_NUMBER_LLONG) 78 | if (sizeof(UBits) == sizeof(lua_Number)) 79 | b = bn.b; 80 | else 81 | b = (UBits)(SBits)bn.n; 82 | #elif defined(LUA_NUMBER_FLOAT) 83 | #error "A 'float' lua_Number type is incompatible with this library" 84 | #else 85 | #error "Unknown number type, check LUA_NUMBER_* in luaconf.h" 86 | #endif 87 | #if LUA_VERSION_NUM < 502 88 | if (b == 0 && !lua_isnumber(L, idx)) { 89 | luaL_typerror(L, idx, "number"); 90 | } 91 | #endif 92 | #endif 93 | return b; 94 | } 95 | 96 | /* Return bit type. */ 97 | #define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1; 98 | 99 | static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) } 100 | static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) } 101 | 102 | #define BIT_OP(func, opr) \ 103 | static int func(lua_State *L) { int i; UBits b = barg(L, 1); \ 104 | for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) } 105 | BIT_OP(bit_band, &=) 106 | BIT_OP(bit_bor, |=) 107 | BIT_OP(bit_bxor, ^=) 108 | 109 | #define bshl(b, n) (b << n) 110 | #define bshr(b, n) (b >> n) 111 | #define bsar(b, n) ((SBits)b >> n) 112 | #define brol(b, n) ((b << n) | (b >> (32-n))) 113 | #define bror(b, n) ((b << (32-n)) | (b >> n)) 114 | #define BIT_SH(func, fn) \ 115 | static int func(lua_State *L) { \ 116 | UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) } 117 | BIT_SH(bit_lshift, bshl) 118 | BIT_SH(bit_rshift, bshr) 119 | BIT_SH(bit_arshift, bsar) 120 | BIT_SH(bit_rol, brol) 121 | BIT_SH(bit_ror, bror) 122 | 123 | static int bit_bswap(lua_State *L) 124 | { 125 | UBits b = barg(L, 1); 126 | b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24); 127 | BRET(b) 128 | } 129 | 130 | static int bit_tohex(lua_State *L) 131 | { 132 | UBits b = barg(L, 1); 133 | SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2); 134 | const char *hexdigits = "0123456789abcdef"; 135 | char buf[8]; 136 | int i; 137 | if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } 138 | if (n > 8) n = 8; 139 | for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; } 140 | lua_pushlstring(L, buf, (size_t)n); 141 | return 1; 142 | } 143 | 144 | static const struct luaL_Reg bit_funcs[] = { 145 | { "tobit", bit_tobit }, 146 | { "bnot", bit_bnot }, 147 | { "band", bit_band }, 148 | { "bor", bit_bor }, 149 | { "bxor", bit_bxor }, 150 | { "lshift", bit_lshift }, 151 | { "rshift", bit_rshift }, 152 | { "arshift", bit_arshift }, 153 | { "rol", bit_rol }, 154 | { "ror", bit_ror }, 155 | { "bswap", bit_bswap }, 156 | { "tohex", bit_tohex }, 157 | { NULL, NULL } 158 | }; 159 | 160 | /* Signed right-shifts are implementation-defined per C89/C99. 161 | ** But the de facto standard are arithmetic right-shifts on two's 162 | ** complement CPUs. This behaviour is required here, so test for it. 163 | */ 164 | #define BAD_SAR (bsar(-8, 2) != (SBits)-2) 165 | 166 | LUALIB_API int luaopen_bit(lua_State *L) 167 | { 168 | UBits b; 169 | lua_pushnumber(L, (lua_Number)1437217655L); 170 | b = barg(L, -1); 171 | if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */ 172 | const char *msg = "compiled with incompatible luaconf.h"; 173 | #ifdef LUA_NUMBER_DOUBLE 174 | #ifdef _WIN32 175 | if (b == (UBits)1610612736L) 176 | msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; 177 | #endif 178 | if (b == (UBits)1127743488L) 179 | msg = "not compiled with SWAPPED_DOUBLE"; 180 | #endif 181 | if (BAD_SAR) 182 | msg = "arithmetic right-shift broken"; 183 | luaL_error(L, "bit library self-test failed (%s)", msg); 184 | } 185 | #if LUA_VERSION_NUM < 502 186 | luaL_register(L, "bit", bit_funcs); 187 | #else 188 | luaL_newlib(L, bit_funcs); 189 | #endif 190 | return 1; 191 | } 192 | 193 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(luvi C) 4 | 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules") 6 | 7 | if (MSVC) 8 | cmake_policy(SET CMP0091 NEW) 9 | # Statically build against C runtime (use the right version for Release/Debug) 10 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 11 | endif () 12 | 13 | if (CMAKE_COMPILER_IS_GNUCC) 14 | add_compile_options(-Wno-unused-function) 15 | endif () 16 | 17 | if (MINGW) 18 | add_compile_options(-Wno-error=incompatible-pointer-types) 19 | endif () 20 | 21 | if (CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") 22 | add_compile_options(-Wno-incompatible-pointer-types) 23 | endif() 24 | 25 | if (UNIX) 26 | add_compile_options(-Wall) 27 | endif () 28 | 29 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION") 30 | file (STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" LUVI_VERSION) 31 | message("-- Found luvi version: ${LUVI_VERSION}") 32 | else () 33 | execute_process( 34 | COMMAND git describe --tags 35 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 36 | OUTPUT_VARIABLE LUVI_VERSION 37 | OUTPUT_STRIP_TRAILING_WHITESPACE) 38 | 39 | # Handle shallow clones 40 | if (LUVI_VERSION STREQUAL "") 41 | execute_process( 42 | COMMAND git rev-parse --short HEAD 43 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 44 | OUTPUT_VARIABLE LUVI_VERSION 45 | OUTPUT_STRIP_TRAILING_WHITESPACE) 46 | 47 | set(LUVI_VERSION "v0.0.0-0-g${LUVI_VERSION}") 48 | endif () 49 | message("-- Found luvi git version: ${LUVI_VERSION}") 50 | endif () 51 | 52 | option(WithSharedLibluv "Shared or Static libluv" OFF) 53 | option(WithOpenSSL "Include OpenSSL" OFF) 54 | option(WithOpenSSLASM "Enable Assembly Optimizations" ON) 55 | option(WithSharedOpenSSL "Shared or Static OpenSSL" OFF) 56 | option(WithPCRE2 "Include PCRE2" OFF) 57 | option(WithSharedPCRE2 "Shared or Static PCRE2" OFF) 58 | option(WithLPEG "Include LPEG" OFF) 59 | option(WithSharedLPEG "Shared or Static LPEG" OFF) 60 | option(WithZLIB "Include ZLIB" OFF) 61 | option(WithSharedZLIB "Shared or Static ZLIB" OFF) 62 | 63 | find_package(Threads) 64 | set (LUVI_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) 65 | 66 | # When linking against a shared libluv, we assume that luajit and libuv are also shared 67 | # Luvi does not support linking a static libluv *and* a static lua/luajit or libuv 68 | if (WithSharedLibluv) 69 | find_package(Luv REQUIRED) 70 | include_directories(${LUV_INCLUDE_DIRS}) 71 | 72 | find_package(LuaJIT REQUIRED) 73 | include_directories(${LUAJIT_INCLUDE_DIRS}) 74 | 75 | find_package(Libuv REQUIRED) 76 | include_directories(${LIBUV_INCLUDE_DIRS}) 77 | 78 | include(CheckCSourceCompiles) 79 | 80 | set(CMAKE_REQUIRED_INCLUDES ${LUAJIT_INCLUDE_DIRS}) 81 | set(CMAKE_REQUIRED_LIBRARIES ${LUAJIT_LIBRARIES}) 82 | check_c_source_compiles(" 83 | #include 84 | int main() { 85 | LUAJIT_VERSION_SYM(); 86 | return 0; 87 | }" LUAJIT_HAS_VERSION_SYM) 88 | if (NOT LUAJIT_HAS_VERSION_SYM) 89 | add_compile_definitions(LUAJIT_MISSING_VERSION_SYM) 90 | endif () 91 | 92 | list(APPEND LUVI_LIBRARIES ${LUV_LIBRARIES} ${LUAJIT_LIBRARIES} ${LIBUV_LIBRARIES}) 93 | else (WithSharedLibluv) 94 | # Build luv as static library instead of as module 95 | set(BUILD_MODULE OFF CACHE BOOL "Turn off building luv as module") 96 | set(BUILD_STATIC_LIBS ON CACHE BOOL "Build luv as static lib") 97 | include_directories(deps/luv/src) 98 | include_directories(deps/luv/deps/libuv/include) 99 | if (WITH_LUA_ENGINE STREQUAL Lua) 100 | include_directories(deps/luv/deps/lua) 101 | list(APPEND LUVI_LIBRARIES lualib) 102 | else () 103 | include_directories(deps/luv/deps/luajit/src) 104 | list(APPEND LUVI_LIBRARIES luajit-5.1) 105 | 106 | set(luajit_vmdef ${CMAKE_CURRENT_BINARY_DIR}/deps/luv/vmdef.lua) 107 | set_source_files_properties(${luajit_vmdef} PROPERTIES GENERATED TRUE) 108 | 109 | list(APPEND LUVI_DEFINITIONS WITH_LJ_VMDEF) 110 | endif () 111 | 112 | list(APPEND LUVI_LIBRARIES libluv_a uv_a) 113 | add_subdirectory(deps/luv) # Build luv 114 | endif (WithSharedLibluv) 115 | 116 | set(LUA_COMPAT53_DIR "${CMAKE_CURRENT_SOURCE_DIR}/deps/luv/deps/lua-compat-5.3" CACHE PATH "Path to lua-compat-5.3") 117 | include_directories(${LUA_COMPAT53_DIR}) 118 | include_directories(${LUA_COMPAT53_DIR}/c-api) 119 | 120 | if (WithOpenSSL) 121 | include(deps/lua-openssl.cmake) 122 | endif () 123 | 124 | if (WithPCRE2) 125 | include(deps/lrexlib.cmake) 126 | endif () 127 | 128 | if (WithLPEG) 129 | include(deps/lpeg.cmake) 130 | set(lpeg_re_lua ${LPEGLIB_DIR}/re.lua) 131 | endif () 132 | 133 | if (WithZLIB) 134 | include(deps/lua-zlib.cmake) 135 | endif () 136 | 137 | if (WIN32) 138 | set(winsvc src/winsvc.h src/winsvcaux.h src/winsvc.c src/winsvcaux.c) 139 | if (WithSharedLibluv) 140 | add_definitions( -DLUA_BUILD_AS_DLL -DBUILDING_UV_SHARED ) 141 | endif () 142 | add_definitions( -DWITH_WINSVC ) 143 | add_definitions( -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS ) 144 | 145 | add_library (luvi_renamed src/luvi_renamed.c) 146 | endif () 147 | 148 | if (WITH_LUA_ENGINE STREQUAL Lua) 149 | add_definitions(-DWITH_PLAIN_LUA) 150 | endif () 151 | 152 | include(LuaAddExecutable) 153 | 154 | # TODO: define MINIZ_NO_STDIO for small size 155 | add_subdirectory(deps/miniz miniz.dir) 156 | set(lminiz src/lminiz.c) 157 | include_directories(deps/miniz) 158 | list(APPEND LUVI_LIBRARIES miniz) 159 | 160 | lua_add_executable(luvi 161 | ${winsvc} 162 | ${lminiz} 163 | src/main.c 164 | src/luvi_compat.c 165 | src/lua/init.lua 166 | src/lua/luvipath.lua 167 | src/lua/luvibundle.lua 168 | ${luajit_vmdef} 169 | ${lpeg_re_lua} 170 | ) 171 | 172 | if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") 173 | set(CMAKE_EXE_LINKER_FLAGS "-Wl,-E") 174 | endif () 175 | 176 | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 177 | set(list APPEND LUVI_LIBRARIES rt) 178 | endif () 179 | 180 | target_link_libraries(luvi ${LUVI_LIBRARIES} ${EXTRA_LIBS}) 181 | set_target_properties(luvi PROPERTIES ENABLE_EXPORTS ON) 182 | 183 | target_compile_definitions(luvi PRIVATE LUVI_VERSION="${LUVI_VERSION}") 184 | target_compile_definitions(luvi PRIVATE ${LUVI_DEFINITIONS}) # Add any extra definitions, like the WITH_{LIB} defines 185 | 186 | message("Configuration Summary:") 187 | message(" LUVI_VERSION: ${LUVI_VERSION}") 188 | message(" LUVI_DEFINITIONS: ${LUVI_DEFINITIONS}") 189 | message(" LUVI_LIBRARIES: ${LUVI_LIBRARIES}") 190 | 191 | ############################################################################### 192 | ## Installation Targets 193 | ############################################################################### 194 | 195 | install(TARGETS luvi DESTINATION bin) 196 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ################################################################################################### 3 | # Default build options 4 | ################################################################################################### 5 | # NPROCS: Number of processors to use for parallel jobs 6 | # GENERATOR: CMake generator to configure and build with 7 | # PREFIX: Where to install luvi, defaults to /usr/local or C:\Program Files\luvit 8 | # BINPREFIX: Where to install luvi binary, defaults to $PREFIX/bin 9 | # EXTRA_CONFIGURE_FLAGS: Extra options to pass to cmake when configuring 10 | # EXTRA_BUILD_FLAGS: Extra options to pass to make when building 11 | # 12 | # CMAKE_BUILD_TYPE: The cmake build type to use, defaults to Release 13 | # WITH_AMALG: Whether to build the lua amalgamated, will use more memory but is faster 14 | # WITH_LUA_ENGINE: Which lua engine to use, defaults to LuaJIT 15 | # WITH_SHARED_LIBLUV: Whether to use libluv as a shared library. 16 | # Note: Shared libluv implies shared libuv and luajit. 17 | # 18 | # WITH_{OPENSSL,PCRE,LPEG,ZLIB}: Whether to include the given library in the build 19 | # WITH_SHARED_{OPENSSL,PCRE,LPEG,ZLIB}: Whether to use shared or static versions of the given library 20 | ################################################################################################### 21 | 22 | all: default 23 | 24 | ifdef MAKEDIR: ######################## 25 | !ifdef MAKEDIR #### Start of nmake #### 26 | 27 | !ifndef CMAKE_BUILD_TYPE 28 | CMAKE_BUILD_TYPE = Release 29 | !endif 30 | 31 | !ifndef WITH_AMALG 32 | WITH_AMALG = ON 33 | !endif 34 | 35 | !ifndef WITH_LUA_ENGINE 36 | WITH_LUA_ENGINE = LuaJIT 37 | !endif 38 | 39 | !ifndef WITH_SHARED_LIBLUV 40 | WITH_SHARED_LIBLUV = OFF 41 | !endif 42 | 43 | !ifndef WITH_OPENSSL 44 | WITH_OPENSSL = ON 45 | !endif 46 | 47 | !ifndef WITH_PCRE2 48 | WITH_PCRE2 = ON 49 | !endif 50 | 51 | !ifndef WITH_LPEG 52 | WITH_LPEG = ON 53 | !endif 54 | 55 | !ifndef WITH_ZLIB 56 | WITH_ZLIB = OFF 57 | !endif 58 | 59 | !ifndef WITH_OPENSSL_ASM 60 | WITH_OPENSSL_ASM = ON 61 | !endif 62 | 63 | !ifndef WITH_SHARED_OPENSSL 64 | WITH_SHARED_OPENSSL = OFF 65 | !endif 66 | 67 | !ifndef WITH_SHARED_PCRE2 68 | WITH_SHARED_PCRE2 = OFF 69 | !endif 70 | 71 | !ifndef WITH_SHARED_LPEG 72 | WITH_SHARED_LPEG = OFF 73 | !endif 74 | 75 | !ifndef WITH_SHARED_ZLIB 76 | WITH_SHARED_ZLIB = OFF 77 | !endif 78 | 79 | !ifndef PREFIX 80 | PREFIX = C:\Program Files\luvit 81 | !endif 82 | 83 | !ifndef BINPREFIX 84 | BINPREFIX = $(PREFIX)\bin 85 | !endif 86 | 87 | !ifndef BUILD_PREFIX 88 | BUILD_PREFIX = build 89 | !endif 90 | 91 | RMR = cmd /c rmdir /s /q 92 | RM = cmd /c del /f 93 | INSTALL = cmd /c copy /y 94 | LUVI = cmd /c $(BUILD_PREFIX)\$(CMAKE_BUILD_TYPE)\luvi.exe 95 | TEST_BIN = cmd /c test.bin 96 | 97 | !ifndef CMAKE 98 | CMAKE = cmake 99 | !endif 100 | 101 | !else #### End of nmake #### 102 | else #### Start of gmake #### 103 | 104 | CMAKE_BUILD_TYPE ?= Release 105 | 106 | WITH_AMALG ?= ON 107 | WITH_LUA_ENGINE ?= LuaJIT 108 | WITH_SHARED_LIBLUV ?= OFF 109 | 110 | WITH_OPENSSL ?= ON 111 | WITH_PCRE2 ?= ON 112 | WITH_LPEG ?= ON 113 | WITH_ZLIB ?= OFF 114 | 115 | WITH_OPENSSL_ASM ?= ON 116 | WITH_SHARED_OPENSSL ?= OFF 117 | WITH_SHARED_PCRE2 ?= OFF 118 | WITH_SHARED_LPEG ?= OFF 119 | WITH_SHARED_ZLIB ?= OFF 120 | 121 | OS := $(shell uname -s) 122 | ifeq ($(OS),Windows_NT) 123 | PREFIX ?= C:\Program Files\luvit 124 | else 125 | PREFIX ?= /usr/local 126 | endif 127 | 128 | BINPREFIX ?= $(PREFIX)/bin 129 | BUILD_PREFIX ?= build 130 | 131 | RMR = rm -rf 132 | RM = rm -f 133 | INSTALL = install -p 134 | LUVI = $(BUILD_PREFIX)/luvi 135 | TEST_BIN = ./test.bin 136 | 137 | CMAKE ?= cmake 138 | 139 | endif #### End of gmake #### 140 | !endif : ######################## 141 | 142 | ############################################################################### 143 | 144 | CONFIGURE_FLAGS = \ 145 | -Wno-dev \ 146 | -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) \ 147 | -DWITH_AMALG=$(WITH_AMALG) \ 148 | -DWITH_LUA_ENGINE=$(WITH_LUA_ENGINE) \ 149 | -DWithSharedLibluv=$(WITH_SHARED_LIBLUV) 150 | 151 | CONFIGURE_REGULAR_FLAGS = $(CONFIGURE_FLAGS) \ 152 | -DWithOpenSSL=$(WITH_OPENSSL) \ 153 | -DWithPCRE2=$(WITH_PCRE2) \ 154 | -DWithLPEG=$(WITH_LPEG) \ 155 | -DWithZLIB=$(WITH_ZLIB) \ 156 | -DWithOpenSSLASM=$(WITH_OPENSSL_ASM) \ 157 | -DWithSharedOpenSSL=$(WITH_SHARED_OPENSSL) \ 158 | -DWithSharedPCRE2=$(WITH_SHARED_PCRE2) \ 159 | -DWithSharedLPEG=$(WITH_SHARED_LPEG) \ 160 | -DWithSharedZLIB=$(WITH_SHARED_ZLIB) 161 | 162 | ifdef MAKEDIR: ######################## 163 | !ifdef MAKEDIR #### Start of nmake #### 164 | 165 | !ifdef GENERATOR 166 | CONFIGURE_FLAGS = "$(CONFIGURE_FLAGS) -G$(GENERATOR)" 167 | !endif 168 | 169 | !ifdef ARCH 170 | CONFIGURE_FLAGS = "$(CONFIGURE_FLAGS) -A$(ARCH)" 171 | !endif 172 | 173 | !else #### End of nmake #### 174 | else #### Start of gmake #### 175 | 176 | ifdef GENERATOR 177 | CONFIGURE_FLAGS += -G"$(GENERATOR)" 178 | endif 179 | 180 | ifdef NPROCS 181 | BUILD_OPTIONS += -j$(NPROCS) 182 | endif 183 | 184 | endif #### End of gmake #### 185 | !endif : ######################## 186 | 187 | ### Build targets 188 | 189 | default: luvi 190 | 191 | # This does the actual build and configures as default flavor is there is no build folder. 192 | luvi: $(BUILD_PREFIX) 193 | $(CMAKE) --build $(BUILD_PREFIX) --config $(CMAKE_BUILD_TYPE) -- $(BUILD_OPTIONS) $(EXTRA_OPTIONS) 194 | 195 | ### Directories and dependencies 196 | 197 | # Ensure the build prefix exists, ie. we have configured the build 198 | $(BUILD_PREFIX): 199 | @echo "Please run 'make tiny' or 'make regular' first to configure" 200 | 201 | # In case the user forgot to pull in submodules, grab them. 202 | deps/luv/CMakeLists.txt: 203 | git submodule update --init --recursive 204 | 205 | ### Configuration targets 206 | 207 | # Configure the build with minimal dependencies 208 | tiny: deps/luv/CMakeLists.txt 209 | $(CMAKE) -H. -B$(BUILD_PREFIX) $(CONFIGURE_FLAGS) $(EXTRA_CONFIGURE_FLAGS) 210 | 211 | # Configure the build with any libraries requested 212 | regular: deps/luv/CMakeLists.txt 213 | $(CMAKE) -H. -B$(BUILD_PREFIX) $(CONFIGURE_REGULAR_FLAGS) $(EXTRA_CONFIGURE_FLAGS) 214 | 215 | ### Phony targets 216 | 217 | .PHONY: clean test install uninstall reset 218 | clean: 219 | $(RMR) $(BUILD_PREFIX) test.bin 220 | 221 | install: luvi 222 | install -p $(BUILD_PREFIX)/luvi $(BINPREFIX)/luvi 223 | 224 | uninstall: 225 | $(RM) $(BINPREFIX)/luvi 226 | 227 | test: luvi 228 | $(RM) test.bin 229 | $(LUVI) samples/test.app -- 1 2 3 4 230 | $(LUVI) samples/test.app -o test.bin 231 | $(TEST_BIN) 1 2 3 4 232 | $(RM) test.bin 233 | 234 | reset: 235 | git submodule update --init --recursive && \ 236 | git clean -f -d && \ 237 | git checkout . 238 | -------------------------------------------------------------------------------- /src/lenv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The Luvit Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include "./luvi.h" 19 | 20 | #ifdef __APPLE__ 21 | #include 22 | #define environ (*_NSGetEnviron()) 23 | #elif !defined(_MSC_VER) 24 | extern char **environ; 25 | #endif 26 | 27 | static int lenv_keys(lua_State* L) { 28 | #ifndef _WIN32 29 | 30 | unsigned int i, size = 0; 31 | while (environ[size]) size++; 32 | 33 | lua_createtable(L, size, 0); 34 | 35 | for (i = 0; i < size; ++i) { 36 | const char* var = environ[i]; 37 | const char* s = strchr(var, '='); 38 | const size_t length = s ? s - var : strlen(var); 39 | 40 | lua_pushlstring(L, var, length); 41 | lua_rawseti(L, -2, i + 1); 42 | } 43 | 44 | #else // _WIN32 45 | int i = 0; 46 | int show_hidden = 0; 47 | WCHAR* p; 48 | WCHAR* environment = GetEnvironmentStringsW(); 49 | if (!environment) { 50 | return 0; 51 | } 52 | p = environment; 53 | if (lua_type(L, 1) == LUA_TBOOLEAN) { 54 | show_hidden = lua_toboolean(L, 1); 55 | } 56 | 57 | lua_newtable(L); 58 | while (*p) { 59 | char* utf8; 60 | size_t utf8_len; 61 | WCHAR* s; 62 | 63 | if (*p == L'=') { 64 | // If the key starts with '=' it is a hidden environment variable. 65 | if (show_hidden) { 66 | s = wcschr(p + 1, L'='); 67 | } 68 | else { 69 | // Skip it 70 | p += wcslen(p) + 1; 71 | continue; 72 | } 73 | } 74 | else { 75 | s = wcschr(p, L'='); 76 | } 77 | 78 | if (!s) { 79 | s = p + wcslen(p); 80 | } 81 | // Convert from WCHAR to UTF-8 encoded char 82 | utf8_len = WideCharToMultiByte(CP_UTF8, 0, p, s - p, NULL, 0, NULL, NULL); 83 | utf8 = malloc(utf8_len); 84 | WideCharToMultiByte(CP_UTF8, 0, p, s - p, utf8, utf8_len, NULL, NULL); 85 | 86 | lua_pushlstring(L, utf8, utf8_len); 87 | lua_rawseti(L, -2, ++i); 88 | 89 | free(utf8); 90 | 91 | p = s + wcslen(s) + 1; 92 | } 93 | FreeEnvironmentStringsW(environment); 94 | 95 | #endif 96 | 97 | return 1; 98 | } 99 | 100 | static int lenv_get(lua_State* L) { 101 | const char* name = luaL_checkstring(L, 1); 102 | #ifdef _WIN32 103 | char* value; 104 | WCHAR* wname; 105 | WCHAR* wvalue; 106 | size_t wsize, size, wname_size; 107 | 108 | // Convert UTF8 char* name to WCHAR* wname with null terminator 109 | wname_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); 110 | wname = malloc(wname_size * sizeof(WCHAR)); 111 | if (!wname) { 112 | return luaL_error(L, "Problem allocating memory for environment variable."); 113 | } 114 | MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, wname_size); 115 | 116 | // Check for the key 117 | wsize = GetEnvironmentVariableW(wname, NULL, 0); 118 | if (!wsize) { 119 | free(wname); 120 | return 0; 121 | } 122 | 123 | // Read the value 124 | wvalue = malloc(wsize * sizeof(WCHAR)); 125 | if (!wvalue) { 126 | free(wname); 127 | return luaL_error(L, "Problem allocating memory for environment variable."); 128 | } 129 | GetEnvironmentVariableW(wname, wvalue, wsize); 130 | 131 | // Convert WCHAR* wvalue to UTF8 char* value 132 | size = WideCharToMultiByte(CP_UTF8, 0, wvalue, -1, NULL, 0, NULL, NULL); 133 | value = malloc(size); 134 | if (!value) { 135 | free(wname); 136 | free(wvalue); 137 | return luaL_error(L, "Problem allocating memory for environment variable."); 138 | } 139 | WideCharToMultiByte(CP_UTF8, 0, wvalue, -1, value, size, NULL, NULL); 140 | 141 | lua_pushlstring(L, value, size - 1); 142 | 143 | free(wname); 144 | free(wvalue); 145 | free(value); 146 | 147 | #else 148 | lua_pushstring(L, getenv(name)); 149 | #endif 150 | return 1; 151 | } 152 | 153 | static int lenv_set(lua_State* L) { 154 | const char* name = luaL_checkstring(L, 1); 155 | const char* value = luaL_checkstring(L, 2); 156 | 157 | #ifdef _WIN32 158 | WCHAR* wname; 159 | WCHAR* wvalue; 160 | size_t wname_size, wvalue_size; 161 | int r; 162 | // Convert UTF8 char* name to WCHAR* wname with null terminator 163 | wname_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); 164 | wname = malloc(wname_size * sizeof(WCHAR)); 165 | if (!wname) return luaL_error(L, "Problem allocating memory for environment variable."); 166 | MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, wname_size); 167 | 168 | // Convert UTF8 char* value to WCHAR* wvalue with null terminator 169 | wvalue_size = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0); 170 | wvalue = malloc(wvalue_size * sizeof(WCHAR)); 171 | if (!wvalue) { 172 | free(wname); 173 | return luaL_error(L, "Problem allocating memory for environment variable."); 174 | } 175 | MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, wvalue_size); 176 | 177 | r = SetEnvironmentVariableW(wname, wvalue); 178 | 179 | free(wname); 180 | free(wvalue); 181 | if (r == 0) { 182 | return luaL_error(L, "Failed to set environment variable"); 183 | } 184 | #else 185 | int r = setenv(name, value, 1); 186 | if (r) { 187 | if (r == EINVAL) { 188 | return luaL_error(L, "EINVAL: Invalid name."); 189 | } 190 | return luaL_error(L, "ENOMEM: Insufficient memory to add a new variable to the environment."); 191 | } 192 | #endif 193 | return 0; 194 | } 195 | 196 | static int lenv_unset(lua_State* L) { 197 | const char* name = luaL_checkstring(L, 1); 198 | 199 | #ifdef __linux__ 200 | if (unsetenv(name)) { 201 | if (errno == EINVAL) 202 | return luaL_error(L, "EINVAL: name contained an '=' character"); 203 | return luaL_error(L, "unsetenv: Unknown error"); 204 | } 205 | #elif defined(_WIN32) 206 | WCHAR* wname; 207 | size_t wname_size; 208 | // Convert UTF8 char* name to WCHAR* wname with null terminator 209 | wname_size = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); 210 | wname = malloc(wname_size * sizeof(WCHAR)); 211 | if (!wname) { 212 | return luaL_error(L, "Problem allocating memory for environment variable."); 213 | } 214 | MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, wname_size); 215 | SetEnvironmentVariableW(wname, NULL); 216 | #else 217 | unsetenv(name); 218 | #endif 219 | return 0; 220 | } 221 | 222 | static const luaL_Reg lenv_f[] = { 223 | {"keys", lenv_keys}, 224 | {"get", lenv_get}, 225 | {"set", lenv_set}, 226 | {"unset", lenv_unset}, 227 | {NULL, NULL} 228 | }; 229 | 230 | LUALIB_API int luaopen_env(lua_State *L) { 231 | luaL_newlib(L, lenv_f); 232 | #if defined(_WIN32) && !defined(_XBOX_VER) 233 | lua_pushstring(L, "Windows"); 234 | #elif defined(__linux__) 235 | lua_pushstring(L, "Linux"); 236 | #elif defined(__MACH__) && defined(__APPLE__) 237 | lua_pushstring(L, "OSX"); 238 | #elif (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ 239 | defined(__NetBSD__) || defined(__OpenBSD__) || \ 240 | defined(__DragonFly__)) && !defined(__ORBIS__) 241 | lua_pushstring(L, "BSD"); 242 | #elif (defined(__sun__) && defined(__svr4__)) || defined(__CYGWIN__) 243 | lua_pushstring(L, "POSIX"); 244 | #else 245 | lua_pushstring(L, "Other"); 246 | #endif 247 | lua_setfield(L, -2, "os"); 248 | 249 | #if defined(__i386) || defined(__i386__) || defined(_M_IX86) 250 | lua_pushstring(L, "x86"); 251 | #elif defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) 252 | lua_pushstring(L, "x64"); 253 | #elif defined(__arm__) || defined(__arm) || defined(__ARM__) || defined(__ARM) 254 | lua_pushstring(L, "arm"); 255 | #elif defined(__aarch64__) 256 | lua_pushstring(L, "arm64"); 257 | #elif defined(__ppc__) || defined(__ppc) || defined(__PPC__) || defined(__PPC) || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) || defined(__POWERPC) || defined(_M_PPC) 258 | lua_pushstring(L, "ppc"); 259 | #elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || defined(__MIPS) 260 | lua_pushstring(L, "mips"); 261 | #else 262 | lua_pushstring(L, "other"); 263 | #endif 264 | lua_setfield(L, -2, "arch"); 265 | 266 | return 1; 267 | } 268 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | check-shared: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | fetch-depth: 0 12 | submodules: recursive 13 | 14 | - name: Ensure dependencies are installed 15 | run: | 16 | sudo apt-get update 17 | sudo apt-get install -y libssl-dev libpcre2-dev zlib1g-dev lua-luv-dev libluajit-5.1-dev luajit 18 | 19 | - name: Configure 20 | run: make regular WITH_SHARED_LIBLUV=ON WITH_SHARED_OPENSSL=ON WITH_SHARED_PCRE2=ON WITH_SHARED_ZLIB=ON 21 | 22 | - name: Build 23 | run: make 24 | 25 | - name: Test 26 | run: make test 27 | 28 | build-posix: 29 | runs-on: ${{ matrix.os }} 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | os: [macos-13, macos-latest] 34 | build_type: [tiny, regular] 35 | lua_engine: [LuaJIT, Lua] 36 | env: 37 | MAKEFLAGS: -j4 38 | NPROCS: 4 39 | CFLAGS: -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -Wno-nullability-completeness 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | with: 44 | fetch-depth: 0 45 | submodules: recursive 46 | 47 | - name: Configure 48 | run: | 49 | make ${{ matrix.build_type }} WITH_LUA_ENGINE=${{ matrix.lua_engine }} 50 | 51 | - name: Build 52 | run: make 53 | 54 | - name: Test 55 | run: make test 56 | 57 | - name: Fetch System Name 58 | run: | 59 | OS=$(uname -s) 60 | ARCH=$(uname -m) 61 | LUA=${{ matrix.lua_engine }} 62 | BUILD=${{ matrix.build_type }} 63 | 64 | echo "ARTIFACT=$OS-$ARCH-$(echo $LUA | awk '{print tolower($0)}')-$BUILD" >> $GITHUB_ENV 65 | 66 | - name: Rename Artifacts 67 | run: | 68 | cp build/luvi luvi-${{ env.ARTIFACT }} 69 | 70 | - name: Publish Artifacts 71 | uses: actions/upload-artifact@v4 72 | with: 73 | name: ${{ env.ARTIFACT }} 74 | path: | 75 | luvi-${{ env.ARTIFACT }} 76 | 77 | build-linux: 78 | runs-on: ubuntu-latest 79 | strategy: 80 | fail-fast: false 81 | matrix: 82 | build_type: [tiny, regular] 83 | lua_engine: [LuaJIT, Lua] 84 | arch: [x86_64, i686, aarch64] 85 | libc: [glibc, musl] 86 | env: 87 | MAKEFLAGS: -j4 88 | NPROCS: 4 89 | 90 | steps: 91 | - uses: actions/checkout@v4 92 | with: 93 | fetch-depth: 0 94 | submodules: recursive 95 | 96 | - name: Setup QEMU 97 | if: matrix.arch == 'aarch64' 98 | run: | 99 | sudo apt-get update 100 | sudo apt-get install -y qemu-user-static 101 | 102 | - name: Build 103 | run: | 104 | bash packaging/linux-run.sh ${{ matrix.libc }} ${{ matrix.arch }} ${{ matrix.build_type }} ${{ matrix.lua_engine }} 105 | 106 | - name: Fetch System Name 107 | run: | 108 | OS=$(uname -s) 109 | ARCH=${{ matrix.arch }} 110 | LUA=${{ matrix.lua_engine }} 111 | BUILD=${{ matrix.build_type }} 112 | if [ ${{ matrix.libc }} == "glibc" ]; then 113 | LIBC="" 114 | else 115 | LIBC=-${{ matrix.libc }} 116 | fi 117 | 118 | echo "ARTIFACT=$OS-$ARCH-${LUA,,}-$BUILD$LIBC" >> $GITHUB_ENV 119 | 120 | - name: Rename Artifacts 121 | run: | 122 | cp build/luvi luvi-${{ env.ARTIFACT }} 123 | 124 | - name: Publish Artifacts 125 | uses: actions/upload-artifact@v4 126 | with: 127 | name: ${{ env.ARTIFACT }} 128 | path: | 129 | luvi-${{ env.ARTIFACT }} 130 | 131 | build-mingw: 132 | runs-on: windows-latest 133 | strategy: 134 | fail-fast: false 135 | matrix: 136 | build_type: [tiny, regular] 137 | lua_engine: [LuaJIT, Lua] 138 | msystem: [mingw32, mingw64] 139 | env: 140 | MAKEFLAGS: -j4 141 | NPROCS: 4 142 | 143 | steps: 144 | - name: 'Setup MSYS2' 145 | uses: msys2/setup-msys2@v2 146 | with: 147 | msystem: ${{ matrix.msystem }} 148 | update: true 149 | pacboy: toolchain:p cmake:p 150 | install: git 151 | 152 | - name: Install NASM 153 | if: matrix.build_type == 'regular' 154 | run: | 155 | choco install nasm 156 | echo "C:\Program Files\NASM" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 157 | 158 | - uses: actions/checkout@v4 159 | with: 160 | fetch-depth: 0 161 | submodules: recursive 162 | 163 | - name: Configure 164 | shell: msys2 {0} 165 | run: mingw32-make ${{ matrix.build_type }} WITH_LUA_ENGINE=${{ matrix.lua_engine }} GENERATOR="MinGW Makefiles" 166 | 167 | - name: Build 168 | shell: msys2 {0} 169 | run: mingw32-make 170 | 171 | - name: Test 172 | shell: msys2 {0} 173 | run: mingw32-make test 174 | 175 | - name: Fetch System Name 176 | run: | 177 | $MSYSTEM="${{ matrix.msystem }}" 178 | $LUA="${{ matrix.lua_engine }}" 179 | $BUILD="${{ matrix.build_type }}" 180 | 181 | echo "ARTIFACT=Windows-$MSYSTEM-$($LUA.ToLower())-$BUILD" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 182 | 183 | - name: Rename Artifacts 184 | run: | 185 | cp build/luvi.exe luvi-${{ env.ARTIFACT }}.exe 186 | 187 | - name: Publish Artifacts 188 | uses: actions/upload-artifact@v4 189 | with: 190 | name: ${{ env.ARTIFACT }} 191 | path: luvi-${{ env.ARTIFACT }}.exe 192 | 193 | build-msvc: 194 | runs-on: windows-latest 195 | strategy: 196 | fail-fast: false 197 | matrix: 198 | build_type: [tiny, regular] 199 | lua_engine: [LuaJIT, Lua] 200 | arch: [amd64, x86] 201 | 202 | steps: 203 | - name: Setup MSVC Developer Prompt 204 | uses: ilammy/msvc-dev-cmd@v1 205 | with: 206 | arch: ${{ matrix.arch }} 207 | 208 | - name: Install NASM 209 | if: matrix.build_type == 'regular' 210 | run: | 211 | choco install nasm 212 | echo "C:\Program Files\NASM" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 213 | 214 | - uses: actions/checkout@v4 215 | with: 216 | fetch-depth: 0 217 | submodules: recursive 218 | 219 | - name: Configure 220 | run: nmake ${{ matrix.build_type }} WITH_LUA_ENGINE=${{ matrix.lua_engine }} 221 | 222 | - name: Build 223 | run: nmake 224 | 225 | - name: Test 226 | run: nmake test 227 | 228 | - name: Fetch System Name 229 | run: | 230 | $ARCH="${{ matrix.arch }}" 231 | $LUA="${{ matrix.lua_engine }}" 232 | $BUILD="${{ matrix.build_type }}" 233 | 234 | echo "ARTIFACT=Windows-$ARCH-$($LUA.ToLower())-$BUILD" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 235 | 236 | - name: Rename Artifacts 237 | run: | 238 | cp build/Release/luvi.exe build/luvi-${{ env.ARTIFACT }}.exe 239 | cp build/Release/luvi.lib build/luvi-${{ env.ARTIFACT }}.lib 240 | cp build/Release/luvi_renamed.lib build/luvi-${{ env.ARTIFACT }}_renamed.lib 241 | 242 | - name: Publish Artifacts 243 | uses: actions/upload-artifact@v4 244 | with: 245 | name: ${{ env.ARTIFACT }} 246 | path: | 247 | build/luvi-${{ env.ARTIFACT }}* 248 | 249 | package-source: 250 | runs-on: ubuntu-latest 251 | steps: 252 | - uses: actions/checkout@v4 253 | with: 254 | fetch-depth: 0 255 | submodules: recursive 256 | 257 | - name: Package Source 258 | run: | 259 | git describe --tags > VERSION 260 | touch deps/luv/deps/luajit/src/luajit_relver.txt 261 | 262 | tar -czvf /tmp/luvi-source.tar.gz --exclude-vcs --exclude 'git*' . 263 | 264 | - name: Publish Source 265 | uses: actions/upload-artifact@v4 266 | with: 267 | name: luvi-source 268 | path: /tmp/luvi-source.tar.gz 269 | 270 | publish: 271 | needs: [build-posix, build-linux, build-mingw, build-msvc, package-source, check-shared] 272 | runs-on: ubuntu-latest 273 | steps: 274 | - name: Download Artifacts 275 | uses: actions/download-artifact@v4 276 | with: 277 | path: download 278 | merge-multiple: true 279 | 280 | - name: Publish Artifact 281 | uses: actions/upload-artifact@v4 282 | with: 283 | name: artifacts 284 | path: download 285 | 286 | - name: Create Release 287 | if: startsWith(github.ref, 'refs/tags/') 288 | uses: softprops/action-gh-release@v2 289 | with: 290 | files: download/* -------------------------------------------------------------------------------- /samples/test.app/main.lua: -------------------------------------------------------------------------------- 1 | local env = require('env') 2 | local uv = require('uv') 3 | local bundle = require('luvi').bundle 4 | -- Register the utils lib as a module 5 | bundle.register("utils", "utils.lua") 6 | 7 | local utils = require('utils') 8 | local p = utils.prettyPrint 9 | local stdout = utils.stdout 10 | 11 | 12 | local function deepEqual(expected, actual, path) 13 | if expected == actual then 14 | return true 15 | end 16 | local prefix = path and (path .. ": ") or "" 17 | local expectedType = type(expected) 18 | local actualType = type(actual) 19 | if expectedType ~= actualType then 20 | return false, prefix .. "Expected type " .. expectedType .. " but found " .. actualType 21 | end 22 | if expectedType ~= "table" then 23 | return false, prefix .. "Expected " .. tostring(expected) .. " but found " .. tostring(actual) 24 | end 25 | local expectedLength = #expected 26 | local actualLength = #actual 27 | for key in pairs(expected) do 28 | if actual[key] == nil then 29 | return false, prefix .. "Missing table key " .. key 30 | end 31 | local newPath = path and (path .. '.' .. key) or key 32 | local same, message = deepEqual(expected[key], actual[key], newPath) 33 | if not same then 34 | return same, message 35 | end 36 | end 37 | if expectedLength ~= actualLength then 38 | return false, prefix .. "Expected table length " .. expectedLength .. " but found " .. actualLength 39 | end 40 | for key in pairs(actual) do 41 | if expected[key] == nil then 42 | return false, prefix .. "Unexpected table key " .. key 43 | end 44 | end 45 | return true 46 | end 47 | 48 | local env = setmetatable({}, { 49 | __pairs = function (table) 50 | local keys = env.keys(true) 51 | local index = 0 52 | return function () 53 | index = index + 1 54 | local name = keys[index] 55 | if name then 56 | return name, table[name] 57 | end 58 | end 59 | end, 60 | __index = function (_, name) 61 | local value = env.get(name) 62 | return value 63 | end, 64 | __newindex = function (_, name, value) 65 | if value then 66 | env.set(name, value) 67 | else 68 | env.unset(name) 69 | end 70 | end 71 | }) 72 | 73 | -- Make sure unicode can round-trip in unicode environment variable names and values. 74 | local r1 = "На берегу пустынных волн" 75 | local r2 = "Стоял он, дум великих полн" 76 | env[r1] = r2 77 | assert(env[r1] == r2) 78 | p(env) 79 | p{ 80 | args=args, 81 | bundle=bundle 82 | } 83 | p{ 84 | [""] = bundle.stat(""), 85 | ["add"] = bundle.stat("add"), 86 | ["main.lua"] = bundle.stat("main.lua"), 87 | ["fake"] = bundle.stat("fake"), 88 | } 89 | p(bundle.readfile("greetings.txt")) 90 | 91 | 92 | print("Testing bundle.stat") 93 | local rootStat = bundle.stat("") 94 | assert(rootStat.type == "directory") 95 | local addStat = bundle.stat("add") 96 | assert(addStat.type == "directory") 97 | local mainStat = bundle.stat("main.lua") 98 | assert(mainStat.type == "file") 99 | assert(mainStat.size > 3000) 100 | local tests = { 101 | "", rootStat, 102 | "/", rootStat, 103 | "/a/../", rootStat, 104 | "add", addStat, 105 | "add/", addStat, 106 | "/add/", addStat, 107 | "foo/../add/", addStat, 108 | "main.lua", mainStat, 109 | "/main.lua", mainStat, 110 | } 111 | for i = 1, #tests, 2 do 112 | local path = tests[i] 113 | local expected = tests[i + 1] 114 | local actual = bundle.stat(path) 115 | p(path, actual) 116 | assert(deepEqual(expected, actual), "ERROR: stat(" .. path .. ")") 117 | end 118 | 119 | print("Testing bundle.readdir") 120 | local rootTree = { "add", "greetings.txt", "main.lua", "sonnet-133.txt", "utils.lua" } 121 | local addTree = { "a.lua", "b.lua", "init.lua" } 122 | tests = { 123 | "", rootTree, 124 | "/", rootTree, 125 | "/a/../", rootTree, 126 | "add", addTree, 127 | "add/", addTree, 128 | "/add/", addTree, 129 | "foo/../add/", addTree, 130 | } 131 | table.sort(rootTree) 132 | table.sort(addTree) 133 | for i = 1, #tests, 2 do 134 | local path = tests[i] 135 | local expected = tests[i + 1] 136 | local actual = bundle.readdir(path) 137 | table.sort(actual) 138 | p(path, actual) 139 | assert(deepEqual(expected, actual), "ERROR: readdir(" .. path .. ")") 140 | end 141 | 142 | if _VERSION=="Lua 5.2" then 143 | print("Testing for lua 5.2 extensions") 144 | local thread, ismain = coroutine.running() 145 | p(thread, ismain) 146 | assert(thread) 147 | assert(ismain) 148 | end 149 | 150 | print("Testing miniz") 151 | local miniz = require('miniz') 152 | p(miniz) 153 | 154 | local writer = miniz.new_writer() 155 | 156 | local reader = miniz.new_reader(uv.exepath()) or miniz.new_reader("samples/test.zip") 157 | if reader then 158 | p { 159 | reader=reader, 160 | offset=reader:get_offset(), 161 | } 162 | for i = 1, reader:get_num_files() do 163 | writer:add_from_zip(reader, i) 164 | end 165 | end 166 | 167 | writer:add("README.md", "# A Readme\n\nThis is neat?", 9) 168 | writer:add("data.json", '{"name":"Tim","age":32}\n', 9) 169 | writer:add("a/big/file.dat", string.rep("12345\n", 10000), 9) 170 | writer:add("main.lua", 'print(require("luvi").version)', 9) 171 | 172 | p("zip bytes", #writer:finalize()) 173 | 174 | do 175 | print("miniz zlib compression - full data") 176 | local original = string.rep(bundle.readfile("sonnet-133.txt"), 1000) 177 | local deflator = miniz.new_deflator(9) 178 | local deflated, err, part = deflator:deflate(original, "finish") 179 | p("Compressed", #(deflated or part or "")) 180 | deflated = assert(deflated, err) 181 | local inflator = miniz.new_inflator() 182 | local inflated, err, part = inflator:inflate(deflated) 183 | p("Decompressed", #(inflated or part or "")) 184 | inflated = assert(inflated, err) 185 | assert(inflated == original, "inflated data doesn't match original") 186 | end 187 | 188 | do 189 | print("miniz zlib compression - partial data stream") 190 | local original_full = bundle.readfile("sonnet-133.txt") 191 | local original_parts = { } 192 | for part in original_full:gmatch((".?"):rep(64)) do 193 | original_parts[#original_parts+1] = part 194 | end 195 | local deflator = miniz.new_deflator(9) 196 | local inflator = miniz.new_inflator() 197 | for i, part in ipairs(original_parts) do 198 | p("part", part) 199 | local deflated, err, partial = deflator:deflate(part, 200 | i == #original_parts and "finish" or "sync") 201 | p("compressed", deflated, partial) 202 | deflated = assert(not err, err) and (deflated or partial) 203 | local inflated, err, partial = inflator:inflate(deflated, 204 | i == #original_parts and "finish" or "sync") 205 | p("decompressed", inflated, partial) 206 | inflated = assert(not err, err) and (inflated or partial) 207 | 208 | assert(inflated == part, "inflated data doesn't match original") 209 | end 210 | end 211 | 212 | do 213 | print("miniz zlib compression - no stream") 214 | local original = string.rep(bundle.readfile("sonnet-133.txt"), 1000) 215 | local compressed = assert(miniz.compress(original)) 216 | local uncompressed = assert(miniz.uncompress(compressed, #original)) 217 | assert(uncompressed == original, "inflated data doesn't match original") 218 | end 219 | 220 | local options = require('luvi').options 221 | 222 | if options.zlib then 223 | local zlib = require("zlib") 224 | print("Testing zlib") 225 | p("zlib version", zlib.version()) 226 | local tozblob = bundle.readfile("sonnet-133.txt") 227 | local defstreamf = zlib.deflate() 228 | local infstreamf = zlib.inflate() 229 | local deflated, def_eof, def_bytes_in, def_bytes_out = defstreamf(tozblob, 'finish') 230 | assert(def_eof, "deflate not finished?") 231 | assert(def_bytes_in > def_bytes_out, "deflate failed") 232 | local inflated, inf_eof, inf_bytes_in, inf_bytes_out = infstreamf(deflated) 233 | assert(inf_eof, "inflate not finished?") 234 | assert(inf_bytes_in < inf_bytes_out, "inflate failed") 235 | assert(inf_bytes_in == def_bytes_out, "inflate byte in count != deflate byte out count") 236 | assert(def_bytes_in == inf_bytes_out, "inflate byte out count != deflate byte in count") 237 | assert(inflated == tozblob, "inflated data doesn't match original") 238 | end 239 | 240 | if options.rex then 241 | local rex = require('rex') 242 | local string = "The red frog sits on the blue box in the green well." 243 | local colors = {} 244 | for color in rex.gmatch(string, "(red|blue|green)") do 245 | colors[#colors + 1] = color 246 | end 247 | p(colors) 248 | assert(#colors == 3) 249 | end 250 | 251 | print("Testing utf8") 252 | 253 | local emoji = "🎃" 254 | assert(utf8.len(emoji) == 1) 255 | assert(utf8.char(0x1F383) == emoji) 256 | assert(emoji:match(utf8.charpattern) == emoji) 257 | assert(utf8.offset(emoji, 1) == 1) 258 | 259 | print("All tests pass!\n") 260 | 261 | require('uv').run() 262 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # luvi 2 | 3 | [![Build Status](https://github.com/luvit/luvi/actions/workflows/ci.yml/badge.svg)](https://github.com/luvit/luvi/actions/workflows/ci.yml) 4 | 5 | A project in-between [luv][] and [luvit][]. 6 | 7 | The goal of this is to make building [luvit][] and [derivatives][] much easier. 8 | 9 | ## Workflow 10 | 11 | Luvi has a somewhat unique, but very easy workflow for creating self-contained binaries on systems that don't have a 12 | compiler. 13 | 14 | ```sh 15 | # Make a folder 16 | git init myapp 17 | # Write the app 18 | vim myapp/main.lua 19 | # Run the app 20 | luvi myapp 21 | # Build the binary when done 22 | luvi myapp -o mybinary 23 | # Build the binary with compiled Lua bytecode 24 | luvi myapp -o mybinary --compile 25 | # Build the binary with compiled and stripped Lua bytecode 26 | luvi myapp -o mybinary --strip 27 | # Run the new self-contained binary 28 | ./mybinary 29 | # Deploy / Publish / Profit! 30 | ``` 31 | 32 | ## Main API 33 | 34 | Your `main.lua` is run in either a mostly stock [lua][] or [luajit][] environment with a few extra things added. Luajit 35 | is built with `LUAJIT_ENABLE_LUA52COMPAT` features turned on, and all luajit [extensions][] are available. Lua is built 36 | with the `bit` library included, for parity with luajit. 37 | 38 | ### Libuv is baked in 39 | 40 | The "uv" module contains bindings to [libuv][] as defined in the [luv][] project. Simply `require("uv")` to access it. 41 | The "uv" module is also provided under the name "luv" for parity with luarocks, so `require("luv")` will also work. 42 | 43 | Use this for file I/O, network I/O, timers, or various interfaces with the operating system. This lets you write fast 44 | non-blocking network servers or frameworks. The APIs in [luv][] mirror what's in [libuv][] allowing you to add 45 | whatever API sugar you want on top be it callbacks, coroutines, or whatever. 46 | 47 | Just be sure to call `uv.run()` and the end of your script to start the event loop if you want to actually wait for any 48 | events to happen. 49 | 50 | [extensions]: http://luajit.org/extensions.html 51 | [lua]: https://www.lua.org/ 52 | [luajit]: https://luajit.org/ 53 | [libuv]: https://github.com/joyent/libuv 54 | [luv]: https://github.com/luvit/luv 55 | [luvit]: https://luvit.io/ 56 | [derivatives]: http://virgoagent.com/ 57 | 58 | ```lua 59 | local uv = require('uv') 60 | 61 | local function setTimeout(timeout, callback) 62 | local timer = uv.new_timer() 63 | local function ontimeout() 64 | print("ontimeout", self) 65 | uv.timer_stop(timer) 66 | uv.close(timer) 67 | callback(self) 68 | end 69 | uv.timer_start(timer, timeout, 0, ontimeout) 70 | return timer 71 | end 72 | 73 | setTimeout(1000, function () 74 | print("This happens later") 75 | end) 76 | 77 | print("This happens first") 78 | 79 | -- This blocks till the timer is done 80 | uv.run() 81 | ``` 82 | 83 | ### Integration with C's main function 84 | 85 | The raw `argc` and `argv` from C side is exposed as a **zero** indexed lua table of strings at `args`. The `0`-th 86 | element is generally the name of the binary that was executed. 87 | 88 | ```lua 89 | print("Your arguments were") 90 | for i = 0, #args do 91 | print(i, args[i]) 92 | end 93 | ``` 94 | 95 | The "env" module provides read/write access to your local environment variables via `env.keys`, `env.get`, `env.put`, 96 | `env.set`, and `env.unset`. 97 | 98 | If you return an integer from `main.lua` it will be your program's exit code. 99 | 100 | ### Bundle I/O 101 | 102 | If you're running from a unzipped folder on disk or a zipped bundle appended to the binary, the I/O to read from this 103 | is the same. This is exposed as the `bundle` property in the "luvi" module. 104 | 105 | ```lua 106 | local bundle = require("luvi").bundle 107 | local files = bundle.readdir("") 108 | ``` 109 | 110 | #### bundle.stat(path) 111 | 112 | Load metadata about a file in the bundle. This includes `type` ("file" or "directory"), `mtime` (in ms since epoch), 113 | and `size` (in bytes). 114 | 115 | If the file doesn't exist, it returns `nil`. 116 | 117 | #### bundle.readdir(path) 118 | 119 | Read a directory. Returns a list of filenames in the directory. 120 | 121 | If the directory doesn't exist, it return `nil`. 122 | 123 | #### bundle.readfile(path) 124 | 125 | Read the contents of a file. Returns a string if the file exists and `nil` if it doesn't. 126 | 127 | ## Building from Source 128 | 129 | We maintain several [binary releases of luvi](https://github.com/luvit/luvi/releases) to ease bootstrapping of lit and 130 | luvit apps. 131 | 132 | The following platforms are actively supported and tested by the CI system: 133 | 134 | - Windows >= 10 (x86_64 / i386) 135 | - Linux >= 3.10, glibc >= 2.17 OR musl >= 1.0 (x86_64 / i386 / aarch64) 136 | - Debian 8+ 137 | - Ubuntu 13.10+ 138 | - Fedora 19+ 139 | - OSX 13+ (x86_64 / aarch64) 140 | 141 | The following platforms are supported but not actively tested by the CI system: 142 | 143 | - Windows >= 8 (x86_64 / i386) 144 | - OSX 11+ (x86_64 / aarch64) 145 | - FreeBSD 12+ 146 | 147 | Platform support is primarily based on libuv's [platform support](https://github.com/libuv/libuv/blob/v1.x/SUPPORTED_PLATFORMS.md). 148 | 149 | Architecture support is primarily based on luajit's [platform support](https://luajit.org/luajit.html). 150 | 151 | ### Build Dependencies 152 | 153 | If you want to not wait for pre-built binaries and dive right in, building is based on CMake and is pretty simple. 154 | 155 | - Git 156 | - CMake 157 | - A C Compiler (Visual Studio 15+ OR MinGW on Windows) 158 | - Perl (required for OpenSSL) 159 | - NASM (required for OpenSSL ASM optimizations on Windows) 160 | 161 | First clone this repo recursively. 162 | 163 | ```sh 164 | git clone --recursive https://github.com/luvit/luvi.git 165 | ``` 166 | 167 | > [!IMPORTANT] 168 | > If you're on windows, for all following steps you will need to be in a [Visual Studio Command Prompt](https://learn.microsoft.com/en-us/visualstudio/ide/reference/command-prompt-powershell?view=vs-2022). 169 | > 170 | > You will need to replace `make` with `nmake` in the following commands. 171 | 172 | Then enter the directory and run the makefile inside it, which will assume you have all the dependencies installed, 173 | primarily CMake. 174 | 175 | Prior to building luvi you must configure the version of luvi that you want to build. Currently there are two versions: 176 | 177 | - `tiny`: only the necessities, includes only Lua, Libuv, miniz, and minimal luvi modules. 178 | - `regular`: the normal luvit experience, includes OpenSSL, LPeg, and lrexlib. 179 | 180 | ```sh 181 | cd luvi 182 | make regular 183 | make 184 | make test 185 | ``` 186 | 187 | When that's done you should have a luvi binary in `build/luvi`. 188 | 189 | ```sh 190 | $ ls -lh build/luvi 191 | -rwxr-xr-x 1 tim tim 948K Nov 20 16:39 build/luvi 192 | ``` 193 | 194 | ## Usage 195 | 196 | Run it to see usage information: 197 | 198 | ```sh 199 | $ luvi -h 200 | 201 | Usage: luvi bundle+ [options] [-- extra args] 202 | 203 | bundle Path to directory or zip file containing bundle source. 204 | `bundle` can be specified multiple times to layer bundles 205 | on top of each other. 206 | --version Show luvi version and compiled in options. 207 | --output target Build a luvi app by zipping the bundle and inserting luvi. 208 | --main path Specify a custom main bundle path (normally main.lua) 209 | --compile Compile Lua code into bytecode before bundling. 210 | --strip Compile Lua code and strip debug info. 211 | --force Ignore errors when compiling Lua code. 212 | --help Show this help file. 213 | -- All args after this go to the luvi app itself. 214 | 215 | Examples: 216 | 217 | # Run an app from disk, but pass in arguments 218 | luvi path/to/app -- app args 219 | 220 | # Run from a app zip 221 | luvi path/to/app.zip 222 | 223 | # Run an app that layers on top of luvit 224 | luvi path/to/app path/to/luvit 225 | 226 | # Bundle an app with luvi to create standalone 227 | luvi path/to/app -o target 228 | ./target some args 229 | 230 | # Run unit tests for a luvi app using custom main 231 | luvi path/to/app -m tests/run.lua 232 | ``` 233 | 234 | You can run the sample repl app by doing: 235 | 236 | ```sh 237 | build/luvi samples/repl.app 238 | ``` 239 | 240 | Ot the test suite with: 241 | 242 | ```sh 243 | build/luvi samples/test.app 244 | ``` 245 | 246 | ## CMake Flags 247 | 248 | You can use the predefined makefile targets if you want or use cmake directly 249 | for more control. 250 | 251 | ```text 252 | WithOpenSSL (Default: OFF) - Enable OpenSSL Support 253 | WithOpenSSLASM (Default: OFF) - Enable OpenSSL Assembly Optimizations 254 | WithSharedOpenSSL (Default: ON) - Use System OpenSSL Library 255 | Otherwise use static library 256 | 257 | OPENSSL_ROOT_DIR - Override the OpenSSL Root Directory 258 | OPENSSL_INCLUDE_DIR - Override the OpenSSL Include Directory 259 | OPENSSL_LIBRARIES - Override the OpenSSL Library Path 260 | ``` 261 | 262 | Example (Static OpenSSL): 263 | 264 | ```sh 265 | cmake \ 266 | -DWithOpenSSL=ON \ 267 | -DWithSharedOpenSSL=OFF \ 268 | .. 269 | ``` 270 | 271 | Example (Shared OpenSSL): 272 | 273 | ```sh 274 | cmake \ 275 | -DWithSharedOpenSSL=ON \ 276 | -DWithOpenSSL=ON \ 277 | -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl \ 278 | -DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl/include \ 279 | -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib \ 280 | .. 281 | ``` 282 | -------------------------------------------------------------------------------- /samples/winsvc.app/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Copyright 2015 The Luvit Authors. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS-IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | --]] 18 | 19 | local options = require('luvi').options 20 | if not options.winsvc then 21 | print("not has winsvc feature, skip"); 22 | return 23 | end 24 | local table = require('table') 25 | local winsvc = require('winsvc') 26 | local winsvcaux = require('winsvcaux') 27 | local uv = require('uv') 28 | 29 | local svcname = 'Test Lua Service' 30 | local gSvcStatus = {} 31 | local gSvcStatusHandle 32 | local gRunning = true 33 | 34 | local function ReportSvcStatus(dwCurrentState, dwWin32ExitCode, dwWaitHint) 35 | local dwCheckPoint = 1 36 | 37 | -- Fill in the SERVICE_STATUS structure. 38 | 39 | gSvcStatus.dwCurrentState = dwCurrentState 40 | gSvcStatus.dwWin32ExitCode = dwWin32ExitCode 41 | gSvcStatus.dwWaitHint = dwWaitHint 42 | 43 | if dwCurrentState == winsvc.SERVICE_START_PENDING then 44 | gSvcStatus.dwControlsAccepted = 0 45 | else 46 | gSvcStatus.dwControlsAccepted = winsvc.SERVICE_ACCEPT_STOP 47 | end 48 | 49 | if dwCurrentState == winsvc.SERVICE_RUNNING or 50 | dwCurrentState == winsvc.SERVICE_STOPPED then 51 | gSvcStatus.dwCheckPoint = 0 52 | else 53 | dwCheckPoint = dwCheckPoint + 1 54 | gSvcStatus.dwCheckPoint = dwCheckPoint 55 | end 56 | 57 | -- Report the status of the service to the SCM. 58 | winsvc.SetServiceStatus(gSvcStatusHandle, gSvcStatus) 59 | end 60 | 61 | 62 | local function SvcHandler(dwControl, dwEventType, lpEventData, lpContext) 63 | -- Handle the requested control code. 64 | 65 | if dwControl == winsvc.SERVICE_CONTROL_STOP then 66 | ReportSvcStatus(winsvc.SERVICE_STOP_PENDING, winsvc.NO_ERROR, 0) 67 | 68 | -- Signal the service to stop. 69 | 70 | gRunning = false 71 | ReportSvcStatus(gSvcStatus.dwCurrentState, winsvc.NO_ERROR, 0) 72 | 73 | return winsvc.NO_ERROR 74 | elseif dwControl == winsvc.SERVICE_CONTROL_INTERROGATE then 75 | return winsvc.NO_ERROR 76 | else 77 | return winsvc.ERROR_CALL_NOT_IMPLEMENTED 78 | end 79 | end 80 | 81 | 82 | local function SvcReportEvent(...) 83 | -- Log that somewhere 84 | local args = {...} 85 | local s 86 | if type(args) == 'string' then 87 | s = args 88 | else 89 | s = table.concat(args, ' ') 90 | end 91 | print(s) 92 | s = s .. '\n' 93 | -- synchronous file i/o for the logging so it can work even outside the event loop 94 | local fd, err = uv.fs_open('logfile.txt', 'a', tonumber('644', 8)) 95 | if not err then 96 | uv.fs_write(fd, s, -1) 97 | uv.fs_close(fd) 98 | end 99 | end 100 | 101 | 102 | local function SvcInit(args, context) 103 | -- TO_DO: Declare and set any required variables. 104 | -- Be sure to periodically call ReportSvcStatus() with 105 | -- SERVICE_START_PENDING. If initialization fails, call 106 | -- ReportSvcStatus with SERVICE_STOPPED. 107 | 108 | -- Create an event. The control handler function, SvcCtrlHandler, 109 | -- signals this event when it receives the stop control code. 110 | 111 | ReportSvcStatus(winsvc.SERVICE_RUNNING, winsvc.NO_ERROR, 0) 112 | 113 | -- TO_DO: Setup Serive Work To Be done 114 | 115 | local timer = uv.new_timer() 116 | uv.timer_start(timer, 0, 2000, function() 117 | if gRunning then 118 | SvcReportEvent('Just waiting...') 119 | uv.timer_again(timer) 120 | else 121 | uv.timer_stop(timer) 122 | uv.close(timer) 123 | ReportSvcStatus(winsvc.SERVICE_STOPPED, winsvc.NO_ERROR, 0); 124 | winsvc.EndService(context) 125 | end 126 | end) 127 | end 128 | 129 | 130 | local function SvcMain(args, context) 131 | gSvcStatusHandle = winsvc.GetStatusHandleFromContext(context) 132 | -- These SERVICE_STATUS members remain as set here 133 | 134 | gSvcStatus.dwServiceType = winsvc.SERVICE_WIN32_OWN_PROCESS 135 | gSvcStatus.dwServiceSpecificExitCode = 0 136 | 137 | -- Report initial status to the SCM 138 | 139 | ReportSvcStatus(winsvc.SERVICE_START_PENDING, winsvc.NO_ERROR, 3000) 140 | 141 | -- Perform service-specific initialization and work. 142 | 143 | SvcInit(args, context) 144 | end 145 | 146 | 147 | local function SvcInstall() 148 | local svcPath, err = winsvcaux.GetModuleFileName() 149 | if svcPath == nil then 150 | SvcReportEvent('Cannot install service, service path unobtainable', winsvcaux.GetErrorString(err)) 151 | return 152 | end 153 | 154 | -- Get a handle to the SCM database 155 | local schSCManager, err = winsvc.OpenSCManager(nil, nil, winsvc.SC_MANAGER_ALL_ACCESS) 156 | if schSCManager == nil then 157 | SvcReportEvent('OpenSCManager failed', winsvcaux.GetErrorString(err)) 158 | return 159 | end 160 | 161 | -- Create the Service 162 | local schService, tagid, err = winsvc.CreateService( 163 | schSCManager, 164 | svcname, 165 | svcname, 166 | winsvc.SERVICE_ALL_ACCESS, 167 | winsvc.SERVICE_WIN32_OWN_PROCESS, 168 | winsvc.SERVICE_DEMAND_START, 169 | winsvc.SERVICE_ERROR_NORMAL, 170 | svcPath, 171 | nil, 172 | nil, 173 | nil, 174 | nil) 175 | 176 | if schService == nil then 177 | SvcReportEvent('CreateService failed', winsvcaux.GetErrorString(err)) 178 | winsvc.CloseServiceHandle(schSCManager) 179 | return 180 | end 181 | 182 | -- Describe the service 183 | winsvc.ChangeServiceConfig2(schService, winsvc.SERVICE_CONFIG_DESCRIPTION, {lpDescription = "This is a test service written in Luvi/Lua"}) 184 | -- Set the service to restart on failure in 15 seconds 185 | winsvc.ChangeServiceConfig2(schService, winsvc.SERVICE_CONFIG_FAILURE_ACTIONS, 186 | {dwResetPeriod = 0, lpsaActions = { 187 | {Delay = 15000, Type = winsvc.SC_ACTION_RESTART} 188 | }}) 189 | 190 | SvcReportEvent('Service installed successfully') 191 | 192 | winsvc.CloseServiceHandle(schService) 193 | winsvc.CloseServiceHandle(schSCManager) 194 | 195 | end 196 | 197 | 198 | local function SvcDelete() 199 | -- Get a handle to the SCM database 200 | local schSCManager, err = winsvc.OpenSCManager(nil, nil, winsvc.SC_MANAGER_ALL_ACCESS) 201 | if schSCManager == nil then 202 | SvcReportEvent('OpenSCManager failed', winsvcaux.GetErrorString(err)) 203 | return 204 | end 205 | 206 | -- Open the Service 207 | local schService, err = winsvc.OpenService( 208 | schSCManager, 209 | svcname, 210 | winsvc.DELETE) 211 | 212 | if schService == nil then 213 | SvcReportEvent('OpenService failed', winsvcaux.GetErrorString(err)) 214 | winsvc.CloseServiceHandle(schSCManager) 215 | return 216 | end 217 | 218 | local delsuccess, err = winsvc.DeleteService(schService) 219 | if not delsuccess then 220 | SvcReportEvent('DeleteService failed', winsvcaux.GetErrorString(err)) 221 | else 222 | SvcReportEvent('DeleteService succeeded') 223 | end 224 | 225 | winsvc.CloseServiceHandle(schService) 226 | winsvc.CloseServiceHandle(schSCManager) 227 | 228 | end 229 | 230 | 231 | 232 | local function SvcStart() 233 | -- Get a handle to the SCM database 234 | local schSCManager, err = winsvc.OpenSCManager(nil, nil, winsvc.SC_MANAGER_ALL_ACCESS) 235 | if schSCManager == nil then 236 | SvcReportEvent('OpenSCManager failed', winsvcaux.GetErrorString(err)) 237 | return 238 | end 239 | 240 | -- Open the Service 241 | local schService, err = winsvc.OpenService( 242 | schSCManager, 243 | svcname, 244 | winsvc.SERVICE_START) 245 | 246 | if schService == nil then 247 | SvcReportEvent('OpenService failed', winsvcaux.GetErrorString(err)) 248 | winsvc.CloseServiceHandle(schSCManager) 249 | return 250 | end 251 | 252 | local startsuccess, err = winsvc.StartService(schService, nil) 253 | if not startsuccess then 254 | SvcReportEvent('StartService failed', winsvcaux.GetErrorString(err)) 255 | else 256 | SvcReportEvent('StartService succeeded') 257 | end 258 | 259 | winsvc.CloseServiceHandle(schService) 260 | winsvc.CloseServiceHandle(schSCManager) 261 | 262 | end 263 | 264 | 265 | 266 | local function SvcStop() 267 | -- Get a handle to the SCM database 268 | local schSCManager, err = winsvc.OpenSCManager(nil, nil, winsvc.SC_MANAGER_ALL_ACCESS) 269 | if schSCManager == nil then 270 | SvcReportEvent('OpenSCManager failed', winsvcaux.GetErrorString(err)) 271 | return 272 | end 273 | 274 | -- Open the Service 275 | local schService, err = winsvc.OpenService( 276 | schSCManager, 277 | svcname, 278 | winsvc.SERVICE_STOP) 279 | 280 | if schService == nil then 281 | SvcReportEvent('OpenService failed', winsvcaux.GetErrorString(err)) 282 | winsvc.CloseServiceHandle(schSCManager) 283 | return 284 | end 285 | 286 | -- Stop the Service 287 | local success, status, err = winsvc.ControlService(schService, winsvc.SERVICE_CONTROL_STOP, nil) 288 | if not success then 289 | SvcReportEvent('ControlService stop failed', winsvcaux.GetErrorString(err)) 290 | else 291 | local i, v, fstatus 292 | fstatus = {} 293 | for i, v in pairs(status) do 294 | table.insert(fstatus, i .. ': ' .. v) 295 | end 296 | SvcReportEvent('ControlService stop succeeded, status:', table.concat(fstatus, ', ')) 297 | end 298 | 299 | winsvc.CloseServiceHandle(schService) 300 | winsvc.CloseServiceHandle(schSCManager) 301 | 302 | end 303 | 304 | 305 | 306 | -- Main Code 307 | if args[1] == 'install' then 308 | SvcInstall() 309 | return 310 | elseif args[1] == 'delete' then 311 | SvcDelete() 312 | return 313 | elseif args[1] == 'start' then 314 | SvcStart() 315 | return 316 | elseif args[1] == 'stop' then 317 | SvcStop() 318 | return 319 | end 320 | 321 | local DispatchTable = {} 322 | DispatchTable[svcname] = { SvcMain, SvcHandler }; 323 | 324 | local ret, err = winsvc.SpawnServiceCtrlDispatcher(DispatchTable, function(success, err) 325 | if success then 326 | SvcReportEvent('Service Control Dispatcher returned after threads exited ok') 327 | else 328 | SvcReportEvent('Service Control Dispatcher returned with err', winsvcaux.GetErrorString(err)) 329 | end 330 | end, function(err) 331 | SvcReportEvent('A Service function returned with err', err) 332 | end) 333 | 334 | if ret then 335 | SvcReportEvent('SpawnServiceCtrlDispatcher Succeeded') 336 | else 337 | SvcReportEvent('SpawnServiceCtrlDispatcher Failed', winsvcaux.GetErrorString(err)) 338 | end 339 | 340 | uv.run('default') 341 | -------------------------------------------------------------------------------- /samples/repl.app/utils.lua: -------------------------------------------------------------------------------- 1 | local uv = require('uv') 2 | local env = require('env') 3 | 4 | local unpack = unpack or table.unpack 5 | local prettyPrint, dump, strip, color, colorize, loadColors 6 | local theme = {} 7 | local useColors = false 8 | local defaultTheme 9 | 10 | local stdout, stdin, stderr, width 11 | 12 | local quote, quote2, dquote, dquote2, obracket, cbracket, obrace, cbrace, comma, equals, controls 13 | 14 | local themes = { 15 | 16 | -- nice color theme using 16 ansi colors 17 | [16] = { 18 | property = "0;37", -- white 19 | sep = "1;30", -- bright-black 20 | braces = "1;30", -- bright-black 21 | 22 | ["nil"] = "1;30", -- bright-black 23 | boolean = "0;33", -- yellow 24 | number = "1;33", -- bright-yellow 25 | string = "0;32", -- green 26 | quotes = "1;32", -- bright-green 27 | escape = "1;32", -- bright-green 28 | ["function"] = "0;35", -- purple 29 | thread = "1;35", -- bright-purple 30 | 31 | table = "1;34", -- bright blue 32 | userdata = "1;36", -- bright cyan 33 | cdata = "0;36", -- cyan 34 | 35 | err = "1;31", -- bright red 36 | success = "1;33;42", -- bright-yellow on green 37 | failure = "1;33;41", -- bright-yellow on red 38 | highlight = "1;36;44", -- bright-cyan on blue 39 | }, 40 | 41 | -- nice color theme using ansi 256-mode colors 42 | [256] = { 43 | property = "38;5;253", 44 | braces = "38;5;247", 45 | sep = "38;5;240", 46 | 47 | ["nil"] = "38;5;244", 48 | boolean = "38;5;220", -- yellow-orange 49 | number = "38;5;202", -- orange 50 | string = "38;5;34", -- darker green 51 | quotes = "38;5;40", -- green 52 | escape = "38;5;46", -- bright green 53 | ["function"] = "38;5;129", -- purple 54 | thread = "38;5;199", -- pink 55 | 56 | table = "38;5;27", -- blue 57 | userdata = "38;5;39", -- blue2 58 | cdata = "38;5;69", -- teal 59 | 60 | err = "38;5;196", -- bright red 61 | success = "38;5;120;48;5;22", -- bright green on dark green 62 | failure = "38;5;215;48;5;52", -- bright red on dark red 63 | highlight = "38;5;45;48;5;236", -- bright teal on dark grey 64 | }, 65 | 66 | } 67 | 68 | local special = { 69 | [7] = 'a', 70 | [8] = 'b', 71 | [9] = 't', 72 | [10] = 'n', 73 | [11] = 'v', 74 | [12] = 'f', 75 | [13] = 'r' 76 | } 77 | 78 | function loadColors(index) 79 | if index == nil then index = defaultTheme end 80 | 81 | -- Remove the old theme 82 | for key in pairs(theme) do 83 | theme[key] = nil 84 | end 85 | 86 | if index then 87 | local new = themes[index] 88 | if not new then error("Invalid theme index: " .. tostring(index)) end 89 | -- Add the new theme 90 | for key in pairs(new) do 91 | theme[key] = new[key] 92 | end 93 | useColors = true 94 | else 95 | useColors = false 96 | end 97 | 98 | quote = colorize('quotes', "'", 'string') 99 | quote2 = colorize('quotes', "'") 100 | dquote = colorize('quotes', '"', 'string') 101 | dquote2 = colorize('quotes', '"') 102 | obrace = colorize('braces', '{ ') 103 | cbrace = colorize('braces', '}') 104 | obracket = colorize('property', '[') 105 | cbracket = colorize('property', ']') 106 | comma = colorize('sep', ', ') 107 | equals = colorize('sep', ' = ') 108 | 109 | controls = {} 110 | for i = 0, 31 do 111 | local c = special[i] 112 | if not c then 113 | if i < 10 then 114 | c = "00" .. tostring(i) 115 | else 116 | c = "0" .. tostring(i) 117 | end 118 | end 119 | controls[i] = colorize('escape', '\\' .. c, 'string') 120 | end 121 | controls[92] = colorize('escape', '\\\\', 'string') 122 | controls[34] = colorize('escape', '\\"', 'string') 123 | controls[39] = colorize('escape', "\\'", 'string') 124 | 125 | end 126 | 127 | function color(colorName) 128 | return '\27[' .. (theme[colorName] or '0') .. 'm' 129 | end 130 | 131 | function colorize(colorName, string, resetName) 132 | return useColors and 133 | (color(colorName) .. tostring(string) .. color(resetName)) or 134 | tostring(string) 135 | end 136 | 137 | local function stringEscape(c) 138 | return controls[string.byte(c, 1)] 139 | end 140 | 141 | function dump(value) 142 | local seen = {} 143 | local output = {} 144 | local offset = 0 145 | local stack = {} 146 | 147 | local function recalcOffset(index) 148 | for i = index + 1, #output do 149 | local m = string.match(output[i], "\n([^\n]*)$") 150 | if m then 151 | offset = #(strip(m)) 152 | else 153 | offset = offset + #(strip(output[i])) 154 | end 155 | end 156 | end 157 | 158 | local function write(text, length) 159 | if not length then length = #(strip(text)) end 160 | -- Create room for data by opening parent blocks 161 | -- Start at the root and go down. 162 | local i = 1 163 | while offset + length > width and stack[i] do 164 | local entry = stack[i] 165 | if not entry.opened then 166 | entry.opened = true 167 | table.insert(output, entry.index + 1, "\n" .. string.rep(" ", i)) 168 | -- Recalculate the offset 169 | recalcOffset(entry.index) 170 | -- Bump the index of all deeper entries 171 | for j = i + 1, #stack do 172 | stack[j].index = stack[j].index + 1 173 | end 174 | end 175 | i = i + 1 176 | end 177 | output[#output + 1] = text 178 | offset = offset + length 179 | if offset > width then 180 | dump(stack) 181 | end 182 | end 183 | 184 | local function indent() 185 | stack[#stack + 1] = { 186 | index = #output, 187 | opened = false, 188 | } 189 | end 190 | 191 | local function unindent() 192 | stack[#stack] = nil 193 | end 194 | 195 | local function process(value) 196 | local typ = type(value) 197 | if typ == 'string' then 198 | if string.match(value, "'") and not string.match(value, '"') then 199 | write(dquote .. string.gsub(value, '[%c\\]', stringEscape) .. dquote2) 200 | else 201 | write(quote .. string.gsub(value, "[%c\\']", stringEscape) .. quote2) 202 | end 203 | elseif typ == 'table' and not seen[value] then 204 | 205 | seen[value] = true 206 | write(obrace) 207 | local i = 1 208 | -- Count the number of keys so we know when to stop adding commas 209 | local total = 0 210 | for _ in pairs(value) do total = total + 1 end 211 | 212 | for k, v in pairs(value) do 213 | indent() 214 | if k == i then 215 | -- if the key matches the index, don't show it. 216 | -- This is how lists print without keys 217 | process(v) 218 | else 219 | if type(k) == "string" and string.find(k,"^[%a_][%a%d_]*$") then 220 | write(colorize("property", k) .. equals) 221 | else 222 | write(obracket) 223 | process(k) 224 | write(cbracket .. equals) 225 | end 226 | if type(v) == "table" then 227 | process(v) 228 | else 229 | indent() 230 | process(v) 231 | unindent() 232 | end 233 | end 234 | if i < total then 235 | write(comma) 236 | else 237 | write(" ") 238 | end 239 | i = i + 1 240 | unindent() 241 | end 242 | write(cbrace) 243 | else 244 | write(colorize(typ, tostring(value))) 245 | end 246 | end 247 | 248 | process(value) 249 | 250 | return table.concat(output, "") 251 | end 252 | 253 | -- Print replacement that goes through libuv. This is useful on windows 254 | -- to use libuv's code to translate ansi escape codes to windows API calls. 255 | function print(...) 256 | local n = select('#', ...) 257 | local arguments = {...} 258 | for i = 1, n do 259 | arguments[i] = tostring(arguments[i]) 260 | end 261 | uv.write(stdout, table.concat(arguments, "\t") .. "\n") 262 | end 263 | 264 | function prettyPrint(...) 265 | local n = select('#', ...) 266 | local arguments = { ... } 267 | 268 | for i = 1, n do 269 | arguments[i] = dump(arguments[i]) 270 | end 271 | 272 | print(table.concat(arguments, "\t")) 273 | end 274 | 275 | function strip(str) 276 | return string.gsub(str, '\027%[[^m]*m', '') 277 | end 278 | 279 | if uv.guess_handle(0) == 'tty' then 280 | stdin = assert(uv.new_tty(0, true)) 281 | else 282 | stdin = uv.new_pipe(false) 283 | uv.pipe_open(stdin, 0) 284 | end 285 | 286 | if uv.guess_handle(1) == 'tty' then 287 | stdout = assert(uv.new_tty(1, false)) 288 | width = uv.tty_get_winsize(stdout) 289 | -- auto-detect when 16 color mode should be used 290 | local term = env.get("TERM") 291 | if term == 'xterm' or term == 'xterm-256color' then 292 | defaultTheme = 256 293 | else 294 | defaultTheme = 16 295 | end 296 | else 297 | stdout = uv.new_pipe(false) 298 | uv.pipe_open(stdout, 1) 299 | width = 80 300 | end 301 | loadColors() 302 | 303 | if uv.guess_handle(2) == 'tty' then 304 | stderr = assert(uv.new_tty(2, false)) 305 | else 306 | stderr = uv.new_pipe(false) 307 | uv.pipe_open(stderr, 2) 308 | end 309 | 310 | local function bind(fn, ...) 311 | local args = {...} 312 | if #args == 0 then return fn end 313 | return function () 314 | return fn(unpack(args)) 315 | end 316 | end 317 | 318 | local function noop(err) 319 | if err then print("Unhandled callback error", err) end 320 | end 321 | 322 | local function adapt(c, fn, ...) 323 | local nargs = select('#', ...) 324 | local args = {...} 325 | -- No continuation defaults to noop callback 326 | if not c then c = noop end 327 | local t = type(c) 328 | if t == 'function' then 329 | args[nargs + 1] = c 330 | return fn(unpack(args)) 331 | elseif t ~= 'thread' then 332 | error("Illegal continuation type " .. t) 333 | end 334 | local err, data, waiting 335 | args[nargs + 1] = function (err, ...) 336 | if waiting then 337 | if err then 338 | assert(coroutine.resume(c, nil, err)) 339 | else 340 | assert(coroutine.resume(c, ...)) 341 | end 342 | else 343 | error, data = err, {...} 344 | c = nil 345 | end 346 | end 347 | fn(unpack(args)) 348 | if c then 349 | waiting = true 350 | return coroutine.yield(c) 351 | elseif err then 352 | return nil, err 353 | else 354 | return unpack(data) 355 | end 356 | end 357 | 358 | return { 359 | bind = bind, 360 | loadColors = loadColors, 361 | theme = theme, 362 | print = print, 363 | prettyPrint = prettyPrint, 364 | dump = dump, 365 | strip = strip, 366 | color = color, 367 | colorize = colorize, 368 | stdin = stdin, 369 | stdout = stdout, 370 | stderr = stderr, 371 | noop = noop, 372 | adapt = adapt, 373 | } 374 | -------------------------------------------------------------------------------- /samples/test.app/utils.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Copyright 2014-2015 The Luvit Authors. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS-IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | --]] 18 | --[[lit-meta 19 | name = "luvit/pretty-print" 20 | version = "2.0.0" 21 | homepage = "https://github.com/luvit/luvit/blob/master/deps/pretty-print.lua" 22 | description = "A lua value pretty printer and colorizer for terminals." 23 | tags = {"colors", "tty"} 24 | license = "Apache 2" 25 | author = { name = "Tim Caswell" } 26 | ]] 27 | 28 | local success, uv = pcall(require, 'uv') 29 | if not success then 30 | success, uv = pcall(require, 'luv') 31 | end 32 | assert(success, uv) 33 | local getenv = require('os').getenv 34 | 35 | local prettyPrint, dump, strip, color, colorize, loadColors 36 | local theme = {} 37 | local useColors = false 38 | local defaultTheme 39 | 40 | local stdout, stdin, stderr, width 41 | 42 | local quote, quote2, dquote, dquote2, obracket, cbracket, obrace, cbrace, comma, equals, controls 43 | 44 | local themes = { 45 | -- nice color theme using 16 ansi colors 46 | [16] = { 47 | property = "0;37", -- white 48 | sep = "1;30", -- bright-black 49 | braces = "1;30", -- bright-black 50 | 51 | ["nil"] = "1;30", -- bright-black 52 | boolean = "0;33", -- yellow 53 | number = "1;33", -- bright-yellow 54 | string = "0;32", -- green 55 | quotes = "1;32", -- bright-green 56 | escape = "1;32", -- bright-green 57 | ["function"] = "0;35", -- purple 58 | thread = "1;35", -- bright-purple 59 | 60 | table = "1;34", -- bright blue 61 | userdata = "1;36", -- bright cyan 62 | cdata = "0;36", -- cyan 63 | 64 | err = "1;31", -- bright red 65 | success = "1;33;42", -- bright-yellow on green 66 | failure = "1;33;41", -- bright-yellow on red 67 | highlight = "1;36;44", -- bright-cyan on blue 68 | }, 69 | -- nice color theme using ansi 256-mode colors 70 | [256] = { 71 | property = "38;5;253", 72 | braces = "38;5;247", 73 | sep = "38;5;240", 74 | 75 | ["nil"] = "38;5;244", 76 | boolean = "38;5;220", -- yellow-orange 77 | number = "38;5;202", -- orange 78 | string = "38;5;34", -- darker green 79 | quotes = "38;5;40", -- green 80 | escape = "38;5;46", -- bright green 81 | ["function"] = "38;5;129", -- purple 82 | thread = "38;5;199", -- pink 83 | 84 | table = "38;5;27", -- blue 85 | userdata = "38;5;39", -- blue2 86 | cdata = "38;5;69", -- teal 87 | 88 | err = "38;5;196", -- bright red 89 | success = "38;5;120;48;5;22", -- bright green on dark green 90 | failure = "38;5;215;48;5;52", -- bright red on dark red 91 | highlight = "38;5;45;48;5;236", -- bright teal on dark grey 92 | }, 93 | } 94 | 95 | local special = { 96 | [7] = 'a', 97 | [8] = 'b', 98 | [9] = 't', 99 | [10] = 'n', 100 | [11] = 'v', 101 | [12] = 'f', 102 | [13] = 'r' 103 | } 104 | 105 | function strip(str) 106 | return string.gsub(str, '\027%[[^m]*m', '') 107 | end 108 | 109 | 110 | function loadColors(index) 111 | if index == nil then index = defaultTheme end 112 | 113 | -- Remove the old theme 114 | for key in pairs(theme) do 115 | theme[key] = nil 116 | end 117 | 118 | if index then 119 | local new = themes[index] 120 | if not new then error("Invalid theme index: " .. tostring(index)) end 121 | -- Add the new theme 122 | for key in pairs(new) do 123 | theme[key] = new[key] 124 | end 125 | useColors = true 126 | else 127 | useColors = false 128 | end 129 | 130 | quote = colorize('quotes', "'", 'string') 131 | quote2 = colorize('quotes', "'") 132 | dquote = colorize('quotes', '"', 'string') 133 | dquote2 = colorize('quotes', '"') 134 | obrace = colorize('braces', '{ ') 135 | cbrace = colorize('braces', '}') 136 | obracket = colorize('property', '[') 137 | cbracket = colorize('property', ']') 138 | comma = colorize('sep', ', ') 139 | equals = colorize('sep', ' = ') 140 | 141 | controls = {} 142 | for i = 0, 31 do 143 | local c = special[i] 144 | if not c then 145 | if i < 10 then 146 | c = "00" .. tostring(i) 147 | else 148 | c = "0" .. tostring(i) 149 | end 150 | end 151 | controls[i] = colorize('escape', '\\' .. c, 'string') 152 | end 153 | controls[92] = colorize('escape', '\\\\', 'string') 154 | controls[34] = colorize('escape', '\\"', 'string') 155 | controls[39] = colorize('escape', "\\'", 'string') 156 | for i = 128, 255 do 157 | local c 158 | if i < 100 then 159 | c = "0" .. tostring(i) 160 | else 161 | c = tostring(i) 162 | end 163 | controls[i] = colorize('escape', '\\' .. c, 'string') 164 | end 165 | 166 | end 167 | 168 | function color(colorName) 169 | return '\27[' .. (theme[colorName] or '0') .. 'm' 170 | end 171 | 172 | function colorize(colorName, string, resetName) 173 | return useColors and 174 | (color(colorName) .. tostring(string) .. color(resetName)) or 175 | tostring(string) 176 | end 177 | 178 | local function stringEscape(c) 179 | return controls[string.byte(c, 1)] 180 | end 181 | 182 | function dump(value, recurse, nocolor) 183 | local seen = {} 184 | local output = {} 185 | local offset = 0 186 | local stack = {} 187 | 188 | local function recalcOffset(index) 189 | for i = index + 1, #output do 190 | local m = string.match(output[i], "\n([^\n]*)$") 191 | if m then 192 | offset = #(strip(m)) 193 | else 194 | offset = offset + #(strip(output[i])) 195 | end 196 | end 197 | end 198 | 199 | local function write(text, length) 200 | if not length then length = #(strip(text)) end 201 | -- Create room for data by opening parent blocks 202 | -- Start at the root and go down. 203 | local i = 1 204 | while offset + length > width and stack[i] do 205 | local entry = stack[i] 206 | if not entry.opened then 207 | entry.opened = true 208 | table.insert(output, entry.index + 1, "\n" .. string.rep(" ", i)) 209 | -- Recalculate the offset 210 | recalcOffset(entry.index) 211 | -- Bump the index of all deeper entries 212 | for j = i + 1, #stack do 213 | stack[j].index = stack[j].index + 1 214 | end 215 | end 216 | i = i + 1 217 | end 218 | output[#output + 1] = text 219 | offset = offset + length 220 | if offset > width then 221 | return dump(stack) 222 | end 223 | end 224 | 225 | local function indent() 226 | stack[#stack + 1] = { 227 | index = #output, 228 | opened = false, 229 | } 230 | end 231 | 232 | local function unindent() 233 | stack[#stack] = nil 234 | end 235 | 236 | local function process(localValue) 237 | local typ = type(localValue) 238 | if typ == 'string' then 239 | if string.match(localValue, "'") and not string.match(localValue, '"') then 240 | write(dquote .. string.gsub(localValue, '[%c\\\128-\255]', stringEscape) .. dquote2) 241 | else 242 | write(quote .. string.gsub(localValue, "[%c\\'\128-\255]", stringEscape) .. quote2) 243 | end 244 | elseif typ == 'table' and not seen[localValue] then 245 | if not recurse then seen[localValue] = true end 246 | write(obrace) 247 | local i = 1 248 | -- Count the number of keys so we know when to stop adding commas 249 | local total = 0 250 | for _ in pairs(localValue) do total = total + 1 end 251 | 252 | local nextIndex = 1 253 | for k, v in pairs(localValue) do 254 | indent() 255 | if k == nextIndex then 256 | -- if the key matches the last numerical index + 1 257 | -- This is how lists print without keys 258 | nextIndex = k + 1 259 | process(v) 260 | else 261 | if type(k) == "string" and string.find(k,"^[%a_][%a%d_]*$") then 262 | write(colorize("property", k) .. equals) 263 | else 264 | write(obracket) 265 | process(k) 266 | write(cbracket .. equals) 267 | end 268 | if type(v) == "table" then 269 | process(v) 270 | else 271 | indent() 272 | process(v) 273 | unindent() 274 | end 275 | end 276 | if i < total then 277 | write(comma) 278 | else 279 | write(" ") 280 | end 281 | i = i + 1 282 | unindent() 283 | end 284 | write(cbrace) 285 | else 286 | write(colorize(typ, tostring(localValue))) 287 | end 288 | end 289 | 290 | process(value) 291 | local s = table.concat(output, "") 292 | return nocolor and strip(s) or s 293 | end 294 | 295 | -- Print replacement that goes through libuv. This is useful on windows 296 | -- to use libuv's code to translate ansi escape codes to windows API calls. 297 | function _G.print(...) 298 | local n = select('#', ...) 299 | local arguments = {...} 300 | for i = 1, n do 301 | arguments[i] = tostring(arguments[i]) 302 | end 303 | uv.write(stdout, table.concat(arguments, "\t") .. "\n") 304 | end 305 | 306 | function prettyPrint(...) 307 | local n = select('#', ...) 308 | local arguments = { ... } 309 | 310 | for i = 1, n do 311 | arguments[i] = dump(arguments[i]) 312 | end 313 | 314 | print(table.concat(arguments, "\t")) 315 | end 316 | 317 | function strip(str) 318 | return string.gsub(str, '\027%[[^m]*m', '') 319 | end 320 | 321 | if uv.guess_handle(0) == 'tty' then 322 | stdin = assert(uv.new_tty(0, true)) 323 | else 324 | stdin = uv.new_pipe(false) 325 | uv.pipe_open(stdin, 0) 326 | end 327 | 328 | if uv.guess_handle(1) == 'tty' then 329 | stdout = assert(uv.new_tty(1, false)) 330 | width = uv.tty_get_winsize(stdout) 331 | if width == 0 then width = 80 end 332 | -- auto-detect when 16 color mode should be used 333 | local term = getenv("TERM") 334 | if term and (term == 'xterm' or term:match'-256color$') then 335 | defaultTheme = 256 336 | else 337 | defaultTheme = 16 338 | end 339 | else 340 | stdout = uv.new_pipe(false) 341 | uv.pipe_open(stdout, 1) 342 | width = 80 343 | end 344 | loadColors() 345 | 346 | if uv.guess_handle(2) == 'tty' then 347 | stderr = assert(uv.new_tty(2, false)) 348 | else 349 | stderr = uv.new_pipe(false) 350 | uv.pipe_open(stderr, 2) 351 | end 352 | 353 | return { 354 | loadColors = loadColors, 355 | theme = theme, 356 | print = print, 357 | prettyPrint = prettyPrint, 358 | dump = dump, 359 | color = color, 360 | colorize = colorize, 361 | stdin = stdin, 362 | stdout = stdout, 363 | stderr = stderr, 364 | strip = strip, 365 | } 366 | -------------------------------------------------------------------------------- /src/snapshot.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 codingow.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | // Adapted from https://github.com/cloudwu/lua-snapshot 23 | 24 | #include "./luvi.h" 25 | 26 | #if (LUA_VERSION_NUM > 501) 27 | #define lua_getfenv lua_getuservalue 28 | #define lua_setfenv lua_setuservalue 29 | #endif 30 | 31 | #if defined(_MSC_VER) && _MSC_VER < 1900 32 | 33 | #define snprintf c99_snprintf 34 | #define vsnprintf c99_vsnprintf 35 | 36 | static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) 37 | { 38 | int count = -1; 39 | 40 | if (size != 0) 41 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); 42 | if (count == -1) 43 | count = _vscprintf(format, ap); 44 | 45 | return count; 46 | } 47 | 48 | static int c99_snprintf(char *outBuf, size_t size, const char *format, ...) 49 | { 50 | int count; 51 | va_list ap; 52 | 53 | va_start(ap, format); 54 | count = c99_vsnprintf(outBuf, size, format, ap); 55 | va_end(ap); 56 | 57 | return count; 58 | } 59 | 60 | #endif 61 | 62 | static void mark_object(lua_State *L, lua_State *dL, const void * parent, const char * desc); 63 | 64 | #if LUA_VERSION_NUM == 501 65 | 66 | static void 67 | mark_function_env(lua_State *L, lua_State *dL, const void * t) { 68 | lua_getfenv(L,-1); 69 | if (lua_istable(L,-1)) { 70 | mark_object(L, dL, t, "[environment]"); 71 | } else { 72 | lua_pop(L,1); 73 | } 74 | } 75 | 76 | #else 77 | 78 | #define mark_function_env(L,dL,t) 79 | 80 | #endif 81 | 82 | #include 83 | #include 84 | #include 85 | 86 | #define TABLE 1 87 | #define FUNCTION 2 88 | #define SOURCE 3 89 | #define THREAD 4 90 | #define USERDATA 5 91 | #define MARK 6 92 | 93 | static bool 94 | ismarked(lua_State *dL, const void *p) { 95 | lua_rawgetp(dL, MARK, p); 96 | if (lua_isnil(dL,-1)) { 97 | lua_pop(dL,1); 98 | lua_pushboolean(dL,1); 99 | lua_rawsetp(dL, MARK, p); 100 | return false; 101 | } 102 | lua_pop(dL,1); 103 | return true; 104 | } 105 | 106 | static const void * 107 | readobject(lua_State *L, lua_State *dL, const void *parent, const char *desc) { 108 | int t = lua_type(L, -1); 109 | int tidx = 0; 110 | switch (t) { 111 | case LUA_TTABLE: 112 | tidx = TABLE; 113 | break; 114 | case LUA_TFUNCTION: 115 | tidx = FUNCTION; 116 | break; 117 | case LUA_TTHREAD: 118 | tidx = THREAD; 119 | break; 120 | case LUA_TUSERDATA: 121 | tidx = USERDATA; 122 | break; 123 | default: 124 | return NULL; 125 | } 126 | 127 | const void * p = lua_topointer(L, -1); 128 | if (ismarked(dL, p)) { 129 | lua_rawgetp(dL, tidx, p); 130 | if (!lua_isnil(dL,-1)) { 131 | lua_pushstring(dL,desc); 132 | lua_rawsetp(dL, -2, parent); 133 | } 134 | lua_pop(dL,1); 135 | lua_pop(L,1); 136 | return NULL; 137 | } 138 | 139 | lua_newtable(dL); 140 | lua_pushstring(dL,desc); 141 | lua_rawsetp(dL, -2, parent); 142 | lua_rawsetp(dL, tidx, p); 143 | 144 | return p; 145 | } 146 | 147 | static const char * 148 | keystring(lua_State *L, int index, char * buffer) { 149 | int t = lua_type(L,index); 150 | switch (t) { 151 | case LUA_TSTRING: 152 | return lua_tostring(L,index); 153 | case LUA_TNUMBER: 154 | sprintf(buffer,"[%lg]",lua_tonumber(L,index)); 155 | break; 156 | case LUA_TBOOLEAN: 157 | sprintf(buffer,"[%s]",lua_toboolean(L,index) ? "true" : "false"); 158 | break; 159 | case LUA_TNIL: 160 | sprintf(buffer,"[nil]"); 161 | break; 162 | default: 163 | sprintf(buffer,"[%s:%p]",lua_typename(L,t),lua_topointer(L,index)); 164 | break; 165 | } 166 | return buffer; 167 | } 168 | 169 | static void 170 | mark_table(lua_State *L, lua_State *dL, const void * parent, const char * desc) { 171 | const void * t = readobject(L, dL, parent, desc); 172 | if (t == NULL) 173 | return; 174 | 175 | bool weakk = false; 176 | bool weakv = false; 177 | if (lua_getmetatable(L, -1)) { 178 | lua_pushliteral(L, "__mode"); 179 | lua_rawget(L, -2); 180 | if (lua_isstring(L,-1)) { 181 | const char *mode = lua_tostring(L, -1); 182 | if (strchr(mode, 'k')) { 183 | weakk = true; 184 | } 185 | if (strchr(mode, 'v')) { 186 | weakv = true; 187 | } 188 | } 189 | lua_pop(L,1); 190 | 191 | luaL_checkstack(L, LUA_MINSTACK, NULL); 192 | mark_table(L, dL, t, "[metatable]"); 193 | } 194 | 195 | lua_pushnil(L); 196 | while (lua_next(L, -2) != 0) { 197 | if (weakv) { 198 | lua_pop(L,1); 199 | } else { 200 | char temp[32]; 201 | desc = keystring(L, -2, temp); 202 | mark_object(L, dL, t , desc); 203 | } 204 | if (!weakk) { 205 | lua_pushvalue(L,-1); 206 | mark_object(L, dL, t , "[key]"); 207 | } 208 | } 209 | 210 | lua_pop(L,1); 211 | } 212 | 213 | static void 214 | mark_userdata(lua_State *L, lua_State *dL, const void * parent, const char *desc) { 215 | const void * t = readobject(L, dL, parent, desc); 216 | if (t == NULL) 217 | return; 218 | if (lua_getmetatable(L, -1)) { 219 | mark_table(L, dL, t, "[metatable]"); 220 | } 221 | 222 | lua_getfenv(L,-1); 223 | if (lua_isnil(L,-1)) { 224 | lua_pop(L,2); 225 | } else { 226 | mark_table(L, dL, t, "[uservalue]"); 227 | lua_pop(L,1); 228 | } 229 | } 230 | 231 | static void 232 | mark_function(lua_State *L, lua_State *dL, const void * parent, const char *desc) { 233 | const void * t = readobject(L, dL, parent, desc); 234 | if (t == NULL) 235 | return; 236 | 237 | mark_function_env(L,dL,t); 238 | int i; 239 | for (i=1;;i++) { 240 | const char *name = lua_getupvalue(L,-1,i); 241 | if (name == NULL) 242 | break; 243 | mark_object(L, dL, t, name[0] ? name : "[upvalue]"); 244 | } 245 | if (lua_iscfunction(L,-1)) { 246 | if (i==1) { 247 | // light c function 248 | lua_pushnil(dL); 249 | lua_rawsetp(dL, FUNCTION, t); 250 | } 251 | lua_pop(L,1); 252 | } else { 253 | lua_Debug ar; 254 | lua_getinfo(L, ">S", &ar); 255 | luaL_Buffer b; 256 | luaL_buffinit(dL, &b); 257 | luaL_addstring(&b, ar.short_src); 258 | char tmp[16]; 259 | sprintf(tmp,":%d",ar.linedefined); 260 | luaL_addstring(&b, tmp); 261 | luaL_pushresult(&b); 262 | lua_rawsetp(dL, SOURCE, t); 263 | } 264 | } 265 | 266 | static void 267 | mark_thread(lua_State *L, lua_State *dL, const void * parent, const char *desc) { 268 | const void * t = readobject(L, dL, parent, desc); 269 | if (t == NULL) 270 | return; 271 | int level = 0; 272 | lua_State *cL = lua_tothread(L,-1); 273 | if (cL == L) { 274 | level = 1; 275 | } 276 | lua_Debug ar; 277 | luaL_Buffer b; 278 | luaL_buffinit(dL, &b); 279 | while (lua_getstack(cL, level, &ar)) { 280 | char tmp[128]; 281 | lua_getinfo(cL, "Sl", &ar); 282 | luaL_addstring(&b, ar.short_src); 283 | if (ar.currentline >=0) { 284 | char tmp[16]; 285 | sprintf(tmp,":%d ",ar.currentline); 286 | luaL_addstring(&b, tmp); 287 | } 288 | 289 | int i,j; 290 | for (j=1;j>-1;j-=2) { 291 | for (i=j;;i+=j) { 292 | const char * name = lua_getlocal(cL, &ar, i); 293 | if (name == NULL) 294 | break; 295 | snprintf(tmp, sizeof(tmp), "%s : %s:%d",name,ar.short_src,ar.currentline); 296 | mark_object(cL, dL, t, tmp); 297 | } 298 | } 299 | 300 | ++level; 301 | } 302 | luaL_pushresult(&b); 303 | lua_rawsetp(dL, SOURCE, t); 304 | lua_pop(L,1); 305 | } 306 | 307 | static void 308 | mark_object(lua_State *L, lua_State *dL, const void * parent, const char *desc) { 309 | luaL_checkstack(L, LUA_MINSTACK, NULL); 310 | int t = lua_type(L, -1); 311 | switch (t) { 312 | case LUA_TTABLE: 313 | mark_table(L, dL, parent, desc); 314 | break; 315 | case LUA_TUSERDATA: 316 | mark_userdata(L, dL, parent, desc); 317 | break; 318 | case LUA_TFUNCTION: 319 | mark_function(L, dL, parent, desc); 320 | break; 321 | case LUA_TTHREAD: 322 | mark_thread(L, dL, parent, desc); 323 | break; 324 | default: 325 | lua_pop(L,1); 326 | break; 327 | } 328 | } 329 | 330 | static int 331 | count_table(lua_State *L, int idx) { 332 | int n = 0; 333 | lua_pushnil(L); 334 | while (lua_next(L, idx) != 0) { 335 | ++n; 336 | lua_pop(L,1); 337 | } 338 | return n; 339 | } 340 | 341 | static void 342 | gen_table_desc(lua_State *dL, luaL_Buffer *b, const void * parent, const char *desc) { 343 | char tmp[32]; 344 | size_t l = sprintf(tmp,"%p : ",parent); 345 | luaL_addlstring(b, tmp, l); 346 | luaL_addstring(b, desc); 347 | luaL_addchar(b, '\n'); 348 | } 349 | 350 | static void 351 | pdesc(lua_State *L, lua_State *dL, int idx, const char * typename) { 352 | lua_pushnil(dL); 353 | while (lua_next(dL, idx) != 0) { 354 | luaL_Buffer b; 355 | luaL_buffinit(L, &b); 356 | const void * key = lua_touserdata(dL, -2); 357 | if (idx == FUNCTION) { 358 | lua_rawgetp(dL, SOURCE, key); 359 | if (lua_isnil(dL, -1)) { 360 | luaL_addstring(&b,"cfunction\n"); 361 | } else { 362 | size_t l = 0; 363 | const char * s = lua_tolstring(dL, -1, &l); 364 | luaL_addlstring(&b,s,l); 365 | luaL_addchar(&b,'\n'); 366 | } 367 | lua_pop(dL, 1); 368 | } else if (idx == THREAD) { 369 | lua_rawgetp(dL, SOURCE, key); 370 | size_t l = 0; 371 | const char * s = lua_tolstring(dL, -1, &l); 372 | luaL_addlstring(&b,s,l); 373 | luaL_addchar(&b,'\n'); 374 | lua_pop(dL, 1); 375 | } else { 376 | luaL_addstring(&b, typename); 377 | luaL_addchar(&b,'\n'); 378 | } 379 | lua_pushnil(dL); 380 | while (lua_next(dL, -2) != 0) { 381 | const void * parent = lua_touserdata(dL,-2); 382 | const char * desc = luaL_checkstring(dL,-1); 383 | gen_table_desc(dL, &b, parent, desc); 384 | lua_pop(dL,1); 385 | } 386 | luaL_pushresult(&b); 387 | lua_rawsetp(L, -2, key); 388 | lua_pop(dL,1); 389 | } 390 | } 391 | 392 | static void 393 | gen_result(lua_State *L, lua_State *dL) { 394 | int count = 0; 395 | count += count_table(dL, TABLE); 396 | count += count_table(dL, FUNCTION); 397 | count += count_table(dL, USERDATA); 398 | count += count_table(dL, THREAD); 399 | lua_createtable(L, 0, count); 400 | pdesc(L, dL, TABLE, "table"); 401 | pdesc(L, dL, USERDATA, "userdata"); 402 | pdesc(L, dL, FUNCTION, "function"); 403 | pdesc(L, dL, THREAD, "thread"); 404 | } 405 | 406 | static int 407 | snapshot(lua_State *L) { 408 | int i; 409 | lua_State *dL = luaL_newstate(); 410 | for (i=0;i