/*
 * patchsym a.out old new [old new [old new ... ]]
 *
 * Modifies the given a.out in-place to change symbol names as given on the
 * command line.
 *
 * This program is (at the moment) defined for suns only.
 */

#ifdef sun

#include <stdio.h>
#include <a.out.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

/*extern*/ char **argvec;

void *sbrk();
#define SBRKERR ((void*)-1)

int aout_fd;
struct stat stb;
struct exec hdr;
int symsize;
char *symtbl;
int strsize;
char *strtbl;

growstrings(nb)
int nb;
{
 char *new;

 new = (char*)sbrk(nb);
 if (new != strtbl+strsize)
  { sbrk(strsize);
    bcopy(strtbl,new,strsize);
  }
 strsize += nb;
}

main(ac,av)
int ac;
char **av;
{
 int nchg;
 int i;
 int j;
 int l;
 struct nlist *sym;
 char *name;

 argvec = av;
 if ((ac & 1) || (ac < 4))
  { fprintf(stderr,"Usage: %s a.out-file old new [old new [old new ... ]]\n",argvec[0]);
    exit(1);
  }
 nchg = (ac / 2) - 1;
#define OLDNAME(i) av[(((i)*2)+2)]
#define NEWNAME(i) av[(((i)*2)+3)]
/*
 for (i=0;i<nchg;i++)
  { if (strlen(OLDNAME(i)) != strlen(NEWNAME(i)))
     { fprintf(stderr,"%s: old and new symbol names must be the same length (%s->%s)\n",argvec[0],OLDNAME(i),NEWNAME(i));
       exit(1);
     }
  }
*/
 aout_fd = open(av[1],O_RDWR,0);
 if (aout_fd < 0)
  { fprintf(stderr,"%s: can't open ",argvec[0]);
    perror(av[1]);
    exit(1);
  }
 if (fstat(aout_fd,&stb) < 0)
  { fprintf(stderr,"%s: can't get size of %s\n",argvec[0],av[1]);
    exit(1);
  }
 if (read(aout_fd,(char *)&hdr,sizeof(hdr)) != sizeof(hdr))
  { fprintf(stderr,"%s: can't read a.out header from %s\n",argvec[0],av[1]);
    exit(1);
  }
 if (N_BADMAG(hdr))
  { fprintf(stderr,"%s: %s: bad magic number\n",argvec[0],av[1]);
    exit(1);
  }
 symsize = hdr.a_syms / sizeof(struct nlist);
 symtbl = (char*)sbrk(symsize*sizeof(struct nlist));
 if (symtbl == SBRKERR)
  { fprintf(stderr,"%s: can't allocate %lu bytes of memory for symbol table\n",symsize*sizeof(struct nlist));
    exit(1);
  }
 strsize = stb.st_size - N_STROFF(hdr);
 strtbl = (char*)sbrk(strsize+1);
 if (strtbl == SBRKERR)
  { fprintf(stderr,"%s: can't allocate %d bytes of memory for string table\n",strsize);
    exit(1);
  }
 if ( (lseek(aout_fd,N_SYMOFF(hdr),SEEK_SET) < 0) ||
      (read(aout_fd,symtbl,hdr.a_syms) != hdr.a_syms) )
  { fprintf(stderr,"%s: can't read symbol table from %s\n",argvec[0],av[1]);
    exit(1);
  }
 if ( (lseek(aout_fd,N_STROFF(hdr),SEEK_SET) < 0) ||
      (read(aout_fd,strtbl,strsize) != strsize) )
  { fprintf(stderr,"%s: can't read string table from %s\n",argvec[0],av[1]);
    exit(1);
  }
 if (strtbl[strsize-1] != '\0')
  { growstrings(1);
    strtbl[strsize-1] = '\0';
  }
 for (i=0;i<symsize;i++)
  { sym = ((struct nlist *) symtbl) + i;
    if ((sym->n_un.n_strx <= 0) || (sym->n_un.n_strx >= strsize)) continue;
    name = strtbl + sym->n_un.n_strx;
    for (j=0;j<nchg;j++)
     { if (!strcmp(name,OLDNAME(j))) break;
     }
    if (j < nchg)
     { l = strlen(NEWNAME(j));
       if (l <= strlen(OLDNAME(j)))
	{ strcpy(strtbl+sym->n_un.n_strx,NEWNAME(j));
	}
       else
	{ growstrings(l+1);
	  bcopy(NEWNAME(j),&strtbl[strsize-l-1],l+1);
	  sym->n_un.n_strx = strsize - l - 1;
	}
     }
  }
 if ( (lseek(aout_fd,N_SYMOFF(hdr),SEEK_SET) < 0) ||
      (write(aout_fd,(char *)symtbl,symsize*sizeof(struct nlist)) != symsize*sizeof(struct nlist)) )
  { fprintf(stderr,"%s: error writing new symbol table, result is probably mangled\n",argvec[0]);
    exit(1);
  }
 *(int *)strtbl = strsize;
 if ( (lseek(aout_fd,N_STROFF(hdr),SEEK_SET) < 0) ||
      (write(aout_fd,(char *)strtbl,strsize) != strsize) )
  { fprintf(stderr,"%s: error writing new string table, result is probably mangled\n",argvec[0]);
    exit(1);
  }
 exit(0);
}

#endif
