/*
 * undump.c - Convert a core file to an a.out.
 *
 * Author:	Spencer W. Thomas
 * 		Computer Science Dept.
 * 		University of Utah
 * Date:	Wed Feb 17 1982
 * Copyright (c) 1982 Spencer W. Thomas
 * Copyright Xenix version (c) 1987 Paul De Bra
 *
 * Usage:
 * undump new-a.out [a.out] [core]
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/ulimit.h>
#include <core.h>

#define PSIZE	    10240
char page[PSIZE];

struct user u;
struct xexec hdr, ohdr;
struct xext exthdr;
struct xseg seghdr[100];
char mdttab[5000];
long datacnt=0;
long firstcnt=0;

main(argc, argv)
char **argv;
{
    char *new_name, *a_out_name = "a.out", *core_name = "core";
    FILE *new, *a_out, *core;

    if (scanargs(argc, argv, "undump new-a.out!s a.out%s core%s",
	    &new_name, &a_out_name, &core_name)
		    != 1)
	exit(1);

    if ((a_out = fopen(a_out_name, "r")) == NULL)
    {
	perror(a_out_name);
	exit(1);
    }
    if ((core = fopen(core_name, "r")) == NULL)
    {
	perror(core_name);
	exit(1);
    }
    if ((new = fopen(new_name, "w")) == NULL)
    {
	perror(new_name);
	exit(1);
    }

    read_u(core);
    make_hdr(new, a_out);
    copy_segtable(new,a_out);
    copy_text(new, a_out);
    copy_data(new, core);
    copy_sym(new, a_out);
    fclose(new);
    fclose(core);
    fclose(a_out);
    mark_x(new_name);
}

/*
 * read the u structure from the core file.
 */
read_u(core)
FILE *core;
{
    if ( fread(&u, sizeof u, 1, core) != 1 )
    {
	perror("Couldn't read user structure from core file");
	exit(1);
    }
}

/*
 * Make the header in the new a.out from the header in the old one
 * modified by the new data size.
 */
make_hdr(new, a_out)
FILE *new, *a_out;
{
    if (fread(&hdr, sizeof hdr, 1, a_out) != 1)
    {
	perror("Couldn't read header from a.out file");
	exit(1);
    }
    ohdr = hdr;
    if (hdr.x_text != u.u_exdata.x_text ||
	hdr.x_data + hdr.x_bss != u.u_exdata.x_data + u.u_exdata.x_bss)
    {
	fprintf(stderr, "Core file didn't come from this a.out\n");
	exit(1);
    }

    hdr.x_bss = 0;			/* all data is inited now! */

    if (fread(&exthdr, sizeof exthdr, 1, a_out) != 1)
    {
	perror("Couldn't read header extension from a.out file");
	exit(1);
    }
}
/* Copy the segment table from the a.out to the new a.out */
copy_segtable(new,a_out)
FILE *new, *a_out;
{
    int i;
    long ofset=exthdr.xe_segsize+exthdr.xe_segpos;
    fread(page,(int)exthdr.xe_segpos-sizeof(hdr)-sizeof(exthdr),1,a_out);
    if (fread(seghdr, (int)exthdr.xe_segsize,1,a_out) != 1)
    {
	perror("Couldn't read segment table from a.out");
	exit(1);
    }
    for (i=0;i<exthdr.xe_segsize/sizeof(struct xseg);i++)
    {
      if (seghdr[i].xs_type <3)
	seghdr[i].xs_psize=seghdr[i].xs_vsize;
      if (seghdr[i].xs_type == 2)
	if (firstcnt==0)
	  firstcnt = seghdr[i].xs_vsize;
	else
	  datacnt += seghdr[i].xs_vsize;
      if (seghdr[i].xs_type == 4)
        ofset+=4;
      seghdr[i].xs_filpos=ofset;
      ofset+=seghdr[i].xs_vsize;
    }
    hdr.x_data=firstcnt+datacnt;
    if (fwrite(&hdr, sizeof hdr, 1, new) != 1)
    {
	perror("Couldn't write header to new a.out file");
	exit(1);
    }
    if (fwrite(&exthdr, sizeof exthdr,1,new) != 1)
    {
	perror("Couldn't write header extension to new a.out file");
	exit(1);
    }
    fwrite(page,(int)exthdr.xe_segpos-sizeof(hdr)-sizeof(exthdr),1,new);
    if (fwrite(seghdr, (int)exthdr.xe_segsize,1,new)!=1)
    {
	perror("Couldn't write segment table to new a.out");
	exit(1);
    }
}

/*
 * Copy the text from the a.out to the new a.out
 */
copy_text(new, a_out)
FILE *new, *a_out;
{
    long txtcnt = hdr.x_text;

    if (hdr.x_magic == OMAGIC)
    {
	printf("a.out file is not shared text, getting text from core file\n");
	fseek(a_out, hdr.x_text, 1);	/* skip over text */
	return;
    }
    while (txtcnt >= PSIZE)
    {
	if (fread(page, PSIZE, 1, a_out) != 1)
	{
	    perror("Read failure on a.out text");
	    exit(1);
	}
	if (fwrite(page, PSIZE, 1, new) != 1)
	{
	    perror("Write failure in text segment");
	    exit(1);
	}
	txtcnt -= PSIZE;
    }
    if (txtcnt)
    {
	if (fread(page,(int)txtcnt, 1, a_out) != 1)
	{
	    perror("Read failure on a.out text");
	    exit(1);
	}
	if (fwrite(page, (int)txtcnt, 1, new) != 1)
	{
	    perror("Write failure in text segment");
	    exit(1);
	}
    }
}

/*
 * copy the data from the core file to the new a.out
 */
copy_data(new, core)
FILE *new, *core;
{
    struct stat corestat;
    fstat(fileno(core),&corestat);
    if (hdr.x_magic == OMAGIC)
	datacnt += u.u_tsize;
    fseek(core, corestat.st_size - hdr.x_data - u.u_ssize*512L, 0);
    while (firstcnt >= PSIZE)
    {
	if (fread(page, PSIZE, 1, core) != 1)
	{
	    perror("Read failure on core data");
	    exit(1);
	}
	if (fwrite(page, PSIZE, 1, new) != 1)
	{
	    perror("Write failure in data segment");
	    exit(1);
	}
	firstcnt -= PSIZE;
    }
    if (firstcnt)
    {
	if (fread(page, (int)firstcnt, 1, core) != 1)
	{
	    perror("Read failure on core data");
	    exit(1);
	}
	if (fwrite(page, (int)firstcnt, 1, new) != 1)
	{
	    perror("Write failure in data segment");
	    exit(1);
	}
    }
    fseek(core,(long)u.u_ssize*512L,1);
    while (datacnt >= PSIZE)
    {
	if (fread(page, PSIZE, 1, core) != 1)
	{
	    perror("Read failure on core data");
	    exit(1);
	}
	if (fwrite(page, PSIZE, 1, new) != 1)
	{
	    perror("Write failure in data segment");
	    exit(1);
	}
	datacnt -= PSIZE;
    }
    if (datacnt)
    {
	if (fread(page, (int)datacnt, 1, core) != 1)
	{
	    perror("Read failure on core data");
	    exit(1);
	}
	if (fwrite(page, (int)datacnt, 1, new) != 1)
	{
	    perror("Write failure in data segment");
	    exit(1);
	}
    }
}

/*
 * Copy the relocation information and symbol table from the a.out to the new
 */
copy_sym(new, a_out)
FILE *new, *a_out;
{
    int n;
    long total=0;

    fseek(a_out, ohdr.x_data, 1);	/* skip over data segment */
    while ((n = fread(page, 1, PSIZE, a_out)) > 0)
    {
	if (fwrite(page, 1, n, new) != n)
	{
	    perror("Error writing symbol table to new a.out");
	    fprintf(stderr, "new a.out should be ok otherwise\n");
	    return;
	}
	total+=n;
    }
    if (n < 0)
    {
	perror("Error reading symbol table from a.out");
	fprintf(stderr, "new a.out should be ok otherwise\n");
    }
}

/*
 * After succesfully building the new a.out, mark it executable
 */
mark_x(name)
char *name;
{
    struct stat sbuf;
    int um;

    um = umask(777);
    umask(um);
    if (stat(name, &sbuf) == -1)
    {
	perror ("Can't stat new a.out");
	fprintf(stderr, "Setting protection to %o\n", 0777 & ~um);
	sbuf.st_mode = 0777;
    }
    sbuf.st_mode |= 0111 & ~um;
    if (chmod(name, sbuf.st_mode) == -1)
	perror("Couldn't change mode of new a.out to executable");

}

