diff options
Diffstat (limited to 'source/luametatex/source/luaoptional')
22 files changed, 6302 insertions, 0 deletions
diff --git a/source/luametatex/source/luaoptional/cmake/mujs/CMakeLists.txt b/source/luametatex/source/luaoptional/cmake/mujs/CMakeLists.txt new file mode 100644 index 000000000..cfe2ee2bf --- /dev/null +++ b/source/luametatex/source/luaoptional/cmake/mujs/CMakeLists.txt @@ -0,0 +1,107 @@ +# This file is made by Mojca and Hans and is subjected to changes +# as we proceed with luametatex and the contextgarden compile farm. + +cmake_minimum_required(VERSION 3.7) + +# Lucky us: only normal C is used: + +project (mujs + VERSION 1.0.6 + DESCRIPTION "MuJS embeddable Javascript interpreter" + LANGUAGES C) + +# The jsrepr.c is not needed and depends on utf.c as well has some function +# pointer cast issue (accessing unknown name field). + +set (mujs_sources + jsarray.c + jsboolean.c + jsbuiltin.c + jscompile.c + jsdate.c + jsdtoa.c + jsdump.c + jserror.c + jsfunction.c + jsgc.c + jsintern.c + jslex.c + jsmath.c + jsnumber.c + jsobject.c + json.c + jsparse.c + jsproperty.c + jsregexp.c +# jsrepr.c + jsrun.c + jsstate.c + jsstring.c + jsvalue.c + regexp.c + utf.c + utftype.c +) + +set (mujs_headers + jsbuiltin.h + jscompile.h + jsi.h + jslex.h + jsparse.h + jsrun.h + jsvalue.h + mujs.h + regexp.h + utf.h +) + +# We need this in order for msvc to export the symbols (which is default on +# gcc). Otherwise we need this dllexport stuff. + +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# The previous one has to come before the next one! + +if (MSVC) + + add_library(mujs SHARED ${mujs_headers} ${mujs_sources} ) + +else() + + # Is this hack still needed or does the above work ok.? + + add_library(mujs SHARED ${mujs_headers} one.c) + +endif() + +# As per make file. + +set_property(TARGET mujs PROPERTY C_STANDARD 99) + +# We want consistent and predictable names: + +set_target_properties(mujs PROPERTIES OUTPUT_NAME "libmujs") + +# Some options based on what's in upstream's make file. + +if (NOT MSVC) + + target_compile_options(mujs + PRIVATE + -pedantic + -Wall + -Wextra + -Wno-unused-parameter + ) + + if (CMAKE_C_COMPILER_ID STREQUAL "Clang") + + target_compile_options(mujs + PRIVATE + -Wunreachable-code + ) + + endif() + +endif() diff --git a/source/luametatex/source/luaoptional/cmake/mujs/CMakeSettings.json b/source/luametatex/source/luaoptional/cmake/mujs/CMakeSettings.json new file mode 100644 index 000000000..fc67a089b --- /dev/null +++ b/source/luametatex/source/luaoptional/cmake/mujs/CMakeSettings.json @@ -0,0 +1,28 @@ +{ + "configurations": [ + { + "name": "msvc-x64-release", + "generator": "Ninja", + "configurationType": "Release", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [] + }, + { + "name": "msvc-x64-debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [] + } + ] +}
\ No newline at end of file diff --git a/source/luametatex/source/luaoptional/lmtcerflib.c b/source/luametatex/source/luaoptional/lmtcerflib.c new file mode 100644 index 000000000..b18e3fb47 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtcerflib.c @@ -0,0 +1,133 @@ +/* + + See license.txt in the root of this project. + + In order to match the xmath library we also support complex error functions. For that we use + the libcerf funcitonality. That library itself is a follow up on other code (you can find + articles on the web). + + One complication is that the library (at the time we started using it) is not suitable for the + MSVC compiler so we use adapted code, so yet another succession. We currently embed libcerf but + when we have the optional library compilation up and running on the garden that might become a + real optional module instead. + + Note: Alan has to test if all works okay. + +*/ + +# include "lmtoptional.h" +# include "luametatex.h" + +# include <complex.h> +# include <cerf.h> + +/*tex We start with some similar code as in |xcomplex.c|. */ + +# define COMPLEX_METATABLE "complex number" + +# if (_MSC_VER) + + # define Complex _Dcomplex + + static Complex lmt_tocomplex(lua_State *L, int i) + { + switch (lua_type(L, i)) { + case LUA_TNUMBER: + case LUA_TSTRING: + return _Cbuild(luaL_checknumber(L, i), 0); + default: + return *((Complex*)luaL_checkudata(L, i, COMPLEX_METATABLE)); + } + } + +# else + + # define Complex double complex + + static Complex lmt_tocomplex(lua_State *L, int i) + { + switch (lua_type(L, i)) { + case LUA_TNUMBER: + case LUA_TSTRING: + return luaL_checknumber(L, i); + default: + return *((Complex*)luaL_checkudata(L, i, COMPLEX_METATABLE)); + } + } + +# endif + +static int lmt_pushcomplex(lua_State *L, Complex z) +{ + Complex *p = lua_newuserdatauv(L, sizeof(Complex), 0); + luaL_setmetatable(L, COMPLEX_METATABLE); + *p = z; + return 1; +} + +/*tex We use that here: */ + +static int xcomplexlib_cerf_erf (lua_State *L) { + return lmt_pushcomplex(L, cerf(lmt_tocomplex(L, 1))); +} + +static int xcomplexlib_cerf_erfc (lua_State *L) { + return lmt_pushcomplex(L, lmt_tocomplex(L, 1)); +} + +static int xcomplexlib_cerf_erfcx (lua_State *L) { + return lmt_pushcomplex(L, cerfcx(lmt_tocomplex(L, 1))); +} + +static int xcomplexlib_cerf_erfi (lua_State *L) { + return lmt_pushcomplex(L, cerfi(lmt_tocomplex(L, 1))); +} + +static int xcomplexlib_cerf_dawson (lua_State *L) { + return lmt_pushcomplex(L, cdawson(lmt_tocomplex(L, 1))); +} + +static int xcomplexlib_cerf_voigt (lua_State *L) { + lua_pushnumber(L, voigt(lua_tonumber(L, 1), lua_tonumber(L, 2), lua_tonumber(L, 3))); + return 1; +} + +static int xcomplexlib_cerf_voigt_hwhm (lua_State *L) { + int error = 0; + double result = voigt_hwhm(lua_tonumber(L, 1), lua_tonumber(L, 2), &error); + lua_pushnumber(L, result); + switch (error) { + case 1 : + tex_formatted_warning("voigt_hwhm", "bad arguments"); + break; + case 2 : + tex_formatted_warning("voigt_hwhm", "huge deviation"); + break; + case 3 : + tex_formatted_warning("voigt_hwhm", "no convergence"); + break; + } + return 1; +} + +static struct luaL_Reg xcomplexlib_cerf_function_list[] = { + { "erf", xcomplexlib_cerf_erf }, + { "erfc", xcomplexlib_cerf_erfc }, + { "erfcx", xcomplexlib_cerf_erfcx }, + { "erfi", xcomplexlib_cerf_erfi }, + { "dawson", xcomplexlib_cerf_dawson }, + { "voigt", xcomplexlib_cerf_voigt }, + { "voigt_hwhm", xcomplexlib_cerf_voigt_hwhm }, + { NULL, NULL }, +}; + +int luaextend_xcomplex(lua_State *L) +{ + lua_getglobal(L, "string"); + for (const luaL_Reg *lib = xcomplexlib_cerf_function_list; lib->name; lib++) { + lua_pushcfunction(L, lib->func); + lua_setfield(L, -2, lib->name); + } + lua_pop(L, 1); + return 1; +} diff --git a/source/luametatex/source/luaoptional/lmtcurl.c b/source/luametatex/source/luaoptional/lmtcurl.c new file mode 100644 index 000000000..6a54174e5 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtcurl.c @@ -0,0 +1,506 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +typedef void* curl_instance ; +typedef int curl_return_code ; +typedef int curl_error_code ; + +typedef enum curl_option_type { + curl_ignore = 0, + curl_integer = 1, + curl_string = 2, + curl_function = 3, /* ignored */ + curl_offset = 4, /* ignored */ +} curl_option_type; + +/*tex At the \LUA\ end we can have a mapping of useful ones, */ + +static const int curl_options[] = { + curl_ignore, /* 0 */ + curl_string, /* 1 file | writedata */ + curl_string, /* 2 url */ + curl_integer, /* 3 port */ + curl_string, /* 4 proxy */ + curl_string, /* 5 userpwd */ + curl_string, /* 6 proxyuserpwd */ + curl_string, /* 7 range */ + curl_ignore, /* 8 */ + curl_string, /* 9 infile | readdata */ + curl_string, /* 10 errorbuffer */ + curl_function, /* 11 writefunction */ + curl_function, /* 12 readfunction */ + curl_integer, /* 13 timeout */ + curl_integer, /* 14 infilesize */ + curl_string, /* 15 postfields */ + curl_string, /* 16 referer */ + curl_string, /* 17 ftpport */ + curl_string, /* 18 useragent */ + curl_integer, /* 19 low_speed_limit */ + curl_integer, /* 20 low_speed_time */ + curl_integer, /* 21 resume_from */ + curl_string, /* 22 cookie */ + curl_string, /* 23 httpheader | rtspheader */ + curl_string, /* 24 httppost */ + curl_string, /* 25 sslcert */ + curl_string, /* 26 keypasswd */ + curl_integer, /* 27 crlf */ + curl_string, /* 28 quote */ + curl_string, /* 29 writeheader | headerdata */ + curl_ignore, /* 30 */ + curl_string, /* 31 cookiefile */ + curl_integer, /* 32 sslversion */ + curl_integer, /* 33 timecondition */ + curl_integer, /* 34 timevalue */ + curl_ignore, /* 35 */ + curl_string, /* 36 customrequest */ + curl_string, /* 37 stderr */ + curl_ignore, /* 38 */ + curl_string, /* 39 postquote */ + curl_string, /* 40 writeinfo */ + curl_integer, /* 41 verbose */ + curl_integer, /* 42 header */ + curl_integer, /* 43 noprogress */ + curl_integer, /* 44 nobody */ + curl_integer, /* 45 failonerror */ + curl_integer, /* 46 upload */ + curl_integer, /* 47 post */ + curl_integer, /* 48 dirlistonly */ + curl_ignore, /* 49 */ + curl_integer, /* 50 append */ + curl_integer, /* 51 netrc */ + curl_integer, /* 52 followlocation */ + curl_integer, /* 53 transfertext */ + curl_integer, /* 54 put */ + curl_ignore, /* 55 */ + curl_function, /* 56 progressfunction */ + curl_string, /* 57 xferinfodata | progressdata */ + curl_integer, /* 58 autoreferer */ + curl_integer, /* 59 proxyport */ + curl_integer, /* 60 postfieldsize */ + curl_integer, /* 61 httpproxytunnel */ + curl_string, /* 62 interface */ + curl_string, /* 63 krblevel */ + curl_integer, /* 64 ssl_verifypeer */ + curl_string, /* 65 cainfo */ + curl_ignore, /* 66 */ + curl_ignore, /* 67 */ + curl_integer, /* 68 maxredirs */ + curl_integer, /* 69 filetime */ + curl_string, /* 70 telnetoptions */ + curl_integer, /* 71 maxconnects */ + curl_integer, /* 72 closepolicy */ + curl_ignore, /* 73 */ + curl_integer, /* 74 fresh_connect */ + curl_integer, /* 75 forbid_reuse */ + curl_string, /* 76 random_file */ + curl_string, /* 77 egdsocket */ + curl_integer, /* 78 connecttimeout */ + curl_function, /* 79 headerfunction */ + curl_integer, /* 80 httpget */ + curl_integer, /* 81 ssl_verifyhost */ + curl_string, /* 82 cookiejar */ + curl_string, /* 83 ssl_cipher_list */ + curl_integer, /* 84 http_version */ + curl_integer, /* 85 ftp_use_epsv */ + curl_string, /* 86 sslcerttype */ + curl_string, /* 87 sslkey */ + curl_string, /* 88 sslkeytype */ + curl_string, /* 89 sslengine */ + curl_integer, /* 90 sslengine_default */ + curl_integer, /* 91 dns_use_global_cache */ + curl_integer, /* 92 dns_cache_timeout */ + curl_string, /* 93 prequote */ + curl_function, /* 94 debugfunction */ + curl_string, /* 95 debugdata */ + curl_integer, /* 96 cookiesession */ + curl_string, /* 97 capath */ + curl_integer, /* 98 buffersize */ + curl_integer, /* 99 nosignal */ + curl_string, /* 100 share */ + curl_integer, /* 101 proxytype */ + curl_string, /* 102 accept_encoding */ + curl_string, /* 103 private */ + curl_string, /* 104 http200aliases */ + curl_integer, /* 105 unrestricted_auth */ + curl_integer, /* 106 ftp_use_eprt */ + curl_integer, /* 107 httpauth */ + curl_function, /* 108 ssl_ctx_function */ + curl_string, /* 109 ssl_ctx_data */ + curl_integer, /* 110 ftp_create_missing_dirs */ + curl_integer, /* 111 proxyauth */ + curl_integer, /* 112 server_response_timeout | ftp_response_timeout */ + curl_integer, /* 113 ipresolve */ + curl_integer, /* 114 maxfilesize */ + curl_offset, /* 115 infilesize_large */ + curl_offset, /* 116 resume_from_large */ + curl_offset, /* 117 maxfilesize_large */ + curl_string, /* 118 netrc_file */ + curl_integer, /* 119 use_ssl */ + curl_offset, /* 120 postfieldsize_large */ + curl_integer, /* 121 tcp_nodelay */ + curl_ignore, /* 122 */ + curl_ignore, /* 123 */ + curl_ignore, /* 124 */ + curl_ignore, /* 125 */ + curl_ignore, /* 126 */ + curl_ignore, /* 127 */ + curl_ignore, /* 128 */ + curl_integer, /* 129 ftpsslauth */ + curl_function, /* 130 ioctlfunction */ + curl_string, /* 131 ioctldata */ + curl_ignore, /* 132 */ + curl_ignore, /* 133 */ + curl_string, /* 134 ftp_account */ + curl_string, /* 135 cookielist */ + curl_integer, /* 136 ignore_content_length */ + curl_integer, /* 137 ftp_skip_pasv_ip */ + curl_integer, /* 138 ftp_filemethod */ + curl_integer, /* 139 localport */ + curl_integer, /* 140 localportrange */ + curl_integer, /* 141 connect_only */ + curl_function, /* 142 conv_from_network_function */ + curl_function, /* 143 conv_to_network_function */ + curl_function, /* 144 conv_from_utf8_function */ + curl_offset, /* 145 max_send_speed_large */ + curl_offset, /* 146 max_recv_speed_large */ + curl_string, /* 147 ftp_alternative_to_user */ + curl_function, /* 148 sockoptfunction */ + curl_string, /* 149 sockoptdata */ + curl_integer, /* 150 ssl_sessionid_cache */ + curl_integer, /* 151 ssh_auth_types */ + curl_string, /* 152 ssh_public_keyfile */ + curl_string, /* 153 ssh_private_keyfile */ + curl_integer, /* 154 ftp_ssl_ccc */ + curl_integer, /* 155 timeout_ms */ + curl_integer, /* 156 connecttimeout_ms */ + curl_integer, /* 157 http_transfer_decoding */ + curl_integer, /* 158 http_content_decoding */ + curl_integer, /* 159 new_file_perms */ + curl_integer, /* 160 new_directory_perms */ + curl_integer, /* 161 postredir */ + curl_string, /* 162 ssh_host_public_key_md5 */ + curl_function, /* 163 opensocketfunction */ + curl_string, /* 164 opensocketdata */ + curl_string, /* 165 copypostfields */ + curl_integer, /* 166 proxy_transfer_mode */ + curl_function, /* 167 seekfunction */ + curl_string, /* 168 seekdata */ + curl_string, /* 169 crlfile */ + curl_string, /* 170 issuercert */ + curl_integer, /* 171 address_scope */ + curl_integer, /* 172 certinfo */ + curl_string, /* 173 username */ + curl_string, /* 174 password */ + curl_string, /* 175 proxyusername */ + curl_string, /* 176 proxypassword */ + curl_string, /* 177 noproxy */ + curl_integer, /* 178 tftp_blksize */ + curl_string, /* 179 socks5_gssapi_service */ + curl_integer, /* 180 socks5_gssapi_nec */ + curl_integer, /* 181 protocols */ + curl_integer, /* 182 redir_protocols */ + curl_string, /* 183 ssh_knownhosts */ + curl_function, /* 184 ssh_keyfunction */ + curl_string, /* 185 ssh_keydata */ + curl_string, /* 186 mail_from */ + curl_string, /* 187 mail_rcpt */ + curl_integer, /* 188 ftp_use_pret */ + curl_integer, /* 189 rtsp_request */ + curl_string, /* 190 rtsp_session_id */ + curl_string, /* 191 rtsp_stream_uri */ + curl_string, /* 192 rtsp_transport */ + curl_integer, /* 193 rtsp_client_cseq */ + curl_integer, /* 194 rtsp_server_cseq */ + curl_string, /* 195 interleavedata */ + curl_function, /* 196 interleavefunction */ + curl_integer, /* 197 wildcardmatch */ + curl_function, /* 198 chunk_bgn_function */ + curl_function, /* 199 chunk_end_function */ + curl_function, /* 200 fnmatch_function */ + curl_string, /* 201 chunk_data */ + curl_string, /* 202 fnmatch_data */ + curl_string, /* 203 resolve */ + curl_string, /* 204 tlsauth_username */ + curl_string, /* 205 tlsauth_password */ + curl_string, /* 206 tlsauth_type */ + curl_integer, /* 207 transfer_encoding */ + curl_function, /* 208 closesocketfunction */ + curl_string, /* 209 closesocketdata */ + curl_integer, /* 210 gssapi_delegation */ + curl_string, /* 211 dns_servers */ + curl_integer, /* 212 accepttimeout_ms */ + curl_integer, /* 213 tcp_keepalive */ + curl_integer, /* 214 tcp_keepidle */ + curl_integer, /* 215 tcp_keepintvl */ + curl_integer, /* 216 ssl_options */ + curl_string, /* 217 mail_auth */ + curl_integer, /* 218 sasl_ir */ + curl_function, /* 219 xferinfofunction */ + curl_string, /* 220 xoauth2_bearer */ + curl_string, /* 221 dns_interface */ + curl_string, /* 222 dns_local_ip4 */ + curl_string, /* 223 dns_local_ip6 */ + curl_string, /* 224 login_options */ + curl_integer, /* 225 ssl_enable_npn */ + curl_integer, /* 226 ssl_enable_alpn */ + curl_integer /* 227 expect_100_timeout_ms */ +}; + +# define curl_option_min 1 +# define curl_option_max 227 +# define curl_option_writedata 1 +# define curl_option_url 2 +# define curl_option_writefunction 11 + +# define curl_integer_base 0 /* long */ +# define curl_string_base 10000 +# define curl_object_base 10000 +# define curl_function_base 20000 +# define curl_offset_base 30000 +# define curl_offset_blob 40000 + +typedef size_t (*curl_write_callback) ( + char *buffer, + size_t size, + size_t nitems, + void *userdata +); + +typedef struct curllib_state_info { + + int initialized; + int padding; + + char * (*curl_version) ( + void + ); + + void (*curl_free) ( + void* p + ); + + curl_instance (*curl_easy_init) ( + void + ); + + void (*curl_easy_cleanup) ( + curl_instance handle + ); + + curl_return_code (*curl_easy_perform) ( + curl_instance handle + ); + + curl_return_code (*curl_easy_setopt) ( + curl_instance handle, + int option, + ... + ); + + char* (*curl_easy_escape) ( + curl_instance handle, + const char *url, + int length + ); + + char* (*curl_easy_unescape) ( + curl_instance handle, + const char *url, + int length, + int *outlength + ); + + const char* (*curl_easy_strerror) ( + curl_error_code errcode + ); + +} curllib_state_info; + +static curllib_state_info curllib_state = { + + .initialized = 0, + .padding = 0, + + .curl_version = NULL, + .curl_free = NULL, + .curl_easy_init = NULL, + .curl_easy_cleanup = NULL, + .curl_easy_perform = NULL, + .curl_easy_setopt = NULL, + .curl_easy_escape = NULL, + .curl_easy_unescape = NULL, + .curl_easy_strerror = NULL, + +}; + +static int curllib_initialize(lua_State * L) +{ + if (! curllib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + curllib_state.curl_version = lmt_library_find(lib, "curl_version"); + curllib_state.curl_free = lmt_library_find(lib, "curl_free"); + curllib_state.curl_easy_init = lmt_library_find(lib, "curl_easy_init"); + curllib_state.curl_easy_cleanup = lmt_library_find(lib, "curl_easy_cleanup"); + curllib_state.curl_easy_perform = lmt_library_find(lib, "curl_easy_perform"); + curllib_state.curl_easy_setopt = lmt_library_find(lib, "curl_easy_setopt"); + curllib_state.curl_easy_escape = lmt_library_find(lib, "curl_easy_escape"); + curllib_state.curl_easy_unescape = lmt_library_find(lib, "curl_easy_unescape"); + curllib_state.curl_easy_strerror = lmt_library_find(lib, "curl_easy_strerror"); + + curllib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, curllib_state.initialized); + return 1; +} + +/* fetch(url, { options }) | fetch({ options }) */ + +/* we don't need threads so we can just use the local init */ + +static size_t curllib_write_cb(char *data, size_t n, size_t l, void *b) +{ + luaL_addlstring((luaL_Buffer *) b, data, n * l); + return n * l; +} + +/*tex + Always assume a table as we need to sanitize keys anyway. A former variant also accepted strings + but why have more code than needed. +*/ + +static int curllib_fetch(lua_State * L) +{ + if (curllib_state.initialized) { + if (lua_type(L,1) == LUA_TTABLE) { + curl_instance *curl = curllib_state.curl_easy_init(); + if (curl) { + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + curllib_state.curl_easy_setopt(curl, curl_object_base + curl_option_writedata, &buffer); + curllib_state.curl_easy_setopt(curl, curl_function_base + curl_option_writefunction, &curllib_write_cb); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1) != 0) { + if (lua_type(L, -2) == LUA_TNUMBER) { + int o = lmt_tointeger(L, -2); + if (o >= curl_option_min && o <= curl_option_max) { + switch (curl_options[o]) { + case curl_string: + if (lua_type(L, -1) == LUA_TSTRING) { + curllib_state.curl_easy_setopt(curl, curl_string_base + o, lua_tostring(L, -1)); + } else { + // return luaL_error(L, "curl option %d must be a string", o); + } + break; + case curl_integer: + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + curllib_state.curl_easy_setopt(curl, curl_integer_base + o, lua_tointeger(L, -1)); + break; + case LUA_TBOOLEAN: + curllib_state.curl_easy_setopt(curl, curl_integer_base + o, lua_toboolean(L, -1)); + break; + default: + // return luaL_error(L, "curl option %d must be a number of boolean", o); + break; + } + break; + } + } else { + // return luaL_error(L, "curl option %d is invalid", o); + } + } else { + // return luaL_error(L, "curl option id should en a number"); + } + lua_pop(L, 1); /* removes 'value' and keeps 'key' for next iteration */ + } + int result = curllib_state.curl_easy_perform(curl); + if (result) { + lua_pushboolean(L, 0); + lua_pushstring(L, curllib_state.curl_easy_strerror(result)); + result = 2; + } else { + luaL_pushresult(&buffer); + result = 1; + } + curllib_state.curl_easy_cleanup(curl); + return result; + } + } + } + return 0; +} + +static int curllib_escape(lua_State * L) +{ + if (curllib_state.initialized) { + curl_instance *curl = curllib_state.curl_easy_init(); + if (curl) { + size_t length = 0; + const char * url = lua_tolstring(L, 1, &length); + char *s = curllib_state.curl_easy_escape(curl, url, (int) length); + if (s) { + lua_pushstring(L,(const char *) s); + curllib_state.curl_free(s); + curllib_state.curl_easy_cleanup(curl); + return 1; + } + } + } + return 0; +} + +static int curllib_unescape(lua_State * L) +{ + if (curllib_state.initialized) { + curl_instance *curl = curllib_state.curl_easy_init(); + if (curl) { + size_t length = 0; + const char *url = lua_tolstring(L, 1, &length); + int l = 0; + char *s = curllib_state.curl_easy_unescape(curl, url, (int) length, &l); + if (s) { + lua_pushlstring(L, s, l); + curllib_state.curl_free(s); + curllib_state.curl_easy_cleanup(curl); + return 1; + } + } + } + return 0; +} + +static int curllib_getversion(lua_State * L) +{ + if (curllib_state.initialized) { + char *version = curllib_state.curl_version(); + if (version) { + lua_pushstring(L, version); + return 1; + } + } + return 0; +} + +static struct luaL_Reg curllib_function_list[] = { + { "initialize", curllib_initialize }, + { "fetch", curllib_fetch }, + { "escape", curllib_escape }, + { "unescape", curllib_unescape }, + { "getversion", curllib_getversion }, + { NULL, NULL }, +}; + +int luaopen_curl(lua_State * L) +{ + lmt_library_register(L, "curl", curllib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtforeign.c b/source/luametatex/source/luaoptional/lmtforeign.c new file mode 100644 index 000000000..da04eca12 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtforeign.c @@ -0,0 +1,1191 @@ +/* + See license.txt in the root of this project. +*/ + +/*tex + + In \LUATEX\ we provide an ffi library that is derived from luajit but it got orphaned a few years + after it showed up. A problem with such libraries is that they need to be maintained actively + because platforms and processors evolve. So, having a mechanism like that makes not much sense in + a \TEX\ engine. In \LUAMETATEX\ we therefore don't use that library but have a model for delayed + loading of optional libraries. A few interfaces are built in (like zint) but we don't ship any + library: you get what is installed on the system (which actually is the whole idea behind using + most libraries). Delayed loading is implemented by using function pointers and in practice that + is fast enough (after all we use them in \TEX\ as well as \METAPOST\ without much penalty). + + But \unknown\ what if a user wants to use a library and doesn't want to write an interface? We + kind of end up again with something ffi. When looking around I ran into an \LUA\ module that + was made a few years ago (for 5.1 and 5.2) called 'alien' that also binds to libffi. But that + library looks a bit more complex than we need. For instance, mechanisms for callbacks use libffi + calls than need some compile time properties that normally come from the h files generated on + the system and I don't want to impose on users to also install and compile a whole chain of + dependencies. We could (and maybe some day will if users really need it) provide callbacks + too but then we also need to keep some system specific 'constants' in sync. We then probaly + also need to provide libraries at the contextgarden. + + The basics of an interface as used in this module (running over specs) is given in the \LUA\ + manual\ and we also use it for callbacks in \LUATEX\ and therefore \LUAMETATEX. There we use + varargs but here specification if converted into a recipe that libffi will bind to a function. + Some code below looks like the code in alien (after all I took a good look at it). The ffi + part is filtered from the ffi.h.in file. The interfaces are sort of what we do with other + libraries. + + For the record: when testing, I just used a version of libffi that came with inkscape and I + saw several other instances on my system disk. A quick test with loading showed that it is no + problem in the ecosystem that we use in \TEX. The buildot on the contextgarden generates + binaries for several platforms and one can observe that some platforms (like bsd) are not + that downward compatible, so there we have multiple versions. This also means that finding + matching libraries can be an issue. In \CONTEXT\ we never depend on external or evolving + libraries so it's a user's choice in the end. + + The \LUATEX\ build script and infrastructure are more complex than the \LUAMETATEX\ ones and + it's often the libraries that make for the - sometimes incompatible - changes which in turn + demands adaptation of the scripts etc in the build farm. We try to avoid that as much as + possible but if we ever decide to also provide libraries that match the binaries, but it + remains a depencenie that you want to avoid in long running projects. + + Comment: I might look into a vararg variant some day, just for fun. Actually, this module is + mostly about the fun, so it will take some time to evolve. + +*/ + +/*tex + Because it is an optional module, we use the optional interface. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +/*tex + We need to define a few ffi datatypes and function prototypes. We need to keep an eye on how + the library evolves but I assume the api is rather stable. We don't want to depend on a system + specific header file. +*/ + +typedef struct ffi_type { + size_t size; + unsigned short alignment; + unsigned short type; + struct ffi_type **elements; +} ffi_type; + +typedef enum ffi_types { + ffi_void_type, + ffi_int_type, + ffi_float_type, + ffi_double_type, + ffi_longdouble_type, + ffi_uint8_type, + ffi_int8_type, + ffi_uint16_type, + ffi_int16_type, + ffi_uint32_type, + ffi_int32_type, + ffi_uint64_type, + ffi_int64_type, + ffi_struct_type, + ffi_pointer_type, + ffi_complex_type, /* unsupported */ + ffi_last_type, +} ffi_types; + +/* + The libffi api document says that the size and alignment should be zero but somehow we do crash + when we set the size to some value. Only size_t is now system dependent (e.g. on 32 bit windows + it's different). + + We only need to support the architectures and operating systems that the ecosystem runs on so we + check a bit differently. We just don't want all these dependencies in the source tree. We have: + + -- 32 64 bit intel linux | freebsd | openbsd + -- 64 bit intel osx + -- 32 64 bit intel windows mingw + -- 64 bit windows msvc + -- 64 bit arm msvc + -- 32 64 bit arm (rpi etc) + -- 64 bit arm darwin + +*/ + +# if PTRDIFF_MAX == 65535 +# define ffi_size_t_type ffi_uint16_type +# elif PTRDIFF_MAX == 2147483647 +# define ffi_size_t_type ffi_uint32_type +# elif PTRDIFF_MAX == 9223372036854775807 +# define ffi_size_t_type ffi_uint64_type +# elif defined(_WIN64) +# define ffi_size_t_type ffi_uint64_type +# else +# define ffi_size_t_type ffi_uint32_type +# endif + +/*tex This comes from the libffi.h* file: */ + +typedef enum ffi_abi { + +# if defined (X86_WIN64) + + FFI_FIRST_ABI = 0, + FFI_WIN64, /* sizeof(long double) == 8 - microsoft compilers */ + FFI_GNUW64, /* sizeof(long double) == 16 - GNU compilers */ + FFI_LAST_ABI, +# ifdef __GNUC__ + FFI_DEFAULT_ABI = FFI_GNUW64 +# else + FFI_DEFAULT_ABI = FFI_WIN64 +# endif + +# elif defined (X86_64) || (defined (__x86_64__) && defined (X86_DARWIN)) + + FFI_FIRST_ABI = 1, + FFI_UNIX64, + FFI_WIN64, + FFI_EFI64 = FFI_WIN64, + FFI_GNUW64, + FFI_LAST_ABI, + FFI_DEFAULT_ABI = FFI_UNIX64 + +# elif defined (X86_WIN32) + + FFI_FIRST_ABI = 0, + FFI_SYSV = 1, + FFI_STDCALL = 2, + FFI_THISCALL = 3, + FFI_FASTCALL = 4, + FFI_MS_CDECL = 5, + FFI_PASCAL = 6, + FFI_REGISTER = 7, + FFI_LAST_ABI, + FFI_DEFAULT_ABI = FFI_MS_CDECL + +# else + + FFI_FIRST_ABI = 0, + FFI_SYSV = 1, + FFI_THISCALL = 3, + FFI_FASTCALL = 4, + FFI_STDCALL = 5, + FFI_PASCAL = 6, + FFI_REGISTER = 7, + FFI_MS_CDECL = 8, + FFI_LAST_ABI, + FFI_DEFAULT_ABI = FFI_SYSV + +#endif + +} ffi_abi; + +typedef enum ffi_status { + FFI_OK, + FFI_BAD_TYPEDEF, + FFI_BAD_ABI +} ffi_status; + +typedef struct { + ffi_abi abi; + unsigned nargs; + ffi_type **arg_types; + ffi_type *rtype; + unsigned bytes; + unsigned flags; +} ffi_cif; + +typedef struct foreign_state_info { + + int initialized; + int padding; + + ffi_status (*ffi_prep_cif) ( + ffi_cif *cif, + ffi_abi abi, + unsigned int nargs, + ffi_type *rtype, + ffi_type **atypes + ); + + void (*ffi_call) ( + ffi_cif *cif, + void (*fn) (void), + void *rvalue, + void **avalue + ); + + ffi_type ffi_type_void; + ffi_type ffi_type_uint8; + ffi_type ffi_type_int8; + ffi_type ffi_type_uint16; + ffi_type ffi_type_int16; + ffi_type ffi_type_uint32; + ffi_type ffi_type_int32; + ffi_type ffi_type_uint64; + ffi_type ffi_type_int64; + ffi_type ffi_type_float; + ffi_type ffi_type_double; + ffi_type ffi_type_pointer; + ffi_type ffi_type_size_t; + + +} foreign_state_info; + +static foreign_state_info foreign_state = { + + .initialized = 0, + .padding = 0, + + .ffi_prep_cif = NULL, + .ffi_call = NULL, + + .ffi_type_void = { .size = 1, .alignment = 0, .type = ffi_void_type, .elements = NULL }, + .ffi_type_uint8 = { .size = sizeof(uint8_t), .alignment = 0, .type = ffi_uint8_type, .elements = NULL }, + .ffi_type_int8 = { .size = sizeof(int8_t), .alignment = 0, .type = ffi_int8_type, .elements = NULL }, + .ffi_type_uint16 = { .size = sizeof(uint16_t), .alignment = 0, .type = ffi_uint16_type, .elements = NULL }, + .ffi_type_int16 = { .size = sizeof(int16_t), .alignment = 0, .type = ffi_int16_type, .elements = NULL }, + .ffi_type_uint32 = { .size = sizeof(uint32_t), .alignment = 0, .type = ffi_uint32_type, .elements = NULL }, + .ffi_type_int32 = { .size = sizeof(int32_t), .alignment = 0, .type = ffi_int32_type, .elements = NULL }, + .ffi_type_uint64 = { .size = sizeof(uint64_t), .alignment = 0, .type = ffi_uint64_type, .elements = NULL }, + .ffi_type_int64 = { .size = sizeof(int64_t), .alignment = 0, .type = ffi_int64_type, .elements = NULL }, + .ffi_type_float = { .size = sizeof(float), .alignment = 0, .type = ffi_float_type, .elements = NULL }, + .ffi_type_double = { .size = sizeof(double), .alignment = 0, .type = ffi_double_type, .elements = NULL }, + .ffi_type_pointer = { .size = sizeof(void *), .alignment = 0, .type = ffi_pointer_type, .elements = NULL }, + .ffi_type_size_t = { .size = sizeof(size_t), .alignment = 0, .type = ffi_size_t_type, .elements = NULL }, + +}; + +/*tex + We use similar names as in other modules: +*/ + +#define FOREIGN_METATABLE_LIBRARY "foreign.library" +#define FOREIGN_METATABLE_FUNCTION "foreign.function" +#define FOREIGN_METATABLE_POINTER "foreign.pointer" + +/*tex + First I had some info structure as we have elsewhere but in the end not much was needed so we + now have some simple arrays instead. +*/ + +typedef enum foreign_type { + foreign_type_void, + foreign_type_byte, foreign_type_char, + foreign_type_short, foreign_type_ushort, + foreign_type_int, foreign_type_uint, + foreign_type_long, foreign_type_ulong, + foreign_type_longlong, foreign_type_ulonglong, + foreign_type_float, foreign_type_double, + foreign_type_size_t, + foreign_type_string, + foreign_type_pointer, + foreign_type_reference_to_char, + foreign_type_reference_to_int, + foreign_type_reference_to_uint, + foreign_type_reference_to_double, + foreign_type_max, +} foreign_type; + +# define foreign_first_value_return_type foreign_type_void +# define foreign_last_value_return_type foreign_type_pointer + +static const char *foreign_typenames[] = { + "void", + /* basic types */ + "byte", "char", + "short", "ushort", + "int", "uint", + "long", "ulong", + "longlong", "ulonglong", + "float", "double", + "size_t", + "string", + "pointer", + "reference to char", + "reference to int", + "reference to uint", + "reference to double", + NULL, +}; + +static ffi_type *foreign_typecodes[] = { + &foreign_state.ffi_type_void, + &foreign_state.ffi_type_int8, &foreign_state.ffi_type_uint8, + &foreign_state.ffi_type_int16, &foreign_state.ffi_type_uint16, + &foreign_state.ffi_type_int32, &foreign_state.ffi_type_uint32, + &foreign_state.ffi_type_int64, &foreign_state.ffi_type_uint64, + &foreign_state.ffi_type_int64, &foreign_state.ffi_type_uint64, + &foreign_state.ffi_type_float, &foreign_state.ffi_type_double, + &foreign_state.ffi_type_size_t, + &foreign_state.ffi_type_pointer, /* string */ + &foreign_state.ffi_type_pointer, /* pointer */ + &foreign_state.ffi_type_pointer, + &foreign_state.ffi_type_pointer, + &foreign_state.ffi_type_pointer, + &foreign_state.ffi_type_pointer, + NULL, +}; + +typedef struct foreign_library { + void *library; + char *name; + ffi_abi abi; + int padding; +} foreign_library; + +typedef enum foreign_states { + foreign_state_initialized, + foreign_state_registered, +} foreign_states; + +typedef struct foreign_function { + foreign_library *library; + char *name; + void *function; + foreign_type result_type; + int nofarguments; + foreign_type *arguments; + ffi_type *ffi_result_type; + ffi_type **ffi_arguments; + ffi_cif cif; + ffi_abi abi; +} foreign_function; + +typedef enum foreign_pointer_types { + foreign_pointer_state_regular, + foreign_pointer_state_buffer, +} foreign_pointer_types; + +typedef struct foreign_pointer { + void *ptr; + int state; + int padding; +} foreign_pointer; + +/*tex + We use the already defined helpers instead of setting up loading here. That way we're also + consistent in lookups. You need to pass the resolved name (so at the \LUA\ end we wrap the + loader to use the library resolver. So no check for loaders here etc. +*/ + + +#ifdef WIN32 +# ifndef WINDOWS +# define WINDOWS +# endif +#endif + +#if !defined(WINDOWS) || defined(_WIN64) +#define FFI_STDCALL FFI_DEFAULT_ABI +#endif + +#ifdef __APPLE__ +#define FFI_SYSV FFI_DEFAULT_ABI +#endif + +typedef struct foreign_abi_entry { + const char *name; + ffi_abi abi; +} foreign_abi_entry; + +# define foreign_abi_max 3 + +static foreign_abi_entry foreign_abi_map[] = { + { .name = "default", .abi = FFI_DEFAULT_ABI }, + { .name = "cdecl", .abi = FFI_SYSV }, + { .name = "stdcall", .abi = FFI_STDCALL }, +}; + +typedef enum foreign_library_uv_slots { + library_name_uv = 1, + library_registry_uv = 2, + +} foreign_library_uv_slots; + +typedef enum foreign_function_uv_slots { + function_name_uv = 1, + function_finalizer_uv = 2, +} foreign_function_uv_slots; + +static int foreignlib_not_yet_initialized(lua_State *L) +{ + return luaL_error(L, "foreign: not yet initialized"); +} + +static int foreignlib_allocation_error(lua_State *L) +{ + return luaL_error(L, "foreign: allocation error"); +} + +static foreign_library *foreignlib_library_check(lua_State *L, int index) +{ + return (foreign_library *) luaL_checkudata(L, index, FOREIGN_METATABLE_LIBRARY); +} + +static foreign_function *foreignlib_function_check(lua_State *L, int index) +{ + return (foreign_function *) luaL_checkudata(L, index, FOREIGN_METATABLE_FUNCTION); +} + +static foreign_pointer *foreignlib_pointer_check(lua_State *L, int index) +{ + return (foreign_pointer *) luaL_checkudata(L, index, FOREIGN_METATABLE_POINTER); +} + +static int foreignlib_library_tostring(lua_State *L) +{ + foreign_library *library = foreignlib_library_check(L, 1); + if (library) { + lua_pushfstring(L, "<foreign.library %s>", library->name ? library->name : "unknown"); + return 1; + } else { + return 0; + } +} + +static int foreignlib_function_tostring(lua_State *L) +{ + foreign_function *function = foreignlib_function_check(L, 1); + if (function) { + foreign_library *library = function->library; + if (library) { + lua_pushfstring(L, "<foreign.function %s in library %s>", function->name ? function->name : "unknown", ((library && library->name) ? library->name : "unknown")); + return 1; + } + } + return 0; +} + +static int foreignlib_pointer_tostring(lua_State *L) +{ + foreign_pointer *pointer = foreignlib_pointer_check(L, 1); + if (! pointer) { + return 0; + } else { + lua_pushfstring(L, pointer->state == foreign_pointer_state_buffer ? "<foreign.buffer %p>" : "<foreign.pointer %p>", pointer->ptr); + return 1; + } +} + +static int foreignlib_pointer_gc(lua_State *L) +{ + foreign_pointer *pointer = foreignlib_pointer_check(L, 1); + if (pointer->state == foreign_pointer_state_buffer) { + lmt_memory_free(pointer->ptr); + /* not needed: */ + pointer->state = foreign_pointer_state_regular; + pointer->ptr = NULL; + } + return 0; +} + +/*tex + We accept numbers as well as names (just in case go symboloc as we do with other modules). +*/ + +static int foreignlib_type_found(lua_State *L, int slot, int dflt) +{ + switch (lua_type(L, slot)) { + case LUA_TNUMBER: + { + int i = (int) lua_tointeger(L, slot); + if (i >= 0 && i < foreign_type_max) { + return i; + } + break; + } + case LUA_TSTRING: + { + const char *s = lua_tostring(L, slot); + for (int i = 0; i < foreign_type_max; i++) { + if (strcmp(s, foreign_typenames[i]) == 0) { + return i; + } + } + break; + } + } + return dflt; +} + +static int foreignlib_abi_found(lua_State *L, int slot, int dflt) +{ + switch (lua_type(L, slot)) { + case LUA_TNUMBER: + { + int i = (int) lua_tointeger(L, slot); + if (i >= 0 && i < foreign_abi_max) { + return foreign_abi_map[i].abi; + } + break; + } + case LUA_TSTRING: + { + const char *s = lua_tostring(L, slot); + for (int i = 0; i < foreign_abi_max; i++) { + if (strcmp(s, foreign_abi_map[i].name) == 0) { + return foreign_abi_map[i].abi; + } + } + break; + } + } + return dflt; +} + +static int foreignlib_types(lua_State* L) +{ + lua_createtable(L, foreign_type_max, 0); + for (lua_Integer i = 0; i < foreign_type_max; i++) { + lua_pushstring(L, foreign_typenames[i]); + lua_rawseti(L, -2, i + 1); + } + return 1; +} + +static int foreignlib_abivalues(lua_State* L) +{ + lua_createtable(L, 0, foreign_abi_max); + for (lua_Integer i = 0; i < foreign_abi_max; i++) { + lua_pushstring(L, foreign_abi_map[i].name); + lua_pushinteger(L, foreign_abi_map[i].abi); + lua_rawset(L, -3); + } + return 1; +} + +static int foreignlib_load(lua_State *L) +{ + if (foreign_state.initialized) { + size_t len; + const char *libraryname = lua_tolstring(L, 1, &len); + if (libraryname && len > 0) { + foreign_library *library = (foreign_library *) lua_newuserdatauv(L, sizeof(foreign_library), 2); + if (library) { + void *libraryreference = lmt_library_open_indeed(libraryname); + if (libraryreference) { + library->name = lmt_memory_malloc(sizeof(char) * (len + 1)); + if (library->name) { + strcpy(library->name, libraryname); + library->library = libraryreference; + library->abi = foreignlib_abi_found(L, 2, FFI_DEFAULT_ABI); + lua_pushvalue(L, 1); + lua_setiuservalue(L, -2, library_name_uv); + lua_newtable(L); + lua_setiuservalue(L, -2, library_registry_uv); + luaL_getmetatable(L, FOREIGN_METATABLE_LIBRARY); + lua_setmetatable(L, -2); + return 1; + } else { + goto ALLOCATION_ERROR; + } + } else { + return luaL_error(L, "foreign: invalid library"); + } + } else { + goto ALLOCATION_ERROR; + } + } else { + return luaL_error(L, "foreign: invalid library name"); + } + ALLOCATION_ERROR: + return foreignlib_allocation_error(L); + } else { + return foreignlib_not_yet_initialized(L); + } +} + +static int foreignlib_library_register(lua_State *L) +{ + if (foreign_state.initialized) { + /* 1:library 2:specification */ + foreign_library *library = foreignlib_library_check(L, 1); + if (lua_type(L, 2) == LUA_TTABLE) { + /* 1:library 2:specification -1:name */ + if (lua_getfield(L, 2, "name") == LUA_TSTRING) { + /* 1:library 2:specification -2:name */ + lua_getiuservalue(L, 1, library_registry_uv); + /* 1:library 2:specification -2:name -1:registry */ + lua_pushvalue(L, -2); + /* 1:library 2:specification -3:name -2:registry -1:name */ + lua_rawget(L, -2); + if (lua_type(L, -1) == LUA_TUSERDATA) { + /* 1:library 2:specification -3:name -2:registry -1:function */ + return 1; + } else { + /* 1:library 2:specification -3:name -2:registry -1:nil */ + size_t len; + const char *functionname = lua_tolstring(L, -3, &len); + void *functionreference = lmt_library_find_indeed(library->library, functionname); + lua_pop(L, 1); + if (functionreference) { + /* 1:library 2:specification -2:name -1:registry */ + foreign_function *function = (foreign_function *) lua_newuserdatauv(L, sizeof(foreign_function), 2); + if (function) { + /* 1:library 2:specification -3:name -2:registry -1:function */ + lua_pushvalue(L, -3); + /* 1:library 2:specification -4:name -3:registry -2:function -1:name */ + lua_pushvalue(L, -2); + /* 1:library 2:specification -5:name -4:registry -3:function -2:name -1:function */ + lua_rawset(L, -4); + /* 1:library 2:specification -3:name -2:registry -1:function */ + lua_pushvalue(L, -3); + /* 1:library 2:specification -4:name -3:registry -2:function -1:name */ + lua_setiuservalue(L, -2, function_name_uv); + lua_getfield(L, 2, "finalizer"); + /* 1:library 2:specification -4:name -3:registry -2:function -1:finalizer */ + lua_setiuservalue(L, -2, function_finalizer_uv); + /* 1:library 2:specification -3:name -2:registry -1:function */ + luaL_getmetatable(L, FOREIGN_METATABLE_FUNCTION); + /* 1:library 2:specification -4:name -3:registry -2:function -1:metatable */ + lua_setmetatable(L, -2); + /* 1:library 2:specification -3:name -2:registry -1:function */ + function->name = (char *) lmt_memory_malloc((size_t) len + 1); + if (function->name) { + strcpy(function->name, functionname); + function->function = functionreference; + function->library = library; + function->arguments = NULL; + function->ffi_arguments = NULL; + /* set the return type */ + lua_getfield(L, 2, "result"); + function->result_type = foreignlib_type_found(L, -1, foreign_type_void); + if (function->result_type >= foreign_first_value_return_type && function->result_type <= foreign_last_value_return_type) { + function->ffi_result_type = foreign_typecodes[function->result_type]; + lua_pop(L, 1); + /* set the abi (will move to library) */ + lua_getfield(L, 2, "abi"); + function->abi = foreignlib_abi_found(L, -1, library->abi); + lua_pop(L, 1); + /* set the argument types */ + switch (lua_getfield(L, 2, "arguments")) { + case LUA_TTABLE: + { + function->nofarguments = (int) lua_rawlen(L, -1); + if (function->nofarguments > 0) { + function->ffi_arguments = (ffi_type **) lmt_memory_malloc(function->nofarguments * sizeof(ffi_type *)); + function->arguments = (foreign_type *) lmt_memory_malloc(function->nofarguments * sizeof(foreign_type)); + if (function->ffi_arguments && function->arguments) { + for (lua_Integer i = 0; i < function->nofarguments; i++) { + lua_rawgeti(L, -1, i + 1); + function->arguments[i] = foreignlib_type_found(L, -1, foreign_type_int); /* maybe issue an error */ + function->ffi_arguments[i] = foreign_typecodes[function->arguments[i]]; + lua_pop(L, 1); + } + } else { + goto ALLOCATION_ERROR; + } + } + break; + } + case LUA_TSTRING: + { + /* Just one argument, no varag here as it's too ugly otherwise. */ + function->nofarguments = 1; + function->ffi_arguments = (ffi_type **) lmt_memory_malloc(sizeof(ffi_type *)); + function->arguments = (foreign_type *) lmt_memory_malloc(sizeof(foreign_type)); + if (function->ffi_arguments && function->arguments) { + function->arguments[0] = foreignlib_type_found(L, -1, foreign_type_int); /* maybe issue an error */ + function->ffi_arguments[0] = foreign_typecodes[function->arguments[0]]; + } else { + goto ALLOCATION_ERROR; + } + break; + } + } + lua_pop(L, 1); + if (foreign_state.ffi_prep_cif(&(function->cif), function->abi, function->nofarguments, function->ffi_result_type, function->ffi_arguments) == FFI_OK) { + return 1; + } else { + return luaL_error(L, "foreign: error in libffi preparation"); + } + } else { + return luaL_error(L, "foreign: invalid return type for function %s", functionname); + } + } else { + goto ALLOCATION_ERROR; + } + } + } else { + return luaL_error(L, "foreign: unknown function %s", functionname); + } + } + } else { + return luaL_error(L, "foreign: function name expected"); + } + } else { + return luaL_error(L, "foreign: specification table expected"); + } + ALLOCATION_ERROR: + return foreignlib_allocation_error(L); + } else { + return foreignlib_not_yet_initialized(L); + } +} + +static int foreignlib_library_registered(lua_State *L) +{ + if (foreign_state.initialized) { + foreign_library *library = foreignlib_library_check(L, 1); + if (library) { + lua_getiuservalue(L, 1, library_registry_uv); + if (lua_type(L, 2) == LUA_TSTRING) { + lua_pushvalue(L, 2); + lua_rawget(L, -2); + if (lua_type(L, -1) == LUA_TUSERDATA) { + /* 1:library 2:name -3:registry -2:name -1:function */ + return 1; + } else { + size_t len; + const char *functionname = lua_tolstring(L, 2, &len); + return luaL_error(L, "foreign: unknown function %s", functionname); + } + } else { + lua_newtable(L); + lua_pushnil(L); + while (lua_next(L, -3)) { + /* key -2 value -1 | key has to stay*/ + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + lua_pop(L, 1); + return 1; + } + } + } else { + return foreignlib_not_yet_initialized(L); + } + return 0; +} + +static int foreignlib_library_available(lua_State *L) +{ + if (foreign_state.initialized) { + foreign_library *library = foreignlib_library_check(L, 1); + if (library && lua_type(L, 2) == LUA_TSTRING) { + lua_getiuservalue(L, 1, library_registry_uv); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + lua_pushboolean(L, lua_type(L, -1) == LUA_TUSERDATA); + return 1; + } + } else { + return foreignlib_not_yet_initialized(L); + } + return 0; +} + + /*tex This one is adapted from the alien version (watch the way pointer arguments are returned). */ + +static int foreignlib_function_call(lua_State *L) +{ + int nofreturnvalues = 1; /* we always return at least nil */ + foreign_function *function = foreignlib_function_check(L, 1); + ffi_cif *cif = &(function->cif); + int nofarguments = lua_gettop(L) - 1; + void **arguments = NULL; + int luacall = 0; + if (nofarguments != function->nofarguments) { + return luaL_error(L, "foreign: function '%s' expects %d arguments", function->name, function->nofarguments); + } + lua_getiuservalue(L, 1, function_finalizer_uv); + luacall = lua_type(L, -1) == LUA_TFUNCTION; + if (! luacall) { + lua_pop(L, 1); + } + if (nofarguments > 0) { + arguments = lmt_memory_malloc(sizeof(void*) * nofarguments); + if (arguments) { + for (int i = 0; i < nofarguments; i++) { + void *argument = NULL; + int slot = i + 2; + switch (function->arguments[i]) { + case foreign_type_byte : argument = lmt_memory_malloc(sizeof(char)); *((char *) argument) = (signed char) lua_tointeger(L, slot); break; + case foreign_type_char : argument = lmt_memory_malloc(sizeof(unsigned char)); *((unsigned char *) argument) = (unsigned char) lua_tointeger(L, slot); break; + case foreign_type_short : argument = lmt_memory_malloc(sizeof(short)); *((short *) argument) = (short) lua_tointeger(L, slot); break; + case foreign_type_ushort : argument = lmt_memory_malloc(sizeof(unsigned short)); *((unsigned short *) argument) = (unsigned short) lua_tointeger(L, slot); break; + case foreign_type_int : argument = lmt_memory_malloc(sizeof(int)); *((int *) argument) = (int) lua_tointeger(L, slot); break; + case foreign_type_uint : argument = lmt_memory_malloc(sizeof(unsigned int)); *((unsigned int *) argument) = (unsigned int) lua_tointeger(L, slot); break; + case foreign_type_long : argument = lmt_memory_malloc(sizeof(long)); *((long *) argument) = (long) lua_tointeger(L, slot); break; + case foreign_type_ulong : argument = lmt_memory_malloc(sizeof(unsigned long)); *((unsigned long *) argument) = (unsigned long) lua_tointeger(L, slot); break; + case foreign_type_longlong : argument = lmt_memory_malloc(sizeof(long long)); *((long long *) argument) = (long long) lua_tointeger(L, slot); break; + case foreign_type_ulonglong: argument = lmt_memory_malloc(sizeof(unsigned long long)); *((unsigned long long *) argument) = (unsigned long long) lua_tointeger(L, slot); break; + case foreign_type_float : argument = lmt_memory_malloc(sizeof(float)); *((float *) argument) = (float) lua_tonumber (L, slot); break; + case foreign_type_double : argument = lmt_memory_malloc(sizeof(double)); *((double *) argument) = (double) lua_tonumber (L, slot); break; + case foreign_type_size_t : argument = lmt_memory_malloc(sizeof(size_t)); *((size_t *) argument) = (size_t) lua_tointeger(L, slot); break; + case foreign_type_string : + { + argument = lmt_memory_malloc(sizeof(char*)); + if (argument) { + *((const char**) argument) = lua_type(L, slot) == LUA_TSTRING ? lua_tostring(L, slot) : NULL; + break; + } else { + return foreignlib_allocation_error(L); + } + } + case foreign_type_pointer : + { + /* why not just use the pointers */ + argument = lmt_memory_malloc(sizeof(char*)); + if (argument) { + switch (lua_type(L, slot)) { + case LUA_TSTRING: + { + /*tex A packed 5.4 string. */ + *((const char **) argument) = lua_tostring(L, slot); + break; + } + case LUA_TUSERDATA: + { + /*tex A constructed array or so. */ + foreign_pointer *pointer = foreignlib_pointer_check(L, slot); + *((void **) argument) = pointer ? pointer->ptr : NULL; + break; + } + default: + { + *((void **) argument) = NULL; + break; + } + } + break; + } else { + return foreignlib_allocation_error(L); + } + } + case foreign_type_reference_to_char: + { + argument = lmt_memory_malloc(sizeof(char *)); + if (argument) { + *((char **) argument) = lmt_memory_malloc(sizeof(char)); + **((char **) argument) = (char) lua_tointeger(L, slot); + nofreturnvalues++; + break; + } else { + return foreignlib_allocation_error(L); + } + } + case foreign_type_reference_to_int: + { + argument = lmt_memory_malloc(sizeof(int *)); + if (argument) { + *((int **) argument) = lmt_memory_malloc(sizeof(int)); + **((int **) argument) = (int) lua_tointeger(L, slot); + nofreturnvalues++; + break; + } else { + return foreignlib_allocation_error(L); + } + } + case foreign_type_reference_to_uint: + { + argument = lmt_memory_malloc(sizeof(unsigned int *)); + if (argument) { + *((unsigned int **) argument) = lmt_memory_malloc(sizeof(unsigned int)); + **((unsigned int **) argument) = (unsigned int) lua_tointeger(L, slot); + nofreturnvalues++; + break; + } else { + return foreignlib_allocation_error(L); + } + } + case foreign_type_reference_to_double: + { + argument = lmt_memory_malloc(sizeof(double *)); + if (argument) { + *((double **) argument) = lmt_memory_malloc(sizeof(double)); + **((double **) argument) = (double) lua_tonumber(L, slot); + nofreturnvalues++; + break; + } else { + return foreignlib_allocation_error(L); + } + } + default: + return luaL_error(L, "foreign: invalid parameter %d for '%s')", function->arguments[i], function->name); + } + arguments[i] = argument; + } + } else { + return foreignlib_allocation_error(L); + } + } + switch (function->result_type) { + case foreign_type_void : { foreign_state.ffi_call(cif, function->function, NULL, arguments); lua_pushnil (L); break; } + case foreign_type_byte : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (signed char) r); break; } + case foreign_type_char : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned char) r); break; } + case foreign_type_short : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (short) r); break; } + case foreign_type_ushort : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned short) r); break; } + case foreign_type_int : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (int) r); break; } + case foreign_type_uint : { int r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned int) r); break; } + case foreign_type_long : { long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (long) r); break; } + case foreign_type_ulong : { unsigned long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (unsigned long) r); break; } + case foreign_type_longlong : { long long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (lua_Integer) r); break; } + case foreign_type_ulonglong: { unsigned long long r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, (lua_Integer) r); break; } + case foreign_type_float : { float r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushnumber (L, r); break; } + case foreign_type_double : { double r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushnumber (L, r); break; } + case foreign_type_size_t : { size_t r; foreign_state.ffi_call(cif, function->function, &r, arguments); lua_pushinteger(L, r); break; } + case foreign_type_string : + { + void *str = NULL; + foreign_state.ffi_call(cif, function->function, &str, arguments); + if (str) { + lua_pushstring(L, (char *) str); + } else { + lua_pushnil(L); + } + break; + } + case foreign_type_pointer : + { + void *ptr = NULL; + foreign_state.ffi_call(cif, function->function, &ptr, arguments); + if (ptr) { + foreign_pointer *pointer = (foreign_pointer *) lua_newuserdatauv(L, sizeof(foreign_pointer), 0); + luaL_getmetatable(L, FOREIGN_METATABLE_POINTER); + lua_setmetatable(L, -2); + pointer->ptr = ptr; + pointer->state = foreign_pointer_state_regular; + } else { + lua_pushnil(L); + } + break; + } + default: + return luaL_error(L, "foreign: invalid return value %d for '%s')", function->result_type, function->name); + } + for (int i = 0; i < nofarguments; i++) { + switch (function->arguments[i]) { + case foreign_type_reference_to_char : lua_pushinteger(L, **(char **) arguments[i]); break; + case foreign_type_reference_to_int : lua_pushinteger(L, **(int **) arguments[i]); break; + case foreign_type_reference_to_uint : lua_pushinteger(L, **(unsigned int **) arguments[i]); break; + case foreign_type_reference_to_double: lua_pushnumber (L, **(double **) arguments[i]); break; + default: break; + } + lmt_memory_free(arguments[i]); /* not needed for pointers when we just use pointer */ + } + lmt_memory_free(arguments); + if (luacall) { + lua_call(L, nofreturnvalues, 1); + return 1; + } else { + return nofreturnvalues; + } +} + +static int foreignlib_library_gc(lua_State *L) +{ + foreign_library *library = foreignlib_library_check(L, 1); + if (library->library) { + lmt_library_open_indeed(library->library); + lmt_memory_free(library->name); + } + return 0; +} + +static int foreignlib_function_gc(lua_State *L) +{ + foreign_function *function = foreignlib_function_check(L, 1); + lmt_memory_free(function->name); + lmt_memory_free(function->arguments); + lmt_memory_free(function->ffi_arguments); + return 0; +} + +/* */ + +static int foreignlib_newbuffer(lua_State *L) +{ + size_t size = lua_tointeger(L, 1); + foreign_pointer *pointer = (foreign_pointer *) lua_newuserdatauv(L, sizeof(foreign_pointer), 0); + luaL_getmetatable(L, FOREIGN_METATABLE_POINTER); + lua_setmetatable(L, -2); + pointer->ptr = lmt_memory_malloc(size); + pointer->state = foreign_pointer_state_buffer; + return 1; +} + +static int foreignlib_getbuffer(lua_State *L) +{ + foreign_pointer *pointer = foreignlib_pointer_check(L, 1); + if (pointer && pointer->state == foreign_pointer_state_buffer && pointer->ptr) { + size_t size = lua_tointeger(L, 2); + if (size > 0) { + lua_pushlstring(L, pointer->ptr, size); + } else { + lua_pushnil(L); + } + lmt_memory_free(pointer->ptr); + pointer->ptr = NULL; + pointer->state = foreign_pointer_state_regular; + } else { + lua_pushnil(L); + } + return 1; +} + +/* pointer to array of pointers */ + +static int foreignlib_totable(lua_State *L) +{ + foreign_pointer *pointer = foreignlib_pointer_check(L, 1); + if (pointer) { + void *ptr = pointer->ptr; + if (ptr) { + int resulttype = foreignlib_type_found(L, 2, foreign_type_void); + int size = (int) luaL_optinteger(L, 3, -1); + lua_createtable(L, size > 0 ? size : 0, 0); + switch (resulttype) { + case foreign_type_void: + return 0; + case foreign_type_string: + { + void **ptr = pointer->ptr; + if (ptr) { + lua_Integer r = 0; + lua_newtable(L); + if (size < 0) { + while (ptr[r]) { + lua_pushstring(L, ptr[r]); + lua_rawseti(L, -2, ++r); + } + } else { + for (lua_Integer i = 0; i < size; i++) { + lua_pushstring(L, ptr[i]); + lua_rawseti(L, -2, ++r); + } + } + } + break; + } + case foreign_type_byte : { signed char *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_char : { unsigned char *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_short : { short *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_ushort : { unsigned short *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_int : { int *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_uint : { unsigned int *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_long : { long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_ulong : { unsigned long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_longlong : { long long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_ulonglong: { unsigned long long *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_float : { float *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushnumber (L, (lua_Number) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_double : { double *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushnumber (L, (lua_Number) p[i]); lua_rawseti(L, -2, i + 1); } break; } + case foreign_type_size_t : { size_t *p = ptr; for (lua_Integer i = 0; i < size; i++) { lua_pushinteger(L, (lua_Integer) p[i]); lua_rawseti(L, -2, i + 1); } break; } + } + return 1; + } + } + lua_pushnil(L); + return 1; +} + +/*tex + + Here we prepare some metatables. Todo: newindex. When we don't use a metatable for the + library we can have more keys, like list and so. + + local library = foreign.load("whatever","abi") + + library:register { name = ..., result = ..., arguments = { ... }, abi = ... ) + library:registered ("name") + library:registered () + library:available ("name") + + foreign.load() + foreign.abivalues() + foreign.types() + + todo: ckeck what this abi does: probably better at lib loading time than per function + +*/ + +static struct luaL_Reg foreignlib_function_methods[] = { + { "register", foreignlib_library_register }, + { "registered", foreignlib_library_registered }, + { "available", foreignlib_library_available }, + { NULL, NULL }, +}; + +static void foreignlib_populate(lua_State *L) +{ + luaL_newmetatable(L, FOREIGN_METATABLE_LIBRARY); + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, foreignlib_library_gc); + lua_settable(L, -3); + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, foreignlib_library_tostring); + lua_settable(L, -3); + lua_pushliteral(L, "__index"); + lua_newtable(L); + for (int i = 0; foreignlib_function_methods[i].name; i++) { + lua_pushstring(L, foreignlib_function_methods[i].name); + lua_pushcfunction(L, foreignlib_function_methods[i].func); + lua_settable(L, -3); + } + lua_settable(L, -3); + lua_pop(L, 1); + + luaL_newmetatable(L, FOREIGN_METATABLE_FUNCTION); + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, foreignlib_function_gc); + lua_settable(L, -3); + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, foreignlib_function_tostring); + lua_settable(L, -3); + lua_pushliteral(L, "__call"); + lua_pushcfunction(L, foreignlib_function_call); + lua_settable(L, -3); + lua_pop(L, 1); + + luaL_newmetatable(L, FOREIGN_METATABLE_POINTER); + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, foreignlib_pointer_gc); + lua_settable(L, -3); + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, foreignlib_pointer_tostring); + lua_settable(L, -3); +} + +/*tex + Finally it all somes together in the initializer. We expect the caller to handle the lookup + of |libffi| which can have different names per operating system. +*/ + +static int foreignlib_initialize(lua_State * L) +{ + if (! foreign_state.initialized) { + if (lmt_engine_state.permit_loadlib) { + /*tex Just an experiment. */ + const char *filename = lua_tostring(L, 1); /* libffi */ + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + foreign_state.ffi_prep_cif = lmt_library_find(lib, "ffi_prep_cif"); + foreign_state.ffi_call = lmt_library_find(lib, "ffi_call" ); + + foreign_state.initialized = lmt_library_okay(lib); + } + if (foreign_state.initialized) { + foreignlib_populate(L); + } + } else { + return luaL_error(L, "foreign: use --permitloadlib to enable this"); + } + } + lua_pushboolean(L, foreign_state.initialized); + return 1; +} + +static struct luaL_Reg foreignlib_function_list[] = { + { "initialize", foreignlib_initialize }, + { "load", foreignlib_load }, + { "types", foreignlib_types }, + { "newbuffer", foreignlib_newbuffer }, + { "getbuffer", foreignlib_getbuffer }, + { "abivalues", foreignlib_abivalues }, /* mostly for diagnostics */ + { "totable", foreignlib_totable }, + { NULL, NULL }, +}; + +int luaopen_foreign(lua_State * L) +{ + lmt_library_register(L, "foreign", foreignlib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtghostscript.c b/source/luametatex/source/luaoptional/lmtghostscript.c new file mode 100644 index 000000000..b16c3767c --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtghostscript.c @@ -0,0 +1,175 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +# define GS_ARG_ENCODING_UTF8 1 + +typedef struct gslib_state_info { + + int initialized; + int padding; + luaL_Buffer outbuffer; + luaL_Buffer errbuffer; + + int (*gsapi_new_instance) ( + void **pinstance, + void *caller_handle + ); + + void (*gsapi_delete_instance) ( + void * instance + ); + + int (*gsapi_set_arg_encoding) ( + void *instance, + int encoding + ); + + int (*gsapi_init_with_args) ( + void *instance, + int argc, + const char **argv + ); + + int (*gsapi_set_stdio) ( + void *instance, + int (*stdin_fn )(void *caller_handle, char *buf, int len), + int (*stdout_fn)(void *caller_handle, const char *str, int len), + int (*stderr_fn)(void *caller_handle, const char *str, int len) + ); + + /* + int (*gsapi_run_string_begin) (void *instance, int user_errors, int *pexit_code); + int (*gsapi_run_string_continue) (void *instance, const char *str, unsigned int length, int user_errors, int *pexit_code); + int (*gsapi_run_string_end) (void *instance, int user_errors, int *pexit_code); + int (*gsapi_run_string_with_length) (void *instance, const char *str, unsigned int length, int user_errors, int *pexit_code); + int (*gsapi_run_string) (void *instance, const char *str, int user_errors, int *pexit_code); + int (*gsapi_run_file) (void *instance, const char *file_name, int user_errors, int *pexit_code); + int (*gsapi_exit) (void *instance); + */ + +} gslib_state_info; + +static gslib_state_info gslib_state = { + + .initialized = 0, + .padding = 0, + /* .outbuffer = NULL, */ + /* .errbuffer = NULL, */ + + .gsapi_new_instance = NULL, + .gsapi_delete_instance = NULL, + .gsapi_set_arg_encoding = NULL, + .gsapi_init_with_args = NULL, + .gsapi_set_stdio = NULL, + +}; + +static int gslib_initialize(lua_State * L) +{ + if (! gslib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + gslib_state.gsapi_new_instance = lmt_library_find(lib, "gsapi_new_instance"); + gslib_state.gsapi_delete_instance = lmt_library_find(lib, "gsapi_delete_instance"); + gslib_state.gsapi_set_arg_encoding = lmt_library_find(lib, "gsapi_set_arg_encoding"); + gslib_state.gsapi_init_with_args = lmt_library_find(lib, "gsapi_init_with_args"); + gslib_state.gsapi_set_stdio = lmt_library_find(lib, "gsapi_set_stdio"); + + gslib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, gslib_state.initialized); + return 1; +} + +/* We could have a callback for stdout and error. */ + +static int gslib_stdout(void * caller_handle, const char *str, int len) +{ + (void)caller_handle; + luaL_addlstring(&gslib_state.outbuffer, str, len); + return len; +} + +static int gslib_stderr(void * caller_handle, const char *str, int len) +{ + (void)caller_handle; + luaL_addlstring(&gslib_state.errbuffer, str, len); + return len; +} + +static int gslib_execute(lua_State * L) +{ + if (gslib_state.initialized) { + if (lua_type(L, 1) == LUA_TTABLE) { + size_t n = (int) lua_rawlen(L, 1); + if (n > 0) { + void *instance = NULL; + int result = gslib_state.gsapi_new_instance(&instance, NULL); + if (result >= 0) { + /*tex + Strings are not yet garbage colected. We add some slack. Here MSVC wants + |char**| and gcc wants |const char**| i.e.\ doesn't like a castso we just + accept the less annoying MSVC warning. + */ + const char** arguments = malloc((n + 2) * sizeof(char*)); + if (arguments) { + int m = 1; + /*tex This is a kind of dummy. */ + arguments[0] = "ghostscript"; + luaL_buffinit(L, &gslib_state.outbuffer); + luaL_buffinit(L, &gslib_state.errbuffer); + gslib_state.gsapi_set_stdio(instance, NULL, &gslib_stdout, &gslib_stderr); + for (size_t i = 1; i <= n; i++) { + lua_rawgeti(L, 1, i); + switch (lua_type(L, -1)) { + case LUA_TSTRING: + case LUA_TNUMBER: + { + size_t l = 0; + const char *s = lua_tolstring(L, -1, &l); + if (l > 0) { + arguments[m] = s; + m += 1; + } + } + break; + } + lua_pop(L, 1); + } + arguments[m] = NULL; + result = gslib_state.gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_UTF8); + result = gslib_state.gsapi_init_with_args(instance, m, arguments); + gslib_state.gsapi_delete_instance(instance); + /* Nothing done with the array cells! No gc done yet anyway. */ + free((void *) arguments); + lua_pushboolean(L, result >= 0); + luaL_pushresult(&gslib_state.outbuffer); + luaL_pushresult(&gslib_state.errbuffer); + return 3; + } + } + } + } + } + return 0; +} + +static struct luaL_Reg gslib_function_list[] = { + { "initialize", gslib_initialize }, + { "execute", gslib_execute }, + { NULL, NULL }, +}; + +int luaopen_ghostscript(lua_State * L) +{ + lmt_library_register(L, "ghostscript", gslib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtgraphicsmagick.c b/source/luametatex/source/luaoptional/lmtgraphicsmagick.c new file mode 100644 index 000000000..c71c68c8c --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtgraphicsmagick.c @@ -0,0 +1,199 @@ +/* + See license.txt in the root of this project. +*/ + +/* For now just a simple conversion, like in the example module. */ + +# include "luametatex.h" +# include "lmtoptional.h" + +typedef enum gmlib_NoiseType { + UniformNoise, + GaussianNoise, + MultiplicativeGaussianNoise, + ImpulseNoise, + LaplacianNoise, + PoissonNoise, + RandomNoise, + UndefinedNoise +} gmlib_NoiseType; + +typedef struct gmlib_state_info { + + int initialized; + int padding; + + void (*gm_InitializeMagick) ( + // void **argv + void *path + ); + + void (*gm_DestroyMagick) ( + void + ); + + void * (*gm_NewMagickWand) ( + void + ); + + void (*gm_DestroyMagickWand) ( + void *wand + ); + + int (*gm_MagickReadImage) ( + void *wand, + const char *name + ); + + int (*gm_MagickWriteImage) ( + void *wand, + const char *name + ); + + int (*gm_MagickBlurImage) ( + void *wand, + const double radius, + const double sigma + ); + + int (*gm_MagickAddNoiseImage) ( + void *wand, + const gmlib_NoiseType noise_type + ); + +} gmlib_state_info; + +static gmlib_state_info gmlib_state = { + + .initialized = 0, + .padding = 0, + + .gm_InitializeMagick = NULL, + .gm_DestroyMagick = NULL, + .gm_NewMagickWand = NULL, + .gm_DestroyMagickWand = NULL, + .gm_MagickReadImage = NULL, + .gm_MagickWriteImage = NULL, + + .gm_MagickBlurImage = NULL, + .gm_MagickAddNoiseImage = NULL, + +}; + +static int gmlib_initialize(lua_State * L) // todo: table +{ + if (! gmlib_state.initialized) { + const char *filename1 = lua_tostring(L,1); + const char *filename2 = lua_tostring(L,2); + if (filename1) { + + lmt_library lib = lmt_library_load(filename1); + + gmlib_state.gm_InitializeMagick = lmt_library_find(lib, "InitializeMagick"); + gmlib_state.gm_DestroyMagick = lmt_library_find(lib, "DestroyMagick"); + + gmlib_state.initialized = lmt_library_okay(lib); + } + if (gmlib_state.initialized && filename2) { + + lmt_library lib = lmt_library_load(filename2); + + gmlib_state.gm_NewMagickWand = lmt_library_find(lib, "NewMagickWand"); + gmlib_state.gm_DestroyMagickWand = lmt_library_find(lib, "DestroyMagickWand"); + gmlib_state.gm_MagickReadImage = lmt_library_find(lib, "MagickReadImage"); + gmlib_state.gm_MagickWriteImage = lmt_library_find(lib, "MagickWriteImage"); + + gmlib_state.gm_MagickBlurImage = lmt_library_find(lib, "MagickBlurImage"); + gmlib_state.gm_MagickAddNoiseImage = lmt_library_find(lib, "MagickAddNoiseImage"); + + gmlib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, gmlib_state.initialized); + return 1; +} + +/* We could have a callback for stdout and error. */ + +/* Somehow not in gm: (void) MagickImageCommand(image_info, arg_count, args, NULL, exception); */ + +static int gmlib_execute(lua_State * L) +{ + if (gmlib_state.initialized) { + if (gmlib_state.initialized == 1) { + /* Once per run. */ + gmlib_state.gm_InitializeMagick(NULL); + gmlib_state.initialized = 2; + } + if (lua_type(L, 1) == LUA_TTABLE) { + void *wand = NULL; + const char *inpname = NULL; + const char *outname = NULL; + lua_getfield(L, -1, "inputfile" ); inpname = luaL_optstring(L, -1, NULL); lua_pop(L, 1); + lua_getfield(L, -1, "outputfile"); outname = luaL_optstring(L, -1, NULL); lua_pop(L, 1); + /* gmlib_state.gm_InitializeMagick(NULL); */ + wand = gmlib_state.gm_NewMagickWand(); + if (wand) { + int state = gmlib_state.gm_MagickReadImage(wand, inpname); /* todo: check return status */ + if (state) { + /* fun stuff */ + if (lua_getfield(L, -1, "blur" ) == LUA_TTABLE) { + lua_getfield(L, -1, "radius"); + lua_getfield(L, -2, "sigma"); + gmlib_state.gm_MagickBlurImage(wand, lua_tonumber(L, -2), lua_tonumber(L, -1)); + lua_pop(L, 3); + } else { + lua_pop(L, 1); + } + if (lua_getfield(L, -1, "noise" ) == LUA_TTABLE) { + lua_getfield(L, -1, "type"); + gmlib_state.gm_MagickAddNoiseImage(wand, lua_tointeger(L, -1)); + lua_pop(L, 2); + } else { + lua_pop(L, 1); + } + /* done */ + state = gmlib_state.gm_MagickWriteImage(wand, outname); /* todo: check return status */ + gmlib_state.gm_DestroyMagickWand(wand); + if (state) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushboolean(L, 0); + lua_pushliteral(L, "possible write error"); + return 2; + } + } else { + gmlib_state.gm_DestroyMagickWand(wand); + lua_pushboolean(L, 0); + lua_pushliteral(L, "possible read error"); + return 2; + } + } else { + lua_pushboolean(L, 0); + lua_pushliteral(L, "possible memory issue"); + return 2; + } + /* gmlib_state.gm_DestroyMagick(); */ + } else { + lua_pushboolean(L, 0); + lua_pushliteral(L, "invalid specification"); + return 2; + } + } + lua_pushboolean(L, 0); + lua_pushliteral(L, "not initialized"); + return 2; +} + +static struct luaL_Reg gmlib_function_list[] = { + { "initialize", gmlib_initialize }, + { "execute", gmlib_execute }, + { NULL, NULL }, +}; + +int luaopen_graphicsmagick(lua_State * L) +{ + lmt_library_register(L, "graphicsmagick", gmlib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmthb.c b/source/luametatex/source/luaoptional/lmthb.c new file mode 100644 index 000000000..d853256cf --- /dev/null +++ b/source/luametatex/source/luaoptional/lmthb.c @@ -0,0 +1,761 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" + +/*tex + + This is using similar c-lua-interfacing code as that Kai Eigner wrote for ffi. I cleaned it up + a bit but the principles remain because that way we are downward compatible. Don't expect + miracles here. We use function pointers as we delay binding to the module. We use the system + library as it comes, because after all, that is what is expected: shaping conform what the + system offers. + + This interface is only for testing. We load the font in \LUA\ anyway, so we don't need to + collect information here. The code runs on top of the \CONTEXT\ font plugin interface that + itself was written for testing purposes. When we wanted to test uniscribe the hb command + line program could be used for that, but more direct support was also added. The tests that + were done at that time (irr it was when xetex switched to hb and folks used that as reference) + ended up in articles. Later we used this mechanism to check how Idris advanced Arabic font + behaves in different shapers (context, uniscribe, hb, ...) as we try to follow uniscribe when + possible and this gave the glue to that. It showed interesting differences and overlap in + interpretation (and made us wonder when bugs - in whatever program or standard - get turned + into features, but that's another matter, and we can always adapt and provide variants and + overloads if needed). + + The following code is not dependent on h files so we don't need to install a whole bunch of + dependencies. Also, because we delay loading, there is no default overhead in startup. The + loading of the library happens (as usual) at the \LUA\ end, but in order for it to work okay + the initializer needs to be called, so that the functions get resolved. So, it works a bit + like the ffi interface: delayed loading, but (maybe) with a bit less overhead. I should + probably look at the latest api to see if things can be done with less code but on the other + hand there is no real reason to change something that already worked okay some years ago. + + I guess that the script enumeration is no longer right but it probably doesn't matter as + numbers are passed anyway. We can probably make that into an integer (I need to test that + some day) as these enumerations are just that: integers, and the less hard-coding we have + here the better. + + When this module is used (which is triggered via loading an optional module and setting the + mode in a font definition) other features of the \CONTEXT\ font handler are lost for that + specific font instance, simply because these mechanism operate independently. But that is + probably what a user expects anyway: no interference from other code, just the results from + a library. It makes no sense to complicate the machinery even more. This is comparable with + basemode and nodemode that are also seperated code paths. + + So, we could probably simplify the following typedefs, but this is what Kai started with so + I stick to it. From the enumerations only the direction constant is used. + +*/ + +typedef struct hb_blob_t hb_blob_t; + +/* typedef int hb_memory_mode_t; */ + +typedef enum hb_memory_mode_t { + HB_MEMORY_MODE_DUPLICATE, + HB_MEMORY_MODE_READONLY, + HB_MEMORY_MODE_WRITABLE, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE +} hb_memory_mode_t; + +typedef void (*hb_destroy_func_t) ( + void *user_data +); + +typedef struct hb_face_t hb_face_t; +typedef const struct hb_language_impl_t *hb_language_t; +typedef struct hb_buffer_t hb_buffer_t; + +/* typedef int hb_script_t; */ +/* typedef int hb_direction_t; */ + +/* + The content of this enum doesn't really matter here because we don't use it. Integers are + passed around. So, even if the following is not up to date we're okay. +*/ + +typedef enum hb_script_t { + HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, HB_SCRIPT_UNKNOWN, + + HB_SCRIPT_ARABIC, HB_SCRIPT_ARMENIAN, HB_SCRIPT_BENGALI, HB_SCRIPT_CYRILLIC, + HB_SCRIPT_DEVANAGARI, HB_SCRIPT_GEORGIAN, HB_SCRIPT_GREEK, + HB_SCRIPT_GUJARATI, HB_SCRIPT_GURMUKHI, HB_SCRIPT_HANGUL, HB_SCRIPT_HAN, + HB_SCRIPT_HEBREW, HB_SCRIPT_HIRAGANA, HB_SCRIPT_KANNADA, HB_SCRIPT_KATAKANA, + HB_SCRIPT_LAO, HB_SCRIPT_LATIN, HB_SCRIPT_MALAYALAM, HB_SCRIPT_ORIYA, + HB_SCRIPT_TAMIL, HB_SCRIPT_TELUGU, HB_SCRIPT_THAI, HB_SCRIPT_TIBETAN, + HB_SCRIPT_BOPOMOFO, HB_SCRIPT_BRAILLE, HB_SCRIPT_CANADIAN_SYLLABICS, + HB_SCRIPT_CHEROKEE, HB_SCRIPT_ETHIOPIC, HB_SCRIPT_KHMER, HB_SCRIPT_MONGOLIAN, + HB_SCRIPT_MYANMAR, HB_SCRIPT_OGHAM, HB_SCRIPT_RUNIC, HB_SCRIPT_SINHALA, + HB_SCRIPT_SYRIAC, HB_SCRIPT_THAANA, HB_SCRIPT_YI, HB_SCRIPT_DESERET, + HB_SCRIPT_GOTHIC, HB_SCRIPT_OLD_ITALIC, HB_SCRIPT_BUHID, HB_SCRIPT_HANUNOO, + HB_SCRIPT_TAGALOG, HB_SCRIPT_TAGBANWA, HB_SCRIPT_CYPRIOT, HB_SCRIPT_LIMBU, + HB_SCRIPT_LINEAR_B, HB_SCRIPT_OSMANYA, HB_SCRIPT_SHAVIAN, HB_SCRIPT_TAI_LE, + HB_SCRIPT_UGARITIC, HB_SCRIPT_BUGINESE, HB_SCRIPT_COPTIC, + HB_SCRIPT_GLAGOLITIC, HB_SCRIPT_KHAROSHTHI, HB_SCRIPT_NEW_TAI_LUE, + HB_SCRIPT_OLD_PERSIAN, HB_SCRIPT_SYLOTI_NAGRI, HB_SCRIPT_TIFINAGH, + HB_SCRIPT_BALINESE, HB_SCRIPT_CUNEIFORM, HB_SCRIPT_NKO, HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_PHOENICIAN, HB_SCRIPT_CARIAN, HB_SCRIPT_CHAM, HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_LEPCHA, HB_SCRIPT_LYCIAN, HB_SCRIPT_LYDIAN, HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_REJANG, HB_SCRIPT_SAURASHTRA, HB_SCRIPT_SUNDANESE, HB_SCRIPT_VAI, + HB_SCRIPT_AVESTAN, HB_SCRIPT_BAMUM, HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, + HB_SCRIPT_IMPERIAL_ARAMAIC, HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, HB_SCRIPT_JAVANESE, HB_SCRIPT_KAITHI, + HB_SCRIPT_LISU, HB_SCRIPT_MEETEI_MAYEK, HB_SCRIPT_OLD_SOUTH_ARABIAN, + HB_SCRIPT_OLD_TURKIC, HB_SCRIPT_SAMARITAN, HB_SCRIPT_TAI_THAM, + HB_SCRIPT_TAI_VIET, HB_SCRIPT_BATAK, HB_SCRIPT_BRAHMI, HB_SCRIPT_MANDAIC, + HB_SCRIPT_CHAKMA, HB_SCRIPT_MEROITIC_CURSIVE, HB_SCRIPT_MEROITIC_HIEROGLYPHS, + HB_SCRIPT_MIAO, HB_SCRIPT_SHARADA, HB_SCRIPT_SORA_SOMPENG, HB_SCRIPT_TAKRI, + HB_SCRIPT_BASSA_VAH, HB_SCRIPT_CAUCASIAN_ALBANIAN, HB_SCRIPT_DUPLOYAN, + HB_SCRIPT_ELBASAN, HB_SCRIPT_GRANTHA, HB_SCRIPT_KHOJKI, HB_SCRIPT_KHUDAWADI, + HB_SCRIPT_LINEAR_A, HB_SCRIPT_MAHAJANI, HB_SCRIPT_MANICHAEAN, + HB_SCRIPT_MENDE_KIKAKUI, HB_SCRIPT_MODI, HB_SCRIPT_MRO, HB_SCRIPT_NABATAEAN, + HB_SCRIPT_OLD_NORTH_ARABIAN, HB_SCRIPT_OLD_PERMIC, HB_SCRIPT_PAHAWH_HMONG, + HB_SCRIPT_PALMYRENE, HB_SCRIPT_PAU_CIN_HAU, HB_SCRIPT_PSALTER_PAHLAVI, + HB_SCRIPT_SIDDHAM, HB_SCRIPT_TIRHUTA, HB_SCRIPT_WARANG_CITI, HB_SCRIPT_AHOM, + HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, HB_SCRIPT_HATRAN, HB_SCRIPT_MULTANI, + HB_SCRIPT_OLD_HUNGARIAN, HB_SCRIPT_SIGNWRITING, HB_SCRIPT_ADLAM, + HB_SCRIPT_BHAIKSUKI, HB_SCRIPT_MARCHEN, HB_SCRIPT_OSAGE, HB_SCRIPT_TANGUT, + HB_SCRIPT_NEWA, HB_SCRIPT_MASARAM_GONDI, HB_SCRIPT_NUSHU, HB_SCRIPT_SOYOMBO, + HB_SCRIPT_ZANABAZAR_SQUARE, HB_SCRIPT_DOGRA, HB_SCRIPT_GUNJALA_GONDI, + HB_SCRIPT_HANIFI_ROHINGYA, HB_SCRIPT_MAKASAR, HB_SCRIPT_MEDEFAIDRIN, + HB_SCRIPT_OLD_SOGDIAN, HB_SCRIPT_SOGDIAN, HB_SCRIPT_ELYMAIC, + HB_SCRIPT_NANDINAGARI, HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, HB_SCRIPT_WANCHO, + + HB_SCRIPT_INVALID, _HB_SCRIPT_MAX_VALUE, _HB_SCRIPT_MAX_VALUE_SIGNED, +} hb_script_t; + +typedef enum hb_direction_t { + HB_DIRECTION_INVALID, + HB_DIRECTION_LTR, + HB_DIRECTION_RTL, + HB_DIRECTION_TTB, + HB_DIRECTION_BTT +} hb_direction_t; + +typedef int hb_bool_t; + +typedef uint32_t hb_tag_t; + +typedef struct hb_feature_t { + hb_tag_t tag; + uint32_t value; + unsigned int start; + unsigned int end; +} hb_feature_t; + +typedef struct hb_font_t hb_font_t; + +typedef uint32_t hb_codepoint_t; +typedef int32_t hb_position_t; +typedef uint32_t hb_mask_t; + +typedef union _hb_var_int_t { + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_int_t; + +typedef struct hb_glyph_info_t { + hb_codepoint_t codepoint; + hb_mask_t mask; + uint32_t cluster; + /* private */ + hb_var_int_t var1; + hb_var_int_t var2; +} hb_glyph_info_t; + +typedef struct hb_glyph_position_t { + hb_position_t x_advance; + hb_position_t y_advance; + hb_position_t x_offset; + hb_position_t y_offset; + /* private */ + hb_var_int_t var; +} hb_glyph_position_t; + +/*tex + + We only need to initialize the font and call a shaper. There is no need to interface more as we + won't use those features. I never compiled this library myself and just took it from the system + (e.g from inkscape). Keep in mind that names can be different on windows and linux. + + If needed we can reuse buffers and cache a bit more but it probably doesn't make much difference + performance wise. Also, in a bit more complex document font handling is not the most time + critical and when you use specific scripts in \TEX\ that are not supported otherwise and + therefore demand a library run time is probably the least of your problems. So best is that we + keep it all abstract. + +*/ + +# define HBLIB_METATABLE "optional.hblib" + +typedef struct hblib_data { + hb_font_t *font; +} hblib_data; + +typedef struct hblib_state_info { + + int initialized; + int padding; + + const char * (*hb_version_string) ( + void + ); + + hb_blob_t * (*hb_blob_create) ( + const char *data, + unsigned int length, + hb_memory_mode_t mode, /* Could be int I guess. */ + void *user_data, + hb_destroy_func_t destroy + ); + + void (*hb_blob_destroy) ( + hb_blob_t *blob + ); + + hb_face_t * (*hb_face_create) ( + hb_blob_t *blob, + unsigned int index + ); + + void (*hb_face_destroy) ( + hb_face_t *face + ); + + hb_language_t (*hb_language_from_string) ( + const char *str, + int len + ); + + void (*hb_buffer_set_language) ( + hb_buffer_t *buffer, + hb_language_t language + ); + + hb_script_t (*hb_script_from_string) ( + const char *s, + int len + ); + + void (*hb_buffer_set_script) ( + hb_buffer_t *buffer, + hb_script_t script + ); + + hb_direction_t (*hb_direction_from_string) ( + const char *str, + int len + ); + + void (*hb_buffer_set_direction) ( + hb_buffer_t *buffer, + hb_direction_t direction + ); + + hb_bool_t (*hb_feature_from_string) ( + const char *str, + int len, + hb_feature_t *feature + ); + + hb_bool_t (*hb_shape_full) ( + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list + ); + + hb_buffer_t * (*hb_buffer_create )( + void + ); + + void (*hb_buffer_destroy)( + hb_buffer_t *buffer + ); + + void (*hb_buffer_add_utf8) ( + hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length + ); + + void (*hb_buffer_add_utf32) ( + hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length + ); + + /* void (*hb_buffer_add) ( + hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster + ); */ + + unsigned int (*hb_buffer_get_length) ( + hb_buffer_t *buffer + ); + + hb_glyph_info_t * (*hb_buffer_get_glyph_infos) ( + hb_buffer_t *buffer, + unsigned int *length + ); + + hb_glyph_position_t * (*hb_buffer_get_glyph_positions) ( + hb_buffer_t *buffer, + unsigned int *length + ); + + void (*hb_buffer_reverse) ( + hb_buffer_t *buffer + ); + + void (*hb_buffer_reset) ( + hb_buffer_t *buffer + ); + + void (*hb_buffer_guess_segment_properties) ( + hb_buffer_t *buffer + ); + + hb_font_t * (*hb_font_create) ( + hb_face_t *face + ); + + void (*hb_font_destroy) ( + hb_font_t *font + ); + + void (*hb_font_set_scale) ( + hb_font_t *font, + int x_scale, + int y_scale + ); + + void (*hb_ot_font_set_funcs) ( + hb_font_t *font + ); + + unsigned int (*hb_face_get_upem) ( + hb_face_t *face + ); + + const char ** (*hb_shape_list_shapers) ( + void + ); + +} hblib_state_info; + +static hblib_state_info hblib_state = { + + .initialized = 0, + .padding = 0, + + .hb_version_string = NULL, + .hb_blob_create = NULL, + .hb_blob_destroy = NULL, + .hb_face_create = NULL, + .hb_face_destroy = NULL, + .hb_language_from_string = NULL, + .hb_buffer_set_language = NULL, + .hb_script_from_string = NULL, + .hb_buffer_set_script = NULL, + .hb_direction_from_string = NULL, + .hb_buffer_set_direction = NULL, + .hb_feature_from_string = NULL, + .hb_shape_full = NULL, + .hb_buffer_create = NULL, + .hb_buffer_destroy = NULL, + .hb_buffer_add_utf8 = NULL, + .hb_buffer_add_utf32 = NULL, + /* .hb_buffer_add = NULL, */ + .hb_buffer_get_length = NULL, + .hb_buffer_get_glyph_infos = NULL, + .hb_buffer_get_glyph_positions = NULL, + .hb_buffer_reverse = NULL, + .hb_buffer_reset = NULL, + .hb_buffer_guess_segment_properties = NULL, + .hb_font_create = NULL, + .hb_font_destroy = NULL, + .hb_font_set_scale = NULL, + .hb_ot_font_set_funcs = NULL, + .hb_face_get_upem = NULL, + .hb_shape_list_shapers = NULL, + +}; + +/* <boolean> = initialize(full_path_of_library) */ + +static int hblib_initialize(lua_State * L) +{ + if (! hblib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + hblib_state.hb_version_string = lmt_library_find(lib, "hb_version_string"); + hblib_state.hb_language_from_string = lmt_library_find(lib, "hb_language_from_string"); + hblib_state.hb_script_from_string = lmt_library_find(lib, "hb_script_from_string"); + hblib_state.hb_direction_from_string = lmt_library_find(lib, "hb_direction_from_string"); + hblib_state.hb_feature_from_string = lmt_library_find(lib, "hb_feature_from_string"); + + hblib_state.hb_buffer_set_language = lmt_library_find(lib, "hb_buffer_set_language"); + hblib_state.hb_buffer_set_script = lmt_library_find(lib, "hb_buffer_set_script"); + hblib_state.hb_buffer_set_direction = lmt_library_find(lib, "hb_buffer_set_direction"); + + hblib_state.hb_buffer_create = lmt_library_find(lib, "hb_buffer_create"); + hblib_state.hb_buffer_destroy = lmt_library_find(lib, "hb_buffer_destroy"); + hblib_state.hb_buffer_reverse = lmt_library_find(lib, "hb_buffer_reverse"); + hblib_state.hb_buffer_get_length = lmt_library_find(lib, "hb_buffer_get_length"); + hblib_state.hb_buffer_reset = lmt_library_find(lib, "hb_buffer_reset"); + hblib_state.hb_buffer_add_utf8 = lmt_library_find(lib, "hb_buffer_add_utf8"); + hblib_state.hb_buffer_add_utf32 = lmt_library_find(lib, "hb_buffer_add_utf32"); + + hblib_state.hb_blob_create = lmt_library_find(lib, "hb_blob_create"); + hblib_state.hb_blob_destroy = lmt_library_find(lib, "hb_blob_destroy"); + + hblib_state.hb_face_create = lmt_library_find(lib, "hb_face_create"); + hblib_state.hb_face_destroy = lmt_library_find(lib, "hb_face_destroy"); + hblib_state.hb_face_get_upem = lmt_library_find(lib, "hb_face_get_upem"); + + hblib_state.hb_font_create = lmt_library_find(lib, "hb_font_create"); + hblib_state.hb_font_destroy = lmt_library_find(lib, "hb_font_destroy"); + hblib_state.hb_font_set_scale = lmt_library_find(lib, "hb_font_set_scale"); + + hblib_state.hb_shape_list_shapers = lmt_library_find(lib, "hb_shape_list_shapers"); + hblib_state.hb_shape_full = lmt_library_find(lib, "hb_shape_full"); + + hblib_state.hb_ot_font_set_funcs = lmt_library_find(lib, "hb_ot_font_set_funcs"); + + hblib_state.hb_buffer_guess_segment_properties = lmt_library_find(lib, "hb_buffer_guess_segment_properties"); + hblib_state.hb_buffer_get_glyph_positions = lmt_library_find(lib, "hb_buffer_get_glyph_positions"); + hblib_state.hb_buffer_get_glyph_infos = lmt_library_find(lib, "hb_buffer_get_glyph_infos"); + + hblib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, hblib_state.initialized); + return 1; +} + +/* <string> = getversion() */ + +static int hblib_get_version(lua_State * L) +{ + if (hblib_state.initialized) { + lua_pushstring(L, hblib_state.hb_version_string()); + return 1; + } else { + return 0; + } +} + +/* <instance> = loadfont(identifier, fontdata) */ + +static int hblib_load_font(lua_State * L) +{ + if (hblib_state.initialized) { + int id = (int) lua_tointeger(L, 1); + const char *str= lua_tostring(L, 2); + int size = (int) lua_rawlen(L, 2); + hb_blob_t *blob = hblib_state.hb_blob_create(str, size, 0, NULL, NULL); + hb_face_t *face = hblib_state.hb_face_create(blob, id); + unsigned int scale = hblib_state.hb_face_get_upem(face); + hb_font_t *font = hblib_state.hb_font_create(face); + hblib_state.hb_font_set_scale(font, scale, scale); + hblib_state.hb_ot_font_set_funcs(font); + hblib_data *data = lua_newuserdatauv(L, sizeof(data), 0); + data->font = font; + luaL_getmetatable(L, HBLIB_METATABLE); + lua_setmetatable(L, -2); + hblib_state.hb_blob_destroy(blob); + hblib_state.hb_face_destroy(face); + return 1; + } else { + return 0; + } +} + +/* <table> = shapestring(instance, script, language, direction, { shapers }, { features }, text, reverse) */ + +static int hblib_utf8len(const char *text, size_t size) /* todo: take from utilities */ +{ + size_t ls = size; + int ind = 0; + int num = 0; + while (ind < (int) ls) { + unsigned char i = (unsigned char) *(text + ind); + if (i < 0x80) { + ind += 1; + } else if (i >= 0xF0) { + ind += 4; + } else if (i >= 0xE0) { + ind += 3; + } else if (i >= 0xC0) { + ind += 2; + } else { + ind += 1; + } + num += 1; + } + return num; +} + +static int hblib_utf32len(const char *text, size_t size) +{ + /* not okay, hb doesn't stop at \0 */ + /* (void) s; */ + /* return (int) size / 4; */ + /* so we do this instead */ + size_t ls = size; + int ind = 0; + int num = 0; + while (ind < (int) ls) { + unsigned char i = (unsigned char) *(text + ind); + if (i) { + ind += 4; + } else { + break; + } + num += 1; + } + return num; +} + +/*tex + + Maybe with |utfbits == 0| take a table with code points, but then we might also need cluster + stuff, so there is no gain here. + + I remember some issues with passing features (maybe because some defaults are always set) but + it's not really that important because one actually expects the library to handle them that way + (read: only enable additional ones). But I will look into it when needed. + +*/ + +static int hblib_shape_string(lua_State * L) +{ + if (hblib_state.initialized) { + hblib_data *data = luaL_checkudata(L, 1, HBLIB_METATABLE); + if (data == NULL) { + lua_pushnil(L); + } else { + /* Maybe we can better take a table, so it's a yet undecided api. */ + size_t nofscript = 0; + const char *script = lua_tolstring(L, 2, &nofscript); + size_t noflanguage = 0; + const char *language = lua_tolstring(L, 3, &noflanguage); + size_t nofdirection = 0; + const char *direction = lua_tolstring(L, 4, &nofdirection); + int nofshapers = 0; + const char * *shapers = NULL; /* slot 5 */ + int noffeatures = 0; + hb_feature_t *features = NULL; /* slot 6 */ + size_t noftext = 0; + const char *text = lua_tolstring(L, 7, &noftext); + int reverse = lua_toboolean(L, 8); + int utfbits = (int) luaL_optinteger(L, 9, 8); + hb_buffer_t *buffer = NULL; + /* + Shapers are passed as a table; why not pass the length here too ... simpler in + ffi -) Maybe I'll make this more static: a general setshaper or so, which is + more natural than having it as argument to the shape function. + + MSVC wants |char**| for the shapers and gcc wants |const char**| i.e.\ doesn't + like a cast so we just accept the less annoying MSVC warning. + */ + if (lua_istable(L,5)) { + lua_Unsigned n = lua_rawlen(L, 5); + if (n > 0) { + shapers = malloc((size_t) (n + 1) * sizeof(char *)); + if (shapers) { + for (lua_Unsigned i = 0; i < n; i++) { + lua_rawgeti(L, 5, i + 1); + if (lua_isstring(L, -1)) { + shapers[nofshapers] = lua_tostring(L, -1); + nofshapers += 1; + } + lua_pop(L, 1); + } + } else { + luaL_error(L, "optional hblib: unable to allocate shaper memory"); + } + /* sentinal */ + shapers[nofshapers] = NULL; + } + } + /* + Features need to be converted to a table of features (manual work); simpler in + ffi -) Maybe I'll move this to the loadfont function. + */ + if (lua_istable(L, 6)) { + lua_Unsigned n = lua_rawlen(L, 6); + if (n > 0) { + features = malloc((size_t) n * sizeof(hb_feature_t)); + if (features) { + for (lua_Unsigned i = 0; i < n; i++) { + lua_rawgeti(L, 6, i + 1); + if (lua_isstring(L, -1)) { + size_t l = 0; + const char *s = lua_tolstring(L, -1, &l); + hblib_state.hb_feature_from_string(s, (int) l, &features[noffeatures]); + noffeatures += 1; + } + lua_pop(L, 1); + } + } else { + luaL_error(L, "optional hblib: unable to allocate feature memory"); + } + } + } + /* Some preparations (see original ffi variant). */ + buffer =hblib_state. hb_buffer_create(); /* we could put this in the data blob */ + /* + When using ffi we used to use utf32 plus some slack because utf8 crashed. It would + be more handy if we could pass an array of integers (maybe we can). + */ + if (utfbits == 32) { + hblib_state.hb_buffer_add_utf32(buffer, text, (int) noftext, 0, hblib_utf32len(text, noftext)); + } else { /* 8 */ + hblib_state.hb_buffer_add_utf8(buffer, text, (int) noftext, 0, hblib_utf8len(text, noftext)); + } + hblib_state.hb_buffer_set_language(buffer, hblib_state.hb_language_from_string(language, (int) noflanguage)); + hblib_state.hb_buffer_set_script(buffer, hblib_state.hb_script_from_string(script, (int) nofscript)); + hblib_state.hb_buffer_set_direction(buffer, hblib_state.hb_direction_from_string(direction, (int) nofdirection)); + hblib_state.hb_buffer_guess_segment_properties(buffer); + /* Do it! */ + hblib_state.hb_shape_full(data->font, buffer, features, noffeatures, shapers); + /* Fixup. */ + if (reverse) { + hblib_state.hb_buffer_reverse(buffer); + } + /* Convert the result: plain and simple.*/ + { + unsigned length = hblib_state.hb_buffer_get_length(buffer); + hb_glyph_info_t *infos = hblib_state.hb_buffer_get_glyph_infos(buffer, NULL); + hb_glyph_position_t *positions = hblib_state.hb_buffer_get_glyph_positions(buffer, NULL); + lua_createtable(L, length, 0); + for (unsigned i = 0; i < length; i++) { + lua_createtable(L, 6, 0); + lua_pushinteger(L, infos[i].codepoint); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, infos[i].cluster); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, positions[i].x_offset); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, positions[i].y_offset); + lua_rawseti(L, -2, 4); + lua_pushinteger(L, positions[i].x_advance); + lua_rawseti(L, -2, 5); + lua_pushinteger(L, positions[i].y_advance); + lua_rawseti(L, -2, 6); + lua_rawseti(L, -2, i + 1); + } + } + hblib_state.hb_buffer_destroy(buffer); + free((void *) shapers); /* we didn't make copies of the lua strings, ms compiler gives warning */ + free((void *) features); + } + return 1; + } else { + return 0; + } +} + +/* <table> = getshapers() */ + +static int hblib_get_shapers(lua_State * L) +{ + if (hblib_state.initialized) { + const char * *shapers = hblib_state.hb_shape_list_shapers(); + if (shapers) { + int nofshapers = 0; + lua_createtable(L, 1, 0); + while (1) { + const char *s = shapers[nofshapers]; + if (s) { + nofshapers++; + lua_pushstring(L, s); + lua_rawseti(L, -2, nofshapers); + } else { + break; + } + } + return 1; + } + } + return 0; +} + +/* private */ + +static int hblib_free(lua_State * L) +{ + if (hblib_state.initialized) { + hblib_data *data = luaL_checkudata(L, 1, HBLIB_METATABLE); + if (data) { + hblib_state.hb_font_destroy(data->font); + } + } + return 0; +} + +/* <string> = tostring(instance) */ + +static int hblib_tostring(lua_State * L) +{ + if (hblib_state.initialized) { + hblib_data *data = luaL_checkudata(L, 1, HBLIB_METATABLE); + if (data) { + lua_pushfstring(L, "<optional.hblib.instance %p>", data); + } else { + lua_pushnil(L); + } + return 1; + } else { + return 0; + } +} + +/*tex We can do with a rather mimimal user data object. */ + +static const struct luaL_Reg hblib_metatable[] = { + { "__tostring", hblib_tostring }, + { "__gc", hblib_free }, + { NULL, NULL }, +}; + +/*tex + + Idem, just the collected calls of the ffi variant. The less the better because that way there + is no tricky code needed at the \LUA\ end. + +*/ + +static struct luaL_Reg hblib_function_list[] = { + { "initialize", hblib_initialize }, + { "getversion", hblib_get_version }, + { "getshapers", hblib_get_shapers }, + { "loadfont", hblib_load_font }, + { "shapestring", hblib_shape_string }, + { NULL, NULL }, +}; + +int luaopen_hb(lua_State *L) +{ + luaL_newmetatable(L, HBLIB_METATABLE); + luaL_setfuncs(L, hblib_metatable, 0); + lmt_library_register(L, "hb", hblib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtimagemagick.c b/source/luametatex/source/luaoptional/lmtimagemagick.c new file mode 100644 index 000000000..9af5e6cd7 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtimagemagick.c @@ -0,0 +1,144 @@ +/* + See license.txt in the root of this project. +*/ + +/* This one is real simple. */ + +# include "luametatex.h" +# include "lmtoptional.h" + +typedef struct imlib_state_info { + + int initialized; + int padding; + + int (*im_MagickCommandGenesis) ( + void *image_info, + // int *command, + void *command, + int argc, + const char **argv, + char **metadata, + void *exception + ); + + void * (*im_AcquireImageInfo) ( + void + ); + + void * (*im_AcquireExceptionInfo) ( + void + ); + + int (*im_ConvertImageCommand) ( + void *image_info, + int argc, + const char **argv, + char **metadata, + void *exception + ); + +} imlib_state_info; + +static imlib_state_info imlib_state = { + + .initialized = 0, + .padding = 0, + + .im_MagickCommandGenesis = NULL, + .im_AcquireImageInfo = NULL, + .im_AcquireExceptionInfo = NULL, + .im_ConvertImageCommand = NULL, + +}; + +static int imlib_initialize(lua_State * L) // todo: table +{ + if (! imlib_state.initialized) { + const char *filename1 = lua_tostring(L,1); + const char *filename2 = lua_tostring(L,2); + if (filename1) { + + lmt_library lib = lmt_library_load(filename1); + + imlib_state.initialized = lmt_library_okay(lib); + + imlib_state.im_AcquireImageInfo = lmt_library_find(lib, "AcquireImageInfo"); + imlib_state.im_AcquireExceptionInfo = lmt_library_find(lib, "AcquireExceptionInfo"); + + } + if (imlib_state.initialized && filename2) { + + lmt_library lib = lmt_library_load(filename2); + + imlib_state.im_MagickCommandGenesis = lmt_library_find(lib, "MagickCommandGenesis"); + imlib_state.im_ConvertImageCommand = lmt_library_find(lib, "ConvertImageCommand"); + + imlib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, imlib_state.initialized); + return 1; +} + +static int imlib_execute(lua_State * L) +{ + if (imlib_state.initialized) { + if (lua_type(L, 1) == LUA_TTABLE) { + const char *inpname = NULL; + const char *outname = NULL; + lua_getfield(L, -1, "inputfile" ); inpname = lua_tostring(L, -1); lua_pop(L, 1); + lua_getfield(L, -1, "outputfile"); outname = lua_tostring(L, -1); lua_pop(L, 1); + if (inpname && outname) { + lua_Integer nofarguments = 0; + lua_Integer nofoptions = 0; + const char **arguments = NULL; + void *info = imlib_state.im_AcquireImageInfo(); + void *exep = imlib_state.im_AcquireExceptionInfo(); + if (lua_getfield(L, -1, "options" ) == LUA_TTABLE) { + nofoptions = luaL_len(L, -1); + } + arguments = lmt_memory_malloc((nofoptions + 4) * sizeof(char *)); + arguments[nofarguments++] = "convert"; + arguments[nofarguments++] = inpname; + for (lua_Integer i = 1; i <= nofoptions; i++) { + switch (lua_rawgeti(L, -1, i)) { + case LUA_TSTRING: + case LUA_TNUMBER: + arguments[nofarguments++] = lua_tostring(L, -1); + break; + case LUA_TBOOLEAN: + arguments[nofarguments++] = lua_toboolean(L, -1) ? "true" : "false"; + break; + } + lua_pop(L, 1); + } + arguments[nofarguments++] = outname; + imlib_state.im_MagickCommandGenesis(info, imlib_state.im_ConvertImageCommand, (int) nofarguments, arguments, NULL, exep); + lmt_memory_free((char *) arguments); + lua_pop(L, 1); + lua_pushboolean(L, 1); + return 2; + } + } else { + lua_pushboolean(L, 0); + lua_pushliteral(L, "invalid specification"); + return 2; + } + } + lua_pushboolean(L, 0); + lua_pushliteral(L, "not initialized"); + return 2; +} + +static struct luaL_Reg imlib_function_list[] = { + { "initialize", imlib_initialize }, + { "execute", imlib_execute }, + { NULL, NULL }, +}; + +int luaopen_imagemagick(lua_State * L) +{ + lmt_library_register(L, "imagemagick", imlib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtkpse.c b/source/luametatex/source/luaoptional/lmtkpse.c new file mode 100644 index 000000000..e593f8b9b --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtkpse.c @@ -0,0 +1,311 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +/*tex + + As part of the lean and mean concept we have no \KPSE\ on board and as with \LUATEX\ the + \CONTEXT\ macro package doesn't need it. However, because we might want to play with it being + a runner for other engines (as we do with the \LUATEX\ binary in kpse mode), we have at least + an interface for it. One problem is to locate the right version of the delayed loaded kpse + library (but we can add some clever locating code for that if needed). We keep the interface + mostly the same as \LUATEX. + + This is actually a left-over from an experiment, but it works okay, so I moved the code into + the source tree and made a proper \CONTEXT\ library wrapper too. We are less clever than in + \LUATEX, so there are no additional lookup functions. After all, it is just about locating + files and not about writing a searcher in \LUA. So, there no subdir magic either, as one has + \LUA\ for that kind of stuff. No \DPI\ related magic either. In \LUATEX\ there are some more + functions in the \type {kpse} namespace but these don't really relate to locating files. + + We can actually omit the next two lists and pass numbers but then we need to store that list at + the \LUA\ end so we don't save much (but I might do it some day nevertheless). The nect code is + rather lightweight which is on purpose. Of course occationally we need to check the \API\ but + \KPSE\ pretty stable and we don't need the extra stuff that it provides (keep in mind that it + has to serve all kind of programs in the \TEX\ infrastructure so it's a complex beast). + +*/ + +typedef enum kpselib_file_format_type { + kpse_gf_format, kpse_pk_format, kpse_any_glyph_format, kpse_tfm_format, kpse_afm_format, + kpse_base_format, kpse_bib_format, kpse_bst_format, kpse_cnf_format, kpse_db_format, + kpse_fmt_format, kpse_fontmap_format, kpse_mem_format, kpse_mf_format, kpse_mfpool_format, + kpse_mft_format, kpse_mp_format, kpse_mppool_format, kpse_mpsupport_format, kpse_ocp_format, + kpse_ofm_format, kpse_opl_format, kpse_otp_format, kpse_ovf_format, kpse_ovp_format, + kpse_pict_format, kpse_tex_format, kpse_texdoc_format, kpse_texpool_format, + kpse_texsource_format, kpse_tex_ps_header_format, kpse_troff_font_format, kpse_type1_format, + kpse_vf_format, kpse_dvips_config_format, kpse_ist_format, kpse_truetype_format, + kpse_type42_format, kpse_web2c_format, kpse_program_text_format, kpse_program_binary_format, + kpse_miscfonts_format, kpse_web_format, kpse_cweb_format, kpse_enc_format, kpse_cmap_format, + kpse_sfd_format, kpse_opentype_format, kpse_pdftex_config_format, kpse_lig_format, + kpse_texmfscripts_format, kpse_lua_format, kpse_fea_format, kpse_cid_format, kpse_mlbib_format, + kpse_mlbst_format, kpse_clua_format, /* kpse_ris_format, */ /* kpse_bltxml_format, */ + kpse_last_format +} kpselib_file_format_type; + +static const char *const kpselib_file_type_names[] = { + "gf", "pk", "bitmap font", "tfm", "afm", "base", "bib", "bst", "cnf", "ls-R", "fmt", "map", + "mem", "mf", "mfpool", "mft", "mp", "mppool", "MetaPost support", "ocp", "ofm", "opl", "otp", + "ovf", "ovp", "graphic/figure", "tex", "TeX system documentation", "texpool", + "TeX system sources", "PostScript header", "Troff fonts", "type1 fonts", "vf", "dvips config", + "ist", "truetype fonts", "type42 fonts", "web2c files", "other text files", "other binary files", + "misc fonts", "web", "cweb", "enc files", "cmap files", "subfont definition files", + "opentype fonts", "pdftex config", "lig files", "texmfscripts", "lua", "font feature files", + "cid maps", "mlbib", "mlbst", "clua", + NULL +}; + +typedef struct kpselib_state_info { + + int initialized; + int prognameset; + + void (*lib_kpse_set_program_name) ( const char *prog, const char *name ); + void (*lib_kpse_reset_program_name) ( const char *name ); + char * (*lib_kpse_path_expand) ( const char *name ); + char * (*lib_kpse_brace_expand) ( const char *name ); + char * (*lib_kpse_var_expand) ( const char *name ); + char * (*lib_kpse_var_value) ( const char *name ); + char * (*lib_kpse_readable_file) ( const char *name ); + char * (*lib_kpse_find_file) ( const char *name, int filetype, int mustexist ); + char **(*lib_kpse_all_path_search) ( const char *path, const char *name ); + +} kpselib_state_info; + +static kpselib_state_info kpselib_state = { + + .initialized = 0, + .prognameset = 0, + + .lib_kpse_set_program_name = NULL, + .lib_kpse_reset_program_name = NULL, + .lib_kpse_path_expand = NULL, + .lib_kpse_brace_expand = NULL, + .lib_kpse_var_expand = NULL, + .lib_kpse_var_value = NULL, + .lib_kpse_readable_file = NULL, + .lib_kpse_find_file = NULL, + .lib_kpse_all_path_search = NULL, + +}; + +static int kpselib_aux_valid_progname(lua_State *L) +{ + (void) L; + if (kpselib_state.prognameset) { + return 1; + } else if (! kpselib_state.initialized) { + tex_normal_warning("kpse", "not yet initialized"); + return 0; + } else { + tex_normal_warning("kpse", "no program name set"); + return 0; + } +} + +static int kpselib_set_program_name(lua_State *L) +{ + (void) L; + if (kpselib_state.initialized) { + const char *exe_name = luaL_checkstring(L, 1); + const char *prog_name = luaL_optstring(L, 2, exe_name); + if (kpselib_state.prognameset) { + kpselib_state.lib_kpse_reset_program_name(prog_name); + } else { + kpselib_state.lib_kpse_set_program_name(exe_name, prog_name); + kpselib_state.prognameset = 1; + } + } + return 0; +} + +static int kpselib_find_file(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + unsigned filetype = kpse_tex_format; + int mustexist = 0; + const char *filename = luaL_checkstring(L, 1); + int top = lua_gettop(L); + for (int i = 2; i <= top; i++) { + switch (lua_type(L, i)) { + case LUA_TBOOLEAN: + mustexist = lua_toboolean(L, i); + break; + case LUA_TNUMBER: + /*tex This is different from \LUATEX: we accept a filetype number. */ + filetype = (unsigned) lua_tointeger(L, i); + break; + case LUA_TSTRING: + filetype = luaL_checkoption(L, i, NULL, kpselib_file_type_names); + break; + } + if (filetype >= kpse_last_format) { + filetype = kpse_tex_format; + } + } + lua_pushstring(L, kpselib_state.lib_kpse_find_file(filename, filetype, mustexist)); + return 1; + } else { + return 0; + } +} + +/* + I'll ask Taco about the free. For now it will do. Currently I only need to do some lookups for + checking clashes with other installations (issue reported on context ml). +*/ + +static int kpselib_find_files(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + const char *userpath = luaL_checkstring(L, 1); + const char *filename = luaL_checkstring(L, 2); + char *filepath = kpselib_state.lib_kpse_path_expand(userpath); + if (filepath) { + char **result = kpselib_state.lib_kpse_all_path_search(filepath, filename); + /* free(filepath); */ /* crashes, so it looks like def kpse keeps it */ + if (result) { + lua_Integer r = 0; + lua_newtable(L); + while (result[r]) { + lua_pushstring(L, result[r]); + lua_rawseti(L, -2, ++r); + } + /* free(result); */ /* idem */ + return 1; + } + } else { + /* free(filepath); */ /* idem */ + } + } + return 0; +} + +static int kpselib_expand_path(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + lua_pushstring(L, kpselib_state.lib_kpse_path_expand(luaL_checkstring(L, 1))); + return 1; + } else { + return 0; + } +} + +static int kpselib_expand_braces(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + lua_pushstring(L, kpselib_state.lib_kpse_brace_expand(luaL_checkstring(L, 1))); + return 1; + } else { + return 0; + } +} + +static int kpselib_expand_var(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + lua_pushstring(L, kpselib_state.lib_kpse_var_expand(luaL_checkstring(L, 1))); + return 1; + } else { + return 0; + } +} + +static int kpselib_var_value(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + lua_pushstring(L, kpselib_state.lib_kpse_var_value(luaL_checkstring(L, 1))); + return 1; + } else { + return 0; + } +} + +static int kpselib_readable_file(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + /* Why the dup? */ + char *name = strdup(luaL_checkstring(L, 1)); + lua_pushstring(L, kpselib_state.lib_kpse_readable_file(name)); + free(name); + return 1; + } else { + return 0; + } +} + +static int kpselib_get_file_types(lua_State *L) +{ + if (kpselib_aux_valid_progname(L)) { + lua_createtable(L, kpse_last_format, 0); + for (lua_Integer i = 0; i < kpse_last_format; i++) { + if (kpselib_file_type_names[i]) { + lua_pushstring(L, kpselib_file_type_names[i]); + lua_rawseti(L, -2, i + 1); + } else { + break; + } + } + return 1; + } else { + return 0; + } +} + +static int kpselib_initialize(lua_State *L) +{ + if (! kpselib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + kpselib_state.lib_kpse_set_program_name = lmt_library_find(lib, "kpse_set_program_name"); + kpselib_state.lib_kpse_reset_program_name = lmt_library_find(lib, "kpse_reset_program_name"); + kpselib_state.lib_kpse_all_path_search = lmt_library_find(lib, "kpse_all_path_search"); + kpselib_state.lib_kpse_find_file = lmt_library_find(lib, "kpse_find_file"); + kpselib_state.lib_kpse_path_expand = lmt_library_find(lib, "kpse_path_expand"); + kpselib_state.lib_kpse_brace_expand = lmt_library_find(lib, "kpse_brace_expand"); + kpselib_state.lib_kpse_var_expand = lmt_library_find(lib, "kpse_var_expand"); + kpselib_state.lib_kpse_var_value = lmt_library_find(lib, "kpse_var_value"); + kpselib_state.lib_kpse_readable_file = lmt_library_find(lib, "kpse_readable_file"); + + kpselib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, kpselib_state.initialized); + return 1; +} + +/*tex We use the official names here, with underscores. */ + +/* init_prog : no need */ +/* show_path : maybe */ +/* lookup : maybe */ +/* default_texmfcnf : not that useful */ +/* record_output_file : makes no sense */ +/* record_input_file : makes no sense */ +/* check_permissions : luatex extra */ + +static struct luaL_Reg kpselib_function_list[] = { + { "initialize", kpselib_initialize }, + { "set_program_name", kpselib_set_program_name }, + { "find_file", kpselib_find_file }, + { "find_files", kpselib_find_files }, + { "expand_path", kpselib_expand_path }, + { "expand_var", kpselib_expand_var }, + { "expand_braces", kpselib_expand_braces }, + { "var_value", kpselib_var_value }, + { "readable_file", kpselib_readable_file }, + { "get_file_types", kpselib_get_file_types }, + { NULL, NULL }, +}; + +int luaopen_kpse(lua_State * L) +{ + lmt_library_register(L, "kpse", kpselib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtlz4.c b/source/luametatex/source/luaoptional/lmtlz4.c new file mode 100644 index 000000000..d54442635 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtlz4.c @@ -0,0 +1,193 @@ +/* + See license.txt in the root of this project. +*/ + +# include <stdlib.h> + +# include "luametatex.h" + +# define LZ4F_VERSION 100 /* used to check for an incompatible API breaking change */ + +typedef struct lz4lib_state_info { + + int initialized; + int padding; + + int (*LZ4_compressBound) (int inputSize); + int (*LZ4_compress_fast) (const char *src, char *dst, int srcSize, int dstCapacity, int acceleration); + int (*LZ4_decompress_safe) (const char *src, char *dst, int compressedSize, int dstCapacity); + size_t (*LZ4F_compressFrameBound) (size_t srcSize, void *); + size_t (*LZ4F_compressFrame) (void *dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize, void *); + unsigned (*LZ4F_isError) (int code); + int (*LZ4F_createDecompressionContext) (void **dctxPtr, unsigned version); + int (*LZ4F_freeDecompressionContext) (void *dctx); + size_t (*LZ4F_decompress) (void *dctx, void *dstBuffer, size_t *dstSizePtr, const void *srcBuffer, size_t *srcSizePtr, void *); + +} lz4lib_state_info; + +static lz4lib_state_info lz4lib_state = { + + .initialized = 0, + .padding = 0, + + .LZ4_compressBound = NULL, + .LZ4_compress_fast = NULL, + .LZ4_decompress_safe = NULL, + .LZ4F_compressFrameBound = NULL, + .LZ4F_compressFrame = NULL, + .LZ4F_isError = NULL, + .LZ4F_createDecompressionContext = NULL, + .LZ4F_freeDecompressionContext = NULL, + .LZ4F_decompress = NULL, + +}; + +static int lz4lib_compress(lua_State *L) +{ + if (lz4lib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + lua_Integer acceleration = luaL_optinteger(L, 2, 1); + size_t targetsize = lz4lib_state.LZ4_compressBound((int) sourcesize); + luaL_Buffer buffer; + char *target = luaL_buffinitsize(L, &buffer, targetsize); + int result = lz4lib_state.LZ4_compress_fast(source, target, (int) sourcesize, (int) targetsize, (int) acceleration); + if (result > 0) { + luaL_pushresultsize(&buffer, result); + } else { + lua_pushnil(L); + } + } + return 1; +} + +/* + + There is no info about the target size so we don't provide a decompress function. Either use + the frame variant or save and restore the targetsize, + + static int lz4lib_decompress(lua_State *L) + { + lua_pushnil(L); + return 1; + } + +*/ + +static int lz4lib_decompresssize(lua_State *L) +{ + if (lz4lib_state.initialized) { + size_t sourcesize = 0; + size_t targetsize = luaL_checkinteger(L, 2); + const char *source = luaL_checklstring(L, 1, &sourcesize); + if (source && targetsize > 0) { + luaL_Buffer buffer; + char *target = luaL_buffinitsize(L, &buffer, targetsize); + int result = lz4lib_state.LZ4_decompress_safe(source, target, (int) sourcesize, (int) targetsize); + if (result > 0) { + luaL_pushresultsize(&buffer, result); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + } + return 1; +} + +static int lz4lib_framecompress(lua_State *L) +{ + if (lz4lib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + luaL_Buffer buffer; + size_t targetsize = lz4lib_state.LZ4F_compressFrameBound(sourcesize, NULL); + char *target = luaL_buffinitsize(L, &buffer, targetsize); + size_t result = lz4lib_state.LZ4F_compressFrame(target, targetsize, source, sourcesize, NULL); + luaL_pushresultsize(&buffer, result); + } + return 1; +} + +static int lz4lib_framedecompress(lua_State *L) +{ + if (lz4lib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + if (source) { + void *context = NULL; + int errorcode = lz4lib_state.LZ4F_createDecompressionContext(&context, LZ4F_VERSION); + if (lz4lib_state.LZ4F_isError(errorcode)) { + lua_pushnil(L); + } else { + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + while (1) { + size_t targetsize = 0xFFFF; + char *target = luaL_prepbuffsize(&buffer, targetsize); + size_t consumed = sourcesize; + size_t errorcode = lz4lib_state.LZ4F_decompress(context, target, &targetsize, source, &consumed, NULL); + if (lz4lib_state.LZ4F_isError((int) errorcode)) { + lua_pushnil(L); + break; + } else if (targetsize == 0) { + luaL_pushresult(&buffer); + break; + } else { + luaL_addsize(&buffer, targetsize); + sourcesize -= consumed; + source += consumed; + } + } + } + if (context) { + lz4lib_state.LZ4F_freeDecompressionContext(context); + } + } else { + lua_pushnil(L); + } + } + return 1; +} + +static int lz4lib_initialize(lua_State *L) +{ + if (! lz4lib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + lz4lib_state.LZ4_compressBound = lmt_library_find(lib, "LZ4_compressBound"); + lz4lib_state.LZ4_compress_fast = lmt_library_find(lib, "LZ4_compress_fast"); + lz4lib_state.LZ4_decompress_safe = lmt_library_find(lib, "LZ4_decompress_safe"); + lz4lib_state.LZ4F_compressFrameBound = lmt_library_find(lib, "LZ4F_compressFrameBound"); + lz4lib_state.LZ4F_compressFrame = lmt_library_find(lib, "LZ4F_compressFrame"); + lz4lib_state.LZ4F_isError = lmt_library_find(lib, "LZ4F_isError"); + lz4lib_state.LZ4F_createDecompressionContext = lmt_library_find(lib, "LZ4F_createDecompressionContext"); + lz4lib_state.LZ4F_freeDecompressionContext = lmt_library_find(lib, "LZ4F_freeDecompressionContext"); + lz4lib_state.LZ4F_decompress = lmt_library_find(lib, "LZ4F_decompress"); + + lz4lib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, lz4lib_state.initialized); + return 1; +} + +static struct luaL_Reg lz4lib_function_list[] = { + { "initialize", lz4lib_initialize }, + { "compress", lz4lib_compress }, + /* { "decompress", lz4lib_decompress }, */ + { "decompresssize", lz4lib_decompresssize }, + { "framecompress", lz4lib_framecompress }, + { "framedecompress", lz4lib_framedecompress }, + { NULL, NULL }, +}; + +int luaopen_lz4(lua_State * L) +{ + lmt_library_register(L, "lz4", lz4lib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtlzma.c b/source/luametatex/source/luaoptional/lmtlzma.c new file mode 100644 index 000000000..6ffe6fedf --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtlzma.c @@ -0,0 +1,228 @@ +/* + See license.txt in the root of this project. +*/ + +# include <stdlib.h> + +# include "luametatex.h" + +/* + We only need a few definitions and it's nice that they are already prepared for extensions. +*/ + +typedef enum { + LZMA_RESERVED_ENUM = 0 +} lzma_reserved_enum; + +typedef enum { + LZMA_OK = 0, + LZMA_STREAM_END = 1, + LZMA_NO_CHECK = 2, + LZMA_UNSUPPORTED_CHECK = 3, + LZMA_GET_CHECK = 4, + LZMA_MEM_ERROR = 5, + LZMA_MEMLIMIT_ERROR = 6, + LZMA_FORMAT_ERROR = 7, + LZMA_OPTIONS_ERROR = 8, + LZMA_DATA_ERROR = 9, + LZMA_BUF_ERROR = 10, + LZMA_PROG_ERROR = 11, +} lzma_ret; + +typedef enum { + LZMA_RUN = 0, + LZMA_SYNC_FLUSH = 1, + LZMA_FULL_FLUSH = 2, + LZMA_FULL_BARRIER = 4, + LZMA_FINISH = 3 +} lzma_action; + +typedef enum { + LZMA_CHECK_NONE = 0, + LZMA_CHECK_CRC32 = 1, + LZMA_CHECK_CRC64 = 4, + LZMA_CHECK_SHA256 = 10 +} lzma_check; + +typedef struct lzma_internal_s lzma_internal; + +typedef struct { + void *(*alloc)(void *opaque, size_t nmemb, size_t size); + void (*free )(void *opaque, void *ptr); + void *opaque; +} lzma_allocator; + +typedef struct { + const uint8_t *next_in; + size_t avail_in; + uint64_t total_in; + uint8_t *next_out; + size_t avail_out; + uint64_t total_out; + const lzma_allocator *allocator; + lzma_internal *internal; + void *reserved_ptr1; + void *reserved_ptr2; + void *reserved_ptr3; + void *reserved_ptr4; + uint64_t reserved_int1; + uint64_t reserved_int2; + size_t reserved_int3; + size_t reserved_int4; + lzma_reserved_enum reserved_enum1; + lzma_reserved_enum reserved_enum2; +} lzma_stream; + + +# define LZMA_STREAM_INIT { NULL, 0, 0, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, LZMA_RESERVED_ENUM, LZMA_RESERVED_ENUM } + +# define LZMA_TELL_NO_CHECK UINT32_C(0x01) +# define LZMA_TELL_UNSUPPORTED_CHECK UINT32_C(0x02) +# define LZMA_TELL_ANY_CHECK UINT32_C(0x04) +# define LZMA_CONCATENATED UINT32_C(0x08) + +typedef struct lzmalib_state_info { + + int initialized; + int padding; + + int (*lzma_auto_decoder) (lzma_stream *strm, uint64_t memlimit, uint32_t flags); + int (*lzma_easy_encoder) (lzma_stream *strm, uint32_t preset, lzma_check check); + int (*lzma_code) (lzma_stream *strm, lzma_action action); + int (*lzma_end) (lzma_stream *strm); + +} lzmalib_state_info; + +static lzmalib_state_info lzmalib_state = { + + .initialized = 0, + .padding = 0, + + .lzma_auto_decoder = NULL, + .lzma_easy_encoder = NULL, + .lzma_code = NULL, + .lzma_end = NULL, +}; + + +# define lzma_default_level 6 +# define lzma_default_size 0xFFFF + +static int lzmalib_compress(lua_State *L) +{ + if (lzmalib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + int level = lmt_optinteger(L, 2, lzma_default_level); + int targetsize = lmt_optinteger(L, 3, lzma_default_size); + if (level < 0 || level > 9) { + level = lzma_default_level; + } + if (source) { + lzma_stream strm = LZMA_STREAM_INIT; + int errorcode = lzmalib_state.lzma_easy_encoder(&strm, level, LZMA_CHECK_CRC64); + if (errorcode == LZMA_OK) { + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + strm.next_in = (const uint8_t *) source; + strm.avail_in = sourcesize; + if (targetsize < lzma_default_size) { + targetsize = lzma_default_size; + } + while (1) { + char *target = luaL_prepbuffsize(&buffer, targetsize); + size_t produced = strm.total_out; + strm.next_out = (uint8_t *) target; + strm.avail_out = targetsize; + errorcode = lzmalib_state.lzma_code(&strm, LZMA_FINISH); + produced = strm.total_out - produced; + luaL_addsize(&buffer, produced); + if (errorcode == LZMA_STREAM_END) { + lzmalib_state.lzma_end(&strm); + luaL_pushresult(&buffer); + return 1; + } else if (errorcode != LZMA_OK) { + lzmalib_state.lzma_end(&strm); + break; + } + } + } + } + } + lua_pushnil(L); + return 1; +} + +static int lzmalib_decompress(lua_State *L) +{ + if (lzmalib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + int targetsize = lmt_optinteger(L, 2, lzma_default_size); + if (source) { + lzma_stream strm = LZMA_STREAM_INIT; + int errorcode = lzmalib_state.lzma_auto_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); + if (errorcode == LZMA_OK) { + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + strm.next_in = (const uint8_t *) source; + strm.avail_in = sourcesize; + if (targetsize < lzma_default_size) { + targetsize = lzma_default_size; + } + while (1) { + char *target = luaL_prepbuffsize(&buffer, targetsize); + size_t produced = strm.total_out; + strm.next_out = (uint8_t *) target; + strm.avail_out = targetsize; + errorcode = lzmalib_state.lzma_code(&strm, LZMA_RUN); + produced = strm.total_out - produced; + luaL_addsize(&buffer, produced); + if (errorcode == LZMA_STREAM_END || produced == 0) { + lzmalib_state.lzma_end(&strm); + luaL_pushresult(&buffer); + return 1; + } else if (errorcode != LZMA_OK) { + lzmalib_state.lzma_end(&strm); + break; + } + } + } + } + } + lua_pushnil(L); + return 1; +} + +static int lzmalib_initialize(lua_State *L) +{ + if (! lzmalib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + lzmalib_state.lzma_auto_decoder = lmt_library_find(lib, "lzma_auto_decoder"); + lzmalib_state.lzma_easy_encoder = lmt_library_find(lib, "lzma_easy_encoder"); + lzmalib_state.lzma_code = lmt_library_find(lib, "lzma_code"); + lzmalib_state.lzma_end = lmt_library_find(lib, "lzma_end"); + + lzmalib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, lzmalib_state.initialized); + return 1; +} + +static struct luaL_Reg lzmalib_function_list[] = { + { "initialize", lzmalib_initialize }, + { "compress", lzmalib_compress }, + { "decompress", lzmalib_decompress }, + { NULL, NULL }, +}; + +int luaopen_lzma(lua_State * L) +{ + lmt_library_register(L, "lzma", lzmalib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtlzo.c b/source/luametatex/source/luaoptional/lmtlzo.c new file mode 100644 index 000000000..766e824ca --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtlzo.c @@ -0,0 +1,108 @@ +/* + See license.txt in the root of this project. +*/ + +# include <stdlib.h> + +# include "luametatex.h" + +# define lzo_output_length(n) (n + n / 16 + 64 + 64) /* we add 64 instead of 3 */ + +# define LZO_E_OK 0 + +typedef struct lzolib_state_info { + + int initialized; + int padding; + + int (*lzo1x_1_compress) (const char *source, size_t sourcesize, char *target, size_t *targetsize, void *wrkmem); + int (*lzo1x_decompress_safe) (const char *source, size_t sourcesize, char *target, size_t *targetsize, void *wrkmem); + +} lzolib_state_info; + +static lzolib_state_info lzolib_state = { + + .initialized = 0, + .padding = 0, + + .lzo1x_1_compress = NULL, + .lzo1x_decompress_safe = NULL, + +}; + +static int lzolib_compress(lua_State *L) +{ + if (lzolib_state.initialized) { + char *wrkmem = lmt_memory_malloc(16384 + 32); /* we some plenty of slack, normally 2 seemss enough */ + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + luaL_Buffer buffer; + size_t targetsize = lzo_output_length(sourcesize); + char *target = luaL_buffinitsize(L, &buffer, targetsize); + int result = lzolib_state.lzo1x_1_compress(source, sourcesize, target, &targetsize, wrkmem); + if (result == LZO_E_OK) { + luaL_pushresultsize(&buffer, targetsize); + } else { + lua_pushnil(L); + } + lmt_memory_free(wrkmem); + return 1; + } else { + return 0; + } +} + +static int lzolib_decompresssize(lua_State *L) +{ + if (lzolib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + size_t targetsize = luaL_checkinteger(L, 2); + if (source && targetsize > 0) { + luaL_Buffer buffer; + char *target = luaL_buffinitsize(L, &buffer, targetsize); + int result = lzolib_state.lzo1x_decompress_safe(source, sourcesize, target, &targetsize, NULL); + if (result == LZO_E_OK) { + luaL_pushresultsize(&buffer, targetsize); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; + } else { + return 0; + } +} + +static int lzolib_initialize(lua_State *L) +{ + if (! lzolib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + lzolib_state.lzo1x_1_compress = lmt_library_find(lib, "lzo1x_1_compress"); + lzolib_state.lzo1x_decompress_safe = lmt_library_find(lib, "lzo1x_decompress_safe"); + + lzolib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, lzolib_state.initialized); + return 1; +} + +static struct luaL_Reg lzolib_function_list[] = { + { "initialize", lzolib_initialize }, + { "compress", lzolib_compress }, + { "decompresssize", lzolib_decompresssize }, + { NULL, NULL }, +}; + +int luaopen_lzo(lua_State * L) +{ + lmt_library_register(L, "lzo", lzolib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtmujs.c b/source/luametatex/source/luaoptional/lmtmujs.c new file mode 100644 index 000000000..e1abfdc0b --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtmujs.c @@ -0,0 +1,609 @@ +/* + See license.txt in the root of this project. +*/ + +/*tex + The \CLANGUAGE\ interface looks quite a bit like the \LUA\ interface. This module will only + provide print-to-tex functions and no other interfacing. It really makes no sense to provide + more. An interesting lightweight interface could map onto \LUA\ calls but there is no gain + either. Consider it an experiment that might attract kids to \TEX, just because \JAVASCRIPT\ + looks familiar. We have only one instance. Of course we could use some userdata object but I + don't think it is worth the effort and one would like to be persistent across calls. (We used + to have multiple \LUA\ instances which made no sense either.) + + url: https://mujs.com/index.html + + Keep in mind: we don't have a \JAVASCRIPT\ interoreter embedded because this is just a small + minimal interface to code that {\em can} be loaded at runtime, if present at all. + +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +typedef struct js_State js_State; + +typedef void (*js_CFunction) (js_State *J); +typedef void (*js_Report) (js_State *J, const char *message); +typedef void (*js_Finalize) (js_State *J, void *p); + +typedef enum js_states { + JS_STRICT = 1, +} js_states; + +typedef enum js_properties { + JS_READONLY = 1, + JS_DONTENUM = 2, + JS_DONTCONF = 4, +} js_properties; + +/*tex A couple of status variables: */ + +typedef struct mujslib_state_info { + + js_State *instance; + int initialized; + int find_file_id; + int open_file_id; + int close_file_id; + int read_file_id; + int seek_file_id; + int console_id; + int padding; + + struct js_State * (*js_newstate) ( + void *alloc, + void *actx, + int flags + ); + + void (*js_freestate) ( + js_State *J + ); + + void (*js_setreport) ( + js_State *J, + js_Report report + ); + + int (*js_dostring) ( + js_State *J, + const char *source + ); + + void (*js_newcfunction) ( + js_State *J, + js_CFunction fun, + const char *name, + int length + ); + + void (*js_newuserdata) ( + js_State *J, + const char *tag, + void *data, + js_Finalize finalize + ); + + void (*js_newcconstructor) ( + js_State *J, + js_CFunction fun, + js_CFunction con, + const char *name, + int length + ); + + int (*js_dofile) ( + js_State *J, + const char *filename + ); + + void (*js_currentfunction) ( + js_State *J + ); + + void (*js_getglobal) (js_State *J, const char *name ); + void (*js_setglobal) (js_State *J, const char *name ); + void (*js_defglobal) (js_State *J, const char *name, int atts ); + + void (*js_getproperty) (js_State *J, int idx, const char *name ); + void (*js_setproperty) (js_State *J, int idx, const char *name ); + void (*js_defproperty) (js_State *J, int idx, const char *name, int atts); + + void (*js_pushundefined) (js_State *J ); + void (*js_pushnull) (js_State *J ); + void (*js_pushnumber) (js_State *J, double v ); + void (*js_pushstring) (js_State *J, const char *v ); + + const char * (*js_tostring) (js_State *J, int idx ); + int (*js_tointeger) (js_State *J, int idx ); + void * (*js_touserdata) (js_State *J, int idx, const char *tag ); + + int (*js_isnumber) (js_State *J, int idx ); + int (*js_isstring) (js_State *J, int idx ); + int (*js_isundefined) (js_State *J, int idx ); + +} mujslib_state_info; + +static mujslib_state_info mujslib_state = { + + .initialized = 0, + .instance = NULL, + .find_file_id = 0, + .open_file_id = 0, + .close_file_id = 0, + .read_file_id = 0, + .seek_file_id = 0, + .console_id = 0, + .padding = 0, + + .js_newstate = NULL, + .js_freestate = NULL, + .js_setreport = NULL, + .js_dostring = NULL, + .js_newcfunction = NULL, + .js_newuserdata = NULL, + .js_newcconstructor = NULL, + .js_dofile = NULL, + .js_currentfunction = NULL, + + .js_getglobal = NULL, + .js_setglobal = NULL, + .js_defglobal = NULL, + + .js_getproperty = NULL, + .js_setproperty = NULL, + .js_defproperty = NULL, + + .js_pushundefined = NULL, + .js_pushnull = NULL, + .js_pushnumber = NULL, + .js_pushstring = NULL, + + .js_tostring = NULL, + .js_tointeger = NULL, + .js_touserdata = NULL, + + .js_isnumber = NULL, + .js_isstring = NULL, + .js_isundefined = NULL, + +}; + +/*tex A few callbacks: */ + +static int mujslib_register_function(lua_State * L, int old_id) +{ + if (! (lua_isfunction(L, -1) || lua_isnil(L, -1))) { + return 0; + } else { + lua_pushvalue(L, -1); + if (old_id) { + luaL_unref(L, LUA_REGISTRYINDEX, old_id); + } + return luaL_ref(L, LUA_REGISTRYINDEX); + } +} + +static int mujslib_set_find_file(lua_State *L) +{ + mujslib_state.find_file_id = mujslib_register_function(L, mujslib_state.find_file_id); + return 0; +} + +static int mujslib_set_open_file(lua_State *L) +{ + mujslib_state.open_file_id = mujslib_register_function(L, mujslib_state.open_file_id); + return 0; +} + +static int mujslib_set_close_file(lua_State *L) +{ + mujslib_state.close_file_id = mujslib_register_function(L, mujslib_state.close_file_id); + return 0; +} + +static int mujslib_set_read_file(lua_State *L) +{ + mujslib_state.read_file_id = mujslib_register_function(L, mujslib_state.read_file_id); + return 0; +} + +static int mujslib_set_seek_file(lua_State *L) +{ + mujslib_state.seek_file_id = mujslib_register_function(L, mujslib_state.seek_file_id); + return 0; +} + +static int mujslib_set_console(lua_State *L) +{ + mujslib_state.console_id = mujslib_register_function(L, mujslib_state.console_id); + return 0; +} + +static char *mujslib_find_file(const char *fname, const char *fmode) +{ + if (mujslib_state.find_file_id) { + lua_State *L = lmt_lua_state.lua_instance; /* todo: pass */ + lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.find_file_id); + lua_pushstring(L, fname); + lua_pushstring(L, fmode); + if (lua_pcall(L, 2, 1, 0)) { + tex_formatted_warning("mujs", "find file: %s\n", lua_tostring(L, -1)); + } else { + char *s = NULL; + const char *x = lua_tostring(L, -1); + if (x) { + s = strdup(x); + } + lua_pop(L, 1); + return s; + } + } else { + tex_normal_warning("mujs", "missing callback: find file"); + } + return NULL; +} + +/*tex A few helpers: */ + +static void mujslib_aux_texcprint(js_State *J, int ispartial) +{ + int c = default_catcode_table_preset; + int i = 0; + if (mujslib_state.js_isnumber(J, 1)) { + if (mujslib_state.js_isnumber(J, 2) || mujslib_state.js_isstring(J, 2)) { + c = mujslib_state.js_tointeger(J, 1); + i = 2; + } else { + i = 1; + } + } else if (mujslib_state.js_isstring(J, 1)) { + i = 1; + } + if (i) { + const char *s = mujslib_state.js_tostring(J, i); + if (s) { + lmt_cstring_print(c, s, ispartial); + } + } else { + tex_normal_warning("mujs", "invalid argument(s) for printing to tex"); + } + mujslib_state.js_pushundefined(J); /* needed ? */ +} + +static void mujslib_aux_texprint(js_State *J) +{ + mujslib_aux_texcprint(J, 0); /* full line */ +} + +static void mujslib_aux_texsprint(js_State *J) +{ + mujslib_aux_texcprint(J, 1); /* partial line */ +} + +static void mujslib_aux_feedback(js_State *J, const char *category, const char *message) +{ + if (message) { + if (mujslib_state.console_id) { + lua_State *L = lmt_lua_state.lua_instance; + lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.console_id); + lua_pushstring(L, category); + lua_pushstring(L, message); + if (lua_pcall(L, 2, 0, 0)) { + tex_formatted_warning("mujs", "console: %s\n", lua_tostring(L, -1)); + } + } else { + tex_print_message(message); + } + } + mujslib_state.js_pushundefined(J); +} + +static void mujslib_aux_console(js_State *J) +{ + mujslib_aux_feedback(J, "console", mujslib_state.js_tostring(J, 1)); +} + +static void mujslib_aux_report(js_State *J, const char *s) +{ + mujslib_aux_feedback(J, "report", s); +} + +/*tex + The interfaces: for loading files a finder callback is mandate so that + we keep control over what gets read from where. +*/ + +static int mujslib_execute(lua_State *L) +{ + if (mujslib_state.instance) { + const char *s = lua_tostring(L, 1); + if (s) { + mujslib_state.js_dostring(mujslib_state.instance, s); + } + } + return 0; +} + +static int mujslib_dofile(lua_State *L) +{ + if (mujslib_state.instance) { + const char *name = lua_tostring(L, 1); + if (name) { + char *found = mujslib_find_file(name, "rb"); + if (found) { + mujslib_state.js_dofile(mujslib_state.instance, found); + } + free(found); + } + } else { + tex_normal_warning("mujs", "missing callback: find file"); + } + return 0; +} + +static void mujslib_start(void) +{ + if (mujslib_state.instance) { + mujslib_state.js_freestate(mujslib_state.instance); + } + mujslib_state.instance = mujslib_state.js_newstate(NULL, NULL, JS_STRICT); + if (mujslib_state.instance) { + mujslib_state.js_newcfunction(mujslib_state.instance, mujslib_aux_texprint, "texprint", 2); + mujslib_state.js_setglobal (mujslib_state.instance, "texprint"); + mujslib_state.js_newcfunction(mujslib_state.instance, mujslib_aux_texsprint, "texsprint", 2); + mujslib_state.js_setglobal (mujslib_state.instance, "texsprint"); + mujslib_state.js_newcfunction(mujslib_state.instance, mujslib_aux_console, "console", 1); + mujslib_state.js_setglobal (mujslib_state.instance, "console"); + mujslib_state.js_setreport (mujslib_state.instance, mujslib_aux_report); + } +} + +static int mujslib_reset(lua_State *L) +{ + if (mujslib_state.initialized) { + mujslib_start(); + } + lua_pushboolean(L, mujslib_state.initialized && mujslib_state.instance); + return 1; +} + +/*tex + File handling: we go via the \LUA\ interface so that we have control + over what happens. Another benefit is that we don't need memory + management when fetching data from files. +*/ + +static void mujslib_file_finalize(js_State *J, void *p) +{ + int *id = p; + (void) J; + if (*id) { + lua_State *L = lmt_lua_state.lua_instance; + int top = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.close_file_id); + lua_pushinteger(L, *id); + if (lua_pcall(L, 1, 0, 0)) { + tex_formatted_warning("mujs", "close file: %s\n", lua_tostring(L, -1)); + } + lua_settop(L,top); + } +} + +static void mujslib_file_close(js_State *J) +{ + if (mujslib_state.instance) { + if (mujslib_state.close_file_id) { + int *id = mujslib_state.js_touserdata(J, 0, "File"); + if (*id) { + mujslib_file_finalize(J, id); + } + } else { + tex_normal_warning("mujs", "missing callback: close file"); + } + } + mujslib_state.js_pushundefined(J); +} + +static void mujslib_file_read(js_State *J) +{ + if (mujslib_state.instance) { + if (mujslib_state.read_file_id) { + int *id = mujslib_state.js_touserdata(J, 0, "File"); + if (*id) { + lua_State *L = lmt_lua_state.lua_instance; + int top = lua_gettop(L); + int n = 1; + lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.read_file_id); + lua_pushinteger(L, *id); + if (mujslib_state.js_isstring(J, 1)) { + const char *how = mujslib_state.js_tostring(J, 1); + if (how) { + lua_pushstring(L, how); + n = 2; + } + } else if (mujslib_state.js_isnumber(J, 1)) { + int how = mujslib_state.js_tointeger(J, 1); + if (how) { + lua_pushinteger(L, how); + n = 2; + } + } + if (lua_pcall(L, n, 1, 0)) { + tex_formatted_warning("mujs", "close file: %s\n", lua_tostring(L, -1)); + } else { + const char *result = strdup(lua_tostring(L, -1)); + if (result) { + mujslib_state.js_pushstring(J, result); + lua_settop(L, top); + return; + } + } + lua_settop(L, top); + } + } else { + tex_normal_warning("mujs", "missing callback: read file"); + } + } + mujslib_state.js_pushundefined(J); +} + +static void mujslib_file_seek(js_State *J) +{ + if (mujslib_state.instance) { + if (mujslib_state.seek_file_id) { + int *id = mujslib_state.js_touserdata(J, 0, "File"); + if (*id) { + lua_State *L = lmt_lua_state.lua_instance; + int top = lua_gettop(L); + int n = 2; + lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.seek_file_id); + lua_pushinteger(L, *id); + /* no checking here */ + lua_pushstring(L, mujslib_state.js_tostring(J, 1)); + if (mujslib_state.js_isnumber(J, 2)) { + lua_pushinteger(L, mujslib_state.js_tointeger(J, 2)); + n = 3; + } + if (lua_pcall(L, n, 1, 0)) { + tex_formatted_warning("mujs", "seek file: %s\n", lua_tostring(L, -1)); + } else if (lua_type(L, -1) == LUA_TNUMBER) { + mujslib_state.js_pushnumber(J, lua_tonumber(L, -1)); + lua_settop(L, top); + return; + } + lua_settop(L, top); + } + } else { + tex_normal_warning("mujs", "missing callback: seek file"); + } + } + mujslib_state.js_pushundefined(J); +} + +static void mujslib_file_new(js_State *J) +{ + if (mujslib_state.instance) { + if (mujslib_state.open_file_id) { + const char *name = mujslib_state.js_tostring(J, 1); + if (name) { + lua_State *L = lmt_lua_state.lua_instance; + int top = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.open_file_id); + lua_pushstring(L, name); + if (lua_pcall(L, 1, 1, 0)) { + tex_formatted_warning("mujs", "open file: %s\n", lua_tostring(L, -1)); + } else { + int *id = malloc(sizeof(int)); + if (id) { + *((int*) id) = (int) lua_tointeger(L, -1); + lua_settop(L, top); + if (id) { + mujslib_state.js_currentfunction(J); + mujslib_state.js_getproperty(J, -1, "prototype"); + mujslib_state.js_newuserdata(J, "File", id, mujslib_file_finalize); + return; + } + } + } + lua_settop(L, top); + } + } else { + tex_normal_warning("mujs", "missing callback: open file"); + } + } + mujslib_state.js_pushnull(J); +} + +/* Setting things up. */ + +static void mujslib_file_initialize(js_State *J) +{ + mujslib_state.js_getglobal(J, "Object"); + mujslib_state.js_getproperty(J, -1, "prototype"); + mujslib_state.js_newuserdata(J, "File", stdin, NULL); + { + mujslib_state.js_newcfunction(J, mujslib_file_read, "File.prototype.read", 0); + mujslib_state.js_defproperty(J, -2, "read", JS_DONTENUM); + mujslib_state.js_newcfunction(J, mujslib_file_seek, "File.prototype.seek", 0); + mujslib_state.js_defproperty(J, -2, "seek", JS_DONTENUM); + mujslib_state.js_newcfunction(J, mujslib_file_close, "File.prototype.close", 0); + mujslib_state.js_defproperty(J, -2, "close", JS_DONTENUM); + } + mujslib_state.js_newcconstructor(J, mujslib_file_new, mujslib_file_new, "File", 1); + mujslib_state.js_defglobal(J, "File", JS_DONTENUM); +} + +static int mujslib_initialize(lua_State *L) +{ + if (! mujslib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + mujslib_state.js_newstate = lmt_library_find(lib, "js_newstate"); + mujslib_state.js_freestate = lmt_library_find(lib, "js_freestate"); + mujslib_state.js_setreport = lmt_library_find(lib, "js_setreport"); + + mujslib_state.js_newcfunction = lmt_library_find(lib, "js_newcfunction"); + mujslib_state.js_newuserdata = lmt_library_find(lib, "js_newuserdata"); + mujslib_state.js_newcconstructor = lmt_library_find(lib, "js_newcconstructor"); + + mujslib_state.js_pushundefined = lmt_library_find(lib, "js_pushundefined"); + mujslib_state.js_pushnull = lmt_library_find(lib, "js_pushnull"); + mujslib_state.js_pushnumber = lmt_library_find(lib, "js_pushnumber"); + mujslib_state.js_pushstring = lmt_library_find(lib, "js_pushstring"); + + mujslib_state.js_dostring = lmt_library_find(lib, "js_dostring"); + mujslib_state.js_dofile = lmt_library_find(lib, "js_dofile"); + + mujslib_state.js_tostring = lmt_library_find(lib, "js_tostring"); + mujslib_state.js_tointeger = lmt_library_find(lib, "js_tointeger"); + mujslib_state.js_touserdata = lmt_library_find(lib, "js_touserdata"); + + mujslib_state.js_getglobal = lmt_library_find(lib, "js_getglobal"); + mujslib_state.js_setglobal = lmt_library_find(lib, "js_setglobal"); + mujslib_state.js_defglobal = lmt_library_find(lib, "js_defglobal"); + + mujslib_state.js_getproperty = lmt_library_find(lib, "js_getproperty"); + mujslib_state.js_setproperty = lmt_library_find(lib, "js_setproperty"); + mujslib_state.js_defproperty = lmt_library_find(lib, "js_defproperty"); + + mujslib_state.js_isstring = lmt_library_find(lib, "js_isstring"); + mujslib_state.js_isnumber = lmt_library_find(lib, "js_isnumber"); + mujslib_state.js_isundefined = lmt_library_find(lib, "js_isundefined"); + + mujslib_state.js_currentfunction = lmt_library_find(lib, "js_currentfunction"); + + mujslib_state.initialized = lmt_library_okay(lib); + + mujslib_start(); + + mujslib_file_initialize(mujslib_state.instance); + } + } + lua_pushboolean(L, mujslib_state.initialized && mujslib_state.instance); + return 1; +} + +static struct luaL_Reg mujslib_function_list[] = { + { "initialize", mujslib_initialize }, /* mandate */ + { "reset", mujslib_reset }, + { "execute", mujslib_execute }, + { "dofile", mujslib_dofile }, + { "setfindfile", mujslib_set_find_file }, /* mandate */ + { "setopenfile", mujslib_set_open_file }, /* mandate */ + { "setclosefile", mujslib_set_close_file }, /* mandate */ + { "setreadfile", mujslib_set_read_file }, /* mandate */ + { "setseekfile", mujslib_set_seek_file }, + { "setconsole", mujslib_set_console }, + { NULL, NULL }, +}; + +int luaopen_mujs(lua_State *L) +{ + lmt_library_register(L, "mujs", mujslib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtmysql.c b/source/luametatex/source/luaoptional/lmtmysql.c new file mode 100644 index 000000000..8509df7aa --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtmysql.c @@ -0,0 +1,325 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +typedef void mysql_instance; +typedef void mysql_result; +typedef char **mysql_row; +typedef unsigned int mysql_offset; + +typedef struct mysql_field { + char *name; + char *org_name; + char *table; + char *org_table; + char *db; + char *catalog; + char *def; + unsigned long length; + unsigned long max_length; + unsigned int name_length; + unsigned int org_name_length; + unsigned int table_length; + unsigned int org_table_length; + unsigned int db_length; + unsigned int catalog_length; + unsigned int def_length; + unsigned int flags; + unsigned int decimals; + unsigned int charsetnr; + int type; + void *extension; +} mysql_field; + +# define MYSQLLIB_METATABLE "luatex.mysqllib" + +typedef struct mysqllib_data { + /*tex There is not much more than a pointer currently. */ + mysql_instance * db; +} mysqllib_data ; + +typedef struct mysqllib_state_info { + + int initialized; + int padding; + + mysql_instance * (*mysql_init) ( + mysql_instance *mysql + ); + + mysql_instance * (*mysql_real_connect) ( + mysql_instance *mysql, + const char *host, + const char *user, + const char *passwd, + const char *db, + unsigned int port, + const char *unix_socket, + unsigned long clientflag + ); + + unsigned int (*mysql_errno) ( + mysql_instance *mysql + ); + + const char * (*mysql_error) ( + mysql_instance *mysql + ); + + int (*mysql_real_query) ( + mysql_instance *mysql, + const char *q, + unsigned long length + ); + + mysql_result * (*mysql_store_result) ( + mysql_instance *mysql + ); + + void (*mysql_free_result) ( + mysql_result *result + ); + + unsigned long long (*mysql_num_rows) ( + mysql_result *res + ); + + mysql_row (*mysql_fetch_row) ( + mysql_result *result + ); + + unsigned int (*mysql_affected_rows) ( + mysql_instance *mysql + ); + + unsigned int (*mysql_field_count) ( + mysql_instance *mysql + ); + + unsigned int (*mysql_num_fields) ( + mysql_result *res + ); + + mysql_field * (*mysql_fetch_fields) ( + mysql_result *res + ); + + mysql_offset (*mysql_field_seek) ( + mysql_result *result, + mysql_offset offset + ); + + void (*mysql_close) ( + mysql_instance *sock + ); + +} mysqllib_state_info; + +static mysqllib_state_info mysqllib_state = { + + .initialized = 0, + .padding = 0, + + .mysql_init = NULL, + .mysql_real_connect = NULL, + .mysql_errno = NULL, + .mysql_error = NULL, + .mysql_real_query = NULL, + .mysql_store_result = NULL, + .mysql_free_result = NULL, + .mysql_num_rows = NULL, + .mysql_fetch_row = NULL, + .mysql_affected_rows = NULL, + .mysql_field_count = NULL, + .mysql_num_fields = NULL, + .mysql_fetch_fields = NULL, + .mysql_field_seek = NULL, + .mysql_close = NULL, + +}; + +static int mysqllib_initialize(lua_State * L) +{ + if (! mysqllib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename != NULL) { + + lmt_library lib = lmt_library_load(filename); + + mysqllib_state.mysql_init = lmt_library_find(lib, "mysql_init" ); + mysqllib_state.mysql_real_connect = lmt_library_find(lib, "mysql_real_connect" ); + mysqllib_state.mysql_errno = lmt_library_find(lib, "mysql_errno" ); + mysqllib_state.mysql_error = lmt_library_find(lib, "mysql_error" ); + mysqllib_state.mysql_real_query = lmt_library_find(lib, "mysql_real_query" ); + mysqllib_state.mysql_store_result = lmt_library_find(lib, "mysql_store_result" ); + mysqllib_state.mysql_free_result = lmt_library_find(lib, "mysql_free_result" ); + mysqllib_state.mysql_num_rows = lmt_library_find(lib, "mysql_num_rows" ); + mysqllib_state.mysql_fetch_row = lmt_library_find(lib, "mysql_fetch_row" ); + mysqllib_state.mysql_affected_rows = lmt_library_find(lib, "mysql_affected_rows" ); + mysqllib_state.mysql_field_count = lmt_library_find(lib, "mysql_field_count" ); + mysqllib_state.mysql_num_fields = lmt_library_find(lib, "mysql_num_fields" ); + mysqllib_state.mysql_fetch_fields = lmt_library_find(lib, "mysql_fetch_fields" ); + mysqllib_state.mysql_field_seek = lmt_library_find(lib, "mysql_field_seek" ); + mysqllib_state.mysql_close = lmt_library_find(lib, "mysql_close" ); + + mysqllib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, mysqllib_state.initialized); + return 1; +} + +static int mysqllib_open(lua_State * L) +{ + if (mysqllib_state.initialized) { + const char * database = luaL_checkstring(L, 1); + const char * username = luaL_optstring(L, 2, NULL); + const char * password = luaL_optstring(L, 3, NULL); + const char * host = luaL_optstring(L, 4, NULL); + int port = lmt_optinteger(L, 5, 0); + const char * socket = NULL; /* luaL_optstring(L, 6, NULL); */ + int flag = 0; /* luaL_optinteger(L, 7, 0); */ + mysql_instance * db = mysqllib_state.mysql_init(NULL); + if (db != NULL) { + if (mysqllib_state.mysql_real_connect(db, host, username, password, database, port, socket, flag)) { + mysqllib_data *data = lua_newuserdatauv(L, sizeof(data), 0); + data->db = db ; + luaL_getmetatable(L, MYSQLLIB_METATABLE); + lua_setmetatable(L, -2); + return 1; + } else { + mysqllib_state.mysql_close(db); + } + } + } + return 0; +} + +static int mysqllib_close(lua_State * L) +{ + if (mysqllib_state.initialized) { + mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE); + if (data != NULL) { + mysqllib_state.mysql_close(data->db); + data->db = NULL; + } + } + return 0; +} + +/* execute(database,querystring,callback) : callback(nofcolumns,values,fields) */ + +static int mysqllib_execute(lua_State * L) +{ + if (mysqllib_state.initialized) { + mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE); + if (data != NULL) { + size_t length = 0; + const char *query = lua_tolstring(L, 2, &length); + if (query != NULL) { + int error = mysqllib_state.mysql_real_query(data->db, query, (int) length); + if (!error) { + mysql_result * result = mysqllib_state.mysql_store_result(data->db); + if (result != NULL) { + int nofrows = 0; + int nofcolumns = 0; + mysqllib_state.mysql_field_seek(result, 0); + nofrows = (int) mysqllib_state.mysql_num_rows(result); + nofcolumns = mysqllib_state.mysql_num_fields(result); + /* This is similar to sqlite but there the callback is more indirect. */ + if (nofcolumns > 0 && nofrows > 0) { + for (int r = 0; r < nofrows; r++) { + mysql_row row = mysqllib_state.mysql_fetch_row(result); + lua_pushvalue(L, -1); + lua_pushinteger(L, nofcolumns); + lua_createtable(L, nofcolumns, 0); + for (int c = 0; c < nofcolumns; c++) { + lua_pushstring(L, row[c]); + lua_rawseti(L, -2, (lua_Integer)c + 1); + } + if (r) { + lua_call(L, 2, 0); + } else { + mysql_field * fields = mysqllib_state.mysql_fetch_fields(result); + lua_createtable(L, nofcolumns, 0); + for (int c = 0; c < nofcolumns; c++) { + lua_pushstring(L, fields[c].name); + lua_rawseti(L, -2, (lua_Integer)c + 1); + } + lua_call(L, 3, 0); + } + } + } + mysqllib_state.mysql_free_result(result); + } + lua_pushboolean(L, 1); + return 1; + } + } + } + } + lua_pushboolean(L, 0); + return 1; +} + +static int mysqllib_getmessage(lua_State * L) +{ + if (mysqllib_state.initialized) { + mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE); + if (data != NULL) { + lua_pushstring(L, mysqllib_state.mysql_error(data->db)); + return 1; + } + } + return 0; +} + +/* private */ + +static int mysqllib_free(lua_State * L) +{ + return mysqllib_close(L); +} + +/* <string> = tostring(instance) */ + +static int mysqllib_tostring(lua_State * L) +{ + if (mysqllib_state.initialized) { + mysqllib_data * data = luaL_checkudata(L, 1, MYSQLLIB_METATABLE); + if (data != NULL) { + (void) lua_pushfstring(L, "<mysqllib-instance %p>", data); + } else { + lua_pushnil(L); + } + return 1; + } else { + return 0; + } +} + +static const struct luaL_Reg mysqllib_metatable[] = { + { "__tostring", mysqllib_tostring }, + { "__gc", mysqllib_free }, + { NULL, NULL }, +}; + +static struct luaL_Reg mysqllib_function_list[] = { + { "initialize", mysqllib_initialize }, + { "open", mysqllib_open }, + { "close", mysqllib_close }, + { "execute", mysqllib_execute }, + { "getmessage", mysqllib_getmessage }, + { NULL, NULL }, +}; + +int luaopen_mysql(lua_State * L) +{ + luaL_newmetatable(L, MYSQLLIB_METATABLE); + luaL_setfuncs(L, mysqllib_metatable, 0); + lmt_library_register(L, "mysql", mysqllib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtoptional.c b/source/luametatex/source/luaoptional/lmtoptional.c new file mode 100644 index 000000000..0cfbd166c --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtoptional.c @@ -0,0 +1,50 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +/*tex + + We don't want the binary top explode and have depdencies that will kill this project in the + end. So, we provide optionals: these are loaded lazy and libraries need to be present in + the tree. They are unofficial and not supported in the sense that ConTeXt doesn't depend on + them. + + The socket library is a candidate for ending up here too, as are the optional rest modules + lzo and lz4. + +*/ + +int luaopen_optional(lua_State *L) { + /*tex We always have an |optional| root table. */ + lmt_library_initialize(L); + luaopen_library(L); + luaopen_foreign(L); /* maybe in main */ + /*tex These are kind of standard. */ + luaopen_sqlite(L); + luaopen_mysql(L); + luaopen_postgress(L); + luaopen_curl(L); + luaopen_ghostscript(L); + luaopen_graphicsmagick(L); + luaopen_imagemagick(L); + luaopen_zint(L); + /*tex These are fun. */ + luaopen_mujs(L); + /*tex These might be handy. */ + luaopen_lzo(L); + luaopen_lz4(L); + luaopen_zstd(L); + luaopen_lzma(L); + /*tex These are extras. */ +# ifdef LMT_KPSE_TOO + luaopen_kpse(L); +# endif +# ifdef LMT_HB_TOO + luaopen_hb(L); +# endif + /*tex Done. */ + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtoptional.h b/source/luametatex/source/luaoptional/lmtoptional.h new file mode 100644 index 000000000..bceec90df --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtoptional.h @@ -0,0 +1,34 @@ +/* + See license.txt in the root of this project. +*/ + +# ifndef LMT_LUAOPTIONAL_H +# define LMT_LUAOPTIONAL_H + +# include <lua.h> + +/*tex This saves a bunch of h files. */ + +extern int luaopen_optional (lua_State *L); +extern int luaopen_library (lua_State *L); +extern int luaopen_foreign (lua_State *L); + +extern int luaopen_sqlite (lua_State *L); +extern int luaopen_mysql (lua_State *L); +extern int luaopen_curl (lua_State *L); +extern int luaopen_postgress (lua_State *L); +extern int luaopen_ghostscript (lua_State *L); +extern int luaopen_graphicsmagick(lua_State *L); +extern int luaopen_imagemagick (lua_State *L); +extern int luaopen_zint (lua_State *L); +extern int luaopen_mujs (lua_State *L); +extern int luaopen_lzo (lua_State *L); +extern int luaopen_lz4 (lua_State *L); +extern int luaopen_zstd (lua_State *L); +extern int luaopen_lzma (lua_State *L); +extern int luaopen_kpse (lua_State *L); /*tex For testing compatibility, if needed at all, not really I guess. */ +extern int luaopen_hb (lua_State *L); /*tex For when Idris needs to check fonts some day ... old stuff, not tested much. */ + +extern int luaextend_xcomplex (lua_State *L); + +# endif diff --git a/source/luametatex/source/luaoptional/lmtpostgress.c b/source/luametatex/source/luaoptional/lmtpostgress.c new file mode 100644 index 000000000..cdf515155 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtpostgress.c @@ -0,0 +1,306 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +typedef void PGconn; +typedef void PGresult; + +typedef enum postgres_polling_status_type { + PGRES_POLLING_FAILED, + PGRES_POLLING_READING, + PGRES_POLLING_WRITING, + PGRES_POLLING_OK +} postgres_polling_status_type; + +typedef enum postgres_exec_status_type { + PGRES_EMPTY_QUERY, + PGRES_COMMAND_OK, + PGRES_TUPLES_OK, + PGRES_COPY_OUT, + PGRES_COPY_IN, + PGRES_BAD_RESPONSE, + PGRES_NONFATAL_ERROR, + PGRES_FATAL_ERROR, + PGRES_COPY_BOTH, + PGRES_SINGLE_TUPLE +} postgres_exec_status_type; + +typedef enum postgres_connection_status_type { + PGRES_CONNECTION_OK, + PGRES_CONNECTION_BAD, + PGRES_CONNECTION_STARTED, + PGRES_CONNECTION_MADE, + PGRES_CONNECTION_AWAITING_RESPONSE, + PGRES_CONNECTION_AUTH_OK, + PGRES_CONNECTION_SETENV, + PGRES_CONNECTION_SSL_STARTUP, + PGRES_CONNECTION_NEEDED +} postgres_connection_status_type; + +# define POSTGRESSLIB_METATABLE "luatex.postgresslib" + +typedef struct postgresslib_data { + /*tex There is not much more than a pointer currently. */ + PGconn * db; +} postgresslib_data ; + +typedef struct postgresslib_state_info { + + int initialized; + int padding; + + PGconn * (*PQsetdbLogin) ( + const char *pghost, + const char *pgport, + const char *pgoptions, + const char *pgtty, + const char *dbName, + const char *login, + const char *pwd + ); + + postgres_connection_status_type (*PQstatus) ( + const PGconn *conn + ); + + void (*PQfinish) ( + PGconn *conn + ); + + char * (*PQerrorMessage) ( + const PGconn *conn + ); + + int (*PQsendQuery) ( + PGconn *conn, + const char *command + ); + + PGresult * (*PQgetResult) ( + PGconn *conn + ); + + postgres_exec_status_type (*PQresultStatus) ( + const PGresult *res + ); + + int (*PQntuples) ( + const PGresult *res + ); + + int (*PQnfields) ( + const PGresult *res + ); + + void (*PQclear) ( + PGresult *res + ); + + char * (*PQfname) ( + const PGresult *res, + int column_number + ); + + char * (*PQgetvalue) ( + const PGresult *res, + int row_number, + int column_number + ); + +} postgresslib_state_info; + +static postgresslib_state_info postgresslib_state = { + + .initialized = 0, + .padding = 0, + + .PQsetdbLogin = NULL, + .PQstatus = NULL, + .PQfinish = NULL, + .PQerrorMessage = NULL, + .PQsendQuery = NULL, + .PQgetResult = NULL, + .PQresultStatus = NULL, + .PQntuples = NULL, + .PQnfields = NULL, + .PQclear = NULL, + .PQfname = NULL, + .PQgetvalue = NULL, + +}; + +static int postgresslib_initialize(lua_State * L) +{ + if (! postgresslib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename != NULL) { + + lmt_library lib = lmt_library_load(filename); + + postgresslib_state.PQsetdbLogin = lmt_library_find(lib, "PQsetdbLogin"); + postgresslib_state.PQstatus = lmt_library_find(lib, "PQstatus"); + postgresslib_state.PQfinish = lmt_library_find(lib, "PQfinish"); + postgresslib_state.PQerrorMessage = lmt_library_find(lib, "PQerrorMessage"); + postgresslib_state.PQsendQuery = lmt_library_find(lib, "PQsendQuery"); + postgresslib_state.PQgetResult = lmt_library_find(lib, "PQgetResult"); + postgresslib_state.PQresultStatus = lmt_library_find(lib, "PQresultStatus"); + postgresslib_state.PQntuples = lmt_library_find(lib, "PQntuples"); + postgresslib_state.PQnfields = lmt_library_find(lib, "PQnfields"); + postgresslib_state.PQclear = lmt_library_find(lib, "PQclear"); + postgresslib_state.PQfname = lmt_library_find(lib, "PQfname"); + postgresslib_state.PQgetvalue = lmt_library_find(lib, "PQgetvalue"); + + postgresslib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, postgresslib_state.initialized); + return 1; +} + +static int postgresslib_open(lua_State * L) +{ + if (postgresslib_state.initialized) { + const char *database = luaL_checkstring(L, 1); + const char *username = luaL_optstring(L, 2, NULL); + const char *password = luaL_optstring(L, 3, NULL); + const char *host = luaL_optstring(L, 4, NULL); + const char *port = luaL_optstring(L, 5, NULL); + PGconn *db = postgresslib_state.PQsetdbLogin(host, port, NULL, NULL, database, username, password); + if (db != NULL && postgresslib_state.PQstatus(db) == PGRES_CONNECTION_BAD) { + postgresslib_state.PQfinish(db); + } else { + postgresslib_data *data = lua_newuserdatauv(L, sizeof(data), 0); + data->db = db ; + luaL_getmetatable(L, POSTGRESSLIB_METATABLE); + lua_setmetatable(L, -2); + return 1; + } + } + return 0; +} + +static int postgresslib_close(lua_State * L) +{ + if (postgresslib_state.initialized) { + postgresslib_data * data = luaL_checkudata(L,1,POSTGRESSLIB_METATABLE); + if (data != NULL) { + postgresslib_state.PQfinish(data->db); + data->db = NULL; + } + } + return 0; +} + +/* execute(database,querystring,callback) : callback(nofcolumns,values,fields) */ + +static int postgresslib_execute(lua_State * L) +{ + if (postgresslib_state.initialized) { + postgresslib_data * data = luaL_checkudata(L, 1, POSTGRESSLIB_METATABLE); + if (data != NULL) { + size_t length = 0; + const char *query = lua_tolstring(L, 2, &length); + if (query != NULL) { + int error = postgresslib_state.PQsendQuery(data->db, query); + if (!error) { + PGresult * result = postgresslib_state.PQgetResult(data->db); + if (result) { + if (postgresslib_state.PQresultStatus(result) == PGRES_TUPLES_OK) { + int nofrows = postgresslib_state.PQntuples(result); + int nofcolumns = postgresslib_state.PQnfields(result); + /* This is similar to sqlite but there the callback is more indirect. */ + if (nofcolumns > 0 && nofrows > 0) { + for (int r = 0; r < nofrows; r++) { + lua_pushvalue(L, -1); + lua_pushinteger(L, nofcolumns); + lua_createtable(L, nofcolumns, 0); + for (int c = 0; c < nofcolumns; c++) { + lua_pushstring(L, postgresslib_state.PQgetvalue(result, r, c)); + lua_rawseti(L,- 2, (lua_Integer)c + 1); + } + if (r) { + lua_call(L, 2, 0); + } else { + lua_createtable(L, nofcolumns, 0); + for (int c = 0; c < nofcolumns; c++) { + lua_pushstring(L, postgresslib_state.PQfname(result,c)); + lua_rawseti(L, -2, (lua_Integer)c + 1); + } + lua_call(L,3,0); + } + } + } + } + postgresslib_state.PQclear(result); + } + lua_pushboolean(L, 1); + return 1; + } + } + } + } + lua_pushboolean(L, 0); + return 1; +} + +static int postgresslib_getmessage(lua_State * L) +{ + if (postgresslib_state.initialized) { + postgresslib_data * data = luaL_checkudata(L, 1, POSTGRESSLIB_METATABLE); + if (data != NULL) { + lua_pushstring(L, postgresslib_state.PQerrorMessage(data->db)); + return 1; + } + } + return 0; +} + +/* private */ + +static int postgresslib_free(lua_State * L) +{ + return postgresslib_close(L); +} + +/* <string> = tostring(instance) */ + +static int postgresslib_tostring(lua_State * L) + { + if (postgresslib_state.initialized) { + postgresslib_data * data = luaL_checkudata(L, 1, POSTGRESSLIB_METATABLE); + if (data != NULL) { + (void) lua_pushfstring(L, "<postgresslib-instance %p>", data); + } else { + lua_pushnil(L); + } + return 1; + } else { + return 0; + } +} + +static const struct luaL_Reg postgresslib_metatable[] = { + { "__tostring", postgresslib_tostring }, + { "__gc", postgresslib_free }, + { NULL, NULL }, +}; + +static struct luaL_Reg postgresslib_function_list[] = { + { "initialize", postgresslib_initialize }, + { "open", postgresslib_open }, + { "close", postgresslib_close }, + { "execute", postgresslib_execute }, + { "getmessage", postgresslib_getmessage }, + { NULL, NULL }, +}; + +int luaopen_postgress(lua_State * L) +{ + luaL_newmetatable(L, POSTGRESSLIB_METATABLE); + luaL_setfuncs(L, postgresslib_metatable, 0); + lmt_library_register(L, "postgress", postgresslib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtsqlite.c b/source/luametatex/source/luaoptional/lmtsqlite.c new file mode 100644 index 000000000..e6e8f1239 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtsqlite.c @@ -0,0 +1,228 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +typedef struct sqlite3_instance sqlite3_instance; + +# define SQLITELIB_METATABLE "luatex.sqlitelib" + +typedef struct sqlitelib_data { + /*tex There is not much more than a pointer currently. */ + sqlite3_instance *db; +} sqlitelib_data ; + +typedef struct sqlitelib_state_info { + + int initialized; + int padding; + + int (*sqlite3_initialize) ( + void + ); + + int (*sqlite3_open) ( + const char *filename, + sqlite3_instance **ppDb + ); + + int (*sqlite3_close) ( + sqlite3_instance * + ); + + int (*sqlite3_exec) ( + sqlite3_instance *, + const char *sql, + int (*callback)(void*, int, char**, char**), + void *, + char **errmsg + ); + + const char * (*sqlite3_errmsg) ( + sqlite3_instance * + ); + +} sqlitelib_state_info; + +static sqlitelib_state_info sqlitelib_state = { + + .initialized = 0, + .padding = 0, + + .sqlite3_initialize = NULL, + .sqlite3_open = NULL, + .sqlite3_close = NULL, + .sqlite3_exec = NULL, + .sqlite3_errmsg = NULL, + +}; + +static int sqlitelib_initialize(lua_State * L) +{ + if (! sqlitelib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + sqlitelib_state.sqlite3_initialize = lmt_library_find(lib, "sqlite3_initialize"); + sqlitelib_state.sqlite3_open = lmt_library_find(lib, "sqlite3_open"); + sqlitelib_state.sqlite3_close = lmt_library_find(lib, "sqlite3_close"); + sqlitelib_state.sqlite3_exec = lmt_library_find(lib, "sqlite3_exec"); + sqlitelib_state.sqlite3_errmsg = lmt_library_find(lib, "sqlite3_errmsg"); + + sqlitelib_state.initialized = lmt_library_okay(lib); + } + if (sqlitelib_state.initialized) { + sqlitelib_state.sqlite3_initialize(); + } + } + lua_pushboolean(L, sqlitelib_state.initialized); + return 1; +} + +static int sqlitelib_open(lua_State * L) +{ + if (sqlitelib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename != NULL) { + sqlitelib_data *data = lua_newuserdatauv(L, sizeof(data), 0); + if (! sqlitelib_state.sqlite3_open(filename, &(data->db))) { + luaL_getmetatable(L, SQLITELIB_METATABLE); + lua_setmetatable(L, -2); + return 1; + } + } + } + return 0; +} + +static int sqlitelib_close(lua_State * L) +{ + if (sqlitelib_state.initialized) { + sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE); + if (data != NULL) { + sqlitelib_state.sqlite3_close(data->db); + data->db = NULL; + } + } + return 0; +} + +/* we could save the fields in the registry */ + +static int rows_done = 0; /* can go on stack */ + +static int sqlitelib_callback(void * L, int nofcolumns, char **values, char **fields) +{ + lua_pushvalue(L, -1); + lua_pushinteger(L, nofcolumns); + if (nofcolumns > 0 && values != NULL) { + lua_createtable(L, nofcolumns, 0); + for (int i = 0; i < nofcolumns; i++) { + lua_pushstring(L, values[i]); + lua_rawseti(L, -2, (lua_Integer)i + 1); + } + if (! rows_done && fields != NULL) { + lua_createtable(L, nofcolumns, 0); + for (int i = 0; i < nofcolumns; i++) { + lua_pushstring(L, fields[i]); + lua_rawseti(L, -2, (lua_Integer)i + 1); + } + lua_call(L, 3, 0); + } else { + lua_call(L, 2, 0); + } + } else { + lua_call(L, 1, 0); + } + ++rows_done; + return 0; +} + +/* execute(database,querystring,callback) : callback(nofcolumns,values,fields) */ + +static int sqlitelib_execute(lua_State * L) +{ + if (sqlitelib_state.initialized && ! rows_done) { + sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE); + if (data != NULL) { + const char *query = lua_tostring(L, 2); + if (query != NULL) { + int result = 0; + rows_done = 0; + if (lua_isfunction(L, 3)) { + result = sqlitelib_state.sqlite3_exec(data->db, query, &sqlitelib_callback, L, NULL); + } else { + result = sqlitelib_state.sqlite3_exec(data->db, query, NULL, NULL, NULL); + } + rows_done = 0; + lua_pushboolean(L, ! result); + return 1; + } + } + } + lua_pushboolean(L, 0); + return 1; +} + +static int sqlitelib_getmessage(lua_State * L) +{ + if (sqlitelib_state.initialized) { + sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE); + if (data != NULL) { + lua_pushstring(L, sqlitelib_state.sqlite3_errmsg(data->db)); + return 1; + } + } + return 0; +} + +/* private */ + +static int sqlitelib_free(lua_State * L) +{ + return sqlitelib_close(L); +} + +/* <string> = tostring(instance) */ + +static int sqlitelib_tostring(lua_State * L) +{ + if (sqlitelib_state.initialized) { + sqlitelib_data * data = luaL_checkudata(L, 1, SQLITELIB_METATABLE); + if (data != NULL) { + (void) lua_pushfstring(L, "<sqlitelib-instance %p>", data); + } else { + lua_pushnil(L); + } + return 1; + } else { + return 0; + } +} + +static const struct luaL_Reg sqlitelib_metatable[] = { + { "__tostring", sqlitelib_tostring }, + { "__gc", sqlitelib_free }, + { NULL, NULL }, +}; + +static struct luaL_Reg sqlitelib_function_list[] = { + { "initialize", sqlitelib_initialize }, + { "open", sqlitelib_open }, + { "close", sqlitelib_close }, + { "execute", sqlitelib_execute }, + { "getmessage", sqlitelib_getmessage }, + { NULL, NULL }, +}; + +int luaopen_sqlite(lua_State * L) +{ + luaL_newmetatable(L, SQLITELIB_METATABLE); + luaL_setfuncs(L, sqlitelib_metatable, 0); + lmt_library_register(L, "sqlite", sqlitelib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtzint.c b/source/luametatex/source/luaoptional/lmtzint.c new file mode 100644 index 000000000..0783238c0 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtzint.c @@ -0,0 +1,518 @@ +/* + See license.txt in the root of this project. +*/ + +/* + For a long time the zint api was quite stable but in after 2020 it started changing: the data + structures got fields added in the middle So, after a few updates Michal Vlasák suggested that + we adapt to versions. We can always decide to drop older ones when we get too many. The next + variant is a mix of our attempts to deal with this issue. + +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +# define ZINT_UNICODE_MODE 1 +# define ZINT_OUT_BUFFER 0 +# define ZINT_DM_SQUARE 100 + +struct zint_vector; + +typedef struct { + int symbology; + float height; + int whitespace_width; + int whitespace_height; + int border_width; + int output_options; + char fgcolour[10]; + char bgcolour[10]; + char *fgcolor; + char *bgcolor; + char outfile[256]; + float scale; + int option_1; + int option_2; + int option_3; + int show_hrt; + int fontsize; + int input_mode; + int eci; + unsigned char text[128]; + int rows; + int width; + char primary[128]; + unsigned char encoded_data[200][143]; + float row_height[200]; + char errtxt[100]; + unsigned char *bitmap; + int bitmap_width; + int bitmap_height; + unsigned char *alphamap; + unsigned int bitmap_byte_length; + float dot_size; + struct zint_vector *vector; + int debug; + int warn_level; +} zint_symbol_210; + +struct zint_structapp { + int index; + int count; + char id[32]; +}; + +typedef struct { + int symbology; + float height; + float scale; + int whitespace_width; + int whitespace_height; + int border_width; + int output_options; + char fgcolour[10]; + char bgcolour[10]; + char *fgcolor; + char *bgcolor; + char outfile[256]; + char primary[128]; + int option_1; + int option_2; + int option_3; + int show_hrt; + int fontsize; + int input_mode; + int eci; + float dot_size; + float guard_descent; + struct zint_structapp structapp; + int warn_level; + int debug; + unsigned char text[128]; + int rows; + int width; + unsigned char encoded_data[200][144]; + float row_height[200]; + char errtxt[100]; + unsigned char *bitmap; + int bitmap_width; + int bitmap_height; + unsigned char *alphamap; + unsigned int bitmap_byte_length; + struct zint_vector *vector; +} zint_symbol_211; + + +typedef struct zint_rectangle zint_rectangle; + +typedef struct { + double x; + double y; + double w; + double h; + zint_rectangle *next; +} lmt_zint_rectangle; + +static void lmt_zint_get_rect( + zint_rectangle *zint_, + lmt_zint_rectangle *lmt +) +{ + struct { + float x; + float y; + float height; + float width; + int colour; + zint_rectangle *next; + } *zint = (void*) zint_; + lmt->x = (double) zint->x; + lmt->y = (double) zint->y; + lmt->w = (double) zint->width; + lmt->h = (double) zint->height; + lmt->next = zint->next; +} + +typedef struct zint_circle zint_circle; + +typedef struct { + double x; + double y; + double d; + zint_circle *next; +} lmt_zint_circle; + +static void lmt_zint_get_circle_210 +( + zint_circle *zint_, + lmt_zint_circle *lmt +) +{ + struct { + float x; + float y; + float diameter; + int colour; + zint_circle *next; + } *zint = (void*) zint_; + lmt->x = (double) zint->x; + lmt->y = (double) zint->y; + lmt->d = (double) zint->diameter; + lmt->next = zint->next; +} + +static void lmt_zint_get_circle_211 +( + zint_circle *zint_, + lmt_zint_circle *lmt +) +{ + struct { + float x; + float y; + float diameter; + float width; + int colour; + zint_circle *next; + } *zint = (void*) zint_; + lmt->x = (double) zint->x; + lmt->y = (double) zint->y; + lmt->d = (double) zint->diameter; + lmt->next = zint->next; +} + +typedef struct zint_hexagon zint_hexagon; + +typedef struct { + double x; + double y; + double d; + zint_hexagon *next; +} lmt_zint_hexagon; + +static void lmt_zint_get_hexagon +( + zint_hexagon *zint_, + lmt_zint_hexagon *lmt +) +{ + struct { + float x; + float y; + float diameter; + int rotation; + zint_hexagon *next; + } *zint = (void *) zint_; + lmt->x = (double) zint->x; + lmt->y = (double) zint->y; + lmt->d = (double) zint->diameter; + lmt->next = zint->next; +} + +typedef struct zint_string zint_string; + +typedef struct { + double x; + double y; + double s; + const char *t; + zint_string *next; +} lmt_zint_string; + +static void lmt_zint_next_string(zint_string *zint_, lmt_zint_string *lmt) +{ + struct { + float x; + float y; + float fsize; + float width; + int length; + int rotation; + int halign; + unsigned char *text; + zint_string *next; + } *zint = (void *) zint_; + lmt->x = (double) zint->x; + lmt->y = (double) zint->y; + lmt->s = (double) zint->fsize; + lmt->t = (const char *) zint->text; + lmt->next = zint->next; +} + +typedef struct zint_symbol zint_symbol; + +typedef struct zint_vector { + float width; + float height; + zint_rectangle *rectangles; + zint_hexagon *hexagons; + zint_string *strings; + zint_circle *circles; +} zint_vector; + +static zint_vector *lmt_zint_vector_210(zint_symbol *symbol_) +{ + zint_symbol_210 *symbol = (void*) symbol_; + return symbol->vector; +} + +static zint_vector *lmt_zint_vector_211(zint_symbol *symbol_) +{ + zint_symbol_211 *symbol = (void*) symbol_; + return symbol->vector; +} + +static void lmt_zint_symbol_set_options_210(zint_symbol *symbol_, int symbology, int input_mode, int output_options, int square) +{ + zint_symbol_210 *symbol = (void*) symbol_; + symbol->symbology = symbology; + symbol->input_mode = input_mode; + symbol->output_options = output_options; + if (square) + symbol->option_3 = ZINT_DM_SQUARE; +} + +static void lmt_zint_symbol_set_options_211(zint_symbol *symbol_, int symbology, int input_mode, int output_options, int square) +{ + zint_symbol_211 *symbol = (void*) symbol_; + symbol->symbology = symbology; + symbol->input_mode = input_mode; + symbol->output_options = output_options; + if (square) { + symbol->option_3 = ZINT_DM_SQUARE; + } +} + +typedef struct zintlib_state_info { + + int initialized; + int version; + + int (*ZBarcode_Version) ( + void + ); + + zint_symbol * (*ZBarcode_Create) ( + void + ); + + void (*ZBarcode_Delete) ( + zint_symbol *symbol + ); + + int (*ZBarcode_Encode_and_Buffer_Vector) ( + zint_symbol *symbol, + const unsigned char *input, + int length, + int rotate_angle + ); + +} zintlib_state_info; + +static zintlib_state_info zintlib_state = { + + .initialized = 0, + .version = 0, + + .ZBarcode_Version = NULL, + .ZBarcode_Create = NULL, + .ZBarcode_Delete = NULL, + .ZBarcode_Encode_and_Buffer_Vector = NULL, + +}; + +static void (*lmt_zint_get_circle) ( + zint_circle *zint_, + lmt_zint_circle *lmt +); + +static zint_vector *(*lmt_zint_vector)( + zint_symbol *symbol_ +); +static void (*lmt_zint_symbol_set_options)( + zint_symbol *symbol, + int symbology, + int input_mode, + int output_options, + int square +); + +static int zintlib_initialize(lua_State * L) +{ + if (! zintlib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + zintlib_state.ZBarcode_Version = lmt_library_find(lib, "ZBarcode_Version"); + zintlib_state.ZBarcode_Create = lmt_library_find(lib, "ZBarcode_Create"); + zintlib_state.ZBarcode_Delete = lmt_library_find(lib, "ZBarcode_Delete"); + zintlib_state.ZBarcode_Encode_and_Buffer_Vector = lmt_library_find(lib, "ZBarcode_Encode_and_Buffer_Vector"); + + zintlib_state.initialized = lmt_library_okay(lib); + + if (zintlib_state.ZBarcode_Version) { + zintlib_state.version = zintlib_state.ZBarcode_Version(); + } + zintlib_state.version = zintlib_state.version / 100; + if (zintlib_state.version < 210) { + zintlib_state.initialized = 0; + } else if (zintlib_state.version < 211) { + lmt_zint_get_circle = lmt_zint_get_circle_210; + lmt_zint_vector = lmt_zint_vector_210; + lmt_zint_symbol_set_options = lmt_zint_symbol_set_options_210; + } else { + lmt_zint_get_circle = lmt_zint_get_circle_211; + lmt_zint_vector = lmt_zint_vector_211; + lmt_zint_symbol_set_options = lmt_zint_symbol_set_options_211; + } + } + } + lua_pushboolean(L, zintlib_state.initialized); + return 1; +} + +static int zintlib_execute(lua_State * L) +{ + if (zintlib_state.initialized) { + if (lua_type(L, 1) == LUA_TTABLE) { + int code = -1; + size_t l = 0; + const unsigned char *s = NULL; + const char *o = NULL; + if (lua_getfield(L, 1, "code") == LUA_TNUMBER) { + code = lmt_tointeger(L, -1); + } + lua_pop(L, 1); + switch (lua_getfield(L, 1, "text")) { + case LUA_TSTRING: + case LUA_TNUMBER: + s = (const unsigned char *) lua_tolstring(L, -1, &l); + break; + } + lua_pop(L, 1); + if (lua_getfield(L, 1, "option") == LUA_TSTRING) { + /* for the moment one option */ + o = lua_tostring(L, -1); + } + lua_pop(L, 1); + if (code >= 0 && l > 0) { + zint_symbol *symbol = zintlib_state.ZBarcode_Create(); + if (symbol) { + /*tex + We could handle this at the \LUA\ end but as we only have a few options we + do it here. + */ + int square = (o && (strcmp(o, "square") == 0)) ? 1 : 0; + lmt_zint_symbol_set_options(symbol, code, ZINT_UNICODE_MODE, ZINT_OUT_BUFFER, square); + if (zintlib_state.ZBarcode_Encode_and_Buffer_Vector(symbol, s, (int) l, 0)) { + zintlib_state.ZBarcode_Delete(symbol); + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid result"); + } else { + zint_vector *vector = lmt_zint_vector(symbol); + if (vector) { + /*tex + It's a bit like the svg output ... first I used named fields but a + list is more efficient, not so much in the \LUA\ interfacing but in + generating an compact path at the \METAPOST\ end. + */ + lua_createtable(L, 0, 4); + if (vector->rectangles) { + lmt_zint_rectangle rectangle; + int i = 1; + lua_newtable(L); + for (zint_rectangle *r = vector->rectangles; r; r = rectangle.next) { + lmt_zint_get_rect(r, &rectangle); + lua_createtable(L, 4, 0); + lua_pushinteger(L, lmt_roundedfloat(rectangle.x)); lua_rawseti(L, -2, 1); + lua_pushinteger(L, lmt_roundedfloat(rectangle.y)); lua_rawseti(L, -2, 2); + lua_pushinteger(L, lmt_roundedfloat(rectangle.w)); lua_rawseti(L, -2, 3); + lua_pushinteger(L, lmt_roundedfloat(rectangle.h)); lua_rawseti(L, -2, 4); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "rectangles"); + } + if (vector->hexagons) { + lmt_zint_hexagon hexagon; + int i = 1; + lua_newtable(L); + for (zint_hexagon *h = vector->hexagons; h; h = hexagon.next) { + lmt_zint_get_hexagon(h, &hexagon); + lua_createtable(L, 0, 3); + lua_pushinteger(L, lmt_roundedfloat(hexagon.x)); lua_rawseti(L, -2, 1); + lua_pushinteger(L, lmt_roundedfloat(hexagon.y)); lua_rawseti(L, -2, 2); + lua_pushinteger(L, lmt_roundedfloat(hexagon.d)); lua_rawseti(L, -2, 3); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "hexagons"); + } + if (vector->circles) { + lmt_zint_circle circle; + int i = 1; + lua_newtable(L); + for (zint_circle *c = vector->circles; c; c = circle.next) { + lmt_zint_get_circle(c, &circle); + lua_createtable(L, 0, 3); + lua_pushinteger(L, lmt_roundedfloat(circle.x)); lua_rawseti(L, -2, 1); + lua_pushinteger(L, lmt_roundedfloat(circle.y)); lua_rawseti(L, -2, 2); + lua_pushinteger(L, lmt_roundedfloat(circle.d)); lua_rawseti(L, -2, 3); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "circles"); + } + if (vector->strings) { + lmt_zint_string string; + int i = 1; + lua_newtable(L); + for (zint_string *s = vector->strings; s; s = string.next) { + lmt_zint_next_string(s, &string); + lua_createtable(L, 0, 4); + lua_pushinteger(L, lmt_roundedfloat(string.x)); lua_rawseti(L, -2, 1); + lua_pushinteger(L, lmt_roundedfloat(string.y)); lua_rawseti(L, -2, 2); + lua_pushinteger(L, lmt_roundedfloat(string.s)); lua_rawseti(L, -2, 3); + lua_pushstring (L, string.t ); lua_rawseti(L, -2, 4); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "strings"); + } + zintlib_state.ZBarcode_Delete(symbol); + return 1; + } else { + zintlib_state.ZBarcode_Delete(symbol); + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid result vector"); + } + } + } else { + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid result symbol"); + } + } else { + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid code"); + } + } else { + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid specification"); + } + } else { + lua_pushboolean(L, 0); + lua_pushstring(L, "not initialized"); + } + return 2; +} + +static struct luaL_Reg zintlib_function_list[] = { + { "initialize", zintlib_initialize }, + { "execute", zintlib_execute }, + { NULL, NULL }, +}; + +int luaopen_zint(lua_State * L) +{ + lmt_library_register(L, "zint", zintlib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/lmtzstd.c b/source/luametatex/source/luaoptional/lmtzstd.c new file mode 100644 index 000000000..ebb188d54 --- /dev/null +++ b/source/luametatex/source/luaoptional/lmtzstd.c @@ -0,0 +1,118 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" +# include "lmtoptional.h" + +# define ZSTD_DEFAULTCLEVEL 3 + +typedef struct zstdlib_state_info { + + int initialized; + int padding; + + size_t (*ZSTD_compressBound) (size_t srcSize); + size_t (*ZSTD_getFrameContentSize) (const void *, size_t); + size_t (*ZSTD_compress) (void *dst, size_t dstCapacity, const void *src, size_t srcSize, int compressionLevel); + size_t (*ZSTD_decompress) (void *dst, size_t dstCapacity, const void *src, size_t compressedSize); + /* int (*ZSTD_minCLevel) (void); */ + /* int (*ZSTD_maxCLevel) (void); */ + /* unsigned (*ZSTD_isError) (size_t code); */ + /* const char *(*ZSTD_getErrorName) (size_t code); */ + +} zstdlib_state_info; + +static zstdlib_state_info zstdlib_state = { + + .initialized = 0, + .padding = 0, + + .ZSTD_compressBound = NULL, + .ZSTD_getFrameContentSize = NULL, + .ZSTD_compress = NULL, + .ZSTD_decompress = NULL, + /* .ZSTD_minCLevel = NULL, */ + /* .ZSTD_maxCLevel = NULL, */ + /* .ZSTD_isError = NULL, */ + /* .ZSTD_getErrorName = NULL, */ + +}; + +static int zstdlib_compress(lua_State *L) +{ + if (zstdlib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + int level = lmt_optinteger(L, 2, ZSTD_DEFAULTCLEVEL); + size_t targetsize = zstdlib_state.ZSTD_compressBound(sourcesize); + luaL_Buffer buffer; + char *target = luaL_buffinitsize(L, &buffer, targetsize); + size_t result = zstdlib_state.ZSTD_compress(target, targetsize, source, sourcesize, level); + if (result > 0) { + luaL_pushresultsize(&buffer, result); + } else { + lua_pushnil(L); + } + return 1; + } else { + return 0; + } +} + +static int zstdlib_decompress(lua_State *L) +{ + if (zstdlib_state.initialized) { + size_t sourcesize = 0; + const char *source = luaL_checklstring(L, 1, &sourcesize); + size_t targetsize = zstdlib_state.ZSTD_getFrameContentSize(source, sourcesize); + luaL_Buffer buffer; + char *target = luaL_buffinitsize(L, &buffer, targetsize); + size_t result = zstdlib_state.ZSTD_decompress(target, targetsize, source, sourcesize); + if (result > 0) { + luaL_pushresultsize(&buffer, result); + } else { + lua_pushnil(L); + } + return 1; + } else { + return 0; + } +} + +static int zstdlib_initialize(lua_State *L) +{ + if (! zstdlib_state.initialized) { + const char *filename = lua_tostring(L, 1); + if (filename) { + + lmt_library lib = lmt_library_load(filename); + + zstdlib_state.ZSTD_compressBound = lmt_library_find(lib, "ZSTD_compressBound"); + zstdlib_state.ZSTD_getFrameContentSize = lmt_library_find(lib, "ZSTD_getFrameContentSize"); + zstdlib_state.ZSTD_compress = lmt_library_find(lib, "ZSTD_compress"); + zstdlib_state.ZSTD_decompress = lmt_library_find(lib, "ZSTD_decompress"); + /* zstdlib_state.ZSTD_minCLevel = lmt_library_find(lib, "ZSTD_minCLevel"); */ + /* zstdlib_state.ZSTD_maxCLevel = lmt_library_find(lib, "ZSTD_maxCLevel"); */ + /* zstdlib_state.ZSTD_isError = lmt_library_find(lib, "ZSTD_isError"); */ + /* zstdlib_state.ZSTD_getErrorName = lmt_library_find(lib, "ZSTD_getErrorName"); */ + + zstdlib_state.initialized = lmt_library_okay(lib); + } + } + lua_pushboolean(L, zstdlib_state.initialized); + return 1; +} + +static struct luaL_Reg zstdlib_function_list[] = { + { "initialize", zstdlib_initialize }, + { "compress", zstdlib_compress }, + { "decompress", zstdlib_decompress }, + { NULL, NULL }, +}; + +int luaopen_zstd(lua_State * L) +{ + lmt_library_register(L, "zstd", zstdlib_function_list); + return 0; +} diff --git a/source/luametatex/source/luaoptional/readme.txt b/source/luametatex/source/luaoptional/readme.txt new file mode 100644 index 000000000..31b489da9 --- /dev/null +++ b/source/luametatex/source/luaoptional/readme.txt @@ -0,0 +1,30 @@ +Nota bene, + +This is the directory where optional module support ends up. Optional modules have an interface but +are not (nor will be) part of the binary. We might ship some at the context garden (like zint and +mujs) but the large one (read: with many dependencies or written in c++) have to come from the +operating system because if you use a library that is what you want: the external black box thing. +No sources end up in the distribution either, athough we will archive some. + +There will be no user modules here, just those interfaces that we provide and maintain as part of +standard ConTeXt LMTX. What users add themselves is up to them, including (long time !) support. So, +this is the canonnical version of optional. + +We might at some point add some safeguards so that we can be sure that ConTeXt is run with the +right binary because we want to prevent side effects (of any kind) resulting from a binary being +used with the same name and different features ... just because one of the objective is to have +a long term stable binary / macro package combination. Of course, what users do on their machines +is up to them. + +It might take a while before the interfaces and way we do this is stable. Also, keep in mind that +regular users never deal with these matters directly and only use the interfaces at the TeX and +Lua end. + +PS. The socket library (and maybe cerf) are also candidates for optional although cerf needs to be +compiled for windows which is not supported out of the box and sockets are way to large. We only +do optional libs that add little to the binary, a few KB at most! I'll definitely try to stick to +this principle! + +PS. Todo: move function pointers into state structures. + +Hans |