/* compiler.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2005-2009 */

/*

Copyright (c) 2005-2009 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

#include	"compiler.h"

#ifdef WIN32
#include	<windows.h>
#endif


namespace sswf
{
namespace as
{

#ifdef _MSVC
// replicate the Unix directory functions
struct DIRECTORY {
	const char              *path;
	struct _finddata_t      info;
	long                    handle;
};

typedef struct DIRECTORY        DIR;


struct dirent {
	char                    d_name[128];
};

DIR *opendir(const char *path)
{
	static DIR	dir;

	memset(&dir, 0, sizeof(dir));
	dir.path = path;
	dir.handle = -1;

	return (&dir);
}


struct dirent *readdir(DIR *dir)
{
	static struct dirent	entry;

	long		err, len;
	char		*name;

	memset(&entry, 0, sizeof(entry));

	if(dir->handle == -1) {
		len = strlen(dir->path) + 3;
		name = new char[len];
		memcpy(name, dir->path, len - 3);
		name[len - 3] = '\\';
		name[len - 2] = '*';
		name[len - 1] = '\0';
		dir->handle = _findfirst(
				name,
				&dir->info);
		delete [] name;
		if(dir->handle < 0) {
			err = -1;
		}
		else {
			err = 0;
		}
	}
	else {
		err = _findnext(dir->handle,
				&dir->info);
	}

	if(err < 0) {	/* end of directory */
		return (NULL);
	}

	strncpy(entry.d_name, dir->info.name,
			sizeof(entry.d_name));

	return (&entry);
}


void closedir(DIR *dir)
{
	if(dir->handle != -1) {
		_findclose(dir->handle);
	}

	return;
}
#endif	/* #if _MSVC */


/**********************************************************************/
/**********************************************************************/
/***  COMPILER CREATOR  ***********************************************/
/**********************************************************************/
/**********************************************************************/

Compiler *Compiler::CreateCompiler(InputRetriever *input)
{
	return new IntCompiler(input);
}


const char *Compiler::Version(void)
{
	return TO_STR(SSWF_VERSION);
}


/**********************************************************************/
/**********************************************************************/
/***  INTERNAL COMPILER (Basics)  *************************************/
/**********************************************************************/
/**********************************************************************/


IntCompiler::rc_t	IntCompiler::g_rc;
NodePtr			IntCompiler::g_global_import;
NodePtr			IntCompiler::g_system_import;
NodePtr			IntCompiler::g_native_import;


IntCompiler::IntCompiler(InputRetriever *input)
{
	// TODO: under MS-Windows, we may want to use something such
	//	 as a SHGetFolder() call
	f_home = getenv("HOME");

	//f_default_error_stream -- auto-init
	f_error_stream = &f_default_error_stream;
	//f_optimizer -- auto-init
	f_optimizer.SetErrorStream(f_default_error_stream);
	f_options = 0;
	f_input_retriever = input;
	//f_program -- auto-init
	f_time = time(NULL);
	f_err_flags = 0;
	//f_scope -- auto-init
	f_db = 0;
	f_db_size = 0;
	f_db_data = 0;
	f_db_count = 0;
	f_db_max = 0;
	f_db_packages = 0;
	f_mod_count = 0;
	f_mod_max = 0;
	f_modules = 0;

	InternalImports();
}


IntCompiler::~IntCompiler()
{
	if(f_db != 0) {
		fclose(f_db);
	}
	delete [] f_db_data;

	// delete packages we added in this session
	for(size_t idx = 0; idx < f_db_count; ++idx) {
		if(f_db_packages[idx] < f_db_data
		|| f_db_packages[idx] > f_db_data + f_db_size) {
			delete [] f_db_packages[idx];
		}
	}

	delete [] f_db_packages;
}


InputRetriever *IntCompiler::SetInputRetriever(InputRetriever *retriever)
{
	InputRetriever *old(f_input_retriever);

	f_input_retriever = retriever;

	return old;
}


void IntCompiler::SetErrorStream(ErrorStream& error_stream)
{
	f_error_stream = &error_stream;
	f_optimizer.SetErrorStream(error_stream);
}


void IntCompiler::SetOptions(Options& options)
{
	f_options = &options;
	f_optimizer.SetOptions(options);
}





bool IntCompiler::isspace(int c)
{
	return c == ' ' || c == '\t';
}




void IntCompiler::rc_t::FindRC(const String& home, bool accept_if_missing)
{
	// first try to find a place with a .rc
	// (at this time, these directories are kind of random...)
	static const char *directories[] = {
		// Mainly for Unices
		".",
		"include/sswf/scripts",
		"@include/sswf/scripts",	// For Win32
		".sswf",
		"~/.sswf",
		"~/sswf",
		"/etc",
		"/etc/sswf",
		"/usr/include/sswf/scripts",
		"/usr/share/sswf/scripts",
		0
	};

	const char **dir, *d;

	for(dir = directories; *dir != 0; ++dir) {
		d = *dir;
		if(*d == '@') {
			// by default I assume I cannot apply this function properly
			f_filename[0] = '\0';
#ifdef WIN32
			char		fullpath[MAX_PATH * 5];

			fullpath[0] = '\0';
			DWORD sz = GetModuleFileNameA(NULL, fullpath, MAX_PATH * 5);
			if(sz < MAX_PATH * 5) {
				char *s = fullpath + strlen(fullpath);
				while(s > fullpath) {
					if(s[-1] == '\\') {
						s--;
						while(s > fullpath) {
							if(s[-1] == '\\') {
								s[0] = '\0';
								snprintf(f_filename, sizeof(f_filename),
									"%s%s\\sswf.rc", fullpath, *dir + 1);
								break;
							}
							s--;
						}
						break;
					}
					s--;
				}
			}
#endif
		}
		else if(*d == '~') {
			if(home.IsEmpty()) {
				// no valid HOME directory
				continue;
			}
			char buf[256];
			size_t sz = sizeof(buf);
			home.ToUTF8(buf, sz);
			snprintf(f_filename, sizeof(f_filename), "%s/%s/sswf.rc", buf, *dir + 1);
		}
		else {
			snprintf(f_filename, sizeof(f_filename), "%s/sswf.rc", *dir);
		}
		if(f_filename[0] != '\0') {
#ifdef WIN32
			// Win98 can have problems with / in filenames
			char *s = f_filename;
			while(*s != '\0') {
				if(*s == '/') {
					*s = '\\';
				}
				s++;
			}
#endif
			f_f = fopen(f_filename, "rb");
			if(f_f != 0) {
				break;
			}
		}
	}
	if(f_f == 0) {
		if(!accept_if_missing) {
			fprintf(stderr, "INSTALLATION ERROR: cannot find the sswf.rc file; it is usually put in "
#ifdef WIN32
				"include/sswf/scripts found under the folder of the sswf executable.\n"
#else
				"/etc/sswf/sswf.rc\n"
#endif
				);
			exit(1);
		}
		// if we want everything internal, we'll just use working defaults
		f_path = "include/sswf/scripts";
		f_db = "tmp/asc_packages.db";
		strcpy(f_filename, "internal.rc");
	}
}


void IntCompiler::rc_t::ReadRC(void)
{
	char buf[256], *s, *name, *param;
	int l, line, quote;

	// if f_f is null, we already have the defaults
	// and f_input_retriever is not NULL
	if(f_f == 0) {
		return;
	}

	line = 0;
	while(fgets(buf, sizeof(buf), f_f) != 0) {
		line++;
		s = buf;
		while(isspace(*s)) {
			s++;
		}
		if(*s == '#' || *s == '\n' || *s == '\0') {
			continue;
		}
		name = s;
		while(*s != '\0' && *s != '=' && !isspace(*s)) {
			s++;
		}
		l = s - name;
		while(isspace(*s)) {
			s++;
		}
		if(*s != '=') {
fprintf(stderr, "%s:%d: syntax error; expected an equal sign\n", f_filename, line);
			continue;
		}
		// skip the = and following spaces
		s++;
		while(isspace(*s)) {
			s++;
		}
		if(*s == '"' || *s == '\'') {
			quote = *s++;
			param = s;
			while(*s != '\0' && *s != quote && *s != '\n') {
				s++;
			}
		}
		else {
			param = s;
			while(*s != '\0' && *s != '\n') {
				s++;
			}
		}
		*s = '\0';
		if(l == 7 && strncmp(name, "version", 7) == 0) {
			// TODO: check that we understand this version
			continue;
		}
		if(l == 8 && strncmp(name, "asc_path", 8) == 0) {
//fprintf(stderr, "Got path! [%s]\n", param);
			f_path = param;
			continue;
		}
		if(l == 6 && strncmp(name, "asc_db", 6) == 0) {
			f_db = param;
			continue;
		}
	}
}




String IntCompiler::GetPackageFilename(const char *package_info)
{
	int		cnt;

	cnt = 0;
	while(package_info != '\0') {
		package_info++;
		if(package_info[-1] == ' ') {
			cnt++;
			if(cnt >= 3) {
				break;
			}
		}
	}
	if(*package_info != '"') {
		return "";
	}
	package_info++;
	const char *name = package_info;
	while(*package_info != '"' && *package_info != '\0') {
		package_info++;
	}

	String result;
	result.FromUTF8(name, package_info - name);

	return result;
}


/** \fn InputRetriever::Retrieve(const char *filename) = 0
 *
 * \brief Function you want to overload to provide your own input file to the loader.
 *
 * The IntCompiler::FindModule() function calls this function to retrieve an
 * input file. The input file is used to load the module specified by
 * \p filename.
 *
 * This is used by the libasc extension that enables you to put all the system
 * ASC files in your binary and thus avoid you having to deal with a directory
 * structure.
 *
 * \bug
 * The returned pointer MUST be a class derived from Input and MUST be allocated.
 * If the file cannot be opened by your retriever, then return NULL.
 *
 * \param[in] filename  The name of the module to be loaded
 *
 * \return NULL or an object derived from the Input class. You MUST allocate this object
 * since the IntCompiler::FindModule() will delete it once the module was loaded.
 */

bool IntCompiler::FindModule(const String& filename, NodePtr& result)
{
	int		i, j, p, r;
	module_t	*mod;

	p = 0;		// avoid warnings
	j = f_mod_count;
	if(j < 4) {
		// lack of precision forces us to do this with 0 to 3 elements
		p = 0;
		while(p < j) {
			r = filename.Compare(f_modules[p].f_filename);
			if(r == 0) {
				result = f_modules[p].f_node;
				return true;
			}
			// when filename < f_modules.f_filename,
			// we won't find anything anymore; also
			// p is at the right place to add the
			// file!
			if(r < 0) {
				break;
			}
			p++;
		}
	}
	else {
		// this is really the binary search
		// (see the / 2 below?)
		i = 0;
		while(i < j) {
			p = i + (j - i) / 2;
			r = filename.Compare(f_modules[p].f_filename);
			if(r == 0) {
				result = f_modules[p].f_node;
				return true;
			}
			if(r > 0) {
				p++;
				i = p;
			}
			else {
				j = p;
			}
		}
	}

// we couldn't find this entry, when n != 0 insert a new row
// otherwise try to load the module
	if(!result.HasNode()) {
		FileInput file_input;
		Input *in(NULL);
		char *fn = filename.GetUTF8();
		if(f_input_retriever != NULL) {
			in = f_input_retriever->Retrieve(fn);
		}
		if(in == NULL) {
			if(!file_input.Open(fn)) {
				fprintf(stderr, "FATAL ERROR: cannot open module file \"%s\".\n", fn);
				delete [] fn;
				exit(1);
			}
			in = &file_input;
		}

		Parser *parser = Parser::CreateParser();
		if(f_options != 0) {
			parser->SetOptions(*f_options);
		}
		parser->SetInput(*in);

		result = parser->Parse();

		delete parser;
		if(in != &file_input) {
			// This is ugly, we should have a shared pointer or
			// some other feature to handle that one!
			delete in;
		}

#if 0 && (defined(_DEBUG) || defined(DEBUG))
fprintf(stderr, "%s module:\n", fn);
result.Display(stderr);
#endif

		if(!result.HasNode()) {
			fprintf(stderr, "FATAL ERROR: cannot compile module file \"%s\".\n", fn);
			delete [] fn;
			exit(1);
		}

		delete [] fn;
	}

	// we need to enlarge the f_db_packages buffer
	if(f_mod_count >= f_mod_max) {
		// enlarge buffer
		f_mod_max += 250;
		mod = new module_t[f_mod_max];
		for(i = 0; (size_t) i < f_mod_count; ++i) {
			mod[i] = f_modules[i];
		}
		delete [] f_modules;
		f_modules = mod;
	}

	i = f_mod_count;
	while(i > p) {
		f_modules[i] = f_modules[i - 1];
		i--;
	}
	f_mod_count++;

	f_modules[p].f_filename = filename;
	f_modules[p].f_node = result;

	return true;
}



NodePtr IntCompiler::LoadModule(const char *module, const char *file)
{
	char		buf[256];
	char		path[256];
	size_t		sz;
	NodePtr		result;

	// create the name of the module
	sz = sizeof(path);
	g_rc.GetPath().ToUTF8(path, sz);
	sz = snprintf(buf, sizeof(buf), "%s/%s/%s", path, module, file);
	if(sz >= sizeof(buf)) {
		fprintf(stderr, "FATAL ERROR: filename too long; cannot load module.\n");
		exit(1);
	}

	// search for the module; if not already loaded, load it
	FindModule(buf, result);
	return result;
}


void IntCompiler::ReadDB(void)
{
	Input		*in(NULL);

	if(f_db != 0) {
		fclose(f_db);
		f_db = 0;
	}

// the input retriever defines the packages database?
	if(f_input_retriever != NULL) {
		// if we have a retriever query the retriever for the database
		// in this case the database is read only (i.e. f_db remains set
		// to zero)
		in = f_input_retriever->Retrieve("asc_packages.db");
	}
	if(in != NULL) {
		f_db_size = in->GetSize();
		delete [] f_db_data;
		f_db_data = new char[f_db_size + 2];
		// TODO: we do not currently support UTF-8 here, only ASCII...
		for(size_t i = 0; i < f_db_size; ++i) {
			f_db_data[i] = in->GetC();
		}
		// we're done with the input
		delete in;

		f_db_data[f_db_size] = '\0';
	}
	else {
// get resource filename if any
		String db = g_rc.GetDB();
		if(db.GetLength() == 0) {
			// internal default
			db = "~/.sswf/asc_packages.db";
		}

// check for user path
		const long *path = db.Get();
		long len = db.GetLength();
		if(len > 1
		&& path[0] == '~'
		&& (path[1] == '/' || path[1] == '\\')) {
			String s = f_home;
			s.AppendStr(path + 1, len - 1);
			db = s;
		}

// open the file
		char filename[256];
		size_t sz = sizeof(filename);
		db.ToUTF8(filename, sz);
		f_db = fopen(filename, "rb+");
		if(f_db == 0) {
			// if it fails, try to create possibly missing directories
			char *fn;
			fn = filename;
			while(*fn != '\0') {
				if(*fn == '/' || *fn == '\\') {
					char c = *fn;
					*fn = '\0';
#ifdef WIN32
					mkdir(filename);
#else
					mkdir(filename, 0700);
#endif
					*fn = c;
					do {
						fn++;
					} while(*fn == '/' || *fn == '\\');
				}
				else {
					fn++;
				}
			}
			// now try again to open the file...
			f_db = fopen(filename, "wb+");
			if(f_db == 0) {
				// we can't create the database!
				fprintf(stderr, "FATAL ERROR: can't open or create database file \"%s\" for package information.\n", filename);
				exit(1);
			}
		}

// read the file at once [we need it this way to do binary searches]
// mmap would be a pain to manage since we would need to remap all the
// time whenever we insert new lines in the DB.
		fseek(f_db, 0, SEEK_END);
		f_db_size = ftell(f_db);
		fseek(f_db, 0, SEEK_SET);

		if(f_db_size == 0) {
			// when the file just got created, print out a comment
			fprintf(f_db, "# Database of SSWF ActionScript Compiler (asc)\n");
			fprintf(f_db, "# DO NOT EDIT UNLESS YOU KNOW WHAT YOU ARE DOING\n");
			fprintf(f_db, "# Copyright (c) 2005-2009 by Made to Order Software Corp.\n");
			fprintf(f_db, "# WARNING: package names below MUST be sorted\n");
			fprintf(f_db, "# This file is written in UTF-8\n");
			fprintf(f_db, "# You can safely modify it with an editor which supports UTF-8\n");
			fprintf(f_db, "# package name + element name + type + file name + line number\n");
			fflush(f_db);
        
			fseek(f_db, 0, SEEK_END);
			f_db_size = ftell(f_db);
			fseek(f_db, 0, SEEK_SET);
		}

		delete [] f_db_data;
		f_db_data = new char[f_db_size + 2];
		if(fread(f_db_data, 1, f_db_size, f_db) != f_db_size) {
			fprintf(stderr, "FATAL ERROR: can't read the database file: \"%s\".\n", filename);
			exit(1);
		}

		f_db_data[f_db_size] = '\0';
	}

// "parse" the file and create the sorted array
// [the file is assumed to already be sorted]
	// count the lines which aren't commented out
	// and we remove empty lines and change new lines to just '\n'
	f_db_count = 0;
	char c, *s, *d;
	s = d = f_db_data;
	c = *s;
	while(c != '\0') {
		// skip leading spaces and empty lines
		// (those are lost)
		while(isspace(*s) || *s == '\n' || *s == '\r') {
			s++;
		}
		// don't count comments (we don't need them in our array)
		if(*s != '#') {
			f_db_count++;
		}
		while(*s != '\n' && *s != '\r' && *s != '\0') {
			*d++ = *s++;
		}
		// We need to skip this here otherwise a comment on
		// the last line generates an error!
		while(*s == '\n' || *s == '\r') {
			s++;
		}
		c = *s;
		// we only want '\n', no '\r\n' nor just '\r'
		*d++ = '\n';
	}
	*d = '\0';
	// f_db_size can now be smaller or 1 byte bigger
	f_db_size = d - f_db_data;
//fprintf(stderr, "Count = %ld\n", (long) f_db_count);

	if(f_db_count < 1000) {
		f_db_max = 1000;
	}
	else {
		f_db_max = f_db_count + 100;
	}

	// save the start of each line which isn't commented
	f_db_packages = new char *[f_db_max];
	s = f_db_data;
	char **p = f_db_packages;
	while(*s != '\0') {
		// ignore comments
		if(*s != '#') {
			*p++ = s;
		}
		// TODO: we really need to check the validity of each
		//	 entry and skip invalid lines as comments
		//	 go to the next line
		while(*s != '\0') {
			s++;
			if(s[-1] == '\n') {
				break;
			}
		}
	}

// it's ready.
}




void IntCompiler::WriteDB(void)
{
	const char	*s, *start;
	size_t		idx;

// if we are in ReadOnly mode, we simply cannot save the database
	if(f_db == 0) {
		return;
	}

// clear the file
	fseek(f_db, 0, SEEK_SET);
#ifdef WIN32
	_chsize(fileno(f_db), 0);
#else
	ftruncate(fileno(f_db), 0);
#endif

// save all the comments at the start of the file
	s = f_db_data;
	while(*s != '\0') {
		start = s;
		// ignore lines that are not comments
		if(*s != '#') {
			continue;
		}
		while(*s != '\n' && *s != '\0') {
			s++;
		}
		fprintf(f_db, "%.*s\n", s - start, start);
		while(*s == '\n') {
			s++;
		}
	}

// now save the list of rows
	for(idx = 0; idx < f_db_count; ++idx) {
		start = s = f_db_packages[idx];
		while(*s != '\n' && *s != '\0') {
			s++;
		}
		fprintf(f_db, "%.*s\n", s - start, start);
	}

// make sure it's written on disk
	fflush(f_db);
}



// s2 ends with a space (name from database)
// s1 ends with a null terminator
int pckcmp(const char *s1, const char *s2)
{
	int	r, cnt;

	cnt = 0;
	while(*s1 != '\0' && *s2 != '\n' && *s2 != '\0') {
		if(*s2 == ' ') {
			cnt++;
			if(cnt == 2) {
				break;
			}
			if(*s1 != ' ') {
				r = *s1 - *s2;
				return r < 0 ? -1 : 1;
			}
			s1++;
			s2++;
			if(s1[0] == '*' && s1[1] == '\0') {
				return 0;
			}
			continue;
		}
		r = *s1 - *s2;
		if(r != 0) {
			return r < 0 ? -1 : 1;
		}
		s1++;
		s2++;
	}

	if(*s1 == '\0' && *s2 == ' ') {
		return 0;
	}

	return *s1 != '\0' ? 1 : -1;
}


// Search for a named element:
// <package name>{.<package name>}.<class, function, variable name>
// TODO: add support for '*' in <package name>
const char *IntCompiler::FindElement(const String& package_name, const String& element_name, NodePtr *element, const char *type)
{
	int		i, j, p, r, len;
	size_t		sz;
	char		buf[16], **pck;

	// transform the name in a UTF-8 string as supported by the database
	len = package_name.GetUTF8Length() + element_name.GetUTF8Length();
	if(len < 0) {
		fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (1)\n");
		exit(1);
	}
	len += 3;
#ifdef _MSVC
	// alloca() not available with cl
	class AutoDelete {
	public: AutoDelete(char *ptr) { f_ptr = ptr; }
		~AutoDelete() { delete f_ptr; }
	private: char *f_ptr;
	};
	char *n = new char[len];
	AutoDelete ad_n(n);
#else
	char n[len];
#endif
	sz = len;
	package_name.ToUTF8(n, sz);
	n[len - sz] = ' ';
	sz--;
	element_name.ToUTF8(n + len - sz, sz);

	// the f_db_packages list of strings are sorted
	p = i = 0;
	j = f_db_count;
	if(j < 4) {
		// lack of precision forces us to do this with 0 to 3 elements
		p = 0;
		while(p < j) {
			r = pckcmp(n, f_db_packages[p]);
			if(r == 0) {
				goto found;
			}
			// when n < f_db_packages, we won't find anything
			if(r < 0) {
				break;
			}
			p++;
		}
	}
	else {
		// this is really the binary search
		// (see the / 2 below?)
		while(i < j) {
			p = i + (j - i) / 2;
			r = pckcmp(n, f_db_packages[p]);
			if(r == 0) {
				goto found;
			}
			if(r > 0) {
				p++;
				i = p;
			}
			else {
				j = p;
			}
		}
	}

// we couldn't find this entry, when type != 0 insert a new row
	if(type == 0) {
		return 0;
	}

	// we need to enlarge the f_db_packages buffer
	if(f_db_count >= f_db_max) {
		// enlarge buffer
		f_db_max += 250;
		pck = new char *[f_db_max];
		memcpy(pck, f_db_packages, sizeof(char *) * f_db_count);
		delete [] f_db_packages;
		f_db_packages = pck;
	}

	if((int) f_db_count - p > 0) {
		memmove(f_db_packages + p + 1, f_db_packages + p, sizeof(char *) * (f_db_count - p));
	}
	f_db_count++;

	// build the string which is:
	// <package name> <element name> <type> "<filename>" <line>
	{
	String entry(package_name);
	entry += " ";
	entry += element_name;
	entry += " ";
	entry += type;
	entry += " \"";
	entry += element->GetFilename();
	entry += "\" ";
	snprintf(buf, sizeof(buf), "%ld", element->GetLine());
	entry += buf;
	entry += "\n";

	len = entry.GetUTF8Length();
	if(len < 0) {
		fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (2)\n");
		exit(1);
	}
	len += 2;
	f_db_packages[p] = new char[len];
	sz = len;
	if(entry.ToUTF8(f_db_packages[p], sz) < 0) {
		fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (3)\n");
		exit(1);
	}
//fflush(stdout);
//fprintf(stderr, "Add [%s]\n", f_db_packages[p]);
	}

	return f_db_packages[p];

found:
	// TODO: if type != 0 we want to test that the filename referenced
	//	 is the same as the one in the database
	return f_db_packages[p];
}


void IntCompiler::FindPackages_AddDatabaseEntry(const String& package_name, NodePtr& element, const char *type)
{
	// here, we totally ignore internal, private
	// and false entries right away
	unsigned long attr = GetAttributes(element);
	if((attr & (NODE_ATTR_PRIVATE | NODE_ATTR_FALSE | NODE_ATTR_INTERNAL)) != 0) {
		return;
	}

	Data& data = element.GetData();
	FindElement(package_name, data.f_str, &element, type);
}



// This function searches a list of directives for class, functions
// and variables which are defined in a package. Their names are
// then saved in the import database for fast search.
void IntCompiler::FindPackages_SavePackageElements(NodePtr& package, const String& package_name)
{
	int max = package.GetChildCount();
	for(int idx = 0; idx < max; ++idx) {
		NodePtr& child = package.GetChild(idx);
		Data& data = child.GetData();
		if(data.f_type == NODE_DIRECTIVE_LIST) {
			FindPackages_SavePackageElements(child, package_name);
		}
		else if(data.f_type == NODE_CLASS) {
			FindPackages_AddDatabaseEntry(
					package_name,
					child,
					"class"
				);
		}
		else if(data.f_type == NODE_FUNCTION) {
			// we don't save prototypes, that's tested later
			int flg = data.f_int.Get();
			const char *type;
			if((flg & NODE_FUNCTION_FLAG_GETTER) != 0) {
				type = "getter";
			}
			else if((flg & NODE_FUNCTION_FLAG_SETTER) != 0) {
				type = "setter";
			}
			else {
				type = "function";
			}
			FindPackages_AddDatabaseEntry(
					package_name,
					child,
					type
				);
		}
		else if(data.f_type == NODE_VAR) {
			int vcnt = child.GetChildCount();
			for(int v = 0; v < vcnt; ++v) {
				NodePtr& variable = child.GetChild(v);
				// we don't save the variable type,
				// it wouldn't help resolution
				FindPackages_AddDatabaseEntry(
						package_name,
						variable,
						"variable"
					);
			}
		}
		else if(data.f_type == NODE_PACKAGE) {
			// sub packages
			NodePtr& list = child.GetChild(0);
			String name = package_name;
			name += ".";
			name += data.f_str;
			FindPackages_SavePackageElements(list, name);
		}
	}
}


// this function searches the tree for packages (it stops at classes,
// functions, and other such blocks)
void IntCompiler::FindPackages_DirectiveList(NodePtr& list)
{
	int max = list.GetChildCount();
	for(int idx = 0; idx < max; ++idx) {
		NodePtr& child = list.GetChild(idx);
		Data& data = child.GetData();
		if(data.f_type == NODE_DIRECTIVE_LIST) {
			FindPackages_DirectiveList(child);
		}
		else if(data.f_type == NODE_PACKAGE) {
			// Found a package! Save all the functions
			// variables and classes in the database
			// if not there yet.
			NodePtr& directive_list = child.GetChild(0);
			FindPackages_SavePackageElements(directive_list, data.f_str);
		}
	}
}


void IntCompiler::FindPackages(NodePtr& program)
{
	Data& data = program.GetData();
	if(data.f_type != NODE_PROGRAM) {
		return;
	}

	FindPackages_DirectiveList(program);
}


void IntCompiler::LoadInternalPackages(const char *module)
{
#ifndef PATH_MAX
#define	PATH_MAX 259
#endif
	char		buf[PATH_MAX], path[PATH_MAX];
	size_t		sz;
	DIR		*dir;

	sz = sizeof(path);
	g_rc.GetPath().ToUTF8(path, sz);
	snprintf(buf, sizeof(buf), "%s/%s", path, module);
	dir = opendir(buf);
	if(dir == 0) {
#ifdef WIN32
		if(buf[0] != '/' && buf[0] != '\\'
		&& (((buf[0] < 'a' || buf[0] > 'z')
		  && (buf[0] < 'A' || buf[0] > 'Z'))
				|| buf[1] != ':')) {
			char fullpath[MAX_PATH * 5];

			fullpath[0] = '\0';
			fullpath[sizeof(fullpath) - 1] = '\0';
			DWORD sz = GetModuleFileNameA(NULL, fullpath, sizeof(fullpath));
			if(sz < MAX_PATH * 5) {
				char *s = fullpath + strlen(fullpath);
				while(s > fullpath) {
					if(s[-1] == '\\') {
						s--;
						while(s > fullpath) {
							if(s[-1] == '\\') {
								strncpy(s, buf, (sizeof(fullpath) - 1) - (s - fullpath));
								dir = opendir(fullpath);
								break;
							}
							s--;
						}
						break;
					}
					s--;
				}
			}
		}
#endif
		if(dir == 0) {
			fprintf(stderr, "INSTALLATION ERROR: cannot read directory \"%s\".\n", buf);
			exit(1);
		}
	}

	struct dirent *ent;
	while((ent = readdir(dir)) != 0) {
		const char *s = ent->d_name;
		const char *e = 0;
		while(*s != '\0') {
			if(*s == '.') {
				e = s;
			}
			s++;
		}
		// TODO: under MS-Windows and other such file systems,
		//	 a filename is not case sensitive
		if(e == 0 || strcmp(e, ".asc") != 0
		|| strcmp(ent->d_name, "as_init.asc") == 0) {
			continue;
		}
		// we've got a file of interest
		// TODO: we want to keep this package in RAM since
		//	 we already parsed it!
		NodePtr p = LoadModule(module, ent->d_name);
		// now we can search the package in the actual code
		FindPackages(p);
	}

	// avoid leaks
	closedir(dir);
}


void IntCompiler::InternalImports(void)
{
	if(!g_global_import.HasNode()) {
#if defined(_DEBUG) || defined(DEBUG)
fflush(stdout);
#endif
		// Read the resource file
		g_rc.FindRC(f_home, f_input_retriever != NULL);
		g_rc.ReadRC();
		g_rc.Close();

		g_global_import = LoadModule("global", "as_init.asc");
		g_system_import = LoadModule("system", "as_init.asc");
		g_native_import = LoadModule("native", "as_init.asc");
	}

	ReadDB();

	if(f_db_count == 0) {
		LoadInternalPackages("global");
		LoadInternalPackages("system");
		LoadInternalPackages("native");

		// this saves the internal packages info
		WriteDB();
	}
}



};	// namespace as
};	// namespace sswf
