lmttexiolib.c /size: 11 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5/*tex
6
7    This is a small module that deals with logging. We inherit from \TEX\ the dualistic model
8    of console (terminal) and log file. One can write to one of them or both at the same time.
9    We also inherit most of the logic that deals with going to a new line but we don't have the
10    escaping with |^^| any longer: we live in \UNICODE\ times now. Because \TEX\ itself often
11    outputs single characters and/or small strings, the console actually can have some real
12    impact on performance: updating the display, rendering with complex fonts, intercepting
13    \ANSI\ control sequences, scrolling, etc.
14
15*/
16
17# include "luametatex.h"
18
19FILE *lmt_valid_file(lua_State *L) {
20    luaL_Stream *p = (luaL_Stream *) lua_touserdata(L, 1);
21    if (p && lua_getmetatable(L, 1)) {
22     // luaL_getmetatable(L, LUA_FILEHANDLE);
23        lua_get_metatablelua(file_handle_instance);
24        if (! lua_rawequal(L, -1, -2)) {
25            p = NULL;
26        }
27        lua_pop(L, 2);
28        return (p && (p)->closef) ? p->f : NULL;
29    }
30    return NULL;
31}
32
33typedef void (*texio_printer) (const char *);
34
35
36static int texiolib_getselectorvalues(lua_State *L)
37{
38    lua_createtable(L, 3, 0);
39    lua_push_key_at_index(L, logfile,              terminal_selector_code            );
40    lua_push_key_at_index(L, terminal,             logfile_selector_code             );
41    lua_push_key_at_index(L, terminal_and_logfile, terminal_and_logfile_selector_code);
42    return 1;
43}
44
45static inline int texiolib_aux_get_selector_value(lua_State *L, int i, int *l, int dflt)
46{
47    switch (lua_type(L, i)) {
48        case LUA_TSTRING:
49            {
50                const char *s = lua_tostring(L, i);
51                if (lua_key_eq(s, logfile)) {
52                    *l = logfile_selector_code;
53                } else if (lua_key_eq(s, terminal)) {
54                    *l = terminal_selector_code;
55                } else if (lua_key_eq(s, terminal_and_logfile)) {
56                    *l = terminal_and_logfile_selector_code;
57                } else {
58                    *l = dflt;
59                }
60                return 1;
61            }
62        case LUA_TNUMBER:
63            {
64                int n = lmt_tointeger(L, i);
65                *l = n >= terminal_selector_code && n <= terminal_and_logfile_selector_code ? n : dflt;
66                return 1;
67            }
68        default:
69            return luaL_error(L, "(first) argument is not 'terminal_and_logfile', 'terminal' or 'logfile'");
70    }
71}
72
73static void texiolib_aux_print(lua_State *L, int n, texio_printer printfunction, const char *dflt)
74{
75    int i = 1;
76    int saved_selector = lmt_print_state.selector;
77    if (n > 1 && texiolib_aux_get_selector_value(L, i, &lmt_print_state.selector, terminal_selector_code)) {
78        i++;
79    }
80    switch (lmt_print_state.selector) {
81        case terminal_and_logfile_selector_code:
82        case logfile_selector_code:
83        case terminal_selector_code:
84            if (i <= n) {
85                do {
86                    switch (lua_type(L, i)) {
87                        case LUA_TNIL:
88                            break;
89                        case LUA_TBOOLEAN:
90                        case LUA_TNUMBER:
91                        case LUA_TSTRING:
92                            printfunction(lua_tostring(L, i));
93                            break;
94                        default:
95                            luaL_error(L, "argument is not a string, number or boolean");
96                    }
97                    i++;
98                } while (i <= n);
99            } else if (dflt) {
100                printfunction(dflt);
101            }
102        break;
103    }
104    lmt_print_state.selector = saved_selector;
105}
106
107static void texiolib_aux_print_selector(lua_State *L, int n, texio_printer printfunction, const char *dflt)
108{
109    int saved_selector = lmt_print_state.selector;
110    texiolib_aux_get_selector_value(L, 1, &lmt_print_state.selector, no_print_selector_code);
111    switch (lmt_print_state.selector) {
112        case terminal_and_logfile_selector_code:
113        case logfile_selector_code:
114        case terminal_selector_code:
115            {
116                if (n > 1) {
117                    for (int i = 2; i <= n; i++) {
118                        switch (lua_type(L, i)) {
119                            case LUA_TNIL:
120                                break;
121                            case LUA_TBOOLEAN:
122                            case LUA_TNUMBER:
123                            case LUA_TSTRING:
124                                printfunction(lua_tostring(L, i));
125                                break;
126                            default:
127                                luaL_error(L, "argument is not a string, number or boolean");
128                        }
129                    };
130                } else if (dflt) {
131                    printfunction(dflt);
132                }
133                break;
134            }
135    }
136    lmt_print_state.selector = saved_selector;
137}
138
139static void texiolib_aux_print_stdout(lua_State *L, const char *extra)
140{
141    int i = 1;
142    int l = terminal_and_logfile_selector_code;
143    int n = lua_gettop(L);
144    if (n > 1 && texiolib_aux_get_selector_value(L, i, &l, terminal_selector_code)) {
145        i++;
146    }
147    for (; i <= n; i++) {
148        if (lua_isstring(L, i)) { /* or number */
149            const char *s = lua_tostring(L, i);
150            if (l == terminal_and_logfile_selector_code || l == terminal_selector_code) {
151                fputs(extra, stdout);
152                fputs(s, stdout);
153            }
154            if (l == terminal_and_logfile_selector_code || l == logfile_selector_code) {
155                if (lmt_print_state.loggable_info) {
156                    char *v = (char*) lmt_memory_malloc(strlen(lmt_print_state.loggable_info) + strlen(extra) + strlen(s) + 1);
157                    if (v) {
158                        sprintf(v, "%s%s%s", lmt_print_state.loggable_info, extra, s);
159                    }
160                    lmt_memory_free(lmt_print_state.loggable_info);
161                    lmt_print_state.loggable_info = v;
162                } else {
163                    lmt_print_state.loggable_info = lmt_memory_strdup(s);
164                }
165            }
166        }
167    }
168}
169
170static void texiolib_aux_print_nlp_str(const char *s)
171{
172    tex_print_nlp();
173    tex_print_str(s);
174}
175
176static int texiolib_write(lua_State *L)
177{
178    if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
179        texiolib_aux_print_stdout(L, "");
180    } else {
181        int n = lua_gettop(L);
182        if (n > 0) {
183            texiolib_aux_print(L, n, tex_print_str, NULL);
184        } else {
185            /*tex We silently ignore bogus calls. */
186        }
187    }
188    return 0;
189}
190
191static int texiolib_write_nl(lua_State *L)
192{
193    if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
194        texiolib_aux_print_stdout(L, "\n");
195    } else {
196        int n = lua_gettop(L);
197        if (n > 0) {
198            texiolib_aux_print(L, n, texiolib_aux_print_nlp_str, "\n");
199        } else {
200            /*tex We silently ignore bogus calls. */
201        }
202    }
203    return 0;
204}
205
206static int texiolib_write_selector(lua_State *L)
207{
208    if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
209        texiolib_aux_print_stdout(L, "");
210    } else {
211        int n = lua_gettop(L);
212        if (n > 1) {
213            texiolib_aux_print_selector(L, n, tex_print_str, NULL);
214        } else {
215            /*tex We silently ignore bogus calls. */
216        }
217    }
218    return 0;
219}
220
221
222static int texiolib_write_selector_nl(lua_State *L)
223{
224    if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
225        texiolib_aux_print_stdout(L, "\n");
226    } else {
227        int n = lua_gettop(L);
228        if (n > 1) {
229            texiolib_aux_print_selector(L, n, texiolib_aux_print_nlp_str, "");
230        } else {
231            /*tex We silently ignore bogus calls. */
232        }
233    }
234    return 0;
235}
236
237static int texiolib_write_selector_lf(lua_State *L)
238{
239    if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
240        texiolib_aux_print_stdout(L, "\n");
241    } else {
242        int n = lua_gettop(L);
243        if (n >= 1) {
244            texiolib_aux_print_selector(L, n, texiolib_aux_print_nlp_str, "");
245        } else {
246            /*tex We silently ignore bogus calls. */
247        }
248    }
249    return 0;
250}
251
252/*tex At the point this function is called, the selector is log_only. */
253
254static int texiolib_closeinput(lua_State *L)
255{
256    (void) (L);
257    if (lmt_input_state.cur_input.index > 0) {
258        tex_end_token_list();
259        tex_end_file_reading();
260    }
261    return 0 ;
262}
263
264static int texiolib_getinputindex(lua_State *L)
265{
266    lua_pushinteger(L, lmt_input_state.cur_input.index);
267    return 1;
268}
269
270static int texiolib_getsourcefilename(lua_State *L)
271{
272    lua_pushstring(L, lmt_input_state.in_stack[lmt_input_state.in_stack_data.ptr].full_source_filename);
273    return 1;
274}
275
276/*tex
277    This is a private hack, handy for testing runtime math font patches in lfg files with a bit of
278    low level tracing. Setting the logfile is already handles by a callback so we don't support
279    string argument here because we'd end up in that callback which then returns the same logfile
280    name as we already had.
281*/
282
283static int texiolib_setlogfile(lua_State *L)
284{
285    FILE *f = lmt_valid_file(L);
286    if (f) {
287        /* If not writeable then all goes into the void. */
288        if (! lmt_print_state.logfile) {
289            lmt_print_state.saved_logfile = lmt_print_state.logfile;
290            lmt_print_state.saved_logfile_offset = lmt_print_state.logfile_offset;
291
292        }
293        lmt_print_state.logfile = f;
294        lmt_print_state.logfile_offset = 0;
295    } else if (lmt_print_state.logfile) {
296        lmt_print_state.logfile = lmt_print_state.saved_logfile;
297        lmt_print_state.logfile_offset = lmt_print_state.saved_logfile_offset;
298    }
299    return 0;
300}
301
302static int texiolib_forceendoffile(lua_State *L)
303{
304    (void) L;
305    lmt_token_state.force_eof = 1;
306    return 0;
307}
308
309static const struct luaL_Reg texiolib_function_list[] = {
310    { "write",             texiolib_write             },
311    { "writenl",           texiolib_write_nl          },
312    { "write_nl",          texiolib_write_nl          }, /* depricated */
313    { "writeselector",     texiolib_write_selector    },
314    { "writeselectornl",   texiolib_write_selector_nl },
315    { "writeselectorlf",   texiolib_write_selector_lf },
316    { "closeinput",        texiolib_closeinput        },
317    { "setlogfile",        texiolib_setlogfile        },
318    { "getinputindex",     texiolib_getinputindex     }, /*tex temporary, testing only */
319    { "getsourcefilename", texiolib_getsourcefilename }, /*tex temporary, testing only */
320    { "forceendoffile",    texiolib_forceendoffile    }, /*tex temporary, testing only */
321    { "getselectorvalues", texiolib_getselectorvalues },
322    { NULL,                NULL                       },
323};
324
325static const struct luaL_Reg texiolib_function_list_only[] = {
326    { "write",           texiolib_write             },
327    { "writenl",         texiolib_write_nl          },
328    { "write_nl",        texiolib_write_nl          }, /* depricated */
329    { "writeselector",   texiolib_write_selector    },
330    { "writeselectornl", texiolib_write_selector_nl },
331    { "writeselectorlf", texiolib_write_selector_lf },
332    { NULL,              NULL                       },
333};
334
335int luaopen_texio(lua_State *L)
336{
337    lua_newtable(L);
338    luaL_setfuncs(L, lmt_engine_state.lua_only ? texiolib_function_list_only : texiolib_function_list, 0);
339    return 1;
340}
341