/******************************************************************************
 *
 * 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.
 *
 ******************************************************************************/

/*
 * My whole ixemul includes are messed up * so this is hacky fix 
 */

#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, GATESTUBS
}
output_mode = NEW;

int PowerUP = 0, MorphOS = 0;
int PreLibFlag = 0;
int PostLibFlag = 0;
int LocalFlag = 0;
char *prefixname = "";
char *subprefixname = "";
char *premacro = "";

char BaseName[32 + 32], BaseNamU[32 + 32], BaseNamL[32 + 32];
char	Buffer[512];

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;

/*******************************************
 * 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];
}

static INLINE const char *
RegStrU(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(printf("{%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(printf("in# %s\n", obj->line));
				return false;
			}
			len = strlen(low);
			bpoint = low + len - 1;
			while (len && isspace(*bpoint))
			{
				bpoint--;
				len--;
			}
			if (*bpoint == ';' || *bpoint == ')')
			{
				DBP(printf("\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(printf("+"));
		}
	}
	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(printf("end:%s:", upper));

			for (obraces = 1, ready = false; ready == false; upper = lower)
			{
				lower = StrNRBrk(obj->line, "()", --upper);
				if (lower)
				{
					switch (*lower)
					 {
					 case ')':
						 obraces++;
						 DBP(printf(" )%ld%s", obraces, lower));
						 break;
					 case '(':
						 obraces--;
						 DBP(printf(" (%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++) ;
		DBP(printf("< )0> RegNum=%ld\n",count));
		return count;
	}
	else
	{
		illparams("fD_RegNum");
		DBP(printf( "< )0> RegNum illegal\n"));
		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_GetRegStrU(const fdDef * obj, shortcard at)
{
	if (obj && at < illegal)
		return RegStrU(obj->reg[at]);
	else
	{
		illparams("fD_GetReg");
		return RegStrU(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(printf( "set offset to %ld\n",
										  fF_GetOffset(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;

			DBP(printf( "ParNum %ld ParName %s\n",parnum,parname));
			do
			{
				int num;
				const char *type = fD_GetType(obj);
				char *str;

				finished = 1;

				DBP(printf( "DoLoop\n"));
				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... 
							    */

					DBP(printf( "str1 %s\n",str));
					strcpy(buf, parname);
					strcat(buf, "_");
					DBP(printf( "NewParam1 ParNum %ld Buf %s\n",parnum,buf));
					fD_NewParam(obj, parnum, buf);
					finished = 0;
				}
				else
				{
					DBP(printf( "str2 %s\n",str));
					for (num = 0; num < fD_ParamNum(obj); num++)
					{
						const char *name = fD_GetParam(obj, num);
						const char *proto = fD_GetProto(obj, num);

						DBP(printf( "num %ld name %s proto %s\n",num,name,proto));

						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, "_");
							DBP(printf( "NewParam2 ParNum %ld Buf %s\n",parnum,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);


	DBP(printf("buf %s\n",buf));
	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'))
				bpoint++;

			DBP(printf("count %ld args %ld\n",count,args));
			DBP(printf("bpoint %s\n",bpoint));
			bnext = strpbrk(bpoint, "(),");
			DBP(printf("bnext %s\n",bnext));

			if (bnext)
			{
				switch (*bnext)
				{
				 case '(':
					 if (!obraces)
					 {
						if ((PowerUP==0) && (MorphOS==0))
						{
							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(printf( "< (%ld%s >", obraces, bnext));
					 break;

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

						 if (count != args - 1)
						 {
							 DBP(printf( "%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(printf( " [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(printf( "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)
{
	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",
	"vsyslog", "syslog",
	"MUI_RequestA", "MUI_Request",
	"PrintDTObjectA", "PrintDTObject",
	"RefreshDTObjectA", "RefreshDTObjects",
	"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];

	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(printf( "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;
	}

	for (count = d0; count < numregs; count++)
	{
		const char *reg = fD_GetRegStr(obj, count);

		if (!((output_mode == NEW) && (PowerUP == TRUE)))
		{
			if (strcmp(reg, "a4") == 0 || strcmp(reg, "a5") == 0)
			{
				if (!a45)
				{
					a45 = (strcmp(reg, "a4") ? 5 : 4);
				}
				else
				{
					fprintf(stderr, "Warning: both a4 and a5 are used. This is not "
							"supported!\n");
				}
			}
			if (strcmp(reg, "d7") == 0)
			{
				d7 = 1;
			}
		}
	}

	if (!((output_mode == NEW) && (PowerUP == TRUE)))
	{
		if (a45 && d7)
		{
			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) && (MorphOS == 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 || MorphOS == 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));
		}
/*
 * PPC doesn`t care anymore for A4,A5 registers problems the 68k engine has 
 */

		fprintf(outfile, ") \\\n\tLP%d%s%s%s(0x%lx, ",
				numregs,
				(vd ? "NR" : ""),
				(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);
			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 || MorphOS == 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 (output_mode == GATESTUBS)
	{
		fprintf(outfile, "%s %s%s(void)\n{\n",
				rettype,
				prefixname,
				name);

		if (numregs > 0)
		{
			for (count = d0; count < numregs; count++)
			{
				if (strchr(fD_GetProto(obj, count),'%'))
				{
					/* Function parameter */
					fprintf(outfile, "\t");

					sprintf(Buffer,
						fD_GetProto(obj, count),
						fD_GetParam(obj, count));
					fprintf(outfile, Buffer);

					fprintf(outfile, "\t=(");

					sprintf(Buffer,
						fD_GetProto(obj, count),
						"");
					fprintf(outfile, Buffer);

					fprintf(outfile, ") REG_%s;\n",
							fD_GetRegStrU(obj, count));
				}
				else
				{
					fprintf(outfile, "\t%s %s\t=(%s) REG_%s;\n",
							fD_GetProto(obj, count),
							fD_GetParam(obj, count),
							fD_GetProto(obj, count),
							fD_GetRegStrU(obj, count));
				}
			}
		}
		if (vd)
		{
			fprintf(outfile, "\t(%s%s(",
					subprefixname,
					name);
		}
		else
		{
			fprintf(outfile, "\treturn(%s%s(",
					subprefixname,
					name);
		}
		if (numregs > 0)
		{
			if (PreLibFlag)
			{
				fprintf(outfile, "BASE_NAME,");
			}

			for (count = d0; count < numregs; count++)
			{
				if (count >= numregs - 1)
				{
					if (PostLibFlag)
					{
						fprintf(outfile, "%s,BASE_NAME));\n}\n",
							fD_GetParam(obj, count));
					}
					else
					{
						fprintf(outfile, "%s));\n}\n",
							fD_GetParam(obj, count));
					}
				}
				else
				{
					fprintf(outfile, "%s,",
							fD_GetParam(obj, count));
				}
			}
		}
		else
		{
			if (PreLibFlag || PostLibFlag)
			{
				fprintf(outfile, "BASE_NAME));\n}\n");
			}
			else
			{
				fprintf(outfile, "));\n}\n");
			}
		}

	}
	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 if (MorphOS == TRUE)
		{
			fprintf(outfile, "%s%s\n%s(%s",
					(output_mode == STUBS ? "" : "static __inline "), 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 EmulCaos\tMyCaos;\n");

			if (numregs > 0)
			{
				for (count = d0; count < numregs; count++)
				{
					fprintf(outfile, "\tMyCaos.reg_%s\t\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.reg_a6\t\t=\t(ULONG) BASE_NAME;\n");
			}
			if (vd)
			{
				fprintf(outfile, "\t(*MyEmulHandle->EmulCallOS)(&MyCaos);\n}\n\n");
			}
			else
			{
				fprintf(outfile, "\treturn((%s)(*MyEmulHandle->EmulCallOS)(&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 || MorphOS)
			{
				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 || MorphOS)
			{
				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
		{
			if (MorphOS)
			{
				int n = 9 - numregs;
				int d = n & 1 ? 4 : 0;
				int local = (n * 4 + d + 16 + 64 + 15) & ~15;	/*

																 * size of the stack frame 
																 */
			  /*
			   *  Stack frame:
			   *
			   *   0 -  3: next frame ptr
			   *   4 -  7: save lr
			   *   8 - 71: struct Caos
			   *  72 - 72+n*4+d+8-1: tag list start
			   *   ? - local-1: padding
			   */

				fprintf(outfile,
						"asm(\".align\t2\\n\"\n"
						"\"\t.globl\t%s\\n\"\n"
						"\"\t.type\t%s,@function\\n\"\n"
						"\"%s:\\n\"\n"
						"\"\tstwu\t1,-%d(1)\\n\"\n"		/*
														 * create stack frame 
														 */
						"\"\tmflr\t0\\n\"\n"
						"\"\tstw\t0,%d(1)\\n\"\n",
						tagname, tagname, tagname, local, local + 4);

			  /*
			   * If n is odd, one tag is split between regs and stack.
			   * * Copy its ti_Data together with the ti_Tag. 
			   */
				if (d)
					fprintf(outfile, "\"\tlwz\t0,%d(1)\\n\"\n", local + 8);		/*
																				 * read ti_Data 
																				 */

			  /*
			   * Save the registers 
			   */
				for (count = numregs; count <= 8; count++)
					fprintf(outfile, "\"\tstw\t%d,%d(1)\\n\"\n", count + 2, (count - numregs) * 4 + 8 + 64);

				if (d)
					fprintf(outfile, "\"\tstw\t0,%d(1)\\n\"\n", 8 + 64 + n * 4);	/*
																					 * write ti_Data 
																					 */

			  /*
			   * Caos.Offset = -fD_GetOffset(obj) 
			   */
				fprintf(outfile,
						"\"\tli\t0,%ld\\n\"\n"
						"\"\tstw\t0,8(1)\\n\"\n",
						fD_GetOffset(obj));

			  /*
			   * Save the non-varargs registers in the Caos struct. 
			   */
				for (count = 0; count < numregs - 1; count++)
				{
					int r = fD_GetReg(obj, count);

					fprintf(outfile, "\"\tstw\t%d,%d(1)\\n\"\n", count + 3, 8 + 4 + r * 4);
				}

				fprintf(outfile,
						"\"\taddi\t0,1,72\\n\"\n"
						"\"\tstw\t0,%d(1)\\n\"\n"	/*
													 * Caos.reg_?? = taglist 
													 */

						"\"\tlis\t3,%s@ha\\n\"\n"
						"\"\tlwz\t0,%s@l(3)\\n\"\n"
						"\"\tstw\t0,68(1)\\n\"\n"	/*
													 * Caos.reg_a6 = libbase 
													 */

						"\"\tli\t0,2\\n\"\n"
						"\"\tstw\t0,%d(1)\\n\"\n"	/*
													 * ti_Tag = TAG_MORE 
													 */

						"\"\taddi\t0,1,%d\\n\"\n"
						"\"\tstw\t0,%d(1)\\n\"\n"	/*
													 * ti_Data = &stack_params 
													 */

						"\"\tlwz\t0,88(2)\\n\"\n"
						"\"\taddi\t3,1,8\\n\"\n"
						"\"\tmtlr\t0\\n\"\n"
						"\"\tblrl\\n\"\n"	/*
											 * EmulCallOS() 
											 */

						"\"\tlwz\t0,%d(1)\\n\"\n"	/*
													 * clear stack frame & return 
													 */
						"\"\tmtlr\t0\\n\"\n"
						"\"\taddi\t1,1,%d\\n\"\n"
						"\"\tblr\\n\"\n"
						"\".L%se1:\\n\"\n"
						"\"\t.size\t%s,.L%se1-%s\\n\"\n);\n\n",
						12 + 4 * fD_GetReg(obj, numregs - 1), BaseName, BaseName,
						8 + 64 + n * 4 + d, local + 8 + d, 8 + 64 + n * 4 + d + 4, local + 4, local,
						name, name, name, name);

			}
			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	output_proto(FILE * outfile)
{
	if (PowerUP || MorphOS)
	{
		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"
				"#ifdef __PPC__\n"
				"#include <ppcinline/%s.h>\n"
				"#else\n"
				"#include <inline/%s.h>\n"
				"#endif /* __PPC__ */\n"
				"#else\n"
				"#include <pragmas/%s_pragmas.h>\n"
				"#endif /* __GNUC__ */\n\n",
				BaseNamU, BaseNamU, BaseNamL, BaseNamL, 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 || MorphOS)
	{
		fprintf(outfile,
				"#endif /* !PPCPROTO_%s_H */\n", BaseNamU);
	}
	else
	{
		fprintf(outfile,
				"#endif /* !PROTO_%s_H */\n", BaseNamU);
	}
}

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

void	printusage(const char *exename)
{
	fprintf(stderr,
			"Usage: %s [options] [--prefix name] [--subprefix name] [--premacro name] fd-file clib-file [[-o] output-file]\n"
			"Options:\n"
			"--new\t\t\tpreprocessor based (default)\n"
			"--old\t\t\tinline based\n"
			"--stubs\t\t\tlibrary stubs\n"
			"--proto\t\t\tbuild proto files (no clib-file required)\n"
			"--pragma\t\tbuild pragma file\n"
			"--powerup\t\tpowerup flag\n"
			"--morphos\t\tmorphos flag\n"
			"--gatestubs\t\tlibrary stubs\n"
			"--prelib\t\tgatestub pre library arg\n"
			"--postlib\t\tgatestub pre library arg\n"
			"--local\t\tlocal includes\n"
			"--version\t\tprint version number and exit\n", exename);
}

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, "gatestubs")==0)
					output_mode=GATESTUBS;
				else if (strcmp(option, "powerup") == 0)
					PowerUP = TRUE;
				else if (strcmp(option, "morphos") == 0)
					MorphOS = TRUE;
				else if (strcmp(option, "prelib") == 0)
				{
					if (PostLibFlag)
					{
						printusage(argv[0]);
						return EXIT_FAILURE;
					}
					PreLibFlag = TRUE;
				}
				else if (strcmp(option, "postlib") == 0)
				{
					if (PreLibFlag)
					{
						printusage(argv[0]);
						return EXIT_FAILURE;
					}
					PostLibFlag = TRUE;
				}
				else if (strcmp(option, "local") == 0)
				{
					LocalFlag = TRUE;
				}
				else if (strcmp(option, "prefix") == 0)
				{

					if (count == argc - 1 || outfilename)
					{
						printusage(argv[0]);
						return EXIT_FAILURE;
					}
					if (strcmp(argv[++count], "-"))
					{
						prefixname = argv[count];
					}
				}
				else if (strcmp(option, "subprefix") == 0)
				{

					if (count == argc - 1 || outfilename)
					{
						printusage(argv[0]);
						return EXIT_FAILURE;
					}
					if (strcmp(argv[++count], "-"))
					{
						subprefixname = argv[count];
					}
				}
				else if (strcmp(option, "premacro") == 0)
				{

					if (count == argc - 1 || outfilename)
					{
						printusage(argv[0]);
						return EXIT_FAILURE;
					}
					if (strcmp(argv[++count], "-"))
					{
						premacro = argv[count];
					}
				}
				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)
			{
				/*
				 * continue even on errors 
				 */
				fF_SetError(myfile, false);

				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(printf( "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 || MorphOS)
			{
				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 <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 || MorphOS)
			{
				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 <ppcinline/%s.h>\n"
					"#endif\n"
					"#else\n\n"
					"#ifndef POWERUP_PPCLIB_INTERFACE_H\n"
					"#include <ppclib/interface.h>\n"
					"#endif\n\n"
					"#ifndef POWERUP_GCCLIB_PROTOS_H\n"
					"#include <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 (output_mode == GATESTUBS)
		{
			if (LocalFlag)
			{
				fprintf(outfile,
					"/* Automatically generated gate stubs */\n\n"
					"%s\n"
					"#include \"%s_protos.h\"\n\n"
					"#include <emul/emulregs.h>\n",
					premacro,
					BaseNamL);
			}
			else
			{
				fprintf(outfile,
					"/* Automatically generated gate stubs */\n\n"
					"%s\n"
					"#include <clib/%s_protos.h>\n\n"
					"#include <emul/emulregs.h>\n",
					premacro,
					BaseNamL);
			}
		}
		else
		{
			if (PowerUP || MorphOS)
			{
				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 <ppcinline/stubs.h>\n"
					"#endif /* !__PPCINLINE_STUB_H */\n\n",
					BaseNamU,
					BaseNamU);
				if (MorphOS)
					fprintf(outfile, "#include <emul/emulregs.h>\n");
			}
			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 || MorphOS == 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(printf( "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 || MorphOS)
			{
				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 || MorphOS)
			{
				fprintf(outfile, "#endif /* !_PPCPRAGMA_%s_H */\n", BaseNamU);
			}
			else
			{
				fprintf(outfile, "#endif /* !_PRAGMA_%s_H */\n", BaseNamU);
			}
		}
		else if (output_mode == NEW)
		{
			if (PowerUP || MorphOS)
			{
				fprintf(outfile, "#endif /* !_PPCINLINE_%s_H */\n", BaseNamU);
			}
			else
			{
				fprintf(outfile, "#endif /* !_INLINE_%s_H */\n", BaseNamU);
			}
		}

	}

	free(arrdefs);

	return EXIT_SUCCESS;
}
