/*  fd2lib  by Volker Barthelmann                         */
/*  rework 09/96 by Johnny Teveßen <j.tevessen@line.org>  */
/*  V1.4 14-Jul-98 phx: ##abi support.                    */ 
/*  V1.5 04-Oct-98 phx: FPU register support.             */
/*                      Allow function names > 28 chars.  */

#define NDEBUG

#ifdef _DCC
#  define CTYPE_NEAR
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

/* AmigaOS version string. Doesn't hurt... */
const char VersTag[] = "\0$VER: fd2lib 1.5 (04.10.98)";

char *clibname,*clibtext;

#define MAXLINELEN       1000
#define BUFFEREDLEN     16384   /* 1024 is standard */

#define NUMREGS        24

static const char *regnames[NUMREGS] =
{
  "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
  "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
  "fp0","fp1","fp2","fp3","fp4","fp5","fp6","fp7"
};

enum
{
  A0=0, A1, A2, A3, A4, A5, A6, A7,
  D0  , D1, D2, D3, D4, D5, D6, D7,
  FP0 ,FP1,FP2,FP3,FP4,FP5,FP6,FP7
};

#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, fsavecount;
  char function[80], tmpfuncnam[80], ff[MAXLINELEN+8], base[50];
  char line[MAXLINELEN], lastchar='A';
  register char *p;
  char *functionp;
  int reg[NUMREGS], loops, linenr = 0;
  BOOL public = -1;           /* Why FALSE? */
  BOOL abim68k = TRUE;
  BOOL fpregs;
  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 ('#'+'a'):
        if (!strncmp(line, "##abi", 5)) {               /* ##abi */
          p = line+5;
          while (isspace((unsigned)*p)) p++;
          if (!strncmp(p, "M68k", 4)) {
            abim68k = TRUE;
            if(mode & DEBUG)
              printf("ABI set to M68k\n");
          }
          else if (!strncmp(p, "PPC", 3)) {
            abim68k = FALSE;
            if(mode & DEBUG)
              printf("ABI set to PPC (WarpOS)\n");
          }
          else
            warnhim(linenr, "Unknown ##abi specification ignored!");
          continue;
        }
        break;

      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 || !abim68k) {
      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++) {
      int saveoffset;
      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);

      /* This should be AMIGA OS only: */
      if (strlen(functionp) > 28) {
        sprintf(ff, "%s%.27s%c.s", outdir, functionp, lastchar);
        warnhim(linenr,"File name %s.s is too long for AmigaOS file system."
                " Changed into: %.27s%c.s",functionp,functionp,lastchar);
        if (lastchar=='Z')
          lastchar = 'A';
        else
          lastchar++;
      }
      else
        sprintf(ff, "%s%s.s", outdir, functionp);

      /* Open function stub source file */

      printf(outform, ff, ff, ff);
      out = fopen(ff, "w");

      if(!out) ExitFailure("Could not create <%s>\n", functionp);
      setvbuf(out, NULL, _IOFBF, BUFFEREDLEN);

      /* 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 = fsavecount = 0;
      fpregs = FALSE;

      while((*p!=')') && *p) {
        char c = *p;

        /* Check whether register description is valid */
        if (!((c=='a' || c=='A' || c=='d' || c=='D') &&
              (p[1]>='0' && p[1]<='7')) &&
            !((c=='f' || c=='F') && (p[1]=='p' || p[1]=='P') &&
              (p[2]>='0' && p[2]<='7')))
            ExitFailure("Bad register description\n", NULL);

        /* Convert description to internal enum format */
        /* Corrected: 'A' was not recognized           */
        if (c=='a' || c=='A')
          j = p[1] - '0';
        else if (c=='d' || c=='D')
          j = p[1] - ('0'-8);
        else
          j = p[2] - ('0'-16);

        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>=FP0) {
          if (j>=FP2 && j<=FP7)
          fsavecount++;
        }
        else if (!(j==A0 || j==A1 || j==A6 || j==D0 || j==D1))
          savecount++;

        /* Search for next register */
        if (j >= FP0) {
          fpregs = TRUE;
          p += 3;
        }
        else
          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);
      }

      /* Write assembler headers */
      if (fpregs) fprintf(out,"\tfpu\t1\n");
      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);

      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 (fsavecount) {
        char c = '\t';
        /* number of FPU registers to be saved */

        if (!(mode & FASTCALL) && (fsavecount > 1))
          fprintf(out,"\tfmovem");

        for (i=FP2; i<=FP7; i++) {
          if (reg[i]) {
            if ((mode & FASTCALL) || (fsavecount == 1)) {
              fprintf(out,"\tfmove.x\t%s,-(%s)\n",
                      regnames[i],regnames[A7]);
            }
            else {
              fprintf(out,"%c%s",c,regnames[i]);
              c = '/';
            }
          }
        }
        if (!(mode & FASTCALL) && (fsavecount > 1))
          fprintf(out,",-(%s)\n",regnames[A7]);
      }

      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=A2; i<=D7; 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);

      saveoffset = ((savecount+2)<<2) + fsavecount*12;

      for(j=1; j<=count; j++)    /* Arguments */
      {
        for(i=0; i<NUMREGS; i++) /* Registers */
        {
          if(reg[i] == j)     /* Argument in THIS register? */
          {

            if (i >= FP0) {
              /* FPU registers fp0-fp7 */

              fprintf(out,(mode & NEWNOTATE) ?
                      "\tfmove.d\t(%d,%s),%s\n" : "\tfmove.d\t%d(%s),%s\n",
                      saveoffset, regnames[A7], regnames[i]);
              saveoffset += 8;
            }

            else {
              /* a0-a5 / d0-d7 */

              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 {
                  /* Varargs */
                  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]);
                  saveoffset += 4;
                }
                fputc('\n', out);
              }

              saveoffset += 4;
            } /* if FPU regs */

          } /* 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=A2; i<=D7; i++)
      {
        if((mode & FASTCALL) || (savecount == 1))
        {
          j = (D7+2) - 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]);
      }

      if (fsavecount) {
        char c = ',';
        /* number of FPU registers to be saved */

        if (!(mode & FASTCALL) && (fsavecount > 1))
          fprintf(out,"\n\tfmovem\t(%s)+",regnames[A7]);

        for (i=FP2; i<=FP7; i++) {
          if (reg[i]) {
            if ((mode & FASTCALL) || (fsavecount == 1)) {
              fprintf(out,"\n\tfmove.x\t(%s)+,%s",
                      regnames[A7],regnames[i]);
            }
            else {
              fprintf(out,"%c%s",c,regnames[i]);
              c = '/';
            }
          }
        }
      }


      /* 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.5  (c) by Volker Barthelmann / "
         "Johnny Teveßen / Frank Wille\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);
}
