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