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