/* 
 * txCmdAZ.c --
 *
 *	This file contains procedures for Tx's top-level commands.
 *	Only commands that are different from the Mx commands are
 *	contained here.
 *
 * Copyright (C) 1986, 1987, 1988 Regents of the University of California
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /sprite/src/lib/mx/RCS/txCmdAZ.c,v 1.20 90/03/12 13:53:49 ouster Exp $ SPRITE (Berkeley)";
#endif not lint


#include <X11/Xlib.h>
#include <option.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include "mxInt.h"

/*
 * The following variable is a hack to provide "out-of-band" information
 * after a new window is created.  The variable holds the display for the
 * most recently-created window.
 */

Display *tx_Display;

/*
 *----------------------------------------------------------------------
 *
 * TxReplaceLines --
 *
 *	This utility procedure replaces a range of lines with another
 *	range of blank lines.
 *
 * Results:
 *	Returns CMD_OK if all went well, or CMD_ERROR if any sort of
 *	error occurred.
 *
 * Side effects:
 *	Starting with "first", "replace" lines are deleted.  In their
 *	place, "new" blank lines are inserted.  If replace is 0, then
 *	the new lines are inserted before first.
 *
 *----------------------------------------------------------------------
 */

void
TxReplaceLines(mxwPtr, first, replace, new)
    MxWindow *mxwPtr;		/* Window whose file will be affected. */
    int first;			/* Index of first line to replace (and/or
				 * line before which to insert). */
    int replace;		/* How many lines to replace.  May be 0. */
    int new;			/* How many new lines to add.  May be 0. */
{
#define MAXATONCE 100
    char string[MAXATONCE+1];
    Mx_Position start, stop;
    int batchCount, i;

    while ((replace > 0) || (new > 0)) {
	start.lineIndex = first;
	start.charIndex = 0;
	stop = start;
	stop.lineIndex += replace;
	if (new > MAXATONCE) {
	    batchCount = MAXATONCE;
	} else {
	    batchCount = new;
	}
	for (i = 0; i < batchCount; i++) {
	    string[i] = '\n';
	}
	string[batchCount] = 0;
	Mx_ReplaceBytes(mxwPtr->fileInfoPtr->file, start, stop, string);
	replace = 0;
	new -= batchCount;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_EscapeCmd --
 *
 *	This is the top-level procedure that implements the
 *	"escape" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

int
Tx_EscapeCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    int length;

    if (argc != 2) {
	sprintf(interp->result,
		"wrong # args: should be \"%.50s on|off\"", argv[0]);
	return TCL_ERROR;
    }

    length = strlen(argv[1]);
    if (strcmp(argv[1], "on") == 0) {
	mxwPtr->fileInfoPtr->flags &= ~NO_ESCAPES;
    } else if ((strncmp(argv[1], "off", length) == 0) && (length >= 2)) {
	mxwPtr->fileInfoPtr->flags |= NO_ESCAPES;
    } else {
	sprintf(interp->result,
		"bad \"%.50s\" option \"%.50s\": should be on or off",
		argv[0], argv[1]);
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_InsertCmd --
 *
 *	This is the top-level procedure that implements the
 *	"insert" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

int
Tx_InsertCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    register MxFileInfo *fileInfoPtr = mxwPtr->fileInfoPtr;

    if (argc < 2) {
	sprintf(interp->result,
		"too few args: should be \"%.50s text text ...\"", argv[0]);
	return TCL_ERROR;
    }

    for (argv++, argc--; argc > 0; argv++, argc--) {
	(*fileInfoPtr->inputProc)(fileInfoPtr->clientData, *argv);
	if (argc > 1) {
	    (*fileInfoPtr->inputProc)(fileInfoPtr->clientData, " ");
	}
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_OpenCmd --
 *
 *	This is the top-level procedure that implements the
 *	"open" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

int
Tx_OpenCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;		/* Pointer to existing window, or
					 * NULL if we're now creating the
					 * first window for this process. */
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    /*
     * Information related to command-line options:
     */
    
    static char *border, *bg, *displayName, *fg, *fn, *geometry;
    static char *iconName, *sbBg, *sbEl, *sbFg, *titleBg, *titleFg;
    static char *titleStripe, *title;
    static int iconX, iconY, doTitle, execArgIndex;

    static Option optionArray[] = {
	{OPT_STRING, "bd", (char *) &border, "Border color"},
	{OPT_STRING, "bg", (char *) &bg, "Background color"},
	{OPT_DOC, (char *) NULL, (char *) NULL,
		"-D:        Don't detach process from parent"},
	{OPT_STRING, "display", (char *) &displayName,
		"Name of display to use"},
	{OPT_REST, "e", (char *) &execArgIndex,
		"Command to execute, plus args"},
	{OPT_STRING, "fg", (char *) &fg, "Foreground color"},
	{OPT_STRING, "fn", (char *) &fn, "Font name"},
	{OPT_STRING, "geometry", (char *) &geometry, "Geometry for window"},
	{OPT_STRING, "icon", (char *) &iconName,
		"Name of bitmap file for icon"},
	{OPT_INT, "ix", (char *) &iconX, "X-coordinate for icon"},
	{OPT_INT, "iy", (char *) &iconY, "Y-coordinate for icon"},
	{OPT_TRUE, "showtitle", (char *) &doTitle, "Display title bar"},
	{OPT_STRING, "sb", (char *) &sbBg, "Scrollbar background color"},
	{OPT_STRING, "se", (char *) &sbEl, "Scrollbar elevator color"},
	{OPT_STRING, "sf", (char *) &sbFg, "Scrollbar foreground color"},
	{OPT_STRING, "tb", (char *) &titleBg, "Title background color"},
	{OPT_STRING, "tf", (char *) &titleFg, "Title foreground color"},
	{OPT_STRING, "title", (char *) &title, "Title to use for window"},
	{OPT_STRING, "ts", (char *) &titleStripe, "Title stripe color"},
    };

    Tx_WindowInfo info;
    Window window;
    MxWindow *mxwPtr2;
    char id[20];
    int	i, result, newDisplay;
    Display *display;

    /*
     * Parse arguments.
     */

    border = bg = displayName = fg = fn = geometry = iconName = NULL;
    sbBg = sbEl = sbFg = titleBg = titleFg = titleStripe = title = NULL;
    iconX = iconY = -1;
    doTitle = 0;
    execArgIndex = 0;
    argc = Opt_Parse(argc, argv, optionArray, Opt_Number(optionArray), 0);
    for (i = 1; i < argc; i++) {
	if (i == execArgIndex) {
	    break;
	}
	if (*argv[i] == '=') {
	    geometry = argv[i];
	    argv[i] = "";
	} else if (index(argv[i], ':') != NULL) {
	    displayName = argv[i];
	    argv[i] = "";
	}
    }

    /*
     * For now, can't specify display except for first window (need to
     * make changes to Sx).
     */

    if ((displayName != NULL) && (mxwPtr != NULL)) {
	sprintf(interp->result, "can't open a window on a different display");
	return TCL_ERROR;
    }

    /*
     * Get a display open, if it isn't open already.
     */

    if ((displayName != NULL) || (mxwPtr == NULL)) {
	display = XOpenDisplay(displayName);
	if (display == NULL) {
	    if (displayName == NULL) {
		sprintf(interp->result, "couldn't find display %s",
			"(missing DISPLAY environment variable?)");
	    } else {
		sprintf(interp->result, "couldn't open display \"%.50s\"",
		    displayName);
	    }
	    return TCL_ERROR;
	}
    } else {
	display = mxwPtr->display;
    }
    tx_Display = display;

    /*
     * Process various options and parameters.  The code below is a bit
     * tricky because if this is the first window, defaults generally
     * come from XGetDefault, whereas if this isn't the first window
     * then the defaults come from the existing window.  In some cases,
     * a switch to a different display has to be treated the same as
     * the first window.
     */

    newDisplay = 0;
    if ((mxwPtr == NULL) || (displayName != NULL)) {
	newDisplay = 1;
    }
    if (geometry == NULL) {
	if (mxwPtr == NULL) {
	    geometry = XGetDefault(display, "tx", "geometry");
	} else {
	    geometry = Tcl_GetVar(mxwPtr->interp, "geometry", 1);
	}
	if ((geometry == NULL) || (*geometry == 0)) {
	    geometry = "=80x30";
	}
    }
    if (!doTitle) {
	char *tmp;
	tmp = XGetDefault(display, "tx", "showTitle");
	if ((tmp != NULL) && (strcmp(tmp, "yes") == 0)) {
	    doTitle = 1;
	}
    }
    info.source = NULL;
    info.title = title;
    if (newDisplay) {
	info.fontPtr = NULL;
	info.foreground = BlackPixel(display, DefaultScreen(display));
	info.background = WhitePixel(display, DefaultScreen(display));
    } else {
	info.fontPtr = mxwPtr->fontPtr;
	info.foreground = mxwPtr->foreground;
	info.background = mxwPtr->background;
	info.border = mxwPtr->border;
	info.titleForeground = mxwPtr->titleForeground;
	info.titleBackground = mxwPtr->titleBackground;
	info.titleStripe = mxwPtr->titleStripe;
	info.sbForeground = mxwPtr->sbForeground;
	info.sbBackground = mxwPtr->sbBackground;
	info.sbElevator = mxwPtr->sbElevator;
    }
    if ((fn == NULL) && (newDisplay)) {
	fn = XGetDefault(display, "tx", "font");
    }
    if (fn != NULL) {
	info.fontPtr = XLoadQueryFont(display, fn);
	if (info.fontPtr != NULL) {
	    if (newDisplay) {
		Sx_SetDefaultFont(info.fontPtr);
	    }
	}
    }
    if (info.fontPtr == NULL) {
	info.fontPtr = Sx_GetDefaultFont(display);
    }
    MxGetColor(display, fg, "tx", "foreground", newDisplay, &info.foreground);
    MxGetColor(display, bg, "tx", "background", newDisplay, &info.background);
    if (newDisplay) {
	info.border = info.titleForeground = info.titleStripe
		= info.sbForeground = info.foreground;
	info.titleBackground = info.sbBackground = info.sbElevator
		= info.background;
    }
    MxGetColor(display, border, "tx", "borderColor", newDisplay, &info.border);
    MxGetColor(display, sbFg, "tx", "scrollbar.foreground", newDisplay,
	    &info.sbForeground);
    MxGetColor(display, sbBg, "tx", "scrollbar.background", newDisplay,
	    &info.sbBackground);
    MxGetColor(display, sbEl, "tx", "scrollbar.elevator", newDisplay,
	    &info.sbElevator);
    MxGetColor(display, titleFg, "tx", "title.foreground", newDisplay,
	    &info.titleForeground);
    MxGetColor(display, titleBg, "tx", "title.background", newDisplay,
	    &info.titleBackground);
    MxGetColor(display, titleStripe, "tx", "title.stripe", newDisplay,
	    &info.titleStripe);
    info.flags = 0;
    if (doTitle) {
	window = MxCreateWindow(display, geometry, info.fontPtr,
		3*Sx_DefaultHeight(display, info.fontPtr) + 4,
		info.foreground, info.background, &info.width,
		&info.height);
    } else {
	info.flags |= TX_NO_TITLE;
	window = MxCreateWindow(display, geometry, info.fontPtr,
		2*Sx_DefaultHeight(display, info.fontPtr) + 2,
		info.foreground, info.background, &info.width,
		&info.height);
    }

    if ((execArgIndex == 0) && (mxwPtr != NULL)) {
	info.source = mxwPtr->w;
	if (info.title == NULL) {
	    info.title = Tcl_GetVar(mxwPtr->interp, "title", 1);
	    if (info.title == NULL) {
		info.title = "";
	    }
	}
	result = Tx_Make(interp, display, window, &info,
		(void (*)()) NULL, (void (*)()) NULL, (void (*)()) NULL,
		(ClientData) NULL);
    } else {
	result = Tx_SetupAndFork(interp, display, window, &info,
		(execArgIndex != 0) ? &argv[execArgIndex] : (char **) NULL);
    }
    if (result != TCL_OK) {
	XDestroyWindow(display, window);
	return result;
    }

    mxwPtr2 = TxGetMxWindow(display, window);
    MxSetWMHints(mxwPtr2, iconName,
	    newDisplay ? (Pixmap) NULL : mxwPtr->iconPixmap,
	    iconX, iconY);
    XMapWindow(display, window);

    if (mxwPtr != NULL) {
	sprintf(id, "0x%x", window);
	Tcl_SetVar(mxwPtr->interp, "newWindow", id, 1);
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_OutputCmd --
 *
 *	This is the top-level procedure that implements the
 *	"output" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

int
Tx_OutputCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    if (argc != 2) {
	sprintf(interp->result,
		"wrong # args:  should be \"%.50s bytes\"",
		argv[0]);
	return TCL_ERROR;
    }
    Tx_Output(mxwPtr->fileInfoPtr, argv[1]);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_QuitCmd --
 *
 *	This is the top-level procedure that implements the
 *	"quit" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
int
Tx_QuitCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    if (argc > 1) {
	sprintf(interp->result, "too many args:  should be \"%.50s\"",
		argv[0]);
	return TCL_ERROR;
    }
    XDestroyWindow(mxwPtr->display, mxwPtr->w);
    mxwPtr->flags |= DESTROYED;
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_SendCmd --
 *
 *	This is the top-level procedure that implements the
 *	"send" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

int
Tx_SendCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    MxWindow *mxwPtr2;
    int window, result;

    if (argc != 3) {
	sprintf(interp->result,
		"wrong # args: should be \"%.50s window command\"",
		argv[0]);
	return TCL_ERROR;
    }

    if (sscanf(argv[1], "0x%x", &window) != 1) {
	badId:
	sprintf(interp->result, "\"%.50s\" got bad window id \"%.50s\"",
		argv[0], argv[1]);
	return TCL_ERROR;
    }
    mxwPtr2 = TxGetMxWindow(mxwPtr->display, (Window) window);
    if (mxwPtr2 == NULL) {
	goto badId;
    }
    result = Tcl_Eval(mxwPtr2->interp, argv[2], 0, (char **) 0);
    Tcl_Return(interp, mxwPtr2->interp->result, TCL_VOLATILE);
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_TitleCmd --
 *
 *	This is the top-level procedure that implements the
 *	"title" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
int
Tx_TitleCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    char *left, *center, *right;

    if (argc != 5) {
	sprintf(interp->result,
		"wrong # args: should be \"%.50s left center right wmName\"",
		argv[0]);
	return TCL_ERROR;
    }

    if (mxwPtr->title != NULL) {
	left = argv[1];
	if (*left == 0) {
	    left = NULL;
	}
	center = argv[2];
	if (*center == 0) {
	    center = NULL;
	}
	right = argv[3];
	if (*right == 0) {
	    right = NULL;
	}
	Sx_TitleMake(mxwPtr->display, mxwPtr->title,
		mxwPtr->fontPtr, mxwPtr->titleForeground,
		mxwPtr->titleBackground, mxwPtr->titleStripe,
		left, center, right);
    }
    XStoreName(mxwPtr->display, mxwPtr->w, argv[4]);
    XSetIconName(mxwPtr->display, mxwPtr->w, argv[4]);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_UpdateCmd --
 *
 *	This is the top-level procedure that implements the
 *	"update" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
int
Tx_UpdateCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    if (argc != 1) {
	sprintf(interp->result, "too many args: should be \"%.50s\"",
		argv[0]);
	return TCL_ERROR;
    }
    Tx_Update();
    Mx_Update();
    XFlush(mxwPtr->display);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tx_ViCmd --
 *
 *	This is the top-level procedure that implements the
 *	"vi" command.  See the man page for details on what it
 *	does and what it returns.
 *
 * Results:
 *	See the man page.
 *
 * Side effects:
 *	See the man page.
 *
 *----------------------------------------------------------------------
 */

int
Tx_ViCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    register Tcl_Interp *interp;
    int argc;
    char **argv;
{
    register MxFileInfo *fileInfoPtr = mxwPtr->fileInfoPtr;
    int length, viLines;
    Mx_Position eof;
    
    if (argc < 2) {
	sprintf(interp->result,
		"too few args: should be \"%.50s option [args]\"",
		argv[0]);
	return TCL_ERROR;
    }

    length = strlen(argv[1]);
    viLines = fileInfoPtr->viLines;
    if (strncmp(argv[1], "enter", length) == 0) {
	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s enter\"", argv[0]);
	    return TCL_ERROR;
	}
	if (viLines > 0) {
	    return TCL_OK;
	}
	fileInfoPtr->viLines = mxwPtr->	height/mxwPtr->fontHeight;
	TxReplaceLines(mxwPtr, 0, 0, fileInfoPtr->viLines + 1);
	MxCaretSetPosition(mxwPtr, Mx_ZeroPosition, 0);
	MxGetInWindow(mxwPtr, Mx_ZeroPosition, 0, 1);
    } else if (strncmp(argv[1], "leave", length) == 0) {
	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s leave\"", argv[0]);
	    return TCL_ERROR;
	}
	if (viLines <= 0) {
	    return TCL_OK;
	}
	eof = Mx_EndOfFile(fileInfoPtr->file);
	MxGetInWindow(mxwPtr, eof, mxwPtr->heightLines-1, 1);
	MxCaretSetPosition(mxwPtr, eof, 0);
	TxReplaceLines(mxwPtr, 0, viLines+1, 0);
	fileInfoPtr->viLines = -1;
    } else if (strncmp(argv[1], "clear", length) == 0) {
	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s clear\"", argv[0]);
	    return TCL_ERROR;
	}
	if (viLines <= 0) {
	    eof = Mx_EndOfFile(fileInfoPtr->file);
	    Mx_ReplaceBytes(mxwPtr->fileInfoPtr->file, Mx_ZeroPosition,
		    eof, (char *) NULL);
	    MxGetInWindow(mxwPtr,eof,mxwPtr->heightLines/2,0);
	} else {
	    TxReplaceLines(mxwPtr, 0, viLines, viLines);
	}
	MxCaretSetPosition(mxwPtr, Mx_ZeroPosition, 0);
    } else if (strncmp(argv[1], "cursor", length) == 0) {
	Mx_Position caret;
	char *term;
	
	if (argc != 4) {
	    cursorError:
	    sprintf(interp->result,
		    "wrong # args: should be \"%.50s cursor line column\"",
		    argv[0]);
	    return TCL_ERROR;
	}
	caret.lineIndex = strtoul(argv[2], &term, 10);
	if (term == argv[2]) {
	    goto cursorError;
	}
	caret.charIndex = strtoul(argv[3], &term, 10);
	if (term == argv[3]) {
	    goto cursorError;
	}
	if (viLines > 0) {
	    if (caret.lineIndex >= viLines) {
		caret.lineIndex = viLines-1;
	    }
	} else {
	    caret.lineIndex += mxwPtr->linePtr->position.lineIndex;
	    if (caret.lineIndex >= mxwPtr->linePtr[mxwPtr->heightLines-1].
		    position.lineIndex) {
		caret.lineIndex = mxwPtr->linePtr[mxwPtr->heightLines-1].
			position.lineIndex;
	    }
	}
	TxMakePosExist(fileInfoPtr->file, caret);
	MxCaretSetPosition(mxwPtr, caret, 0);
    } else if (strncmp(argv[1], "insertline", length) == 0) {
	Mx_Position caret;
	
	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s insertline\"", argv[0]);
	    return TCL_ERROR;
	}
	TxReplaceLines(mxwPtr, fileInfoPtr->caretFirst.lineIndex, 0, 1);
	if (viLines > 0) {
	    TxReplaceLines(mxwPtr, viLines, 1, 0);
	}
	caret = fileInfoPtr->caretFirst;
	caret.lineIndex--;
	TxMakePosExist(fileInfoPtr->file, caret);
	MxCaretSetPosition(mxwPtr, caret, 0);
    } else if (strncmp(argv[1], "deleteline", length) == 0) {
	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s deleteline\"", argv[0]);
	    return TCL_ERROR;
	}
	if (viLines > 0) {
	    TxReplaceLines(mxwPtr, viLines, 0, 1);
	}
	eof = Mx_EndOfFile(fileInfoPtr->file);
	if (fileInfoPtr->caretFirst.lineIndex < eof.lineIndex) {
	    TxReplaceLines(mxwPtr, fileInfoPtr->caretFirst.lineIndex, 1, 0);
	}
    } else if (strncmp(argv[1], "up", length) == 0) {
	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s up\"", argv[0]);
	    return TCL_ERROR;
	}
	if (fileInfoPtr->caretFirst.lineIndex > 0) {
	    Mx_Position pos;

	    pos.lineIndex = fileInfoPtr->caretFirst.lineIndex - 1;
	    pos.charIndex = fileInfoPtr->caretFirst.charIndex;
	    TxMakePosExist(fileInfoPtr->file, pos);
	    MxCaretSetPosition(mxwPtr, pos, 0);
	}
    } else if (strncmp(argv[1], "tab", length) == 0) {
	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s tab\"", argv[0]);
	    return TCL_ERROR;
	}
	if (fileInfoPtr->caretFirst.lineIndex >= 0) {
	    Mx_Position pos;

	    pos.lineIndex = fileInfoPtr->caretFirst.lineIndex;
	    pos.charIndex = (fileInfoPtr->caretFirst.charIndex + 8) & ~07;
	    TxMakePosExist(fileInfoPtr->file, pos);
	    MxCaretSetPosition(mxwPtr, pos, 0);
	}
    } else if (strncmp(argv[1], "ce", length) == 0) {
	int length;
	Mx_Position pos, last;

	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s ce\"", argv[0]);
	    return TCL_ERROR;
	}

	/*
	 * This command is permitted even in non-vi mode, for the
	 * benefit of the "more" program.
	 */
	 
	pos = fileInfoPtr->caretFirst;
	Mx_GetLine(fileInfoPtr->file, pos.lineIndex, &length);
	if ((length-1) > fileInfoPtr->caretFirst.charIndex) {
	    last.lineIndex = pos.lineIndex;
	    last.charIndex = length-1;
	    Mx_ReplaceBytes(fileInfoPtr->file, fileInfoPtr->caretFirst,
		    last, " ");
	    MxCaretSetPosition(mxwPtr, pos, 0);
	}
    } else if (strncmp(argv[1], "cd", length) == 0) {
	Mx_Position pos;

	if (argc != 2) {
	    sprintf(interp->result,
		    "too many args: should be \"%.50s cd\"", argv[0]);
	    return TCL_ERROR;
	}
	pos.lineIndex = fileInfoPtr->caretFirst.lineIndex;
	pos.charIndex = 0;
	if (viLines <= 0) {
	    eof = Mx_EndOfFile(fileInfoPtr->file);
	    TxReplaceLines(mxwPtr, pos.lineIndex, eof.lineIndex -
		    pos.lineIndex,0);
	}
	else {
	    TxReplaceLines(mxwPtr, pos.lineIndex, viLines-pos.lineIndex,
		    viLines-pos.lineIndex);
	}
	MxCaretSetPosition(mxwPtr, pos, 0);
    } else if (strncmp(argv[1], "on", length) == 0) {
	if (viLines > 0) {
	    interp->result = "1";
	} else {
	    interp->result = "0";
	}
    } else {
	sprintf(interp->result,
		"bad \"%.50s\" option;  must be cd, ce, clear, cursor, deleteline, enter, insertline, leave, tab, or up",
		argv[0]);
	return TCL_ERROR;
    }
    return TCL_OK;
}
