module linker;

import StdInt,StdFile,StdArray,StdBool,StdChar,StdString,StdClass;

import xcoff_linker;

/*
file_names = [
	"_startup0.o","_startup1.o","_startup2.o","_system.o","t.o","nfib.o"
/*
	"_startup0.o",		"deltaPicture.o",	"picture.o",		"StdInt.o",
	"_startup1.o",		"deltaSystem.o",	"scrabble.o",		"StdList.o",
	"_startup2.o",		"deltaTimer.o",		"scrollList.o",		"StdMisc.o",
	"_system.o",		"Dialog0.o",		"StdArray.o",		"StdOverloaded.o",
	"_SystemArray.o",	"Dialog1.o",		"StdBool.o",		"StdReal.o",      
	"_SystemEnum.o",	"Events.o",			"StdChar.o",		"StdString.o",       
	"Bord.o",			"Grafiek.o",		"StdCharList.o",	"StdTuple.o",
	"clCrossCall.o",	"intrface.o",		"StdClass.o",		"controllayout.o",
	"ioTypes.o",		"StdEnum.o",		"Timer0.o",			"deltaDialog.o",
	"Letters.o",		"StdEnv.o",			"Timer1.o",			"deltaEventIO.o",
	"Menu0.o",			"StdFile.o",		"Window0.o",		"deltaFont.o",
	"StdFunc.o",		"Window1.o",		"deltaIOSystem.o",	"cCrossCall.obj",
	"cDebug.obj",		"cPicture.obj",		"util.obj",			"t.o"
*/
];

library_file_names = [
	"kernel_library"
];

*/

n_args :: Int;
n_args = code {
	ccall n_args "-I"
}

program_arg :: !Int -> {#Char};
program_arg i = code {
	ccall program_arg "I-S"
}

parse_arguments = program_arguments 1 n_args;
{
	program_arguments n n_args
		| n>=n_args
			= ([],[]);
		| size argument>=2 && argument.[0]=='-' && argument.[1]=='l'
			= (files1,[argument % (2,size argument-1):libraries1]);
			= ([argument:files1],libraries1);
		{}{
			argument=program_arg n;
			(files1,libraries1)=program_arguments (inc n) n_args;
		}
}

Start world
	= undefined_symbols;
{
	(ok1,undefined_symbols,files1) = link_xcoff_files file_names library_file_names "a.exe" files0;
	(file_names,library_file_names) = parse_arguments;
	(files0,_) = openfiles world;
}

GetFullPathname modname {editor=ed=:{editwindows,defaults={paths=defpaths},project},disk}
	| windowpaths	=  ({editor=ed,disk=disk1}, pathname1);
	| diskpaths		=  ({editor=ed,disk=disk2}, pathname2);
					=  ({editor=ed,disk=disk2}, EmptyPathname);
	where {
	(disk2,diskpaths,pathname2)		= SearchDisk False modname defpaths prjpaths disk1;
	(disk1,windowpaths,pathname1)	= SearchWindows windows modname Nil disk;
	windows							= GetUsedWindows editwindows;
	prjpaths						= PR_GetPaths project;
	};
	
SearchWindows :: !(List EditWindow) !Modulename (!List Pathname) !Files -> (!Files,!Bool,!Pathname);
SearchWindows Nil modname done disk =  (disk, False, EmptyPathname);
SearchWindows (wd=:{wstate={pathname}}:!rest) modname done disk
	| skip		=  SearchWindows rest modname done disk;
	| exists	=  (disk1, True, dir_modname);
				=  SearchWindows rest modname done1 disk1	;
	where {
	dir					= RemoveFilename pathname;
	dir_modname			= MakeFullPathname dir modname;
	done1				= dir :! done;
	(exists,disk1)		= FExists dir_modname disk;
	skip				= StringOccurs dir done || dir == EmptyPathname;
	};
	
//	Search for a ABC file

SearchABCFile :: !Modulename !(List Pathname) !Files -> (!Files,!Bool,!Pathname);
SearchABCFile modname Nil disk
	= (disk,False,EmptyPathname);
SearchABCFile modname (def:!rest) disk
	| exists	= (disk`, True, abcpath);
				= SearchABCFile modname rest disk`;
	where {
	(exists,disk`)	= FExists abcpath disk1;
	(disk1,abcpath)	= MakeABCSystemPathname (MakeFullPathname def modname) disk;
	};

// Search disk for a file

SearchDisk :: !Bool !Modulename !(List Pathname) !(List Pathname) !Files -> (!Files,!Bool,!Pathname);
SearchDisk def_and_imp modname defdirs prjdirs disk
	= SearchDisk2 def_and_imp modname modname2 prjdirs defdirs disk;
	where {
		modname2	| not def_and_imp		= modname;
					| IsDefPathname modname	= MakeImpPathname modname;
											= MakeDefPathname modname;
	};

SearchDisk2 :: !Bool !Modulename !Modulename !(List Pathname) !(List Pathname) !Files -> (!Files,!Bool,!Pathname);
SearchDisk2 def_and_imp modname1 modname2 Nil Nil disk
	=  (disk, False, EmptyPathname);
SearchDisk2 def_and_imp modname1 modname2 Nil more_dirs disk
	= SearchDisk2 def_and_imp modname1 modname2 more_dirs Nil disk;
SearchDisk2 def_and_imp modname1 modname2 (dir:!rest) more_dirs disk
	| existsa			= (diska, True, dir_modname1);
	| not def_and_imp	= SearchDisk2 def_and_imp modname1 modname2 rest more_dirs diska;
	| existsb			= (diskb, True, dir_modname1);
						= SearchDisk2 def_and_imp modname1 modname2 rest more_dirs diskb;
	where {
		dir_modname1	= MakeFullPathname dir modname1;
		dir_modname2	= MakeFullPathname dir modname2;
		(existsa,diska)	= FExists dir_modname1 disk;
		(existsb,diskb)	= FExists dir_modname2 diska;
	};

//	Read a file

ReadFile :: !Pathname !Files -> (!Files, !Text, !NrLines, !Bool);
ReadFile path disk
	| opened	=  (disk`, text, nrlines, closed);
				=  (disk1, EmptyText, 1, False);
	where {
	(opened,file,disk1)		= fopen path FReadText disk;
	(list,nrlines,file`) 	= ReadLinesFromFile file;
	text                 	= Text_StringsToText list;
	(closed,disk`)			= fclose file` disk1;
	};

ReadLinesFromFile :: !*File -> (!List String, !Int, !*File);
ReadLinesFromFile file
	| eof	=  (last, nr, fil2);
			=  (ReplaceLastChar string :! rest, inc nrlines, ne