summaryrefslogtreecommitdiff
path: root/source/luametatex/source/luaoptional
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/luaoptional')
-rw-r--r--source/luametatex/source/luaoptional/cmake/mujs/CMakeLists.txt107
-rw-r--r--source/luametatex/source/luaoptional/cmake/mujs/CMakeSettings.json28
-rw-r--r--source/luametatex/source/luaoptional/lmtcerflib.c133
-rw-r--r--source/luametatex/source/luaoptional/lmtcurl.c506
-rw-r--r--source/luametatex/source/luaoptional/lmtforeign.c1191
-rw-r--r--source/luametatex/source/luaoptional/lmtghostscript.c175
-rw-r--r--source/luametatex/source/luaoptional/lmtgraphicsmagick.c199
-rw-r--r--source/luametatex/source/luaoptional/lmthb.c761
-rw-r--r--source/luametatex/source/luaoptional/lmtimagemagick.c144
-rw-r--r--source/luametatex/source/luaoptional/lmtkpse.c311
-rw-r--r--source/luametatex/source/luaoptional/lmtlz4.c193
-rw-r--r--source/luametatex/source/luaoptional/lmtlzma.c228
-rw-r--r--source/luametatex/source/luaoptional/lmtlzo.c108
-rw-r--r--source/luametatex/source/luaoptional/lmtmujs.c609
-rw-r--r--source/luametatex/source/luaoptional/lmtmysql.c325
-rw-r--r--source/luametatex/source/luaoptional/lmtoptional.c50
-rw-r--r--source/luametatex/source/luaoptional/lmtoptional.h34
-rw-r--r--source/luametatex/source/luaoptional/lmtpostgress.c306
-rw-r--r--source/luametatex/source/luaoptional/lmtsqlite.c228
-rw-r--r--source/luametatex/source/luaoptional/lmtzint.c518
-rw-r--r--source/luametatex/source/luaoptional/lmtzstd.c118
-rw-r--r--source/luametatex/source/luaoptional/readme.txt30
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