#include <sgtty.h>
#include <sys/file.h>
#include <a.out.h>
#include <signal.h>
#include <sys/wait.h>

#ifndef	DDTCAT
#include	<pdefs.h>
#include	<ptypes.h>
#include	"mdep.h"
#include	"ddt.h"

ext	ddtst	d_hcds;

#endif

/* int	ddtregs[16]; */
int	ddtpc, ddtps;
int	noddt = 0;	/* This is a flag used in the standalone version */

struct sgttyb term;		/* For keeping the terminal modes straight */
short	oflags, nflags;
int	proc_id;		/* PID of debugged process */

extern struct nlist *symbol_table, *end_syms;
extern char *environ[];
extern int ps_ptr, pc_ptr;

main(argc, argv)
int argc;
char *argv[];
{
    int fd;
    struct nlist *sym;
    int done(), suspend();

    if (argc == 1) {
	printf("Usage:\n\t%s file\n", argv[0]);
	return 1;
    }
    fd = open(argv[1], O_RDONLY, 0777);
    if (fd < 0) {
	printf("Couldn't open %s for read\n", argv[1]);
	return 1;
    }
    if (!d_initsym(fd)) {
	printf("Error reading symbol table\n");
	return 1;
    }
    close(fd);

    /* Set up so that SIGINT and SIGTSTP restore the terminal before exiting
     * and the terminal is fixed again when ddt is continued. */
    signal(SIGINT, done);
    signal(SIGTSTP, suspend);

    gtty(0, &term);
    oflags = nflags = term.sg_flags;
    nflags |= CBREAK;
    nflags &= ~CRMOD;
    nflags &= ~ECHO;
    term.sg_flags = nflags;
    stty(0, &term);

    DDT(++argv);
}

done()
{
    term.sg_flags = oflags;
    stty(0, &term);
    printf("\n");
    exit(0);
}

suspend()
{
    term.sg_flags = oflags;
    stty(0, &term);
    kill(0, SIGSTOP);
    term.sg_flags = nflags;
    stty(0, &term);
}

DDT(argv)
char *argv[];
{
    union wait status;
    BPTTYP inst;
    char *msgp;
    char msg[128];

    mkioptab();	/* initialize disasembler (needs to somewhere else) */
    d_clrst();

    if ((proc_id = run_child(argv, &status, 1)) == -1)
      done();
    if (!WIFSTOPPED(status)) {
	printf("Child didn't stop: %d\n", status.w_termsig);
	done();
    }
    ddtpc = ptrace(1, proc_id, pc_ptr, 0);
    ddtps = ptrace(1, proc_id, ps_ptr, 0);

    ddthc();
    for (;;) {
	ptrace(1, proc_id, pc_ptr, ddtpc);
	ptrace(1, proc_id, ps_ptr, ddtps);

	term.sg_flags = oflags;
	stty(0, &term);

	/* If the trace bit was set in the PSL, then single step */
	ptrace(ddtps & PSTRC ? 9 : 7, proc_id, 1, 0);
	wait(&status);
	term.sg_flags = nflags;
	stty(0, &term);
	if (!WIFSTOPPED(status)) {
	    printf("Child terminated: signal %d,status %d\n",
		   status.w_termsig, status.w_retcode);
	    done();			/* Shouldn't just exit here */
	}
	ddtpc = ptrace(1, proc_id, pc_ptr, 0);
	ddtps = ptrace(1, proc_id, ps_ptr, 0);
	switch (status.w_stopsig) {
	case SIGTRAP:
	    md_fetch(&d_hcds, A_INST, sizeof(inst), ddtpc, &inst);
	    if (inst == BPTINST)
	      ddtbpt();
	    else
	      ddttrc();
	    continue;
	case SIGSEGV:
	    msgp = "Segmentation violation";
	    break;
	default:
	    sprintf(msg, "Child stopped: signal %d", status.w_stopsig);
	    msgp = msg;
	    break;
	}
	ddtrte(msgp);
    }
}

ngetchar()
{
	char	c;

	c = getchar() & 0177;
	if (c == 4) {
		stty(0, &term);
		exit();
	}
	return c;
}

/* a puts() which  doesn't put a newline on the end */
puts(s)
char	*s;
{
	printf("%s", s);
}

/* Forks off a process.  Waits for some status and returns it. */
run_child(argv, status, trace)
char *argv[];
struct wait *status;
int trace;
{
    int proc_id;
    int wpid;
    int pgrp;

    term.sg_flags = oflags;
    stty(0, &term);

    if ((proc_id = fork()) == 0) {
	if (trace) {
	    signal(SIGSEGV, SIG_IGN);
	    ptrace(0);
	}
	if (execvp(argv[0], argv) == -1) {
	    printf("Could exec %s\n", argv[0]);
	    perror("");
	    exit(-1);
	}
    }
    else {
	if (proc_id == -1) {
	    perror("DDT couldn't fork to run child");
	    return proc_id;
	}

/*	setpgrp(proc_id, proc_id);
	ioctl(0, TIOCSPGRP, proc_id); */
	while ((wpid = wait(status)) != proc_id)
	  ;
/*	ioctl(0, TIOCSPGRP, getpgrp(0)); */
	term.sg_flags = nflags;
	stty(0, &term);
	return proc_id;
    }
}
