lmtmplib.c /size: 114 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 an adapted version of \MPLIB\ for ConTeXt LMTX. Interfaces might change as experiments
8    demand or indicate this. It's meant for usage in \LUAMETATEX, which is an engine geared at
9    \CONTEXT, and which, although useable in production, will in principle be experimental and
10    moving for a while, depending on needs, experiments and mood.
11
12    In \LUATEX\, at some point \MPLIB\ got an interface to \LUA\ and that greatly enhanced the
13    posibilities. In \LUAMETATEX\ this interface was further improved and in addition scanners
14    got added. So in the meantime we have a rather complete and tight integration of \TEX, \LUA,
15    and \METAPOST\ and in \CONTEXT\ we use that integration as much as possible.
16
17    An important difference with \LUATEX\ is that we use an upgraded \MPLIB. The \POSTSCRIPT\
18    backend has been dropped and there is no longer font and file related code because that
19    all goes via \LUA\ now. The binary frontend has been dropped to so we now only have scaled,
20    double and decimal.
21
22*/
23
24# include "mp.h"
25
26/*tex
27    We can use common definitions but can only load this file after we load the \METAPOST\ stuff,
28    which defines a whole bunch of macros that can clash. Using the next one is actually a good
29    check for such conflicts.
30*/
31
32# include "luametatex.h"
33
34/*tex
35
36    Here are some enumeration arrays to map MPlib enums to \LUA\ strings. If needed we can also
37    predefine keys here, as we do with nodes. Some tables relate to the scanners.
38
39*/
40
41# define MPLIB_PATH      0
42# define MPLIB_PEN       1
43# define MPLIB_PATH_SIZE 8
44
45static const char *mplib_math_options[] = {
46    "scaled",
47    "double",
48    "binary",  /* not available in luatex */
49    "decimal",
50    "posit",
51    NULL
52};
53
54static const char *mplib_interaction_options[] = {
55    "unknown",
56    "batch",
57    "nonstop",
58    "scroll",
59    "errorstop",
60    "silent",
61    NULL
62};
63
64static const char *mplib_filetype_names[] = {
65    "terminal", /* mp_filetype_terminal */
66    "mp",       /* mp_filetype_program  */
67    "data",     /* mp_filetype_text     */
68    NULL
69};
70
71/*
72static const char *knot_type_enum[] = {
73    "endpoint", "explicit", "given", "curl", "open", "end_cycle"
74};
75*/
76
77static const char *mplib_fill_fields[] = {
78    "type",
79    "path", "htap",
80    "pen", "color",
81    "linejoin", "miterlimit",
82    "prescript", "postscript",
83    "stacking", "curvature",
84    NULL
85};
86
87static const char *mplib_stroked_fields[] =  {
88    "type",
89    "path",
90    "pen", "color",
91    "linejoin", "miterlimit", "linecap",
92    "dash",
93    "prescript", "postscript",
94    "stacking", "curvature",
95    NULL
96};
97
98static const char *mplib_start_clip_fields[] = {
99    "type",
100    "path",
101    "prescript", "postscript",
102    "stacking",
103    NULL
104};
105
106static const char *mplib_start_group_fields[] = {
107    "type",
108    "path",
109    "prescript", "postscript",
110    "stacking",
111    NULL
112};
113
114static const char *mplib_start_bounds_fields[] = {
115    "type",
116    "path",
117    "prescript", "postscript",
118    "stacking",
119    NULL
120};
121
122static const char *mplib_stop_clip_fields[] = {
123    "type",
124    "stacking",
125    NULL
126};
127
128static const char *mplib_stop_group_fields[] = {
129    "type",
130    "stacking",
131    NULL
132};
133
134static const char *mplib_stop_bounds_fields[] = {
135    "type",
136    "stacking",
137    NULL
138};
139
140static const char *mplib_no_fields[] = {
141    NULL
142};
143
144// static const char *mplib_scan_codes[] = {
145//     "expression", // mp_expression_scan_code
146//     "primary",    // mp_primary_scan_code
147//     "secondary",  // mp_secondary_scan_code
148//     "tertiary",   // mp_tertiary_scan_code
149//     NULL
150// };
151
152// static const char *mplib_internal_actions_codes[] = {
153//    "initialize", // mp_initialize_internal_code
154//    "save",       // mp_save_internal_code
155//    "restore",    // mp_restore_internal_code
156//     NULL
157// };
158
159static const char *mplib_log_targets[] = {
160    "void",
161    "terminal",
162    "file",
163    "both",
164    "error",
165    NULL
166};
167
168static const char *mplib_codes[] = {
169    "undefined",
170    "btex",            /* mp_btex_command                */ /* btex verbatimtex */
171    "etex",            /* mp_etex_command                */ /* etex */
172    "if",              /* mp_if_test_command             */ /* if */
173    "fiorelse",        /* mp_fi_or_else_command          */ /* elseif else fi */
174    "input",           /* mp_input_command               */ /* input endinput */
175    "iteration",       /* mp_iteration_command           */ /* for forsuffixes forever endfor */
176    "repeatloop",      /* mp_repeat_loop_command         */ /* used in repeat loop (endfor) */
177    "exittest",        /* mp_exit_test_command           */ /* exitif */
178    "relax",           /* mp_relax_command               */ /* \\ */
179    "scantokens",      /* mp_scan_tokens_command         */ /* scantokens */
180    "runscript",       /* mp_runscript_command           */ /* runscript */
181    "maketext",        /* mp_maketext_command            */ /* maketext */
182    "expandafter",     /* mp_expand_after_command        */ /* expandafter */
183    "definedmacro",    /* mp_defined_macro_command       */ /* */
184    "save",            /* mp_save_command                */ /* save */
185    "interim",         /* mp_interim_command             */ /* interim */
186    "let",             /* mp_let_command                 */ /* let */
187    "newinternal",     /* mp_new_internal_command        */ /* newinternal */
188    "macrodef",        /* mp_macro_def_command           */ /* def vardef (etc) */
189    "shipout",         /* mp_ship_out_command            */ /* shipout */
190    "addto",           /* mp_add_to_command              */ /* addto */
191    "setbounds",       /* mp_bounds_command              */ /* setbounds clip group */
192    "protection",      /* mp_protection_command          */
193    "property",        /* mp_property_command            */
194    "show",            /* mp_show_command                */ /* show showvariable (etc) */
195    "mode",            /* mp_mode_command                */ /* batchmode (etc) */
196    "onlyset",         /* mp_only_set_command            */ /* randomseed, maxknotpool */
197    "message",         /* mp_message_command             */ /* message errmessage */
198    "everyjob",        /* mp_every_job_command           */ /* everyjob */
199    "delimiters",      /* mp_delimiters_command          */ /* delimiters */
200    "write",           /* mp_write_command               */ /* write */
201    "typename",        /* mp_type_name_command           */ /* (declare) numeric pair */
202    "leftdelimiter",   /* mp_left_delimiter_command      */ /* the left delimiter of a matching pair */
203    "begingroup",      /* mp_begin_group_command         */ /* begingroup */
204    "nullary",         /* mp_nullary_command             */ /* operator without arguments: normaldeviate (etc) */
205    "unary",           /* mp_unary_command               */ /* operator with one argument: sqrt (etc) */
206    "str",             /* mp_str_command                 */ /* convert a suffix to a string: str */
207    "void",            /* mp_void_command                */ /* convert a suffix to a boolean: void */
208    "cycle",           /* mp_cycle_command               */ /* cycle */
209    "ofbinary",        /* mp_of_binary_command           */ /* binary operation taking "of", like "point of" */
210    "capsule",         /* mp_capsule_command             */ /* */
211    "string",          /* mp_string_command              */ /* */
212    "internal",        /* mp_internal_quantity_command   */ /* */
213    "tag",             /* mp_tag_command                 */ /* a symbolic token without a primitive meaning */
214    "numeric",         /* mp_numeric_command             */ /* numeric constant */
215    "plusorminus",     /* mp_plus_or_minus_command       */ /* + - */
216    "secondarydef",    /* mp_secondary_def_command       */ /* secondarydef */
217    "tertiarybinary",  /* mp_tertiary_binary_command     */ /* an operator at the tertiary level: ++ (etc) */
218    "leftbrace",       /* mp_left_brace_command          */ /* { */
219    "pathjoin",        /* mp_path_join_command           */ /* .. */
220    "pathconnect",     /* mp_path_connect_command        */ /* -- */
221    "ampersand",       /* mp_ampersand_command           */ /* & */
222    "tertiarydef",     /* mp_tertiary_def_command        */ /* tertiarydef */
223    "primarybinary",   /* mp_primary_binary_command      */ /* < (etc) */
224    "equals",          /* mp_equals_command              */ /* = */
225    "and",             /* mp_and_command                 */ /* and */
226    "primarydef",      /* mp_primary_def_command         */ /* primarydef */
227    "slash",           /* mp_slash_command               */ /* / */
228    "secondarybinary", /* mp_secondary_binary_command    */ /* an operator at the binary level: shifted (etc) */
229    "parametertype",   /* mp_parameter_commmand          */ /* primary expr suffix (etc) */
230    "controls",        /* mp_controls_command            */ /* controls */
231    "tension",         /* mp_tension_command             */ /* tension */
232    "atleast",         /* mp_at_least_command            */ /* atleast */
233    "curl",            /* mp_curl_command                */ /* curl */
234    "macrospecial",    /* mp_macro_special_command       */ /* quote, #@ (etc) */
235    "rightdelimiter",  /* mp_right_delimiter_command     */ /* the right delimiter of a matching pair */
236    "leftbracket",     /* mp_left_bracket_command        */ /* [ */
237    "rightbracket",    /* mp_right_bracket_command       */ /* ] */
238    "rightbrace",      /* mp_right_brace_command         */ /* } */
239    "with",            /* mp_with_option_command         */ /* withpen (etc) */
240    "thingstoadd",     /* mp_thing_to_add_command        */ /* addto contour doublepath also */
241    "of",              /* mp_of_command                  */ /* of */
242    "to",              /* mp_to_command                  */ /* to */
243    "step",            /* mp_step_command                */ /* step */
244    "until",           /* mp_until_command               */ /* until */
245    "within",          /* mp_within_command              */ /* within */
246    "assignment",      /* mp_assignment_command          */ /* := */
247    "colon",           /* mp_colon_command               */ /* : */
248    "comma",           /* mp_comma_command               */ /* , */
249    "semicolon",       /* mp_semicolon_command           */ /* ; */
250    "endgroup",        /* mp_end_group_command           */ /* endgroup */
251    "stop",            /* mp_stop_command                */ /* end dump */
252 // "outertag",        /* mp_outer_tag_command           */ /* protection code added to command code */
253    "undefinedcs",     /* mp_undefined_cs_command        */ /* protection code added to command code */
254    NULL
255};
256
257static const char *mplib_states[] = {
258    "normal",
259    "skipping",
260    "flushing",
261    "absorbing",
262    "var_defining",
263    "op_defining",
264    "loop_defining",
265    NULL
266};
267
268static const char *mplib_knot_states[] = {
269    "regular",
270    "begin",
271    "end",
272    "single",
273    NULL,
274};
275
276static const char *mplib_types[] = {
277    "undefined",       /* mp_undefined_type        */
278    "vacuous",         /* mp_vacuous_type          */
279    "boolean",         /* mp_boolean_type          */
280    "unknownboolean",  /* mp_unknown_boolean_type  */
281    "string",          /* mp_string_type           */
282    "unknownstring",   /* mp_unknown_string_type   */
283    "pen",             /* mp_pen_type              */
284    "unknownpen",      /* mp_unknown_pen_type      */
285    "nep",             /* mp_nep_type              */
286    "unknownnep",      /* mp_unknown_nep_type      */
287    "path",            /* mp_path_type             */
288    "unknownpath",     /* mp_unknown_path_type     */
289    "picture",         /* mp_picture_type          */
290    "unknownpicture",  /* mp_unknown_picture_type  */
291    "transform",       /* mp_transform_type        */
292    "color",           /* mp_color_type            */
293    "cmykcolor",       /* mp_cmykcolor_type        */
294    "pair",            /* mp_pair_type             */
295 // "script",          /*                          */
296    "numeric",         /* mp_numeric_type          */
297    "known",           /* mp_known_type            */
298    "dependent",       /* mp_dependent_type        */
299    "protodependent",  /* mp_proto_dependent_type  */
300    "independent",     /* mp_independent_type      */
301    "tokenlist",       /* mp_token_list_type       */
302    "structured",      /* mp_structured_type       */
303    "unsuffixedmacro", /* mp_unsuffixed_macro_type */
304    "suffixedmacro",   /* mp_suffixed_macro_type   */
305    NULL
306};
307
308static const char *mplib_colormodels[] = {
309    "no",
310    "grey",
311    "rgb",
312    "cmyk",
313    NULL
314};
315
316/*tex Some statistics. */
317
318typedef struct mplib_state_info {
319    int file_callbacks;
320    int text_callbacks;
321    int script_callbacks;
322    int log_callbacks;
323    int overload_callbacks;
324    int error_callbacks;
325    int warning_callbacks;
326} mplib_state_info;
327
328static mplib_state_info mplib_state = {
329    .file_callbacks     = 0,
330    .text_callbacks     = 0,
331    .script_callbacks   = 0,
332    .log_callbacks      = 0,
333    .overload_callbacks = 0,
334    .error_callbacks    = 0,
335    .warning_callbacks  = 0,
336};
337
338/*tex
339
340    We need a few metatable identifiers in order to access the metatables for the main object and result
341    userdata. The following code is now replaced by the method that uses keys.
342
343*/
344
345/*
346# define MP_METATABLE_INSTANCE "mp.instance"
347# define MP_METATABLE_FIGURE   "mp.figure"
348# define MP_METATABLE_OBJECT   "mp.object"
349*/
350
351/*tex
352    This is used for heuristics wrt curves or lines. The default value is rather small
353    and often leads to curved rectangles.
354
355    \starttabulate[||||]
356    \NC 131/65536.0           \NC 0.0019989013671875 \NC default \NC \NR
357    \NC 0.001 * 0x7FFF/0x4000 \NC 0.0019999389648438 \NC kind of the default \NC \NR
358    \NC  32/16000.0           \NC 0.002              \NC somewhat cleaner \NC \NR
359    \NC  10/ 2000.0           \NC 0.005              \NC often good enough \NC \NR
360    \stoptabulate
361
362*/
363
364# define default_bend_tolerance 131/65536.0
365# define default_move_tolerance 131/65536.0
366
367typedef enum mp_variables {
368    mp_bend_tolerance = 1,
369    mp_move_tolerance = 2,
370} mp_variables;
371
372static lua_Number mplib_aux_get_bend_tolerance(lua_State *L, int slot)
373{
374    lua_Number tolerance;
375    lua_getiuservalue(L, slot, mp_bend_tolerance);
376    tolerance = lua_tonumber(L, -1);
377    lua_pop(L, 1);
378    return tolerance;
379}
380
381static lua_Number mplib_aux_get_move_tolerance(lua_State *L, int slot)
382{
383    lua_Number tolerance;
384    lua_getiuservalue(L, slot, mp_move_tolerance);
385    tolerance = lua_tonumber(L, -1);
386    lua_pop(L, 1);
387    return tolerance;
388}
389
390static void mplib_aux_set_bend_tolerance(lua_State *L, lua_Number tolerance)
391{
392    lua_pushnumber(L, tolerance);
393    lua_setiuservalue(L, -2, mp_bend_tolerance);
394}
395
396static void mplib_aux_set_move_tolerance(lua_State *L, lua_Number tolerance)
397{
398    lua_pushnumber(L, tolerance);
399    lua_setiuservalue(L, -2, mp_move_tolerance);
400}
401
402static inline char *lmt_string_from_index(lua_State *L, int n)
403{
404    size_t l;
405    const char *x = lua_tolstring(L, n, &l);
406 // return  (x && l > 0) ? lmt_generic_strdup(x) : NULL;
407    return  (x && l > 0) ? lmt_memory_strdup(x) : NULL;
408}
409
410static inline char *lmt_lstring_from_index(lua_State *L, int n, size_t *l)
411{
412    const char *x = lua_tolstring(L, n, l);
413 // return  (x && l > 0) ? lmt_generic_strdup(x) : NULL;
414    return  (x && l > 0) ? lmt_memory_strdup(x) : NULL;
415}
416
417static void mplib_aux_invalid_object_warning(const char * detail)
418{
419    tex_formatted_warning("mp lib","lua <mp %s> expected", detail);
420}
421
422static void mplib_aux_invalid_object_error(const char * detail)
423{
424    tex_formatted_error("mp lib","lua <mp %s> expected", detail);
425}
426
427static inline MP *mplib_aux_is_mpud(lua_State *L, int n)
428{
429    MP *p = (MP *) lua_touserdata(L, n);
430    if (p && lua_getmetatable(L, n)) {
431        lua_get_metatablelua(mplib_instance);
432        if (! lua_rawequal(L, -1, -2)) {
433            p = NULL;
434        }
435        lua_pop(L, 2);
436        if (p) {
437            return p;
438        }
439    }
440    mplib_aux_invalid_object_error("instance");
441    return NULL;
442}
443
444static inline MP mplib_aux_is_mp(lua_State *L, int n)
445{
446    MP *p = (MP *) lua_touserdata(L, n);
447    if (p && lua_getmetatable(L, n)) {
448        lua_get_metatablelua(mplib_instance);
449        if (! lua_rawequal(L, -1, -2)) {
450            p = NULL;
451        }
452        lua_pop(L, 2);
453        if (p) {
454            return *p;
455        }
456    }
457    mplib_aux_invalid_object_error("instance");
458    return NULL;
459}
460
461static inline mp_edge_object **mplib_aux_is_figure(lua_State *L, int n)
462{
463    mp_edge_object **p = (mp_edge_object **) lua_touserdata(L, n);
464    if (p && lua_getmetatable(L, n)) {
465        lua_get_metatablelua(mplib_figure);
466        if (! lua_rawequal(L, -1, -2)) {
467            p = NULL;
468        }
469        lua_pop(L, 2);
470        if (p) {
471            return p;
472        }
473    }
474    mplib_aux_invalid_object_warning("figure");
475    return NULL;
476}
477
478static inline mp_graphic_object **mplib_aux_is_graphic_object(lua_State *L, int n)
479{
480    mp_graphic_object **p = (mp_graphic_object **) lua_touserdata(L, n);
481    if (p && lua_getmetatable(L, n)) {
482        lua_get_metatablelua(mplib_object);
483        if (! lua_rawequal(L, -1, -2)) {
484            p = NULL;
485        }
486        lua_pop(L, 2);
487        if (p) {
488            return p;
489        }
490    }
491    mplib_aux_invalid_object_warning("object");
492    return NULL;
493}
494
495/*tex In the next array entry 0 is not used */
496
497static int mplib_values_type[mp_stop_bounds_code + 1] = { 0 };
498static int mplib_values_knot[6] = { 0 };
499
500static void mplib_aux_initialize_lua(lua_State *L)
501{
502    (void) L;
503    mplib_values_type[mp_fill_code]         = lua_key_index(fill);
504    mplib_values_type[mp_stroked_code]      = lua_key_index(outline);
505    mplib_values_type[mp_start_clip_code]   = lua_key_index(start_clip);
506    mplib_values_type[mp_start_group_code]  = lua_key_index(start_group);
507    mplib_values_type[mp_start_bounds_code] = lua_key_index(start_bounds);
508    mplib_values_type[mp_stop_clip_code]    = lua_key_index(stop_clip);
509    mplib_values_type[mp_stop_group_code]   = lua_key_index(stop_group);
510    mplib_values_type[mp_stop_bounds_code]  = lua_key_index(stop_bounds);
511
512    mplib_values_knot[mp_endpoint_knot]  = lua_key_index(endpoint);
513    mplib_values_knot[mp_explicit_knot]  = lua_key_index(explicit);
514    mplib_values_knot[mp_given_knot]     = lua_key_index(given);
515    mplib_values_knot[mp_curl_knot]      = lua_key_index(curl);
516    mplib_values_knot[mp_open_knot]      = lua_key_index(open);
517    mplib_values_knot[mp_end_cycle_knot] = lua_key_index(end_cycle);
518}
519
520static void mplib_aux_push_pentype(lua_State *L, mp_graphic_knot h)
521{
522    if (h && h == h->next) {
523        lua_push_value_at_key(L, type, elliptical);
524    }
525}
526
527static int mplib_set_tolerance(lua_State *L)
528{
529    MP mp = mplib_aux_is_mp(L, 1);
530    if (mp) {
531        mplib_aux_set_bend_tolerance(L, luaL_optnumber(L, 2, default_bend_tolerance));
532        mplib_aux_set_move_tolerance(L, luaL_optnumber(L, 3, default_move_tolerance));
533    }
534    return 0;
535}
536
537static int mplib_get_tolerance(lua_State *L)
538{
539
540    if (lua_type(L, 1) == LUA_TUSERDATA) {
541        MP mp = mplib_aux_is_mp(L, 1);
542        if (mp) {
543            lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1));
544            lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1));
545            return 2;
546        } else {
547            return 0;
548        }
549    } else {
550        lua_pushnumber(L, default_bend_tolerance);
551        lua_pushnumber(L, default_move_tolerance);
552        return 2;
553    }
554}
555
556/*tex
557
558    We start by defining the needed callback routines for the library. We could store them per
559    instance but it has no advantages so that will be done when we feel the need.
560
561*/
562
563static int mplib_aux_register_function(lua_State *L, int old_id)
564{
565    if (! (lua_isfunction(L, -1) || lua_isnil(L, -1))) {
566        return 0;
567    } else {
568        lua_pushvalue(L, -1);
569        if (old_id) {
570            luaL_unref(L, LUA_REGISTRYINDEX, old_id);
571        }
572        return luaL_ref(L, LUA_REGISTRYINDEX); /*tex |new_id| */
573    }
574}
575
576static int mplib_aux_find_file_function(lua_State *L, MP_options *options)
577{
578    options->find_file_id = mplib_aux_register_function(L, options->find_file_id);
579    return (! options->find_file_id);
580}
581
582static int mplib_aux_run_script_function(lua_State *L, MP_options *options)
583{
584    options->run_script_id = mplib_aux_register_function(L, options->run_script_id);
585    return (! options->run_script_id);
586}
587
588static int mplib_aux_run_internal_function(lua_State *L, MP_options *options)
589{
590    options->run_internal_id = mplib_aux_register_function(L, options->run_internal_id);
591    return (! options->run_internal_id);
592}
593
594static int mplib_aux_make_text_function(lua_State *L, MP_options *options)
595{
596    options->make_text_id = mplib_aux_register_function(L, options->make_text_id);
597    return (! options->make_text_id);
598}
599
600static int mplib_aux_run_logger_function(lua_State *L, MP_options *options)
601{
602    options->run_logger_id = mplib_aux_register_function(L, options->run_logger_id);
603    return (! options->run_logger_id);
604}
605
606static int mplib_aux_run_overload_function(lua_State *L, MP_options *options)
607{
608    options->run_overload_id = mplib_aux_register_function(L, options->run_overload_id);
609    return (! options->run_overload_id);
610}
611
612static int mplib_aux_run_error_function(lua_State *L, MP_options *options)
613{
614    options->run_error_id = mplib_aux_register_function(L, options->run_error_id);
615    return (! options->run_error_id);
616}
617
618static int mplib_aux_open_file_function(lua_State *L, MP_options *options)
619{
620    options->open_file_id = mplib_aux_register_function(L, options->open_file_id);
621    return (! options->open_file_id);
622}
623
624static char *mplib_aux_find_file(MP mp, const char *fname, const char *fmode, int ftype)
625{
626    if (mp->find_file_id) {
627        lua_State *L = (lua_State *) mp_userdata(mp);
628        int stacktop = lua_gettop(L);
629        char *s = NULL;
630        lua_rawgeti(L, LUA_REGISTRYINDEX, mp->find_file_id);
631        /*tex For use at the \METAPOST\ end: */
632        lua_pushstring(L, fname);
633        lua_pushstring(L, fmode); /* "r" "w" */
634        /*tex For use at the \LUA\ end: */
635        if (ftype > mp_filetype_text) {
636            lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text);
637        } else {
638            lua_pushstring(L, mplib_filetype_names[ftype]);
639        }
640        ++mplib_state.file_callbacks;
641        if (lua_pcall(L, 3, 1, 0)) {
642            tex_formatted_warning("mplib", "find file: %s", lua_tostring(L, -1));
643        } else {
644            s = lmt_string_from_index(L, -1);
645        }
646        lua_settop(L, stacktop);
647        return s;
648    } else if (fmode[0] != 'r' || (! access(fname, R_OK)) || ftype) {
649     // return lmt_generic_strdup(fname);
650        return lmt_memory_strdup(fname);
651    }
652    return NULL;
653}
654
655/*
656
657    In retrospect we could have has a more granular approach: a flag that tells that the result is
658    a string to be scanned. Maybe I'll that anyway but it definitely means an adaption of the low
659    level interface we have in MetaFun. Also, the interface would be a bit ugly because then we
660    would like to handle values as in the 'whatever' function which in turn means a flag indicating
661    that a string is a string and not something to be scanned as tokens, and being compatible then
662    means that this flag comes after the string which kind of conflicts with multiple (number)
663    arguments ... in the end we gain too little to accept such an ugly mess as option. So, we stick
664    to:
665
666    -- nil     : nothing is injected and no scanning will happen
667    -- string  : passed on in order to be scanned
668    -- table   : passed on concatenated in order to be scanned
669    -- number  : injected as numeric (so no scanning later on)
670    -- boolean : injected as boolean (so no scanning later on)
671
672    and dealing with other datatypes is delegated to the injectors.
673
674*/
675
676static int mplib_aux_with_path(lua_State *L, MP mp, int index, int what, int multiple);
677
678static void mplib_aux_inject_whatever(lua_State *L, MP mp, int index)
679{
680    switch (lua_type(L, index)) {
681        case LUA_TBOOLEAN:
682            mp_push_boolean_value(mp, lua_toboolean(L, index));
683            break;
684        case LUA_TNUMBER:
685            mp_push_numeric_value(mp, lua_tonumber(L, index));
686            break;
687        case LUA_TSTRING:
688            {
689                size_t l;
690                const char *s = lua_tolstring(L, index, &l);
691                mp_push_string_value(mp, s, (int) l);
692                break;
693            }
694        case LUA_TTABLE:
695            {
696                if (lua_rawgeti(L, index, 1) == LUA_TTABLE) {
697                    /* table of tables */
698                    lua_pop(L, 1);
699                    mplib_aux_with_path(L, mp, index, 1, 0);
700                } else {
701                    lua_pop(L, 1);
702                    switch (lua_rawlen(L, index)) {
703                        case 2 :
704                            mp_push_pair_value(mp,
705                                lmt_number_from_table(L, index, 1, 0.0),
706                                lmt_number_from_table(L, index, 2, 0.0)
707                            );
708                            break;
709                        case 3 :
710                            mp_push_color_value(mp,
711                                lmt_number_from_table(L, index, 1, 0.0),
712                                lmt_number_from_table(L, index, 2, 0.0),
713                                lmt_number_from_table(L, index, 3, 0.0)
714                            );
715                            break;
716                        case 4 :
717                            mp_push_cmykcolor_value(mp,
718                                lmt_number_from_table(L, index, 1, 0.0),
719                                lmt_number_from_table(L, index, 2, 0.0),
720                                lmt_number_from_table(L, index, 3, 0.0),
721                                lmt_number_from_table(L, index, 4, 0.0)
722                            );
723                            break;
724                        case 6 :
725                            mp_push_transform_value(mp,
726                                lmt_number_from_table(L, index, 1, 0.0),
727                                lmt_number_from_table(L, index, 2, 0.0),
728                                lmt_number_from_table(L, index, 3, 0.0),
729                                lmt_number_from_table(L, index, 4, 0.0),
730                                lmt_number_from_table(L, index, 5, 0.0),
731                                lmt_number_from_table(L, index, 6, 0.0)
732                            );
733                            break;
734                    }
735                }
736                break;
737            }
738    }
739}
740
741static char *mplib_aux_return_whatever(lua_State *L, MP mp, int index)
742{
743    switch (lua_type(L, index)) {
744        case LUA_TBOOLEAN:
745            mp_push_boolean_value(mp, lua_toboolean(L, index));
746            break;
747        case LUA_TNUMBER:
748            mp_push_numeric_value(mp, lua_tonumber(L, index));
749            break;
750        /* A string is passed to scantokens. */
751        case LUA_TSTRING:
752            return lmt_string_from_index(L, index);
753        /*tex A table is concatenated and passed to scantokens. */
754        case LUA_TTABLE:
755            {
756                luaL_Buffer b;
757                lua_Integer n = (lua_Integer) lua_rawlen(L, index);
758                luaL_buffinit(L, &b);
759                for (lua_Integer i = 1; i <= n; i++) {
760                    lua_rawgeti(L, index, i);
761                    luaL_addvalue(&b);
762                    lua_pop(L, 1);
763                }
764                luaL_pushresult(&b);
765                return lmt_string_from_index(L, -1);
766            }
767    }
768    return NULL;
769}
770
771static char *mplib_aux_run_script(MP mp, const char *str, size_t len, int n)
772{
773    if (mp->run_script_id) {
774        lua_State *L = (lua_State *) mp_userdata(mp);
775        int stacktop = lua_gettop(L);
776        lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_script_id);
777        if (str) {
778            lua_pushlstring(L, str, len);
779        } else if (n > 0) {
780            lua_pushinteger(L, n);
781        } else {
782            lua_pushnil(L);
783        }
784        ++mplib_state.script_callbacks;
785        if (lua_pcall(L, 1, 2, 0)) {
786            tex_formatted_warning("mplib", "run script: %s", lua_tostring(L, -1));
787        } else if (lua_toboolean(L, -1)) {
788            /* value boolean */
789            mplib_aux_inject_whatever(L, mp, -2);
790            lua_settop(L, stacktop);
791            return NULL;
792        } else {
793            /* value nil */
794            char *s = mplib_aux_return_whatever(L, mp, -2);
795            lua_settop(L, stacktop);
796            return s;
797        }
798    }
799    return NULL;
800}
801
802static void mplib_aux_run_internal(MP mp, int action, int n, int type, const char *name)
803{
804    if (mp->run_internal_id) {
805        lua_State *L = (lua_State *) mp_userdata(mp);
806        ++mplib_state.script_callbacks; /* maybe a special counter */
807        lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_internal_id);
808        lua_pushinteger(L, action); /* 0=initialize, 1=save, 2=restore */
809        lua_pushinteger(L, n);
810        if (name) {
811            lua_pushinteger(L, type);
812            lua_pushstring(L, name);
813            if (! lua_pcall(L, 4, 0, 0)) {
814                return;
815            }
816        } else {
817            if (lua_pcall(L, 2, 0, 0)) {
818                return;
819            }
820        }
821        tex_formatted_warning("mplib", "run internal: %s", lua_tostring(L, -1));
822    }
823}
824
825static char *mplib_aux_make_text(MP mp, const char *str, size_t len, int mode)
826{
827    if (mp->make_text_id) {
828        lua_State *L = (lua_State *) mp_userdata(mp);
829        int stacktop = lua_gettop(L);
830        char *s = NULL;
831        lua_rawgeti(L, LUA_REGISTRYINDEX, mp->make_text_id);
832        lua_pushlstring(L, str, len);
833        lua_pushinteger(L, mode);
834        ++mplib_state.text_callbacks;
835        if (lua_pcall(L, 2, 1, 0)) {
836            tex_formatted_warning("mplib", "make text: %s", lua_tostring(L, -1));
837        } else {
838            s = lmt_string_from_index(L, -1);
839        }
840        lua_settop(L, stacktop);
841        return s;
842    }
843    return NULL;
844}
845
846static void mplib_aux_run_logger(MP mp, int target, const char *str, size_t len)
847{
848    if (mp->run_logger_id) {
849        lua_State *L = (lua_State *) mp_userdata(mp);
850        int stacktop = lua_gettop(L);
851        lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_logger_id);
852        lua_pushinteger(L, target);
853        lua_pushlstring(L, str, len);
854        ++mplib_state.log_callbacks;
855        if (lua_pcall(L, 2, 0, 0)) {
856            tex_formatted_warning("mplib", "run logger: %s", lua_tostring(L, -1));
857        }
858        lua_settop(L, stacktop);
859    }
860}
861
862static int mplib_aux_run_overload(MP mp, int property, const char *str, int mode)
863{
864    int quit = 0;
865    if (mp->run_overload_id) {
866        lua_State *L = (lua_State *) mp_userdata(mp);
867        int stacktop = lua_gettop(L);
868        lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_overload_id);
869        lua_pushinteger(L, property);
870        lua_pushstring(L, str);
871        lua_pushinteger(L, mode);
872        ++mplib_state.overload_callbacks;
873        if (lua_pcall(L, 3, 1, 0)) {
874            tex_formatted_warning("mplib", "run overload: %s", lua_tostring(L, -1));
875            quit = 1;
876        } else {
877            quit = lua_toboolean(L, -1);
878        }
879        lua_settop(L, stacktop);
880    }
881    return quit;
882}
883
884static void mplib_aux_run_error(MP mp, const char *str, const char *help, int interaction)
885{
886    if (mp->run_error_id) {
887        lua_State *L = (lua_State *) mp_userdata(mp);
888        int stacktop = lua_gettop(L);
889        lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_error_id);
890        lua_pushstring(L, str);
891        lua_pushstring(L, help);
892        lua_pushinteger(L, interaction);
893        ++mplib_state.error_callbacks;
894        if (lua_pcall(L, 3, 0, 0)) {
895           tex_formatted_warning("mplib", "run error: %s", lua_tostring(L, -1));
896        }
897        lua_settop(L, stacktop);
898    }
899}
900
901/*
902
903    We keep all management in Lua, so for now we don't create an object. Files are normally closed
904    anyway. We can always make it nicer. The opener has to return an integer. A value zero indicates
905    that no file is opened.
906
907*/
908
909/* 
910    Are all these files kept open? 
911*/
912
913static void *mplib_aux_open_file(MP mp, const char *fname, const char *fmode, int ftype)
914{
915    if (mp->open_file_id) {
916        int *index = mp_memory_allocate(sizeof(int));
917        if (index) {
918            lua_State *L = (lua_State *) mp_userdata(mp);
919            int stacktop = lua_gettop(L);
920            lua_rawgeti(L, LUA_REGISTRYINDEX, mp->open_file_id);
921            lua_pushstring(L, fname);
922            lua_pushstring(L, fmode);
923            if (ftype > mp_filetype_text) {
924                lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text);
925            } else {
926                lua_pushstring(L, mplib_filetype_names[ftype]);
927            }
928            ++mplib_state.file_callbacks;
929            if (lua_pcall(L, 3, 1, 0)) {
930                *((int*) index) = 0;
931            } else if (lua_istable(L, -1)) {
932                lua_pushvalue(L, -1);
933                *((int*) index) = luaL_ref(L, LUA_REGISTRYINDEX);
934            } else {
935                tex_normal_warning("mplib", "open file: table expected");
936                *((int*) index) = 0;
937            }
938            lua_settop(L, stacktop);
939            return index;
940        }
941    }
942    return NULL;
943}
944
945# define mplib_pop_function(idx,tag) \
946    lua_rawgeti(L, LUA_REGISTRYINDEX, idx); \
947    lua_push_key(tag); \
948    lua_rawget(L, -2);
949
950static void mplib_aux_close_file(MP mp, void *index)
951{
952    if (mp->open_file_id && index) {
953        int idx = *((int*) index);
954        lua_State *L = (lua_State *) mp_userdata(mp);
955        int stacktop = lua_gettop(L);
956        mplib_pop_function(idx, close)
957        if (lua_isfunction(L, -1)) {
958            ++mplib_state.file_callbacks;
959            if (lua_pcall(L, 0, 0, 0)) {
960                /* no message */
961            } else {
962                /* nothing to be done here */
963            }
964        }
965        /*
966        if (index) {
967            luaL_unref(L, idx));
968        }
969        */
970        lua_settop(L, stacktop);
971        mp_memory_free(index);
972    }
973}
974
975static char *mplib_aux_read_file(MP mp, void *index, size_t *size)
976{
977    if (mp->open_file_id && index) {
978        lua_State *L = (lua_State *) mp_userdata(mp);
979        int stacktop = lua_gettop(L);
980        int idx = *((int*) index);
981        char *s = NULL;
982        mplib_pop_function(idx, reader)
983        if (lua_isfunction(L, -1)) {
984            ++mplib_state.file_callbacks;
985            if (lua_pcall(L, 0, 1, 0)) {
986                *size = 0;
987            } else if (lua_type(L, -1) == LUA_TSTRING) {
988                s = lmt_lstring_from_index(L, -1, size);
989            }
990        }
991        lua_settop(L, stacktop);
992        return s;
993    }
994    return NULL;
995}
996
997static void mplib_aux_write_file(MP mp, void *index, const char *s)
998{
999    if (mp->open_file_id && index) {
1000        lua_State *L = (lua_State *) mp_userdata(mp);
1001        int stacktop = lua_gettop(L);
1002        int idx = *((int*) index);
1003        mplib_pop_function(idx, writer)
1004        if (lua_isfunction(L, -1)) {
1005            lua_pushstring(L, s);
1006            ++mplib_state.file_callbacks;
1007            if (lua_pcall(L, 1, 0, 0)) {
1008                /* no message */
1009            } else {
1010                /* nothing to be done here */
1011            }
1012        }
1013        lua_settop(L, stacktop);
1014    }
1015}
1016
1017static int mplib_scan_next(lua_State *L)
1018{
1019    MP mp = mplib_aux_is_mp(L, 1);
1020    int token = 0;
1021    int mode = 0;
1022    int kind = 0;
1023    if (mp) {
1024        int keep = 0 ;
1025        if (lua_gettop(L) > 1) {
1026            keep = lua_toboolean(L, 2);
1027        }
1028        mp_scan_next_value(mp, keep, &token, &mode, &kind);
1029    }
1030    lua_pushinteger(L, token);
1031    lua_pushinteger(L, mode);
1032    lua_pushinteger(L, kind);
1033    return 3;
1034}
1035
1036static int mplib_scan_expression(lua_State *L)
1037{
1038    MP mp = mplib_aux_is_mp(L, 1);
1039    int kind = 0;
1040    if (mp) {
1041        int keep = 0 ;
1042        if (lua_gettop(L) > 1) {
1043            keep = lua_toboolean(L, 2);
1044        }
1045        mp_scan_expr_value(mp, keep, &kind);
1046    }
1047    lua_pushinteger(L, kind);
1048    return 1;
1049}
1050
1051static int mplib_scan_token(lua_State *L)
1052{
1053    MP mp = mplib_aux_is_mp(L, 1);
1054    int token = 0;
1055    int mode = 0;
1056    int kind = 0;
1057    if (mp) {
1058        int keep = 0 ;
1059        if (lua_gettop(L) > 1) {
1060            keep = lua_toboolean(L, 2);
1061        }
1062        mp_scan_token_value(mp, keep, &token, &mode, &kind);
1063    }
1064    lua_pushinteger(L, token);
1065    lua_pushinteger(L, mode);
1066    lua_pushinteger(L, kind);
1067    return 3;
1068}
1069
1070static int mplib_skip_token(lua_State *L)
1071{
1072    MP mp = mplib_aux_is_mp(L, 1);
1073    lua_pushboolean(L, mp ? mp_skip_token_value(mp, lmt_tointeger(L, 2)) : 0);
1074    return 1;
1075}
1076
1077static int mplib_scan_symbol(lua_State *L)
1078{
1079    MP mp = mplib_aux_is_mp(L, 1);
1080    if (mp) {
1081        int keep = 0 ;
1082        int expand = 1 ;
1083        int top = lua_gettop(L) ;
1084        char *s = NULL;
1085        if (top > 2) { // no need to check
1086            expand = lua_toboolean(L, 3);
1087        }
1088        if (top > 1) { // no need to check
1089            keep = lua_toboolean(L, 2);
1090        }
1091        mp_scan_symbol_value(mp, keep, &s, expand) ;
1092        if (s) {
1093            /* we could do without the copy */
1094            lua_pushstring(L, s);
1095            mp_memory_free(s);
1096            return 1;
1097        }
1098    }
1099    lua_pushliteral(L,"");
1100    return 1;
1101}
1102
1103static int mplib_scan_property(lua_State *L)
1104{
1105    MP mp = mplib_aux_is_mp(L, 1);
1106    if (mp) {
1107        int keep = 0 ;
1108        int top = lua_gettop(L) ;
1109        int type = 0 ;
1110        int property = 0 ;
1111        int detail = 0 ;
1112        char *s = NULL;
1113        if (top > 1) {
1114            keep = lua_toboolean(L, 2);
1115        }
1116        mp_scan_property_value(mp, keep, &type, &s, &property, &detail);
1117        if (s) {
1118            lua_pushinteger(L, type);
1119            lua_pushstring(L, s);
1120            lua_pushinteger(L, property);
1121            lua_pushinteger(L, detail);
1122            mp_memory_free(s);
1123            return 4;
1124        }
1125    }
1126    return 0;
1127}
1128
1129/*tex
1130    A circle has 8 points and a square 4 so let's just start with 8 slots in the table. 
1131*/
1132
1133// static int aux_is_curved_gr(mp_graphic_knot ith, mp_graphic_knot pth, lua_Number tolerance)
1134// {
1135//     lua_Number d = pth->left_x - ith->right_x;
1136//     if (fabs(ith->right_x - ith->x_coord - d) <= tolerance && fabs(pth->x_coord - pth->left_x - d) <= tolerance) {
1137//         d = pth->left_y - ith->right_y;
1138//         if (fabs(ith->right_y - ith->y_coord - d) <= tolerance && fabs(pth->y_coord - pth->left_y - d) <= tolerance) {
1139//             return 0;
1140//         }
1141//     }
1142//     return 1;
1143// }
1144
1145/*tex 
1146
1147The first three tests are okay for most cases, but control points can run ourside the natural 
1148boundingbox of the path. So we need two more tests. The final two tests are for the case when the 
1149control point lie on the other side of the other point (so to say). One could use a different 
1150factor in front of the parentheses, but I have not managed to find it (there are two control 
1151points, so t might be complicated in the end). Here we only test if it is on the other side
1152of the other point. If so, we mark it as curve. Thus, if this is not the case, then the control 
1153points lie between the points, and we should be safe.
1154
1155We (MS & HH) dis lots of tests and eventually decided that adding |withcurvature| made sense, 
1156if only for experimenting and documentation. The current solution is different from what we had 
1157before (pre March 2024). 
1158
1159*/
1160
1161static int aux_is_curved_gr(mp_graphic_knot ith, mp_graphic_knot pth, lua_Number tolerance)
1162{
1163    lua_Number v1x, v1y, v2x, v2y, v3x, v3y, eps;
1164    v1x = ith->right_x - ith->x_coord;
1165    v1y = ith->right_y - ith->y_coord;
1166    v2x = pth->left_x  - pth->x_coord;
1167    v2y = pth->left_y  - pth->y_coord;
1168    eps = fabs(v1x*v2y - v2x*v1y);
1169    if (eps > tolerance) {
1170        return 1;
1171    } 
1172    v3x = pth->x_coord - ith->x_coord;
1173    v3y = pth->y_coord - ith->y_coord;
1174    eps = fabs(v3x*v2y - v2x*v3y);
1175    if (eps > tolerance) {
1176        return 1;
1177    } 
1178    eps = fabs(v3x*v1y - v1x*v3y);
1179    if (eps > tolerance) {
1180        return 1;
1181    } 
1182    eps = v1x * v3x + v1y * v3y; /* \im { v__1 \cdot v__3 = |v__1| \times |v__3|\times cos([v__1,v__3]) } */
1183    if (eps < 0) {
1184        return 1;
1185    }
1186    eps = v2x * v3x + v2y * v3y; /* \im { v__2 \cdot v__3 = |v__2| \times |v__3|\times cos([v__2,v__3]) } */
1187    if  (eps > 0) {
1188        return 1;
1189    }
1190    eps = (v1x * v1x + v1y * v1y) - (v3x * v3x + v3y * v3y) ; /* checking lengths */
1191    if (eps > 0) {
1192        return 1;
1193    }
1194    eps = (v2x * v2x + v2y * v2y) - (v3x * v3x + v3y * v3y); /* checking lengths */
1195    if (eps > 0) {
1196        return 1;
1197    }
1198    return 0;
1199}
1200
1201static int aux_is_duplicate_gr(mp_graphic_knot pth, mp_graphic_knot nxt, lua_Number tolerance)
1202{
1203    return (fabs(pth->x_coord - nxt->x_coord) <= tolerance && fabs(pth->y_coord - nxt->y_coord) <= tolerance);
1204}
1205
1206# define valid_knot_type(t) (t >= mp_endpoint_knot && t <= mp_end_cycle_knot) /* pens can act weird here */
1207
1208// -68.031485 2.83464 l
1209// -68.031485 2.83464 -68.031485 -2.83464 -68.031485 -2.83464 c
1210
1211static void mplib_aux_push_path(lua_State *L, mp_graphic_knot h, int ispen, lua_Number bendtolerance, lua_Number movetolerance, int curvature)
1212{
1213    if (h) {
1214        int i = 0;
1215        mp_graphic_knot p = h;
1216        mp_graphic_knot q = h;
1217        int iscycle = 1;
1218        curvature = curvature == mp_always_curvature_code;
1219        lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1);
1220        do {
1221            mp_graphic_knot n = p->next;
1222            int lt = p->left_type;
1223            int rt = p->right_type;
1224            if (ispen) {
1225                lua_createtable(L, 0, 6);
1226            } else if (i > 0 && p != h && n != h && aux_is_duplicate_gr(p, n, movetolerance) && (curvature || aux_is_curved_gr(p, n, bendtolerance)) ) {
1227                n->left_x = p->left_x;
1228                n->left_y = p->left_y;
1229                goto NEXTONE;
1230            } else {
1231                int ln = lt != mp_explicit_knot;
1232                int rn = rt != mp_explicit_knot;
1233                int st = p->state;
1234                int ic = curvature || (i > 0 && aux_is_curved_gr(q, p, bendtolerance));
1235                lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0) + (st ? 1: 0));
1236                if (ln && valid_knot_type(lt)) {
1237                    lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]);
1238                }
1239                if (rn && valid_knot_type(rt)) {
1240                    lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]);
1241                }
1242                if (ic) {
1243                    lua_push_boolean_at_key(L, curved, 1);
1244                }
1245                if (st) {
1246                    lua_push_integer_at_key(L, state, st);
1247                }
1248                lua_push_number_at_key(L, x_coord, p->x_coord);
1249                lua_push_number_at_key(L, y_coord, p->y_coord);
1250                lua_push_number_at_key(L, left_x,  p->left_x);
1251                lua_push_number_at_key(L, left_y,  p->left_y);
1252                lua_push_number_at_key(L, right_x, p->right_x);
1253                lua_push_number_at_key(L, right_y, p->right_y);
1254                lua_rawseti(L, -2, ++i);
1255                if (rt == mp_endpoint_knot) {
1256                    iscycle = 0;
1257                    break;
1258                }
1259            }
1260          NEXTONE:   
1261            q = p;
1262            p = n;
1263        } while (p && p != h);
1264        if (iscycle && i > 1 && (curvature || aux_is_curved_gr(q, h, bendtolerance))) {
1265            lua_rawgeti(L, -1, 1);
1266            lua_push_boolean_at_key(L, curved, 1);
1267            lua_pop(L, 1);
1268        }
1269        if (ispen) {
1270            lua_push_boolean_at_key(L, pen, 1);
1271        }
1272        lua_push_boolean_at_key(L, cycle, iscycle);
1273    } else {
1274        lua_pushnil(L);
1275    }
1276}
1277
1278static int aux_is_curved(MP mp, mp_knot ith, mp_knot pth, lua_Number tolerance)
1279{
1280    lua_Number d = mp_number_as_double(mp, pth->left_x) - mp_number_as_double(mp, ith->right_x);
1281    if (fabs(mp_number_as_double(mp, ith->right_x) - mp_number_as_double(mp, ith->x_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->x_coord) - mp_number_as_double(mp, pth->left_x) - d) <= tolerance) {
1282        d = mp_number_as_double(mp, pth->left_y) - mp_number_as_double(mp, ith->right_y);
1283        if (fabs(mp_number_as_double(mp, ith->right_y) - mp_number_as_double(mp, ith->y_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->y_coord) - mp_number_as_double(mp, pth->left_y) - d) <= tolerance) {
1284            return 0;
1285        }
1286    }
1287    return 1;
1288}
1289
1290static void aux_mplib_knot_to_path(lua_State *L, MP mp, mp_knot h, int ispen, int compact, int check, lua_Number bendtolerance) // maybe also movetolerance
1291{
1292    int i = 1;
1293    mp_knot p = h;
1294    mp_knot q = h;
1295    int iscycle = 1;
1296    lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1);
1297    if (compact) {
1298        do {
1299            lua_createtable(L, 2, 0);
1300            lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord));
1301            lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord));
1302            lua_rawseti(L, -2, i++);
1303            if (p->right_type == mp_endpoint_knot) {
1304                iscycle = 0;
1305                break;
1306            } else {
1307                p = p->next;
1308            }
1309        } while (p && p != h);
1310    } else {
1311        do {
1312            int lt = p->left_type;
1313            int rt = p->right_type;
1314            int ln = lt != mp_explicit_knot;
1315            int rn = rt != mp_explicit_knot;
1316            int ic = check && (i > 1) && aux_is_curved(mp, q, p, bendtolerance);
1317            lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0));
1318            if (ln && valid_knot_type(lt)) {
1319                lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]);
1320            } else {
1321                /* a pen */
1322            }
1323            if (rn && valid_knot_type(rt)) {
1324                lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]);
1325            } else {
1326                /* a pen */
1327            }
1328            if (ic) {
1329                lua_push_boolean_at_key(L, curved, 1);
1330            }
1331            lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord));
1332            lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord));
1333            lua_push_number_at_index(L, 3, mp_number_as_double(mp, p->left_x));
1334            lua_push_number_at_index(L, 4, mp_number_as_double(mp, p->left_y));
1335            lua_push_number_at_index(L, 5, mp_number_as_double(mp, p->right_x));
1336            lua_push_number_at_index(L, 6, mp_number_as_double(mp, p->right_y));
1337            lua_rawseti(L, -2, i++);
1338            if (rt == mp_endpoint_knot) {
1339                iscycle = 0;
1340                break;
1341            } else {
1342                q = p;
1343                p = p->next;
1344            }
1345        } while (p && p != h);
1346        if (check && iscycle && i > 1 && aux_is_curved(mp, q, h, bendtolerance)) {
1347            lua_rawgeti(L, -1, 1);
1348            lua_push_boolean_at_key(L, curved, 1);
1349            lua_pop(L, 1);
1350        }
1351    }
1352    if (ispen) {
1353        lua_push_boolean_at_key(L, pen, 1);
1354    }
1355    lua_push_boolean_at_key(L, cycle, iscycle);
1356}
1357
1358/*tex
1359
1360    A couple of scanners. I know what I want to change but not now. First some longer term
1361    experiments.
1362
1363*/
1364
1365# define push_number_in_slot(L,i,n) \
1366    lua_pushnumber(L, n); \
1367    lua_rawseti(L, -2, i);
1368
1369# define kind_of_expression(n) \
1370    lmt_optinteger(L, n, 0)
1371
1372static int mplib_scan_string(lua_State *L)
1373{
1374    MP mp = mplib_aux_is_mp(L, 1);
1375    if (mp) {
1376        char *s = NULL;
1377        size_t l = 0;
1378        mp_scan_string_value(mp, kind_of_expression(2), &s, &l) ;
1379        if (s) {
1380            lua_pushlstring(L, s, l);
1381            return 1;
1382        }
1383    }
1384    lua_pushliteral(L,"");
1385    return 1;
1386}
1387
1388static int mplib_scan_boolean(lua_State *L)
1389{
1390    MP mp = mplib_aux_is_mp(L, 1);
1391    int b = 0;
1392    if (mp) {
1393        mp_scan_boolean_value(mp, kind_of_expression(2), &b);
1394    }
1395    lua_pushboolean(L, b);
1396    return 1;
1397}
1398
1399static int mplib_scan_numeric(lua_State *L)
1400{
1401    MP mp = mplib_aux_is_mp(L, 1);
1402    double d = 0.0;
1403    if (mp) {
1404        mp_scan_numeric_value(mp, kind_of_expression(2), &d);
1405    }
1406    lua_pushnumber(L, d);
1407    return 1;
1408}
1409
1410static int mplib_scan_integer(lua_State *L)
1411{
1412    MP mp = mplib_aux_is_mp(L, 1);
1413    double d = 0.0;
1414    if (mp) {
1415        mp_scan_numeric_value(mp, kind_of_expression(2), &d);
1416    }
1417    lua_pushinteger(L, (int) d); /* floored */
1418    return 1;
1419}
1420
1421static int mplib_scan_pair(lua_State *L)
1422{
1423    MP mp = mplib_aux_is_mp(L, 1);
1424    double x = 0.0;
1425    double y = 0.0;
1426    if (mp) {
1427        mp_scan_pair_value(mp, kind_of_expression(3), &x, &y);
1428    }
1429    if (lua_toboolean(L, 2)) {
1430        lua_createtable(L, 2, 0);
1431        push_number_in_slot(L, 1, x);
1432        push_number_in_slot(L, 2, y);
1433        return 1;
1434    } else {
1435        lua_pushnumber(L, x);
1436        lua_pushnumber(L, y);
1437        return 2;
1438    }
1439}
1440
1441static int mplib_scan_color(lua_State *L)
1442{
1443    MP mp = mplib_aux_is_mp(L, 1);
1444    double r = 0.0;
1445    double g = 0.0;
1446    double b = 0.0;
1447    if (mp) {
1448        mp_scan_color_value(mp, kind_of_expression(3), &r, &g, &b);
1449    }
1450    if (lua_toboolean(L, 2)) {
1451        lua_createtable(L, 3, 0);
1452        push_number_in_slot(L, 1, r);
1453        push_number_in_slot(L, 2, g);
1454        push_number_in_slot(L, 3, b);
1455        return 1;
1456    } else {
1457        lua_pushnumber(L, r);
1458        lua_pushnumber(L, g);
1459        lua_pushnumber(L, b);
1460        return 3;
1461    }
1462}
1463
1464static int mplib_scan_cmykcolor(lua_State *L)
1465{
1466    MP mp = mplib_aux_is_mp(L, 1);
1467    double c = 0.0;
1468    double m = 0.0;
1469    double y = 0.0;
1470    double k = 0.0;
1471    if (mp) {
1472        mp_scan_cmykcolor_value(mp, kind_of_expression(3), &c, &m, &y, &k);
1473    }
1474    if (lua_toboolean(L, 2)) {
1475        lua_createtable(L, 4, 0);
1476        push_number_in_slot(L, 1, c);
1477        push_number_in_slot(L, 2, m);
1478        push_number_in_slot(L, 3, y);
1479        push_number_in_slot(L, 4, k);
1480        return 1;
1481    } else {
1482        lua_pushnumber(L, c);
1483        lua_pushnumber(L, m);
1484        lua_pushnumber(L, y);
1485        lua_pushnumber(L, k);
1486        return 4;
1487    }
1488}
1489
1490static int mplib_scan_transform(lua_State *L)
1491{
1492    MP mp = mplib_aux_is_mp(L, 1);
1493    double x  = 0.0;
1494    double y  = 0.0;
1495    double xx = 0.0;
1496    double xy = 0.0;
1497    double yx = 0.0;
1498    double yy = 0.0;
1499    if (mp) {
1500        mp_scan_transform_value(mp, kind_of_expression(3), &x, &y, &xx, &xy, &yx, &yy);
1501    }
1502    if (lua_toboolean(L, 2)) {
1503        lua_createtable(L, 6, 0);
1504        push_number_in_slot(L, 1, x);
1505        push_number_in_slot(L, 2, y);
1506        push_number_in_slot(L, 3, xx);
1507        push_number_in_slot(L, 4, xy);
1508        push_number_in_slot(L, 5, yx);
1509        push_number_in_slot(L, 6, yy);
1510        return 1;
1511    } else {
1512        lua_pushnumber(L, x);
1513        lua_pushnumber(L, y);
1514        lua_pushnumber(L, xx);
1515        lua_pushnumber(L, xy);
1516        lua_pushnumber(L, yx);
1517        lua_pushnumber(L, yy);
1518        return 6;
1519    }
1520}
1521
1522static int mplib_scan_path(lua_State *L) /* 1=mp 2=compact 3=kind(prim) 4=check */
1523{
1524    MP mp = mplib_aux_is_mp(L, 1);
1525    if (mp) {
1526        mp_knot p = NULL;
1527        lua_Number t = mplib_aux_get_bend_tolerance(L, 1); /* iuservalue */
1528        mp_scan_path_value(mp, kind_of_expression(3), &p);
1529        if (p) {
1530            aux_mplib_knot_to_path(L, mp, p, 0, lua_toboolean(L, 2), lua_toboolean(L, 4), t);
1531            return 1;
1532        }
1533    }
1534    return 0;
1535}
1536
1537static int mplib_scan_pen(lua_State *L)
1538{
1539    MP mp = mplib_aux_is_mp(L, 1);
1540    if (mp) {
1541        mp_knot p = NULL ;
1542        lua_Number t = mplib_aux_get_bend_tolerance(L, 1);
1543        mp_scan_path_value(mp, kind_of_expression(3), &p) ;
1544        if (p) {
1545            aux_mplib_knot_to_path(L, mp, p, 1, lua_toboolean(L, 2), lua_toboolean(L, 4), t);
1546            return 1;
1547        }
1548    }
1549    return 0;
1550}
1551
1552static int mplib_inject_string(lua_State *L)
1553{
1554    MP mp = mplib_aux_is_mp(L, 1);
1555    if (mp) {
1556        size_t l = 0;
1557        const char *s = lua_tolstring(L, 2, &l);
1558        mp_push_string_value(mp, s, (int) l);
1559    }
1560    return 0;
1561}
1562
1563static int mplib_inject_boolean(lua_State *L)
1564{
1565    MP mp = mplib_aux_is_mp(L, 1);
1566    if (mp) {
1567        int b = lua_toboolean(L, 2);
1568        mp_push_boolean_value(mp, b);
1569    }
1570    return 0;
1571}
1572
1573static int mplib_inject_numeric(lua_State *L)
1574{
1575    MP mp = mplib_aux_is_mp(L, 1);
1576    if (mp) {
1577        double d = lua_tonumber(L, 2);
1578        mp_push_numeric_value(mp, d);
1579    }
1580    return 0;
1581}
1582
1583static int mplib_inject_integer(lua_State *L)
1584{
1585    MP mp = mplib_aux_is_mp(L, 1);
1586    if (mp) {
1587        int i = lmt_tointeger(L, 2);
1588        mp_push_integer_value(mp, i);
1589    }
1590    return 0;
1591}
1592
1593static int mplib_inject_pair(lua_State *L)
1594{
1595    MP mp = mplib_aux_is_mp(L, 1);
1596    if (mp) {
1597        switch (lua_type(L, 2)) {
1598            case LUA_TNUMBER:
1599                mp_push_pair_value(mp,
1600                    luaL_optnumber(L, 2, 0),
1601                    luaL_optnumber(L, 3, 0)
1602                );
1603                break;
1604            case LUA_TTABLE:
1605                mp_push_pair_value(mp,
1606                    lmt_number_from_table(L, 2, 1, 0.0),
1607                    lmt_number_from_table(L, 2, 2, 0.0)
1608                );
1609                break;
1610        }
1611    }
1612    return 0;
1613}
1614
1615static int mplib_inject_color(lua_State *L)
1616{
1617    MP mp = mplib_aux_is_mp(L, 1);
1618    if (mp) {
1619        switch (lua_type(L, 2)) {
1620            case LUA_TNUMBER:
1621                mp_push_color_value(mp,
1622                    luaL_optnumber(L, 2, 0),
1623                    luaL_optnumber(L, 3, 0),
1624                    luaL_optnumber(L, 4, 0)
1625                );
1626                break;
1627            case LUA_TTABLE:
1628                mp_push_color_value(mp,
1629                    lmt_number_from_table(L, 2, 1, 0.0),
1630                    lmt_number_from_table(L, 2, 2, 0.0),
1631                    lmt_number_from_table(L, 2, 3, 0.0)
1632                );
1633                break;
1634        }
1635    }
1636    return 0;
1637}
1638
1639static int mplib_inject_cmykcolor(lua_State *L)
1640{
1641    MP mp = mplib_aux_is_mp(L, 1);
1642    if (mp) {
1643        switch (lua_type(L, 2)) {
1644            case LUA_TNUMBER:
1645                mp_push_cmykcolor_value(mp,
1646                    luaL_optnumber(L, 2, 0),
1647                    luaL_optnumber(L, 3, 0),
1648                    luaL_optnumber(L, 4, 0),
1649                    luaL_optnumber(L, 5, 0)
1650                );
1651                break;
1652            case LUA_TTABLE:
1653                mp_push_cmykcolor_value(mp,
1654                    lmt_number_from_table(L, 2, 1, 0.0),
1655                    lmt_number_from_table(L, 2, 2, 0.0),
1656                    lmt_number_from_table(L, 2, 3, 0.0),
1657                    lmt_number_from_table(L, 2, 4, 0.0)
1658                );
1659                break;
1660        }
1661    }
1662    return 0;
1663}
1664
1665static int mplib_inject_transform(lua_State *L)
1666{
1667    MP mp = mplib_aux_is_mp(L, 1);
1668    if (mp) {
1669        switch (lua_type(L, 2)) {
1670            case LUA_TNUMBER:
1671                mp_push_transform_value(mp,
1672                    luaL_optnumber(L, 2, 0), // 1
1673                    luaL_optnumber(L, 3, 0),
1674                    luaL_optnumber(L, 4, 0),
1675                    luaL_optnumber(L, 5, 0), // 1
1676                    luaL_optnumber(L, 6, 0),
1677                    luaL_optnumber(L, 7, 0)
1678                );
1679                break;
1680            case LUA_TTABLE:
1681                mp_push_transform_value(mp,
1682                    lmt_number_from_table(L, 2, 1, 0.0), // 1.0
1683                    lmt_number_from_table(L, 2, 2, 0.0),
1684                    lmt_number_from_table(L, 2, 3, 0.0),
1685                    lmt_number_from_table(L, 2, 4, 0.0), // 1.0
1686                    lmt_number_from_table(L, 2, 5, 0.0),
1687                    lmt_number_from_table(L, 2, 6, 0.0)
1688                );
1689                break;
1690        }
1691    }
1692    return 0;
1693}
1694
1695static int mplib_new(lua_State *L)
1696{
1697    MP *mpud = lua_newuserdatauv(L, sizeof(MP *), 2);
1698    if (mpud) {
1699        MP mp = NULL;
1700        struct MP_options *options = mp_options();
1701        lua_Number bendtolerance = default_bend_tolerance;
1702        lua_Number movetolerance = default_move_tolerance;
1703        options->userdata        = (void *) L;
1704        options->job_name        = NULL;
1705        options->utf8_mode       = 0;
1706        options->text_mode       = 0;
1707        options->show_mode       = 0;
1708        options->halt_on_error   = 0;
1709        options->find_file       = mplib_aux_find_file;
1710        options->open_file       = mplib_aux_open_file;
1711        options->close_file      = mplib_aux_close_file;
1712        options->read_file       = mplib_aux_read_file;
1713        options->write_file      = mplib_aux_write_file;
1714        options->run_script      = mplib_aux_run_script;
1715        options->run_internal    = mplib_aux_run_internal;
1716        options->run_logger      = mplib_aux_run_logger;
1717        options->run_overload    = mplib_aux_run_overload;
1718        options->run_error       = mplib_aux_run_error;
1719        options->make_text       = mplib_aux_make_text;
1720        options->shipout_backend = mplib_shipout_backend;
1721        if (lua_type(L, 1) == LUA_TTABLE) {
1722            lua_pushnil(L);
1723            while (lua_next(L, 1)) {
1724                if (lua_type(L, -2) == LUA_TSTRING) {
1725                    const char *s = lua_tostring(L, -2);
1726                    if (lua_key_eq(s, random_seed)) {
1727                        options->random_seed = (int) lua_tointeger(L, -1);
1728                    } else if (lua_key_eq(s, interaction)) {
1729                        options->interaction = luaL_checkoption(L, -1, "silent", mplib_interaction_options);
1730                    } else if (lua_key_eq(s, job_name)) {
1731                     // options->job_name = lmt_generic_strdup(lua_tostring(L, -1));
1732                        options->job_name = lmt_memory_strdup(lua_tostring(L, -1));
1733                    } else if (lua_key_eq(s, find_file)) {
1734                        if (mplib_aux_find_file_function(L, options)) {
1735                            tex_normal_warning("mplib", "find file: function expected");
1736                        }
1737                    } else if (lua_key_eq(s, run_script)) {
1738                        if (mplib_aux_run_script_function(L, options)) {
1739                            tex_normal_warning("mplib", "run script: function expected");
1740                        }
1741                    } else if (lua_key_eq(s, run_internal)) {
1742                        if (mplib_aux_run_internal_function(L, options)) {
1743                            tex_normal_warning("mplib", "run internal: function expected");
1744                        }
1745                    } else if (lua_key_eq(s, make_text)) {
1746                        if (mplib_aux_make_text_function(L, options)) {
1747                            tex_normal_warning("mplib", "make text: function expected");
1748                        }
1749                    } else if (lua_key_eq(s, math_mode)) {
1750                        options->math_mode = luaL_checkoption(L, -1, "scaled", mplib_math_options);
1751                    } else if (lua_key_eq(s, utf8_mode)) {
1752                        options->utf8_mode = (int) lua_toboolean(L, -1);
1753                    } else if (lua_key_eq(s, text_mode)) {
1754                        options->text_mode = (int) lua_toboolean(L, -1);
1755                    } else if (lua_key_eq(s, show_mode)) {
1756                        options->show_mode = (int) lua_toboolean(L, -1);
1757                    } else if (lua_key_eq(s, halt_on_error)) {
1758                        options->halt_on_error = (int) lua_toboolean(L, -1);
1759                    } else if (lua_key_eq(s, run_logger)) {
1760                        if (mplib_aux_run_logger_function(L, options)) {
1761                            tex_normal_warning("mplib", "run logger: function expected");
1762                        }
1763                    } else if (lua_key_eq(s, run_overload)) {
1764                        if (mplib_aux_run_overload_function(L, options)) {
1765                            tex_normal_warning("mplib", "run overload: function expected");
1766                        }
1767                    } else if (lua_key_eq(s, run_error)) {
1768                        if (mplib_aux_run_error_function(L, options)) {
1769                            tex_normal_warning("mplib", "run error: function expected");
1770                        }
1771                    } else if (lua_key_eq(s, open_file)) {
1772                        if (mplib_aux_open_file_function(L, options)) {
1773                            tex_normal_warning("mplib", "open file: function expected");
1774                        }
1775                    } else if (lua_key_eq(s, bend_tolerance)) {
1776                        bendtolerance = lua_tonumber(L, -1);
1777                    } else if (lua_key_eq(s, move_tolerance)) {
1778                        movetolerance = lua_tonumber(L, -1);
1779                    }
1780                }
1781                lua_pop(L, 1);
1782            }
1783        }
1784        if (! options->job_name || ! *(options->job_name)) {
1785            mp_memory_free(options); /* leaks */
1786            tex_normal_warning("mplib", "job_name is not set");
1787            goto BAD;
1788        }
1789        mp = mp_initialize(options);
1790        mp_memory_free(options); /* leaks */
1791        if (mp) {
1792            *mpud = mp;
1793            mplib_aux_set_bend_tolerance(L, bendtolerance);
1794            mplib_aux_set_move_tolerance(L, movetolerance);
1795         // luaL_getmetatable(L, MP_METATABLE_INSTANCE);
1796            lua_get_metatablelua(mplib_instance);
1797            lua_setmetatable(L, -2);
1798            return 1;
1799        }
1800    }
1801  BAD:
1802    lua_pushnil(L);
1803    return 1;
1804}
1805
1806# define mplib_collect_id(id) do { \
1807    if (id) { \
1808        luaL_unref(L, LUA_REGISTRYINDEX, id); \
1809    } \
1810} while(0)
1811
1812static int mplib_instance_collect(lua_State *L)
1813{
1814    MP *mpud = mplib_aux_is_mpud(L, 1);
1815    if (*mpud) {
1816        MP mp = *mpud;
1817        int run_logger_id = (mp)->run_logger_id;
1818        mplib_collect_id((mp)->find_file_id);
1819        mplib_collect_id((mp)->run_script_id);
1820        mplib_collect_id((mp)->run_internal_id);
1821        mplib_collect_id((mp)->run_overload_id);
1822        mplib_collect_id((mp)->run_error_id);
1823        mplib_collect_id((mp)->make_text_id);
1824        mplib_collect_id((mp)->open_file_id);
1825        mp_finish(mp);
1826        *mpud = NULL;
1827        mplib_collect_id(run_logger_id);
1828    }
1829    return 0;
1830}
1831
1832static int mplib_instance_tostring(lua_State *L)
1833{
1834    MP mp = mplib_aux_is_mp(L, 1);
1835    if (mp) {
1836        lua_pushfstring(L, "<mp.instance %p>", mp);
1837    } else {
1838        lua_pushnil(L);
1839    }
1840    return 1;
1841}
1842
1843static int mplib_aux_wrapresults(lua_State *L, mp_run_data *res, int status, lua_Number bendtolerance, lua_Number movetolerance)
1844{
1845    lua_newtable(L);
1846    if (res->edges) {
1847        /*tex 
1848            In needed, we could add some more to this table, like status info. 
1849        */
1850        struct mp_edge_object *p = res->edges;
1851        int i = 1;
1852        lua_push_key(fig);
1853        lua_newtable(L);
1854        while (p) {
1855            struct mp_edge_object **v = lua_newuserdatauv(L, sizeof(struct mp_edge_object *), 2);
1856            *v = p;
1857            mplib_aux_set_bend_tolerance(L, bendtolerance);
1858            mplib_aux_set_move_tolerance(L, movetolerance);
1859         // luaL_getmetatable(L, MP_METATABLE_FIGURE);
1860            lua_get_metatablelua(mplib_figure);
1861            lua_setmetatable(L, -2);
1862            lua_rawseti(L, -2, i);
1863            i++;
1864            p = p->next;
1865        }
1866        lua_rawset(L,-3);
1867        res->edges = NULL;
1868    }
1869    lua_push_integer_at_key(L, status, status);
1870    return 1;
1871}
1872
1873static int mplib_execute(lua_State *L)
1874{
1875    MP mp = mplib_aux_is_mp(L, 1);
1876    if (mp) {
1877        /* no string in slot 2 or an empty string means that we already filled the terminal */
1878        size_t l = 0;
1879        lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
1880        lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
1881        const char *s = lua_isstring(L, 2) ? lua_tolstring(L, 2, &l) : NULL;
1882        int h = mp_execute(mp, s, l);
1883        mp_run_data *res = mp_rundata(mp);
1884        return mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance);
1885    }
1886    lua_pushnil(L);
1887    return 1;
1888}
1889
1890static int mplib_finish(lua_State *L)
1891{
1892    MP *mpud = mplib_aux_is_mpud(L, 1);
1893    if (*mpud) {
1894        MP mp = *mpud;
1895        lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
1896        lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
1897        int h = mp_execute(mp, NULL, 0);
1898        mp_run_data *res = mp_rundata(mp);
1899        int i = mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance);
1900        mp_finish(mp);
1901        *mpud = NULL;
1902        return i;
1903    } else {
1904        lua_pushnil(L);
1905    }
1906    return 1;
1907}
1908
1909static int mplib_showcontext(lua_State *L)
1910{
1911    MP *mpud = mplib_aux_is_mpud(L, 1);
1912    if (*mpud) {
1913        MP mp = *mpud;
1914        mp_show_context(mp);
1915    }
1916    return 0;
1917}
1918
1919static int mplib_gethashentry(lua_State *L)
1920{
1921    MP *mpud = mplib_aux_is_mpud(L, 1);
1922    if (*mpud) {
1923        MP mp = *mpud;
1924        char *name = (char *) lua_tostring(L, 2);
1925        if (name) {
1926            mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol(mp, name);
1927            if (s) {
1928                mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL;
1929                lua_pushinteger(L, s->type);
1930                lua_pushinteger(L, s->property);
1931                if (q) {
1932                    lua_pushinteger(L, q->type);
1933                    return 3;
1934                } else if (s->property == 0x1) {
1935                    lua_pushinteger(L, s->v.data.indep.serial);
1936                    return 3;
1937                } else {
1938                    return 2;
1939                }
1940            }
1941        }
1942    }
1943    return 0;
1944}
1945
1946static int mplib_gethashentries(lua_State *L)
1947{
1948    MP *mpud = mplib_aux_is_mpud(L, 1);
1949    if (*mpud) {
1950        MP mp = *mpud;
1951        int full = lua_toboolean(L, 2);
1952        if (mp_initialize_symbol_traverse(mp)) {
1953            size_t n = 0;
1954            lua_newtable(L);
1955            while (1) {
1956                mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol_traverse(mp);
1957                if (s) {
1958                    if (full) {
1959                        mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL;
1960                        lua_createtable(L, (q || s->property == 0x1) ? 4 : 3, 0);
1961                        lua_pushinteger(L, s->type);
1962                        lua_rawseti(L, -2, 1);
1963                        lua_pushinteger(L, s->property);
1964                        lua_rawseti(L, -2, 2);
1965                        lua_pushlstring(L, (const char *) s->text->str, s->text->len);
1966                        lua_rawseti(L, -2, 3);
1967                        if (q) {
1968                            lua_pushinteger(L, q->type);
1969                            lua_rawseti(L, -2, 4);
1970                        } else if (s->property == 0x1) {
1971                            lua_pushinteger(L, s->v.data.indep.serial);
1972                            lua_rawseti(L, -2, 4);
1973                        }
1974                    } else {
1975                        lua_pushlstring(L, (const char *) s->text->str, s->text->len);
1976                    }
1977                    lua_rawseti(L, -2, ++n);
1978                } else {
1979                    break;
1980                }
1981            }
1982            mp_kill_symbol_traverse(mp);
1983            return 1;
1984        }
1985    }
1986    return 0;
1987}
1988
1989static int mplib_version(lua_State *L)
1990{
1991    char *s = mp_metapost_version();
1992    lua_pushstring(L, s);
1993    mp_memory_free(s);
1994    return 1;
1995}
1996
1997static int mplib_getstatistics(lua_State *L)
1998{
1999    MP mp = mplib_aux_is_mp(L, 1);
2000    if (mp) {
2001        lua_createtable(L, 0, 9);
2002        lua_push_integer_at_key(L, memory,     mp->var_used);                     /* bytes of node memory */
2003        lua_push_integer_at_key(L, hash,       mp->st_count);
2004        lua_push_integer_at_key(L, parameters, mp->max_param_stack);              /* allocated: mp->param_size */
2005        lua_push_integer_at_key(L, input,      mp->max_in_stack);                 /* allocated: mp->stack_size */
2006        lua_push_integer_at_key(L, tokens,     mp->num_token_nodes);
2007        lua_push_integer_at_key(L, pairs,      mp->num_pair_nodes);
2008        lua_push_integer_at_key(L, knots,      mp->num_knot_nodes);
2009        lua_push_integer_at_key(L, nodes,      mp->num_value_nodes);
2010        lua_push_integer_at_key(L, symbols,    mp->num_symbolic_nodes);
2011        lua_push_integer_at_key(L, characters, mp->max_pl_used);
2012        lua_push_integer_at_key(L, strings,    mp->max_strs_used);
2013        lua_push_integer_at_key(L, internals,  mp->int_ptr);                      /* allocates: mp->max_internal */
2014     /* lua_push_integer_at_key(L, buffer,     mp->max_buf_stack + 1); */         /* allocated: mp->buf_size */
2015     /* lua_push_integer_at_key(L, open,       mp->in_open_max - file_bottom); */ /* allocated: mp->max_in_open - file_bottom */
2016    } else {
2017        lua_pushnil(L);
2018    }
2019    return 1;
2020}
2021
2022static int mplib_getstatus(lua_State *L)
2023{
2024    MP mp = mplib_aux_is_mp(L, 1);
2025    if (mp) {
2026        lua_pushinteger(L, mp->scanner_status);
2027        return 1;
2028    } else {
2029        return 0;
2030    }
2031}
2032
2033static int mplib_aux_set_direction(lua_State *L, MP mp, mp_knot p) {
2034    double direction_x = (double) lua_tonumber(L, -1);
2035    double direction_y = 0;
2036    lua_pop(L, 1);
2037    lua_push_key(direction_y);
2038    if (lua_rawget(L, -2) == LUA_TNUMBER) {
2039        direction_y = (double) lua_tonumber(L, -1);
2040        lua_pop(L, 1);
2041        return mp_set_knot_direction(mp, p, direction_x, direction_y) ? 1 : 0;
2042    } else {
2043        return 0;
2044    }
2045}
2046
2047static int mplib_aux_set_left_curl(lua_State *L, MP mp, mp_knot p) {
2048    double curl = (double) lua_tonumber(L, -1);
2049    lua_pop(L, 1);
2050    return mp_set_knot_left_curl(mp, p, curl) ? 1 : 0;
2051}
2052
2053static int mplib_aux_set_left_tension(lua_State *L, MP mp, mp_knot p) {
2054    double tension = (double) lua_tonumber(L, -1);
2055    lua_pop(L, 1);
2056    return mp_set_knot_left_tension(mp, p, tension) ? 1 : 0;
2057}
2058
2059static int mplib_aux_set_left_control(lua_State *L, MP mp, mp_knot p) {
2060    double x = (double) lua_tonumber(L, -1);
2061    double y = 0;
2062    lua_pop(L, 1);
2063    lua_push_key(left_y);
2064    if (lua_rawget(L, -2) == LUA_TNUMBER) {
2065        y = (double) lua_tonumber(L, -1);
2066        lua_pop(L, 1);
2067        return mp_set_knot_left_control(mp, p, x, y) ? 1 : 0;
2068    } else {
2069        return 0;
2070    }
2071}
2072
2073static int mplib_aux_set_right_curl(lua_State *L, MP mp, mp_knot p) {
2074    double curl = (double) lua_tonumber(L, -1);
2075    lua_pop(L, 1);
2076    return mp_set_knot_right_curl(mp, p, curl) ? 1 : 0;
2077}
2078
2079static int mplib_aux_set_right_tension(lua_State *L, MP mp, mp_knot p) {
2080    double tension = (double) lua_tonumber(L, -1);
2081    lua_pop(L, 1);
2082    return mp_set_knot_right_tension(mp, p, tension) ? 1 : 0;
2083}
2084
2085static int mplib_aux_set_right_control(lua_State *L, MP mp, mp_knot p) {
2086    double x = (double) lua_tonumber(L, -1);
2087    lua_pop(L, 1);
2088    lua_push_key(right_y);
2089    if (lua_rawget(L, -2) == LUA_TNUMBER) {
2090        double y = (double) lua_tonumber(L, -1);
2091        lua_pop(L, 1);
2092        return mp_set_knot_right_control(mp, p, x, y) ? 1 : 0;
2093    } else {
2094        return 0;
2095    }
2096}
2097
2098static const char * mplib_aux_with_path_indexed(lua_State *L, MP mp, int index, int numpoints, int *curled, int *cyclic, mp_knot *first, mp_knot *p, mp_knot *q, mp_knot *f, mp_knot *l)
2099{
2100    int midcycle = 0;
2101    for (int i = 1; i <= numpoints; i++) {
2102        switch (lua_rawgeti(L, index, i)) { 
2103            case LUA_TTABLE:
2104                {
2105                    double x0, y0;
2106                    if (midcycle) { 
2107                        if (*first && i == numpoints) {
2108                            *cyclic = 0;
2109                            goto DONE;
2110                        } else if (*f && *l) { 
2111                            (*f)->left_type = mp_explicit_knot;
2112                            (*l)->right_type = mp_explicit_knot;
2113                        }
2114                        midcycle = 0; 
2115                        *f = NULL;
2116                        *l = NULL;
2117                    }
2118                    lua_rawgeti(L, -1, 1);
2119                    lua_rawgeti(L, -2, 2);
2120                    x0 = lua_tonumber(L, -2);
2121                    y0 = lua_tonumber(L, -1);
2122                    *q = *p;
2123                    *p = mp_append_knot_xy(mp, *p, x0, y0); /* makes end point */
2124                    lua_pop(L, 2);
2125                    if (*p) {
2126                        double x1, y1, x2, y2;
2127                        if (*curled) {
2128                            x1 = x0;
2129                            y1 = y0;
2130                            x2 = x0;
2131                            y2 = y0;
2132                        } else {
2133                            lua_rawgeti(L, -1, 3);
2134                            lua_rawgeti(L, -2, 4);
2135                            lua_rawgeti(L, -3, 5);
2136                            lua_rawgeti(L, -4, 6);
2137                            x1 = luaL_optnumber(L, -4, x0);
2138                            y1 = luaL_optnumber(L, -3, y0);
2139                            x2 = luaL_optnumber(L, -2, x0);
2140                            y2 = luaL_optnumber(L, -1, y0);
2141                            lua_pop(L, 4);
2142                        }
2143                        mp_set_knot_left_control(mp, *p, x1, y1);
2144                        mp_set_knot_right_control(mp, *p, x2, y2);
2145                        if (! *first) {
2146                            *first = *p;
2147                        }
2148                        if (! *f) {
2149                            *f = *p; 
2150                        }
2151                        *l = *p; 
2152                    } else {
2153                        return "knot creation failure";
2154                    }
2155                    break;
2156                }
2157            case LUA_TSTRING:
2158                {
2159                    const char *s = lua_tostring(L, -1);
2160                    if (lua_key_eq(s, cycle)) {
2161                        midcycle = 1;
2162                    } else if (lua_key_eq(s, append)) { 
2163                        if (*f && *l) { 
2164                            if (midcycle) {
2165                                (*f)->left_type = mp_explicit_knot;
2166                                (*l)->right_type = mp_explicit_knot;
2167                                midcycle = 0; 
2168                            }
2169                            (*f)->right_type = mp_explicit_knot;
2170                            (*l)->left_type = mp_explicit_knot;
2171                            (*f)->state = mp_begin_knot;
2172                            (*l)->state = mp_end_knot;   
2173                            *f = NULL;
2174                            *l = NULL;
2175                        }
2176                    }
2177                    break;
2178                }
2179        }
2180        /*tex Up the next item */
2181      DONE:
2182        lua_pop(L, 1);
2183    }
2184    if (midcycle) { 
2185        *cyclic = 1;
2186    }
2187    /* 
2188        We could handle the cycle here but we need to do it for the hashed variant anyway so let's
2189        stay with the old method and only do mid cycles here. 
2190    */
2191    return NULL;
2192}
2193
2194static const char * mplib_aux_with_path_hashed(lua_State *L, MP mp, mp_knot *first, mp_knot *p, mp_knot *q, int *solve)
2195{
2196    /* We can probably also use the _xy here. */
2197    int left_set = 0;
2198    int right_set = 0;
2199    double x_coord, y_coord;
2200    if (! lua_istable(L, -1)) {
2201        return "wrong argument types";
2202    }
2203    lua_push_key(x_coord);
2204    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2205        return "missing x coordinate";
2206    }
2207    x_coord = (double) lua_tonumber(L, -1);
2208    lua_pop(L, 1);
2209    lua_push_key(y_coord);
2210    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2211        return "missing y coordinate";
2212    }
2213    y_coord = (double) lua_tonumber(L, -1);
2214    lua_pop(L, 1);
2215    /* */
2216    *q = *p;
2217    if (*q) {
2218        /*tex
2219            We have to save the right_tension because |mp_append_knot| trashes it, believing that 
2220            it is as yet uninitialized .. I need to check this.
2221        */
2222        double saved_tension = mp_number_as_double(mp, (*p)->right_tension);
2223        *p = mp_append_knot(mp, *p, x_coord, y_coord);
2224        if (*p) {
2225            mp_set_knot_right_tension(mp, *q, saved_tension);
2226        }
2227    } else {
2228        *p = mp_append_knot(mp, *p, x_coord, y_coord);
2229    }
2230    if (! *p) {
2231        return "knot creation failure";
2232    }
2233    /* */
2234    if (! *first) {
2235        *first = *p;
2236    }
2237    lua_push_key(left_curl);
2238    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2239        lua_pop(L, 1);
2240    } else if (! mplib_aux_set_left_curl(L, mp, *p)) {
2241        return "failed to set left curl";
2242    } else {
2243        left_set  = 1;
2244        *solve = 1;
2245    }
2246    lua_push_key(left_tension);
2247    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2248        lua_pop(L, 1);
2249    } else if (left_set) {
2250        return "left side already set";
2251    } else if (! mplib_aux_set_left_tension(L, mp, *p)) {
2252        return "failed to set left tension";
2253    } else {
2254        left_set = 1;
2255        *solve = 1;
2256    }
2257    lua_push_key(left_x);
2258    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2259        lua_pop(L, 1); /* ignore left_y */
2260    } else if (left_set) {
2261        return "left side already set";
2262    } else if (! mplib_aux_set_left_control(L, mp, *p)) { /* also uses left_y */
2263        return "failed to set left control";
2264    }
2265    lua_push_key(right_curl);
2266    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2267        lua_pop(L, 1);
2268    } else if (! mplib_aux_set_right_curl(L, mp, *p)) {
2269        return "failed to set right curl";
2270    } else {
2271        right_set  = 1;
2272        *solve = 1;
2273    }
2274    lua_push_key(right_tension);
2275    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2276        lua_pop(L,1);
2277    } else if (right_set) {
2278        return "right side already set";
2279    } else if (! mplib_aux_set_right_tension(L, mp, *p)) {
2280        return "failed to set right tension";
2281    } else {
2282        right_set = 1;
2283        *solve = 1;
2284    }
2285    lua_push_key(right_x);
2286    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2287        lua_pop(L, 1);
2288    } else if (right_set) {
2289        return "right side already set";
2290    } else if (! mplib_aux_set_right_control(L, mp, *p)) {
2291        return "failed to set right control";
2292    }
2293    lua_push_key(direction_x);
2294    if (lua_rawget(L, -2) != LUA_TNUMBER) {
2295        lua_pop(L, 1);
2296    } else if (! mplib_aux_set_direction(L, mp, *p)) {
2297        return "failed to set direction";
2298    }
2299    return NULL;
2300}
2301
2302static int mplib_aux_with_path(lua_State *L, MP mp, int index, int inject, int multiple)
2303{
2304 // setbuf(stdout, NULL);
2305    if (! mp) {
2306        lua_pushboolean(L, 0);
2307        lua_pushstring(L, "valid instance expected");
2308        return 2;
2309    } else if (! lua_istable(L, index) || lua_rawlen(L, index) <= 0) {
2310        lua_pushboolean(L, 0);
2311        lua_pushstring(L, "non empty table expected");
2312        return 2;
2313    } else {
2314        mp_knot p = NULL;
2315        mp_knot q = NULL;
2316        mp_knot first = NULL;
2317        mp_knot f = NULL;
2318        mp_knot l = NULL;
2319        const char *errormsg = NULL;
2320        int cyclic = 0;
2321        int curled = 0;
2322        int solve = 0;
2323        int numpoints = (int) lua_rawlen(L, index);
2324        /*tex
2325            As a bonus we check for two keys. When an index is negative we come from the
2326            callback in which case we definitely cannot check the rest of the arguments.
2327        */
2328        if (multiple && lua_type(L, index + 1) == LUA_TBOOLEAN) {
2329            cyclic = lua_toboolean(L, index + 1);
2330        } else {
2331            lua_push_key(close);
2332            if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
2333                cyclic = lua_toboolean(L, -1);
2334            }
2335            lua_pop(L, 1);
2336            lua_push_key(cycle); /* wins */
2337            if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
2338                cyclic = lua_toboolean(L, -1);
2339            }
2340            lua_pop(L, 1);
2341        }
2342        if (multiple && lua_type(L, index + 2) == LUA_TBOOLEAN) {
2343            curled = lua_toboolean(L, index + 2);
2344        } else {
2345            lua_push_key(curled);
2346            if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
2347                curled = lua_toboolean(L, -1);
2348            }
2349            lua_pop(L, 1);
2350        }
2351        /*tex We build up the path. */
2352        if (lua_rawgeti(L, index, 1) == LUA_TTABLE) {
2353            lua_Unsigned len = lua_rawlen(L, -1);
2354            lua_pop(L, 1);
2355            if (len >= 2) {
2356                /* .. : p1 .. controls a and b         .. p2  : { p1 a   b   } */
2357                /* -- : p1 .. { curl 1 } .. { curl 1 } .. p2  : { p1 nil nil } */
2358                errormsg = mplib_aux_with_path_indexed(L, mp, index, numpoints, &curled, &cyclic, &first, &p, &q, &f, &l);
2359                if (errormsg) {
2360                   goto BAD;
2361                }
2362            } else if (len > 0) {
2363                errormsg = "messy table";
2364                goto BAD;
2365            } else {
2366                /*tex Here we have a list of tables with |path| and |append| keys. */
2367                for (int i = 1; i <= numpoints; i++) {
2368                    cyclic = 0;
2369                    f = NULL;
2370                    l = NULL;
2371                    switch (lua_rawgeti(L, index, i)) { 
2372                        case LUA_TTABLE: 
2373                            {
2374                                lua_push_key(path);
2375                                if (lua_rawget(L, -2) == LUA_TTABLE) {
2376                                    int n = (int) lua_rawlen(L, -1);
2377                                    errormsg = mplib_aux_with_path_indexed(L, mp, -1, n, &curled, &cyclic, &first, &p, &q, &f, &l);
2378                                    if (errormsg) {
2379                                       goto BAD;
2380                                    }
2381                                    lua_pop(L, 1); /* path value */
2382                                    lua_push_key(append);
2383                                    if (lua_rawget(L, -2) == LUA_TBOOLEAN) {
2384                                        f->right_type = mp_explicit_knot;
2385                                        l->left_type = mp_explicit_knot;
2386                                        f->state = mp_begin_knot;
2387                                        l->state = mp_end_knot;   
2388                                    }
2389                                    lua_pop(L, 1); /* append value */
2390                                 // if (i == numpoints) { 
2391                                 //     lua_push_key(cycle);
2392                                 //     if (lua_rawget(L, -2) == LUA_TBOOLEAN) {
2393                                 //         cyclic = 1;
2394                                 //     }
2395                                 //     lua_pop(L, 1); /* cycle value */
2396                                 // }
2397                                } else {
2398                                    lua_pop(L, 1);
2399                                    errormsg = mplib_aux_with_path_hashed(L, mp, &first, &p, &q, &solve);
2400                                    if (errormsg) {
2401                                        goto BAD;
2402                                    }
2403                                }
2404                            }
2405                            break;
2406                        case LUA_TSTRING:
2407                            /*tex Maybe some day also |append| and |cycle| here. */
2408                            break;
2409                    }
2410                    lua_pop(L, 1); /* table entry i */
2411                }
2412            }
2413        }
2414        if (first && p) {
2415            /* not: mp_close_path(mp, p, first); */
2416            if (cyclic) {
2417                p->right_type = mp_explicit_knot;
2418                first->left_type = mp_explicit_knot;
2419            } else {
2420                /* check this on shapes-001.tex and arrows-001.tex */
2421                p->right_type = mp_endpoint_knot;
2422                first->left_type = mp_endpoint_knot;
2423            }
2424            p->next = first;
2425            first->prev = p;
2426        } else { 
2427            errormsg = "invalid path";
2428            goto BAD;
2429        }
2430        if (inject) {
2431            if (solve && ! mp_solve_path(mp, first)) {
2432                tex_normal_warning("lua", "failed to solve the path");
2433            }
2434            mp_push_path_value(mp, first);
2435            return 0;
2436        } else if (mp_solve_path(mp, first)) {
2437            /* We replace in the original table .. maybe not a good idea at all. */
2438            p = first;
2439            for (int i = 1; i <= numpoints; i++) {
2440                lua_rawgeti(L, -1, i);
2441                lua_push_number_at_key(L, left_x,  mp_number_as_double(mp, p->left_x));
2442                lua_push_number_at_key(L, left_y,  mp_number_as_double(mp, p->left_y));
2443                lua_push_number_at_key(L, right_x, mp_number_as_double(mp, p->right_x));
2444                lua_push_number_at_key(L, right_y, mp_number_as_double(mp, p->right_y));
2445                /*tex This is a bit overkill, wiping  \unknown */
2446                lua_push_nil_at_key(L, left_tension);
2447                lua_push_nil_at_key(L, right_tension);
2448                lua_push_nil_at_key(L, left_curl);
2449                lua_push_nil_at_key(L, right_curl);
2450                lua_push_nil_at_key(L, direction_x);
2451                lua_push_nil_at_key(L, direction_y);
2452                /*tex \unknown\ till here. */
2453                lua_push_svalue_at_key(L, left_type, mplib_values_knot[p->left_type]);
2454                lua_push_svalue_at_key(L, right_type, mplib_values_knot[p->right_type]);
2455                lua_pop(L, 1);
2456                p = p->next;
2457            }
2458            lua_pushboolean(L, 1);
2459            return 1;
2460        } else {
2461            errormsg = "failed to solve the path";
2462        }
2463      BAD:
2464        if (p) {
2465            /* can fail */
2466            mp_free_path(mp, p);
2467        }
2468        lua_pushboolean(L, 0);
2469        if (errormsg) {
2470            lua_pushstring(L, errormsg);
2471            return 2;
2472        } else {
2473            return 1;
2474        }
2475    }
2476}
2477
2478static int mplib_solvepath(lua_State *L)
2479{
2480    MP mp = mplib_aux_is_mp(L, 1);
2481    if (mp) {
2482        return mplib_aux_with_path(L, mp, 2, 0, 1);
2483    } else {
2484        return 0;
2485    }
2486}
2487
2488static int mplib_inject_path(lua_State *L)
2489{
2490    MP mp = mplib_aux_is_mp(L, 1);
2491    if (mp) {
2492        return mplib_aux_with_path(L, mp, 2, 1, 1);
2493    } else {
2494        return 0;
2495    }
2496}
2497
2498static int mplib_inject_whatever(lua_State *L)
2499{
2500    MP mp = mplib_aux_is_mp(L, 1);
2501    if (mp) {
2502        mplib_aux_inject_whatever(L, mp, 2);
2503    }
2504    return 0;
2505}
2506
2507/*tex The next methods are for collecting the results from |fig|. */
2508
2509static int mplib_figure_collect(lua_State *L)
2510{
2511    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2512    if (*hh) {
2513        mp_graphic_toss_objects(*hh);
2514        *hh = NULL;
2515    }
2516    return 0;
2517}
2518
2519static int mplib_figure_objects(lua_State *L)
2520{
2521    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2522    if (*hh) {
2523        int i = 1;
2524        struct mp_graphic_object *p = (*hh)->body;
2525        lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
2526        lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
2527        lua_newtable(L);
2528        while (p) {
2529            struct mp_graphic_object **v = lua_newuserdatauv(L, sizeof(struct mp_graphic_object *), 2);
2530            *v = p;
2531            mplib_aux_set_bend_tolerance(L, bendtolerance);
2532            mplib_aux_set_move_tolerance(L, movetolerance);
2533         // luaL_getmetatable(L, MP_METATABLE_OBJECT);
2534            lua_get_metatablelua(mplib_object);
2535            lua_setmetatable(L, -2);
2536            lua_rawseti(L, -2, i);
2537            i++;
2538            p = p->next;
2539        }
2540        /*tex Prevent a double free: */
2541        (*hh)->body = NULL;
2542    } else {
2543        lua_pushnil(L);
2544    }
2545    return 1;
2546}
2547
2548static int mplib_figure_stacking(lua_State *L)
2549{
2550    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2551    int stacking = 0; /* This only works when before fetching objects! */
2552    if (*hh) {
2553        struct mp_graphic_object *p = (*hh)->body;
2554        while (p) {
2555            if (((mp_shape_object *) p)->stacking) {
2556                stacking = 1;
2557                break;
2558            } else {
2559                p = p->next;
2560            }
2561        }
2562    }
2563    lua_pushboolean(L, stacking);
2564    return 1;
2565}
2566
2567static int mplib_figure_tostring(lua_State *L)
2568{
2569    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2570    if (*hh) {
2571        lua_pushfstring(L, "<mp.figure %p>", *hh);
2572    } else {
2573        lua_pushnil(L);
2574    }
2575    return 1;
2576}
2577
2578static int mplib_figure_width(lua_State *L)
2579{
2580    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2581    if (*hh) {
2582        lua_pushnumber(L, (*hh)->width);
2583    } else {
2584        lua_pushnil(L);
2585    }
2586    return 1;
2587}
2588
2589static int mplib_figure_height(lua_State *L)
2590{
2591    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2592    if (*hh) {
2593        lua_pushnumber(L, (*hh)->height);
2594    } else {
2595        lua_pushnil(L);
2596    }
2597    return 1;
2598}
2599
2600static int mplib_figure_depth(lua_State *L)
2601{
2602    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2603    if (*hh) {
2604        lua_pushnumber(L, (*hh)->depth);
2605    } else {
2606        lua_pushnil(L);
2607    }
2608    return 1;
2609}
2610
2611static int mplib_figure_italic(lua_State *L)
2612{
2613    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2614    if (*hh) {
2615        lua_pushnumber(L, (*hh)->italic);
2616    } else {
2617        lua_pushnil(L);
2618    }
2619    return 1;
2620}
2621
2622static int mplib_figure_charcode(lua_State *L)
2623{
2624    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2625    if (*hh) {
2626        lua_pushinteger(L, (lua_Integer) (*hh)->charcode);
2627    } else {
2628        lua_pushnil(L);
2629    }
2630    return 1;
2631}
2632
2633static int mplib_figure_tolerance(lua_State *L)
2634{
2635    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2636    if (*hh) {
2637        lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1));
2638        lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1));
2639    } else {
2640        lua_pushnil(L);
2641        lua_pushnil(L);
2642    }
2643    return 2;
2644}
2645
2646static int mplib_figure_bounds(lua_State *L)
2647{
2648    struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
2649    lua_createtable(L, 4, 0);
2650    lua_push_number_at_index(L, 1, (*hh)->minx);
2651    lua_push_number_at_index(L, 2, (*hh)->miny);
2652    lua_push_number_at_index(L, 3, (*hh)->maxx);
2653    lua_push_number_at_index(L, 4, (*hh)->maxy);
2654    return 1;
2655}
2656
2657/*tex The methods for the figure objects plus a few helpers. */
2658
2659static int mplib_object_collect(lua_State *L)
2660{
2661    struct mp_graphic_object **hh = mplib_aux_is_graphic_object(L, 1);
2662    if (*hh) {
2663        mp_graphic_toss_object(*hh);
2664        *hh = NULL;
2665    }
2666    return 0;
2667}
2668
2669static int mplib_object_tostring(lua_State *L)
2670{
2671    struct mp_graphic_object **hh = mplib_aux_is_graphic_object(L, 1);
2672    lua_pushfstring(L, "<mp.object %p>", *hh);
2673    return 1;
2674}
2675
2676# define pyth(a,b)      (sqrt((a)*(a) + (b)*(b)))
2677# define aspect_bound   (10.0/65536.0)
2678# define aspect_default 1.0
2679# define eps            0.0001
2680
2681static double mplib_aux_coord_range_x(mp_graphic_knot h, double dz)
2682{
2683    double zlo = 0.0;
2684    double zhi = 0.0;
2685    mp_graphic_knot f = h;
2686    while (h) {
2687        double z = h->x_coord;
2688        if (z < zlo) {
2689            zlo = z;
2690        } else if (z > zhi) {
2691            zhi = z;
2692        }
2693        z = h->right_x;
2694        if (z < zlo) {
2695            zlo = z;
2696        } else if (z > zhi) {
2697            zhi = z;
2698        }
2699        z = h->left_x;
2700        if (z < zlo) {
2701            zlo = z;
2702        } else if (z > zhi) {
2703            zhi = z;
2704        }
2705        h = h->next;
2706        if (h == f) {
2707            break;
2708        }
2709    }
2710    return (zhi - zlo <= dz) ? aspect_bound : aspect_default;
2711}
2712
2713static double mplib_aux_coord_range_y(mp_graphic_knot h, double dz)
2714{
2715    double zlo = 0.0;
2716    double zhi = 0.0;
2717    mp_graphic_knot f = h;
2718    while (h) {
2719        double z = h->y_coord;
2720        if (z < zlo) {
2721            zlo = z;
2722        } else if (z > zhi) {
2723            zhi = z;
2724        }
2725        z = h->right_y;
2726        if (z < zlo) {
2727            zlo = z;
2728        } else if (z > zhi) {
2729            zhi = z;
2730        }
2731        z = h->left_y;
2732        if (z < zlo) {
2733            zlo = z;
2734        } else if (z > zhi) {
2735            zhi = z;
2736        }
2737        h = h->next;
2738        if (h == f) {
2739            break;
2740        }
2741    }
2742    return (zhi - zlo <= dz) ? aspect_bound : aspect_default;
2743}
2744
2745static int mplib_object_peninfo(lua_State *L)
2746{
2747    struct mp_graphic_object **hh = mplib_aux_is_graphic_object(L, -1);
2748    if (! *hh) {
2749        lua_pushnil(L);
2750        return 1;
2751    } else {
2752        mp_graphic_knot p = NULL;
2753        mp_graphic_knot path = NULL;
2754        switch ((*hh)->type) { 
2755            case mp_fill_code:
2756            case mp_stroked_code:
2757                p    = ((mp_shape_object *) (*hh))->pen;
2758                path = ((mp_shape_object *) (*hh))->path;
2759                break;
2760        }
2761        if (! p || ! path) {
2762            lua_pushnil(L);
2763            return 1;
2764        } else {
2765            double wx, wy;
2766            double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0;
2767            double width = 1.0;
2768            double x_coord = p->x_coord;
2769            double y_coord = p->y_coord;
2770            double left_x  = p->left_x;
2771            double left_y  = p->left_y;
2772            double right_x = p->right_x;
2773            double right_y = p->right_y;
2774            if ((right_x == x_coord) && (left_y == y_coord)) {
2775                wx = fabs(left_x  - x_coord);
2776                wy = fabs(right_y - y_coord);
2777            } else {
2778                wx = pyth(left_x - x_coord, right_x - x_coord);
2779                wy = pyth(left_y - y_coord, right_y - y_coord);
2780            }
2781            if ((wy/mplib_aux_coord_range_x(path, wx)) >= (wx/mplib_aux_coord_range_y(path, wy))) {
2782                width = wy;
2783            } else {
2784                width = wx;
2785            }
2786            tx = x_coord;
2787            ty = y_coord;
2788            sx = left_x - tx;
2789            rx = left_y - ty;
2790            ry = right_x - tx;
2791            sy = right_y - ty;
2792            if (width != 1.0) {
2793                if (width == 0.0) {
2794                    sx = 1.0;
2795                    sy = 1.0;
2796                } else {
2797                    rx /= width;
2798                    ry /= width;
2799                    sx /= width;
2800                    sy /= width;
2801                }
2802            }
2803            if (fabs(sx) < eps) {
2804                sx = eps;
2805            }
2806            if (fabs(sy) < eps) {
2807                sy = eps;
2808            }
2809            lua_createtable(L,0,7);
2810            lua_push_number_at_key(L, width, width);
2811            lua_push_number_at_key(L, rx, rx);
2812            lua_push_number_at_key(L, sx, sx);
2813            lua_push_number_at_key(L, sy, sy);
2814            lua_push_number_at_key(L, ry, ry);
2815            lua_push_number_at_key(L, tx, tx);
2816            lua_push_number_at_key(L, ty, ty);
2817            return 1;
2818        }
2819    }
2820}
2821
2822/*tex Here is a helper that reports the valid field names of the possible objects. */
2823
2824static void mplib_aux_mplib_push_fields(lua_State* L, const char **fields)
2825{
2826    lua_newtable(L);
2827    for (lua_Integer i = 0; fields[i]; i++) {
2828        lua_pushstring(L, fields[i]); /* not yet an index */
2829        lua_rawseti(L, -2, i + 1);
2830    }
2831}
2832
2833static int mplib_gettype(lua_State *L)
2834{
2835    struct mp_graphic_object **hh = mplib_aux_is_graphic_object(L, 1);
2836    if (*hh) {
2837        lua_pushinteger(L, (*hh)->type);
2838    } else {
2839        lua_pushnil(L);
2840    }
2841    return 1;
2842}
2843
2844static int mplib_getobjecttypes(lua_State* L)
2845{
2846    lua_createtable(L, 7, 1);
2847    lua_push_key_at_index(L, fill,         mp_fill_code);
2848    lua_push_key_at_index(L, outline,      mp_stroked_code);
2849    lua_push_key_at_index(L, start_clip,   mp_start_clip_code);
2850    lua_push_key_at_index(L, start_group,  mp_start_group_code);
2851    lua_push_key_at_index(L, start_bounds, mp_start_bounds_code);
2852    lua_push_key_at_index(L, stop_clip,    mp_stop_clip_code);
2853    lua_push_key_at_index(L, stop_group,   mp_stop_group_code);
2854    lua_push_key_at_index(L, stop_bounds,  mp_stop_bounds_code);
2855    return 1;
2856}
2857
2858static int mplib_getscantypes(lua_State* L)
2859{
2860    lua_createtable(L, 3, 1);
2861    lua_push_key_at_index(L, expression, mp_expression_scan_code);
2862    lua_push_key_at_index(L, primary,    mp_primary_scan_code);
2863    lua_push_key_at_index(L, secondary,  mp_secondary_scan_code);
2864    lua_push_key_at_index(L, tertiary,   mp_tertiary_scan_code);
2865    return 1;
2866}
2867
2868// static int mplib_getscantypes(lua_State* L)
2869// {
2870//     return mplib_push_values(L, mplib_scan_codes);
2871// }
2872
2873static int mplib_getinternalactions(lua_State* L)
2874{
2875    lua_createtable(L, 2, 1);
2876    lua_push_key_at_index(L, initialize, mp_initialize_internal_code);
2877    lua_push_key_at_index(L, save,       mp_save_internal_code);
2878    lua_push_key_at_index(L, restore,    mp_restore_internal_code); 
2879 // lua_push_key_at_index(L, tracing,    mp_tracing_internal_code); 
2880    return 1;
2881}
2882
2883// static int getinternalactions(lua_State* L)
2884// {
2885//     return mplib_push_values(L, mplib_internal_sction_codes);
2886// }
2887
2888static int mplib_getfields(lua_State *L)
2889{
2890    int n = -1; 
2891    switch (lua_type(L, 1)) { 
2892        case LUA_TUSERDATA: 
2893            {
2894                struct mp_graphic_object **hh = mplib_aux_is_graphic_object(L, 1);
2895                if (*hh) {
2896                    n = (*hh)->type;
2897                }
2898                break;
2899            }
2900        case LUA_TNUMBER: 
2901            {
2902                n = (int) lua_tointeger(L, 1);
2903                break;
2904            }
2905        default:
2906            {
2907                lua_createtable(L, 8, 0);
2908                mplib_aux_mplib_push_fields(L, mplib_fill_fields);         lua_rawseti(L, -2, mp_fill_code);
2909                mplib_aux_mplib_push_fields(L, mplib_stroked_fields);      lua_rawseti(L, -2, mp_stroked_code);
2910                mplib_aux_mplib_push_fields(L, mplib_start_clip_fields);   lua_rawseti(L, -2, mp_start_clip_code);
2911                mplib_aux_mplib_push_fields(L, mplib_start_group_fields);  lua_rawseti(L, -2, mp_start_group_code);
2912                mplib_aux_mplib_push_fields(L, mplib_start_bounds_fields); lua_rawseti(L, -2, mp_start_bounds_code);
2913                mplib_aux_mplib_push_fields(L, mplib_stop_clip_fields);    lua_rawseti(L, -2, mp_stop_clip_code);
2914                mplib_aux_mplib_push_fields(L, mplib_stop_group_fields);   lua_rawseti(L, -2, mp_stop_group_code);
2915                mplib_aux_mplib_push_fields(L, mplib_stop_bounds_fields);  lua_rawseti(L, -2, mp_stop_bounds_code);
2916                return 1;
2917            }
2918    }
2919    if (n >= mp_fill_code && n <= mp_stop_bounds_code) { 
2920        const char **fields;
2921        switch (n) {
2922            case mp_fill_code        : fields = mplib_fill_fields;         break;
2923            case mp_stroked_code     : fields = mplib_stroked_fields;      break;
2924            case mp_start_clip_code  : fields = mplib_start_clip_fields;   break;
2925            case mp_start_group_code : fields = mplib_start_group_fields;  break;
2926            case mp_start_bounds_code: fields = mplib_start_bounds_fields; break;
2927            case mp_stop_clip_code   : fields = mplib_stop_clip_fields;    break;
2928            case mp_stop_group_code  : fields = mplib_stop_group_fields;   break;
2929            case mp_stop_bounds_code : fields = mplib_stop_bounds_fields;  break;
2930            default                  : fields = mplib_no_fields;           break;
2931        }
2932        mplib_aux_mplib_push_fields(L, fields);
2933    } else {
2934        lua_pushnil(L);
2935    }
2936    return 1;
2937}
2938
2939static int mplib_push_values(lua_State *L, const char *list[])
2940{
2941    lua_newtable(L);
2942    for (lua_Integer i = 0; list[i]; i++) {
2943        lua_pushstring(L, list[i]);
2944        lua_rawseti(L, -2, i);
2945    }
2946    return 1;
2947}
2948
2949static int mplib_getcodes(lua_State *L)
2950{
2951    return mplib_push_values(L, mplib_codes);
2952}
2953
2954static int mplib_gettypes(lua_State *L)
2955{
2956    return mplib_push_values(L, mplib_types);
2957}
2958
2959static int mplib_getcolormodels(lua_State *L)
2960{
2961    return mplib_push_values(L, mplib_colormodels);
2962}
2963
2964static int mplib_getstates(lua_State *L)
2965{
2966    return mplib_push_values(L, mplib_states);
2967}
2968
2969static int mplib_getknotstates(lua_State *L)
2970{
2971    return mplib_push_values(L, mplib_knot_states);
2972}
2973
2974static int mplib_getlogtargets(lua_State* L)
2975{
2976    return mplib_push_values(L, mplib_log_targets);
2977}
2978
2979static int mplib_getcallbackstate(lua_State *L)
2980{
2981    lua_createtable(L, 0, 8);
2982    lua_push_integer_at_key(L, file,       mplib_state.file_callbacks);
2983    lua_push_integer_at_key(L, text,       mplib_state.text_callbacks);
2984    lua_push_integer_at_key(L, script,     mplib_state.script_callbacks);
2985    lua_push_integer_at_key(L, log,        mplib_state.log_callbacks);
2986    lua_push_integer_at_key(L, overloaded, mplib_state.overload_callbacks);
2987    lua_push_integer_at_key(L, error,      mplib_state.error_callbacks);
2988    lua_push_integer_at_key(L, warning,    mplib_state.warning_callbacks);
2989    lua_push_integer_at_key(L, count,
2990          mplib_state.file_callbacks     + mplib_state.text_callbacks
2991        + mplib_state.script_callbacks   + mplib_state.log_callbacks
2992        + mplib_state.overload_callbacks + mplib_state.error_callbacks
2993        + mplib_state.warning_callbacks
2994    );
2995    return 1;
2996}
2997
2998/*tex
2999
3000    This assumes that the top of the stack is a table or nil already in the case.
3001*/
3002
3003static void mplib_aux_push_color(lua_State *L, struct mp_graphic_object *p)
3004{
3005    if (p) {
3006        int object_color_model;
3007        mp_color object_color;
3008        switch (p->type) {
3009            case mp_fill_code:
3010            case mp_stroked_code:
3011                {
3012                    mp_shape_object *h = (mp_shape_object *) p;
3013                    object_color_model = h->color_model; 
3014                    object_color = h->color; 
3015                }
3016                break;
3017            default:
3018                object_color_model = mp_no_model;
3019                object_color = (mp_color) { { 0.0 }, { 0.0 }, { 0.0 }, { 0.0 } };
3020                break;
3021        }
3022        switch (object_color_model) {
3023            case mp_grey_model:
3024                lua_createtable(L, 1, 0);
3025                lua_push_number_at_index(L, 1, object_color.gray);
3026                break;
3027            case mp_rgb_model:
3028                lua_createtable(L, 3, 0);
3029                lua_push_number_at_index(L, 1, object_color.red);
3030                lua_push_number_at_index(L, 2, object_color.green);
3031                lua_push_number_at_index(L, 3, object_color.blue);
3032                break;
3033            case mp_cmyk_model:
3034                lua_createtable(L, 4, 0);
3035                lua_push_number_at_index(L, 1, object_color.cyan);
3036                lua_push_number_at_index(L, 2, object_color.magenta);
3037                lua_push_number_at_index(L, 3, object_color.yellow);
3038                lua_push_number_at_index(L, 4, object_color.black);
3039                break;
3040            default:
3041                lua_pushnil(L);
3042                break;
3043        }
3044    } else {
3045        lua_pushnil(L);
3046    }
3047}
3048
3049/*tex The dash scale is not exported, the field has no external value. */
3050
3051static void mplib_aux_push_dash(lua_State *L, struct mp_shape_object *h)
3052{
3053    if (h && h->dash) {
3054        mp_dash_object *d = h->dash;
3055        lua_newtable(L); /* we could start at size 2 or so */
3056        lua_push_number_at_key(L, offset, d->offset);
3057        if (d->array) {
3058            int i = 0;
3059            lua_push_key(dashes);
3060            lua_newtable(L);
3061            while (*(d->array + i) != -1) {
3062                double ds = *(d->array + i);
3063                lua_pushnumber(L, ds);
3064                i++;
3065                lua_rawseti(L, -2, i);
3066            }
3067            lua_rawset(L, -3);
3068        }
3069    } else {
3070        lua_pushnil(L);
3071    }
3072}
3073
3074static void mplib_aux_shape(lua_State *L, const char *s, struct mp_shape_object *h, lua_Number bendtolerance, lua_Number movetolerance)
3075{
3076    if (lua_key_eq(s, path)) {
3077        mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance, h->curvature);
3078    } else if (lua_key_eq(s, htap)) {
3079        mplib_aux_push_path(L, h->htap, MPLIB_PATH, bendtolerance, movetolerance, h->curvature);
3080    } else if (lua_key_eq(s, pen)) {
3081        mplib_aux_push_path(L, h->pen, MPLIB_PEN, bendtolerance, movetolerance, h->curvature);
3082        /* pushed in the table at the top */
3083        mplib_aux_push_pentype(L, h->pen);
3084    } else if (lua_key_eq(s, color)) {
3085        mplib_aux_push_color(L, (mp_graphic_object *) h);
3086    } else if (lua_key_eq(s, linejoin)) {
3087        lua_pushnumber(L, (lua_Number) h->linejoin);
3088    } else if (lua_key_eq(s, linecap)) {
3089        lua_pushnumber(L, (lua_Number) h->linecap);
3090 // } else if (lua_key_eq(s, stacking)) {
3091 //     lua_pushinteger(L, (lua_Integer) h->stacking);
3092 // } else if (lua_key_eq(s, curvature)) {
3093 //     lua_pushnumber(L, (lua_Number) h->curvature);
3094    } else if (lua_key_eq(s, miterlimit)) {
3095        lua_pushnumber(L, h->miterlimit);
3096    } else if (lua_key_eq(s, prescript)) {
3097        lua_pushlstring(L, h->pre_script, h->pre_length);
3098    } else if (lua_key_eq(s, postscript)) {
3099        lua_pushlstring(L, h->post_script, h->post_length);
3100    } else if (lua_key_eq(s, dash)) {
3101        mplib_aux_push_dash(L, h);
3102    } else {
3103        lua_pushnil(L);
3104    }
3105}
3106
3107static void mplib_aux_start(lua_State *L, const char *s, struct mp_start_object *h, lua_Number bendtolerance, lua_Number movetolerance)
3108{
3109    if (lua_key_eq(s, path)) {
3110        mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance, -1);
3111    } else if (lua_key_eq(s, prescript)) {
3112        lua_pushlstring(L, h->pre_script, h->pre_length);
3113    } else if (lua_key_eq(s, postscript)) {
3114        lua_pushlstring(L, h->post_script, h->post_length);
3115 // } else if (lua_key_eq(s, stacking)) {
3116 //     lua_pushinteger(L, (lua_Integer) h->stacking);
3117    } else {
3118        lua_pushnil(L);
3119    }
3120}
3121
3122// static void mplib_aux_stop(lua_State *L, const char *s, struct mp_stop_object *h, lua_Number bendtolerance, lua_Number movetolerance)
3123// {
3124//     if (lua_key_eq(s, stacking)) {
3125//         lua_pushinteger(L, (lua_Integer) h->stacking);
3126//     } else {
3127//         lua_pushnil(L);
3128//     }
3129// }
3130
3131static int mplib_object_index(lua_State *L)
3132{
3133    struct mp_graphic_object **hh = mplib_aux_is_graphic_object(L, 1); /* no need for test */
3134    if (*hh) {
3135        struct mp_graphic_object *h = *hh;
3136        const char *s = lua_tostring(L, 2);
3137        /* todo: remove stacking from specific aux  */
3138        if (lua_key_eq(s, type)) {
3139            lua_push_key_by_index(mplib_values_type[h->type]);
3140        } else if (lua_key_eq(s, stacking)) {
3141            lua_pushinteger(L, (lua_Integer) h->stacking);
3142        } else {
3143            lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
3144            lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
3145            /* todo: we can use generic casts */
3146            switch (h->type) {
3147                case mp_fill_code:
3148                case mp_stroked_code:
3149                    mplib_aux_shape(L, s, (mp_shape_object *) h, bendtolerance, movetolerance);
3150                    break;
3151                case mp_start_clip_code:
3152                case mp_start_group_code:
3153                case mp_start_bounds_code:
3154                    mplib_aux_start(L, s, (mp_start_object *) h, bendtolerance, movetolerance);
3155                    break;
3156             // case mp_stop_clip_code:
3157             // case mp_stop_group_code:
3158             // case mp_stop_bounds_code:
3159             //     mplib_aux_stop_clip(L, s, (mp_stop_object *) h, bendtolerance, movetolerance);
3160             //     break;
3161                default:
3162                    lua_pushnil(L);
3163                    break;
3164            }
3165        }
3166    } else {
3167        lua_pushnil(L);
3168    }
3169    return 1;
3170}
3171
3172/* Experiment: mpx, kind, macro, arguments */
3173
3174static int mplib_expand_tex(lua_State *L)
3175{
3176    MP mp = mplib_aux_is_mp(L, 1);
3177    if (mp) {
3178        int kind = lmt_tointeger(L, 2);
3179        halfword tail = null;
3180        halfword head = lmt_macro_to_tok(L, 3, &tail);
3181        if (head) {
3182            switch (kind) {
3183                case lua_value_none_code:
3184                case lua_value_dimension_code:
3185                    {
3186                        halfword value = 0;
3187                        halfword space = tex_get_available_token(space_token);
3188                        halfword relax = tex_get_available_token(deep_frozen_relax_token);
3189                        token_link(tail) = space;
3190                        token_link(space) = relax;
3191                        tex_begin_inserted_list(head);
3192                        lmt_error_state.intercept = 1;
3193                        lmt_error_state.last_intercept = 0;
3194                        value = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3195                        lmt_error_state.intercept = 0;
3196                        while (cur_tok != deep_frozen_relax_token) {
3197                            tex_get_token();
3198                        }
3199                        if (! lmt_error_state.last_intercept) {
3200                            mp_push_numeric_value(mp, (double) value * (7200.0/7227.0) / 65536.0);
3201                            break;
3202                        } else if (kind == lua_value_none_code) {
3203                            head = lmt_macro_to_tok(L, 3, &tail);
3204                            goto TRYAGAIN;
3205                        } else {
3206                         // head = lmt_macro_to_tok(L, 3, &tail);
3207                         // goto JUSTINCASE;
3208                            lua_pushboolean(L, 0);
3209                            return 1;
3210                        }
3211                    }
3212                case lua_value_integer_code:
3213                case lua_value_cardinal_code:
3214                case lua_value_boolean_code:
3215                  TRYAGAIN:
3216                    {
3217                        halfword value = 0;
3218                        halfword space = tex_get_available_token(space_token);
3219                        halfword relax = tex_get_available_token(deep_frozen_relax_token);
3220                        token_link(tail) = space;
3221                        token_link(space) = relax;
3222                        tex_begin_inserted_list(head);
3223                        lmt_error_state.intercept = 1;
3224                        lmt_error_state.last_intercept = 0;
3225                        value = tex_scan_integer(0, NULL, NULL);
3226                        lmt_error_state.intercept = 0;
3227                        while (cur_tok != deep_frozen_relax_token) {
3228                            tex_get_token();
3229                        }
3230                        if (lmt_error_state.last_intercept) {
3231                         // head = lmt_macro_to_tok(L, 3, &tail);
3232                         // goto JUSTINCASE;
3233                            lua_pushboolean(L, 0);
3234                            return 1;
3235                        } else if (kind == lua_value_boolean_code) {
3236                            mp_push_boolean_value(mp, value);
3237                            break;
3238                        } else {
3239                            mp_push_numeric_value(mp, value);
3240                            break;
3241                        }
3242                    }
3243                default:
3244               // JUSTINCASE:
3245                    {
3246                        int len = 0;
3247                        const char *str = (const char *) lmt_get_expansion(head, &len);
3248                        mp_push_string_value(mp, str, str ? len : 0); /* len includes \0 */
3249                        break;
3250                    }
3251            }
3252        }
3253    }
3254    lua_pushboolean(L, 1);
3255    return 1;
3256}
3257
3258/* */
3259
3260static const struct luaL_Reg mplib_instance_metatable[] = {
3261    { "__gc",       mplib_instance_collect  },
3262    { "__tostring", mplib_instance_tostring },
3263    { NULL,         NULL                    },
3264};
3265
3266static const struct luaL_Reg mplib_figure_metatable[] = {
3267    { "__gc",         mplib_figure_collect   },
3268    { "__tostring",   mplib_figure_tostring  },
3269    { "objects",      mplib_figure_objects   },
3270    { "boundingbox",  mplib_figure_bounds    },
3271    { "width",        mplib_figure_width     },
3272    { "height",       mplib_figure_height    },
3273    { "depth",        mplib_figure_depth     },
3274    { "italic",       mplib_figure_italic    },
3275    { "charcode",     mplib_figure_charcode  },
3276    { "tolerance",    mplib_figure_tolerance },
3277    { "stacking",     mplib_figure_stacking  },
3278    { NULL,           NULL                   },
3279};
3280
3281static const struct luaL_Reg mplib_object_metatable[] = {
3282    { "__gc",       mplib_object_collect  },
3283    { "__tostring", mplib_object_tostring },
3284    { "__index",    mplib_object_index    },
3285    { NULL,         NULL                  },
3286};
3287
3288static const struct luaL_Reg mplib_instance_functions_list[] = {
3289    { "execute",       mplib_execute       },
3290    { "finish",        mplib_finish        },
3291    { "getstatistics", mplib_getstatistics },
3292    { "getstatus",     mplib_getstatus     },
3293    { "solvepath",     mplib_solvepath     },
3294    { NULL,            NULL                },
3295};
3296
3297static const struct luaL_Reg mplib_functions_list[] = {
3298    { "new",                mplib_new                },
3299    { "version",            mplib_version            },
3300    /* */                                            
3301    { "getfields",          mplib_getfields          },
3302    { "gettype",            mplib_gettype            },
3303    { "gettypes",           mplib_gettypes           },
3304    { "getcolormodels",     mplib_getcolormodels     },
3305    { "getcodes",           mplib_getcodes           },
3306    { "getstates",          mplib_getstates          },
3307    { "getknotstates",      mplib_getknotstates      },
3308    { "getobjecttypes",     mplib_getobjecttypes     },
3309    { "getscantypes",       mplib_getscantypes       },
3310    { "getlogtargets",      mplib_getlogtargets      },
3311    { "getinternalactions", mplib_getinternalactions },
3312    { "getcallbackstate",   mplib_getcallbackstate   },
3313    /* */                                            
3314    { "settolerance",       mplib_set_tolerance      },
3315    { "gettolerance",       mplib_get_tolerance      },
3316    /* indirect */                                   
3317    { "execute",            mplib_execute            },
3318    { "finish",             mplib_finish             },
3319    { "showcontext",        mplib_showcontext        },
3320    { "gethashentries",     mplib_gethashentries     },
3321    { "gethashentry",       mplib_gethashentry       },
3322    { "getstatistics",      mplib_getstatistics      },
3323    { "getstatus",          mplib_getstatus          },
3324    { "solvepath",          mplib_solvepath          },
3325    /* helpers */                                    
3326    { "peninfo",            mplib_object_peninfo     },
3327    /* scanners */                                   
3328    { "scannext",           mplib_scan_next          },
3329    { "scanexpression",     mplib_scan_expression    },
3330    { "scantoken",          mplib_scan_token         },
3331    { "scansymbol",         mplib_scan_symbol        },
3332    { "scanproperty",       mplib_scan_property      },
3333    { "scannumeric",        mplib_scan_numeric       },
3334    { "scannumber",         mplib_scan_numeric       }, /* bonus */
3335    { "scaninteger",        mplib_scan_integer       },
3336    { "scanboolean",        mplib_scan_boolean       },
3337    { "scanstring",         mplib_scan_string        },
3338    { "scanpair",           mplib_scan_pair          },
3339    { "scancolor",          mplib_scan_color         },
3340    { "scancmykcolor",      mplib_scan_cmykcolor     },
3341    { "scantransform",      mplib_scan_transform     },
3342    { "scanpath",           mplib_scan_path          },
3343    { "scanpen",            mplib_scan_pen           },
3344    /* skippers */                                   
3345    { "skiptoken",          mplib_skip_token         },
3346    /* injectors */                                  
3347    { "injectnumeric",      mplib_inject_numeric     },
3348    { "injectnumber",       mplib_inject_numeric     }, /* bonus */
3349    { "injectinteger",      mplib_inject_integer     },
3350    { "injectboolean",      mplib_inject_boolean     },
3351    { "injectstring",       mplib_inject_string      },
3352    { "injectpair",         mplib_inject_pair        },
3353    { "injectcolor",        mplib_inject_color       },
3354    { "injectcmykcolor",    mplib_inject_cmykcolor   },
3355    { "injecttransform",    mplib_inject_transform   },
3356    { "injectpath",         mplib_inject_path        },
3357    { "injectwhatever",     mplib_inject_whatever    },
3358    /* */                                            
3359    { "expandtex",          mplib_expand_tex         },
3360    /* */                                            
3361    { NULL,                 NULL                     },
3362};
3363
3364int luaopen_mplib(lua_State *L)
3365{
3366    mplib_aux_initialize_lua(L);
3367
3368    luaL_newmetatable(L, MP_METATABLE_OBJECT);
3369    lua_pushvalue(L, -1);
3370    lua_setfield(L, -2, "__index");
3371    luaL_setfuncs(L, mplib_object_metatable, 0);
3372    luaL_newmetatable(L, MP_METATABLE_FIGURE);
3373    lua_pushvalue(L, -1);
3374    lua_setfield(L, -2, "__index");
3375    luaL_setfuncs(L, mplib_figure_metatable, 0);
3376    luaL_newmetatable(L, MP_METATABLE_INSTANCE);
3377    lua_pushvalue(L, -1);
3378    lua_setfield(L, -2, "__index");
3379    luaL_setfuncs(L, mplib_instance_metatable, 0);
3380    luaL_setfuncs(L, mplib_instance_functions_list, 0);
3381    lua_newtable(L);
3382    luaL_setfuncs(L, mplib_functions_list, 0);
3383    return 1;
3384}
3385