/* 
 * mxCmdNR.c --
 *
 *	This file contains the top-level command procedures for
 *	all Mx commands whose names begin with the letters n-r.
 *	Some of these commands are also used by Tx.
 *
 * Copyright (C) 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/mxCmdNR.c,v 1.39 90/04/19 09:24:12 ouster Exp $ SPRITE (Berkeley)";
#endif not lint

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <ctype.h>
#include <errno.h>
#include <option.h>
#include <stdio.h>
#include <string.h>
#include <sys/file.h>
#include <stdlib.h>
#include "mxInt.h"
#include "regex.h"

/*
 * The following variable is a hack, which is needed to make the
 * display available after a new window is created.  The variable
 * holds the display for the most recently-created window.
 */

Display *mx_Display;

extern char *regOldLine;

/*
 *----------------------------------------------------------------------
 *
 * Mx_NewlineCmd --
 *
 *	This is the top-level procedure that implements the
 *	"newline" 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
Mx_NewlineCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    register MxFileInfo *fileInfoPtr = mxwPtr->fileInfoPtr;
    Mx_Position insertPoint;
    int indent;

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

    /*
     * Duplicate the old line's indentation on the new line.
     */

    indent = MxGetIndent(fileInfoPtr->file, insertPoint.lineIndex);
    Mx_ReplaceBytes(fileInfoPtr->file, insertPoint, insertPoint, "\n");
    (void) MxSetIndent(fileInfoPtr->file,
	    fileInfoPtr->caretFirst.lineIndex, indent);
    MxGetInWindow(mxwPtr, fileInfoPtr->caretFirst, mxwPtr->heightLines-1,
	    0);
    MxCleanIndent(fileInfoPtr->file, insertPoint.lineIndex);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Mx_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
Mx_OpenCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;		/* Pointer to existing Mx window;
					 * NULL means this is the first
					 * window for Mx. */
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    /*
     * Information related to command-line options:
     */
    
    static char *border, *bg, *displayName, *fg, *fn, *geometry, *iconName;
    static char *sbBg, *sbEl, *sbFg, *searchString, *tag, *titleBg, *titleFg;
    static char *titleStripe;
    static int iconX, iconY, lineNumber, doTitle, files;

    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, "f", (char *) &files,
		"Treat all remaining arguments as file names"},
	{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, "t", (char *) &tag, "Tag string to search for"},
	{OPT_STRING, "tb", (char *) &titleBg, "Title background color"},
	{OPT_STRING, "tf", (char *) &titleFg, "Title foreground color"},
	{OPT_STRING, "ts", (char *) &titleStripe, "Title stripe color"},
    };

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

    /*
     * Parse arguments.
     */

    info.name = NULL;
    border = bg = displayName = fg = fn = geometry = sbBg = sbEl = NULL;
    sbFg = searchString = tag = titleBg = titleFg = titleStripe = NULL;
    iconName = NULL;
    lineNumber = iconX = iconY = -1;
    files = -1;
    doTitle = 0;
    argc = Opt_Parse(argc, argv, optionArray, Opt_Number(optionArray), 0);
    for (i = 1; i < argc; i++) {
	if (*argv[i] == '=') {
	    geometry = argv[i];
	    argv[i] = "";
	} else if ((index(argv[i], ':') != NULL)
		&& ((files < 0) || (i < files))) {
	    displayName = argv[i];
	    argv[i] = "";
	} else if (argv[i][0] == '+') {
	    if (argv[i][1] == 0) {
		lineNumber = -2;
		searchString = NULL;
	    } else if (!isdigit(argv[i][1])) {
		searchString = argv[i] + 1;
		if (*searchString == '/') {
		    searchString++;
		}
		lineNumber = -1;
	    } else { 
		char *result;
		lineNumber = strtol(argv[i] + 1, &result, 10);
		if ((result == (argv[i] + 1)) || (*result != 0)
			|| (lineNumber < 1)) {
		    sprintf(interp->result,
			    "bad line number \"%.50s\".\n", argv[i]);
		    return TCL_ERROR;
		}
		searchString = NULL;
	    }
	    argv[i] = "";
	} else {
	    if (info.name == NULL) {
		info.name = 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;
    }
    mx_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, "mx", "geometry");
	} else {
	    geometry = Tcl_GetVar(mxwPtr->interp, "geometry", 1);
	}
	if ((geometry == NULL) || (*geometry == 0)) {
	    geometry = "=80x30";
	}
    }
    if (!doTitle) {
	char *tmp;
	tmp = XGetDefault(display, "mx", "showTitle");
	if ((tmp != NULL) && (strcmp(tmp, "yes") == 0)) {
	    doTitle = 1;
	}
    }
    if (tag != NULL) {
	int result;

	result = Mx_GetTag(tag, (char *) NULL, 1,
		&info.name, &searchString, interp);
	if (result != TCL_OK) {
	    return result;
	}
	lineNumber = -1;
    }
    info.file = NULL;
    if (info.name == NULL) {
	if (mxwPtr == NULL) {
	    static char tutName[100];
	    sprintf(tutName, "%.80s/%s", MX_LIB_DIR, "tutorial1");
	    info.name = tutName;
	    if (geometry == NULL) {
		geometry = "=80x50+0+0";
	    }
	} else {
	    info.file = mxwPtr->fileInfoPtr->file;
	}
    } else if (*info.name == 0) {
	info.file = Mx_FileLoad((char *) NULL);
	info.name = NULL;
    }
    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, "mx", "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, "mx", "foreground", newDisplay, &info.foreground);
    MxGetColor(display, bg, "mx", "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, "mx", "borderColor", newDisplay, &info.border);
    MxGetColor(display, sbFg, "mx", "scrollbar.foreground", newDisplay,
	    &info.sbForeground);
    MxGetColor(display, sbBg, "mx", "scrollbar.background", newDisplay,
	    &info.sbBackground);
    MxGetColor(display, sbEl, "mx", "scrollbar.elevator", newDisplay,
	    &info.sbElevator);
    MxGetColor(display, titleFg, "mx", "title.foreground", newDisplay,
	    &info.titleForeground);
    MxGetColor(display, titleBg, "mx", "title.background", newDisplay,
	    &info.titleBackground);
    MxGetColor(display, titleStripe, "mx", "title.stripe", newDisplay,
	    &info.titleStripe);
    info.flags = MX_UNDO | MX_DELETE;
    if (doTitle) {
	window = MxCreateWindow(display, geometry, info.fontPtr,
		4*Sx_DefaultHeight(display, info.fontPtr) + 5,
		info.foreground, info.background, &info.width,
		&info.height);
    } else {
	info.flags |= MX_NO_TITLE;
	window = MxCreateWindow(display, geometry, info.fontPtr,
		3*Sx_DefaultHeight(display, info.fontPtr) + 3,
		info.foreground, info.background, &info.width,
		&info.height);
    }

    result = Mx_Make(display, window, &info, interp);
    if (result != TCL_OK) {
	XDestroyWindow(display, window);
	return result;
    }

    /*
     * If a line number or tag or search string was given, set the view at
     * the correct place.  Don't forget:  users number lines from 1, but we
     * number from 0.
     */

    mxwPtr2 = MxGetMxWindow(display, window);
    if (lineNumber != -1) {
	Mx_Position first, eof;

	eof = Mx_EndOfFile(mxwPtr2->fileInfoPtr->file);
	if (lineNumber == -2) {
	    first = eof;
	} else {
	    first.lineIndex = lineNumber-1;
	    first.charIndex = 0;
	    if (MX_POS_LESS(eof, first)) {
		first = eof;
	    }
	}
	first.charIndex = 0;
	MxSelectionSet(mxwPtr2, first, first);
	MxGetInWindow(mxwPtr2, first, mxwPtr2->heightLines/2, 0);
    } else if (searchString != NULL) {
	Mx_Position first, last;
	char *errStr;
	char msg[200];

	errStr = Mx_CompileRegExp(searchString);
	if (errStr != NULL) {
	    sprintf(msg, "bad pattern \"%.50s\": %.50s", searchString, errStr);
	    MxOutputMsg(mxwPtr2, msg);
	} else if (!Mx_SearchRegExp(mxwPtr2->fileInfoPtr->file,
		Mx_ZeroPosition, Mx_EndOfFile(mxwPtr2->fileInfoPtr->file),
		&first, &last)) {
	    sprintf(msg, "couldn't find pattern matching \"%.50s\"",
		    searchString);
	    MxOutputMsg(mxwPtr2, msg);
	} else {
	    MxSelectionSet(mxwPtr2, first, last);
	    MxGetInWindow(mxwPtr2, first, mxwPtr2->heightLines/2, 0);
	}
    }
    MxSetWMHints(mxwPtr2, iconName,
	    newDisplay ? (Pixmap) NULL : mxwPtr->iconPixmap,
	    iconX, iconY);
    XMapWindow(mxwPtr2->display, window);

    /*
     * Pass all of the file names to the new window in the "files"
     * variable (but first eliminate any empty "file names":  these
     * correspond to arguments like ":" and "=" that were NULLed out
     * above).
     */

    for (i = 1, j = 0; i < argc; i++) {
	if (*argv[i] != 0) {
	    argv[j] = argv[i];
	    j++;
	}
    }
    fileNames = Tcl_Merge(j, argv);
    Tcl_SetVar(mxwPtr2->interp, "files", fileNames, 1);
    free(fileNames);

    if (mxwPtr != NULL) {
	sprintf(id, "0x%x", window);
	Tcl_SetVar(mxwPtr->interp, "newWindow", id, 1);
    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * Mx_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
Mx_QuitCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    int result;

    if (argc > 1) {
	sprintf(interp->result, "too many args: should be \"%.50s\"",
		argv[0]);
	return TCL_ERROR;
    }
    result = MxCheckWritten(mxwPtr, 0);
    if (result != TCL_OK) {
	return result;
    }
    Tcl_Return(interp, (char *) NULL, TCL_STATIC);
    sprintf(interp->result, "\"%.50s\" command caused window to be destroyed",
	    argv[0]);
    XDestroyWindow(mxwPtr->display, mxwPtr->w);
    mxwPtr->flags |= DESTROYED;
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * Mx_QuoteCmd --
 *
 *	This is the top-level procedure that implements the
 *	"quote" 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
Mx_QuoteCmd(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;
    }
    mxwPtr->quoteFlag = 1;
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Mx_ReadCmd --
 *
 *	This is the top-level procedure that implements the
 *	"read" 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
Mx_ReadCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    int streamID;
#define BUFFER_SIZE 4096
    char buffer[BUFFER_SIZE + 1];
    int result = TCL_OK;
    char *fileName;

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

    fileName = Tcl_TildeSubst(interp, argv[1]);
    if (fileName == NULL) {
	return TCL_ERROR;
    }
    streamID = open(fileName, O_RDONLY, 0);
    if (streamID < 0) {
	sprintf(interp->result, "couldn't read file \"%.50s\": %.50s",
		argv[1], strerror(errno));
	return TCL_ERROR;
    }
    while (1) {
	int count;

	count = read(streamID, buffer, BUFFER_SIZE);
	if (count < 0) {
	    sprintf(interp->result,
		    "error while reading \"%.50s\": %.50s", argv[1],
		    strerror(errno));
	    result = TCL_ERROR;
	    goto done;
	}
	if (count == 0) {
	    goto done;
	}
	buffer[count] = 0;
	Mx_ReplaceBytes(mxwPtr->fileInfoPtr->file,
		mxwPtr->fileInfoPtr->caretFirst,
		mxwPtr->fileInfoPtr->caretFirst, buffer);
    }

    done:
    close(streamID);
    MxGetInWindow(mxwPtr, mxwPtr->fileInfoPtr->caretFirst,
	    mxwPtr->heightLines-1, 0);
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * Mx_ReplaceCmd --
 *
 *	This is the top-level procedure that implements the
 *	"replace" 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
Mx_ReplaceCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    Mx_Position start, stop;		/* Range in which to replace all
					 * occurrences. */
    Mx_Position first, last;		/* Range of thing to be replaced. */
    char *pattern, *replace;		/* Search, replacement strings. */
    int rangeReplace;			/* 1 means all instances of
					 * pattern will be replaced in the
					 * range between start and stop.  0
					 * means just replace the selection. */
    int getFromWindow;			/* 1 means get pattern and replace
					 * from search window rather than
					 * from command line. */
    Mx_Floater floater;			/* In range replacement, keeps track of
					 * the space left to check (the range
					 * may grow or shrink as things are
					 * replaced). */
    int numReplaced;			/* Number of replacements made. */
    int result;
    int regExp=1;			/* 1 if using reg. exp. matching */
    char *errStr;			/* Error string */
    char *value;

    /*
     * Parse arguments.
     */

    if (argc == 1) {
	getFromWindow = 1;
	rangeReplace = 0;
    } else {
	int length;

	length = strlen(argv[1]);
	if (strncmp(argv[1], "range", length) == 0) {
	    rangeReplace = 1;
	    if (argc == 4) {
		getFromWindow = 1;
	    } else if (argc == 6) {
		getFromWindow = 0;
		pattern = argv[4];
		replace = argv[5];
	    } else {
		sprintf(interp->result, "wrong # args: should be \"%.50s range first last [pattern string]\"",
			argv[0]);
		return TCL_ERROR;
	    }
	    result = MxGetMark(mxwPtr, argv[2], &start);
	    if (result != TCL_OK) {
		return result;
	    }
	    result = MxGetMark(mxwPtr, argv[3], &stop);
	    if (result != TCL_OK) {
		return result;
	    }
	} else if (strncmp(argv[1], "selection", length) == 0) {
	    rangeReplace = 0;
	    if (argc != 3) {
		sprintf(interp->result,
			"wrong # args: should be \"%.50s selection string\"",
			argv[0]);
		return TCL_ERROR;
	    }
	    replace = argv[2];
	    getFromWindow = 0;
	} else {
	    sprintf(interp->result,
		    "bad option for \"%.50s\": should be range or selection\"",
		    argv[0]);
	    return TCL_ERROR;
	}
    }

    /*
     * Pick up the search and replacement strings from the search
     * window, if desired.  If the window isn't visible, just pop
     * it up.
     */
    
    if (getFromWindow) {
	if ((mxwPtr->searchWindow == 0) || (*mxwPtr->replaceString == 0)) {
	    return Tcl_Eval(mxwPtr->interp, "focus replace",
		    0, (char **) NULL);
	}
	pattern = mxwPtr->searchString;
	replace = mxwPtr->replaceString;
    }

    value = Tcl_GetVar(mxwPtr->interp, "noRegExps", 1);
    if ((value != NULL) && (*value == '1')) {
        regExp = 0;
    }

    /*
     * For non-range replacements, just replace the selection (there
     * better be one!) with the replacement string.
     */
    
    if (!rangeReplace) {
	result = MxGetSelRange(mxwPtr, &first, &last);
	if (result != TCL_OK) {
	    return result;
	}
	Sx_SelectionClear(mxwPtr->display);
	if (regExp && regOldLine != NULL) {
	    Mx_ReplaceRegExp(mxwPtr, first, last, replace, &start);
	} else {
	    Mx_ReplaceBytes(mxwPtr->fileInfoPtr->file, first,
		    Mx_Offset(mxwPtr->fileInfoPtr->file, last, 1), replace);
	}
	MxGetInWindow(mxwPtr, first, mxwPtr->heightLines/2, 0);
	return TCL_OK;
    }

    /*
     * Range replacement:  search the entire range that's selected,
     * and replace each instance of the pattern.
     */

    if (regExp) {
	if (pattern[0] == '\0') {
	    sprintf(interp->result, "0 replacements made");
	    return TCL_OK;
	}
	errStr = Mx_CompileRegExp(pattern);
	if (errStr != NULL) {
	    if (errStr != NULL) {
		sprintf(interp->result, "bad \"%.50s\" pattern: %.50s",
		        argv[0], errStr);
		return TCL_ERROR;
	    }
	}
    }
    numReplaced = 0;
    floater = Mx_FloaterCreate(mxwPtr->fileInfoPtr->file, &start, &stop);
    while (MX_POS_LEQ(start, stop)) {
	if (regExp) {
	    if (!Mx_SearchRegExp(mxwPtr->fileInfoPtr->file, start,
		    stop, &first, &last) || MX_POS_LESS(stop, last)) {
		break;
	    }
	    Mx_ReplaceRegExp(mxwPtr, first, last,
		    replace, &start);
	} else {
	    if (!Mx_SearchPattern(mxwPtr->fileInfoPtr->file, start,
		    stop, pattern, &first, &last) || MX_POS_LESS(stop, last)) {
		break;
	    }
	    Mx_ReplaceBytes(mxwPtr->fileInfoPtr->file, first,
		    Mx_Offset(mxwPtr->fileInfoPtr->file, last, 1),
		    replace);
	    start = Mx_Offset(mxwPtr->fileInfoPtr->file, first,
		    strlen(replace));
	}
	numReplaced++;
	MxGetInWindow(mxwPtr, first, mxwPtr->heightLines/2, 0);
    }
    Mx_FloaterDelete(floater);
    sprintf(interp->result, "%d replacements made", numReplaced);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Mx_ResetCmd --
 *
 *	This is the top-level procedure that implements the
 *	"reset" 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
Mx_ResetCmd(mxwPtr, interp, argc, argv)
    register MxWindow *mxwPtr;
    Tcl_Interp *interp;
    int argc;
    char **argv;
{
    int result;

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