/*
 * Copyright (c) 2003 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 <mach/mach.h> /* first so to get rid of a precomp warning */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/stab.h>
#include <mach-o/arch.h>
#include "stuff/bool.h"
#include "stuff/ofile.h"
#include "stuff/errors.h"
#include "stuff/allocate.h"
#include "stuff/guess_short_name.h"
char *progname = NULL;

static void nm(
    struct ofile *ofile,
    char *arch_name,
    void *cookie);

static char * get_full_path(
    char *short_name,
    struct ofile *ofile);

/* Define the structures used by SymInfo */
typedef struct {
    char *name;
    const char *arch;
    char *ordinal; /* for imports only */
}_SymInfoSymbol;
typedef _SymInfoSymbol *SymInfoSymbol;

typedef struct {
    char **subUmbrellas;
    char **subFrameworks;
    unsigned int nSubFrameworks;
    unsigned int nSubUmbrellas;
}_SymInfoDependencies;
typedef _SymInfoDependencies *SymInfoDependencies;

typedef struct {
    SymInfoSymbol *exports;
    SymInfoSymbol *imports;
    SymInfoDependencies dependencies;
    unsigned int nExports;
    unsigned int nImports;
    char *path;
    char *shortName;
}_SymInfoList;

typedef _SymInfoList *SymInfoList;
#define __SymInfoTypes__
#include <cbt/libsyminfo.h>


/* flags to control the nm callback */
struct cmd_flags {
    enum bool g;        /* select only global symbols */
    enum bool u;        /* select only undefined symbols */
    enum bool d;	/* select only defined symbols, opposite of u */
    enum bool getLibDeps; /* Get the library dependency info like
			     SUB_UMBRELLA, SUB_FRAMEWORK, etc */
    enum bool import;	/* Flag to identify were looking for imported symbols
			   data */
};

/* flags set by processing a specific object file */
struct process_flags {
    unsigned long nlibs;	/* For printing the twolevel namespace */
    char **lib_names;		/*  references types, the number of libraries */
				/*  an array of pointers to library names */
};

/* the internal symbol structure used in this file */
struct symbol {
    char *name;
    char *indr_name;
    struct nlist_64 nl;
};

static void set_symbol_names(
    struct symbol *symbols,
    unsigned long nsymbols,
    char *strings,
    unsigned long strsize);
static enum bool select_symbol(
    struct symbol *symbol,
    struct cmd_flags *cmd_flags,
    struct process_flags *process_flags);
    static void make_symbol_32(struct symbol *symbol,
    struct nlist *nl);
static void make_symbol_32(
    struct symbol *symbol,
    struct nlist *nl);
static void make_symbol_64(
    struct symbol *symbol,
    struct nlist_64 *nl);
static struct symbol *select_symbols(
    struct ofile *ofile,
    struct symtab_command *st,
    struct dysymtab_command *dyst,
    struct cmd_flags *cmd_flags,
    struct process_flags *process_flags,
    unsigned long *nsymbols,
    struct nlist *all_symbols,
    struct nlist_64 *all_symbols64);

/*
 * Super hack so values can get passed back from  the nm callback.
 * The only other way I see to do this is to copy in the ofile_process code
 * and edit it to return a value but that wouldn't work to well.  This is
 * ugly but effective.
 */
struct selectedSymbolListInfo {
    vm_size_t mappedFileSize;
    void *mappedFile;
    char *fileName;
    char *cachedFileName;
    int byteSex;
    struct nlist *all_symbols;
    struct nlist_64 *all_symbols64;
    struct symtab_command *st;
};
static struct selectedSymbolListInfo *gInfo;
static SymInfoList self;

SymInfoList
SymInfoCreate(
char *fileName)
{
    SymInfoList rList;
    struct cmd_flags cmd_flags = { 0 };
    kern_return_t r;

	/* Allocate return value and point the global pointer self at it */
	rList = malloc(sizeof(_SymInfoList));
	bzero(rList, sizeof(_SymInfoList));
	if(rList == NULL)
	    return(rList);
	self = rList;
	gInfo = allocate(sizeof(struct selectedSymbolListInfo));
	bzero(gInfo,sizeof(struct selectedSymbolListInfo));
	
	/*
	 * We want to process all the ofiles possible for this file.
	 * The cmd_flags are set in nm depending on what is being accomplished.
	 */
	ofile_process(fileName,	/* name */
		      NULL,	/* arch_flags */
		      0,	/* narch_flags */
		      TRUE,	/* all_archs */
		      FALSE,	/* process_non_objects */
		      FALSE,	/* dylib_flat */
		      TRUE,	/* use_member_syntax */
		      nm,	/* processor */
		      &cmd_flags); /* cookie */

	/* Clean up */
	if((r = vm_deallocate(mach_task_self(), (vm_address_t)gInfo->mappedFile,
			  (vm_size_t)gInfo->mappedFileSize)) != KERN_SUCCESS){
	    my_mach_error(r, "Can't vm_deallocate mapped memory for file: "
		   "%s",fileName);
	}
	free(gInfo->fileName);
	free(gInfo->cachedFileName);
	return(rList);
}

void
SymInfoFreeSymbol(
SymInfoSymbol symbol)
{
	if(symbol == NULL){
	    printf("Null symbol, not doing a thing\n");
	    return;
	}
	if(symbol->name)
	    free(symbol->name);
	if(symbol->ordinal)
	    free(symbol->ordinal);
	if(symbol->arch)
	    free((char *)symbol->arch);
	if(symbol)
	    free(symbol);
}

void
SymInfoFree(
SymInfoList nmList)
{
    unsigned long i;
    
	if(nmList == NULL)
	    return;
	
	/* Free the Symbols */
	for(i = 0; i < nmList->nExports; i++)
	    SymInfoFreeSymbol(nmList->exports[i]);
	if(nmList->exports)
	    free(nmList->exports);
	for(i = 0; i < nmList->nImports; i++)
	    SymInfoFreeSymbol(nmList->imports[i]);
	if(nmList->imports)
	    free(nmList->imports);
	
	/* Free the Dependencies */
	SymInfoFreeDependencies(nmList->dependencies);

	/* Free the rest */
	if(nmList->path)
	    free(nmList->path);
	
	if(nmList->shortName)
	    free(nmList->shortName);
	if(nmList)
	    free(nmList);
	if(gInfo)
	    free(gInfo);
}

void
SymInfoFreeDependencies(
SymInfoDependencies deps)
{
    unsigned long i;
    
	if(deps == NULL)
	    return;
	
	/* Free the subumbrellas */
	for(i = 0; i < deps->nSubUmbrellas; i++){
	    if(deps->subUmbrellas[i])
		free(deps->subUmbrellas[i]);
	}
	if(deps->subUmbrellas)
	    free(deps->subUmbrellas);
	
	/* Free the subFrameworks */
	for(i = 0; i < deps->nSubFrameworks; i++){
	    if(deps->subFrameworks[i])
		free(deps->subFrameworks[i]);
	}
	if(deps->subFrameworks)
	    free(deps->subFrameworks);
	free(deps);
}

/* Access functions */
SymInfoSymbol *
SymInfoGetImports(
SymInfoList nmList)
{
	if(nmList == NULL)
	    return(NULL);
	return(nmList->imports);
}

SymInfoSymbol *
SymInfoGetExports(
SymInfoList nmList)
{
	if(nmList == NULL)
	    return(NULL);
	return(nmList->exports);
}

SymInfoDependencies
SymInfoGetLibraryInfo(
SymInfoList nmList)
{
	if(nmList == NULL)
	    return(NULL);
	return(nmList->dependencies);
}

unsigned int
SymInfoGetSubFrameworkCount(
SymInfoDependencies deps)
{
	if(deps == NULL)
	    return(0);
	return(deps->nSubFrameworks);
}

unsigned int
SymInfoGetSubUmbrellaCount(
SymInfoDependencies deps)
{
	if(deps == NULL)
	    return(0);
	return(deps->nSubUmbrellas);
}

char **
SymInfoGetSubUmbrellas(
SymInfoDependencies deps)
{
	if(deps == NULL)
	    return(NULL);
	return(deps->subUmbrellas);
}

char **
SymInfoGetSubFrameworks(
SymInfoDependencies deps)
{
	if(deps == NULL)
	    return(NULL);
	return(deps->subFrameworks);
}

char *
SymInfoGetSymbolName(
SymInfoSymbol symbol)
{
	if(symbol == NULL)
	    return(NULL);
	return(symbol->name);
}

const char *
SymInfoGetSymbolArch(
SymInfoSymbol symbol)
{
	if(symbol == NULL)
	    return(NULL);    
	return(symbol->arch);
}

char *
SymInfoGetSymbolOrdinal(
SymInfoSymbol symbol)
{
	if(symbol == NULL)
	    return(NULL);
	return(symbol->ordinal);
}

unsigned int
SymInfoGetExportCount(
SymInfoList nmList)
{
	if(nmList == NULL)
	    return(0);
	return(nmList->nExports);
}

unsigned int
SymInfoGetImportCount(
SymInfoList nmList)
{
	if(nmList == NULL)
	    return(0);
	return(nmList->nImports);
}

char *
SymInfoGetShortName(
SymInfoList nmList)
{
	if(nmList == NULL)
	    return(NULL);
	return(nmList->shortName);
}

/* Function for creating SymInfoSymbol */
SymInfoSymbol
SymInfoCreateSymbols(
char *name,
char *arch,
char *ordinal) 
{
	SymInfoSymbol symbol;
	symbol = malloc(sizeof(_SymInfoSymbol));
	symbol->name = name;
	symbol->arch = arch;
	symbol->ordinal = ordinal;
	return(symbol);
}

SymInfoDependencies
SymInfoCreateDependencies(
char **subUmbrellas,
char **subFrameworks,
int nSubUmbrellas,
int nSubFrameworks)
{
    SymInfoDependencies deps;

	deps = malloc(sizeof(_SymInfoDependencies));
	bzero(deps,sizeof(_SymInfoDependencies));
	deps->subUmbrellas = subUmbrellas;
	deps->subFrameworks = subFrameworks;
	deps->nSubUmbrellas = nSubUmbrellas;
	deps->nSubFrameworks = nSubFrameworks;
	return(deps);
}

/*
 * nm() is the processor routine that will extract export and import info as
 * well as the library information.
 */
static
void
nm(
struct ofile *ofile,
char *arch_name,
void *cookie)
{
    struct cmd_flags *cmd_flags;
    struct process_flags process_flags;
    unsigned long i, j;
    struct load_command *lc;
    struct symtab_command *st;
    struct dysymtab_command *dyst;
    struct dylib_command *dl;
    unsigned long library_ordinal;
    unsigned long strsize = 0;
    char *strings = NULL; 
    struct symbol *symbols = NULL;
    unsigned long nsymbols;
    unsigned long nlibnames = 0;
    char *short_name, *has_suffix;
    enum bool is_framework;
    int symbolIndex;
    struct nlist *all_symbols;
    struct nlist_64 *all_symbols64;
    struct dylib_module m;
    struct dylib_module_64 m64;
    struct dylib_reference *refs;
    uint32_t ncmds, mh_flags;
    
	cmd_flags = (struct cmd_flags *)cookie;
	process_flags.nlibs = 0;
	process_flags.lib_names = NULL;

	if(ofile->mh != NULL){
	    ncmds = ofile->mh->ncmds;
	    mh_flags = ofile->mh->flags;
	}
	else{
	    ncmds = ofile->mh64->ncmds;
	    mh_flags = ofile->mh64->flags;
	}
	st = NULL;
	dyst = NULL;
	lc = ofile->load_commands;
	for(i = 0; i < ncmds; i++){
	    if(st == NULL && lc->cmd == LC_SYMTAB){
		st = (struct symtab_command *)lc;
	    }
	    else if(dyst == NULL && lc->cmd == LC_DYSYMTAB){
		dyst = (struct dysymtab_command *)lc;
	    }
	    else if((mh_flags & MH_TWOLEVEL) == MH_TWOLEVEL &&
		    (lc->cmd == LC_LOAD_DYLIB ||
			lc->cmd == LC_LOAD_WEAK_DYLIB)){
		process_flags.nlibs++;
	    }
	    lc = (struct load_command *)((char *)lc + lc->cmdsize);
	}
	if(st == NULL || st->nsyms == 0){
	    /* If there is no name list we don't really care */
	    return;
	}
	if((mh_flags & MH_TWOLEVEL) == MH_TWOLEVEL &&
	    process_flags.nlibs > 0){
	    process_flags.lib_names = (char **)
			malloc(sizeof(char *) * process_flags.nlibs);
	    j = 0;
	    lc = ofile->load_commands;
	    for(i = 0; i < ncmds; i++){
		if(lc->cmd == LC_LOAD_DYLIB || lc->cmd == LC_LOAD_WEAK_DYLIB){
		    dl = (struct dylib_command *)lc;
		    process_flags.lib_names[j] =
			savestr((char *)dl + dl->dylib.name.offset);
		    short_name = guess_short_name(process_flags.lib_names[j],
						    &is_framework, &has_suffix);
		    if(has_suffix)
			free(has_suffix);

		    if(short_name != NULL)
			process_flags.lib_names[j] = short_name;
		    j++;
		}
		lc = (struct load_command *)((char *)lc + lc->cmdsize);
	    }
	    nlibnames = j;
	}
	/* Correct the endianness of this ofile */
	if(ofile->mh != NULL){
	    all_symbols = (struct nlist *)(ofile->object_addr + st->symoff);
	    all_symbols64 = NULL;
	}
	else{
	    all_symbols = NULL;
	    all_symbols64 = (struct nlist_64 *)(ofile->object_addr +st->symoff);
	}

	if(ofile->object_byte_sex != get_host_byte_sex()){
	    /* 
	     * Cache the symbols whose endianness has been flipped so it doesn't
	     * have to be done again
	     */
	    if(gInfo->cachedFileName &&
		  (strcmp(gInfo->cachedFileName,ofile->file_name) == 0) &&
		  gInfo->byteSex == (int)ofile->object_byte_sex && 
		  st->symoff == gInfo->st->symoff &&
		  st->nsyms == gInfo->st->nsyms &&
		  st->stroff == gInfo->st->stroff &&
		  st->strsize == gInfo->st->strsize){
		all_symbols = gInfo->all_symbols;
		all_symbols64 = gInfo->all_symbols64;
	    }
	    else{
		if(ofile->mh != NULL)
		    swap_nlist(all_symbols, st->nsyms, get_host_byte_sex());
		else
		    swap_nlist_64(all_symbols64, st->nsyms,get_host_byte_sex());
		gInfo->byteSex =  ofile->object_byte_sex;
		if(gInfo->cachedFileName){
		    free(gInfo->cachedFileName);
		    gInfo->cachedFileName = NULL;
		}
		gInfo->cachedFileName = savestr(ofile->file_name);
		gInfo->all_symbols = all_symbols;
		gInfo->all_symbols64 = all_symbols64;
		gInfo->st = st;
	    }
	}
	if(ofile->dylib_module != NULL){
	    m = *ofile->dylib_module;
	    refs = (struct dylib_reference *)(ofile->object_addr +
					dyst->extrefsymoff);
	    if(ofile->object_byte_sex != get_host_byte_sex()){
		swap_dylib_module(&m, 1, get_host_byte_sex());
		swap_dylib_reference(refs + m.irefsym, m.nrefsym,
			get_host_byte_sex());
	    }
	}
	else if(ofile->dylib_module64 != NULL){
	    m64 = *ofile->dylib_module64;
	    refs = (struct dylib_reference *)(ofile->object_addr +
					dyst->extrefsymoff);
	    if(ofile->object_byte_sex != get_host_byte_sex()){
		swap_dylib_module_64(&m64, 1, get_host_byte_sex());
		swap_dylib_reference(refs + m64.irefsym, m64.nrefsym,
			get_host_byte_sex());
	    }
	}

	/* select export symbols to return */
	cmd_flags->g = TRUE;
	cmd_flags->d = TRUE;
	symbols = select_symbols(ofile, st, dyst, cmd_flags, &process_flags,
				 &nsymbols, all_symbols, all_symbols64);
	strings = ofile->object_addr + st->stroff;
	strsize = st->strsize;
	set_symbol_names(symbols, nsymbols, strings, strsize);

	self->nExports += nsymbols;
	
	/* Store all this info so that it can be cleaned up later */
	gInfo->mappedFile = ofile->file_addr;
	gInfo->mappedFileSize = ofile->file_size;
	gInfo->fileName = ofile->file_name;

	/* Reallocate the array of SymInfoSymbol export structs in self */
	self->exports = reallocate(self->exports,
				   sizeof(SymInfoSymbol *) * self->nExports);

	/*
	 * Loop through saving the symbol information in SymInfoSymbol structs.
	 */
	for(i = self->nExports - nsymbols; i < self->nExports; i++){
	    const NXArchInfo *archInfo;
	    symbolIndex = i - (self->nExports - nsymbols);
	    self->exports[i] = malloc(sizeof(_SymInfoSymbol));

	    /* Save the symbol info */
	    archInfo = NXGetArchInfoFromCpuType(ofile->mh_cputype,
						CPU_SUBTYPE_MULTIPLE);
	    self->exports[i]->name = savestr(symbols[symbolIndex].name);
	    /* If we don't know the arch name use the number */
	    if(archInfo == NULL){
		char archString[10];
		sprintf(archString, "%d", ofile->mh_cputype);
		self->exports[i]->arch = savestr(archString);
	    }
	    else{
                self->exports[i]->arch = savestr(archInfo->name);
	    }
	    self->exports[i]->ordinal = NULL;
	}
	free(symbols);

	/* Unset the cmd_flags used to find exports */
	cmd_flags->d = FALSE;

	/* Set up the flags to get the symbol info for the imports */
	cmd_flags->g = TRUE;
	cmd_flags->u = TRUE;
	cmd_flags->import = TRUE;
	
	symbols = select_symbols(ofile, st, dyst, cmd_flags, &process_flags,
				 &nsymbols, all_symbols, all_symbols64);
	strings = ofile->object_addr + st->stroff;
	strsize = st->strsize;
	set_symbol_names(symbols, nsymbols, strings, strsize);

	self->nImports += nsymbols;

	/* Reallocate the array of SymInfoSymbol imports structs in self */
	self->imports = reallocate(self->imports,
				   sizeof(SymInfoSymbol *) * self->nImports);

	/*
	 * Loop through saving the symbol information in SymInfoSymbol structs.
	 */
	for(i = self->nImports - nsymbols; i < self->nImports; i++){
	    const NXArchInfo *archInfo;
	    symbolIndex = i - (self->nImports - nsymbols);
	    self->imports[i] = malloc(sizeof(_SymInfoSymbol));
	    archInfo = NXGetArchInfoFromCpuType(ofile->mh_cputype, 
						CPU_SUBTYPE_MULTIPLE);

	    /* Save the name and arch */
	    self->imports[i]->name = savestr(symbols[symbolIndex].name);
	    if(archInfo == NULL){
		char archString[10];
		sprintf(archString, "%d", ofile->mh_cputype);
		self->imports[i]->arch = savestr(archString);
	    }
	    else{
                self->imports[i]->arch = savestr(archInfo->name);
	    }

	    /* Now extract the ordinal info and save the short library name */
	    library_ordinal =
		GET_LIBRARY_ORDINAL(symbols[symbolIndex].nl.n_desc);
	    if(library_ordinal != 0){
		if(library_ordinal == EXECUTABLE_ORDINAL)
		    self->imports[i]->ordinal = savestr("from executable");
		else if(library_ordinal == DYNAMIC_LOOKUP_ORDINAL)
		    self->imports[i]->ordinal =savestr("dynamically looked up");
		else if(library_ordinal-1 >= process_flags.nlibs)
		    self->imports[i]->ordinal = savestr("bad library ordinal");
		else 
		    /* Find the full path to the library */
		    self->imports[i]->ordinal = 
		       get_full_path(process_flags.lib_names[library_ordinal-1],
				     ofile);
	    }
	    else{
		self->imports[i]->ordinal = NULL;
	    }
	}
	free(symbols);

	cmd_flags->g = FALSE;
	cmd_flags->u = FALSE;
	cmd_flags->import = FALSE;

	/* Now get the dependency information from the load commands */
	if(self->dependencies == NULL){
	    /* Allocate the memory */
	    self->dependencies = malloc(sizeof(_SymInfoDependencies));
	    bzero(self->dependencies, sizeof(_SymInfoDependencies));
	    lc = ofile->load_commands;

	    /* Now get the dependency info */
	    for(j = 0; j < ncmds; j++){
		char *p;
		if(lc->cmd == LC_SUB_UMBRELLA || lc->cmd == LC_SUB_LIBRARY){
		    struct sub_umbrella_command usub;
		    memset((char *)&usub, '\0',
			    sizeof(struct sub_umbrella_command));
		    memcpy((char *)&usub, (char *)lc, lc->cmdsize);
		    p = (char *)lc + usub.sub_umbrella.offset;
		    self->dependencies->subUmbrellas =
			reallocate(self->dependencies->subUmbrellas,
		    (self->dependencies->nSubUmbrellas+1)*
		    sizeof(char **));
		    self->dependencies ->
			subUmbrellas[self->dependencies->nSubUmbrellas] =
								 savestr(p);
		    self->dependencies->nSubUmbrellas++;
		}
		else if(lc->cmd == LC_SUB_FRAMEWORK){ 
		    struct sub_framework_command subFramework;
		    memset((char *)&subFramework,'\0',
		    sizeof(struct sub_framework_command));
		    memcpy((char *)&subFramework,(char *)lc,lc->cmdsize);
		    p = (char *)lc + subFramework.umbrella.offset;
		    self->dependencies-> subFrameworks =
			    reallocate(self->dependencies->subFrameworks,
		    		       (self->dependencies->nSubFrameworks+1) *
				       sizeof(char **));
		    self->dependencies->
			subFrameworks[self->dependencies->nSubFrameworks] =
								    savestr(p);
		    self->dependencies->nSubFrameworks++;
		}
		lc = (struct load_command *)((char *)lc + lc->cmdsize);
	    }

	    /*
	    * Save the short name for this binary, to be used
	    * to map short name to full path
	    */
	    short_name =
		guess_short_name(ofile->file_name, &is_framework, &has_suffix);
	    if(has_suffix)
		free(has_suffix);
	    if(short_name){
		self->shortName = short_name;
	    }
	}
	
	/* Free the memory that was malloced in this function */
	for(i = 0; i < nlibnames; i++)
	    free(process_flags.lib_names[i]);
	if(process_flags.lib_names)
	    free(process_flags.lib_names);
        if(ofile->arch_flag.name != NULL) {
           free(ofile->arch_flag.name);
           ofile->arch_flag.name = NULL;
        }
}

/*
 * set_symbol_names() sets the name and the indr_name fields of the symbols
 * passed to it from the symbol table pass to it.
 */
static
void
set_symbol_names(
struct symbol *symbols,
unsigned long nsymbols,
char *strings,
unsigned long strsize)
{
    unsigned long i;

	for(i = 0; i < nsymbols; i++){
	    if(symbols[i].nl.n_un.n_strx == 0)
		symbols[i].name = "";
	    else if(symbols[i].nl.n_un.n_strx < 0 ||
		    (unsigned long)symbols[i].nl.n_un.n_strx > strsize){
		symbols[i].name = "bad string index";
		printf ("Setting bad string index in exports\n");
	    }
	    else
		symbols[i].name = strings + symbols[i].nl.n_un.n_strx;
	    if((symbols[i].nl.n_type & N_TYPE) == N_INDR){
		if(symbols[i].nl.n_value == 0)
		    symbols[i].indr_name = "";
		else if(symbols[i].nl.n_value > strsize){
		    symbols[i].indr_name = "bad string index";
		    printf ("Setting bad string index in exports2\n");
		}
		else
		    symbols[i].indr_name = strings + symbols[i].nl.n_value;
	    }
	}
}

static
char *
get_full_path(
char *short_name,
struct ofile *ofile) 
{
    unsigned long j;
    struct dylib_command *dl;
    struct load_command *lc;
    char *has_suffix;
    enum bool is_framework;
    uint32_t ncmds;
    
	lc = ofile->load_commands;
	if(ofile->mh != NULL)
	    ncmds = ofile->mh->ncmds;
	else
	    ncmds = ofile->mh64->ncmds;

	for(j = 0; j < ncmds; j++) {
	    if(lc->cmd == LC_LOAD_DYLIB || lc->cmd == LC_LOAD_WEAK_DYLIB){
		char *longName;
		char *shortName;
		char *returnLongName;
		dl = (struct dylib_command *)lc;
		longName = (char *)lc + dl->dylib.name.offset;
		shortName = guess_short_name(longName, &is_framework,
					     &has_suffix);
		if(shortName && strcmp(shortName, short_name) == 0){
		    free(shortName);
		    returnLongName = malloc(strlen(longName) + 2);
		    returnLongName[0] = '.';
		    returnLongName[1] = '\0';
		    strcat(returnLongName, longName);
		    return(returnLongName);
		}
	    }
	    lc = (struct load_command *)((char *)lc + lc->cmdsize);
	}
	/*
	fprintf(stderr,
		"WARNING: Couldn't find full path for: %s\n", short_name);
	*/
     
	return(savestr(short_name));
}

static
struct symbol *
select_symbols(
struct ofile *ofile,
struct symtab_command *st,
struct dysymtab_command *dyst,
struct cmd_flags *cmd_flags,
struct process_flags *process_flags,
unsigned long *nsymbols,
struct nlist *all_symbols,
struct nlist_64 *all_symbols64)
{
    unsigned long i, flags;
    struct symbol *selected_symbols, symbol;
    struct dylib_module m;
    struct dylib_module_64 m64;
    struct dylib_reference *refs;
    uint32_t irefsym, nrefsym, nextdefsym, iextdefsym, nlocalsym, ilocalsym;

	selected_symbols = allocate(sizeof(struct symbol) * st->nsyms);
	*nsymbols = 0;

	if(ofile->dylib_module != NULL){
	    if(ofile->mh != NULL){
		m = *ofile->dylib_module;
		if(ofile->object_byte_sex != get_host_byte_sex())
		    swap_dylib_module(&m, 1, get_host_byte_sex());
		irefsym = m.irefsym;
		nrefsym = m.nrefsym;
		nextdefsym = m.nextdefsym;
		iextdefsym = m.iextdefsym;
		nlocalsym = m.nlocalsym;
		ilocalsym = m.ilocalsym;
	    }
	    else{
		m64 = *ofile->dylib_module64;
		if(ofile->object_byte_sex != get_host_byte_sex())
		    swap_dylib_module_64(&m64, 1, get_host_byte_sex());
		irefsym = m64.irefsym;
		nrefsym = m64.nrefsym;
		nextdefsym = m64.nextdefsym;
		iextdefsym = m64.iextdefsym;
		nlocalsym = m64.nlocalsym;
		ilocalsym = m64.ilocalsym;
	    }
	    refs = (struct dylib_reference *)(ofile->object_addr +
					      dyst->extrefsymoff);
	    for(i = 0; i < nrefsym; i++){
		flags = refs[i + irefsym].flags;
		if(flags == REFERENCE_FLAG_UNDEFINED_NON_LAZY ||
		   flags == REFERENCE_FLAG_UNDEFINED_LAZY ||
		   flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY ||
		   flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY){
		    if(ofile->mh != NULL)
			make_symbol_32(&symbol,
				      all_symbols + refs[i + irefsym].isym);
		    else
			make_symbol_64(&symbol,
				      all_symbols64 + refs[i + irefsym].isym);
		    if(flags == REFERENCE_FLAG_UNDEFINED_NON_LAZY ||
		       flags == REFERENCE_FLAG_UNDEFINED_LAZY)
			symbol.nl.n_type = N_UNDF | N_EXT;
		    else
			symbol.nl.n_type = N_UNDF;
		    symbol.nl.n_desc = (symbol.nl.n_desc &~ REFERENCE_TYPE) |
				       flags;
		    symbol.nl.n_value = 0;
		    if(select_symbol(&symbol, cmd_flags, process_flags))
			selected_symbols[(*nsymbols)++] = symbol;
		}
	    }
	    for(i = 0; i < nextdefsym && iextdefsym + i < st->nsyms; i++){
		if(ofile->mh != NULL)
		    make_symbol_32(&symbol, all_symbols + iextdefsym + i);
		else
		    make_symbol_64(&symbol, all_symbols64 + iextdefsym + i);
		if(select_symbol(&symbol, cmd_flags, process_flags))
		    selected_symbols[(*nsymbols)++] = symbol;
	    }
	    for(i = 0; i < nlocalsym && ilocalsym + i < st->nsyms; i++){
		if(ofile->mh != NULL)
		    make_symbol_32(&symbol, all_symbols + ilocalsym + i);
		else
		    make_symbol_64(&symbol, all_symbols64 + ilocalsym + i);
		if(select_symbol(&symbol, cmd_flags, process_flags))
		    selected_symbols[(*nsymbols)++] = symbol;
	    }
	}
	else{
	    for(i = 0; i < st->nsyms; i++){
		if(ofile->mh != NULL)
		    make_symbol_32(&symbol, all_symbols + i);
		else
		    make_symbol_64(&symbol, all_symbols64 + i);
		if(select_symbol(&symbol, cmd_flags, process_flags))
		    selected_symbols[(*nsymbols)++] = symbol;
	    }
	}
	/*
	 * Could reallocate selected symbols to the exact size but it is more
	 * of a time waste than a memory savings.
	 */
	return(selected_symbols);
}

static
void
make_symbol_32(
struct symbol *symbol,
struct nlist *nl)
{
	symbol->nl.n_un.n_strx = nl->n_un.n_strx;
	symbol->nl.n_type = nl->n_type;
	symbol->nl.n_sect = nl->n_sect;
	symbol->nl.n_desc = nl->n_desc;
	symbol->nl.n_value = nl->n_value;
}

static
void
make_symbol_64(
struct symbol *symbol,
struct nlist_64 *nl)
{
	symbol->nl = *nl;
}

/*
 * select_symbol() returns TRUE or FALSE if the specified symbol is to be
 * select based on the flags.
 */
static
enum bool
select_symbol(
struct symbol *symbol,
struct cmd_flags *cmd_flags,
struct process_flags *process_flags)
{
	if((cmd_flags->import == TRUE) &&
	   (process_flags->nlibs > 0) &&
	   GET_LIBRARY_ORDINAL(symbol->nl.n_desc) == 0)
	    return(FALSE);
	
	if(cmd_flags->u == TRUE){
	    if((symbol->nl.n_type == (N_UNDF | N_EXT) &&
		symbol->nl.n_value == 0) ||
		symbol->nl.n_type == (N_PBUD | N_EXT))
		return(TRUE);
	    else
		return(FALSE);
	}

	if(cmd_flags->d == TRUE){
	    if((((symbol->nl.n_type & N_TYPE) != N_UNDF)
		    && ((symbol->nl.n_type & N_EXT) != 0))
		    && ((symbol->nl.n_type & N_TYPE) != N_PBUD))
		return(TRUE);
	    else
		return(FALSE);
	}

	if(cmd_flags->g == TRUE && (symbol->nl.n_type & N_EXT) == 0)
	    return(FALSE);

	return(TRUE);
}
