#!/bin/bash # https://stackoverflow.com/a/13062682 uncomment() { [ $# -eq 2 ] && arg="$1" || arg="" eval file="\$$#" sed 's/a/aA/g; s/__/aB/g; s/#/aC/g' "$file" | \ gcc -P -E $arg - | \ sed 's/aC/#/g; s/aB/__/g; s/aA/a/g' } # this is the magic that turns multiline statements into # single line statements # LITERALLY DOES NOT WORK WITH PREPROCESSOR onelineize() { grep -v '^#' | sed -e ':r;$!{N;br};s/\([^{;]\)\n\s*/\1 /g' } discard_preprocessors() { grep -v '#\(include\|if\|endif\)' } # sed regex for extracting data from function signature # if this isn't regex, idk what is # LUA_API (return type as \2) (function name as \3) (args as \4) sym_regex='^LUA\(LIB\)\?_API\s\+\([^(]\+\)\s*(\([^)]\+\))\s\+(\([^)]\+\));' # get funcptr declarations ptrize() { grep '^LUA' | sed -e "s/$sym_regex/static \2(*\3) (\4);/" } import_sym() { grep '^LUA' | sed -e "s/$sym_regex/\tIMPORT_SYMBOL(\3, \2, \4);/" } export_sym() { grep '^LUA' | sed -e "s/$sym_regex/\t\tEXPORT_SYMBOL(\3),/" } decl() { header="$(uncomment $1 | discard_preprocessors)" header1="$(onelineize <<< "$header")" # typedef grep -v '^\(LUA\|#\|extern\)' <<< "$header1" # funcptrs ptrize <<< "$header1" # defines grep '^#' <<< "$header" } decl_import() { uncomment $1 | onelineize | import_sym } decl_export() { uncomment $1 | onelineize | export_sym } generate_header() { local LUA_PATH="$1" echo "#ifndef LITE_XL_PLUGIN_API" echo "#define LITE_XL_PLUGIN_API" echo "/**" echo "The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long" echo "as it has an entrypoint that looks like the following, where xxxxx is the plugin name:" echo '#include "lite_xl_plugin_api.h"' echo "int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) {" echo " lite_xl_plugin_init(XL);" echo " ..." echo " return 1;" echo "}" echo "In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple!" echo "Due to the way the API is structured, you *should not* link or include lua libraries." echo "This file was automatically generated by the below code. DO NOT MODIFY DIRECTLY." echo echo cat "$0" echo "**/" echo "#include " decl "$LUA_PATH/lua.h" decl "$LUA_PATH/lauxlib.h" echo "#define IMPORT_SYMBOL(name, ret, ...) name = (ret (*) (__VA_ARGS__)) symbol(#name)" echo "static void lite_xl_plugin_init(void *XL) {" echo -e "\tvoid* (*symbol)(const char *) = (void* (*) (const char *)) XL;" decl_import "$LUA_PATH/lua.h" decl_import "$LUA_PATH/lauxlib.h" echo "}" echo "#endif" } generate_api_require() { local LUA_PATH="$1" echo "#ifndef API_REQUIRE_H" echo "#define API_REQUIRE_H" echo "/**" echo "This file contains the function api_require that" echo "returns a function pointer with it's corresponding name." echo echo "This file is automatically generated. DO NOT MODIFY." echo "*/" echo "#include " echo "#include " echo '#include "lua.h"' echo '#include "lauxlib.h"' echo echo "typedef struct fnptr_s {" echo -e "\tconst char* name;" echo -e "\tvoid *addr;" echo "} fnptr_t;" echo echo "#define EXPORT_SYMBOL(SYM) { #SYM, (void*)(SYM) }" echo "static void *api_require(const char *symbol) {" echo -e "\tstatic fnptr_t nodes[] = {" decl_export "$LUA_PATH/lua.h" decl_export "$LUA_PATH/lauxlib.h" echo -e "\t};" echo -e "\tfor (int i = 0; i < sizeof(nodes) / sizeof(fnptr_t); i++)" echo -e "\t\tif (strcmp(nodes[i].name, symbol) == 0)" echo -e "\t\t\treturn nodes[i].addr;" echo -e "\treturn NULL;" echo "}" echo "#endif" } show_help() { echo -e "Usage: $0 prefix" echo echo -e "Available options:" echo echo -e "-a\t--api-header\tGenerate lite_xl_plugin_api.h" echo -e "-b\t--api-require\tGenerate api_require.h" echo -e "-p\t--prefix\tSet prefix (where to find lua.h and lauxlib.h)" } main() { local header=0 local require=0 local prefix="" for i in "$@"; do case $i in -h|--help) show_help exit 0 ;; -a|--api-header) header=1 shift ;; -b|--api-require) require=1 shift ;; -p|--prefix) prefix="$2" shift shift ;; *) ;; esac done if [[ "$header" -eq 1 ]]; then generate_header "$prefix" elif [[ "$require" -eq 1 ]]; then generate_api_require "$prefix" else show_help exit 1 fi } main "$@"