2.11BSD/src/bin/nm.c.use.disk

/*
 * This version of nm will use a disk file, rather than trying to store
 * the entire symbol table in memory.  Useful for some sites.
 *
 * 11/25/90 - The ASCII version of archives has been implemented, but this
 * program was NOT updated.  the normal version of 'nm' not only handles
 * .a files correctly but can even process /unix. so this program is of even 
 * less value than before, this program is extremely slow even for cases
 * which the regular 'nm' works well.
 */

/*
 *	print symbol tables for
 *	object or archive files
 *
 *	nm [-fgoprun] [name ...]
*/

#include	<sys/param.h>
#include	<ar.h>
#include	<a.out.h>
#include	<stdio.h>
#include	<signal.h>
#include	<ctype.h>
#include	<fcntl.h>
#include	<sys/file.h>

struct	nnlist {	/* symbol table entry */
	char		n_name[8];	/* symbol name */
	char		nn_type;	/* type flag */
	char		nn_ovno;
	unsigned int	n_value;	/* value */
};
#define SYMFILE	"/tmp/nm.sym.XXXXXX"
#define	SELECT	arch_flg ? arp.ar_name : *argv
int	numsort_flg;
int	undef_flg;
int	revsort_flg = 1;
int	globl_flg;
int	nosort_flg;
int	arch_flg;
int	prep_flg;
int	file_flg;
struct	ar_hdr	arp;
struct	exec	exp;
FILE	*fi;
long	off;
long	ftell();
char	*malloc();
char	*realloc();
char	*mktemp();
int 	symfile = -1;
char 	*symfname;
long	lseek();
long	save_sym();
struct  nnlist *get_sym();
int	cleanup();

main(argc, argv)
char **argv;
{
	int narg;
	int compare();
	int fcompare();

	signal(SIGINT,cleanup);
	signal(SIGHUP,cleanup);
	signal(SIGTERM,cleanup);
	signal(SIGQUIT,cleanup);

	if (--argc>0 && argv[1][0]=='-' && argv[1][1]!=0) {
		argv++;
		while (*++*argv) switch (**argv) {
		case 'n':		/* sort numerically */
			numsort_flg++;
			continue;

		case 'g':		/* globl symbols only */
			globl_flg++;
			continue;

		case 'u':		/* undefined symbols only */
			undef_flg++;
			continue;

		case 'r':		/* sort in reverse order */
			revsort_flg = -1;
			continue;

		case 'p':		/* don't sort -- symbol table order */
			nosort_flg++;
			continue;

		case 'o':		/* prepend a name to each line */
			prep_flg++;
			continue;

		case 'f':		/* use a file insted of memory  - slow */
			file_flg++;
			continue;

		default:		/* oops */
			fprintf(stderr, "nm: invalid argument -%c\n", *argv[0]);
			cleanup(1);
		}
		argc--;
	}
	if (argc == 0) {
		argc = 1;
		argv[1] = "a.out";
	}
	narg = argc;
	while(argc--) {
		fi = fopen(*++argv,"r");
		if (fi == NULL) {
			fprintf(stderr, "nm: cannot open %s\n", *argv);
			continue;
		}
		off = sizeof(exp.a_magic);
		fread((char *)&exp, 1, sizeof(exp.a_magic), fi);	/* get magic no. */
		if (exp.a_magic == ARMAG)
			arch_flg++;
		else if (N_BADMAG(exp)) {
			fprintf(stderr, "nm: %s-- bad format\n", *argv);
			continue;
		}
		fseek(fi, 0L, L_SET);
		if (arch_flg) {
			(void) nextel(fi);
			if (narg > 1)
				printf("\n%s:\n", *argv);
		}
		do {
			long o;
			register i, n, c;
			struct nnlist sym;
			struct nnlist *symp = NULL;
			long *idxsymfile = NULL;
			unsigned ovsizes[1 + NOVL];

			fread((char *)&exp, 1, sizeof(struct exec), fi);
			if (N_BADMAG(exp))		/* archive element not in  */
				continue;	/* proper format - skip it */
			if (exp.a_magic == A_MAGIC5 || exp.a_magic == A_MAGIC6) {
				fread((char *)ovsizes, 1, sizeof ovsizes, fi);
				o	= 0L;
				for (i = 1; i <= NOVL; i++)
					o	+= (long) ovsizes[i];
				fseek(fi, o, L_INCR);
			}
			o = (long)exp.a_text + exp.a_data;
			if ((exp.a_flag & 01) == 0)
				o *= 2;
			fseek(fi, o, L_INCR);
			if (file_flg)
			    set_sym();
			i = 0;
			while (fread((char *)&sym, sizeof(sym), 1, fi) == 1) {
				if (globl_flg && (sym.nn_type&N_EXT)==0)
					continue;
				switch (sym.nn_type&N_TYPE) {
					case N_UNDF:
						c = 'u';
						if (sym.n_value)
							c = 'c';
						break;

					default:
					case N_ABS:
						c = 'a';
						break;

					case N_TEXT:
						c = 't';
						break;

					case N_DATA:
						c = 'd';
						break;

					case N_BSS:
						c = 'b';
						break;

					case N_FN:
						c = 'f';
						break;

					case N_REG:
						c = 'r';
						break;
				}
				if (undef_flg && c!='u')
					continue;
				if (sym.nn_type&N_EXT)
					c = toupper(c);
				sym.nn_type = c;
				if (file_flg) {
				    if (idxsymfile==NULL)
					    idxsymfile = (long *)malloc(sizeof(long));
				    else
					    idxsymfile = (long *)realloc(idxsymfile, (i+1)*sizeof(long));
				} else {
				    if (symp==NULL)
					    symp = (struct nnlist *)malloc(sizeof(struct nnlist));
				    else
					    symp = (struct nnlist *)realloc(symp, (i+1)*sizeof(struct nnlist));
				}
				if (file_flg) {
				    if (idxsymfile == NULL) {
					fprintf(stderr, "nm: out of memory on %s\n", *argv);
					cleanup(2);
				    }
				} else {
				    if (symp == NULL) {
					fprintf(stderr, "nm: try using the '-f' option.\n");
					cleanup(2);
				    }
				}
				if (file_flg)
				    idxsymfile[i++] = save_sym(&sym);
				else
				    symp[i++] = sym;
			}
			if (i == 0) {
				fprintf(stderr, "nm: %s-- no name list\n", SELECT);
				continue;
			}
			if (nosort_flg==0)
			    if (file_flg)
				qsort(idxsymfile, i, sizeof(long), fcompare);
			    else
				qsort(symp, i, sizeof(struct nnlist), compare);
			if ((arch_flg || narg>1) && prep_flg==0)
				printf("\n%s:\n", SELECT);
			for (n=0; n<i; n++) {
				struct nnlist *tt;
				if (prep_flg) {
					if (arch_flg)
						printf("%s:", *argv);
					printf("%s:", SELECT);
				}
				if (file_flg) {
				    tt = get_sym(idxsymfile[n]);
				    c = tt->nn_type;
				} else
				    c = symp[n].nn_type;
				if (!undef_flg) {
					if (c=='u' || c=='U')
						printf("      ");
					else {
					    if (file_flg)
						printf(N_FORMAT, tt->n_value);
					    else
						printf(N_FORMAT, symp[n].n_value);
					}
					printf(" %c ", c);
				}
				if (file_flg) {
				    if (tt->nn_ovno)
					printf("%-8.8s %d", tt->n_name,
					   tt->nn_ovno);
				    else
					printf("%.8s", tt->n_name);
				} else {
				    if (symp[n].nn_ovno)
					printf("%-8.8s %d", symp[n].n_name,
					   symp[n].nn_ovno);
				    else
					printf("%.8s", symp[n].n_name);
				}
				printf("\n");
				if (file_flg)
				    free(tt);
			}
			if (file_flg)
			    if (idxsymfile)
				free((char *)idxsymfile);
			else
			    if (symp)
				free((char *)symp);
		} while(arch_flg && nextel(fi));
		fclose(fi);
	}
	cleanup(0);
}

compare(p1, p2)
register struct nnlist *p1, *p2;
{
	register i;

	if (numsort_flg) {
		if (p1->n_value > p2->n_value)
			return(revsort_flg);
		if (p1->n_value < p2->n_value)
			return(-revsort_flg);
	}
	for(i=0; i<sizeof(p1->n_name); i++)
		if (p1->n_name[i] != p2->n_name[i]) {
			if (p1->n_name[i] > p2->n_name[i])
			    return(revsort_flg);
			else
			    return(-revsort_flg);
		}
	return(0);
}

nextel(af)
register FILE *af;
{
	register r;

	fseek(af, off, L_SET);
	r = fread((char *)&arp, 1, sizeof(struct ar_hdr), af);  /* read archive header */
	if (r <= 0)
		return(0);
	if (arp.ar_size & 1)
		++arp.ar_size;
	off = ftell(af) + arp.ar_size;	/* offset to next element */
	return(1);
}

fcompare(fi1, fi2)
register long *fi1, *fi2;
{
	register i;
	register struct nnlist *p1, *p2;

	p1 = get_sym(*fi1);
	p2 = get_sym(*fi2);
	if (numsort_flg) {
		if (p1->n_value > p2->n_value) {
			free((char *)p1);
			free((char *)p2);
			return(revsort_flg);
		}
		if (p1->n_value < p2->n_value) {
			free((char *)p1);
			free((char *)p2);
			return(-revsort_flg);
		}
	}
	for(i=0; i<sizeof(p1->n_name); i++)
		if (p1->n_name[i] != p2->n_name[i]) {
			if (p1->n_name[i] > p2->n_name[i]) {
			    free((char *)p1);
			    free((char *)p2);
			    return(revsort_flg);
			} else {
			    free((char *)p1);
			    free((char *)p2);
			    return(-revsort_flg);
			}
		}
	free((char *)p1);
	free((char *)p2);
	return(0);
}

/*
 * Save a symbol in symfile
 */

long
save_sym(sym)
struct nlist *sym;
{
	long ret_val;

	if ((ret_val = lseek(symfile,0L,L_INCR)) < 0) {
		perror("lseek:save_sym");
		cleanup(1);
	}

	if (write(symfile,sym,sizeof(struct nlist)) < 0) {
		perror("write:save_sym");
		cleanup(1);
	}
	return(ret_val);
}

/*
 * Retrieve a symbol from symfile 
 */

struct nnlist *
get_sym(offset)
long offset;
{
	struct nnlist *temp;

	if(lseek(symfile,offset,L_SET) < 0) {
		perror("lseek:get_sym");
		cleanup(1);
	}

	temp = (struct nnlist *) malloc(sizeof(struct nnlist));
	if(read(symfile,temp,sizeof(struct nnlist)) != sizeof (struct nnlist)) {
		perror("read:get_sym");
		cleanup(1);
	}

	return(temp);
}

/*
 * Open The symfile
 */

set_sym()
{
    if (symfile != -1)
	close(symfile);
    else
	symfname = mktemp(SYMFILE);
    if ((symfile = open(symfname,O_CREAT|O_RDWR|O_TRUNC,0666)) < 0) {
	perror("open:symfile");
	cleanup(1);
    }
}

/*
 * Cleanup Routine
 */

int
cleanup(n)
int n;
{
	if (file_flg)
		unlink(symfname);
	exit(n);
}