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