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