/*
	File:		clm.c
	Written by: John van Groningen
	At:			University of Nijmegen
*/

#define CLM_VERSION "2.2"

#define VERSION 920

#ifndef SYSTEM_LINKER
# define OPTIMISE_LINK
#endif

#if defined (MAKE_MPW_TOOL) && defined (NB)
# define NO_CLEAN_SYSTEM_FILES_FOLDERS
#endif

#define for_l(v,l,n) for(v=(l);v!=NULL;v=v->n)

#ifdef OS2
# define OMF
# define NO_CLIB
#endif

#if defined (_WINDOWS_)
# undef USE_WLINK
# define USE_CLEANLINKER
# define NO_CLIB
#endif

#if defined (_WINDOWS_) || defined (OMF) || defined (LINUX) || defined (SOLARIS)
# define NO_ASSEMBLE
#endif

#define MARKING_GC

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

#if defined(I486) || defined (SOLARIS) || defined (LINUX)
#	include <unistd.h>
#endif

#include <signal.h>

#ifdef A_64
# include <stdint.h>
# define Int32 int32_t
#else
# define Int32 long
#endif

#if defined (applec) || defined (__MWERKS__)
#	define MACINTOSH 1
#	if __MWERKS__
#		define POWER
#	endif
#else
#	define MACINTOSH 0
#endif

#if MACINTOSH
#	include <Files.h>
#	include <Strings.h>
#	ifdef applec
#		include <FCntl.h>
#	endif
#	define USE_PATH_CACHE 1
#else
# if !(defined (_WINDOWS_) || defined (OS2))
#	include <sys/file.h>
#	include <sys/types.h>
#	include <sys/wait.h>
# endif
# ifdef OS2
#	include <sys/types.h>
# endif
#	include <sys/stat.h>
#	define USE_PATH_CACHE 0
#endif

#if USE_PATH_CACHE
#	include "path_cache.h"
#endif

#ifdef _WINDOWS_
# ifdef __MWERKS__
#  include <x86_prefix.h>
# else
#  ifndef GNU_C
#   define _X86_
#  endif
#  include <stdarg.h>
# endif
# ifdef GNU_C
#  include <windows.h>
# else
#  include <windef.h>
#  include <winbase.h>
# endif
# include <process.h>
#endif

#ifdef OS2
# define INCL_ERRORS
# define INCL_DOSFILEMGR
# define INCL_DOSPROCESS
# include <os2.h>
#endif

#if !MACINTOSH
 extern char *getenv();
#endif

#define PATH_NAME_STRING_SIZE 256
#define PATH_LIST_STRING_SIZE 1024

/* file and directory names which may be patched using patchbin */
#if defined (I486) && !defined (LINUX)
#  ifdef OS2
	char clean_lib_directory[PATH_NAME_STRING_SIZE] = "\\clean\\lib";
	char gcc_lib_directory[PATH_NAME_STRING_SIZE] = "\\emx\\lib";
	char assembler_file_name[PATH_NAME_STRING_SIZE] = "as.exe";
#	ifdef OMF
	char linker_file_name[PATH_NAME_STRING_SIZE] = "link386.exe";
	char crt0_file_name [PATH_NAME_STRING_SIZE] = "crt0.obj";
#	else
	char gcc_lib_directory_arg[PATH_NAME_STRING_SIZE+2];
	char linker_file_name[PATH_NAME_STRING_SIZE] = "ld.exe";
	char crt0_file_name [PATH_NAME_STRING_SIZE] = "crt0.o";
#	endif
#  else
#   ifdef _WINDOWS_
	char clean_lib_directory[PATH_NAME_STRING_SIZE] = "d:\\john";
	char assembler_file_name[PATH_NAME_STRING_SIZE] = "as.exe";
#	 ifndef USE_WLINK
#     ifdef USE_CLEANLINKER
	char linker_file_name[PATH_NAME_STRING_SIZE] = "linker.exe";
#     else
	char linker_file_name[PATH_NAME_STRING_SIZE] = "ld.exe";
#     endif
	char crt0_file_name [PATH_NAME_STRING_SIZE] = "c:\\gnu\\lib\\crt0.o";
#	 else
	char linker_file_name[PATH_NAME_STRING_SIZE] = "wlink";
	char crt0_file_name [PATH_NAME_STRING_SIZE] = "crt0.o";
#	 endif
#   else
	char clean_lib_directory[PATH_NAME_STRING_SIZE] = "/usr/scratch/johnvg/clean";
	char assembler_file_name[PATH_NAME_STRING_SIZE] = "/usr/bin/as";
	char linker_file_name[PATH_NAME_STRING_SIZE] = "/usr/bin/ld";
	char crt0_file_name [PATH_NAME_STRING_SIZE] = "/usr/lib/crt0.o";
#   endif
#  endif
#else
#  if defined (SOLARIS) || defined (LINUX)
	char _clean_lib_directory[PATH_NAME_STRING_SIZE] = "#$@CLEANLIB  %*&.";
	char _clean_directory_list[PATH_LIST_STRING_SIZE]= "#$@CLEANPATH %*&.";
	char _clean_include_library_directory[PATH_NAME_STRING_SIZE] = "#$@CLEANILIB %*&.";
#   ifdef SOLARIS
    char _assembler_file_name[PATH_NAME_STRING_SIZE] = "#$@ASSEMBLER %*&/usr/ccs/bin/as";
    char _linker_file_name[PATH_NAME_STRING_SIZE]    = "#$@LINKER    %*&/usr/ccs/bin/ld";
#   else
    char _assembler_file_name[PATH_NAME_STRING_SIZE] = "#$@ASSEMBLER %*&/usr/bin/as";
    char _linker_file_name[PATH_NAME_STRING_SIZE]    = "#$@LINKER    %*&/usr/bin/gcc";
#   endif
    char _crt0_file_name [PATH_NAME_STRING_SIZE]     = "#$@CRT       %*&crt1.o";
	char _ld_args [PATH_LIST_STRING_SIZE]            = "#$@LDARGS    %*&";
/* 	char _ld_args [PATH_LIST_STRING_SIZE]			 = "#$@LDARGS    %*& --dynamic /lib/ld-linux.so.1"; */
#	define clean_lib_directory (&_clean_lib_directory[16])
#	define clean_directory_list (&_clean_directory_list[16])
#	define clean_include_library_directory (&_clean_include_library_directory[16])
#   define assembler_file_name (&_assembler_file_name[16])
#   define linker_file_name (&_linker_file_name[16])
#   define crt0_file_name (&_crt0_file_name[16])
#   define ld_args (&_ld_args[16])
#  else
	char clean_lib_directory[PATH_NAME_STRING_SIZE] = ".";
	char assembler_file_name[PATH_NAME_STRING_SIZE] = "/bin/as";
	char linker_file_name[PATH_NAME_STRING_SIZE] = "/bin/ld";
	char crt0_file_name [PATH_NAME_STRING_SIZE] = "/lib/crt0.o";
#  endif
#endif

#ifdef M68000
char Mcrt1_file_name [PATH_NAME_STRING_SIZE] = "/lib/Mcrt1.o";
#endif

/* default options of cocl */

#define DEFAULT_WARNING 				1
#define DEFAULT_INLINE					1
#define DEFAULT_VERBOSE 				0
#define DEFAULT_DEBUG					0
#define DEFAULT_NO_TIME_PROFILE			1
#define DEFAULT_STRICTNESS_ANALYSIS 	1
#define DEFAULT_LIST_TYPES				0
#define DEFAULT_LIST_ALL_TYPES			0

#define DEBUG_MASK					1
#define INLINE_MASK 				2
#define PARALLEL_MASK				4
#define STACK_LAYOUT_MASK			8
#define STRICTNESS_ANALYSIS_MASK	16
#define NO_TIME_PROFILE_MASK 		32
#define VERBOSE_MASK				64
#define WARNING_MASK				128
#define SYSTEM_MASK 				256

#define LIST_TYPES_MASK 			512
#define LIST_ALL_TYPES_MASK 		1024

#define REUSE_UNIQUE_NODES			2048
#define MEMORY_PROFILE				8192
 
#define DEFAULT_OPTIONS \
	(DEFAULT_WARNING ? WARNING_MASK : 0)\
	| (DEFAULT_INLINE ? INLINE_MASK : 0)\
	| (DEFAULT_VERBOSE ? VERBOSE_MASK : 0)\
	| (DEFAULT_DEBUG ? DEBUG_MASK : 0)\
	| (DEFAULT_NO_TIME_PROFILE ? NO_TIME_PROFILE_MASK : 0)\
	| (DEFAULT_STRICTNESS_ANALYSIS ? STRICTNESS_ANALYSIS_MASK : 0)\
	| (DEFAULT_LIST_TYPES ? LIST_TYPES_MASK : 0)\
	| (DEFAULT_LIST_ALL_TYPES ? LIST_ALL_TYPES_MASK : 0)

static int clean_options=DEFAULT_OPTIONS;	
static int clean_options_mask=0;
static int remove_symbol_table;
static int	list_strict_export_types=0,funcmayfail_error=0,varnotused_error=0,funcnotused_error=0,
			nowarn=0,progress=0;

static char *cocl_redirect_stdout,*cocl_redirect_stdout_option;
static char *cocl_redirect_stderr,*cocl_redirect_stderr_option;

static int check_stack_overflow,check_indices;
#if defined (MAKE_MPW_TOOL) && defined (NB)
static int generate_mc68020_code=0,show_help=0;
#endif

#ifdef OS2
static int window_application;
#endif

#ifdef _WINDOWS_
	typedef FILETIME FileTime;
#	define FILE_TIME_LE(t1,t2) ((unsigned)(t1.dwHighDateTime)<(unsigned)(t2.dwHighDateTime) \
		|| (t1.dwHighDateTime==t2.dwHighDateTime && (unsigned)(t1.dwLowDateTime)<=(unsigned)(t2.dwLowDateTime)))
#else
	typedef unsigned long FileTime;
#	define FILE_TIME_LE(t1,t2) (t1<=t2)
#endif

typedef struct project_node {
	struct project_node *		pro_next;
	char *						pro_fname;
	struct dep_list *			pro_depend;
	int 						pro_options;
	FileTime					pro_abc_time;
	FileTime					pro_dcl_time;
	unsigned int				pro_up_to_date:1,
								pro_valid_abc_time:1,
								pro_valid_dcl_time:1,
								pro_no_abc_file:1,
								pro_no_dcl_file:1,
								pro_valid_options:1,
								pro_ignore_o:1;
	struct object_file_list *	pro_imported_object_files;
} *P_NODE;

typedef struct dep_list {
	struct dep_list *		dep_next;
	P_NODE					dep_node;
} *DEP_LIST;

P_NODE first_project_node,last_project_node,main_project_node;

struct object_file_list {
	struct object_file_list *	object_file_next;
#if !defined (GNU_C)
	char						object_file_name[];
#else
	char						object_file_name[1];
#endif
};

struct library_list {
	struct library_list *	library_next;
#if !defined (GNU_C)
	char					library_file_name[];
#else
	char					library_file_name[1];
#endif
};

static struct library_list *first_library,*last_library;

static void warning_s (char *warning_string,char *s)
{
	fprintf (stderr,warning_string,s);
	fputc ('\n',stderr);
}

static void warning_s_s (char *warning_string,char *s1,char *s2)
{
	fprintf (stderr,warning_string,s1,s2);
	fputc ('\n',stderr);
}

static void error (char *error_string)
{
	fprintf (stderr,"%s\n",error_string);
	exit (1);
}

static void error_s (char *error_string,char *s)
{
	fprintf (stderr,error_string,s);
	fputc ('\n',stderr);
	exit (1);
}

static void error_s_s (char *error_string,char *s1,char *s2)
{
	fprintf (stderr,error_string,s1,s2);
	fputc ('\n',stderr);
	exit (1);
}

static void *memory_allocate (int size)
{
	void *block;
	
	block=malloc (size);
	if (block==NULL)
		error ("Out of memory");
	return block;
}

#if defined (OS2) || defined (_WINDOWS_)
int dos_exec (char *file_name,char **args,int use_temp_file)
{
# ifdef OS2
	char command[8192];
# else
	char command[1024];
# endif

	if (!use_temp_file){
		int length;

		strcpy (command,file_name);
		strcat (command," ");

		++args;
		while (*args){
			strcat (command,*args);
			strcat (command," ");
			++args;
		}
	
		length=strlen (command);

		command[--length]='\0';

		return system (command);
	} else {
		char *temp_file_name;
		FILE *temp_file;	
		int r;

#if defined (_WINDOWS_) || defined (OS2)
		temp_file_name="param.$$$";
# ifdef OMF
		{
			char *p;
			
			for (p=temp_file_name; *p; ++p)
				if (*p=='/')
					*p='\\';
		}
# endif
		temp_file=fopen (temp_file_name,"w");
#else
		{
		static char param_tmp_file_name[]="/tmp/paramXXXXXX";
		int temp_file_fd;

		temp_file_fd=mkstemp (param_tmp_file_name);
		if (temp_file_fd<0)
			return 1;
	
		temp_file_name=param_tmp_file_name;	
		temp_file=fdopen (temp_file_fd,"w");
		}
# endif
		if (temp_file==NULL)
			return 1;

		++args;
		while (*args!=NULL){
#ifdef OMF
			if (*args!=NULL && **args==','){
				++args;
				fprintf (temp_file,"\n");
			} else {
				if (args[1]!=NULL && *args[1]=='/')
					fprintf (temp_file,"%s ",*args);
				else if (args[1]!=NULL && *args[1]!=',')
					fprintf (temp_file," %s +\n",*args);
				else
					fprintf (temp_file,"%s\n",*args);
				++args;
				if (*args!=NULL && **args==',')
					++args;
			}
#else
			fprintf (temp_file,"%s\n",*args);
			++args;
#endif
		}
			
	 	if (fclose (temp_file)!=0){
			return 1;
		}

		sprintf (command,"%s @%s",file_name,temp_file_name);
		r=system (command);

		unlink (temp_file_name);

		return r;
	}
}
#endif

static P_NODE add_project_node (char *module_name)
{
	P_NODE new_project_node;
	char *pro_fname;
	
	new_project_node=memory_allocate (sizeof (struct project_node));
	new_project_node->pro_next=NULL;
	
	pro_fname=memory_allocate (1+strlen (module_name));
	strcpy (pro_fname,module_name);

	new_project_node->pro_fname=pro_fname;
	new_project_node->pro_depend=NULL;
	new_project_node->pro_up_to_date=0;
	new_project_node->pro_valid_abc_time=0;
	new_project_node->pro_valid_dcl_time=0;
	new_project_node->pro_no_abc_file=0;
	new_project_node->pro_no_dcl_file=0;
	new_project_node->pro_ignore_o=0;
	new_project_node->pro_valid_options=0;
	new_project_node->pro_imported_object_files=NULL;
	
	if (first_project_node==NULL)
		first_project_node=new_project_node;
	else
		last_project_node->pro_next=new_project_node;
	last_project_node=new_project_node;
	
	return new_project_node;
}

static P_NODE add_dependency (DEP_LIST *dependency_list_p,char *dependent_module)
{
	DEP_LIST new_dependency,dependency;
	P_NODE project_node;

	for_l (dependency,*dependency_list_p,dep_next)
		if (!strcmp (dependent_module,dependency->dep_node->pro_fname))
			return NULL;
	
	new_dependency=memory_allocate (sizeof (struct dep_list));
	
	for_l (project_node,first_project_node,pro_next)
		if (!strcmp (dependent_module,project_node->pro_fname))
			break;

	if (project_node==NULL)
		project_node=add_project_node (dependent_module);
	
	new_dependency->dep_node=project_node;
	
	new_dependency->dep_next=*dependency_list_p;
	*dependency_list_p=new_dependency;
	
	return project_node;
}

static char *clean_path_list,clean_path_list_copy[PATH_LIST_STRING_SIZE];
static char *clean_lib_path,clean_lib_path_copy[PATH_NAME_STRING_SIZE];

#ifdef NB
	char *clean_abc_path;
	static char *clean_o_path;
#else
	static char *clean_abc_path,*clean_o_path;
#endif

#if defined (OS2) && !defined (OMF)
static char gcc_lib_path_copy[PATH_NAME_STRING_SIZE];
#endif

static int verbose=0;
static int silent=0;

#if MACINTOSH
#	define DIRECTORY_SEPARATOR_STRING ":"
#	define DIRECTORY_SEPARATOR_CHAR ':'
#	define PATH_SEPARATOR_CHAR ','
#else
# if defined (OS2) || defined (_WINDOWS_)
#	define DIRECTORY_SEPARATOR_STRING "\\"
#	define DIRECTORY_SEPARATOR_CHAR '\\'
#	define PATH_SEPARATOR_CHAR ';'
# else
#	define DIRECTORY_SEPARATOR_STRING "/"
#	define DIRECTORY_SEPARATOR_CHAR '/'
#	define PATH_SEPARATOR_CHAR ':'
# endif
#endif

#if MACINTOSH

#	undef TEST

#	ifdef TEST
		unsigned long f_get_f_info_time=0;
		int f_get_f_info_count=0;
#	endif

	static unsigned long get_file_time (char *fname,int wd_ref_num)
	{
		FileParam fpb;
		char fn[256];
		int err;
		
		strcpy (fn, fname);
	
		fpb.ioNamePtr=c2pstr (fn);
		fpb.ioFDirIndex=0;
		fpb.ioFVersNum=0;
		fpb.ioVRefNum=wd_ref_num;
	
#	ifdef TEST
		{
		unsigned long begin_time=TickCount();
#	endif
		
		err=PBGetFInfoSync ((ParmBlkPtr)&fpb);
	
#	ifdef TEST
		f_get_f_info_time+=TickCount()-begin_time;
		
		printf ("t=%d %d\n",f_get_f_info_time,++f_get_f_info_count);
		}
#	endif
	
		if (err)
			return 0;
		else
			return fpb.ioFlMdDat;
	}

	struct path_list {
		short				path_wd_ref_num;
		short				path_clean_system_files_wd_ref_num;
		struct path_list *	path_next;
#	ifdef __MWERKS__
		char				path_name[];
#	else
		char				path_name[0];
#	endif
	};

	static struct path_list *path_list;
	
	static unsigned char *copy_c_to_p_string (char *c_string,char *p_string)
	{
		char *s,*d,c;
		
		d=p_string+1;
		s=c_string;
		while (c=*s++, c!='\0')
			*d++=c;
		
		*p_string=s-1-c_string;
		
		return (unsigned char*) p_string;
	}

	static void add_directory_to_path_list (char *path_name)
	{
		short wd_ref_num,clean_system_files_wd_ref_num;
		struct path_list *new_path,**last_path_p;
		int path_name_length;
		char p_string [256];
		CInfoPBRec fpb;
		WDPBRec wd_pb;
		int err,root_path;
	
		root_path=0;
		
		if (path_name){
			char *p;
			
			for (p=path_name; *p!=DIRECTORY_SEPARATOR_CHAR && *p!='\0'; ++p)
				;
				
			if (*p=='\0'){
				root_path=1;
				p[0]=DIRECTORY_SEPARATOR_CHAR;
				p[1]='\0';
			}

			fpb.hFileInfo.ioNamePtr=copy_c_to_p_string (path_name,p_string);
		} else
			fpb.hFileInfo.ioNamePtr=(unsigned char*)"\001:";
		
		fpb.hFileInfo.ioVRefNum =0;
		fpb.hFileInfo.ioFDirIndex=0;
		fpb.hFileInfo.ioDirID=0;
	
		err = PBGetCatInfoSync (&fpb);
	
		if (err!=0){
			if (path_name)
				error_s ("folder '%s' does not exist",path_name);
			return;
		}
	
		wd_pb.ioNamePtr=fpb.hFileInfo.ioNamePtr;
		wd_pb.ioWDProcID='ClCo';

		wd_pb.ioVRefNum=0;
		wd_pb.ioWDDirID=0;
/*
		wd_pb.ioVRefNum=fpb.hFileInfo.ioVRefNum;
		wd_pb.ioWDDirID=fpb.hFileInfo.ioDirID;
*/

		err = PBOpenWD (&wd_pb,0);

		if (err!=0){
			if (path_name)
				error_s ("folder '%s' does not exist",path_name);
			return;
		}

		wd_ref_num=wd_pb.ioVRefNum;

# ifndef NO_CLEAN_SYSTEM_FILES_FOLDERS
		if (path_name)
			if (root_path)
				strcat (path_name,"Clean System Files");
			else
				strcat (path_name,":Clean System Files");
		else
			path_name="Clean System Files";
		
		fpb.hFileInfo.ioNamePtr=copy_c_to_p_string (path_name,p_string);
		fpb.hFileInfo.ioVRefNum =0;
		fpb.hFileInfo.ioFDirIndex=0;
		fpb.hFileInfo.ioDirID=0;
	
		err = PBGetCatInfoSync (&fpb);
	
		if (err!=0){
			if (path_name)
				error_s ("folder '%s' does not exist",path_name);
			return;
		}
	
		wd_pb.ioNamePtr=fpb.hFileInfo.ioNamePtr;
		wd_pb.ioWDProcID='ClCo';

		wd_pb.ioVRefNum=0;
		wd_pb.ioWDDirID=0;

		err = PBOpenWD (&wd_pb,0);
		
		if (err!=0){
			if (path_name)
				error_s ("folder '%s' does not exist",path_name);
			return;
		}

		clean_system_files_wd_ref_num=wd_pb.ioVRefNum;
	
		path_name_length=strlen (path_name)-strlen (":Clean System Files");
		if (path_name_length<0)
			path_name_length=0;
		path_name[path_name_length]='\0';
# else
		clean_system_files_wd_ref_num=0;
		
		if (path_name==NULL)
			path_name="";
		
		path_name_length=strlen (path_name);
# endif
	
		new_path=memory_allocate (sizeof (struct path_list)+1+path_name_length);
	
		last_path_p=&path_list;
		while (*last_path_p)
			last_path_p=&(*last_path_p)->path_next;
		
		new_path->path_wd_ref_num=wd_ref_num;
		new_path->path_clean_system_files_wd_ref_num=clean_system_files_wd_ref_num;
		strcpy (new_path->path_name,path_name);
		new_path->path_next=NULL;
	
		*last_path_p=new_path;
	}

#if defined (MAKE_MPW_TOOL) && defined (NB)
	static char *main_module_path;
#endif
	
	static void initialise_path_list (void)
	{
		char path[PATH_NAME_STRING_SIZE];
		char *s,*path_elem;
		int c;
		
		path_list=NULL;

		if (clean_abc_path)
			add_directory_to_path_list (clean_abc_path);

# if defined (MAKE_MPW_TOOL) && defined (NB)
		add_directory_to_path_list (main_module_path);
# else
		add_directory_to_path_list (NULL);
# endif				
		s = clean_path_list;
		
		if (s!=NULL){
			path_elem = s;
		
			for (c=*s; ; c=*s){
				if (c==PATH_SEPARATOR_CHAR || c == '\0'){
					char *s_p,*d_p;
					
					d_p=path;
					s_p=path_elem;
					while (s_p!=s)
						*d_p++ = *s_p++;
					*d_p='\0';
		
					add_directory_to_path_list (path);
								
					if (c == '\0')
						break;
					
					path_elem = ++s;
				} else
					++s;
			}
		}
	}
	
	static int last_path_clean_system_files_wd_ref_num_defined;
	static short last_path_clean_system_files_wd_ref_num;

	static unsigned long find_file (char *file_name,char *extension,char *complete_file_name)
	{
		unsigned long file_time;
	
		if (file_name[0]!=DIRECTORY_SEPARATOR_CHAR){
			struct path_list *path_elem;
# if USE_PATH_CACHE
			int dcl_file;
# endif
# ifdef NB
			int abc_or_obj_file;
# endif	
			strcpy (complete_file_name,file_name);
			strcat (complete_file_name,extension);
	
#	if USE_PATH_CACHE
			dcl_file=	extension[0]=='.' && extension[1]=='d' && extension[2]=='c' && 
						extension[3]=='l' && extension[4]=='\0';
	
			if (dcl_file){
				struct search_dcl_path_in_cache_result r;
				
				if (search_dcl_path_in_cache (file_name,&r)){
					short wd_ref_num;
				
					wd_ref_num=r.wd_ref_num;
					file_time=r.file_time;
	
					last_path_clean_system_files_wd_ref_num_defined=1;
					last_path_clean_system_files_wd_ref_num=r.clean_system_files_wd_ref_num;
	
					strcpy (complete_file_name,r.path);
					if (complete_file_name[0]!='\0' && complete_file_name[strlen (complete_file_name)-1]!=DIRECTORY_SEPARATOR_CHAR)
						strcat (complete_file_name,DIRECTORY_SEPARATOR_STRING);
					strcat (complete_file_name,file_name);
					strcat (complete_file_name,extension);
								
					return file_time;
				}
			}
#	endif

# ifdef NB
			abc_or_obj_file
				= extension[0]=='.' && (
					(extension[1]=='a' && extension[2]=='b' && extension[3]=='c' && extension[4]=='\0') ||
					(extension[1]=='o' && extension[2]=='b' && extension[3]=='j')
				  ) ;

			for (path_elem=(clean_abc_path!=NULL && !abc_or_obj_file && path_list!=NULL) ? path_list->path_next : path_list;
				 path_elem!=NULL;
				 path_elem=(clean_abc_path!=NULL && abc_or_obj_file) ? NULL :path_elem->path_next)
# else
			for (path_elem:q
=path_list; path_elem!=NULL;path_elem=path_elem->path_next)
# endif
			{
				short wd_ref_num;
				
				wd_ref_num=path_elem->path_wd_ref_num;
	
				file_time=get_file_time (complete_file_name,wd_ref_num);
				
				if (file_time){
					last_path_clean_system_files_wd_ref_num_defined=1;
					last_path_clean_system_files_wd_ref_num=path_elem->path_clean_system_files_wd_ref_num;
				
					strcpy (complete_file_name,path_elem->path_name);
					if (complete_file_name[0]!='\0' && complete_file_name[strlen (complete_file_name)-1]!=DIRECTORY_SEPARATOR_CHAR)
						strcat (complete_file_name,DIRECTORY_SEPARATOR_STRING);
					strcat (complete_file_name,file_name);
					strcat (complete_file_name,extension);
	
# if USE_PATH_CACHE
					if (dcl_file)
						cache_dcl_path
							(file_name,wd_ref_num,last_path_clean_system_files_wd_ref_num,file_time,path_elem->path_name);
# endif
								
					return file_time;
				}
			}

# ifdef NB
			return 0;
# endif
		}

		strcpy (complete_file_name,file_name);
		strcat (complete_file_name,extension);

		return get_file_time (complete_file_name,0);
	}

	static unsigned long find_clean_system_file (char *file_name,char *extension,char *complete_file_name)
	{
#ifdef NO_CLEAN_SYSTEM_FILES_FOLDERS
		return find_file (file_name,extension,complete_file_name);
#else
		unsigned long file_time;
		last_path_clean_system_files_wd_ref_num_defined=0;
		
		file_time=find_file (file_name,".dcl",complete_file_name);
		if (!file_time)
			file_time=find_file (file_name,".icl",complete_file_name);
		
		if (file_time){
			char *p,*last_colon;
			
			last_colon=complete_file_name;
			p=complete_file_name;
			while (*p){
				if (*p==DIRECTORY_SEPARATOR_CHAR)
					last_colon=p+1;
				++p;
			}
			
# ifndef NO_CLEAN_SYSTEM_FILES_FOLDERS
			strcpy (last_colon,"Clean System Files" DIRECTORY_SEPARATOR_STRING);
# else
			*last_colon='\0';
# endif
			strcat (last_colon,file_name);
			strcat (last_colon,extension);
			
			if (last_path_clean_system_files_wd_ref_num_defined){
				char file_name_with_extension[PATH_NAME_STRING_SIZE];
				
				strcpy (file_name_with_extension,file_name);
				strcat (file_name_with_extension,extension);
				
				file_time=get_file_time (file_name_with_extension,last_path_clean_system_files_wd_ref_num);
			} else
				file_time=get_file_time (complete_file_name,0);
		}
		
		return file_time;
#endif
	}

#else
# ifdef _WINDOWS_
	static int file_exists (char *file_name,FileTime *time_p)
	{
		HANDLE h;
#  ifdef GNU_C
		WIN32_FIND_DATAA find_data;

		h=FindFirstFileA (file_name,&find_data);
#  else
		WIN32_FIND_DATA find_data;

		h=FindFirstFile (file_name,&find_data);
#  endif
	
		if (h!=INVALID_HANDLE_VALUE){
			FindClose (h);
			*time_p=find_data.ftLastWriteTime;
			return 1;
		} else
			return 0;
	}
# endif

# ifdef OS2
	static int file_exists (char *file_name)
	{
		FILESTATUS3 fstatus;
		APIRET rc;
		
		rc = DosQueryPathInfo (file_name,FIL_STANDARD,&fstatus,sizeof (FILESTATUS3));
	
		return rc==NO_ERROR;
	}
# endif

	int find_file (char *file_name,char *extension,char *complete_file_name,char *first_path
#	ifdef _WINDOWS_
		,FileTime *file_time_p
#	endif
		)
	{
		if (file_name[0]!=DIRECTORY_SEPARATOR_CHAR){
			if (first_path!=NULL){
				strcpy (complete_file_name,first_path);
				strcat (complete_file_name,DIRECTORY_SEPARATOR_STRING);
				strcat (complete_file_name,file_name);
				strcat (complete_file_name,extension);
					
#	ifdef _WINDOWS_
				if (file_exists (complete_file_name,file_time_p))
#	else
#	 ifdef OS2
				if (file_exists (complete_file_name))
#	 else
				if (access (complete_file_name,F_OK)==0)
#	 endif
#	endif
					return 1;
			}
			
			if (clean_path_list!=NULL){
				char *s;
				int c;
				
				s=clean_path_list;
				c=*s++;
				while (c!='\0'){
					char *p;
					
					p=complete_file_name;
	
					while (c!=PATH_SEPARATOR_CHAR && c!='\0'){
						*p++=c;
						c=*s++;
					}
	
					*p++=DIRECTORY_SEPARATOR_CHAR;
	
					strcpy (p,file_name);
					strcat (p,extension);
					
#	ifdef _WINDOWS_
					if (file_exists (complete_file_name,file_time_p))
#	else
#	 ifdef OS2
					if (file_exists (complete_file_name))
#	 else
					if (access (complete_file_name,F_OK)==0)
#	 endif
#	endif
						return 1;
	
					if (c==PATH_SEPARATOR_CHAR)
						c=*s++;
				}
			}
#	if 0
			strcpy (complete_file_name,clean_lib_path);
			strcat (complete_file_name,DIRECTORY_SEPARATOR_STRING "stdenv" DIRECTORY_SEPARATOR_STRING);
			strcat (complete_file_name,file_name);
			strcat (complete_file_name,extension);

#	ifdef _WINDOWS_
			if (file_exists (complete_file_name,file_time_p))
#	else
#	 ifdef OS2
			if (file_exists (complete_file_name))
#	 else
			if (access (complete_file_name,F_OK)==0)
#	 endif
#	endif
				return 1;
#	endif

#ifdef OS2
			if (window_application){
				int length;
				char *lib_p;

				strcpy (complete_file_name,clean_lib_path);
				
				length=strlen (complete_file_name);
				lib_p=&complete_file_name[length-3];
				if (length>3 && (lib_p[0] & 0xdf)=='L' && (lib_p[1] & 0xdf)=='I' && (lib_p[2] & 0xdf)=='B'){
					strcpy (lib_p,"CleanIO\\");
					strcat (lib_p,file_name);
					strcat (lib_p,extension);
		
					if (file_exists (complete_file_name))
						return 1;
	
					strcpy (lib_p,"OsIO\\");
					strcat (lib_p,file_name);
					strcat (lib_p,extension);
		
					if (file_exists (complete_file_name))
						return 1;
				}
			}
#endif
		}
			
		strcpy (complete_file_name,file_name);
		strcat (complete_file_name,extension);
	
#	ifdef _WINDOWS_
		return file_exists (complete_file_name,file_time_p);
#	else
#	 ifdef OS2
		return file_exists (complete_file_name);
#	 else
		return access (complete_file_name,F_OK)==0;
#	 endif
#	endif
	 }

	static void replace_file_name_in_path (char *path,char *file_name,char *extension)
	{
		char *p,*last_colon;

		last_colon=path;
		p=path;
		while (*p){
			if (*p==DIRECTORY_SEPARATOR_CHAR)
				last_colon=p+1;
			++p;
		}
		strcpy (last_colon,file_name);
		strcat (last_colon,extension);
	}

	static void add_clean_system_files_and_replace_file_name_in_path (char *path,char *file_name,char *extension)
	{
		char *p,*last_colon;

		last_colon=path;
		p=path;
		while (*p){
			if (*p==DIRECTORY_SEPARATOR_CHAR)
				last_colon=p+1;
			++p;
		}
# ifndef NO_CLEAN_SYSTEM_FILES_FOLDERS
		strcpy (last_colon,"Clean System Files" DIRECTORY_SEPARATOR_STRING);
# else
		*last_colon='\0';
# endif
		strcat (last_colon,file_name);
		strcat (last_colon,extension);
	}

	int find_clean_system_file (char *file_name,char *extension,char *complete_file_name,char *first_path
#	ifdef _WINDOWS_
		,FileTime *file_time_p
#	endif
		)
	{
		int found;

		found=find_file (file_name,".dcl",complete_file_name,first_path
#	ifdef _WINDOWS_
					,file_time_p
#	endif
			);
		if (!found)
			found=find_file (file_name,".icl",complete_file_name,first_path
#	ifdef _WINDOWS_
					,file_time_p
#	endif
				);
		
		if (found){
			add_clean_system_files_and_replace_file_name_in_path (complete_file_name,file_name,extension);

#	ifdef _WINDOWS_
			return file_exists (complete_file_name,file_time_p);
#	else
#	 ifdef OS2
			return file_exists (complete_file_name);
#	 else
			return access (complete_file_name,F_OK)==0;
#	 endif
#	endif
		}
		
		return found;
	}
#endif

#ifdef OS2
static char *clm_file_name;
#endif

static void need_file(char *file_name,char *extension,char *complete_file_name)
{
	if (!find_clean_system_file (file_name, extension,complete_file_name,NULL)){
		fprintf (stderr,"Couldn't find %s%s\n", file_name, extension);
		exit (1);
	}
}

static void get_paths (void)
{
#if MACINTOSH
	clean_path_list = NULL;
	clean_lib_path = NULL;
#else
	clean_path_list=getenv ("CLEANPATH");		
	if (clean_path_list!=NULL)
		clean_path_list=strcpy (clean_path_list_copy,clean_path_list);
	else
		clean_path_list=clean_directory_list;

# ifdef OS2
	{
		int l;
		
		clean_lib_path=clean_lib_path_copy;

		l=strlen (clm_file_name);
		if (l<PATH_NAME_STRING_SIZE-1){
			int n;
			
			strcpy (clean_lib_path_copy,clm_file_name);
			
			for (n=l-1; n>=0; --n)
				if (clean_lib_path_copy[n]=='\\'){
					clean_lib_path_copy[n]='\0';
					l=n;
					break;
				}

			if (clean_lib_path_copy[l-4]=='\\'
				&& (clean_lib_path_copy[l-3] & 0xdf)=='B'
				&& (clean_lib_path_copy[l-2] & 0xdf)=='I'
				&& (clean_lib_path_copy[l-1] & 0xdf)=='N')
			{
				clean_lib_path_copy[l-3]+='l'-'b';
				clean_lib_path_copy[l-2]+='i'-'i';
				clean_lib_path_copy[l-1]+='b'-'n';
			}
		} else {
			clean_lib_path_copy[0]='.';
			clean_lib_path_copy[1]='\0';
		}
	}	
# else
	clean_lib_path=getenv ("CLEANLIB");
	if (clean_lib_path!=NULL)
		clean_lib_path=strcpy (clean_lib_path_copy,clean_lib_path);
	else
		clean_lib_path=clean_lib_directory;
# endif
#endif

#if MACINTOSH
	clean_abc_path=NULL;
	clean_o_path=NULL;
#else
	clean_abc_path=getenv ("CLEANABCPATH");
	clean_o_path=getenv ("CLEANOPATH");
#endif

#if defined (OS2) && !defined (OMF)
	{
		char *gcc_lib_path;
	
		gcc_lib_path=getenv ("GCCLIB");
		if (gcc_lib_path!=NULL)
			gcc_lib_path=strcpy (gcc_lib_path_copy,gcc_lib_path);
		else
			gcc_lib_path=gcc_lib_directory;
		
		gcc_lib_directory_arg[0]='-';
		gcc_lib_directory_arg[1]='L';
		strcpy (&gcc_lib_directory_arg[2], gcc_lib_path);
		sprintf (crt0_file_name,"%s\\crt0.o",&gcc_lib_directory_arg[2]);
	}
#endif
}

static int get_abc_time (P_NODE project_node,FileTime *time_p)
{
	char file_name[PATH_NAME_STRING_SIZE];
	FileTime time;
	
	if (project_node->pro_valid_abc_time)
		time=project_node->pro_abc_time;
	else {
		if (project_node->pro_no_abc_file)
			return 0;

#if MACINTOSH
		time=find_clean_system_file (project_node->pro_fname,".abc",file_name);

		if (time==0){
			project_node->pro_no_abc_file=1;
			return 0;
		}
#else
# ifdef _WINDOWS_
		if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path,&time))
			return 0;
# else
		{
		struct stat stat_buffer;
		
		if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path))
			return 0;
	
		if (stat (file_name,&stat_buffer)<0){
			project_node->pro_no_abc_file=1;
			return 0;
		}
		time=stat_buffer.st_mtime;
		}
# endif
#endif		
		project_node->pro_abc_time=time;
		project_node->pro_valid_abc_time=1;
	}
	
	*time_p=time;
	return 1;
}

static int get_dcl_time (P_NODE project_node,FileTime *time_p)
{
	char file_name[PATH_NAME_STRING_SIZE];
	FileTime time;
	
	if (project_node->pro_valid_dcl_time)
		time=project_node->pro_dcl_time;
	else {
		if (project_node->pro_no_dcl_file)
			return 0;
#if MACINTOSH
		time=find_file (project_node->pro_fname,".dcl",file_name);

		if (time==0){
			project_node->pro_no_dcl_file=1;
			return 0;
		}
#else
# ifdef _WINDOWS_
		if (!find_file (project_node->pro_fname,".dcl",file_name,NULL,&time))
			return 0;
# else
		{	
		struct stat stat_buffer;

		if (!find_file (project_node->pro_fname,".dcl",file_name,NULL))
			return 0;
	
		if (stat (file_name,&stat_buffer)<0){
			project_node->pro_no_dcl_file=1;
			return 0;
		}
	
		time=stat_buffer.st_mtime;
		}
# endif
#endif
		project_node->pro_dcl_time=time;
		project_node->pro_valid_dcl_time=1;
	}
	
	*time_p=time;
	return 1;
}

#if MACINTOSH
	static int get_time (P_NODE project_node,char *extension,FileTime *time_p)
	{
		char file_name[PATH_NAME_STRING_SIZE];
		unsigned long time;
	
		time=find_file (project_node->pro_fname,extension,file_name);
	
		if (time==0)
			return 0;
		
		*time_p=time;
	
		return 1;
	}

	static int get_clean_system_time (P_NODE project_node,char *extension,FileTime *time_p)
	{
		char file_name[PATH_NAME_STRING_SIZE];
		unsigned long time;
	
		time=find_clean_system_file (project_node->pro_fname,extension,file_name);
	
		if (time==0)
			return 0;
		
		*time_p=time;
	
		return 1;
	}

#else
	static int get_time (P_NODE project_node,char *extension,FileTime *time_p,char *first_path)
	{
		char file_name[PATH_NAME_STRING_SIZE];
# ifdef _WINDOWS_
		return find_file (project_node->pro_fname,extension,file_name,first_path,time_p);
# else
		struct stat stat_buffer;
		
		if (!find_file (project_node->pro_fname,extension,file_name,first_path))
			return 0;
		
		if (stat (file_name,&stat_buffer)<0)
			return 0;
	
		*time_p=stat_buffer.st_mtime;
	
		return 1;
# endif
	}

	static int get_clean_system_time (P_NODE project_node,char *extension,FileTime *time_p,char *first_path)
	{
		char file_name[PATH_NAME_STRING_SIZE];
# ifdef _WINDOWS_
		return find_clean_system_file (project_node->pro_fname,extension,file_name,first_path,time_p);
# else
		struct stat stat_buffer;
		
		if (!find_clean_system_file (project_node->pro_fname,extension,file_name,first_path))
			return 0;
		
		if (stat (file_name,&stat_buffer)<0)
			return 0;
	
		*time_p=stat_buffer.st_mtime;
	
		return 1;
# endif
	}
#endif

FILE *abc_file;
int last_char;

static int open_abc_file2 (P_NODE project_node,char file_name[])
{
	FILE *f;
	
#if MACINTOSH
	if (!find_clean_system_file (project_node->pro_fname,".abc",file_name))
		return 0;
#else
# ifdef _WINDOWS_
	{
	FileTime time;
	
	if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path,&time))
		return 0;
	}
# else
	if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path))
		return 0;
# endif
#endif
	
	f=fopen (file_name,"r");
	if (f==NULL)
		return 0;
	
	setvbuf (f,NULL,_IOFBF,2048);
	
	last_char=getc (f);
	abc_file=f;
	return 1;
}

static int open_abc_file (P_NODE project_node)
{
	char file_name [PATH_NAME_STRING_SIZE];

	return open_abc_file2 (project_node,file_name);
}

static void skip_spaces (void)
{
	while (last_char==' ' || last_char=='\t')
		last_char=getc (abc_file);
}

static void skip_to_next_line (void)
{
	while (last_char!='\n' && last_char!=EOF)
		last_char=getc (abc_file);
	if (last_char=='\n')
		last_char=getc (abc_file);
}

static int last_char_was (int c)
{
	if (last_char==c){
		last_char=getc (abc_file);
		return 1;
	} else
		return 0;
}

static int last_char_was_digit (void)
{
	if (last_char<='9' && last_char>='0'){
		last_char=getc (abc_file);
		return 1;
	} else
		return 0;
}

static int get_version_and_options_of_abc_file (P_NODE project_node,int *version_p,long *comp_options_position_p)
{
	int version,options;
	
	*comp_options_position_p = -1;

	while (last_char!=EOF){
		skip_spaces();
		if (last_char!='.')
			return 0;
		last_char=getc (abc_file);
		if (last_char_was ('c')){
			if (last_char_was ('o') && last_char_was ('m') && last_char_was ('p')){
				int digit_1,digit_2,digit_3;
				
				skip_spaces();
				
				if ((digit_1=last_char,last_char_was_digit())
					&& (digit_2=last_char,last_char_was_digit())
					&& (digit_3=last_char,last_char_was_digit()))
				{
					int digit_n;
					
					version=(digit_1-'0')*100 + (digit_2-'0')*10 + (digit_3-'0');
					
					skip_spaces();
					
					options=0;
					if (last_char!='\n'){
						long comp_options_position;

						comp_options_position=ftell (abc_file)-1;

						for (digit_n=0; digit_n<9; ++digit_n){
							if (last_char=='0')
								;
							else if (last_char=='1')
								options |= (1<<digit_n);
							else
								return 0;
							last_char=getc (abc_file);
						}

						*comp_options_position_p=comp_options_position;
					}
					skip_to_next_line();
					
					project_node->pro_options=options;
					project_node->pro_valid_options=1;
					
					*version_p=version;
					return 1;
				}
			}
		} else if (last_char_was ('e') && last_char_was ('n') && last_char_was ('d')
			&& last_char_was ('i') && last_char_was ('n') && last_char_was ('f')
			&& last_char_was ('o'))
		{
			return 0;
		}
		skip_to_next_line();
	}
	return 0;
}

static int get_dependency_or_imported_object_file_of_abc_file (char *file_name,int max_length)
{
	int depend_or_impobj;

	depend_or_impobj=1;
	while (last_char!=EOF){
		skip_spaces();
		if (last_char!='.')
			return 0;
		last_char=getc (abc_file);
		if (last_char_was ('d')){
			if (last_char_was ('e') && last_char_was ('p') && last_char_was ('e')
				&& last_char_was ('n') && last_char_was ('d'))
			{
				depend_or_impobj=1;
				break;
			}
		} else if (last_char_was ('i')){
			if (last_char_was ('m') && last_char_was ('p') && last_char_was ('o')
				&& last_char_was ('b') && last_char_was ('j'))
			{
				depend_or_impobj=2;
				break;
			}
		} else if (last_char_was ('e') && last_char_was ('n') && last_char_was ('d')
			&& last_char_was ('i') && last_char_was ('n') && last_char_was ('f')
			&& last_char_was ('o'))
		{
			return 0;
		} else
			skip_to_next_line();
	}

   	if (depend_or_impobj==0)
   		return 0;

	skip_spaces();
				
	if (last_char=='\"'){
		int n;

		last_char=getc (abc_file);
		n=0;
		while (last_char!='\"' && last_char!='\n' && last_char!=EOF
		   && n<max_length)
		{
			*file_name++=last_char;
			last_char=getc (abc_file);
			++n;
		}
		if (n<max_length && last_char=='\"'){
			*file_name='\0';
			skip_to_next_line();
			return depend_or_impobj;
		}
	}

	return 0;
}

static void close_abc_file (void)
{
	fclose (abc_file);
}

static void add_imported_object_file_to_project_node (char *object_file_name,P_NODE project_node)
{
	int object_file_name_size;
	struct object_file_list *new_imported_object_file,**object_file_h;

	object_file_name_size=strlen (object_file_name);
	new_imported_object_file=memory_allocate (sizeof (struct object_file_list)+object_file_name_size+1);

	strcpy (new_imported_object_file->object_file_name,object_file_name);
	new_imported_object_file->object_file_next=NULL;

	object_file_h=&project_node->pro_imported_object_files;
	while (*object_file_h!=NULL)
	object_file_h=&(*object_file_h)->object_file_next;
	*object_file_h=new_imported_object_file;
}

static int single_module=0;
static int syntax_check=0;
static int only_abc_files=0;
static int only_s_files=0;
static int only_o_files=0;

static int project_node_is_abc_up_to_date (P_NODE project_node)
{
	FileTime abc_time,dcl_time,icl_time;
	int version,abc_options;
	DEP_LIST dependency_list;
	long comp_options_position;
	char abc_file_name [PATH_NAME_STRING_SIZE];
	char dependent_module [PATH_NAME_STRING_SIZE];

	if (!get_abc_time (project_node,&abc_time)){
		if (verbose)
			warning_s ("(%s.abc doesn't exist)",project_node->pro_fname);
		return 0;
	}
	
	if (!open_abc_file2 (project_node,abc_file_name))
		return 0;
	
	if (!get_version_and_options_of_abc_file (project_node,&version,&comp_options_position)){
		close_abc_file();
		return 0;
	}
	abc_options=project_node->pro_options;
	
	if (version!=VERSION){
		close_abc_file();
		warning_s ("Warning: %s.abc is generated with another compiler version",
				   project_node->pro_fname);
		return 0;
	}
	
	if (!(abc_options & SYSTEM_MASK)){
		if ((abc_options & PARALLEL_MASK) || !(abc_options & STACK_LAYOUT_MASK)){
			if (verbose)
				warning_s ("(%s.abc is not sequential abc code with stack information)",
						   project_node->pro_fname);
			close_abc_file();
			return 0;
		}

		if ((NO_TIME_PROFILE_MASK & (clean_options ^ abc_options))!=0){
			if (verbose)
 				warning_s ("(%s.icl is compiled with different time profile options)",project_node->pro_fname);
			close_abc_file();
			return 0;
		}

#if MACINTOSH
		if (get_time (project_node,".icl",&icl_time) && abc_time<=icl_time){
#else
		if (get_time (project_node,".icl",&icl_time,NULL) && FILE_TIME_LE (abc_time,icl_time)){
#endif
			if (verbose)
				warning_s ("(%s.abc is older than corresponding icl file)",
						   project_node->pro_fname);
			close_abc_file();
			return 0;
		}
		
		if (get_dcl_time (project_node,&dcl_time) && FILE_TIME_LE (abc_time,dcl_time)){
			if (verbose)
				warning_s ("(%s.abc is older than corresponding dcl file)",
						   project_node->pro_fname);
			return 0;
		}
	} else {
		if ((NO_TIME_PROFILE_MASK & (clean_options ^ abc_options))!=0){
			if (verbose)
 				warning_s ("(%s.icl is compiled with different time profile options)",project_node->pro_fname);
			close_abc_file();

			if (comp_options_position>=0){
				FILE *f;

				f=fopen (abc_file_name,"r+");
				if (f==NULL)
					error_s ("Could not write to file %s\n",abc_file_name);
		
				if (fseek (f,comp_options_position+5,SEEK_SET)!=0)
					error_s ("Could not write to file %s\n",abc_file_name);

				if ((clean_options & NO_TIME_PROFILE_MASK)==0)
					fputc ('0',f);
				else
					fputc ('1',f);
	
				if (fclose (f)!=0)
					error_s ("Could not write to file %s\n",abc_file_name);
			
				project_node->pro_options &= ~NO_TIME_PROFILE_MASK;
				project_node->pro_options |= clean_options & NO_TIME_PROFILE_MASK;

				return 1;
			}
	
			return 0;
		}
	}
	
	if (project_node==main_project_node &&
		((DEBUG_MASK | STRICTNESS_ANALYSIS_MASK) & clean_options_mask & (clean_options ^ abc_options))!=0)
	{
		if (verbose)
			warning_s ("(%s.icl is compiled with different options)", project_node->pro_fname);

		close_abc_file();
		return 0;
	}	

	{
	int depend_or_impobj;

	dependency_list=NULL;
	while (
		depend_or_impobj=get_dependency_or_imported_object_file_of_abc_file (dependent_module,PATH_NAME_STRING_SIZE-1),
		depend_or_impobj!=0
	){
		if (depend_or_impobj==1){
			FileTime dcl_time;
			P_NODE dependency_node;
		
			dependency_node=add_dependency (&dependency_list,dependent_module);
		
			if (dependency_node==NULL)
				continue;
		
			if (!(abc_options & SYSTEM_MASK)){
				if (get_dcl_time (dependency_node,&dcl_time) && FILE_TIME_LE(abc_time,dcl_time)){
					if (verbose)
						warning_s_s ("(%s.icl conflicts with %s.dcl)",project_node->pro_fname,
									 dependent_module);
					close_abc_file();
					return 0;
				}
			
				/*
				if (is_system_file (dependency_node)){
					long dcl_abc_time;
				
					if (get_abc_time (dependency_node,&dcl_abc_time) && abc_time<=dcl_abc_time){
						if (verbose)
							warning_s_s ("(%s.icl conflicts with %s.abc)",
										 project_node->pro_fname,dependency_node->pro_fname);
						close_abc_file();
						return 0;
					}
				}
				*/
			}
		} else if (depend_or_impobj==2)
			add_imported_object_file_to_project_node (dependent_module,project_node);
	}

	}

	close_abc_file();
	
	project_node->pro_depend=dependency_list;
	
	if (verbose)
		warning_s ("(%s.abc is up to date)",project_node->pro_fname);
	return 1;
}

static int is_system_file (P_NODE project_node)
{
	if (project_node->pro_no_abc_file)
		return 0;
	
	if (!project_node->pro_valid_options){
		int version;
		long comp_options_position;
		FILE *old_file;
		
		old_file=abc_file;
		
		if (!open_abc_file (project_node)){
			abc_file=old_file;
			return 0;
		}
	
		if (!get_version_and_options_of_abc_file (project_node,&version,&comp_options_position)){
			close_abc_file();
			abc_file=old_file;
			return 0;
		}
		close_abc_file();
		abc_file=old_file;
	}
	
	return (project_node->pro_options & SYSTEM_MASK)!=0;
}

#if defined(_WINDOWS_) && defined (GNU_C)
extern int wait (int*);
#endif

#include <errno.h>

static int wait_for_child (pid_t pid, char *child_name, int *status_p)
{
    int result;

   	result=waitpid (pid, status_p, 0);

    if (*status_p & 255)
        fprintf (stderr,"%s exited abnormally\n",child_name);

    return result;
}

#if MACINTOSH
extern int CallCompiler (int argc,char **argv);
#endif

#ifdef OS2
static char *arg_v_to_arguments (char **argv)
{
	int arguments_length;
	char **argv_p,*arguments,*arg_p;
	
	arguments_length=2;
	for (argv_p=argv; *argv_p!=NULL; ++argv_p)
		arguments_length += strlen (*argv_p)+1;
	
	arguments=memory_allocate (arguments_length);
	
	if (arguments!=NULL){
		arg_p=arguments;
		argv_p=argv;
		
		strcpy (arg_p,*argv_p);
		arg_p += strlen (arg_p)+1;

		if (*++argv_p!=NULL){
			strcpy (arg_p,*argv_p);
			arg_p += strlen (arg_p);

			while (*++argv_p!=NULL){
				*arg_p++ =' ';
				strcpy (arg_p,*argv_p);
				arg_p += strlen (arg_p);
			}
		}
		*arg_p++ = '\0';
		*arg_p++ = '\0';
	}
	
	return arguments;
}
#endif

#define CACHING_COMPILER

#ifdef CACHING_COMPILER

#include "Clean.h"
#include "cachingcompiler.h"

static int compiler_started=0;

static void start_compiler (void)
{
	int r;
	char cocl_file_name[PATH_NAME_STRING_SIZE];

	strcpy (cocl_file_name,clean_lib_path); 
	strcat (cocl_file_name,"/cocl");

	r=start_caching_compiler ((unsigned char *)cocl_file_name);
	if (r < 0)
		exit(r);	
}

static void stop_compiler (void)
{
	int r;

	r=stop_caching_compiler();
	if (r < 0)
		exit(r);
}

static char *concatenate_args (char **argv)
{
	int size;
	char **argv2, *p, *args;

	if (*argv == NULL)
		error ("concatenate_args: no args\n");

	size=0;
	for (argv2=argv; *argv2!=NULL; argv2++){
		char *arg;

		size += 1; /* '"' */
		for (arg=*argv2; *arg!='\0'; arg++)
			if (*arg=='"')
				size += 2; /* '\' and '"' */
			else
				size += 1; /* c */
		size += 2; /* '"' and (' ' or '\0') */
	}

	args=malloc(size);
	if (args==NULL)
		return NULL;

	p=args;
	for (argv2=argv; *argv2!=NULL; argv2++){
		char *arg;

		*p++='"';
		for (arg=*argv2; *arg!='\0'; arg++)
			if (*arg=='"')
			{
				*p++='\\';
				*p++='"';
			}
			else
				*p++=*arg;
		*p++='"';
		*p++=' ';
	}
	*(p-1)='\0';

	if (p-args!=size)
		error ("concatenate_args: fatal programming error\n");

	return args;
}	
#endif

static int call_compiler (char *cocl_file_name, char **argv)
{
#ifdef CACHING_COMPILER
	int r;
	char *args;

	if (!compiler_started){
		start_compiler();
		compiler_started=1;
	}

	args=concatenate_args(argv);
	if (args != NULL)
	{
		r=call_caching_compiler ((unsigned char *)args);
		free (args);
	}
	else
		r=-1;

	return r>=0;
#else /* ifndef CACHING_COMPILER */
	int pid,r,status;

	pid=fork();
	if (pid<0)
		error ("Fork failed");
	
	if (!pid){
		strcat (cocl_file_name,"/cocl");
		execv (cocl_file_name,argv);
		
		error ("Can't execute the clean compiler");
	}
	
	r=wait_for_child (pid, "Clean compiler",&status);
	
	return r>=0 && status==0;
#endif
}

static int compile_project_node (P_NODE project_node)
{
	char file_name[PATH_NAME_STRING_SIZE];
	char *abc_file_name,abc_file_name_s[PATH_NAME_STRING_SIZE];
	char cocl_file_name[PATH_NAME_STRING_SIZE];
	char *argv[16],**arg;
	int options;

	if (!silent)
		printf ("Compiling %s\n",project_node->pro_fname);
	
	project_node->pro_no_abc_file=0;
	project_node->pro_valid_abc_time=0;

#if MACINTOSH
	if (!find_file (project_node->pro_fname,".icl",file_name))
		error_s ("Can't find %s.icl",project_node->pro_fname);
#else
# ifdef _WINDOWS_
	{
	FileTime time;
	
	if (!find_file (project_node->pro_fname,".icl",file_name,NULL,&time))
		error_s ("Can't find %s.icl",project_node->pro_fname);
	}
# else
	if (!find_file (project_node->pro_fname,".icl",file_name,NULL))
		error_s ("Can't find %s.icl",project_node->pro_fname);
# endif
#endif

	if (clean_abc_path==NULL){
#ifdef NO_CLEAN_SYSTEM_FILES
		int length;
#endif

		abc_file_name=abc_file_name_s;
		strcpy (abc_file_name_s,file_name);
#ifdef NO_CLEAN_SYSTEM_FILES
		length=strlen (abc_file_name_s);
		abc_file_name_s[length-3]='a';
		abc_file_name_s[length-2]='b';
		abc_file_name_s[length-1]='c';
#else
		{
			char *p,*last_colon;
			
			last_colon=abc_file_name_s;
			p=abc_file_name_s;
			while (*p){
				if (*p==DIRECTORY_SEPARATOR_CHAR)
					last_colon=p+1;
				++p;
			}
			strcpy (last_colon,"Clean System Files");
		
			if (access (abc_file_name,F_OK)!=0){
				if (mkdir (abc_file_name,0777)!=0)
					error_s ("Could not create directory %s\n",abc_file_name);
			}

			strcat (last_colon,DIRECTORY_SEPARATOR_STRING);
			strcat (last_colon,project_node->pro_fname);
			strcat (last_colon,".abc");
		}
#endif
	} else {
		abc_file_name=abc_file_name_s;
		strcpy (abc_file_name,clean_abc_path);
#if MACINTOSH
		if (clean_abc_path[0]!='\0' && clean_abc_path[strlen (clean_abc_path)-1]!=DIRECTORY_SEPARATOR_CHAR)
#endif
		strcat (abc_file_name,DIRECTORY_SEPARATOR_STRING);
		strcat (abc_file_name,project_node->pro_fname);
		strcat (abc_file_name,".abc");
	}

	arg=argv;
	*arg++="cocl";

    if (cocl_redirect_stdout!=NULL){
        *arg++ = cocl_redirect_stdout_option;
        *arg++ = cocl_redirect_stdout;
    }

    if (cocl_redirect_stderr!=NULL){
        *arg++ = cocl_redirect_stderr_option;
        *arg++ = cocl_redirect_stderr;
    }

	*arg++="-sl";

#ifndef NO_CLEAN_SYSTEM_FILES
	*arg++="-csf";
#endif
	
	*arg++="-P";
	
#if defined (MAKE_MPW_TOOL) && defined (NB)
	{
	char *cocl_path_list;
	int path_list_length;
	
	path_list_length=1;
	if (clean_abc_path!=NULL)
		path_list_length+=strlen (clean_abc_path)+1;
	if (main_module_path!=NULL)
		path_list_length+=strlen (main_module_path);
	path_list_length+=2;
	if (clean_path_list!=NULL)
		path_list_length+=strlen (clean_path_list)+2;
		
	cocl_path_list=memory_allocate (path_list_length);
	cocl_path_list[0]='\0';
	
	if (clean_abc_path!=NULL)
		strcat (cocl_path_list,clean_abc_path);
	
	if (clean_abc_path[0])
		strcat (cocl_path_list,",");
	
	if (main_module_path==NULL)
		strcat (cocl_path_list,":");
	else
		strcat (cocl_path_list,main_module_path);

	if (clean_path_list!=NULL){
		strcat (cocl_path_list,",");
		strcat (cocl_path_list,clean_path_list);
	}
		
	*arg++=cocl_path_list;
#else
	if (clean_path_list!=NULL) {
		int path_list_length;
		char *cocl_path_list;

		path_list_length=strlen (clean_path_list)+3;
		cocl_path_list=memory_allocate (path_list_length);
		cocl_path_list[0]='\0';
		strcat (cocl_path_list,clean_path_list);
		strcat (cocl_path_list,":.");

		*arg++=cocl_path_list;
	} else
		*arg++=".";
#endif
	
	if (syntax_check)
		*arg++="-c";
	
	if (progress || ((clean_options & VERBOSE_MASK)!=0)!=DEFAULT_VERBOSE)
		*arg++="-v";
	if (((clean_options & LIST_TYPES_MASK)!=0)!=DEFAULT_LIST_TYPES)
		*arg++="-lt";
	if (((clean_options & LIST_ALL_TYPES_MASK)!=0)!=DEFAULT_LIST_ALL_TYPES)
		*arg++="-lat";

	if ((clean_options & REUSE_UNIQUE_NODES)!=0)
		*arg++="-ou";
	if ((clean_options & MEMORY_PROFILE)!=0)
		*arg++="-pm";
	if ((clean_options & NO_TIME_PROFILE_MASK)==0)
		*arg++="-pt";

	if (project_node->pro_valid_options)
		options=project_node->pro_options;
	else
		options=DEFAULT_OPTIONS;

	project_node->pro_valid_options=0;
		
	if (project_node==main_project_node)
		options=(options & ~clean_options_mask) 
				| (clean_options & clean_options_mask);
	
	if (nowarn || ((options & WARNING_MASK)!=0)!=DEFAULT_WARNING)
		*arg++="-w";
	if (((options & DEBUG_MASK)!=0)!=DEFAULT_DEBUG)
		*arg++="-d";
	if (((options & STRICTNESS_ANALYSIS_MASK)!=0)!=DEFAULT_STRICTNESS_ANALYSIS)
		*arg++="-sa";

	if (list_strict_export_types)
		*arg++="-lset";

#ifdef OS2
	if (window_application)
		*arg++="-p";
#endif

	if (funcmayfail_error)
		*arg++="-emf";
	if (varnotused_error)
		*arg++="-enu";
	if (funcnotused_error)
		*arg++="-efnu";

	*arg++="-o";
	*arg++=abc_file_name;

	*arg++=file_name;
	*arg=NULL;

#if MACINTOSH
	r=CallCompiler (arg-argv-1,argv+1);

#if defined (MAKE_MPW_TOOL) && defined (NB)
	if (cocl_path_list!=NULL){
		free (cocl_path_list);
		cocl_path_list=NULL;
	}
	
	}
# endif
	
	if (r)
		return 1;
	else
		return 0;
#else
	strcpy (cocl_file_name,clean_lib_path);
# if defined (OS2) || defined (_WINDOWS_)
	strcat (cocl_file_name,"\\cocl.exe");
	argv[0]=cocl_file_name;
#  if defined (_WINDOWS_)
#   ifdef GNU_C
	r=spawnvp (P_WAIT,cocl_file_name,argv);
	if (r>=0)
		r=wait (&status);
	return r>=0 && status==0;
#   else
	return spawnv (P_WAIT,cocl_file_name,argv)==0;
#   endif
#  else
	{
		CHAR load_error [17];
		RESULTCODES return_codes;
		APIRET rc;
		char *arguments;

		arguments=arg_v_to_arguments (argv);

		if (arguments!=NULL){					
			rc=DosExecPgm (load_error,sizeof (load_error),EXEC_SYNC,arguments,NULL,&return_codes,cocl_file_name);
					
			free (arguments);
			
			if (rc==0 && return_codes.codeTerminate==0)
				return return_codes.codeResult==0;
		}
		
		error ("Can't execute the clean compiler");
		return 0;
	}
#  endif
# else
	return call_compiler (cocl_file_name, argv);
# endif
#endif
}

#ifdef USE_TOOLSERVER
#include <Processes.h>
#include <AppleEvents.h>
#include "AERegistry.h"

static int send_mpw_command (char *mpw_command)
{
	AppleEvent the_event;
	OSType signature;
	OSErr err;
	AEAddressDesc target;

	signature = 'MPSX'; /* toolserver */

	err=AECreateDesc (typeApplSignature,(Ptr)&signature,sizeof (signature),&target);
	if (err!=noErr)
		return 1; /* err; */

	err=AECreateAppleEvent (kAEMiscStandards,kAEDoScript,&target,kAutoGenerateReturnID,kAnyTransactionID,&the_event);
	if (err!=noErr)
		return 2; /* err; */
	
	err=AEPutParamPtr (&the_event,keyDirectObject,'TEXT',mpw_command,strlen (mpw_command));
	if (err!=noErr)
		return 3; /* err; */

	err=AESend (&the_event,NULL,kAEWaitReply/* kAENoReply */,kAENormalPriority,kNoTimeOut,NULL,NULL);
	if (err!=noErr)
		return err;

	return 0;
}
#endif

#if MACINTOSH && !defined (G_POWER)
static char *OBJECT_FILE_EXTENSION;
/* #	define OBJECT_FILE_EXTENSION ".obj1" */
#else
# if (defined (_WINDOWS_) && (defined (USE_WLINK)) || defined (OMF))
#	define OBJECT_FILE_EXTENSION ".obj"
# else
#	define OBJECT_FILE_EXTENSION ".o"
# endif
#endif

#if MACINTOSH
extern int generate_code (int,char **);
#endif

static int generate_code_for_project_node (P_NODE project_node,char *file_name)
{
	char *argv[16],**arg;

	if (!silent)
		printf ("Generating code for %s\n",project_node->pro_fname);
		
	arg=argv;
	*arg++="cg";

#if MACINTOSH || defined (NO_ASSEMBLE)
	if (only_s_files)
		*arg++="-a";
#endif
	if (check_stack_overflow)
		*arg++="-os";

	if (check_indices)
		*arg++="-ci";

#if defined (MAKE_MPW_TOOL) && defined (NB)
	*arg++="-sane";
	if (!generate_mc68020_code)
		*arg++="-mc68000";
#endif

	*arg++=file_name;
	*arg=NULL;

#if MACINTOSH
# ifdef USE_TOOLSERVER
	{
		char mpw_command[512];
		int result;

		if (generate_code (arg-argv,argv)!=0)
			return 0;
/*
		if (!silent)
			printf ("Assembling %s\n",project_node->pro_fname);
		
		sprintf (mpw_command,"PPCAsm -w off \'%s.a\' -o \'%s.o\'",file_name,file_name);

		result=send_mpw_command (mpw_command);
		if (result!=0){
			printf ("%d\n",result);
			error_s ("Assembling %s failed\n",project_node->pro_fname);
		}

		{
			char assembly_file_name[PATH_NAME_STRING_SIZE];
			extern unlink (char*);
			
			sprintf (assembly_file_name,"%s.a",file_name);
			unlink (assembly_file_name);
		}
*/
		return 1;
	}
# else
	return generate_code (arg-argv,argv)==0;
# endif
#else
	{
	char cg_file_name[PATH_NAME_STRING_SIZE];

	strcpy (cg_file_name,clean_lib_path);
# if defined (OS2) || defined (_WINDOWS_)
	strcat (cg_file_name,"\\cg.exe");
	argv[0]=cg_file_name;
#  ifdef _WINDOWS_
#   ifdef GNU_C
	{
		int r,status;
	
		argv[0]=cg_file_name;
		r=spawnvp (P_WAIT,cg_file_name,argv);
		if (r>=0)
			r=wait (&status);
		return r>=0 && status==0;
	}
#   else
	return spawnv (P_WAIT,cg_file_name,argv)==0;
#   endif
#  else
	{
		CHAR load_error [17];
		RESULTCODES return_codes;
		APIRET rc;
		char *arguments;

		arguments=arg_v_to_arguments (argv);

		if (arguments!=NULL){					
			rc=DosExecPgm (load_error,sizeof (load_error),EXEC_SYNC,arguments,NULL,&return_codes,cg_file_name);
					
			free (arguments);
			
			if (rc==0 && return_codes.codeTerminate==0)
				return return_codes.codeResult==0;
		}
		
		error ("Can't execute the code generator");
		return 0;
	}
#  endif
# else
	{
		int pid,r,status;
	
		pid=fork();
		if (pid<0)
			error ("Fork failed");
		
		if (!pid){
			strcat (cg_file_name,"/cg");

			execv (cg_file_name,argv);
			
			error ("Can't execute the code generator");
		}
	
		r=wait_for_child (pid, "Code generator",&status);
		return r>=0 && status==0;
	}
# endif
	}
#endif
}

static int assemble (P_NODE project_node,char *file_name)
{
	int pid,r,status;
	char s_file_name[PATH_NAME_STRING_SIZE],o_file_name[PATH_NAME_STRING_SIZE];
	
	if (!silent)
		printf ("Assembling %s\n",project_node->pro_fname);

	strcpy (s_file_name,file_name);
	strcat (s_file_name,".s");

#if ! MACINTOSH
	if (clean_o_path==NULL)
#endif
		strcpy (o_file_name,file_name);
#if ! MACINTOSH
	else {
		strcpy (o_file_name,clean_o_path);
		strcat (o_file_name,DIRECTORY_SEPARATOR_STRING);
		strcat (o_file_name,project_node->pro_fname);
	}
#endif
	strcat (o_file_name,OBJECT_FILE_EXTENSION);

#if !(defined (OS2) || defined (_WINDOWS_)) && ! MACINTOSH
	pid=fork();
	if (pid<0)
		error ("Fork failed");
	
	if (!pid)
#endif
	{
		char *argv[16],**arg;
		
		arg=argv;
		*arg++="as";

#if defined (OS2) && defined (OMF)
		*arg++="-Zomf";
#endif

		*arg++=s_file_name;
		*arg++="-o";
		*arg++=o_file_name;
		*arg=0;
#if defined (OS2) || defined (_WINDOWS_)
# if defined (OS2)
		r=dos_exec (assembler_file_name,argv,0);
# else
#  ifdef _WINDOWS_
#   ifdef GNU_C
		{
			int status;
		
			argv[0]=assembler_file_name;
			r=spawnvp (P_WAIT,assembler_file_name,argv);
			if (r>=0)
				r=wait (&status);
			return r>=0 && status==0;
		}
#   else
		return spawnvp (P_WAIT,assembler_file_name,argv)==0;
#   endif
#  else
		r=dos_exec (assembler_file_name,argv,1);
#  endif
# endif

		return r==0;
	}
#else
#	if MACINTOSH
	}
	return 1;
#	else
		execv (assembler_file_name,argv);

		error ("Can't execute the assembler");
	}
	
	r=wait_for_child (pid, "Assembler",&status);
	return r>=0 && status==0;
#	endif
#endif
}

static void get_dependencies (P_NODE project_node)
{
	DEP_LIST dependency_list;
	char dependent_module [PATH_NAME_STRING_SIZE];
	long comp_options_position;
	int depend_or_impobj,version;

	if (!open_abc_file (project_node))
		return;

	if (!get_version_and_options_of_abc_file (project_node,&version,&comp_options_position)){
		close_abc_file();
		return;
	}

	dependency_list=NULL;
	do {
		depend_or_impobj=get_dependency_or_imported_object_file_of_abc_file (dependent_module,PATH_NAME_STRING_SIZE-1);

		if (depend_or_impobj==1)
			add_dependency (&dependency_list,dependent_module);
		else if (depend_or_impobj==2)
			add_imported_object_file_to_project_node (dependent_module,project_node);
	} while (depend_or_impobj!=0);

	close_abc_file();

	project_node->pro_depend=dependency_list;
}

static int make_project_to_abc_files (P_NODE project_node)
{
	DEP_LIST dependency;
	
	if (project_node->pro_up_to_date)
		return 1;
	
	project_node->pro_up_to_date=1;
	
	if (!project_node->pro_ignore_o && !project_node_is_abc_up_to_date (project_node)){
		if (is_system_file (project_node))
			return 0;
		if (!compile_project_node (project_node))
			return 0;
		if (!single_module)
			get_dependencies (project_node);
	}
	
	if (!single_module)
		for_l (dependency,project_node->pro_depend,dep_next)
			if (!make_project_to_abc_files (dependency->dep_node))
				return 0;
	
	return 1;
}

static int project_node_is_o_up_to_date (P_NODE project_node)
{
	FileTime abc_time,o_time;

	if (!get_clean_system_time (project_node,OBJECT_FILE_EXTENSION,&o_time
#if ! MACINTOSH
				   ,clean_o_path!=NULL ? clean_o_path : clean_abc_path
#endif
		))
	{
		if (verbose)
			warning_s ("(%s.o doesn't exist)",project_node->pro_fname);
		return 0;
	}
	
	if (get_abc_time (project_node,&abc_time) && FILE_TIME_LE (o_time,abc_time)){
		if (verbose)
			warning_s ("(%s.o is older than corresponding abc file)",
					   project_node->pro_fname);
		return 0;
	}
	
	if (verbose)
		warning_s ("(%s.o is up to date)",project_node->pro_fname);
	
	return 1;
}

static int make_project_to_o_files (void)
{
	P_NODE project_node;
	
	for_l (project_node,first_project_node,pro_next){
		if (project_node->pro_up_to_date && !project_node->pro_ignore_o && !project_node_is_o_up_to_date (project_node)){
			char file_name[PATH_NAME_STRING_SIZE];
			
#if MACINTOSH
			if (!find_clean_system_file (project_node->pro_fname,".abc",file_name))
				error_s ("Can't find %s.abc",project_node->pro_fname);
#else
# ifdef _WINDOWS_
			{
			FileTime time;
			if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path,&time))
				error_s ("Can't find %s.abc",project_node->pro_fname);
			}
# else
			if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path))
				error_s ("Can't find %s.abc",project_node->pro_fname);
# endif
#endif
			
			file_name[strlen (file_name)-4]='\0';
			
			if (!generate_code_for_project_node (project_node,file_name))
				return 0;
#if ! MACINTOSH && !defined (NO_ASSEMBLE)
			if (!assemble (project_node,file_name))
				return 0;

			strcat (file_name,".s");
			unlink (file_name);
#endif
		}
	}
	return 1;
}

static int project_node_is_s_up_to_date (P_NODE project_node)
{
	FileTime abc_time,s_time;
	
	if (!get_clean_system_time (project_node,".s",&s_time
#if ! MACINTOSH
					,clean_abc_path
#endif
	)){
		if (verbose)
			warning_s ("(%s.s doesn't exist)",project_node->pro_fname);
		return 0;
	}
	
	if (get_abc_time (project_node,&abc_time) && FILE_TIME_LE (s_time,abc_time)){
		if (verbose)
			warning_s ("(%s.s is older than corresponding abc file)",
					   project_node->pro_fname);
		return 0;
	}
	
	if (verbose)
		warning_s ("(%s.s is up to date)",project_node->pro_fname);
	return 1;
}

static int make_project_to_s_files (void)
{
	P_NODE project_node;
	
	for_l (project_node,first_project_node,pro_next){
		if (project_node->pro_up_to_date && !project_node_is_s_up_to_date (project_node)){
			char file_name[PATH_NAME_STRING_SIZE];

#if MACINTOSH
			if (!find_clean_system_file (project_node->pro_fname,".abc",file_name))
				error_s ("Can't find %s.abc",project_node->pro_fname);
#else
# ifdef _WINDOWS_
			{
			FileTime time;
			if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path,&time))
				error_s ("Can't find %s.abc",project_node->pro_fname);
			}
# else
			if (!find_clean_system_file (project_node->pro_fname,".abc",file_name,clean_abc_path))
				error_s ("Can't find %s.abc",project_node->pro_fname);
# endif
#endif
			
			file_name[strlen (file_name)-4]='\0';
			
			if (!generate_code_for_project_node (project_node,file_name))
				return 0;
		}
	}
	return 1;
}

int data [] =
{
#ifdef M68000
	0x00020107, 0x00000000, 0x00000010, 0x00000000,
	0x0000003c, 0x00000000, 0x00000000, 0x00000000,
	0x00200000, 0x00080000, 0x00080000, 0x00000008,
	0x00000004, 0x07000000, 0x0000000c, 0x0000000b,
	0x07000000, 0x00000010, 0x00000014, 0x07000000,
	0x00000000, 0x0000001f, 0x07000000, 0x00000008,
	0x0000002d, 0x07000000, 0x00000004, 0x0000003c,
	0x5f666c61, 0x67730064, 0x6174615f, 0x656e6400,
	0x5f686561, 0x705f7369, 0x7a65005f, 0x635f7374,
	0x61636b5f, 0x73697a65, 0x005f6162, 0x5f737461,
	0x636b5f73, 0x697a6500
#else
# ifdef I486
#  ifdef _WINDOWS_
#   if 0 /* without _ */
      196940,   814507665,         152,          11,
    17104896,  2019914798,         116,           0,
           0,           0,           0,           0,
           0,           0,          32,  1952539694,
          97,           0,           0,          12,
         140,           0,           0,           0,
          64,  1936941614,           0,          12,
          12,           0,           0,           0,
           0,           0,         128,          11,
          21,          31,  1818846766,         101,
           0,       65534,  1634074983,       25963,
           0,           0,           0,           0,
           4,           0,           2,           2,
      917504,      262144,      131072,      131072,
  1734437990,         115,           8,           2,
  1949171714,     7632997,           0,       65536,
    16973824,           0,           0,           0,
           0,  1680736256,     6386785,           0,
      131072,    16973824,          12,           0,
           0,           0,  1647181824,       29555,
           0,      196608,    16973824,           0,
           0,           0,           0,     1835008,
  1701314560,  1935634529,     6650473,  1935630945,
  1801675124,  2053731167,         101
#   else /* with _ */
      196940,   817729185,         152,          11,
    17039360,  2019914798,         116,           0,
           0,           0,           0,           0,
           0,           0,  1610612768,  1952539694,
          97,           0,           0,          12,
         140,           0,           0,           0,
 -1073741760,  1936941614,           0,           0,
          12,           0,           0,           0,
           0,           0, -1073741696,          11,
          21,          31,  1818846766,         101,
           0,       65534,  1634074983,       25963,
           0,           0,           0,  2019914798,
         116,           0,           1,         259,
           0,           0,           0,           0,
  1952539694,          97,           0,           2,
      786691,           0,           0,           0,
           0,  1936941614,           0,          12,
           3,         259,           0,           0,
           0,           0,           0,           4,
           0,           2,           2,      983040,
      262144,      131072,      131072,  1634494047,
       29543,           8,           2,     1966082,
  1751056384,  1601200485,  1702521203,  1650548480,
  1635021663,  1935633251,     6650473
#   endif
#  else
#   ifdef OMF
	 0x0b000d80,0x6974706f,0x2e736e6f,0xf36a626f
	,0x80000688,0x4c4803a1,0x005096ba,0x45540600
	,0x32335458,0x54414406,0x05323341,0x33535342
	,0x24240932,0x424d5953,0x07534c4f,0x59542424
	,0x04534550,0x45444f43,0x54414404,0x53420341
	,0x45440653,0x4d595342,0x42454406,0x04505954
	,0x54414c46,0x52474406,0x7350554f,0xa9000798
	,0x07020000,0x0798ae01,0x000ca900,0xa0010803
	,0xa9000798,0x09040000,0x0798aa01,0x0017a900
	,0x91010a05,0xa9000798,0x0b060000,0x029aa601
	,0x9a580c00,0xff0d0006,0x5002ff03,0x01002a90
	,0x65680902,0x735f7061,0x00657a69,0x610d0000
	,0x74735f62,0x5f6b6361,0x657a6973,0x05000004
	,0x67616c66,0x00000873,0x00048805,0x9101a240
	,0x020010a0,0x00010000,0x00020000,0x00030000
	,0xa0480000,0x00040015,0x01401000,0x20202004
	,0x35100020,0x01170017,0xa02b07cc,0x1104000a
	,0x00110500,0x2b000000,0x4400089d,0xc8011001
	,0x95af8c02,0x01000027,0x00000000,0x00000000
	,0x00000018,0x00000000,0x00000000,0x00000001
	,0x74706f0b,0x736e6f69,0x6a626f2e,0x00028baa
	,0x00007300
#   else
#	 ifdef LINUX_ELF
#     ifdef A_64
	0x464c457f,0x00010102,0x00000000,0x00000000,
	0x003e0001,0x00000001,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000094,0x00000000,
	0x00000000,0x00000040,0x00400000,0x00040007,
	
	0x00000001,0x00000000,0x00000004,0x00000000,
	0x00000005,0x00000000,0x00000002,0x00000000,
	0x00000003,0x00000000,
						  0x79732e00,0x6261746d,
	0x74732e00,0x62617472,0x68732e00,0x74727473,
	0x2e006261,0x74786574,0x61642e00,0x2e006174,
	0x00737362,
			   0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,
			   0x0000001b,0x00000001,0x00000006,
	0x00000000,0x00000000,0x00000000,0x00000040,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000004,0x00000000,0x00000000,
	0x00000000,
			   0x00000021,0x00000001,0x00000003,
	0x00000000,0x00000000,0x00000000,0x00000040,
	0x00000000,0x00000028,0x00000000,0x00000000,
	0x00000000,0x00000004,0x00000000,0x00000000,
	0x00000000,
			   0x00000027,0x00000008,0x00000003,
	0x00000000,0x00000000,0x00000000,0x00000068,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000004,0x00000000,0x00000000,
	0x00000000,
			   0x00000011,0x00000003,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000068,
	0x00000000,0x0000002c,0x00000000,0x00000000,
	0x00000000,0x00000001,0x00000000,0x00000000,
	0x00000000,
			   0x00000001,0x00000002,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000254,
	0x00000000,0x000000d8,0x00000000,0x00000006,
	0x00000004,0x00000004,0x00000000,0x00000018,
	0x00000000,
			   0x00000009,0x00000003,0x00000000,
	0x00000000,0x00000000,0x00000000,0x0000032c,
	0x00000000,0x00000044,0x00000000,0x00000000,
	0x00000000,0x00000001,0x00000000,0x00000000,
	0x00000000,
			   0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,
									 0x00000000,
	0x00010003,0x00000000,0x00000000,0x00000000,
	0x00000000,
	           0x00000000,0x00020003,0x00000000,
	0x00000000,0x00000000,0x00000000,
		 	 				         0x00000000,
	0x00030003,0x00000000,0x00000000,0x00000000,
	0x00000000,
	           0x00000001,0x00020011,0x00000000,
	0x00000000,0x00000000,0x00000000,
									 0x0000000b,
	0x00020011,0x00000008,0x00000000,0x00000000,
	0x00000000,
	           0x00000019,0x00020011,0x00000010,
	0x00000000,0x00000000,0x00000000,
						     		 0x0000001f,
	0x00020011,0x00000018,0x00000000,0x00000000,
	0x00000000,
	           0x00000032,0x00020011,0x00000020,
	0x00000000,0x00000000,0x00000000,
									 0x61656800,
	0x69735f70,0x6100657a,0x74735f62,0x5f6b6361,
	0x657a6973,0x616c6600,0x68007367,0x5f706165,
	0x657a6973,0x6c756d5f,0x6c706974,0x6e690065,
	0x61697469,0x65685f6c,0x735f7061,0x00657a69
#     else
	0x464c457f,0x00010101,0x00000000,0x00000000,
	0x00030001,0x00000001,0x00000000,0x00000000,
	0x00000074,0x00000000,0x00000034,0x00280000,
	0x00040007,0x00000001,0x00000004,0x00000005,
	0x00000002,0x00000003,0x79732e00,0x6261746d,
	0x74732e00,0x62617472,0x68732e00,0x74727473,
	0x2e006261,0x74786574,0x61642e00,0x2e006174,
	0x00737362,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x0000001b,
	0x00000001,0x00000006,0x00000000,0x00000034,
	0x00000000,0x00000000,0x00000000,0x00000004,
	0x00000000,0x00000021,0x00000001,0x00000003,
	0x00000000,0x00000034,0x00000014,0x00000000,
	0x00000000,0x00000004,0x00000000,0x00000027,
	0x00000008,0x00000003,0x00000000,0x00000048,
	0x00000000,0x00000000,0x00000000,0x00000004,
	0x00000000,0x00000011,0x00000003,0x00000000,
	0x00000000,0x00000048,0x0000002c,0x00000000,
	0x00000000,0x00000001,0x00000000,0x00000001,
	0x00000002,0x00000000,0x00000000,0x0000018c,
	0x00000090,0x00000006,0x00000004,0x00000004,
	0x00000010,0x00000009,0x00000003,0x00000000,
	0x00000000,0x0000021c,0x00000044,0x00000000,
	0x00000000,0x00000001,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00010003,0x00000000,
	0x00000000,0x00000000,0x00020003,0x00000000,
	0x00000000,0x00000000,0x00030003,0x00000001,
	0x00000000,0x00000000,0x00020011,0x0000000b,
	0x00000004,0x00000000,0x00020011,0x00000019,
	0x00000008,0x00000000,0x00020011,0x0000001f,
	0x0000000c,0x00000000,0x00020011,0x00000032,
	0x00000010,0x00000000,0x00020011,0x61656800,
	0x69735f70,0x6100657a,0x74735f62,0x5f6b6361,
	0x657a6973,0x616c6600,0x68007367,0x5f706165,
	0x657a6973,0x6c756d5f,0x6c706974,0x6e690065,
	0x61697469,0x65685f6c,0x735f7061,0x00657a69
#     endif
#	 else
	0x00640107,0x00000000,0x0000000c,0x00000000,
	0x00000024,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000004,
	0x00000007,0x00000000,0x0000000f,0x00000007,
	0x00000004,0x0000001e,0x00000007,0x00000008,
	0x00000025,0x6165685f,0x69735f70,0x5f00657a,
	0x735f6261,0x6b636174,0x7a69735f,0x665f0065,
	0x7367616c,0x00000000
#	 endif
#   endif
#  endif
# else
#  if defined (SOLARIS)
	0x7f454c46,0x01020100,0x00000000,0x00000000,
	0x00010002,0x00000001,0x00000000,0x00000000,
	0x000000ec,0x00000000,0x00340000,0x00000028,
	0x00050001,0x002e7368,0x73747274,0x6162002e,
	0x64617461,0x002e7379,0x6d746162,0x002e7374,
	0x72746162,0x00000000,0x00000001,0x00000002,
	0x00000003,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000001,0x0000000c,0x00000000,
	0x00000002,0x00000000,0x00000000,0x00000000,
	0x03000002,0x0000000a,0x00000000,0x00000000,
	0x10000002,0x00000014,0x00000004,0x00000000,
	0x10000002,0x00000022,0x00000008,0x00000000,
	0x10000002,0x00646174,0x615f656e,0x64006865,
	0x61705f73,0x697a6500,0x61625f73,0x7461636b,
	0x5f73697a,0x6500666c,0x61677300,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000001,0x00000003,0x00000000,
	0x00000000,0x00000034,0x00000021,0x00000000,
	0x00000000,0x00000001,0x00000000,0x0000000b,
	0x00000001,0x00000003,0x00000000,0x00000058,
	0x0000000c,0x00000000,0x00000000,0x00000004,
	0x00000000,0x00000011,0x00000002,0x00000002,
	0x00000000,0x00000064,0x00000060,0x00000004,
	0x00000003,0x00000004,0x00000010,0x00000019,
	0x00000003,0x00000002,0x00000000,0x000000c4,
	0x00000028,0x00000000,0x00000000,0x00000001,
	0x00000000
#  else
#   ifdef LINUX
	0x7f454c46,0x01020100,0x00000000,0x00000000,
	0x00010014,0x00000001,0x00000000,0x00000000,
	0x00000074,0x00000000,0x00340000,0x00000028,
	0x00070004,0x00000008,0x00200000,0x00080000,
	0x00001400,0x00019000,0x002e7379,0x6d746162,
	0x002e7374,0x72746162,0x002e7368,0x73747274,
	0x6162002e,0x74657874,0x002e6461,0x7461002e,
	0x62737300,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x0000001b,
	0x00000001,0x00000006,0x00000000,0x00000034,
	0x00000000,0x00000000,0x00000000,0x00000001,
	0x00000000,0x00000021,0x00000001,0x00000003,
	0x00000000,0x00000034,0x00000014,0x00000000,
	0x00000000,0x00000001,0x00000000,0x00000027,
	0x00000008,0x00000003,0x00000000,0x00000048,
	0x00000000,0x00000000,0x00000000,0x00000001,
	0x00000000,0x00000011,0x00000003,0x00000000,
	0x00000000,0x00000048,0x0000002c,0x00000000,
	0x00000000,0x00000001,0x00000000,0x00000001,
	0x00000002,0x00000000,0x00000000,0x0000018c,
	0x00000090,0x00000006,0x00000004,0x00000004,
	0x00000010,0x00000009,0x00000003,0x00000000,
	0x00000000,0x0000021c,0x00000041,0x00000000,
	0x00000000,0x00000001,0x00000000,0x00000000,
	0x00000000,0x00000000,0x00000000,0x00000000,
	0x00000000,0x00000000,0x03000001,0x00000000,
	0x00000000,0x00000000,0x03000002,0x00000000,
	0x00000000,0x00000000,0x03000003,0x00000001,
	0x00000000,0x00000000,0x11000002,0x00000007,
	0x00000004,0x00000000,0x11000002,0x00000011,
	0x00000008,0x00000000,0x11000002,0x0000001c,
	0x0000000c,0x00000000,0x11000002,0x0000002f,
	0x00000010,0x00000000,0x11000002,0x00666c61,
	0x67730068,0x6561705f,0x73697a65,0x00737461,
	0x636b5f73,0x697a6500,0x68656170,0x5f73697a,
	0x655f6d75,0x6c746970,0x6c650069,0x6e697469,
	0x616c5f68,0x6561705f,0x73697a65,0x00
#   else
	0x01030107,0x00000000,0x00000010,0x00000000,
	0x0000003c,0x00000000,0x00000000,0x00000000,
	0x00200000,0x00080000,0x00080000,0x00000008,
	0x00000004,0x07000000,0x00000008,0x00000012,
	0x07000000,0x0000000c,0x00000019,0x07000000,
	0x00000004,0x00000028,0x07000000,0x00000000,
	0x00000033,0x07000000,0x00000010,0x0000003c,
	0x5f635f73,0x7461636b,0x5f73697a,0x65005f66,
	0x6c616773,0x005f6162,0x5f737461,0x636b5f73,
	0x697a6500,0x5f686561,0x705f7369,0x7a650064,
	0x6174615f,0x656e6400
#   endif
#  endif
# endif
#endif
};

#define COMMENT_CHAR '#'

static char *parse_word (char **line_h)
{
    char *begin,*end;
	int c;

    begin=*line_h;
	while ((c = (*begin=='\\' && *(begin+1)!='\0') ? * ++begin : *begin),isspace (c))
		++begin;

    if (*begin==COMMENT_CHAR || *begin=='\0')
        return NULL;

    for (end=begin; !isspace ((int) *end) && *end!='\0'; ++end)
        if (*end=='\\' && *(end+1)!='\0')
            ++end;

    if (*end=='\0')
        *line_h = end;
    else {
        *line_h = end+1;
        *end = '\0';
    }

    return begin;
}

long ab_stack_size,heap_size,flags;

#ifdef MARKING_GC
long heap_size_multiple=20<<8,initial_heap_size=100<<10;
#endif

#if ! MACINTOSH
int create_options_file (char **options_file_name_p)
{
	char *options_file_name;
	FILE *f;
	unsigned int data_size;

#if defined (_WINDOWS_) || defined (OS2)
	options_file_name="cgopt.$$$";

# ifdef OMF
	{
		char *p;
		
		for (p=options_file_name; *p; ++p)
			if (*p=='/')
				*p='\\';
	}
# endif

	f=fopen (options_file_name,"wb");
#else
	{
	static char cgopt_file_name[]="/tmp/cgoptXXXXXX";
	int options_fd;

	options_fd=mkstemp (cgopt_file_name);
	if (options_fd<0)
		return 0;

	options_file_name=cgopt_file_name;
	
	f=fdopen (options_fd,"wb");
	}
#endif
	if (f==NULL)
		error_s ("Can't create options file %s",options_file_name);

#if defined (SOLARIS)
	data[22]=heap_size;
	data[23]=ab_stack_size;
	data[24]=flags;
	data_size=sizeof (data);
#else
# if defined (LINUX_ELF)
#  ifdef A_64
	data[16]=heap_size;
	data[18]=ab_stack_size;
	data[20]=flags;
	data[22]=heap_size_multiple;
	data[24]=initial_heap_size;
#  else
	data[13]=heap_size;
	data[14]=ab_stack_size;
	data[15]=flags;
	data[16]=heap_size_multiple;
	data[17]=initial_heap_size;
#  endif
	data_size=sizeof (data);
# elif defined (LINUX) && defined (POWERPC)
	data[13]=flags;
	data[14]=heap_size;
	data[15]=ab_stack_size;
	data[16]=heap_size_multiple;
	data[17]=initial_heap_size;
	data_size=sizeof (data);
# else
#  ifdef _WINDOWS_
	data[35]=heap_size;
	data[36]=ab_stack_size;
	data[37]=flags;
#   if 0 /* without _ */
	data_size=sizeof (data)-2;
#   else /* with _ */
	data_size=sizeof (data);
#   endif
#  else
#   ifdef OMF
	*(long*)((char*)data+230)=heap_size;
	*(long*)((char*)data+234)=ab_stack_size;
	*(long*)((char*)data+238)=flags;
	data_size=sizeof (data)-2;
#   else
	data[8]=heap_size;
	data[9]=ab_stack_size;
#    ifdef I486
	data[10]=flags;
	data_size=sizeof (data)-3;
#    else
	data[10]=512<<10;
	data[11]=flags;
	data_size=sizeof (data);
#    endif
#   endif
#  endif
# endif
#endif
	
	if (fwrite (data,1,data_size,f)!=data_size){
		fclose (f);
		unlink (options_file_name);
		error ("Error writing options file");
	}
	
	if (fclose (f)!=0){
		unlink (options_file_name);
		error ("Error writing options file");
	}
	
	*options_file_name_p=options_file_name;
	return 1;
}
#else

#include <Resources.h>

static void remove_resources (ResType resource_type)
{
	SetResLoad (0);
	while (Count1Resources (resource_type)>0){
		Handle resource;
		
		resource=Get1IndResource (resource_type,1);
		if (resource==NULL || ResError()){
			SetResLoad (1);
			error ("Error while removing resources from application file");
		}
		
		RmveResource (resource);
		if (ResError()){
			SetResLoad (1);
			error ("Error while removing resources from application file");
		}
	}
	SetResLoad (1);
}

static int add_stack_and_size_resource_to_resource_file (void)
{
	Handle size_handle;
	unsigned short *size_p;
	Handle stack_handle;
	unsigned long *stack_p;
	unsigned long size;

	remove_resources ('SIZE');
	remove_resources ('STCK');

	stack_handle=NewHandle (16);
	if (stack_handle==NULL)
		return 0;
	
	size_handle=NewHandle (10);
	if (size_handle==NULL){
		DisposeHandle (stack_handle);
		return 0;
	}
	
	stack_p=(unsigned long*)*stack_handle;
	*stack_p++=ab_stack_size;
	*stack_p++=0;
	*stack_p++=heap_size;
	*stack_p=flags;

	size_p=(unsigned short*)*size_handle;
	*size_p++=0;
	size = ab_stack_size + heap_size + 160*1024;
	size = (size+3l) & ~3l;
	*(*(unsigned long**)&size_p)++= size;
	*(*(unsigned long**)&size_p)++= size;

	AddResource (stack_handle,'STCK',0,(ConstStr255Param)"\p");
	if (ResError()){
		DisposeHandle (stack_handle);
		DisposeHandle (size_handle);
		return 0;
	}
	
	AddResource (size_handle,'SIZE',-1,(ConstStr255Param)"\p");
	if (ResError()){
		ReleaseResource (stack_handle);
		DisposeHandle (size_handle);
		return 0;
	}

	ReleaseResource (stack_handle);
	if (ResError()){
		ReleaseResource (size_handle);
		return 0;
	}

	ReleaseResource (size_handle);
	if (ResError())
		return 0;
	
	return 1;
}

static int add_stack_and_size_resource_to_application (char *application_file_name)
{
	int resource_file_n,error;
	char resource_file_name[256];

	if (application_file_name!=NULL)
		copy_c_to_p_string (application_file_name,resource_file_name);
	else
		copy_c_to_p_string ("hd:desktop folder:a",resource_file_name);

	resource_file_n=HOpenResFile (0,0,(ConstStr255Param)resource_file_name,fsRdWrPerm);
	if (resource_file_n==-1)
		return 0;

	error=add_stack_and_size_resource_to_resource_file();

	CloseResFile (resource_file_n);
	if (ResError())
		return 0;
	
	return error;	
}
#endif

#if MACINTOSH && !defined (G_POWER)
extern int link_application_argc_argv (int,char **);
#endif

#ifdef OPTIMISE_LINK
static char *linker_output_object_file_name;
static int no_optimise_link;
#endif

#ifdef USE_TOOLSERVER

static char *append_string (char *s1,char *s2)
{
	char c;
	
	while (c=*s2++,c!='\0')
		*s1++=c;
	
	return s1;
}

# define USE_METROWERKS_LINKER 0

static int link_project_toolserver (P_NODE first_project_node,char *options_file_name,char *application_file_name)
{
	P_NODE project_node;
	char o_file_name[PATH_NAME_STRING_SIZE];
	struct library_list *library;
	char *mpw_command,*p;

	mpw_command=malloc (8192);
	if (mpw_command==NULL)
		error ("Out of memory");
	
	if (!silent)
		printf ("Linking %s\n",main_project_node->pro_fname);

# if USE_METROWERKS_LINKER
	p=append_string (mpw_command,"MWLinkPPC >= PPCLinkErrors -o ");
# else
	p=append_string (mpw_command,"PPCLink -mf >= PPCLinkErrors -o ");
# endif
	
	if (application_file_name!=NULL){
		p=append_string (p,application_file_name);
	} else
		p=append_string (p,"\'hd:desktop folder:a\'");

	*p++=' ';
	*p++='\'';
	p=append_string (p,clean_lib_path);
	p=append_string (p,":Clean System Files:_startup" OBJECT_FILE_EXTENSION);
	*p++='\'';

	*p++=' ';
	*p++='\'';
	p=append_string (p,clean_lib_path);
	p=append_string (p,":Clean System Files:_system" OBJECT_FILE_EXTENSION);
	*p++='\'';
	
	for_l (project_node,first_project_node,pro_next){
		if (project_node->pro_up_to_date && !(project_node->pro_ignore_o)){
			if (!find_clean_system_file (project_node->pro_fname,OBJECT_FILE_EXTENSION,o_file_name))
				error_s ("Can't find %s" OBJECT_FILE_EXTENSION,project_node->pro_fname);

			*p++=' ';
			*p++='\'';
			p=append_string (p,o_file_name);
			*p++='\'';
		}
	}

	p=append_string (p,
		" \'Applications:MPW:Libraries:SharedLibraries:InterfaceLib\'"
		" \'Applications:MPW:Libraries:PPCLibraries:MathLib.xcoff\'"
		" \'Applications:MPW:Libraries:SharedLibraries:StdCLib\'"
		" \'Applications:MPW:Libraries:PPCLibraries:StdCRuntime.o\'"
		" \'Applications:MPW:Libraries:PPCLibraries:PPCCRuntime.o\'"
# if !USE_METROWERKS_LINKER
		" -librename MathLib.xcoff=MathLib"
# endif
		" -main __start");

	*p='\0';

	if (send_mpw_command (mpw_command)!=0){
		free (mpw_command);
		error ("Linking failed");
		return 0;
	}
/*
	p=append_string (mpw_command,"makePEF >= MakePEFErrors ");

	if (application_file_name!=NULL){
		p=append_string (p,application_file_name);
		p=append_string (p,".xcoff");
		p=append_string (p," -o ");
		p=append_string (p,application_file_name);
	} else {
		p=append_string (p,"\'hd:desktop folder:a.xcoff\'");
		p=append_string (p," -o ");
		p=append_string (p,"\'hd:desktop folder:a\'");
	}
	
	p=append_string (p,	" -l InterfaceLib.xcoff=InterfaceLib"
						" -l MathLib.xcoff=MathLib"
						" -l StdCLib.xcoff=StdCLib"
						" -ft APPL -fc \'????\'");
	*p='\0';

	if (send_mpw_command (mpw_command)!=0){
		free (mpw_command);
		error ("Making PEF failed");
		return 0;
	}
*/	
	free (mpw_command);

	return 1;
}

#endif

static int stack_trace;

static char **add_imported_object_files (P_NODE first_project_node,char *o_file_name,char **arg)
{
	P_NODE project_node;

	for_l (project_node,first_project_node,pro_next){
		struct object_file_list *imported_object_file;
		char *file_name;

		for_l (imported_object_file,project_node->pro_imported_object_files,object_file_next){
# ifdef _WINDOWS_
			FileTime time;
# endif

			/* use "Clean System Files/" as file name and the object file name as extension: */
			if (!find_file ("Clean System Files" DIRECTORY_SEPARATOR_STRING,imported_object_file->object_file_name,
# if MACINTOSH
							o_file_name
# else
							o_file_name,clean_o_path!=NULL ? clean_o_path : clean_abc_path
#  ifdef _WINDOWS_
				,&time
#  endif
# endif
			))
			{
				error_s_s ("Can't find object file %s imported from %s",
							imported_object_file->object_file_name,project_node->pro_fname);
			}

			file_name=memory_allocate (strlen (o_file_name)+1);

			strcpy (file_name,o_file_name);
			*arg++=file_name;
		}
	}
	return arg;
}

#if !(defined (MAKE_MPW_TOOL) || defined (USE_TOOLSERVER))

static int link_project (P_NODE first_project_node,char *options_file_name,char *application_file_name)
{
	P_NODE project_node;
	char o_file_name[PATH_NAME_STRING_SIZE],system_file_name[PATH_NAME_STRING_SIZE];
	char *argv[1024],**arg;
	struct library_list *library;

# if (defined (_WINDOWS_) && !defined (USE_WLINK)) || defined (OMF)
#  if defined (NO_CLIB)
	char startup0_file_name[PATH_NAME_STRING_SIZE];
#  endif
	char startup1_file_name[PATH_NAME_STRING_SIZE],startup2_file_name[PATH_NAME_STRING_SIZE];
# else
	char start_up_file_name[PATH_NAME_STRING_SIZE];
# endif
# ifdef sparc
	char reals_file_name[PATH_NAME_STRING_SIZE];
# endif
# ifdef OS2
	char clean_resources_file_name[PATH_NAME_STRING_SIZE], clean_def_file_name[PATH_NAME_STRING_SIZE],appl_file_name[PATH_NAME_STRING_SIZE];
#  ifndef OMF
	char os2_interface_file_name[PATH_NAME_STRING_SIZE];
#  endif
#  ifdef NO_CLIB
	char os2lib_file_name[PATH_NAME_STRING_SIZE];
#  endif
	int l;
# endif
# if defined (_WINDOWS_) && defined (USE_WLINK)
	char w_crt0_file_name[PATH_NAME_STRING_SIZE];
# endif
# if defined (_WINDOWS_) || defined (OS2)
	char stack_option[32];
# endif	
# if defined (USE_CLEANLINKER)
	char kernel32_file_name[PATH_NAME_STRING_SIZE];
	char user32_file_name[PATH_NAME_STRING_SIZE];
	char gdi32_file_name[PATH_NAME_STRING_SIZE];
# endif
# ifdef SOLARIS
        char crt [128], crti [128], crtn [128];
# endif
 
	if (!silent)
		printf ("Linking %s\n",main_project_node->pro_fname);

# ifdef OPTIMISE_LINK
	if (!no_optimise_link){
	{
	static char linker_output_file_name[]="/tmp/linkerXXXXXX";
	int linker_output_fd;

	linker_output_fd=mkstemp (linker_output_file_name);
	if (linker_output_fd<0){
		error_s ("Can't create %s",linker_output_file_name);
		return 0;
	}
	close (linker_output_fd);
	linker_output_object_file_name=linker_output_file_name;
	}

	arg=argv;

	*arg++="linker";

	*arg++=linker_output_object_file_name;

	if ((clean_options & NO_TIME_PROFILE_MASK)!=0)
		need_file ("_startup",OBJECT_FILE_EXTENSION,start_up_file_name);
	else if (stack_trace)
		need_file ("_startupTrace",OBJECT_FILE_EXTENSION,start_up_file_name);
	else
		need_file ("_startupProfile",OBJECT_FILE_EXTENSION,start_up_file_name);
	*arg++=start_up_file_name;

	need_file ("_system",OBJECT_FILE_EXTENSION,system_file_name);
	*arg++=system_file_name;

	for_l (project_node,first_project_node,pro_next){
		if (project_node->pro_up_to_date && !project_node->pro_ignore_o){
			char *file_name;

			if (!find_clean_system_file (project_node->pro_fname,OBJECT_FILE_EXTENSION,o_file_name,
							clean_o_path!=NULL ? clean_o_path : clean_abc_path
			))
			{
				error_s ("Can't find %s.o",project_node->pro_fname);
			}

			file_name=memory_allocate (strlen (o_file_name)+1);
		
			strcpy (file_name,o_file_name);
			*arg++=file_name;
		}
	}

	arg = add_imported_object_files (first_project_node,o_file_name,arg);
	
	for_l (library,first_library,library_next){
		int s;

		s=strlen (library->library_file_name);
		if (s>=2 && library->library_file_name[0]!=':' && library->library_file_name[s-2]=='.' && library->library_file_name[s-1]=='o')
			*arg++=library->library_file_name;
	}
	*arg=NULL;

	{
	int pid,r,status;

# if 0
	char **a;

	a=argv;
	while (*a){
		printf ("%s\n",*a);
		++a;
	}
# endif

	pid=fork();
	if (pid<0)
		error ("Fork failed");
	
	if (!pid){
		char linker_file_name_[PATH_NAME_STRING_SIZE];

		strcpy (linker_file_name_,clean_lib_path);
		strcat (linker_file_name_,"/linker");

		execv (linker_file_name_,argv);
		
		error ("Can't execute the linker");
	}
	
	r=wait_for_child (pid, "Linker",&status);

	if (!(r>=0 && status==0))
		return 0;
	}
	}
#endif
	
	arg=argv;

# ifdef USE_WLINK
	*arg++="wlink";
	*arg++="sys";
	*arg++="nt";
	*arg++="op";
	*arg++="c";
	*arg++="op";
	*arg++="q";
	*arg++="op";
	sprintf (stack_option,"stack=%dk",(ab_stack_size+1023) >> 10);
	*arg++=stack_option;
# else
#  ifdef OMF
	*arg++="link386";
	*arg++="/bat";
	*arg++="/nol";
	*arg++="/noe";
	*arg++="/noi";
	*arg++="/nod";
	*arg++="/base:0x10000";
	sprintf (stack_option,"/st:%ld",(long)ab_stack_size);
	*arg++=stack_option;
#  else
#   ifdef USE_CLEANLINKER
	*arg++="linker";
#   else
	*arg++=linker_file_name;
#   endif
#  endif
# endif

    {
        static char ld_args_copy[PATH_LIST_STRING_SIZE];
        char *rest,*ld_arg;

        rest = strcpy (ld_args_copy,ld_args);
        while ((ld_arg=parse_word (&rest))!=NULL)
            *arg++=ld_arg;
    }

# ifdef M68000
	*arg++="-N";
# endif

# if !defined (I486) && !MACINTOSH && !defined (SOLARIS) && !defined (LINUX)
	*arg++="-e";
	*arg++="start";
	*arg++="-dc";
	*arg++="-dp";
# endif

# if !MACINTOSH
#  if !(defined (USE_WLINK) || defined (OMF) || defined (USE_CLEANLINKER))
	if (remove_symbol_table)
		*arg++="-s";
#  endif
#  ifdef OS2
#   ifndef OMF
	*arg++ = "-o";
	l=strlen (application_file_name);

	if (l > 4 && strcmp (&application_file_name[l-4],".exe")==0)
		*arg++=application_file_name;
	else {
		strcpy (appl_file_name,application_file_name);
		strcat (appl_file_name,".exe");
		*arg++=appl_file_name;
	}
#   endif
#  else
	if (application_file_name!=NULL){
#   ifdef USE_WLINK
		*arg++="name";
#   else
		*arg++="-o";
#   endif
		*arg++=application_file_name;
	}
#   if defined (_WINDOWS_) && !defined (USE_CLEANLINKER)
	else {
#    ifdef USE_WLINK
		*arg++="name";
		*arg++="a";
#    else 
		*arg++="-o";
		*arg++="a.exe";
#    endif
	}
#   endif
#  endif
#  if defined (OS2) && !defined (OMF)
	*arg++=gcc_lib_directory_arg;
#  endif

#  ifndef NO_CLIB
#   ifdef USE_WLINK
	*arg++="file";
	strcpy (w_crt0_file_name,clean_lib_path);
	strcat (w_crt0_file_name,"\\crt0.obj");
	*arg++=w_crt0_file_name;
#   else
#    ifdef OMF
	*arg++="e:\\emx\\lib\\crt0.obj";
#    else
#     ifndef LINUX
#      ifdef SOLARIS
		if ((clean_options & NO_TIME_PROFILE_MASK)!=0)
			need_file ("_startup",OBJECT_FILE_EXTENSION,start_up_file_name);
		else if (stack_trace)
			need_file ("_startupTrace",OBJECT_FILE_EXTENSION,start_up_file_name);
		else		
			need_file ("_startupProfile",OBJECT_FILE_EXTENSION,start_up_file_name);

		strcpy (crt,start_up_file_name);
		replace_file_name_in_path (crt,"crt",OBJECT_FILE_EXTENSION);
		*arg++=crt;

		strcpy (crti,start_up_file_name);
		replace_file_name_in_path (crti,"crti",OBJECT_FILE_EXTENSION);
		*arg++=crti;
#      else
	*arg++=crt0_file_name;
#      endif
#     endif
#    endif
#   endif
#  endif

#  ifdef M68000
	*arg++=Mcrt1_file_name;
#  endif
# endif

# ifdef OPTIMISE_LINK
	if (!no_optimise_link)
		*arg++ = linker_output_object_file_name;
	else {
# endif

# if (defined (_WINDOWS_) && !defined (USE_WLINK)) || defined (OMF)
#  ifdef NO_CLIB
	strcpy (startup0_file_name,clean_lib_path);
	strcat (startup0_file_name,"\\_startup0" OBJECT_FILE_EXTENSION);
	*arg++=startup0_file_name;
#  endif
#  ifdef OS2
	{
		P_NODE project_node;

		for_l (project_node,first_project_node,pro_next)
			if (!strcmp (project_node->pro_fname,"_startup1"))
				break;
		if (project_node==NULL){
#  endif
	strcpy (startup1_file_name,clean_lib_path);
	strcat (startup1_file_name,"\\_startup1" OBJECT_FILE_EXTENSION);
	*arg++=startup1_file_name;
#  ifdef OS2
		}
	}
	{
		P_NODE project_node;

		for_l (project_node,first_project_node,pro_next)
			if (!strcmp (project_node->pro_fname,"_startup2"))
				break;
		if (project_node==NULL){
#  endif
	strcpy (startup2_file_name,clean_lib_path);
	strcat (startup2_file_name,"\\_startup2" OBJECT_FILE_EXTENSION);
	*arg++=startup2_file_name;
#  ifdef OS2
		}
	}
#  endif
# else
#  if !(!defined (LINUX) && defined (SOLARIS))
	if ((clean_options & NO_TIME_PROFILE_MASK)!=0)
		need_file ("_startup",OBJECT_FILE_EXTENSION,start_up_file_name);
	else if (stack_trace)
		need_file ("_startupTrace",OBJECT_FILE_EXTENSION,start_up_file_name);
	else
		need_file ("_startupProfile",OBJECT_FILE_EXTENSION,start_up_file_name);
#  endif
	*arg++=start_up_file_name;
# endif	

# ifdef OS2
	{
		P_NODE project_node;

		for_l (project_node,first_project_node,pro_next)
			if (!strcmp (project_node->pro_fname,"_system"))
				break;
		if (project_node==NULL){
# endif
	need_file ("_system",OBJECT_FILE_EXTENSION,system_file_name);
	*arg++=system_file_name;
# ifdef OS2
		}
	}
# endif

	for_l (project_node,first_project_node,pro_next){
		if (project_node->pro_up_to_date && !project_node->pro_ignore_o){
			char *file_name;
# ifdef _WINDOWS_
			FileTime time;
# endif

# if ! MACINTOSH
			if (!find_clean_system_file (project_node->pro_fname,OBJECT_FILE_EXTENSION,o_file_name,
							clean_o_path!=NULL ? clean_o_path : clean_abc_path
#  ifdef _WINDOWS_
				,&time
#  endif
			))
			{
				error_s ("Can't find %s.o",project_node->pro_fname);
			}
# else
			if (!find_clean_system_file (project_node->pro_fname,OBJECT_FILE_EXTENSION,o_file_name))
				error_s ("Can't find %s" OBJECT_FILE_EXTENSION,project_node->pro_fname);
# endif
				
			file_name=memory_allocate (strlen (o_file_name)+1);
		
			strcpy (file_name,o_file_name);
# ifdef USE_WLINK
			*arg++="file";
# endif
			*arg++=file_name;
		}
	}

	arg = add_imported_object_files (first_project_node,o_file_name,arg);

# ifdef OPTIMISE_LINK
	}
# endif

#ifndef OS2
	for_l (library,first_library,library_next){
# ifdef OPTIMISE_LINK
		if (!no_optimise_link){
			int s;

			if (library->library_file_name[0]==':')
				*arg++=&library->library_file_name[1];
			else
				if (s=strlen (library->library_file_name), ! (s>=2 && library->library_file_name[s-2]=='.' && library->library_file_name[s-1]=='o'))
					*arg++=library->library_file_name;
		} else
# endif
		*arg++=library->library_file_name;
	}
#endif

# if MACINTOSH
#  if 1
	{
	char library_obj_file_name[PATH_NAME_STRING_SIZE];

	strcpy (library_obj_file_name,clean_lib_path);
	strcat (library_obj_file_name,":Clean System Files:_library" OBJECT_FILE_EXTENSION);
	*arg++=library_obj_file_name;
	*arg=NULL;

	return link_application_argc_argv (arg-argv,argv)==0;
	}
#  else
	{
	char **argument;
	
	if (application_file_name!=NULL)
		printf ("linker %s ",application_file_name);
	else
		printf ("linker a.out");
	
	for (argument=argv; argument!=arg; ++argument)
		printf ("[partial]\n \'%s\'",*argument);

	printf ("[partial]\n \'%s:Clean System Files:_library.obj2\'\n",clean_lib_path);

	return 1;
	}
#  endif
# else
#  if defined (OS2) && !defined (OMF)
	if (window_application){
		strcpy (clean_resources_file_name,clean_lib_path);
		strcat (clean_resources_file_name,"\\clean.res");
		*arg++ = clean_resources_file_name;
		strcpy (clean_def_file_name,clean_lib_path);
		strcat (clean_def_file_name,"\\clean.def");
		*arg++ = clean_def_file_name;
		strcpy (os2_interface_file_name,clean_lib_path);
		strcat (os2_interface_file_name,"\\os2interface.lib");
		*arg++ = os2_interface_file_name;
	}
#  endif

#  ifdef USE_WLINK
	*arg++="file";
#  endif
	*arg++=options_file_name;

#  ifdef sparc
	strcpy (reals_file_name,start_up_file_name);\
	replace_file_name_in_path (reals_file_name,"_reals",OBJECT_FILE_EXTENSION);
	*arg++=reals_file_name;
#  endif

#  if defined(_WINDOWS_) && !defined (USE_WLINK) && !defined (USE_CLEANLINKER)
	*arg++="-Lc:\\Gnu\\lib";
#  endif

#  if defined (sparc) || (defined (I486) || defined (LINUX) && !defined (USE_WLINK) && !defined (OMF) && !defined (USE_CLEANLINKER))
	*arg++="-lm";
#  endif

#  ifdef OS2
#   ifdef OMF
	*arg++=",";
	*arg++=application_file_name;
	*arg++=",";
	*arg++=",";
#    ifndef NO_CLIB
	*arg++="e:\\emx\\lib\\gcc.lib";
	*arg++="e:\\emx\\lib\\st\\c.lib";
	*arg++="e:\\emx\\lib\\st\\c_app.lib";
	*arg++="e:\\emx\\lib\\st\\sys.lib";
#    endif

#    ifdef OS2
	for_l (library,first_library,library_next)
		*arg++=library->library_file_name;
#    endif

#    ifdef NO_CLIB
	strcpy (os2lib_file_name,clean_lib_path);
	strcat (os2lib_file_name,"\\os2.lib");
	*arg++=os2lib_file_name;
#    else
	*arg++="e:\\emx\\lib\\os2.lib";
#    endif

	*arg++=",";
	strcpy (clean_def_file_name,clean_lib_path);
	if (!window_application)
		strcat (clean_def_file_name,"\\text.def");
	else
		strcat (clean_def_file_name,"\\clean.def");
	*arg++ = clean_def_file_name;
	*arg++=",";
#   else
	*arg++="-lgcc";
	*arg++="-lst/c";
	*arg++="-lst/c_app";
	*arg++="-lst/c_import";
	*arg++="-lst/emx";
	*arg++="-lemx2";
	*arg++="-los2";
#   endif
#  else
#   ifndef USE_WLINK
#    if defined (_WINDOWS_) && !defined (USE_WLINK)
#     ifndef NO_CLIB
	*arg++="-lgcc";
	*arg++="-lc";
#     endif
#     if defined (USE_CLEANLINKER)
	strcpy (kernel32_file_name,"-l");
	strcat (kernel32_file_name,clean_lib_path);
	strcat (kernel32_file_name,"\\kernel_library");
	*arg++ = kernel32_file_name;
	strcpy (user32_file_name,"-l");
	strcat (user32_file_name,clean_lib_path);
	strcat (user32_file_name,"\\user_library");
	*arg++ = user32_file_name;
	strcpy (gdi32_file_name,"-l");
	strcat (gdi32_file_name,clean_lib_path);
	strcat (gdi32_file_name,"\\gdi_library");
	*arg++ = gdi32_file_name;
#     else
	*arg++="-lkernel32";
#     endif
#     ifndef NO_CLIB
	*arg++="-ladvapi32";
	*arg++="-luser32";
	*arg++="-lgcc";
#     endif
#    else	
	*arg++="-lc";
#     ifdef SOLARIS
	strcpy (crtn,start_up_file_name);
	replace_file_name_in_path (crtn,"crtn",OBJECT_FILE_EXTENSION);
	*arg++=crtn;
#     endif
#    endif
#   endif
#  endif

	*arg=NULL;

#  if defined (OS2) || defined (_WINDOWS_)
	{
	int r;

#   ifdef _WINDOWS_
#    ifdef GNU_C
	{
		int status;

		argv[0]=linker_file_name;
		r=spawnvp (P_WAIT,linker_file_name,argv);
		if (r>=0)
			r=wait (&status);

		return r>=0 && status==0;
	}
#    else
	r=spawnvp (P_WAIT,linker_file_name,argv);
#    endif
#   else
	r=dos_exec (linker_file_name,argv,1);
#   endif

	return r==0;
	}
#  else
	{
	int pid,r,status;

	pid=fork();
	if (pid<0)
		error ("Fork failed");
	
	if (!pid){
		execv (linker_file_name,argv);
		
		error ("Can't execute the linker");
	}
	
	r=wait_for_child (pid, "Linker",&status);

#   ifdef OPTIMISE_LINK
	if (!no_optimise_link)
		unlink (linker_output_object_file_name);
#   endif

	return r>=0 && status==0;
	}
#  endif
# endif
}
#endif

static void print_version (void)
{
	printf (
		"Clean Compiler 1.2.6/0.%03d/1.2\n"
		"Copyright (C) 1987-1997 University of Nijmegen and HILT - High Level Software Tools B.V.\n"
		,VERSION
	);
}

#if defined (MAKE_MPW_TOOL) && defined (NB)
static void print_help (void)
{
	print_version();

	printf (
	"\n"
	"Clean [option...] file [option...]\n"
	"    \"file\" is the name of the top-level .icl file\n"
	"    -i directory[,directory...]     # Search for imports in the named directories\n"
	"                                    # in addition to the directory in which \"file\" resides\n"
	"    -o directory                    # Look for and place all .abc and .objN files in directory\n"
	"    -p                              # Write progress information to diagnostic\n"
	"    -mc68020                        # Generate code for mc68020 (w/o 68881) instead of mc68000\n"
	"    -checkstack                     # Generate stack overflow checks\n"
	"    -checkindices                   # Check array indices\n"
	"    -funcmayfail                    # Function may fail is error\n"
	"    -varnotused                     # Variable not used is error\n"
	"    -funcnotused                    # Function not used is error\n"
	"    -help                           # Print out usage information\n"
	"    -h                              # Same as -help\n"
	"    -nowarn                         # Suppress warnings\n"
	"    -mv                             # Make verbose\n"
	"    -ms                             # Make silent\n"
	"    -O                              # Only make .objN file for main module\n"	
	"    -ABC                            # Only make .abc file for main module\n"
	"    -PABC                           # Only make .abc files for project\n"
	);
}
#endif

static void print_version_and_variables (void)
{
    fprintf (stderr, "clm - Clean make (version %s)\n", CLM_VERSION);
#if defined (I486) && defined (DOS)
    fprintf (stderr,"\tclean_lib_directory = \n\t\t%s\n", clean_lib_directory);
    fprintf (stderr,"\tassembler_file_name =\n\t\t%s\n", assembler_file_name);
    fprintf (stderr,"\tlinker_file_name =\n\t\t%s\n", linker_file_name);
    fprintf (stderr,"\tcrt0_file_name =\n\t\t%s\n", crt0_file_name);
    fprintf (stderr,"\tgcc_lib_directory_arg =\n\t\t%s\n", gcc_lib_directory_arg);
#else
    fprintf (stderr,"\tclean_lib_directory =\n\t\t%s\n", clean_lib_directory);
    fprintf (stderr,"\tclean_default_path_list =\n\t\t%s\n", clean_directory_list);
    fprintf (stderr,"\tclean_include_library_directory =\n\t\t%s\n", clean_include_library_directory);
	fprintf (stderr,"\tassembler_file_name =\n\t\t%s\n", assembler_file_name);
    fprintf (stderr,"\tlinker_file_name =\n\t\t%s\n", linker_file_name);
    fprintf (stderr,"\tcrt0_file_name =\n\t\t%s\n", crt0_file_name);
    fprintf (stderr,"\tld_args =\n\t\t%s\n", ld_args);
#endif

#ifdef M68000
    fprintf (stderr, "\tMcrt1_file_name =\n\t\t%s\n", Mcrt1_file_namee);
#endif
}

static void argument_error (void)
{
#if defined (MAKE_MPW_TOOL) && defined (NB)
	print_help();
#else
	printf ("Usage: clm [options] module_name [-o application_name]\n");
	printf ("path options: -I path -IL library -P paths\n");
	printf ("project options: -mv -ms -O -S -ABC -PO -PS -PABC -c -lt -lat -lset -pt -tst\n");
	printf ("application options: -h size -s size -b -sc -t -nt -gc -ngc -st -nst -nr\n"); 
#ifdef MARKING_GC
	printf ("application options: -gcm -gcc -gcf int -gci size\n");
#endif
	printf ("main module options: -w -d -sa -v -nw -nd -nsa -nv\n");
	printf ("linker options: -ns -no-opt-link\n");
#endif
	exit (1);
}

static long parse_size (register char *s)
{
	register int c;
	register long n;
	
	c=*s++;
	if (c<'0' || c>'9')
		error ("Digit expected in argument\n");
	
	n=c-'0';
	
	while (c=*s++,c>='0' && c<='9')
		n=n*10+(c-'0');
	
	if (c=='k' || c=='K'){
		c=*s++;
		n<<=10;
	} else if (c=='m' || c=='M'){
		c=*s++;
		n<<=20;
	}
	
	if (c!='\0')
		error ("Error in size");
	
	return n;
}

#ifdef MARKING_GC
static long parse_integer (register char *s)
{
	register int c;
	register long n;

	c=*s++;
	if (c<'0' || c>'9')
		error ("Digit expected in argument\n");

	n=c-'0';

	while (c=*s++,c>='0' && c<='9')
		n=n*10+(c-'0');

	if (c!='\0')
		error ("Error in integer");

	return n;
}
#endif

static void add_library (char *library_file_name)
{
	int file_name_length;
	struct library_list *new_library_file;
	
	file_name_length=strlen (library_file_name);
	new_library_file=memory_allocate (sizeof (struct library_list)+file_name_length+1);

	strcpy (new_library_file->library_file_name,library_file_name);
	new_library_file->library_next=NULL;
	
	if (first_library==NULL)
		first_library=new_library_file;
	else
		last_library->library_next=new_library_file;
	last_library=new_library_file;
}

static P_NODE add_ignored (char *ignored_name)
{
    P_NODE ignored_module;

    for_l (ignored_module,first_project_node,pro_next)
        if (!strcmp (ignored_name,ignored_module->pro_fname))
            break;
    
	if (ignored_module==NULL)
        ignored_module=add_project_node(ignored_name);

    ignored_module->pro_ignore_o=1;

    return ignored_module;
}

static void read_shared_library (char *shared_script_name)
{
	char script_file_name[PATH_NAME_STRING_SIZE];
	char file_name[PATH_NAME_STRING_SIZE];
	FILE *fp;
#ifdef _WINDOWS_
	FileTime time;
#endif

#if MACINTOSH
	if (!find_file (shared_script_name,".lo",script_file_name))
#else
	if (!find_file (shared_script_name,".lo",script_file_name,clean_lib_path
# ifdef _WINDOWS_
		,&time
# endif
	))
#endif
		error_s ("Can't find %s.lo",shared_script_name);

	if (!(fp=fopen (script_file_name,"r")))
		error_s ("Can't open %s.lo",shared_script_name);

	while (fgets (file_name,PATH_NAME_STRING_SIZE,fp)!=NULL && file_name[0]!='='){
		char *rest,*parsed_file_name;

		rest=file_name;
		while ((parsed_file_name=parse_word (&rest))!=NULL)
			add_library (parsed_file_name);
	}
	{
	P_NODE ignored_module;
	int adding_dependencies;
	
	ignored_module=NULL;
	adding_dependencies=0;
	
	while (fgets (file_name,PATH_NAME_STRING_SIZE,fp)!=NULL){
		char *rest,*parsed_file_name;

		rest=file_name;
		while ((parsed_file_name=parse_word (&rest))!=NULL){
			if (parsed_file_name[0]=='('){
				++parsed_file_name;
				adding_dependencies=1;
			}

			if (adding_dependencies){
				int length;

				length=strlen (parsed_file_name);
				if (parsed_file_name[length-1]==')'){
					--length;
					parsed_file_name[length]='\0';
					adding_dependencies=0;
				}

				if (length>0){
				    if (ignored_module==NULL)
						fprintf (stderr,"no ignored module specified for %s\n",parsed_file_name);
					else
						add_dependency (&ignored_module->pro_depend,parsed_file_name);
				}
			} else 
				ignored_module=add_ignored (file_name);
		}
	}
	}
}

#define option_on(mask)  clean_options|=(mask);  clean_options_mask|=(mask);
#define option_off(mask) clean_options&=~(mask); clean_options_mask|=(mask);

#ifdef __MWERKS__
#	include <SIOUX.h>
extern int ccommand(char ***);
#	include <Memory.h>
#endif

#ifdef applec
#	include <Memory.h>
#endif

#if defined (OS2) && defined (OMF)
static int add_resource_to_application (char *application_file_name)
{
	char *argv[8],**arg;
	char clean_resources_file_name[PATH_NAME_STRING_SIZE];
	int r;

	strcpy (clean_resources_file_name,clean_lib_path);
	strcat (clean_resources_file_name,"\\clean.res");

	arg=argv;
	*arg++="rc.exe";
	*arg++=clean_resources_file_name;
	*arg++=application_file_name;
	*arg=NULL;
	
	r=dos_exec ("rc.exe",argv,0);

	return r==0;
}
#endif

#if defined (__MWERKS__) && !defined (MAKE_MPW_TOOL)
int main (void)
{

	int argc;
	char *(arg_vector[32]),**argv;
#else
int main (int argc,char **argv)
{
#endif
	int arg_n;
	char *application_file_name,*main_module_name;

#if MACINTOSH || defined (POWER)
# if defined (__MWERKS__) && !defined (MAKE_MPW_TOOL)
	SetApplLimit (GetApplLimit() - 200*1024);
	argv=arg_vector;
	argc = ccommand (&argv);
# else
#  ifdef applec
			SetApplLimit (GetApplLimit() - 200*1024);
#  endif
#  ifdef _LBFSIZ
	setvbuf (stdout,NULL,_IOLBF,_LBFSIZ);
#  else
	setvbuf (stdout,NULL,_IOLBF,256);
#  endif
# endif
#endif

#if defined (__MWERKS__) && !defined (MAKE_MPW_TOOL)
	SIOUXSettings.showstatusline=0;
#endif

#ifdef OS2
	clm_file_name=argv[0];
#endif

#if MACINTOSH && !defined (G_POWER)
	OBJECT_FILE_EXTENSION=".obj0";
#endif

#ifdef DOS
	{
		int length;
	
		strcpy (clean_lib_directory,argv[0]);

		length=strlen (clean_lib_directory);
		while (length>0){
			--length;
			if (clean_lib_directory[length]=='/' ||
				clean_lib_directory[length]=='\\')
			{
				clean_lib_directory[length]=0;
				break;
			}
		}
		
		if (length==0)
			error ("argv[0] doesn't contain path name");
		strcat (clean_lib_directory,"/lib");
	}	
#endif

    if (argc==2 && strcmp (argv[1],"-V")==0){
        print_version_and_variables();
        exit (0);
    }

	if (argc<2 || argv[argc-1][0]=='-')
		argument_error();
	
	heap_size=2048<<10;
	ab_stack_size=512<<10;
	flags=8;
#ifdef MARKING_GC
	initial_heap_size=100<10;
	heap_size_multiple=20<<8;
#endif
 	
	remove_symbol_table=1;
	check_stack_overflow=0;
	check_indices=0;
#ifdef OPTIMISE_LINK
	no_optimise_link=0;
#endif
	stack_trace=0;

#ifdef OS2
	window_application=0;
#endif

	cocl_redirect_stdout=NULL;
	cocl_redirect_stderr=NULL;
 
	first_library=NULL;
	last_library=NULL;

	get_paths();
	
#if defined (MAKE_MPW_TOOL) && defined (NB)
	main_module_name=NULL;

	for (arg_n=1; arg_n<argc; ++arg_n){
#else
	for (arg_n=1; arg_n<argc && argv[arg_n][0]=='-'; ++arg_n){
#endif
		char *argument,*s;
		
		argument=argv[arg_n];
		
#if defined (MAKE_MPW_TOOL) && defined (NB)
		if (argument[0]!='-'){
			if (main_module_name!=NULL)
				error_s ("Error in arguments: only one file name allowed: %s",argument);	
			
			main_module_name=argument;
			continue;
		}
#endif
		
		s=argument+1;
		
		/* driver options */
		
		if (!strcmp (s,"mv"))
			verbose=1;
		else if (!strcmp (s,"ms"))
			silent=1;
		else if (!strcmp (s,"c")){
			single_module=1;
			syntax_check=1;
		} else if (!strcmp (s,"ABC")){
			single_module=1;
			only_abc_files=1;
		} else if (!strcmp (s,"S")){
			single_module=1;
			only_s_files=1;
		} else if (!strcmp (s,"O")){
			single_module=1;
			only_o_files=1;
		} else if (!strcmp (s,"PABC"))
			only_abc_files=1;
		else if (!strcmp (s,"PS"))
			only_s_files=1;
		else if (!strcmp (s,"PO"))
			only_o_files=1;
		else if (!strcmp (s,"ns"))
			remove_symbol_table=0;
#ifdef OPTIMISE_LINK
		else if (!strcmp (s,"no-opt-link"))
			no_optimise_link=1;
#endif

		/* application options */
		
#if ! (defined (MAKE_MPW_TOOL) && defined (NB))
		else if (!strcmp (s,"h")){
			++arg_n;
			if (arg_n>=argc)
				error ("Heap size missing\n");
			heap_size=parse_size (argv[arg_n]);
		}
#endif
		else if (!strcmp (s,"s")){
			++arg_n;
			if (arg_n>=argc)
				error ("Stack size missing\n");
			ab_stack_size=parse_size (argv[arg_n]);
		} else if (!strcmp (s,"b"))
			flags |= 1;
		else if (!strcmp (s,"sc"))
			flags &= ~1;
		else if (!strcmp (s,"t"))
			flags |= 8;
		else if (!strcmp (s,"nt"))
			flags &= ~8;
		else if (!strcmp (s,"gc"))
			flags |= 2;
		else if (!strcmp (s,"ngc"))
			flags &= ~2;
		else if (!strcmp (s,"st"))
			flags |= 4;
		else if (!strcmp (s,"nst"))
			flags &= ~4;
		else if (!strcmp (s,"nr"))
			flags |= 16;
#ifdef MARKING_GC
		else if (!strcmp (s,"gcm"))
			flags |= 64;
		else if (!strcmp (s,"gcc"))
			flags &= ~64;
		else if (!strcmp (s,"gci")){
			++arg_n;
			if (arg_n>=argc)
				error ("Initial heap size missing\n");
			initial_heap_size=parse_size (argv[arg_n]);
		} else if (!strcmp (s,"gcf")){
			++arg_n;
			if (arg_n>=argc)
				error ("Next heap size factor missing\n");
			heap_size_multiple=parse_integer (argv[arg_n])<<8;
		}
#endif
		/* clean options */
		
		else if (!strcmp (s,"v")){
			option_on (VERBOSE_MASK);
		} else if (!strcmp (s,"nv")){
			option_off (VERBOSE_MASK);
		} else if (!strcmp (s,"w")){
			option_on (WARNING_MASK);
		} else if (!strcmp (s,"nw")){
			option_off (WARNING_MASK);
		} else if (!strcmp (s,"d")){
			option_on (DEBUG_MASK);
		} else if (!strcmp (s,"nd")){
			option_off (DEBUG_MASK);
		} else if (!strcmp (s,"sa")){
			option_on (STRICTNESS_ANALYSIS_MASK);
		} else if (!strcmp (s,"nsa")){
			option_off (STRICTNESS_ANALYSIS_MASK);
		} else if (!strcmp (s,"lt")){
			option_on (LIST_TYPES_MASK);
		} else if (!strcmp (s,"nlt")){
			option_off (LIST_TYPES_MASK);
		} else if (!strcmp (s,"lat")){
			option_on (LIST_ALL_TYPES_MASK);
		} else if (!strcmp (s,"nlat")){
			option_off (LIST_ALL_TYPES_MASK);
		} else if (!strcmp (s,"lset")){
			list_strict_export_types=1;
        } else if (!strcmp (s,"ou")){
            option_on (REUSE_UNIQUE_NODES);
        } else if (!strcmp (s,"nou")){
            option_off (REUSE_UNIQUE_NODES);
/*
        } else if (!strcmp (s,"pm")){
            option_on (MEMORY_PROFILE);
        } else if (!strcmp (s,"npm")){
            option_off (MEMORY_PROFILE);
*/
        } else if (!strcmp (s,"pt")){
            option_off (NO_TIME_PROFILE_MASK);
			stack_trace=0;
        } else if (!strcmp (s,"npt")){
            option_on (NO_TIME_PROFILE_MASK);
        } else if (!strcmp (s,"tst")){
            option_off (NO_TIME_PROFILE_MASK);
			stack_trace=1;
        } else if (strcmp (s, "RE") == 0 || strcmp (s, "RAE") == 0){
            cocl_redirect_stderr_option = argument;
            if (++arg_n < argc)
                cocl_redirect_stderr = argv [arg_n];
            else
                error ("file name expected after -RE");
        } else if (strcmp (s, "RO") == 0 || strcmp (s, "RAO") == 0){
            cocl_redirect_stdout_option = argument;
            if (++arg_n < argc)
                cocl_redirect_stdout = argv [arg_n];
            else
                error ("file name expected after -RO");
		} else if (!strcmp (s,"funcmayfail")){
			funcmayfail_error=1;
		} else if (!strcmp (s,"varnotused")){
			varnotused_error=1;
		} else if (!strcmp (s,"funcnotused")){
			funcnotused_error=1;
		} else if (!strcmp (s,"nowarn")){
			nowarn=1;
#ifndef OS2
		} else if (!strcmp (s,"p")){
			progress=1;
#endif
		/* code generator options */
		} else if (!strcmp (s,"ci")){
			check_indices=1;
#if defined (MAKE_MPW_TOOL) && defined (NB)
		} else if (!strcmp (s,"checkstack")){
			check_stack_overflow=1;
		} else if (!strcmp (s,"checkindices")){
			check_indices=1;
		} else if (!strcmp (s,"mc68020")){
			generate_mc68020_code=1;
			OBJECT_FILE_EXTENSION=".obj1";
		} else if (!strcmp (s,"h") || !strcmp (s,"help")){
			show_help=1;
#endif
		} else if (!strcmp (s,"l")){
			++arg_n;
			if (arg_n>=argc)
				error ("Library name missing\n");
			add_library (argv[arg_n]);
		} else if (!strcmp (s,"sl")){
			++arg_n;
			if (arg_n>=argc)
			   error ("Shared library name missing\n");
			read_shared_library (argv[arg_n]);
#ifdef OS2
		} else if (!strcmp (s,"p")) {
			window_application=1;
#endif
		} else if (!strcmp (s,"P")){
			++arg_n;
			if (arg_n>=argc)
			   error ("Path list missing\n");
			clean_path_list=strcpy (clean_path_list_copy,argv[arg_n]);
		} else if (!strcmp (s,"I")){
			++arg_n;
			if (arg_n>=argc)
			   error ("Path missing\n");
			if (clean_path_list[0]!='\0')
				strcat (clean_path_list,":");
			strcat (clean_path_list,argv[arg_n]);
		} else if (!strcmp (s,"IL")){
			++arg_n;
			if (arg_n>=argc)
			   error ("Path missing\n");
			if (clean_path_list[0]!='\0')
				strcat (clean_path_list,":");
			strcat (clean_path_list,clean_include_library_directory);
			strcat (clean_path_list,"/");
			strcat (clean_path_list,argv[arg_n]);
#if defined (MAKE_MPW_TOOL) && defined (NB)
		} else if (!strcmp (s,"o")){
			++arg_n;
			if (arg_n>=argc)
			   error ("Output path missing\n");
			clean_abc_path=clean_o_path=argv[arg_n];
#endif
		} else
			error_s ("Unknown option: %s",argument);
	}

#if defined (MAKE_MPW_TOOL) && defined (NB)
	if (main_module_name==NULL)
		argument_error();
	
	if (show_help)
		print_help();
	
	{
		int l;
		
		l=strlen (main_module_name);
		if (l>4 && !strcmp (&main_module_name[l-4],".icl")){
			main_module_name[l-4]='\0';
			l-=4;
		}
		
		while (l>=0 && main_module_name[l]!=':')
			--l;
		
		if (l>=0){
			main_module_path=memory_allocate (l+2);
			main_module_name[l]=0;
			
			strcpy (main_module_path,main_module_name);
			strcat (main_module_path,":");
			
			main_module_name=&main_module_name[l+1];
		} else
			main_module_path=NULL;
	}

	application_file_name=NULL;
#else	
	main_module_name=argv[arg_n];

	if (arg_n==argc-3){
		if (strcmp (argv[arg_n+1],"-o")!=0)
			argument_error();
		application_file_name=argv[arg_n+2];
	} else {
# ifdef OS2
		int length;
		
		length=strlen (main_module_name);
		
		application_file_name=memory_allocate (length+5);
		
		strcpy (application_file_name,main_module_name);
		strcat (application_file_name,".exe");
# else
		application_file_name=NULL;
# endif
		if (arg_n!=argc-1)
			argument_error();
	}
#endif

#if MACINTOSH
	initialise_path_list();
#endif

	if (progress)
		print_version();

	if (first_project_node==NULL)
		main_project_node=add_project_node (main_module_name);
	else {
		P_NODE old_first_project_node,old_last_project_node;
		
		old_first_project_node=first_project_node;
		old_last_project_node=last_project_node;

		first_project_node=NULL;

		main_project_node=add_project_node (main_module_name);

		main_project_node->pro_next=old_first_project_node;
		last_project_node=old_last_project_node;
	}


#ifdef NB
	{
	P_NODE _system_pnode;
	
	_system_pnode=add_project_node ("_system");
#endif

	
	if (make_project_to_abc_files (first_project_node)
#ifdef NB
		&& make_project_to_abc_files (_system_pnode)
#endif
	){
#ifdef CACHING_COMPILER
		if (compiler_started){
			stop_compiler();
			compiler_started=0;
		}
#endif

		if (only_abc_files || syntax_check)
			return 0;
		
		if (only_s_files)
			return make_project_to_s_files();
		
		if (make_project_to_o_files()){
#if ! MACINTOSH
			char *options_file_name;
#endif
			if (only_o_files)
				return 0;

#ifdef USE_TOOLSERVER
			if (!link_project_toolserver (first_project_node,NULL,application_file_name))
				return 1;

			add_stack_and_size_resource_to_application (application_file_name);
			return 0;			
#else
# ifndef MAKE_MPW_TOOL
			if (create_options_file (&options_file_name)){
				if (!link_project (first_project_node,options_file_name,application_file_name)){
					unlink (options_file_name);
					return 1;
				}
 				unlink (options_file_name);
#  if defined (OS2) && defined (OMF)
				if (window_application && !add_resource_to_application (application_file_name))
					return 1;
#  endif
				return 0;
			}
# endif
#endif
		}
	}

#ifdef NB
	}
#endif
	
#ifdef CACHING_COMPILER
	stop_compiler ();
#endif

	return 1;
}

