/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)ex_cmds2.c	2.4 (Chris & John Downey) 11/6/92";
#endif

/***

* program name:
    xvi
* function:
    PD version of UNIX "vi" editor, with extensions.
* module name:
    ex_cmds2.c
* module function:
    Command functions for miscellaneous ex (colon) commands.
    See ex_cmds1.c for file- and buffer-related colon commands.
* 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"

#ifdef	MEGAMAX
overlay "ex_cmds2"
#endif

/*
 * Run shell command.
 *
 * You might think it would be easier to do this as
 *
 *	sys_endv();
 *	system(command);
 *	sys_startv();
 *	prompt("[Hit return to continue] ");
 *	...
 *
 * but the trouble is that, with some systems, sys_startv() can be
 * used to swap display pages, which would mean that the user would
 * never have time to see the output from the command. (This applies
 * to some terminals, as well as machines with memory-mappped video;
 * cmdtool windows on Sun workstations also do the same thing.)
 *
 * This means we have to display the prompt before calling
 * sys_startv(), so we just use good old fputs(). (We're trying not to
 * use printf()).
 */
/*ARGSUSED*/
void
do_shcmd(window, command)
Xviwin	*window;
char	*command;
{
    int	c;

    sys_endv();

    (void) fputs(command, stdout);
    (void) fputs("\r\n", stdout);
    (void) fflush(stdout);
    (void) call_system(command);
    (void) fputs("[Hit return to continue] ", stdout);
    (void) fflush(stdout);
    while ((c = getc(stdin)) != '\n' && c != '\r' && c != EOF)
	;

    sys_startv();
    redraw_screen();
}

void
do_shell(window)
Xviwin	*window;
{
    char	*sh = NULL;
    int	sysret;

    sh = Ps(P_shell);
    if (sh == NULL) {
	show_error(window, "SHELL variable not set");
	return;
    }

    sys_endv();

    sysret = call_shell(sh);

    sys_startv();
    redraw_screen();

    if (sysret != 0) {
#ifdef STRERROR_AVAIL
	show_error(window, "Can't execute %s [%s]", sh,
			(errno > 0 ? strerror(errno) : "Unknown Error"));
#else	/* strerror() not available */
	show_error(window, "Can't execute %s", sh);
#endif	/* strerror() not available */
    }
}

/*ARGSUSED*/
void
do_suspend(window)
Xviwin	*window;
{
    if (State == NORMAL) {
#	ifdef	SIGSTOP
	extern	int	kill P((int, int));
	extern	int	getpid P((void));

	sys_endv();

	(void) kill(getpid(), SIGSTOP);

	sys_startv();
	redraw_screen();

#	else /* SIGSTOP */

	/*
	 * Can't suspend unless we're on a BSD UNIX system;
	 * just pretend by invoking a shell instead.
	 */
	do_shell(window);

#	endif /* SIGSTOP */
    }
}

void
do_equals(window, line)
Xviwin	*window;
Line	*line;
{
    if (line == NULL) {
	/*
	 * Default position is ".".
	 */
	line = window->w_cursor->p_line;
    }

    show_message(window, "Line %ld", lineno(window->w_buffer, line));
}

void
do_help(window)
Xviwin	*window;
{
    unsigned	savecho;

    savecho = echo;
    echo &= ~(e_SCROLL | e_REPORT | e_SHOWINFO);
    if (Ps(P_helpfile) != NULL && do_buffer(window, Ps(P_helpfile))) {
	/*
	 * We use "curbuf" here because the new buffer will
	 * have been made the current one by do_buffer().
	 */
	curbuf->b_flags |= FL_READONLY | FL_NOEDIT;
	move_window_to_cursor(curwin);
	show_file_info(curwin, TRUE);
	update_window(curwin);
    }
    echo = savecho;
}

bool_t
do_source(interactive, file)
bool_t	interactive;
char	*file;
{
    static char		cmdbuf[256];
    FILE		*fp;
    register int	c;
    register char	*bufp;
    bool_t		literal;

    fp = fopen(file, "r");
    if (fp == NULL) {
	if (interactive) {
	    show_error(curwin, "Can't open \"%s\"", file);
	}
	return(FALSE);
    }

    bufp = cmdbuf;
    literal = FALSE;
    while ((c = getc(fp)) != EOF) {
	if (!literal && (c == CTRL('V') || c == '\n')) {
	    switch (c) {
	    case CTRL('V'):
		literal = TRUE;
		break;

	    case '\n':
		if (kbdintr) {
		    imessage = TRUE;
		    break;
		}
		if (bufp > cmdbuf) {
		    *bufp = '\0';
		    do_colon(cmdbuf, FALSE);
		}
		bufp = cmdbuf;
		break;
	    }
	} else {
	    literal = FALSE;
	    if (bufp < &cmdbuf[sizeof(cmdbuf) - 1]) {
		*bufp++ = c;
	    }
	}
    }
    (void) fclose(fp);
    return(TRUE);
}

/*
 * Change directory.
 *
 * With a NULL argument, change to the directory given by the HOME
 * environment variable if it is defined; with an argument of "-",
 * change back to the previous directory (like ksh).
 *
 * Return NULL pointer if OK, otherwise error message to say
 * what went wrong.
 */
char *
do_chdir(dir)
    char	*dir;
{
    static char	*homedir = NULL;
    static char	*prevdir = NULL;
    char	*ret;
    char	*curdir;

    if (dir == NULL && homedir == NULL &&
    				(homedir = getenv("HOME")) == NULL) {
	return("HOME environment variable not set");
    }

    if (dir == NULL) {
	dir = homedir;
    } else if (*dir == '-' && dir[1] == '\0') {
	if (prevdir == NULL) {
	    return("No previous directory");
	} else {
	    dir = prevdir;
	}
    }

    curdir = malloc(MAXPATHLEN + 2);
    if (curdir != NULL && getcwd(curdir, MAXPATHLEN + 2) == NULL) {
	free(curdir);
	curdir = NULL;
    }
    ret = (chdir(dir) == 0 ? NULL : "Invalid directory");
    if (prevdir) {
	free(prevdir);
	prevdir = NULL;
    }
    if (curdir) {
	prevdir = realloc(curdir, (unsigned) strlen(curdir) + 1);
    }
    return(ret);
}

void
do_cdmy(type, l1, l2, destline)
int	type;			/* one of [cdmy] */
Line	*l1, *l2;		/* start and end (inclusive) of range */
Line	*destline;		/* destination line for copy/move */
{
    Posn	p1, p2;
    Posn	destpos;

    p1.p_line = (l1 != NULL) ? l1 : curwin->w_cursor->p_line;
    p2.p_line = (l2 != NULL) ? l2 : p1.p_line;
    p1.p_index = p2.p_index = 0;

    if (type == 'c' || type == 'm') {
	if (destline == NULL) {
	    show_error(curwin, "No destination specified");
	    return;
	}
    }

    /*
     * Check that the destination is not inside
     * the source range for "move" operations.
     */
    if (type == 'm') {
	unsigned long	destlineno;

	destlineno = lineno(curbuf, destline);
	if (destlineno >= lineno(curbuf, p1.p_line) &&
			    destlineno <= lineno(curbuf, p2.p_line)) {
	    show_error(curwin, "Source conflicts with destination of move");
	    return;
	}
    }

    /*
     * Yank the text to be copied.
     */
    if (do_yank(curbuf, &p1, &p2, FALSE, '@') == FALSE) {
	show_error(curwin, "Not enough memory to yank text");
	return;
    }

    if (!start_command(curwin)) {
	return;
    }

    switch (type) {
    case 'd':			/* delete */
    case 'm':			/* move */
	move_cursor(curwin, p1.p_line, 0);
	repllines(curwin, p1.p_line, cntllines(p1.p_line, p2.p_line),
						(Line *) NULL);
	update_buffer(curbuf);
	cursupdate(curwin);
	begin_line(curwin, TRUE);
    }

    switch (type) {
    case 'c':			/* copy */
    case 'm':			/* move */
	/*
	 * And put it back at the destination point.
	 */
	destpos.p_line = destline;
	destpos.p_index = 0;
	do_put(curwin, &destpos, FORWARD, '@');

	update_buffer(curbuf);
	cursupdate(curwin);
    }

    end_command(curwin);
}
