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