/* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)main.c 1.6 8/16/83";

/*
 * Debugger main routine.
 */

#include "defs.h"
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include "main.h"
#include "symbols.h"
#include "scanner.h"
#include "process.h"
#include "source.h"
#include "object.h"
#include "mappings.h"

#ifndef public

#define isterm(file)	(interactive or isatty(fileno(file)))

#include <sgtty.h>

typedef struct {
    struct sgttyb sgttyb;
    struct tchars tchars;
    int lmode;
    struct ltchars ltchars;
    int fcntl_flags;
    int fcntl_owner;
  } Ttyinfo;

#endif

public Boolean coredump;		/* true if using a core dump */
public Boolean runfirst;		/* run program immediately */
public Boolean interactive;		/* standard input IS a terminal */
public Boolean lexdebug;		/* trace yylex return values */
public Boolean tracebpts;		/* trace create/delete breakpoints */
public Boolean traceexec;		/* trace process execution */
public Boolean tracesyms;		/* print symbols as their read */

public File corefile;			/* File id of core dump */

#define FIRST_TIME 0			/* initial value setjmp returns */

private Boolean initdone = false;	/* true if initialization done */
private jmp_buf env;			/* setjmp/longjmp data */
private char outbuf[BUFSIZ];		/* standard output buffer */
private char namebuf[512];		/* possible name of object file */
private int firstarg;			/* first program argument (for -r) */

private Ttyinfo ttyinfo;

private catchintr();

/*
 * Main program.
 */

main(argc, argv)
int argc;
String argv[];
{
    register Integer i;
    extern String date;

    cmdname = argv[0];
    catcherrs();
    onsyserr(EINTR, nil);
    setbuf(stdout, outbuf);
    printf("dbx version of %s.\nType 'help' for help.\n", date);
    fflush(stdout);
    scanargs(argc, argv);
    language_init();
    symbols_init();
    process_init();
    if (runfirst) {
	if (setjmp(env) == FIRST_TIME) {
	    arginit();
	    for (i = firstarg; i < argc; i++) {
		newarg(argv[i]);
	    }
	    run();
	    /* NOTREACHED */
	} else {
	    runfirst = false;
	}
    } else {
	init();
    }
    setjmp(env);
    restoretty(stdout, &ttyinfo);
    signal(SIGINT, catchintr);
    yyparse();
    putchar('\n');
    quit(0);
}

/*
 * Initialize the world, including setting initial input file
 * if the file exists.
 */

public init()
{
    File f;
    String home;
    char buf[100];
    extern String getenv();

    savetty(stdout, &ttyinfo);
    enterkeywords();
    scanner_init();
    if (not coredump and not runfirst) {
	start(nil, nil, nil);
    }
    printf("reading symbolic information ...");
    fflush(stdout);
    readobj(objname);
    printf("\n");
    fflush(stdout);
    if (coredump) {
	curfunc = whatblock(pc);
    } else {
	curfunc = program;
    }
    bpinit();
    f = fopen(initfile, "r");
    if (f != nil) {
	fclose(f);
	setinput(initfile);
    } else {
	home = getenv("HOME");
	if (home != nil) {
	    sprintf(buf, "%s/%s", home, initfile);
	    f = fopen(buf, "r");
	    if (f != nil) {
		fclose(f);
		setinput(strdup(buf));
	    }
	}
    }
    initdone = true;
}

/*
 * Re-initialize the world, first de-allocating all storage.
 * This is necessary when the symbol information must be re-read
 * from the object file when it has changed.
 *
 * Before "forgetting" things, we save the current tracing/breakpoint
 * information to a temp file.  Then after re-creating the world,
 * we read the temp file as commands.  This isn't always the right thing;
 * if a procedure that was being traced is deleted, an error message
 * will be generated.
 *
 * If the argument vector is not nil, then this is re-initialize is being
 * done in preparation for running the program.  Since we want to process
 * the commands in the temp file before running the program, we add the
 * run command at the end of the temp file.  In this case, reinit longjmps
 * back to parsing rather than returning.
 */

public reinit(argv, infile, outfile)
String *argv;
String infile;
String outfile;
{
    register Integer i;
    String tmpfile;
    extern String mktemp();

    tmpfile = mktemp("/tmp/dbxXXXX");
    setout(tmpfile);
    status();
    print_alias(nil);
    if (argv != nil) {
	printf("run");
	for (i = 1; argv[i] != nil; i++) {
	    printf(" %s", argv[i]);
	}
	if (infile != nil) {
	    printf(" < %s", infile);
	}
	if (outfile != nil) {
	    printf(" > %s", outfile);
	}
	putchar('\n');
    }
    unsetout();
    bpfree();
    objfree();
    process_init();
    enterkeywords();
    scanner_init();
    readobj(objname);
    bpinit();
    fflush(stdout);
    setinput(tmpfile);
    unlink(tmpfile);
    if (argv != nil) {
	longjmp(env, 1);
	/* NOTREACHED */
    }
}

/*
 * After a non-fatal error we jump back to command parsing.
 */

public erecover()
{
    if (initdone) {
	gobble();
	longjmp(env, 1);
    }
}

/*
 * This routine is called when an interrupt occurs.
 */

private catchintr()
{
    putchar('\n');
    longjmp(env, 1);
}

/*
 * Scan the argument list.
 */

private scanargs(argc, argv)
int argc;
String argv[];
{
    register int i, j;
    register Boolean foundfile;
    register File f;
    char *tmp;

    runfirst = false;
    interactive = false;
    lexdebug = false;
    tracebpts = false;
    traceexec = false;
    tracesyms = false;
    foundfile = false;
    corefile = nil;
    coredump = true;
    sourcepath = list_alloc();
    list_append(list_item("."), nil, sourcepath);
    i = 1;
    while (i < argc and (not foundfile or (corefile == nil and not runfirst))) {
	if (argv[i][0] == '-') {
	    if (streq(argv[i], "-I")) {
		++i;
		if (i >= argc) {
		    fatal("missing directory for -I");
		}
		list_append(list_item(argv[i]), nil, sourcepath);
	    } else {
		for (j = 1; argv[i][j] != '\0'; j++) {
		    setoption(argv[i][j]);
		}
	    }
	} else if (not foundfile) {
	    objname = argv[i];
	    foundfile = true;
	} else if (coredump and corefile == nil) {
	    corefile = fopen(argv[i], "r");
	    if (corefile == nil) {
		coredump = false;
	    }
	}
	++i;
    }
    if (i < argc and not runfirst) {
	fatal("extraneous argument %s", argv[i]);
    }
    firstarg = i;
    if (not foundfile and isatty(0)) {
	printf("enter object file name (default is `%s'): ", objname);
	fflush(stdout);
	gets(namebuf);
	if (namebuf[0] != '\0') {
	    objname = namebuf;
	}
    }
    f = fopen(objname, "r");
    if (f == nil) {
	fatal("can't read %s", objname);
    } else {
	fclose(f);
    }
    if (rindex(objname, '/') != nil) {
	tmp = strdup(objname);
	*(rindex(tmp, '/')) = '\0';
	list_append(list_item(tmp), nil, sourcepath);
    }
    if (coredump and corefile == nil) {
	corefile = fopen("core", "r");
	if (corefile == nil) {
	    coredump = false;
	}
    }
}

/*
 * Take appropriate action for recognized command argument.
 */

private setoption(c)
char c;
{
    switch (c) {
	case 'r':   /* run program before accepting commands */
	    runfirst = true;
	    coredump = false;
	    break;

	case 'i':
	    interactive = true;
	    break;

	case 'b':
	    tracebpts = true;
	    break;

	case 'e':
	    traceexec = true;
	    break;

	case 's':
	    tracesyms = true;
	    break;

	case 'l':
#   	    ifdef LEXDEBUG
		lexdebug = true;
#	    else
		fatal("\"-l\" only applicable when compiled with LEXDEBUG");
#	    endif
	    break;

	default:
	    fatal("unknown option '%c'", c);
    }
}

/*
 * Save/restore the state of a tty.
 */

public savetty(f, t)
File f;
Ttyinfo *t;
{
    gtty(fileno(f), &t->sgttyb);
    ioctl(fileno(f), TIOCGETC, &t->tchars);
    ioctl(fileno(f), TIOCLGET, &t->lmode);
    ioctl(fileno(f), TIOCGLTC, &t->ltchars);
    t->fcntl_flags = fcntl(fileno(f), F_GETFL);
    t->fcntl_owner = fcntl(fileno(f), F_GETOWN);
}

public restoretty(f, t)
File f;
Ttyinfo *t;
{
    ioctl(fileno(f), TIOCSETN, &t->sgttyb);	/* instead of stty --saj */
    ioctl(fileno(f), TIOCSETC, &t->tchars);
    ioctl(fileno(f), TIOCLSET, &t->lmode);
    ioctl(fileno(f), TIOCSLTC, &t->ltchars);
    fcntl(fileno(f), F_SETFL, t->fcntl_flags);
    fcntl(fileno(f), F_SETOWN, t->fcntl_owner);
}

/*
 * Exit gracefully.
 */

public quit(r)
Integer r;
{
    exit(r);
}
