/*
 * utility to adjust _stksize in gcc-cc1.ttp
 *
 *	Usage: fixstk size [<filename>]
 *	  size:	specified as # of bytes 		nnn
 *		specified as # of Kilo Bytes		nnnK
 *		specified as # of Mega Bytes		nnnM
 *	  filename:
 *		optional, defaults to \.gcc-cc1.ttp
 *
 *	++jrb
 *
 *      modified to allow fixing applications for which size of a stack
 *      is defined via _initial_stack -- mj
 */

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

#if __STDC__
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#else
#include <string.h>
extern char *malloc();
extern long lseek();
#define size_t unsigned long
#endif

#ifndef FILENAME_MAX
# define FILENAME_MAX 128
#endif

#ifdef WORD_ALIGNED
# define SIZEOF_AEXEC ((2*sizeof(short)) + (6*sizeof(long)))
# define SIZEOF_ASYM  ((SYMLEN*sizeof(char)) + sizeof(short) + sizeof(long))
# define SYM_OFFSET   (sizeof(short) + (3*sizeof(long)))
#endif


static char *sym_names[] = { "__stksize", "__initial_stack" };

long find_offset (fd, fn, what)
int fd;
char *fn;
int *what;
{
    struct aexec head;
    struct asym  sym;
    int found;
    int	all = 0;
    int index = 1;
    
#ifndef WORD_ALIGNED
    if(read(fd, &head, sizeof(head)) != sizeof(head))
#else
    if(read_head(fd, &head))
#endif
    {
	perror(fn);
	exit(2);
    }
    if(head.a_magic != CMAGIC)
    {
	fprintf(stderr,"Invalid magic number %x\n", head.a_magic);
	exit(3);
    }
    if(head.a_syms == 0)
    {
	fprintf(stderr,"%s: no symbol table\n", fn);
	exit(4);
    }
    if(lseek(fd, head.a_text+head.a_data, 1) != 
#ifndef WORD_ALIGNED
       (head.a_text+head.a_data+sizeof(head)))
#else
       (head.a_text+head.a_data+SIZEOF_AEXEC))
#endif
    {
	perror(fn);
	exit(5);
    }
    for(;;)
    {
#ifndef WORD_ALIGNED
	if(index && (read(fd, &sym, sizeof(sym)) != sizeof(sym)))
#else
	if(index && read_sym(fd, &sym))
#endif
	{
	    fprintf(stderr, "symbol _stksize not found\n");
	    exit(6);
	}
	/* after symbol read check first for _stksize */
	index ^= 1;
	if (strncmp(sym_names[index], sym.a_name, 8) == 0)
	{
	    if ((found = (sym.a_type & A_DATA)) || all++)
		break;
	}
    }
    
    if(!found)
    {
	fprintf(stderr, "symbol _stksize is undefined\n");
	exit(9);
    }
    *what = index;
#ifndef WORD_ALIGNED
    return sym.a_value + sizeof(head);
#else
    return sym.a_value + SIZEOF_AEXEC;
#endif
}

long calc_newsize(s)
char *s;
{
    size_t len = strlen(s) - 1;
    long mul = 1;
    long atol();

    switch(s[len])
    {
      case 'k': case 'K':
	mul = 1L << 10;
	break;
      case 'm': case 'M':
	mul = 1L << 20;
	break;
      default:
	len += 1;
    }
    
    s[len] = '\0';
    return mul * atol(s);
}

int main(argc, argv)
int argc;
char **argv;
{
    int fd;
    int what;
    long newstksize, stksize, offset;
    char sizestr[16], fn[FILENAME_MAX];
    
    if(argc < 2)
    {
	fprintf(stderr, "usage: fixstk size [<filename>]\n");
	exit(1);
    }

    strcpy(sizestr, *++argv);

    if(argc > 2)
	(void) strcpy(fn, *++argv);
    else
	(void) strcpy(fn, "gcc-cc1.ttp");

    if((fd = open(fn, 2)) < 0)
    {
	perror(fn);
	exit(1);
    }
    
    newstksize = calc_newsize(sizestr);
    offset = find_offset(fd, fn, &what);
    if(lseek(fd, offset, 0) != offset)
    {
	perror(fn);
	close(fd);
	exit(7);
    }
    read(fd, &stksize, sizeof(long));
    printf("%s: %s was %ld (%dK)\n",
    	 fn, sym_names[what] + 1, stksize, (int)(stksize/1024));
    
    lseek(fd, -((long)sizeof(long)), 1);
    
    if(write(fd, &newstksize, sizeof(long)) != sizeof(long))
    {
	perror(fn);
	close(fd);
	exit(8);
    }
    
    lseek(fd, -((long)sizeof(long)), 1);
    
    read(fd, &stksize, sizeof(long));
    printf("%s: %s now is %ld (%dK)\n",
    	 fn, sym_names[what] + 1, stksize, (int)(stksize/1024));
    return close(fd);
}

#ifdef WORD_ALIGNED
#ifndef atarist
# define lread  read
# define lwrite write
#endif

/*
 * read header -- return !0 on err
  */
#define ck_read(fd, addr, siz) \
  if((long)siz != lread(fd, addr, (long)siz)) return !0;
  
int read_head (fd, a)
int fd;
struct aexec *a;
{
    ck_read(fd, &a->a_magic,   sizeof(a->a_magic));
    ck_read(fd, &a->a_text,    sizeof(a->a_text));
    ck_read(fd, &a->a_data,    sizeof(a->a_data));
    ck_read(fd, &a->a_bss,     sizeof(a->a_bss));
    ck_read(fd, &a->a_syms,    sizeof(a->a_syms));
    ck_read(fd, &a->a_AZero1,  sizeof(a->a_AZero1));
    ck_read(fd, &a->a_AZero2,  sizeof(a->a_AZero2));
    ck_read(fd, &a->a_isreloc, sizeof(a->a_isreloc));
    return 0;
}

int read_sym(fd, s)
int fd;
struct asym *s;
{
    ck_read(fd, s->a_name, 8);
    ck_read(fd, &(s->a_type), sizeof(short));
    ck_read(fd, &(s->a_value), sizeof(long));
    return 0;
}
#endif
