/* Process include files */

#include "global.h"
#include "misc.h"
#include "readdir.h"
#include "incfile.h"

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

struct FileNode **IncludeFileArray;
struct FileNode *IncludeFileList;

struct StructNode **StructArray;
struct StructNode *StructList;

struct StructNode **TypedefArray;
struct StructNode *TypedefList;

struct TypedefTempNode *TypedefTempList;

static int ExpandedTypedefs;

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

BOOL PrintIncludeFileList(void)
{
	int Index;

	if (FPrintf(XRefFile, "\n/* Include files */\n") == -1)
	{
		PrintFault(IoErr(), Arguments.XRefFile);
		return (FALSE);
	}
	for (Index = 1; Index <= (int) IncludeFileArray[0]; Index++)
	{
		if (FPrintf(XRefFile, "\x22<%s>\x22 \x22%s\x22 0 3\n", IncludeFileArray[Index]->Node.Name, IncludeFileArray[Index]->Node.Name) == -1)
		{
			PrintFault(IoErr(), Arguments.XRefFile);
			return (FALSE);
		}
	}
	return (TRUE);
}

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

BOOL PrintStructList(void)
{
	int Index;

	printf("      %ld structs found\n", (int) StructArray[0]);
	if (FPrintf(XRefFile, "\n/* Structures */\n") == -1)
	{
		PrintFault(IoErr(), Arguments.XRefFile);
		return (FALSE);
	}
	for (Index = 1; Index <= (int) StructArray[0]; Index++)
	{
		if (FPrintf(XRefFile, "\x22%s\x22 \x22%s\x22 %ld 5\n", StructArray[Index]->Node.Name, StructArray[Index]->File, StructArray[Index]->Line) == -1)
		{
			PrintFault(IoErr(), Arguments.XRefFile);
			return (FALSE);
		}
	}
	return (TRUE);
}

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

BOOL PrintTypedefs(void)
{
	int Index;

	printf("      %ld typedefs found\n", (int) TypedefArray[0]);
	if (FPrintf(XRefFile, "\n/* Typedefs */\n") == -1)
	{
		PrintFault(IoErr(), Arguments.XRefFile);
		return (FALSE);
	}
	for (Index = 1; Index <= (int) TypedefArray[0]; Index++)
	{
		if (FPrintf(XRefFile, "\x22%s\x22 \x22%s\x22 %ld 5\n", TypedefArray[Index]->Node.Name, TypedefArray[Index]->File, TypedefArray[Index]->Line) == -1)
		{
			PrintFault(IoErr(), Arguments.XRefFile);
			return (FALSE);
		}
	}
	return (TRUE);
}

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

BOOL AddIdent(char *Name, char *File, long Line, BOOL IsTypedef)
{
	struct StructNode *NewNode;

	if (IsTypedef)
	{
		NewNode = TypedefList;
	}
	else
	{
		NewNode = StructList;
	}
	while (NewNode && strcmp(NewNode->Node.Name, Name))
	{
		NewNode = (struct StructNode *) (NewNode->Node.Next);
	}
	if (NewNode)
	{
		WriteError("Warning: %s %s defined twice.\n"
				   "         First definition in %s, line %ld\n"
				   "         Second definition in %s, line %ld\n"
				   "         Ignoring 2nd definition\n", (IsTypedef ? "typedef" : "struct"), Name, NewNode->File, NewNode->Line, File, Line);
		return (TRUE);
	}
	if (NewNode = AllocVec(sizeof(struct StructNode) + strlen(Name) + strlen(File) + 2, 0))
	{
		NewNode->Node.Name = (char *) (NewNode + 1);
		NewNode->File = stpcpy(NewNode->Node.Name, Name) + 1;
		stpcpy(NewNode->File, File);
		NewNode->Line = Line;
		if (IsTypedef)
		{
			NewNode->Node.Next = (struct AnyNode *) TypedefList;
			TypedefList = NewNode;
		}
		else
		{
			NewNode->Node.Next = (struct AnyNode *) StructList;
			StructList = NewNode;
		}
		return (TRUE);
	}
	else
	{
		PrintFault(ERROR_NO_FREE_STORE, NULL);
	}
	return (FALSE);
}

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

static BOOL AddTypedefTemp(char *Identifier, char *Struct, char *File, long Line)
{
	struct TypedefTempNode *NewNode;

	if (NewNode = AllocVec(sizeof(struct TypedefTempNode) + strlen(Identifier) + strlen(Struct) + strlen(File) + 3, 0))
	{
		NewNode->Node.Name = (char *) (NewNode + 1);
		NewNode->Struct = stpcpy(NewNode->Node.Name, Identifier) + 1;
		NewNode->File = stpcpy(NewNode->Struct, Struct) + 1;
		stpcpy(NewNode->File, File);
		NewNode->Line = Line;
		NewNode->Node.Next = (struct AnyNode *) TypedefTempList;
		TypedefTempList = NewNode;
		return (TRUE);
	}
	else
	{
		PrintFault(ERROR_NO_FREE_STORE, NULL);
		return (FALSE);
	}
}

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

static long MyFGetC(void)
{
	long Character;

	Character = FGetC(CurrentFile);
	switch (Character)
	{
		case '\n':
			CurrentLine++;
		case '\t':
			Character = ' ';
	}
	return (Character);
}

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

#define ERROR_TOKEN   -1
#define EOF_TOKEN      0
#define IDENT_TOKEN    1
#define ANY_TOKEN      2

static WORD ReadToken(void)
{
	long Character;
	int Index;

  DoRead:
	do
	{
		if (Break())
		{
			return (ERROR_TOKEN);
		}
		Character = MyFGetC();
	}
	while (Character == ' ');
	switch (Character)
	{
		case -1:
			return (WORD) (IoErr()? ERROR_TOKEN : EOF_TOKEN);

		case '/':
			if ((Character = MyFGetC()) == '*')
			{
				do
				{
					while ((Character = MyFGetC()) != '*')
					{
						if (Character == -1)
						{
							return (WORD) (IoErr()? ERROR_TOKEN : EOF_TOKEN);
						}
						if (Break())
						{
							return (WORD) (ERROR_TOKEN);
						}
					}
					Character = MyFGetC();
					if (Character != '/')
					{
						UnGetC(CurrentFile, Character);
					}
				}
				while (Character != '/');
				goto DoRead;
			}
			else
			{
				UnGetC(CurrentFile, Character);
			}
			return (WORD) (ANY_TOKEN);

		case ';':
		case '[':
		case ']':
		case '{':
		case '}':
		case '(':
		case ')':
			return ((WORD) Character);

		default:
			Index = 0;
			while ((Character >= 'a' && Character <= 'z') ||
				   (Character >= 'A' && Character <= 'Z') ||
				   (Character >= '0' && Character <= '9') ||
				   (Character == '_' || Character == '#'))
			{
				Identifier[Index++] = Character;
				if (Index == MAX_LEN - 1)
				{
					WriteError("Error: Identifier too long.\n"
							   "       Operation aborted.\n");
					return (ERROR_TOKEN);
				}
				Character = MyFGetC();
			}
			if (Index)
			{
				Identifier[Index] = '\0';
				UnGetC(CurrentFile, Character);
				return (IDENT_TOKEN);
			}
			return (ANY_TOKEN);
	}
}

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

static BOOL ProcessIncludeFile(char *Name)
{
	long BlockLevel;
	long StructLine;
	long TypedefLine;
	BOOL Success;
	WORD Typedef;
	WORD Token;

	Success = TRUE;
	/* if (stricmp(FileList->Node.Name,"exec/types.h")) */
	{
		printf("      Reading %s\n", FileList->Node.Name);
		if (CurrentFile = Open(Name, MODE_OLDFILE))
		{
			CurrentLine = 0;
			BlockLevel = 0;
			Typedef = 0;
			while ((Token = ReadToken()) > 0 && Success)
			{
			  CheckToken:switch (Token)
				{
					case '(':
					case '{':
					case '[':
						BlockLevel++;
						break;
					case ')':
					case '}':
					case ']':
						BlockLevel--;
						break;

					case IDENT_TOKEN:
						if (!BlockLevel)
						{
							if (!strcmp(Identifier, "typedef"))
							{
								Typedef = 1;
								TypedefLine = CurrentLine;
							}
							else if (!strcmp(Identifier, "struct"))
							{
								StructLine = CurrentLine;
								Token = ReadToken();
								if (Token == IDENT_TOKEN)
								{
									if (Typedef)
									{
										stpcpy(Buffer, Identifier);
										Typedef = 2;
									}
									Token = ReadToken();
									if (Token == '{')
									{
										Success = AddIdent(Identifier, FileList->Node.Name, StructLine, FALSE);
									}
									goto CheckToken;
								}
								else
								{
									goto CheckToken;
								}
							}
						}
						break;

					case ';':
						if (!BlockLevel)
						{
							if (Arguments.NoExpand && Typedef == 2)
								Typedef = 1;
							switch (Typedef)
							{
								case 1:
									Success = AddIdent(Identifier, FileList->Node.Name, TypedefLine, TRUE);
									break;
								case 2:
									Success = AddTypedefTemp(Identifier, Buffer, FileList->Node.Name, TypedefLine);
									break;
							}
							Typedef = 0;
						}
						break;
				}
			}
			if (Token)
			{
				Success = FALSE;
			}
			Close(CurrentFile);
		}
		else
		{
			PrintFault(IoErr(), FileList->Node.Name);
			Success = FALSE;
		}
	}
	return (Success);
}

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

static BOOL ExpandTypedefs(void)
{
	struct AnyNode *AnyNode;

	while (TypedefTempList)
	{
		AnyNode = (struct AnyNode *) StructList;
		while (AnyNode && strcmp(AnyNode->Name, TypedefTempList->Struct))
		{
			AnyNode = AnyNode->Next;
		}
		if (AnyNode)
		{
			if (!AddIdent(TypedefTempList->Node.Name, ((struct StructNode *) AnyNode)->File, ((struct StructNode *) AnyNode)->Line, TRUE))
			{
				return (FALSE);
			}
			AnyNode = TypedefTempList->Node.Next;
			FreeVec(TypedefTempList);
			TypedefTempList = (struct TypedefTempNode *) AnyNode;
			ExpandedTypedefs++;
		}
		else
		{
			AnyNode = TypedefTempList->Node.Next;
			TypedefTempList->Node.Next = (struct AnyNode *) TypedefList;
			TypedefList = (struct StructNode *) TypedefTempList;
			TypedefTempList = (struct TypedefTempNode *) AnyNode;
		}
	}
	printf("      %ld typedef struct statements expanded\n", ExpandedTypedefs);
}

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

BOOL ProcessIncludes(BPTR Directory, char *Name)
{
	if (ReadDir(Directory, Name, ".h", ProcessIncludeFile))
	{
		return (ExpandTypedefs());
	}
	return (FALSE);
}
