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