#ifndef lint
static char sccsid[] = "@(#)selex.c	1.1 (UKC) 8/5/86";
#endif  lint
/***

* program name:
	selex
* function:
	SELectively EXport items from symbol table of a.out file.
	Ie localise any external symbols included in argument list.
	Symbol names will usually start with an underscore.

	Stdio not used for reading/writing symbol table because each write
	must be atomic to avoid corrupting the file in case of interrupts etc.
* syntax:
	selex [-v] [-x] file.o symbol [symbol] ...
		-v	verbose mode: print the name of each symbol localised
		-x	localise ALL symbols except those in argument list
* libraries used:
	standard
* compile time parameters:
	cc selex.c
* what's wrong
	You don't need to write back a whole symbol entry -
	just the unsigned char n_type part of the structure.

	Should check that all arguments occur in symbol table before shooting
	any of them in case of mistyping.
* history:
	March 85	Written by Martin Guy, UKC.
	October 85	Ported to 4.1 Orion Unix
***/

#include <stdio.h>
#include <a.out.h>

extern int errno;
char *malloc();

main(argc, argv)
char **argv;
{
	register int i,j;
	register int fd;	/* file descriptor open to the .o file */
	struct exec header;	/* a.out file header */
	struct nlist symbol;	/* buffer for symbol being examined */
	int	nsyms;		/* number of symbols in symbol table */
	char *	strtab;		/* the string table */
	long	strtabsize;	/* size of string table */
	int	inarglist;	/* is this symbol included in the arguments */
	char *	argused;	/* array: element is nonzero if arg has been found in symbol table */
	int	vflag = 0;	/* -v flag: output list of deleted symbols */
	int	xflag = 0;	/* localise all symbols *except* those in argument list */

check:	if (argc < 3) {
		fputs("selex: Too few arguments.\n", stderr);
usage:		fputs("Usage: selex [-s] file.o symbol [symbol] ...\n", stderr);
		exit(1);
	}
	if (argv[1][0] == '-') {
		switch (argv[1][1]) {

		case 'v':
			vflag = 1;
			break;
		case 'x':
			xflag = 1;
			break;

		default:
			fprintf(stderr, "selex: unrecognised flag '%c'.\n", argv[1][1]);
			goto usage;
		}
		argv++; argc--;
		goto check;
	}
#ifdef DEBUG
	vflag = 1;
#endif
	argused = malloc ((unsigned) argc);

	/* open object file */
	fd = open (argv[1],2);
	if (fd < 0) {
		fprintf(stderr, "selex: Cannot open file \"%s\" for reading and writing.\n", argv[1]);
		exit(1);
	}

	/* Read in header */

	if (read(fd, &header, sizeof(header)) != sizeof(header)) {
		fputs("selex: Read fails to get header of object file.\n", stderr);
		perror("selex");
		exit(1);
	}

	/* check magic number */

	if (N_BADMAG(header)) {
		fprintf (stderr, "selex: %s is not an a.out file.\n", argv[1]);
		exit(1);
	}

	/* read in string table in one gulp */

	if (lseek(fd, N_STROFF(header), 0) < 0) {
		perror("selex");
		exit(1);
	}
	/* sizeof(strtabsize) is defined to be 4. */
	read(fd, &strtabsize, 4);
#ifdef DEBUG
	fprintf(stderr, "debug: strtabsize = %d.\n", strtabsize);
#endif
	strtab = malloc((unsigned) strtabsize);
	if (strtab == NULL) {
		fputs("selex: Out of memory.\n", stderr);
		exit(1);
	}
	if (read (fd, strtab+4, strtabsize-4) != strtabsize-4) {
		fputs("selex: Object file too short.\n", stderr);
		exit(1);
	}

	errno = 0;	/* No error checking in this block */

	/* Now run through the symbols */
	if (header.a_syms % sizeof(struct nlist) != 0) {
		fputs("selex: Corrupt object file\n.", stderr);
		exit(1);
	}
	nsyms = header.a_syms / sizeof(struct nlist);
#ifdef DEBUG
	fprintf(stderr, "debug: nsyms = %d.\n", nsyms);
#endif
	lseek(fd, N_SYMOFF(header), 0);
	for (i=0; i<nsyms; i++) {
		read(fd, &symbol, sizeof(symbol));
#ifdef DEBUG
		fprintf(stderr, "debug: Found symbol \"%s\"; ", strtab + symbol.n_un.n_strx);
#endif
		/* If symbol is external (except undefined) */
		if ((symbol.n_type & N_EXT) && symbol.n_type != (N_UNDF|N_EXT)) {
			/* Shoot it if not in argument list */
			inarglist = 0;
			for (j=2; j<argc; j++) {
				if (strcmp(argv[j], &(strtab[symbol.n_un.n_strx])) == 0) {
					argused[j] = 1;
					inarglist = 1;
					break;
				}
			}
			if (inarglist ^ xflag) {
				symbol.n_type &= ~N_EXT;
				lseek(fd, -sizeof(symbol), 1);
				write(fd, &symbol, sizeof(symbol));
				if (vflag) printf("%s ", strtab + symbol.n_un.n_strx);
#ifdef DEBUG
				fputs("localising!!\n", stderr);
#endif
			}
#ifdef DEBUG
			else fputs("saved by argument list.\n", stderr);
#endif
		}
#ifdef DEBUG
		else fputs("internal or undefined.\n", stderr);
#endif
	}
	if (vflag) putchar('\n');
	if (errno != 0) {
		/* An error occurred in the last block */
		perror("selex: undetected error");
	}
	for (i=2; i<argc; i++)
		if (argused[i]==0)
			fprintf(stderr, "selex: Warning: no symbol called \"%s\".\n", argv[i]);
	exit(0);
}
