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