lmtcallbacklib.c /size: 20 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    These are the supported callbacks (by name). This list must have the same size and order as the
10    array in |luatexcallbackids.h|! We could have kept the names private here and maybe they will
11    become that again. On the other hand we can now use them in reports.
12
13*/
14
15callback_state_info lmt_callback_state = {
16    .metatable_id = 0,
17    .padding      = 0,
18    .values       = { 0 },
19};
20
21/* todo: use lua keywords instead */
22
23static const char *callbacklib_names[total_callbacks] = {
24    "", /*tex empty on purpose */
25    "find_log_file",
26    "find_format_file",
27    "open_data_file",
28    "process_jobname",
29    "start_run",
30    "stop_run",
31    "define_font",
32    "quality_font",
33    "pre_output_filter",
34    "buildpage_filter",
35    "hpack_filter",
36    "vpack_filter",
37    "hyphenate",
38    "ligaturing",
39    "kerning",
40    "glyph_run",
41    "pre_linebreak_filter",
42    "linebreak_filter",
43    "post_linebreak_filter",
44    "append_to_vlist_filter",
45    "alignment_filter",
46    "local_box_filter",
47    "packed_vbox_filter",
48    "mlist_to_hlist",
49    "pre_dump",
50    "start_file",
51    "stop_file",
52    "intercept_tex_error",
53    "intercept_lua_error",
54    "show_error_message",
55    "show_warning_message",
56    "hpack_quality",
57    "vpack_quality",
58    "show_break",
59    "show_build",
60    "insert_par",
61    "append_line_filter",
62    "insert_distance",
63 /* "fire_up_output", */
64    "wrapup_run",
65    "begin_paragraph",
66    "paragraph_context",
67 /* "get_math_char", */
68    "math_rule",
69    "make_extensible",
70    "register_extensible",
71    "show_whatsit",
72    "get_attribute",
73    "get_noad_class",
74    "get_math_dictionary",
75    "show_lua_call",
76    "trace_memory",
77    "handle_overload",
78    "missing_character",
79    "process_character",
80    "linebreak_quality",
81    "paragraph_pass",
82    "handle_uleader",
83    "italic_correction",
84};
85
86/*tex
87
88    This is the generic callback handler, inspired by the one described in the \LUA\ manual(s). It
89    got adapted over time and can also handle some userdata arguments.
90
91*/
92
93static int callbacklib_aux_run(lua_State *L, int id, int special, const char *values, va_list vl, int top, int base)
94{
95    int narg = 0;
96    int nres = 0;
97    if (special == 2) {
98        /*tex copy the enclosing table */
99        lua_pushvalue(L, -2);
100    }
101    for (narg = 0; *values; narg++) {
102        switch (*values++) {
103            case callback_boolean_key:
104                /*tex A boolean: */
105                lua_pushboolean(L, va_arg(vl, int));
106                break;
107            case callback_charnum_key:
108                /*tex A (8 bit) character: */
109                {
110                    char cs = (char) va_arg(vl, int);
111                    lua_pushlstring(L, &cs, 1);
112                }
113                break;
114            case callback_integer_key:
115                /*tex An integer: */
116                lua_pushinteger(L, va_arg(vl, int));
117                break;
118            case callback_line_key:
119                /*tex A buffer section, with implied start: */
120                lua_pushlstring(L, (char *) (lmt_fileio_state.io_buffer + lmt_fileio_state.io_first), (size_t) va_arg(vl, int));
121                break;
122            case callback_strnumber_key:
123                /*tex A \TEX\ string (indicated by an index): */
124                {
125                    size_t len;
126                    const char *s = tex_makeclstring(va_arg(vl, int), &len);
127                    lua_pushlstring(L, s, len);
128                }
129                break;
130            case callback_lstring_key:
131                /*tex A \LUA\ string: */
132                {
133                    lstring *lstr = va_arg(vl, lstring *);
134                    lua_pushlstring(L, (const char *) lstr->s, lstr->l);
135                }
136                break;
137            case callback_node_key:
138                /*tex A \TEX\ node: */
139                lmt_push_node_fast(L, va_arg(vl, int));
140                break;
141            case callback_string_key:
142                /*tex A \CCODE\ string: */
143                lua_pushstring(L, va_arg(vl, char *));
144                break;
145            case '-':
146                narg--;
147                break;
148            case '>':
149                goto ENDARGS;
150            default:
151                ;
152        }
153    }
154  ENDARGS:
155    nres = (int) strlen(values);
156    if (special == 1) {
157        nres++;
158    } else if (special == 2) {
159        narg++;
160    }
161    lmt_lua_state.saved_callback_count++;
162    {
163        int i = lua_pcall(L, narg, nres, base);
164        if (i) {
165            /*tex
166                We can't be more precise here as it could be called before \TEX\ initialization is
167                complete.
168            */
169            lua_remove(L, top + 2);
170            lmt_error(L, "run callback", id, (i == LUA_ERRRUN ? 0 : 1));
171            lua_settop(L, top);
172            return 0;
173        }
174    }
175    if (nres == 0) {
176        return 1;
177    }
178    nres = -nres;
179    while (*values) {
180        int t = lua_type(L, nres);
181        switch (*values++) {
182            case callback_boolean_key:
183                switch (t) {
184                    case LUA_TBOOLEAN:
185                        *va_arg(vl, int *) = lua_toboolean(L, nres);
186                        break;
187                    case LUA_TNIL:
188                        *va_arg(vl, int *) = 0;
189                        break;
190                    default:
191                        return tex_formatted_error("callback", "boolean or nil expected, false or nil, not: %s\n", lua_typename(L, t));
192                }
193                break;
194            /*
195            case callback_charnum_key:
196                break;
197            */
198            case callback_integer_key:
199                switch (t) {
200                    case LUA_TNUMBER:
201                        *va_arg(vl, int *) = lmt_tointeger(L, nres);
202                        break;
203                    default:
204                        return tex_formatted_error("callback", "number expected, not: %s\n", lua_typename(L, t));
205                }
206                break;
207            case callback_line_key:
208                switch (t) {
209                    case LUA_TSTRING:
210                        {
211                            size_t len;
212                            const char *s = lua_tolstring(L, nres, &len);
213                            if (s && (len > 0)) {
214                                int *bufloc = va_arg(vl, int *);
215                                int ret = *bufloc;
216                                if (tex_room_in_buffer(ret + (int) len)) {
217                                    strncpy((char *) (lmt_fileio_state.io_buffer + ret), s, len);
218                                    *bufloc += (int) len;
219                                    /* while (len--) {  fileio_state.io_buffer[(*bufloc)++] = *s++; } */
220                                    while ((*bufloc) - 1 > ret && lmt_fileio_state.io_buffer[(*bufloc) - 1] == ' ') {
221                                        (*bufloc)--;
222                                    }
223                               } else {
224                                    return 0;
225                               }
226                            }
227                            /*tex We can assume no more arguments! */
228                        }
229                        break;
230                    case LUA_TNIL:
231                        /*tex We assume no more arguments! */
232                        return 0;
233                    default:
234                        return tex_formatted_error("callback", "string or nil expected, not: %s\n", lua_typename(L, t));
235                }
236                break;
237            case callback_strnumber_key:
238                switch (t) {
239                    case LUA_TSTRING:
240                        {
241                            size_t len;
242                            const char *s = lua_tolstring(L, nres, &len);
243                            if (s) {
244                                *va_arg(vl, int *) = tex_maketexlstring(s, len);
245                            } else {
246                                /*tex |len| can be zero */
247                                *va_arg(vl, int *) = 0;
248                            }
249                        }
250                        break;
251                    default:
252                        return tex_formatted_error("callback", "string expected, not: %s\n", lua_typename(L, t));
253               }
254                break;
255            case callback_lstring_key:
256                switch (t) {
257                    case LUA_TSTRING:
258                        {
259                            size_t len;
260                            const char *s = lua_tolstring(L, nres, &len);
261                            if (s && len > 0) {
262                                lstring *lsret = lmt_memory_malloc(sizeof(lstring));
263                                if (lsret) {
264                                    lsret->s = lmt_memory_malloc((unsigned) (len + 1));
265                                    if (lsret->s) {
266                                        (void) memcpy(lsret->s, s, (len + 1));
267                                        lsret->l = len;
268                                        *va_arg(vl, lstring **) = lsret;
269                                    } else {
270                                        *va_arg(vl, int *) = 0;
271                                    }
272                                } else {
273                                    *va_arg(vl, int *) = 0;
274                                }
275                            } else {
276                                /*tex |len| can be zero */
277                                *va_arg(vl, int *) = 0;
278                            }
279                        }
280                        break;
281                    default:
282                        return tex_formatted_error("callback", "string expected, not: %s\n", lua_typename(L, t));
283                }
284                break;
285            case callback_node_key:
286                switch (t) {
287                    case LUA_TUSERDATA:
288                        *va_arg(vl, int *) = lmt_check_isnode(L, nres);
289                        break;
290                    default:
291                        *va_arg(vl, int *) = null;
292                        break;
293                }
294                break;
295            case callback_string_key:
296                switch (t) {
297                    case LUA_TSTRING:
298                        {
299                            size_t len;
300                            const char *s = lua_tolstring(L, nres, &len);
301                            if (s) {
302                                char *ss = lmt_memory_malloc((unsigned) (len + 1));
303                                if (ss) {
304                                    memcpy(ss, s, (len + 1));
305                                 }
306                                *va_arg(vl, char **) = ss;
307                            } else {
308                                *va_arg(vl, char **) = NULL;
309                             // *va_arg(vl, int *) = 0;
310                            }
311                        }
312                        break;
313                    default:
314                        return tex_formatted_error("callback", "string expected, not: %s\n", lua_typename(L, t));
315                }
316                break;
317            case callback_result_s_key:
318                switch (t) {
319                    case LUA_TNIL:
320                        *va_arg(vl, int *) = 0;
321                        break;
322                    case LUA_TBOOLEAN:
323                        if (lua_toboolean(L, nres) == 0) {
324                            *va_arg(vl, int *) = 0;
325                            break;
326                        } else {
327                            return tex_formatted_error("callback", "string, false or nil expected, not: %s\n", lua_typename(L, t));
328                        }
329                    case LUA_TSTRING:
330                        {
331                            size_t len;
332                            const char *s = lua_tolstring(L, nres, &len);
333                            if (s) {
334                                char *ss = lmt_memory_malloc((unsigned) (len + 1));
335                                if (ss) {
336                                    memcpy(ss, s, (len + 1));
337                                    *va_arg(vl, char **) = ss;
338                                } else {
339                                   *va_arg(vl, char **) = NULL;
340                                // *va_arg(vl, int *) = 0;
341                                }
342                            } else {
343                                *va_arg(vl, char **) = NULL;
344                             // *va_arg(vl, int *) = 0;
345                            }
346                        }
347                        break;
348                    default:
349                        return tex_formatted_error("callback", "string, false or nil expected, not: %s\n", lua_typename(L, t));
350                }
351                break;
352            case callback_result_i_key:
353                switch (t) {
354                    case LUA_TNUMBER:
355                        *va_arg(vl, int *) = lmt_tointeger(L, nres);
356                        break;
357                    default:
358                     /* *va_arg(vl, int *) = 0; */ /*tex We keep the value! */
359                        break;
360                }
361                break;
362            default:
363                return tex_formatted_error("callback", "invalid value type returned\n");
364        }
365        nres++;
366    }
367    return 1;
368}
369
370/*tex
371    Especially the \IO\ related callbacks are registered once, for instance when a file is opened,
372    and (re)used later. These are dealt with here.
373*/
374
375int lmt_run_saved_callback_close(lua_State *L, int r)
376{
377    int ret = 0;
378    int stacktop = lua_gettop(L);
379    lua_rawgeti(L, LUA_REGISTRYINDEX, r);
380    lua_push_key(close);
381    if (lua_rawget(L, -2) == LUA_TFUNCTION) {
382        ret = lua_pcall(L, 0, 0, 0);
383        if (ret) {
384            return tex_formatted_error("lua", "error in close file callback") - 1;
385        }
386    }
387    lua_settop(L, stacktop);
388    return ret;
389}
390
391int lmt_run_saved_callback_line(lua_State *L, int r, int firstpos)
392{
393    int ret = -1; /* -1 is error, >= 0 is buffer length */
394    int stacktop = lua_gettop(L);
395    lua_rawgeti(L, LUA_REGISTRYINDEX, r);
396    lua_push_key(reader);
397    if (lua_rawget(L, -2) == LUA_TFUNCTION) {
398        lua_pushvalue(L, -2);
399        lmt_lua_state.file_callback_count++;
400        ret = lua_pcall(L, 1, 1, 0);
401        if (ret) {
402            ret = tex_formatted_error("lua", "error in read line callback") - 1;
403        } else if (lua_type(L, -1) == LUA_TSTRING) {
404            size_t len;
405            const char *s = lua_tolstring(L, -1, &len);
406            if (s && len > 0) {
407                while (len >= 1 && s[len-1] == ' ') {
408                    len--;
409                }
410                if (len > 0) {
411                    if (tex_room_in_buffer(firstpos + (int) len)) {
412                        strncpy((char *) (lmt_fileio_state.io_buffer + firstpos), s, len);
413                        ret = firstpos + (int) len;
414                    } else {
415                        tex_overflow_error("buffer", (int) len);
416                        ret = 0;
417                    }
418                } else {
419                    ret = 0;
420                }
421            } else {
422                ret = 0;
423            }
424        } else {
425            ret = -1;
426        }
427    }
428    lua_settop(L, stacktop);
429    return ret;
430}
431
432/*tex
433
434    Many callbacks have a specific handler, so they don't use the previously mentioned generic one.
435    The next bunch of helpers checks for them being set and deals invoking them as well as reporting
436    errors.
437
438*/
439
440int lmt_callback_okay(lua_State *L, int i, int *top)
441{
442    *top = lua_gettop(L);
443    lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
444    lua_pushcfunction(L, lmt_traceback); /* goes before function */
445    if (lua_rawgeti(L, -2, i) == LUA_TFUNCTION) {
446        lmt_lua_state.saved_callback_count++;
447        return 1;
448    } else {
449        lua_pop(L, 3);
450        return 0;
451    }
452}
453
454void lmt_callback_error(lua_State *L, int top, int i)
455{
456    lua_remove(L, top + 2);
457    lmt_error(L, "callback error", -1, (i == LUA_ERRRUN ? 0 : 1));
458    lua_settop(L, top);
459}
460
461int lmt_run_and_save_callback(lua_State *L, int i, const char *values, ...)
462{
463    int top = 0;
464    int ret = 0;
465    if (lmt_callback_okay(L, i, &top)) {
466        va_list args;
467        va_start(args, values);
468        ret = callbacklib_aux_run(L, i, 1, values, args, top, top + 2);
469        va_end(args);
470        if (ret > 0) {
471            ret = lua_type(L, -1) == LUA_TTABLE ? luaL_ref(L, LUA_REGISTRYINDEX) : 0;
472        }
473        lua_settop(L, top);
474    }
475    return ret;
476}
477
478int lmt_run_callback(lua_State *L, int i, const char *values, ...)
479{
480    int top = 0;
481    int ret = 0;
482    if (lmt_callback_okay(L, i, &top)) {
483        va_list args;
484        va_start(args, values);
485        ret = callbacklib_aux_run(L, i, 0, values, args, top, top + 2);
486        va_end(args);
487        lua_settop(L, top);
488    }
489    return ret;
490}
491
492void lmt_destroy_saved_callback(lua_State *L, int i)
493{
494    luaL_unref(L, LUA_REGISTRYINDEX, i);
495}
496
497static int callbacklib_callback_found(const char *s)
498{
499    if (s) {
500        for (int cb = 0; cb < total_callbacks; cb++) {
501            if (strcmp(callbacklib_names[cb], s) == 0) {
502                return cb;
503            }
504        }
505    }
506    return -1;
507}
508
509static int callbacklib_callback_register(lua_State *L)
510{
511    const char *s = lua_tostring(L, 1);
512    int cb = callbacklib_callback_found(s);
513    if (cb >= 0) {
514        switch (lua_type(L, 2)) {
515            case LUA_TFUNCTION:
516                lmt_callback_state.values[cb] = cb;
517                break;
518            case LUA_TBOOLEAN:
519                if (lua_toboolean(L, 2)) {
520                    goto BAD; /*tex Only |false| is valid. */
521                }
522                // fall through
523            case LUA_TNIL:
524                lmt_callback_state.values[cb] = -1;
525                break;
526        }
527        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
528        lua_pushvalue(L, 2); /*tex the function or nil */
529        lua_rawseti(L, -2, cb);
530        lua_rawseti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
531        lua_pushinteger(L, cb);
532        return 1;
533    }
534  BAD:
535    lua_pushnil(L);
536    return 1;
537}
538
539void lmt_run_memory_callback(const char* what, int success)
540{
541    lmt_run_callback(lmt_lua_state.lua_instance, trace_memory_callback, "Sb->", what, success);
542    fflush(stdout);
543}
544
545/*tex
546
547    The \LUA\ library that deals with callbacks has some diagnostic helpers that makes it possible
548    to implement a higher level interface.
549
550*/
551
552static int callbacklib_callback_find(lua_State *L)
553{
554    const char *s = lua_tostring(L, 1);
555    if (s) {
556        int cb = callbacklib_callback_found(s);
557        if (cb >= 0) {
558            lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
559            lua_rawgeti(L, -1, cb);
560            return 1;
561        }
562    }
563    lua_pushnil(L);
564    return 1;
565}
566
567static int callbacklib_callback_known(lua_State *L)
568{
569    const char *s = lua_tostring(L, 1);
570    lua_pushboolean(L, s && (callbacklib_callback_found(s) >= 0));
571    return 1;
572}
573
574static int callbacklib_callback_list(lua_State *L)
575{
576    lua_createtable(L, 0, total_callbacks);
577    for (int cb = 1; cb < total_callbacks; cb++) {
578        lua_pushstring(L, callbacklib_names[cb]);
579        lua_pushboolean(L, lmt_callback_defined(cb));
580        lua_rawset(L, -3);
581    }
582    return 1;
583}
584
585/* todo: language function calls */
586
587void lmt_push_callback_usage(lua_State *L)
588{
589    lua_createtable(L, 0, 9);
590    lua_push_integer_at_key(L, saved,    lmt_lua_state.saved_callback_count);
591    lua_push_integer_at_key(L, file,     lmt_lua_state.file_callback_count);
592    lua_push_integer_at_key(L, direct,   lmt_lua_state.direct_callback_count);
593    lua_push_integer_at_key(L, function, lmt_lua_state.function_callback_count);
594    lua_push_integer_at_key(L, value,    lmt_lua_state.value_callback_count);
595    lua_push_integer_at_key(L, local,    lmt_lua_state.local_callback_count);
596    lua_push_integer_at_key(L, bytecode, lmt_lua_state.bytecode_callback_count);
597    lua_push_integer_at_key(L, message,  lmt_lua_state.message_callback_count);
598    lua_push_integer_at_key(L, count,
599        lmt_lua_state.saved_callback_count
600      + lmt_lua_state.file_callback_count
601      + lmt_lua_state.direct_callback_count
602      + lmt_lua_state.function_callback_count
603      + lmt_lua_state.value_callback_count
604      + lmt_lua_state.local_callback_count
605      + lmt_lua_state.bytecode_callback_count
606      + lmt_lua_state.message_callback_count
607    );
608}
609
610static int callbacklib_callback_usage(lua_State *L)
611{
612    lmt_push_callback_usage(L);
613    return 1;
614}
615
616static const struct luaL_Reg callbacklib_function_list[] = {
617    { "find",     callbacklib_callback_find     },
618    { "known",    callbacklib_callback_known    },
619    { "register", callbacklib_callback_register },
620    { "list",     callbacklib_callback_list     },
621    { "usage",    callbacklib_callback_usage    },
622    { NULL,       NULL                          },
623};
624
625int luaopen_callback(lua_State *L)
626{
627    lua_newtable(L);
628    luaL_setfuncs(L, callbacklib_function_list, 0);
629    lua_newtable(L);
630    lmt_callback_state.metatable_id = luaL_ref(L, LUA_REGISTRYINDEX);
631    return 1;
632}
633