/******************************************************************************
 *
 * fd2inline
 *
 * Should be able to parse CBM fd files and generate vanilla inline calls
 * for gcc. Works as a filter.
 *
 * by Wolfgang Baron, all rights reserved.
 *
 * improved, updated, simply made workable by Rainer F. Trunz 1/95
 *
 * Completely reworked Version, cleaned up and removed a whole lot of bugs
 * found by Kamil Iskra.
 *
 * Expect miracles now (hopefully...). Ok, I am just kidding :)
 *
 * Version 0.99a by Rainer F. Trunz 6/95
 *
 * Version 0.99b and later by Kamil Iskra.
 *
 * Version 1.12 by Ralph Schmidt
 *              PowerUP GCC/SAS extensions
 *              I hope Kamil Iskra integrates that
 *              in the official ADE version.
 *
 ******************************************************************************/

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

/******************************************************************************
 * The program has a few sort of class definitions, which are the result of
 * object oriented thinking, to be imlemented in plain C. I just haven't
 * had the time to learn C++ or install the compiler. The design does however
 * improve robustness, which allows the source to be used over and over again.
 * if you use this code, please leave a little origin note.
 ******************************************************************************/

const static char version_str[]="$VER: fd2inline " VERSION " (12.12.97)";

/******************************************************************************
 * These are general definitions including types for defining registers etc.
 ******************************************************************************/

#ifdef DEBUG
#define DBP(a) a
#else /* !DEBUG */
#define DBP(a)
#endif /* !DEBUG */

#if (defined(__GNUC__) || defined(__SASC)) && 0
#define INLINE __inline /* Gives 20% *larger* executable with GCC?! */
#else
#define INLINE
#endif

#define REGS 16  /* d0=0,...,a7=15 */
#define FDS 1000

typedef enum
{
	d0, d1, d2, d3, d4, d5, d6, d7, a0, a1, a2, a3, a4, a5, a6, a7, illegal
} regs;

typedef unsigned char uchar, shortcard;
typedef unsigned long ulong;

#ifndef	FALSE
#define	FALSE	0
#endif
#ifndef	TRUE
#define	TRUE	1
#endif

typedef enum { false, nodef, real_error } Error;

enum {NEW, OLD, STUBS, PROTO, PRAGMA} output_mode=NEW;

short	PowerUP=0;

char BaseName[32], BaseNamU[32],  BaseNamL[32];

const static char *LibExcTable[]=
{
	"BattClockBase",			"Node",
	"BattMemBase",				"Node",
	"ConsoleDevice",			"Device",
	"DiskBase",				"DiskResource",
	"DOSBase",				"DosLibrary",
	"SysBase",				"ExecBase",
	"ExpansionBase",			"ExpansionBase",
	"GfxBase",				"GfxBase",
	"InputBase",				"Device",
	"IntuitionBase",			"IntuitionBase",
	"LocaleBase",				"LocaleBase",
	"MathIeeeDoubBasBase",	"MathIEEEBase",
	"MathIeeeDoubTransBase","MathIEEEBase",
	"MathIeeeSingBasBase",	"MathIEEEBase",
	"MathIeeeSingTransBase","MathIEEEBase",
	"MiscBase",				"Node",
	"PotgoBase",				"Node",
	"RamdriveDevice",			"Device",
	"RealTimeBase",				"RealTimeBase",
	"RexxSysBase",				"RxsLib",
	"TimerBase",				"Device",
	"UtilityBase",				"UtilityBase"
};
const char *StdLib; /* global lib-name ptr */

/*******************************************
 * just some support functions, no checking
 *******************************************/

char*
NewString(char** new, const char* old)
{
	const char *high;
	ulong len;

	while (*old && (*old==' ' || *old=='\t'))
		old++;
	len=strlen(old);
	for (high=old+len-1; high>=old && (*high==' ' || *high=='\t'); high--);
	high++;
	len=high-old;
	*new=malloc(1+len);
	if (*new)
	{
		strncpy(*new, old, len);
		(*new)[len]='\0';
	}
	else
		fprintf(stderr, "no mem for string\n");
	return *new;
}

static INLINE void
illparams(const char* funcname)
{
	fprintf(stderr, "%s: illegal Parameters\n", funcname);
}

static INLINE const char*
RegStr(regs reg)
{
	const static char *myregs[]=
	{
		"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
		"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "illegal"
	};

	if (reg>illegal)
		reg=illegal;
	if (reg<d0)
		reg=d0;
	return myregs[reg];
}

/******************************************************************************
 *    StrNRBrk
 *
 * searches string in from position at downwards, as long as in does not
 * contain any character in not.
 *
 ******************************************************************************/

const char*
StrNRBrk(const char* in, const char* not, const char* at)
{
	const char *chcheck;
	Error ready;

	chcheck=""; /* if at<in, the result will be NULL */
	for (ready=false; ready==false && at>=in;)
	{
		for (chcheck=not; *chcheck && *chcheck != *at; chcheck++);
		if (*chcheck)
			ready=real_error;
		else
			at--;
	}
	DBP(fprintf(stderr, "{%c}", *chcheck));
	return *chcheck ? at : NULL;
}

/*
  Our own "strupr", since it is a non-standard function.
*/
void
StrUpr(char* str)
{
	while (*str)
	{
		*str=toupper(*str);
		str++;
	}
}

/******************************************************************************
 *    CLASS fdFile
 *
 * stores a file with a temporary buffer (static length, sorry), a line number,
 * an offset (used for library offsets and an error field.
 * When there's no error, line will contain line #lineno and offset will be
 * the last offset set by the interpretation of the last line. If there's been
 * no ##bias line, this field assumes a bias of 30, which is the standard bias.
 * It is assumed offsets are always negative.
 ******************************************************************************/

#define fF_BUFSIZE 1024

/* all you need to know about an fdFile you parse */

typedef struct
{
	FILE*	file;					/* the file we're reading from		*/
	char	line[fF_BUFSIZE];	/* the current line						*/
	ulong	lineno;				/* current line number					*/
	long	offset;				/* current fd offset (-bias)			*/
	Error	error;				/* is everything o.k.					*/
	char	private;				/* are we in ##public or ##private	*/
} fdFile;

fdFile*
fF_ctor			(const char* fname);
static void
fF_dtor			(fdFile* obj);
static void
fF_SetError		(fdFile* obj, Error error);
static void
fF_SetOffset	(fdFile* obj, long at);
Error
fF_readln		(fdFile* obj);
static Error
fF_GetError		(const fdFile* obj);
static long
fF_GetOffset	(const fdFile* obj);
char*
fF_FuncName		(fdFile* obj); /* return name or null */
static void
fF_SetPrivate	(fdFile* obj, char priv);
static char
fF_GetPrivate	(const fdFile* obj);

static INLINE void
fF_dtor(fdFile* obj)
{
  fclose(obj->file);
  free(obj);
}

static INLINE void
fF_SetError(fdFile* obj, Error error)
{
	if (obj)
		obj->error=error;
	else
		illparams("fF_SetError");
}

static INLINE void
fF_SetOffset(fdFile* obj, long at)
{
	if (obj)
		obj->offset= at;
	else
		illparams("fFSetOffset");
}

static INLINE void
fF_SetPrivate(fdFile* obj, char priv)
{
  if (obj)
    obj->private=priv;
  else
    illparams("fF_SetPrivate");
}

fdFile*
fF_ctor(const char* fname)
{
	fdFile *result;

	if (fname)
	{
		result=malloc(sizeof(fdFile));
		if (result)
		{
			result->file=fopen(fname, "r");
			if (result->file)
			{
				result->lineno=0;
				fF_SetOffset(result, -30);
				fF_SetError(result, false);
				fF_SetPrivate(result, 0);
				result->line[0]='\0';
			}
			else
			{
				free(result);
				result=NULL;
			}
		}
	}
	else
	{
		result=NULL;
		illparams("fF_ctor");
	}
	return result;
}

Error
fF_readln(fdFile* obj)
{
	char *low, *bpoint;
	long glen,	/* the length we read until now */
	len;			/* the length of the last segment */

	if (obj)
	{
		low=obj->line;
		glen=0;

		for (;;)
		{
			obj->lineno++;
			if (!fgets(low, fF_BUFSIZE-1-glen, obj->file))
			{
				fF_SetError(obj, real_error);
				obj->line[0]='\0';
				return real_error;
			}
			if (low==strpbrk(low, "*#/"))
			{
				DBP(fprintf(stderr, "in# %s\n", obj->line));
				return false;
			}
			len=strlen(low);
			bpoint=low+len-1;
			while (len && isspace(*bpoint))
			{
				bpoint--;
				len--;
			}
			if (*bpoint==';' || *bpoint==')')
			{
				DBP(fprintf(stderr, "\nin: %s\n", obj->line));
				return false;
			}
			glen+=len;
			low+=len;
			if (glen>=fF_BUFSIZE-10) /* somewhat pessimistic? */
			{
				fF_SetError(obj, real_error);
				fprintf(stderr, "line %lu too long.\n", obj->lineno);
				return real_error;
			}
			DBP(fprintf(stderr, "+"));
		}
	}
	illparams("fF_readln");
	return real_error;
}

static INLINE Error
fF_GetError(const fdFile* obj)
{
	if (obj)
		return obj->error;
	illparams("fF_GetError");
	return real_error;
}

static INLINE long
fF_GetOffset(const fdFile* obj)
{
	if (obj)
		return obj->offset;
	illparams("fF_GetOffset");
	return -1;
}

/******************************************************************************
 * fF_FuncName
 *
 * checks if it can find a function-name and return it's address, or NULL
 * if the current line does not seem to contain one. The return value will
 * be a pointer into a malloced buffer, thus the caller will have to free().
 ******************************************************************************/

char*
fF_FuncName(fdFile* obj)
{
	const char *lower;
	const char *upper;
	char *buf;
	long obraces;	/* count of open braces */
	Error ready;	/* ready with searching */

	if (!obj || fF_GetError(obj)==real_error)
	{
		illparams("fF_FuncName");
		return NULL;
	}

	lower=obj->line;
	while (*lower && (*lower==' ' || *lower=='\t'))
		lower++;

	if (!*lower || (!isalpha(*lower) && *lower!='_'))
	{
		fF_SetError(obj, nodef);
		return NULL;
	}

	while (*lower)
	{
		if (!isalnum(*lower) && !isspace(*lower) && *lower!='*' && *lower!=','
		&& *lower!='.' && *lower!=';' && *lower!='(' && *lower!=')' &&
		*lower!='[' && *lower!=']' && *lower!='_' && *lower!='\\')
		{
			fF_SetError(obj, nodef);
			return NULL;
		}
		lower++;
	}

	lower=NULL;
	buf=NULL;

	if (obj && fF_GetError(obj)==false)
	{
		if ((upper=strrchr(obj->line, ')'))!=0)
		{
			DBP(fprintf(stderr, "end:%s:", upper));

			for (obraces=1, ready=false; ready==false; upper=lower)
			{
				lower=StrNRBrk(obj->line, "()", --upper);
				if (lower)
				{
					switch (*lower)
					{
						case ')':
							obraces++;
							DBP(fprintf(stderr, " )%ld%s", obraces, lower));
							break;
						case '(':
							obraces--;
							DBP(fprintf(stderr, " (%ld%s", obraces, lower));
							if (!obraces)
								ready=nodef;
							break;
						default:
							fprintf(stderr, "faulty StrNRBrk\n");
					}
				}
				else
				{
					fprintf(stderr, "'(' or ')' expected in line %lu.\n",
						obj->lineno);
					ready=real_error;
				}
			}
			if (ready==nodef) /* we found the matching '(' */
			{
				long newlen;
				const char* name;

				upper--;

				while (upper>=obj->line && (*upper==' ' || *upper=='\t'))
					upper--;

				lower=StrNRBrk(obj->line, " \t*)", upper);

				if (!lower)
					lower=obj->line;
				else
					lower++;

				for (name=lower; name<=upper; name++)
					if (!isalnum(*name) && *name!='_')
					{
						fF_SetError(obj, nodef);
						return NULL;
					}

				newlen=upper-lower+1;
				buf=malloc(newlen+1);

				if (buf)
				{
					strncpy(buf, lower, newlen);
					buf[newlen]='\0';
				}
				else
					fprintf(stderr, "no mem for fF_FuncName");
			}
		}
	}
	else
		illparams("fF_FuncName");
	return buf;
}

static INLINE char
fF_GetPrivate(const fdFile* obj)
{
	if (obj)
		return obj->private;
	illparams("fF_GetPrivate");
	return 0;
}

/*********************
 *    CLASS fdDef    *
 *********************/

typedef struct
{
	char*	name;
	char*	type;
	long	offset;
	regs	reg[REGS];
	char*	param[REGS];
	char*	proto[REGS];
	regs	funcpar; /* number of argument that has type "pointer to function" */
} fdDef;

fdDef*
fD_ctor				(void);
void
fD_dtor				(fdDef* obj);
static void
fD_NewName			(fdDef* obj, const char* newname);
void
fD_NewParam			(fdDef* obj, shortcard at, const char* newstr);
void
fD_NewProto			(fdDef* obj, shortcard at, char* newstr);
static void
fD_NewReg			(fdDef* obj, shortcard at, regs reg);
static void
fD_NewType			(fdDef* obj, const char* newstr);
static void
fD_SetOffset		(fdDef* obj, long off);
Error
fD_parsefd			(fdDef* obj, fdFile* infile);
Error
fD_parsepr			(fdDef* obj, fdFile* infile);
static const char*
fD_GetName			(const fdDef* obj);
static long
fD_GetOffset		(const fdDef* obj);
static const char*
fD_GetParam			(const fdDef* obj, shortcard at);
static regs
fD_GetReg			(const fdDef* obj, shortcard at);
static const char*
fD_GetRegStr		(const fdDef* obj, shortcard at);
static const char*
fD_GetType			(const fdDef* obj);
static shortcard
fD_ParamNum			(const fdDef* obj);
static shortcard
fD_ProtoNum			(const fdDef* obj);
static shortcard
fD_RegNum			(const fdDef* obj);
int
fD_cmpName			(const void* big, const void* small);
void
fD_write				(FILE* outfile, const fdDef* obj);
static shortcard
fD_GetFuncParNum	(const fdDef* obj);
static void
fD_SetFuncParNum	(fdDef* obj, shortcard at);
static void
fD_adjustargnames(fdDef *obj);

fdDef **arrdefs;
long fds;

char *fD_nostring="";

fdDef*
fD_ctor(void)
{
	fdDef *result;
	regs count;

	result=malloc(sizeof(fdDef));

	if (result)
	{
		result->name=fD_nostring;
		result->type=fD_nostring;
		result->funcpar=illegal;

		for (count=d0; count<illegal; count++ )
		{
			result->reg[count]=illegal;
			result->param[count]=fD_nostring; /* if (!strlen) dont't free() */
			result->proto[count]=fD_nostring;
		}
	}
	return result;
}

/* free all resources and make the object as illegal as possible */

void
fD_dtor(fdDef* obj)
{
	regs count;

	if (obj)
	{
		if (!obj->name)
			fprintf(stderr, "fD_dtor: null name");
		else
			if (obj->name!=fD_nostring)
				free(obj->name);

		if (!obj->type)
			fprintf(stderr, "fD_dtor: null type");
		else
			if (obj->type!=fD_nostring)
				free(obj->type);

		obj->name=obj->type=NULL;

		for (count=d0; count<illegal; count++)
		{
			obj->reg[count]=illegal;

			if (!obj->param[count])
				fprintf(stderr, "fD_dtor: null param");
			else
				if (obj->param[count]!=fD_nostring)
					free(obj->param[count]);

			if (!obj->proto[count])
				fprintf(stderr, "fD_dtor: null proto");
			else
				if (obj->proto[count]!=fD_nostring)
					free(obj->proto[count]);

			obj->param[count]=obj->proto[count]=NULL;
		}

		free(obj);
	}
	else
		fprintf(stderr, "fd_dtor(NULL)\n");
}

static INLINE void
fD_NewName(fdDef* obj, const char* newname)
{
	if (obj && newname)
	{
		if (obj->name && obj->name!=fD_nostring)
			free(obj->name);
		if (!NewString(&obj->name, newname))
			obj->name=fD_nostring;
	}
	else
		illparams("fD_NewName");
}

void
fD_NewParam(fdDef* obj, shortcard at, const char* newstr)
{
	char *pa;

	if (newstr && obj && at<illegal)
	{
		pa=obj->param[at];

		if (pa && pa!=fD_nostring)
			free(pa);

		while (*newstr==' ' || *newstr=='\t')
			newstr++;

		if (NewString(&pa, newstr))
			obj->param[at]=pa;
		else
			obj->param[at]=fD_nostring;
	}
	else
		illparams("fD_NewParam");
}

/* get first free *reg or illegal */

static INLINE shortcard
fD_RegNum(const fdDef* obj)
{
	shortcard count;

	if (obj)
	{
		for (count=d0; count<illegal && obj->reg[count]!=illegal; count++);
		return count;
	}
	else
	{
		illparams("fD_RegNum");
		return illegal;
	}
}

static INLINE void
fD_NewReg(fdDef* obj, shortcard at, regs reg)
{
	if (obj && at<illegal && reg>=d0 && reg<=illegal)
		obj->reg[at]=reg;
	else
		illparams("fD_NewReg");
}

static INLINE regs
fD_GetReg(const fdDef* obj, shortcard at)
{
	if (obj && at<illegal)
		return obj->reg[at];
	else
	{
		illparams("fD_GetReg");
		return illegal;
	}
}

static INLINE shortcard
fD_GetFuncParNum(const fdDef* obj)
{
	if (obj)
		return (shortcard)obj->funcpar;
	else
	{
		illparams("fD_GetFuncParNum");
		return illegal;
	}
}

static INLINE void
fD_SetFuncParNum(fdDef* obj, shortcard at)
{
	if (obj && at<illegal)
		obj->funcpar=at;
	else
		illparams("fD_SetFuncParNum");
}

void
fD_NewProto(fdDef* obj, shortcard at, char* newstr)
{
	char *pr;

	if (newstr && obj && at<illegal)
	{
		char *t, arr[200]; /* I hope 200 will be enough... */
		int numwords=1;
		pr=obj->proto[at];

		if (pr && pr!=fD_nostring)
			free(pr);

		while (*newstr==' ' || *newstr=='\t')
			newstr++; /* Skip leading spaces */

		t=arr;
		while ((*t++=*newstr)!=0)
		{
			/* Copy the rest, counting number of words */
			if ((*newstr==' ' || *newstr=='\t') && newstr[1] && newstr[1]!=' ' &&
			newstr[1]!='\t')
				numwords++;
			newstr++;
		}

		t=arr+strlen(arr)-1;
		while (*t==' ' || *t=='\t')
			t--;
		t[1]='\0'; /* Get rid of tailing spaces */
		if (at!=fD_GetFuncParNum(obj))
		{
			if (numwords>1) /* One word - must be type */
				if (*t!='*')
				{
					/* '*' on the end - no parameter name used */
					while (*t!=' ' && *t!='\t' && *t!='*')
						t--;
					t++;
					if (strcmp(t, "char") && strcmp(t, "short") && strcmp(t, "int")
					&& strcmp(t, "long"))
					{
						/* Not one of applicable keywords - must be parameter name.
							Get rid of it. */
						t--;
						while (*t==' ' || *t=='\t')
							t--;
						t[1]='\0';
					}
				}
		}
		else
		{
			/* Parameter of type "pointer to function". */
			char *end;
			t=strchr(arr, '(');
			while (*t++!='*');
			end=strchr(t, ')');
			memmove(t+2, end, strlen(end)+1);
			*t='%';
			t[1]='s';
		}

		if (NewString(&pr, arr))
		{
			obj->proto[at]=pr;
			while (*pr==' ' || *pr=='\t')
				pr++;
			if (!stricmp(pr, "double"))
			{
				/* "double" needs two data registers */
				int count, regs=fD_RegNum(obj);
				for (count=at+1; count<regs; count++)
					fD_NewReg(obj, count, fD_GetReg(obj, count+1));
			}
		}
		else
			obj->proto[at]=fD_nostring;
	}
	else
		illparams("fD_NewProto");
}

static INLINE void
fD_NewType(fdDef* obj, const char* newtype)
{
	if (obj && newtype)
	{
		if (obj->type && obj->type!=fD_nostring)
			free(obj->type);
		if (!NewString(&obj->type, newtype))
			obj->type=fD_nostring;
	}
	else
		illparams("fD_NewType");
}

static INLINE void
fD_SetOffset(fdDef* obj, long off)
{
	if (obj)
		obj->offset=off;
	else
		illparams("fD_SetOffset");
}

static INLINE const char*
fD_GetName(const fdDef* obj)
{
	if (obj && obj->name)
		return obj->name;
	else
	{
		illparams("fD_GetName");
		return fD_nostring;
	}
}

static INLINE long
fD_GetOffset(const fdDef* obj)
{
	if (obj)
		return obj->offset;
	else
	{
		illparams("fD_GetOffset");
		return 0;
	}
}

static INLINE const char*
fD_GetProto(const fdDef* obj, shortcard at)
{
	if (obj && at<illegal && obj->proto[at])
		return obj->proto[at];
	else
	{
		illparams("fD_GetProto");
		return fD_nostring;
	}
}

static INLINE const char*
fD_GetParam(const fdDef* obj, shortcard at)
{
	if (obj && at<illegal && obj->param[at])
		return obj->param[at];
	else
	{
		illparams("fD_GetParam");
		return fD_nostring;
	}
}

static INLINE const char*
fD_GetRegStr(const fdDef* obj, shortcard at)
{
	if (obj && at<illegal)
		return RegStr(obj->reg[at]);
	else
	{
		illparams("fD_GetReg");
		return RegStr(illegal);
	}
}

static INLINE const char*
fD_GetType(const fdDef* obj)
{
	if (obj && obj->type)
		return obj->type;
	else
	{
		illparams("fD_GetType");
		return fD_nostring;
	}
}

/* get first free param or illegal */

static INLINE shortcard
fD_ParamNum(const fdDef* obj)
{
	shortcard count;

	if (obj)
	{
		for (count=d0; count<illegal && obj->param[count]!=fD_nostring;
		count++);
		return count;
	}
	else
	{
		illparams("fD_ParamNum");
		return illegal;
	}
}

static INLINE shortcard
fD_ProtoNum(const fdDef* obj)
{
	shortcard count;

	if (obj)
	{
		for (count=d0; count<illegal && obj->proto[count]!=fD_nostring;
		count++);
		return count;
	}
	else
	{
		illparams("fD_ProtoNum");
		return illegal;
	}
}

/******************************************************************************
 *    fD_parsefd
 *
 *  parse the current line. Needs to copy input, in order to insert \0's
 *  RETURN
 *    fF_GetError(infile):
 *	false = read a definition.
 *	nodef = not a definition on line (so try again)
 *	error = real error
 ******************************************************************************/

Error
fD_parsefd(fdDef* obj, fdFile* infile)
{
	enum parse_info { name, params, regs, ready } parsing;
	char *buf, *bpoint, *bnext;
	ulong index;

	if (obj && infile && fF_GetError(infile)==false)
	{
		parsing=name;

		if (!NewString(&buf, infile->line))
		{
			fprintf(stderr, "no mem for line %lu\n", infile->lineno);
			fF_SetError(infile, real_error);
		}
		bpoint=buf; /* so -Wall keeps quiet */

		/* try to parse the line until there's an error or we are done */

		while (parsing!=ready && fF_GetError(infile)==false)
		{
			switch (parsing)
			{
				case name:
					switch (buf[0])
					{
						case '#':
							if (strncmp("##base", buf, 6)==0)
							{
								bnext=buf+6;
								while (*bnext==' ' || *bnext=='\t' || *bnext=='_')
									bnext++;
								strcpy(BaseName, bnext);
								BaseName[strlen(BaseName)-1]='\0';
							}
							else
								if (strncmp("##bias", buf, 6)==0)
								{
									if (!sscanf(buf+6, "%ld", &infile->offset))
									{
										fprintf(stderr, "illegal ##bias in line %lu: %s\n",
											infile->lineno, infile->line);
										fF_SetError(infile, real_error);
										break; /* avoid nodef */
									}
									else
									{
										if (fF_GetOffset(infile)>0)
											fF_SetOffset(infile, -fF_GetOffset(infile));
										DBP(fprintf(stderr, "set offset to %ld\n",
											fFGetOffset(infile)));
									}
								}
								else
								{
									if (strncmp("##private", buf, 9)==0)
										fF_SetPrivate(infile, 1);
									else
										if (strncmp("##public", buf, 8)==0)
											fF_SetPrivate(infile, 0);
								}
							/* drop through for error comment */

						case '*':
							/* try again somewhere else */
							fF_SetError(infile, nodef);
								break;

						default:
							/* assume a regular line here */
							if (fF_GetPrivate(infile))
							{
								/* don't store names of privates */
								fF_SetError(infile, nodef);
								fF_SetOffset(infile, fF_GetOffset(infile)-6);
								break;
							}
							parsing=name; /* switch (parsing) */
							for (index=0; buf[index] && buf[index]!='('; index++);

							if (!buf[index])
							{
								/* oops, no fd ? */
								fprintf(stderr, "not an fd, line %lu: %s\n",
									infile->lineno, buf /* infile->line */);
								fF_SetError(infile, nodef);
							} /* maybe next time */
							else
							{
								buf[index]=0;

								fD_NewName(obj, buf);
								fD_SetOffset(obj, fF_GetOffset(infile));

								bpoint=buf+index+1;
								parsing=params; /* continue the loop */
							}
					}
					break;

				case params:
				{
					char *bptmp; /* needed for fD_NewParam */

					/* look for parameters now */

					for (bnext = bpoint; *bnext && *bnext!=',' && *bnext!=')';
					bnext++);

					if (*bnext)
					{
						bptmp=bpoint;

						if (*bnext == ')')
						{
							if (bnext[1] != '(')
							{
								fprintf(stderr, "registers expected in line %lu: %s\n",
									infile->lineno, infile->line);
								fF_SetError(infile, nodef);
							}
							else
							{
								parsing=regs;
								bpoint=bnext+2;
							}
						}
						else
							bpoint = bnext+1;

						/* terminate string and advance to next item */

						*bnext='\0';
						fD_NewParam(obj, fD_ParamNum(obj), bptmp);
					}
					else
					{
						fF_SetError(infile, nodef);
						fprintf(stderr, "param expected in line %lu: %s\n",
							infile->lineno, infile->line);
					}
					break;  /* switch parsing */
				}

				case regs:
					/* look for parameters now */

					for (bnext=bpoint; *bnext && *bnext!='/' && *bnext!=',' &&
					*bnext!=')'; bnext++);

					if (*bnext)
					{
						if (')'==*bnext)
						{
							/* wow, we've finished */
							fF_SetOffset(infile, fF_GetOffset(infile)-6);
							parsing=ready;
						}
						*bnext = '\0';

						bpoint[0]=tolower(bpoint[0]);

						if ((bpoint[0]=='d' || bpoint[0]=='a') && bpoint[1]>='0' &&
						bpoint[1]<='8' && bnext==bpoint+2)
							fD_NewReg(obj, fD_RegNum(obj),
								bpoint[1]-'0'+(bpoint[0]=='a'? 8 : 0));
						else
							if (bnext!=bpoint)
							{
								/* it is when our function is void */
								fprintf(stderr, "illegal register %s in line %ld\n",
									bpoint, infile->lineno);
								fF_SetError(infile, nodef);
							}
						bpoint = bnext+1;
					}
					else
					{
						fF_SetError(infile, nodef);
						fprintf(stderr, "reg expected in line %lu\n",
							infile->lineno);
					}
					break; /* switch parsing */

				case ready:
					fprintf(stderr, "internal error, use another compiler.\n");
					break;
			}
		}

		free(buf);
		return fF_GetError(infile);
	}
	else
	{
		illparams("fD_parsefd");
		return real_error;
	}
}

static void
fD_adjustargnames(fdDef *obj)
{
	int parnum;

	if (output_mode==NEW)
	{

		/* For #define-base output mode, we have to check if argument names are not
			the same as some words in type names. We check from the first argument
			to the last, resolving conflicts by changing argument names, if
			necessary. */

		for (parnum=0; parnum<fD_ParamNum(obj); parnum++)
		{
			const char *parname=fD_GetParam(obj, parnum);
			int finished;
			do
			{
				int num;
				const char *type=fD_GetType(obj);
				char *str;
				finished=1;

				if ((str=strstr(type, parname))!=0 && (str==type ||
				(!isalnum(str[-1]) && str[-1]!='_')) &&
				(!*(str+=strlen(parname)) || (!isalnum(*str) && *str!='_')))
				{
					char buf[300]; /* Hope will be enough... */
					strcpy(buf, parname);
					strcat(buf, "_");
					fD_NewParam(obj, parnum, buf);
					finished=0;
				}
				else
					for (num=0; num<fD_ParamNum(obj); num++)
					{
						const char *name=fD_GetParam(obj, num);
						const char *proto=fD_GetProto(obj, num);
						if ((num<parnum && strcmp(name, parname)==0) ||
						((str=strstr(proto, parname))!=0 && (str==proto ||
						(!isalnum(str[-1]) && str[-1]!='_')) &&
						(!*(str+=strlen(parname)) || (!isalnum(*str) && *str!='_'))))
						{
							char buf[300]; /* Hope will be enough... */
							strcpy(buf, parname);
							strcat(buf, "_");
							fD_NewParam(obj, parnum, buf);
							finished=0;
							break;
						}
					}
			} while (!finished);
		}
	}
}

Error
fD_parsepr(fdDef* obj, fdFile* infile)
{
	char	*buf;		/* a copy of infile->line							*/
	char	*bpoint,	/* cursor in buf										*/
			*bnext,	/* looking for the end								*/
			*lowarg;	/* beginning of this argument						*/
	long	obraces;	/* count of open braces								*/
	regs	count,	/* count parameter number							*/
			args;		/* the number of arguments for this function */

	if (!(obj && infile && fF_GetError(infile)==false))
	{
		illparams("fD_parsepr");
		fF_SetError(infile, real_error);
		return real_error;
	}
	if (!NewString(&buf, infile->line))
	{
		fprintf(stderr, "no mem for fD_parsepr\n");
		fF_SetError(infile, real_error);
		return real_error;
	}
	fF_SetError(infile, false);

	bpoint=strchr(buf, '(');
	while (--bpoint>=buf && strstr(bpoint, fD_GetName(obj))!=bpoint);
	if (bpoint>=buf)
	{
		while (--bpoint >= buf && (*bpoint==' ' || *bpoint=='\t'));
		*++bpoint='\0';

		fD_NewType(obj, buf);

		while (bpoint && *bpoint++!='('); /* one beyond '(' */

		lowarg=bpoint;
		obraces=0;

		for (count=0, args=fD_RegNum(obj); count<args; bpoint=bnext+1)
		{
			while (*bpoint && (*bpoint==' ' || *bpoint=='\t')) /* ignore spaces */
				bpoint++;

			bnext=strpbrk(bpoint, "(),");

			if (bnext)
			{
				switch (*bnext)
				{
					case '(':
						if (!obraces)
						{
							if (fD_GetFuncParNum(obj)!=illegal &&
							fD_GetFuncParNum(obj)!=count)
								fprintf(stderr, "Warning: two parameters of type "
									"pointer to function are used.\nThis is not "
									"supported!\n");
							fD_SetFuncParNum(obj, count);
						}
						obraces++;
						DBP(fprintf(stderr, "< (%ld%s >", obraces, bnext));
						break;

					case ')':
						if (obraces)
						{
							DBP(fprintf(stderr, "< )%ld%s >", obraces, bnext));
							obraces--;
						}
						else
						{
							*bnext='\0';
							DBP(fprintf(stderr, "< )0> [LAST PROTO=%s]", lowarg));
							fD_NewProto(obj, count, lowarg);
							lowarg=bnext+1;

							if (count!=args-1)
							{
								DBP(fprintf(stderr, "%s needs %u arguments and got %u.\n",
									fD_GetName(obj), args, count+1));
								fF_SetError(infile, nodef);
							}
							count++;
						}
						break;

					case ',':
						if (!obraces)
						{
							*bnext='\0';
							DBP(fprintf(stderr, " [PROTO=%s] ", lowarg));
							fD_NewProto(obj, count, lowarg);
							lowarg=bnext+1;
							count++;
						}
						break;

					default:
						fprintf(stderr, "faulty strpbrk in line %lu.\n",
							infile->lineno);
				}
			}
			else
			{
				DBP(fprintf(stderr, "faulty argument %u in line %lu.\n", count+1,
					infile->lineno));
				count=args; /* this will effectively quit the for loop */
				fF_SetError(infile, nodef);
			}
		}
		if (fD_ProtoNum(obj)!=fD_RegNum(obj))
			fF_SetError(infile, nodef);
	}
	else
	{
		fprintf(stderr, "fD_parsepr was fooled in line %lu\n", infile->lineno);
		fprintf(stderr, "function , definition %s.\n",
			/* fD_GetName(obj),*/ infile->line);
		fF_SetError(infile, nodef);
	}

	free(buf);

	fD_adjustargnames(obj);

	return fF_GetError(infile);
}

int
fD_cmpName(const void* big, const void* small) /* for qsort and bsearch */
{
	return strcmp(fD_GetName(*(fdDef**)big), fD_GetName(*(fdDef**)small));
}

const static char *TagExcTable[]=
{
	"BuildEasyRequestArgs",	"BuildEasyRequest",
	"DoDTMethodA",				"DoDTMethod",
	"DoGadgetMethodA",		"DoGadgetMethod",
	"EasyRequestArgs",		"EasyRequest",
	"MUI_MakeObjectA",		"MUI_MakeObject",
	"MUI_RequestA",			"MUI_Request",
	"PrintDTObjectA",			"PrintDTObject",
	"UMSVLog",					"UMSLog",
	"VFWritef",					"FWritef",
	"VFPrintf",					"FPrintf",
	"VPrintf",					"Printf",
};

const char*
taggedfunction(const fdDef* obj)
{
	shortcard numregs=fD_RegNum(obj);
	unsigned int count;
	const char *name=fD_GetName(obj);
	static char newname[200];	/* Hope will be enough... static because used
											out of the function. */
	const char *lastarg;
	const static char *TagExcTable2[]=
	{
		"ApplyTagChanges",
		"CloneTagItems",
		"FindTagItem",
		"FreeTagItems",
		"GetTagData",
		"PackBoolTags",
		"PackStructureTags",
		"RefreshTagItemClones",
		"UnpackStructureTags",
	};

	if (!numregs)
		return NULL;

	for (count=0; count<sizeof TagExcTable/sizeof TagExcTable[0]; count+=2)
		if (strcmp(name, TagExcTable[count])==0)
			return TagExcTable[count+1];

	for (count=0; count<sizeof TagExcTable2/sizeof TagExcTable2[0]; count++)
		if (strcmp(name, TagExcTable2[count])==0)
			return NULL;

	lastarg=fD_GetProto(obj, numregs-1);
	if (strncmp(lastarg, "struct", 6))
		return NULL;
	lastarg+=6;
	while (*lastarg==' ' || *lastarg=='\t')
		lastarg++;
	if (strncmp(lastarg, "TagItem", 7))
		return NULL;
	lastarg+=7;
	while (*lastarg==' ' || *lastarg=='\t')
		lastarg++;
	if (strcmp(lastarg, "*"))
		return NULL;

	strcpy(newname, name);
	if (newname[strlen(newname)-1]=='A')
		newname[strlen(newname)-1]='\0';
	else
		if (strlen(newname)>7 && !strcmp(newname+strlen(newname)-7, "TagList"))
			strcpy(newname+strlen(newname)-4, "s");
		else
			strcat(newname, "Tags");
	return newname;
}

const char*
aliasfunction(const char* name)
{
	const static char *AliasTable[]=
	{
		"AllocDosObject",	"AllocDosObjectTagList",
		"CreateNewProc",	"CreateNewProcTagList",
		"NewLoadSeg",		"NewLoadSegTagList",
		"System",			"SystemTagList",
	};
	unsigned int count;
	for (count=0; count<sizeof AliasTable/sizeof AliasTable[0]; count++)
		if (strcmp(name, AliasTable[count])==0)
			return AliasTable[count+(count%2 ? -1 : 1)];
	return NULL;
}

void
fD_write(FILE* outfile, const fdDef* obj)
{
	shortcard count, numregs;
	const char *chtmp, *tagname, *name, *rettype;
	int vd=0, a45=0, d7=0;

	DBP(fprintf(stderr, "func %s\n", fD_GetName(obj)));

	numregs=fD_RegNum(obj);

	if ((rettype=fD_GetType(obj))==fD_nostring)
	{
		fprintf(stderr, "%s has no prototype.\n", fD_GetName(obj));
		return;
	}
	if (!stricmp(rettype, "void"))
		vd = 1; /* set flag */
	for (count=d0; count<numregs; count++)
	{
		const char *reg=fD_GetRegStr(obj, count);
		if (strcmp(reg, "a4")==0 || strcmp(reg, "a5")==0)
			if (!a45)
				a45=(strcmp(reg, "a4") ? 5 : 4); /* set flag */
			else /* Security check */
				fprintf(stderr, "Warning: both a4 and a5 are used. This is not "
					"supported!\n");
		if (strcmp(reg, "d7")==0) /* Used only when a45!=0 */
			d7=1;
	}
	if (a45 && d7) /* Security check */
		fprintf(stderr, "Warning: d7 and a4 or a5 are used. This is not "
			"supported!\n");

	name=fD_GetName(obj);

	if (fD_ProtoNum(obj)!=numregs)
	{
		fprintf(stderr, "%s gets %d fd args and %d proto%s.\n", name, numregs,
			fD_ProtoNum(obj), fD_ProtoNum(obj)!= 1 ? "s" : "");
		return;
	}

	if ((output_mode==NEW) && (PowerUP==FALSE))
	{
		fprintf(outfile, "#define %s(", name);

		if (numregs>0)
		{
			for (count=d0; count<numregs-1; count++)
				fprintf(outfile, "%s, ", fD_GetParam(obj, count));
			fprintf(outfile, "%s", fD_GetParam(obj, count));
		}

		fprintf(outfile, ") \\\n\tLP%d%s%s%s%s(0x%lx, ", numregs,
			(vd ? "NR" : ""), (a45 ? (a45==4 ? "A4" : "A5") : ""),
			(BaseName[0] ? "" : "UB"),
			(fD_GetFuncParNum(obj)==illegal ? "" : "FP"), -fD_GetOffset(obj));
		if (!vd)
			fprintf(outfile, "%s, ", rettype);
		fprintf(outfile, "%s, ", name);

		for (count=d0; count<numregs; count++)
		{
			chtmp=fD_GetRegStr(obj, count);
			if (a45 && (strcmp(chtmp, "a4")==0 || strcmp(chtmp, "a5")==0))
				chtmp="d7";
			fprintf(outfile, "%s, %s, %s%s", (fD_GetFuncParNum(obj)==count ?
				"__fpt" : fD_GetProto(obj, count)), fD_GetParam(obj, count),
				chtmp, (count==numregs-1 && !BaseName[0] ? "" : ", "));
		}

		if (BaseName[0]) /* was "##base" used? */
			fprintf(outfile, "\\\n\t, %s_BASE_NAME", BaseNamU);
		if (fD_GetFuncParNum(obj)!=illegal)
		{
			fprintf(outfile, ", ");
			fprintf(outfile, fD_GetProto(obj, fD_GetFuncParNum(obj)), "__fpt");
		}
		fprintf(outfile, ")\n\n");
	}
	else
	if ((output_mode==NEW) && (PowerUP==TRUE))
	{
		fprintf(outfile, "#define %s(", name);

		if (numregs>0)
		{
			for (count=d0; count<numregs-1; count++)
				fprintf(outfile, "%s, ", fD_GetParam(obj, count));
			fprintf(outfile, "%s", fD_GetParam(obj, count));
		}
/* A45 could be removed..but i let it stay so the M68k and PPC functions are the
 * the same
 */
		fprintf(outfile, ") \\\n\tLP%d%s%s%s%s(0x%lx, ", numregs,
			(vd ? "NR" : ""), (a45 ? (a45==4 ? "A4" : "A5") : ""),
			(BaseName[0] ? "" : "UB"),
			(fD_GetFuncParNum(obj)==illegal ? "" : "FP"), -fD_GetOffset(obj));
		if (!vd)
			fprintf(outfile, "%s, ", rettype);
		fprintf(outfile, "%s, ", name);

		for (count=d0; count<numregs; count++)
		{
			chtmp=fD_GetRegStr(obj, count);
			if (a45 && (strcmp(chtmp, "a4")==0 || strcmp(chtmp, "a5")==0))
				chtmp="d7";
			fprintf(outfile, "%s, %s, %s%s", (fD_GetFuncParNum(obj)==count ?
				"__fpt" : fD_GetProto(obj, count)), fD_GetParam(obj, count),
				chtmp, (count==numregs-1 && !BaseName[0] ? "" : ", "));
		}

		if (BaseName[0]) /* was "##base" used? */
			fprintf(outfile, "\\\n\t, %s_BASE_NAME", BaseNamU);
		if (fD_GetFuncParNum(obj)!=illegal)
		{
			fprintf(outfile, ", ");
			fprintf(outfile, fD_GetProto(obj, fD_GetFuncParNum(obj)), "__fpt");
		}
/* Here it would make sense to create a database file to integrate optimizations
 * automaticly into every new build.
 * Not every function needs a complete flush. For example functions with no parameter
 * wouldn`t need a PPC flush normally.
 * Or Read(File,Addr,Size); would only need a flush for Addr with the Size
 */
		fprintf(outfile, ", IF_CACHEFLUSHALL, NULL, 0, IF_CACHEFLUSHALL, NULL, 0");
		fprintf(outfile, ")\n\n");

	}
	else if (output_mode==PRAGMA)
	{
		if (PowerUP==TRUE)
		{

			fprintf(outfile, "#define\t%s",
				name);

			if (numregs)
			{
				fprintf(outfile, "(");

				if (numregs>0)
				{
					for (count=d0; count<numregs-1; count++)
					{
						fprintf(outfile, "%s, ", fD_GetParam(obj, count));
					}
					fprintf(outfile, "%s", fD_GetParam(obj, count));
				}

				fprintf(outfile, ")");

				if (BaseName[0]) /* was "##base" used? */
				{
					fprintf(outfile, "\t_%s(%s_BASE_NAME%s",
						name,
						BaseNamU,
						numregs ? ", " : "");
				}
				else
				{
					fprintf(outfile, "\t_%s(",
						name);
				}

				if (numregs>0)
				{
					for (count=d0; count<numregs-1; count++)
					{
						fprintf(outfile, "%s, ", fD_GetParam(obj, count));
					}
					fprintf(outfile, "%s", fD_GetParam(obj, count));
				}

				fprintf(outfile, ")\n\n");
			}
			else
			{
				if (BaseName[0]) /* was "##base" used? */
				{
					fprintf(outfile, "()\t_%s(%s_BASE_NAME)\n\n",
						name,
						BaseNamU);
				}
				else
				{
					fprintf(outfile, "()\t_%s()\n\n",
						name);
				}
			}

			fprintf(outfile, "static __inline %s\n_%s(%s%s%s",
				rettype,
				name,
				BaseName[0] ? "void *" : "",
				BaseName[0] ? BaseName : "",
				(BaseName[0] ? (numregs ? ", " : "") : ""));

			for (count=d0; count<numregs; count++)
			{
				chtmp=fD_GetProto(obj, count);
				if (fD_GetFuncParNum(obj)==count)
				{
					fprintf(outfile, chtmp, fD_GetParam(obj, count));
				}
				else
				{
					fprintf(outfile, "%s%s%s", chtmp, (*(chtmp+strlen(chtmp)-1)=='*' ?
						"" : " "), fD_GetParam(obj, count));
				}
				if (count<numregs-1)
				{
					fprintf(outfile, ", ");	
				}
			}

			fprintf(outfile, ")\n");
			fprintf(outfile, "{\n");
			fprintf(outfile, "struct Caos\tMyCaos;\n");
			fprintf(outfile, "\tMyCaos.M68kCacheMode\t=\tIF_CACHEFLUSHALL;\n");
			fprintf(outfile, "//\tMyCaos.M68kStart\t=\tNULL;\n");
			fprintf(outfile, "//\tMyCaos.M68kSize\t\t=\t0;\n");
			fprintf(outfile, "\tMyCaos.PPCCacheMode\t=\tIF_CACHEFLUSHALL;\n");
			fprintf(outfile, "//\tMyCaos.PPCStart\t\t=\tNULL;\n");
			fprintf(outfile, "//\tMyCaos.PPCSize\t\t=\t0;\n");

			if (numregs>0)
			{
				for (count=d0; count<numregs; count++)
				{
				    fprintf(outfile, "\tMyCaos.%s\t\t=(ULONG) %s;\n",
					   fD_GetRegStr(obj,count),
				           fD_GetParam(obj,count));
				}
			}

			fprintf(outfile, "\tMyCaos.caos_Un.Offset\t=\t(%ld);\n",
				fD_GetOffset(obj));

			if (BaseName[0]) /* was "##base" used? */
                	{
				fprintf(outfile, "\tMyCaos.a6\t\t=(ULONG) %s;\t\n",
					BaseName);
	                }
			if (vd)
			{
				fprintf(outfile, "\tPPCCallOS(&MyCaos);\n}\n\n");
			}
			else
			{
				fprintf(outfile, "\treturn((%s)PPCCallOS(&MyCaos));\n}\n\n",
					rettype);
/*
				fprintf(outfile, "\treturn((%s)MyCaos.d0);\n}\n\n",
						rettype);
*/
			}

		}
		else
		{
			/* No M68k support yet */
		}
	}
	else
	{
		if (PowerUP==TRUE)
		{
			fprintf(outfile, "%sstatic __inline %s\n%s(%s",
				(output_mode==STUBS ? "" : "extern "), rettype, name,
				(BaseName[0] ? (numregs ? "BASE_PAR_DECL " : "BASE_PAR_DECL0") : ""));

			for (count=d0; count<numregs; count++)
			{
				chtmp=fD_GetProto(obj, count);
				if (fD_GetFuncParNum(obj)==count)
					fprintf(outfile, chtmp, fD_GetParam(obj, count));
				else
					fprintf(outfile, "%s%s%s", chtmp, (*(chtmp+strlen(chtmp)-1)=='*' ?
						"" : " "), fD_GetParam(obj, count));
				if (count<numregs-1)
					fprintf(outfile, ", ");
			}

			fprintf(outfile, ")\t\n");
			fprintf(outfile, "{\t\n");
			fprintf(outfile, "struct Caos\tMyCaos;\n");
			fprintf(outfile, "\tMyCaos.M68kCacheMode\t=\tIF_CACHEFLUSHALL;\t\n");
			fprintf(outfile, "//\tMyCaos.M68kStart\t=\tNULL;\t\n");
			fprintf(outfile, "//\tMyCaos.M68kSize\t\t=\t0;\t\n");
			fprintf(outfile, "\tMyCaos.PPCCacheMode\t=\tIF_CACHEFLUSHALL;\t\n");
			fprintf(outfile, "//\tMyCaos.PPCStart\t\t=\tNULL;\t\n");
			fprintf(outfile, "//\tMyCaos.PPCSize\t\t=\t0;\t\n");

			if (numregs>0)
			{
				for (count=d0; count<numregs; count++)
				{
				    fprintf(outfile, "\tMyCaos.%s\t\t=(ULONG) %s;\t\n",
					   fD_GetRegStr(obj,count),
				           fD_GetParam(obj,count));
				}
			}

			fprintf(outfile, "\tMyCaos.caos_Un.Offset\t=\t(%ld);\t\n",fD_GetOffset(obj));
			if (BaseName[0]) /* was "##base" used? */
                	{
				fprintf(outfile, "\tMyCaos.a6\t\t=\t(ULONG) %s_BASE_NAME;\t\n",
					BaseNamU);
	                }
			if (vd)
			{
				fprintf(outfile, "\tPPCCallOS(&MyCaos);\t\n}\n\n");
			}
			else
			{
				fprintf(outfile, "\treturn((%s)PPCCallOS(&MyCaos));\n}\n\n",
					rettype);
/*
				fprintf(outfile, "\treturn((%s)MyCaos.d0);\t\n}\n\n",
						rettype);
*/
			}
		}
		else
		{
			fprintf(outfile, "%s__inline %s\n%s(%s",
				(output_mode==STUBS ? "" : "extern "), rettype, name,
				(BaseName[0] ? (numregs ? "BASE_PAR_DECL " : "BASE_PAR_DECL0") : ""));

			for (count=d0; count<numregs; count++)
			{
				chtmp=fD_GetProto(obj, count);
				if (fD_GetFuncParNum(obj)==count)
					fprintf(outfile, chtmp, fD_GetParam(obj, count));
				else
					fprintf(outfile, "%s%s%s", chtmp, (*(chtmp+strlen(chtmp)-1)=='*' ?
						"" : " "), fD_GetParam(obj, count));
				if (count<numregs-1)
					fprintf(outfile, ", ");
			}
			fprintf(outfile, ")\n{\n%s", (BaseName[0] ? "   BASE_EXT_DECL\n" : ""));
			if (!vd)
				fprintf(outfile, "   register %s%sres __asm(\"d0\");\n", rettype,
					(*(rettype+strlen(rettype)-1)=='*' ? "" : " "));

			if (BaseName[0])
				fprintf(outfile, "   register struct %s *a6 __asm(\"a6\") = BASE_NAME;\n",
					StdLib);

			for (count=d0; count<numregs; count++)
			{
				chtmp=fD_GetRegStr(obj, count);
				if (a45 && (strcmp(chtmp, "a4")==0 || strcmp(chtmp, "a5")==0))
					chtmp="d7";
				if (fD_GetFuncParNum(obj)==count)
				{
					fprintf(outfile, "   register ");
					fprintf(outfile, fD_GetProto(obj, count), chtmp);
					fprintf(outfile, " __asm(\"%s\") = %s;\n", chtmp, fD_GetParam(obj,
						count));
				}
				else
				{
					const char *proto=fD_GetProto(obj, count);
					fprintf(outfile, "   register %s%s%s __asm(\"%s\") = %s;\n",
						proto, (*(proto+strlen(proto)-1)=='*' ? "" : " "), chtmp,
						chtmp, fD_GetParam(obj, count));
				}
			}
			if (a45)
				fprintf(outfile, "   __asm volatile (\"exg d7,%s\\n\\t"
					"jsr a6@(-0x%lx:W)\\n\\texg d7,%s\"\n", (a45==4 ? "a4" : "a5"),
					-fD_GetOffset(obj), (a45==4 ? "a4" : "a5"));
			else
				fprintf(outfile, "   __asm volatile (\"jsr a6@(-0x%lx:W)\"\n",
					-fD_GetOffset(obj));

			fprintf(outfile, (vd ? "   : /* No Output */\n" : "   : \"=r\" (res)\n"));

			fprintf(outfile, "   : ");
			if (BaseName[0])
				fprintf(outfile, "\"r\" (a6)%s", (numregs ? ", ": ""));

			for (count=d0; count<numregs; count++)
			{
				chtmp=fD_GetRegStr(obj, count);
				if (a45 && (strcmp(chtmp, "a4")==0 || strcmp(chtmp, "a5")==0))
					chtmp="d7";
				fprintf(outfile, "\"r\" (%s)%s", chtmp, (count<numregs-1 ? ", " : ""));
			}
			fprintf(outfile, "\n   : \"d0\", \"d1\", \"a0\", \"a1\", \"fp0\", \"fp1\"");

			if (vd)
				fprintf(outfile, ", \"cc\", \"memory\");\n}\n\n"); /* { */
			else
				fprintf(outfile, ", \"cc\", \"memory\");\n   return res;\n}\n\n");
		}
	}

	if ((tagname=aliasfunction(fD_GetName(obj)))!=0)
	{
		fprintf(outfile, "#define %s(", tagname);
		for (count=d0; count<numregs-1; count++)
			fprintf(outfile, "a%d, ", count);
		fprintf(outfile, "a%d) %s (", count, name);
		for (count=d0; count<numregs-1; count++)
			fprintf(outfile, "(a%d), ", count);
		fprintf(outfile, "(a%d))\n\n", count);
	}

	if ((tagname=taggedfunction(obj))!=0)
	{
		if (output_mode!=STUBS)
		{
			if (PowerUP)
			{
				fprintf(outfile, "#ifndef NO_PPCINLINE_STDARG\n#define %s(", tagname);
			}
			else
			{
				fprintf(outfile, "#ifndef NO_INLINE_STDARG\n#define %s(", tagname);
			}

			for (count=d0; count<numregs-1; count++)
				fprintf(outfile, "a%d, ", count);

			fprintf(outfile, "tags...) \\\n\t({ULONG _tags[] = { tags }; %s(",
				name);

			for (count=d0; count<numregs-1; count++)
				fprintf(outfile, "(a%d), ", count);

			if (PowerUP)
			{
				fprintf(outfile, "(%s)_tags);})\n#endif /* !NO_PPCINLINE_STDARG */\n\n",
					fD_GetProto(obj, fD_RegNum(obj)-1));
			}
			else
			{
				fprintf(outfile, "(%s)_tags);})\n#endif /* !NO_INLINE_STDARG */\n\n",
					fD_GetProto(obj, fD_RegNum(obj)-1));
			}
		}
		else
		{
			fprintf(outfile, "%s %s(", rettype, tagname);

			for (count=d0; count<numregs-1; count++)
			{
				chtmp=fD_GetProto(obj, count);
				if (count==fD_GetFuncParNum(obj))
					fprintf(outfile, chtmp, fD_GetParam(obj, count));
				else
					fprintf(outfile, "%s%s%s", chtmp,
						(*(chtmp+strlen(chtmp)-1)=='*' ? "" : " "),
						fD_GetParam(obj, count));
				fprintf(outfile, ", ");
			}

			fprintf(outfile, "int tag, ...)\n{\n   ");
			if (!vd)
				fprintf(outfile, "return ");

			fprintf(outfile, "%s(", name);
			for (count=d0; count<numregs-1; count++)
				fprintf(outfile, "%s, ", fD_GetParam(obj, count));

			fprintf(outfile, "(%s)&tag);\n}\n\n", fD_GetProto(obj, fD_RegNum(obj)-1));
		}
	}

	if (strcmp(name, "DoPkt")==0)
	{
		fdDef *objnc=(fdDef*)obj;
		char newname[7]="DoPkt0";
		objnc->name=newname;
		for (count=2; count<7; count++)
		{
			regs reg=objnc->reg[count];
			char *proto=objnc->proto[count];
			objnc->reg[count]=illegal;
			objnc->proto[count]=fD_nostring;
			fD_write(outfile, objnc);
			objnc->reg[count]=reg;
			objnc->proto[count]=proto;
			newname[5]++;
		}
		objnc->name=(char*)name;
	}
}

int
varargsfunction(const char* proto, const char* funcname)
{
	const char *end=proto+strlen(proto)-1;
	while (isspace(*end))
		end--;
	if (*end--==';')
	{
		while (isspace(*end))
			end--;
		if (*end--==')')
		{
			while (isspace(*end))
				end--;
			if (!strncmp(end-2, "...", 3))
			{
				/* Seems to be a varargs function. Check if it will be recognized
					as "tagged". */
				unsigned int count;
				char fixedname[200]; /* Hope will be enough... */
				fdDef *tmpdef;

				for (count=0; count<sizeof TagExcTable/sizeof TagExcTable[0];
				count+=2)
					if (strcmp(funcname, TagExcTable[count+1])==0)
						return 1;

				if (!(tmpdef=fD_ctor()))
				{
					fprintf(stderr, "No mem for FDs\n");
					exit(EXIT_FAILURE);
				}

				strcpy(fixedname, funcname);
				if (strlen(funcname)>4 &&
				!strcmp(funcname+strlen(funcname)-4, "Tags"))
				{
					/* Might be either nothing or "TagList". */
					fixedname[strlen(fixedname)-4]='\0';
					fD_NewName(tmpdef, fixedname);
					if (bsearch(&tmpdef, arrdefs, fds, sizeof arrdefs[0],
					fD_cmpName))
						return 1;

					strcat(fixedname, "TagList");
					fD_NewName(tmpdef, fixedname);
					if (bsearch(&tmpdef, arrdefs, fds, sizeof arrdefs[0],
					fD_cmpName))
						return 1;
				}
				else
				{
					strcat(fixedname, "A");
					fD_NewName(tmpdef, fixedname);
					if (bsearch(&tmpdef, arrdefs, fds, sizeof arrdefs[0],
					fD_cmpName))
						return 1;
				}
			}
		}
	}
	return 0;
}

int
ishandleddifferently(const char* proto, const char* funcname)
{
	/* First check if this is a vararg call? */
	if (varargsfunction(proto, funcname))
		return 1;

	/* It might be a dos.library "alias" name. */
	if (aliasfunction(funcname))
		return 1;

	/* It might be one from dos.library/DoPkt() family. */
	if (strlen(funcname)==6 && !strncmp(funcname, "DoPkt", 5) &&
	funcname[5]>='0' && funcname[6]<='4')
		return 1;

	/* Finally, it can be intuition.library/ReportMouse1(). */
	return !strcmp(funcname, "ReportMouse1");
}

void
printusage(const char* exename)
{
	fprintf(stderr,
		"Usage: %s [options] fd-file clib-file [[-o] output-file]\n"
		"Options:\n"
		"--new\t\tpreprocessor based (default)\n"
		"--old\t\tinline based\n"
		"--stubs\t\tlibrary stubs\n"
		"--proto\t\tbuild proto files (no clib-file required)\n"
		"--pragma\t\tbuild pragma file\n"
		"--powerup\t\tpowerup flag\n"
		"--version\tprint version number and exit\n", exename);
}

void output_proto(FILE* outfile)
{
	if (PowerUP)
	{
		fprintf(outfile,
			"/* Automatically generated header! Do not edit! */\n\n"
			"#ifndef PPCPROTO_%s_H\n"
			"#define PPCPROTO_%s_H\n\n"
			"#include <clib/%s_protos.h>\n\n"
			"#ifdef __GNUC__\n"
			"#include <powerup/ppcinline/%s.h>\n"
			"#endif /* __GNUC__ */\n\n",
			BaseNamU, BaseNamU, BaseNamL, BaseNamL);
	}
	else
	{
		fprintf(outfile,
			"/* Automatically generated header! Do not edit! */\n\n"
			"#ifndef PROTO_%s_H\n"
			"#define PROTO_%s_H\n\n"
			"#include <clib/%s_protos.h>\n\n"
			"#ifdef __GNUC__\n"
			"#include <inline/%s.h>\n"
			"#endif /* __GNUC__ */\n\n",
			BaseNamU, BaseNamU, BaseNamL, BaseNamL);
	}
	if (BaseName[0])
		fprintf(outfile,
			"#ifndef __NOLIBBASE__\n"
			"extern struct %s *\n"
			"#ifdef __CONSTLIBBASEDECL__\n"
			"__CONSTLIBBASEDECL__\n"
			"#endif /* __CONSTLIBBASEDECL__ */\n"
			"%s;\n"
			"#endif /* !__NOLIBBASE__ */\n\n",
			StdLib, BaseName);

	if (PowerUP)
	{
		fprintf(outfile,
			"#endif /* !PPCPROTO_%s_H */\n", BaseNamU);
	}
	else
	{
		fprintf(outfile,
			"#endif /* !PROTO_%s_H */\n", BaseNamU);
	}
}

/******************************************************************************/

int
main(int argc, char** argv)
{
	fdDef *tmpdef,			/* a dummy to contain the name to look for */
		*founddef;		/* the fdDef for which we found a prototype */
	fdFile *myfile;
	char *tmpstr;
	FILE *outfile;
	char *fdfilename=0, *clibfilename=0, *outfilename=0;

	int count;
	Error lerror;

	for (count=1; count<argc; count++)
	{
		char *option=argv[count];
		if (*option=='-')
		{
			option++;
			if (strcmp(option, "o")==0)
			{
				if (count==argc-1 || outfilename)
				{
					printusage(argv[0]);
					return EXIT_FAILURE;
				}
				if (strcmp(argv[++count], "-"))
					outfilename=argv[count];
			}
			else
			{
				if (*option=='-') /* Accept GNU-style '--' options */
					option++;
				if (strcmp(option, "new")==0)
					output_mode=NEW;
				else if (strcmp(option, "old")==0)
					output_mode=OLD;
				else if (strcmp(option, "stubs")==0)
					output_mode=STUBS;
				else if (strcmp(option, "proto")==0)
					output_mode=PROTO;
				else if (strcmp(option, "pragma")==0)
					output_mode=PRAGMA;
				else if (strcmp(option, "powerup")==0)
					PowerUP	=	TRUE;
				else if (strcmp(option, "version")==0)
				{
					fprintf(stderr, "fd2inline version " VERSION "\n");
					return EXIT_SUCCESS;
				}
				else
				{
					printusage(argv[0]);
					return EXIT_FAILURE;
				}
			}
		}
		else
		{
			/* One of the filenames */
			if (!fdfilename)
				fdfilename=option;
			else if (!clibfilename)
				clibfilename=option;
			else if (!outfilename)
				outfilename=option;
			else
			{
				printusage(argv[0]);
				return EXIT_FAILURE;
			}
		}
	}

	if (!fdfilename || (!clibfilename && output_mode!=PROTO))
	{
		printusage(argv[0]);
		return EXIT_FAILURE;
	}

	if (!(arrdefs=malloc(FDS*sizeof(fdDef*))))
	{
		fprintf(stderr, "No mem for FDs\n");
		return EXIT_FAILURE;
	}
	for (count=0; count<FDS; count++)
		arrdefs[count]=NULL;

	if (!(myfile=fF_ctor(fdfilename)))
	{
		fprintf(stderr, "Couldn't open file '%s'.\n", fdfilename);
		return EXIT_FAILURE;
	}

	lerror=false;

	for (count=0; count<FDS && lerror==false; count++)
	{
		if (!(arrdefs[count]=fD_ctor()))
		{
			fprintf(stderr, "No mem for FDs\n" );
			return EXIT_FAILURE;
		}
		do
		{
			if ((lerror=fF_readln(myfile))==false)
			{
				fF_SetError(myfile, false);
				lerror=fD_parsefd(arrdefs[count], myfile);
			}
		}
		while (lerror==nodef);
	}
	if (count<FDS)
	{
		count--;
		fD_dtor(arrdefs[count]);
		arrdefs[count]=NULL;
	}
	fds=count;

	qsort(arrdefs, count, sizeof arrdefs[0], fD_cmpName);

	if (output_mode!=NEW)
	{
		unsigned int count2;
		StdLib="Library";

		for (count2=0; count2<sizeof LibExcTable/sizeof LibExcTable[0]; count2+=2)
			if (strcmp(BaseName, LibExcTable[count2])==0)
			{
				StdLib=LibExcTable[count2+1];
				break;
			}
	}

	fF_dtor(myfile);

	if (output_mode!=PROTO)
	{
		if (!(myfile=fF_ctor(clibfilename)))
		{
			fprintf(stderr, "Couldn't open file '%s'.\n", clibfilename);
			return EXIT_FAILURE;
		}

		if (!(tmpdef=fD_ctor()))
		{
			fprintf(stderr, "No mem for FDs\n");
			return EXIT_FAILURE;
		}

		for (lerror=false; lerror==false || lerror==nodef;)
			if ((lerror=fF_readln(myfile))==false)
			{
				fF_SetError(myfile, false); /* continue even on errors */
				tmpstr=fF_FuncName(myfile);

				if (tmpstr)
				{
				   fdDef **res;
					fD_NewName(tmpdef, tmpstr);
					res=(fdDef**)bsearch(&tmpdef, arrdefs, fds, sizeof arrdefs[0],
						fD_cmpName);

					if (res)
					{
						founddef=*res;
						DBP(fprintf(stderr, "found (%s).\n", fD_GetName(founddef)));
						fF_SetError(myfile, false);
						lerror=fD_parsepr(founddef, myfile);
					}
					else
						if (!ishandleddifferently(myfile->line, tmpstr))
							fprintf(stderr, "don't know what to do with <%s> in line %lu.\n",
								tmpstr, myfile->lineno);
					free(tmpstr);
				}
			}

		fD_dtor(tmpdef);

		fF_dtor(myfile);
	}

	if (strlen(fdfilename)>7 &&
	!strcmp(fdfilename+strlen(fdfilename)-7, "_lib.fd"))
	{
		char *str=fdfilename+strlen(fdfilename)-8;
		while (str!=fdfilename && str[-1]!='/' && str[-1]!=':')
			str--;
		strncpy(BaseNamL, str, strlen(str)-7);
		strncpy(BaseNamU, str, strlen(str)-7);
		BaseNamU[strlen(str)-7]='\0';
	}
	else
	{
		/* Shouldn't really happen, but... */
		strcpy(BaseNamU, BaseName);
		if (strlen(BaseNamU)>4 && strcmp(BaseNamU+strlen(BaseNamU)-4, "Base")==0)
			BaseNamU[strlen(BaseNamU)-4]='\0';
	}
	StrUpr(BaseNamU);

	if (outfilename)
	{
		if (!(outfile=fopen(outfilename, "w")))
		{
			fprintf(stderr, "Couldn't open output file.\n");
			return EXIT_FAILURE;
		}
	}
	else
		outfile=stdout;

	if (output_mode==PROTO)
	{
		output_proto(outfile);
	}
	else
	{
		if (output_mode==NEW)
		{
			if (PowerUP)
			{
				fprintf(outfile,
					"/* Automatically generated header! Do not edit! */\n\n"
					"#ifndef _PPCINLINE_%s_H\n"
					"#define _PPCINLINE_%s_H\n\n"
					"#ifndef __PPCINLINE_MACROS_H\n"
					"#include <powerup/ppcinline/macros.h>\n"
					"#endif /* !__PPCINLINE_MACROS_H */\n\n",
					BaseNamU,
					BaseNamU);
			}
			else
			{
				fprintf(outfile,
					"/* Automatically generated header! Do not edit! */\n\n"
					"#ifndef _INLINE_%s_H\n"
					"#define _INLINE_%s_H\n\n"
					"#ifndef __INLINE_MACROS_H\n"
					"#include <inline/macros.h>\n"
					"#endif /* !__INLINE_MACROS_H */\n\n",
					BaseNamU,
					BaseNamU);
			}
		}
		else if (output_mode==PRAGMA)
		{
			if (PowerUP)
			{
				fprintf(outfile,
					"/* Automatically generated header! Do not edit! */\n\n"
					"#ifndef _PPCPRAGMA_%s_H\n"
					"#define _PPCPRAGMA_%s_H\n"
					"#ifdef __GNUC__\n"
					"#ifndef _PPCINLINE__%s_H\n"
					"#include <powerup/ppcinline/%s.h>\n"
					"#endif\n"
					"#else\n\n"
					"#ifndef POWERUP_PPCLIB_INTERFACE_H\n"
					"#include <powerup/ppclib/interface.h>\n"
					"#endif\n\n"
					"#ifndef POWERUP_GCCLIB_PROTOS_H\n"
					"#include <powerup/gcclib/powerup_protos.h>\n"
					"#endif\n\n"
					"#ifndef NO_PPCINLINE_STDARG\n"
					"#define NO_PPCINLINE_STDARG\n"
					"#endif"
					"/* SAS C PPC inlines */\n\n",
					BaseNamU,
					BaseNamU,
					BaseNamU,
					BaseNamL);
			}
			else
			{
				fprintf(outfile,
					"/* Automatically generated header! Do not edit! */\n\n"
					"#ifndef _INLINE__%s_H\n"
					"#include <inline/%s.h>\n\n",
					BaseNamU,
					BaseNamL);
			}

		}
		else
		{
			if (PowerUP)
			{
				fprintf(outfile,
					"/* Automatically generated header! Do not edit! */\n\n"
					"#ifndef _PPCINLINE_%s_H\n"
					"#define _PPCINLINE_%s_H\n\n"
					"#ifndef __PPCINLINE_STUB_H\n"
					"#include <powerup/ppcinline/stubs.h>\n"
					"#endif /* !__PPCINLINE_STUB_H */\n\n",
					BaseNamU,
					BaseNamU);
			}
			else
			{
				fprintf(outfile,
					"/* Automatically generated header! Do not edit! */\n\n"
					"#ifndef _INLINE_%s_H\n"
					"#define _INLINE_%s_H\n\n"
					"#ifndef __INLINE_STUB_H\n"
					"#include <inline/stubs.h>\n"
					"#endif /* !__INLINE_STUB_H */\n\n",
					BaseNamU,
					BaseNamU);
			}
		}

		if (BaseName[0])
		{
			if (output_mode==NEW || (output_mode==PRAGMA && PowerUP==TRUE))
			{
				fprintf(outfile,
					"#ifndef %s_BASE_NAME\n"
					"#define %s_BASE_NAME %s\n"
					"#endif /* !%s_BASE_NAME */\n\n",
					BaseNamU, BaseNamU, BaseName, BaseNamU);
			}
			else
			{
				fprintf(outfile,
					"#ifndef BASE_EXT_DECL\n"
					"#define BASE_EXT_DECL\n"
					"#define BASE_EXT_DECL0 extern struct %s *%s;\n"
					"#endif /* !BASE_EXT_DECL */\n"
					"#ifndef BASE_PAR_DECL\n"
					"#define BASE_PAR_DECL\n"
					"#define BASE_PAR_DECL0 void\n"
					"#endif /* !BASE_PAR_DECL */\n"
					"#ifndef BASE_NAME\n"
					"#define BASE_NAME %s\n"
					"#endif /* !BASE_NAME */\n\n"
					"BASE_EXT_DECL0\n\n", StdLib, BaseName, BaseName);
			}
		}

		for (count=0; count<FDS && arrdefs[count]; count++)
		{
			DBP(fprintf(stderr, "outputting %ld...\n", count));

			fD_write(outfile, arrdefs[count]);
			fD_dtor(arrdefs[count]);
			arrdefs[count]=NULL;
		}

		if (output_mode==OLD || output_mode==STUBS)
		{
			if (BaseName[0])
			{
				fprintf(outfile,
					"#undef BASE_EXT_DECL\n"
					"#undef BASE_EXT_DECL0\n"
					"#undef BASE_PAR_DECL\n"
					"#undef BASE_PAR_DECL0\n"
					"#undef BASE_NAME\n\n");
			}

			if (PowerUP)
			{
				fprintf(outfile, "#endif /* !_PPCINLINE_%s_H */\n", BaseNamU);
			}
			else
			{
				fprintf(outfile, "#endif /* !_INLINE_%s_H */\n", BaseNamU);
			}
		}
		else
		if (output_mode==PRAGMA)
		{
			fprintf(outfile, "#endif /* SASC Pragmas */\n");
			if (PowerUP)
			{
				fprintf(outfile, "#endif /* !_PPCPRAGMA_%s_H */\n", BaseNamU);
			}
			else
			{
				fprintf(outfile, "#endif /* !_PRAGMA_%s_H */\n", BaseNamU);
			}
		}
		else
		if (output_mode==NEW)
		{
			if (PowerUP)
			{
				fprintf(outfile, "#endif /* !_PPCINLINE_%s_H */\n", BaseNamU);
			}
			else
			{
				fprintf(outfile, "#endif /* !_INLINE_%s_H */\n", BaseNamU);
			}
		}

	}

	free(arrdefs);

	return EXIT_SUCCESS;
}
