/*
    fd2pragma.c

    This is a small little hack which converts fd-files to either
    pragmas readable by a C-Compiler of LVO files readable by an
    assembler. Use it as you want, but WITHOUT ANY WARRANTY!

    V1.2:   Added pragmas for the Dice compiler. Available via switch "Dice".
	    Added switches "Aztec", "SAS" and "Maxon": Maxon and Aztec just
	    turn on the default (except that Maxon expects pragma files to be
	    called "xxx_pragmas.h" instead of "xxx_lib.h"), SAS is equal to
	    Dice, except that SAS supports the pragma tagcall.

    V2.0:   Added support for tag functions. See the docs for details.

    Computer: Amiga 1200			Compiler: Aztec-C V5.0a
							  Dice 2.07.54 (3.0)

    Author:	Jochen Wiedmann
		Am Eisteich 9
	  72555 Metzingen (Germany)
		Tel. 07123 / 14881
		Internet: wiedmann@mailserv.zdv.uni-tuebingen.de
*/





/*
    Include files
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/libraries.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>

#ifdef AZTEC_C
#include <pragmas/exec_lib.h>
#include <pragmas/dos_lib.h>
#endif

#if defined(_DCC)  ||  defined(__SASC)  ||  defined(__MAXON)
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#endif

#ifdef __GNUC__
#include <inline/exec.h>
#include <inline/dos.h>
#endif





/*
    Constants
*/
#define MAXNAMELEN 128

const UBYTE VERSION[] = "$VER: fd2pragma 2.0 (11.03.94)   by  Jochen Wiedmann";
const UBYTE TEMPLATE[] =
    "FDFILE/A,AZTEC/K,AS/K,SAS/K,DICE/K,MAXON/K,TAGDIR/K,HELP/S";

const STRPTR RegNames[16] =
{ (STRPTR) "d0", (STRPTR) "d1", (STRPTR) "d2", (STRPTR) "d3",
  (STRPTR) "d4", (STRPTR) "d5", (STRPTR) "d6", (STRPTR) "d7",
  (STRPTR) "a0", (STRPTR) "a1", (STRPTR) "a2", (STRPTR) "a3",
  (STRPTR) "a4", (STRPTR) "a5", (STRPTR) "a6", (STRPTR) "a7"
};

const UBYTE HexDigits[16] = "0123456789ABCDEF";



/*
    This structure is used to represent the pragmas that are read.
*/
struct AmiPragma
{ struct MinNode Node;
  LONG Bias;
  LONG Public;
  STRPTR FuncName;
  STRPTR TagName;
  ULONG NumArgs;
  struct
  { STRPTR ArgName;
    ULONG ArgReg;
  } Args[14];	/*  a6 and a7 must not be used for function arguments	*/
};





/*
    Global variables
*/
struct MinList AmiPragmaList;
STRPTR BaseName;
STRPTR ShortBaseName;





/*
    This function works similar to strdup, but doesn't duplicate the
    whole string.

    Inputs: Str - the string to be duplicated
	    Len - the number of bytes to be duplicated

    Result: Pointer to the copy of the string or NULL.
*/
STRPTR strndup(const STRPTR Str, ULONG Len)

{ STRPTR result;

  if ((result = malloc(Len+1)))
  { memcpy(result, Str, Len);
    result[Len] = '\0';
  }
  return(result);
}





/*
    This function prints help information.
*/
void Usage(void)

{ fprintf(stderr, "\nUsage: fd2pragma %s\n\n", TEMPLATE);

  fprintf(stderr, "This program reads the given FDFILE and converts it ");
  fprintf(stderr, "into pragmas for\n");
  fprintf(stderr, "a C-Compiler (SAS, Dice, Aztec or Maxon) or LVO files ");
  fprintf(stderr, "for an\n");
  fprintf(stderr, "Assembler (Aztec-As).\n\n");
  fprintf(stderr, "TAGDIR is the name of a directory where to store stub ");
  fprintf(stderr, "routines for\n");
  fprintf(stderr, "pragma functions, if any are found. \"\" is the current ");
  fprintf(stderr, "directory.\n\n\n");
  fprintf(stderr, "%s\n\n", VERSION+6);
  fprintf(stderr,
	  "This is public domain, use it as you want, but WITHOUT ANY WARRANTY!\n");
  fprintf(stderr,
	  "Bugs and suggestions to wiedmann@mailserv.zdv.uni-tuebingen.de.\n\n");

  exit (1);
}





/*
    This function is used to skip over blanks.

    Inputs: OldPtr  - pointer to the beginning of a string.

    Result: Pointer to the first nonblank character of the string.
*/
STRPTR SkipBlanks(const STRPTR OldPtr)

{ STRPTR oldptr = OldPtr;

  while (*oldptr == ' '  ||  *oldptr == '\t')
  { ++oldptr;
  }
  return(oldptr);
}





/*
    This function is used to skip over vvariable names.

    Inputs: OldPtr  - pointer to the beginning of a string.

    Result: Pointer to the first character of the string, that is not one
	    of a-z, A-Z, 0-9 or the underscore.
*/
STRPTR SkipName(const STRPTR OldPtr)

{ STRPTR oldptr;
  UBYTE c;

  oldptr = OldPtr;
  while((c = *oldptr) == '_'  ||
	(c >= '0'  &&  c <= '9')  ||
	(c >= 'a'  &&  c <= 'z')  ||
	(c >= 'A'  &&  c <= 'Z'))
  { ++oldptr;
  }
  return(oldptr);
}





/*
    This function tells, that we ran out of memory.
*/
void MemError(void)

{ fprintf(stderr, "Fatal: Out of memory!\n");
}






/*
    This function is called to scan the FD file.

    Inputs: FDFile  - the name of the file to scan

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG ScanFDFile(const STRPTR FDFile)

{ FILE *fp;
  ULONG public = TRUE;
  ULONG bias = -1;
  ULONG linenum = 0;
  ULONG result = FALSE;
  UBYTE line[512];

  if (!(fp = fopen((char *) FDFile, "r")))
  { fprintf(stderr, "Fatal: Cannot open FD file %s.\n", FDFile);
    return(FALSE);
  }

  NewList((struct List *) &AmiPragmaList);

  while(fgets((char *) line, sizeof(line), fp)  !=  NULL)
  { ULONG len;

    ++linenum;

    for (len = 0;  len < sizeof(line);  ++len)
    { if (line[len] == '\n')
      { break;
      }
    }
    if (len == sizeof(line))
    { int c;

      fprintf(stderr, "Error: Line %ld too long.\n", linenum);
      while((c = getc(fp)) != EOF  &&  c != '\n')
      {
      }
      continue;
    }
    line[len] = '\0';   /*  Remove Line Feed    */

    if (line[0] == '*')
    { /*  Comment   */
      STRPTR ptr;

      ptr = SkipBlanks(line+1);
      if(strnicmp((char *) ptr, "tagcall", 7) == 0)  /*  Tag to create?  */
      { struct AmiPragma *prevpragma;

	ptr = SkipBlanks(ptr+7);

	prevpragma = (struct AmiPragma *) AmiPragmaList.mlh_TailPred;
	if (!prevpragma->Node.mln_Pred)
	{ fprintf(stderr,
		  "Line %ld, Error: Tag definition without preceding Pragma.\n",
		  linenum);
	  continue;
	}

	if (prevpragma->TagName)
	{ fprintf(stderr, "Line %ld, Error: Tag function declared twice.\n",
		  linenum);
	  continue;
	}

	if (!prevpragma->NumArgs)
	{ fprintf(stderr,
		  "Line %ld, Error: Tag function must have arguments.\n",
		  linenum);
	}

	/*
	    Get the tag functions name.
	*/
	len = strlen((char *) prevpragma->FuncName)+strlen((char *) ptr)+1;

	if (!(prevpragma->TagName = strndup(prevpragma->FuncName, len)))
	{ MemError();
	  goto exit_ScanFDFile;
	}

	if (!*ptr)
	{ len = strlen((char *) prevpragma->TagName);
	  if (prevpragma->TagName[len-1] == 'A')
	  { prevpragma->TagName[len-1] = '\0';
	  }
	}
	else
	{ STRPTR nextptr;

	  if (*ptr == '-')
	  { STRPTR removeptr;

	    ptr = SkipBlanks(ptr+1);
	    nextptr = SkipName(ptr);
	    if ((len = nextptr-ptr))
	    { removeptr = prevpragma->TagName+strlen((char *) prevpragma->TagName)-len;
	      if (!strncmp((char *) removeptr, (char *) ptr, len))
	      { fprintf(stderr,
			"Line %ld, Error: Cannot convert pragma name into tag name.\n",
			linenum);
		continue;
	      }
	      *removeptr = '\0';
	    }
	    ptr = SkipBlanks(nextptr);
	  }
	  if (*ptr == '+')
	  { ptr = SkipBlanks(ptr+1);
	  }
	  else
	  { *ptr = toupper((int) *ptr);
	  }

	  nextptr = SkipName(ptr);
	  len = nextptr-ptr;
	  if (len)
	  { strncat((char *) prevpragma->TagName, (char *) ptr, len);
	    ptr += len;
	  }

	  if (*SkipBlanks(nextptr))
	  { fprintf(stderr, "Line %ld, warning: Extra characters\n", linenum);
	  }
	}
      }
    }
    else if (strnicmp((char *) line, "##base", 6) == 0)
    { STRPTR ptr, nextptr;
      LONG len;

      if (BaseName)
      { fprintf(stderr, "Line %ld, Error: Basename declared twice.\n",
		linenum);
      }

      ptr = SkipBlanks(line+6);
      if (*ptr != '_')
      { fprintf(stderr, "Line %ld, Warning: Expected preceding _ in Basename.\n",
		linenum);
      }
      else
      { ++ptr;
      }
      nextptr = SkipName(ptr);
      if ((len = nextptr-ptr))
      { if (!(BaseName = strndup(ptr, len)))
	{ MemError();
	  goto exit_ScanFDFile;
	}

	ptr = FilePart(FDFile);
	len = strlen((char *) ptr)-7;
	if (len >= 0  &&  stricmp((char *) ptr+len, "_lib.fd") == 0)
	{ if (!(ShortBaseName = (STRPTR) strdup((char *) ptr)))
	  { MemError();
	    goto exit_ScanFDFile;
	  }
	  ShortBaseName[len] = '\0';
	}
	else
	{ if (!(ShortBaseName = (STRPTR) strdup((char *) BaseName)))
	  { MemError();
	    goto exit_ScanFDFile;
	  }
	  len = strlen((char *) ShortBaseName)-4;
	  if (len >= 0  &&  stricmp((char *) ShortBaseName+len, "base") == 0)
	  { ShortBaseName[len] = '\0';
	  }
	}

	if (*SkipBlanks(nextptr))
	{ fprintf(stderr, "Line %ld, warning: Extra characters\n", linenum);
	}
      }
      else
      { fprintf(stderr, "Line %ld, Error: Expected Basename.\n", linenum);
      }
    }
    else if (strnicmp((char *) line, "##bias", 6) == 0)
    { STRPTR ptr;
      LONG newbias;

      newbias = strtol((char *) line+6, (char **) &ptr, 0);
      if (ptr == line+6)
      { fprintf(stderr, "Line %ld, Error: Expected Bias value.\n", linenum);
      }
      else
      { if (newbias < 0)
	{ fprintf(stderr, "Line %ld, warning: Assuming positive value.\n",
		  linenum);
	  bias = -newbias;
	}
	else
	{ bias = newbias;
	}
      }
      if (*SkipBlanks(ptr))
      { fprintf(stderr, "Line %ld, warning: Extra characters\n", linenum);
      }
    }
    else if (strnicmp((char *) line, "##end", 5) == 0)
    { if (*SkipBlanks(line+5))
      { fprintf(stderr, "Line %ld, warning: Extra characters\n", linenum);
      }
      break;
    }
    else if (strnicmp((char *) line, "##public", 8) == 0)
    { if (*SkipBlanks(line+8))
      { fprintf(stderr, "Line %ld, warning: Extra characters\n", linenum);
      }
      public = TRUE;
    }
    else if (strnicmp((char *) line, "##private", 9) == 0)
    { if (*SkipBlanks(line+9))
      { fprintf(stderr, "Line %ld, warning: Extra characters\n", linenum);
      }
      public = FALSE;
    }
    else
    { STRPTR ptr, nextptr;
      struct AmiPragma *newpragma;
      ULONG len, numargs;

      ptr = SkipBlanks(line);
      nextptr = SkipName(ptr);
      if (!(len = nextptr-ptr))
      { fprintf(stderr, "Line %ld, Error: Missing function name\n", linenum);
	continue;
      }

      if (!(newpragma = calloc(sizeof(*newpragma), 1))  ||
	  !(newpragma->FuncName = strndup(ptr, len)))
      { MemError();
	goto exit_ScanFDFile;
      }

      if (*(ptr = SkipBlanks(nextptr)) != '(')
      { fprintf(stderr, "Line %ld, Error: Expected '('.\n", linenum);
	continue;
      }

      do
      { ptr = SkipBlanks(ptr+1);

	if (*ptr == ')' && newpragma->NumArgs == 0)
	{ break;
	}

	if (newpragma->NumArgs == 14)
	{ fprintf(stderr, "Line %ld, Error: Too much arguments.\n", linenum);
	}

	nextptr = SkipName(ptr);
	if (!(len = nextptr-ptr))
	{ fprintf(stderr, "Line %ld, Error: Expected argument name.\n",
		  linenum);
	  goto continue_loop;
	}

	if (!(ptr = strndup(ptr, len)))
	{ MemError();
	  goto exit_ScanFDFile;
	}
	newpragma->Args[newpragma->NumArgs++].ArgName = ptr;

	ptr = SkipBlanks(nextptr);
	if (*ptr != ','  &&  *ptr != '/'  &&  *ptr != ')')
	{ fprintf(stderr, "Line %ld, Error: Expected ')'.\n", linenum);
	  goto continue_loop;
	}
      }
      while (*ptr != ')');

      if (*(ptr = SkipBlanks(ptr+1)) != '(')
      { fprintf(stderr, "Line %ld, Error: Expected '('.\n", linenum);
	continue;
      }

      numargs = 0;
      do
      { ULONG i;

	ptr = SkipBlanks(ptr+1);

	if (*ptr == ')'  &&  numargs == 0)
	{ break;
	}

	if (numargs > newpragma->NumArgs)
	{ fprintf(stderr,
		  "Line %ld, Error: Number of arguments != number of registers.\n",
		  linenum);
	  goto continue_loop;
	}

	nextptr = SkipName(ptr);
	if (!(len = nextptr-ptr))
	{ fprintf(stderr, "Line %ld, Error: Expected register name.\n",
		  linenum);
	  goto continue_loop;
	}

	for (i = 0;  i < 16;  i++)
	{ if (strnicmp((char *) RegNames[i], (char *) ptr, len) == 0)
	  { break;
	  }
	}

	if (i > 16)
	{ fprintf(stderr, "Line %ld, Error: Expected register name.\n",
		  linenum);
	  goto continue_loop;
	}
	if (i > 14)
	{ fprintf(stderr,
		  "Line %ld, Error: %s not allowed as argument register.\n",
		  linenum, RegNames[i]);
	  goto continue_loop;
	}

	newpragma->Args[numargs].ArgReg = i;

	for (i = 0;  i < numargs;  i++)
	{ if (newpragma->Args[numargs].ArgReg == newpragma->Args[i].ArgReg)
	  { fprintf(stderr, "Line %ld, Error: Register %s used twice.\n",
		    linenum, RegNames[newpragma->Args[numargs].ArgReg]);
	    goto continue_loop;
	  }
	}
	++numargs;

	ptr = SkipBlanks(nextptr);
	if (*ptr != ','  &&  *ptr != '/'  &&  *ptr != ')')
	{ fprintf(stderr, "Line %ld, Error: Expected ')'.\n", linenum);
	  goto continue_loop;
	}
      }
      while (*ptr != ')');

      if (numargs < newpragma->NumArgs)
      { fprintf(stderr,
		"Line %ld, Error: Number of arguments != number of registers.\n",
		linenum);
	goto continue_loop;
      }

      if (bias == -1)
      { fprintf(stderr, "Line %ld, warning: Assuming bias of 30.\n",
		 linenum);
	bias = 30;
      }
      newpragma->Bias = bias;
      bias += 6;

      newpragma->Public = public;

      AddTail((struct List *) &AmiPragmaList, (struct Node *) newpragma);

      if (*SkipBlanks(ptr+1))
      { fprintf(stderr, "Line %ld, warning: Extra characters.\n", linenum);
      }
    }

continue_loop:
  ;
  }

  if (!BaseName)
  { fprintf(stderr, "Error: Missing Basename.\n");
  }
  else
  { result = TRUE;
  }

exit_ScanFDFile:
  fclose(fp);
  return(result);
}





/*
    This function is similar to puts, but converts the string to lowercase.

    Inputs: Str - the string to write
	    Fp	- the file where to write to

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG fputslower(const STRPTR Str, FILE *Fp)

{ STRPTR str = Str;

  while(*str)
  { if (putc(tolower((int) *str++), Fp) == EOF)
    { return(FALSE);
    }
  }
  return(TRUE);
}





/*
    This function is similar to puts, but converts the string to uppercase.

    Inputs: Str - the string to write
	    Fp	- the file where to write to

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG fputsupper(const STRPTR Str, FILE *Fp)

{ STRPTR str = Str;

  while(*str)
  { if (putc(toupper((int) *str++), Fp) == EOF)
    { return(FALSE);
    }
  }
  return(TRUE);
}





/*
    This function writes the header of a pragma file.

    Inputs: Fp	    - the file to write to.
	    Type    - Either "pragmas" or "lib", depending on the
		      typical pragma name. (Aztec uses something
		      like "pragmas/exec_pragmas.h" while SAS, Dice
		      and MAXON prefer "pragmas/exec_lib.h".)

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG WriteHeader(FILE *Fp, const STRPTR Type)

{
  if (fputs("#ifndef PRAGMAS_", Fp) < 0             ||
      !fputsupper(ShortBaseName, Fp)                ||
      (putc('_', Fp) == EOF)                        ||
      !fputsupper(Type, Fp)                         ||
      fputs("_H\n#define PRAGMAS_", Fp) < 0         ||
      !fputsupper(ShortBaseName, Fp)                ||
      (putc('_', Fp) == EOF)                        ||
      !fputsupper(Type, Fp)                         ||
      fputs("_H\n\n#ifndef CLIB_", Fp) < 0          ||
      !fputsupper(ShortBaseName, Fp)                ||
      fputs("_PROTOS_H\n#include <clib/", Fp) < 0   ||
      !fputslower(ShortBaseName, Fp)                ||
      fputs("_protos.h>\n#endif\n\n", Fp) < 0)
  { return(FALSE);
  }

  return(TRUE);
}





/*
    This function writes the footer of a pragma file.

    Inputs: Fp	    - the file to write to.
	    Type    - Either "pragmas" or "lib", depending on the
		      typical pragma name. (Aztec uses something
		      like "pragmas/exec_pragmas.h" while SAS, Dice
		      and MAXON prefer "pragmas/exec_lib.h".)

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG WriteFooter(FILE *Fp, const STRPTR Type)

{
  if (fputs("\n#endif\t/*  PRAGMAS_", Fp) < 0   ||
      !fputsupper(ShortBaseName, Fp)            ||
      (putc('_', Fp) == EOF)                    ||
      !fputsupper(Type, Fp)                     ||
      fputs("_H  */\n", Fp) < 0)
  { return(FALSE);
  }

  return(TRUE);
}





/*
    This function writes one pragma for Aztec-C.

    Inputs: Ap	- a pointer to the pragma which should be written.
	    Fp	- the file to write to

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG WriteAztecPragma(struct AmiPragma *Ap, FILE *Fp)

{ int i;

  if (!Ap->Public)
  { return(TRUE);
  }

  if (fprintf(Fp, "#pragma amicall(%s,0x%lx,%s(",
	      BaseName, Ap->Bias, Ap->FuncName) < 0)
  { return(FALSE);
  }

  for (i = 0;  i < Ap->NumArgs;  ++i)
  { if (!fputslower(RegNames[Ap->Args[i].ArgReg], Fp))
    { return(FALSE);
    }
    if (i+1 < Ap->NumArgs  &&  putc(',', Fp) == EOF)
    { return(FALSE);
    }
  }

  if (fputs("))\n", Fp) < 0)
  { return(FALSE);
  }

  return(TRUE);
}





/*
    This function writes one pragma for SAS-C.

    Inputs: Ap	    - a pointer to the pragma which should be written.
	    Fp	    - the file to write to
	    TagCall - TRUE for a tagcall pragma, FALSE for a libcall pragma

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG WriteSASPragma(struct AmiPragma *Ap, FILE *Fp, ULONG TagCall)

{ int i;

  if (!Ap->Public)
  { return(TRUE);
  }

  if (fprintf(Fp, "#pragma %s %s %s %lx ",
	      TagCall ? "tagcall" : "libcall",
	      BaseName,
	      TagCall ? Ap->TagName : Ap->FuncName,
	      Ap->Bias) < 0)
  { return(FALSE);
  }

  for (i = Ap->NumArgs-1;  i >= 0;  --i)
  { if ((fputc((int) HexDigits[Ap->Args[i].ArgReg], Fp) == EOF))
    { return(FALSE);
    }
  }

  if (fprintf(Fp, "0%lc\n", HexDigits[Ap->NumArgs]) < 0)
  { return(FALSE);
  }

  return(TRUE);
}





/*
    This function creates a pragmas file.

    Inputs: PragmaFile	- name of the file to be created.
	    PragmaExt	- TRUE, if the typical pragma filename is similar to
			  clib/exec_pragmas.h, FALSE for clib/exec_lib.h
	    SASPragmas	- TRUE for SAS-like pragmas, FALSE for Aztec
	    UseTags	- TRUE, if tagcall pragmas should be created.
	    Prototype	- TRUE, if a prototype for the library basepointer
			  should be created. (Dice seems to need this.)

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG CreatePragmaFile(const STRPTR PragmaFile, ULONG PragmaExt,
		       ULONG SASPragmas, ULONG UseTags, ULONG Prototype)

{ FILE *fp;
  struct AmiPragma *ap;
  ULONG result = FALSE;
  ULONG tagcall_seen = FALSE;

  if (!(fp = fopen((char *) PragmaFile, "w")))
  { fprintf(stderr, "Error: Cannot open %s for writing.\n", PragmaFile);
    return(FALSE);
  }

  if (!WriteHeader(fp, (STRPTR) (PragmaExt ? "pragmas" : "lib")))
  { goto exit_CreatePragmaFile;
  }

  if (Prototype  &&
      fprintf(fp, "extern struct Library *%s;\n\n", BaseName) < 0)
  { goto exit_CreatePragmaFile;
  }

  /*
      Write the pragmas.
  */
  for (ap = (struct AmiPragma *) AmiPragmaList.mlh_Head;
       ap->Node.mln_Succ;
       ap = (struct AmiPragma *) ap->Node.mln_Succ)
  { if ((SASPragmas  &&  !WriteSASPragma(ap, fp, FALSE))  ||
	(!SASPragmas  &&  !WriteAztecPragma(ap, fp)))
    { goto exit_CreatePragmaFile;
    }
    if (ap->TagName  &&  ap->Public)
    { tagcall_seen = TRUE;
    }
  }
  if (tagcall_seen  &&  UseTags)
  { if (fputs("\n#ifdef __SASC_60\n", fp) < 0)
    { goto exit_CreatePragmaFile;
    }

    for (ap = (struct AmiPragma *) AmiPragmaList.mlh_Head;
	 ap->Node.mln_Succ;
	 ap = (struct AmiPragma *) ap->Node.mln_Succ)
     { if (ap->TagName  &&  !WriteSASPragma(ap, fp, TRUE))
       { goto exit_CreatePragmaFile;
       }
     }

    if (fputs("#endif\t/*  __SASC_60  */\n", fp) < 0)
    { goto exit_CreatePragmaFile;
    }
   }

  /*
      Write the footer.
  */
  if (!WriteFooter(fp, (STRPTR) (PragmaExt ? "pragmas" : "lib")))
  { goto exit_CreatePragmaFile;
  }

  result = TRUE;

exit_CreatePragmaFile:
  if (!result)
  { fprintf(stderr, "Error while writing %s.\n", PragmaFile);
  }
  fclose(fp);
  return(result);
}





/*
    This function creates a LVO file.

    Inputs: LVOFile - the name of the file

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG CreateLVOFile(const STRPTR LVOFile)

{ FILE *fp;
  struct AmiPragma *ap;
  ULONG result;

  if (!(fp = fopen((char *) LVOFile, "w")))
  { fprintf(stderr, "Error: Cannot open %s for writing.\n", LVOFile);
    return(FALSE);
  }

  if (fputs("\t\tIFND ", fp) < 0            ||
      !fputsupper(ShortBaseName, fp)        ||
      fputs("_LIB_I\n", fp) < 0             ||
      !fputsupper(ShortBaseName, fp)        ||
      fputs("_LIB_I\tSET\t1\n\n", fp) > 0)
  { goto exit_CreateLVOFile;
  }

  for (ap = (struct AmiPragma *) AmiPragmaList.mlh_Head;
       ap->Node.mln_Succ;
       ap = (struct AmiPragma *) ap->Node.mln_Succ)
  { if (ap->Public  &&
	fprintf(fp, "\t\tXDEF\t_LVO%s\n", ap->FuncName) < 0)
    { goto exit_CreateLVOFile;
    }
  }

  if (fputs("\n\n", fp) < 0)
  { goto exit_CreateLVOFile;
  }

  for (ap = (struct AmiPragma *) AmiPragmaList.mlh_Head;
       ap->Node.mln_Succ;
       ap = (struct AmiPragma *) ap->Node.mln_Succ)
  { if (ap->Public  &&
	fprintf(fp, "_LVO%s\tEQU\t-%ld\n", ap->FuncName, ap->Bias) < 0)
    { goto exit_CreateLVOFile;
    }
  }

  if (fputs("\n\t\tEND", fp) < 0)
  { goto exit_CreateLVOFile;
  }

  result = TRUE;

exit_CreateLVOFile:
  if (!result)
  { fprintf(stderr, "Error while writing %s.\n", LVOFile);
  }
  fclose(fp);
  return(result);
}





/*
    This function creates stub routines for the tag functions.

    Inputs: TagDir  - a pointer to the directory, where to store the created
		      sources.

    Result: TRUE, if successful, FALSE otherwise
*/
ULONG CreateTagFuncs(const STRPTR TagDir)

{ FILE *fp;
  struct AmiPragma *ap;

  for (ap = (struct AmiPragma *) AmiPragmaList.mlh_Head;
       ap->Node.mln_Succ;
       ap = (struct AmiPragma *) ap->Node.mln_Succ)
  { if (ap->TagName  &&  ap->Public)
    { STRPTR sourcename;
      ULONG size = strlen((char *) TagDir) +
		   strlen((char *) ap->TagName) + 128;
      ULONG i;
      ULONG result = FALSE;

      /*
	  Get name of sourcefile to create.
      */
      if (!(sourcename = strndup(TagDir, size)))
      { MemError();
	return(FALSE);
      }
      AddPart(sourcename, ap->TagName, size);
      strcat((char *) sourcename, ".c");

      /*
	  Open sourcefile.
      */
      if (!(fp = fopen((char *) sourcename, "w")))
      { fprintf(stderr, "Error: Cannot open %s for writing.\n", sourcename);
	return(FALSE);
      }

      if (fputs("typedef unsigned long ULONG;\n", fp) < 0               ||
	  fprintf(fp, "extern struct Library *%s;\n", BaseName) < 0     ||
	  fprintf(fp, "extern ULONG %s(", ap->FuncName) < 0)
      { goto exit_CreateTagFuncs;
      }
      for(i = 0;  i < ap->NumArgs;  i++)
      { if (fputs("ULONG", fp) < 0)
	{ goto exit_CreateTagFuncs;
	}
	if (i+1 < ap->NumArgs  &&  fputs(", ", fp) < 0)
	{ goto exit_CreateTagFuncs;
	}
      }
      if (fputs(");\n\n", fp) < 0                                           ||
	  fputs("#if defined(AZTEC_C)  ||  defined(__MAXON__)\n", fp) < 0   ||
	  !WriteAztecPragma(ap, fp)                                         ||
	  fputs("#endif\n\n", fp) < 0                                       ||
	  fputs("#if defined(_DCC)  ||  defined(__SASC)\n", fp) < 0         ||
	  !WriteSASPragma(ap, fp, FALSE)                                    ||
	  fputs("#endif\n\n\n", fp) < 0                                     ||
	  fprintf(fp, "ULONG %s(", ap->TagName) < 0)
      { goto exit_CreateTagFuncs;
      }
      for (i = 0;  i < ap->NumArgs;  i++)
      { if (fprintf(fp, "ULONG %s, ", ap->Args[i].ArgName) < 0)
	{ goto exit_CreateTagFuncs;
	}
      }
      if (fputs("...)\n\n", fp) < 0                         ||
	  fprintf(fp, "{ return(%s(", ap->FuncName) < 0)
      { goto exit_CreateTagFuncs;
      }
      for (i = 0;  i < ap->NumArgs-1;  i++)
      { if (fprintf(fp, "%s, ", ap->Args[i].ArgName) < 0)
	{ goto exit_CreateTagFuncs;
	}
      }
      if (fprintf(fp, "(ULONG) &%s));\n}\n", ap->Args[ap->NumArgs-1].ArgName) < 0)
      { goto exit_CreateTagFuncs;
      }

      result = TRUE;

exit_CreateTagFuncs:
      if (!result)
      { fprintf(stderr, "Error while writing %s.\n", sourcename);
      }
      fclose(fp);
      free(sourcename);
      if (!result)
      { return(FALSE);
      }
    }
  }

  return(TRUE);
}





/*
    This is main().
*/
void main (int argc, char *argv[])

{ struct RDArgs *rdargs;
  struct
  { STRPTR FDFILE;
    STRPTR AZTEC;
    STRPTR AS;
    STRPTR SAS;
    STRPTR DICE;
    STRPTR MAXON;
    STRPTR TAGDIR;
    ULONG HELP;
  } args = {NULL, NULL, NULL, NULL, NULL, NULL, FALSE};
  extern struct Library *SysBase;

  if (!argc)        /*  Prevent calling from Workbench.     */
  { exit(-1);
  }

  if (SysBase->lib_Version < 36)
  { fprintf(stderr, "Need at least Kickstart 2.0.\n");
    exit(20);
  }

  if (!(rdargs = ReadArgs((STRPTR) TEMPLATE, (LONG *) &args, NULL)))
  { Usage();
  }

  if(args.HELP)
  { Usage();
  }

  if (ScanFDFile(args.FDFILE))
  { if ((args.AZTEC  &&
	 !CreatePragmaFile(args.AZTEC, FALSE, FALSE, FALSE, FALSE))         ||
	(args.AS  &&
	 !CreateLVOFile(args.AS))                                           ||
	(args.SAS  &&
	 !CreatePragmaFile(args.SAS, TRUE, TRUE, TRUE, FALSE))              ||
	(args.MAXON  &&
	 !CreatePragmaFile(args.MAXON, TRUE, FALSE, FALSE, FALSE))          ||
	(args.DICE  &&
	 !CreatePragmaFile(args.DICE, TRUE, TRUE, FALSE, TRUE))             ||
	(args.TAGDIR  &&
	 !CreateTagFuncs(args.TAGDIR)))
    { exit(5);
    }
  }
  exit(0);
}
