#define NAME	 "FD2Pragma"
#define VERSION  "2"
#define REVISION "44"
#define SDI_ENDCODE
//#define DEBUG
//#define DEBUG_OLD

/* Programmheader

	Name:		FD2Pragma
	Author: 	SDI
	Distribution:	PD
	Description:	creates pragmas files, lvo files and stub functions
	Compileropts:	-
	Linkeropts:	-gd
	Bugs:		CLIB scan does not recognize functions getting a
			function as parameter, CPP name for functions:
			'PF' <returnvalue> <parameters> 'p'

 1.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.
 2.0	: Added support for tag functions. See the docs for details.
	Author until this version:
	Jochen Wiedmann
	Am Eisteich 9
	72555 Metzingen (Germany)
	Tel. 07123 / 14881
	E-Mail: wiedmann@mailserv.zdv.uni-tuebingen.de
 2.1   19.08.96 : now made by SDI, added correct __MAXON__ support and
	support for StormC++, added auto recognition of tagcall functions
	changed the CLI interface completely
 2.2   21.08.96 : fixed a lot of errors, added debug code
 2.3   22.08.96 : little changes
 2.4   24.08.96 : added proto-file creation
 2.5   25.08.96 : added syscall and fix for functions ending in ...DMA
 2.6   26.08.96 : fixed some errors, added CLIB parameter (used later for
	CSTUBS)
 2.7   01.09.96 : added correct Storm definition, added CLIB scan
 2.8   02.09.96 : added assembler stub functions, added first ASM-stub code
 2.9   04.09.96 : added Comment-Support
 2.10  05.09.96 : changed CSTUB creation a bit
 2.11  07.09.96 : speeded up output, reduced number of strndup calls
 2.12  26.09.96 : pressing CTRL-C in early startup brought a wrong error
	message - fixed
 2.13  30.09.96 : made RegNames field to RegNames string - shorter Exe-file
 2.14  01.10.96 : made SPECIAL 6 default, COMMENT also in LVO files
 2.15  13.10.96 : corrected an error text
 2.16  14.10.96 : added correct comment support and PRIVATE option
 2.17  19.10.96 : now Maxon-compiled in Small data mode
 2.18  22.10.96 : removed EXTERNC in Storm, Maxon and all pragmas, corrected
	the texts, again SAS compiled
 2.19  26.10.96 : added option to create FD files out of pragma files,
	reworked a lot in the source
 2.20  27.10.96 : fixed errors of previous version
 2.21  28.10.96 : fixed error in CLIB scan
 2.22  27.11.96 : SPECIAL numbers for lib and ASM code were wrong, removed
	bug in Tag function stubs
 2.23  06.12.96 : lib and stub creation still was wrong
 2.24  31.12.96 : formed stub libs matching C++ file names, corrected CLIB
	scan errors
 2.25  04.01.97 : added HEADER option (I was asked for)
 2.26  05.01.97 : added HEADER scan (in old file) and auto inserting
 2.27  10.01.97 : stub functions missed register saving, outfuncs skip now,
	when error occured (makes lots of error checking obsolete)
 2.28  11.01.97 : forgot to add offset made by register saving
 2.29  18.01.97 : now libtags and amitags defines only, when at least 1
	tagfunc
 2.30  13.02.97 : added local library base functions, rearranged SPECIAL
		  options, fixed some bugs
 2.31  15.02.97 : corrected bugs inserted in previous version
 2.32  16.02.97 : and again bug fixes, still didn't work
 2.33  18.02.97 : corrected texts, added SPECIAL 28
 2.34  25.03.97 : corrected Pragma --> FD file conversion, added ##shadow
 2.35  26.03.97 : added STORMFD option, COMMENT, PRIVATE work again
 2.36  29.03.97 : corrected *tagcall scan a bit
 2.37  20.06.97 : added PASCAL stub lib production (SPECIAL 14, 15)
 2.38  01.07.97 : fixed ##end handling
 2.39  20.07.97 : added better proto file (__GNUC__ inline and pragma call),
	removed C++ comments
 2.40  24.11.97 : added new basenames to the list (devices and resources),
	added tag-exception name checking (dos, utility libraries)
 2.41  27.11.97 : fixed little bug with private functions, CSTUBS now
	special option and no longer commandline arg, SPECIAL 10-15 got
	numbers 11-16 (Sorry)
 2.42  28.11.97 : Added two new warnings for CLIB
 2.43  12.12.97 : faster FD file scan, one new warning
 2.44  19.12.97 : fixed MODE settings for SPECIAL 15,16
*/

#include <exec/memory.h>
#include <dos/doshunks.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>

#define SDI_TO_ANSI		/* Replaces ANSI functions with my ASM ones */
#include "SDI_ASM_STD_protos.h" /* You may use <stdio.h> and <stdlib.h> instead. */
#include "SDI_defines.h"
#include "SDI_structures.h"

#define TEXT_SAS	"__SASC"	/* verified	*/
#define TEXT_SAS_60	"__SASC_60"	/* verified	*/
#define TEXT_MAXON	"__MAXON__"	/* verified	*/
#define TEXT_STORM	"__STORM__"	/* verified	*/
#define TEXT_DICE	"_DCC"		/* in 2.0 code	*/
#define TEXT_AZTEC	"AZTEC_C"	/* verified	*/
#define TEXT_GNUC	"__GNUC__"	/* verified	*/

#define FLAG_EXTERNC	(1<<0)
#define FLAG_SYSCALL	(1<<1)
#define FLAG_DOCOMMENT	(1<<2)
#define FLAG_PRIVATE	(1<<3)
#define FLAG_LOCALREG	(1<<4)
#define FLAG_STORMFD	(1<<5)
#define FLAG_PASCAL	(1<<6)

#define MODUS_STUBTEXT	1
#define MODUS_STUBCODE	2
#define MODUS_LOCALDATA 3
#define MODUS_PRAGMA	4
#define MODUS_CSTUB	5
#define MODUS_LVO	10	/* and 11 and 12 and 13 		*/
#define MODUS_PROTO	20	/* and 21 and 22 and 23 and 24 and 25	*/
#define MODUS_ERROR	100

#define TAGMODE_NORMAL	0
#define TAGMODE_TAGS	1
#define TAGMODE_BOTH	2

#define BIAS_OFFSET	6

#define PARAM "FROM=FDFILE/A,SPECIAL/N,TO/K,COMMENT/S,MODE/N,AMICALL/K," \
	"LIBCALL/K,AMITAGS/K,LIBTAGS/K,EXTERNC/S,USESYSCALL/S," \
	"CLIB/K,PRIVATE/S,HEADER/K,STORMFD/S"

UBYTE RegNames[] =
"d0\0\0" "d1\0\0" "d2\0\0" "d3\0\0" "d4\0\0" "d5\0\0" "d6\0\0" "d7\0\0"
"a0\0\0" "a1\0\0" "a2\0\0" "a3\0\0" "a4\0\0" "a5\0\0" "a6\0\0" "a7";
/* double zero, to make the finding easier (by using <<2 instead of *3) */

struct ShortList {
  struct ShortList	*Next;
};

struct ShortListRoot {
  struct ShortList	*First;
  struct ShortList	*Last;
  ULONG 		Size;
};

struct AmiPragma {
  struct ShortList	List;
  UWORD 		Bias;
  UWORD 		Public;
  STRPTR		FuncName;
  STRPTR		TagName;
  UWORD 		NumArgs;
  struct AmiArgs
  {
    STRPTR		ArgName;
    UWORD		ArgReg;
  } Args[14];	/*  a6 and a7 must not be used for function arguments	*/
};

struct Comment {
  struct ShortList	List;
  STRPTR		Data;
  UWORD 		Bias;
};

struct PragList {
  struct ShortList	List;
  STRPTR		Basename;
  struct ShortListRoot	Data;
};

struct PragData {
  struct ShortList	List;
  ULONG 		NumNames;
  ULONG 		NumArgs;
  UBYTE 		ArgReg[14];
  ULONG 		Bias;
  struct ShortListRoot	Name;
};

struct FDData {
  STRPTR	Name;
  STRPTR	Basename;
  ULONG 	NumArgs;
  UBYTE 	ArgReg[14];
  ULONG 	Bias;
  ULONG 	Mode;		/* 0 = Normal, != 0 is TagName */
};

#define CPP_TYPE_VOID		'v'	/* void,	VOID	*/
#define CPP_TYPE_BYTE		'c'	/* char,	BYTE	*/
#define CPP_TYPE_WORD		's'	/* short,	WORD	*/
#define CPP_TYPE_LONG		'j'	/* long,	LONG	*/
#define CPP_TYPE_FLOAT		'f'	/* float,	FLOAT	*/
#define CPP_TYPE_DOUBLE 	'd'	/* double,	DOUBLE	*/
#define CPP_TYPE_STRUCTURE	0
#define CPP_TYPE_VARARGS	'e'

#define CPP_FLAG_UNSIGNED	(1<<0)
#define CPP_FLAG_CONST		(1<<1)
#define CPP_FLAG_STRPTR 	(1<<2)
#define CPP_FLAG_POINTER	(1<<3)
/* STRPTR is defined different under C and CPP -> I have to create two
names, one time unsigned char *, one time signed char *, when somewhere
a STRPTR occurs */

struct CPP_NameType {
  STRPTR	StructureName;	 /* if a structure only 	 */
  UWORD 	StructureLength; /* length of the structure name */
  UBYTE 	Type;		 /* see above defines		 */
  UBYTE 	Flags;		 /* see above flags		 */
  UWORD 	PointerDepth;	 /* number of * in type 	 */
};

struct CPP_TypeField {
  STRPTR	Text;
  UWORD 	Length;
  UBYTE 	Flags;
  UBYTE 	Type;
};

struct Proto_LibTypes {
  STRPTR	BaseName;
  STRPTR	StructureName;
};

struct Pragma_ExecpNames { /* TagName 0 is valid as well */
  STRPTR	FunctionName;
  STRPTR	TagName;
};

#define NTP_NORMAL	0	/* no tags/args 	*/
#define NTP_TAGS	1	/* TagFunction		*/
#define NTP_ARGS	2	/* ArgFunction		*/
#define NTP_UNKNOWN	3	/* CommentFunction	*/

struct NameList {
  struct ShortList	List;
  ULONG 		Type;	/* set by OptimizeFDData */
  STRPTR		NormName;
  STRPTR		PragName;
};

struct RDArgs	*rda			= 0;
struct SDI_InOut in			= {0,0,0,0,0},
		 out			= {0,0,5120,0,0};
STRPTR		clibbuf 		= 0;
ULONG		clibsize		= 0;
struct FileInfoBlock *fib		= 0;
struct ShortListRoot	AmiPragma	= {0,0,sizeof(struct AmiPragma)},
			Comment 	= {0,0,sizeof(struct Comment)};
struct Remember *remember		= 0;
ULONG		oldfh			= 0;
ULONG		lock			= 0;
STRPTR		BaseName		= 0;
STRPTR		ShortBaseName		= 0;
STRPTR		ShortBaseNameUpper	= 0;
ULONG		DosVersion		= 37; /* force OS2.0 */
ULONG		MODE			= 1;
STRPTR		AMICALL 		= 0;
STRPTR		LIBCALL 		= 0;
STRPTR		AMITAGS 		= 0;
STRPTR		LIBTAGS 		= 0;
STRPTR		HEADER			= 0;
ULONG		headersize		= 0;
ULONG		Flags			= 0;
struct IntuitionBase *IntuitionBase	= 0;
ULONG		Output_Error		= 1; /* Output error occured when 0 */
ULONG		tagfuncs		= 0; /* are there some tagfuncs in FD */
UBYTE		deftype[]		= "ULONG";

/* Prototypes for the functions */
STRPTR strndup(STRPTR, ULONG);
void DoError(UBYTE, ULONG, ...);
ULONG Out(ULONG);
ULONG DoOutput(STRPTR, ...);
ULONG DoOutputDirect(APTR, ULONG);
STRPTR SkipBlanks(STRPTR);
STRPTR SkipName(STRPTR);
ULONG MakeTagFunction(struct AmiPragma *);
void MakeLines(STRPTR, ULONG, ULONG);
ULONG ScanFDFile(void);
void FindHeader(void);
ULONG GetRegisterData(struct AmiPragma *);
ULONG OutputXDEF(STRPTR, ...);
/* ------------------------------------------------------------------ */
struct ShortList *NewItem(struct ShortListRoot *);
struct ShortList *RemoveItem(struct ShortListRoot *, struct ShortList *);
void		  AddItem(struct ShortListRoot *, struct ShortList *);
/* ------------------------------------------------------------------ */
ULONG FuncAMICALL(struct AmiPragma *, ULONG);
ULONG FuncLIBCALL(struct AmiPragma *, ULONG);
ULONG FuncAsmText(struct AmiPragma *, ULONG);
ULONG FuncAsmCode(struct AmiPragma *, ULONG);
ULONG FuncCSTUBS (struct AmiPragma *, ULONG);
ULONG FuncLVOXDEF(struct AmiPragma *, ULONG);
ULONG FuncLVO	 (struct AmiPragma *, ULONG);
ULONG FuncLocCode(struct AmiPragma *, ULONG);
ULONG FuncLocText(struct AmiPragma *, ULONG);
ULONG CallFunc(ULONG, STRPTR, ULONG (*) (struct AmiPragma *, ULONG));
/* ------------------------------------------------------------------ */
ULONG CopyCPPType(STRPTR, STRPTR, ULONG, STRPTR, struct AmiArgs *);
void GetCPPType(struct CPP_NameType *, STRPTR);
STRPTR FindClibFunc(STRPTR);	/* finds the needed function */
ULONG GetClibLength(STRPTR);	/* returns length of the type */
ULONG OutClibType(STRPTR);
STRPTR GetClibType(STRPTR);	/* searches for next type definition */
/* ------------------------------------------------------------------ */
ULONG CallPrag(ULONG, STRPTR, ULONG (*) (struct AmiPragma *, ULONG));
ULONG CreatePragmaFile(void);
ULONG CreateCSTUBS(void);
ULONG CreateLVOFile(ULONG);
ULONG CreateAsmStubs(ULONG);
ULONG CreateProtoFile(ULONG);
ULONG CreateLocalData(void);
/* ------------------------------------------------------------------ */
ULONG GetName(struct NameList *, struct ShortListRoot *, ULONG);
ULONG MakeFD(struct PragList *);
void OptimizeFDData(struct PragData *);
UBYTE GetHexValue(UBYTE);
ULONG AddFDData(struct ShortListRoot *, struct FDData *);
ULONG GetLibData(struct FDData *);
ULONG GetAmiData(struct FDData *);
ULONG CreateFDFile(STRPTR, STRPTR);
/* ------------------------------------------------------------------ */
void main(void);
void end(void);

STRPTR strndup(STRPTR Str, ULONG Len)
{
  STRPTR res;
  if((res = (STRPTR) AllocRemember(&remember, Len+1, MEMF_ANY|MEMF_CLEAR)))
  {
    CopyMem(Str, res, Len);
    res[Len] = '\0';
  }
  return res;
}

enum {
ERR_TAGFUNC_NEEDS_ARGUMENT,
ERR_CANNOT_CONVERT_PRAGMA_TAGCALL,
ERR_TAG_DEF_WITHOUT_PRAGMA,
ERR_BASENAME_DECLARED_TWICE,
ERR_EXPECTED_SLASH_IN_BASENAME,
ERR_EXPECTED_BASENAME,
ERR_EXPECTED_BIAS_VALUE,
ERR_ASSUMING_POSITIVE_BIAS_VALUE,
ERR_MISSING_FUNCTION_NAME,
ERR_EXPECTED_OPEN_BRACKET,
ERR_TO_MUCH_ARGUMENTS,
ERR_EXPECTED_ARGUMENT_NAME,
ERR_EXPECTED_CLOSE_BRACKET,
ERR_EXPECTED_REGISTER_NAME,
ERR_ILLEGAL_REGISTER,
ERR_REGISTER_USED_TWICE,
ERR_AGUMENTNUMBER_DIFFERS_FROM_REGISTERNUMBER,
ERR_ASSUMING_BIAS_OF_30,
ERR_EXTRA_CHARACTERS,
ERR_MISSING_BASENAME,
ERR_WRITING_FILE,
ERR_EXPECTED_COMMA,
ERR_DIFFERENT_TO_PREVIOUS,
ERR_UNKNOWN_VARIABLE_TYPE,
ERR_UNKNOWN_ERROR,
ERR_MISSING_END,
ERR_PROTOTYPE_MISSING,
ERR_NOPROTOTYPES_FILE,
ERR_UNKNOWN_DIRECTIVE,
};

struct ErrField {
  UBYTE  Type;	/* 0 = Error, 1 = Warning */
  UBYTE  Skip;
  STRPTR Error;
} Errors[] = {
1, 1, "Tag function must have arguments.",
1, 1, "Cannot convert pragma name into tag name.",
1, 1, "Tag definition without preceding Pragma.",
1, 0, "Basename declared twice.",
1, 0, "Expected preceding _ in Basename.",
1, 1, "Expected Basename.",
1, 0, "Expected Bias value.",
1, 0, "Assuming positive bias value.",
1, 1, "Missing function name.",
1, 1, "Expected '('.",
1, 1, "Too much arguments.",
1, 1, "Expected argument name.",
1, 1, "Expected ')'.",
1, 1, "Expected register name.",
1, 1, "A5 and A6 not allowed as argument register.",
1, 1, "Register used twice.",
1, 0, "Number of arguments != number of registers.",
1, 0, "Assuming bias of 30.",
1, 1, "Extra characters.",
0, 0, "Missing Basename.",
0, 0, "Failed to write destination file.",
1, 1, "Expected ','.",
1, 1, "Data different to previous given.",
1, 0, "Unknown type \"%s\", no C++ name for %s.",
0, 0, "Unknown problem: program error or corrupt input data.",
1, 0, "Missing ##end.",
1, 0, "Prototype for function \"%s\" not found.",
1, 0, "No prototypes file (CLIB parameter) was specified.",
1, 1, "Unknown directive '##%s' found.",
};

struct CPP_TypeField CPP_Field[] = {
{"long",	4, 0,			CPP_TYPE_LONG},
{"ULONG",	5, CPP_FLAG_UNSIGNED,	CPP_TYPE_LONG},
{"LONGBITS",	8, CPP_FLAG_UNSIGNED,	CPP_TYPE_LONG},
{"CPTR",	4, CPP_FLAG_UNSIGNED,	CPP_TYPE_LONG},
{"Tag", 	3, CPP_FLAG_UNSIGNED,	CPP_TYPE_LONG},
{"Object",	6, CPP_FLAG_UNSIGNED,	CPP_TYPE_LONG},
{"LONG",	4, 0,			CPP_TYPE_LONG},
{"BPTR",	4, 0,			CPP_TYPE_LONG},
{"BSTR",	4, 0,			CPP_TYPE_LONG},
{"short",	5, 0,			CPP_TYPE_WORD},
{"CxObj",	5, 0,			CPP_TYPE_LONG},
{"CxMsg",	5, 0,			CPP_TYPE_LONG},
{"USHORT",	6, CPP_FLAG_UNSIGNED,	CPP_TYPE_WORD},
{"UWORD",	5, CPP_FLAG_UNSIGNED,	CPP_TYPE_WORD},
{"UCOUNT",	6, CPP_FLAG_UNSIGNED,	CPP_TYPE_WORD},
{"WORDBITS",	8, CPP_FLAG_UNSIGNED,	CPP_TYPE_WORD},
{"RPTR",	4, CPP_FLAG_UNSIGNED,	CPP_TYPE_WORD},
{"SHORT",	5, 0,			CPP_TYPE_WORD},
{"COUNT",	5, 0,			CPP_TYPE_WORD},
{"WORD",	4, 0,			CPP_TYPE_WORD},
{"BOOL",	4, 0,			CPP_TYPE_WORD},
{"char",	4, 0,			CPP_TYPE_BYTE},
{"UBYTE",	5, CPP_FLAG_UNSIGNED,	CPP_TYPE_BYTE},
{"TEXT",	4, CPP_FLAG_UNSIGNED,	CPP_TYPE_BYTE},
{"BYTEBITS",	8, CPP_FLAG_UNSIGNED,	CPP_TYPE_BYTE},
{"BYTE",	4, 0,			CPP_TYPE_BYTE},
{"void",	4, 0,			CPP_TYPE_VOID},
{"VOID",	4, 0,			CPP_TYPE_VOID},
{"float",	5, 0,			CPP_TYPE_FLOAT},
{"FLOAT",	5, 0,			CPP_TYPE_FLOAT},
{"double",	6, 0,			CPP_TYPE_DOUBLE},
{"DOUBLE",	6, 0,			CPP_TYPE_DOUBLE},
{"STRPTR",	6, CPP_FLAG_POINTER|CPP_FLAG_STRPTR,	CPP_TYPE_BYTE},
{"APTR",	4, CPP_FLAG_POINTER,	CPP_TYPE_VOID},
{"ClassID",	7, CPP_FLAG_POINTER|CPP_FLAG_UNSIGNED,	CPP_TYPE_BYTE},
{"PLANEPTR",	8, CPP_FLAG_POINTER|CPP_FLAG_UNSIGNED,	CPP_TYPE_BYTE},
{"DisplayInfoHandle",17,CPP_FLAG_POINTER,CPP_TYPE_VOID},
{0,0,0,0},
};

struct Proto_LibTypes Proto_LibTypes[] = {
{"DOSBase",			"DosLibrary"},
{"SysBase",			"ExecBase"},
{"ExpansionBase",		"ExpansionBase"},
{"GfxBase",			"GfxBase"},
{"IntuitionBase",		"IntuitionBase"},
{"LocaleBase",			"LocaleBase"},
{"MathIeeeDoubBasBase", 	"MathIEEEBase"},
{"MathIeeeDoubTransBase",	"MathIEEEBase"},
{"MathIeeeSingBasBase", 	"MathIEEEBase"},
{"MathIeeeSingTransBase",	"MathIEEEBase"},
{"RealTimeBase",		"RealTimeBase"},
{"RexxSysBase", 		"RxsLib"},
{"UtilityBase", 		"UtilityBase"},
/* resources - The Node entries may be correct, but I don't know it. */
/*	{"BattClockBase",		"Node"}, */
/*	{"BattMemBase",			"Node"}, */
{"DiskBase",			"DiskResource"},
/*	{"MiscBase",			"Node"}, */
/*	{"PotgoBase",			"Node"}, */
/* devices */
{"ConsoleDevice",		"Device"},
{"InputBase",			"Device"},
{"RamdriveDevice",		"Device"},
{"TimerBase",			"Device"},
/* non default Basenames */
{"DatamasterBase",		"DatamasterBase"},
{"PPBase",			"PPBase"},
{"ReqToolsBase",		"ReqToolsBase"},
{"UnpackBase",			"UnpackLibrary"},
{"xfdMasterBase",		"xfdMasterBase"},
{"GTXBase",			"GTXBase"},
{0, 0},
};

/* CachePostDMA, CachePreDMA are done by #?DMA check */
struct Pragma_ExecpNames Pragma_ExecpNames[] = {
{"VFWritef",			"FWritef"},
{"VFPrintf",			"FPrintf"},
{"VPrintf",			"Printf"},
{"ReadArgs",			0},
{"FreeArgs",			0},
{"CloneTagItems",		0},
{"FindTagItem",			0},
{"FreeTagItems",		0},
{"GetTagData",			0},
{"PackBoolTags",		0},
{"PackStructureTags",		0},
{"UnpackStructureTags",		0},
{0,0},
};

void DoError(UBYTE errnum, ULONG line, ...)
{
  STRPTR *arguments = (STRPTR *) (((STRPTR) &line) + sizeof(ULONG));
  struct {
    STRPTR type;
    ULONG typenum;
    ULONG linenum;
  } args;

  args.type = Errors[errnum].Type ? "Warning" : "Error";
  args.typenum = errnum;
  args.linenum = line;

  VPrintf((line ? "%s %ld in line %ld: " : "%s %ld : "), &args);
  VPrintf(Errors[errnum].Error, arguments);
  PutStr("\n");
  if(line && Errors[errnum].Skip)
  {
    while(*in.pos)
      ++in.pos;
  }
}

ULONG Out(ULONG size)
{
  ULONG i = out.pos-out.buf;

  if(i && out.size - i <= size)
  {
    if(Write(out.file, out.buf, i) != i)
    {
      Output_Error = 0;
      return 0;
    }
    out.count += i;
    out.pos = out.buf;
  }
  return out.size;
}

#ifdef __MAXON__
  #define __asm
  #define __saveds
  #include <linkerfunc.h>
#endif

static void __asm __saveds putfunc(register __d0 UBYTE data,
register __a3 LONG *a)
{
#ifdef __MAXON__
  GetBaseReg();
#endif

  if(data)
  {
    if(!*a)
      *a = Out(1);
    if((*a)-- > 0)
      *(out.pos++) = data;
  }
}

typedef void (*putchtype) ();

ULONG DoOutput(STRPTR format, ...)
{
  LONG a = out.buf+out.size-out.pos;

  if(!Output_Error)
    return 0;

  RawDoFmt(format, (APTR) ((ULONG)&format+sizeof(STRPTR)),
  (putchtype) putfunc, &a);

  if(a < 0)
    return 0;
  return 1;
}

ULONG DoOutputDirect(APTR data, ULONG size)
{
  if(!Output_Error || !Out(size))
    return 0;
  CopyMem(data, out.pos, size);
  out.pos += size;
  return 1;
}

STRPTR SkipBlanks(STRPTR OldPtr)
{
  while(*OldPtr == ' ' || *OldPtr == '\t')
    ++OldPtr;
  return OldPtr;
}

/*
    This function is used to skip over variable 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(STRPTR OldPtr)
{
  while(isalnum(*OldPtr) || *OldPtr == '_')
    ++OldPtr;
  return OldPtr;
}

ULONG MakeTagFunction(struct AmiPragma *ap)
{
  ULONG len = strlen(ap->FuncName), i=0;

#ifdef DEBUG_OLD
  VPrintf("MakeTagFunction:\n", 0);
#endif

  ++tagfuncs;

  while(Pragma_ExecpNames[i].FunctionName && /* check the exception names */
  strcmp(ap->FuncName, Pragma_ExecpNames[i].FunctionName))
    ++i;

  if(Pragma_ExecpNames[i].FunctionName)
  {
    if(!(ap->TagName = Pragma_ExecpNames[i].TagName));
      --tagfuncs;
  }
  else if(ap->FuncName[len-1] == 'A')
  {
    if(!strcmp(ap->FuncName+len-3, "DMA")) /* skip names with DMA at end */
    { --tagfuncs; return 1;}
    if(!(ap->TagName = strndup(ap->FuncName, len-1)))
      return 0;
  }
  else if(!strcmp(ap->FuncName + len-7, "TagList"))
  {
    if(!(ap->TagName = strndup(ap->FuncName, len-3)))
      return 0;
    ap->TagName[len-4] = 's';
  }
  else if(!strcmp(ap->FuncName + len-4, "Args"))
  {
    if(!(ap->TagName = strndup(ap->FuncName, len-4)))
      return 0;
  }
  else if(!stricmp(ap->Args[ap->NumArgs-1].ArgName, "tags") ||
  !stricmp(ap->Args[ap->NumArgs-1].ArgName, "taglist"))
  {
    if(!(ap->TagName = strndup(ap->FuncName, len+4)))
      return 0;
    CopyMem("Tags", ap->TagName + len, 5);
  }
  else if(!stricmp(ap->Args[ap->NumArgs-1].ArgName, "args"))
  {
    if(!(ap->TagName = strndup(ap->FuncName, len+4)))
      return 0;
    CopyMem("Args", ap->TagName + len, 5);
  }
  else
    --tagfuncs; /* not a tagfunction, incrementing was false, undo it */

  return 1;
}

void MakeLines(STRPTR buffer, ULONG size, ULONG test)
{
  ULONG a = 0;
  if(size && buffer)
  {
    /* make a real C++ zero string ending line */
    while(--size)
    {
      switch(*buffer)
      {
      case '(': ++a; break;
      case ')': --a; break;
      case '\n':
	if(!test || !a)
	  *buffer = '\0';
	else
	  *buffer = ' ';
	break;
      }
      ++buffer;
    }
    *buffer = '\0';
  }
}

ULONG ScanFDFile(void)
{
#ifdef DEBUG_OLD
  VPrintf("ScanFDFile:\n", 0);
#endif

  ULONG _public = 1;
  LONG bias = -1;
  ULONG linenum, len;
  ULONG actcom = 0;

  for(linenum = 1; !CTRL_C && in.pos < in.buf + in.size; ++linenum)
  {
    if(*in.pos == '*')	     /*  Comment   */
    {
      STRPTR oldpos = in.pos;
#ifdef DEBUG_OLD
  VPrintf("ScanFDFile: found a comment\n", 0);
#endif
      in.pos = SkipBlanks(in.pos+1);
      if(!strnicmp(in.pos, "tagcall", 7))  /*  Tag to create?  */
      {
	struct AmiPragma *prevpragma = (struct AmiPragma *)
		AmiPragma.Last;

	in.pos = SkipBlanks(in.pos + 7);
	if(!prevpragma)
	{
	  DoError(ERR_TAG_DEF_WITHOUT_PRAGMA, linenum);
	  ++in.pos;
	  continue;
	}

	if(!prevpragma->NumArgs)
	{
	  DoError(ERR_TAGFUNC_NEEDS_ARGUMENT, linenum);
	  ++in.pos;
	  continue;
	}

	/* Get the tag functions name. */

	if(!prevpragma->TagName && (_public || (Flags & FLAG_PRIVATE)))
	  ++tagfuncs;

	if(*in.pos)
	{
	  STRPTR oldptr, tptr = prevpragma->TagName;

	  len = strlen(prevpragma->FuncName)+strlen(in.pos)+1;
	  if(!(prevpragma->TagName = strndup(prevpragma->FuncName, len)))
	    return 0;

	  if(*in.pos == '-')
	  {
	    STRPTR removeptr;

	    oldptr = in.pos = SkipBlanks(in.pos+1);
	    in.pos = SkipName(in.pos);
	    if((len = in.pos-oldptr))
	    {
	      removeptr = prevpragma->TagName+strlen(prevpragma->TagName)-len;
	      if(strncmp(removeptr, oldptr, len))
	      {
#ifdef DEBUG_OLD
		{
		  struct data {STRPTR a;STRPTR b;ULONG c;} a;
		  a.a = removeptr;
		  a.b = oldptr;
		  a.c = len;
		  VPrintf("ScanFDFile: *tagcall -: %s, %s, %ld\n", &a);
		}
#endif
		DoError(ERR_CANNOT_CONVERT_PRAGMA_TAGCALL, linenum);
		prevpragma->TagName = tptr;
		++in.pos;
		continue;
	      }

	      *removeptr = '\0';
	    }
	    in.pos = SkipBlanks(in.pos);
	  }
	  if(*in.pos == '+')
	    in.pos = SkipBlanks(in.pos+1);
	  else
	    *in.pos = toupper(*in.pos);

	  in.pos = SkipName((oldptr = in.pos));
	  len = in.pos-oldptr;
	  if(len)
	  {
	    ULONG a = strlen(prevpragma->TagName);
	    CopyMem(oldptr, prevpragma->TagName+a, len);
	    prevpragma->TagName[a+len] = '\0';
	  }
	}
	else if(!prevpragma->TagName)
	{
	  len = strlen(prevpragma->FuncName);
	  if(!(prevpragma->TagName = strndup(prevpragma->FuncName, len+4)))
	    return 0;
	  CopyMem("Tags", prevpragma->TagName + len, 5);
	}
      }
      else
      {
	if(actcom)
	  *(oldpos-1) = '\n';
	else if(Flags & FLAG_DOCOMMENT)
	{
	  struct Comment *d;
	  if(!(d = (struct Comment *) NewItem(&Comment)))
	    return 0;
	  d->Bias = bias;
	  d->Data = oldpos;
	  AddItem(&Comment, (struct ShortList *) d);
	  actcom = 1;
	}
	while(*in.pos)
	  ++in.pos;
      }
    }
    else if(*in.pos == '#' && in.pos[1] == '#')
    {
      in.pos += 2;
      actcom = 0; /* no Comment */

      if(!strnicmp(in.pos, "base", 4))
      {
#ifdef DEBUG_OLD
  VPrintf("ScanFDFile: found ##base\n", 0);
#endif
        STRPTR oldptr;

        if(BaseName)
	  DoError(ERR_BASENAME_DECLARED_TWICE, linenum);

        in.pos = SkipBlanks(in.pos+4);
        if(*in.pos != '_')
	  DoError(ERR_EXPECTED_SLASH_IN_BASENAME, linenum);
        else
	  ++in.pos;

        in.pos = SkipName((oldptr = in.pos));
        if((len = in.pos-oldptr))
        {
	  if(!(BaseName = strndup(oldptr, len)))
	    return 0;
	  if(!ShortBaseName && !(ShortBaseName = strndup(BaseName, len-4)))
	    return 0;

	  if(!(ShortBaseNameUpper = strndup(ShortBaseName, strlen(ShortBaseName))))
	    return 0;
	  else
	  {
	    STRPTR a = ShortBaseNameUpper;
	    while((*a = toupper(*a)))	/* Convert to uppercase */
	      a++;
	  }
        }
        else
	  DoError(ERR_EXPECTED_BASENAME, linenum);
      }
      else if(!strnicmp(in.pos, "bias", 4))
      {
#ifdef DEBUG_OLD
  VPrintf("ScanFDFile: found ##bias\n", 0);
#endif
        STRPTR ptr;
        LONG newbias;

        in.pos += 5;
        newbias = strtol(in.pos, &ptr, 10);
        if(ptr == in.pos)
	  DoError(ERR_EXPECTED_BIAS_VALUE, linenum);
        else if(newbias < 0)
        {
	  DoError(ERR_ASSUMING_POSITIVE_BIAS_VALUE, linenum);
	  bias = -newbias;
        }
        else
	  bias = newbias;
        in.pos = SkipName(in.pos);
      }
      else if(!strnicmp(in.pos, "end", 3))
      {
        bias = 0; break;
      }
      else if(!strnicmp(in.pos, "shadow", 6)) /* introduced by Storm */
      {
        in.pos += 6;
        bias -= BIAS_OFFSET;
      }
      else if(!strnicmp(in.pos, "public", 6))
      {
        in.pos += 6;
        _public = 1;
      }
      else if(!strnicmp(in.pos, "private", 7))
      {
        in.pos += 7;
        _public = 0;
      }
      else
        DoError(ERR_UNKNOWN_DIRECTIVE, linenum, in.pos);
    }
    else
    {
#ifdef DEBUG_OLD
  VPrintf("ScanFDFile: scan Function\n", 0);
#endif
      STRPTR oldptr;
      struct AmiPragma ap;
      ULONG numargs;

      memset(&ap, 0, sizeof(struct AmiPragma));
      actcom = 0;

      oldptr = in.pos = SkipBlanks(in.pos);
      in.pos = SkipName(oldptr);
      if(!(len = in.pos-oldptr))
      {
	DoError(ERR_MISSING_FUNCTION_NAME, linenum);
	++in.pos;
	continue;
      }

      ap.FuncName = oldptr;

      in.pos = SkipBlanks(in.pos);
      if(*in.pos != '(')
      {
	DoError(ERR_EXPECTED_OPEN_BRACKET, linenum);
	++in.pos;
	continue;
      }

      *in.pos = '\0'; /* create c string of FunctionName */

#ifdef DEBUG_OLD
  VPrintf("ScanFDFile: found function %s\n", &ap.FuncName);
#endif

      do
      {
	oldptr = in.pos = SkipBlanks(in.pos+1);

	if(*in.pos == ')' && !ap.NumArgs)
	  break;

	if(ap.NumArgs == 14)
	{
	  DoError(ERR_TO_MUCH_ARGUMENTS, linenum); break;
	}

	in.pos = SkipName(oldptr);
	if(*in.pos == '*')
	  ++in.pos;
	if(!(len = in.pos-oldptr))
	{
	  DoError(ERR_EXPECTED_ARGUMENT_NAME, linenum);
	  break;
	}

	ap.Args[ap.NumArgs++].ArgName = oldptr;
	oldptr = in.pos;

	in.pos = SkipBlanks(in.pos);
	if(*in.pos != ',' && *in.pos != '/' && *in.pos != ')')
	{
	  DoError(ERR_EXPECTED_CLOSE_BRACKET, linenum);
	  break;
	}
	if(*in.pos != ')') /* create c string ending */
	  *oldptr = '\0';
      }
      while(*in.pos != ')');
      if(*in.pos != ')')
      {
	while(*(in.pos++))
	  ++in.pos;
	continue;
      }
      else
	*oldptr = '\0'; /* create c string ending for last argument */

      if(*(in.pos = SkipBlanks(in.pos+1)) != '(')
      {
	DoError(ERR_EXPECTED_OPEN_BRACKET, linenum);
	++in.pos;
	continue;
      }

      numargs = 0;
      do
      {
	ULONG i;

	oldptr = in.pos = SkipBlanks(in.pos + 1);

	if(*in.pos == ')' && numargs == 0)
	  break;

	in.pos = SkipName(oldptr);
	len = in.pos-oldptr;

	for(i = 0; i < 16; i++)
	  if(!strnicmp(&RegNames[i<<2], oldptr, len))
	    break;

	if(i == 16)
	{
	  DoError(ERR_EXPECTED_REGISTER_NAME, linenum);
	  break;
	}
	if(i > 14)
	{
	  DoError(ERR_ILLEGAL_REGISTER, linenum);
	  break;
	}

	ap.Args[numargs].ArgReg = i;

	for(i = 0; i < numargs; i++)
	{
	  if(ap.Args[numargs].ArgReg == ap.Args[i].ArgReg)
	  {
	    DoError(ERR_REGISTER_USED_TWICE, linenum);
	    break;
	  }
	}
	if(i < numargs)
	  break;

	++numargs;

	in.pos = SkipBlanks(in.pos);
	if(*in.pos != ',' && *in.pos != '/' && *in.pos != ')')
	{
	  DoError(ERR_EXPECTED_CLOSE_BRACKET, linenum);
	  break;
	}
      }
      while(*in.pos != ')');
      if(*in.pos != ')')
      {
	while(*(in.pos++))
	  ++in.pos;
	continue;
      }
      else
	++in.pos;

      if(Flags & FLAG_STORMFD)
      {
	if(!strcmp(ap.Args[ap.NumArgs-1].ArgName, "tags"))
	{
	  ap.TagName = ap.FuncName;
	  ap.FuncName = 0;
	  ++tagfuncs;
#ifdef DEBUG_OLD
	  VPrintf("ScanFDFile: StormFD mode, tag func: %s\n", &ap.TagName);
#endif
	}
#ifdef DEBUG_OLD
	else
	{
	  STRPTR a[2];
	  a[0] = ap.FuncName;
	  a[1] = ap.Args[ap.NumArgs-1].ArgName;
	  VPrintf("ScanFDFile: StormFD mode, normal func: %s(..., %s)\n", &a);
	}
#endif
      }
      else if((_public || (Flags & FLAG_PRIVATE)) && !MakeTagFunction(&ap))
	return 0;

      if(numargs != ap.NumArgs)
      {
	DoError(ERR_AGUMENTNUMBER_DIFFERS_FROM_REGISTERNUMBER, linenum);
	ap.NumArgs = numargs;
      }

      if(bias == -1)
      {
	DoError(ERR_ASSUMING_BIAS_OF_30, linenum);
	bias = 30;
      }
      ap.Bias = bias;
      bias += BIAS_OFFSET;

      ap.Public = _public;

      {
	struct AmiPragma *d;
	if(!(d = (struct AmiPragma *) NewItem(&AmiPragma)))
	  return 0;
	CopyMem(&ap, d, sizeof(struct AmiPragma));
	AddItem(&AmiPragma, (struct ShortList *) d);
      }
    }

    in.pos = SkipBlanks(in.pos);
    if(*in.pos)
      DoError(ERR_EXTRA_CHARACTERS, linenum);
    ++in.pos; /* skip '\0' */
  }

  if(CTRL_C)
    return 0;

  if(!BaseName)
  {
    DoError(ERR_MISSING_BASENAME, 0); return 0;
  }
  else if(bias)
    DoError(ERR_MISSING_END, 0);

  return 1;
}

void FindHeader(void)
{
  STRPTR str = HEADER;
  ULONG mode = 0;

  do
  {
    if(!mode)
      HEADER = str;

    if(*str == '/')
    {
      ++str;
      if(*str == '*')
      {
	mode = 2; break;
      }
      else if(*str == '/')
	mode = 1;
    }
    else if(*str == '*' || *str == ';')
      mode = 1;
    else if(mode)
      break;
    while(*str && *(str++) != '\n')
      ;
  } while(*str);

  if(mode == 2)
  {
    while(*str && (*(str-1) != '*' || *str != '/'))
      ++str;
    while(*str && *(str++) != '\n')
      ;
  }

  if(mode)
    headersize = str-HEADER;
  else
  {
    HEADER = 0; headersize = 0;
  }
}

/* returns decrement data in bits 0-15 and increment data in bits 16-31 */
ULONG GetRegisterData(struct AmiPragma *ap)
{
/* usage of result:
	48E7 <lower word>	MOVEM.L <registers>,-(A7) ; D0 is bit 15
	4CDF <upper word>	MOVEM.L (A7)+,<registers> ; D0 is bit 0
*/
  register ULONG i, data = 0, reg;

  for(i = 0; i < ap->NumArgs; ++i)
  {
    reg = ap->Args[i].ArgReg;

    if(reg >= 10 || (reg >= 2 &&  reg <= 7)) /* A2-A7 and D2-D7 */
      data |= (1 << (reg + 16)) + (1 << (15 - reg));
  }
  if(data)	/* set A6 only when other register used */
    data |= 0x40000002;
  return data;
}

static void __asm __saveds xdefputfunc(register __d0 UBYTE data,
register __a3 STRPTR *a)
{
  *((*a)++) = data;
}

ULONG OutputXDEF(STRPTR format, ...)
{
  UBYTE b[150];
  STRPTR c = b + 4;
  UWORD i;

  memset(b, 0, 150);
  RawDoFmt(format, (APTR) ((ULONG)&format+sizeof(STRPTR)),
  (putchtype) xdefputfunc, &c);
  /* c now holds pointer to end */
  i = (c-(b+4)+2)>>2;
  *((ULONG *) b) = 0x01000000 + i;

  return DoOutputDirect(b, (i+2)<<2);
}

/* ------------------------------------------------------------------ */

struct ShortList *NewItem(struct ShortListRoot *list)
{
  struct ShortList *item;
  if(!list || !list->Size)
    return 0;
  if(!(item = (struct ShortList *) AllocRemember(&remember, list->Size,
  MEMF_ANY|MEMF_CLEAR)))
    return 0;
  return item;
}

struct ShortList *RemoveItem(struct ShortListRoot *list, struct ShortList *item)
{
  struct ShortList *n = list->First;

  if(n == item)
    list->First = item->Next;
  else
  {
    while(n && n->Next != item)
      n = n->Next;
    if(!n)
      return 0;
    if(!(n->Next = item->Next))
      list->Last = n;
  }
  item->Next = 0;
  return item;
}

void AddItem(struct ShortListRoot *list, struct ShortList *item)
{
  if(!list->First)
    list->First = list->Last = item;
  else
  {
    list->Last->Next = item;
    list->Last = item;
  }
}

/* ------------------------------------------------------------------ */

ULONG FuncAMICALL(struct AmiPragma *Ap, ULONG tagmode)
{
  ULONG i;

  DoOutput("#pragma %s(%s,0x%03lx,%s("/*))*/, tagmode ? "tagcall" :
  "amicall", BaseName, Ap->Bias, tagmode ? Ap->TagName : Ap->FuncName);

  for(i = 0; i < Ap->NumArgs; ++i)
  {
    DoOutput(&RegNames[(Ap->Args[i].ArgReg)<<2]);
    if(i+1 < Ap->NumArgs)
      DoOutput(",");
  }

  return DoOutput(/*((*/"))\n");
}

ULONG FuncLIBCALL(struct AmiPragma *Ap, ULONG tagmode)
{
  LONG i;

  if((Flags & FLAG_SYSCALL) && !strcmp(BaseName,"SysBase"))
    DoOutput("#pragma syscall %-20s %03lx ", Ap->FuncName, Ap->Bias);
  else
    DoOutput("#pragma %s %s %-20s %03lx ", tagmode ? "tagcall" :
    "libcall", BaseName, tagmode ? Ap->TagName : Ap->FuncName, Ap->Bias);

  for(i = Ap->NumArgs-1; i >= 0; --i)
    DoOutput("%lx",Ap->Args[i].ArgReg);

  return DoOutput("0%lx\n", Ap->NumArgs);
}

ULONG FuncAsmText(struct AmiPragma *ap, ULONG tagmode)
{
  STRPTR text = tagmode ? ap->TagName : ap->FuncName, a;
  LONG i;
  ULONG registers;
  ULONG offset = 1;

  DoOutput("\n\tXDEF\t_%s\n_%s:\n",text, text);
  if(!(Flags & FLAG_PASCAL))
  {
    DoOutput("\n\tXDEF\t%s\n%s:\n",text, text);
    if(!ap->NumArgs && clibbuf)
      DoOutput("\tXDEF\t%s_\n%s_:\n",text, text);
    else if((a = FindClibFunc(text)))
    {
      UBYTE name[300];
      ULONG ret = 0;

      do
      {
	if((ret = CopyCPPType(name, a, ret, text, ap->Args)))
	  DoOutput("\tXDEF\t%s__%s\n%s__%s:\n",text, name, text, name);
      } while(ret == 0xFFFFFFFF);
    }
  }

  if((registers = GetRegisterData(ap) >> 16))
  {
    UWORD l = registers;

    DoOutput("\tMOVEM.L\t");

    for(i = 0; i <= 15; ++i)
    {
      if(l & (1 << i))
      {
	++offset;
	l ^= 1 << i;
	DoOutput(&RegNames[i<<2]);
	if(l)
	  DoOutput("/");
      }
    }
    DoOutput(",-(A7)\n");
  }
  else
  {
    DoOutput("\tMOVE.L\tA6,-(A7)\n"); ++offset;
  }

  DoOutput("\tMOVE.L\t_%s,A6\n", BaseName);

  if(!(Flags & FLAG_PASCAL))
  {
    for(i = 0; i < ap->NumArgs - (tagmode ? 1 : 0); ++i)
      DoOutput("\tMOVE.L\t%02ld(A7),%s\n", (i+offset)<<2,
      &RegNames[(ap->Args[i].ArgReg)<<2]);

    if(i < ap->NumArgs)
    {
      if(ap->Args[i].ArgReg > 7)
	DoOutput("\tLEA\t%02ld(A7),%s\n", (i+offset)<<2,
	&RegNames[(ap->Args[i].ArgReg)<<2]);
      else
	DoOutput("\tMOVE.L\tA7,%s\n\tADD%s.L\t#%02ld,%s\n",
	&RegNames[(ap->Args[i].ArgReg)<<2], (i + offset <= 2 ? "Q" : ""),
	(i+offset)<<2, &RegNames[(ap->Args[i].ArgReg)<<2]);
    }
  }
  else
  {
    for(i = 0; i < ap->NumArgs; ++i)
      DoOutput("\tMOVE.L\t%02ld(A7),%s\n", (ap->NumArgs-(i+1)+offset)<<2,
      &RegNames[(ap->Args[i].ArgReg)<<2]);
  }

  DoOutput("\tJSR\t-%03ld(A6)\n",ap->Bias);

  if(registers)
  {
    DoOutput("\tMOVEM.L\t(A7)+,");

    for(i = 0; i <= 15; ++i)
    {
      if(registers & (1 << i))
      {
	registers ^= 1 << i;
	DoOutput(&RegNames[i<<2]);
	if(registers)
	  DoOutput("/");
      }
    }
    DoOutput("\n");
  }
  else
    DoOutput("\tMOVE.L\t(A7)+,A6\n");

  return DoOutput("\tRTS\n");
}

ULONG FuncAsmCode(struct AmiPragma *ap, ULONG tagmode)
{
  ULONG registers;
  STRPTR text = tagmode ? ap->TagName : ap->FuncName, str;
  ULONG a[5];
  ULONG offset = 1;
  LONG i = strlen(ShortBaseNameUpper);

  registers = GetRegisterData(ap);

  a[0] = HUNK_UNIT; a[1] = 0; a[2] = HUNK_NAME;
  a[3] = (i + 6 + 3)>>2;

  DoOutputDirect(a, 16);
  DoOutputDirect(ShortBaseNameUpper, i);
  DoOutputDirect("_STUBS\0\0\0", (a[3]<<2)-i);

  a[0] = HUNK_CODE; a[1] = ap->NumArgs + (registers ? 5 : 4);

  if(tagmode && ap->NumArgs > 1 && ap->Args[ap->NumArgs-1].ArgReg <= 7)
    ++a[1];	/* one longword more, when D register and tagcall */

  DoOutputDirect(a, 8);

  a[0] = 0x2F0E2C79, a[1] = 0;

  if(registers)
  {
    UWORD l = 0x48E7;
    a[0] = (registers << 16) + 0x2C79;
    DoOutputDirect(&l, 2);
    for(l = (UWORD) registers; l; l >>= 1)
    {
      if(l & 1)
	++offset;
    }
  }
  else
    ++offset;

  DoOutputDirect(a, 8);

  if(!(Flags & FLAG_PASCAL))
  {
    for(i = 0; i < ap->NumArgs - (tagmode ? 1 : 0); ++i) /* generate MOVE.L code */
    {
      ULONG j;

      a[0] = 0x202F0000 + ((i+offset)<<2);

      j = ap->Args[i].ArgReg;
      if(j > 7)
      {
	a[0] |= 0x400000; j -= 8;	/* set MOVEA bit */
      }
      a[0] |= j << 25;		/* set destination register */

      DoOutputDirect(a, 4);
    }

    if(i < ap->NumArgs)
    {
      ULONG j = ap->Args[i].ArgReg;
      if(j > 7)
      { 			/* LEA x(A7),Ax */
	a[0] = (0x41EF0000 | ((j-8) << 25)) + ((i+offset) << 2);
      }
      else if(i+offset == 2)
      { 			/* MOVE.L A7,Dx  -- ADDQ.L #8,Dx */
	a[0] = 0x200F5080 | j | (j << 25);
      }
      else
      { 			/* MOVE.L A7,Dx  -- ADD.L #x,Dx */
	a[0] = 0x200FD0BC | (j << 25) | (j<<9);
	a[1] = (i+offset) << 2;
      }

      DoOutputDirect(a, (i + offset != 2 && j <= 7) ? 8 : 4);
    }
  }
  else
  {
    for(i = 0; i < ap->NumArgs; ++i) /* generate MOVE.L code */
    {
      ULONG j;

      a[0] = 0x202F0000 + ((ap->NumArgs-(i+1)+offset)<<2);

      j = ap->Args[i].ArgReg;
      if(j > 7)
      {
	a[0] |= 0x400000; j -= 8;	/* set MOVEA bit */
      }
      a[0] |= j << 25;		/* set destination register */

      DoOutputDirect(a, 4);
    }
  }

  /* here comes the base reference */
  a[0] = 0x4EAE0000 + (UWORD) (- ap->Bias); /* JSR instruction */

  DoOutputDirect(a, 4);

  if(registers)
  {
    a[0] = 0x4CDF0000 + (registers >> 16);
    a[1] = 0x4E750000;
    DoOutputDirect(a, 6);
  }
  else
  {
    a[0] = 0x2C5F4E75;
    DoOutputDirect(a, 4);
  }

  a[0] = HUNK_EXT;
  a[1] = 0x81000000 + ((strlen(BaseName) + 1 + 3)>>2);
  a[2] = 0x5F000000;

  DoOutputDirect(a, 9);
  DoOutputDirect(BaseName, (((UWORD) a[1])<<2)-1);

  a[0] = 1; a[1] = (registers ? 6 : 4);
  DoOutputDirect(a, 8);

  /* here come the XDEF name references */
  OutputXDEF("_%s", text);		/* C name */

  if(!(Flags & FLAG_PASCAL))
  {
    OutputXDEF("%s", text);		/* ASM name */

    if(!ap->NumArgs && clibbuf)
      OutputXDEF("%s_", text);		/* C++ name no parameters */
    else if((str = FindClibFunc(text)))
    {
      UBYTE name[100];
      ULONG ret = 0;

      do
      {
	if((ret = CopyCPPType(name, str, ret, text, ap->Args)))
	  OutputXDEF("%s__%s", text, name); /* C++ name with parameters */
      } while(ret == 0xFFFFFFFF);
    }
  }

  a[0] = 0; a[1] = HUNK_END;

  return DoOutputDirect(a, 8);
}

ULONG FuncCSTUBS(struct AmiPragma *ap, ULONG TagCall)
{
  STRPTR string,str2,lastarg;
  STRPTR ret = "return ";
  ULONG i;

  str2 = FindClibFunc(ap->TagName);
  if(!(string = FindClibFunc(ap->FuncName)))
    string = deftype;

  string = SkipBlanks(string);
  if(!strnicmp("void", string, 4))
    ret = 0;

  if(!OutClibType(string) || !DoOutput(" %s("/*)*/, ap->TagName))
    return 0;
  for(i = 0; i < ap->NumArgs-1; i++)
  {
    str2 = GetClibType(str2);		/* skip the equal parts */
    if(!(string = GetClibType(string)) || !OutClibType(string) ||
    !DoOutput(" %s, ", ap->Args[i].ArgName))
      return 0;
  }
  lastarg = string;
  if((str2 = GetClibType(str2)))
  {
    if(!strncmp("...", str2, 3))
      str2 = 0;
    else if(!OutClibType(str2) || !DoOutput(" %s, ", ap->Args[i].ArgName))
      return 0;
  }
  else if(ap->NumArgs == 1 && !DoOutput("ULONG tag, "))
    return 0;

  if(!DoOutput(/*(*/"...)\n{\n  %s%s("/*)*/, ret, ap->FuncName))
    return 0;
  for(i = 0; i < ap->NumArgs-1; i++)
  {
    if(!DoOutput("%s, ", ap->Args[i].ArgName))
      return 0;
  }
  if(!DoOutput("("/*)*/) || !(string = GetClibType(string)) ||
  !OutClibType(string))
    return 0;
  if(str2)
  {
    if(!DoOutput(/*((*/") &%s);\n}\n\n", ap->Args[ap->NumArgs-1].ArgName))
      return 0;
  }
  else if(ap->NumArgs == 1)
  {
    if(!DoOutput(/*((*/") &tag);\n}\n\n"))
      return 0;
  }
  else if (!DoOutput(/*(*/") ((ULONG) &%s + sizeof("/*))*/,
  ap->Args[ap->NumArgs-2].ArgName) || !OutClibType(lastarg) ||
  !DoOutput(/*(((*/")));\n}\n\n"))
    return 0;
  return 1;
}

ULONG FuncLVOXDEF(struct AmiPragma *ap, ULONG data)
{
  return DoOutput("\t\tXDEF\t_LVO%s\n", ap->FuncName);
}

ULONG FuncLVO(struct AmiPragma *ap, ULONG data)
{
  return DoOutput("\n_LVO%-24s\tEQU\t-%ld", ap->FuncName, ap->Bias);
}

ULONG FuncLocCode(struct AmiPragma *ap, ULONG tagmode)
{
  ULONG a[5];
  STRPTR text = tagmode ? ap->TagName : ap->FuncName, str,
     str2 = Flags & FLAG_LOCALREG ? "rE" : "";
  LONG i = strlen(ShortBaseNameUpper);
  ULONG j;

  a[0] = HUNK_UNIT; a[1] = 0; a[2] = HUNK_NAME;
  a[3] = (i + 4 + 3)>>2;

  DoOutputDirect(a, 16);
  DoOutputDirect(ShortBaseNameUpper, i);
  DoOutputDirect("_LOC\0\0\0", (a[3]<<2)-i);

  if(Flags & FLAG_LOCALREG)
  {
    if(tagmode)
    {
      j = ap->Args[ap->NumArgs-1].ArgReg;
      a[0] = HUNK_CODE; a[1] = 4;
      a[2] = 0x2F000000 + (j << 16);	/* MOVE <ea>,-(A7) */

      DoOutputDirect(a, 10);

      a[1] = 0x4EAE0000 + (UWORD) (- ap->Bias); /* JSR instruction */
      a[2] = 0x201F4E75;		/* MOVE (A7)+,<ea> */
      a[3] = 0;
      if(j > 7)
      { 				/* LEA x(A7),Ax */
	j -= 8;
	a[0] = 0x41EF0008 | (j << 25);
	a[2] += 0x400000;		/* set A flag */
      }
      else
      { 				/* MOVE.L A7,Dx  -- ADDQ.L #8,Dx */
	a[0] = 0x200F5080 | j | (j << 25);
      }
      a[2] += j << 25;

      DoOutputDirect(a, 14);
    }
    else
    {
      a[0] = HUNK_CODE; a[1] = 1;
      a[2] = 0x4EEE0000 + (UWORD) (- ap->Bias); /* JMP instruction */
      DoOutputDirect(a, 12);
    }
  }
  else
  {
    ULONG registers;
    ULONG offset = 1;

    registers = GetRegisterData(ap) | 0x40000002;

    a[0] = HUNK_CODE; a[1] = ap->NumArgs + 5;

    if(tagmode && ap->Args[ap->NumArgs-1].ArgReg <= 7)
      ++a[1];	/* one longword more, when D register and tagcall */

    DoOutputDirect(a, 8);

    a[0] = 0x48E70000 + (registers & 0xFFFF);
    for(j = (UWORD) registers; j; j >>= 1)
    {
      if(j & 1)
	++offset;
    }
    a[1] = 0x2C6F0000+ ((offset++)<<2);
    DoOutputDirect(a, 8);

    for(i = 0; i < ap->NumArgs - (tagmode ? 1 : 0); ++i) /* generate MOVE.L code */
    {
      a[0] = 0x202F0000 + ((i+offset)<<2);

      j = ap->Args[i].ArgReg;
      if(j > 7)
      {
	a[0] |= 0x400000; j -= 8;	/* set MOVEA bit */
      }
      a[0] |= j << 25;			/* set destination register */

      DoOutputDirect(a, 4);
    }

    if(i < ap->NumArgs)
    {
      if((j = ap->Args[i].ArgReg) > 7)
      { 				/* LEA x(A7),Ax */
	a[0] = (0x41EF0000 | ((j-8) << 25)) + ((i+offset) << 2);
      }
      else
      { 				/* MOVE.L A7,Dx  -- ADD.L #x,Dx */
	a[0] = 0x200FD0BC | (j << 25) | (j<<9);
	a[1] = (i+offset) << 2;
      }

      DoOutputDirect(a, j <= 7 ? 8 : 4);
    }

    /* here comes the base reference */
    a[0] = 0x4EAE0000 + (UWORD) (- ap->Bias); /* JSR instruction */

    DoOutputDirect(a, 4);

    a[0] = 0x4CDF0000 + (registers >> 16);
    a[1] = 0x4E750000;
    DoOutputDirect(a, 8);
  }

  a[0] = HUNK_EXT;
  DoOutputDirect(a,4);

  /* here come the XDEF name references */

  OutputXDEF("%s", text);	/* ASM names */
  OutputXDEF("LOC_%s", text);

  OutputXDEF("_%s", text);	/* C names */
  OutputXDEF("_LOC_%s", text);

  if(!ap->NumArgs && clibbuf)
  {
    OutputXDEF("%s_%sP07Library", text, str2);	/* C++ names no parameters */
    OutputXDEF("LOC_%s__%sP07Library", text, str2);
  }
  else if((str = FindClibFunc(text)))
  {
    UBYTE name[100];
    ULONG ret = 0;

    do
    {
      if((ret = CopyCPPType(name, str, ret, text, ap->Args)))
      { 				/* C++ names with parameters */
	OutputXDEF("%s__%sP07Library%s", text, str2, name);
	OutputXDEF("LOC_%s__%sP07Library%s", text, str2, name);
      }
    } while(ret == 0xFFFFFFFF);
  }

  a[0] = 0; a[1] = HUNK_END;

  return DoOutputDirect(a, 8);
}

ULONG FuncLocText(struct AmiPragma *ap, ULONG tagmode)
{
  STRPTR text = tagmode ? ap->TagName : ap->FuncName;
  STRPTR string;
  LONG i;

  if(!(string = FindClibFunc(text)))
    string = deftype;

  string = SkipBlanks(string);

  OutClibType(string);
  DoOutput(" LOC_%s("/*)*/, text);
  if(Flags & FLAG_LOCALREG)
    DoOutput("register __a6 ");
  DoOutput("struct Library * libbase");
  if(ap->NumArgs)
  {
    DoOutput(", ");

    for(i = 0; i < ap->NumArgs-1; i++)
    {
      if(!(string = GetClibType(string)) ||
      ((Flags & FLAG_LOCALREG &&
      !DoOutput("register __%s ", &RegNames[(ap->Args[i].ArgReg)<<2]))) ||
      !OutClibType(string) || !DoOutput(" %s, ", ap->Args[i].ArgName))
	return 0;
    }

    if(!tagmode)
    {
      if(!(string = GetClibType(string)) ||
      ((Flags & FLAG_LOCALREG &&
      !DoOutput("register __%s ", &RegNames[(ap->Args[i].ArgReg)<<2]))) ||
      !OutClibType(string) ||
      !DoOutput(/*(*/" %s);\n#define %s("/*)*/, ap->Args[i].ArgName, text))
	return 0;
      for(i = 0; i < ap->NumArgs-1; ++i)
	DoOutput("%lc, ", (i+'a'));
      DoOutput(/*(*/"%lc) LOC_%s((struct Library *) %s, "/*)*/,(i+'a'),
      text, BaseName);
      for(i = 0; i < ap->NumArgs-1; ++i)
	DoOutput("%lc, ",(i+'a'));
      return DoOutput(/*(*/"%lc)\n\n",(i+'a'));
    }
    else
      return DoOutput(/*(*/"...);\n");
  }
  else
    return DoOutput(/*(*/");\n");
}

ULONG CallFunc(ULONG tagmode, STRPTR comment,
ULONG (*Func) (struct AmiPragma *, ULONG))
{
  struct Comment *com = 0;
  struct AmiPragma *ap;

  if(comment)
    com = (struct Comment *) Comment.First;

  for(ap = (struct AmiPragma *) AmiPragma.First; ap && !CTRL_C;
  ap = (struct AmiPragma *) ap->List.Next)
  {
    if(ap->Public || (Flags & FLAG_PRIVATE))
    {
      while(com && com->Bias <= ap->Bias)
      {
	if(!DoOutput(comment, com->Data))
	  return 0;
	com = (struct Comment *) com->List.Next;
      }

      if(tagmode && ap->TagName && !Func(ap, tagmode))
	return 0;
      if(tagmode != TAGMODE_TAGS && ap->FuncName && !Func(ap, 0))
	return 0;
    }
  }
  while(com)
  {
    if(!DoOutput(comment, com->Data))
      return 0;
    com = (struct Comment *) com->List.Next;
  }
  return 1;
}

/* ------------------------------------------------------------------ */

/* return non zero, when ok, return 0xFFFFFFFF when STRPTR and not flags */
ULONG CopyCPPType(STRPTR buffer, STRPTR a, ULONG flag, STRPTR func,
struct AmiArgs *args)
{
/* when flag, then STRPTR is unsigned char * mode, else signed char * */
  ULONG ret = 1;
  ULONG k = 0;
  struct CPP_NameType nt;

  while((a = GetClibType(a)))
  {
    SDI_memset(&nt, 0, sizeof(struct CPP_NameType));
    GetCPPType(&nt, a);

    if(!flag && (nt.Flags & CPP_FLAG_STRPTR))
      ret = 0xFFFFFFFF;

    if(!nt.StructureName && !nt.Type)
    {
      APTR str;
      ULONG l = GetClibLength(a);

      if((str = AllocMem(l+1, MEMF_ANY|MEMF_CLEAR)))
	CopyMem(a, str, l);

      DoError(ERR_UNKNOWN_VARIABLE_TYPE, 0, str ? str : "?", func);

      if(str)
	FreeMem(str, l);

      return 0;
    }
    if((Flags & FLAG_LOCALREG) && (nt.Type != CPP_TYPE_VARARGS))
    {
      *(buffer++) = 'r';
      *(buffer++) = args[k].ArgReg + (args[k].ArgReg < 10 ? '0' : 'A'-10);
    }
    while(nt.PointerDepth--)
      *(buffer++) = 'P';
    if(nt.Flags & CPP_FLAG_CONST)
      *(buffer++) = 'C';
    if((nt.Flags & CPP_FLAG_UNSIGNED) || (flag && (nt.Flags & CPP_FLAG_STRPTR)))
      *(buffer++) = 'U';
    if(nt.Type)
      *(buffer++) = nt.Type;
    else
    {
      ULONG i;
      sprintf(buffer, "%02ld", nt.StructureLength); buffer += 2;
      for(i = 0; i < nt.StructureLength; ++i)
	*(buffer++) = nt.StructureName[i];
    }
    ++k;
  }

  *(buffer) = 0;
  return ret;
}

void GetCPPType(struct CPP_NameType *data, STRPTR start)
{
  ULONG a = 0;
  STRPTR u;

  while(a < 100)
  {
    start = SkipBlanks(start);
    if(!strncmp("...",start,3))
    {
      data->Type = CPP_TYPE_VARARGS; return;
    }
    if(!strncmp("const",start, 5))
    {
      data->Flags |= CPP_FLAG_CONST; start += 6;
    }
    else if(!strncmp("signed", start,6))
      start += 7;
    else if(!strncmp("unsigned",start,8))
    {
      data->Flags |= CPP_FLAG_UNSIGNED; start += 9;
    }
    else if(!strncmp("struct",start,6))
    {
      data->StructureName = SkipBlanks(start+6);
      start = SkipName(data->StructureName);
      data->StructureLength = start-data->StructureName;
      a = 100;
    }
    else if(!a)
    {
      ULONG i;

      for(i = 0; CPP_Field[i].Text; ++i)
      {
	if(!strncmp(start, CPP_Field[i].Text, CPP_Field[i].Length))
	{
	  start += CPP_Field[i].Length + 1;
	  data->Type = CPP_Field[i].Type;
	  data->Flags |= (CPP_Field[i].Flags & ~CPP_FLAG_POINTER);
	  if(data->Flags & CPP_FLAG_POINTER)
	    ++data->PointerDepth;
	  break;
	}
      }
      a = 2;
    }
    else
      a = 100;
  }

  do
  {
    u = SkipBlanks(start);
    if(*u == '*')
    {
      start = u+1; ++data->PointerDepth;
    }
  } while(*u == '*');
}

STRPTR FindClibFunc(STRPTR func) /* finds the needed function */
{
  ULONG len = strlen(func);
  STRPTR string, linestart, strend = clibbuf + clibsize;
  ULONG commentmode = 0;

#ifdef DEBUG_OLD
  VPrintf("FindClibFunc: %s\n", &func);
#endif

  if(!(linestart = string = clibbuf))
    return 0;

  do
  {
    while(*string && (commentmode || *string != '('/*)*/))
    {
      if(*(string++) == '/')
      {
	if(*string == '*')		/* start of comment */
	  commentmode = 1;
	else if(*(string-2) == '*')	/* end of comment */
	  commentmode = 0;
	else if(*string == '/') 	/* skip line comments */
	{
	  while(*string)
	    ++string;
	}
      }
    }

    if(*string == '(' /*)*/)
    {
      while(*(--string) == ' ' || *string == '\t')
	;
      if(!strncmp(func, string-len+1, len) && (*(string-len) == ' ' ||
      *(string-len) == '\t' || *(string-len) == '*' || !*(string-len)))
	return linestart;
    }
    while(string < strend && *string)
      ++string;
    if(!*string)
      linestart = ++string;
  } while(string < strend);

  DoError(ERR_PROTOTYPE_MISSING, 0, func);

  return 0;
}

ULONG GetClibLength(STRPTR start) /* returns length of the type */
{
  STRPTR old = start, u;
  ULONG a = 1;

#ifdef DEBUG_OLD
  VPrintf("GetClibLength:\n", 0);
#endif

  while(a)
  {
    start = SkipBlanks(start); a = 0;
    if(!strncmp("const",start, 5))
    {
      ++a; start += 6;
    }
    if(!strncmp("struct",start,6) || !strncmp("signed", start,6))
    {
      ++a; start += 7;
    }
    if(!strncmp("unsigned",start,8))
    {
      ++a; start += 9;
    }
  }

  while(*start && *start != ' ' && *start != '\t' && *start != '*' &&
  *start != /*(*/')')
   ++start;

  do
  {
    u = SkipBlanks(start);
    if(*u == '*')
      start = u+1;
  } while(*u == '*');

  return (ULONG) (start-old);
}

ULONG OutClibType(STRPTR start)
{
  UBYTE string[11];
  ULONG length = GetClibLength(start), i;

#ifdef DEBUG_OLD
  VPrintf("OutClibType:\n", 0);
#endif

  while(length)
  {
    if(length > 10)
      i = 10;
    else
      i = length;

    CopyMem(start, string, i);
    string[i] = '\0'; length -= i; start += i;
    if(!DoOutput(string))
      return 0;
  }
  return 1;
}

STRPTR GetClibType(STRPTR start) /* searches for next type definition */
{
  if(start == (STRPTR) deftype)
    return start;
#ifdef DEBUG_OLD
  VPrintf("GetClibType:\n", 0);
#endif
  while(*start && *start != '(' && *start != ',' && *start != ')')
    ++start;
  if(!*start || *start == /*(*/')')
    return 0;
  return SkipBlanks(++start);
}

/* ------------------------------------------------------------------ */

ULONG CallPrag(ULONG tagmode, STRPTR type,
ULONG (*Func) (struct AmiPragma *, ULONG))
{
  if(type)
    if((*type && !DoOutput("#if%s\n", type)) ||
    !(CallFunc(tagmode, tagmode ? 0 : "/%s */\n", Func)) ||
    (*type && !DoOutput("#endif\n")) || CTRL_C)
      return 0;
  return 1;
}

ULONG CreatePragmaFile(void)
{
  switch(MODE)
  {
  case 1: DoOutput("#ifndef _INCLUDE_PRAGMA_%s_LIB_H\n#define _INCLUDE_PRAGMA_%s_LIB_H\n",
    ShortBaseNameUpper, ShortBaseNameUpper); break;
  case 2: DoOutput("#ifndef PRAGMAS_%s_LIB_H\n#define PRAGMAS_%s_LIB_H\n",
    ShortBaseNameUpper, ShortBaseNameUpper); break;
  case 3: DoOutput("#ifndef PRAGMAS_%s_PRAGMAS_H\n#define PRAGMAS_%s_PRAGMAS_H\n",
    ShortBaseNameUpper, ShortBaseNameUpper); break;
  case 4: break;
  default: return 0;
  }

  if(HEADER)
  {
    DoOutput("\n");
    DoOutputDirect(HEADER, headersize);
  }

  if(MODE != 4 && !DoOutput("\n#ifndef CLIB_%s_PROTOS_H\n#include "
  "<clib/%s_protos.h>\n#endif\n\n", ShortBaseNameUpper, ShortBaseName))
    return 0;

  if((Flags & FLAG_EXTERNC) &&
  !DoOutput("#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"))
    return 0;

  if(
  !CallPrag(TAGMODE_NORMAL, AMICALL, FuncAMICALL) ||
  !CallPrag(TAGMODE_NORMAL, LIBCALL, FuncLIBCALL))
    return 0;

  if(tagfuncs)
  {
    if(
    !CallPrag(TAGMODE_TAGS, AMITAGS, FuncAMICALL) ||
    !CallPrag(TAGMODE_TAGS, LIBTAGS, FuncLIBCALL))
      return 0;
  }

  if((Flags & FLAG_EXTERNC) &&
  !DoOutput("\n#ifdef __cplusplus\n}\n#endif\n"))
    return 0;

  switch(MODE)
  {
  case 1: DoOutput("\n#endif\t/*  _INCLUDE_PRAGMA_%s_LIB_H  */",
    ShortBaseNameUpper); break;
  case 2: DoOutput("\n#endif\t/*  PRAGMAS_%s_LIB_H  */",
    ShortBaseNameUpper); break;
  case 3: DoOutput("\n#endif\t/*  PRAGMAS_%s_PRAGMA_H  */",
    ShortBaseNameUpper); break;
  case 4: break;
  default: return 0;
  }
  return Output_Error;
}

ULONG CreateCSTUBSFile(void)
{
  DoOutput("#ifndef _INCLUDE_%s_CSTUBS_H\n#define _INCLUDE_%s_CSTUBS_H\n",
    ShortBaseNameUpper, ShortBaseNameUpper);

  if(!clibbuf)
    DoError(ERR_NOPROTOTYPES_FILE, 0);

  if(HEADER)
  {
    DoOutput("\n");
    DoOutputDirect(HEADER, headersize);
  }

  if(!DoOutput("\n#ifndef CLIB_%s_PROTOS_H\n#include "
  "<clib/%s_protos.h>\n#endif\n\n", ShortBaseNameUpper, ShortBaseName))
    return 0;

  if(!CallFunc(TAGMODE_TAGS, "/%s */\n", FuncCSTUBS))
    return 0;

  return DoOutput("#endif\t/*  _INCLUDE_%s_CSTUBS_H  */",
  ShortBaseNameUpper);
}

ULONG CreateLVOFile(ULONG mode)
{
  STRPTR data = "_LVO_I";

  if(mode == 2 || mode == 4)
    data = "_LIB_I";

  if(!DoOutput("\t\tIFND LIBRARIES_%s%s\nLIBRARIES_%s%s\tSET\t1\n\n",
  ShortBaseNameUpper, data, ShortBaseNameUpper, data) ||
  (HEADER && (!DoOutput("\n") || !DoOutputDirect(HEADER, headersize))) ||
  (mode <= 2 && !CallFunc(TAGMODE_NORMAL, 0, FuncLVOXDEF)) ||
  !CallFunc(TAGMODE_NORMAL, "\n%s", FuncLVO) ||
  !DoOutput("\n\n\t\tENDC") || CTRL_C)
    return 0;

  return 1;
}

ULONG CreateAsmStubs(ULONG mode)
{
  /* 1 = Text, 2 = Code */
  switch(mode)
  {
  case 1:
    if((HEADER && (!DoOutput("\n") || !DoOutputDirect(HEADER, headersize))) ||
    !DoOutput("\tSECTION\t \"%s_STUBS\",CODE\n\tXREF\t_%s\n",
    ShortBaseNameUpper, BaseName) ||
    !CallFunc(MODE, 0, FuncAsmText))
      return 0;
    break;
  case 2:
    if(!CallFunc(MODE, 0, FuncAsmCode))
      return 0;
    break;
  }

  if(CTRL_C)
    return 0;
  return 1;
}

ULONG CreateProtoFile(ULONG Type)
{
  STRPTR str1 = "pragma", str2 = "lib", str3 = "Library";
  ULONG i;

  for(i = 0; Proto_LibTypes[i].BaseName; ++i)
  {
    if(!(strcmp(Proto_LibTypes[i].BaseName, BaseName)))
    {
      str3 = Proto_LibTypes[i].StructureName; break;
    }
  }

  DoOutput("#ifndef _PROTO_%s_H\n#define _PROTO_%s_H\n", ShortBaseNameUpper,
  ShortBaseNameUpper);

  if(HEADER)
  {
    DoOutput("\n");
    DoOutputDirect(HEADER, headersize);
  }

  switch(Type)
  {
    case 4: str1 = "pragmas"; /* no break; */
    case 2: str2 = "pragmas"; break;
    case 3: str1 = "pragmas"; break;
    case 5: str1 = "local"; str2 = "loc"; break;
  }

  DoOutput("\n");
  if(Type == 6)
    DoOutput("#ifndef __NOLIBBASE__\n  ");
  DoOutput("extern struct %s *%s;\n", str3, BaseName);
  if(Type == 6)
    DoOutput("#endif\n");
  DoOutput("\n#include <exec/types.h>\n");
  if(Type != 5)
    DoOutput("#include <clib/%s_protos.h>\n", ShortBaseName);
  if(Type == 6)
    DoOutput("\n#ifdef " TEXT_GNUC "\n  #include <inline/%s.h>\n#else\n  ",
    ShortBaseName);
  DoOutput("#include <%s/%s_%s.h>\n", str1, ShortBaseName, str2);
  if(Type == 6)
    DoOutput("#endif\n");

  return DoOutput("\n#endif\t/*  _PROTO_%s_H  */\n", ShortBaseNameUpper);
}

ULONG CreateLocalData(void)
{
  UBYTE a[40];

  DoOutput("#ifndef _INCLUDE_PROTO_%s_LOC_H\n#define _INCLUDE_PROTO_%s_LOC_H\n\n",
  ShortBaseNameUpper, ShortBaseNameUpper);

  if(HEADER)
  {
    DoOutput("\n");
    DoOutputDirect(HEADER, headersize);
  }

  { /* copies the include lines */
    STRPTR str = clibbuf, strend = clibbuf + clibsize;
    ULONG i = 0;

    /* works too, when no clibbuf, because then following is everytime false */
    while(str < strend)
    {
      if(!strncmp(str, "#include", 8))
      {
	DoOutput("%s\n",str); ++i;
      }
      while(*(str++))
	;
    }
    DoOutput(i ? "\n" : "#include <exec/types.h>\n\n");
  }

  if((Flags & FLAG_EXTERNC) &&
  !DoOutput("#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"))
    return 0;

  if(!CallFunc(MODE, 0, FuncLocText))
    return 0;

  if((Flags & FLAG_EXTERNC) &&
  !DoOutput("#ifdef __cplusplus\n}\n#endif\n\n"))
    return 0;

  DoOutput("#endif\t/*  _INCLUDE_PROTO_%s_LOC_H  */", ShortBaseNameUpper);
  Out(out.size); /* clears buffer */

  sprintf(a, "%s_loc.lib", ShortBaseName);
  Close(out.file);
  if(!(out.file = Open(a, MODE_NEWFILE)))
    return 0;

  return CallFunc(MODE, 0, FuncLocCode);
}

/* ------------------------------------------------------------------ */

ULONG GetName(struct NameList *t, struct ShortListRoot *p, ULONG args)
{
  struct NameList *p2 = (struct NameList *) p->First;
  struct AmiPragma ap;
  ap.FuncName = t->NormName;
  ap.NumArgs = 1;
  ap.Args[0].ArgName = (args ? "args" : "tags");
  if(!MakeTagFunction(&ap))
    return 0;

  while(p2 && strcmp(p2->PragName, ap.TagName))
   p2 = (struct NameList *) p2->List.Next;

  if(!p2)
    return 0;

  t->Type = (args ? NTP_ARGS : NTP_TAGS);
  t->PragName = ap.TagName;
  RemoveItem(p, (struct ShortList *) p2);

#ifdef DEBUG_OLD
  VPrintf("GetName: name matches - %s _ %s\n", &t->NormName);
#endif

  return 1;
}

void OptimizeFDData(struct PragData *pd)
{
#ifdef DEBUG_OLD
  PutStr("OptimizeFDData\n");
#endif

  while(pd && !CTRL_C)
  {
    if(pd->NumNames > 1)
    {
      struct ShortListRoot n = {0,0,0}, p = {0,0,0};
      struct NameList *t;
      while(pd->Name.First)	/* sorts in AmiCall and TagCall */
      {
	t = (struct NameList *) pd->Name.First;

	RemoveItem(&pd->Name, (struct ShortList *) t);
	AddItem(t->PragName ? &p : &n, (struct ShortList *) t);
      }

      if(p.First)
      {
	t = (struct NameList *) n.First;
	while(p.First && t)
	{
	  if(!GetName(t, &p, 0))
	  {
	    GetName(t, &p, 1);
	  }
	  if(t->PragName)
	  {
	    struct NameList *t2 = (struct NameList *) t->List.Next;
	    RemoveItem(&n, (struct ShortList *)t);
	    AddItem(&pd->Name, (struct ShortList *) t);
	    t = t2;
	  }
	  else
	    t = (struct NameList *) t->List.Next;
	}
	while(p.First)
	{
	  if(n.First)
	  {
	    t = (struct NameList *) n.First;
	    t->PragName = ((struct NameList *)(p.First))->PragName;
	    RemoveItem(&n, (struct ShortList *) t);
#ifdef DEBUG_OLD
	    VPrintf("OptimizeFDData: names together - %s _ %s\n", &t->NormName);
#endif
	    t->Type = NTP_UNKNOWN;
	  }
	  else
	  {
	    ULONG i;

	    t = (struct NameList *) p.First;
	    i = strlen(t->PragName);
	    t->NormName = strndup(t->PragName, i+1);
	    t->NormName[i++] = 'A';
	    t->NormName[i] = 0;
	    t->Type = NTP_TAGS;
#ifdef DEBUG_OLD
	    VPrintf("OptimizeFDData: NormName created - %s _ %s\n", &t->NormName);
#endif
	  }

	  AddItem(&pd->Name, (struct ShortList *) t);
	  RemoveItem(&p, p.First);
	}
      }

      AddItem(&pd->Name, n.First); /* add left NormNames */
    }
    pd = (struct PragData *) pd->List.Next;
  }
}

ULONG MakeFD(struct PragList *pl)
{
  struct PragData *pd = (struct PragData *) pl->Data.First;
  ULONG bias;

#ifdef DEBUG_OLD
  PutStr("MakeFD\n");
#endif
  bias = pd->Bias;

  OptimizeFDData(pd);
#ifdef DEBUG_OLD
  PutStr("MakeFD: after Optimizing\n");
#endif
  DoOutput("##base _%s\n##bias %ld\n##public\n", pl->Basename, bias);

  while(pd && !CTRL_C && Output_Error)
  {
    struct NameList *n = (struct NameList *) pd->Name.First;

    if(bias != pd->Bias)
      DoOutput("##bias %ld\n", (bias = pd->Bias));

    while(n)
    {
      STRPTR lastpar = "last";
      ULONG i;

      if(n->Type == NTP_TAGS)
	lastpar = "tags";
      else if(n->Type == NTP_ARGS)
	lastpar = "args";

      DoOutput("%s("/*)*/,n->NormName);
      if(!pd->NumArgs)
	DoOutput(/*(*/")()\n");
      else
      {
	for(i = 0; i < pd->NumArgs-1; ++i)
	  DoOutput("par%ld,",i+1);
	DoOutput("%s)(", lastpar);
	for(i = 0; i < pd->NumArgs-1; ++i)
	  DoOutput("%s,", &RegNames[(pd->ArgReg[i])<<2]);
	DoOutput("%s)\n", &RegNames[(pd->ArgReg[i])<<2]);

	if(n->Type == NTP_UNKNOWN)
	{
	  ULONG i;
	  for(i = 0; n->NormName[i] == n->PragName[i]; ++i)
	    ;
	  DoOutput("*tagcall");
	  if(n->NormName[i])
	    DoOutput("-%s", n->NormName+i);
	  if(n->PragName[i])
	    DoOutput("+%s", n->PragName+i);

	  DoOutput("\n");
	}
      }
      if((n = (struct NameList *) n->List.Next))
	DoOutput("##bias %ld\n", pd->Bias);
    }

    pd = (struct PragData *)pd->List.Next; bias += 6;
  }

  if(CTRL_C)
    return 0;

  DoOutput("##end\n");

  return Output_Error;
}

ULONG AddFDData(struct ShortListRoot *pls, struct FDData *fd)
{
  struct NameList *t;
  struct PragList *pl = (struct PragList *) pls->First;
  struct PragData *pd;

  while(pl && strcmp(pl->Basename, fd->Basename))
    pl = (struct PragList *) pl->List.Next;

  if(!pl)
  {
#ifdef DEBUG_OLD
  VPrintf("AddFDData: New PragList - %s\n", &fd->Basename);
#endif
    if(!(pl = (struct PragList *) NewItem(pls)))
      return 100;
    pl->Basename = fd->Basename;
    pl->Data.Size = sizeof(struct PragData);
    AddItem(pls, (struct ShortList *) pl);
  }

  if((pd = (struct PragData *) pl->Data.First))
  {
    while(pd->List.Next && ((struct PragData *) pd->List.Next)->Bias
    <= fd->Bias)
      pd = (struct PragData *) pd->List.Next;
  }

  if(!pd || pd->Bias != fd->Bias)
  {
    struct PragData *pd2;
#ifdef DEBUG_OLD
    {
      ULONG args[2];
      args[0] = fd->Bias;
      args[1] = fd->NumArgs;
      VPrintf("AddFDData: New PragData - %ld, %ld\n", &args);
    }
#endif
    if(!(pd2 = (struct PragData *) NewItem(&pl->Data)))
      return 100;
    pd2->Bias = fd->Bias;
    CopyMem(fd->ArgReg, pd2->ArgReg, 14);
    pd2->NumArgs = fd->NumArgs;
    pd2->Name.Size = sizeof(struct NameList);
    if(!pd)
      AddItem(&pl->Data, (struct ShortList *) pd2);
    else if(pd->Bias > fd->Bias) /* Insert at start */
    {
      pd2->List.Next = pl->Data.First;
      pl->Data.First = (struct ShortList *) pd2;
    }
    else /* Insert the entry */
    {
      pd2->List.Next = pd->List.Next;
      pd->List.Next = (struct ShortList *) pd2;
    }
    pd = pd2;
  }
  else
  {
    ULONG i = fd->NumArgs;
    if(fd->NumArgs != pd->NumArgs)
      return ERR_DIFFERENT_TO_PREVIOUS;

    while(i--)
    {
      if(fd->ArgReg[i] != pd->ArgReg[i])
	return ERR_DIFFERENT_TO_PREVIOUS;
    }
  }

  t = (struct NameList *) pd->Name.First;	/* skips same names */
  while(t && strcmp(fd->Name, fd->Mode ? t->PragName : t->NormName))
    t = (struct NameList *) t->List.Next;

  if(t)
    return 0;

  if(!(t = (struct NameList *) NewItem(&pd->Name)))
    return 100;
  if(fd->Mode)
    t->PragName = fd->Name;
  else
    t->NormName = fd->Name;
  AddItem(&pd->Name, (struct ShortList *) t);
  ++(pd->NumNames);
#ifdef DEBUG_OLD
  VPrintf("AddFDData: New NameList - %s\n", &fd->Name);
#endif
  return 0;
}

UBYTE GetHexValue(UBYTE data)
{
  if(data >= 'a')
    return (UBYTE) (data - 'a' + 10);
  else if(data >= 'A')
    return (UBYTE) (data - 'A' + 10);
  else
    return (UBYTE) (data - '0');
}

ULONG GetLibData(struct FDData *fd)
{
  ULONG i;
  fd->Name = SkipBlanks(in.pos);
  in.pos = SkipName(fd->Name); *(in.pos++) = 0;
  in.pos = SkipBlanks(in.pos);
  fd->Bias = strtoul(in.pos, 0, 16);
  while(*in.pos)
    ++in.pos;
  if((fd->NumArgs = GetHexValue(*(--in.pos))) > 14)
    return ERR_TO_MUCH_ARGUMENTS;
  --in.pos; /* skips return register */
  for(i = 0; i < fd->NumArgs; ++i)
  {
    if((fd->ArgReg[i] = GetHexValue(*(--in.pos))) > 14)
      return ERR_EXPECTED_REGISTER_NAME;
  }
  return 0;
}

ULONG GetAmiData(struct FDData *fd)
{
  STRPTR endptr;
  in.pos = SkipBlanks(in.pos);
  if(*in.pos != '('/*)*/)
    return ERR_EXPECTED_OPEN_BRACKET;
  fd->Basename = ++in.pos;
  in.pos = SkipBlanks(endptr = SkipName(in.pos));
  if(*in.pos != ',')
    return ERR_EXPECTED_COMMA;
  *endptr = 0;
  in.pos = SkipBlanks(++in.pos);
  if(!strncmp(in.pos, "0x", 2))
    fd->Bias = strtoul(in.pos+2, 0, 16);
  else
    fd->Bias = strtoul(in.pos, 0, 10);

  in.pos = SkipBlanks(SkipName(in.pos));
  if(*in.pos != ',')
    return ERR_EXPECTED_COMMA;
  fd->Name = in.pos = SkipBlanks(++in.pos);
  in.pos = SkipBlanks(endptr = SkipName(in.pos));
  if(*in.pos != '('/*)*/)
    return ERR_EXPECTED_OPEN_BRACKET;
  *endptr = 0;
  in.pos = SkipBlanks(++in.pos);
  if(*in.pos == /*(*/')')
    return 0;
  --in.pos;
  while(*in.pos != /*(*/')')
  {
    ULONG i;
    in.pos = SkipBlanks(in.pos+1);
    for(i = 0; i < 16; i++)
      if(!strnicmp(&RegNames[i<<2], in.pos, 2))
	break;
    if(i == 16)
      return ERR_EXPECTED_REGISTER_NAME;
    if(i > 14)
      return ERR_ILLEGAL_REGISTER;

    if(fd->NumArgs > 14)
      return ERR_TO_MUCH_ARGUMENTS;

    fd->ArgReg[fd->NumArgs] = i; ++fd->NumArgs;

    in.pos = SkipBlanks(in.pos+2);

    if(*in.pos != ',' && *in.pos != ')')
      return ERR_EXPECTED_CLOSE_BRACKET;
  }
  in.pos = SkipBlanks(in.pos+1);
  if(*in.pos != ')')
    return ERR_EXPECTED_CLOSE_BRACKET;
  return 0;
}

ULONG CreateFDFile(STRPTR file, STRPTR to)
{
  struct ShortListRoot pl = {0, 0, sizeof(struct PragList)};
  ULONG linenum, err = 0, skip;

  for(linenum = 1; !CTRL_C && in.pos < in.buf + in.size; ++linenum)
  {
    in.pos = SkipBlanks(in.pos);
    if(!strncmp("#pragma", in.pos, 7))
    {
      struct FDData fd;

      skip = 0;
      memset(&fd, 0, sizeof(struct FDData));

      in.pos = SkipBlanks(in.pos+7);
      if(!strncmp("tagcall", in.pos, 7))
      {
	fd.Mode = 1;
	in.pos = SkipBlanks(in.pos+7);
	if(*in.pos == '(' /*)*/)	/* Storm method */
	  err = GetAmiData(&fd);
	else				/* SAS method */
	{
	  fd.Basename = in.pos;
	  in.pos = SkipName(fd.Basename); *(in.pos++) = 0;
	  err = GetLibData(&fd);
	}
      }
      else if(!strncmp("amicall", in.pos, 7))	/* Storm method */
      {
	in.pos += 7;
	err = GetAmiData(&fd);
      }
      else if(!strncmp("libcall", in.pos, 7))	/* SAS method */
      {
	fd.Basename = SkipBlanks(in.pos+7);
	in.pos = SkipName(fd.Basename); *(in.pos++) = 0;
	err = GetLibData(&fd);
      }
      else if(!strncmp("syscall", in.pos, 7))	/* SAS method */
      {
	fd.Basename = "SysBase";
	err = GetLibData(&fd);
      }
      else
	skip = 1;

      if(err)
	DoError(err, linenum);
      else if(skip)
	;
      else if((err = AddFDData(&pl, &fd)))
      {
	if(err != 100)
	  DoError(err, linenum);
	return 0;
      }
    }
    while(*(in.pos++))	/* jumps to first char of next line */
      ;
  }

  if(pl.First && !CTRL_C)
  {
    struct PragList *p = (struct PragList *) pl.First;
    if(!p->List.Next)
    {
      if(!to)
      {
	STRPTR text;
	ULONG i;

	if(ShortBaseName)
	{
	  text = ShortBaseName; i = strlen(text);
	}
	else
	{
	  text = p->Basename; i = strlen(text)-4;
	}

	to = strndup(text, i + 7);
	CopyMem("_lib.fd", to+i, 8);
      }
      if(!(out.file = Open(to, MODE_NEWFILE)) || !MakeFD(p))
	return 0;
    }
    else
    {
      while(p)
      {
	ULONG i;
	i = strlen(p->Basename) - 4;
	to = strndup(p->Basename, i+7);
	CopyMem("_lib.fd", to+i, 8);
	if(!(out.file = Open(to, MODE_NEWFILE)) || !MakeFD(p))
	  return 0;
	Out(out.size); /* free buffer */
	Close(out.file);
	out.file = 0;
	p = (struct PragList *) p->List.Next;
      }
    }
  }

  if(CTRL_C)
    return 0;
  return 1;
}

STRPTR helptext =
"FDFILE:  the FD file which should be used\n"
"SPECIAL: 1 - Aztec compiler (xxx_lib.h,     MODE 2, AMICALL)\n"
"\t 2 - DICE compiler  (xxx_pragmas.h, MODE 3, LIBCALL)\n"
"\t 3 - SAS compiler   (xxx_pragmas.h, MODE 3, LIBCALL,LIBTAGS)\n"
"\t 4 - MAXON compiler (xxx_lib.h,     MODE 1, AMICALL)\n"
"\t 5 - STORM compiler (xxx_lib.h,     MODE 1, AMITAGS,AMICALL)\n"
"\t 6 - all compilers [default]\n"
"\t10 - stub-functions for C - C text\n"
"\t11 - stub-functions for C - assembler text\n"
"\t12 - stub-functions for C - link library\n"
"\t13 - defines and link library for local library base (register call)\n"
"\t14 - defines and link library for local library base (stack call)\n"
"\t15 - stub-functions for Pascal - assembler text\n"
"\t16 - stub-functions for Pascal - link library\n"
"\t20 - assembler lvo _lvo.i file\n"
"\t21 - assembler lvo _lib.i file\n"
"\t22 - assembler lvo _lvo.i file no XDEF\n"
"\t23 - assembler lvo _lib.i file no XDEF\n"
"\t30 - proto file with pragma/..._lib.h call\n"
"\t31 - proto file with pragma/..._pragmas.h call\n"
"\t32 - proto file with pragmas/..._lib.h call\n"
"\t33 - proto file with pragmas/..._pragmas.h call\n"
"\t34 - proto file with local/..._loc.h call\n"
"\t35 - proto file for all compilers\n"
"\t50 - FD file (source is a pragma file!)\n"
"TO:\t the destination directory (self creation of filename) or\n"
"\t the destination file\n"
"COMMENT: copy comments found in FD file\n"
"MODE:\t SPECIAL 1-6,AMICALL,LIBCALL,AMITAGS,LIBTAGS:\n"
"\t 1 - _INCLUDE_PRAGMA_..._LIB_H definition method [default]\n"
"\t 2 - _PRAGMAS_..._LIB_H definition method\n"
"\t 3 - _PRAGMAS_..._PRAGMAS_H definition method\n"
"\t 4 - no definition\n"
"\t SPECIAL 11-14:\n"
"\t 1 - all functions, normal interface\n"
"\t 2 - only tag-functions, tagcall interface [default]\n"
"\t 3 - all functions, normal and tagcall interface\n"
"AMICALL: creates amicall pragmas\n"
"LIBCALL: creates libcall pragmas\n"
"AMITAGS: creates tagcall pragmas (amicall like method (StormC++))\n"
"LIBTAGS: creates tagcall pragmas (libcall like method (SAS C))\n"
"The last four need a string as argument. This string is used to set\n"
"a #if<given string> before the set method.\n"
"EXTERNC: add a #ifdef __cplusplus ... statement to pragma file\n"
"USESYSCALL: uses syscall pragma instead of libcall SysBase\n"
"CLIB:    name of the prototypes file in clib directory\n"
"PRIVATE: includes private declared functions\n"
"HEADER:  inserts given file into header of created file (\"\" is scan)\n"
"STORMFD: converts FD files of strange StormC++ format";

void main(void)
{
  ULONG mode = 0;
  ULONG spec = 0; /* for default setting I need a ULONG var to get a pointer */
  UBYTE filename[255];	/* needed for filename */
  struct
  {
    STRPTR FDFILE;
    ULONG* SPECIAL;
    STRPTR TO;
    ULONG  COMMENT;
    ULONG* MODE;
    STRPTR AMICALL;
    STRPTR LIBCALL;
    STRPTR AMITAGS;
    STRPTR LIBTAGS;
    ULONG  EXTERNC;
    ULONG  USESYSCALL;
    STRPTR CLIB;
    ULONG  PRIVATE;
    STRPTR HEADER;
    ULONG  STORMFD;
  } args = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

  args.SPECIAL = &spec;

#ifdef __SASC	/* with __MAXON__ this is done automatic by my StartUp */
  TestOS;
#endif

  if(!(rda = (struct RDArgs *) AllocDosObject(DOS_RDARGS, 0)))
    End(RETURN_FAIL);
  rda->RDA_ExtHelp = helptext;
  if(!ReadArgs(PARAM, (LONG *) &args, rda))
    End(RETURN_FAIL);

  VPrintf("SourceFile: %s\n", &args.FDFILE);

  if(
  !(in.file = Open(args.FDFILE, MODE_OLDFILE)) ||
  !(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37)) ||
  !(fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)) ||
  !ExamineFH(in.file, fib) ||
  !(in.buf = in.pos = (STRPTR) AllocRemember(&remember, (in.size = fib->fib_Size)+1, MEMF_ANY)) ||
  Read(in.file, in.buf, in.size) != in.size ||
  !(out.buf = out.pos = (STRPTR) AllocRemember(&remember, out.size, MEMF_ANY)))
    End(RETURN_FAIL);

  {
    STRPTR ptr = FilePart(args.FDFILE);
    LONG len = strlen(ptr)-7;
    if(len >= 0 && !stricmp(ptr+len, "_lib.fd"))
    {
      ShortBaseName = ptr;
      ptr[len] = '\0';
    }
  }

  MakeLines(in.pos, in.size, 0);

  if(args.CLIB)
  {
    Close(in.file);
    if(!(in.file = Open(args.CLIB, MODE_OLDFILE)) ||
    !ExamineFH(in.file, fib) ||
    !(clibbuf = (STRPTR) AllocRemember(&remember, (clibsize = fib->fib_Size)+1, MEMF_ANY)) ||
    Read(in.file, clibbuf, clibsize) != clibsize)
      End(RETURN_FAIL);
    MakeLines(clibbuf, clibsize, 1);
  }
  Close(in.file); in.file = 0;

  if(args.TO)
  {
    if((lock = Lock(args.TO, SHARED_LOCK)) && Examine(lock, fib) &&
    fib->fib_DirEntryType > 0)
    {
      oldfh = CurrentDir(lock);
      args.TO = 0;
    }
    else if(lock)
    {
      UnLock(lock);
      lock = 0;
    }
  }

  if(*args.SPECIAL == 50)
  {
    STRPTR ptr = FilePart(args.FDFILE), ptr2;

    for(ptr2 = ptr; *ptr2 && *ptr2 != '_' && *ptr2 != '.'; ++ptr2)
      ;
    if(ptr2 != ptr)
    {
      ShortBaseName = ptr;
      *ptr2 = '\0';
    }
    if(!CreateFDFile(args.FDFILE, args.TO))
      End(RETURN_FAIL);
    End(RETURN_OK);
  }

  if(args.EXTERNC)	Flags ^= FLAG_EXTERNC;
  if(args.STORMFD)	Flags ^= FLAG_STORMFD;
  if(args.COMMENT)	Flags ^= FLAG_DOCOMMENT;
  if(args.PRIVATE)	Flags ^= FLAG_PRIVATE;
  if(args.USESYSCALL) 	Flags ^= FLAG_SYSCALL;

  if(!ScanFDFile())
    End(RETURN_FAIL);

  if(*args.SPECIAL < 10)
  {
    mode = MODUS_PRAGMA;
    sprintf(filename, "%s_lib.h", ShortBaseName);

    if(!args.LIBTAGS && !args.AMITAGS && !args.LIBCALL && !args.AMICALL &&
    !*args.SPECIAL)
      *args.SPECIAL = 6;

    switch(*args.SPECIAL)
    {
      case 0: break;
      case 1: MODE = 2; AMICALL = ""; break;
      case 2: sprintf(filename, "%s_pragmas.h", ShortBaseName);
	      MODE = 3; LIBCALL = ""; break;
      case 3: sprintf(filename, "%s_pragmas.h", ShortBaseName);
	      MODE = 3; LIBCALL = ""; LIBTAGS = "def " TEXT_SAS_60; break;
      case 4: MODE = 1; AMICALL = ""; break;
      case 5: MODE = 1; AMICALL = AMITAGS = ""; break;
      case 6: MODE = 1; AMICALL = " defined(" TEXT_AZTEC ") || defined("
	TEXT_MAXON ") || defined(" TEXT_STORM ")";
	LIBCALL = " defined(" TEXT_DICE ") || defined(" TEXT_SAS ")";
	LIBTAGS = "def " TEXT_SAS_60; AMITAGS ="def " TEXT_STORM; break;
      default: mode = MODUS_ERROR;
    }

    if(args.AMICALL)		AMICALL = args.AMICALL;
    if(args.LIBCALL)		LIBCALL = args.LIBCALL;
    if(args.AMITAGS)		AMITAGS = args.AMITAGS;
    if(args.LIBTAGS)		LIBTAGS = args.LIBTAGS;

    if(args.MODE && *args.MODE > 0 && *args.MODE < 5)
      MODE = *args.MODE;
  }
  else if(*args.SPECIAL < 20)
  {
    if(args.MODE && *args.MODE > 0 && *args.MODE < 4)
      MODE = *args.MODE - 1;
    switch(*args.SPECIAL)
    {
    case 10: mode = MODUS_CSTUB;
      sprintf(filename, "%s_cstub.h", ShortBaseName); break;
    case 11: mode = MODUS_STUBTEXT;
      sprintf(filename, "%s_stub.a", ShortBaseName); break;
    case 12: mode = MODUS_STUBCODE;
      sprintf(filename, "%s.lib", ShortBaseName); break;
    case 13: Flags |= FLAG_LOCALREG; /* no break ! */
    case 14: mode = MODUS_LOCALDATA;
      sprintf(filename, "%s_loc.h", ShortBaseName); args.TO = 0; break;
    case 15: mode = MODUS_STUBTEXT; MODE = 0; Flags ^= FLAG_PASCAL;
      sprintf(filename, "%s_stub.a", ShortBaseName); break;
    case 16: mode = MODUS_STUBCODE; MODE = 0; Flags ^= FLAG_PASCAL;
      sprintf(filename, "%s.lib", ShortBaseName); break;
    default: mode = MODUS_ERROR;
    }
  }
  else if(*args.SPECIAL < 30)
  {
    switch(*args.SPECIAL)
    {
    case 20: case 22: mode = MODUS_LVO+*args.SPECIAL-20;
      sprintf(filename, "%s_lvo.i", ShortBaseName); break;
    case 21: case 23: mode = MODUS_LVO+*args.SPECIAL-20;
      sprintf(filename, "%s_lib.i", ShortBaseName); break;
    default: mode = MODUS_ERROR;
    }
  }
  else if(*args.SPECIAL < 40)
  {
    if(*args.SPECIAL < 36)
    {
      mode = MODUS_PROTO+*args.SPECIAL-30;
      sprintf(filename, "%s.h", ShortBaseName);
    }
    else
      mode = MODUS_ERROR;
  }

  if(mode == MODUS_ERROR)
  {
    SetIoErr(ERROR_TOO_MANY_ARGS);
    End(RETURN_FAIL);
  }

  if(!mode)
  {
    SetIoErr(ERROR_REQUIRED_ARG_MISSING);
    End(RETURN_FAIL);
  }

  if(!args.TO)
    args.TO = filename;

  if(args.HEADER)
  {
    if(!*args.HEADER)
      args.HEADER = args.TO;
    if(!(in.file = Open(args.HEADER, MODE_OLDFILE)) ||
    !ExamineFH(in.file, fib) ||
    !(HEADER = (STRPTR) AllocRemember(&remember, (headersize = fib->fib_Size)+1, MEMF_ANY|MEMF_CLEAR)) ||
    Read(in.file, HEADER, headersize) != headersize)
      End(RETURN_FAIL);
    if(args.HEADER == args.TO)
      FindHeader();
    Close(in.file);
    in.file = 0;
  }

  if(!(out.file = Open(args.TO, MODE_NEWFILE)))
    End(RETURN_FAIL);

  SetIoErr(0);

  if(mode == MODUS_PRAGMA)
    mode = CreatePragmaFile();
  else if(mode == MODUS_CSTUB)
    mode = CreateCSTUBSFile();
  else if(mode == MODUS_LOCALDATA)
    mode = CreateLocalData();
  else if(mode >= MODUS_PROTO)
    mode = CreateProtoFile(mode-MODUS_PROTO+1);
  else if(mode >= MODUS_LVO)
    mode = CreateLVOFile(mode-MODUS_LVO+1);
  else if(mode) 			/* MODUS_STUBTEXT starts with 1 */
    mode = CreateAsmStubs(mode);

  if(!mode)
  {
    DoError(Output_Error ? ERR_UNKNOWN_ERROR : ERR_WRITING_FILE, 0);
    End(RETURN_FAIL);
  }

  End(RETURN_OK);
}

void end(void)
{
  if(fib)		FreeDosObject(DOS_FIB, fib);
  if(in.file)		Close(in.file);
  if(out.file)
  {
    Out(out.size);	/* clears Output-Buffer */
    Close(out.file);
  }
  if(oldfh)		CurrentDir(oldfh);
  if(lock)		UnLock(lock);
  if(remember)		FreeRemember(&remember, TRUE);
  if(IntuitionBase)	CloseLibrary((struct Library *) IntuitionBase);
  if(rda)
  {
    FreeArgs(rda);
    FreeDosObject(DOS_RDARGS, rda);
  }
}
