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