/* Copyright (c) 1988 by Sozobon, Limited.  Author: Johann Ruegg
 *
 * Permission is granted to anyone to use this software for any purpose
 * on any computer system, and to redistribute it freely, with the
 * following restrictions:
 * 1) No charge may be made other than reasonable charges for reproduction.
 * 2) Modified versions must be clearly marked as such.
 * 3) The authors are not responsible for any harmful consequences
 *    of using this software, even if they result from defects in it.
 *
 *	Alternative ST symbol table lister.
 */

/* highly munged from above, VERY quick and dirty nm, just enough
   here to find _stksize for fixstk.c and printstk.c, may be
   useful for other stuff.

   WARNING: -g option will not work with gcc-ld produced a.out


     ++jrb
 */
/*
 * Modified to handle expanded, GST linker derived, format for symbols.
 * This format is produced with -G flag to a proper version of gcc-ld.
 * -g flag also will work if ld coached properly.
 *
 *   --mj
 */

#ifdef atarist
long _stksize = -1L;  /* grab all memory you can get */
#endif

#include <stdio.h>

#define  A_LNAM 0x48

struct hdr {
	short magic;
	long tsize, dsize, bsize;
	long syms;
	long f1,f2;
	short f3;
} h;

struct sym {
	char name[8];
	char flags;
	char xflags;
	long value;
};

struct xsym {
	char name[8];
	char flags;
	char xflags;
	long value;
	char tail[sizeof (struct sym)];
};

int gflag;

int main(int argc, char **argv);
int doopt(char *s);
int cmp(struct xsym *s1, struct xsym *s2);
void doname(char *s, int many);
int dohdr(FILE *fd);
void dosym(struct xsym *s);
int not_glob(int x);
void sflags(int x);

main(argc, argv)
char **argv;
{
	int many;

	many = argc - 2;

	while (--argc) {
		argv++;
		if (*argv[0] == '-')
			many -= doopt(argv[0]);
		else
			doname(argv[0], many);
	}
	exit(0);
}

doopt(s)
char *s;
{
	while (*++s)
		switch (*s) {
		case 'g':
			gflag++;
		}
	return 1;	/* return number of processed options */
}

int  cmp(s1, s2)
struct xsym *s1, *s2;
{
    return( s1->value - s2->value);
}

void
doname(s, many)
char *s;
{
	FILE *fd, *fopen();
	int i, count;

	fd = fopen(s, "r");
	if (fd == NULL) {
		fprintf(stderr, "Can't open %s\n", s);
		return;
	}

	if (many)
		printf("\n%s:\n", s);

	if (i = dohdr(fd))
	{
	    register struct xsym *syms;
	    register struct xsym *currsym;
	    printf("%d slots mem %d\n", i, i*sizeof(struct sym));
	    
	    if((syms = (struct xsym *)malloc(i*sizeof(struct xsym))) == NULL)
	    {
		perror("Outa mem");
		exit(1);
	    }
	    count = i;
	    currsym = syms;
	    while (count)
	    {
	        if(fread(currsym, sizeof(struct sym), 1, fd) != 1)
	        {
		    perror("reading syms");
		    exit(2);
	        }
		if (--count) {
		    if (A_LNAM == (currsym->xflags & A_LNAM))
		    {
	                if(fread(&(currsym->tail),
			    sizeof(struct sym), 1, fd) != 1)
	                {
		            perror("reading syms");
		            exit(2);
	                }
		        --i;
		        --count;
		    }
		}
		else /* object was partially stripped */
		    currsym->xflags &= ~A_LNAM;		
		if (gflag && not_glob(currsym->flags))
		    --i;
		else
		    currsym++;
            }
	    fclose(fd);
	    printf("%d %ssymbols\n", i, (gflag ? "global ": ""));
	    qsort(syms, i, sizeof(struct xsym), cmp);
	    while (i--)
	    {
		dosym(syms);
		syms += 1;
	    }
	    free(syms);
	}
}

dohdr(fd)
FILE *fd;
{
	int i;
	long len;

#if 0
	fread(&h, 2, 1, fd);
	if (h.magic == ARMAG1)
		return -1;
#endif
	i = fread((char *)&h , sizeof(h), 1, fd);
	if (i != 1 || h.magic != 0x601a) {
		printf("Bad header\n");
		return 0;
	}
	len = h.tsize + h.dsize;
	fseek(fd, len, 1);
	return h.syms / sizeof(struct sym);
}

void
dosym(s)
struct xsym *s;
{
	printf("%-8.8s", s->name);
	printf("%-14.14s", (A_LNAM == (s->xflags & A_LNAM) ? s->tail : ""));
	printf("\t%8lx ", s->value);
	sflags(s->flags);
	putchar('\n');
}

#if 0
fill8(s)
char *s;
{
	int i;

	for (i=0; i<8; i++)
		if (s[i] == 0)
			putchar(' ');
}
#endif

char *fname[] = {
	"?0?", " bss", " text", "?3?", " data",
	"?5?", "?6?", "?7?"
};
char *Fname[] = {
	"?0?", "Bss", "Text", "?3?", "Data",
	"?5?", "?6?", "?7?"
};

not_glob(x)
{
	x &= 0xff;
	if (x & 0x20)
		return 0;
	x &= ~0x20;
	if (x == 0x88)
		return 0;
	return 1;
}

void
sflags(x)
{
	char **category;
	int lflag;
	
	if (0 != (lflag = not_glob(x)))
		category = fname;
	else
		category = Fname;
	x &= 0xff;
	if (x & 0xd8) {
		if (x & 0x08)
			printf (" external");
		if (x & 0x50) {
			printf (" equ");
			if (x & 0x10)
				printf (" reg");
		}
		if (x & 0x08)
			printf (" abs");
		if (!lflag)
			printf(" G");
	}
	else {
		x &= 7;
		printf(category[x]);
	}
}
