
/*
 * 			O B J R E F
 *
 * NOTE: this program has been superceded by additional capabilities in
 * nm.c (in tools).  It is included here as it offers a simple set of
 * subroutines for processing symbol tables.
 */

/*)BUILD	$(TKBOPTIONS) = {
			TASK	= ...REF
		}
*/

#ifdef	DOCUMENTATION

title	objref	Object file cross reference
index		Object file cross reference

synopsis

	objref file_list

description

	objref reads each file in the list, building a cross reference
	listing of symbols (where symbols were defined and referenced).
	.s
	The files in the file_list were output by nm.  The file_list
	arguments may contain wildcards.  The argument string to nm
	is as follows:
	.s
		nm "-1" file.obj >file.nm

author

	Martin Minow

bugs

	Superceded by additional capabilities in nm.c

#endif

#include <stdio.h>
#define	FALSE	0
#define	TRUE	1
#define	EOS	0

typedef struct symbol {
	struct symbol	*s_next;
	char		*s_name;
	struct module	*s_defn;	/* Where it's defined		*/
	struct refer	*s_refer;	/* Where it's referenced	*/
} SYMBOL;

typedef struct module {
	struct module	*m_next;
	char		*m_name;
	int		m_index;
} MODULE;

typedef struct refer {
	struct refer	*r_next;
	struct module	*r_module;
} REFERENCE;

SYMBOL		*sfirst		= NULL;
MODULE		*mfirst		= NULL;
REFERENCE	*rfirst		= NULL;

FILE		*fd;
char		line[133];
char		name[133];
char		value[133];
#define		CROSS_MAX	25
int		cross[CROSS_MAX][CROSS_MAX];
char		*mname[CROSS_MAX];
int		cmax		= -1;
extern char	*strsave();
extern char	*myalloc();


main(argc, argv)
int		argc;
char		*argv[];
{
	register int		nfiles;
	register int		i;

	for (i = 1; i < argc; i++) {
		if ((fd = fwild(argv[i], "r")) == NULL) {
			fprintf(stderr, "can't open wild card file: ");
			perror(argv[i]);
			continue;
		}
		for (nfiles = 0; fnext(fd) != NULL; nfiles++) {
			process();
		}
		if (nfiles == 0) {
			fprintf(stderr, "no files match \"%s\"\n", argv[i]);
		}
	}
	mnames();
	output();
}

process()
/*
 * For each file ...
 */
{
	register MODULE		*mp;
	register SYMBOL		*sy;
	extern	MODULE		*newmodule();
	extern	SYMBOL		*lookup();

	getfilename(fd, line);
	mp = newmodule(line);
	while (fgets(line, sizeof line, fd) != NULL) {
		sscanf(line, "%s%*s%s", &name, &value);
		sy = lookup(name);
		if (value[0] == '*') {
			addref(sy, mp);		/* Undefined here	*/
		}
		else {				/* Defined here		*/
			if (sy->s_defn != NULL) {
				printf("** symbol %s defined in %s and %s\n",
					sy->s_name, (sy->s_defn)->m_name,
					mp->m_name);
				fprintf(stderr,
					"** symbol %s defined in %s and %s\n",
					sy->s_name, (sy->s_defn)->m_name,
					mp->m_name);
			}
			else {
				sy->s_defn = mp;
			}
		}
	}
}

output()
{
	register SYMBOL		*sy;
	register int		nsy;
	register REFERENCE	*rp;
	int			i, j;

	/*
	 * Pass 1 for "library" symbols only
	 */
	printf("The following are not defined:\n");
	nsy = 0;
	for (sy = sfirst; sy != NULL; sy = sy->s_next) {
		if (sy->s_defn == NULL) {
			printf("%c%s",
				((nsy & 7) == 0) ? '\n' : '\t', sy->s_name);
			nsy++;
		}
	}
	printf("\n\n");
	/*
	 * Pass 2 for symbols not used elsewhere
	 */
	printf("The following are not used outside of their module:\n");
	nsy = 0;
	for (sy = sfirst; sy != NULL; sy = sy->s_next) {
		if (sy->s_defn != NULL && sy->s_refer == NULL) {
			printf("%c%s\t%s",
				((nsy & 3) == 0) ? '\n' : '\t',
					sy->s_name, (sy->s_defn)->m_name);
			nsy++;
		}
	}
	/*
	 * Pass 3 -- all the rest
	 */
	printf("\n\nThe following are defined and used elsewhere\n");
	for (sy = sfirst; sy != NULL; sy = sy->s_next) {
		if (sy->s_defn == NULL || sy->s_refer == NULL) {
			continue;
		}
		i = (sy->s_defn)->m_index;
		printf("\n%s\t%s:", sy->s_name, (sy->s_defn)->m_name);
		nsy = 0;
		for (rp = sy->s_refer; rp != NULL; rp = rp->r_next) {
			printf("%s%s",
				((++nsy % 6) == 0) ? "\n\t\t" : "\t",
				(rp->r_module)->m_name);
			j = (rp->r_module)->m_index;
			cross[i][j]++;
		}
	}
	printf("\n");
	/*
	 * Now for the map
	 */
	printf("\nmodule");
	for (i = 0; i <= cmax; i++) {
		printf("\n%s\t", mname[i]);
		for (j = 0; j < i; j++) {
			printf("%4d", cross[i][j] + cross[j][i]);
		}
		printf("   %s", mname[i]);
	}
}

/*
 * Manage symbol table
 */
MODULE *
newmodule(text)
/*
 * Make this a new module entry.
 */
{
	register MODULE		*mp;
	register MODULE		**mplast;
	register MODULE		*new;
	int			i;

	new = myalloc(sizeof (MODULE));
	new->m_name = strsave(text);
	for (mplast = &mfirst; (mp = *mplast) != NULL; mplast = &mp->m_next) {
		if ((i = strcmp(text, mp->m_name)) < 0)
			break;
		else if (i == 0) {
			fprintf(stderr, "duplicate module name \"%s\"\n",
				text);
			return(mp);
		}
	}
	new->m_next = mp;
	*mplast = new;
	cmax++;
	if (cmax >= CROSS_MAX)
		error("too many modules");
	return(new);
}

addref(sy, mp)
SYMBOL		*sy;
MODULE		*mp;
/*
 * The module references this symbol.
 */
{
	register REFERENCE	*rp;
 	REFERENCE		**rplast;
	register REFERENCE	*new;
	register int		i;

	new = myalloc(sizeof (REFERENCE));
	new->r_module = mp;
	for (rplast = &sy->s_refer;
			(rp = *rplast) != NULL;
			rplast = &rp->r_next) {
		if ((i = strcmp(mp->m_name, (rp->r_module)->m_name)) == 0) {
			fprintf(stderr, "\"%s\" references \"%s\" twice\n",
				sy->s_name, mp->m_name);
		}
		else if (i < 0) {
			break;
		}
	}
	new->r_next = rp;
	*rplast = new;
}

SYMBOL *
lookup(text)
char		*text;
/*
 * Return pointer to this symbol, insert if new
 */
{
	register SYMBOL		*sy;
 	SYMBOL			**sylast;
	register SYMBOL		*new;
	register int		i;

	for (sylast = &sfirst; (sy = *sylast) != NULL; sylast = &sy->s_next) {
		if ((i = strcmp(text, sy->s_name)) == 0) {
			return(sy);
		}
		else if (i < 0) {
			break;
		}
	}
	new = myalloc(sizeof (SYMBOL));
	new->s_name = strsave(text);
	new->s_next = sy;
	*sylast = new;
	return (new);
}

mnames()
/*
 * Build mname[] vector
 */
{
	register int		i;
	register MODULE		*mp;

	i = 0;
	for (mp = mfirst; mp != NULL; mp = mp->m_next, i++) {
		mname[i] = mp->m_name;
		mp->m_index = i;
	}
}

/*
 * Support routines
 */
getfilename(filedes, buffer)
FILE	*filedes;
char	*buffer;
{
	register char *tp;
	register char c;

	fgetname(filedes, buffer);
	/*
	 * Skip over device name
	 */
	for (tp = buffer; (c = *tp) != EOS && c != ':'; tp++);
	if (c)	tp++;
	else	tp = buffer;
	/*
	 * Skip over [UIC] or
	 * or [PPN] if present
	 */
	if (*tp == '[' || *tp == '(') {
		while ((c = *tp++)
				&& c != ']'
				&& c != ')');
		if (c == 0) {
			error("Can't happen");
			tp--;
		}
	}
	strcpy(buffer, tp);
	/*
	 * Don't include version
	 */
	for (tp = buffer; (c = *tp) && c != ';'; tp++);
	*tp = 0;
	/*
	 * Don't include .ext
	 */
	for (tp = buffer; (c = *tp) && c != '.'; tp++);
	*tp = 0;
	/*
	 * Now, buffer has the file name,
	 * tp - buffer, its length.
	 */
	return(buffer);
}

char *
strsave(text)
char		*text;
/*
 * Save this text
 */
{
	register char	*p;

	if ((p = malloc(strlen(text) + 1)) == NULL)
		error("No room for string alloc.");
	strcpy(p, text);
	return (p);
}

char *
myalloc(size)
int		size;
/*
 * Allocate or die
 */
{
	register char	*p;

	if ((p = calloc(1, size)) == NULL) {
		fprintf(stderr, "Can't allocate %d bytes\n", size);
		error("Fatal");
	}
	return (p);
}

