/* 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)
      {
         if (!Arguments.Types && (!strcmp(Name,"BSTR") || !strcmp(Name,"BPTR")))
            {
               return(TRUE);
            }
         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=TypedefList;
               TypedefList=NewNode;
            }
         else
            {
               NewNode->Node.Next=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=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(IoErr() ? ERROR_TOKEN : EOF_TOKEN);

         case '/':  if ((Character=MyFGetC())=='*')
                       {
                          do
                             {
                                while ((Character=MyFGetC())!='*')
                                   {
                                      if (Character==-1)
                                         {
                                            return(IoErr() ? ERROR_TOKEN : EOF_TOKEN);
                                         }
                                      if (Break())
                                         {
                                            return(ERROR_TOKEN);
                                         }
                                   }
                                Character=MyFGetC();
                                if (Character!='/')
                                   {
                                      UnGetC(CurrentFile,Character);
                                   }
                             }
                          while (Character!='/');
                          goto DoRead;
                       }
                    else
                       {
                          UnGetC(CurrentFile,Character);
                       }
                    return(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 (!__builtin_strcmp(Identifier,"typedef"))
                                                      {
                                                         Typedef=1;
                                                         TypedefLine=CurrentLine;
                                                      }
                                                   else if (!__builtin_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=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 TypedefTempList *)AnyNode;
               ExpandedTypedefs++;
            }
         else
            {
               AnyNode=TypedefTempList->Node.Next;
               TypedefTempList->Node.Next=TypedefList;
               TypedefList=(struct TypedefList *)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);
}
