summaryrefslogtreecommitdiff
path: root/source/luametatex/source/lua/lmttexlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/lua/lmttexlib.c')
-rw-r--r--source/luametatex/source/lua/lmttexlib.c5580
1 files changed, 5580 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmttexlib.c b/source/luametatex/source/lua/lmttexlib.c
new file mode 100644
index 000000000..12510206a
--- /dev/null
+++ b/source/luametatex/source/lua/lmttexlib.c
@@ -0,0 +1,5580 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*
+
+ This module deals with access to some if the internal quanities of \TEX, like registers,
+ internal variables and all kind of lists. Because we provide access by name and/or number
+ (index) there is quite a bit of code here, and sometimes if can look a bit confusing.
+
+ The code here differs from \LUATEX\ in the sense that there are some more checks, a bit
+ more abstraction, more access and better performance. What we see here is the result of
+ years of experimenting and usage in \CONTEXT.
+
+ A remark about some of the special node lists that one can query: because some start with
+ a so called |temp| node, we have to set the |prev| link to |nil| because otherwise at the
+ \LUA\ end we expose that |temp| node and users are not suposed to touch them! In the setter
+ no |prev| link is set so we can presume that it's not used later on anyway; this is because
+ original \TEX\ has no |prev| links.
+
+ There is still room for improvement but I'll deal with that when I have a reason (read: when
+ I need something).
+
+*/
+
+# include "luametatex.h"
+
+/*tex
+ Due to the nature of the accessors, this is the module with most metatables. However, we
+ provide getters and setters too. Users can choose what they like most.
+*/
+
+# define TEX_METATABLE_ATTRIBUTE "tex.attribute"
+# define TEX_METATABLE_SKIP "tex.skip"
+# define TEX_METATABLE_GLUE "tex.glue"
+# define TEX_METATABLE_MUSKIP "tex.muskip"
+# define TEX_METATABLE_MUGLUE "tex.muglue"
+# define TEX_METATABLE_DIMEN "tex.dimen"
+# define TEX_METATABLE_COUNT "tex.count"
+# define TEX_METATABLE_TOKS "tex.toks"
+# define TEX_METATABLE_BOX "tex.box"
+# define TEX_METATABLE_SFCODE "tex.sfcode"
+# define TEX_METATABLE_LCCODE "tex.lccode"
+# define TEX_METATABLE_UCCODE "tex.uccode"
+# define TEX_METATABLE_HCCODE "tex.hccode"
+# define TEX_METATABLE_HMCODE "tex.hmcode"
+# define TEX_METATABLE_CATCODE "tex.catcode"
+# define TEX_METATABLE_MATHCODE "tex.mathcode"
+# define TEX_METATABLE_DELCODE "tex.delcode"
+# define TEX_METATABLE_LISTS "tex.lists"
+# define TEX_METATABLE_NEST "tex.nest"
+
+# define TEX_METATABLE_TEX "tex.tex"
+
+# define TEX_NEST_INSTANCE "tex.nest.instance"
+
+/*tex Let's share these. */
+
+static void texlib_aux_show_box_index_error(lua_State *L)
+{
+ luaL_error(L, "invalid index passed, range 0.." LMT_TOSTRING(max_box_register_index) " or name expected");
+}
+
+static void texlib_aux_show_character_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid character value %d passed, range 0.." LMT_TOSTRING(max_character_code), i);
+}
+
+static void texlib_aux_show_catcode_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid catcode %d passed, range 0.." LMT_TOSTRING(max_category_code), i);
+}
+
+static void texlib_aux_show_family_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid family %d passed, range 0.." LMT_TOSTRING(max_math_family_index), i);
+}
+
+static void texlib_aux_show_class_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid class %d passed, range 0.." LMT_TOSTRING(max_math_class_code), i);
+}
+
+static void texlib_aux_show_half_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid value %d passed, range 0.." LMT_TOSTRING(max_half_value), i);
+}
+
+/*tex
+
+ The rope model dates from the time that we had multiple \LUA\ instances so probably we can
+ simplify it a bit. On the other hand, it works, and also is upgraded for nodes, tokens and some
+ caching, so there is no real need to change anything now. A complication is anyway that input
+ states can nest and any change can mess it up.
+
+ We use a flag to indicate the kind of data that we are dealing with:
+
+ \starttabulate
+ \NC 0 \NC string \NC \NR
+ \NC 1 \NC char \NC \NR
+ \NC 2 \NC token \NC \NR
+ \NC 3 \NC node \NC \NR
+ \stoptabulate
+
+ By treating simple \ASCII\ characters special we prevent mallocs. We also pack small strings if
+ only because we have the room available anyway (due to padding).
+
+ For quite a while we used to have this:
+
+ \starttyping
+ typedef struct spindle_char {
+ unsigned char c1, c2, c3, c4;
+ } spindle_char;
+
+ typedef union spindle_data {
+ spindle_char c;
+ halfword h;
+ } spindle_data;
+ \stopttyping
+
+ The spindle and rope terminology was introduced by Taco early in the development of \LUATEX,
+ but in the meantime the datastructures have been adapted to deal with tokens and nodes. There
+ are also quite some optimizations in performance and memort usage (e.g. for small strings and
+ single characters).
+
+*/
+
+# define FULL_LINE 0
+# define PARTIAL_LINE 1
+# define PACKED_SIZE 8
+# define INITIAL_SIZE 32
+# define MAX_ROPE_CACHE 5000
+
+typedef union spindle_data {
+ unsigned char c[PACKED_SIZE];
+ halfword h;
+ char *t;
+} spindle_data;
+
+typedef struct spindle_rope {
+// char *text;
+ void *next;
+ union {
+ unsigned int tsize;
+ halfword tail;
+ };
+ unsigned char kind;
+ unsigned char partial;
+ short cattable;
+ spindle_data data;
+ /* alignment, not needed when we have c[PACKED_SIZE] */
+ /* int padding; */
+} spindle_rope;
+
+typedef struct spindle {
+ spindle_rope *head;
+ spindle_rope *tail;
+ int complete;
+ /* alignment */
+ int padding;
+} spindle;
+
+typedef struct spindle_state_info {
+ int spindle_size;
+ int spindle_index;
+ spindle *spindles;
+ spindle_rope *rope_cache;
+ int rope_count;
+ /* alignment */
+ int padding;
+} spindle_state_info ;
+
+static spindle_state_info lmt_spindle_state = {
+ .spindle_size = 0,
+ .spindle_index = 0,
+ .spindles = NULL,
+ .rope_cache = NULL,
+ .rope_count = 0,
+ .padding = 0
+};
+
+# define write_spindle lmt_spindle_state.spindles[lmt_spindle_state.spindle_index]
+# define read_spindle lmt_spindle_state.spindles[lmt_spindle_state.spindle_index - 1]
+
+inline static void texlib_aux_reset_spindle(int i)
+{
+ lmt_spindle_state.spindles[i].head = NULL;
+ lmt_spindle_state.spindles[i].tail = NULL;
+ lmt_spindle_state.spindles[i].complete = 0;
+}
+
+/*
+
+ Each rope takes 48 bytes. So, caching some 100 K ropes is not really a problem. In practice we
+ seldom reach that number anyway.
+
+*/
+
+inline static spindle_rope *texlib_aux_new_rope(void)
+{
+ spindle_rope *r;
+ if (lmt_spindle_state.rope_cache) {
+ r = lmt_spindle_state.rope_cache;
+ lmt_spindle_state.rope_cache = r->next;
+ } else {
+ r = (spindle_rope *) lmt_memory_malloc(sizeof(spindle_rope));
+ ++lmt_spindle_state.rope_count;
+ if (r) {
+ r->next = NULL;
+ } else {
+ tex_overflow_error("spindle", sizeof(spindle_rope));
+ }
+ }
+ return r;
+}
+
+inline static void texlib_aux_dispose_rope(spindle_rope *r)
+{
+ if (r) {
+ if (lmt_spindle_state.rope_count > MAX_ROPE_CACHE) {
+ lmt_memory_free(r);
+ --lmt_spindle_state.rope_count;
+ } else {
+ r->next = lmt_spindle_state.rope_cache;
+ lmt_spindle_state.rope_cache = r;
+ }
+ }
+}
+
+static void texlib_aux_initialize(void)
+{
+ lmt_spindle_state.spindles = lmt_memory_malloc(INITIAL_SIZE * sizeof(spindle));
+ if (lmt_spindle_state.spindles) {
+ for (int i = 0; i < INITIAL_SIZE; i++) {
+ texlib_aux_reset_spindle(i);
+ }
+ lmt_spindle_state.spindle_index = 0;
+ lmt_spindle_state.spindle_size = INITIAL_SIZE;
+ } else {
+ tex_overflow_error("spindle", sizeof(spindle));
+ }
+}
+
+/*tex
+ We could convert strings into tokenlists here but conceptually the split is cleaner.
+*/
+
+static int texlib_aux_store(lua_State *L, int i, int partial, int cattable)
+{
+ size_t tsize = 0;
+ spindle_rope *rn = NULL;
+ unsigned char kind = unset_lua_input;
+ spindle_data data = { .h = 0 };
+ switch (lua_type(L, i)) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ {
+ const char *sttemp = lua_tolstring(L, i, &tsize);
+ if (! partial) {
+ while (tsize > 0 && sttemp[tsize-1] == ' ') {
+ tsize--;
+ }
+ }
+ if (tsize > PACKED_SIZE) {
+ data.t = lmt_memory_malloc(tsize + 1);
+ kind = string_lua_input;
+ if (data.t) {
+ memcpy(data.t, sttemp, (tsize + 1));
+ break;
+ } else {
+ return 0;
+ }
+ } else {
+ /*tex
+ We could append to a previous but partial interferes and in practice it then
+ never can be done.
+ */
+ for (unsigned i = 0; i < tsize; i++) {
+ /*tex When we end up here we often don't have that many bytes. */
+ data.c[i] = (unsigned char) sttemp[i];
+ }
+ kind = packed_lua_input;
+ }
+ }
+ break;
+ case LUA_TUSERDATA:
+ {
+ void *p = lua_touserdata(L, i);
+ if (p && lua_getmetatable(L, i)) {
+ lua_get_metatablelua(token_instance);
+ if (lua_rawequal(L, -1, -2)) {
+ halfword token = (*((lua_token *) p)).token;
+ if (token_link(token)) {
+ /*tex
+ We cannnot pass a list unless we copy it, alternatively we can bump the ref count
+ but a quick test didn't work out that well.
+ */
+ token = token_link(token);
+ if (token) {
+ /*tex
+ We're now past the ref count head. Like below we could actually append to a
+ current rope but so far we seldom end up in here. Maybe I'll do add that later.
+ */
+ halfword t = null;
+ kind = token_list_lua_input;
+ data.h = tex_copy_token_list(token, &t);
+ tsize = t;
+ } else {
+ lua_pop(L, 2);
+ return 0;
+ }
+ } else {
+ /*tex
+ This is a little (mostly memory) optimization. We use token list instead of adding
+ single token ropes. That way we also need to push less back into the input. We
+ check for partial. This optimization is experimental and might go.
+ */
+ if (write_spindle.tail && write_spindle.tail->partial && partial == write_spindle.tail->partial) {
+ switch (write_spindle.tail->kind) {
+ case token_lua_input:
+ /*tex
+ Okay, we now allocate a token but still pushing into the input later has
+ less (nesting) overhead then because we process a sequence.
+ */
+ write_spindle.tail->kind = token_list_lua_input;
+ write_spindle.tail->data.h = tex_store_new_token(null, write_spindle.tail->data.h);
+ write_spindle.tail->tail = tex_store_new_token(write_spindle.tail->data.h, token_info(token));
+ break;
+ case token_list_lua_input:
+ /*tex
+ Normally we have short lists but it still pays off to store the tail
+ in |tsize| instead of locating the tail each time.
+ */
+ write_spindle.tail->tail = tex_store_new_token(write_spindle.tail->tail, token_info(token));
+ break;
+ default:
+ goto SINGLE;
+ }
+ lmt_token_state.luacstrings++; /* already set */
+ write_spindle.complete = 0; /* already set */
+ lua_pop(L, 2);
+ return 1;
+ }
+ /*tex The non-optimized case: */
+ SINGLE:
+ kind = token_lua_input;
+ data.h = token_info(token);
+ }
+ lua_pop(L, 2);
+ } else {
+ lua_get_metatablelua(node_instance);
+ if (lua_rawequal(L, -1, -3)) {
+ kind = node_lua_input;
+ data.h = *((halfword *) p);
+ lua_pop(L, 3);
+ } else {
+ lua_pop(L, 3);
+ return 0;
+ }
+ }
+ } else {
+ return 0;
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+ lmt_token_state.luacstrings++;
+ rn = texlib_aux_new_rope();
+ /* set */
+ rn->tsize = (unsigned) tsize;
+ rn->next = NULL;
+ rn->kind = kind;
+ rn->partial = (unsigned char) partial;
+ rn->cattable = (short) cattable;
+ rn->data = data;
+ /* add */
+ if (write_spindle.head) {
+ write_spindle.tail->next = rn;
+ } else {
+ write_spindle.head = rn;
+ }
+ write_spindle.tail = rn;
+ write_spindle.complete = 0;
+ return 1;
+}
+
+static void texlib_aux_store_token(halfword token, int partial, int cattable)
+{
+ spindle_rope *rn = texlib_aux_new_rope();
+ /* set */
+ rn->tsize = 0;
+ rn->next = NULL;
+ rn->kind = token_lua_input;
+ rn->partial = (unsigned char) partial;
+ rn->cattable = (short) cattable;
+ rn->data.h = token;
+ /* add */
+ if (write_spindle.head) {
+ write_spindle.tail->next = rn;
+ } else {
+ write_spindle.head = rn;
+ }
+ write_spindle.tail = rn;
+ write_spindle.complete = 0;
+ lmt_token_state.luacstrings++;
+}
+
+static void lmx_aux_store_string(char *str, int len, int cattable)
+{
+ spindle_rope *rn = texlib_aux_new_rope();
+ rn->data.h = 0; /* wipes */
+ if (len > PACKED_SIZE) {
+ rn->data.t = lmt_memory_malloc((size_t) len + 1);
+ if (rn->data.t) {
+ memcpy(rn->data.t, str, (size_t) len + 1);
+ } else {
+ len = 0;
+ }
+ rn->kind = string_lua_input;
+ } else {
+ for (int i = 0; i < len; i++) {
+ /* when we end up here we often don't have that many bytes */
+ rn->data.c[i] = (unsigned char) str[i];
+ }
+ rn->kind = packed_lua_input;
+ }
+ /* set */
+ rn->tsize = (unsigned) len;
+ rn->next = NULL;
+ rn->partial = FULL_LINE;
+ rn->cattable = (unsigned char) cattable;
+ /* add */
+ if (write_spindle.head) {
+ write_spindle.tail->next = rn;
+ } else {
+ write_spindle.head = rn;
+ }
+ write_spindle.tail = rn;
+ write_spindle.complete = 0;
+ lmt_token_state.luacstrings++;
+}
+
+static int texlib_aux_cprint(lua_State *L, int partial, int cattable, int startstrings)
+{
+ int n = lua_gettop(L);
+ int t = lua_type(L, startstrings);
+ if (n > startstrings && cattable != no_catcode_table_preset && t == LUA_TNUMBER) {
+ cattable = lmt_tointeger(L, startstrings);
+ ++startstrings;
+ if (cattable != default_catcode_table_preset && cattable != no_catcode_table_preset && ! tex_valid_catcode_table(cattable)) {
+ cattable = default_catcode_table_preset;
+ }
+ t = lua_type(L, startstrings);
+ }
+ if (t == LUA_TTABLE) {
+ for (int i = 1;; i++) {
+ lua_rawgeti(L, startstrings, i);
+ if (texlib_aux_store(L, -1, partial, cattable)) {
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ } else {
+ for (int i = startstrings; i <= n; i++) {
+ texlib_aux_store(L, i, partial, cattable);
+ }
+ }
+ return 0;
+}
+
+/*tex
+ We now feed back to be tokenized input from the \TEX\ end into the same handler as we use for
+ \LUA. It saves code but more important is that we no longer need the pseudo files and lines
+ that are kind of inefficient and depend on variable nodes.
+*/
+
+void lmt_cstring_store(char *str, int len, int cattable)
+{
+ lmx_aux_store_string(str, len, cattable);
+}
+
+void lmt_tstring_store(strnumber s, int cattable)
+{
+ lmx_aux_store_string((char *) str_string(s), (int) str_length(s), cattable);
+}
+
+/*tex
+ This is a bit of a dirty trick, needed for an experiment and it's fast enough for our purpose.
+*/
+
+void lmt_cstring_print(int cattable, const char *s, int ispartial)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = lua_gettop(L);
+ lua_settop(L, 0);
+ lua_pushinteger(L, cattable);
+ lua_pushstring(L, s);
+ texlib_aux_cprint(L, ispartial ? PARTIAL_LINE : FULL_LINE, default_catcode_table_preset, 1);
+ lua_settop(L, top);
+}
+
+/* lua.write */
+
+static int texlib_write(lua_State *L)
+{
+ return texlib_aux_cprint(L, FULL_LINE, no_catcode_table_preset, 1);
+}
+
+/* lua.print */
+
+static int texlib_print(lua_State *L)
+{
+ return texlib_aux_cprint(L, FULL_LINE, default_catcode_table_preset, 1);
+}
+
+/* lua.sprint */
+
+static int texlib_sprint(lua_State *L)
+{
+ return texlib_aux_cprint(L, PARTIAL_LINE, default_catcode_table_preset, 1);
+}
+
+static int texlib_mprint(lua_State *L)
+{
+ int ini = 1;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering local control via (run) macro");
+ }
+ texlib_aux_store_token(token_val(end_local_cmd, 0), PARTIAL_LINE, default_catcode_table_preset);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ int cs = tex_string_locate(name, lname, 0);
+ int cmd = eq_type(cs);
+ if (is_call_cmd(cmd)) {
+ texlib_aux_store_token(cs_token_flag + cs, PARTIAL_LINE, default_catcode_table_preset);
+ ++ini;
+ } else {
+ tex_local_control_message("invalid (mprint) macro");
+ }
+ }
+ if (lua_gettop(L) >= ini) {
+ texlib_aux_cprint(L, PARTIAL_LINE, default_catcode_table_preset, ini);
+ }
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering local control via mprint");
+ }
+ tex_local_control(1);
+ return 0;
+}
+
+/* we default to obeymode */
+
+static int texlib_pushlocal(lua_State *L)
+{
+ (void) L;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("pushing local control");
+ }
+ texlib_aux_store_token(token_val(end_local_cmd, 0), PARTIAL_LINE, default_catcode_table_preset);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ return 0;
+}
+
+static int texlib_poplocal(lua_State *L)
+{
+ (void) L;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering local control via pop");
+ }
+ tex_local_control(1);
+ return 0;
+}
+
+/* lua.cprint */
+
+static int texlib_cprint(lua_State *L)
+{
+ /*tex
+ We map a catcode to a pseudo cattable. So a negative value is a specific catcode with offset 1.
+ */
+ int cattable = lmt_tointeger(L, 1);
+ if (cattable < 0 || cattable > 15) {
+ cattable = - 12 - 0xFF ;
+ } else {
+ cattable = - cattable - 0xFF;
+ }
+ if (lua_type(L, 2) == LUA_TTABLE) {
+ for (int i = 1; ; i++) {
+ lua_rawgeti(L, 2, i);
+ if (texlib_aux_store(L, -1, PARTIAL_LINE, cattable)) {
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ } else {
+ int n = lua_gettop(L);
+ for (int i = 2; i <= n; i++) {
+ texlib_aux_store(L, i, PARTIAL_LINE, cattable);
+ }
+ }
+ return 0;
+}
+
+/* lua.tprint */
+
+static int texlib_tprint(lua_State *L)
+{
+ int n = lua_gettop(L);
+ for (int i = 1; i <= n; i++) {
+ int cattable = default_catcode_table_preset;
+ int startstrings = 1;
+ if (lua_type(L, i) != LUA_TTABLE) {
+ luaL_error(L, "no string to print");
+ }
+ lua_pushvalue(L, i); /* the table */
+ lua_pushinteger(L, 1);
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ cattable = lmt_tointeger(L, -1);
+ startstrings = 2;
+ if (cattable != default_catcode_table_preset && cattable != no_catcode_table_preset && ! tex_valid_catcode_table(cattable)) {
+ cattable = default_catcode_table_preset;
+ }
+ }
+ lua_pop(L, 1);
+ for (int j = startstrings; ; j++) {
+ lua_pushinteger(L, j);
+ lua_gettable(L, -2);
+ if (texlib_aux_store(L, -1, PARTIAL_LINE, cattable)) {
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ lua_pop(L, 1); /* the table */
+ }
+ return 0;
+}
+
+static int texlib_isprintable(lua_State* L)
+{
+ halfword okay = 0;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING :
+ okay = 1;
+ break;
+ case LUA_TUSERDATA :
+ {
+ if (lua_getmetatable(L, 1)) {
+ lua_get_metatablelua(token_instance);
+ if (lua_rawequal(L, -1, -2)) {
+ okay = 1;
+ // lua_pop(L, 2);
+ } else {
+ lua_get_metatablelua(node_instance);
+ if (lua_rawequal(L, -1, -3)) {
+ okay = 1;
+ }
+ // lua_pop(L, 3);
+ }
+ }
+ break;
+ }
+ }
+ lua_pushboolean(L, okay);
+ return 1;
+}
+
+/*tex We actually don't need to copy and could read from the string. */
+
+int lmt_cstring_input(halfword *n, int *cattable, int *partial, int *finalline)
+{
+ spindle_rope *t = read_spindle.head;
+ int ret = eof_tex_input ;
+ if (! read_spindle.complete) {
+ read_spindle.complete = 1;
+ read_spindle.tail = NULL;
+ }
+ if (t) {
+ switch (t->kind) {
+ case string_lua_input:
+ {
+ if (t->data.t) {
+ /*tex put that thing in the buffer */
+ int strsize = (int) t->tsize;
+ int newlast = lmt_fileio_state.io_first + strsize;
+ lmt_fileio_state.io_last = lmt_fileio_state.io_first;
+ if (tex_room_in_buffer(newlast)) {
+ memcpy(&lmt_fileio_state.io_buffer[lmt_fileio_state.io_last], &t->data.t[0], sizeof(unsigned char) * strsize);
+ lmt_fileio_state.io_last = newlast;
+ lmt_memory_free(t->data.t);
+ t->data.t = NULL;
+ } else {
+ return ret;
+ }
+ }
+ *cattable = t->cattable;
+ *partial = t->partial;
+ *finalline = (t->next == NULL);
+ ret = string_tex_input;
+ break;
+ }
+ case packed_lua_input:
+ {
+ unsigned strsize = t->tsize;
+ int newlast = lmt_fileio_state.io_first + strsize;
+ lmt_fileio_state.io_last = lmt_fileio_state.io_first;
+ if (tex_room_in_buffer(newlast)) {
+ for (unsigned i = 0; i < strsize; i++) {
+ /* when we end up here we often don't have that many bytes */
+ lmt_fileio_state.io_buffer[lmt_fileio_state.io_last + i] = t->data.c[i];
+ }
+ lmt_fileio_state.io_last = newlast;
+ *cattable = t->cattable;
+ *partial = t->partial;
+ *finalline = (t->next == NULL);
+ ret = string_tex_input;
+ } else {
+ return ret;
+ }
+ break;
+ }
+ case token_lua_input:
+ {
+ *n = t->data.h;
+ ret = token_tex_input;
+ break;
+ }
+ case token_list_lua_input:
+ {
+ *n = t->data.h;
+ ret = token_list_tex_input;
+ break;
+ }
+ case node_lua_input:
+ {
+ *n = t->data.h;
+ ret = node_tex_input;
+ break;
+ }
+ }
+ texlib_aux_dispose_rope(read_spindle.tail);
+ read_spindle.tail = t;
+ read_spindle.head = t->next;
+ } else {
+ texlib_aux_dispose_rope(read_spindle.tail);
+ read_spindle.tail = NULL;
+ }
+ return ret;
+}
+
+/*tex Open for reading, and make a new one for writing. */
+
+void lmt_cstring_start(void)
+{
+ lmt_spindle_state.spindle_index++;
+ if (lmt_spindle_state.spindle_size == lmt_spindle_state.spindle_index) {
+ int size = (lmt_spindle_state.spindle_size + 1) * sizeof(spindle);
+ spindle *spindles = lmt_memory_realloc(lmt_spindle_state.spindles, (size_t) size);
+ if (spindles) {
+ lmt_spindle_state.spindles = spindles;
+ texlib_aux_reset_spindle(lmt_spindle_state.spindle_index);
+ lmt_spindle_state.spindle_size++;
+ } else {
+ tex_overflow_error("spindle", size);
+ }
+ }
+}
+
+/*tex Close for reading. */
+
+void lmt_cstring_close(void)
+{
+ spindle_rope *t;
+ spindle_rope *next = read_spindle.head;
+ while (next) {
+ if (next->kind == string_tex_input && next->data.t) {
+ lmt_memory_free(next->data.t);
+ next->data.t = NULL;
+ }
+ t = next;
+ next = next->next;
+ if (t == read_spindle.tail) {
+ read_spindle.tail = NULL;
+ }
+ texlib_aux_dispose_rope(t);
+ }
+ read_spindle.head = NULL;
+ texlib_aux_dispose_rope(read_spindle.tail);
+ read_spindle.tail = NULL;
+ read_spindle.complete = 0;
+ lmt_spindle_state.spindle_index--;
+}
+
+/*tex
+ The original was close to the \TEX\ original (even using |cur_val|) but there is no need to have
+ that all-in-one loop with radix magic.
+*/
+
+static const char *texlib_aux_scan_integer_part(lua_State *L, const char *ss, int *ret, int *radix_ret)
+{
+ int negative = 0; /*tex should the answer be negated? */
+ int vacuous = 1; /*tex have no digits appeared? */
+ int overflow = 0;
+ int c = 0; /*tex the current character */
+ const char *s = ss; /*tex where we stopped in the string |ss| */
+ long long result = 0; /*tex return value */
+ while (1) {
+ c = *s++;
+ switch (c) {
+ case ' ':
+ case '+':
+ break;
+ case '-':
+ negative = ! negative;
+ break;
+ case '\'':
+ {
+ int d;
+ *radix_ret = 8;
+ c = *s++;
+ while (c) {
+ if ((c >= '0') && (c <= '0' + 7)) {
+ d = c - '0';
+ } else {
+ break;
+ }
+ if (! overflow) {
+ vacuous = 0;
+ result = result * 8 + d;
+ if (result > max_integer) {
+ overflow = 1;
+ }
+ }
+ c = *s++;
+ }
+ goto DONE;
+ }
+ case '"':
+ {
+ int d;
+ *radix_ret = 16;
+ c = *s++;
+ while (c) {
+ if ((c >= '0') && (c <= '0' + 9)) {
+ d = c - '0';
+ } else if ((c <= 'A' + 5) && (c >= 'A')) {
+ d = c - 'A' + 10;
+ } else if ((c <= 'a' + 5) && (c >= 'a')) {
+ /*tex Actually \TEX\ only handles uppercase. */
+ d = c - 'a' + 10;
+ } else {
+ goto DONE;
+ }
+ if (! overflow) {
+ vacuous = 0;
+ result = result * 16 + d;
+ if (result > max_integer) {
+ overflow = 1;
+ }
+ }
+ c = *s++;
+ }
+ goto DONE;
+ }
+ default:
+ {
+ int d;
+ *radix_ret = 10;
+ while (c) {
+ if ((c >= '0') && (c <= '0' + 9)) {
+ d = c - '0';
+ } else {
+ goto DONE;
+ }
+ if (! overflow) {
+ vacuous = 0;
+ result = result * 10 + d;
+ if (result > max_integer) {
+ overflow = 1;
+ }
+ }
+ c = *s++;
+ }
+ goto DONE;
+ }
+ }
+ }
+ DONE:
+ if (overflow) {
+ luaL_error(L, "number too big");
+ result = infinity;
+ } else if (vacuous) {
+ luaL_error(L, "missing number, treated as zero") ;
+ }
+ if (negative) {
+ result = -result;
+ }
+ *ret = (int) result;
+ if (c != ' ' && s > ss) {
+ s--;
+ }
+ return s;
+}
+
+/*tex
+ This sets |cur_val| to a dimension. We can clean this up a bit like the normal dimen scanner,
+ but it's seldom called. Scanning is like in \TEX, with gobbling spaces and such. When no unit
+ is given we assume points. When nothing is given we assume zero. Trailing crap is just ignored.
+*/
+
+static const char *texlib_aux_scan_dimen_part(lua_State * L, const char *ss, int *ret)
+{
+ int negative = 0; /*tex should the answer be negated? */
+ int fraction = 0; /*tex numerator of a fraction whose denominator is $2^{16}$ */
+ int numerator;
+ int denominator;
+ scaled special; /*tex an internal dimension */
+ int result = 0;
+ int radix = 0; /*tex the current radix */
+ int remainder = 0; /*tex the to be remainder */
+ int saved_error = lmt_scanner_state.arithmic_error; /*tex to save |arith_error| */
+ const char *s = NULL;
+ if (ss && (*ss == '.' || *ss == ',')) {
+ s = ss;
+ goto FRACTION;
+ } else {
+ s = texlib_aux_scan_integer_part(L, ss, &result, &radix);
+ }
+ if (! (char) *s) {
+ /* error, no unit, assume scaled points */
+ goto ATTACH_FRACTION;
+ }
+ if (result < 0) {
+ negative = ! negative;
+ result = -result;
+ }
+ FRACTION:
+ if ((radix == 0 || radix == 10) && (*s == '.' || *s == ',')) {
+ unsigned k = 0;
+ unsigned char digits[18];
+ s++;
+ while (1) {
+ int c = *s++;
+ if ((c > '0' + 9) || (c < '0')) {
+ break;
+ } else if (k < 17) {
+ digits[k++] = (unsigned char) c - '0';
+ }
+ }
+ fraction = tex_round_decimals_digits(digits, k);
+ if (*s != ' ') {
+ --s;
+ }
+ }
+ /* the unit can have spaces in front */
+/*UNIT: */
+ while ((char) *s == ' ') {
+ s++;
+ }
+ /* We dropped the |nd| and |nc| units as well as the |true| prefix. */
+ if (! (char) *s) {
+ goto ATTACH_FRACTION;
+ } else if (strncmp(s, "pt", 2) == 0) {
+ s += 2;
+ goto ATTACH_FRACTION;
+ } else if (strncmp(s, "mm", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 2540;
+ goto CONVERSION;
+ } else if (strncmp(s, "cm", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 254;
+ goto CONVERSION;
+ } else if (strncmp(s, "sp", 2) == 0) {
+ s += 2;
+ goto DONE;
+ } else if (strncmp(s, "bp", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 7200;
+ goto CONVERSION;
+ } else if (strncmp(s, "in", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 100;
+ goto CONVERSION;
+ } else if (strncmp(s, "dd", 2) == 0) {
+ s += 2;
+ numerator = 1238;
+ denominator = 1157;
+ goto CONVERSION;
+ } else if (strncmp(s, "cc", 2) == 0) {
+ s += 2;
+ numerator = 14856;
+ denominator = 1157;
+ goto CONVERSION;
+ } else if (strncmp(s, "pc", 2) == 0) {
+ s += 2;
+ numerator = 12;
+ denominator = 1;
+ goto CONVERSION;
+ } else if (strncmp(s, "dk", 2) == 0) {
+ s += 2;
+ numerator = 49838;
+ denominator = 7739;
+ goto CONVERSION;
+ } else if (strncmp(s, "em", 2) == 0) {
+ s += 2;
+ special = tex_get_font_em_width(cur_font_par);
+ goto SPECIAL;
+ } else if (strncmp(s, "ex", 2) == 0) {
+ s += 2;
+ special = tex_get_font_ex_height(cur_font_par);
+ goto SPECIAL;
+ } else if (strncmp(s, "px", 2) == 0) {
+ s += 2;
+ special = px_dimen_par;
+ goto SPECIAL;
+ } else if (strncmp(s, "mu", 2) == 0) {
+ s += 2;
+ goto ATTACH_FRACTION;
+ /* } else if (strncmp(s, "true", 4) == 0) { */
+ /* s += 4; */
+ /* goto UNIT; */
+ } else {
+ /* luaL_error(L, "illegal unit of measure (pt inserted)"); */
+ goto ATTACH_FRACTION;
+ }
+ SPECIAL:
+ result = tex_nx_plus_y(result, special, tex_xn_over_d_r(special, fraction, 0200000, &remainder));
+ goto DONE;
+ CONVERSION:
+ result = tex_xn_over_d_r(result, numerator, denominator, &remainder);
+ fraction = (numerator * fraction + 0200000 * remainder) / denominator;
+ result = result + (fraction / 0200000);
+ fraction = fraction % 0200000;
+ ATTACH_FRACTION:
+ if (result >= 040000) {
+ lmt_scanner_state.arithmic_error = 1;
+ } else {
+ result = result * 65536 + fraction;
+ }
+ DONE:
+ if (lmt_scanner_state.arithmic_error || (abs(result) >= 010000000000)) {
+ result = max_dimen;
+ luaL_error(L, "dimension too large");
+ }
+ *ret = negative ? - result : result;
+ lmt_scanner_state.arithmic_error = saved_error;
+ /* only when we want to report junk */
+ while ((char) *s == ' ') {
+ s++;
+ }
+ return s;
+}
+
+static int texlib_aux_dimen_to_number(lua_State *L, const char *s)
+{
+ int result = 0;
+ const char *d = texlib_aux_scan_dimen_part(L, s, &result);
+ if (*d) {
+ return luaL_error(L, "conversion failed (trailing junk?)");
+ } else {
+ return result;
+ }
+}
+
+static int texlib_aux_integer_to_number(lua_State *L, const char *s)
+{
+ int result = 0;
+ int radix = 10;
+ const char *d = texlib_aux_scan_integer_part(L, s, &result, &radix);
+ if (*d) {
+ return luaL_error(L, "conversion failed (trailing junk?)");
+ } else {
+ return result;
+ }
+}
+
+static int texlib_toscaled(lua_State *L)
+{
+ int sp;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ sp = lmt_toroundnumber(L, 1);
+ break;
+ case LUA_TSTRING:
+ sp = texlib_aux_dimen_to_number(L, lua_tostring(L, 1));
+ break;
+ default:
+ return luaL_error(L, "string or a number expected");
+ }
+ lua_pushinteger(L, sp);
+ return 1;
+}
+
+static int texlib_tonumber(lua_State *L)
+{
+ int i;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ i = lmt_toroundnumber(L, 1);
+ break;
+ case LUA_TSTRING:
+ i = texlib_aux_integer_to_number(L, lua_tostring(L, 1));
+ break;
+ default:
+ return luaL_error(L, "string or a number expected");
+ }
+ lua_pushinteger(L, i);
+ return 1;
+}
+
+static int texlib_error(lua_State *L)
+{
+ const char *error = luaL_checkstring(L, 1);
+ const char *help = lua_type(L, 2) == LUA_TSTRING ? luaL_checkstring(L, 2) : NULL;
+ tex_handle_error(normal_error_type, error, help);
+ return 0;
+}
+
+/*tex
+
+ The lua interface needs some extra functions. The functions themselves are quite boring, but they
+ are handy because otherwise this internal stuff has to be accessed from \CCODE\ directly, where
+ lots of the defines are not available.
+
+*/
+
+inline static int texlib_aux_valid_register_index(lua_State *L, int slot, int cmd, int base, int max)
+{
+ int index = -1;
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *str = lua_tolstring(L, 1, &len);
+ int cs = tex_string_locate(str, len, 0);
+ if (eq_type(cs) == cmd) {
+ index = eq_value(cs) - base;
+ }
+ }
+ break;
+ case LUA_TNUMBER:
+ index = lmt_tointeger(L, slot);
+ break;
+ default:
+ luaL_error(L, "string or a number expected");
+ break;
+ }
+ if (index >= 0 && index <= max) {
+ return index;
+ } else {
+ return -1;
+ }
+}
+
+static int texlib_get_register_index(lua_State *L)
+{
+ size_t len;
+ const char *str = lua_tolstring(L, 1, &len);
+ int cs = tex_string_locate(str, len, 0);
+ int index = -1;
+ switch (eq_type(cs)) {
+ case register_toks_cmd : index = eq_value(cs) - register_toks_base; break;
+ case register_int_cmd : index = eq_value(cs) - register_int_base; break;
+ case register_attribute_cmd : index = eq_value(cs) - register_attribute_base; break;
+ case register_dimen_cmd : index = eq_value(cs) - register_dimen_base; break;
+ case register_glue_cmd : index = eq_value(cs) - register_glue_base; break;
+ case register_mu_glue_cmd : index = eq_value(cs) - register_mu_glue_base; break;
+ }
+ if (index >= 0) {
+ lua_pushinteger(L, index);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+inline static int texlib_aux_checked_register(lua_State *L, int cmd, int base, int max)
+{
+ int index = texlib_aux_valid_register_index(L, 1, cmd, base, max);
+ if (index >= 0) {
+ lua_pushinteger(L, index);
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+typedef void (*setfunc) (int, halfword, int, int);
+typedef halfword (*getfunc) (int, int);
+
+int lmt_check_for_flags(lua_State *L, int slot, int *flags, int prefixes, int numeric)
+{
+ if (global_defs_par) {
+ *flags = add_global_flag(*flags);
+ }
+ if (prefixes) {
+ while (1) {
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ const char *str = lua_tostring(L, slot);
+ if (! str || lua_key_eq(str, macro)) {
+ /*tex For practical reasons we skip empty strings. */
+ slot += 1;
+ } else if (lua_key_eq(str, global)) {
+ slot += 1;
+ *flags = add_global_flag(*flags);
+ } else if (lua_key_eq(str, frozen)) {
+ slot += 1;
+ *flags = add_frozen_flag(*flags);
+ } else if (lua_key_eq(str, permanent)) {
+ slot += 1;
+ *flags = add_permanent_flag(*flags);
+ } else if (lua_key_eq(str, protected)) {
+ slot += 1;
+ *flags = add_protected_flag(*flags);
+ } else if (lua_key_eq(str, untraced)) {
+ slot += 1;
+ *flags = add_untraced_flag(*flags);
+ } else if (lua_key_eq(str, immutable)) {
+ slot += 1;
+ *flags = add_immutable_flag(*flags);
+ } else if (lua_key_eq(str, overloaded)) {
+ slot += 1;
+ *flags = add_overloaded_flag(*flags);
+ } else if (lua_key_eq(str, value)) {
+ slot += 1;
+ *flags = add_value_flag(*flags);
+ } else if (lua_key_eq(str, conditional) || lua_key_eq(str, condition)) {
+ /* condition will go, conditional stays */
+ slot += 1;
+ *flags = add_conditional_flag(*flags);
+ } else {
+ /*tex When we have this at the start we now can have a csname. */
+ return slot;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ if (numeric) {
+ *flags |= lua_tointeger(L, slot);
+ slot += 1;
+ break;
+ } else {
+ return slot;
+ }
+ case LUA_TNIL:
+ /*tex This is quite convenient if we use some composer. */
+ slot += 1;
+ break;
+ default:
+ return slot;
+ }
+ }
+ }
+ return slot;
+}
+
+int lmt_check_for_level(lua_State *L, int slot, quarterword *level, quarterword defaultlevel)
+{
+ if (lua_type(L, slot) == LUA_TSTRING) {
+ const char *str = lua_tostring(L, slot);
+ *level = lua_key_eq(str, global) ? level_one : defaultlevel;
+ ++slot;
+ } else {
+ *level = defaultlevel;
+ }
+ return slot;
+}
+
+/* -1=noindex, 0=register 1=internal */
+
+static int texlib_aux_check_for_index(
+ lua_State *L,
+ int slot,
+ const char *what,
+ int *index,
+ int internal_cmd,
+ int register_cmd,
+ int internal_base,
+ int register_base,
+ int max_index
+) {
+ *index = -1;
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *str = lua_tolstring(L, slot, &len);
+ int cs = tex_string_locate(str, len, 0);
+ if (eq_type(cs) == internal_cmd) {
+ *index = eq_value(cs) - internal_base;
+ return 1;
+ } else if (eq_type(cs) == register_cmd) {
+ *index = eq_value(cs) - register_base;
+ return 0;
+ } else {
+ luaL_error(L, "incorrect %s name", what);
+ return -1;
+ }
+ }
+ case LUA_TNUMBER:
+ *index = lmt_tointeger(L, slot);
+ if (*index >= 0 && *index <= max_index) {
+ return 0;
+ } else {
+ return -1;
+ }
+ default:
+ luaL_error(L, "%s name or valid index expected", what);
+ return -1;
+ }
+}
+
+static int texlib_get(lua_State *L);
+
+/*tex
+
+ We intercept the first string and when it is |global| then we check the second one which can
+ also be a string. It is unlikely that one will use |\global| as register name so we don't need
+ to check for the number of further arguments. This permits to treat lack of them as a reset.
+
+*/
+
+static int texlib_isdimen(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_dimen_cmd, register_dimen_base, max_dimen_register_index);
+}
+
+/* [global] name|index integer|dimension|false|nil */
+
+static int texlib_setdimen(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "dimen", &index, internal_dimen_cmd, register_dimen_cmd, internal_dimen_base, register_dimen_base, max_dimen_register_index);
+ if (state >= 0) {
+ halfword value = 0;
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ value = lmt_toroundnumber(L, slot++);
+ break;
+ case LUA_TSTRING:
+ value = texlib_aux_dimen_to_number(L, lua_tostring(L, slot++));
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, slot++)) {
+ /*tex The value |true| makes no sense. */
+ return 0;
+ }
+ break;
+ case LUA_TNONE:
+ case LUA_TNIL:
+ break;
+ default:
+ luaL_error(L, "unsupported dimen value type");
+ break;
+ }
+ tex_set_tex_dimen_register(index, value, flags, state);
+ if (state == 1 && lua_toboolean(L, slot)) {
+ tex_update_par_par(internal_dimen_cmd, index);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getdimen(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "dimen", &index, internal_dimen_cmd, register_dimen_cmd, internal_dimen_base, register_dimen_base, max_dimen_register_index);
+ lua_pushinteger(L, state >= 0 ? tex_get_tex_dimen_register(index, state) : 0);
+ return 1;
+}
+
+// static halfword texlib_aux_make_glue(lua_State *L, int top, int slot)
+// {
+// halfword value = copy_node(zero_glue);
+// if (++slot <= top) {
+// glue_amount(value) = lmt_toroundnumber(L, slot);
+// if (++slot <= top) {
+// glue_stretch(value) = lmt_toroundnumber(L, slot);
+// if (++slot <= top) {
+// glue_shrink(value) = lmt_toroundnumber(L, slot);
+// if (++slot <= top) {
+// glue_stretch_order(value) = lmt_tohalfword(L, slot);
+// if (++slot <= top) {
+// glue_shrink_order(value) = lmt_tohalfword(L, slot);
+// }
+// }
+// }
+// }
+// }
+// return value;
+// }
+
+static halfword texlib_aux_make_glue(lua_State *L, int top, int slot)
+{
+ halfword value = tex_copy_node(zero_glue);
+ if (slot <= top) {
+ glue_amount(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_shrink(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch_order(value) = lmt_tohalfword(L, slot++);
+ if (slot <= top) {
+ glue_shrink_order(value) = lmt_tohalfword(L, slot++);
+ }
+ }
+ }
+ }
+ }
+ return value;
+}
+
+inline static int texlib_aux_push_glue(lua_State* L, halfword g)
+{
+ if (g) {
+ lua_pushinteger(L, glue_amount(g));
+ lua_pushinteger(L, glue_stretch(g));
+ lua_pushinteger(L, glue_shrink(g));
+ lua_pushinteger(L, glue_stretch_order(g));
+ lua_pushinteger(L, glue_shrink_order(g));
+ } else {
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ }
+ return 5;
+}
+
+static halfword texlib_aux_get_glue_spec(lua_State *L, int slot)
+{
+ halfword value = null;
+ switch (lua_type(L, slot + 1)) {
+ case LUA_TBOOLEAN:
+ // if (lua_toboolean(L, slot + 1)) {
+ // /*tex The value |true| makes no sense. */
+ // }
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ value = lmt_check_isnode(L, slot + 1);
+ if (node_type(value) != glue_spec_node) {
+ value = null;
+ luaL_error(L, "glue_spec expected");
+ }
+ }
+ return value;
+}
+
+static int texlib_isskip(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_glue_cmd, register_glue_base, max_glue_register_index);
+}
+
+/* [global] name|index gluespec|false|nil */
+
+static int texlib_setskip(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ if (state >= 0) {
+ halfword value = texlib_aux_get_glue_spec(L, slot++);
+ tex_set_tex_skip_register(index, value, flags, state);
+ if (state == 1 && lua_toboolean(L, slot)) {
+ tex_update_par_par(internal_glue_cmd, index);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getskip(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_skip_register(index, state) : null;
+ lmt_push_node_fast(L, tex_copy_node(value ? value : zero_glue));
+ return 1;
+}
+
+static int texlib_isglue(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_glue_cmd, register_glue_base, max_glue_register_index);
+}
+
+/* [global] slot [width] [stretch] [shrink] [stretch_order] [shrink_order] */
+
+static int texlib_setglue(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ if (state >= 0) {
+ tex_set_tex_skip_register(index, texlib_aux_make_glue(L, lua_gettop(L), slot), flags, state);
+ }
+ return 0;
+}
+
+static int texlib_getglue(lua_State *L)
+{
+ int index;
+ int all = (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : 1;
+ int state = texlib_aux_check_for_index(L, 1, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_skip_register(index, state) : null;
+ if (! value) {
+ lua_pushinteger(L, 0);
+ if (all) {
+ /* save the trouble of testing p/m */
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ return 3;
+ } else {
+ return 1;
+ }
+ } else if (all) {
+ return texlib_aux_push_glue(L, value);
+ } else {
+ /* false */
+ lua_pushinteger(L, value ? glue_amount(value) : 0);
+ return 1;
+ }
+}
+
+static int texlib_ismuskip(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_mu_glue_cmd, register_mu_glue_base, max_mu_glue_register_index);
+}
+
+static int texlib_setmuskip(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ tex_set_tex_mu_skip_register(index, texlib_aux_get_glue_spec(L, slot), flags, state);
+ return 0;
+}
+
+static int texlib_getmuskip(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_mu_skip_register(index, state) : null;
+ lmt_push_node_fast(L, tex_copy_node(value ? value : zero_glue));
+ return 1;
+}
+
+static int texlib_ismuglue(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_mu_glue_cmd, register_mu_glue_base, max_mu_glue_register_index);
+}
+
+static int texlib_setmuglue(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ halfword value = texlib_aux_make_glue(L, lua_gettop(L), slot);
+ if (state >= 0) {
+ tex_set_tex_mu_skip_register(index, value, flags, state);
+ }
+ return 0;
+}
+
+static int texlib_getmuglue(lua_State *L)
+{
+ int index;
+ int all = (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : 1;
+ int state = texlib_aux_check_for_index(L, 1, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_mu_skip_register(index, state) : null;
+ if (! value) {
+ lua_pushinteger(L, 0);
+ return 1;
+ } else if (all) {
+ return texlib_aux_push_glue(L, value);
+ } else {
+ /* false */
+ lua_pushinteger(L, value ? glue_amount(value) : 0);
+ return 1;
+ }
+}
+
+static int texlib_iscount(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_int_cmd, register_int_base, max_int_register_index);
+}
+
+static int texlib_setcount(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "count", &index, internal_int_cmd, register_int_cmd, internal_int_base, register_int_base, max_int_register_index);
+ if (state >= 0) {
+ halfword value = lmt_optinteger(L, slot++, 0);
+ tex_set_tex_count_register(index, value, flags, state);
+ if (state == 1 && lua_toboolean(L, slot)) {
+ tex_update_par_par(internal_int_cmd, index);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getcount(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "count", &index, internal_int_cmd, register_int_cmd, internal_int_base, register_int_base, max_int_register_index);
+ lua_pushinteger(L, state >= 0 ? tex_get_tex_count_register(index, state) : 0);
+ return 1;
+}
+
+static int texlib_isattribute(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_attribute_cmd, register_attribute_base, max_attribute_register_index);
+}
+
+/*tex there are no system set attributes so this is a bit overkill */
+
+static int texlib_setattribute(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "attribute", &index, internal_attribute_cmd, register_attribute_cmd, internal_attribute_base, register_attribute_base, max_attribute_register_index);
+ if (state >= 0) {
+ halfword value = lmt_optinteger(L, slot++, unused_attribute_value);
+ tex_set_tex_attribute_register(index, value, flags, state);
+ }
+ return 0;
+}
+
+static int texlib_getattribute(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "attribute", &index, internal_attribute_cmd, register_attribute_cmd, internal_attribute_base, register_attribute_base, max_attribute_register_index);
+ lua_pushinteger(L, state >= 0 ? tex_get_tex_attribute_register(index, state) : 0);
+ return 1;
+}
+
+/*tex todo: we can avoid memcpy as there is no need to go through the pool */
+
+/* use string_to_toks */
+
+static int texlib_istoks(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_toks_cmd, register_toks_base, max_toks_register_index);
+}
+
+/* [global] name|integer string|nil */
+
+static int texlib_settoks(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base,max_toks_register_index);
+ if (state >= 0) {
+ lstring value = { .c = NULL, .l = 0 };
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ value.c = lua_tolstring(L, slot, &value.l);
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ return luaL_error(L, "string or nil expected");
+ }
+ tex_set_tex_toks_register(index, value, flags, state);
+ }
+ return 0;
+}
+
+/* [global] name|index catcode string */
+
+static int texlib_scantoks(lua_State *L) // TODO
+{
+ int index = 0;
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base,max_toks_register_index);
+ if (state >= 0) {
+ lstring value = { .c = NULL, .l = 0 };
+ int cattable = lmt_checkinteger(L, slot++);
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ value.c = lua_tolstring(L, slot, &value.l);
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ return luaL_error(L, "string or nil expected");
+ }
+ tex_scan_tex_toks_register(index, cattable, value, flags, state);
+ }
+ return 0;
+}
+
+static int texlib_gettoks(lua_State *L)
+{
+ int index;
+ int slot = 1;
+ int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base, max_toks_register_index);
+ if (state >= 0) {
+ if (lua_toboolean(L, slot)) {
+ lmt_token_register_to_lua(L, state ? toks_parameter(index) : toks_register(index));
+ } else {
+ strnumber value = tex_get_tex_toks_register(index, state);
+ char *s = tex_makecstring(value);
+ lua_pushstring(L, s);
+ lmt_memory_free(s);
+ tex_flush_str(value);
+ }
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+ return 1;
+}
+
+static int texlib_getmark(lua_State *L)
+{
+ if (lua_gettop(L) == 0) {
+ lua_pushinteger(L, lmt_mark_state.mark_data.ptr);
+ return 1;
+ } else if (lua_type(L, 1) == LUA_TSTRING) {
+ int mrk = -1;
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, top)) {
+ mrk = top_marks_code;
+ } else if (lua_key_eq(s, first)) {
+ mrk = first_marks_code;
+ } else if (lua_key_eq(s, bottom)) {
+ mrk = bot_marks_code;
+ } else if (lua_key_eq(s, splitfirst)) {
+ mrk = split_first_marks_code;
+ } else if (lua_key_eq(s, splitbottom)) {
+ mrk = split_bot_marks_code;
+ } else if (lua_key_eq(s, current)) {
+ mrk = current_marks_code;
+ }
+ if (mrk >= 0) {
+ int num = lmt_optinteger(L, 2, 0);
+ if (num >= 0 && num <= lmt_mark_state.mark_data.ptr) {
+ halfword ptr = tex_get_some_mark(mrk, num);
+ if (ptr) {
+ char *str = tex_tokenlist_to_tstring(ptr, 1, NULL, 0, 0, 0);
+ if (str) {
+ lua_pushstring(L, str);
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 1;
+ }
+ } else {
+ luaL_error(L, "valid mark class expected");
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+int lmt_get_box_id(lua_State *L, int i, int report)
+{
+ int index = -1;
+ switch (lua_type(L, i)) {
+ case LUA_TSTRING:
+ {
+ size_t k = 0;
+ const char *s = lua_tolstring(L, i, &k);
+ int cs = tex_string_locate(s, k, 0);
+ int cmd = eq_type(cs);
+ switch (cmd) {
+ case char_given_cmd:
+ // case math_char_given_cmd:
+ case integer_cmd:
+ index = eq_value(cs);
+ break;
+ case register_int_cmd:
+ index = register_int_number(eq_value(cs));
+ break;
+ default:
+ /* we don't accept other commands as it makes no sense */
+ break;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ index = lmt_tointeger(L, i);
+ default:
+ break;
+ }
+ if (index >= 0 && index <= max_box_register_index) {
+ return index;
+ } else {
+ if (report) {
+ luaL_error(L, "string or a number within range expected");
+ }
+ return -1;
+ }
+}
+
+static int texlib_getbox(lua_State *L)
+{
+ halfword index = lmt_get_box_id(L, 1, 1);
+ lmt_node_list_to_lua(L, index >= 0 ? tex_get_tex_box_register(index, 0) : null);
+ return 1;
+}
+
+static int texlib_splitbox(lua_State *L)
+{
+ int index = lmt_get_box_id(L, 1, 1);
+ if (index >= 0) {
+ if (lua_isnumber(L, 2)) {
+ int m = packing_additional;
+ switch (lua_type(L, 3)) {
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 3);
+ if (lua_key_eq(s, exactly)) {
+ m = packing_exactly;
+ } else if (lua_key_eq(s, additional)) {
+ m = packing_additional;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ m = lmt_tointeger(L, 3);
+ if (m != packing_exactly && m != packing_additional) {
+ m = packing_exactly;
+ luaL_error(L, "wrong mode in splitbox");
+ }
+ break;
+ }
+ }
+ lmt_node_list_to_lua(L, tex_vsplit(index, lmt_toroundnumber(L, 2), m));
+ } else {
+ /* maybe a warning */
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* todo */
+
+static int texlib_isbox(lua_State *L)
+{
+ lua_pushboolean(L, lmt_get_box_id(L, 1, 0) >= 0);
+ return 1;
+}
+
+static int texlib_setbox(lua_State *L)
+{
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int index = lmt_get_box_id(L, slot++, 1);
+ if (index >= 0) {
+ int n = null;
+ switch (lua_type(L, slot)) {
+ case LUA_TBOOLEAN:
+ n = lua_toboolean(L, slot);
+ if (n) {
+ return 0;
+ } else {
+ n = null;
+ }
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ n = lmt_node_list_from_lua(L, slot);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ break;
+ default:
+ return luaL_error(L, "invalid node type %s passed", get_node_name(node_type(n)));
+ }
+ }
+ break;
+ }
+ tex_set_tex_box_register(index, n, flags, 0);
+ }
+ return 0;
+}
+
+/* [global] index first second */
+
+static int texlib_setlccode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch1 = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch1)) {
+ halfword ch2 = lmt_checkhalfword(L, slot++);
+ if (character_in_range(ch2)) {
+ tex_set_lc_code(ch1, ch2, level);
+ if (slot <= top) {
+ halfword ch3 = lmt_checkhalfword(L, slot);
+ if (character_in_range(ch3)) {
+ tex_set_uc_code(ch1, ch3, level);
+ } else {
+ texlib_aux_show_character_error(L, ch3);
+ }
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch2);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch1);
+ }
+ }
+ return 0;
+}
+
+static int texlib_setuccode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch1 = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch1)) {
+ halfword ch2 = lmt_checkhalfword(L, slot++);
+ if (character_in_range(ch2)) {
+ tex_set_uc_code(ch1, ch2, level);
+ if (slot <= top) {
+ halfword ch3 = lmt_checkhalfword(L, slot);
+ if (character_in_range(ch3)) {
+ tex_set_lc_code(ch1, ch3, level);
+ } else {
+ texlib_aux_show_character_error(L, ch3);
+ }
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch2);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch1);
+ }
+ }
+ return 0;
+}
+
+static int texlib_setsfcode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ if (half_in_range(val)) {
+ tex_set_sf_code(ch, val, level);
+ } else {
+ texlib_aux_show_half_error(L, val);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+static int texlib_sethccode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ if (half_in_range(val)) {
+ tex_set_hc_code(ch, val, level);
+ } else {
+ texlib_aux_show_half_error(L, val);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+static int texlib_sethmcode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ tex_set_hm_code(ch, val, level);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getlccode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_lc_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_getuccode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_uc_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_getsfcode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_sf_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_gethccode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_hc_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_gethmcode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_hm_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+/* [global] [cattable] code value */
+
+static int texlib_setcatcode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int cattable = ((top - slot + 1) >= 3) ? lmt_checkinteger(L, slot++) : cat_code_table_par;
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ if (catcode_in_range(val)) {
+ tex_set_cat_code(cattable, ch, val, level);
+ } else {
+ texlib_aux_show_catcode_error(L, val);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+/* [cattable] code */
+
+static int texlib_getcatcode(lua_State *L)
+{
+ int slot = 1;
+ int cattable = (lua_gettop(L) > 1) ? lmt_checkinteger(L, slot++) : cat_code_table_par;
+ int ch = lmt_checkinteger(L, slot);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_cat_code(cattable, ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 12); /* other */
+ }
+ return 1;
+}
+
+/*
+ [global] code { c f ch }
+ [global] code c f ch (a bit easier on memory, counterpart of getter)
+*/
+
+static int texlib_setmathcode(lua_State *L)
+{
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword cval, fval, chval;
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ cval = lmt_checkhalfword(L, slot++);
+ fval = lmt_checkhalfword(L, slot++);
+ chval = lmt_checkhalfword(L, slot);
+ break;
+ case LUA_TTABLE:
+ lua_rawgeti(L, slot, 1);
+ cval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 2);
+ fval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 3);
+ chval = lmt_checkhalfword(L, -1);
+ lua_pop(L, 3);
+ break;
+ default:
+ return luaL_error(L, "number of table expected");
+ }
+ if (class_in_range(cval)) {
+ if (family_in_range(fval)) {
+ if (character_in_range(chval)) {
+ mathcodeval m;
+ m.character_value = chval;
+ m.class_value = (short) cval;
+ m.family_value = (short) fval;
+ tex_set_math_code(ch, m, (quarterword) (level));
+ } else {
+ texlib_aux_show_character_error(L, chval);
+ }
+ } else {
+ texlib_aux_show_family_error(L, fval);
+ }
+ } else {
+ texlib_aux_show_class_error(L, cval);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+return 0;
+}
+
+static int texlib_getmathcode(lua_State* L)
+{
+ mathcodeval mval = { 0, 0, 0 };
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ mval = tex_get_math_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ lua_createtable(L, 3, 0);
+ lua_pushinteger(L, mval.class_value);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, mval.family_value);
+ lua_rawseti(L, -2, 2);
+ lua_pushinteger(L, mval.character_value);
+ lua_rawseti(L, -2, 3);
+ return 1;
+}
+
+static int texlib_getmathcodes(lua_State* L)
+{
+ mathcodeval mval = { 0, 0, 0 };
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ mval = tex_get_math_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ lua_pushinteger(L, mval.class_value);
+ lua_pushinteger(L, mval.family_value);
+ lua_pushinteger(L, mval.character_value);
+ return 3;
+}
+
+/*
+ [global] code { c f ch }
+ [global] code c f ch (a bit easier on memory, counterpart of getter)
+*/
+
+static int texlib_setdelcode(lua_State* L)
+{
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ /* todo: when no integer than do a reset */
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword sfval, scval, lfval, lcval;
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ sfval = lmt_checkhalfword(L, slot++);
+ scval = lmt_checkhalfword(L, slot++);
+ lfval = lmt_checkhalfword(L, slot++);
+ lcval = lmt_checkhalfword(L, slot);
+ break;
+ case LUA_TTABLE:
+ lua_rawgeti(L, slot, 1);
+ sfval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 2);
+ scval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 3);
+ lfval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 4);
+ lcval = lmt_checkhalfword(L, -1);
+ lua_pop(L, 4);
+ break;
+ default:
+ return luaL_error(L, "number of table expected");
+ }
+ if (family_in_range(sfval)) {
+ if (character_in_range(scval)) {
+ if (family_in_range(lfval)) {
+ if (character_in_range(lcval)) {
+ delcodeval d;
+ d.small.class_value = 0;
+ d.small.family_value = (short) sfval;
+ d.small.character_value = scval;
+ d.large.class_value = 0;
+ d.large.family_value = (short) lfval;
+ d.large.character_value = lcval;
+ tex_set_del_code(ch, d, (quarterword) (level));
+ }
+ else {
+ texlib_aux_show_character_error(L, lcval);
+ }
+ }
+ else {
+ texlib_aux_show_family_error(L, lfval);
+ }
+ }
+ else {
+ texlib_aux_show_character_error(L, scval);
+ }
+ }
+ else {
+ texlib_aux_show_family_error(L, sfval);
+ }
+ }
+ else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ return 0;
+}
+
+static int texlib_getdelcode(lua_State* L)
+{
+ delcodeval dval = tex_no_del_code();
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ dval = tex_get_del_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ if (tex_has_del_code(dval)) {
+ lua_createtable(L, 4, 0);
+ lua_pushinteger(L, dval.small.family_value);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, dval.small.character_value);
+ lua_rawseti(L, -2, 2);
+ lua_pushinteger(L, dval.large.family_value);
+ lua_rawseti(L, -2, 3);
+ lua_pushinteger(L, dval.large.character_value);
+ lua_rawseti(L, -2, 4);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_getdelcodes(lua_State* L)
+{
+ delcodeval dval = tex_no_del_code();
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ dval = tex_get_del_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ if (tex_has_del_code(dval)) {
+ lua_pushinteger(L, dval.small.family_value);
+ lua_pushinteger(L, dval.small.character_value);
+ lua_pushinteger(L, dval.large.family_value);
+ lua_pushinteger(L, dval.large.character_value);
+ } else {
+ lua_pushnil(L);
+ }
+ return 4;
+}
+
+static halfword texlib_aux_getdimension(lua_State* L, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ return lmt_toroundnumber(L, index);
+ case LUA_TSTRING:
+ return texlib_aux_dimen_to_number(L, lua_tostring(L, index));
+ default:
+ luaL_error(L, "string or number expected (dimension)");
+ return 0;
+ }
+}
+
+static halfword texlib_aux_getinteger(lua_State* L, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ return lmt_toroundnumber(L, index);
+ default:
+ luaL_error(L, "number expected (integer)");
+ return 0;
+ }
+}
+
+static halfword texlib_toparshape(lua_State *L, int i)
+{
+ if (lua_type(L, i) == LUA_TTABLE) {
+ halfword n = (halfword) luaL_len(L, i);
+ if (n > 0) {
+ halfword p = tex_new_specification_node(n, par_shape_code, 0); /* todo: repeat but then not top based */
+ lua_push_key(repeat);
+ if (lua_rawget(L, -2) == LUA_TBOOLEAN && lua_toboolean(L, -1)) {
+ tex_set_specification_option(p, specification_option_repeat);
+ }
+ lua_pop(L, 1);
+ /* fill |p| */
+ for (int j = 1; j <= n; j++) {
+ halfword indent = 0;
+ halfword width = 0;
+ if (lua_rawgeti(L, i, j) == LUA_TTABLE) {
+ if (lua_rawgeti(L, -1, 1) == LUA_TNUMBER) {
+ indent = lmt_toroundnumber(L, -1);
+ if (lua_rawgeti(L, -2, 2) == LUA_TNUMBER) {
+ width = lmt_toroundnumber(L, -1);
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ tex_set_specification_indent(p, j, indent);
+ tex_set_specification_width(p, j, width);
+ }
+ return p;
+ }
+ }
+ return null;
+}
+
+static int texlib_shiftparshape(lua_State *L)
+{
+ if (par_shape_par) {
+ tex_shift_specification_list(par_shape_par, lmt_tointeger(L, 1), lua_toboolean(L, 2));
+ }
+ return 0;
+}
+
+static int texlib_snapshotpar(lua_State *L)
+{
+ halfword par = tex_find_par_par(cur_list.head);
+ if (par) {
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ tex_snapshot_par(par, lmt_tointeger(L, 1));
+ }
+ lua_pushinteger(L, par_state(par));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int texlib_getparstate(lua_State *L)
+{
+ lua_createtable(L, 0, 7);
+ lua_push_integer_at_key(L, hsize, hsize_par);
+ lua_push_integer_at_key(L, leftskip, left_skip_par ? glue_amount(left_skip_par) : 0);
+ lua_push_integer_at_key(L, rightskip, right_skip_par ? glue_amount(right_skip_par) : 0);
+ lua_push_integer_at_key(L, hangindent, hang_indent_par);
+ lua_push_integer_at_key(L, hangafter, hang_after_par);
+ lua_push_integer_at_key(L, parindent, par_indent_par);
+ lua_push_specification_at_key(L, parshape, par_shape_par);
+ return 1;
+}
+
+static int texlib_set_item(lua_State* L, int index, int prefixes)
+{
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, index, &flags, prefixes, 0);
+ size_t sl;
+ const char *st = lua_tolstring(L, slot++, &sl);
+ if (sl > 0) {
+ int cs = tex_string_locate(st, sl, 0);
+ if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
+ int cmd = eq_type(cs);
+ switch (cmd) {
+ case internal_int_cmd:
+ case register_int_cmd: /* ? */
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ {
+ int n = lmt_tointeger(L, slot++);
+ if (cmd == register_int_cmd) {
+ tex_word_define(flags, eq_value(cs), n);
+ } else {
+ tex_assign_internal_int_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
+ }
+ break;
+ }
+ default:
+ luaL_error(L, "number expected");
+ break;
+ }
+ return 1;
+ case internal_dimen_cmd:
+ case register_dimen_cmd:
+ {
+ halfword n = texlib_aux_getdimension(L, slot);
+ if (cmd == register_dimen_cmd) {
+ tex_word_define(flags, eq_value(cs), n);
+ } else {
+ tex_assign_internal_dimen_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
+ }
+ return 1;
+ }
+ case internal_glue_cmd:
+ case register_glue_cmd:
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ {
+ int top = lua_gettop(L);
+ halfword value = tex_copy_node(zero_glue);
+ glue_amount(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_shrink(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch_order(value) = lmt_tohalfword(L, slot++);
+ if (slot <= top) {
+ glue_shrink_order(value) = lmt_tohalfword(L, slot);
+ }
+ }
+ }
+ }
+ if (cmd == register_glue_cmd) {
+ tex_word_define(flags, eq_value(cs), value);
+ } else {
+ tex_assign_internal_skip_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), value);
+ }
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ halfword n = lmt_check_isnode(L, slot);
+ if (node_type(n) == glue_spec_node) {
+ if (cmd == register_glue_cmd) {
+ tex_word_define(flags, eq_value(cs), n);
+ } else {
+ tex_assign_internal_skip_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
+ }
+ } else {
+ luaL_error(L, "gluespec node expected");
+ }
+ break;
+ }
+ default:
+ luaL_error(L, "number or node expected");
+ break;
+ }
+ return 1;
+ case internal_toks_cmd:
+ case register_toks_cmd:
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ int t = lmt_token_list_from_lua(L, slot);
+ // define(flags, eq_value(cs), call_cmd, t); /* was call_cmd */
+ tex_define(flags, eq_value(cs), cmd == internal_toks_cmd ? internal_toks_reference_cmd : register_toks_reference_cmd, t); /* eq_value(cs) and not cs ? */
+ break;
+ }
+ default:
+ luaL_error(L, "string expected");
+ break;
+ }
+ return 1;
+ case set_page_property_cmd:
+ /*tex This could be |set_page_property_value| instead. */
+ switch (eq_value(cs)) {
+ // case page_goal_code:
+ // case page_total_code:
+ // case page_vsize_code:
+ case page_depth_code:
+ lmt_page_builder_state.depth = texlib_aux_getdimension(L, slot);
+ break;
+ // case page_stretch_code:
+ // case page_filstretch_code:
+ // case page_fillstretch_code:
+ // case page_filllstretch_code:
+ // case page_shrink_code:
+ case insert_storing_code:
+ lmt_insert_state.storing = texlib_aux_getinteger(L, slot);
+ break;
+ // case dead_cycles_code:
+ // case insert_penalties_code:
+ // case interaction_mode_code:
+ default:
+ return 0;
+ }
+ case set_auxiliary_cmd:
+ /*tex This could be |set_aux_value| instead. */
+ switch (eq_value(cs)) {
+ case space_factor_code:
+ cur_list.space_factor = texlib_aux_getinteger(L, slot);
+ return 1;
+ case prev_depth_code:
+ cur_list.prev_depth = texlib_aux_getdimension(L, slot);
+ return 1;
+ case prev_graf_code:
+ cur_list.prev_graf = texlib_aux_getinteger(L, slot);
+ return 1;
+ default:
+ return 0;
+ }
+ case set_box_property_cmd:
+ /*tex This could be |set_box_property_value| instead. */
+ return 0;
+ case set_specification_cmd:
+ {
+ int chr = internal_specification_number(eq_value(cs));
+ switch (chr) {
+ case par_shape_code:
+ {
+ halfword p = texlib_toparshape(L, slot);
+ tex_define(flags, eq_value(cs), specification_reference_cmd, p);
+ // lua_toboolean(L, slot + 1) ? add_frozen_flag(flags) : flags
+ if (is_frozen(flags) && cur_mode == hmode) {
+ tex_update_par_par(specification_reference_cmd, chr);
+ }
+ break;
+ }
+ }
+ return 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_set(lua_State *L)
+{
+ texlib_set_item(L, 1, 1);
+ return 0;
+}
+
+static int texlib_newindex(lua_State *L)
+{
+ if (! texlib_set_item(L, 2, 0)) {
+ lua_rawset(L, 1);
+ }
+ return 0;
+}
+
+static int texlib_aux_convert(lua_State *L, int cur_code)
+{
+ int i = -1;
+ char *str = NULL;
+ switch (cur_code) {
+ /* ignored (yet) */
+ case insert_progress_code: /* arg <register int> */
+ case lua_code: /* arg complex */
+ case lua_escape_string_code: /* arg token list */
+ case string_code: /* arg token */
+ case cs_string_code: /* arg token */
+ case detokenized_code: /* arg token */
+ case meaning_code: /* arg token */
+ case to_mathstyle_code:
+ break;
+ /* the next fall through, and come from 'official' indices! */
+ case font_name_code: /* arg fontid */
+ case font_specification_code: /* arg fontid */
+ case font_identifier_code: /* arg fontid */
+ case number_code: /* arg int */
+ case to_integer_code: /* arg int */
+ case to_hexadecimal_code: /* arg int */
+ case to_scaled_code: /* arg int */
+ case to_sparse_scaled_code: /* arg int */
+ case to_dimension_code: /* arg int */
+ case to_sparse_dimension_code: /* arg int */
+ case roman_numeral_code: /* arg int */
+ if (lua_gettop(L) < 1) {
+ /* error */
+ }
+ i = lmt_tointeger(L, 1);
+ // fall through
+ default:
+ /* no backend here */
+ if (cur_code < 32) {
+ int texstr = tex_the_convert_string(cur_code, i);
+ if (texstr) {
+ str = tex_makecstring(texstr);
+ tex_flush_str(texstr);
+ }
+ }
+ break;
+ }
+ /* end */
+ if (str) {
+ lua_pushstring(L, str);
+ lmt_memory_free(str);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_aux_scan_internal(lua_State *L, int cmd, int code, int values)
+{
+ int retval = 1 ;
+ int save_cur_val = cur_val;
+ int save_cur_val_level = cur_val_level;
+ tex_scan_something_simple(cmd, code);
+ switch (cur_val_level) {
+ case int_val_level:
+ case dimen_val_level:
+ case attr_val_level:
+ lua_pushinteger(L, cur_val);
+ break;
+ case glue_val_level:
+ case mu_val_level:
+ switch (values) {
+ case 0:
+ lua_pushinteger(L, glue_amount(cur_val));
+ tex_flush_node(cur_val);
+ break;
+ case 1:
+ lua_pushinteger(L, glue_amount(cur_val));
+ lua_pushinteger(L, glue_stretch(cur_val));
+ lua_pushinteger(L, glue_shrink(cur_val));
+ lua_pushinteger(L, glue_stretch_order(cur_val));
+ lua_pushinteger(L, glue_shrink_order(cur_val));
+ tex_flush_node(cur_val);
+ retval = 5;
+ break;
+ default:
+ lmt_push_node_fast(L, cur_val);
+ break;
+ }
+ break;
+ case list_val_level:
+ lmt_push_node_fast(L, cur_val);
+ break;
+ default:
+ {
+ int texstr = tex_the_scanned_result();
+ char *str = tex_makecstring(texstr);
+ if (str) {
+ lua_pushstring(L, str);
+ lmt_memory_free(str);
+ } else {
+ lua_pushnil(L);
+ }
+ tex_flush_str(texstr);
+ }
+ break;
+ }
+ cur_val = save_cur_val;
+ cur_val_level = save_cur_val_level;
+ return retval;
+}
+
+/*tex
+ Todo: complete this one.
+*/
+
+static int texlib_aux_someitem(lua_State *L, int code)
+{
+ switch (code) {
+ /* the next two do not actually exist */
+ /* case attrexpr_code: */
+ /* break; */
+ /* the expressions do something complicated with arguments, yuck */
+ case numexpr_code:
+ case dimexpr_code:
+ case glueexpr_code:
+ case muexpr_code:
+ case numexpression_code:
+ case dimexpression_code:
+ break;
+ // case dimen_to_scale_code:
+ case numeric_scale_code:
+ break;
+ case index_of_register_code:
+ case index_of_character_code:
+ break;
+ case last_chk_num_code:
+ case last_chk_dim_code:
+ break;
+ /* these read a glue or muglue, todo */
+ case mu_to_glue_code:
+ case glue_to_mu_code:
+ case glue_stretch_order_code:
+ case glue_shrink_order_code:
+ case glue_stretch_code:
+ case glue_shrink_code:
+ break;
+ /* these read a fontid and a char, todo */
+ case font_id_code:
+ case glyph_x_scaled_code:
+ case glyph_y_scaled_code:
+ /* these read a font, todo */
+ case font_spec_id_code:
+ case font_spec_scale_code:
+ case font_spec_xscale_code:
+ case font_spec_yscale_code:
+ /* these need a spec, todo */
+ break;
+ case font_char_wd_code:
+ case font_char_ht_code:
+ case font_char_dp_code:
+ case font_char_ic_code:
+ case font_char_ta_code:
+ /* these read a char, todo */
+ break;
+ case font_size_code:
+ lua_pushinteger(L, font_size(cur_font_par));
+ break;
+ case font_math_control_code:
+ lua_pushinteger(L, font_mathcontrol(cur_font_par));
+ break;
+ case font_text_control_code:
+ lua_pushinteger(L, font_textcontrol(cur_font_par));
+ break;
+ case math_scale_code:
+ break;
+ case math_style_code:
+ {
+ int style = tex_current_math_style();
+ if (style >= 0) {
+ lua_pushinteger(L, style);
+ return 1;
+ } else {
+ break;
+ }
+ }
+ /* these read a char, todo */
+ case math_main_style_code:
+ {
+ int style = tex_current_math_main_style();
+ if (style >= 0) {
+ lua_pushinteger(L, style);
+ return 1;
+ } else {
+ break;
+ }
+ }
+ /* these read a char, todo */
+ case math_char_class_code:
+ case math_char_fam_code:
+ case math_char_slot_code:
+ break;
+ case last_arguments_code:
+ lua_pushinteger(L, lmt_expand_state.arguments);
+ return 1;
+ case parameter_count_code:
+ lua_pushinteger(L, tex_get_parameter_count());
+ return 1;
+ /* case lua_value_function_code: */
+ /* break; */
+ case insert_progress_code:
+ break;
+ /* these read an integer, todo */
+ case left_margin_kern_code:
+ case right_margin_kern_code:
+ break;
+ case par_shape_length_code:
+ case par_shape_indent_code:
+ case par_shape_dimen_code:
+ break;
+ case lastpenalty_code:
+ case lastkern_code:
+ case lastskip_code:
+ case lastboundary_code:
+ case last_node_type_code:
+ case last_node_subtype_code:
+ case input_line_no_code:
+ case badness_code:
+ case overshoot_code:
+ case luatex_version_code:
+ case luatex_revision_code:
+ case current_group_level_code:
+ case current_group_type_code:
+ case current_if_level_code:
+ case current_if_type_code:
+ case current_if_branch_code:
+ return texlib_aux_scan_internal(L, some_item_cmd, code, -1);
+ case last_left_class_code:
+ lua_pushinteger(L, lmt_math_state.last_left);
+ return 1;
+ case last_right_class_code:
+ lua_pushinteger(L, lmt_math_state.last_right);
+ return 1;
+ case last_atom_class_code:
+ lua_pushinteger(L, lmt_math_state.last_atom);
+ return 1;
+ case current_loop_iterator_code:
+ case last_loop_iterator_code:
+ lua_pushinteger(L, lmt_main_control_state.loop_iterator);
+ return 1;
+ case current_loop_nesting_code:
+ lua_pushinteger(L, lmt_main_control_state.loop_nesting);
+ return 1;
+ case last_par_context_code:
+ lua_pushinteger(L, lmt_main_control_state.last_par_context);
+ return 1;
+ case last_page_extra_code:
+ lua_pushinteger(L, lmt_page_builder_state.last_extra_used);
+ return 1;
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_setmath(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 3) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int param = lmt_get_math_parameter(L, slot++, -1);
+ int style = lmt_get_math_style(L, slot++, -1);
+ if (param < 0 || style < 0) {
+ /* invalid spec, just ignore it */
+ } else {
+ switch (math_parameter_value_type(param)) {
+ case math_int_parameter:
+ case math_dimen_parameter:
+ case math_style_parameter:
+ tex_def_math_parameter(style, param, (scaled) lmt_optroundnumber(L, slot, 0), level, indirect_math_regular);
+ break;
+ case math_muglue_parameter:
+ {
+ halfword p = tex_copy_node(zero_glue);
+ glue_amount(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_stretch(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_shrink(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_stretch_order(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_shrink_order(p) = lmt_optroundnumber(L, slot, 0);
+ tex_def_math_parameter(style, param, (scaled) p, level, indirect_math_regular);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_getmath(lua_State *L)
+{
+ if (lua_gettop(L) == 2) {
+ int param = lmt_get_math_parameter(L, 1, -1);
+ int style = lmt_get_math_style(L, 2, -1);
+ if (param >= 0 && style >= 0) {
+ scaled value = tex_get_math_parameter(style, param, NULL);
+ if (value != undefined_math_parameter) {
+ switch (math_parameter_value_type(param)) {
+ case math_int_parameter:
+ case math_dimen_parameter:
+ case math_style_parameter:
+ lua_pushinteger(L, value);
+ return 1;
+ case math_muglue_parameter:
+ if (value <= thick_mu_skip_code) {
+ value = glue_parameter(value);
+ }
+ lua_pushinteger(L, glue_amount(value));
+ lua_pushinteger(L, glue_stretch(value));
+ lua_pushinteger(L, glue_shrink(value));
+ lua_pushinteger(L, glue_stretch_order(value));
+ lua_pushinteger(L, glue_shrink_order(value));
+ return 5;
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/*tex
+
+ This one is purely for diagnostic pusposed as normally there is some scaling
+ involved related to the current style and such.
+
+*/
+
+static int texlib_getfontname(lua_State *L)
+{
+ return texlib_aux_convert(L, font_name_code);
+}
+
+static int texlib_getfontidentifier(lua_State *L)
+{
+ return texlib_aux_convert(L, font_identifier_code);
+}
+
+static int texlib_getfontoffamily(lua_State *L)
+{
+ int f = lmt_checkinteger(L, 1);
+ int s = lmt_optinteger(L, 2, 0); /* this should be a multiple of 256 ! */
+ lua_pushinteger(L, tex_fam_fnt(f, s));
+ return 1;
+}
+
+static int texlib_getnumber(lua_State *L)
+{
+ return texlib_aux_convert(L, number_code); /* check */
+}
+
+// static int texlib_getdimension(lua_State *L)
+// {
+// return texlib_aux_convert(L, to_dimension_code); /* check */
+// }
+
+static int texlib_getromannumeral(lua_State *L)
+{
+ return texlib_aux_convert(L, roman_numeral_code);
+}
+
+static int texlib_get_internal(lua_State *L, int index, int all)
+{
+ if (lua_type(L, index) == LUA_TSTRING) {
+ size_t l;
+ const char *s = lua_tolstring(L, index, &l);
+ if (l == 0) {
+ return 0;
+ } else if (lua_key_eq(s, prevdepth)) {
+ lua_pushinteger(L, cur_list.prev_depth);
+ return 1;
+ } else if (lua_key_eq(s, prevgraf)) {
+ lua_pushinteger(L, cur_list.prev_graf);
+ return 1;
+ } else if (lua_key_eq(s, spacefactor)) {
+ lua_pushinteger(L, cur_list.space_factor);
+ return 1;
+ } else {
+ /*tex
+ We no longer get the info from the primitives hash but use the current
+ primitive meaning.
+ */ /*
+ int ts = maketexlstring(s, l);
+ int cs = prim_lookup(ts);
+ flush_str(ts);
+ if (cs > 0) {
+ int cs = string_locate(s, l, 0);
+ if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
+ int cmd = get_prim_eq_type(cs);
+ int code = get_prim_equiv(cs);
+ */
+ int cs = tex_string_locate(s, l, 0);
+ if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
+ int cmd = eq_type(cs);
+ int code = eq_value(cs);
+ switch (cmd) {
+ case some_item_cmd:
+ return texlib_aux_someitem(L, code);
+ case convert_cmd:
+ return texlib_aux_convert(L, code);
+ case internal_toks_cmd:
+ case register_toks_cmd:
+ case internal_int_cmd:
+ case register_int_cmd:
+ case internal_attribute_cmd:
+ case register_attribute_cmd:
+ case internal_dimen_cmd:
+ case register_dimen_cmd:
+ case lua_value_cmd:
+ case iterator_value_cmd:
+ case set_auxiliary_cmd:
+ case set_page_property_cmd:
+ case char_given_cmd:
+ // case math_char_given_cmd:
+ case integer_cmd:
+ case dimension_cmd:
+ case gluespec_cmd:
+ case mugluespec_cmd:
+ case mathspec_cmd:
+ case fontspec_cmd:
+ return texlib_aux_scan_internal(L, cmd, code, -1);
+ case internal_glue_cmd:
+ case register_glue_cmd:
+ case internal_mu_glue_cmd:
+ case register_mu_glue_cmd:
+ return texlib_aux_scan_internal(L, cmd, code, all);
+ case set_specification_cmd:
+ return lmt_push_specification(L, specification_parameter(internal_specification_number(code)), all); /* all == countonly */
+ default:
+ /* tex_formatted_warning("tex.get", "ignoring cmd %i: %s\n", cmd, s); */
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_get(lua_State *L)
+{
+ /* stack: key [boolean] */
+ int ret = texlib_get_internal(L, 1, (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : -1);
+ if (ret) {
+ return ret;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int texlib_index(lua_State *L)
+{
+ /* stack: table key */
+ int ret = texlib_get_internal(L, 2, -1);
+ if (ret) {
+ return ret;
+ } else {
+ lua_rawget(L, 1);
+ return 1;
+ }
+}
+
+static int texlib_getlist(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ if (! s) {
+ lua_pushnil(L);
+ } else if (lua_key_eq(s, pageinserthead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(page_insert_list_type, NULL));
+ } else if (lua_key_eq(s, contributehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(contribute_list_type, NULL));
+ } else if (lua_key_eq(s, pagehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(page_list_type, NULL));
+ } else if (lua_key_eq(s, temphead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(temp_list_type, NULL));
+ } else if (lua_key_eq(s, holdhead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(hold_list_type, NULL));
+ } else if (lua_key_eq(s, postadjusthead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(post_adjust_list_type, NULL));
+ } else if (lua_key_eq(s, preadjusthead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(pre_adjust_list_type, NULL));
+ } else if (lua_key_eq(s, postmigratehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(post_migrate_list_type, NULL));
+ } else if (lua_key_eq(s, premigratehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(pre_migrate_list_type, NULL));
+ } else if (lua_key_eq(s, alignhead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(align_list_type, NULL));
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(page_discards_list_type, NULL));
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(split_discards_list_type, NULL));
+ } else if (lua_key_eq(s, bestpagebreak)) {
+ lmt_push_node_fast(L, lmt_page_builder_state.best_break);
+ } else if (lua_key_eq(s, leastpagecost)) {
+ lua_pushinteger(L, lmt_page_builder_state.least_cost);
+ } else if (lua_key_eq(s, bestsize)) {
+ lua_pushinteger(L, lmt_page_builder_state.best_size); /* is pagegoal but can be unset and also persistent */
+ } else if (lua_key_eq(s, insertpenalties)) {
+ lua_pushinteger(L, lmt_page_builder_state.insert_penalties);
+ } else if (lua_key_eq(s, insertheights)) {
+ lua_pushinteger(L, lmt_page_builder_state.insert_heights);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* todo: accept direct node too */
+
+static int texlib_setlist(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ if (! s) {
+ /* This is silently ignored */
+ } else if (lua_key_eq(s, bestsize)) {
+ lmt_page_builder_state.best_size = lmt_toscaled(L, 2); /* is pagegoal but can be unset and also persistent */
+ } else if (lua_key_eq(s, leastpagecost)) {
+ lmt_page_builder_state.least_cost = lmt_tointeger(L, 2);
+ } else if (lua_key_eq(s, insertpenalties)) {
+ lmt_page_builder_state.insert_penalties = lmt_tointeger(L, 2);
+ } else if (lua_key_eq(s, insertheights)) {
+ lmt_page_builder_state.insert_heights = lmt_tointeger(L, 2);
+ } else {
+ halfword n = null;
+ if (! lua_isnil(L, 2)) {
+ n = lmt_check_isnode(L, 2);
+ }
+ if (lua_key_eq(s, pageinserthead)) {
+ tex_set_special_node_list(page_insert_list_type, n);
+ } else if (lua_key_eq(s, contributehead)) {
+ tex_set_special_node_list(contribute_list_type, n);
+ } else if (lua_key_eq(s, pagehead)) {
+ tex_set_special_node_list(page_list_type, n);
+ } else if (lua_key_eq(s, temphead)) {
+ tex_set_special_node_list(temp_list_type, n);
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ tex_set_special_node_list(page_discards_list_type, n);
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ tex_set_special_node_list(split_discards_list_type, n);
+ } else if (lua_key_eq(s, holdhead)) {
+ tex_set_special_node_list(hold_list_type, n);
+ } else if (lua_key_eq(s, postadjusthead)) {
+ tex_set_special_node_list(post_adjust_list_type, n);
+ } else if (lua_key_eq(s, preadjusthead)) {
+ tex_set_special_node_list(pre_adjust_list_type, n);
+ } else if (lua_key_eq(s, postmigratehead)) {
+ tex_set_special_node_list(post_migrate_list_type, n);
+ } else if (lua_key_eq(s, premigratehead)) {
+ tex_set_special_node_list(pre_migrate_list_type, n);
+ } else if (lua_key_eq(s, alignhead)) {
+ tex_set_special_node_list(align_list_type, n);
+ } else if (lua_key_eq(s, bestpagebreak)) {
+ lmt_page_builder_state.best_break = n;
+ }
+ }
+ return 0;
+}
+
+static void texlib_get_nest_field(lua_State *L, const char *field, list_state_record *r)
+{
+
+ if (lua_key_eq(field, mode)) {
+ lua_pushinteger(L, r->mode);
+ } else if (lua_key_eq(field, head) || lua_key_eq(field, list)) {
+ /* we no longer check for special list nodes here so beware of prev-of-head */
+ lmt_push_node_fast(L, r->head);
+ } else if (lua_key_eq(field, tail)) {
+ /* we no longer check for special list nodes here so beware of next-of-tail */
+ lmt_push_node_fast(L, r->tail);
+ } else if (lua_key_eq(field, delimiter)) {
+ lmt_push_node_fast(L, r->delim);
+ } else if (lua_key_eq(field, prevgraf)) {
+ lua_pushinteger(L, r->prev_graf);
+ } else if (lua_key_eq(field, modeline)) {
+ lua_pushinteger(L, r->mode_line);
+ } else if (lua_key_eq(field, prevdepth)) {
+ lua_pushinteger(L, r->prev_depth);
+ } else if (lua_key_eq(field, spacefactor)) {
+ lua_pushinteger(L, r->space_factor);
+ } else if (lua_key_eq(field, noad)) {
+ lmt_push_node_fast(L, r->incomplete_noad);
+ } else if (lua_key_eq(field, direction)) {
+ lmt_push_node_fast(L, r->direction_stack);
+ } else if (lua_key_eq(field, mathdir)) {
+ lua_pushboolean(L, r->math_dir);
+ } else if (lua_key_eq(field, mathstyle)) {
+ lua_pushinteger(L, r->math_style);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void texlib_set_nest_field(lua_State *L, int n, const char *field, list_state_record *r)
+{
+ if (lua_key_eq(field, mode)) {
+ r->mode = lmt_tointeger(L, n);
+ } else if (lua_key_eq(field, head) || lua_key_eq(field, list)) {
+ r->head = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, tail)) {
+ r->tail = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, delimiter)) {
+ r->delim = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, prevgraf)) {
+ r->prev_graf = lmt_tointeger(L, n);
+ } else if (lua_key_eq(field, modeline)) {
+ r->mode_line = lmt_tointeger(L, n);
+ } else if (lua_key_eq(field, prevdepth)) {
+ r->prev_depth = lmt_toroundnumber(L, n);
+ } else if (lua_key_eq(field, spacefactor)) {
+ r->space_factor = lmt_toroundnumber(L, n);
+ } else if (lua_key_eq(field, noad)) {
+ r->incomplete_noad = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, direction)) {
+ r->direction_stack = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, mathdir)) {
+ r->math_dir = lua_toboolean(L, n);
+ } else if (lua_key_eq(field, mathstyle)) {
+ r->math_style = lmt_tointeger(L, n);
+ }
+}
+
+static int texlib_aux_nest_getfield(lua_State *L)
+{
+ list_state_record **rv = lua_touserdata(L, -2);
+ list_state_record *r = *rv;
+ const char *field = lua_tostring(L, -1);
+ texlib_get_nest_field(L, field, r);
+ return 1;
+}
+
+static int texlib_aux_nest_setfield(lua_State *L)
+{
+ list_state_record **rv = lua_touserdata(L, -3);
+ list_state_record *r = *rv;
+ const char *field = lua_tostring(L, -2);
+ texlib_set_nest_field(L, -1, field, r);
+ return 0;
+}
+
+static const struct luaL_Reg texlib_nest_metatable[] = {
+ { "__index", texlib_aux_nest_getfield },
+ { "__newindex", texlib_aux_nest_setfield },
+ { NULL, NULL },
+};
+
+static void texlib_aux_init_nest_lib(lua_State *L)
+{
+ luaL_newmetatable(L, TEX_NEST_INSTANCE);
+ luaL_setfuncs(L, texlib_nest_metatable, 0);
+ lua_pop(L, 1);
+}
+
+/* getnest(<number>|top|ptr,[fieldname]) */
+
+static int texlib_getnest(lua_State *L)
+{
+ int p = -1 ;
+ int t = lua_gettop(L);
+ if (t == 0) {
+ p = lmt_nest_state.nest_data.ptr;
+ } else {
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ {
+ int ptr = lmt_tointeger(L, 1);
+ if (ptr >= 0 && ptr <= lmt_nest_state.nest_data.ptr) {
+ p = ptr;
+ }
+ }
+ break;
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, top)) {
+ p = lmt_nest_state.nest_data.ptr;
+ } else if (lua_key_eq(s, ptr)) {
+ lua_pushinteger(L, lmt_nest_state.nest_data.ptr);
+ return 1;
+ }
+ }
+ break;
+ }
+ }
+ if (p > -1) {
+ if (t > 1) {
+ const char *field = lua_tostring(L, 2);
+ if (field) {
+ texlib_get_nest_field(L, field, &lmt_nest_state.nest[p]);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ list_state_record **nestitem = lua_newuserdatauv(L, sizeof(list_state_record *), 0);
+ *nestitem = &lmt_nest_state.nest[p];
+ luaL_getmetatable(L, TEX_NEST_INSTANCE);
+ lua_setmetatable(L, -2);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* setnest(<number>|top,fieldname,value) */
+
+static int texlib_setnest(lua_State *L)
+{
+ if (lua_gettop(L) > 2) {
+ int p = -1 ;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ {
+ int ptr = lmt_tointeger(L, 1);
+ if (ptr >= 0 && ptr <= lmt_nest_state.nest_data.ptr) {
+ p = ptr;
+ }
+ }
+ break;
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, top)) {
+ p = lmt_nest_state.nest_data.ptr;
+ }
+ }
+ break;
+ }
+ if (p > -1) {
+ const char *field = lua_tostring(L, 2);
+ if (field) {
+ texlib_set_nest_field(L, 3, field, &lmt_nest_state.nest[p]);
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_round(lua_State *L)
+{
+ /* lua_pushinteger(L, lmt_roundedfloat((double) lua_tonumber(L, 1))); */
+ lua_pushinteger(L, clippedround((double) lua_tonumber(L, 1)));
+ return 1;
+}
+
+static int texlib_scale(lua_State *L)
+{
+ double delta = luaL_checknumber(L, 2);
+ switch (lua_type(L, 1)) {
+ case LUA_TTABLE:
+ {
+ /*tex
+ We could preallocate the table or maybe scale in-place. The
+ new table is at index 3.
+ */
+ lua_newtable(L);
+ lua_pushnil(L);
+ while (lua_next(L, 1)) {
+ /*tex We have a numeric value. */
+ lua_pushvalue(L, -2);
+ lua_insert(L, -2);
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+ double m = (double) lua_tonumber(L, -1) * delta;
+ lua_pop(L, 1);
+ /* lua_pushinteger(L, lmt_roundedfloat(m)); */
+ lua_pushinteger(L, clippedround(m));
+ }
+ lua_rawset(L, 3);
+ }
+ }
+ break;
+ case LUA_TNUMBER:
+ /* lua_pushinteger(L, lmt_roundedfloat((double) lua_tonumber(L, 1) * delta)); */
+ lua_pushinteger(L, clippedround((double) lua_tonumber(L, 1) * delta));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ return 1;
+}
+
+/*tex
+ For compatibility reasons we keep the check for a boolean for a while. For consistency
+ we now support flags too: |global cs id|.
+
+*/
+
+static int texlib_definefont(lua_State *L)
+{
+ size_t l;
+ int slot = 1;
+ int flags = (lua_isboolean(L, slot) && lua_toboolean(L, slot++)) ? add_global_flag(0) : 0;
+ const char *csname = lua_tolstring(L, slot++, &l);
+ halfword id = lmt_tohalfword(L, slot++);
+ int cs = tex_string_locate(csname, l, 1);
+ lmt_check_for_flags(L, slot, &flags, 1, 1);
+ tex_define(flags, cs, set_font_cmd, id);
+ return 0;
+}
+
+static int texlib_hashtokens(lua_State *L)
+{
+ int cs = 1;
+ int nt = 0;
+ int nx = 0;
+ int all = lua_toboolean(L, 1);
+ lua_createtable(L, hash_size, 0);
+ if (all) {
+ while (cs <= hash_size) {
+ /* because strings never get freed we can as well directly access |s|. */
+ strnumber s = cs_text(cs);
+ if (s > 0) {
+ halfword n = cs_next(cs);
+ char *ss = tex_makecstring(s);
+ if (n) {
+ int mt = 0;
+ lua_createtable(L, 2, 0);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ ++nt;
+ lua_rawseti(L, -2, ++mt);
+ while (n) {
+ s = cs_text(n);
+ if (s) {
+ ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++mt);
+ ++nt;
+ ++nx;
+ }
+ n = cs_next(n);
+ }
+ } else {
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ ++nt;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ lua_rawseti(L, -2, cs);
+ cs++;
+ }
+ } else {
+ while (cs < hash_size) {
+ strnumber s = cs_text(cs);
+ if (s > 0) {
+ halfword n = cs_next(cs);
+ char *ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ while (n) {
+ s = cs_text(n);
+ if (s) {
+ ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ ++nx;
+ }
+ n = cs_next(n);
+ }
+ }
+ cs++;
+ }
+ }
+ lua_pushinteger(L, --cs);
+ lua_pushinteger(L, nt);
+ lua_pushinteger(L, nx);
+ return 4;
+}
+
+static int texlib_primitives(lua_State *L)
+{
+ int cs = 0;
+ int nt = 0;
+ lua_createtable(L, prim_size, 0);
+ while (cs < prim_size) {
+ strnumber s = get_prim_text(cs);
+ if (s > 0 && (get_prim_origin(cs) != no_command)) {
+ char *ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ }
+ cs++;
+ }
+ return 1;
+}
+
+static int texlib_extraprimitives(lua_State *L)
+{
+ int mask = 0;
+ int cs = 0;
+ int nt = 0;
+ int n = lua_gettop(L);
+ if (n == 0) {
+ mask = tex_command + etex_command + luatex_command;
+ } else {
+ for (int i = 1; i <= n; i++) {
+ if (lua_type(L, i) == LUA_TSTRING) {
+ const char *s = lua_tostring(L, i);
+ if (lua_key_eq(s, tex)) {
+ mask |= tex_command;
+ } else if (lua_key_eq(s, etex)) {
+ mask |= etex_command;
+ } else if (lua_key_eq(s, luatex)) {
+ mask |= luatex_command;
+ }
+ }
+ }
+ }
+ lua_createtable(L, prim_size, 0);
+ while (cs < prim_size) {
+ strnumber s = get_prim_text(cs);
+ if (s > 0 && (get_prim_origin(cs) & mask)) {
+ char *ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ }
+ cs++;
+ }
+ return 1;
+}
+
+static void texlib_aux_enableprimitive(const char *pre, size_t prel, const char *prm)
+{
+ strnumber s = tex_maketexstring(prm);
+ halfword prm_val = tex_prim_lookup(s);
+ tex_flush_str(s);
+ if (prm_val != undefined_primitive && get_prim_origin(prm_val) != no_command) {
+ char *newprm;
+ size_t newlen;
+ halfword cmd = get_prim_eq_type(prm_val);
+ halfword chr = get_prim_equiv(prm_val);
+ if (strncmp(pre, prm, prel) != 0) {
+ /* not a prefix */
+ newlen = strlen(prm) + prel;
+ newprm = (char *) lmt_memory_malloc((size_t) newlen + 1);
+ if (newprm) {
+ strcpy(newprm, pre);
+ strcat(newprm + prel, prm);
+ } else {
+ tex_overflow_error("primitives", (int) newlen + 1);
+ }
+ } else {
+ newlen = strlen(prm);
+ newprm = (char *) lmt_memory_malloc((size_t) newlen + 1);
+ if (newprm) {
+ strcpy(newprm, prm);
+ } else {
+ tex_overflow_error("primitives", (int) newlen + 1);
+ }
+ }
+ if (newprm) {
+ halfword val = tex_string_locate(newprm, newlen, 1);
+ if (val == undefined_control_sequence || eq_type(val) == undefined_cs_cmd) {
+ tex_primitive_def(newprm, newlen, (singleword) cmd, chr);
+ }
+ lmt_memory_free(newprm);
+ }
+ }
+}
+
+static int texlib_enableprimitives(lua_State *L)
+{
+ if (lua_gettop(L) == 2) {
+ size_t lpre;
+ const char *pre = luaL_checklstring(L, 1, &lpre);
+ switch (lua_type(L, 2)) {
+ case LUA_TTABLE:
+ {
+ int i = 1;
+ while (1) {
+ if (lua_rawgeti(L, 2, i) == LUA_TSTRING) {
+ const char *prm = lua_tostring(L, 3);
+ texlib_aux_enableprimitive(pre, lpre, prm);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ lua_pop(L, 1);
+ i++;
+ }
+ }
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ for (int cs = 0; cs < prim_size; cs++) {
+ strnumber s = get_prim_text(cs);
+ if (s > 0) {
+ /* there is actually no need to copy */
+ char *prm = tex_makecstring(s);
+ texlib_aux_enableprimitive(pre, lpre, prm);
+ lmt_memory_free(prm);
+ }
+ }
+ }
+ break;
+ default:
+ luaL_error(L, "array of names or 'true' expected");
+ }
+ } else {
+ luaL_error(L, "wrong number of arguments");
+ }
+ return 0;
+}
+
+/*tex penalties */
+
+static halfword texlib_topenalties(lua_State *L, int i, quarterword s)
+{
+ int n = 0;
+ lua_pushnil(L);
+ while (lua_next(L, i)) {
+ n++;
+ lua_pop(L, 1);
+ }
+ if (n > 0) {
+ int j = 0;
+ halfword p = tex_new_specification_node(n, s, 0); /* todo: repeat */
+ lua_pushnil(L);
+ while (lua_next(L, i)) {
+ j++;
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ tex_set_specification_penalty(p, j, lmt_tohalfword(L, -1));
+ }
+ lua_pop(L, 1);
+ }
+ return p;
+ } else {
+ return null;
+ }
+}
+
+/*tex We should check for proper glue spec nodes ... todo. */
+
+# define get_dimen_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : B; \
+ lua_pop(L, 1);
+
+# define get_glue_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TUSERDATA) ? lmt_check_isnode(L, -1) : B; \
+ lua_pop(L, 1);
+
+# define get_integer_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_tohalfword(L, -1) : B; \
+ lua_pop(L, 1);
+
+# define get_penalties_par(P,A,B,C) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_topenalties(L, lua_gettop(L), C) : B; \
+ lua_pop(L, 1);
+
+# define get_shape_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_toparshape(L, lua_gettop(L)) : B; \
+ lua_pop(L, 1);
+
+/*tex
+ The next function needs to be kept in sync with the regular linebreak handler, wrt the special
+ skips. This one can be called from within the callback so then we already have intialized.
+*/
+
+
+/* par leftinit rightinit leftindent ... leftfill rightfill */
+
+static int texlib_preparelinebreak(lua_State *L)
+{
+ halfword direct;
+ halfword par = lmt_check_isdirectornode(L, 1, &direct);
+ if (node_type(par) == par_node) {
+ halfword tail = tex_tail_of_node_list(par);
+ if (node_type(tail) == glue_node && node_subtype(tail) == par_fill_right_skip_glue) {
+ tex_formatted_warning("linebreak", "list seems already prepared");
+ } else {
+ halfword parinit_left_skip_glue = null;
+ halfword parinit_right_skip_glue = null;
+ halfword parfill_left_skip_glue = null;
+ halfword parfill_right_skip_glue = null;
+ halfword final_penalty = null;
+ tex_line_break_prepare(par, &tail, &parinit_left_skip_glue, &parinit_right_skip_glue, &parfill_left_skip_glue, &parfill_right_skip_glue, &final_penalty);
+ lmt_push_directornode(L, par, direct);
+ lmt_push_directornode(L, tail, direct);
+ lmt_push_directornode(L, parinit_left_skip_glue, direct);
+ lmt_push_directornode(L, parinit_right_skip_glue, direct);
+ lmt_push_directornode(L, parfill_left_skip_glue , direct);
+ lmt_push_directornode(L, parfill_right_skip_glue, direct);
+ /* lmt_push_directornode(L, final_penalty, direct); */ /*tex Not that relevant to know. */
+ return 6;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_linebreak(lua_State *L)
+{
+ // halfword par = lmt_check_isnode(L, 1);
+ halfword direct;
+ halfword par = lmt_check_isdirectornode(L, 1, &direct);
+ if (node_type(par) == par_node) {
+ line_break_properties properties;
+ halfword tail = par;
+ halfword has_indent = null;
+ halfword has_penalty = 0;
+ halfword prepared = 0;
+ properties.initial_par = par;
+ properties.display_math = 0;
+ properties.paragraph_dir = par_dir(par);
+ properties.parfill_left_skip = null;
+ properties.parfill_right_skip = null;
+ properties.parinit_left_skip = null;
+ properties.parinit_right_skip = null;
+ while (tail) {
+ switch (node_type(tail)) {
+ case glue_node:
+ switch (node_subtype(tail)) {
+ case indent_skip_glue:
+ if (has_indent) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "indent");
+ goto NOTHING;
+ } else {
+ has_indent = 1;
+ }
+ break;
+ case par_fill_left_skip_glue:
+ if (properties.parfill_left_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "leftskip");
+ goto NOTHING;
+ } else {
+ properties.parfill_left_skip = tail;
+ }
+ break;
+ case par_fill_right_skip_glue:
+ if (properties.parfill_right_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "rightskip");
+ goto NOTHING;
+ } else {
+ properties.parfill_right_skip = tail;
+ }
+ break;
+ case par_init_left_skip_glue:
+ if (properties.parinit_left_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "leftinit");
+ goto NOTHING;
+ } else {
+ properties.parinit_left_skip = tail;
+ }
+ break;
+ case par_init_right_skip_glue:
+ if (properties.parinit_right_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "rightinit");
+ goto NOTHING;
+ } else {
+ properties.parinit_right_skip = tail;
+ }
+ break;
+ }
+ break;
+ case penalty_node:
+ if (node_subtype(tail) == line_penalty_subtype && penalty_amount(tail) == infinite_penalty && ! (properties.parfill_left_skip && properties.parfill_right_skip)) {
+ has_penalty = 1;
+ }
+ }
+ if (node_next(tail)) {
+ tail = node_next(tail);
+ } else {
+ break;
+ }
+ }
+ {
+ int has_init = properties.parinit_left_skip && properties.parinit_right_skip;
+ int has_fill = properties.parfill_left_skip && properties.parfill_right_skip;
+ if (lmt_linebreak_state.calling_back) {
+ if (has_indent && ! (has_init && has_fill && has_penalty)) {
+ tex_formatted_warning("linebreak", "[ par + leftinit + rightinit + indentglue + ... + penalty + leftfill + righfill ] expected");
+ goto NOTHING;
+ } else if (! (has_fill && has_penalty)) {
+ tex_formatted_warning("linebreak", "[ par + indentbox + ... + penalty + leftfill + righfill ] expected");
+ goto NOTHING;
+ } else {
+ prepared = 1;
+ }
+ } else {
+ if (! (has_indent && has_init && has_fill)) {
+ tex_formatted_warning("linebreak", "[ leftinit | rightinit | leftfill | rigthfill ] expected");
+ goto NOTHING;
+ } else {
+ // prepared = 0;
+ prepared = has_init && has_fill;
+ }
+ }
+ }
+ tex_push_nest();
+ node_next(temp_head) = par;
+ /*tex initialize local parameters */
+ if (lua_gettop(L) != 2 || lua_type(L, 2) != LUA_TTABLE) {
+ lua_newtable(L);
+ }
+ lua_push_key(direction);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ properties.paragraph_dir = checked_direction_value(lmt_tointeger(L, -1));
+ }
+ lua_pop(L, 1);
+ get_integer_par (properties.tracing_paragraphs, tracingparagraphs, tracing_paragraphs_par);
+ get_integer_par (properties.pretolerance, pretolerance, tex_get_par_par(par, par_pre_tolerance_code));
+ get_integer_par (properties.tolerance, tolerance, tex_get_par_par(par, par_tolerance_code));
+ get_dimen_par (properties.emergency_stretch, emergencystretch, tex_get_par_par(par, par_emergency_stretch_code));
+ get_integer_par (properties.looseness, looseness, tex_get_par_par(par, par_looseness_code));
+ get_integer_par (properties.adjust_spacing, adjustspacing, tex_get_par_par(par, par_adjust_spacing_code));
+ get_integer_par (properties.protrude_chars, protrudechars, tex_get_par_par(par, par_protrude_chars_code));
+ get_integer_par (properties.adj_demerits, adjdemerits, tex_get_par_par(par, par_adj_demerits_code));
+ get_integer_par (properties.line_penalty, linepenalty, tex_get_par_par(par, par_line_penalty_code));
+ get_integer_par (properties.last_line_fit, lastlinefit, tex_get_par_par(par, par_last_line_fit_code));
+ get_integer_par (properties.double_hyphen_demerits, doublehyphendemerits, tex_get_par_par(par, par_double_hyphen_demerits_code));
+ get_integer_par (properties.final_hyphen_demerits, finalhyphendemerits, tex_get_par_par(par, par_final_hyphen_demerits_code));
+ get_dimen_par (properties.hsize, hsize, tex_get_par_par(par, par_hsize_code));
+ get_glue_par (properties.left_skip, leftskip, tex_get_par_par(par, par_left_skip_code));
+ get_glue_par (properties.right_skip, rightskip, tex_get_par_par(par, par_right_skip_code));
+ get_dimen_par (properties.hang_indent, hangindent, tex_get_par_par(par, par_hang_indent_code));
+ get_integer_par (properties.hang_after, hangafter, tex_get_par_par(par, par_hang_after_code));
+ get_integer_par (properties.inter_line_penalty, interlinepenalty, tex_get_par_par(par, par_inter_line_penalty_code));
+ get_integer_par (properties.club_penalty, clubpenalty, tex_get_par_par(par, par_club_penalty_code));
+ get_integer_par (properties.widow_penalty, widowpenalty, tex_get_par_par(par, par_widow_penalty_code));
+ get_integer_par (properties.display_widow_penalty, displaywidowpenalty, tex_get_par_par(par, par_display_widow_penalty_code));
+ get_integer_par (properties.orphan_penalty, orphanpenalty, tex_get_par_par(par, par_orphan_penalty_code));
+ get_integer_par (properties.broken_penalty, brokenpenalty, tex_get_par_par(par, par_broken_penalty_code));
+ get_glue_par (properties.baseline_skip, baselineskip, tex_get_par_par(par, par_baseline_skip_code));
+ get_glue_par (properties.line_skip, lineskip, tex_get_par_par(par, par_line_skip_code));
+ get_dimen_par (properties.line_skip_limit, lineskiplimit, tex_get_par_par(par, par_line_skip_limit_code));
+ get_integer_par (properties.adjust_spacing, adjustspacing, tex_get_par_par(par, par_adjust_spacing_code));
+ get_integer_par (properties.adjust_spacing_step, adjustspacingstep, tex_get_par_par(par, par_adjust_spacing_step_code));
+ get_integer_par (properties.adjust_spacing_shrink, adjustspacingshrink, tex_get_par_par(par, par_adjust_spacing_shrink_code));
+ get_integer_par (properties.adjust_spacing_stretch, adjustspacingstretch, tex_get_par_par(par, par_adjust_spacing_stretch_code));
+ get_integer_par (properties.hyphenation_mode, hyphenationmode, tex_get_par_par(par, par_hyphenation_mode_code));
+ get_integer_par (properties.shaping_penalties_mode, shapingpenaltiesmode, tex_get_par_par(par, par_shaping_penalties_mode_code));
+ get_integer_par (properties.shaping_penalty, shapingpenalty, tex_get_par_par(par, par_shaping_penalty_code));
+ get_shape_par (properties.par_shape, parshape, tex_get_par_par(par, par_par_shape_code));
+ get_penalties_par(properties.inter_line_penalties, interlinepenalties, tex_get_par_par(par, par_inter_line_penalties_code), inter_line_penalties_code);
+ get_penalties_par(properties.club_penalties, clubpenalties, tex_get_par_par(par, par_club_penalties_code), club_penalties_code);
+ get_penalties_par(properties.widow_penalties, widowpenalties, tex_get_par_par(par, par_widow_penalties_code), widow_penalties_code);
+ get_penalties_par(properties.display_widow_penalties,displaywidowpenalties,tex_get_par_par(par, par_display_widow_penalties_code), display_widow_penalties_code);
+ get_penalties_par(properties.orphan_penalties, orphanpenalties, tex_get_par_par(par, par_orphan_penalties_code), orphan_penalties_code);
+ if (! prepared) {
+ halfword attr_template = tail;
+ halfword final_penalty = tex_new_penalty_node(infinite_penalty, line_penalty_subtype);
+ /* */
+ get_glue_par(properties.parfill_left_skip, parfillleftskip, tex_get_par_par(par, par_par_fill_left_skip_code));
+ get_glue_par(properties.parfill_right_skip, parfillrightskip, tex_get_par_par(par, par_par_fill_right_skip_code));
+ get_glue_par(properties.parinit_left_skip, parinitleftskip, tex_get_par_par(par, par_par_init_left_skip_code));
+ get_glue_par(properties.parinit_right_skip, parinitrightskip, tex_get_par_par(par, par_par_init_right_skip_code));
+ /* */
+ properties.parfill_left_skip = tex_new_glue_node(properties.parfill_left_skip, par_fill_left_skip_glue);
+ properties.parfill_right_skip = tex_new_glue_node(properties.parfill_right_skip, par_fill_right_skip_glue);
+ tex_attach_attribute_list_copy(final_penalty, attr_template);
+ tex_attach_attribute_list_copy(properties.parfill_left_skip, attr_template);
+ tex_attach_attribute_list_copy(properties.parfill_right_skip, attr_template);
+ tex_couple_nodes(tail, final_penalty);
+ tex_couple_nodes(final_penalty, properties.parfill_left_skip);
+ tex_couple_nodes(properties.parfill_left_skip, properties.parfill_right_skip);
+ if (node_next(par)) { /* test can go, also elsewhere */
+ halfword n = node_next(par);
+ while (n) {
+ if (node_type(n) == glue_node && node_subtype(n) == indent_skip_glue) {
+ properties.parinit_left_skip = tex_new_glue_node(properties.parinit_left_skip, par_init_left_skip_glue);
+ properties.parinit_right_skip = tex_new_glue_node(properties.parinit_right_skip, par_init_right_skip_glue);
+ tex_attach_attribute_list_copy(properties.parinit_left_skip, attr_template); // maybe head .. also elsewhere
+ tex_attach_attribute_list_copy(properties.parinit_right_skip, attr_template); // maybe head .. also elsewhere
+ tex_try_couple_nodes(properties.parinit_right_skip, n);
+ tex_try_couple_nodes(properties.parinit_left_skip, properties.parinit_right_skip);
+ tex_try_couple_nodes(par, properties.parinit_left_skip);
+ break;
+ } else {
+ n = node_next(n);
+ }
+ }
+ }
+ }
+ lmt_linebreak_state.last_line_fill = properties.parfill_right_skip; /*tex I need to redo this. */
+ tex_do_line_break(&properties);
+ {
+ halfword fewest_demerits = 0;
+ halfword actual_looseness = 0;
+ /*tex return the generated list, and its prevdepth */
+ tex_get_linebreak_info(&fewest_demerits, &actual_looseness) ;
+ lmt_push_directornode(L, node_next(cur_list.head), direct);
+ lua_createtable(L, 0, 4);
+ /* set_integer_by_key(L, demerits, fewest_demerits); */
+ lua_push_key(demerits);
+ lua_pushinteger(L, fewest_demerits);
+ lua_settable(L, -3);
+ /* set_integer_by_key(L, looseness, actual_looseness); */
+ lua_push_key(looseness);
+ lua_pushinteger(L, actual_looseness);
+ lua_settable(L, -3);
+ /* set_integer_by_key(L, prevdepth, cur_list.prev_depth); */
+ lua_push_key(prevdepth);
+ lua_pushinteger(L, cur_list.prev_depth);
+ lua_settable(L, -3);
+ /* set_integer_by_key(L, prevgraf, cur_list.prev_graf); */
+ lua_push_key(prevgraf);
+ lua_pushinteger(L, cur_list.prev_graf);
+ lua_settable(L, -3);
+ }
+ tex_pop_nest();
+ if (properties.par_shape != tex_get_par_par(par, par_par_shape_code)) { tex_flush_node(properties.par_shape); }
+ if (properties.inter_line_penalties != tex_get_par_par(par, par_inter_line_penalties_code)) { tex_flush_node(properties.inter_line_penalties); }
+ if (properties.club_penalties != tex_get_par_par(par, par_club_penalties_code)) { tex_flush_node(properties.club_penalties); }
+ if (properties.widow_penalties != tex_get_par_par(par, par_widow_penalties_code)) { tex_flush_node(properties.widow_penalties); }
+ if (properties.display_widow_penalties != tex_get_par_par(par, par_display_widow_penalties_code)) { tex_flush_node(properties.display_widow_penalties); }
+ if (properties.orphan_penalties != tex_get_par_par(par, par_orphan_penalties_code)) { tex_flush_node(properties.orphan_penalties); }
+ return 2;
+ } else {
+ tex_formatted_warning("linebreak", "[ par ... ] expected");
+ }
+ NOTHING:
+ lmt_push_directornode(L, par, direct);
+ return 1;
+}
+
+static int texlib_resetparagraph(lua_State *L)
+{
+ (void) L;
+ tex_normal_paragraph(reset_par_context);
+ return 0;
+}
+
+static int texlib_shipout(lua_State *L)
+{
+ int boxnum = lmt_get_box_id(L, 1, 1);
+ if (box_register(boxnum)) {
+ tex_flush_node_list(box_register(boxnum));
+ box_register(boxnum) = null;
+ }
+ return 0;
+}
+
+static int texlib_badness(lua_State *L)
+{
+ scaled t = lmt_roundnumber(L, 1);
+ scaled s = lmt_roundnumber(L, 2);
+ lua_pushinteger(L, tex_badness(t, s));
+ return 1;
+}
+
+static int texlib_showcontext(lua_State *L)
+{
+ (void) L;
+ tex_show_context();
+ return 0;
+}
+
+/*tex
+ When we pass |true| the page builder will only be invoked in the main vertical list in which
+ case |lmt_nest_state.nest_data.ptr == 1| or |cur_list.mode != vmode|.
+*/
+
+static int texlib_triggerbuildpage(lua_State *L)
+{
+ if (lua_toboolean(L, 1) && cur_list.mode != vmode) {
+ return 0;
+ }
+ tex_build_page();
+ return 0;
+}
+
+static int texlib_getpagestate(lua_State *L)
+{
+ lua_pushinteger(L, lmt_page_builder_state.contents);
+ return 1;
+}
+
+static int texlib_getlocallevel(lua_State *L)
+{
+ lua_pushinteger(L, lmt_main_control_state.local_level);
+ return 1;
+}
+
+/* input state aka synctex */
+
+static int texlib_setinputstatemode(lua_State *L)
+{
+ input_file_state.mode = lmt_tohalfword(L, 1);
+ return 0;
+}
+static int texlib_getinputstatemode(lua_State *L)
+{
+ lua_pushinteger(L, input_file_state.mode);
+ return 1;
+}
+
+static int texlib_setinputstatefile(lua_State *L)
+{
+ lmt_input_state.cur_input.state_file = lmt_tointeger(L, 1);
+ return 0;
+}
+
+static int texlib_getinputstatefile(lua_State *L)
+{
+ lua_pushinteger(L, lmt_input_state.cur_input.state_file);
+ return 1;
+}
+
+static int texlib_forceinputstatefile(lua_State *L)
+{
+ input_file_state.forced_file = lmt_tointeger(L, 1);
+ return 0;
+}
+
+static int texlib_forceinputstateline(lua_State *L)
+{
+ input_file_state.forced_line = lmt_tointeger(L, 1);
+ return 0;
+}
+
+static int texlib_setinputstateline(lua_State *L)
+{
+ input_file_state.line = lmt_tohalfword(L, 1);
+ return 0;
+}
+
+static int texlib_getinputstateline(lua_State *L)
+{
+ lua_pushinteger(L, input_file_state.line);
+ return 1;
+}
+
+/*tex
+ This is experimental and might change. In version 10 we hope to have the final version available.
+ It actually took quite a bit of time to understand the implications of mixing lua prints in here.
+ The current variant is (so far) the most robust (wrt crashes and side effects).
+*/
+
+// # define mode cur_list.mode_field
+
+/*tex
+ When we add save levels then we can get crashes when one flushed bad groups due to out of order
+ flushing. So we play safe! But still we can have issues so best make sure you're in hmode.
+*/
+
+static int texlib_forcehmode(lua_State *L)
+{
+ if (abs(cur_list.mode) == vmode) {
+ if (lua_type(L, 1) == LUA_TBOOLEAN) {
+ tex_begin_paragraph(lua_toboolean(L, 1), force_par_begin);
+ } else {
+ tex_begin_paragraph(1, force_par_begin);
+ }
+ }
+ return 0;
+}
+
+/* tex
+ The first argument can be a number (of a token register), a macro name or the name of a token
+ list. The second argument is optional and when true forces expansion inside a definition. The
+ optional third argument can be used to force oing. The return value indicates an error: 0
+ means no error, 1 means that a bad register number has been passed, a value of 2 indicated an
+ unknown register or macro name, while 3 reports that the macro is not suitable for local
+ control because it takes arguments.
+*/
+
+static int texlib_runlocal(lua_State *L)
+{
+ // int obeymode = lua_toboolean(L, 4);
+ int obeymode = 1; /* always 1 */
+ halfword tok = -1;
+ int mac = 0 ;
+ switch (lua_type(L, 1)) {
+ case LUA_TFUNCTION:
+ {
+ /* todo: also a variant that calls an already registered function */
+ int ref;
+ halfword r, t;
+ lua_pushvalue(L, 1);
+ ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ r = tex_get_available_token(token_val(end_local_cmd, 0));
+ t = tex_get_available_token(token_val(lua_local_call_cmd, ref));
+ token_link(t) = r;
+ tex_begin_inserted_list(t);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering token scanner via function");
+ }
+ tex_local_control(obeymode);
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+ return 0;
+ }
+ case LUA_TNUMBER:
+ {
+ halfword k = lmt_checkhalfword(L, 1);
+ if (k >= 0 && k <= 65535) {
+ tok = toks_register(k);
+ goto TOK;
+ } else {
+ tex_local_control_message("invalid token register number");
+ return 0;
+ }
+ }
+ case LUA_TSTRING:
+ {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ int cs = tex_string_locate(name, lname, 0);
+ int cmd = eq_type(cs);
+ if (cmd < call_cmd) { // is_call_cmd
+ // todo: use the better register helpers and range checkers
+ switch (cmd) {
+ case register_toks_cmd:
+ tok = toks_register(register_toks_number(eq_value(cs)));
+ goto TOK;
+ case undefined_cs_cmd:
+ tex_local_control_message("undefined macro or token register");
+ return 0;
+ default:
+ /* like cs == case undefined_control_sequence */
+ tex_local_control_message("invalid macro or token register");
+ return 0;
+ }
+ } else {
+ halfword ref = eq_value(cs);
+ halfword head = token_link(ref);
+ if (head && get_token_parameters(ref)) {
+ tex_local_control_message("macro takes arguments and is ignored");
+ return 0;
+ } else {
+ tok = cs_token_flag + cs;
+ mac = 1 ;
+ goto TOK;
+ }
+ }
+ }
+ case LUA_TUSERDATA:
+ /* no checking yet */
+ tok = token_info(lmt_token_code_from_lua(L, 1));
+ mac = 1;
+ goto TOK;
+ default:
+ return 0;
+ }
+ TOK:
+ if (tok < 0) {
+ /* nothing to do */
+ } else if (lmt_input_state.scanner_status != scanner_is_defining || lua_toboolean(L, 2)) {
+ // todo: make list
+ int grouped = lua_toboolean(L, 3);
+ if (grouped) {
+ tex_begin_inserted_list(tex_get_available_token(token_val(right_brace_cmd, 0)));
+ }
+ tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
+ if (mac) {
+ tex_begin_inserted_list(tex_get_available_token(tok));
+ } else {
+ tex_begin_token_list(tok, local_text);
+ }
+ if (grouped) {
+ tex_begin_inserted_list(tex_get_available_token(token_val(left_brace_cmd, 0)));
+ }
+ /*tex hm, needed here? */
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (tracing_nesting_par > 2) {
+ if (mac) {
+ tex_local_control_message("entering token scanner via macro");
+ } else {
+ tex_local_control_message("entering token scanner via register");
+ }
+ }
+ tex_local_control(obeymode);
+ } else if (mac) {
+ tex_back_input(tok);
+ } else {
+ halfword h = null;
+ halfword t = null;
+ halfword r = token_link(tok);
+ while (r) {
+ t = tex_store_new_token(t, token_info(r));
+ if (! h) {
+ h = t;
+ }
+ r = token_link(r);
+ }
+ tex_begin_inserted_list(h);
+ }
+ return 0;
+}
+
+static int texlib_quitlocal(lua_State *L)
+{
+ (void) L;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("quitting token scanner");
+ }
+ tex_end_local_control();
+ return 0;
+}
+
+/* todo: no tryagain and justincase here */
+
+static int texlib_expandasvalue(lua_State *L) /* mostly like the mp one */
+{
+ int kind = lmt_tointeger(L, 1);
+ halfword tail = null;
+ halfword head = lmt_macro_to_tok(L, 2, &tail);
+ if (head) {
+ switch (kind) {
+ case lua_value_none_code:
+ case lua_value_dimension_code:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_dimen(0, 0, 0, 0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (! lmt_error_state.last_intercept) {
+ lua_pushinteger(L, value);
+ break;
+ } else if (kind == lua_value_none_code) {
+ head = lmt_macro_to_tok(L, 2, &tail);
+ goto TRYAGAIN;
+ } else {
+ head = lmt_macro_to_tok(L, 2, &tail);
+ goto JUSTINCASE;
+ }
+ }
+ case lua_value_integer_code:
+ case lua_value_cardinal_code:
+ case lua_value_boolean_code:
+ TRYAGAIN:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_int(0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (lmt_error_state.last_intercept) {
+ head = lmt_macro_to_tok(L, 2, &tail);
+ goto JUSTINCASE;
+ } else if (kind == lua_value_boolean_code) {
+ lua_pushboolean(L, value);
+ break;
+ } else {
+ lua_pushinteger(L, value);
+ break;
+ }
+ }
+ default:
+ JUSTINCASE:
+ {
+ int len = 0;
+ const char *str = (const char *) lmt_get_expansion(head, &len);
+ lua_pushlstring(L, str, str ? len : 0); /* len includes \0 */
+ break;
+ }
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* string, expand-in-def, group */
+
+static int texlib_runstring(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top > 0) {
+ size_t lstr = 0;
+ const char *str = NULL;
+ int slot = 1;
+ halfword ct = lua_type(L, slot) == LUA_TNUMBER ? lmt_tohalfword(L, slot++) : cat_code_table_par;
+ if (! tex_valid_catcode_table(ct)) {
+ ct = cat_code_table_par;
+ }
+ str = lua_tolstring(L, slot++, &lstr);
+ if (lstr > 0) {
+ int obeymode = 1; /* always 1 */
+ int expand = lua_toboolean(L, slot++);
+ int grouped = lua_toboolean(L, slot++);
+ int ignore = lua_toboolean(L, slot++);
+ halfword h = get_reference_token();
+ halfword t = h;
+ if (grouped) {
+ // t = tex_store_new_token(a, left_brace_token + '{');
+ t = tex_store_new_token(t, token_val(right_brace_cmd, 0));
+ }
+ /*tex Options: 1=create (will trigger an error), 2=ignore. */
+ tex_parse_str_to_tok(h, &t, ct, str, lstr, ignore ? 2 : 1);
+ if (grouped) {
+ // t = tex_store_new_token(a, left_brace_token + '}');
+ t = tex_store_new_token(t, token_val(left_brace_cmd, 0));
+ }
+ if (lmt_input_state.scanner_status != scanner_is_defining || expand) {
+ // t = tex_store_new_token(t, token_val(end_local_cmd, 0));
+ tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
+ tex_begin_token_list(h, local_text);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering token scanner via register");
+ }
+ tex_local_control(obeymode);
+ } else {
+ tex_begin_inserted_list(h);
+ }
+ }
+ }
+ return 0;
+}
+
+/* new, can go into luatex too */
+
+static int texlib_getmathdir(lua_State *L)
+{
+ lua_pushinteger(L, math_direction_par);
+ return 0;
+}
+
+static int texlib_setmathdir(lua_State *L)
+{
+ tex_set_math_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+static int texlib_getpardir(lua_State *L)
+{
+ lua_pushinteger(L, par_direction_par);
+ return 1;
+}
+
+static int texlib_setpardir(lua_State *L)
+{
+ tex_set_par_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+static int texlib_gettextdir(lua_State *L)
+{
+ lua_pushinteger(L, text_direction_par);
+ return 1;
+}
+
+static int texlib_settextdir(lua_State *L)
+{
+ tex_set_text_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+/* Getting the line direction makes no sense, it's just the text direction. */
+
+static int texlib_setlinedir(lua_State *L)
+{
+ tex_set_line_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+static int texlib_getboxdir(lua_State *L)
+{
+ int index = lmt_tointeger(L, 1);
+ if (index >= 0 && index <= max_box_register_index) {
+ if (box_register(index)) {
+ lua_pushinteger(L, box_dir(box_register(index)));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+ } else {
+ texlib_aux_show_box_index_error(L);
+ }
+ return 0;
+}
+
+static int texlib_setboxdir(lua_State *L)
+{
+ int index = lmt_tointeger(L, 1);
+ if (index >= 0 && index <= max_box_register_index) {
+ tex_set_box_dir(index, lmt_tointeger(L, 2));
+ } else {
+ texlib_aux_show_box_index_error(L);
+ }
+ return 0;
+}
+
+static int texlib_gethelptext(lua_State *L)
+{
+ if (lmt_error_state.help_text) {
+ lua_pushstring(L, lmt_error_state.help_text);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_setinteraction(lua_State *L)
+{
+ if (lua_type(L,1) == LUA_TNUMBER) {
+ int i = lmt_tointeger(L, 1);
+ if (i >= 0 && i <= 3) {
+ lmt_error_state.interaction = i;
+ }
+ }
+ return 0;
+}
+
+static int texlib_getinteraction(lua_State *L)
+{
+ lua_pushinteger(L, lmt_error_state.interaction);
+ return 1;
+}
+
+static int texlib_setglyphdata(lua_State *L)
+{
+ update_tex_glyph_data(0, lmt_opthalfword(L, 1, unused_attribute_value));
+ return 0;
+}
+
+static int texlib_getglyphdata(lua_State *L)
+{
+ lua_pushinteger(L, glyph_data_par);
+ return 1;
+}
+
+static int texlib_setglyphstate(lua_State *L)
+{
+ update_tex_glyph_state(0, lmt_opthalfword(L, 1, unused_state_value));
+ return 0;
+}
+
+static int texlib_getglyphstate(lua_State *L)
+{
+ lua_pushinteger(L, glyph_state_par);
+ return 1;
+}
+
+static int texlib_setglyphscript(lua_State *L)
+{
+ update_tex_glyph_script(0, lmt_opthalfword(L, 1, unused_script_value));
+ return 0;
+}
+
+static int texlib_getglyphscript(lua_State *L)
+{
+ lua_pushinteger(L, glyph_script_par);
+ return 1;
+}
+
+static int texlib_getglyphscales(lua_State *L)
+{
+ lua_pushinteger(L, glyph_scale_par);
+ lua_pushinteger(L, glyph_x_scale_par);
+ lua_pushinteger(L, glyph_y_scale_par);
+ lua_pushinteger(L, glyph_data_par);
+ return 4;
+}
+
+static int texlib_fatalerror(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ tex_fatal_error(s);
+ return 1;
+}
+
+static int texlib_lastnodetype(lua_State *L)
+{
+ halfword tail = cur_list.tail;
+ int t = -1;
+ int s = -1;
+ if (tail) {
+ halfword mode = cur_list.mode;
+ if (mode != nomode && tail != contribute_head && node_type(tail) != glyph_node) {
+ t = node_type(tail);
+ s = node_subtype(tail);
+ } else if (mode == vmode && tail == cur_list.head) {
+ t = lmt_page_builder_state.last_node_type;
+ s = lmt_page_builder_state.last_node_subtype;
+ } else if (mode == nomode || tail == cur_list.head) {
+ /* already -1 */
+ } else {
+ t = node_type(tail);
+ s = node_subtype(tail);
+ }
+ }
+ if (t >= 0) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, s);
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* we can have all defs here */
+
+static int texlib_chardef(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ int code = lmt_tointeger(L, 2);
+ if (code >= 0 && code <= max_character_code) {
+ tex_define(flags, cs, (quarterword) char_given_cmd, code);
+ } else {
+ tex_formatted_error("lua", "chardef only accepts codes in the range 0-%i", max_character_code);
+ }
+ }
+ }
+ return 0;
+}
+
+/* todo: same range checks as in texlib_setmathcode */
+
+static int texlib_mathchardef(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 5, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ mathcodeval m;
+ mathdictval d;
+ m.class_value = lmt_tointeger(L, 2);
+ m.family_value = lmt_tointeger(L, 3);
+ m.character_value = lmt_tointeger(L, 4);
+ d.properties = lmt_optquarterword(L, 6, 0);
+ d.group = lmt_optquarterword(L, 7, 0);
+ d.index = lmt_optinteger(L, 8, 0);
+ if (class_in_range(m.class_value) && family_in_range(m.family_value) && character_in_range(m.character_value)) {
+ tex_define(flags, cs, mathspec_cmd, tex_new_math_dict_spec(d, m, umath_mathcode));
+ // halfword code = math_packed_character(m.class_value, m.family_value, m.character_value);
+ // tex_define(flags, cs, (quarterword) math_char_xgiven_cmd, code);
+ } else {
+ tex_normal_error("lua", "mathchardef needs proper class, family and character codes");
+ }
+ } else {
+ /* maybe a message */
+ }
+ }
+ return 0;
+}
+
+static int texlib_setintegervalue(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ int value = lmt_optroundnumber(L, 2, 0);
+ if (value >= min_integer && value <= max_integer) {
+ tex_define(flags, cs, (quarterword) integer_cmd, value);
+ } else {
+ tex_formatted_error("lua", "integer only accepts values in the range %i-%i", min_integer, max_integer);
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_setdimensionvalue(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ int value = lmt_optroundnumber(L, 2, 0);
+ if (value >= min_dimen && value <= max_dimen) {
+ tex_define(flags, cs, (quarterword) dimension_cmd, value);
+ } else {
+ tex_formatted_error("lua", "dimension only accepts values in the range %i-%i", min_dimen, max_dimen);
+ }
+ }
+ }
+ return 0;
+}
+
+// static int texlib_setgluespecvalue(lua_State *L)
+// {
+// return 0;
+// }
+
+static int texlib_aux_getvalue(lua_State *L, halfword level, halfword cs)
+{
+ halfword chr = eq_value(cs);
+ if (chr && ! get_token_parameters(chr)) {
+ halfword value = 0;
+ tex_begin_inserted_list(tex_get_available_token(cs_token_flag + cs));
+ if (tex_scan_tex_value(level, &value)) {
+ lua_pushinteger(L, value);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_getintegervalue(lua_State *L) /* todo, now has duplicate in tokenlib */
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ int cs = tex_string_locate(s, l, 0);
+ switch (eq_type(cs)) {
+ case integer_cmd:
+ lua_pushinteger(L, eq_value(cs));
+ return 1;
+ case call_cmd:
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ return texlib_aux_getvalue(L, int_val_level, cs);
+ default:
+ /* twice a lookup but fast enough for now */
+ return texlib_getcount(L);
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_getdimensionvalue(lua_State *L) /* todo, now has duplicate in tokenlib */
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ int cs = tex_string_locate(s, l, 0);
+ switch (eq_type(cs)) {
+ case dimension_cmd:
+ lua_pushinteger(L, eq_value(cs));
+ return 1;
+ case call_cmd:
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ return texlib_aux_getvalue(L, dimen_val_level, cs);
+ default:
+ /* twice a lookup but fast enough for now */
+ return texlib_getdimen(L);
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+// static int texlib_getgluespecvalue(lua_State *L) /* todo, now has duplicate in tokenlib */
+// {
+// return 1;
+// }
+
+/*tex
+ Negative values are internal and inline. At some point I might do this as with modes and tokens
+ although we don't have lookups here.
+
+ In these list we don't really need the predefined keys.
+*/
+
+static int texlib_getmodevalues(lua_State *L)
+{
+ lua_createtable(L, 4, 1);
+ lua_push_key_at_index(L, unset, nomode);
+ lua_push_key_at_index(L, vertical, vmode);
+ lua_push_key_at_index(L, horizontal, hmode);
+ lua_push_key_at_index(L, math, mmode);
+ return 1;
+}
+
+static int texlib_getmode(lua_State *L)
+{
+ lua_pushinteger(L, abs(cur_list.mode));
+ return 1;
+}
+
+static int texlib_getrunstatevalues(lua_State *L)
+{
+ lua_createtable(L, 2, 1);
+ lua_set_string_by_index(L, initializing_state, "initializing");
+ lua_set_string_by_index(L, updating_state, "updating");
+ lua_set_string_by_index(L, production_state, "production");
+ return 1;
+}
+
+static int texlib_setrunstate(lua_State *L)
+{
+ int state = lmt_tointeger(L, 1);
+ if (state == updating_state || state == production_state) {
+ lmt_main_state.run_state = state;
+ }
+ return 0;
+}
+
+static int texlib_gethyphenationvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 17);
+ lua_push_key_at_index(L, normal, normal_hyphenation_mode);
+ lua_push_key_at_index(L, automatic, automatic_hyphenation_mode);
+ lua_push_key_at_index(L, explicit, explicit_hyphenation_mode);
+ lua_push_key_at_index(L, syllable, syllable_hyphenation_mode);
+ lua_push_key_at_index(L, uppercase, uppercase_hyphenation_mode);
+ lua_push_key_at_index(L, compound, compound_hyphenation_mode);
+ lua_push_key_at_index(L, strictstart, strict_start_hyphenation_mode);
+ lua_push_key_at_index(L, strictend, strict_end_hyphenation_mode);
+ lua_push_key_at_index(L, automaticpenalty, automatic_penalty_hyphenation_mode);
+ lua_push_key_at_index(L, explicitpenalty, explicit_penalty_hyphenation_mode);
+ lua_push_key_at_index(L, permitglue, permit_glue_hyphenation_mode);
+ lua_push_key_at_index(L, permitall, permit_all_hyphenation_mode);
+ lua_push_key_at_index(L, permitmathreplace, permit_math_replace_hyphenation_mode);
+ lua_push_key_at_index(L, forcecheck, force_check_hyphenation_mode);
+ lua_push_key_at_index(L, lazyligatures, lazy_ligatures_hyphenation_mode);
+ lua_push_key_at_index(L, forcehandler, force_handler_hyphenation_mode);
+ lua_push_key_at_index(L, feedbackcompound, feedback_compound_hyphenation_mode);
+ lua_push_key_at_index(L, ignorebounds, ignore_bounds_hyphenation_mode);
+ lua_push_key_at_index(L, collapse, collapse_hyphenation_mode);
+ return 1;
+}
+
+static int texlib_getglyphoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 3, 7);
+ lua_set_string_by_index(L, glyph_option_normal_glyph, "normal");
+ lua_set_string_by_index(L, glyph_option_no_left_ligature, "noleftligature");
+ lua_set_string_by_index(L, glyph_option_no_right_ligature, "norightligature");
+ lua_set_string_by_index(L, glyph_option_no_left_kern, "noleftkern");
+ lua_set_string_by_index(L, glyph_option_no_right_kern, "norightkern");
+ lua_set_string_by_index(L, glyph_option_no_expansion, "noexpansion");
+ lua_set_string_by_index(L, glyph_option_no_protrusion, "noprotrusion");
+ lua_set_string_by_index(L, glyph_option_no_italic_correction, "noitaliccorrection");
+ lua_set_string_by_index(L, glyph_option_math_discretionary, "mathdiscretionary");
+ lua_set_string_by_index(L, glyph_option_math_italics_too, "mathsitalicstoo");
+ return 1;
+}
+
+static int texlib_getnoadoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 32);
+ lua_push_key_at_index(L, axis, noad_option_axis);
+ lua_push_key_at_index(L, noaxis, noad_option_no_axis);
+ lua_push_key_at_index(L, exact, noad_option_exact);
+ lua_push_key_at_index(L, left, noad_option_left);
+ lua_push_key_at_index(L, middle, noad_option_middle);
+ lua_push_key_at_index(L, right, noad_option_right);
+ lua_push_key_at_index(L, adapttoleftsize, noad_option_adapt_to_left_size);
+ lua_push_key_at_index(L, adapttorightsize, noad_option_adapt_to_right_size);
+ lua_push_key_at_index(L, nosubscript, noad_option_no_sub_script);
+ lua_push_key_at_index(L, nosuperscript, noad_option_no_super_script);
+ lua_push_key_at_index(L, nosubprescript, noad_option_no_sub_pre_script);
+ lua_push_key_at_index(L, nosuperprescript, noad_option_no_super_pre_script);
+ lua_push_key_at_index(L, noscript, noad_option_no_script);
+ lua_push_key_at_index(L, nooverflow, noad_option_no_overflow);
+ lua_push_key_at_index(L, void, noad_option_void);
+ lua_push_key_at_index(L, phantom, noad_option_phantom);
+ lua_push_key_at_index(L, openupheight, noad_option_openup_height);
+ lua_push_key_at_index(L, openupdepth, noad_option_openup_depth);
+ lua_push_key_at_index(L, limits, noad_option_limits);
+ lua_push_key_at_index(L, nolimits, noad_option_no_limits);
+ lua_push_key_at_index(L, preferfontthickness, noad_option_prefer_font_thickness);
+ lua_push_key_at_index(L, noruling, noad_option_no_ruling);
+ lua_push_key_at_index(L, shiftedsubscript, noad_option_shifted_sub_script);
+ lua_push_key_at_index(L, shiftedsuperscript, noad_option_shifted_super_script);
+ lua_push_key_at_index(L, shiftedsubprescript, noad_option_shifted_sub_pre_script);
+ lua_push_key_at_index(L, shiftedsuperprescript, noad_option_shifted_super_pre_script);
+ lua_push_key_at_index(L, unpacklist, noad_option_unpack_list);
+ lua_push_key_at_index(L, nocheck, noad_option_no_check);
+ lua_push_key_at_index(L, auto, noad_option_auto);
+ lua_push_key_at_index(L, unrolllist, noad_option_unroll_list);
+ lua_push_key_at_index(L, followedbyspace, noad_option_followed_by_space);
+ return 1;
+}
+
+static int texlib_getdiscoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 1);
+ lua_set_string_by_index(L, disc_option_normal_word, "normalword");
+ lua_set_string_by_index(L, disc_option_pre_word, "preword");
+ lua_set_string_by_index(L, disc_option_post_word, "postword");
+ return 1;
+}
+
+static int texlib_getlistanchorvalues(lua_State *L)
+{
+ lua_createtable(L, 14, 0);
+ lua_set_string_by_index(L, left_origin_anchor, "leftorigin");
+ lua_set_string_by_index(L, left_height_anchor, "leftheight");
+ lua_set_string_by_index(L, left_depth_anchor, "leftdepth");
+ lua_set_string_by_index(L, right_origin_anchor, "rightorigin");
+ lua_set_string_by_index(L, right_height_anchor, "rightheight");
+ lua_set_string_by_index(L, right_depth_anchor, "rightdepth");
+ lua_set_string_by_index(L, center_origin_anchor, "centerorigin");
+ lua_set_string_by_index(L, center_height_anchor, "centerheight");
+ lua_set_string_by_index(L, center_depth_anchor, "centerdepth");
+ lua_set_string_by_index(L, halfway_total_anchor, "halfwaytotal");
+ lua_set_string_by_index(L, halfway_height_anchor, "halfwayheight");
+ lua_set_string_by_index(L, halfway_depth_anchor, "halfwaydepth");
+ lua_set_string_by_index(L, halfway_left_anchor, "halfwayleft");
+ lua_set_string_by_index(L, halfway_right_anchor, "halfwayright");
+ return 1;
+}
+
+static int texlib_getlistsignvalues(lua_State *L)
+{
+ lua_createtable(L, 0, 2);
+ lua_set_string_by_index(L, negate_x_anchor, "negatex");
+ lua_set_string_by_index(L, negate_y_anchor, "negatey");
+ return 1;
+}
+
+static int texlib_getlistgeometryalues(lua_State *L)
+{
+ lua_createtable(L, 3, 0);
+ lua_set_string_by_index(L, offset_geometry, "offset");
+ lua_set_string_by_index(L, orientation_geometry, "orientation");
+ lua_set_string_by_index(L, anchor_geometry, "anchor");
+ return 1;
+}
+
+static int texlib_getautomigrationvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 3);
+ lua_push_key_at_index(L, mark, auto_migrate_mark);
+ lua_push_key_at_index(L, insert, auto_migrate_insert);
+ lua_push_key_at_index(L, adjust, auto_migrate_adjust);
+ lua_push_key_at_index(L, pre, auto_migrate_pre);
+ lua_push_key_at_index(L, post, auto_migrate_post);
+ return 1;
+}
+
+static int texlib_getflagvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 15);
+ lua_push_key_at_index(L, frozen, frozen_flag_bit);
+ lua_push_key_at_index(L, permanent, permanent_flag_bit);
+ lua_push_key_at_index(L, immutable, immutable_flag_bit);
+ lua_push_key_at_index(L, primitive, primitive_flag_bit);
+ lua_push_key_at_index(L, mutable, mutable_flag_bit);
+ lua_push_key_at_index(L, noaligned, noaligned_flag_bit);
+ lua_push_key_at_index(L, instance, instance_flag_bit);
+ lua_push_key_at_index(L, untraced, untraced_flag_bit);
+ lua_push_key_at_index(L, global, global_flag_bit);
+ lua_push_key_at_index(L, tolerant, tolerant_flag_bit);
+ lua_push_key_at_index(L, protected, protected_flag_bit);
+ lua_push_key_at_index(L, overloaded, overloaded_flag_bit);
+ lua_push_key_at_index(L, aliased, aliased_flag_bit);
+ lua_push_key_at_index(L, immediate, immediate_flag_bit);
+ lua_push_key_at_index(L, conditional, conditional_flag_bit);
+ lua_push_key_at_index(L, value, value_flag_bit);
+ return 1;
+}
+
+static int texlib_getspecialmathclassvalues(lua_State *L)
+{
+ lua_createtable(L, 0, 3);
+ lua_set_string_by_index(L, math_all_class, "all");
+ lua_set_string_by_index(L, math_begin_class, "begin");
+ lua_set_string_by_index(L, math_end_class, "end");
+ return 1;
+}
+
+static int texlib_getmathclassoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 19);
+ lua_set_string_by_index(L, no_pre_slack_class_option, "nopreslack");
+ lua_set_string_by_index(L, no_post_slack_class_option, "nopostslack");
+ lua_set_string_by_index(L, left_top_kern_class_option, "lefttopkern");
+ lua_set_string_by_index(L, right_top_kern_class_option, "righttopkern");
+ lua_set_string_by_index(L, left_bottom_kern_class_option, "leftbottomkern");
+ lua_set_string_by_index(L, right_bottom_kern_class_option, "rightbottomkern");
+ lua_set_string_by_index(L, look_ahead_for_end_class_option, "lookaheadforend");
+ lua_set_string_by_index(L, no_italic_correction_class_option, "noitaliccorrection");
+ lua_set_string_by_index(L, check_ligature_class_option, "checkligature");
+ lua_set_string_by_index(L, check_kern_pair_class_option, "checkkernpair");
+ lua_set_string_by_index(L, check_italic_correction_class_option, "checkitaliccorrection");
+ lua_set_string_by_index(L, flatten_class_option, "flatten");
+ lua_set_string_by_index(L, omit_penalty_class_option, "omitpenalty");
+ // lua_set_string_by_index(L, open_fence_class_option, "openfence");
+ // lua_set_string_by_index(L, close_fence_class_option, "closefence");
+ // lua_set_string_by_index(L, middle_fence_class_option, "middlefence");
+ lua_set_string_by_index(L, unpack_class_option, "unpack");
+ lua_set_string_by_index(L, raise_prime_option, "raiseprime");
+ lua_set_string_by_index(L, carry_over_left_top_kern_class_option, "carryoverlefttopkern");
+ lua_set_string_by_index(L, carry_over_left_bottom_kern_class_option, "carryoverleftbottomkern");
+ lua_set_string_by_index(L, carry_over_right_top_kern_class_option, "carryoverrighttopkern");
+ lua_set_string_by_index(L, carry_over_right_bottom_kern_class_option, "carryoverrightbottomkern");
+ lua_set_string_by_index(L, prefer_delimiter_dimensions_class_option, "preferdelimiterdimensions");
+ lua_set_string_by_index(L, auto_inject_class_option, "autoinject");
+ lua_set_string_by_index(L, remove_italic_correction_class_option, "removeitaliccorrection");
+ return 1;
+}
+
+static int texlib_getnormalizelinevalues(lua_State *L)
+{
+ lua_createtable(L, 2, 7);
+ lua_set_string_by_index(L, normalize_line_mode, "normalizeline");
+ lua_set_string_by_index(L, parindent_skip_mode, "parindentskip");
+ lua_set_string_by_index(L, swap_hangindent_mode, "swaphangindent");
+ lua_set_string_by_index(L, swap_parshape_mode, "swapparshape");
+ lua_set_string_by_index(L, break_after_dir_mode, "breakafterdir");
+ lua_set_string_by_index(L, remove_margin_kerns_mode, "removemarginkerns");
+ lua_set_string_by_index(L, clip_width_mode, "clipwidth");
+ lua_set_string_by_index(L, flatten_discretionaries_mode, "flattendiscretionaries");
+ lua_set_string_by_index(L, discard_zero_tab_skips_mode, "discardzerotabskips");
+ lua_set_string_by_index(L, flatten_h_leaders_mode, "flattenhleaders");
+ return 1;
+}
+
+static int texlib_getnormalizeparvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 0);
+ lua_set_string_by_index(L, normalize_par_mode, "normalizepar");
+ lua_set_string_by_index(L, flatten_v_leaders_mode, "flattenvleaders");
+ return 1;
+}
+
+static int texlib_geterrorvalues(lua_State *L)
+{
+ lua_createtable(L, 7, 1);
+ lua_set_string_by_index(L, normal_error_type, "normal");
+ lua_set_string_by_index(L, back_error_type, "back");
+ lua_set_string_by_index(L, insert_error_type, "insert");
+ lua_set_string_by_index(L, succumb_error_type, "succumb");
+ lua_set_string_by_index(L, eof_error_type, "eof");
+ lua_set_string_by_index(L, condition_error_type,"condition");
+ lua_set_string_by_index(L, runaway_error_type, "runaway");
+ lua_set_string_by_index(L, warning_error_type, "warning");
+ return 1;
+}
+
+static int texlib_getiovalues(lua_State *L) /* for reporting so we keep spaces */
+{
+ lua_createtable(L, 5, 1);
+ lua_set_string_by_index(L, io_initial_input_code, "initial");
+ lua_set_string_by_index(L, io_lua_input_code, "lua print");
+ lua_set_string_by_index(L, io_token_input_code, "scan token");
+ lua_set_string_by_index(L, io_token_eof_input_code, "scan token eof");
+ lua_set_string_by_index(L, io_tex_macro_code, "tex macro");
+ lua_set_string_by_index(L, io_file_input_code, "file");
+ return 1;
+}
+
+static int texlib_getfrozenparvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 20);
+ lua_set_string_by_index(L, par_hsize_category, "hsize");
+ lua_set_string_by_index(L, par_skip_category, "skip");
+ lua_set_string_by_index(L, par_hang_category, "hang");
+ lua_set_string_by_index(L, par_indent_category, "indent");
+ lua_set_string_by_index(L, par_par_fill_category, "parfill");
+ lua_set_string_by_index(L, par_adjust_category, "adjust");
+ lua_set_string_by_index(L, par_protrude_category, "protrude");
+ lua_set_string_by_index(L, par_tolerance_category, "tolerance");
+ lua_set_string_by_index(L, par_stretch_category, "stretch");
+ lua_set_string_by_index(L, par_looseness_category, "looseness");
+ lua_set_string_by_index(L, par_last_line_category, "lastline");
+ lua_set_string_by_index(L, par_line_penalty_category, "linepenalty");
+ lua_set_string_by_index(L, par_club_penalty_category, "clubpenalty");
+ lua_set_string_by_index(L, par_widow_penalty_category, "widowpenalty");
+ lua_set_string_by_index(L, par_display_penalty_category, "displaypenalty");
+ lua_set_string_by_index(L, par_broken_penalty_category, "brokenpenalty");
+ lua_set_string_by_index(L, par_demerits_category, "demerits");
+ lua_set_string_by_index(L, par_shape_category, "shape");
+ lua_set_string_by_index(L, par_line_category, "line");
+ lua_set_string_by_index(L, par_hyphenation_category, "hyphenation");
+ lua_set_string_by_index(L, par_shaping_penalty_category, "shapingpenalty");
+ lua_set_string_by_index(L, par_orphan_penalty_category, "orphanpenalty");
+ /* lua_set_string_by_index(L, par_all_category, "all"); */
+ return 1;
+}
+
+static int texlib_getshapingpenaltiesvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 2);
+ lua_push_key_at_index(L, interlinepenalty, inter_line_penalty_shaping);
+ lua_push_key_at_index(L, widowpenalty, widow_penalty_shaping);
+ lua_push_key_at_index(L, clubpenalty, club_penalty_shaping);
+ lua_push_key_at_index(L, brokenpenalty, broken_penalty_shaping);
+ return 1;
+}
+
+
+static int texlib_getprimitiveorigins(lua_State *L)
+{
+ lua_createtable(L, 2, 1);
+ lua_push_key_at_index(L, tex, tex_command);
+ lua_push_key_at_index(L, etex, etex_command);
+ lua_push_key_at_index(L, luatex, luatex_command);
+ return 1;
+}
+
+static int texlib_getlargestusedmark(lua_State* L)
+{
+ lua_pushinteger(L, lmt_mark_state.mark_data.ptr);
+ return 1;
+}
+
+static int texlib_getoutputactive(lua_State* L)
+{
+ lua_pushboolean(L, lmt_page_builder_state.output_active);
+ return 1;
+}
+
+/*tex Moved from lmtnodelib to here. */
+
+int lmt_push_info_keys(lua_State *L, value_info *values)
+{
+ lua_newtable(L);
+ for (int i = 0; values[i].name; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, values[i].lua);
+ lua_rawseti(L, -2, i);
+ }
+ return 1;
+}
+
+int lmt_push_info_values(lua_State *L, value_info *values)
+{
+ lua_newtable(L);
+ for (int i = 0; values[i].name; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, values[i].lua);
+ lua_rawseti(L, -2, values[i].value);
+ }
+ return 1;
+}
+
+static int texlib_getgroupvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.group_code_values);
+}
+
+static int texlib_getmathparametervalues(lua_State *L)
+{
+ return lmt_push_info_keys(L, lmt_interface.math_parameter_values);
+}
+
+static int texlib_getmathstylevalues(lua_State* L)
+{
+ return lmt_push_info_values(L, lmt_interface.math_style_values);
+}
+
+static int texlib_getpacktypevalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.pack_type_values);
+}
+
+static int texlib_getparcontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.par_context_values);
+}
+
+static int texlib_getpagecontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.page_context_values);
+}
+
+static int texlib_getappendlinecontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.append_line_context_values);
+}
+
+static int texlib_getalignmentcontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.alignment_context_values);
+}
+
+static int texlib_getparbeginvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.par_begin_values);
+}
+
+static int texlib_getparmodevalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.par_mode_values);
+}
+
+static int texlib_getmathstylenamevalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.math_style_name_values);
+}
+
+static int texlib_getmathvariantvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.math_style_variant_values);
+}
+
+// static int texlib_getmathflattenvalues(lua_State *L)
+// {
+// lua_createtable(L, 2, 3);
+// lua_set_string_by_index(L, math_flatten_ordinary, "ord");
+// lua_set_string_by_index(L, math_flatten_binary, "bin");
+// lua_set_string_by_index(L, math_flatten_relation, "rel");
+// lua_set_string_by_index(L, math_flatten_punctuation, "punct");
+// lua_set_string_by_index(L, math_flatten_inner, "inner");
+// return 1;
+// }
+
+static int texlib_getdiscstatevalues(lua_State *L)
+{
+ lua_createtable(L, 4, 1);
+ lua_set_string_by_index(L, glyph_discpart_unset, "unset");
+ lua_set_string_by_index(L, glyph_discpart_pre, "pre");
+ lua_set_string_by_index(L, glyph_discpart_post, "post");
+ lua_set_string_by_index(L, glyph_discpart_replace, "replace");
+ lua_set_string_by_index(L, glyph_discpart_always, "always");
+ return 1;
+}
+
+static int texlib_getmathcontrolvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 19);
+ lua_set_string_by_index(L, math_control_use_font_control, "usefontcontrol");
+ lua_set_string_by_index(L, math_control_over_rule, "overrule");
+ lua_set_string_by_index(L, math_control_under_rule, "underrule");
+ lua_set_string_by_index(L, math_control_radical_rule, "radicalrule");
+ lua_set_string_by_index(L, math_control_fraction_rule, "fractionrule");
+ lua_set_string_by_index(L, math_control_accent_skew_half, "accentskewhalf");
+ lua_set_string_by_index(L, math_control_accent_skew_apply, "accentskewapply");
+ lua_set_string_by_index(L, math_control_apply_ordinary_kern_pair, "applyordinarykernpair");
+ lua_set_string_by_index(L, math_control_apply_vertical_italic_kern, "applyverticalitalickern");
+ lua_set_string_by_index(L, math_control_apply_ordinary_italic_kern, "applyordinaryitalickern");
+ lua_set_string_by_index(L, math_control_apply_char_italic_kern, "applycharitalickern");
+ lua_set_string_by_index(L, math_control_rebox_char_italic_kern, "reboxcharitalickern");
+ lua_set_string_by_index(L, math_control_apply_boxed_italic_kern, "applyboxeditalickern");
+ lua_set_string_by_index(L, math_control_staircase_kern, "staircasekern");
+ lua_set_string_by_index(L, math_control_apply_text_italic_kern, "applytextitalickern");
+ lua_set_string_by_index(L, math_control_check_text_italic_kern, "checktextitalickern");
+ lua_set_string_by_index(L, math_control_check_space_italic_kern, "checkspaceitalickern");
+ lua_set_string_by_index(L, math_control_apply_script_italic_kern, "applyscriptitalickern");
+ lua_set_string_by_index(L, math_control_analyze_script_nucleus_char, "analyzescriptnucleuschar");
+ lua_set_string_by_index(L, math_control_analyze_script_nucleus_list, "analyzescriptnucleuslist");
+ lua_set_string_by_index(L, math_control_analyze_script_nucleus_box, "analyzescriptnucleusbox");
+ return 1;
+}
+
+static int texlib_gettextcontrolvalues(lua_State *L)
+{
+ lua_createtable(L, 1, 0);
+ lua_set_string_by_index(L, text_control_collapse_hyphens, "collapsehyphens");
+ return 1;
+}
+
+/* relatively new */
+
+static int texlib_getinsertdistance(lua_State *L)
+{
+ return texlib_aux_push_glue(L, tex_get_insert_distance(lmt_tointeger(L, 1)));
+}
+
+static int texlib_getinsertmultiplier(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_multiplier(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertlimit(lua_State *L)
+{
+ tex_set_insert_limit(lmt_tointeger(L, 1), lmt_opthalfword(L, 2, 0));
+ return 0;
+}
+
+static int texlib_setinsertdistance(lua_State *L)
+{
+ tex_set_insert_distance(lmt_tointeger(L, 1), texlib_aux_make_glue(L, lua_gettop(L), 2));
+ return 0;
+}
+
+static int texlib_setinsertmultiplier(lua_State *L)
+{
+ tex_set_insert_multiplier(lmt_tointeger(L, 1), lmt_tohalfword(L, 2));
+ return 0;
+}
+
+static int texlib_setinsertlimit(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_limit(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertheight(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_height(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertdepth(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_depth(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertwidth(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_width(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertcontent(lua_State *L)
+{
+ halfword index = lmt_tointeger(L, 1);
+ lmt_node_list_to_lua(L, tex_get_insert_content(index));
+ tex_set_insert_content(index, null);
+ return 1;
+}
+
+static int texlib_setinsertcontent(lua_State *L)
+{
+ halfword index = lmt_tointeger(L, 1);
+ tex_flush_node(tex_get_insert_content(index));
+ tex_set_insert_content(index, lmt_node_list_from_lua(L, 2));
+ return 0;
+}
+
+static int texlib_getlocalbox(lua_State *L)
+{
+ int location = lmt_tointeger(L, 1);
+ if (is_valid_local_box_code(location)) {
+ lmt_node_list_to_lua(L, tex_get_local_boxes(location));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_setlocalbox(lua_State *L)
+{
+ int location = lmt_tointeger(L, 1);
+ if (is_valid_local_box_code(location)) {
+ tex_set_local_boxes(lmt_node_list_from_lua(L, 1), location);
+ }
+ return 0;
+}
+
+static int texlib_pushsavelevel(lua_State *L)
+{
+ (void) L;
+ tex_new_save_level(lua_group);
+ return 0;
+}
+
+static int texlib_popsavelevel(lua_State *L)
+{
+ (void) L;
+ // tex_off_save();
+ tex_unsave();
+ return 0;
+}
+
+/*tex
+ When testing all these math finetuning options we needed to typeset the box contents and
+ instead of filtering from the log or piping the log to a file, this more ssd friendly
+ feature was added. The first argument is a box (id) and the second an optional detail
+ directive. This is (currently) the only case where we write to a \LUA\ buffer, but I
+ might add variants for a macro and tokenlist at some point (less interesting).
+*/
+
+/* till here */
+
+static const struct luaL_Reg texlib_function_list[] = {
+ { "write", texlib_write },
+ { "print", texlib_print },
+ { "sprint", texlib_sprint },
+ { "mprint", texlib_mprint },
+ { "tprint", texlib_tprint },
+ { "cprint", texlib_cprint },
+ { "isprintable", texlib_isprintable },
+ { "pushlocal", texlib_pushlocal },
+ { "poplocal", texlib_poplocal },
+ { "runlocal", texlib_runlocal },
+ { "runstring", texlib_runstring },
+ { "quitlocal", texlib_quitlocal },
+ { "expandasvalue", texlib_expandasvalue }, /* experiment */
+ { "error", texlib_error },
+ { "set", texlib_set },
+ { "get", texlib_get },
+ { "getregisterindex", texlib_get_register_index },
+ { "isdimen", texlib_isdimen },
+ { "setdimen", texlib_setdimen },
+ { "getdimen", texlib_getdimen },
+ { "isskip", texlib_isskip },
+ { "setskip", texlib_setskip },
+ { "getskip", texlib_getskip },
+ { "isglue", texlib_isglue },
+ { "setglue", texlib_setglue },
+ { "getglue", texlib_getglue },
+ { "ismuskip", texlib_ismuskip },
+ { "setmuskip", texlib_setmuskip },
+ { "getmuskip", texlib_getmuskip },
+ { "ismuglue", texlib_ismuglue },
+ { "setmuglue", texlib_setmuglue },
+ { "getmuglue", texlib_getmuglue },
+ { "isattribute", texlib_isattribute },
+ { "setattribute", texlib_setattribute },
+ { "getattribute", texlib_getattribute },
+ { "iscount", texlib_iscount },
+ { "setcount", texlib_setcount },
+ { "getcount", texlib_getcount },
+ { "istoks", texlib_istoks },
+ { "settoks", texlib_settoks },
+ { "scantoks", texlib_scantoks },
+ { "gettoks", texlib_gettoks },
+ { "getmark", texlib_getmark },
+ { "isbox", texlib_isbox },
+ { "setbox", texlib_setbox },
+ { "getbox", texlib_getbox },
+ { "splitbox", texlib_splitbox },
+ { "setlist", texlib_setlist },
+ { "getlist", texlib_getlist },
+ { "setnest", texlib_setnest }, /* only a message */
+ { "getnest", texlib_getnest },
+ { "setcatcode", texlib_setcatcode },
+ { "getcatcode", texlib_getcatcode },
+ { "setdelcode", texlib_setdelcode },
+ { "getdelcode", texlib_getdelcode },
+ { "getdelcodes", texlib_getdelcodes },
+ { "sethccode", texlib_sethccode },
+ { "gethccode", texlib_gethccode },
+ { "sethmcode", texlib_sethmcode },
+ { "gethmcode", texlib_gethmcode },
+ { "setlccode", texlib_setlccode },
+ { "getlccode", texlib_getlccode },
+ { "setmathcode", texlib_setmathcode },
+ { "getmathcode", texlib_getmathcode },
+ { "getmathcodes", texlib_getmathcodes },
+ { "setsfcode", texlib_setsfcode },
+ { "getsfcode", texlib_getsfcode },
+ { "setuccode", texlib_setuccode },
+ { "getuccode", texlib_getuccode },
+ { "round", texlib_round },
+ { "scale", texlib_scale },
+ { "sp", texlib_toscaled },
+ { "toscaled", texlib_toscaled },
+ { "tonumber", texlib_tonumber },
+ { "fontname", texlib_getfontname },
+ { "fontidentifier", texlib_getfontidentifier },
+ { "getfontoffamily", texlib_getfontoffamily },
+ { "number", texlib_getnumber },
+ // { "dimension", texlib_getdimension },
+ { "romannumeral", texlib_getromannumeral },
+ { "definefont", texlib_definefont },
+ { "hashtokens", texlib_hashtokens },
+ { "primitives", texlib_primitives },
+ { "extraprimitives", texlib_extraprimitives },
+ { "enableprimitives", texlib_enableprimitives },
+ { "shipout", texlib_shipout },
+ { "badness", texlib_badness },
+ { "setmath", texlib_setmath },
+ { "getmath", texlib_getmath },
+ { "linebreak", texlib_linebreak },
+ { "preparelinebreak", texlib_preparelinebreak },
+ { "resetparagraph", texlib_resetparagraph },
+ { "showcontext", texlib_showcontext },
+ { "triggerbuildpage", texlib_triggerbuildpage },
+ { "gethelptext", texlib_gethelptext },
+ { "getpagestate", texlib_getpagestate },
+ { "getlocallevel", texlib_getlocallevel },
+ { "setinputstatemode", texlib_setinputstatemode },
+ { "getinputstatemode", texlib_getinputstatemode },
+ { "setinputstatefile", texlib_setinputstatefile },
+ { "getinputstatefile", texlib_getinputstatefile },
+ { "forceinputstatefile", texlib_forceinputstatefile },
+ { "forceinputstateline", texlib_forceinputstateline },
+ { "setinputstateline", texlib_setinputstateline },
+ { "getinputstateline", texlib_getinputstateline },
+ { "forcehmode", texlib_forcehmode },
+ { "gettextdir", texlib_gettextdir },
+ { "settextdir", texlib_settextdir },
+ { "getlinedir", texlib_gettextdir }, /* we're nice */
+ { "setlinedir", texlib_setlinedir },
+ { "getmathdir", texlib_getmathdir },
+ { "setmathdir", texlib_setmathdir },
+ { "getpardir", texlib_getpardir },
+ { "setpardir", texlib_setpardir },
+ { "getboxdir", texlib_getboxdir },
+ { "setboxdir", texlib_setboxdir },
+ { "getinteraction", texlib_getinteraction },
+ { "setinteraction", texlib_setinteraction },
+ { "getglyphdata", texlib_getglyphdata },
+ { "setglyphdata", texlib_setglyphdata },
+ { "getglyphstate", texlib_getglyphstate },
+ { "setglyphstate", texlib_setglyphstate },
+ { "getglyphscript", texlib_getglyphscript },
+ { "setglyphscript", texlib_setglyphscript },
+ { "getglyphscales", texlib_getglyphscales },
+ { "fatalerror", texlib_fatalerror },
+ { "lastnodetype", texlib_lastnodetype },
+ { "chardef", texlib_chardef },
+ { "mathchardef", texlib_mathchardef },
+ { "integerdef", texlib_setintegervalue },
+ { "setintegervalue", texlib_setintegervalue },
+ { "getintegervalue", texlib_getintegervalue },
+ { "dimensiondef", texlib_setdimensionvalue },
+ { "setdimensionvalue", texlib_setdimensionvalue },
+ { "getdimensionvalue", texlib_getdimensionvalue },
+ { "getmode", texlib_getmode },
+ { "getmodevalues", texlib_getmodevalues },
+ { "getrunstatevalues", texlib_getrunstatevalues },
+ { "setrunstate", texlib_setrunstate },
+ { "gethyphenationvalues", texlib_gethyphenationvalues },
+ { "getglyphoptionvalues", texlib_getglyphoptionvalues },
+ { "getnoadoptionvalues", texlib_getnoadoptionvalues },
+ { "getdiscoptionvalues", texlib_getdiscoptionvalues },
+ { "getlistanchorvalues", texlib_getlistanchorvalues },
+ { "getlistsignvalues", texlib_getlistsignvalues },
+ { "getlistgeometryvalues", texlib_getlistgeometryalues },
+ { "getdiscstatevalues", texlib_getdiscstatevalues },
+ { "getmathparametervalues", texlib_getmathparametervalues },
+ { "getmathstylenamevalues", texlib_getmathstylenamevalues },
+ { "getmathstylevalues", texlib_getmathstylevalues },
+ { "getmathvariantvalues", texlib_getmathvariantvalues },
+ /* {"getmathflattenvalues", texlib_getmathflattenvalues }, */
+ { "getmathcontrolvalues", texlib_getmathcontrolvalues },
+ { "gettextcontrolvalues", texlib_gettextcontrolvalues },
+ { "getpacktypevalues", texlib_getpacktypevalues },
+ { "getgroupvalues", texlib_getgroupvalues },
+ { "getparcontextvalues", texlib_getparcontextvalues },
+ { "getpagecontextvalues", texlib_getpagecontextvalues },
+ { "getappendlinecontextvalues", texlib_getappendlinecontextvalues },
+ { "getalignmentcontextvalues", texlib_getalignmentcontextvalues },
+ { "getparbeginvalues", texlib_getparbeginvalues },
+ { "getparmodevalues", texlib_getparmodevalues },
+ { "getautomigrationvalues", texlib_getautomigrationvalues },
+ { "getflagvalues", texlib_getflagvalues },
+ { "getmathclassoptionvalues", texlib_getmathclassoptionvalues },
+ { "getnormalizelinevalues", texlib_getnormalizelinevalues },
+ { "getnormalizeparvalues", texlib_getnormalizeparvalues },
+ { "geterrorvalues", texlib_geterrorvalues },
+ { "getiovalues", texlib_getiovalues },
+ { "getprimitiveorigins", texlib_getprimitiveorigins },
+ { "getfrozenparvalues", texlib_getfrozenparvalues },
+ { "getshapingpenaltiesvalues", texlib_getshapingpenaltiesvalues },
+ { "getspecialmathclassvalues", texlib_getspecialmathclassvalues },
+ { "getlargestusedmark", texlib_getlargestusedmark },
+ { "getoutputactive", texlib_getoutputactive },
+ /* experiment (metafun update) */
+ { "shiftparshape", texlib_shiftparshape },
+ { "snapshotpar", texlib_snapshotpar },
+ { "getparstate", texlib_getparstate },
+ /* */
+ { "getinsertdistance", texlib_getinsertdistance },
+ { "getinsertmultiplier", texlib_getinsertmultiplier },
+ { "getinsertlimit", texlib_getinsertlimit },
+ { "getinsertheight", texlib_getinsertheight },
+ { "getinsertdepth", texlib_getinsertdepth },
+ { "getinsertwidth", texlib_getinsertwidth },
+ { "getinsertcontent", texlib_getinsertcontent },
+ { "setinsertdistance", texlib_setinsertdistance },
+ { "setinsertmultiplier", texlib_setinsertmultiplier },
+ { "setinsertlimit", texlib_setinsertlimit },
+ { "setinsertcontent", texlib_setinsertcontent },
+ { "getlocalbox", texlib_getlocalbox },
+ { "setlocalbox", texlib_setlocalbox },
+ /* */
+ { "pushsavelevel", texlib_pushsavelevel },
+ { "popsavelevel", texlib_popsavelevel },
+ /* */
+ { NULL, NULL },
+};
+
+# define defineindexers(name) \
+ static int texlib_index_##name (lua_State *L) { lua_remove(L, 1); return texlib_get##name(L); } \
+ static int texlib_newindex_##name(lua_State *L) { lua_remove(L, 1); return texlib_set##name(L); }
+
+defineindexers(attribute)
+defineindexers(skip)
+defineindexers(glue)
+defineindexers(muskip)
+defineindexers(muglue)
+defineindexers(dimen)
+defineindexers(count)
+defineindexers(toks)
+defineindexers(box)
+defineindexers(sfcode)
+defineindexers(lccode)
+defineindexers(uccode)
+defineindexers(hccode)
+defineindexers(hmcode)
+defineindexers(catcode)
+defineindexers(mathcode)
+defineindexers(delcode)
+defineindexers(list)
+defineindexers(nest)
+
+/*tex
+ At some point the |__index| and |__newindex |below will go away so that we no longer get
+ interferences when we extedn the |tex| table.
+*/
+
+int luaopen_tex(lua_State *L)
+{
+ texlib_aux_initialize();
+ /* */
+ lua_newtable(L);
+ luaL_setfuncs(L, texlib_function_list, 0);
+ lmt_make_table(L, "attribute", TEX_METATABLE_ATTRIBUTE, texlib_index_attribute, texlib_newindex_attribute);
+ lmt_make_table(L, "skip", TEX_METATABLE_SKIP, texlib_index_skip, texlib_newindex_skip);
+ lmt_make_table(L, "glue", TEX_METATABLE_GLUE, texlib_index_glue, texlib_newindex_glue);
+ lmt_make_table(L, "muskip", TEX_METATABLE_MUSKIP, texlib_index_muskip, texlib_newindex_muskip);
+ lmt_make_table(L, "muglue", TEX_METATABLE_MUGLUE, texlib_index_muglue, texlib_newindex_muglue);
+ lmt_make_table(L, "dimen", TEX_METATABLE_DIMEN, texlib_index_dimen, texlib_newindex_dimen);
+ lmt_make_table(L, "count", TEX_METATABLE_COUNT, texlib_index_count, texlib_newindex_count);
+ lmt_make_table(L, "toks", TEX_METATABLE_TOKS, texlib_index_toks, texlib_newindex_toks);
+ lmt_make_table(L, "box", TEX_METATABLE_BOX, texlib_index_box, texlib_newindex_box);
+ lmt_make_table(L, "sfcode", TEX_METATABLE_SFCODE, texlib_index_sfcode, texlib_newindex_sfcode);
+ lmt_make_table(L, "lccode", TEX_METATABLE_LCCODE, texlib_index_lccode, texlib_newindex_lccode);
+ lmt_make_table(L, "uccode", TEX_METATABLE_UCCODE, texlib_index_uccode, texlib_newindex_uccode);
+ lmt_make_table(L, "hccode", TEX_METATABLE_HCCODE, texlib_index_hccode, texlib_newindex_hccode);
+ lmt_make_table(L, "hmcode", TEX_METATABLE_HMCODE, texlib_index_hmcode, texlib_newindex_hmcode);
+ lmt_make_table(L, "catcode", TEX_METATABLE_CATCODE, texlib_index_catcode, texlib_newindex_catcode);
+ lmt_make_table(L, "mathcode", TEX_METATABLE_MATHCODE, texlib_index_mathcode, texlib_newindex_mathcode);
+ lmt_make_table(L, "delcode", TEX_METATABLE_DELCODE, texlib_index_delcode, texlib_newindex_delcode);
+ lmt_make_table(L, "lists", TEX_METATABLE_LISTS, texlib_index_list, texlib_newindex_list);
+ lmt_make_table(L, "nest", TEX_METATABLE_NEST, texlib_index_nest, texlib_newindex_nest);
+ texlib_aux_init_nest_lib(L);
+ /*tex make the meta entries and fetch it back */
+ luaL_newmetatable(L, TEX_METATABLE_TEX);
+ lua_pushstring(L, "__index");
+ lua_pushcfunction(L, texlib_index);
+ lua_settable(L, -3);
+ lua_pushstring(L, "__newindex");
+ lua_pushcfunction(L, texlib_newindex);
+ lua_settable(L, -3);
+ lua_setmetatable(L, -2);
+ return 1;
+}