make the lua frontend reentrant as well
[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   if (ff!=NULL) {
344     FILE *f = ((File *)ff)->f;
345     if (f!=NULL) 
346       len = fread(*data,1,*size,f);
347     *size = len;
348   }
349 }
350
351 void mplib_write_binary_file (MP mp, void *ff, void *s, size_t size) {
352   if (ff!=NULL) {
353     FILE *f = ((File *)ff)->f;
354     if (f!=NULL)
355       fwrite(s,size,1,f);
356   }
357 }
358
359
360 void mplib_close_file (MP mp, void *ff) {
361   mplib_instance *mplib_data = mplib_get_data(mp);
362   if (ff!=NULL) {
363     void *f = ((File *)ff)->f;
364     if (f != NULL && f != mplib_data->term_file_ptr && f != mplib_data->err_file_ptr
365         && f != mplib_data->log_file_ptr && f != mplib_data->ps_file_ptr) {
366       fclose(f);
367     }
368     free(ff);
369   }
370 }
371
372 int mplib_eof_file (MP mp, void *ff) {
373   mplib_instance *mplib_data = mplib_get_data(mp);
374   if (ff!=NULL) {
375     FILE *f = ((File *)ff)->f;
376     if (f==NULL)
377       return 1;
378     if (f==stdin && mplib_data->input_data != NULL) {   
379       return (mplib_data->input_data_len==0);
380     }
381     return feof(f);
382   }
383   return 1;
384 }
385
386 void mplib_flush_file (MP mp, void *ff) {
387   return ;
388 }
389
390 #define APPEND_TO_EDGES(a) do {                 \
391     if (mplib_data->edges==NULL) {                              \
392       mplib_data->edges = hh;                           \
393     } else {                                    \
394       struct mp_edge_object *p = mplib_data->edges;             \
395       while (p->_next!=NULL) { p = p->_next; }  \
396       p->_next = hh;                            \
397     }                                           \
398 } while (0)
399
400 void mplib_shipout_backend (MP mp, int h) {
401   struct mp_edge_object *hh; 
402   mplib_instance *mplib_data = mplib_get_data(mp);
403   hh = mp_gr_export(mp, h);
404   if (hh) {
405     APPEND_TO_EDGES(hh); 
406   }
407 }
408
409
410 static void 
411 mplib_setup_file_ops(struct MP_options * options) {
412   options->find_file         = mplib_find_file;
413   options->open_file         = mplib_open_file;
414   options->close_file        = mplib_close_file;
415   options->eof_file          = mplib_eof_file;
416   options->flush_file        = mplib_flush_file;
417   options->write_ascii_file  = mplib_write_ascii_file;
418   options->read_ascii_file   = mplib_read_ascii_file;
419   options->write_binary_file = mplib_write_binary_file;
420   options->read_binary_file  = mplib_read_binary_file;
421   options->shipout_backend   = mplib_shipout_backend;
422 }
423
424 static int 
425 mplib_new (lua_State *L) {
426   MP *mp_ptr;
427   int i;
428   mplib_instance *mplib_data;
429   struct MP_options * options; /* instance options */
430   mp_ptr = lua_newuserdata(L, sizeof(MP *));
431   if (mp_ptr) {
432     options = mp_options();
433     mplib_setup_file_ops(options);
434     mplib_data = mplib_make_data();
435     mplib_data->LL = L;
436     options->userdata = (void *)mplib_data;
437     options->noninteractive = 1; /* required ! */
438     options->print_found_names = 0;
439     if (lua_type(L,1)==LUA_TTABLE) {
440       for (i=0;mplib_parms[i].name!=NULL;i++) {
441         lua_getfield(L,1,mplib_parms[i].name);
442         if (lua_isnil(L,-1)) {
443           lua_pop(L,1);
444           continue; /* skip unset */
445         }
446         switch(mplib_parms[i].idx) {
447         case P_ERROR_LINE: 
448           options->error_line = lua_tointeger(L,-1);
449           break;
450         case P_HALF_LINE:   
451           options->half_error_line = lua_tointeger(L,-1);
452           break;
453         case P_MAX_LINE:
454           options->max_print_line = lua_tointeger(L,-1);
455           break;
456         case P_MAIN_MEMORY:
457           options->main_memory = lua_tointeger(L,-1);
458           break;
459         case P_HASH_SIZE:
460           options->hash_size = lua_tointeger(L,-1);
461           break;
462         case P_HASH_PRIME:
463           options->hash_prime = lua_tointeger(L,-1);
464           break;
465         case P_PARAM_SIZE:
466           options->param_size = lua_tointeger(L,-1);
467           break;
468         case P_IN_OPEN:
469           options->max_in_open = lua_tointeger(L,-1);
470           break;
471         case P_RANDOM_SEED:
472           options->random_seed = lua_tointeger(L,-1);
473           break;
474         case P_INTERACTION:
475           options->interaction = luaL_checkoption(L,-1,"errorstopmode", interaction_options);
476           break;
477         case P_INI_VERSION:
478           options->ini_version = lua_toboolean(L,-1);
479           break;
480         case P_TROFF_MODE:
481           options->troff_mode = lua_toboolean(L,-1);
482           break;
483         case P_PRINT_NAMES:
484           options->print_found_names = lua_toboolean(L,-1);
485           break;
486           /*      
487         case P_COMMAND_LINE:
488           options->command_line = strdup((char *)lua_tostring(L,-1));
489           break;
490           */
491         case P_MEM_NAME:
492           options->mem_name = strdup((char *)lua_tostring(L,-1));
493           break;
494         case P_JOB_NAME:
495           options->job_name = strdup((char *)lua_tostring(L,-1));
496           break;
497         case P_FIND_FILE:  
498           if(mplib_find_file_function(L)) { /* error here */
499             fprintf(stdout,"Invalid arguments to mp.new({find_file=...})\n");
500           }
501           break;
502         default:
503           break;
504         }
505         lua_pop(L,1);
506       }
507     }
508     *mp_ptr = mp_new(options);
509     xfree(options->command_line);
510     xfree(options->mem_name);
511     xfree(options->job_name);
512     free(options);
513     if (*mp_ptr) {
514       luaL_getmetatable(L,MPLIB_METATABLE);
515       lua_setmetatable(L,-2);
516       return 1;
517     }
518   }
519   lua_pushnil(L);
520   return 1;
521 }
522
523 static int
524 mplib_collect (lua_State *L) {
525   MP *mp_ptr = is_mp(L,1);
526   xfree(*mp_ptr);
527   return 0;
528 }
529
530 static int
531 mplib_tostring (lua_State *L) {
532   MP *mp_ptr = is_mp(L,1);
533   if (*mp_ptr!=NULL) {
534     lua_pushfstring(L,"<MP %p>",*mp_ptr);
535      return 1;
536   }
537   return 0;
538 }
539
540 static int 
541 mplib_wrapresults(lua_State *L, mplib_instance *mplib_data, int h) {
542    lua_checkstack(L,5);
543    lua_newtable(L);
544    if (mplib_data->term_out != NULL) {
545      lua_pushstring(L,mplib_data->term_out);
546      lua_setfield(L,-2,"term");
547      xfree(mplib_data->term_out);
548    }
549    if (mplib_data->error_out != NULL) {
550      lua_pushstring(L,mplib_data->error_out);
551      lua_setfield(L,-2,"error");
552      xfree(mplib_data->error_out);
553    } 
554    if (mplib_data->log_out != NULL ) {
555      lua_pushstring(L,mplib_data->log_out);
556      lua_setfield(L,-2,"log");
557      xfree(mplib_data->log_out);
558    }
559    if (mplib_data->edges != NULL ) {
560      struct mp_edge_object **v;
561      struct mp_edge_object *p = mplib_data->edges;
562      int i = 1;
563      lua_newtable(L);
564      while (p!=NULL) { 
565        v = lua_newuserdata (L, sizeof(struct mp_edge_object *));
566        *v = p;
567        luaL_getmetatable(L,MPLIB_FIG_METATABLE);
568        lua_setmetatable(L,-2);
569        lua_rawseti(L,-2,i); i++;
570        p = p->_next;
571      }
572      lua_setfield(L,-2,"fig");
573      mplib_data->edges = NULL;
574    }
575    lua_pushnumber(L,h);
576    lua_setfield(L,-2,"status");
577    return 1;
578 }
579
580 static int
581 mplib_execute (lua_State *L) {
582   int h;
583   MP *mp_ptr = is_mp(L,1);
584   if (*mp_ptr!=NULL && lua_isstring(L,2)) {
585     mplib_instance *mplib_data = mplib_get_data(*mp_ptr);
586     mplib_data->input_data = (char *)lua_tolstring(L,2, &(mplib_data->input_data_len));
587     mplib_data->input_data_ptr = mplib_data->input_data;
588     if ((*mp_ptr)->run_state==0) {
589       h = mp_initialize(*mp_ptr);
590     }
591     h = mp_execute(*mp_ptr);
592     if (mplib_data->input_data_len!=0) {
593       xfree(mplib_data->input_data);
594       xfree(mplib_data->input_data_ptr);
595       mplib_data->input_data_len=0;
596     }
597     return mplib_wrapresults(L, mplib_data, h);
598   } else {
599     lua_pushnil(L);
600   }
601   return 1;
602 }
603
604 static int
605 mplib_finish (lua_State *L) {
606   MP *mp_ptr = is_mp(L,1);
607   if (*mp_ptr!=NULL) {
608     mplib_instance *mplib_data = mplib_get_data(*mp_ptr);
609     int h = mp_finish(*mp_ptr);
610     return mplib_wrapresults(L, mplib_data, h);
611   } else {
612     lua_pushnil(L);
613   }
614   return 1;
615 }
616
617 /* figure methods */
618
619 static int
620 mplib_fig_collect (lua_State *L) {
621   struct mp_edge_object **hh = is_fig(L,1);
622   if (*hh!=NULL) {
623     mp_gr_toss_objects (*hh);
624     *hh=NULL;
625   }
626   return 0;
627 }
628
629 static int
630 mplib_fig_body (lua_State *L) {
631   int i = 1;
632   struct mp_graphic_object **v;
633   struct mp_graphic_object *p;
634   struct mp_edge_object **hh = is_fig(L,1);
635   lua_newtable(L);
636   p = (*hh)->body;
637   while (p!=NULL) {
638     v = lua_newuserdata (L, sizeof(struct mp_graphic_object *));
639     *v = p;
640     luaL_getmetatable(L,MPLIB_GR_METATABLE);
641     lua_setmetatable(L,-2);
642     lua_rawseti(L,-2,i); i++;
643     p = p->_link_field;
644   }
645   (*hh)->body = NULL; /* prevent double free */
646   return 1;
647 }
648
649
650 static int
651 mplib_fig_tostring (lua_State *L) {
652   struct mp_edge_object **hh = is_fig(L,1);
653   lua_pushfstring(L,"<figure %p>",*hh);
654   return 1;
655 }
656
657
658
659 static int 
660 mp_wrapped_shipout (struct mp_edge_object *hh, int prologues, int procset) {
661   MP mp = hh->_parent;
662   if (setjmp(mp->jump_buf)) {
663     return 0;
664   }
665   mp_gr_ship_out(hh,prologues,procset);
666   return 1;
667 }
668
669 static int
670 mplib_fig_postscript (lua_State *L) {
671   struct mp_edge_object **hh = is_fig(L,1);
672   int prologues = luaL_optnumber(L,2,-1);
673   int procset = luaL_optnumber(L,3,-1);
674   mplib_instance *mplib_data = mplib_get_data((*hh)->_parent);
675   if (mplib_data->ps_out == NULL) {
676     if (mp_wrapped_shipout(*hh,prologues, procset)) {
677       if (mplib_data->ps_out!=NULL ) {
678         lua_pushstring(L, mplib_data->ps_out);
679         xfree(mplib_data->ps_out);
680       } else {
681         lua_pushnil(L);
682       }
683       return 1;
684     } else {
685       lua_pushnil(L);
686       lua_pushstring(L,mplib_data->log_out);
687       xfree(mplib_data->ps_out); 
688       return 2;
689     }
690   }
691   lua_pushnil(L);
692   return 1;
693 }
694
695 static int
696 mplib_fig_filename (lua_State *L) {
697   struct mp_edge_object **hh = is_fig(L,1);
698   if (*hh!= NULL) { 
699         char *s = (*hh)->_filename;
700         lua_pushstring(L,s);
701   } else {
702         lua_pushnil(L);
703   }
704   return 1;
705 }
706
707
708 static int
709 mplib_fig_bb (lua_State *L) {
710   struct mp_edge_object **hh = is_fig(L,1);
711   lua_newtable(L);
712   lua_pushnumber(L, (double)(*hh)->_minx/65536.0);
713   lua_rawseti(L,-2,1);
714   lua_pushnumber(L, (double)(*hh)->_miny/65536.0);
715   lua_rawseti(L,-2,2);
716   lua_pushnumber(L, (double)(*hh)->_maxx/65536.0);
717   lua_rawseti(L,-2,3);
718   lua_pushnumber(L, (double)(*hh)->_maxy/65536.0);
719   lua_rawseti(L,-2,4);
720   return 1;
721 }
722
723 /* object methods */
724
725 static int
726 mplib_gr_collect (lua_State *L) {
727   struct mp_graphic_object **hh = is_gr_object(L,1);
728   if (*hh!=NULL) {
729     mp_gr_toss_object(*hh);
730     *hh=NULL;
731   }
732   return 0;
733 }
734
735 static int
736 mplib_gr_tostring (lua_State *L) {
737   struct mp_graphic_object **hh = is_gr_object(L,1);
738   lua_pushfstring(L,"<object %p>",*hh);
739   return 1;
740 }
741
742 static void 
743 mplib_push_number (lua_State *L, integer x ) {
744   lua_Number y;
745   y = x / 65536.0;
746   lua_pushnumber(L, y);
747 }
748
749 static void 
750 mplib_push_path (lua_State *L, struct mp_knot *h ) {
751   struct mp_knot *p; /* for scanning the path */
752   int i=1;
753   p=h;
754   if (p!=NULL) {
755     lua_newtable(L);
756     do {  
757       lua_newtable(L);
758       lua_pushstring(L,knot_originator_enum[p->originator_field]);
759       lua_setfield(L,-2,"originator");
760       lua_pushstring(L,knot_type_enum[p->left_type_field]);
761       lua_setfield(L,-2,"left_type");
762       lua_pushstring(L,knot_type_enum[p->right_type_field]);
763       lua_setfield(L,-2,"right_type");
764       mplib_push_number(L,p->x_coord_field);
765       lua_setfield(L,-2,"x_coord");
766       mplib_push_number(L,p->y_coord_field);
767       lua_setfield(L,-2,"y_coord");
768       mplib_push_number(L,p->left_x_field);
769       lua_setfield(L,-2,"left_x");
770       mplib_push_number(L,p->left_y_field);
771       lua_setfield(L,-2,"left_y");
772       mplib_push_number(L,p->right_x_field);
773       lua_setfield(L,-2,"right_x");
774       mplib_push_number(L,p->right_y_field);
775       lua_setfield(L,-2,"right_y");
776       lua_rawseti(L,-2,i); i++;
777       if ( p->right_type_field==mp_endpoint ) { 
778         return;
779       }
780       p=p->next_field;
781     } while (p!=h) ;
782   } else {
783     lua_pushnil(L);
784   }
785 }
786
787 static void 
788 mplib_push_color (lua_State *L, struct mp_graphic_object *h ) {
789   if (h!=NULL) {
790     lua_newtable(L);
791     lua_pushstring(L,color_model_enum[h->color_model_field]);
792     lua_setfield(L,-2,"model");
793
794     if (h->color_model_field == mp_rgb_model ||
795         h->color_model_field == mp_uninitialized_model) {
796       lua_newtable(L);
797       mplib_push_number(L,h->color_field.rgb._red_val);
798       lua_rawseti(L,-2,1);
799       mplib_push_number(L,h->color_field.rgb._green_val);
800       lua_rawseti(L,-2,2);
801       mplib_push_number(L,h->color_field.rgb._blue_val);
802       lua_rawseti(L,-2,3);
803       lua_setfield(L,-2,"rgb");
804     }
805
806     if (h->color_model_field == mp_cmyk_model ||
807         h->color_model_field == mp_uninitialized_model) {
808       lua_newtable(L);
809       mplib_push_number(L,h->color_field.cmyk._cyan_val);
810       lua_rawseti(L,-2,1);
811       mplib_push_number(L,h->color_field.cmyk._magenta_val);
812       lua_rawseti(L,-2,2);
813       mplib_push_number(L,h->color_field.cmyk._yellow_val);
814       lua_rawseti(L,-2,3);
815       mplib_push_number(L,h->color_field.cmyk._black_val);
816       lua_rawseti(L,-2,4);
817       lua_setfield(L,-2,"cmyk");
818     }
819     if (h->color_model_field == mp_grey_model ||
820         h->color_model_field == mp_uninitialized_model) {
821       lua_newtable(L);
822       mplib_push_number(L,h->color_field.grey._grey_val);
823       lua_rawseti(L,-2,1);
824       lua_setfield(L,-2,"grey");
825     }
826     
827   } else {
828     lua_pushnil(L);
829   }
830 }
831
832 /* dashes are complicated, perhaps it would be better if the
833   object had a PS-compatible representation */
834 static void 
835 mplib_push_dash (lua_State *L, struct mp_graphic_object *h ) {
836   if (h!=NULL) {
837     lua_newtable(L);
838     mplib_push_number(L,h->dash_scale_field);
839     lua_setfield(L,-2,"scale");
840     /* todo */
841     lua_pushnumber(L,(int)h->dash_p_field);
842     lua_setfield(L,-2,"dashes");
843   } else {
844     lua_pushnil(L);
845   }
846 }
847
848 static void 
849 mplib_push_transform (lua_State *L, struct mp_graphic_object *h ) {
850   int i = 1;
851   if (h!=NULL) {
852     lua_newtable(L);
853     mplib_push_number(L,h->tx_field);
854     lua_rawseti(L,-2,i); i++;
855     mplib_push_number(L,h->ty_field);
856     lua_rawseti(L,-2,i); i++;
857     mplib_push_number(L,h->txx_field);
858     lua_rawseti(L,-2,i); i++;
859     mplib_push_number(L,h->txy_field);
860     lua_rawseti(L,-2,i); i++;
861     mplib_push_number(L,h->tyx_field);
862     lua_rawseti(L,-2,i); i++;
863     mplib_push_number(L,h->tyy_field);
864     lua_rawseti(L,-2,i); i++;
865   } else {
866     lua_pushnil(L);
867   }
868 }
869
870
871 static void 
872 mplib_fill_field (lua_State *L, struct mp_graphic_object *h, char *field) {
873   if (FIELD(type)) {
874     lua_pushstring(L,"fill");
875   } else if (FIELD(path)) {
876     mplib_push_path(L, h->path_p_field);
877   } else if (FIELD(htap)) {
878     mplib_push_path(L, h->htap_p_field);
879   } else if (FIELD(pen)) {
880     mplib_push_path(L, h->pen_p_field);
881   } else if (FIELD(color)) {
882     mplib_push_color(L, h);
883   } else if (FIELD(linejoin)) {
884     lua_pushnumber(L,h->ljoin_field);
885   } else if (FIELD(miterlimit)) {
886     mplib_push_number(L,h->miterlim_field);
887   } else if (FIELD(prescript)) {
888     lua_pushstring(L,h->pre_script_field);
889   } else if (FIELD(postscript)) {
890     lua_pushstring(L,h->post_script_field);
891   } else {
892     lua_pushnil(L);
893   }
894 }
895
896 static void 
897 mplib_stroked_field (lua_State *L, struct mp_graphic_object *h, char *field) {
898   if (FIELD(type)) {
899     lua_pushstring(L,"outline");
900   } else if (FIELD(path)) {
901     mplib_push_path(L, h->path_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(dash)) {
907     mplib_push_dash(L, h);
908   } else if (FIELD(linecap)) {
909     lua_pushnumber(L,h->lcap_field);
910   } else if (FIELD(linejoin)) {
911     lua_pushnumber(L,h->ljoin_field);
912   } else if (FIELD(miterlimit)) {
913     mplib_push_number(L,h->miterlim_field);
914   } else if (FIELD(prescript)) {
915     lua_pushstring(L,h->pre_script_field);
916   } else if (FIELD(postscript)) {
917     lua_pushstring(L,h->post_script_field);
918   } else {
919     lua_pushnil(L);
920   }
921 }
922
923 static void 
924 mplib_text_field (lua_State *L, struct mp_graphic_object *h, char *field) {
925   
926   if (FIELD(type)) {
927     lua_pushstring(L,"text");
928   } else if (FIELD(text)) {
929     lua_pushstring(L,h->text_p_field);
930   } else if (FIELD(dsize)) {
931     lua_pushnumber(L,h->font_dsize_field);
932   } else if (FIELD(font)) {
933     lua_pushstring(L,h->font_name_field);
934   } else if (FIELD(color)) {
935     mplib_push_color(L, h);
936   } else if (FIELD(width)) {
937     mplib_push_number(L,h->width_field);
938   } else if (FIELD(height)) {
939     mplib_push_number(L,h->height_field);
940   } else if (FIELD(depth)) {
941     mplib_push_number(L,h->depth_field);
942   } else if (FIELD(transform)) {
943     mplib_push_transform(L,h);
944   } else if (FIELD(prescript)) {
945     lua_pushstring(L,h->pre_script_field);
946   } else if (FIELD(postscript)) {
947     lua_pushstring(L,h->post_script_field);
948   } else {
949     lua_pushnil(L);
950   }
951 }
952
953 static void 
954 mplib_special_field (lua_State *L, struct mp_graphic_object *h, char *field) {
955   if (FIELD(type)) {
956     lua_pushstring(L,"special");
957   } else if (FIELD(prescript)) {
958     lua_pushstring(L,h->pre_script_field);
959   } else {
960     lua_pushnil(L);
961   }
962 }
963
964 static void 
965 mplib_start_bounds_field (lua_State *L, struct mp_graphic_object *h, char *field) {
966   if (FIELD(type)) {
967     lua_pushstring(L,"start_bounds");
968   } else if (FIELD(path)) {
969     mplib_push_path(L,h->path_p_field);
970   } else {
971     lua_pushnil(L);
972   }
973 }
974
975 static void 
976 mplib_start_clip_field (lua_State *L, struct mp_graphic_object *h, char *field) {
977   if (FIELD(type)) {
978     lua_pushstring(L,"start_clip");
979   } else if (FIELD(path)) {
980     mplib_push_path(L,h->path_p_field);
981   } else {
982     lua_pushnil(L);
983   }
984 }
985
986 static void 
987 mplib_stop_bounds_field (lua_State *L, struct mp_graphic_object *h, char *field) {
988   if (h!=NULL && FIELD(type)) {
989     lua_pushstring(L,"stop_bounds");
990   } else {
991     lua_pushnil(L);
992   }
993 }
994
995 static void 
996 mplib_stop_clip_field (lua_State *L, struct mp_graphic_object *h, char *field) {
997   if (h!=NULL && FIELD(type)) {
998     lua_pushstring(L,"stop_clip");
999   } else {
1000     lua_pushnil(L);
1001   }
1002 }
1003
1004 static int
1005 mplib_gr_fields (lua_State *L) {
1006   const char **fields;
1007   const char *f;
1008   int i = 1;
1009   struct mp_graphic_object **hh = is_gr_object(L,1);
1010   if (*hh) {
1011     switch ((*hh)->_type_field) {
1012     case mp_fill_code:         fields = fill_fields;         break;
1013     case mp_stroked_code:      fields = stroked_fields;      break;
1014     case mp_text_code:         fields = text_fields;         break;
1015     case mp_special_code:      fields = special_fields;      break;
1016     case mp_start_clip_code:   fields = start_clip_fields;   break;
1017     case mp_start_bounds_code: fields = start_bounds_fields; break;
1018     case mp_stop_clip_code:    fields = stop_clip_fields;    break;
1019     case mp_stop_bounds_code:  fields = stop_bounds_fields;  break;
1020     default:                   fields = no_fields;
1021     }
1022     lua_newtable(L);
1023     for (f = *fields; f != NULL; f++) {
1024       lua_pushstring(L,f);
1025       lua_rawseti(L,-2,i); i++;
1026     }
1027   } else {
1028     lua_pushnil(L);
1029   }
1030   return 1;
1031 }
1032
1033 static int
1034 mplib_gr_index (lua_State *L) {
1035   struct mp_graphic_object **hh = is_gr_object(L,1);
1036   char *field = (char *)luaL_checkstring(L,2);
1037   if (*hh) {
1038     switch ((*hh)->_type_field) {
1039     case mp_fill_code:         mplib_fill_field(L,*hh,field);         break;
1040     case mp_stroked_code:      mplib_stroked_field(L,*hh,field);      break;
1041     case mp_text_code:         mplib_text_field(L,*hh,field);         break;
1042     case mp_special_code:      mplib_special_field(L,*hh,field);      break;
1043     case mp_start_clip_code:   mplib_start_clip_field(L,*hh,field);   break;
1044     case mp_start_bounds_code: mplib_start_bounds_field(L,*hh,field); break;
1045     case mp_stop_clip_code:    mplib_stop_clip_field(L,*hh,field);    break;
1046     case mp_stop_bounds_code:  mplib_stop_bounds_field(L,*hh,field);  break;
1047     default:                   lua_pushnil(L);
1048     }    
1049   } else {
1050     lua_pushnil(L);
1051   }
1052   return 1;
1053 }
1054
1055
1056 static const struct luaL_reg mplib_meta[] = {
1057   {"__gc",               mplib_collect}, 
1058   {"__tostring",         mplib_tostring},
1059   {NULL, NULL}                /* sentinel */
1060 };
1061
1062 static const struct luaL_reg mplib_fig_meta[] = {
1063   {"__gc",               mplib_fig_collect    },
1064   {"__tostring",         mplib_fig_tostring   },
1065   {"objects",            mplib_fig_body       },
1066   {"filename",           mplib_fig_filename   },
1067   {"postscript",         mplib_fig_postscript },
1068   {"boundingbox",        mplib_fig_bb         },
1069   {NULL, NULL}                /* sentinel */
1070 };
1071
1072 static const struct luaL_reg mplib_gr_meta[] = {
1073   {"__gc",               mplib_gr_collect  },
1074   {"__tostring",         mplib_gr_tostring },
1075   {"__index",            mplib_gr_index    },
1076   {"fields",             mplib_gr_fields   },
1077   {NULL, NULL}                /* sentinel */
1078 };
1079
1080
1081 static const struct luaL_reg mplib_d [] = {
1082   {"execute",            mplib_execute },
1083   {"finish",             mplib_finish },
1084   {NULL, NULL}  /* sentinel */
1085 };
1086
1087
1088 static const struct luaL_reg mplib_m[] = {
1089   {"new",                 mplib_new},
1090   {NULL, NULL}                /* sentinel */
1091 };
1092
1093
1094 int 
1095 luaopen_mp (lua_State *L) {
1096   luaL_newmetatable(L,MPLIB_GR_METATABLE);
1097   lua_pushvalue(L, -1); /* push metatable */
1098   lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
1099   luaL_register(L, NULL, mplib_gr_meta);  /* object meta methods */
1100   lua_pop(L,1);
1101
1102   luaL_newmetatable(L,MPLIB_FIG_METATABLE);
1103   lua_pushvalue(L, -1); /* push metatable */
1104   lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
1105   luaL_register(L, NULL, mplib_fig_meta);  /* figure meta methods */
1106   lua_pop(L,1);
1107
1108   luaL_newmetatable(L,MPLIB_METATABLE);
1109   lua_pushvalue(L, -1); /* push metatable */
1110   lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
1111   luaL_register(L, NULL, mplib_meta);  /* meta methods */
1112   luaL_register(L, NULL, mplib_d);  /* dict methods */
1113   luaL_register(L, "mplib", mplib_m); /* module functions */
1114   return 1;
1115 }
1116