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