
//------------------------------------------------------------------
//
// Ancestor.C
//
// Given an Ada package (which exports a private type), program to
// generate a list of files according to package hierarchy.
// Output is written to file `ancestor.dat'.
//
// Items included in this file:
//	definition of structure ListNode
//	FindAncestor
//	InitializeGlobalList
//	GetDir (DOS and Unix versions)
//	ProcessFile
//	SearchFiles
//	UpdateLocalList
//	ProcessPackages
//	InsertGlobalList
//	gprintf
//	InitializeLocalList
//	InsertLocalList
//	UpdateAncestorFile
//	PrintPackages
//	FreeMem
//	PrintLocalList
//	PrintGlobalList
//
//------------------------------------------------------------------

#include "drgen.h"

#include <stdio.h>
#include <string.h>

#ifdef DOS
#include <dos.h>
#include <dir.h>
#endif

#include <stdlib.h>
#include <ctype.h>

//--------------------------------
// Globals
//--------------------------------
struct ListNode {
	char PkgName[81];
	char filename[81];
	char status;
	struct ListNode *nextaddr;
};

struct ListNode *Ghead, *Gtail;
FILE *logfp, *anfp, *trfp;
int NCount = 0;


//--------------------------------
// Function Prototypes
//--------------------------------

void GetDir(void);
void FindAncestors(char *Filename);
void InitializeGlobalList(void);
int compare(char *s1, char *s2);
void ProcessFile(char *Filename);
void SearchFiles(struct ListNode *head);
void ProcessPackages(struct ListNode *head);
void PrintGlobalList(void);
void PrintLocalList(struct ListNode *head);
void gprintf(FILE *fp, char *s1, char *s2);
struct ListNode *InitializeLocalList();
void InsertLocalList(struct ListNode *head, char *PackageName);
void InsertGlobalList(struct ListNode *p);
void UpdateAncestorFile(struct ListNode *head, char *Filename);
void PrintPackages(struct ListNode *head);
void FreeMem(struct ListNode *head);
int UpdateLocalList(struct ListNode *head, char *PackageName, char *filename);



//--------------------------------
// Function Definitions
//--------------------------------


void FindAncestors(char *Filename) {
	InitializeGlobalList();            // Initialize global linked list

	anfp = fopen("ancestor.dat", "w"); // open output files

	GetDir();                          // get current directory filenames
	// cerr << "Invoking ProcessFile with " << Filename << endl;
	ProcessFile(Filename);             // Process the driver file

	fclose(anfp);                      // close output files
	FreeMem(Ghead);                    // free global linked list
}



//----------------------------------------------------------------
// Initializes global linked list for holding package names
//----------------------------------------------------------------
void InitializeGlobalList(void) {
	/* malloc dummy node */
	Ghead = (struct ListNode *) malloc (sizeof(struct ListNode));
	if (Ghead == NULL)
		printf("Memory allocation error!\n");
	Gtail = Ghead;
	Gtail -> nextaddr = NULL;
}


//----------------------------------------------------------------
// Extract all filenames in current directory -- DOS Version
//----------------------------------------------------------------
#ifdef DOS
  	// Gets Current directory filenames into a file.
  	// This function is specific to DOS
	void GetDir(void) {
		struct ffblk ffblk;
		FILE *fp;
		int done, flag;
		char *ptr;
		char ext[5];

		fp = fopen("filelist.dat", "w");     
		done = findfirst("*.*", &ffblk, 0); 
		while (!done) {
			flag = 0;
			ptr = ffblk.ff_name;
			while (*ptr != '\0' && *ptr != '.')
				ptr++;
			if (*ptr == '.') {                
				sscanf(++ptr, "%s", ext);
				strupr(ext);
				if (strcmp(ext, "ADS") == 0)
					flag = 1;
			}
			if (flag)         
				fprintf(fp, "%s\n", ffblk.ff_name);

			done = findnext(&ffblk);
		}
		fclose(fp);
	}
#endif


//----------------------------------------------------------------
// Extract all filenames in current directory -- Unix Version
//----------------------------------------------------------------
#ifdef UNIX
  	// Gets Current directory filenames into a file.
  	// This function is specific to Unix
	void GetDir(void) {
		system("/bin/ls *.ads > filelist.dat");
	}
#endif

//----------------------------------------------------------------
// Find implicit withs for generics
//----------------------------------------------------------------
void FindImplicitWiths(struct ListNode *head){
    char x[81];
    FILE *fp, *fp2;
    fp = fopen("with.dat", "r");
    if (fp == NULL)
      return;
    fclose(fp);
    ifstream fin("with.dat", ios::in);
    fin >> x;
    while (!fin.eof()) {
       InsertLocalList(head, x);
       fin >> x;
    }
    fclose(fp);
    // ugly hack that "erases" the file 'with.dat' on either DOS or Unix
    fp2 = fopen("with.dat", "w");
    fclose(fp2);
    return;
}

//---------------------------------------------------------
// Finds all packages used by given file and processes them
//---------------------------------------------------------
void ProcessFile(char *Filename) {
        ifstream fin(Filename, ios::in);
	struct ListNode *head;
	char *Nextword;
	Nextword = new char[81];

	if (!fin){
		fprintf(stderr,"couldn't open %s !\n", Filename);
		exit(1);
	}

	// initialize linked list to hold package names of this file only
	head = InitializeLocalList();

	// find all packages which are used by this file
	Nextword = get_token(fin);
	strupr(Nextword);
	while (strcmp(Nextword, "PACKAGE") != 0) {
		if (strcmp(Nextword, "WITH")==0) {
		        Nextword = get_token(fin);
			strupr(Nextword);
			InsertLocalList(head, Nextword);
			}
		Nextword = get_token(fin);
		strupr(Nextword);
		}

	//for generics, find implicit withs
        FindImplicitWiths(head);
	fin.close();               // close the file

	// search files for definition of remaining packages
	SearchFiles(head);

	// process files of defined packages
	ProcessPackages(head);

	UpdateAncestorFile(head, Filename);
	FreeMem(head);      // free the dynamically allocated linked list
}

//-----------------------------------------------------------------
//  Searches files in current directory for definitions of packages
//  in the local list
//-----------------------------------------------------------------
void SearchFiles(struct ListNode *head) {
	FILE *fp1;
	ifstream fin;
	char Nextfile[81];
	char *Nextword;
	Nextword = new char[405];
	int flag = 0;

	// open the current directory
	if ((fp1 = fopen("filelist.dat","r")) == NULL) {
		fprintf(stderr,"couldn't open filelist.dat !\n");
		exit(1);
	}
     
	while (fscanf(fp1, "%s", Nextfile) != EOF  &&  flag==0 ) {
		// open next file in current dir
	        fin.open(Nextfile,ios::in);
		if (!fin)
			fprintf(stderr,"couldn't open %s !\n", Nextfile);
		Nextword = get_token(fin);
		strupr(Nextword);
		while ( !fin.eof() ){    
			if (compare(Nextword, "PACKAGE") == 0) {
			        Nextword = get_token(fin);
				strupr(Nextword);
				if (UpdateLocalList(head,Nextword,Nextfile)) {
					flag = 1;
				}
				break;
			      }
			Nextword = get_token(fin);
			strupr(Nextword);
		}

		fin.close();            // close the file
	}

	fclose(fp1);   // close the current directory
}


//---------------------------------------------------------
//  If Package name is found, then corresponding filename
//  is entered in the list
//  Returns: 1 if all filenames have been entered
//           0 otherwise
//---------------------------------------------------------
int UpdateLocalList(struct ListNode *head, char *PackageName, char *filename) {
	struct ListNode *walk;
	int flag = 1;

	walk = head -> nextaddr;
	while (walk != NULL) {               // search Package name in list
		// cerr << "node <pkg/file>: >" << walk->PkgName << "< >" <<
		// 	walk->filename << "<" << endl;
		// cerr << "data <pkg/file>: >" << PackageName << "< >" <<
		// 	filename << "<" << endl;
		if ((walk->status == 'l')
				&& (compare(walk->PkgName, PackageName) == 0)){
			// cerr << "\tfound a match" << endl;
			strcpy(walk -> filename, filename);
			// cerr << "\tnode is now <pkg/file>: >" <<
			// 	walk->PkgName << "< >" <<
			// 	walk->filename << "<" << endl;
		      }
		// return 0, if some filename is missing
		if (walk->status == 'l'
				&& strcmp(walk->filename,"No Definition") == 0)
			flag = 0;

		walk = walk -> nextaddr;
	}
	return (flag);
}

//-------------------------------------------------------------------
//  For all undefined packages
//     insert into global list (they are assumed to exist in library)
//  For all defined packages
//     first process the file and then insert into global list
//-------------------------------------------------------------------
void ProcessPackages(struct ListNode *head) {
	struct ListNode *walk;

	walk = head -> nextaddr;
	while (walk != NULL) {  // if Package NOT defined assume it's in library
		if (walk->status == 'l'
				&& strcmp(walk->filename, "No Definition")==0)
			InsertGlobalList(walk);
        	else if (walk->status == 'l') {    // else if definition exists
			/* NULL */ ;
		}
		walk = walk -> nextaddr;
	}

	walk = head -> nextaddr;
	while (walk != NULL) {    // if there is a definition
		if (walk->status == 'l'
				&& strcmp(walk->filename,"No Definition")!=0) {
                        // process the file in which package is defined
		// cerr << "Invoking ProcessFile with " << walk->filename << endl;
		 	ProcessFile(walk -> filename);  
			InsertGlobalList(walk); 
		}
		walk = walk -> nextaddr;
	}
}



//------------------------------------------------
//   Inserts node at the tail of global list
//------------------------------------------------
void InsertGlobalList(struct ListNode *p) {
	struct ListNode *temp;
	// insert Package in list
	temp = (struct ListNode *)malloc(sizeof(struct ListNode));
	if (temp == NULL) {
		printf("Memory allocation error!\n");
		exit(1);
	}
	strcpy(temp -> PkgName, p -> PkgName);
	strcpy(temp -> filename, p -> filename);
	temp -> nextaddr = NULL;

	Gtail -> nextaddr = temp;
	Gtail = temp;
}



//------------------------------------------------------
//  Performs case insensitive comparison of two strings
//------------------------------------------------------
int compare(char *s1, char *s2) {
	char arr1[128], arr2[128];
	int i;

	for (i=0; *s1 != '\0'; i++) {   // convert first string to uppercase
		arr1[i] = toupper(*s1);
		s1++;
	} 
	arr1[i] = '\0';

	for (i=0; *s2 != '\0'; i++) {  // convert second string to uppercase
		arr2[i] = toupper(*s2);
		s2++;
	} 
	arr2[i] = '\0';
 
	return(strcmp(arr1, arr2));   // compare modified strings
}



//----------------------------------------------------
//  Causes indentation according to nesting 
//----------------------------------------------------
void gprintf(FILE *fp, char *s1, char *s2) {
	int i;
	for (i=0; i<NCount; i++)
		fprintf(fp, "    ");

	fprintf(fp, s1, s2);
}


//---------------------------------------------------------
//  Intializes local linked-list and returns head pointer
//---------------------------------------------------------
struct ListNode *InitializeLocalList() {
	struct ListNode *p;            // malloc a dummy node
	p = (struct ListNode *) malloc (sizeof(struct ListNode));
	if (p == NULL) {
		printf("Memory allocation error\n");
		exit(1);
	}
	p->nextaddr = NULL;
	return(p);
} 


//----------------------------------------------------------------------
//  Inserts a node in local linked-list and fills it with Package name.
//----------------------------------------------------------------------
void InsertLocalList(struct ListNode *head, char *PackageName) {
	struct ListNode *walk, *p;
   
	p = (struct ListNode *) malloc (sizeof(struct ListNode));
	if (p == NULL) {
		printf("Memory allocation error!\n");
		exit(1);
	}
	strcpy(p -> PkgName, PackageName);
	strcpy(p -> filename, "No Definition");  
	p -> status = 'l';
	p -> nextaddr = NULL;

	walk = head;
	while (walk -> nextaddr != NULL)
		walk = walk -> nextaddr;
	walk -> nextaddr = p;                    // insert at tail
}



//----------------------------------------------------------------------
//  Constructs the file `ancestor.dat'.
//  It holds complete information about the hierarchy of packages
//----------------------------------------------------------------------
void UpdateAncestorFile(struct ListNode *head, char *Filename) {
	struct ListNode *walk;

	fprintf(anfp, "\n!!!!\n%s\n", Filename);

	walk = head -> nextaddr;
	while (walk != NULL) {          // if package definition exists
		if (strcmp(walk -> filename, "No Definition") != 0)
			// write filename
			fprintf(anfp, "%s\n", walk -> filename);
		walk = walk -> nextaddr;
	}
	fprintf(anfp, "....\n");
}



//-------------------------------------------------------
//  Prints out package names in the local linked-list
//-------------------------------------------------------
void PrintPackages(struct ListNode *head) {
	struct ListNode *walk;

	gprintf(stdout, "Searching Packages %s\n"," ");
	gprintf(trfp, "Searching Packages %s\n"," ");

	walk = head -> nextaddr;
	while (walk != NULL) {
		fprintf(stdout, "%s  ", walk -> PkgName);
		fprintf(trfp, "%s  ", walk -> PkgName);
		walk = walk -> nextaddr;
	}
	fprintf(stdout, "...\n");
	fprintf(trfp, "...\n");
}



//-------------------------------------------------------
//  Frees dynamically allocated linked list
//-------------------------------------------------------
void FreeMem(struct ListNode *head) {
	struct ListNode *back, *walk;

	back = head;
	walk = head -> nextaddr;
	while (walk != NULL) {    // walk down the list freeing each node
		free(back);
		back = walk;
		walk = walk -> nextaddr;
	}
	free(back);
}




//-------------------------------------------------------
//  Test function to print local linked-list
//-------------------------------------------------------
void PrintLocalList(struct ListNode *head) {
	struct ListNode *walk;

	walk = head -> nextaddr;
	while (walk != NULL) {
		printf("%s\t\t%s\t%c\n",
			walk->PkgName, walk->filename, walk->status);
		walk = walk -> nextaddr;
	}
}



//-------------------------------------------------------
//  Test function to print global linked-list
//-------------------------------------------------------
void PrintGlobalList(void) {
	struct ListNode *walk;

	walk = Ghead -> nextaddr;

	while (walk != NULL) {
		printf("%s   %s\n", walk -> PkgName, walk -> filename);
		walk = walk -> nextaddr;
	}
}
