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