/* 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
 */

/*
 * ++jrb rework and cleanup while adding A_TFILE A_TFARC
 */

#include <compiler.h>
#include <stdio.h>
#include <st-out.h>

#if __STDC__
#include <stdlib.h>
#else
extern char *malloc(), *realloc();
#endif

#ifdef atarist
# define READB	"rb"
#else
# define READB "r"
#endif

#if !__STDC__ && !defined(hpux)
typedef unsigned long size_t;
#endif

#ifndef WORD_ALIGNED
# define SIZEOF_SYM ((size_t)sizeof(struct asym))
#else
# define SIZEOF_SYM ((size_t)14)
#endif

struct xsym {
    char name[8];
    union {
	unsigned char  _flags[2];
	unsigned short _sflgs;
    } u;
    unsigned long value;
    char tail[SIZEOF_SYM];
};

#define flags	u._flags[0]
#define xflags  u._flags[1]
#define sflgs   u._sflgs

#ifdef __STDC__
# define P(s) s
#else
# define P(s) ()
#endif

int main P((int argc , char **argv ));
int cmp P((struct xsym *s1 , struct xsym *s2 ));
int doname P((char *s , int many ));
long dohdr P((FILE *fd ));
void dosym P((struct xsym *s ));
int not_glob P((unsigned int x ));
void sflags P((struct xsym *s ));
void ckfread P((void *buf , int siz , int n , FILE *fp ));
long readhead P((struct aexec *h , int n , FILE *fd ));
int readsyms P((struct xsym *syms , int n , FILE *fd ));
void usage P((void ));

#undef P


int gflag;

int main(argc, argv)
int argc;
char **argv;
{
    int c, status = 0;
    extern char *optarg;
    extern int optind;
    while ((c = getopt(argc, argv, "g")) != -1)
	switch (c)
	{
	  case 'g':
	    gflag = 1;
	    break;
	  case '?':
	    usage();
	}
    c = argc - optind;
    if(c <= 0)
	usage();
    
    for (; optind < argc; optind++) 
	status |= doname(argv[optind], (c > 1));
    return status;
}

int  cmp(s1, s2)
struct xsym *s1, *s2;
{
    return(s1->value - s2->value);
}

int doname(s, many)
char *s;
int many;
{
    FILE *fd, *fopen();
    long i, count;

    if(!(fd = fopen(s, READB)))
    {
	perror(s);
	return 2;
    }
    if (many)
	printf("\n%s:\n", s);

    if (i = dohdr(fd))
    {
	struct xsym *syms, *savesyms;
	struct xsym *currsym;
	printf("%ld slots mem %ld\n", i, (long)(i*sizeof(struct xsym)));
	
	if((savesyms = syms = (struct xsym *)malloc(i*sizeof(struct xsym))) == NULL)
	{
	    perror("Outa mem");
	    exit(4);
	}
	count = i;
	currsym = syms;
	while (count)
	{
	    if(readsyms(currsym, 1, fd) != 1)
	    {
		perror("reading syms");
		exit(8);
	    }
	    if (--count)
	    {
		if (A_LNAM == (currsym->xflags & A_LNAM))
		{
		    if(fread(currsym->tail, SIZEOF_SYM, 1, fd) != 1)
		    {
			perror("reading syms");
			exit(16);
		    }
		    --i;
		    --count;
		}
	    }
	    else /* object was partially stripped */
		currsym->xflags &= ~A_LNAM;		
	    if (gflag && not_glob((unsigned int)(currsym->flags)))
		--i;
	    else
		currsym++;
	}
	fclose(fd);
	printf("%ld %s symbol(s)\n", i, (gflag ? "global ": ""));
	qsort(syms, i, sizeof(struct xsym), cmp);
	while (i--)
	{
	    dosym(syms);
	    syms += 1;
	}
	free(savesyms);
    }
    return 0;
}

long dohdr(fd)
FILE *fd;
{
    struct aexec h;
    long i;
        
    i = readhead(&h, 1, fd);
    if (i != 1 || h.a_magic != CMAGIC)
    {
	fprintf(stderr, "Bad header\n");
	return 0L;
    }
    fseek(fd, (h.a_text + h.a_data), 1);
    return h.a_syms / SIZEOF_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);
    s->xflags &= ~A_LNAM;
    sflags(s);
    putchar('\n');
}

char *fname[] = {
	"?0?", " bss", " text", "?3?", " data",
	"?5?", "?6?", "?7?"
};
char *Fname[] = {
	"?0?", "Bss", "Text", "?3?", "Data",
	"?5?", "?6?", "?7?"
};

int not_glob(x)
unsigned int x;
{
	x &= 0xff;
	if (x & 0x20)
		return 0;
	x &= ~0x20;
	if (x == 0x88)
		return 0;
	return 1;
}

void
sflags(s)
struct xsym *s;
{
    unsigned int x = s->flags;
    char **category;
    int lflag;
	
    if(s->sflgs == A_TFILE)
	printf("text file");
    else if (s->sflgs == A_TFARC)
	printf("text file archive");
    else
    {
	if (0 != (lflag = not_glob(x)))
	    category = fname;
	else
	    category = Fname;

	x &= 0xff;
	if (x & 0x20)
	    printf("global ");
	x &= ~0x20;
	if (x & 0xd8)
	{
	    if (x & 0x08)
		printf (" external");
	    if (x & 0x50)
	    {
		printf (" equ");
		if (x & 0x10)
		    printf (" reg");
	    }
	    if (x & 0x80)
		printf (" abs");
	    if (!lflag)
		printf(" G");
	}
	else
	{
	    x &= 7;
	    printf(category[x]);
	}
    }
    printf(" (0x%04x)",(unsigned int)(s->sflgs));
}

void ckfread(buf, siz, n, fp)
void *buf;
int siz, n;
FILE *fp;
{
	if(fread(buf, siz, n, fp) != n)
	{
	    perror("reading");
	    exit(32);
	}
}

long readhead(h, n, fd)
struct aexec *h;
int n;
FILE *fd;
{
    short i;
    long j;
    int k;

    for(k = 0; k < n; k++)
    {
	ckfread(&i, 2, 1, fd);
	h->a_magic = i;
	ckfread(&j, 4, 1, fd);
	h->a_text = j;
	ckfread(&j, 4, 1, fd);
	h->a_data = j;
	ckfread(&j, 4, 1, fd);
	h->a_bss = j;
	ckfread(&j, 4, 1, fd);
	h->a_syms = j;
	ckfread(&j, 4, 1, fd);
	h->a_AZero1 = j;
	ckfread(&j, 4, 1, fd);
	h->a_ldflgs = j;
	ckfread(&i, 2, 1, fd);
	h->a_isreloc = i;
    }
    return k;
}

int readsyms(syms, n, fd)
struct xsym *syms;
int n;
FILE *fd;
{
    int k;

    for(k = 0; k < n; k++, syms++)
    {
	ckfread(syms->name, 8, 1, fd);
	ckfread(&(syms->sflgs), 2, 1, fd);
	ckfread(&(syms->value), 4, 1, fd);
   }
   return k;
}

void usage()
{
    fprintf(stderr, "cnm [-g] files...\n");
    exit(1);
}

#if !defined(__GNUC__) &&  defined(hpux)

char *xmalloc(n)
int n;	/* sigh! */
{
   char *ret = (char *)malloc(n);

   if(ret) return ret;
   perror("malloc");
   exit(1);
}

char *xrealloc(b, n)
char * b;
int n;	/* sigh! */
{
   char *ret = (char *)realloc(b, n);

   if(ret) return ret;
   perror("realloc");
   exit(2);
}
#endif
