home *** CD-ROM | disk | FTP | other *** search
- /* fd2lib by Volker Barthelmann */
- /* rework 09/96 by Johnny Teveßen <j.tevessen@line.org> */
-
- /* "T:"s removed: Not very portable!
- */
-
- #define NDEBUG
-
- #ifdef _DCC
- # define CTYPE_NEAR
- #endif
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <stdarg.h>
-
- const char VersTag[] = "\0$VER: fd2lib 1.3 (15.9.96)"; /* AmigaOS version string. Doesn't hurt... */
-
- char *clibname,*clibtext;
-
- #define MAXLINELEN 1000
- #define BUFFEREDLEN 16384 /* 1024 is standard */
-
- #define NUMREGS 16
-
- static const char *regnames[NUMREGS] =
- {
- "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
- "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7"
- };
-
- enum
- {
- A0=0, A1, A2, A3, A4, A5, A6, A7,
- D0 , D1, D2, D3, D4, D5, D6, D7
- };
-
- #define SMALLCODE 1
- #define SMALLDATA 2
- #define FASTCALL 4
- #define NEWNOTATE 8
- #define VARGSLOGIC 16
- #define DEBUG 32
-
- #ifndef TRUE
- typedef short BOOL;
- # define TRUE 1
- # define FALSE 0
- #endif
-
- #ifndef NULL
- # define NULL ((void *)0L)
- #endif
-
- #ifdef __GNUC__
- # define gnuspec(x) x
- #else
- # define gnuspec(x)
- #endif
-
- #define NORETURN gnuspec(__attribute__ ((noreturn)))
-
- static const char *varargs[] =
- {
- #include "vargs.h"
- NULL, NULL
- };
-
- int get_type(char *dest,const char *p,const char *fname,int arg)
- {
- int nest,carg,l=strlen(fname);const char *last=p,*m,*m2;
- if(arg<0) return 0;
- while(*p){
- if(*p==';'||*p=='\n'){last=p;while(isspace((unsigned char)*last)) last++;}
- if(!strncmp(p,fname,l)){
- m=p;m2=p-1;p+=l;
- while(isspace((unsigned char)*p)) p++;
- if(*p!='('||isalnum((unsigned char)*m2)){
- continue;
- }else{
- if(arg==0){
- memcpy(dest,last,m-last);
- dest[m-last]=0;
- return 1;
- }
- p++;carg=1;nest=0;
- while(1){
- while(isspace((unsigned char)*p)) p++;
- if(*p==')'||*p==0)
- return 0;
- if(carg==arg){
- while(nest!=0||(*p!=','&&*p!=')')){
- if(!*p) return 0;
- if(*p=='(') nest++;
- if(*p==')') nest--;
- *dest++=*p++;
- }
- *dest=0;
- return 1;
- }
- while(nest!=0||(*p!=','&&*p!=')')){
- if(!*p) return 0;
- if(*p=='(') nest++;
- if(*p==')') nest--;
- p++;
- }
- p++;
- carg++;
- }
- }
- }
- p++;
- }
- return 0;
- }
-
- static void ExitFailure(const char *, const char *) NORETURN;
-
- static void
- ExitFailure(const char *cause, const char *insertme)
- {
- fprintf(stderr, cause, insertme);
-
- exit(EXIT_FAILURE);
- }
-
- static void
- check(const char *ptr)
- {
- if(!*ptr)
- {
- ExitFailure("Unexpected EOL\n", NULL);
- }
- }
-
- static void warnhim(int linenr, const char *format, ...) gnuspec(__attribute__ ((format (printf, 2, 3))));
-
- static void
- warnhim(int linenr, const char *format, ...)
- {
- char linebuf[250];
- va_list vl;
-
- va_start(vl,format);
- vsprintf(linebuf, format, vl);
- va_end(vl);
-
- fprintf(stderr, "Warning line %d: %s\n", linenr, linebuf);
- }
-
- static FILE *
- OpenLVO(const char *name, const char *outdir, const char *outform)
- {
- FILE *lvos;
- char lvoname[MAXLINELEN];
-
- strcpy(lvoname, outdir);
-
- if(name)
- {
- char *k = (char *)name, *p;
- int lvonamlen;
-
- if((p = strrchr(k, '/')) != NULL) k = p + 1;
- if((p = strrchr(k, ':')) != NULL) k = p + 1;
-
- strcat(lvoname, k);
-
- lvonamlen = strlen(lvoname);
-
- if((lvonamlen > 7) && !strcmp(lvoname+lvonamlen-7, "_lib.fd"))
- {
- lvoname[lvonamlen-7] = '\0';
- }
- else if((lvonamlen > 3) && !strcmp(lvoname+lvonamlen-3, ".fd"))
- {
- lvoname[lvonamlen-3] = '\0';
- }
-
- strcat(lvoname, "_lvo.s");
- }
- else
- {
- strcat(lvoname, "fd_lvo.s");
- }
-
- printf(outform, lvoname, lvoname, lvoname);
-
- lvos = fopen(lvoname, "w");
-
- return(lvos);
- }
-
- static void
- ProcessFD(const char *name, int mode, const char *outdir, const char *outform)
- {
- FILE *fd, *lvos;
- FILE *out;
- int offset = 0, i, j, count, savecount;
- char function[80], tmpfuncnam[80], ff[MAXLINELEN+8], base[50];
- char line[MAXLINELEN];
- register char *p;
- char *functionp;
- int reg[NUMREGS], loops, linenr = 0;
- BOOL public = -1; /* Why FALSE? */
- char typ[1024];
-
- /* "-1" is the initial value for "public". It means:
- ** no statement yet.
- */
-
- *function = *base = *line = '\0';
-
- if(name) fd = fopen(name, "r");
- else fd = stdin;
-
- if(!fd) ExitFailure("Could not open `%s'\n", name);
-
- setvbuf(fd, NULL, _IOFBF, BUFFEREDLEN); /* maybe _IOLBF? */
-
- if(!(lvos = OpenLVO(name, outdir, outform)))
- ExitFailure("Could not create lvo file\n", NULL);
-
- setvbuf(lvos, NULL, _IOFBF, BUFFEREDLEN);
-
- for(;;)
- {
- char *k;
-
- if(!fgets(line, MAXLINELEN-1, fd)) ExitFailure("Unexpected EOF\n", NULL);
-
- linenr ++;
-
- if((*line == '*') || !*line) continue;
-
- switch(*line + line[2])
- {
- case ('#'+'b'):
- if(!strncmp(line, "##base", 6))
- {
- if(*base) warnhim(linenr, "##base detected more than once!");
-
- p = line+6; while(isspace(*p)) p++;
- k = base ; while(isgraph(*p)) *k++ = *p++;
-
- *k = '\0';
-
- if(mode & DEBUG) printf("Base set to `%s'\n", base);
- continue;
- }
-
- if(!strncmp(line, "##bias", 6))
- {
- p = line+6; while(isspace(*p)) p++;
- sscanf(p, "%i", &offset);
-
- if(mode & DEBUG) printf("Bias set to -%d\n", offset);
- continue;
- }
- break;
-
- case ('#'+'p'):
- if(!strncmp(line, "##public", 8))
- {
- if(public == TRUE) warnhim(linenr, "##public after ##public detected!");
- else public = TRUE;
-
- if(mode & DEBUG) printf("Turned on public mode at bias -%d\n", offset);
- continue;
- }
-
- if(!strncmp(line, "##private", 9))
- {
- if(public == FALSE) warnhim(linenr, "##private after ##private detected!");
- else public = FALSE;
-
- if(mode & DEBUG) printf("Turned off public mode at bias -%d\n", offset);
- continue;
- }
- break;
-
- case ('#'+'e'):
- if(!strncmp(line, "##end", 5)) return;
- break;
- }
-
- if(*line == '#')
- {
- warnhim(linenr, "Unknown directive: `%s'!", line);
- continue;
- }
-
- if(!public)
- {
- offset += 6;
- continue;
- }
-
- if(public == -1)
- {
- warnhim(linenr, "Neither ##public nor ##private specified yet. Assuming ##public.");
- public = TRUE;
- }
-
- functionp = function;
-
- for(loops=0; loops<=1; loops++)
- {
- char *p = line; char *k = functionp;
-
- while(isspace(*p)) p++;
-
- if(!loops)
- {
- while((*p != '(') && *p) *k++ = *p++;
-
- check(p);
- *k = '\0';
-
- fprintf(lvos, "_LVO%s\tequ\t-%d\n"
- "\txdef\t_LVO%s\n",
- functionp, offset, functionp
- );
- }
- else
- {
- while((*p != '(') && *p) p++;
- check(p);
- }
-
- if(mode & DEBUG) printf("function=%s, loops=%d\n", functionp, loops);
-
- /* Open function stub source file */
-
- sprintf(ff, "%s%s.s", outdir, functionp);
-
- printf(outform, ff, ff, ff);
-
- out = fopen(ff, "w");
- if(!out) ExitFailure("Could not create <%s>\n", functionp);
- setvbuf(out, NULL, _IOFBF, BUFFEREDLEN);
-
- /* Write assembler headers */
-
- if(mode & SMALLDATA) fprintf(out, "\tnear\t%s,-2\n", regnames[A4]);
- if(mode & SMALLCODE) fputs ( "\tnear\tcode\n" , out);
-
- if(*base) fprintf(out, "\txref\t%s\n", base);
-
- fprintf(out, "\txdef\t_%s\n"
- "\tsection\t\"CODE\",code\n"
- "\n"
- "_%s:\n",
- functionp, functionp
- );
-
- /* Set all registers to 'unused' */
-
- for(i=0; i<NUMREGS; i++) reg[i] = 0;
-
- /* Skip argument names */
-
- while((*p!=')') && *p) p++;
-
- check(p);
- p++;
-
- if(clibtext&&!loops){
- if(!get_type(typ,clibtext,functionp,0)) strcpy(typ,"error");
- printf("%s __%s(",typ,functionp);
- }
-
- /* Search for beginning of register list */
-
- while((*p!='(') && *p) p++;
-
- check(p);
- p++;
-
- /* Scan register list */
-
- count = savecount = 0;
-
- while((*p!=')') && *p)
- {
- /* Check whether register description is valid */
-
- if((!((*p=='a') || (*p=='A') || (*p=='d') || (*p=='D'))) || !((p[1]>='0') && (p[1]<='7')))
- ExitFailure("Bad register description\n", NULL);
-
- /* Convert description to internal enum format */
- /* Corrected: 'A' was not recognized */
-
- if((*p=='a') || (*p=='A')) j = p[1] - '0';
- else j = p[1] - ('0'-8);
-
- if(clibtext&&!loops){
- if(!get_type(typ,clibtext,functionp,count+1)) strcpy(typ,"error");
- printf("__reg(\"%s\") %s,",regnames[j],typ);
- }
-
- /* Mark register as used and save its argument counter */
-
- reg[j] = ++count;
-
- /* Increase counter if register has to be saved */
-
- if(!(j==A0 || j==A1 || j==A6 || j==D0 || j==D1))
- savecount ++;
-
- /* Search for next register */
-
- p += 2;
- while(isspace(*p) && *p) p++;
-
- if((*p == '/') || (*p==',')) p++;
- else if(*p != ')') ExitFailure("Parse error - ')' expected\n", NULL);
-
- while(isspace(*p) && *p) p++;
-
- check(p);
- }
-
- if(clibtext&&!loops){
- int i;
- printf("__reg(\"a6\") void *)=\"\\tjsr\\t-%d(a6)\";\n",offset);
- printf("#define %s(",functionp);
- for(i=1;i<=count;i++){
- if(i>1) printf(",");
- printf("x%d",i);
- }
- printf(") __%s(",functionp);
- for(i=1;i<=count;i++) printf("(x%d),",i);
- printf("%s)\n",base+1);
- }
-
-
- if(savecount)
- {
- /* 'savecount' registers have to be saved */
-
- /* Always save library register */
-
- if((mode & FASTCALL) || (savecount == 1))
- {
- /* Store registers sequential. NEW: Changed that it'll also be
- ** used if only two (including library register) registers have
- ** to be stored. That's faster than the original method.
- */
-
- fprintf(out, "\tmove.l\t%s,-(%s)\n", regnames[A6], regnames[A7]);
- }
- else
- {
- /* It would be better to set A6 to the end of the list ...
- */
-
- fprintf(out, "\tmovem.l\t%s", regnames[A6]);
- }
-
- for(i=2; i<NUMREGS; i++) /* Can start at 2, because 0 and 1 are ignored */
- {
- if(reg[i] != 0)
- {
- switch(i)
- {
- case D0:
- case D1:
- break;
-
- case A6:
- warnhim(linenr, "Register conflict in `%s' (using libbasereg)!", functionp);
- break;
-
- default:
- if((mode & FASTCALL) || (savecount == 1))
- {
- fprintf(out, "\tmove.l\t%s,-(%s)\n", regnames[i], regnames[A7]);
- }
- else
- {
- fprintf(out, "/%s", regnames[i]);
- }
- break;
- }
- }
- }
-
- if(!((mode & FASTCALL) || (savecount == 1))) fprintf(out, ",-(%s)\n", regnames[A7]);
- }
- else
- {
- /* No registers have to be saved, so just save the register
- ** where the library base will be stored
- */
-
- fprintf(out, "\tmove.l\t%s,-(%s)\n", regnames[A6], regnames[A7]);
- }
-
- /* Load A6 with base NOW, so there's no conflict with SMALLDATA and A4 anymore ... */
-
- if(*base != '\0')
- {
- if(mode & SMALLDATA)
- {
- fprintf(out, (mode & NEWNOTATE) ? "\tmove.l\t(%s,%s),%s\n"
- : "\tmove.l\t%s(%s),%s\n",
- base, regnames[A4], regnames[A6]
- );
- }
- else
- {
- fprintf(out, "\tmove.l\t%s,%s\n", base, regnames[A6]);
- }
- }
- else ExitFailure("No base defined!\n", NULL);
-
- for(j=1; j<=count; j++) /* Arguments */
- {
- for(i=0; i<NUMREGS; i++) /* Registers */
- {
- if(reg[i] == j) /* Argument in THIS register? */
- {
- int saveoffset = (savecount+j+1)<<2;
-
- if((i>=D7) || (reg[i+1] != j+1) || ((loops==1) && (j>=count-1)))
- {
- if(!loops || (j<count))
- {
- fprintf(out, (mode & NEWNOTATE) ? "\tmove.l\t(%d,%s),%s\n"
- : "\tmove.l\t%d(%s),%s\n",
- saveoffset, regnames[A7], regnames[i]);
- }
- else
- {
- if(i <= 7)
- {
- /* adress register */
-
- fprintf(out, (mode & NEWNOTATE) ? "\tlea\t(%d,%s),%s\n"
- : "\tlea\t%d(%s),%s\n",
- saveoffset, regnames[A7], regnames[i]
- );
- }
- else
- {
- /* data register */
-
- /* Changed: moveq.l will be forced if possible
- ** (there are still assemblers that do not do this
- ** automatically).
- */
-
- fprintf(out, "\tmove%s\t#%d,%s\n" \
- "\tadd.l\t%s,%s\n",
- (saveoffset <= 127) ? "q" : ".l",
- saveoffset, regnames[i], regnames[A7], regnames[i]
- );
- }
- }
- }
- else
- {
- /* Here no fastcall - slower. :-( */
-
- fprintf(out, (mode & NEWNOTATE) ? "\tmovem.l\t(%d,%s),%s"
- : "\tmovem.l\t%d(%s),%s",
- saveoffset, regnames[A7], regnames[i]
- );
-
- while((i<D7) && (reg[i+1] == j+1) && (!loops || (j<count-1)))
- {
- i++; j++;
-
- fprintf(out, "/%s", regnames[i]);
- }
-
- fputc('\n', out);
- }
- } /* if reg[i]==j */
- } /* for i */
- } /* for j */
-
- /* Now place the real function call */
-
- fprintf(out, (mode & NEWNOTATE) ? "\tjsr\t(-%d,%s)\n"
- : "\tjsr\t-%d(%s)\n",
- offset, regnames[A6]
- );
-
- /* Start restoring registers ... libbasereg first. */
-
- if(!((mode & FASTCALL) || (savecount == 1)))
- {
- if(!savecount) fprintf(out, "\tmove.l\t(%s)+,%s" , regnames[A7], regnames[A6]);
- else fprintf(out, "\tmovem.l\t(%s)+,%s", regnames[A7], regnames[A6]);
- }
-
- /* Now the others */
-
- for(i=2; i<NUMREGS; i++)
- {
- if((mode & FASTCALL) || (savecount == 1))
- {
- j = (NUMREGS+1) - i; /* swap direction; okay for != 16 regs? */
- }
- else
- {
- j = i;
- }
-
- if(reg[j])
- {
- switch(j)
- {
- case A6:
- case D0:
- case D1:
- break;
-
- default:
- if((mode & FASTCALL) || (savecount == 1))
- {
- fprintf(out, "\tmove.l\t(%s)+,%s\n", regnames[A7], regnames[j]);
- }
- else
- {
- fprintf(out, "/%s", regnames[j]);
- }
- break;
- }
- }
- }
-
- /* If fastcall was used, a6 was put first. So we pop it last. */
-
- if((mode & FASTCALL) || (savecount == 1))
- {
- fprintf(out, "\tmove.l\t(%s)+,%s", regnames[A7], regnames[A6]);
- }
-
- /* Return from subroutine */
-
- fputs("\n" \
- "\trts\n" \
- "\n" \
- "\tend\n",
- out
- );
-
- fclose(out);
-
- if(loops != 0) break;
-
- p = (char *) *varargs;
- loops = 3;
-
- if(mode & DEBUG) printf("Searching function `%s' in vargs table...\n", functionp);
-
- if(p != NULL)
- {
- if(mode & VARGSLOGIC)
- {
- int fnlen = strlen(functionp);
-
- if((fnlen > 7) && (!strcmp(functionp + fnlen - 7, "TagList")))
- {
- /* xxxTagList function found. Make xxxTags of it
- */
-
- strncpy(tmpfuncnam, functionp, fnlen - 4);
- strcpy (tmpfuncnam + fnlen - 4, "s" );
-
- functionp = tmpfuncnam;
- loops = 0;
- }
- else if((fnlen > 1) && ((functionp[fnlen-1] == 'A') && (functionp[fnlen-2] >= 'a') && (functionp[fnlen-2] <= 'z')))
- {
- /* Not that smart recognition... But you probably
- ** don't want to have a function CreateDA() varargs,
- ** want you?
- **
- ** Recognized are functions that end with 'A' and that
- ** have a lowercase letter before that.
- */
-
- strcpy(tmpfuncnam, functionp);
- tmpfuncnam[fnlen-1] = '\0';
-
- functionp = tmpfuncnam;
- loops = 0;
- }
-
- if(!loops)
- {
- if(mode & DEBUG) puts("Found via internal logic!");
- }
- }
-
- if(loops) for(i=0; p != NULL; i+=2)
- {
- if(!strcmp(p, functionp))
- {
- if(mode & DEBUG) puts("Found!");
-
- functionp = (char *) varargs[i - 1]; /* [i+1] */
- loops = 0;
- break;
- }
-
- /*i += 2;*/
- p = (char *) varargs[i];
- }
- }
- }
-
- offset += 6;
- } /* for(;;) */
-
- if(name) fclose(fd);
-
- fputs("\n\tend\n", lvos);
-
- fclose(lvos);
- }
-
- /* Append '/' to path if needed
- */
-
- static void
- fillpath(char *dirpath)
- {
- int sl = strlen(dirpath);
-
- switch(dirpath[sl-1])
- {
- case ':': /* ':' should be AMIGA-only! This will be commented out. */
- case '/':
- break;
-
- default:
- strcpy(dirpath+sl, "/");
- break;
- }
- }
-
- /* Show program usage
- */
-
- static void Usage(const char *) NORETURN;
-
- static void
- Usage(const char *myname)
- {
- printf("fd2lib 1.3 (c) 9/96 by Volker Barthelmann / Johnny Teveßen\n"
- "\n"
- " -- Caution: Needs ~5000 byte stack! --\n"
- "\n"
- "Usage : %s [-sc] [-sd] [-40] [-on] [-nv] [-o <dir>] [-of <format>]\n"
- " [-d] [-?|--help] [files/pattern]\n"
- "\n"
- " -sc : Use small code model (else large code model)\n"
- " -sd : Use small data model (else large data model)\n"
- " -40 : Use fast call model for 68040\'s (no 'movem's)\n"
- " -on : Use old motorola assembler notation\n"
- " -nv : No varargs logic - ...A and ...TagList will not be detected\n"
- " -o : Specify directory to store source files in\n"
- " -of : C printf style output format to generate compiling\n"
- " script. Three `%%s' are replaced with output file name\n"
- " -d : Turn on debugging/verbose mode\n"
- " -? : Show help/version and quit\n"
- "files : FD files to convert, defaults to stdin\n"
- "\n"
- "Commandline is parsed left-to-right. Specifying\n"
- "\"alib_lib.fd -sd blib_lib.fd\" will result in alib\n"
- "getting large data model.\n",
- myname
- );
-
- exit(0);
- }
-
- /* Remember: ixemul.library does command line expansion, eg.:
- **
- ** redrose# fd2lib -sc -sd /fd/a*_lib.fd
- **
- ** will become:
- **
- ** redrose# fd2lib -sc -sd /fd/amigaguide_lib.fd /fd/asl_lib.fd ...
- **
- */
-
- int
- main(int argc, char **argv)
- {
- int erg = 0 /*EXIT_FAILURE*/;
- int mode = NEWNOTATE | VARGSLOGIC;
- int filesdone = 0;
-
- char outdir[80] = "", outform[250] = "";
-
- if(argc > 1)
- {
- int i;
-
- for(i=1; i<argc; i++)
- {
- if(argv[i][0] == '-')
- {
- /* Parse option */
-
- if( !strcmp(argv[i], "-sc")) mode |= SMALLCODE;
- else if( !strcmp(argv[i], "-sd")) mode |= SMALLDATA;
- else if( !strcmp(argv[i], "-40")) mode |= FASTCALL;
- else if( !strcmp(argv[i], "-on")) mode &= ~NEWNOTATE;
- else if( !strcmp(argv[i], "-nv")) mode &= ~VARGSLOGIC;
- else if( !strcmp(argv[i], "-pr")) clibname=argv[++i];
- else if( !strcmp(argv[i], "-o" ))
- {
- if(i < (argc-1))
- {
- i ++;
-
- if(strlen(argv[i]) < sizeof(outdir))
- {
- strcpy (outdir, argv[i]);
- fillpath(outdir);
- }
- else
- {
- fprintf(stderr, "Path too long. Maximum is %lu characters. Ignored.\n", (unsigned long)sizeof(outdir));
- }
- }
- else
- {
- fputs("No path specified after `-o'!\n", stderr);
- }
- }
- else if( !strcmp(argv[i], "-of"))
- {
- if(i < (argc-1))
- {
- i ++;
-
- if(strlen(argv[i]) < sizeof(outform))
- {
- strcpy(outform, argv[i]);
- strcat(outform, "\n" );
- }
- else
- {
- fprintf(stderr, "Format too long. Maximum is %lu characters. Ignored.\n", (unsigned long)sizeof(outform));
- }
- }
- else
- {
- fputs("No format specified after `-of'!\n", stderr);
- }
- }
- else if( !strcmp(argv[i], "-d" )) mode |= DEBUG;
- else if((!strcmp(argv[i], "-?" )) ||
- (!strcmp(argv[i], "--help"))) Usage(*argv);
- else
- {
- fprintf(stderr, "Unknown option `%s'\n\n", argv[i]);
- Usage(*argv);
- }
- }
- else
- {
- /* Process file */
-
- if(argv[i][0] == '?') Usage(*argv);
- else
- {
- if(clibname){
- FILE *file=fopen(clibname,"r");
- size_t size;
- if(!file){
- fprintf(stderr, "Couldn't open <%s>!\n",clibname);
- exit(EXIT_FAILURE);
- }
- if(fseek(file,0,SEEK_END)) exit(EXIT_FAILURE);
- size=ftell(file);
- if(fseek(file,0,SEEK_SET)) exit(EXIT_FAILURE);
- clibtext=malloc(size+1);
- if(!clibtext){
- fprintf(stderr,"Out of memory!\n");
- exit(EXIT_FAILURE);
- }
- clibtext[fread(clibtext,1,size,file)]=0;
- fclose(file);
- }
- ProcessFD(argv[i], mode, outdir, outform);
- if(clibname){
- free(clibtext);
- clibname=clibtext=0;
- }
- filesdone ++;
- }
- }
- }
- }
-
- if(!filesdone) ProcessFD(NULL, mode, outdir, outform);
-
- return(erg);
- }
-