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