/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
 *
 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>


#include "Options.h"

 __attribute__((noreturn))
void throwf(const char* format, ...) 
{
	va_list	list;
	char*	p;
	va_start(list, format);
	vasprintf(&p, format, list);
	va_end(list);
	
	const char*	t = p;
	throw t;
}


Options::Options(int argc, const char* argv[])
 : fOutputFile("a.out"), fArchitecture(CPU_TYPE_POWERPC64), fOutputKind(kDynamicExecutable), fBindAtLoad(false),
	fStripLocalSymbols(false),  fKeepPrivateExterns(false),
	fInterposable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), fNameSpace(kTwoLevelNameSpace),
	fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fEntryName("start"), fBaseAddress(0),
	fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives),
	fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), fPICTreatment(kPICError),
	fWeakReferenceMismatchTreatment(kWeakReferenceMismatchError), 
	fUmbrellaName(NULL), fInitFunctionName(NULL), fZeroPageSize(0x1000), fStackSize(0), fStackAddr(0), fMinimumHeaderPad(0),
	fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false), fVerbose(false)
{
	this->parsePreCommandLineEnvironmentSettings();
	this->parse(argc, argv);
	this->parsePostCommandLineEnvironmentSettings();
	this->checkIllegalOptionCombinations();
}

Options::~Options()
{
}


ObjectFile::ReaderOptions& Options::readerOptions()
{
	return fReaderOptions;
}

cpu_type_t Options::architecture()
{
	return fArchitecture;
}


const char*	Options::getOutputFilePath()
{
	return fOutputFile;
}


std::vector<Options::FileInfo>& Options::getInputFiles()
{
	return fInputFiles;
}

Options::OutputKind	Options::outputKind()
{
	return fOutputKind;
}

bool Options::stripLocalSymbols()
{
	return fStripLocalSymbols;
}

bool Options::stripDebugInfo()
{
	return fReaderOptions.fStripDebugInfo;
}

bool Options::bindAtLoad()
{
	return fBindAtLoad;
}

bool Options::fullyLoadArchives()
{
	return fReaderOptions.fFullyLoadArchives;
}

Options::NameSpace Options::nameSpace()
{
	return fNameSpace;
}

const char*	Options::installPath()
{
	if ( fDylibInstallName != NULL ) 
		return fDylibInstallName;
	else
		return fOutputFile;
}

uint32_t Options::currentVersion()
{
	return fDylibCurrentVersion;
}

uint32_t Options::compatibilityVersion()
{
	return fDylibCompatVersion;
}

const char*	Options::entryName()
{
	return fEntryName;
}

uint64_t Options::baseAddress()
{
	return fBaseAddress;
}

bool Options::keepPrivateExterns()
{
	return fKeepPrivateExterns;
}

bool Options::interposable()
{
	return fInterposable;
}

bool Options::ignoreOtherArchInputFiles()
{
	return fIgnoreOtherArchFiles;
}

bool Options::forceCpuSubtypeAll()
{
	return fForceSubtypeAll;
}

bool Options::traceDylibs()
{
	return fReaderOptions.fTraceDylibs;
}

bool Options::traceArchives()
{
	return fReaderOptions.fTraceArchives;
}

Options::UndefinedTreatment Options::undefinedTreatment()
{
	return fUndefinedTreatment;
}

Options::WeakReferenceMismatchTreatment	Options::weakReferenceMismatchTreatment()
{
	return fWeakReferenceMismatchTreatment;
}

const char* Options::umbrellaName()
{
	return fUmbrellaName;
}

uint64_t Options::zeroPageSize()
{
	return fZeroPageSize;
}

bool Options::hasCustomStack()
{
	return (fStackSize != 0);
}
	
uint64_t Options::customStackSize()
{
	return fStackSize;
}

uint64_t Options::customStackAddr()
{
	return fStackAddr;
}

std::vector<const char*>& Options::initialUndefines()
{
	return fInitialUndefines;
}

const char*	Options::initFunctionName()
{
	return fInitFunctionName;
}

bool Options::hasExportRestrictList()
{
	return (fExportMode != kExportDefault);
}

uint32_t Options::minimumHeaderPad()
{
	return fMinimumHeaderPad;
}

std::vector<Options::ExtraSection>&	Options::extraSections()
{
	return fExtraSections;
}

std::vector<Options::SectionAlignment>&	Options::sectionAlignments()
{
	return fSectionAlignments;
}


Options::CommonsMode Options::commonsMode()
{
	return fCommonsMode;
}

bool Options::warnCommons()
{
	return fWarnCommons;
}

bool Options::shouldExport(const char* symbolName)
{
	switch (fExportMode) {
		case kExportSome:
			return ( fExportSymbols.find(symbolName) != fExportSymbols.end() );
		case kDontExportSome:
			return ( fDontExportSymbols.find(symbolName) == fDontExportSymbols.end() );
		case kExportDefault:
			return true;
	}
	throw "internal error";
}


void Options::parseArch(const char* architecture)
{
	if ( architecture == NULL )
		throw "-arch must be followed by an architecture string";
	if ( strcmp(architecture, "ppc") == 0 )
		fArchitecture = CPU_TYPE_POWERPC;
	else if ( strcmp(architecture, "ppc64") == 0 )
		fArchitecture = CPU_TYPE_POWERPC64;
	else if ( strcmp(architecture, "i386") == 0 )
		fArchitecture = CPU_TYPE_I386;
	else 
		throw "-arch followed by unknown architecture name";
}

bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result)
{
	struct stat statBuffer;
	char possiblePath[strlen(dir)+strlen(rootName)+20];
	sprintf(possiblePath, format,  dir, rootName);
	if ( stat(possiblePath, &statBuffer) == 0 ) {
		result.path = strdup(possiblePath);
		result.fileLen = statBuffer.st_size;
		return true;
	}
	return false;
} 


Options::FileInfo Options::findLibrary(const char* rootName)
{
	FileInfo result;
	const int rootNameLen = strlen(rootName);
	// if rootName ends in .o there is no .a vs .dylib choice
	if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) {
		for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) {
			const char* dir = *it;
			if ( checkForFile("%s/%s", dir, rootName, result) )
				return result;
		}
	}
	else {
		bool lookForDylibs = ( fOutputKind != Options::kDyld);
		switch ( fLibrarySearchMode ) {
			case kSearchAllDirsForDylibsThenAllDirsForArchives:
				// first look in all directories for just for dylibs
				if ( lookForDylibs ) {
					for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) {
						const char* dir = *it;
						if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) )
							return result;
					}
				}
				// next look in all directories for just for archives
				for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) {
					const char* dir = *it;
					if ( checkForFile("%s/lib%s.a", dir, rootName, result) )
						return result;
				}
				break;

			case kSearchDylibAndArchiveInEachDir:
				// look in each directory for just for a dylib then for an archive
				for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) {
					const char* dir = *it;
					if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) )
						return result;
					if ( checkForFile("%s/lib%s.a", dir, rootName, result) )
						return result;
				}
				break;
		}
	}
	throwf("library not found for -l%s", rootName);
}


Options::FileInfo Options::findFramework(const char* rootName)
{
	struct stat statBuffer;
	const int rootNameLen = strlen(rootName);
	for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) {
		const char* dir = *it;
		char possiblePath[strlen(dir)+2*rootNameLen+20];
		strcpy(possiblePath, dir);
		strcat(possiblePath, "/");
		strcat(possiblePath, rootName);
		strcat(possiblePath, ".framework/");
		strcat(possiblePath, rootName);
		if ( stat(possiblePath, &statBuffer) == 0 ) {
			FileInfo result;
			result.path = strdup(possiblePath);
			result.fileLen = statBuffer.st_size;
			return result;
		}
	}
	throwf("framework not found %s", rootName);
}

Options::FileInfo Options::findFile(const char* path)
{
	FileInfo result;
	struct stat statBuffer;
	
	// if absolute path and not a .o file, the use SDK prefix
	if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) {
		const int pathLen = strlen(path);
		for (std::vector<const char*>::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) {
			const char* sdkPathDir = *it;
			const int sdkPathDirLen = strlen(sdkPathDir);
			char possiblePath[sdkPathDirLen+pathLen+4];
			strcpy(possiblePath, sdkPathDir);
			if ( possiblePath[sdkPathDirLen-1] == '/' )
				possiblePath[sdkPathDirLen-1] = '\0';
			strcat(possiblePath, path);
			if ( stat(possiblePath, &statBuffer) == 0 ) {
				result.path = strdup(possiblePath);
				result.fileLen = statBuffer.st_size;
				return result;
			}
		}
	}
	// try raw path
	if ( stat(path, &statBuffer) == 0 ) {
		result.path = strdup(path);
		result.fileLen = statBuffer.st_size;
		return result;
	}
	// not found
	throwf("file not found: %s", path);
}


void Options::loadFileList(const char* fileOfPaths)
{
	FILE* file = fopen(fileOfPaths, "r");
	if ( file == NULL ) 
		throwf("-filelist file not found: %s\n", fileOfPaths);
	
	char path[1024];
	while ( fgets(path, 1024, file) != NULL ) {
		path[1023] = '\0';
		char* eol = strchr(path, '\n');
		if ( eol != NULL )
			*eol = '\0';
			
		fInputFiles.push_back(findFile(path));
	}
	fclose(file);
}


void Options::loadExportFile(const char* fileOfExports, const char* option, NameSet& set)
{
	// read in whole file
	int fd = ::open(fileOfExports, O_RDONLY, 0);
	if ( fd == -1 )
		throwf("can't open %s file: %s", option, fileOfExports);
	struct stat stat_buf;
	::fstat(fd, &stat_buf);
	char* p = (char*)malloc(stat_buf.st_size);
	if ( p == NULL )
		throwf("can't process %s file: %s", option, fileOfExports);
	
	if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
		throwf("can't read %s file: %s", option, fileOfExports);
	
	::close(fd);
	
	// parse into symbols and add to hash_set
	char * const end = &p[stat_buf.st_size];
	enum { lineStart, inSymbol, inComment } state = lineStart;
	char* symbolStart = NULL;
	for (char* s = p; s < end; ++s ) {
		switch ( state ) {
			case lineStart:
				if ( *s =='#' ) {
					state = inComment;
				}
				else if ( !isspace(*s) ) {
					state = inSymbol;
					symbolStart = s;
				}
				break;
			case inSymbol:
				if ( *s == '\n' ) {
					*s = '\0';
					// removing any trailing spaces
					char* last = s-1;
					while ( isspace(*last) ) {
						*last = '\0';
						--last;
					}
					set.insert(symbolStart);
					symbolStart = NULL;
					state = lineStart;
				}
				break;
			case inComment:
				if ( *s == '\n' )
					state = lineStart;
				break;
		}
	}
	// Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table
}

void Options::setUndefinedTreatment(const char* treatment)
{
	if ( treatment == NULL ) 
		throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]";

	if ( strcmp(treatment, "warning") == 0 )
		fUndefinedTreatment = kUndefinedWarning;
	else if ( strcmp(treatment, "error") == 0 )
		fUndefinedTreatment = kUndefinedError;
	else if ( strcmp(treatment, "suppress") == 0 )
		fUndefinedTreatment = kUndefinedSuppress;
	else if ( strcmp(treatment, "dynamic_lookup") == 0 )
		fUndefinedTreatment = kUndefinedDynamicLookup;
	else
		throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]";
}

void Options::setReadOnlyRelocTreatment(const char* treatment)
{
	if ( treatment == NULL ) 
		throw "-read_only_relocs missing [ warning | error | suppress ]";

	if ( strcmp(treatment, "warning") == 0 )
		throw "-read_only_relocs warning not supported";
	else if ( strcmp(treatment, "suppress") == 0 )
		throw "-read_only_relocs suppress not supported";
	else if ( strcmp(treatment, "error") != 0 )
		throw "invalid option to -read_only_relocs [ warning | error | suppress | dynamic_lookup ]";
}

void Options::setPICTreatment(const char* treatment)
{
	if ( treatment == NULL ) 
		throw "-sect_diff_relocs missing [ warning | error | suppress ]";

	if ( strcmp(treatment, "warning") == 0 )
		fPICTreatment = kPICWarning;
	else if ( strcmp(treatment, "error") == 0 )
		fPICTreatment = kPICError;
	else if ( strcmp(treatment, "suppress") == 0 )
		fPICTreatment = kPICSuppress;
	else
		throw "invalid option to -sect_diff_relocs [ warning | error | suppress ]";
}

void Options::setWeakReferenceMismatchTreatment(const char* treatment)
{
	if ( treatment == NULL ) 
		throw "-weak_reference_mismatches missing [ error | weak | non-weak ]";

	if ( strcmp(treatment, "error") == 0 )
		fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError;
	else if ( strcmp(treatment, "weak") == 0 )
		fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak;
	else if ( strcmp(treatment, "non-weak") == 0 )
		fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak;
	else
		throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]";
}

Options::CommonsMode Options::parseCommonsTreatment(const char* mode)
{
	if ( mode == NULL ) 
		throw "-commons missing [ ignore_dylibs | use_dylibs | error ]";

	if ( strcmp(mode, "ignore_dylibs") == 0 )
		return kCommonsIgnoreDylibs;
	else if ( strcmp(mode, "use_dylibs") == 0 )
		return kCommonsOverriddenByDylibs;
	else if ( strcmp(mode, "error") == 0 )
		return kCommonsConflictsDylibsError;
	else
		throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]";
}


void Options::setDylibInstallNameOverride(const char* paths)
{


}

void Options::setExecutablePath(const char* path)
{


}




uint64_t Options::parseAddress(const char* addr)
{
	char* endptr;
	uint64_t result = strtoull(addr, &endptr, 16);
	return result;
}



//
// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz 
//
//
uint32_t Options::parseVersionNumber(const char* versionString)
{
	unsigned long x = 0;
	unsigned long y = 0;
	unsigned long z = 0;
	char* end;
	x = strtoul(versionString, &end, 10);
	if ( *end == '.' ) {
		y = strtoul(&end[1], &end, 10);
		if ( *end == '.' ) {
			z = strtoul(&end[1], &end, 10);
		}
	}
	if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) )
		throwf("malformed version number: %s", versionString);

	return (x << 16) | ( y << 8 ) | z;
}

void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path)
{
	fprintf(stderr, "ld64: warning -sectorder not yet supported for 64-bit code\n");
}

void Options::addSection(const char* segment, const char* section, const char* path)
{
	if ( strlen(segment) > 16 )
		throw "-seccreate segment name max 16 chars";
	if ( strlen(section) > 16 )
		throw "-seccreate section name max 16 chars";

	// read in whole file
	int fd = ::open(path, O_RDONLY, 0);
	if ( fd == -1 )
		throwf("can't open -sectcreate file: %s", path);
	struct stat stat_buf;
	::fstat(fd, &stat_buf);
	char* p = (char*)malloc(stat_buf.st_size);
	if ( p == NULL )
		throwf("can't process -sectcreate file: %s", path);
	if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size )
		throwf("can't read -sectcreate file: %s", path);
	::close(fd);
	
	// record section to create
	ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size };
	fExtraSections.push_back(info);
}

void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr)
{
	if ( strlen(segment) > 16 )
		throw "-sectalign segment name max 16 chars";
	if ( strlen(section) > 16 )
		throw "-sectalign section name max 16 chars";

	char* endptr;
	unsigned long value = strtoul(alignmentStr, &endptr, 16);
	if ( *endptr != '\0')
		throw "argument for -sectalign is not a hexadecimal number";
	if ( value > 0x8000 )
		throw "argument for -sectalign must be less than or equal to 0x8000";
	uint8_t alignment = 0;
	for(unsigned long x=value; x != 1; x >>= 1)
		++alignment;
	if ( (unsigned long)(1 << alignment) != value )
		throw "argument for -sectalign is not a power of two";

	SectionAlignment info = { segment, section, alignment };
	fSectionAlignments.push_back(info);
}


void Options::parse(int argc, const char* argv[])
{
	// pass one builds search list from -L and -F options
	this->buildSearchPaths(argc, argv);
	
	// pass two parse all other options
	for(int i=1; i < argc; ++i) {
		const char* arg = argv[i];
		
		if ( arg[0] == '-' ) {
			if ( (arg[1] == 'L') || (arg[1] == 'F') ) {
				// previously handled by buildSearchPaths()
			}
			else if ( strcmp(arg, "-arch") == 0 ) {
				parseArch(argv[++i]);
			}
			else if ( strcmp(arg, "-dynamic") == 0 ) {
				// default
			}
			else if ( strcmp(arg, "-static") == 0 ) {
				fOutputKind = kStaticExecutable;
			}
			else if ( strcmp(arg, "-dylib") == 0 ) {
				fOutputKind = kDynamicLibrary;
			}
			else if ( strcmp(arg, "-bundle") == 0 ) {
				fOutputKind = kDynamicBundle;
			}
			else if ( strcmp(arg, "-dylinker") == 0 ) {
				fOutputKind = kDyld;
			}
			else if ( strcmp(arg, "-execute") == 0 ) {
				if ( fOutputKind != kStaticExecutable )
					fOutputKind = kDynamicExecutable;
			}
			else if ( strcmp(arg, "-r") == 0 ) {
				fOutputKind = kObjectFile;
			}
			else if ( strcmp(arg, "-o") == 0 ) {
				fOutputFile = argv[++i];
			}
			else if ( arg[1] == 'l' ) {
				fInputFiles.push_back(findLibrary(&arg[2]));
			}
			else if ( strcmp(arg, "-weak-l") == 0 ) {
				FileInfo info = findLibrary(&arg[2]);
				info.options.fWeakImport = true;
				fInputFiles.push_back(info);
			}
			else if ( strcmp(arg, "-bind_at_load") == 0 ) {
				fBindAtLoad = true;
			}
			else if ( strcmp(arg, "-twolevel_namespace") == 0 ) {
				fNameSpace = kTwoLevelNameSpace;
			}
			else if ( strcmp(arg, "-flat_namespace") == 0 ) {
				fNameSpace = kFlatNameSpace;
			}
			else if ( strcmp(arg, "-force_flat_namespace") == 0 ) {
				fNameSpace = kForceFlatNameSpace;
			}
			else if ( strcmp(arg, "-all_load") == 0 ) {
				fReaderOptions.fFullyLoadArchives = true;
			}
			else if ( strcmp(arg, "-ObjC") == 0 ) {
				fReaderOptions.fLoadObjcClassesInArchives = true;
			}
			else if ( strcmp(arg, "-dylib_compatibility_version") == 0 ) {
				fDylibCompatVersion = parseVersionNumber(argv[++i]);
			}
			else if ( strcmp(arg, "-dylib_current_version") == 0 ) {
				fDylibCurrentVersion = parseVersionNumber(argv[++i]);
			}
			else if ( strcmp(arg, "-sectorder") == 0 ) {
				parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]);
				i += 3;
			}
			else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) {
				addSection(argv[i+1], argv[i+2], argv[i+3]);
				i += 3;
			}
			else if ( (strcmp(arg, "-dylib_install_name") == 0) || (strcmp(arg, "-dylinker_install_name") == 0) ) {
				fDylibInstallName = argv[++i];
			}
			else if ( strcmp(arg, "-seg1addr") == 0 ) {
				fBaseAddress = parseAddress(argv[++i]);
			}
			else if ( strcmp(arg, "-e") == 0 ) {
				fEntryName = argv[++i];
			}
			else if ( strcmp(arg, "-filelist") == 0 ) {
				 loadFileList(argv[++i]);
			}
			else if ( strcmp(arg, "-keep_private_externs") == 0 ) {
				 fKeepPrivateExterns = true;
			}
			else if ( strcmp(arg, "-final_output") == 0 ) {
				 ++i;
				// ignore for now
			}
			else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) {
				 fInterposable = true;
			}
			else if ( strcmp(arg, "-single_module") == 0 ) {
				 fInterposable = false;
			}
			else if ( strcmp(arg, "-exported_symbols_list") == 0 ) {
				if ( fExportMode == kDontExportSome )
					throw "can't use -exported_symbols_list and -unexported_symbols_list";
				fExportMode = kExportSome;
				loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols);
			}
			else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) {
				if ( fExportMode == kExportSome )
					throw "can't use -exported_symbols_list and -unexported_symbols_list";
				fExportMode = kDontExportSome;
				loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols);
			}
			else if ( strcmp(arg, "-no_arch_warnings") == 0 ) {
				 fIgnoreOtherArchFiles = true;
			}
			else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) {
				 fForceSubtypeAll = true;
			}
			else if ( strcmp(arg, "-weak_library") == 0 ) {
				FileInfo info = findFile(argv[++i]);
				info.options.fWeakImport = true;
				fInputFiles.push_back(info);
			}
			else if ( strcmp(arg, "-framework") == 0 ) {
				fInputFiles.push_back(findFramework(argv[++i]));
			}
			else if ( strcmp(arg, "-weak_framework") == 0 ) {
				FileInfo info = findFramework(argv[++i]);
				info.options.fWeakImport = true;
				fInputFiles.push_back(info);
			}
			else if ( strcmp(arg, "-search_paths_first") == 0 ) {
				fLibrarySearchMode = kSearchDylibAndArchiveInEachDir;
			}
			else if ( strcmp(arg, "-undefined") == 0 ) {
				 setUndefinedTreatment(argv[++i]);
			}
			else if ( strcmp(arg, "-arch_multiple") == 0 ) {
				 fMessagesPrefixedWithArchitecture = true;
			}
			else if ( strcmp(arg, "-read_only_relocs") == 0 ) {
				 setReadOnlyRelocTreatment(argv[++i]);
			}
			else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) {
				 setPICTreatment(argv[++i]);
			}
			else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) {
				 setWeakReferenceMismatchTreatment(argv[++i]);
			}
			else if ( strcmp(arg, "-prebind") == 0 ) {
				  // FIX FIX
			}
			else if ( strcmp(arg, "-noprebind") == 0 ) {
				  // FIX FIX
			}
			else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) {
				  // FIX FIX
			}
			else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) {
				  // FIX FIX
			}
			else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) {
				  // FIX FIX
			}
			else if ( strcmp(arg, "-nofixprebinding") == 0 ) {
				  // FIX FIX
			}
			else if ( strcmp(arg, "-dylib_file") == 0 ) {
				 setDylibInstallNameOverride(argv[++i]);
			}
			else if ( strcmp(arg, "-executable_path") == 0 ) {
				 setExecutablePath(argv[++i]);
			}
			else if ( strcmp(arg, "-segalign") == 0 ) {
				// FIX FIX
				++i; 
			}
			else if ( strcmp(arg, "-segaddr") == 0 ) {
				// FIX FIX
				i += 2; 
			}
			else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) {
				// FIX FIX
				++i; 
			}
			else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) {
				// FIX FIX
				++i; 
			}
			else if ( strcmp(arg, "-seg_addr_table") == 0 ) {
				// FIX FIX
				++i; 
			}
			else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) {
				// FIX FIX
				++i; 
			}
			else if ( strcmp(arg, "-segprot") == 0 ) {
				// FIX FIX
				i += 3; 
			}
			else if ( strcmp(arg, "-pagezero_size") == 0 ) {
				fZeroPageSize = parseAddress(argv[++i]);
				fZeroPageSize &= (-4096); // page align
			}
			else if ( strcmp(arg, "-stack_addr") == 0 ) {
				fStackAddr = parseAddress(argv[++i]);
			}
			else if ( strcmp(arg, "-stack_size") == 0 ) {
				fStackSize = parseAddress(argv[++i]);
			}
			else if ( strcmp(arg, "-sectalign") == 0 ) {
				addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]);
				i += 3;
			}
			else if ( strcmp(arg, "-sectorder_detail") == 0 ) {
				 // FIX FIX
			}
			else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) {
				// FIX FIX
				i += 2;
			}
			else if ( strcmp(arg, "-bundle_loader") == 0 ) {
				// FIX FIX
				++i;
			}
			else if ( strcmp(arg, "-private_bundle") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-multiply_defined") == 0 ) {
				// FIX FIX
				++i;
			}
			else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) {
				// FIX FIX
				++i;
			}
			else if ( strcmp(arg, "-nomultidefs") == 0 ) {
				 // FIX FIX
			}
			else if ( arg[1] == 'y' ) {
				 // FIX FIX
			}
			else if ( strcmp(arg, "-Y") == 0 ) {
				 ++i;
				 // FIX FIX
			}
			else if ( strcmp(arg, "-m") == 0 ) {
				 // FIX FIX
			}
			else if ( strcmp(arg, "-whyload") == 0 ) {
				 // FIX FIX
			}
			else if ( strcmp(arg, "-u") == 0 ) {
				const char* name = argv[++i];
				if ( name == NULL )
					throw "-u missing argument";
				fInitialUndefines.push_back(name);
			}
			else if ( strcmp(arg, "-i") == 0 ) {
				   // FIX FIX
			}
			else if ( strcmp(arg, "-U") == 0 ) {
				 // FIX FIX
				 ++i;  
			}
			else if ( strcmp(arg, "-s") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-x") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-S") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-X") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-Si") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-b") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-Sn") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-dead_strip") == 0 ) {
				 // FIX FIX
				 fprintf(stderr, "ld64: warning -dead_strip not yet supported for 64-bit code\n");
			}
			else if ( strcmp(arg, "-w") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-M") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-whatsloaded") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-headerpad") == 0 ) {
				const char* size = argv[++i];
				if ( size == NULL )
					throw "-headerpad missing argument";
				 fMinimumHeaderPad = parseAddress(size);
			}
			else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-t") == 0 ) {
				// FIX FIX
			}
			else if ( strcmp(arg, "-A") == 0 ) {
				 // FIX FIX
				 ++i;  
			}
			else if ( strcmp(arg, "-umbrella") == 0 ) {
				const char* name = argv[++i];
				if ( name == NULL )
					throw "-umbrella missing argument";
				fUmbrellaName = name;
			}
			else if ( strcmp(arg, "-allowable_client") == 0 ) {
				 // FIX FIX
				 ++i;  
			}
			else if ( strcmp(arg, "-client_name") == 0 ) {
				 // FIX FIX
				 ++i;  
			}
			else if ( strcmp(arg, "-sub_umbrella") == 0 ) {
				const char* name = argv[++i];
				if ( name == NULL )
					throw "-sub_umbrella missing argument";
				 fSubUmbellas.push_back(name);
			}
			else if ( strcmp(arg, "-sub_library") == 0 ) {
				const char* name = argv[++i];
				if ( name == NULL )
					throw "-sub_library missing argument";
				 fSubLibraries.push_back(name);
			}
			else if ( strcmp(arg, "-init") == 0 ) {
				const char* name = argv[++i];
				if ( name == NULL )
					throw "-init missing argument";
				fInitFunctionName = name;
			}
			else if ( strcmp(arg, "-warn_commons") == 0 ) {
				fWarnCommons = true;
			}
			else if ( strcmp(arg, "-commons") == 0 ) {
				fCommonsMode = parseCommonsTreatment(argv[++i]);
			}
			else if ( strcmp(arg, "-v") == 0 ) {
				// previously handled by buildSearchPaths()
			}
			else if ( strcmp(arg, "-Z") == 0 ) {
				// previously handled by buildSearchPaths()
			}
			else if ( strcmp(arg, "-syslibroot") == 0 ) {
				++i;
				// previously handled by buildSearchPaths()
			}
			else {
				fprintf(stderr, "unknown option: %s\n", arg);
			}
		}
		else {
			fInputFiles.push_back(findFile(arg));
		}
	}
}



// 
// -syslibroot <path> is used for SDK support.  
// The rule is that all search paths (both explicit and default) are
// checked to see if they exist in the SDK.  If so, that path is
// replaced with the sdk prefixed path.  If not, that search path
// is used as is.  If multiple -syslibroot options are specified
// their directory structures are logically overlayed and files
// from sdks specified earlier on the command line used before later ones.
// 
void Options::buildSearchPaths(int argc, const char* argv[])
{
	bool addStandardLibraryDirectories = true;
	std::vector<const char*> libraryPaths;
	std::vector<const char*> frameworkPaths;
	// scan through argv looking for -L, -F, -Z, and -syslibroot options
	for(int i=0; i < argc; ++i) {
		if ( (argv[i][0] == '-') && (argv[i][1] == 'L') )
			libraryPaths.push_back(&argv[i][2]);
		else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') )
			frameworkPaths.push_back(&argv[i][2]);
		else if ( strcmp(argv[i], "-Z") == 0 )
			addStandardLibraryDirectories = false;
		else if ( strcmp(argv[i], "-v") == 0 ) {
			fVerbose = true;
			extern const char ld64VersionString[];
			fprintf(stderr, "%s", ld64VersionString);
			 // if only -v specified, exit cleanly
			 if ( argc == 2 )
				exit(0);
		}
		else if ( strcmp(argv[i], "-syslibroot") == 0 ) {
			const char* path = argv[++i];
			if ( path == NULL )
				throw "-syslibroot missing argument";
			fSDKPaths.push_back(path);
		}
	}
	if ( addStandardLibraryDirectories ) {
		libraryPaths.push_back("/usr/lib");
		libraryPaths.push_back("/usr/local/lib");
		
		frameworkPaths.push_back("/Library/Frameworks/");
		frameworkPaths.push_back("/Network/Library/Frameworks/");
		frameworkPaths.push_back("/System/Library/Frameworks/");
	}
	
	// now merge sdk and library paths to make real search paths
	for (std::vector<const char*>::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) {
		const char* libDir = *it;
		bool sdkOverride = false;
		if ( libDir[0] == '/' ) {
			char betterLibDir[PATH_MAX];
			if ( strstr(libDir, "/..") != NULL ) {
				if ( realpath(libDir, betterLibDir) != NULL )
					libDir = betterLibDir;
			}
			const int libDirLen = strlen(libDir);
			for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
				const char* sdkDir = *sdkit;
				const int sdkDirLen = strlen(sdkDir);
				char newPath[libDirLen + sdkDirLen+4];
				strcpy(newPath, sdkDir);
				if ( newPath[sdkDirLen-1] == '/' )
					newPath[sdkDirLen-1] = '\0';
				strcat(newPath, libDir);
				struct stat statBuffer;
				if ( stat(newPath, &statBuffer) == 0 ) {
					fLibrarySearchPaths.push_back(strdup(newPath));
					sdkOverride = true;
				}
			}
		}
		if ( !sdkOverride ) 
			fLibrarySearchPaths.push_back(libDir);
	}
	
	// now merge sdk and framework paths to make real search paths
	for (std::vector<const char*>::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) {
		const char* frameworkDir = *it;
		bool sdkOverride = false;
		if ( frameworkDir[0] == '/' ) {
			char betterFrameworkDir[PATH_MAX];
			if ( strstr(frameworkDir, "/..") != NULL ) {
				if ( realpath(frameworkDir, betterFrameworkDir) != NULL )
					frameworkDir = betterFrameworkDir;
			}
			const int frameworkDirLen = strlen(frameworkDir);
			for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
				const char* sdkDir = *sdkit;
				const int sdkDirLen = strlen(sdkDir);
				char newPath[frameworkDirLen + sdkDirLen+4];
				strcpy(newPath, sdkDir);
				if ( newPath[sdkDirLen-1] == '/' )
					newPath[sdkDirLen-1] = '\0';
				strcat(newPath, frameworkDir);
				struct stat statBuffer;
				if ( stat(newPath, &statBuffer) == 0 ) {
					fFrameworkSearchPaths.push_back(strdup(newPath));
					sdkOverride = true;
				}
			}
		}
		if ( !sdkOverride ) 
			fFrameworkSearchPaths.push_back(frameworkDir);
	}
	
	if ( fVerbose ) {
		fprintf(stderr,"Library search paths:\n");
		for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++)
			fprintf(stderr,"\t%s\n", *it);
		fprintf(stderr,"Framework search paths:\n");
		for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++)
			fprintf(stderr,"\t%s\n", *it);
	}
}

// this is run before the command line is parsed
void Options::parsePreCommandLineEnvironmentSettings()
{
	if ( getenv("RC_TRACE_ARCHIVES") != NULL)
	    fReaderOptions.fTraceArchives = true;
		
	if ( getenv("RC_TRACE_DYLIBS") != NULL) {
	    fReaderOptions.fTraceDylibs = true;
		fReaderOptions.fTraceIndirectDylibs = true;
	}
}

// this is run after the command line is parsed
void Options::parsePostCommandLineEnvironmentSettings()
{

}

void Options::checkIllegalOptionCombinations()
{
	// check -undefined setting
	switch ( fUndefinedTreatment ) {
		case kUndefinedError:
		case kUndefinedDynamicLookup:
			// always legal
			break;
		case kUndefinedWarning:
		case kUndefinedSuppress:
			// requires flat namespace
			if ( fNameSpace == kTwoLevelNameSpace )
				throw "can't use -undefined warning or suppress with -twolevel_namespace";
			break;
	}
	
	// unify -sub_umbrella with dylibs
	for (std::vector<const char*>::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) {
		const char* subUmbrella = *it;
		bool found = false;
		for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
			Options::FileInfo& info = *fit;
			const char* lastSlash = strrchr(info.path, '/');
			if ( lastSlash == NULL )
				lastSlash = info.path - 1;
			if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) {
				info.options.fReExport = true;
				found = true;
				break;
			}
		}
		if ( ! found )
			fprintf(stderr, "ld64 warning: -sub_umbrella %s does not match a supplied dylib\n", subUmbrella);
	}
	
	// unify -sub_library with dylibs
	for (std::vector<const char*>::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) {
		const char* subLibrary = *it;
		bool found = false;
		for (std::vector<Options::FileInfo>::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) {
			Options::FileInfo& info = *fit;
			const char* lastSlash = strrchr(info.path, '/');
			if ( lastSlash == NULL )
				lastSlash = info.path - 1;
			const char* dot = strchr(lastSlash, '.');
			if ( dot == NULL )
				dot = &lastSlash[strlen(lastSlash)];
			if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) {
				info.options.fReExport = true;
				found = true;
				break;
			}
		}
		if ( ! found )
			fprintf(stderr, "ld64 warning: -sub_library %s does not match a supplied dylib\n", subLibrary);
	}
	
	// sync reader options
	if ( fNameSpace != kTwoLevelNameSpace )
		fReaderOptions.fFlatNamespace = true;

	// check -stack_addr
	if ( fStackAddr != 0 ) { 
		switch (fArchitecture) {
			case CPU_TYPE_I386:
			case CPU_TYPE_POWERPC:
				if ( fStackAddr > 0xFFFFFFFF ) 
					throw "-stack_addr must be < 4G for 32-bit processes";
				break;
			case CPU_TYPE_POWERPC64:
				break;
		}
		if ( (fStackAddr & -4096) != fStackAddr )
			throw "-stack_addr must be multiples of 4K";
		if ( fStackSize  == 0 )
			throw "-stack_addr must be used with -stack_size";
	}
	
	// check -stack_size
	if ( fStackSize != 0 ) { 
		switch (fArchitecture) {
			case CPU_TYPE_I386:
			case CPU_TYPE_POWERPC:
				if ( fStackSize > 0xFFFFFFFF ) 
					throw "-stack_size must be < 4G for 32-bit processes";
				if ( fStackAddr == 0 ) {
					fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0xC0000000\n");
					fStackAddr = 0xC0000000;
				}
				break;
			case CPU_TYPE_POWERPC64:
				if ( fStackAddr == 0 ) {
					fprintf(stderr, "ld64 warning: -stack_addr not specified, using the default 0x0008000000000000\n");
					fStackAddr = 0x0008000000000000LL;
				}
				break;
		}
		if ( (fStackSize & -4096) != fStackSize )
			throw "-stack_size must be multiples of 4K";
		switch ( fOutputKind ) {
			case Options::kDynamicExecutable:
			case Options::kStaticExecutable:
				// custom stack size only legeal when building main executable
				break;
			case Options::kDynamicLibrary:
			case Options::kDynamicBundle:
			case Options::kObjectFile:
			case Options::kDyld:
				throw "-stack_size option can only be used when linking a main executable";
		}
	}
	
	// check -init is only used when building a dylib
	if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) )
		throw "-init can only be used with -dynamiclib";
		
	// make sure all required exported symbols exist
	for (NameSet::iterator it=fExportSymbols.begin(); it != fExportSymbols.end(); it++)
		fInitialUndefines.push_back(*it);
		
}


