work around a cweb/cpp interference bug
[mplib] / src / texk / web2c / mpdir / lmplib.c
1 /* lmplib.c
2    
3    Copyright 2006-2008 Taco Hoekwater <taco@luatex.org>
4
5    This file is part of LuaTeX.
6
7    LuaTeX is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2 of the License, or (at your
10    option) any later version.
11
12    LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15    License for more details.
16
17    You should have received a copy of the GNU General Public License along
18    with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #ifndef pdfTeX
25 #  include <lua.h>
26 #  include <lauxlib.h>
27 #  include <lualib.h>
28 #else
29 #  include <../lua51/lua.h>
30 #  include <../lua51/lauxlib.h>
31 #  include <../lua51/lualib.h>
32 #endif
33
34 #include "mplib.h"
35 #include "mpmp.h"
36 #include "mppsout.h"            /* for mp_edge_object */
37
38 static const char _svn_version[] =
39     "$Id: lmplib.c 1298 2008-05-28 14:08:27Z taco $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/lua/lmplib.c $";
40
41 #define MPLIB_METATABLE     "MPlib"
42 #define MPLIB_FIG_METATABLE "MPlib.fig"
43 #define MPLIB_GR_METATABLE  "MPlib.gr"
44
45 #define mplib_init_S(a) do {                                                                                    \
46     lua_pushliteral(L,#a);                                              \
47         mplib_##a##_ptr = (char *)lua_tostring(L,-1);                                           \
48     mplib_##a##_index = luaL_ref (L,LUA_REGISTRYINDEX);                                 \
49   } while (0)
50
51 #define mplib_push_S(a) do {                                    \
52     lua_rawgeti(L,LUA_REGISTRYINDEX,mplib_##a##_index);                 \
53   } while (0)
54
55 #define mplib_is_S(a,i) (mplib_##a##_ptr==(char *)lua_tostring(L,i))
56
57 #define mplib_make_S(a)                                                                                                 \
58   static int mplib_##a##_index = 0;                                                                             \
59   static char *mplib_##a##_ptr = NULL
60
61 static int mplib_type_Ses[mp_special_code + 1] = { 0 }; /* [0] is not used */
62
63 mplib_make_S(fill);
64 mplib_make_S(outline);
65 mplib_make_S(text);
66 mplib_make_S(special);
67 mplib_make_S(start_bounds);
68 mplib_make_S(stop_bounds);
69 mplib_make_S(start_clip);
70 mplib_make_S(stop_clip);
71
72 mplib_make_S(left_type);
73 mplib_make_S(right_type);
74 mplib_make_S(x_coord);
75 mplib_make_S(y_coord);
76 mplib_make_S(left_x);
77 mplib_make_S(left_y);
78 mplib_make_S(right_x);
79 mplib_make_S(right_y);
80
81 mplib_make_S(color);
82 mplib_make_S(dash);
83 mplib_make_S(depth);
84 mplib_make_S(dsize);
85 mplib_make_S(font);
86 mplib_make_S(height);
87 mplib_make_S(htap);
88 mplib_make_S(linecap);
89 mplib_make_S(linejoin);
90 mplib_make_S(miterlimit);
91 mplib_make_S(path);
92 mplib_make_S(pen);
93 mplib_make_S(postscript);
94 mplib_make_S(prescript);
95 mplib_make_S(transform);
96 mplib_make_S(type);
97 mplib_make_S(width);
98
99 void mplib_init_Ses(lua_State * L)
100 {
101     mplib_init_S(fill);
102     mplib_init_S(outline);
103     mplib_init_S(text);
104     mplib_init_S(start_bounds);
105     mplib_init_S(stop_bounds);
106     mplib_init_S(start_clip);
107     mplib_init_S(stop_clip);
108     mplib_init_S(special);
109
110     mplib_type_Ses[mp_fill_code] = mplib_fill_index;
111     mplib_type_Ses[mp_stroked_code] = mplib_outline_index;
112     mplib_type_Ses[mp_text_code] = mplib_text_index;
113     mplib_type_Ses[mp_start_bounds_code] = mplib_start_bounds_index;
114     mplib_type_Ses[mp_stop_bounds_code] = mplib_stop_bounds_index;
115     mplib_type_Ses[mp_start_clip_code] = mplib_start_clip_index;
116     mplib_type_Ses[mp_stop_clip_code] = mplib_stop_clip_index;
117     mplib_type_Ses[mp_special_code] = mplib_special_index;
118
119     mplib_init_S(left_type);
120     mplib_init_S(right_type);
121     mplib_init_S(x_coord);
122     mplib_init_S(y_coord);
123     mplib_init_S(left_x);
124     mplib_init_S(left_y);
125     mplib_init_S(right_x);
126     mplib_init_S(right_y);
127
128     mplib_init_S(color);
129     mplib_init_S(dash);
130     mplib_init_S(depth);
131     mplib_init_S(dsize);
132     mplib_init_S(font);
133     mplib_init_S(height);
134     mplib_init_S(htap);
135     mplib_init_S(linecap);
136     mplib_init_S(linejoin);
137     mplib_init_S(miterlimit);
138     mplib_init_S(path);
139     mplib_init_S(pen);
140     mplib_init_S(postscript);
141     mplib_init_S(prescript);
142     mplib_init_S(transform);
143     mplib_init_S(type);
144     mplib_init_S(width);
145 }
146
147
148
149
150 #define xfree(A) if ((A)!=NULL) { free((A)); A = NULL; }
151
152 #define is_mp(L,b) (MP *)luaL_checkudata(L,b,MPLIB_METATABLE)
153 #define is_fig(L,b) (struct mp_edge_object **)luaL_checkudata(L,b,MPLIB_FIG_METATABLE)
154 #define is_gr_object(L,b) (struct mp_graphic_object **)luaL_checkudata(L,b,MPLIB_GR_METATABLE)
155
156 /* Enumeration string arrays */
157
158 static const char *interaction_options[] =
159     { "unknown", "batch", "nonstop", "scroll", "errorstop", NULL };
160
161 static const char *mplib_filetype_names[] =
162     { "term", "error", "mp", "log", "ps", "mem", "tfm", "map", "pfb", "enc",
163 NULL };
164
165 /* only "endpoint" and "explicit" actually happen in paths, 
166    as well as "open" in elliptical pens */
167
168 static const char *knot_type_enum[] =
169     { "endpoint", "explicit", "given", "curl", "open", "end_cycle" };
170
171 /* object fields */
172
173 static const char *fill_fields[] =
174     { "type", "path", "htap", "pen", "color", "linejoin", "miterlimit",
175     "prescript", "postscript", NULL
176 };
177
178 static const char *stroked_fields[] =
179     { "type", "path", "pen", "color", "linejoin", "miterlimit", "linecap",
180 "dash",
181     "prescript", "postscript", NULL
182 };
183
184 static const char *text_fields[] =
185     { "type", "text", "dsize", "font", "color", "width", "height", "depth",
186 "transform",
187     "prescript", "postscript", NULL
188 };
189
190 static const char *special_fields[] = { "type", "prescript", NULL };
191
192 static const char *start_bounds_fields[] = { "type", "path", NULL };
193
194 static const char *start_clip_fields[] = { "type", "path", NULL };
195
196 static const char *stop_bounds_fields[] = { "type", NULL };
197
198 static const char *stop_clip_fields[] = { "type", NULL };
199
200 static const char *no_fields[] = { NULL };
201
202 typedef enum {
203     P_ERROR_LINE, P_HALF_LINE, P_MAX_LINE, P_MAIN_MEMORY,
204     P_HASH_SIZE, P_HASH_PRIME, P_PARAM_SIZE, P_IN_OPEN, P_RANDOM_SEED,
205     P_INTERACTION, P_INI_VERSION, P_TROFF_MODE, P_PRINT_NAMES, P_MEM_NAME,
206     P_JOB_NAME, P_FIND_FILE, P__SENTINEL
207 } mplib_parm_idx;
208
209 typedef struct {
210     const char *name;           /* parameter name */
211     mplib_parm_idx idx;         /* parameter index */
212 } mplib_parm_struct;
213
214 static mplib_parm_struct mplib_parms[] = {
215     {"error_line", P_ERROR_LINE},
216     {"half_error_line", P_HALF_LINE},
217     {"max_print_line", P_MAX_LINE},
218     {"main_memory", P_MAIN_MEMORY},
219     {"hash_size", P_HASH_SIZE},
220     {"hash_prime", P_HASH_PRIME},
221     {"param_size", P_PARAM_SIZE},
222     {"max_in_open", P_IN_OPEN},
223     {"random_seed", P_RANDOM_SEED},
224     {"interaction", P_INTERACTION},
225     {"ini_version", P_INI_VERSION},
226     {"troff_mode", P_TROFF_MODE},
227     {"print_found_names", P_PRINT_NAMES},
228     {"mem_name", P_MEM_NAME},
229     {"job_name", P_JOB_NAME},
230     {"find_file", P_FIND_FILE},
231     {NULL, P__SENTINEL}
232 };
233
234 typedef struct _FILE_ITEM {
235     FILE *f;
236 } _FILE_ITEM;
237
238 typedef struct _FILE_ITEM File;
239
240 #define make_stream_buf(A) char *A; size_t A##_size; size_t A##_used
241
242 #define free_stream_buf(A) xfree(mplib_data->A); mplib_data->A##_size = 0; mplib_data->A##_used = 0
243
244 typedef struct _MPLIB_INSTANCE_DATA {
245     void *term_file_ptr;
246     void *err_file_ptr;
247     void *log_file_ptr;
248     void *ps_file_ptr;
249      make_stream_buf(term_out);
250      make_stream_buf(error_out);
251      make_stream_buf(log_out);
252      make_stream_buf(ps_out);
253     char *input_data;
254     char *input_data_ptr;
255     size_t input_data_len;
256     struct mp_edge_object *edges;
257     lua_State *LL;
258 } _MPLIB_INSTANCE_DATA;
259
260 typedef struct _MPLIB_INSTANCE_DATA mplib_instance;
261
262 static mplib_instance *mplib_get_data(MP mp)
263 {
264     return (mplib_instance *) mp->userdata;
265 }
266
267 static mplib_instance *mplib_make_data(void)
268 {
269     mplib_instance *mplib_data = malloc(sizeof(mplib_instance));
270     memset(mplib_data, 0, sizeof(mplib_instance));
271     return mplib_data;
272 }
273
274
275 /* Start by defining all the callback routines for the library 
276  * except |run_make_mpx| and |run_editor|.
277  */
278
279
280 char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ftype)
281 {
282     mplib_instance *mplib_data = mplib_get_data(mp);
283     lua_State *L = mplib_data->LL;
284     lua_checkstack(L, 4);
285     lua_getfield(L, LUA_REGISTRYINDEX, "mplib_file_finder");
286     if (lua_isfunction(L, -1)) {
287         char *s = NULL, *x = NULL;
288         lua_pushstring(L, fname);
289         lua_pushstring(L, fmode);
290         if (ftype >= mp_filetype_text) {
291             lua_pushnumber(L, ftype - mp_filetype_text);
292         } else {
293             lua_pushstring(L, mplib_filetype_names[ftype]);
294         }
295         if (lua_pcall(L, 3, 1, 0) != 0) {
296             fprintf(stdout, "Error in mp.find_file: %s\n",
297                     (char *) lua_tostring(L, -1));
298             return NULL;
299         }
300         x = (char *) lua_tostring(L, -1);
301         if (x != NULL)
302             s = strdup(x);
303         lua_pop(L, 1);          /* pop the string */
304         return s;
305     } else {
306         lua_pop(L, 1);
307     }
308     if (fmode[0] != 'r' || (!access(fname, R_OK)) || ftype) {
309         return strdup(fname);
310     }
311     return NULL;
312 }
313
314 static int mplib_find_file_function(lua_State * L)
315 {
316     if (!(lua_isfunction(L, -1) || lua_isnil(L, -1))) {
317         return 1;               /* error */
318     }
319     lua_pushstring(L, "mplib_file_finder");
320     lua_pushvalue(L, -2);
321     lua_rawset(L, LUA_REGISTRYINDEX);
322     return 0;
323 }
324
325 void *mplib_open_file(MP mp, const char *fname, const char *fmode, int ftype)
326 {
327     File *ff = malloc(sizeof(File));
328     if (ff) {
329         mplib_instance *mplib_data = mplib_get_data(mp);
330         ff->f = NULL;
331         if (ftype == mp_filetype_terminal) {
332             if (fmode[0] == 'r') {
333                 ff->f = stdin;
334             } else {
335                 xfree(mplib_data->term_file_ptr);
336                 ff->f = malloc(1);
337                 mplib_data->term_file_ptr = ff->f;
338             }
339         } else if (ftype == mp_filetype_error) {
340             xfree(mplib_data->err_file_ptr);
341             ff->f = malloc(1);
342             mplib_data->err_file_ptr = ff->f;
343         } else if (ftype == mp_filetype_log) {
344             xfree(mplib_data->log_file_ptr);
345             ff->f = malloc(1);
346             mplib_data->log_file_ptr = ff->f;
347         } else if (ftype == mp_filetype_postscript) {
348             xfree(mplib_data->ps_file_ptr);
349             ff->f = malloc(1);
350             mplib_data->ps_file_ptr = ff->f;
351         } else {
352             char realmode[3];
353             char *f = mplib_find_file(mp, fname, fmode, ftype);
354             if (f == NULL)
355                 return NULL;
356             realmode[0] = *fmode;
357             realmode[1] = 'b';
358             realmode[2] = 0;
359             ff->f = fopen(f, realmode);
360             if ((fmode[0] == 'r') && (ff->f == NULL)) {
361                 free(ff);
362                 return NULL;
363             }
364         }
365         return ff;
366     }
367     return NULL;
368 }
369
370 static int mplib_get_char(void *f, mplib_instance * mplib_data)
371 {
372     int c;
373     if (f == stdin && mplib_data->input_data != NULL) {
374         if (mplib_data->input_data_len == 0) {
375             if (mplib_data->input_data_ptr != NULL)
376                 mplib_data->input_data_ptr = NULL;
377             else
378                 mplib_data->input_data = NULL;
379             c = EOF;
380         } else {
381             mplib_data->input_data_len--;
382             c = *(mplib_data->input_data_ptr)++;
383         }
384     } else {
385         c = fgetc(f);
386     }
387     return c;
388 }
389
390 static void mplib_unget_char(void *f, mplib_instance * mplib_data, int c)
391 {
392     if (f == stdin && mplib_data->input_data_ptr != NULL) {
393         mplib_data->input_data_len++;
394         mplib_data->input_data_ptr--;
395     } else {
396         ungetc(c, f);
397     }
398 }
399
400
401 char *mplib_read_ascii_file(MP mp, void *ff, size_t * size)
402 {
403     char *s = NULL;
404     if (ff != NULL) {
405         int c;
406         size_t len = 0, lim = 128;
407         mplib_instance *mplib_data = mplib_get_data(mp);
408         FILE *f = ((File *) ff)->f;
409         if (f == NULL)
410             return NULL;
411         *size = 0;
412         c = mplib_get_char(f, mplib_data);
413         if (c == EOF)
414             return NULL;
415         s = malloc(lim);
416         if (s == NULL)
417             return NULL;
418         while (c != EOF && c != '\n' && c != '\r') {
419             if (len == lim) {
420                 s = realloc(s, (lim + (lim >> 2)));
421                 if (s == NULL)
422                     return NULL;
423                 lim += (lim >> 2);
424             }
425             s[len++] = c;
426             c = mplib_get_char(f, mplib_data);
427         }
428         if (c == '\r') {
429             c = mplib_get_char(f, mplib_data);
430             if (c != EOF && c != '\n')
431                 mplib_unget_char(f, mplib_data, c);
432         }
433         s[len] = 0;
434         *size = len;
435     }
436     return s;
437 }
438
439 #define APPEND_STRING(a,b) do {                                         \
440     if ((mplib_data->a##_used+strlen(b))>=mplib_data->a##_size) {       \
441       mplib_data->a##_size += 256+(mplib_data->a##_size)/5+strlen(b);   \
442       mplib_data->a = realloc(mplib_data->a,mplib_data->a##_size);      \
443     }                                                                   \
444     (void)strcpy(mplib_data->a+mplib_data->a##_used,b);                 \
445     mplib_data->a##_used += strlen(b);                                  \
446   } while (0)
447
448 void mplib_write_ascii_file(MP mp, void *ff, const char *s)
449 {
450     if (ff != NULL) {
451         void *f = ((File *) ff)->f;
452         mplib_instance *mplib_data = mplib_get_data(mp);
453         if (f != NULL) {
454             if (f == mplib_data->term_file_ptr) {
455                 APPEND_STRING(term_out, s);
456             } else if (f == mplib_data->err_file_ptr) {
457                 APPEND_STRING(error_out, s);
458             } else if (f == mplib_data->log_file_ptr) {
459                 APPEND_STRING(log_out, s);
460             } else if (f == mplib_data->ps_file_ptr) {
461                 APPEND_STRING(ps_out, s);
462             } else {
463                 fprintf((FILE *) f, "%s", s);
464             }
465         }
466     }
467 }
468
469 void mplib_read_binary_file(MP mp, void *ff, void **data, size_t * size)
470 {
471     (void) mp;
472     if (ff != NULL) {
473         size_t len = 0;
474         FILE *f = ((File *) ff)->f;
475         if (f != NULL)
476             len = fread(*data, 1, *size, f);
477         *size = len;
478     }
479 }
480
481 void mplib_write_binary_file(MP mp, void *ff, void *s, size_t size)
482 {
483     (void) mp;
484     if (ff != NULL) {
485         FILE *f = ((File *) ff)->f;
486         if (f != NULL)
487             fwrite(s, size, 1, f);
488     }
489 }
490
491
492 void mplib_close_file(MP mp, void *ff)
493 {
494     if (ff != NULL) {
495         mplib_instance *mplib_data = mplib_get_data(mp);
496         void *f = ((File *) ff)->f;
497         if (f != NULL && f != mplib_data->term_file_ptr
498             && f != mplib_data->err_file_ptr && f != mplib_data->log_file_ptr
499             && f != mplib_data->ps_file_ptr) {
500             fclose(f);
501         }
502         free(ff);
503     }
504 }
505
506 int mplib_eof_file(MP mp, void *ff)
507 {
508     if (ff != NULL) {
509         mplib_instance *mplib_data = mplib_get_data(mp);
510         FILE *f = ((File *) ff)->f;
511         if (f == NULL)
512             return 1;
513         if (f == stdin && mplib_data->input_data != NULL) {
514             return (mplib_data->input_data_len == 0);
515         }
516         return feof(f);
517     }
518     return 1;
519 }
520
521 void mplib_flush_file(MP mp, void *ff)
522 {
523     (void) mp;
524     (void) ff;
525     return;
526 }
527
528 #define APPEND_TO_EDGES(a) do {                                                 \
529     if (mplib_data->edges==NULL) {                                              \
530       mplib_data->edges = hh;                                                   \
531     } else {                                                                                    \
532       struct mp_edge_object *p = mplib_data->edges;             \
533       while (p->_next!=NULL) { p = p->_next; }                  \
534       p->_next = hh;                                                                    \
535     }                                                                                                   \
536 } while (0)
537
538 void mplib_shipout_backend(MP mp, int h)
539 {
540     struct mp_edge_object *hh = mp_gr_export(mp, h);
541     if (hh) {
542         mplib_instance *mplib_data = mplib_get_data(mp);
543         APPEND_TO_EDGES(hh);
544     }
545 }
546
547
548 static void mplib_setup_file_ops(struct MP_options *options)
549 {
550     options->find_file = mplib_find_file;
551     options->open_file = mplib_open_file;
552     options->close_file = mplib_close_file;
553     options->eof_file = mplib_eof_file;
554     options->flush_file = mplib_flush_file;
555     options->write_ascii_file = mplib_write_ascii_file;
556     options->read_ascii_file = mplib_read_ascii_file;
557     options->write_binary_file = mplib_write_binary_file;
558     options->read_binary_file = mplib_read_binary_file;
559     options->shipout_backend = mplib_shipout_backend;
560 }
561
562 static int mplib_new(lua_State * L)
563 {
564     MP *mp_ptr;
565     mp_ptr = lua_newuserdata(L, sizeof(MP *));
566     if (mp_ptr) {
567         int i;
568         mplib_instance *mplib_data;
569         struct MP_options *options;     /* instance options */
570         options = mp_options();
571         mplib_setup_file_ops(options);
572         mplib_data = mplib_make_data();
573         mplib_data->LL = L;
574         options->userdata = (void *) mplib_data;
575         options->noninteractive = 1;    /* required ! */
576         options->print_found_names = 0;
577         if (lua_type(L, 1) == LUA_TTABLE) {
578             for (i = 0; mplib_parms[i].name != NULL; i++) {
579                 lua_getfield(L, 1, mplib_parms[i].name);
580                 if (lua_isnil(L, -1)) {
581                     lua_pop(L, 1);
582                     continue;   /* skip unset */
583                 }
584                 switch (mplib_parms[i].idx) {
585                 case P_ERROR_LINE:
586                     options->error_line = lua_tointeger(L, -1);
587                     break;
588                 case P_HALF_LINE:
589                     options->half_error_line = lua_tointeger(L, -1);
590                     break;
591                 case P_MAX_LINE:
592                     options->max_print_line = lua_tointeger(L, -1);
593                     break;
594                 case P_MAIN_MEMORY:
595                     options->main_memory = lua_tointeger(L, -1);
596                     break;
597                 case P_HASH_SIZE:
598                     options->hash_size = lua_tointeger(L, -1);
599                     break;
600                 case P_HASH_PRIME:
601                     options->hash_prime = lua_tointeger(L, -1);
602                     break;
603                 case P_PARAM_SIZE:
604                     options->param_size = lua_tointeger(L, -1);
605                     break;
606                 case P_IN_OPEN:
607                     options->max_in_open = lua_tointeger(L, -1);
608                     break;
609                 case P_RANDOM_SEED:
610                     options->random_seed = lua_tointeger(L, -1);
611                     break;
612                 case P_INTERACTION:
613                     options->interaction =
614                         luaL_checkoption(L, -1, "errorstopmode",
615                                          interaction_options);
616                     break;
617                 case P_INI_VERSION:
618                     options->ini_version = lua_toboolean(L, -1);
619                     break;
620                 case P_TROFF_MODE:
621                     options->troff_mode = lua_toboolean(L, -1);
622                     break;
623                 case P_PRINT_NAMES:
624                     options->print_found_names = lua_toboolean(L, -1);
625                     break;
626                     /*      
627                        case P_COMMAND_LINE:
628                        options->command_line = strdup((char *)lua_tostring(L,-1));
629                        break;
630                      */
631                 case P_MEM_NAME:
632                     options->mem_name = strdup((char *) lua_tostring(L, -1));
633                     break;
634                 case P_JOB_NAME:
635                     options->job_name = strdup((char *) lua_tostring(L, -1));
636                     break;
637                 case P_FIND_FILE:
638                     if (mplib_find_file_function(L)) {  /* error here */
639                         fprintf(stdout,
640                                 "Invalid arguments to mp.new({find_file=...})\n");
641                     }
642                     break;
643                 default:
644                     break;
645                 }
646                 lua_pop(L, 1);
647             }
648         }
649         *mp_ptr = mp_new(options);
650         xfree(options->command_line);
651         xfree(options->mem_name);
652         xfree(options->job_name);
653         free(options);
654         if (*mp_ptr) {
655             luaL_getmetatable(L, MPLIB_METATABLE);
656             lua_setmetatable(L, -2);
657             return 1;
658         }
659     }
660     lua_pushnil(L);
661     return 1;
662 }
663
664 static int mplib_collect(lua_State * L)
665 {
666     MP *mp_ptr = is_mp(L, 1);
667     if (*mp_ptr != NULL) {
668       mp_free(*mp_ptr);
669       *mp_ptr = NULL;
670     }
671     return 0;
672 }
673
674 static int mplib_tostring(lua_State * L)
675 {
676     MP *mp_ptr = is_mp(L, 1);
677     if (*mp_ptr != NULL) {
678         lua_pushfstring(L, "<MP %p>", *mp_ptr);
679         return 1;
680     }
681     return 0;
682 }
683
684 static int mplib_wrapresults(lua_State * L, mplib_instance * mplib_data, int h)
685 {
686     lua_checkstack(L, 5);
687     lua_newtable(L);
688     if (mplib_data->term_out != NULL) {
689         lua_pushstring(L, mplib_data->term_out);
690         lua_setfield(L, -2, "term");
691         free_stream_buf(term_out);
692     }
693     if (mplib_data->error_out != NULL) {
694         lua_pushstring(L, mplib_data->error_out);
695         lua_setfield(L, -2, "error");
696         free_stream_buf(error_out);
697     }
698     if (mplib_data->log_out != NULL) {
699         lua_pushstring(L, mplib_data->log_out);
700         lua_setfield(L, -2, "log");
701         free_stream_buf(log_out);
702     }
703     if (mplib_data->edges != NULL) {
704         struct mp_edge_object **v;
705         struct mp_edge_object *p = mplib_data->edges;
706         int i = 1;
707         lua_newtable(L);
708         while (p != NULL) {
709             v = lua_newuserdata(L, sizeof(struct mp_edge_object *));
710             *v = p;
711             luaL_getmetatable(L, MPLIB_FIG_METATABLE);
712             lua_setmetatable(L, -2);
713             lua_rawseti(L, -2, i);
714             i++;
715             p = p->_next;
716         }
717         lua_setfield(L, -2, "fig");
718         mplib_data->edges = NULL;
719     }
720     lua_pushnumber(L, h);
721     lua_setfield(L, -2, "status");
722     return 1;
723 }
724
725 static int mplib_execute(lua_State * L)
726 {
727     MP *mp_ptr = is_mp(L, 1);
728     if (*mp_ptr != NULL && lua_isstring(L, 2)) {
729         int h;
730         mplib_instance *mplib_data = mplib_get_data(*mp_ptr);
731         mplib_data->input_data =
732             (char *) lua_tolstring(L, 2, &(mplib_data->input_data_len));
733         mplib_data->input_data_ptr = mplib_data->input_data;
734         if ((*mp_ptr)->run_state == 0) {
735             h = mp_initialize(*mp_ptr);
736         }
737         h = mp_execute(*mp_ptr);
738         if (mplib_data->input_data_len != 0) {
739             mplib_data->input_data = NULL;
740             mplib_data->input_data_ptr = NULL;
741             mplib_data->input_data_len = 0;
742         }
743         return mplib_wrapresults(L, mplib_data, h);
744     } else {
745         lua_pushnil(L);
746     }
747     return 1;
748 }
749
750 static int mplib_finish(lua_State * L)
751 {
752     MP *mp_ptr = is_mp(L, 1);
753     if (*mp_ptr != NULL) {
754         mplib_instance *mplib_data = mplib_get_data(*mp_ptr);
755         int h = mp_finish(*mp_ptr);
756         mp_free(*mp_ptr);
757         *mp_ptr = NULL;
758         return mplib_wrapresults(L, mplib_data, h);
759     } else {
760         lua_pushnil(L);
761     }
762     return 1;
763 }
764
765 static int mplib_statistics(lua_State * L)
766 {
767     MP *mp_ptr = is_mp(L, 1);
768     if (*mp_ptr != NULL) {
769         lua_newtable(L);
770         lua_pushnumber(L, mp_memory_usage(*mp_ptr));
771         lua_setfield(L, -2, "main_memory");
772         lua_pushnumber(L, mp_hash_usage(*mp_ptr));
773         lua_setfield(L, -2, "hash_size");
774         lua_pushnumber(L, mp_param_usage(*mp_ptr));
775         lua_setfield(L, -2, "param_size");
776         lua_pushnumber(L, mp_open_usage(*mp_ptr));
777         lua_setfield(L, -2, "max_in_open");
778     } else {
779         lua_pushnil(L);
780     }
781     return 1;
782 }
783
784
785 /* figure methods */
786
787 static int mplib_fig_collect(lua_State * L)
788 {
789     struct mp_edge_object **hh = is_fig(L, 1);
790     if (*hh != NULL) {
791         mp_gr_toss_objects(*hh);
792         *hh = NULL;
793     }
794     return 0;
795 }
796
797 static int mplib_fig_body(lua_State * L)
798 {
799     int i = 1;
800     struct mp_graphic_object **v;
801     struct mp_graphic_object *p;
802     struct mp_edge_object **hh = is_fig(L, 1);
803     lua_newtable(L);
804     p = (*hh)->body;
805     while (p != NULL) {
806         v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
807         *v = p;
808         luaL_getmetatable(L, MPLIB_GR_METATABLE);
809         lua_setmetatable(L, -2);
810         lua_rawseti(L, -2, i);
811         i++;
812         p = p->_link_field;
813     }
814     (*hh)->body = NULL;         /* prevent double free */
815     return 1;
816 }
817
818 static int mplib_fig_copy_body(lua_State * L)
819 {
820     int i = 1;
821     struct mp_graphic_object **v;
822     struct mp_graphic_object *p;
823     struct mp_edge_object **hh = is_fig(L, 1);
824     lua_newtable(L);
825     p = (*hh)->body;
826     while (p != NULL) {
827         v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
828         *v = mp_gr_copy_object((*hh)->_parent, p);
829         luaL_getmetatable(L, MPLIB_GR_METATABLE);
830         lua_setmetatable(L, -2);
831         lua_rawseti(L, -2, i);
832         i++;
833         p = p->_link_field;
834     }
835     return 1;
836 }
837
838
839 static int mplib_fig_tostring(lua_State * L)
840 {
841     struct mp_edge_object **hh = is_fig(L, 1);
842     lua_pushfstring(L, "<figure %p>", *hh);
843     return 1;
844 }
845
846
847
848 static int
849 mp_wrapped_shipout(struct mp_edge_object *hh, int prologues, int procset)
850 {
851     MP mp = hh->_parent;
852     if (setjmp(mp->jump_buf)) {
853         return 0;
854     }
855     mp_gr_ship_out(hh, prologues, procset);
856     return 1;
857 }
858
859 static int mplib_fig_postscript(lua_State * L)
860 {
861     struct mp_edge_object **hh = is_fig(L, 1);
862     int prologues = luaL_optnumber(L, 2, -1);
863     int procset = luaL_optnumber(L, 3, -1);
864     mplib_instance *mplib_data = mplib_get_data((*hh)->_parent);
865     if (mplib_data->ps_out == NULL) {
866         if (mp_wrapped_shipout(*hh, prologues, procset)) {
867             if (mplib_data->ps_out != NULL) {
868                 lua_pushstring(L, mplib_data->ps_out);
869                 free_stream_buf(ps_out);
870             } else {
871                 lua_pushnil(L);
872             }
873             return 1;
874         } else {
875             lua_pushnil(L);
876             lua_pushstring(L, mplib_data->log_out);
877             xfree(mplib_data->ps_out);
878             return 2;
879         }
880     }
881     lua_pushnil(L);
882     return 1;
883 }
884
885 static int mplib_fig_filename(lua_State * L)
886 {
887     struct mp_edge_object **hh = is_fig(L, 1);
888     if (*hh != NULL) {
889         char *s = (*hh)->_filename;
890         lua_pushstring(L, s);
891     } else {
892         lua_pushnil(L);
893     }
894     return 1;
895 }
896
897
898 static int mplib_fig_bb(lua_State * L)
899 {
900     struct mp_edge_object **hh = is_fig(L, 1);
901     lua_newtable(L);
902     lua_pushnumber(L, (double) (*hh)->_minx / 65536.0);
903     lua_rawseti(L, -2, 1);
904     lua_pushnumber(L, (double) (*hh)->_miny / 65536.0);
905     lua_rawseti(L, -2, 2);
906     lua_pushnumber(L, (double) (*hh)->_maxx / 65536.0);
907     lua_rawseti(L, -2, 3);
908     lua_pushnumber(L, (double) (*hh)->_maxy / 65536.0);
909     lua_rawseti(L, -2, 4);
910     return 1;
911 }
912
913 /* object methods */
914
915 static int mplib_gr_collect(lua_State * L)
916 {
917     struct mp_graphic_object **hh = is_gr_object(L, 1);
918     if (*hh != NULL) {
919         mp_gr_toss_object(*hh);
920         *hh = NULL;
921     }
922     return 0;
923 }
924
925 static int mplib_gr_tostring(lua_State * L)
926 {
927     struct mp_graphic_object **hh = is_gr_object(L, 1);
928     lua_pushfstring(L, "<object %p>", *hh);
929     return 1;
930 }
931
932
933 static int mplib_gr_fields(lua_State * L)
934 {
935     const char **fields;
936     int i;
937     struct mp_graphic_object **hh = is_gr_object(L, 1);
938     if (*hh) {
939         switch ((*hh)->_type_field) {
940         case mp_fill_code:
941             fields = fill_fields;
942             break;
943         case mp_stroked_code:
944             fields = stroked_fields;
945             break;
946         case mp_text_code:
947             fields = text_fields;
948             break;
949         case mp_special_code:
950             fields = special_fields;
951             break;
952         case mp_start_clip_code:
953             fields = start_clip_fields;
954             break;
955         case mp_start_bounds_code:
956             fields = start_bounds_fields;
957             break;
958         case mp_stop_clip_code:
959             fields = stop_clip_fields;
960             break;
961         case mp_stop_bounds_code:
962             fields = stop_bounds_fields;
963             break;
964         default:
965             fields = no_fields;
966         }
967         lua_newtable(L);
968         for (i = 0; fields[i] != NULL; i++) {
969             lua_pushstring(L, fields[i]);
970             lua_rawseti(L, -2, (i + 1));
971         }
972     } else {
973         lua_pushnil(L);
974     }
975     return 1;
976 }
977
978
979 #define mplib_push_number(L,x) lua_pushnumber(L,(lua_Number)(x)/65536.0)
980
981 #define MPLIB_PATH 0
982 #define MPLIB_PEN 1
983
984 static void mplib_push_path(lua_State * L, struct mp_knot *h, int is_pen)
985 {
986     struct mp_knot *p;          /* for scanning the path */
987     int i = 1;
988     p = h;
989     if (p != NULL) {
990         lua_newtable(L);
991         do {
992             lua_createtable(L, 0, 6);
993             if (!is_pen) {
994                 if (p->left_type_field != mp_explicit) {
995                     mplib_push_S(left_type);
996                     lua_pushstring(L, knot_type_enum[p->left_type_field]);
997                     lua_rawset(L, -3);
998                 }
999                 if (p->right_type_field != mp_explicit) {
1000                     mplib_push_S(right_type);
1001                     lua_pushstring(L, knot_type_enum[p->right_type_field]);
1002                     lua_rawset(L, -3);
1003                 }
1004             }
1005             mplib_push_S(x_coord);
1006             mplib_push_number(L, p->x_coord_field);
1007             lua_rawset(L, -3);
1008             mplib_push_S(y_coord);
1009             mplib_push_number(L, p->y_coord_field);
1010             lua_rawset(L, -3);
1011             mplib_push_S(left_x);
1012             mplib_push_number(L, p->left_x_field);
1013             lua_rawset(L, -3);
1014             mplib_push_S(left_y);
1015             mplib_push_number(L, p->left_y_field);
1016             lua_rawset(L, -3);
1017             mplib_push_S(right_x);
1018             mplib_push_number(L, p->right_x_field);
1019             lua_rawset(L, -3);
1020             mplib_push_S(right_y);
1021             mplib_push_number(L, p->right_y_field);
1022             lua_rawset(L, -3);
1023             lua_rawseti(L, -2, i);
1024             i++;
1025             if (p->right_type_field == mp_endpoint) {
1026                 return;
1027             }
1028             p = p->next_field;
1029         } while (p != h);
1030     } else {
1031         lua_pushnil(L);
1032     }
1033 }
1034
1035 /* this assumes that the top of the stack is a table 
1036    or nil already in the case
1037  */
1038 static void mplib_push_pentype(lua_State * L, struct mp_knot *h)
1039 {
1040     struct mp_knot *p;          /* for scanning the path */
1041     p = h;
1042     if (p == NULL) {
1043         /* do nothing */
1044     } else if (p == p->next_field) {
1045         mplib_push_S(type);
1046         lua_pushstring(L, "elliptical");
1047         lua_rawset(L, -3);
1048     } else {
1049     }
1050 }
1051
1052 #define set_color_objects(pq)                           \
1053   object_color_model = pq->color_model_field;           \
1054   object_color_a = pq->color_field._a_val;              \
1055   object_color_b = pq->color_field._b_val;              \
1056   object_color_c = pq->color_field._c_val;              \
1057   object_color_d = pq->color_field._d_val;
1058
1059
1060 static void mplib_push_color(lua_State * L, struct mp_graphic_object *p)
1061 {
1062     int object_color_model;
1063     int object_color_a, object_color_b, object_color_c, object_color_d;
1064     if (p != NULL) {
1065         if (p->_type_field == mp_fill_code) {
1066             mp_fill_object *h = (mp_fill_object *) p;
1067             set_color_objects(h);
1068         } else if (p->_type_field == mp_stroked_code) {
1069             mp_stroked_object *h = (mp_stroked_object *) p;
1070             set_color_objects(h);
1071         } else {
1072             mp_text_object *h = (mp_text_object *) p;
1073             set_color_objects(h);
1074         }
1075         lua_newtable(L);
1076         if (object_color_model >= mp_grey_model) {
1077             mplib_push_number(L, object_color_a);
1078             lua_rawseti(L, -2, 1);
1079             if (object_color_model >= mp_rgb_model) {
1080                 mplib_push_number(L, object_color_b);
1081                 lua_rawseti(L, -2, 2);
1082                 mplib_push_number(L, object_color_c);
1083                 lua_rawseti(L, -2, 3);
1084                 if (object_color_model == mp_cmyk_model) {
1085                     mplib_push_number(L, object_color_d);
1086                     lua_rawseti(L, -2, 4);
1087                 }
1088             }
1089         }
1090     } else {
1091         lua_pushnil(L);
1092     }
1093 }
1094
1095 /* the dash scale is not exported, the field has no external value */
1096 static void mplib_push_dash(lua_State * L, struct mp_stroked_object *h)
1097 {
1098     mp_dash_object *d;
1099     double ds;
1100     if (h != NULL && h->dash_p_field != NULL) {
1101         d = h->dash_p_field;
1102         lua_newtable(L);
1103         mplib_push_number(L, d->offset_field);
1104         lua_setfield(L, -2, "offset");
1105         if (d->array_field != NULL) {
1106             int i = 0;
1107             lua_newtable(L);
1108             while (*(d->array_field + i) != -1) {
1109                 ds = *(d->array_field + 1) / 65536.0;
1110                 lua_pushnumber(L, ds);
1111                 i++;
1112                 lua_rawseti(L, -2, i);
1113             }
1114             lua_setfield(L, -2, "dashes");
1115         }
1116     } else {
1117         lua_pushnil(L);
1118     }
1119 }
1120
1121 static void mplib_push_transform(lua_State * L, struct mp_text_object *h)
1122 {
1123     int i = 1;
1124     if (h != NULL) {
1125         lua_createtable(L, 6, 0);
1126         mplib_push_number(L, h->tx_field);
1127         lua_rawseti(L, -2, i);
1128         i++;
1129         mplib_push_number(L, h->ty_field);
1130         lua_rawseti(L, -2, i);
1131         i++;
1132         mplib_push_number(L, h->txx_field);
1133         lua_rawseti(L, -2, i);
1134         i++;
1135         mplib_push_number(L, h->tyx_field);
1136         lua_rawseti(L, -2, i);
1137         i++;
1138         mplib_push_number(L, h->txy_field);
1139         lua_rawseti(L, -2, i);
1140         i++;
1141         mplib_push_number(L, h->tyy_field);
1142         lua_rawseti(L, -2, i);
1143         i++;
1144     } else {
1145         lua_pushnil(L);
1146     }
1147 }
1148
1149 #define FIELD(A) (mplib_is_S(A,2))
1150
1151 static void mplib_fill_field(lua_State * L, struct mp_fill_object *h)
1152 {
1153     if (FIELD(path)) {
1154         mplib_push_path(L, h->path_p_field, MPLIB_PATH);
1155     } else if (FIELD(htap)) {
1156         mplib_push_path(L, h->htap_p_field, MPLIB_PATH);
1157     } else if (FIELD(pen)) {
1158         mplib_push_path(L, h->pen_p_field, MPLIB_PEN);
1159         mplib_push_pentype(L, h->pen_p_field);
1160     } else if (FIELD(color)) {
1161         mplib_push_color(L, (mp_graphic_object *) h);
1162     } else if (FIELD(linejoin)) {
1163         lua_pushnumber(L, h->ljoin_field);
1164     } else if (FIELD(miterlimit)) {
1165         mplib_push_number(L, h->miterlim_field);
1166     } else if (FIELD(prescript)) {
1167         lua_pushstring(L, h->pre_script_field);
1168     } else if (FIELD(postscript)) {
1169         lua_pushstring(L, h->post_script_field);
1170     } else {
1171         lua_pushnil(L);
1172     }
1173 }
1174
1175 static void mplib_stroked_field(lua_State * L, struct mp_stroked_object *h)
1176 {
1177     if (FIELD(path)) {
1178         mplib_push_path(L, h->path_p_field, MPLIB_PATH);
1179     } else if (FIELD(pen)) {
1180         mplib_push_path(L, h->pen_p_field, MPLIB_PEN);
1181         mplib_push_pentype(L, h->pen_p_field);
1182     } else if (FIELD(color)) {
1183         mplib_push_color(L, (mp_graphic_object *) h);
1184     } else if (FIELD(dash)) {
1185         mplib_push_dash(L, h);
1186     } else if (FIELD(linecap)) {
1187         lua_pushnumber(L, h->lcap_field);
1188     } else if (FIELD(linejoin)) {
1189         lua_pushnumber(L, h->ljoin_field);
1190     } else if (FIELD(miterlimit)) {
1191         mplib_push_number(L, h->miterlim_field);
1192     } else if (FIELD(prescript)) {
1193         lua_pushstring(L, h->pre_script_field);
1194     } else if (FIELD(postscript)) {
1195         lua_pushstring(L, h->post_script_field);
1196     } else {
1197         lua_pushnil(L);
1198     }
1199 }
1200
1201 static void mplib_text_field(lua_State * L, struct mp_text_object *h)
1202 {
1203     if (FIELD(text)) {
1204         lua_pushstring(L, h->text_p_field);
1205     } else if (FIELD(dsize)) {
1206         mplib_push_number(L, (h->font_dsize_field / 16));
1207     } else if (FIELD(font)) {
1208         lua_pushstring(L, h->font_name_field);
1209     } else if (FIELD(color)) {
1210         mplib_push_color(L, (mp_graphic_object *) h);
1211     } else if (FIELD(width)) {
1212         mplib_push_number(L, h->width_field);
1213     } else if (FIELD(height)) {
1214         mplib_push_number(L, h->height_field);
1215     } else if (FIELD(depth)) {
1216         mplib_push_number(L, h->depth_field);
1217     } else if (FIELD(transform)) {
1218         mplib_push_transform(L, h);
1219     } else if (FIELD(prescript)) {
1220         lua_pushstring(L, h->pre_script_field);
1221     } else if (FIELD(postscript)) {
1222         lua_pushstring(L, h->post_script_field);
1223     } else {
1224         lua_pushnil(L);
1225     }
1226 }
1227
1228 static void mplib_special_field(lua_State * L, struct mp_special_object *h)
1229 {
1230     if (FIELD(prescript)) {
1231         lua_pushstring(L, h->pre_script_field);
1232     } else {
1233         lua_pushnil(L);
1234     }
1235 }
1236
1237 static void mplib_start_bounds_field(lua_State * L, struct mp_bounds_object *h)
1238 {
1239     if (FIELD(path)) {
1240         mplib_push_path(L, h->path_p_field, MPLIB_PATH);
1241     } else {
1242         lua_pushnil(L);
1243     }
1244 }
1245
1246 static void mplib_start_clip_field(lua_State * L, struct mp_clip_object *h)
1247 {
1248     if (FIELD(path)) {
1249         mplib_push_path(L, h->path_p_field, MPLIB_PATH);
1250     } else {
1251         lua_pushnil(L);
1252     }
1253 }
1254
1255 static int mplib_gr_index(lua_State * L)
1256 {
1257     struct mp_graphic_object **hh = is_gr_object(L, 1);
1258     if (*hh) {
1259         struct mp_graphic_object *h = *hh;
1260
1261         if (mplib_is_S(type, 2)) {
1262             lua_rawgeti(L, LUA_REGISTRYINDEX, mplib_type_Ses[h->_type_field]);
1263         } else {
1264             switch (h->_type_field) {
1265             case mp_fill_code:
1266                 mplib_fill_field(L, (mp_fill_object *) h);
1267                 break;
1268             case mp_stroked_code:
1269                 mplib_stroked_field(L, (mp_stroked_object *) h);
1270                 break;
1271             case mp_text_code:
1272                 mplib_text_field(L, (mp_text_object *) h);
1273                 break;
1274             case mp_special_code:
1275                 mplib_special_field(L, (mp_special_object *) h);
1276                 break;
1277             case mp_start_clip_code:
1278                 mplib_start_clip_field(L, (mp_clip_object *) h);
1279                 break;
1280             case mp_start_bounds_code:
1281                 mplib_start_bounds_field(L, (mp_bounds_object *) h);
1282                 break;
1283                 /* case mp_stop_clip_code:    */
1284                 /* case mp_stop_bounds_code:  */
1285             default:
1286                 lua_pushnil(L);
1287             }
1288         }
1289     } else {
1290         lua_pushnil(L);
1291     }
1292     return 1;
1293 }
1294
1295
1296 static const struct luaL_reg mplib_meta[] = {
1297     {"__gc", mplib_collect},
1298     {"__tostring", mplib_tostring},
1299     {NULL, NULL}                /* sentinel */
1300 };
1301
1302 static const struct luaL_reg mplib_fig_meta[] = {
1303     {"__gc", mplib_fig_collect},
1304     {"__tostring", mplib_fig_tostring},
1305     {"objects", mplib_fig_body},
1306     {"copy_objects", mplib_fig_copy_body},
1307     {"filename", mplib_fig_filename},
1308     {"postscript", mplib_fig_postscript},
1309     {"boundingbox", mplib_fig_bb},
1310     {NULL, NULL}                /* sentinel */
1311 };
1312
1313 static const struct luaL_reg mplib_gr_meta[] = {
1314     {"__gc", mplib_gr_collect},
1315     {"__tostring", mplib_gr_tostring},
1316     {"__index", mplib_gr_index},
1317     {NULL, NULL}                /* sentinel */
1318 };
1319
1320
1321 static const struct luaL_reg mplib_d[] = {
1322     {"execute", mplib_execute},
1323     {"finish", mplib_finish},
1324     {"statistics", mplib_statistics},
1325     {NULL, NULL}                /* sentinel */
1326 };
1327
1328
1329 static const struct luaL_reg mplib_m[] = {
1330     {"new", mplib_new},
1331     {"fields", mplib_gr_fields},
1332     {NULL, NULL}                /* sentinel */
1333 };
1334
1335
1336 int luaopen_mp(lua_State * L)
1337 {
1338     mplib_init_Ses(L);
1339
1340     luaL_newmetatable(L, MPLIB_GR_METATABLE);
1341     lua_pushvalue(L, -1);       /* push metatable */
1342     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1343     luaL_register(L, NULL, mplib_gr_meta);      /* object meta methods */
1344     lua_pop(L, 1);
1345
1346     luaL_newmetatable(L, MPLIB_FIG_METATABLE);
1347     lua_pushvalue(L, -1);       /* push metatable */
1348     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1349     luaL_register(L, NULL, mplib_fig_meta);     /* figure meta methods */
1350     lua_pop(L, 1);
1351
1352     luaL_newmetatable(L, MPLIB_METATABLE);
1353     lua_pushvalue(L, -1);       /* push metatable */
1354     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1355     luaL_register(L, NULL, mplib_meta); /* meta methods */
1356     luaL_register(L, NULL, mplib_d);    /* dict methods */
1357     luaL_register(L, "mplib", mplib_m); /* module functions */
1358     return 1;
1359 }