/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)startup.c	2.3 (Chris & John Downey) 9/4/92";
#endif

/***

* program name:
    xvi
* function:
    PD version of UNIX "vi" editor, with extensions.
* module name:
    main.c
* module function:
    Entry point for xvi; setup, argument parsing and signal handling.
* history:
    STEVIE - ST Editor for VI Enthusiasts, Version 3.10
    Originally by Tim Thompson (twitch!tjt)
    Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
    Heavily modified by Chris & John Downey

***/

#include "xvi.h"

/*
 * References to the current cursor position, and the window
 * and buffer into which it references. These make the code a
 * lot simpler, but we have to be a bit careful to update them
 * whenever necessary.
 */
Buffer	*curbuf;
Xviwin	*curwin;

/*
 * Global variables.
 */
state_t		State = NORMAL;	/* This is the current state of the command */
				/* interpreter. */

unsigned	echo;		/*
				 * bitmap controlling the verbosity of
				 * screen output (see xvi.h for details).
				 */

int		indentchars;	/* number of chars indented on current line */

volatile unsigned char
		kbdintr;	/*
				 * global flag set when a keyboard interrupt
				 * is received
				 */

bool_t		imessage;	/*
				 * global flag to indicate whether we should
				 * display the "Interrupted" message
				 */

/*
 * Internal routines.
 */
static	void	usage P((void));

Xviwin *
xvi_startup(vs, argc, argv, envp)
VirtScr	*vs;
int	argc;
char	*argv[];
char	*envp;				/* init string from the environment */
{
    char	*tag = NULL;		/* tag from command line */
    char	*pat = NULL;		/* pattern from command line */
    long	line = -1;		/* line number from command line */
    char	**files;
    int		numfiles = 0;
    int		count;
    char	*env;
    ignore_signals();

    /*
     * Initialise parameter module.
     */
    init_params();

    /*
     * Initialise yank/put module.
     */
    init_yankput();

    /*
     * The critical path this code has to follow is quite tricky.
     * We can't really run the "env" string until after we've set
     * up the first screen window because we don't know what the
     * commands in "env" might do: they might, for all we know,
     * want to display something. And we can't set up the first
     * screen window until we've set up the terminal interface.
     *
     * Also, we can't read the command line arguments until after
     * we've run the "env" string because a "-s param=value" argument
     * should override any setting of that parameter in the environment.
     *
     * All this means that the usage() function, which tells the
     * user that the command line syntax was wrong, can only be
     * called after the display interface has already been set up,
     * which means we must be in visual mode. So usage() has to
     * switch back to system mode (otherwise, on systems where
     * sys_startv() & sys_endv() switch display pages,
     * the user will never see the output of usage()). So ...
     */

    /*
     * Set up the first buffer and screen window.
     * Must call sys_init first.
     */
    curbuf = new_buffer();
    if (curbuf == NULL) {
		sys_endv();
		(void) fputs("Can't allocate buffer memory.\n", stderr);
		sys_exit(2);
    }
    curwin = init_window(vs);
    if (curwin == NULL) {
		sys_endv();
		(void) fputs("Can't allocate buffer memory.\n", stderr);
		sys_exit(2);
    }

    /*
     * Connect the two together.
     */
    map_window_onto_buffer(curwin, curbuf);

    init_sline(curwin);

    /*
     * Save a copy of the passed environment string in case it was
     * obtained from getenv(), so that the subsequent call we make
     * to get the SHELL parameter value does not overwrite it.
     */
    if (envp != NULL) {
		env = strsave(envp);
    } 
	else {
    	env = NULL;
    }

    /*
     * Try to obtain a value for the "shell" parameter from the
     * environment variable SHELL. If this is NULL, do not override
     * any existing value. The system interface code (sys_init()) is
     * free to set up a default value, and the initialisation string
     * in the next part of the startup is free to override both that
     * value and the one from the environment.
     */
    {
	char	*sh;

	sh = getenv("SHELL");
	if (sh != NULL)  set_param(P_shell, sh);
    }

    /*
     * Run any initialisation string passed to us.
     *
     * We can't really do this until we have set up the terminal
     * because we don't know what the initialisation string might do.
     */
    if (env != NULL) {
		register char	*ep;
		register bool_t	escaped = FALSE;

	/*
	 * Commands in the initialization string can be
	 * separated by '|' (or '\n'), but a literal '|' or
	 * '\n' can be escaped by a preceding '\\', so we have
	 * to process the string, looking for all three
	 * characters.
	 */
		for (ep = env; *ep;) {
		    switch (*ep++) {
			    case '\\':
					escaped = TRUE;
					continue;
	    		case '|':
	    		case '\n':
					if (escaped) {
		   				 register char *s, *d;

		    				for (d = (s = --ep) - 1; (*d++ = *s++) != '\0'; )
							;
					}
					else {
		   				 ep[-1] = '\0';
		    		     do_colon(env, FALSE);
		    			 env = ep;
					}
		/* fall through ... */
	    		default:
					escaped = FALSE;
	    	}
		}/*for ep */
		if (ep > env) {
	    	do_colon(env, FALSE);
		}
    }/*if env*/

    /*
     * Process the command line arguments.
     *
     * We can't really do this until we have run the "env" string,
     * because "-s param=value" on the command line should override
     * parameter setting in the environment.
     *
     * This is a bit awkward because it means usage() is called
     * when we're already in vi mode (which means, among other
     * things, that display pages may have been swapped).
     */
    for (count = 1;
	 count < argc && (argv[count][0] == '-' || argv[count][0] == '+');
								count++) {

		if (argv[count][0] == '-') {
		    switch (argv[count][1]) {
			    case 't':
		/*
		 * -t tag or -ttag
		 */
					if (numfiles != 0) usage();
					if (argv[count][2] != '\0') {
		    			tag = &(argv[count][2]);
					}
					else 
						if (count < (argc - 1)) {
		    				count += 1;
		    				tag = argv[count];
						}
						else {
		    				usage();
						}
					break;

	    		case 's':
		/*
		 * -s param=value or
		 * -sparam=value
		 */
					if (argv[count][2] != '\0') {
		    			argv[count] += 2;
					} 
					else 
						if (count < (argc - 1)) {
		    				count += 1;
						} 
						else {
		    				usage();
						}
					do_set(curwin, 1, &argv[count], FALSE);
					break;

	    		default:
					usage();
	    	}

		} 
		else /* argv[count][0] == '+' */ {
	    	char	nc;

	    /*
	     * "+n file" or "+/pat file"
	     */
	    	if (count >= (argc - 1)) usage();

	    	nc = argv[count][1];
	    	if (nc == '/') {
				pat = &(argv[count][2]);
	    	} 
			else
				if (is_digit(nc)) {
					line = atol(&(argv[count][1]));
	    		} else 
					if (nc == '\0') {
						line = 0;
	    			} 
					else {
						usage();
	    			}
	    	count += 1;
	    	files = &argv[count];
	    	numfiles = 1;
		}/* else */
    }
    if (numfiles != 0 || tag != NULL) {
	/*
	 * If we found "-t tag", "+n file" or "+/pat file" on
	 * the command line, we don't want to see any more
	 * file names.
	 */
		if (count < argc) usage();
    }
	else {
	/*
	 * Otherwise, file names are valid.
	 */
			numfiles = argc - count;
			if (numfiles > 0) {
	    	files = &(argv[count]);
			}
    }

    /*
     * Initialise the cursor and top of screen pointers
     * to the start of the first buffer. Note that the
     * bottom of screen pointer is also set up, as some
     * code (e.g. move_window_to_cursor) depends on it.
     */
    curwin->w_topline = curbuf->b_file;
    curwin->w_botline = curbuf->b_file->l_next;
    move_cursor(curwin, curbuf->b_file, 0);
    curwin->w_col = 0;
    curwin->w_row = 0;

    /*
     * Clear the window.
     *
     * It doesn't make sense to do this until we have a value for
     * Pn(P_colour).
     */
    clear(curwin);

    if (numfiles != 0) {
		if (line < 0 && pat == NULL)
	    	echo = e_CHARUPDATE | e_SHOWINFO;

		do_next(curwin, numfiles, files, FALSE);

		if (pat != NULL) {
		    echo = e_CHARUPDATE | e_SHOWINFO | e_REGERR | e_NOMATCH;
		    (void) dosearch(curwin, pat, '/');
		}
		else if (line >= 0) {
	    	echo = e_CHARUPDATE | e_SHOWINFO;
	    	do_goto((line > 0) ? line : MAX_LINENO);
		}

    } 
	else if (tag != NULL) {
		echo = e_CHARUPDATE | e_SHOWINFO | e_REGERR | e_NOMATCH;
			if (do_tag(curwin, tag, FALSE, TRUE, FALSE) == FALSE) {
	    /*
	     * Failed to find tag - wait for a while
	     * to allow user to read tags error and then
	     * display the "no file" message.
	     */
	    		sleep(2);
	    		show_file_info(curwin);
			}
    	 } 
		 else {
			echo = e_CHARUPDATE | e_SHOWINFO;
			show_file_info(curwin);
    	 }

    setpcmark(curwin);

    echo = e_CHARUPDATE;

    /*
     * Ensure we are at the right screen position.
     */
    move_window_to_cursor(curwin);

    /*
     * Draw the screen.
     */
    update_all();

    /*
     * Update the cursor position on the screen, and go there.
     */
    cursupdate(curwin);
    wind_goto(curwin);

    /*
     * Allow everything.
     */
    echo = e_ANY;

    catch_signals();

    if (env != NULL) {
		free(env);
    }

    return(curwin);
}

/*
 * Print usage message and die.
 *
 * This function is only called after we have set the terminal to vi
 * mode.
 *
 * The system interface functions have to ensure that it's safe to
 * call sys_exit() when sys_endv() has already been called (& there
 * hasn't necessarily been any intervening sys_startv()).
 */
static void
usage()
{
    sys_endv();
    (void) fputs("Usage: xvi { options } [ file ... ]\n", stderr);
    (void) fputs("       xvi { options } -t tag\n", stderr);
    (void) fputs("       xvi { options } +[num] file\n", stderr);
    (void) fputs("       xvi { options } +/pat  file\n", stderr);
    (void) fputs("\nOptions are:\n", stderr);
    (void) fputs("       -s [no]boolean-parameter\n", stderr);
    (void) fputs("       -s parameter=value\n", stderr);
    sys_exit(1);
}
