explicit free of gr_objects is better
[mplib] / src / texk / web2c / mpdir / lmplib.c
1 /* $Id$ */
2
3 #include <lua.h>
4 #include <lauxlib.h>
5 #include <lualib.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9
10 #include "mplib.h"
11 #include "mpmp.h"
12 #include "mppsout.h" /* for mp_edge_object */
13
14 #define MPLIB_METATABLE "MPlib"
15 #define MPLIB_FIG_METATABLE "MPlib.fig"
16
17 #define xfree(A) if (A!=NULL) free(A)
18
19
20 #define is_mp(L,b) (MP *)luaL_checkudata(L,b,MPLIB_METATABLE)
21 #define is_fig(L,b) (struct mp_edge_object **)luaL_checkudata(L,b,MPLIB_FIG_METATABLE)
22
23 typedef enum {  
24   P__ZERO,       P_ERROR_LINE,  P_HALF_LINE,   P_MAX_LINE,    P_MAIN_MEMORY, 
25   P_HASH_SIZE,   P_HASH_PRIME,  P_PARAM_SIZE,  P_IN_OPEN,     P_RANDOM_SEED, 
26   P_INTERACTION, P_INI_VERSION, P_TROFF_MODE,  P_PRINT_NAMES, P_COMMAND_LINE,
27   P_MEM_NAME,    P_JOB_NAME,    P_FIND_FILE,   P_OPEN_FILE,   P_CLOSE_FILE,  
28   P_EOF_FILE,    P_FLUSH_FILE,  P_WRITE_ASCII, P_READ_ASCII,  P_WRITE_BINARY,
29   P_READ_BINARY, P_RUN_EDITOR,  P_RUN_MAKEMPX, P_SHIPOUT,     P__SENTINEL,
30 } parm_idx;
31
32 typedef struct {
33     const char *name;           /* parameter name */
34     parm_idx idx;               /* parameter index */
35     int class;                  /* parameter class */
36 } parm_struct;
37
38 const char *interaction_options[] = 
39   { "unknownmode","batchmode","nonstopmode","scrollmode","errorstopmode", NULL};
40
41
42 parm_struct img_parms[] = {
43   {NULL,                P__ZERO,       0   },  /* dummy; lua indices run from 1 */
44   {"error_line",        P_ERROR_LINE,  'i' },
45   {"half_error_line",   P_HALF_LINE,   'i' },
46   {"max_print_line",    P_MAX_LINE,    'i' },
47   {"main_memory",       P_MAIN_MEMORY, 'i' },
48   {"hash_size",         P_HASH_SIZE,   'i' },
49   {"hash_prime",        P_HASH_PRIME,  'i' },
50   {"param_size",        P_PARAM_SIZE,  'i' },
51   {"max_in_open",       P_IN_OPEN,     'i' },
52   {"random_seed",       P_RANDOM_SEED, 'i' },
53   {"interaction",       P_INTERACTION, 'e' },
54   {"ini_version",       P_INI_VERSION, 'b' },
55   {"troff_mode",        P_TROFF_MODE,  'b' },
56   {"print_found_names", P_PRINT_NAMES, 'b' },
57   {"command_line",      P_COMMAND_LINE,'s' },
58   {"mem_name",          P_MEM_NAME,    's' },
59   {"job_name",          P_JOB_NAME,    's' },
60 #if 0
61   {"find_file",         P_FIND_FILE,   'p' }, 
62   {"open_file",         P_OPEN_FILE,   'p' },
63   {"close_file",        P_CLOSE_FILE,  'p' },
64   {"eof_file",          P_EOF_FILE,    'p' },
65   {"flush_file",        P_FLUSH_FILE,  'p' },
66   {"write_ascii_file",  P_WRITE_ASCII, 'p' },
67   {"read_ascii_file",   P_READ_ASCII,  'p' },
68   {"write_binary_file", P_WRITE_BINARY,'p' },
69   {"read_binary_file",  P_READ_BINARY, 'p' },
70   {"run_editor",        P_RUN_EDITOR,  'p' },
71   {"run_make_mpx",      P_RUN_MAKEMPX, 'p' },
72   {"shipout_backend",   P_SHIPOUT,     'p' },
73 #endif
74   {NULL,                P__SENTINEL,   0   }
75 };
76
77 typedef struct _FILE_ITEM {
78   FILE *f;
79 } _FILE_ITEM ;
80
81 typedef struct _FILE_ITEM File;
82
83 /* Start by defining all the callback routines for the library 
84  * except |run_make_mpx| and |run_editor|.
85  */
86
87 char *mplib_filetype_names[] = {"term", "error", "mp", "log", "ps",
88                               "mem", "tfm", "map", "pfb", "enc", NULL};
89
90 lua_State *LL = NULL;
91
92 char *mplib_find_file (char *fname, char *fmode, int ftype)  {
93   if (LL!=NULL) {
94     lua_State *L = LL;
95     lua_checkstack(L,4);
96     lua_getfield(L,LUA_REGISTRYINDEX,"mplib_file_finder");
97     if (lua_isfunction(L,-1)) {
98       char *s = NULL, *x = NULL;
99       lua_pushstring(L, fname);
100       lua_pushstring(L, fmode);
101       if (ftype >= mp_filetype_text) {
102         lua_pushnumber(L, ftype-mp_filetype_text);
103       } else {
104         lua_pushstring(L, mplib_filetype_names[ftype]);
105       }
106       if(lua_pcall(L,3,1,0) != 0) {
107         fprintf(stdout,"Error in mp.find_file: %s\n", (char *)lua_tostring(L,-1));
108         return NULL;
109       }
110       x = (char *)lua_tostring(L,-1);
111       if (x!=NULL)
112         s = strdup(x);
113       lua_pop(L,1); /* pop the string */
114       return s;
115     } else {
116       lua_pop(L,1);
117     }
118   }
119   if (fmode[0] != 'r' || (! access (fname,R_OK)) || ftype) {  
120      return strdup(fname);
121   }
122   return NULL;
123 }
124
125 int mplib_find_file_function (lua_State *L) {
126   if (lua_gettop(L)!=1 || (!(lua_isfunction(L,1) || lua_isnil(L,1) ))) {
127     lua_pop(L,1);
128     lua_pushnil(L);
129     lua_pushstring(L,"Invalid arguments to mp.find_file_function");
130     return 2;
131   }
132   if (lua_isfunction(L,1)) {
133     LL =  L;
134   } else {
135     LL = NULL;
136   }
137   lua_pushstring(L, "mplib_file_finder");
138   lua_pushvalue(L,-2);
139   lua_rawset(L,LUA_REGISTRYINDEX);
140   lua_pop(L,1);
141   return 0;
142 }
143
144 void *term_file_ptr = NULL;
145 void *err_file_ptr = NULL;
146 void *log_file_ptr = NULL;
147 void *ps_file_ptr = NULL;
148
149 void *mplib_open_file(char *fname, char *fmode, int ftype)  {
150   File *ff = malloc(sizeof (File));
151   if (ff) {
152     ff->f = NULL;
153     if (ftype==mp_filetype_terminal) {
154       if (fmode[0] == 'r') {
155         ff->f = stdin;
156       } else {
157         xfree(term_file_ptr); 
158         ff->f = malloc(1);
159         term_file_ptr = ff->f;
160       }
161     } else if (ftype==mp_filetype_error) {
162       xfree(err_file_ptr); 
163       ff->f = malloc(1);
164       err_file_ptr = ff->f;
165     } else if (ftype == mp_filetype_log) {
166       xfree(log_file_ptr); 
167       ff->f = malloc(1);
168       log_file_ptr = ff->f;
169     } else if (ftype == mp_filetype_postscript) {
170       xfree(ps_file_ptr); 
171       ff->f = malloc(1);
172       ps_file_ptr = ff->f;
173     } else { 
174       char *f = fname;
175       if (fmode[0] == 'r') {
176         f = mplib_find_file(fname,fmode,ftype);
177         if (f==NULL)
178           return NULL;
179       }
180       ff->f = fopen(f, fmode);
181       if ((fmode[0] == 'r') && (ff->f == NULL)) {
182         free(ff);
183         return NULL;  
184       }
185     }
186     return ff;
187   }
188   return NULL;
189 }
190
191 static char * input_data = NULL;
192 static char * input_data_ptr = NULL;
193 static size_t input_data_len = 0;
194
195 #define GET_CHAR() do {                                                 \
196     if (f==stdin && input_data != NULL) {                               \
197       if (input_data_len==0) {                                          \
198         if (input_data_ptr!=NULL)                                       \
199           input_data_ptr = NULL;                                        \
200         else                                                            \
201           input_data = NULL;                                            \
202         c = EOF;                                                        \
203       } else {                                                          \
204         input_data_len--;                                               \
205         c = *input_data_ptr++;                                          \
206       }                                                                 \
207     } else {                                                            \
208       c = fgetc(f);                                                     \
209     }                                                                   \
210   } while (0)
211
212 #define UNGET_CHAR() do {                                               \
213     if (f==stdin && input_data != NULL) {                               \
214       input_data_len++; input_data_ptr--;                               \
215     } else {                                                            \
216       ungetc(c,f);                                                      \
217     }                                                                   \
218   } while (0)
219
220
221 char *mplib_read_ascii_file (void *ff, size_t *size) {
222   int c;
223   size_t len = 0, lim = 128;
224   char *s = NULL;
225   if (ff!=NULL) {
226     FILE *f = ((File *)ff)->f;
227     if (f==NULL)
228       return NULL;
229     *size = 0;
230     GET_CHAR();
231     if (c==EOF)
232       return NULL;
233     s = malloc(lim); 
234     if (s==NULL) return NULL;
235     while (c!=EOF && c!='\n' && c!='\r') { 
236       if (len==lim) {
237         s =realloc(s, (lim+(lim>>2)));
238         if (s==NULL) return NULL;
239         lim+=(lim>>2);
240       }
241       s[len++] = c;
242       GET_CHAR();
243     }
244     if (c=='\r') {
245       GET_CHAR();
246       if (c!=EOF && c!='\n')
247         UNGET_CHAR();
248     }
249     s[len] = 0;
250     *size = len;
251   }
252   return s;
253 }
254
255 static char *term_out = NULL;
256 static char *error_out = NULL;
257 static char *log_out = NULL;
258 static char *ps_out = NULL;
259
260 #define APPEND_STRING(a,b) do {                 \
261     if (a==NULL) {                              \
262       a = strdup(b);                            \
263     } else {                                    \
264       a = realloc(a, strlen(a)+strlen(b)+1);    \
265       strcpy(a+strlen(a),b);                    \
266     }                                           \
267   } while (0)
268
269 void mplib_write_ascii_file (void *ff, char *s) {
270   if (ff!=NULL) {
271     void *f = ((File *)ff)->f;
272     if (f!=NULL) {
273       if (f==term_file_ptr) {
274         APPEND_STRING(term_out,s);
275       } else if (f==err_file_ptr) {
276         APPEND_STRING(error_out,s);
277       } else if (f==log_file_ptr) {
278         APPEND_STRING(log_out,s);
279       } else if (f==ps_file_ptr) {
280         APPEND_STRING(ps_out,s);
281       } else {
282         fprintf((FILE *)f,s);
283       }
284     }
285   }
286 }
287
288 void mplib_read_binary_file (void *ff, void **data, size_t *size) {
289   size_t len = 0;
290   if (ff!=NULL) {
291     FILE *f = ((File *)ff)->f;
292     if (f!=NULL) 
293       len = fread(*data,1,*size,f);
294     *size = len;
295   }
296 }
297
298 void mplib_write_binary_file (void *ff, void *s, size_t size) {
299   if (ff!=NULL) {
300     FILE *f = ((File *)ff)->f;
301     if (f!=NULL)
302       fwrite(s,size,1,f);
303   }
304 }
305
306
307 void mplib_close_file (void *ff) {
308   if (ff!=NULL) {
309     void *f = ((File *)ff)->f;
310     if (f != NULL && f != term_file_ptr && f != err_file_ptr
311         && f != log_file_ptr && f != ps_file_ptr) {
312       fclose(f);
313     }
314     free(ff);
315   }
316 }
317
318 int mplib_eof_file (void *ff) {
319   if (ff!=NULL) {
320     FILE *f = ((File *)ff)->f;
321     if (f==NULL)
322       return 1;
323     if (f==stdin && input_data != NULL) {       
324       return (input_data_len==0);
325     }
326     return feof(f);
327   }
328   return 1;
329 }
330
331 void mplib_flush_file (void *ff) {
332   return ;
333 }
334
335 static struct mp_edge_object *edges = NULL;
336
337 #define APPEND_TO_EDGES(a) do {                 \
338     if (edges==NULL) {                          \
339       edges = hh;                               \
340     } else {                                    \
341       struct mp_edge_object *p = edges;         \
342       while (p->_next!=NULL) { p = p->_next; }  \
343       p->_next = hh;                            \
344     }                                           \
345 } while (0)
346
347 void mplib_shipout_backend (MP mp, int h) {
348   struct mp_edge_object *hh; 
349   hh = mp_gr_export(mp, h);
350   if (hh) {
351     APPEND_TO_EDGES(hh); 
352   }
353 }
354
355
356 static void 
357 mplib_setup_file_ops(struct MP_options * options) {
358   options->find_file         = mplib_find_file;
359   options->open_file         = mplib_open_file;
360   options->close_file        = mplib_close_file;
361   options->eof_file          = mplib_eof_file;
362   options->flush_file        = mplib_flush_file;
363   options->write_ascii_file  = mplib_write_ascii_file;
364   options->read_ascii_file   = mplib_read_ascii_file;
365   options->write_binary_file = mplib_write_binary_file;
366   options->read_binary_file  = mplib_read_binary_file;
367   options->shipout_backend   = mplib_shipout_backend;
368 }
369
370 static int 
371 mplib_new (lua_State *L) {
372   MP *mp_ptr;
373   int h,i;
374   struct MP_options * options; /* instance options */
375   mp_ptr = lua_newuserdata(L, sizeof(MP *));
376   if (mp_ptr) {
377     options = mp_options();
378     mplib_setup_file_ops(options);
379     options->noninteractive = 1; /* required ! */
380     options->print_found_names = 0;
381     if (lua_type(L,1)==LUA_TTABLE) {
382       for (i=1;img_parms[i].name!=NULL;i++) {
383         lua_getfield(L,1,img_parms[i].name);
384         if (lua_isnil(L,-1)) {
385           lua_pop(L,1);
386           continue; /* skip unset */
387         }
388         switch(img_parms[i].idx) {
389         case P_ERROR_LINE: 
390           options->error_line = lua_tointeger(L,-1);
391           break;
392         case P_HALF_LINE:   
393           options->half_error_line = lua_tointeger(L,-1);
394           break;
395         case P_MAX_LINE:
396           options->max_print_line = lua_tointeger(L,-1);
397           break;
398         case P_MAIN_MEMORY:
399           options->main_memory = lua_tointeger(L,-1);
400           break;
401         case P_HASH_SIZE:
402           options->hash_size = lua_tointeger(L,-1);
403           break;
404         case P_HASH_PRIME:
405           options->hash_prime = lua_tointeger(L,-1);
406           break;
407         case P_PARAM_SIZE:
408           options->param_size = lua_tointeger(L,-1);
409           break;
410         case P_IN_OPEN:
411           options->max_in_open = lua_tointeger(L,-1);
412           break;
413         case P_RANDOM_SEED:
414           options->random_seed = lua_tointeger(L,-1);
415           break;
416         case P_INTERACTION:
417           options->interaction = luaL_checkoption(L,-1,"errorstopmode", interaction_options);
418           break;
419         case P_INI_VERSION:
420           options->ini_version = lua_toboolean(L,-1);
421           break;
422         case P_TROFF_MODE:
423           options->troff_mode = lua_toboolean(L,-1);
424           break;
425         case P_PRINT_NAMES:
426           options->print_found_names = lua_toboolean(L,-1);
427           break;
428         case P_COMMAND_LINE:
429           options->command_line = strdup((char *)lua_tostring(L,-1));
430           break;
431         case P_MEM_NAME:
432           options->mem_name = strdup((char *)lua_tostring(L,-1));
433           break;
434         case P_JOB_NAME:
435           options->job_name = strdup((char *)lua_tostring(L,-1));
436           break;
437 #if 0
438         case P_FIND_FILE:  
439         case P_OPEN_FILE:
440         case P_CLOSE_FILE:
441         case P_EOF_FILE:
442         case P_FLUSH_FILE:
443         case P_WRITE_ASCII:
444         case P_READ_ASCII:
445         case P_WRITE_BINARY:
446         case P_READ_BINARY:
447           break;
448         case P_SHIPOUT:
449           break;
450         case P_RUN_EDITOR:
451           break;
452         case P_RUN_MAKEMPX:
453           break;
454 #endif
455         default:
456           break;
457         }
458         lua_pop(L,1);
459       }
460     }
461     *mp_ptr = mp_new(options);
462     xfree(options->command_line);
463     xfree(options->mem_name);
464     xfree(options->job_name);
465     free(options);
466     if (*mp_ptr) {
467       h = mp_initialize(*mp_ptr);
468       if (!h) {
469         luaL_getmetatable(L,MPLIB_METATABLE);
470         lua_setmetatable(L,-2);
471         return 1;
472       }
473     }
474   }
475   lua_pushnil(L);
476   return 1;
477 }
478
479 static int
480 mplib_collect (lua_State *L) {
481   MP *mp_ptr = is_mp(L,1);
482   if (*mp_ptr!=NULL) {
483     mp_free(*mp_ptr);
484     *mp_ptr=NULL;
485   }
486   return 0;
487 }
488
489 static int
490 mplib_tostring (lua_State *L) {
491   MP *mp_ptr = is_mp(L,1);
492   if (*mp_ptr!=NULL) {
493     lua_pushfstring(L,"<MP %p>",*mp_ptr);
494         return 1;
495   }
496   return 0;
497 }
498
499 static int
500 mplib_run (lua_State *L) {
501   MP *mp_ptr = is_mp(L,1);
502   if (*mp_ptr!=NULL) {
503         int h = mp_run(*mp_ptr);
504         lua_pushnumber(L,h);
505   } else {
506         lua_pushnil(L);
507   }
508   return 1;
509 }
510
511 static int 
512 mplib_wrapresults(lua_State *L,int h) {
513    lua_newtable(L);
514    if (term_out != NULL) {
515      lua_pushstring(L,term_out);
516      lua_setfield(L,-2,"term");
517      free(term_out); term_out = NULL;
518    }
519    if (error_out != NULL) {
520      lua_pushstring(L,error_out);
521      lua_setfield(L,-2,"error");
522      free(error_out); error_out = NULL;
523    } 
524    if (log_out != NULL ) {
525      lua_pushstring(L,log_out);
526      lua_setfield(L,-2,"log");
527      free(log_out); log_out = NULL;
528    }
529    if (edges != NULL ) {
530      /* todo: convert to array */
531      struct mp_edge_object **v = lua_newuserdata (L, sizeof(struct mp_edge_object *));
532      *v = malloc(sizeof(struct mp_edge_object));
533      memcpy(*v, edges, sizeof(struct mp_edge_object));
534      luaL_getmetatable(L,MPLIB_FIG_METATABLE);
535      lua_setmetatable(L,-2);
536      lua_setfield(L,-2,"fig");
537      free(edges); edges = NULL;
538    }
539    lua_pushnumber(L,h);
540    lua_setfield(L,-2,"status");
541    return 1;
542 }
543
544 static int
545 mplib_execute (lua_State *L) {
546   MP *mp_ptr = is_mp(L,1);
547   if (*mp_ptr!=NULL && lua_isstring(L,2)) {
548     if (input_data_len>0) {  /* this should NOT happen */
549       fprintf(stderr,"Can't do concurrency yet!\n");
550     } else {
551       input_data = (char *)lua_tolstring(L,2, &input_data_len);
552       input_data_ptr = input_data;
553       int h = mp_execute(*mp_ptr);
554       return mplib_wrapresults(L, h);
555     } 
556
557   } else {
558     lua_pushnil(L);
559   }
560   return 1;
561 }
562
563 static int
564 mplib_finish (lua_State *L) {
565   MP *mp_ptr = is_mp(L,1);
566   if (*mp_ptr!=NULL) {
567     int h = mp_finish(*mp_ptr);
568     return mplib_wrapresults(L, h);
569   } else {
570     lua_pushnil(L);
571   }
572   return 1;
573 }
574
575
576 static int
577 mplib_fig_collect (lua_State *L) {
578   struct mp_edge_object **hh = is_fig(L,1);
579   if (*hh!=NULL) {
580     mp_gr_toss_objects((*hh)->_parent,*hh);
581     *hh=NULL;
582   }
583   return 0;
584 }
585
586
587 static int
588 mplib_fig_tostring (lua_State *L) {
589   struct mp_edge_object **hh = is_fig(L,1);
590   lua_pushfstring(L,"<figure %p>",*hh);
591   return 1;
592 }
593
594 static int 
595 mp_wrapped_shipout (struct mp_edge_object *hh, int prologues, int procset) {
596   MP mp = hh->_parent;
597   if (setjmp(mp->jump_buf)) {
598     return 0;
599   }
600   mp_gr_ship_out(hh,prologues,procset);
601   return 1;
602 }
603
604 static int
605 mplib_fig_postscript (lua_State *L) {
606   struct mp_edge_object **hh = is_fig(L,1);
607   int prologues = luaL_optnumber(L,2,-1);
608   int procset = luaL_optnumber(L,3,-1);
609   if (ps_out == NULL) {
610     if (mp_wrapped_shipout(*hh,prologues, procset)) {
611       if (ps_out!=NULL ) {
612         lua_pushstring(L, ps_out);
613         free(ps_out); ps_out = NULL;
614       } else {
615         lua_pushnil(L);
616       }
617       return 1;
618     } else {
619       lua_pushnil(L);
620       lua_pushstring(L,log_out);
621       free(ps_out); ps_out = NULL;
622       return 2;
623     }
624   }
625   lua_pushnil(L);
626   return 1;
627 }
628
629 static int
630 mplib_fig_bb (lua_State *L) {
631   struct mp_edge_object **hh = is_fig(L,1);
632   lua_newtable(L);
633   lua_pushnumber(L, (double)(*hh)->_minx/65536.0);
634   lua_rawseti(L,-2,1);
635   lua_pushnumber(L, (double)(*hh)->_miny/65536.0);
636   lua_rawseti(L,-2,2);
637   lua_pushnumber(L, (double)(*hh)->_maxx/65536.0);
638   lua_rawseti(L,-2,3);
639   lua_pushnumber(L, (double)(*hh)->_maxy/65536.0);
640   lua_rawseti(L,-2,4);
641   return 1;
642 }
643
644
645
646 static const struct luaL_reg mplib_meta[] = {
647   {"__gc",               mplib_collect}, 
648   {"__tostring",         mplib_tostring},
649   {NULL, NULL}                /* sentinel */
650 };
651
652 static const struct luaL_reg mplib_fig_meta[] = {
653   {"__gc",               mplib_fig_collect},
654   {"__tostring",         mplib_fig_tostring},
655   {"postscript",         mplib_fig_postscript},
656   {"boundingbox",        mplib_fig_bb},
657   {NULL, NULL}                /* sentinel */
658 };
659
660 static const struct luaL_reg mplib_d [] = {
661   {"run",                mplib_run },
662   {"execute",            mplib_execute },
663   {"finish",             mplib_finish },
664   {NULL, NULL}  /* sentinel */
665 };
666
667
668 static const struct luaL_reg mplib_m[] = {
669   {"new",               mplib_new},
670   {"find_file_function", mplib_find_file_function },
671   {NULL, NULL}                /* sentinel */
672 };
673
674
675 int 
676 luaopen_mp (lua_State *L) {
677   luaL_newmetatable(L,MPLIB_FIG_METATABLE);
678   lua_pushvalue(L, -1); /* push metatable */
679   lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
680   luaL_register(L, NULL, mplib_fig_meta);  /* figure meta methods */
681   lua_pop(L,1);
682
683   luaL_newmetatable(L,MPLIB_METATABLE);
684   lua_pushvalue(L, -1); /* push metatable */
685   lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
686   luaL_register(L, NULL, mplib_meta);  /* meta methods */
687   luaL_register(L, NULL, mplib_d);  /* dict methods */
688   luaL_register(L, "mp", mplib_m); /* module functions */
689   return 1;
690 }
691