1 /*
2  * $Id: codger.c,v 1.3 2007-03-29 23:56:19 dhmunro Exp $
3  *
4  * codger
5  * automatic CODe GEneratoR for adding compiled functions to yorick
6  *
7  * compiled extensions to yorick come in two forms:
8  * 1. archive libraries of object code, linked into yorick when
9  *    yorick itself is loaded
10  * 2. dynamic libraries of object code, linked into yorick at runtime
11  *    by means of the interpreted plug_in function (which invokes the
12  *    system dynamic loader)
13  *
14  * the compiled code for a yorick extension is the same in either case
15  * (although the object modules must be built with different compiler
16  * flags in order to become part of a dynamic library on some platforms)
17  *
18  * Every yorick extension library (whether a static archive or a dynamic
19  * library) must contain a ywrap.o module, which must define a single
20  * function (with y_pkg_t prototype), called yk_pkgname where "pkgname"
21  * is the name of the package.  Yorick calls this function to return
22  * the names and addresses of the built-in functions and data defined by
23  * the package.  The static archive would be libpkgname.a, and the
24  * dynamic library would be pkgname.so or pkgname.sl or pkgname.dll
25  * depending on the platform.
26  *
27  * yk_pkgname returns pointers to the following objects:
28  * 1. the list of .i file names which must be included in order to
29  *    load the package, typically installed in the Y_HOME/i0/ directory
30  *    - yorick includes these at startup in the static load case,
31  *      and at plug_in in the dynamic load case
32  * 2. the list of pointers to the builtin functions defined by pkgname
33  *      (declared by extern statements in the .i files)
34  * 3. the list of pointers to any compiled data defined by pkgname
35  *      (declared by extern statments followed by reshape statements
36  *       or EXTERN comments in the .i files)
37  * 4. the list of names by which the interpreter can reference (2) and (3)
38  *
39  * ywrap.o optionally contains automatically generated wrappers for
40  * the builtin list (2), which reference C functions defined in other
41  * modules of the package (in the case that the other modules do not
42  * directly provide builtin functions that communicate with the yorick
43  * interpreter for their arguments and results)
44  * - these wrappers are specified by PROTOTYPE comments in the .i files
45  *
46  * codger generates the ywrap.c source code, given the .i files for
47  * the package
48  *
49  * Additionally, codger generates the yinit.c source code, containing the
50  * on_launch callback that passes the function addresses of yk_yor and
51  * any statically loaded yk_pkgname functions to yorick at startup,
52  * plus the Y_HOME and Y_SITE directory names.
53  */
54 /* Copyright (c) 2005, The Regents of the University of California.
55  * All rights reserved.
56  * This file is part of yorick (http://yorick.sourceforge.net).
57  * Read the accompanying LICENSE file for details.
58  */
59 
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 
64 static int usage(void);
65 static int geninit(int argc, char *argv[]);
66 static int genwrap(int argc, char *argv[]);
67 static FILE *out_open(char *name);
68 static FILE *i_open(char *name, char **dirs, int ndirs);
69 static int i_process(FILE *out, FILE *in, int flink, char *inname);
70 static char *deescape(char *path);
71 
72 #define E_(msg) fputs(msg "\n", stderr)
73 #define O_(msg) fputs(msg "\n", out)
74 
75 static int
usage(void)76 usage(void)
77 {
78   E_("codger usage:");
79   E_("  codger w pkgname pkga.i [pkgb.i ...]");
80   E_("    generate ywrap.c for pkgname from pkga.i, pkgb.i, ...");
81   E_("    options (after pkgname):");
82   E_("      -o alt.c   (before any .i) output to alt.c instead of ywrap.c");
83   E_("      -Idir      add dir to search path for .i files");
84   E_("      -Ldir -lname   record dependencies required to link package");
85   E_("      -f_ -f -F_ -F    select fortran linkage convention (default f_)");
86   E_("  codger i Y_HOME Y_SITE pkgname1 [pkgname2 ...]");
87   E_("    generate yinit.c for pkgname1, pkgname2, ...");
88   E_("    options (after Y_SITE):");
89   E_("      -o alt.c   output to alt.c instead of yinit.c");
90   return 1;
91 }
92 
93 int
main(int argc,char * argv[])94 main(int argc, char *argv[])
95 {
96   if (argc>=4 && !argv[1][1]) {
97     if (argv[1][0]=='i')
98       return geninit(argc-2, argv+2);
99     else if (argv[1][0]=='w')
100       return genwrap(argc-2, argv+2);
101   }
102   return usage();
103 }
104 
105 static int
geninit(int argc,char * argv[])106 geninit(int argc, char *argv[])
107 {
108   FILE *out;
109   char *outname = 0;
110   char *home = argv[0];
111   char *site = argv[1];
112   char **pkgs = malloc(sizeof(char *) * argc);
113   int npkgs = 0;
114   long i, n;
115 
116   for (argc-=2,argv+=2 ; argc ; argc--,argv++) {
117     if (argv[0][0] == '-') {
118       if (argv[0][1]=='o' && !argv[0][2]) {
119         argc--,argv++;
120         if (!argc || outname)
121           return usage();
122         outname = argv[0];
123       } else {
124         return usage();
125       }
126     } else {
127       n = strlen(argv[0]);
128       if (n > 100) {
129         E_("codger: pkgname has over 100 chars");
130         return 2;
131       }
132       for (i=0 ; i<n ; i++) {
133         if ((argv[0][i]>='a' && argv[0][i]<='z') ||
134             (argv[0][i]>='A' && argv[0][i]<='Z') ||
135             (argv[0][i]>='0' && argv[0][i]<='9') ||
136             argv[0][i]=='_') continue;
137         E_("codger: pkgname has illegal character, not [a-z][A-Z][0-9]_");
138         return 2;
139       }
140       if (strcmp(argv[0], "yor")) {  /* skip repeats and yor */
141         for (i=0 ; i<npkgs ; i++) if (!strcmp(argv[0], pkgs[i])) break;
142         if (i>=npkgs)
143           pkgs[npkgs++] = argv[0];
144       }
145     }
146   }
147 
148   out = fopen(outname? outname : "yinit.c", "w");
149   if (!out) {
150     fprintf(stderr, "codger: unable to create %s\n",
151             outname? outname : "yinit.c");
152     return 3;
153   }
154 
155   O_("/* codger-generated yorick initialization file */");
156   O_("#include \"play.h\"");
157   O_("#include \"ydata.h\"");
158   O_("");
159   fprintf(out, "static char *yhome = \"%s\";\n", deescape(home));
160   fprintf(out, "static char *ysite = \"%s\";\n", deescape(site));
161   O_("");
162   O_("extern y_pkg_t yk_yor;");
163   for (i=0 ; i<npkgs ; i++)
164     fprintf(out, "extern y_pkg_t yk_%s;\n", pkgs[i]);
165   O_("");
166   O_("static y_pkg_t *ypkgs[] = {");
167   O_("  &yk_yor,");
168   for (i=0 ; i<npkgs ; i++)
169     fprintf(out, "  &yk_%s,\n", pkgs[i]);
170   O_("  0");
171   O_("};");
172   O_("");
173   O_("int");
174   O_("on_launch(int argc, char *argv[])");
175   O_("{");
176   O_("  return y_launch(argc, argv, yhome, ysite, ypkgs);");
177   O_("}");
178 
179   fclose(out);
180   return 0;
181 }
182 
183 static char *
deescape(char * path)184 deescape(char *path)
185 {
186   /* try to cope with escaped blanks in Y_HOME, Y_SITE */
187   char *p, *q = path;
188   for (p=q ; q[0] ; p++,q++) {
189     if (q[0]=='\\' && q[1]==' ') p[0]=' ', q++;
190     else if (p!=q) p[0] = q[0];
191   }
192   if (p!=q) p[0] = '\0';
193   return path;
194 }
195 
196 #if defined(f_linkage)
197 # define F_LINK 1
198 #elif defined(F_LINKAGE_)
199 # define F_LINK 2
200 #elif defined(F_LINKAGE)
201 # define F_LINK 3
202 #else
203 # define F_LINK 0
204 #endif
205 
206 /* ------------------------------------------------------------------------ */
207 /* red-black tree is simplest balanced binary tree
208  * - use it here to store list of extern variable names
209  * - idea is that this is minimal code to support
210  *   efficient name collision detection
211  */
212 
213 typedef struct rbtree rbtree;
214 extern void rbinit(rbtree *node, rbtree *up);
215 extern rbtree *rblookup(rbtree *root, void *data, long ldata);
216 extern rbtree *rbinsert(rbtree **proot, void *data, long ldata);
217 /* rbremove not necessary here */
218 
219 /* --------- application-specific routines and struct */
220 
221 extern rbtree *rballoc(rbtree *up, void *data, long ldata);
222 extern int rbcmp(rbtree *node, void *data, long ldata);
223 
224 rbtree *var_table = 0;
225 rbtree *var_first = 0;
226 rbtree *var_last = 0;
227 
228 struct rbtree {
229   /* generic rbtree links */
230   rbtree *left, *right, *up;
231   int red;
232 
233   /* application-specific data */
234   rbtree *next;
235   char *cname;
236   int type;
237   char name[4];
238 };
239 
240 #define VAR_UNKNOWN 0
241 #define VAR_CODE    1
242 #define VAR_DATA    2
243 #define VAR_FORT    4
244 #define VAR_RESHAPE 8
245 
246 rbtree *
rballoc(rbtree * up,void * data,long ldata)247 rballoc(rbtree *up, void *data, long ldata)
248 {
249   char *name = data;
250   rbtree *node = malloc(sizeof(rbtree)+ldata);
251   if (node) {
252     rbinit(node, up);
253     if (!var_first) var_first = node;
254     else            var_last->next = node;
255     var_last = node;
256     node->next = 0;
257     node->cname = 0;
258     node->type = 0;
259     strncpy(node->name, name, ldata);
260     node->name[ldata] = '\0';
261   }
262   return node;
263 }
264 
265 int
rbcmp(rbtree * node,void * data,long ldata)266 rbcmp(rbtree *node, void *data, long ldata)
267 {
268   int cmp = strncmp(node->name, data, ldata);
269   if (!cmp && node->name[ldata]) cmp = 1;
270   return -cmp;
271 }
272 
273 /* --------- generic rbtree routines */
274 
275 void
rbinit(rbtree * node,rbtree * up)276 rbinit(rbtree *node, rbtree *up)
277 {
278   node->left = node->right = 0;
279   node->up = up;
280   node->red = (up != 0);
281 }
282 
283 rbtree *
rblookup(rbtree * root,void * data,long ldata)284 rblookup(rbtree *root, void *data, long ldata)
285 {
286   int cmp;
287   while (root) {
288     cmp = rbcmp(root, data, ldata);
289     if (!cmp) return root;
290     root = (cmp<0)? root->left : root->right;
291   }
292   return 0;
293 }
294 
295 rbtree *
rbinsert(rbtree ** proot,void * data,long ldata)296 rbinsert(rbtree **proot, void *data, long ldata)
297 {
298   int lchild, lparent;
299   rbtree *node, *child, *gramps, *uncle, *parent = *proot;
300 
301   if (!parent)
302     return *proot = rballoc(0, data, ldata);
303 
304   for (;;) {
305     lchild = rbcmp(parent, data, ldata);
306     if (!lchild) return 0;   /* cannot insert duplicate data */
307     lchild = (lchild < 0);
308     if (lchild) {
309       if (!parent->left) {
310         parent->left = node = rballoc(parent, data, ldata);
311         break;
312       }
313       parent = parent->left;
314     } else {
315       if (!parent->right) {
316         parent->right = node = rballoc(parent, data, ldata);
317         break;
318       }
319       parent = parent->right;
320     }
321   }
322   if (!node) return 0;
323 
324   child = node;
325   while (parent->red) {    /* note this implies parent->up != 0 */
326     gramps = parent->up;
327     lparent = gramps->left == parent;
328     uncle = lparent? gramps->right : gramps->left;
329 
330     if (uncle && uncle->red) {
331       /* red uncle: recolor parent, uncle, gramps and propagate upward */
332       parent->red = uncle->red = 0;
333       parent = gramps->up;
334       if (!parent) break;       /* quit if gramps is root */
335       gramps->red = 1;          /* was black */
336       child = gramps;           /* child always has two black children */
337       lchild = (parent->left == child);
338       continue;
339     }
340 
341     /* black uncle: balance tree (4 cases) and quit */
342     if (lchild == lparent) {
343       if (lparent) {
344         /*     ( (c[R] < p[R] < x[B]) < g[B] < u[B] )
345          * -->   (c[R] < p[B] < (x[B] < g[R] < u[B]) )
346          */
347         gramps->left = parent->right;  parent->right = gramps;
348         if (gramps->left) gramps->left->up = gramps;
349       } else {
350         /*      ( u[B] < g[B] < (x[B] < p[R] < c[R]) )
351          * --> ( (u[B] < g[R] < x[B]) < p[B] < c[R])
352          */
353         gramps->right = parent->left;  parent->left = gramps;
354         if (gramps->right) gramps->right->up = gramps;
355       }
356       parent->up = gramps->up;  gramps->up = parent;
357       if (!parent->up) *proot = parent;
358       else if (parent->up->left == gramps) parent->up->left = parent;
359       else parent->up->right = parent;
360       parent->red = 0;
361     } else {
362       if (lparent) {
363         /*     ( (x[B] < p[R] < (clB < c[R] < crB)) < g[B] < u[B] )
364          * --> ( (x[B] < p[R] < clB) < c[B] < (crB < g[R] < u[B]) )
365          */
366         parent->right = child->left;  child->left = parent;
367         gramps->left = child->right;  child->right = gramps;
368         if (parent->right) parent->right->up = parent;
369         if (gramps->left) gramps->left->up = gramps;
370       } else {
371         /*     ( u[B] < g[B] < ((clB < c[R] < crB) < p[R] < x[B]) )
372          * --> ( (u[B] < g[R] < clB) < c[B] < (crB < p[R] < x[B]) )
373          */
374         parent->left = child->right;  child->right = parent;
375         gramps->right = child->left;  child->left = gramps;
376         if (parent->left) parent->left->up = parent;
377         if (gramps->right) gramps->right->up = gramps;
378       }
379       child->up = gramps->up;  gramps->up = parent->up = child;
380       if (!child->up) *proot = child;
381       else if (child->up->left == gramps) child->up->left = child;
382       else child->up->right = child;
383       child->red = 0;
384     }
385     gramps->red = 1;
386     break;
387   }
388 
389   return node;
390 }
391 
392 /* end of red-black tree code */
393 /* ------------------------------------------------------------------------ */
394 
395 static int
genwrap(int argc,char * argv[])396 genwrap(int argc, char *argv[])
397 {
398   FILE *out=0, *in=0;
399   char *outname = 0;
400   char *pkgname = argv[0];
401   char **dirs = malloc(sizeof(char *) * argc);
402   int ndirs = 0;
403   char **libs = malloc(sizeof(char *) * argc);
404   int nlibs = 0;
405   char **ins = malloc(sizeof(char *) * argc);
406   int nins = 0;
407   int flink = F_LINK;  /* 1 supress trailing _, 2 upper case */
408   int i;
409   rbtree *node;
410 
411   for (argc--,argv++ ; argc ; argc--,argv++) {
412     if (argv[0][0] == '-') {
413       if (argv[0][1]=='o' && !argv[0][2]) {
414         argc--,argv++;
415         if (!argc || outname || out)
416           return usage();
417         outname = argv[0];
418       } else if (argv[0][1]=='I' && argv[0][2]) {
419         dirs[ndirs++] = argv[0]+2;
420       } else if ((argv[0][1]=='L' || argv[0][1]=='l') && argv[0][2]) {
421         libs[nlibs++] = argv[0];
422       } else if (argv[0][1]=='f' || argv[0][1]=='F') {
423         if (argv[0][2]=='_' && !argv[0][3]) flink = 0;
424         else if (!argv[0][2])               flink = 1;
425         else                                return usage();
426         flink |= (argv[0][1]=='F')? 2 : 0;
427       } else {
428         return usage();
429       }
430     } else {
431       if (!out) {
432         out = out_open(outname? outname : "ywrap.c");
433         if (!out) {
434           E_("unable to create output file");
435           return 2;
436         }
437       }
438       ins[nins++] = argv[0];
439       O_("");
440       fprintf(out, "/*----------------begin %s */\n", argv[0]);
441       in = i_open(argv[0], dirs, ndirs);
442       if (!in) {
443         fclose(out);
444         return 3;
445       }
446       /* i_process emits any wrapper code, adds to routines, values, names */
447       if (i_process(out, in, flink, argv[0])) {
448         fclose(out);
449         return 4;
450       }
451     }
452   }
453 
454   O_("");
455   O_("/*----------------list include files */");
456   O_("");
457   O_("static char *y0_includes[] = {");
458   for (i=0 ; i<nins ; i++)
459     fprintf(out, "  \"%s\",\n", ins[i]);
460   O_("  0,");
461   if (nlibs) {
462     O_("  /*--------------dependent libraries this package requires */");
463     fprintf(out, "  \"%s", libs[0]);
464     for (i=1 ; i<nlibs ; i++)
465       fprintf(out, " %s", libs[i]);
466     O_("\",");
467   }
468   O_("  0");
469   O_("};");
470 
471   O_("");
472   O_("/*----------------collect pointers and names */");
473   O_("");
474   O_("static BuiltIn *y0_routines[] = {");
475   for (node=var_first ; node ; node=node->next)
476     if (!(node->type&VAR_DATA)) fprintf(out, "  &Y_%s,\n", node->name);
477   O_("  0");
478   O_("};");
479   O_("");
480   O_("static void *y0_values[] = {");
481   for (node=var_first ; node ; node=node->next)
482     if (node->type&VAR_DATA)
483       fprintf(out, "  %s,\n", node->cname?  node->cname : node->name);
484   O_("  0");
485   O_("};");
486   O_("");
487   O_("static char *y0_names[] = {");
488   for (node=var_first ; node ; node=node->next)
489     if (!(node->type&VAR_DATA)) fprintf(out, "  \"%s\",\n", node->name);
490   for (node=var_first ; node ; node=node->next)
491     if (node->type&VAR_DATA) fprintf(out, "  \"%s\",\n", node->name);
492   O_("  0");
493   O_("};");
494 
495   O_("");
496   O_("/*----------------define package initialization function */");
497   O_("");
498   fprintf(out, "PLUG_EXPORT char *yk_%s(char ***,\n", pkgname);
499   O_("                         BuiltIn ***, void ***, char ***);");
500   fprintf(out, "static char *y0_pkgname = \"%s\";\n", pkgname);
501   O_("");
502   O_("char *");
503   fprintf(out, "yk_%s(char ***ifiles,\n", pkgname);
504   O_("       BuiltIn ***code, void ***data, char ***varname)");
505   O_("{");
506   O_("  *ifiles = y0_includes;");
507   O_("  *code = y0_routines;");
508   O_("  *data = y0_values;");
509   O_("  *varname = y0_names;");
510   O_("  return y0_pkgname;");
511   O_("}");
512 
513   fclose(out);
514   return 0;
515 }
516 
517 static FILE *
out_open(char * name)518 out_open(char *name)
519 {
520   FILE *out = fopen(name, "w");
521   if (!out) {
522     fprintf(stderr, "codger: unable to create %s\n", name);
523     return 0;
524   }
525 
526   O_("/* codger-generated yorick package wrapper file */");
527   O_("#include \"play.h\"");
528   O_("#include \"ydata.h\"");
529   return out;
530 }
531 
532 static FILE *
i_open(char * name,char ** dirs,int ndirs)533 i_open(char *name, char **dirs, int ndirs)
534 {
535   /* first try curent directory, then directories in -I options */
536   FILE *in = fopen(name, "r");
537   if (!in) {
538     char tmp[2048];
539     long i, n, len = strlen(name);
540     for (i=ndirs-1 ; i>=0 && !in ; i--) {  /* scan -I in reverse order */
541       n = strlen(dirs[i]);
542       if (n+len > 2046) {
543         E_("codger: pkg.i name plus -Idir option >2046 characters");
544         return 0;
545       }
546       strcpy(tmp, dirs[i]);
547       if (n && tmp[n-1]!='/') strcpy(tmp+n, "/");
548       strcat(tmp+n, name);
549       in = fopen(tmp, "r");
550     }
551     if (!in)
552       fprintf(stderr, "codger: unable to find %s, need -I?", name);
553     else
554       fprintf(stdout, "found %s in %s\n", name, dirs[i+1]);
555   } else {
556     fprintf(stdout, "found %s in current directory\n", name);
557   }
558   return in;
559 }
560 
561 static char *next_line(FILE *in);
562 static char *skip_white(char *pos);
563 static char *next_nonblank(FILE *in, int hash);
564 static char *skip_single(char *pos);
565 static char *skip_quote(FILE *in, char *pos);
566 static char *skip_comment(FILE *in, char *pos);
567 static char *skip_hash(FILE *in, char *pos);
568 static char *skip_brace(FILE *in, char *pos);
569 static char *next_action(FILE *in, char *pos);
570 static char *next_token(FILE *in, char *pos, int incomment);
571 static int is_fortran(FILE *in, char **ppos);
572 static int name_len(char *pos);
573 static void handle_extern(FILE *out, char *pos, int len);
574 static void handle_reshape(FILE *out, char *pos, int len);
575 static void handle_extcomm(FILE *out, char *pos, int len, int fort);
576 static void handle_procomm(FILE *out, FILE *in, char *pos, int fort);
577 static void put_extern(FILE *out, rbtree *var, int check);
578 static void put_cname(rbtree *var, char *pos, int len);
579 static int get_ctype(FILE *in, char **ppos);
580 static void i_warning(char *msg);
581 
582 #define IS_WHITE(c) (c==' ' || c=='\t' || c=='\n' || c=='\r')
583 #define IS_EWHITE(c) ((!c) || c==' ' || c=='\t' || c=='\n' || c=='\r')
584 #define IS_ALPH(c) ((c>='A' && c<='Z') || (c>='a' && c<='z') || c=='_')
585 #define IS_ALPHNUM(c) (IS_ALPH(c) || (c>='0' && c<='9'))
586 
587 static long line_num;
588 static char *file_name;
589 static int fortran_policy;
590 static int n_errs;
591 
592 static int
i_process(FILE * out,FILE * in,int flink,char * inname)593 i_process(FILE *out, FILE *in, int flink, char *inname)
594 {
595   char *pos;
596   int fort, lname;
597 
598   file_name = inname;
599   line_num = 0;
600   fortran_policy = flink;
601   n_errs = 0;
602 
603   for (pos=next_action(in,"") ; pos ; pos=next_action(in,pos)) {
604     if (pos[0] == 'e') {         /* extern statement */
605       pos = next_token(in, pos+6, 0);
606       if (!pos) break;
607       lname = name_len(pos);
608       if (!lname)
609         i_warning("bad extern statement");
610       else
611         /* this becomes current variable for PROTOTYPE, EXTERNAL comments */
612         handle_extern(out, pos, lname);
613 
614     } else if (pos[0] == 'P') {  /* PROTOTYPE comment */
615       fort = is_fortran(in, &pos);
616       /* can go ahead and generate wrapper immediately,
617        * also mark current variable definitely a builtin
618        */
619       handle_procomm(out, in, pos, fort);
620       pos = skip_comment(in, pos);
621 
622     } else if (pos[0] == 'r') {  /* reshape statement */
623       pos = next_token(in, pos+7, 0);
624       if (!pos) break;
625       if (pos[0] == ',') {
626         pos = next_token(in, pos+1, 0);
627         if (!pos) break;
628       }
629       lname = name_len(pos);
630       if (!lname)
631         i_warning("bad reshape statement");
632       else
633         /* mark variable definitely data if not already marked */
634         handle_reshape(out, pos, lname);
635 
636     } else if (pos[0] == 'E') {  /* EXTERNAL comment */
637       fort = is_fortran(in, &pos);
638       lname = name_len(pos);
639       if (!lname)
640         i_warning("bad EXTERNAL comment");
641       else
642         /* mark current variable definitely data, with compiled alias */
643         handle_extcomm(out, pos, lname, fort);
644       pos = skip_comment(in, pos);
645     }
646   }
647   fclose(in);
648 
649   put_extern(out, var_last, 1);
650 
651   /* make sure output file is uncompilable if errors detected */
652   if (n_errs) {
653     fprintf(out, "\n#error %d codger errors in %s*/\n\n", n_errs, inname);
654     return 1;
655   }
656   return 0;
657 }
658 
659 static void
i_warning(char * msg)660 i_warning(char *msg)
661 {
662   n_errs++;
663   fprintf(stderr, "codger: %s, LINE: %ld FILE: %s\n",
664           msg, line_num, file_name);
665 }
666 
667 static int
is_fortran(FILE * in,char ** ppos)668 is_fortran(FILE *in, char **ppos)
669 {
670   char *pos = *ppos;
671   int fort = 0;
672   pos += 8;                       /* skips EXTERNAL */
673   if (!IS_EWHITE(pos[0])) pos++;  /* skips PROTOTYPE */
674   pos = next_token(in, pos, 1);
675   if (pos[0]=='F' && !strncmp(pos+1,"ORTRAN",6) && IS_EWHITE(pos[7])) {
676     fort = 1;
677     pos = next_token(in, pos+7, 1);
678   }
679   *ppos = pos;
680   return fort;
681 }
682 
683 static int
name_len(char * pos)684 name_len(char *pos)
685 {
686   int len = 0;
687   if (pos && IS_ALPH(pos[0]))
688     for (len++ ; IS_ALPHNUM(pos[len]) ; len++);
689   return len;
690 }
691 
692 static char *
next_line(FILE * in)693 next_line(FILE *in)
694 {
695   static char line[4096];
696   if (feof(in)) return 0;
697   line_num++;
698   return fgets(line, 4096, in);
699 }
700 
701 static char *
skip_white(char * pos)702 skip_white(char *pos)
703 {
704   if (pos)
705     while (pos[0] && IS_WHITE(pos[0])) pos++;
706   return pos;
707 }
708 
709 static char *
next_nonblank(FILE * in,int hash)710 next_nonblank(FILE *in, int hash)
711 {
712   char *pos;
713   do {
714     pos = next_line(in);
715     if (!pos) return 0;
716     pos = skip_white(pos);
717     if (hash && pos[0]=='#') {
718       pos = skip_hash(in, pos);
719       if (!pos) return 0;
720     }
721   } while (!pos[0]);
722   return pos;
723 }
724 
725 static char *
skip_single(char * pos)726 skip_single(char *pos)
727 {
728   /* pos[0] == ' on input, scan to closing ' */
729   pos++;
730   if (pos[0]=='\\' && (++pos)[0]) ++pos;
731   while (pos[0] && pos[0]!='\'') pos++;
732   return pos[0]? pos+1 : pos;
733 }
734 
735 static char *
skip_quote(FILE * in,char * pos)736 skip_quote(FILE *in, char *pos)
737 {
738   /* pos[0] == " on input, scan to closing " */
739   pos++;
740   while (pos[0]!='"') {
741     if (!pos[0]) {
742       pos = next_line(in);
743       if (!pos) return 0;
744     } else if (pos[0]=='\\') {
745       pos++;
746       if (pos[0]<='7' && pos[0]>='0') {
747         pos++;
748         if (pos[0]<='7' && pos[0]>='0') {
749           pos++;
750           if (pos[0]<='7' && pos[0]>='0') pos++;
751         }
752       } else if (pos[0] == 'x') {
753         pos++;
754         while ((pos[0]<='9' && pos[0]>='0')
755                || (pos[0]<='F' && pos[0]>='A')
756                || (pos[0]<='f' && pos[0]>='a')) pos++;
757       } else {
758         pos++;
759       }
760     } else {
761       pos++;
762     }
763   }
764   return pos+1;
765 }
766 
767 static char *
skip_comment(FILE * in,char * pos)768 skip_comment(FILE *in, char *pos)
769 {
770   /* scan to closing star slash */
771   do {
772     while (pos[0])
773       if ((pos++)[0]=='*' && pos[0]=='/') return pos+1;
774     pos = next_line(in);
775   } while (pos);
776   return 0;
777 }
778 
779 static char *
skip_hash(FILE * in,char * pos)780 skip_hash(FILE *in, char *pos)
781 {
782   /* yorick bug:
783    * #if  at beginning of line inside slash-star comment
784    * is correctly commented out,
785    * but   #endif cannot be similarly commented out if inside an #if
786    * this code repeats that bug
787    */
788   /* pos[0] == # on input, scan to next valid line */
789   int depth = 0;
790   for(;;) {
791     pos = skip_white(pos+1);
792     if (depth && !strncmp(pos,"endif",5) && IS_EWHITE(pos[5])) {
793       depth--;
794     } else if (pos[0]=='i' && pos[1]=='f' && (pos[2]==' ' || pos[2]=='\t')) {
795       pos = skip_white(pos+3);
796       if (depth || (pos[0]=='0' && IS_EWHITE(pos[1])))
797         depth++;
798     }
799     for (;;) {
800       pos = next_line(in);
801       if (!pos) return 0;
802       pos = skip_white(pos);
803       if (pos[0]=='#') break;
804       if (depth || !pos[0]) continue;
805       return pos;  /* non-blank, non-# */
806     }
807   }
808 }
809 
810 static char *
skip_brace(FILE * in,char * pos)811 skip_brace(FILE *in, char *pos)
812 {
813   /* pos[0] == { on input, scan to matching } */
814   int depth = 1;
815   pos++;
816   do {
817     pos = skip_white(pos);
818     if (!pos[0]) {
819       pos = next_nonblank(in, 1);
820       if (!pos) return 0;
821     }
822     if (pos[0]=='}') {
823       pos++;
824       depth--;
825     } else if (pos[0]=='{') {
826       pos++;
827       depth++;
828     } else if (pos[0]=='/') {
829       if (pos[1]=='/') {
830         pos = next_nonblank(in, 1);
831       } else if (pos[1]=='*') {
832         pos = skip_comment(in, pos+2);
833       } else {
834         pos++;
835       }
836     } else if (pos[0]=='"') {
837       pos = skip_quote(in, pos);
838       if (!pos) return 0;
839     } else if (pos[0]=='\'') {
840       pos = skip_single(pos);
841     } else {
842       pos++;
843     }
844   } while (pos && depth);
845   return pos;
846 }
847 
848 static char *
next_action(FILE * in,char * pos)849 next_action(FILE *in, char *pos)
850 {
851   /* scan to next extern, reshape, PROTOTYPE, or EXTERNAL */
852   while (pos) {
853     while (pos[0]) {
854       /* clear out remainder of line */
855       if (pos[0]=='"') {
856         pos = skip_quote(in, pos);
857       } else if (pos[0]=='/') {
858         if (pos[1]=='*') pos = skip_comment(in, pos+2);
859         else if (pos[1]=='/') for (pos+=2 ; pos[0] ; pos++);
860         else pos++;
861       } else if (pos[0]=='{') {
862         pos = skip_brace(in, pos);
863       } else {
864         pos++;
865       }
866       if (!pos) return 0;
867     }
868     if (!pos) return 0;
869     pos = next_nonblank(in, 1);
870     if (!pos) return 0;
871     /* check if beginning of line is an action for codger */
872     if (pos[0]=='e') {
873       if (!strncmp(pos+1,"xtern",5) && !IS_ALPHNUM(pos[6]))
874         return pos;           /* extern */
875     } else if (pos[0]=='r') {
876       if (!strncmp(pos+1,"eshape",6) && !IS_ALPHNUM(pos[7]))
877         return pos;           /* reshape */
878     } else if (pos[0]=='/' && pos[1]=='*') {
879       pos = next_token(in, pos+2, 1);
880       if (pos[0]=='P') {
881         if (!strncmp(pos+1,"ROTOTYPE",8) && IS_EWHITE(pos[9]))
882           return pos;       /* PROTOTYPE comment */
883       } else if (pos[0]=='E') {
884         if (!strncmp(pos+1,"XTERNAL",7) && IS_EWHITE(pos[8]))
885           return pos;       /* EXTERNAL comment */
886       }
887       pos = skip_comment(in, pos);
888     }
889   }
890   return pos;
891 }
892 
893 static char *
next_token(FILE * in,char * pos,int incomment)894 next_token(FILE *in, char *pos, int incomment)
895 {
896   if (!pos) return 0;
897   for (;;) {
898     pos = skip_white(pos);
899     while (!pos[0]) {
900       pos = next_line(in);
901       if (!pos) return 0;
902       pos = skip_white(pos);
903     }
904     if (incomment || pos[0]!='/') break;
905     if (pos[1]=='*') pos = skip_comment(in, pos+2);
906     else if (pos[1]=='/') for (pos+=2 ; pos[0] ; pos++);
907     else break;
908   }
909   return pos;
910 }
911 
912 static void
handle_extern(FILE * out,char * pos,int len)913 handle_extern(FILE *out, char *pos, int len)
914 {
915   rbtree *prev = var_last;
916   rbtree *v = rbinsert(&var_table, pos, len);
917   if (!v) {
918     /* multiple externs for same var ugly but legal? */
919     if (!rblookup(var_table, pos, len))
920       i_warning("MEMORY MANAGER FAILED");
921 
922   } else {
923     put_extern(out, prev, 1);
924   }
925 }
926 
927 static void
handle_reshape(FILE * out,char * pos,int len)928 handle_reshape(FILE *out, char *pos, int len)
929 {
930   if (var_last && rblookup(var_table, pos, len)==var_last) {
931     if (var_last->type & VAR_CODE) {
932       i_warning("cannot reshape builtin function");
933     } else if (var_last->type & VAR_DATA) {
934       var_last->type |= VAR_RESHAPE;
935     } else {
936       var_last->type = VAR_DATA | VAR_RESHAPE;
937       /* generate extern declaration immediately */
938       put_extern(out, var_last, 0);
939     }
940   }
941 }
942 
943 static void
handle_extcomm(FILE * out,char * pos,int len,int fort)944 handle_extcomm(FILE *out, char *pos, int len, int fort)
945 {
946   if (var_last) {
947     if (var_last->type & VAR_CODE) {
948       i_warning("cannot EXTERNAL builtin function");
949     } else if (var_last->type & VAR_RESHAPE) {
950       i_warning("EXTERNAL must come before reshape");
951     } else if (var_last->type & VAR_DATA) {
952       i_warning("duplicate EXTERNAL comment illegal");
953     } else {
954       var_last->type = VAR_DATA | (fort? VAR_FORT : 0);
955       put_cname(var_last, pos, len);
956       /* generate extern declaration immediately */
957       put_extern(out, var_last, 0);
958     }
959   }
960 }
961 
962 static void
put_cname(rbtree * var,char * pos,int len)963 put_cname(rbtree *var, char *pos, int len)
964 {
965   int fort = (var->type & VAR_FORT) != 0;
966   /* bit 1 of fortran_policy supresses trailing _ */
967   int underscore = (fort && !(fortran_policy & 1));
968   var->cname = malloc(len+underscore+1);
969   if (var->cname) {
970     strncpy(var->cname, pos, len);
971     if (underscore) var->cname[len++] = '_';
972     var->cname[len] = '\0';
973     if (fort) {
974       int i;
975       /* bit 2 of fortran_policy means upper case, else lower case */
976       if (fortran_policy & 2) {
977         for (i=0 ; i<len ; i++)
978           if (var->cname[i]>='a' && var->cname[i]<='z')
979             var->cname[i] ^= ('a' ^ 'A');
980       } else {
981         for (i=0 ; i<len ; i++)
982           if (var->cname[i]>='A' && var->cname[i]<='Z')
983             var->cname[i] ^= ('a' ^ 'A');
984       }
985     }
986   } else {
987     i_warning("MEMORY MANAGER FAILED");
988   }
989 }
990 
991 static void
put_extern(FILE * out,rbtree * var,int check)992 put_extern(FILE *out, rbtree *var, int check)
993 {
994   if (var && (!check || !var->type)) {
995     if (var->type & VAR_DATA) {
996       fprintf(out, "extern char %s[4];\n", var->cname?var->cname:var->name);
997     } else {
998       fprintf(out, "extern BuiltIn Y_%s;\n", var->name);
999       if (!var->type) var->type = VAR_CODE;
1000     }
1001   }
1002 }
1003 
1004 /* in alphabetical order */
1005 static char *proto_types[11] = {
1006   "char", "complex", "double", "float", "int", "long",
1007   "pointer", "short", "string", "void", 0 };
1008 static char *c_types[11] = {
1009   "char ", "double ", "double ", "float ", "int ", "long ",
1010   "void *", "short ", "char *", "void ", 0 };
1011 /* bit 1 return not ok, 2 arg not okay, 4 complex */
1012 static int c_modes[11] = { 0, 5, 0, 0, 0, 0, 1, 0, 1, 2, -1 };
1013 static char *push_result[11] = {
1014   "PushIntValue((int)", 0, "PushDoubleValue(", "PushDoubleValue((double)",
1015   "PushIntValue(", "PushLongValue(", 0, "PushIntValue((short)", 0, "", 0 };
1016 static char *push_finish[11] = {
1017   ")", 0, ")", ")", ")", ")", 0, ")", 0, "", 0 };
1018 static char *yarg_char[11] = {
1019   "c", "z", "d", "f", "i", "l", "p", "s", "q", 0, 0 };
1020 
1021 static int
get_ctype(FILE * in,char ** ppos)1022 get_ctype(FILE *in, char **ppos)
1023 {
1024   char *pos = *ppos;
1025   int lname = name_len(pos);
1026   int i = -1;
1027   if (lname > 1) {
1028     for (i=0 ; ; i++) {
1029       if (!proto_types[i] || pos[0]<proto_types[i][0]) return -1;
1030       if (pos[0] > proto_types[i][0]) continue;
1031       if (!strncmp(proto_types[i]+1, pos+1, lname-1) &&
1032           !proto_types[i][lname]) break;
1033     }
1034     pos += lname;
1035     *ppos = pos;
1036   }
1037   return i;
1038 }
1039 
1040 static void
handle_procomm(FILE * out,FILE * in,char * pos,int fort)1041 handle_procomm(FILE *out, FILE *in, char *pos, int fort)
1042 {
1043   char proto[256], delim;
1044   int lname, ret, arg, nargs, len, i, star;
1045 
1046   if (!var_last) return;
1047   if (var_last->type & VAR_DATA) {
1048     i_warning("cannot PROTOTYPE data variable");
1049     return;
1050   }
1051   var_last->type = VAR_CODE | (fort? VAR_FORT : 0);
1052   /* generate extern declaration for Y_ wrapper immediately */
1053   put_extern(out, var_last, 0);
1054 
1055   /* remains to generate
1056    * (1) prototype for compiled function
1057    * (2) Y_ wrapper function definition
1058    */
1059   ret = get_ctype(in, &pos);
1060   if (ret<0 || (c_modes[ret]&1)) {
1061     i_warning("bad PROTOTYPE return type");
1062     return;
1063   }
1064   pos = next_token(in, pos, 1);
1065   lname = name_len(pos);
1066   if (!lname) {
1067     i_warning("bad PROTOTYPE function name");
1068     return;
1069   }
1070   put_cname(var_last, pos, lname);
1071   pos = next_token(in, pos+lname, 1);
1072   for (nargs=0,delim = '(' ; ; nargs++) {
1073     /* skip delimiter */
1074     if (pos[0]!=delim && (nargs!=1 || pos[0]!=(delim=','))) {
1075       if (nargs && pos[0]==')') break;        /* only legal way out */
1076       i_warning("bad PROTOTYPE syntax");
1077       return;
1078     } else if (nargs >= 256) {
1079       i_warning("PROTOTYPE has >256 function parameters");
1080       return;
1081     }
1082     pos = next_token(in, pos+1, 1);           /* seek argument type */
1083     arg = get_ctype(in, &pos);
1084     if (arg<0 || (nargs && (c_modes[arg]&2))) {
1085       i_warning("bad PROTOTYPE argument type");
1086       return;
1087     }
1088     pos = next_token(in, pos, 1);
1089     if ((c_modes[arg]&2) && pos[0]!=')') {
1090       i_warning("bad PROTOTYPE void argument");
1091       return;
1092     }
1093     lname = name_len(pos);
1094     if ((lname==5 && !strncmp(pos, "array", 5)) ||
1095         (!lname && pos[0]=='*')) { /* array qualifier */
1096       arg |= 64;
1097       pos = next_token(in, pos+(lname?lname:1), 1);
1098       lname = name_len(pos);
1099     }
1100     proto[nargs] = arg;
1101     if (lname)                       /* skip optional argument name */
1102       pos = next_token(in, pos+lname, 1);
1103   }
1104 
1105   /* first, generate compiled prototype (used in wrapper) */
1106   fprintf(out, "\nextern %s%s(", c_types[ret], var_last->cname);
1107   len = 8+strlen(c_types[ret])+strlen(var_last->cname);
1108   for (i=0 ; i<nargs ; i++) {
1109     if (len > 60) {
1110       fprintf(out, "\n  ");
1111       len = 2;
1112     }
1113     arg = proto[i];
1114     star = ((arg & 64) != 0) || (c_modes[arg&63]&4);
1115     arg &= 63;
1116     fprintf(out, "%s%s%s",
1117             c_types[arg], star? "*":"", (i==nargs-1)?");\n":", ");
1118     len += strlen(c_types[arg]) + star + 2;
1119   }
1120 
1121   /* finally, generate the Y_ wrapper definition itself */
1122   fprintf(out, "void\nY_%s(int n)\n{\n", var_last->name);
1123   if (nargs==1 && (c_modes[(unsigned char)proto[0]&63]&2) &&
1124       (c_modes[ret]&2)) nargs=0;
1125   if (nargs==1 && (c_modes[(unsigned char)proto[0]&63]&2)) {
1126     fprintf(out, "  if (n>1) YError(\"%s takes void argument\");\n",
1127             var_last->name);
1128   } else {
1129     fprintf(out, "  if (n!=%d) YError(\"%s takes exactly %d arguments\");\n",
1130             nargs, var_last->name, nargs);
1131   }
1132   fprintf(out, "  %s%s(", push_result[ret], var_last->cname);
1133   len = 2+strlen(push_result[ret])+strlen(var_last->cname);
1134   for (i=0 ; i<nargs ; i++) {
1135     if (len > 60) {
1136       fprintf(out, "\n    ");
1137       len = 4;
1138     }
1139     arg = proto[i];
1140     star = ((arg & 64) != 0) || (c_modes[arg&63]&4);
1141     arg &= 63;
1142     if (yarg_char[arg]) {
1143       fprintf(out, "yarg_%s%s(%d%s)%s", star? "":"s", yarg_char[arg],
1144               nargs-1-i, star? ",0":"", (i==nargs-1)?"":", ");
1145       len += 13-3*star + ((i<9)?1:2);
1146     }
1147   }
1148   fprintf(out, ")%s;\n}\n\n", push_finish[ret]);
1149 }
1150