#include <sys/file.h>
#include <sys/types.h>
#include <filehdr.h>
#include <aouthdr.h>
#include <scnhdr.h>
#include <sym.h>

extern char *sbrk();

#define BADAOUT(err_msg) {\
    Dump_Finalize;\
    Primitive_Error (err_msg);\
}

#define CHECK_SCNHDR(ptr, name, flags) {\
    if (strcmp (hdr.section[i].s_name, name) == 0) {\
	if (hdr.section[i].s_flags != flags)\
	    Primitive_Error ("bad flags in section header");\
        ptr = hdr.section + i;\
	i++;\
    } else {\
	ptr = 0;\
    }\
}

#define WRITE(fd, buf, size, errmsg)\
    if (write (fd, buf, size) != size) {\
	Dump_Finalize;\
	Primitive_Error (errmsg);\
    }

#define SEEK(fd, pos, errmsg)\
    if (lseek (fd, pos, L_SET) != pos) {\
	Dump_Finalize;\
	Primitive_Error (errmsg);\
    }

#define BUFSIZE 8192

static struct scnhdr *text_section;
static struct scnhdr *init_section;
static struct scnhdr *rdata_section;
static struct scnhdr *data_section;
static struct scnhdr *lit8_section;
static struct scnhdr *lit4_section;
static struct scnhdr *sdata_section;
static struct scnhdr *sbss_section;
static struct scnhdr *bss_section;
static struct scnhdr *rsrc_section;

struct headers {
    struct filehdr fhdr;
    struct aouthdr aout;
    struct scnhdr section[10];
};

Object P_Dump (ofile) Object ofile; {
    char buf[BUFSIZE];
    int i;			/* a simple counter */
    int newsyms;
    int symrel;
    int nread;			/* number of bytes read */
    int scnptr;			/* the section being worked on */
    int vaddr;
    int pagesize;
    unsigned data_end;
    static struct headers hdr;

    Dump_Prolog;

    hdr = *((struct headers *)TEXT_START);
  
    if (hdr.fhdr.f_magic != MIPSELMAGIC && hdr.fhdr.f_magic != MIPSEBMAGIC)
	BADAOUT("input file has bad magic number");

    if (hdr.fhdr.f_opthdr != sizeof(hdr.aout))
	BADAOUT("input file's a.out header has the wrong size");
  
    if (hdr.aout.magic != ZMAGIC)
	BADAOUT("input file is not a ZMAGIC file");

    i = 0;
    CHECK_SCNHDR(text_section,  _TEXT,     STYP_TEXT);
    CHECK_SCNHDR(init_section,  _INIT,     STYP_INIT);
    CHECK_SCNHDR(rdata_section, _RDATA,    STYP_RDATA);
    CHECK_SCNHDR(data_section,  _DATA,     STYP_DATA);
#ifdef _LIT8
    CHECK_SCNHDR(lit8_section,  _LIT8,     STYP_LIT8);
    CHECK_SCNHDR(lit4_section,  _LIT4,     STYP_LIT4);
#endif
    CHECK_SCNHDR(sdata_section, _SDATA,    STYP_SDATA);
    CHECK_SCNHDR(sbss_section,  _SBSS,     STYP_SBSS);
    CHECK_SCNHDR(bss_section,   _BSS,      STYP_BSS);
#ifdef _RESOURCE
    CHECK_SCNHDR(rsrc_section,  _RESOURCE, _STYP_RESOURCE);
#endif
    if (i != hdr.fhdr.f_nscns)
	Primitive_Error ("~s sections expected, ~s found",
	    Make_Fixnum (hdr.fhdr.f_nscns), Make_Fixnum (i));

    Was_Dumped = 1;

    pagesize = getpagesize();
    data_end = ((unsigned)sbrk (0) + pagesize - 1) & (-pagesize);
    hdr.aout.dsize = data_end - DATA_START;
    hdr.aout.bsize =0;
  
    {
	extern __start();
	hdr.aout.entry = (unsigned)__start;
    }

    hdr.aout.bss_start = hdr.aout.data_start + hdr.aout.dsize;

    data_section->s_vaddr = DATA_START + rdata_section->s_size;
    data_section->s_paddr = DATA_START + rdata_section->s_size;

    data_section->s_size = data_end - DATA_START;
    data_section->s_scnptr = rdata_section->s_scnptr + rdata_section->s_size;

    vaddr = data_section->s_vaddr + data_section->s_size;
    scnptr = data_section->s_scnptr + data_section->s_size;
    if (lit8_section) {
	lit8_section->s_vaddr = data_section->s_vaddr + data_section->s_size;
	lit8_section->s_paddr = data_section->s_paddr + data_section->s_size;
	lit8_section->s_scnptr = data_section->s_scnptr + data_section->s_size;
    }
    if (sdata_section && lit8_section) {
	sdata_section->s_vaddr = lit8_section->s_vaddr + lit8_section->s_size;
	sdata_section->s_paddr = lit8_section->s_paddr + lit8_section->s_size;
	sdata_section->s_scnptr = lit8_section->s_scnptr + lit8_section->s_size;
    }
    if (sbss_section) {
	sbss_section->s_vaddr = sdata_section->s_vaddr + sdata_section->s_size;
	sbss_section->s_paddr = sdata_section->s_paddr + sdata_section->s_size;
	sbss_section->s_scnptr = sdata_section->s_scnptr+sdata_section->s_size;
    }
    if (bss_section) {
	bss_section->s_vaddr = sbss_section->s_vaddr + sbss_section->s_size;
	bss_section->s_paddr = sbss_section->s_paddr + sbss_section->s_size;
	bss_section->s_scnptr = sbss_section->s_scnptr + sbss_section->s_size;
    }

    WRITE(ofd, TEXT_START, hdr.aout.tsize,
	"problem writing text section to output file: ~E");

    WRITE(ofd, DATA_START, hdr.aout.dsize,
	"problem writing data section to output file: ~E");
  
    /* Set up and copy the symbol table to the new executable
    */
    SEEK(afd, hdr.fhdr.f_symptr, "can't seek to start of symbol table: ~E");

    if ((nread = read (afd, buf, BUFSIZE)) < sizeof (HDRR))
	Primitive_Error ("problem reading symbol table");

#define symhdr ((pHDRR)buf)
    newsyms = hdr.aout.tsize + hdr.aout.dsize;
    symrel = newsyms - hdr.fhdr.f_symptr;
    hdr.fhdr.f_symptr = newsyms;
    symhdr->cbLineOffset += symrel;
    symhdr->cbDnOffset += symrel;
    symhdr->cbPdOffset += symrel;
    symhdr->cbSymOffset += symrel;
    symhdr->cbOptOffset += symrel;
    symhdr->cbAuxOffset += symrel;
    symhdr->cbSsOffset += symrel;
    symhdr->cbSsExtOffset += symrel;
    symhdr->cbFdOffset += symrel;
    symhdr->cbRfdOffset += symrel;
    symhdr->cbExtOffset += symrel;
#undef symhdr

    do {
	if (write (ofd, buf, nread) != nread)
	    Primitive_Error ("problem writing symbols to output file");
	if ((nread = read (afd, buf, BUFSIZE)) < 0)
	    Primitive_Error ("problem reading symbols");
    } while (nread != 0);

    SEEK(ofd, 0L, "problem seeking to start of header: ~E");
    WRITE(ofd, &hdr, sizeof(hdr), "problem writing header of output file: ~E");

    Dump_Epilog;
}
