1
4
5
20
21# include "luametatex.h"
22# include "lmtoptional.h"
23
24typedef struct js_State js_State;
25
26typedef void (*js_CFunction) (js_State *J);
27typedef void (*js_Report) (js_State *J, const char *message);
28typedef void (*js_Finalize) (js_State *J, void *p);
29
30typedef enum js_states {
31 JS_STRICT = 1,
32} js_states;
33
34typedef enum js_properties {
35 JS_READONLY = 1,
36 JS_DONTENUM = 2,
37 JS_DONTCONF = 4,
38} js_properties;
39
40
41
42typedef struct mujslib_state_info {
43
44 js_State *instance;
45 int initialized;
46 int find_file_id;
47 int open_file_id;
48 int close_file_id;
49 int read_file_id;
50 int seek_file_id;
51 int console_id;
52 int padding;
53
54 struct js_State * (*js_newstate) (
55 void *alloc,
56 void *actx,
57 int flags
58 );
59
60 void (*js_freestate) (
61 js_State *J
62 );
63
64 void (*js_setreport) (
65 js_State *J,
66 js_Report report
67 );
68
69 int (*js_dostring) (
70 js_State *J,
71 const char *source
72 );
73
74 void (*js_newcfunction) (
75 js_State *J,
76 js_CFunction fun,
77 const char *name,
78 int length
79 );
80
81 void (*js_newuserdata) (
82 js_State *J,
83 const char *tag,
84 void *data,
85 js_Finalize finalize
86 );
87
88 void (*js_newcconstructor) (
89 js_State *J,
90 js_CFunction fun,
91 js_CFunction con,
92 const char *name,
93 int length
94 );
95
96 int (*js_dofile) (
97 js_State *J,
98 const char *filename
99 );
100
101 void (*js_currentfunction) (
102 js_State *J
103 );
104
105 void (*js_getglobal) (js_State *J, const char *name );
106 void (*js_setglobal) (js_State *J, const char *name );
107 void (*js_defglobal) (js_State *J, const char *name, int atts );
108
109 void (*js_getproperty) (js_State *J, int idx, const char *name );
110 void (*js_setproperty) (js_State *J, int idx, const char *name );
111 void (*js_defproperty) (js_State *J, int idx, const char *name, int atts);
112
113 void (*js_pushundefined) (js_State *J );
114 void (*js_pushnull) (js_State *J );
115 void (*js_pushnumber) (js_State *J, double v );
116 void (*js_pushstring) (js_State *J, const char *v );
117
118 const char * (*js_tostring) (js_State *J, int idx );
119 int (*js_tointeger) (js_State *J, int idx );
120 void * (*js_touserdata) (js_State *J, int idx, const char *tag );
121
122 int (*js_isnumber) (js_State *J, int idx );
123 int (*js_isstring) (js_State *J, int idx );
124 int (*js_isundefined) (js_State *J, int idx );
125
126} mujslib_state_info;
127
128static mujslib_state_info mujslib_state = {
129
130 .initialized = 0,
131 .instance = NULL,
132 .find_file_id = 0,
133 .open_file_id = 0,
134 .close_file_id = 0,
135 .read_file_id = 0,
136 .seek_file_id = 0,
137 .console_id = 0,
138 .padding = 0,
139
140 .js_newstate = NULL,
141 .js_freestate = NULL,
142 .js_setreport = NULL,
143 .js_dostring = NULL,
144 .js_newcfunction = NULL,
145 .js_newuserdata = NULL,
146 .js_newcconstructor = NULL,
147 .js_dofile = NULL,
148 .js_currentfunction = NULL,
149
150 .js_getglobal = NULL,
151 .js_setglobal = NULL,
152 .js_defglobal = NULL,
153
154 .js_getproperty = NULL,
155 .js_setproperty = NULL,
156 .js_defproperty = NULL,
157
158 .js_pushundefined = NULL,
159 .js_pushnull = NULL,
160 .js_pushnumber = NULL,
161 .js_pushstring = NULL,
162
163 .js_tostring = NULL,
164 .js_tointeger = NULL,
165 .js_touserdata = NULL,
166
167 .js_isnumber = NULL,
168 .js_isstring = NULL,
169 .js_isundefined = NULL,
170
171};
172
173
174
175static int mujslib_register_function(lua_State * L, int old_id)
176{
177 if (! (lua_isfunction(L, -1) || lua_isnil(L, -1))) {
178 return 0;
179 } else {
180 lua_pushvalue(L, -1);
181 if (old_id) {
182 luaL_unref(L, LUA_REGISTRYINDEX, old_id);
183 }
184 return luaL_ref(L, LUA_REGISTRYINDEX);
185 }
186}
187
188static int mujslib_set_find_file(lua_State *L)
189{
190 mujslib_state.find_file_id = mujslib_register_function(L, mujslib_state.find_file_id);
191 return 0;
192}
193
194static int mujslib_set_open_file(lua_State *L)
195{
196 mujslib_state.open_file_id = mujslib_register_function(L, mujslib_state.open_file_id);
197 return 0;
198}
199
200static int mujslib_set_close_file(lua_State *L)
201{
202 mujslib_state.close_file_id = mujslib_register_function(L, mujslib_state.close_file_id);
203 return 0;
204}
205
206static int mujslib_set_read_file(lua_State *L)
207{
208 mujslib_state.read_file_id = mujslib_register_function(L, mujslib_state.read_file_id);
209 return 0;
210}
211
212static int mujslib_set_seek_file(lua_State *L)
213{
214 mujslib_state.seek_file_id = mujslib_register_function(L, mujslib_state.seek_file_id);
215 return 0;
216}
217
218static int mujslib_set_console(lua_State *L)
219{
220 mujslib_state.console_id = mujslib_register_function(L, mujslib_state.console_id);
221 return 0;
222}
223
224static char *mujslib_find_file(const char *fname, const char *fmode)
225{
226 if (mujslib_state.find_file_id) {
227 lua_State *L = lmt_lua_state.lua_instance;
228 lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.find_file_id);
229 lua_pushstring(L, fname);
230 lua_pushstring(L, fmode);
231 if (lua_pcall(L, 2, 1, 0)) {
232 tex_formatted_warning("mujs", "find file: %s\n", lua_tostring(L, -1));
233 } else {
234 char *s = NULL;
235 const char *x = lua_tostring(L, -1);
236 if (x) {
237 s = strdup(x);
238 }
239 lua_pop(L, 1);
240 return s;
241 }
242 } else {
243 tex_normal_warning("mujs", "missing callback: find file");
244 }
245 return NULL;
246}
247
248
249
250static void mujslib_aux_texcprint(js_State *J, int ispartial)
251{
252 int c = default_catcode_table_preset;
253 int i = 0;
254 if (mujslib_state.js_isnumber(J, 1)) {
255 if (mujslib_state.js_isnumber(J, 2) || mujslib_state.js_isstring(J, 2)) {
256 c = mujslib_state.js_tointeger(J, 1);
257 i = 2;
258 } else {
259 i = 1;
260 }
261 } else if (mujslib_state.js_isstring(J, 1)) {
262 i = 1;
263 }
264 if (i) {
265 const char *s = mujslib_state.js_tostring(J, i);
266 if (s) {
267 lmt_cstring_print(c, s, ispartial);
268 }
269 } else {
270 tex_normal_warning("mujs", "invalid argument(s) for printing to tex");
271 }
272 mujslib_state.js_pushundefined(J);
273}
274
275static void mujslib_aux_texprint(js_State *J)
276{
277 mujslib_aux_texcprint(J, 0);
278}
279
280static void mujslib_aux_texsprint(js_State *J)
281{
282 mujslib_aux_texcprint(J, 1);
283}
284
285static void mujslib_aux_feedback(js_State *J, const char *category, const char *message)
286{
287 if (message) {
288 if (mujslib_state.console_id) {
289 lua_State *L = lmt_lua_state.lua_instance;
290 lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.console_id);
291 lua_pushstring(L, category);
292 lua_pushstring(L, message);
293 if (lua_pcall(L, 2, 0, 0)) {
294 tex_formatted_warning("mujs", "console: %s\n", lua_tostring(L, -1));
295 }
296 } else {
297 tex_print_message(message);
298 }
299 }
300 mujslib_state.js_pushundefined(J);
301}
302
303static void mujslib_aux_console(js_State *J)
304{
305 mujslib_aux_feedback(J, "console", mujslib_state.js_tostring(J, 1));
306}
307
308static void mujslib_aux_report(js_State *J, const char *s)
309{
310 mujslib_aux_feedback(J, "report", s);
311}
312
313
317
318static int mujslib_execute(lua_State *L)
319{
320 if (mujslib_state.instance) {
321 const char *s = lua_tostring(L, 1);
322 if (s) {
323 mujslib_state.js_dostring(mujslib_state.instance, s);
324 }
325 }
326 return 0;
327}
328
329static int mujslib_dofile(lua_State *L)
330{
331 if (mujslib_state.instance) {
332 const char *name = lua_tostring(L, 1);
333 if (name) {
334 char *found = mujslib_find_file(name, "rb");
335 if (found) {
336 mujslib_state.js_dofile(mujslib_state.instance, found);
337 }
338 free(found);
339 }
340 } else {
341 tex_normal_warning("mujs", "missing callback: find file");
342 }
343 return 0;
344}
345
346static void mujslib_start(void)
347{
348 if (mujslib_state.instance) {
349 mujslib_state.js_freestate(mujslib_state.instance);
350 }
351 mujslib_state.instance = mujslib_state.js_newstate(NULL, NULL, JS_STRICT);
352 if (mujslib_state.instance) {
353 mujslib_state.js_newcfunction(mujslib_state.instance, mujslib_aux_texprint, "texprint", 2);
354 mujslib_state.js_setglobal (mujslib_state.instance, "texprint");
355 mujslib_state.js_newcfunction(mujslib_state.instance, mujslib_aux_texsprint, "texsprint", 2);
356 mujslib_state.js_setglobal (mujslib_state.instance, "texsprint");
357 mujslib_state.js_newcfunction(mujslib_state.instance, mujslib_aux_console, "console", 1);
358 mujslib_state.js_setglobal (mujslib_state.instance, "console");
359 mujslib_state.js_setreport (mujslib_state.instance, mujslib_aux_report);
360 }
361}
362
363static int mujslib_reset(lua_State *L)
364{
365 if (mujslib_state.initialized) {
366 mujslib_start();
367 }
368 lua_pushboolean(L, mujslib_state.initialized && mujslib_state.instance);
369 return 1;
370}
371
372
377
378static void mujslib_file_finalize(js_State *J, void *p)
379{
380 int *id = p;
381 (void) J;
382 if (*id) {
383 lua_State *L = lmt_lua_state.lua_instance;
384 int top = lua_gettop(L);
385 lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.close_file_id);
386 lua_pushinteger(L, *id);
387 if (lua_pcall(L, 1, 0, 0)) {
388 tex_formatted_warning("mujs", "close file: %s\n", lua_tostring(L, -1));
389 }
390 lua_settop(L,top);
391 }
392}
393
394static void mujslib_file_close(js_State *J)
395{
396 if (mujslib_state.instance) {
397 if (mujslib_state.close_file_id) {
398 int *id = mujslib_state.js_touserdata(J, 0, "File");
399 if (*id) {
400 mujslib_file_finalize(J, id);
401 }
402 } else {
403 tex_normal_warning("mujs", "missing callback: close file");
404 }
405 }
406 mujslib_state.js_pushundefined(J);
407}
408
409static void mujslib_file_read(js_State *J)
410{
411 if (mujslib_state.instance) {
412 if (mujslib_state.read_file_id) {
413 int *id = mujslib_state.js_touserdata(J, 0, "File");
414 if (*id) {
415 lua_State *L = lmt_lua_state.lua_instance;
416 int top = lua_gettop(L);
417 int n = 1;
418 lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.read_file_id);
419 lua_pushinteger(L, *id);
420 if (mujslib_state.js_isstring(J, 1)) {
421 const char *how = mujslib_state.js_tostring(J, 1);
422 if (how) {
423 lua_pushstring(L, how);
424 n = 2;
425 }
426 } else if (mujslib_state.js_isnumber(J, 1)) {
427 int how = mujslib_state.js_tointeger(J, 1);
428 if (how) {
429 lua_pushinteger(L, how);
430 n = 2;
431 }
432 }
433 if (lua_pcall(L, n, 1, 0)) {
434 tex_formatted_warning("mujs", "close file: %s\n", lua_tostring(L, -1));
435 } else {
436 const char *result = strdup(lua_tostring(L, -1));
437 if (result) {
438 mujslib_state.js_pushstring(J, result);
439 lua_settop(L, top);
440 return;
441 }
442 }
443 lua_settop(L, top);
444 }
445 } else {
446 tex_normal_warning("mujs", "missing callback: read file");
447 }
448 }
449 mujslib_state.js_pushundefined(J);
450}
451
452static void mujslib_file_seek(js_State *J)
453{
454 if (mujslib_state.instance) {
455 if (mujslib_state.seek_file_id) {
456 int *id = mujslib_state.js_touserdata(J, 0, "File");
457 if (*id) {
458 lua_State *L = lmt_lua_state.lua_instance;
459 int top = lua_gettop(L);
460 int n = 2;
461 lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.seek_file_id);
462 lua_pushinteger(L, *id);
463
464 lua_pushstring(L, mujslib_state.js_tostring(J, 1));
465 if (mujslib_state.js_isnumber(J, 2)) {
466 lua_pushinteger(L, mujslib_state.js_tointeger(J, 2));
467 n = 3;
468 }
469 if (lua_pcall(L, n, 1, 0)) {
470 tex_formatted_warning("mujs", "seek file: %s\n", lua_tostring(L, -1));
471 } else if (lua_type(L, -1) == LUA_TNUMBER) {
472 mujslib_state.js_pushnumber(J, lua_tonumber(L, -1));
473 lua_settop(L, top);
474 return;
475 }
476 lua_settop(L, top);
477 }
478 } else {
479 tex_normal_warning("mujs", "missing callback: seek file");
480 }
481 }
482 mujslib_state.js_pushundefined(J);
483}
484
485static void mujslib_file_new(js_State *J)
486{
487 if (mujslib_state.instance) {
488 if (mujslib_state.open_file_id) {
489 const char *name = mujslib_state.js_tostring(J, 1);
490 if (name) {
491 lua_State *L = lmt_lua_state.lua_instance;
492 int top = lua_gettop(L);
493 lua_rawgeti(L, LUA_REGISTRYINDEX, mujslib_state.open_file_id);
494 lua_pushstring(L, name);
495 if (lua_pcall(L, 1, 1, 0)) {
496 tex_formatted_warning("mujs", "open file: %s\n", lua_tostring(L, -1));
497 } else {
498 int *id = malloc(sizeof(int));
499 if (id) {
500 *((int*) id) = (int) lua_tointeger(L, -1);
501 lua_settop(L, top);
502 if (id) {
503 mujslib_state.js_currentfunction(J);
504 mujslib_state.js_getproperty(J, -1, "prototype");
505 mujslib_state.js_newuserdata(J, "File", id, mujslib_file_finalize);
506 return;
507 }
508 }
509 }
510 lua_settop(L, top);
511 }
512 } else {
513 tex_normal_warning("mujs", "missing callback: open file");
514 }
515 }
516 mujslib_state.js_pushnull(J);
517}
518
519
520
521static void mujslib_file_initialize(js_State *J)
522{
523 mujslib_state.js_getglobal(J, "Object");
524 mujslib_state.js_getproperty(J, -1, "prototype");
525 mujslib_state.js_newuserdata(J, "File", stdin, NULL);
526 {
527 mujslib_state.js_newcfunction(J, mujslib_file_read, "File.prototype.read", 0);
528 mujslib_state.js_defproperty(J, -2, "read", JS_DONTENUM);
529 mujslib_state.js_newcfunction(J, mujslib_file_seek, "File.prototype.seek", 0);
530 mujslib_state.js_defproperty(J, -2, "seek", JS_DONTENUM);
531 mujslib_state.js_newcfunction(J, mujslib_file_close, "File.prototype.close", 0);
532 mujslib_state.js_defproperty(J, -2, "close", JS_DONTENUM);
533 }
534 mujslib_state.js_newcconstructor(J, mujslib_file_new, mujslib_file_new, "File", 1);
535 mujslib_state.js_defglobal(J, "File", JS_DONTENUM);
536}
537
538static int mujslib_initialize(lua_State *L)
539{
540 if (! mujslib_state.initialized) {
541 const char *filename = lua_tostring(L, 1);
542 if (filename) {
543
544 lmt_library lib = lmt_library_load(filename);
545
546 mujslib_state.js_newstate = lmt_library_find(lib, "js_newstate");
547 mujslib_state.js_freestate = lmt_library_find(lib, "js_freestate");
548 mujslib_state.js_setreport = lmt_library_find(lib, "js_setreport");
549
550 mujslib_state.js_newcfunction = lmt_library_find(lib, "js_newcfunction");
551 mujslib_state.js_newuserdata = lmt_library_find(lib, "js_newuserdata");
552 mujslib_state.js_newcconstructor = lmt_library_find(lib, "js_newcconstructor");
553
554 mujslib_state.js_pushundefined = lmt_library_find(lib, "js_pushundefined");
555 mujslib_state.js_pushnull = lmt_library_find(lib, "js_pushnull");
556 mujslib_state.js_pushnumber = lmt_library_find(lib, "js_pushnumber");
557 mujslib_state.js_pushstring = lmt_library_find(lib, "js_pushstring");
558
559 mujslib_state.js_dostring = lmt_library_find(lib, "js_dostring");
560 mujslib_state.js_dofile = lmt_library_find(lib, "js_dofile");
561
562 mujslib_state.js_tostring = lmt_library_find(lib, "js_tostring");
563 mujslib_state.js_tointeger = lmt_library_find(lib, "js_tointeger");
564 mujslib_state.js_touserdata = lmt_library_find(lib, "js_touserdata");
565
566 mujslib_state.js_getglobal = lmt_library_find(lib, "js_getglobal");
567 mujslib_state.js_setglobal = lmt_library_find(lib, "js_setglobal");
568 mujslib_state.js_defglobal = lmt_library_find(lib, "js_defglobal");
569
570 mujslib_state.js_getproperty = lmt_library_find(lib, "js_getproperty");
571 mujslib_state.js_setproperty = lmt_library_find(lib, "js_setproperty");
572 mujslib_state.js_defproperty = lmt_library_find(lib, "js_defproperty");
573
574 mujslib_state.js_isstring = lmt_library_find(lib, "js_isstring");
575 mujslib_state.js_isnumber = lmt_library_find(lib, "js_isnumber");
576 mujslib_state.js_isundefined = lmt_library_find(lib, "js_isundefined");
577
578 mujslib_state.js_currentfunction = lmt_library_find(lib, "js_currentfunction");
579
580 mujslib_state.initialized = lmt_library_okay(lib);
581
582 mujslib_start();
583
584 mujslib_file_initialize(mujslib_state.instance);
585 }
586 }
587 lua_pushboolean(L, mujslib_state.initialized && mujslib_state.instance);
588 return 1;
589}
590
591static struct luaL_Reg mujslib_function_list[] = {
592 { "initialize", mujslib_initialize },
593 { "reset", mujslib_reset },
594 { "execute", mujslib_execute },
595 { "dofile", mujslib_dofile },
596 { "setfindfile", mujslib_set_find_file },
597 { "setopenfile", mujslib_set_open_file },
598 { "setclosefile", mujslib_set_close_file },
599 { "setreadfile", mujslib_set_read_file },
600 { "setseekfile", mujslib_set_seek_file },
601 { "setconsole", mujslib_set_console },
602 { NULL, NULL },
603};
604
605int luaopen_mujs(lua_State *L)
606{
607 lmt_library_register(L, "mujs", mujslib_function_list);
608 return 0;
609}
610 |