use the options hash to set file_finder instead of a separate function
[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   {"find_file",         P_FIND_FILE,   'p' }, 
61 #if 0
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 static int 
126 mplib_find_file_function (lua_State *L) {
127   if (lua_isfunction(L,-1)) {
128     LL =  L;
129   } else if (lua_isnil(L,-1)) {
130     LL = NULL;
131   } else {
132     return 1; /* error */
133   }
134   lua_pushstring(L, "mplib_file_finder");
135   lua_pushvalue(L,-2);
136   lua_rawset(L,LUA_REGISTRYINDEX);
137   return 0;
138 }
139
140 void *term_file_ptr = NULL;
141 void *err_file_ptr = NULL;
142 void *log_file_ptr = NULL;
143 void *ps_file_ptr = NULL;
144
145 void *mplib_open_file(char *fname, char *fmode, int ftype)  {
146   File *ff = malloc(sizeof (File));
147   if (ff) {
148     ff->f = NULL;
149     if (ftype==mp_filetype_terminal) {
150       if (fmode[0] == 'r') {
151         ff->f = stdin;
152       } else {
153         xfree(term_file_ptr); 
154         ff->f = malloc(1);
155         term_file_ptr = ff->f;
156       }
157     } else if (ftype==mp_filetype_error) {
158       xfree(err_file_ptr); 
159       ff->f = malloc(1);
160       err_file_ptr = ff->f;
161     } else if (ftype == mp_filetype_log) {
162       xfree(log_file_ptr); 
163       ff->f = malloc(1);
164       log_file_ptr = ff->f;
165     } else if (ftype == mp_filetype_postscript) {
166       xfree(ps_file_ptr); 
167       ff->f = malloc(1);
168       ps_file_ptr = ff->f;
169     } else { 
170       char *f = fname;
171       if (fmode[0] == 'r') {
172         f = mplib_find_file(fname,fmode,ftype);
173         if (f==NULL)
174           return NULL;
175       }
176       ff->f = fopen(f, fmode);
177       if ((fmode[0] == 'r') && (ff->f == NULL)) {
178         free(ff);
179         return NULL;  
180       }
181     }
182     return ff;
183   }
184   return NULL;
185 }
186
187 static char * input_data = NULL;
188 static char * input_data_ptr = NULL;
189 static size_t input_data_len = 0;
190
191 #define GET_CHAR() do {                                                 \
192     if (f==stdin && input_data != NULL) {                               \
193       if (input_data_len==0) {                                          \
194         if (input_data_ptr!=NULL)                                       \
195           input_data_ptr = NULL;                                        \
196         else                                                            \
197           input_data = NULL;                                            \
198         c = EOF;                                                        \
199       } else {                                                          \
200         input_data_len--;                                               \
201         c = *input_data_ptr++;                                          \
202       }                                                                 \
203     } else {                                                            \
204       c = fgetc(f);                                                     \
205     }                                                                   \
206   } while (0)
207
208 #define UNGET_CHAR() do {                                               \
209     if (f==stdin && input_data != NULL) {                               \
210       input_data_len++; input_data_ptr--;                               \
211     } else {                                                            \
212       ungetc(c,f);                                                      \
213     }                                                                   \
214   } while (0)
215
216
217 char *mplib_read_ascii_file (void *ff, size_t *size) {
218   int c;
219   size_t len = 0, lim = 128;
220   char *s = NULL;
221   if (ff!=NULL) {
222     FILE *f = ((File *)ff)->f;
223     if (f==NULL)
224       return NULL;
225     *size = 0;
226     GET_CHAR();
227     if (c==EOF)
228       return NULL;
229     s = malloc(lim); 
230     if (s==NULL) return NULL;
231     while (c!=EOF && c!='\n' && c!='\r') { 
232       if (len==lim) {
233         s =realloc(s, (lim+(lim>>2)));
234         if (s==NULL) return NULL;
235         lim+=(lim>>2);
236       }
237       s[len++] = c;
238       GET_CHAR();
239     }
240     if (c=='\r') {
241       GET_CHAR();
242       if (c!=EOF && c!='\n')
243         UNGET_CHAR();
244     }
245     s[len] = 0;
246     *size = len;
247   }
248   return s;
249 }
250
251 static char *term_out = NULL;
252 static char *error_out = NULL;
253 static char *log_out = NULL;
254 static char *ps_out = NULL;
255
256 #define APPEND_STRING(a,b) do {                 \
257     if (a==NULL) {                              \
258       a = strdup(b);                            \
259     } else {                                    \
260       a = realloc(a, strlen(a)+strlen(b)+1);    \
261       strcpy(a+strlen(a),b);                    \
262     }                                           \
263   } while (0)
264
265 void mplib_write_ascii_file (void *ff, char *s) {
266   if (ff!=NULL) {
267     void *f = ((File *)ff)->f;
268     if (f!=NULL) {
269       if (f==term_file_ptr) {
270         APPEND_STRING(term_out,s);
271       } else if (f==err_file_ptr) {
272         APPEND_STRING(error_out,s);
273       } else if (f==log_file_ptr) {
274         APPEND_STRING(log_out,s);
275       } else if (f==ps_file_ptr) {
276         APPEND_STRING(ps_out,s);
277       } else {
278         fprintf((FILE *)f,s);
279       }
280     }
281   }
282 }
283
284 void mplib_read_binary_file (void *ff, void **data, size_t *size) {
285   size_t len = 0;
286   if (ff!=NULL) {
287     FILE *f = ((File *)ff)->f;
288     if (f!=NULL) 
289       len = fread(*data,1,*size,f);
290     *size = len;
291   }
292 }
293
294 void mplib_write_binary_file (void *ff, void *s, size_t size) {
295   if (ff!=NULL) {
296     FILE *f = ((File *)ff)->f;
297     if (f!=NULL)
298       fwrite(s,size,1,f);
299   }
300 }
301
302
303 void mplib_close_file (void *ff) {
304   if (ff!=NULL) {
305     void *f = ((File *)ff)->f;
306     if (f != NULL && f != term_file_ptr && f != err_file_ptr
307         && f != log_file_ptr && f != ps_file_ptr) {
308       fclose(f);
309     }
310     free(ff);
311   }
312 }
313
314 int mplib_eof_file (void *ff) {
315   if (ff!=NULL) {
316     FILE *f = ((File *)ff)->f;
317     if (f==NULL)
318       return 1;
319     if (f==stdin && input_data != NULL) {       
320       return (input_data_len==0);
321     }
322     return feof(f);
323   }
324   return 1;
325 }
326
327 void mplib_flush_file (void *ff) {
328   return ;
329 }
330
331 static struct mp_edge_object *edges = NULL;
332
333 #define APPEND_TO_EDGES(a) do {                 \
334     if (edges==NULL) {                          \
335       edges = hh;                               \
336     } else {                                    \
337       struct mp_edge_object *p = edges;         \
338       while (p->_next!=NULL) { p = p->_next; }  \
339       p->_next = hh;                            \
340     }                                           \
341 } while (0)
342
343 void mplib_shipout_backend (MP mp, int h) {
344   struct mp_edge_object *hh; 
345   hh = mp_gr_export(mp, h);
346   if (hh) {
347     APPEND_TO_EDGES(hh); 
348   }
349 }
350
351
352 static void 
353 mplib_setup_file_ops(struct MP_options * options) {
354   options->find_file         = mplib_find_file;
355   options->open_file         = mplib_open_file;
356   options->close_file        = mplib_close_file;
357   options->eof_file          = mplib_eof_file;
358   options->flush_file        = mplib_flush_file;
359   options->write_ascii_file  = mplib_write_ascii_file;
360   options->read_ascii_file   = mplib_read_ascii_file;
361   options->write_binary_file = mplib_write_binary_file;
362   options->read_binary_file  = mplib_read_binary_file;
363   options->shipout_backend   = mplib_shipout_backend;
364 }
365
366 static int 
367 mplib_new (lua_State *L) {
368   MP *mp_ptr;
369   int h,i;
370   struct MP_options * options; /* instance options */
371   mp_ptr = lua_newuserdata(L, sizeof(MP *));
372   if (mp_ptr) {
373     options = mp_options();
374     mplib_setup_file_ops(options);
375     options->noninteractive = 1; /* required ! */
376     options->print_found_names = 0;
377     if (lua_type(L,1)==LUA_TTABLE) {
378       for (i=1;img_parms[i].name!=NULL;i++) {
379         lua_getfield(L,1,img_parms[i].name);
380         if (lua_isnil(L,-1)) {
381           lua_pop(L,1);
382           continue; /* skip unset */
383         }
384         switch(img_parms[i].idx) {
385         case P_ERROR_LINE: 
386           options->error_line = lua_tointeger(L,-1);
387           break;
388         case P_HALF_LINE:   
389           options->half_error_line = lua_tointeger(L,-1);
390           break;
391         case P_MAX_LINE:
392           options->max_print_line = lua_tointeger(L,-1);
393           break;
394         case P_MAIN_MEMORY:
395           options->main_memory = lua_tointeger(L,-1);
396           break;
397         case P_HASH_SIZE:
398           options->hash_size = lua_tointeger(L,-1);
399           break;
400         case P_HASH_PRIME:
401           options->hash_prime = lua_tointeger(L,-1);
402           break;
403         case P_PARAM_SIZE:
404           options->param_size = lua_tointeger(L,-1);
405           break;
406         case P_IN_OPEN:
407           options->max_in_open = lua_tointeger(L,-1);
408           break;
409         case P_RANDOM_SEED:
410           options->random_seed = lua_tointeger(L,-1);
411           break;
412         case P_INTERACTION:
413           options->interaction = luaL_checkoption(L,-1,"errorstopmode", interaction_options);
414           break;
415         case P_INI_VERSION:
416           options->ini_version = lua_toboolean(L,-1);
417           break;
418         case P_TROFF_MODE:
419           options->troff_mode = lua_toboolean(L,-1);
420           break;
421         case P_PRINT_NAMES:
422           options->print_found_names = lua_toboolean(L,-1);
423           break;
424         case P_COMMAND_LINE:
425           options->command_line = strdup((char *)lua_tostring(L,-1));
426           break;
427         case P_MEM_NAME:
428           options->mem_name = strdup((char *)lua_tostring(L,-1));
429           break;
430         case P_JOB_NAME:
431           options->job_name = strdup((char *)lua_tostring(L,-1));
432           break;
433         case P_FIND_FILE:  
434           if(mplib_find_file_function(L)) { /* error here */
435             fprintf(stdout,"Invalid arguments to mp.new({find_file=...})\n");
436           }
437           break;
438 #if 0
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_checkstack(L,5);
514    lua_newtable(L);
515    if (term_out != NULL) {
516      lua_pushstring(L,term_out);
517      lua_setfield(L,-2,"term");
518      free(term_out); term_out = NULL;
519    }
520    if (error_out != NULL) {
521      lua_pushstring(L,error_out);
522      lua_setfield(L,-2,"error");
523      free(error_out); error_out = NULL;
524    } 
525    if (log_out != NULL ) {
526      lua_pushstring(L,log_out);
527      lua_setfield(L,-2,"log");
528      free(log_out); log_out = NULL;
529    }
530    if (edges != NULL ) {
531      struct mp_edge_object **v;
532      struct mp_edge_object *p = edges;
533      int i = 1;
534      lua_newtable(L);
535      while (p!=NULL) { 
536        v = lua_newuserdata (L, sizeof(struct mp_edge_object *));
537        *v = p;
538        luaL_getmetatable(L,MPLIB_FIG_METATABLE);
539        lua_setmetatable(L,-2);
540        lua_rawseti(L,-2,i); i++;
541        p = p->_next;
542      }
543      lua_setfield(L,-2,"fig");
544      edges = NULL;
545    }
546    lua_pushnumber(L,h);
547    lua_setfield(L,-2,"status");
548    return 1;
549 }
550
551 static int
552 mplib_execute (lua_State *L) {
553   MP *mp_ptr = is_mp(L,1);
554   if (*mp_ptr!=NULL && lua_isstring(L,2)) {
555     if (input_data_len>0) {  /* this should NOT happen */
556       fprintf(stderr,"Can't do concurrency yet!\n");
557     } else {
558       input_data = (char *)lua_tolstring(L,2, &input_data_len);
559       input_data_ptr = input_data;
560       int h = mp_execute(*mp_ptr);
561       return mplib_wrapresults(L, h);
562     } 
563
564   } else {
565     lua_pushnil(L);
566   }
567   return 1;
568 }
569
570 static int
571 mplib_finish (lua_State *L) {
572   MP *mp_ptr = is_mp(L,1);
573   if (*mp_ptr!=NULL) {
574     int h = mp_finish(*mp_ptr);
575     return mplib_wrapresults(L, h);
576   } else {
577     lua_pushnil(L);
578   }
579   return 1;
580 }
581
582
583 static int
584 mplib_fig_collect (lua_State *L) {
585   struct mp_edge_object **hh = is_fig(L,1);
586   if (*hh!=NULL) {
587     mp_gr_toss_objects((*hh)->_parent,*hh);
588     *hh=NULL;
589   }
590   return 0;
591 }
592
593
594 static int
595 mplib_fig_tostring (lua_State *L) {
596   struct mp_edge_object **hh = is_fig(L,1);
597   lua_pushfstring(L,"<figure %p>",*hh);
598   return 1;
599 }
600
601 static int 
602 mp_wrapped_shipout (struct mp_edge_object *hh, int prologues, int procset) {
603   MP mp = hh->_parent;
604   if (setjmp(mp->jump_buf)) {
605     return 0;
606   }
607   mp_gr_ship_out(hh,prologues,procset);
608   return 1;
609 }
610
611 static int
612 mplib_fig_postscript (lua_State *L) {
613   struct mp_edge_object **hh = is_fig(L,1);
614   int prologues = luaL_optnumber(L,2,-1);
615   int procset = luaL_optnumber(L,3,-1);
616   if (ps_out == NULL) {
617     if (mp_wrapped_shipout(*hh,prologues, procset)) {
618       if (ps_out!=NULL ) {
619         lua_pushstring(L, ps_out);
620         free(ps_out); ps_out = NULL;
621       } else {
622         lua_pushnil(L);
623       }
624       return 1;
625     } else {
626       lua_pushnil(L);
627       lua_pushstring(L,log_out);
628       free(ps_out); ps_out = NULL;
629       return 2;
630     }
631   }
632   lua_pushnil(L);
633   return 1;
634 }
635
636 static int
637 mplib_fig_bb (lua_State *L) {
638   struct mp_edge_object **hh = is_fig(L,1);
639   lua_newtable(L);
640   lua_pushnumber(L, (double)(*hh)->_minx/65536.0);
641   lua_rawseti(L,-2,1);
642   lua_pushnumber(L, (double)(*hh)->_miny/65536.0);
643   lua_rawseti(L,-2,2);
644   lua_pushnumber(L, (double)(*hh)->_maxx/65536.0);
645   lua_rawseti(L,-2,3);
646   lua_pushnumber(L, (double)(*hh)->_maxy/65536.0);
647   lua_rawseti(L,-2,4);
648   return 1;
649 }
650
651
652
653 static const struct luaL_reg mplib_meta[] = {
654   {"__gc",               mplib_collect}, 
655   {"__tostring",         mplib_tostring},
656   {NULL, NULL}                /* sentinel */
657 };
658
659 static const struct luaL_reg mplib_fig_meta[] = {
660   {"__gc",               mplib_fig_collect},
661   {"__tostring",         mplib_fig_tostring},
662   {"postscript",         mplib_fig_postscript},
663   {"boundingbox",        mplib_fig_bb},
664   {NULL, NULL}                /* sentinel */
665 };
666
667 static const struct luaL_reg mplib_d [] = {
668   {"run",                mplib_run },
669   {"execute",            mplib_execute },
670   {"finish",             mplib_finish },
671   {NULL, NULL}  /* sentinel */
672 };
673
674
675 static const struct luaL_reg mplib_m[] = {
676   {"new",               mplib_new},
677   {NULL, NULL}                /* sentinel */
678 };
679
680
681 int 
682 luaopen_mp (lua_State *L) {
683   luaL_newmetatable(L,MPLIB_FIG_METATABLE);
684   lua_pushvalue(L, -1); /* push metatable */
685   lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
686   luaL_register(L, NULL, mplib_fig_meta);  /* figure meta methods */
687   lua_pop(L,1);
688
689   luaL_newmetatable(L,MPLIB_METATABLE);
690   lua_pushvalue(L, -1); /* push metatable */
691   lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
692   luaL_register(L, NULL, mplib_meta);  /* meta methods */
693   luaL_register(L, NULL, mplib_d);  /* dict methods */
694   luaL_register(L, "mp", mplib_m); /* module functions */
695   return 1;
696 }
697