/* 
 * tkEmacs.c --
 *
 *	This module implements "tkEmacs" widgets for the Tk
 *	toolkit.  The tkEmacs widget provides access to the
 *      emacs as tk widget.
 *
 * Copyright 1992 by Sven Delmas
 *
 * This widget is based upon the tkFrame widget from:
 *
 * Copyright 1990 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.
 *   file: /user6/ouster/wish/RCS/tkFrame.c,v 1.23
 *           92/03/25 09:25:15 ouster Exp $ SPRITE (Berkeley);
 */

#ifndef lint
static char *AtFSid = "$Header: tkEmacs2.3.c[1.1] Wed Dec 23 14:35:57 1992 garfield@garfield frozen $";
#endif

#include "default.h"
#include "tkConfig.h"
#include "tk.h"

/*
 * A data structure of the following type is kept for each
 * tkEmacs that currently exists for this process:
 */

typedef struct {
  Tk_Window tkwin;		/* Window that embodies the tkEmacs.  NULL
				 * means that the window has been destroyed
				 * but the data structures haven't yet been
				 * cleaned up.*/
  Display *display;             /* Display containing widget.  Used, among
                                 * other things, so that resources can be
                                 * freed even after tkwin has gone away. */
  Tcl_Interp *interp;		/* Interpreter associated with
				 * widget.  Used to delete widget
				 * command.  */

  /*
   * The standard widget resources.
   */
  Tk_3DBorder border;		/* Structure used to draw 3-D border and
				 * background. */
  int borderWidth;		/* Width of 3-D border (if any). */
  int relief;			/* 3-d effect: TK_RELIEF_RAISED, etc. */
  Cursor cursor;		/* Current cursor for window, or None. */

  /*
   * This resources are passed directly (at startup, or via Tcp
   * connection) to emacs.
   */
  int tkEmacsAdvise;            /* use advise to update scrollbars */
  char *tkEmacsCommand;         /* The emacs command to execute. */
  char *tkEmacsCursorColor;	/* Text cursor color of emacs. */
  char *tkEmacsEnd;	        /* The end source for emacs. */
  char *tkEmacsFile;	        /* The file to load into emacs. */
  char *tkEmacsFont;	        /* The font of emacs. */
  char *tkEmacsForeground;	/* Foreground color of emacs. */
  char *tkEmacsGeometry;	/* Geometry of emacs. */
  int tkEmacsHeight;	        /* Height in lines of emacs. */
  char *tkEmacsLispFile;	/* The startup lisp file to load. */
  char *tkEmacsStart;	        /* The start source for emacs. */
  int tkEmacsWidth;	        /* Width in columns of emacs. */

  /*
   * widget specific resources
   */
  char *errorCallback;          /* this procedure is called on error */
  int debug;                    /* debugging switch */
  int pollInterval;             /* interval for scrollbar polling */
  int timeout;                  /* timeout for waiting on result */
  char *xScrollCmd;             /* the x scroll command */
  char *yScrollCmd;             /* the x scroll command */
  
  /*
   * internal status variables
   */
  Window emacsWindowId;         /* the emacs send event window id. */
  char *fileHandle1;            /* connection handle */
  char *fileHandle2;            /* accept handle */
  char *ignore;                 /* all ignored resources point
				 * to this variable. */
  int pid;                      /* the process pid. */
  char *result;                 /* result of the last command */
  Tk_TimerToken timerToken;     /* the timer token */
  char *widgetPathName;         /* the path name of the widget */
  char *focusPath;              /* buffer for previous focus */
  
  /*
   * the usual flags variable
   */
  int flags;			/* Various flags;  see below for
				 * definitions. */
} TkEmacs;

/*
 * misc defines to make life easier
 */

#define TO_SECONDS              1000
/*#define DEBUG_PROT              1*/    /* only for me */

/*
 * event types we send to emacs
 */

#define CONFIGURE_EVENT         1
#define EXPOSE_EVENT            2
#define MAP_EVENT               3

/*
 * TkEmacs widget flags       
 *
 * REDRAW_PENDING:		Non-zero means a DoWhenIdle handler
 *				has already been queued to redraw
 *				this window.
 * CLEAR_NEEDED:		Need to clear the window when redrawing.
 * WAIT_FOR_RESULT:             Wait for result.
 * MAP_WINDOW:                  Allow execution of map function
 */

#define REDRAW_PENDING		1
#define CLEAR_NEEDED		2
#define WAIT_FOR_RESULT         4
#define MAP_WINDOW              8

/*
 * protocol defines
 */

#define PREFIX_OFFSET           5
#define CMD_PREFIX              "CMD: "
#define RET_PREFIX              "RET: "
#define ERR_PREFIX              "ERR: "
#define RST_PREFIX              "RST:"
#define END_PREFIX              "END:"
#define NO_TYPE                 0
#define CMD_TYPE                1
#define RET_TYPE                2
#define ERR_TYPE                3
#define END_TYPE                4

/*
 * resource default values
 */

#define DEFAULT_ADVISE          "yes"
#define DEFAULT_DEBUG           "no"
#define DEFAULT_EMACS           "TkEmacs"
#define DEFAULT_END             ""
#define DEFAULT_GEOMETRY        "400x250"
#define DEFAULT_HEIGHT          "0"
#define DEFAULT_INSERT_BG       BLACK
#define DEFAULT_LISP_FILE       "tkemacs.el"
#define DEFAULT_POLL_INTERVAL   "0"
#define DEFAULT_STARTUP         ""
#define DEFAULT_TIMEOUT         "0"
#define DEFAULT_WIDTH           "0"

/*
 * the resource specification
 */
static Tk_ConfigSpec configSpecs[] = {
  {TK_CONFIG_BORDER, "-background", "background", "Background",
     BISQUE1, Tk_Offset(TkEmacs, border), TK_CONFIG_COLOR_ONLY},
  {TK_CONFIG_BORDER, "-background", "background", "Background",
     WHITE, Tk_Offset(TkEmacs, border), TK_CONFIG_MONO_ONLY},
  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
     (char *) NULL, 0, 0},
  {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
     (char *) NULL, 0, 0},
  {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
     0, Tk_Offset(TkEmacs, borderWidth), 0},
  {TK_CONFIG_STRING, "-command", "command", "Command",
     DEFAULT_EMACS, Tk_Offset(TkEmacs, tkEmacsCommand), 0},
  {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
     ((char *) NULL), Tk_Offset(TkEmacs, cursor), TK_CONFIG_NULL_OK},
  {TK_CONFIG_STRING, "-endsource", "endSource", "EndSource",
     DEFAULT_END, Tk_Offset(TkEmacs, tkEmacsEnd), 0},
  {TK_CONFIG_STRING, "-errorcallback", "errorCallback", "ErrorCallback",
     (char *) NULL, Tk_Offset(TkEmacs, errorCallback), 0},
  {TK_CONFIG_BOOLEAN, "-debug", "debug", "Debug",
     DEFAULT_DEBUG, Tk_Offset(TkEmacs, debug), 0},
  {TK_CONFIG_STRING, "-file", "file", "File",
     (char *) NULL, Tk_Offset(TkEmacs, tkEmacsFile), 0},
  {TK_CONFIG_STRING, "-font", "font", "Font",
     (char *) NULL, Tk_Offset(TkEmacs, tkEmacsFont), 0},
  {TK_CONFIG_STRING, "-foreground", "foreground",
     "Foreground", (char *) NULL,
     Tk_Offset(TkEmacs, tkEmacsForeground), 0},
  {TK_CONFIG_STRING, "-geometry", "geometry", "Geometry",
     DEFAULT_GEOMETRY, Tk_Offset(TkEmacs, tkEmacsGeometry), 0},
  {TK_CONFIG_INT, "-height", "height", "Height",
     DEFAULT_HEIGHT, Tk_Offset(TkEmacs, tkEmacsHeight), 0},
  {TK_CONFIG_STRING, "-insertbackground", "insertBackground",
     "Foreground", DEFAULT_INSERT_BG,
     Tk_Offset(TkEmacs, tkEmacsCursorColor), 0},
  {TK_CONFIG_STRING, "-lispfile", "lispFile", "LispFile",
     DEFAULT_LISP_FILE, Tk_Offset(TkEmacs, tkEmacsLispFile), 0},
  {TK_CONFIG_INT, "-pollinterval", "pollInterval", "PollInterval",
     DEFAULT_POLL_INTERVAL, Tk_Offset(TkEmacs, pollInterval), 0},
  {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
     "flat", Tk_Offset(TkEmacs, relief), 0},
  {TK_CONFIG_STRING, "-startsource", "startSource", "StartSource",
     DEFAULT_STARTUP, Tk_Offset(TkEmacs, tkEmacsStart), 0},
  {TK_CONFIG_INT, "-timeout", "timeout", "Timeout",
     DEFAULT_TIMEOUT, Tk_Offset(TkEmacs, timeout), 0},
  {TK_CONFIG_BOOLEAN, "-useadvise", "useAdvise", "UseAdvise",
     DEFAULT_ADVISE, Tk_Offset(TkEmacs, tkEmacsAdvise), 0},
  {TK_CONFIG_INT, "-width", "width", "Width",
     DEFAULT_WIDTH, Tk_Offset(TkEmacs, tkEmacsWidth), 0},
  {TK_CONFIG_INT, "-windowid", "windowId", "WindowId",
     NULL, Tk_Offset(TkEmacs, emacsWindowId), 0},
  {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
     (char *) NULL, Tk_Offset(TkEmacs, xScrollCmd), TK_CONFIG_NULL_OK},
  {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
     (char *) NULL, Tk_Offset(TkEmacs, yScrollCmd), TK_CONFIG_NULL_OK},

  /* here follow ignored resources */
  {TK_CONFIG_STRING, "-exportselection", "exportSelection",
     "ExportSelection", (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-insertofftime", "insertOffTime", "OffTime",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-insertontime", "insertOnTime", "OnTime",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-insertwidth", "insertWidth", "InsertWidth",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-padx", "padX", "Pad",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-pady", "padY", "Pad",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-selectbackground", "selectBackground", "Foreground",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-selectforeground", "selectForeground", "Background",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-setgrid", "setGrid", "SetGrid",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-state", "state", "State",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_STRING, "-wrap", "wrap", "Wrap",
     (char *) NULL, Tk_Offset(TkEmacs, ignore), 0},
  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
     (char *) NULL, 0, 0}
};
  
/*
 * Forward declarations for procedures defined later in this file:
 */

static int	ConfigureTkEmacs _ANSI_ARGS_((Tcl_Interp *interp,
		    TkEmacs *tkEmacsPtr, int argc, char **argv, int flags));
static void	DestroyTkEmacs _ANSI_ARGS_((ClientData clientData));
static int	TkEmacsAcceptFileHandler _ANSI_ARGS_((ClientData clientData,
		    Tcl_Interp *interp, int argc, char **argv));
static void	TkEmacsDisplay _ANSI_ARGS_((ClientData clientData));
static void	TkEmacsEventProc _ANSI_ARGS_((ClientData clientData,
		    XEvent *eventPtr));
static void	TkEmacsMap _ANSI_ARGS_((ClientData clientData));
static int	TkEmacsReadData _ANSI_ARGS_((TkEmacs *tkEmacsPtr,
		    Tcl_Interp *interp, char *fileHandle));
static int	TkEmacsReadFileHandler _ANSI_ARGS_((ClientData clientData,
		    Tcl_Interp *interp, int argc, char **argv));
static void	TkEmacsSendEvent _ANSI_ARGS_((TkEmacs *tkEmacsPtr,
                    int type));
static int	TkEmacsSendString _ANSI_ARGS_((Tcl_Interp *interp,
		    TkEmacs *tkEmacsPtr, char *prefix, char *buffer));
static void	TkEmacsSetupEmacs _ANSI_ARGS_((ClientData clientData));
static int	TkEmacsStartEmacs _ANSI_ARGS_((Tcl_Interp *interp,
		    TkEmacs *tkEmacsPtr));
static int	TkEmacsStopEmacs _ANSI_ARGS_((TkEmacs *tkEmacsPtr));
static void	TkEmacsTimerProc _ANSI_ARGS_((ClientData clientData));
static void	TkEmacsUpdateScrollbars _ANSI_ARGS_((ClientData clientData));
static void	TkEmacsWaitForResult _ANSI_ARGS_((TkEmacs *tkEmacsPtr));
static int	TkEmacsWidgetCmd _ANSI_ARGS_((ClientData clientData,
		    Tcl_Interp *interp, int argc, char **argv));
static int	TK_CMD _ANSI_ARGS_((char *string));
static int	TK_RET _ANSI_ARGS_((char *string));
static int	TK_ERR _ANSI_ARGS_((char *string));
static int	TK_RST _ANSI_ARGS_((char *string));
static int	TK_END _ANSI_ARGS_((char *string));

/*
 *--------------------------------------------------------------
 *
 * Tk_TkEmacsCmd --
 *
 *	This procedure is invoked to process the "tkEmacs" and
 *	"toplevel" Tcl commands.  See the user documentation for
 *	details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
Tk_TkEmacsCmd(clientData, interp, argc, argv)
     ClientData clientData;	/* Main window associated with
				 * interpreter. */
     Tcl_Interp *interp;	/* Current interpreter. */
     int argc;			/* Number of arguments. */
     char **argv;		/* Argument strings. */
{
  Tk_Window tkwin = (Tk_Window) clientData;
  Tk_Window new;
  register TkEmacs *tkEmacsPtr;
  char *tmpBuffer = (char *) NULL;
  
  if (argc < 2) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "wrong # args: should be \"",
		     argv[0], " pathName ?options?\"", (char *) NULL);
    return TCL_ERROR;
  }

  /*
   * Check if interpreter can handle this widget
   */
  if (Tcl_Eval(interp, "info commands connect", 0,
	       (char **) NULL) != TCL_OK) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "the interpreter does not have tclRawTCP",
		     (char *) NULL);
    return TCL_ERROR;
  }
  if (strlen(interp->result) == 0 ||
      strcmp(interp->result, "connect") != 0) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "the interpreter does not have tclRawTCP",
		     (char *) NULL);
    return TCL_ERROR;
  }
  
  /*
   * Create the window.
   */
  new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  if (new == NULL) {
    return TCL_ERROR;
  }
  
  Tk_SetClass(new, "TkEmacs");
  tkEmacsPtr = (TkEmacs *) ckalloc(sizeof(TkEmacs));
  tkEmacsPtr->tkwin = new;
  tkEmacsPtr->display = Tk_Display(new);
  tkEmacsPtr->interp = interp;
  tkEmacsPtr->border = NULL;
  tkEmacsPtr->cursor = None;
  tkEmacsPtr->borderWidth = 0;
  tkEmacsPtr->relief = TK_RELIEF_FLAT;
  tkEmacsPtr->tkEmacsCommand = NULL;
  tkEmacsPtr->tkEmacsCursorColor = NULL;
  tkEmacsPtr->tkEmacsEnd = NULL;
  tkEmacsPtr->tkEmacsFile = NULL;
  tkEmacsPtr->tkEmacsFont = NULL;
  tkEmacsPtr->tkEmacsForeground = NULL;
  tkEmacsPtr->tkEmacsGeometry = NULL;
  tkEmacsPtr->tkEmacsHeight = 0;
  tkEmacsPtr->tkEmacsLispFile = NULL;
  tkEmacsPtr->tkEmacsStart = NULL;
  tkEmacsPtr->tkEmacsWidth = 0;
  tkEmacsPtr->errorCallback = NULL;
  tkEmacsPtr->xScrollCmd = NULL;
  tkEmacsPtr->yScrollCmd = NULL;
  tkEmacsPtr->emacsWindowId = 0;
  tkEmacsPtr->fileHandle1 = NULL;
  tkEmacsPtr->fileHandle2 = NULL;
  tkEmacsPtr->ignore = NULL;
  tkEmacsPtr->pid = 0;
  tkEmacsPtr->result = NULL;
  tkEmacsPtr->widgetPathName = NULL;
  tkEmacsPtr->focusPath = NULL;
  tkEmacsPtr->flags = 0;

#if !defined(DEBUG_PROT)
  Tk_CreateEventHandler(tkEmacsPtr->tkwin, KeyPressMask |
			KeyReleaseMask | ExposureMask |
			ButtonPressMask | PointerMotionMask |
			ButtonReleaseMask | EnterWindowMask |
			LeaveWindowMask | FocusChangeMask |
			StructureNotifyMask | PropertyChangeMask,
			TkEmacsEventProc, (ClientData) tkEmacsPtr);
#endif

  Tcl_CreateCommand(interp, Tk_PathName(tkEmacsPtr->tkwin),
		    TkEmacsWidgetCmd, (ClientData) tkEmacsPtr,
		    (void (*)()) NULL);
  Tk_MakeWindowExist(tkEmacsPtr->tkwin);
  if (ConfigureTkEmacs(interp, tkEmacsPtr, argc-2, argv+2, 0) != TCL_OK) {
    Tk_DestroyWindow(tkEmacsPtr->tkwin);
    return TCL_ERROR;
  }
  
  tkEmacsPtr->widgetPathName =
    ckalloc(strlen(Tk_PathName(tkEmacsPtr->tkwin)) + 1);
  strcpy(tkEmacsPtr->widgetPathName, Tk_PathName(tkEmacsPtr->tkwin));

#if !defined(DEBUG_PROT)
  tkEmacsPtr->flags |= WAIT_FOR_RESULT;
  if (TkEmacsStartEmacs(interp, tkEmacsPtr) != TCL_OK) {
    if (tkEmacsPtr->fileHandle1 != NULL) {
      Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		  tkEmacsPtr->fileHandle1, (char *) NULL);
    }
    sprintf(tmpBuffer, "TkEmacsAcceptFileHandler%s",
	    Tk_PathName(tkEmacsPtr->tkwin));
    Tcl_DeleteCommand(interp, tmpBuffer);
    if (tkEmacsPtr->fileHandle2 != NULL) {
      Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		  tkEmacsPtr->fileHandle2, (char *) NULL);
    }
    sprintf(tmpBuffer, "TkEmacsReadFileHandler%s",
	    Tk_PathName(tkEmacsPtr->tkwin));
    Tcl_DeleteCommand(interp, tmpBuffer);
    ckfree((char *) tmpBuffer);
    Tk_DestroyWindow(tkEmacsPtr->tkwin);
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "could not create emacs widget.",
		     (char *) NULL);
    return TCL_ERROR;
  }
  TkEmacsWaitForResult(tkEmacsPtr);
#else
  if (TkEmacsStartEmacs(interp, tkEmacsPtr) != TCL_OK) {
    fprintf(stderr, "tkemacs startup error!\n");
    exit(1);
  }
#endif

  interp->result = Tk_PathName(tkEmacsPtr->tkwin);
  return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * TkEmacsWidgetCmd --
 *
 *	This procedure is invoked to process the Tcl command
 *	that corresponds to a tkEmacs widget.  See the user
 *	documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

static int
TkEmacsWidgetCmd(clientData, interp, argc, argv)
     ClientData clientData;	/* Information about tkEmacs widget. */
     Tcl_Interp *interp;		/* Current interpreter. */
     int argc;			/* Number of arguments. */
     char **argv;		/* Argument strings. */
{
  register TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;
  int result = TCL_OK;
  int length = 0;
  char c;
  char *tmpBuffer = (char *) NULL;
  
  if (argc < 2) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "wrong # args: should be \"",
		     argv[0], " option ?arg arg ...?\"", (char *) NULL);
    return TCL_ERROR;
  }
  Tk_Preserve((ClientData) tkEmacsPtr);
  c = argv[1][0];
  length = strlen(argv[1]);
  if ((c == 'c') && (strncmp(argv[1], "compare", length) == 0) &&
     (length >= 3)) {
    if (argc != 5) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " compare index1 op index2\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    tmpBuffer =
      ckalloc(50 + strlen(argv[2]) + strlen(argv[3]) + strlen(argv[4]));
    sprintf(tmpBuffer, "(tk-compare-index \"%s\" \"%s\" \"%s\")",
	    argv[2], argv[3], argv[4]);
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	== TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, tkEmacsPtr->result, (char *) NULL);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
    }
    ckfree((char *) tmpBuffer);
  } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0) &&
	     (length >= 3)) {
    Tcl_ResetResult(interp);
    if (argc == 2) {
      result = Tk_ConfigureInfo(interp, tkEmacsPtr->tkwin, configSpecs,
				(char *) tkEmacsPtr, (char *) NULL, 0);
    } else if (argc == 3) {
      result = Tk_ConfigureInfo(interp, tkEmacsPtr->tkwin, configSpecs,
				(char *) tkEmacsPtr, argv[2], 0);
    } else {
      result = ConfigureTkEmacs(interp, tkEmacsPtr, argc-2, argv+2,
				TK_CONFIG_ARGV_ONLY);
    }
  } else if ((c == 'd') && (strncmp(argv[1], "debug", length) == 0)
	     && (length >= 3)) {
    if (argc > 3) {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " debug ?on|off?\"", (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (argc == 2) {
      interp->result = tkEmacsPtr->debug ? "on" : "off";
    } else {
      if (Tcl_GetBoolean(interp, argv[2], &tkEmacsPtr->debug) != TCL_OK) {
	Tk_Release((ClientData) tkEmacsPtr);
	return TCL_ERROR;
      }
    }
  } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0) &&
	     (length >= 3)) {
    if ((argc < 3) || (argc > 4)) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " delete index ?index?\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (argc == 3) {
      tmpBuffer = ckalloc(50 + strlen(argv[2]));
      sprintf(tmpBuffer, "(tk-delete-text \"%s\")", argv[2]);
    } else {
      tmpBuffer = ckalloc(50 + strlen(argv[2]) + strlen(argv[3]));
      sprintf(tmpBuffer, "(tk-delete-text \"%s\" \"%s\")", argv[2], argv[3]);
    }
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	== TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
    }
    ckfree((char *) tmpBuffer);
  } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
    if ((argc < 2) || (argc > 4)) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " get ?index1? ?index2?\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (argc == 4) {
      tmpBuffer = ckalloc(50 + strlen(argv[2]) + strlen(argv[3]));
      sprintf(tmpBuffer, "(tk-get-buffer-string \"%s\" \"%s\")",
	      argv[2], argv[3]);
    } else {
      if (argc == 3) {
	tmpBuffer = ckalloc(50 + strlen(argv[2]));
	sprintf(tmpBuffer, "(tk-get-buffer-string \"%s\")", argv[2]);
      } else {
	tmpBuffer = ckalloc(50);
	sprintf(tmpBuffer, "(tk-get-buffer-string)");
      }
    }
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	== TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, tkEmacsPtr->result, (char *) NULL);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
    }
    ckfree((char *) tmpBuffer);
  } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
	     && (length >= 3)) {
    if (argc != 3) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " index index\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    tmpBuffer = ckalloc(50 + strlen(argv[2]));
    sprintf(tmpBuffer, "(tk-translate-position \"%s\")", argv[2]);
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	== TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, tkEmacsPtr->result, (char *) NULL);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
    }
    ckfree((char *) tmpBuffer);
  } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
	     && (length >= 3)) {
    if (argc != 4) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " insert index chars ?chars ...?\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    tmpBuffer = ckalloc(50 + strlen(argv[2]) + strlen(argv[3]));
    sprintf(tmpBuffer, "(tk-insert-string \"%s\" \"%s\")", argv[2], argv[3]);
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	== TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
    }
    ckfree((char *) tmpBuffer);
  } else if ((c == 'm') && (strncmp(argv[1], "mark", length) == 0)) {
    if (argc < 3) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " mark option ?arg arg ...?\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (strncmp(argv[2], "set", strlen(argv[2])) == 0 &&
	strncmp(argv[3], "insert", strlen(argv[3])) == 0) {
      tmpBuffer = ckalloc(50 + strlen(argv[4]));
      sprintf(tmpBuffer, "(tk-set-cursor \"%s\")", argv[4]);
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	  == TCL_OK) {
	TkEmacsWaitForResult(tkEmacsPtr);
	Tcl_ResetResult(interp);
      } else {
	TkEmacsWaitForResult(tkEmacsPtr);
      }
      ckfree((char *) tmpBuffer);
    }
  } else if ((c == 'r') && (strncmp(argv[1], "redisplay", length) == 0)
	     && (length >= 3)) {
    if (argc != 2) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " redisplay\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (Tk_IsMapped(tkEmacsPtr->tkwin) &&
	!(tkEmacsPtr->flags & REDRAW_PENDING)) {
      tkEmacsPtr->flags |= REDRAW_PENDING|CLEAR_NEEDED;
      Tk_DoWhenIdle(TkEmacsDisplay, (ClientData) tkEmacsPtr);
    }
  } else if ((c == 'r') && (strncmp(argv[1], "reset", length) == 0)
	     && (length >= 3)) {
    if (argc != 2) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " reset\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, RST_PREFIX, "") ==
	TCL_OK) {
      Tcl_ResetResult(interp);
    }
  } else if ((c == 's') && (strncmp(argv[1], "scan", length) == 0) &&
	     (length >= 2)) {
    if (argc != 4) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " scan mark|dragto x,y\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
  } else if ((c == 's') && (strncmp(argv[1], "send", length) == 0) &&
	     (length >= 3)) {
    if (argc != 3) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " send lisp-command\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, argv[2]) ==
	TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, tkEmacsPtr->result, (char *) NULL);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
      if (strlen(tkEmacsPtr->errorCallback) > 0) {
	if (Tcl_VarEval(tkEmacsPtr->interp,
			tkEmacsPtr->errorCallback,
			" ", tkEmacsPtr->widgetPathName, " {",
			interp->result, "}", (char *) NULL) != TCL_OK) {
	  fprintf(stderr, "%s\n", tkEmacsPtr->result);
	}
      }
    }
  } else if ((c == 's') && (strncmp(argv[1], "setxscroll", length) == 0) &&
	     (length >= 4)) {
    if (argc != 6) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " setxscroll totalUnits windowUnits",
		       " firstUnit lastUnit\"", (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (tkEmacsPtr->xScrollCmd != NULL &&
	strlen(tkEmacsPtr->xScrollCmd) > 0) {
      if (Tcl_VarEval(tkEmacsPtr->interp, tkEmacsPtr->xScrollCmd,
		      " ", argv[2], " ", argv[3], " ", argv[4],
		      " ", argv[5], (char *) NULL) != TCL_OK) {
	return TCL_ERROR;
      }
    }
  } else if ((c == 's') && (strncmp(argv[1], "setyscroll", length) == 0) && 
	     (length >= 4)) {
    if (argc != 6) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " setyscroll totalUnits windowUnits",
		       " firstUnit lastUnit\"", (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (tkEmacsPtr->yScrollCmd != NULL &&
	strlen(tkEmacsPtr->yScrollCmd) > 0) {
      if (Tcl_VarEval(tkEmacsPtr->interp, tkEmacsPtr->yScrollCmd,
		      " ", argv[2], " ", argv[3], " ", argv[4],
		      " ", argv[5], (char *) NULL) != TCL_OK) {
	return TCL_ERROR;
      }
    }
  } else if ((c == 's') && (strncmp(argv[1], "startemacs", length) == 0) && 
	     (length >= 4)) {
    if (argc != 2) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " startemacs\"", (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (tkEmacsPtr->pid != 0) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "emacs is already running",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsStartEmacs(interp, tkEmacsPtr) != TCL_OK) {
      tmpBuffer = ckalloc(50 + strlen(Tk_PathName(tkEmacsPtr->tkwin)));
      if (tkEmacsPtr->fileHandle1 != NULL) {
	Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		    tkEmacsPtr->fileHandle1, (char *) NULL);
      }
      sprintf(tmpBuffer, "TkEmacsAcceptFileHandler%s",
	      Tk_PathName(tkEmacsPtr->tkwin));
      Tcl_DeleteCommand(interp, tmpBuffer);
      if (tkEmacsPtr->fileHandle2 != NULL) {
	Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		    tkEmacsPtr->fileHandle2, (char *) NULL);
      }
      sprintf(tmpBuffer, "TkEmacsReadFileHandler%s",
	      Tk_PathName(tkEmacsPtr->tkwin));
      Tcl_DeleteCommand(interp, tmpBuffer);
      Tk_DestroyWindow(tkEmacsPtr->tkwin);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "could not restart emacs widget.",
		       (char *) NULL);
      ckfree((char *) tmpBuffer);
      return TCL_ERROR;
    }
    TkEmacsWaitForResult(tkEmacsPtr);
  } else if ((c == 's') && (strncmp(argv[1], "stopemacs", length) == 0) && 
	     (length >= 4)) {
    if (argc != 2) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " stopemacs\"", (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (tkEmacsPtr->pid == 0) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "there is no emacs running",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    TkEmacsStopEmacs(tkEmacsPtr);
  } else if ((c == 't') && (strncmp(argv[1], "tag", length) == 0)) {
    if (argc < 3) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " tag option ?arg arg ...?\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
  } else if ((c == 'u') &&
	     (strncmp(argv[1], "updatescrollbars", length) == 0)) {
    if (argc != 2) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " updatescrollbars\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    Tk_DoWhenIdle(TkEmacsUpdateScrollbars, (ClientData) tkEmacsPtr);
  } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
    if ((argc < 3) || (argc > 4)) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " xview ?-pickplace? offset\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (argc == 4 &&
	strncmp(argv[2], "-pickplace", strlen(argv[2])) == 0) {
      tmpBuffer = ckalloc(50 + strlen(argv[3]));
      sprintf(tmpBuffer, "(tk-set-x-view %s)", argv[3]);
    } else {
      if (argc == 4 &&
	  strncmp(argv[3], "-pickplace", strlen(argv[3])) == 0) {
	tmpBuffer = ckalloc(50 + strlen(argv[2]));
	sprintf(tmpBuffer, "(tk-set-x-view %s)", argv[2]);
      } else {
	if (strncmp(argv[2], "-pickplace", strlen(argv[2])) != 0) {
	  tmpBuffer = ckalloc(50 + strlen(argv[2]));
	  sprintf(tmpBuffer, "(tk-set-x-view %s)", argv[2]);
	} else {
	  Tcl_ResetResult(interp);
	  Tcl_AppendResult(interp, "wrong # args: should be \"",
			   argv[0], " xview ?-pickplace? offset\"",
			   (char *) NULL);
	  Tk_Release((ClientData) tkEmacsPtr);
	  return TCL_ERROR;
	}
      }
    }
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	== TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, tkEmacsPtr->result, (char *) NULL);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
    }
    ckfree((char *) tmpBuffer);
  } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
    if ((argc < 3) || (argc > 4)) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "wrong # args: should be \"",
		       argv[0], " yview ?-pickplace? offset\"",
		       (char *) NULL);
      Tk_Release((ClientData) tkEmacsPtr);
      return TCL_ERROR;
    }
    if (argc == 4 &&
	strncmp(argv[2], "-pickplace", strlen(argv[2])) == 0) {
      tmpBuffer = ckalloc(50 + strlen(argv[3]));
      sprintf(tmpBuffer, "(tk-set-y-view %s)", argv[3]);
    } else {
      if (argc == 4 &&
	  strncmp(argv[3], "-pickplace", strlen(argv[3])) == 0) {
	tmpBuffer = ckalloc(50 + strlen(argv[2]));
	sprintf(tmpBuffer, "(tk-set-y-view %s)", argv[2]);
      } else {
	if (strncmp(argv[2], "-pickplace", strlen(argv[2])) != 0) {
	  tmpBuffer = ckalloc(50 + strlen(argv[2]));
	  sprintf(tmpBuffer, "(tk-set-y-view %s)", argv[2]);
	} else {
	  Tcl_ResetResult(interp);
	  Tcl_AppendResult(interp, "wrong # args: should be \"",
			   argv[0], " yview ?-pickplace? offset\"",
			   (char *) NULL);
	  Tk_Release((ClientData) tkEmacsPtr);
	  return TCL_ERROR;
	}
      }
    }
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    if (TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer)
	== TCL_OK) {
      TkEmacsWaitForResult(tkEmacsPtr);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, tkEmacsPtr->result, (char *) NULL);
    } else {
      TkEmacsWaitForResult(tkEmacsPtr);
    }
    ckfree((char *) tmpBuffer);
  } else {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "bad option \"", argv[1],
		     "\":  must be config", (char *) NULL);
    result = TCL_ERROR;
  }
  Tk_Release((ClientData) tkEmacsPtr);
  return result;
}

/*
 *----------------------------------------------------------------------
 *
 * DestroyTkEmacs --
 *
 *	This procedure is invoked by Tk_EventuallyFree or Tk_Release
 *	to clean up the internal structure of a tkEmacs at a safe time
 *	(when no-one is using it anymore).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Everything associated with the tkEmacs is freed up.
 *
 *----------------------------------------------------------------------
 */

static void
DestroyTkEmacs(clientData)
     ClientData clientData;	/* Info about tkEmacs widget. */
{
  register TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;

  TkEmacsStopEmacs(tkEmacsPtr);

  if (tkEmacsPtr->border != NULL) {
    Tk_Free3DBorder(tkEmacsPtr->border);
  }
  if (tkEmacsPtr->cursor != None) {
    Tk_FreeCursor(tkEmacsPtr->cursor);
  }
  if (tkEmacsPtr->tkEmacsCommand != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsCommand);
  }
  if (tkEmacsPtr->tkEmacsCursorColor != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsCursorColor);
  }
  if (tkEmacsPtr->tkEmacsEnd != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsEnd);
  }
  if (tkEmacsPtr->tkEmacsFile != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsFile);
  }
  if (tkEmacsPtr->tkEmacsFont != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsFont);
  }
  if (tkEmacsPtr->tkEmacsForeground != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsForeground);
  }
  if (tkEmacsPtr->tkEmacsGeometry != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsGeometry);
  }
  if (tkEmacsPtr->tkEmacsLispFile != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsLispFile);
  }
  if (tkEmacsPtr->tkEmacsStart != NULL) {
    ckfree((char *) tkEmacsPtr->tkEmacsStart);
  }
  if (tkEmacsPtr->errorCallback != NULL) {
    ckfree((char *) tkEmacsPtr->errorCallback);
  }
  if (tkEmacsPtr->xScrollCmd != NULL) {
    ckfree((char *) tkEmacsPtr->xScrollCmd);
  }
  if (tkEmacsPtr->yScrollCmd != NULL) {
    ckfree((char *) tkEmacsPtr->yScrollCmd);
  }
  if (tkEmacsPtr->fileHandle1 != NULL) {
    ckfree((char *) tkEmacsPtr->fileHandle1);
  }
  if (tkEmacsPtr->fileHandle2 != NULL) {
    ckfree((char *) tkEmacsPtr->fileHandle2);
  }
  if (tkEmacsPtr->ignore != NULL) {
    ckfree((char *) tkEmacsPtr->ignore);
  }
  if (tkEmacsPtr->result != NULL) {
    ckfree((char *) tkEmacsPtr->result);
  }
  if (tkEmacsPtr->widgetPathName != NULL) {
    ckfree((char *) tkEmacsPtr->widgetPathName);
  }
  if (tkEmacsPtr->focusPath != NULL) {
    ckfree((char *) tkEmacsPtr->focusPath);
  }

  ckfree((char *) tkEmacsPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * ConfigureTkEmacs --
 *
 *	This procedure is called to process an argv/argc list, plus
 *	the Tk option database, in order to configure (or
 *	reconfigure) a tkEmacs widget.
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	Configuration information, such as text string, colors, font,
 *	etc. get set for tkEmacsPtr;  old resources get freed, if there
 *	were any.
 *
 *----------------------------------------------------------------------
 */

static int
ConfigureTkEmacs(interp, tkEmacsPtr, argc, argv, flags)
     Tcl_Interp *interp;	/* Used for error reporting. */
     register TkEmacs *tkEmacsPtr;/* Information about widget;  may or may
				   * not already have values for some
				   * fields. */
     int argc;			/* Number of valid entries in argv. */
     char **argv;		/* Arguments. */
     int flags;			/* Flags to pass to Tk_ConfigureWidget. */
{
  char *background = (char *) NULL;
  char *tmpBuffer = (char *) NULL;
  Window oldemacsWindowId = tkEmacsPtr->emacsWindowId;
  Tk_3DBorder oldTkEmacsBorder = tkEmacsPtr->border;
  int oldTkEmacsAdvise = tkEmacsPtr->tkEmacsAdvise;
  int oldTkEmacsBorderWidth = tkEmacsPtr->borderWidth;
  char *oldTkEmacsCursorColor = tkEmacsPtr->tkEmacsCursorColor;
  char *oldTkEmacsFont = tkEmacsPtr->tkEmacsFont;
  char *oldTkEmacsFile = tkEmacsPtr->tkEmacsFile;
  char *oldTkEmacsForeground = tkEmacsPtr->tkEmacsForeground;
  char *oldTkEmacsGeometry = tkEmacsPtr->tkEmacsGeometry;
  int oldTkEmacsPollInterval = tkEmacsPtr->pollInterval;
  XWindowAttributes windowAttr;
  
  if (Tk_ConfigureWidget(interp, tkEmacsPtr->tkwin, configSpecs,
			 argc, argv, (char *) tkEmacsPtr, flags) != TCL_OK) {
    return TCL_ERROR;
  }
  
  Tk_SetBackgroundFromBorder(tkEmacsPtr->tkwin, tkEmacsPtr->border);
#if !defined(DEBUG_PROT)
  if (tkEmacsPtr->tkEmacsHeight == 0 && tkEmacsPtr->tkEmacsWidth == 0) {
    if (tkEmacsPtr->tkEmacsGeometry != NULL &&
	oldTkEmacsGeometry != tkEmacsPtr->tkEmacsGeometry) {
      int height, width;
      
      if (sscanf(tkEmacsPtr->tkEmacsGeometry, "%dx%d", &width, &height) != 2) {
	Tcl_ResetResult(interp);
	Tcl_AppendResult(interp, "bad geometry \"",
			 tkEmacsPtr->tkEmacsGeometry,
			 "\": expected widthxheight", (char *) NULL);
	return TCL_ERROR;
      }
      Tk_GeometryRequest(tkEmacsPtr->tkwin, width, height);
      Tk_SetInternalBorder(tkEmacsPtr->tkwin, tkEmacsPtr->borderWidth);
      if (tkEmacsPtr->pid != 0 &&
	  tkEmacsPtr->emacsWindowId != 0 &&
	  strlen(tkEmacsPtr->tkEmacsGeometry) > 0 &&
	  oldTkEmacsGeometry != tkEmacsPtr->tkEmacsGeometry) {
	TkEmacsSendEvent(tkEmacsPtr, CONFIGURE_EVENT);
      }
      Tk_DoWhenIdle(TkEmacsUpdateScrollbars, (ClientData) tkEmacsPtr);
    }
  } else {
    XGetWindowAttributes(Tk_Display(tkEmacsPtr->tkwin),
			 Tk_WindowId(tkEmacsPtr->tkwin), &windowAttr);
    Tk_GeometryRequest(tkEmacsPtr->tkwin, windowAttr.width,
		       windowAttr.height);
  }
  
  if (tkEmacsPtr->pid != 0 &&
      tkEmacsPtr->emacsWindowId != 0) {
    if (oldTkEmacsAdvise != tkEmacsPtr->tkEmacsAdvise) {
      tmpBuffer = ckalloc(60);
      if (tkEmacsPtr->tkEmacsAdvise) {
	strcpy(tmpBuffer, "(tk-advise-change-view-functions)");
      } else {
	strcpy(tmpBuffer, "(tk-unadvise-change-view-functions)");
      }
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer);
      TkEmacsWaitForResult(tkEmacsPtr);
      ckfree((char *) tmpBuffer);
    }
    if (oldTkEmacsBorder != tkEmacsPtr->border) {
      if (Tk_ConfigureInfo(tkEmacsPtr->interp, tkEmacsPtr->tkwin,
			   configSpecs, (char *) tkEmacsPtr,
			   "-background", 0) == TCL_OK) {
	if ((background = strrchr(tkEmacsPtr->interp->result, ' '))
	    != (char *) NULL) {
	  background++;
	  tmpBuffer = ckalloc(40 + strlen(background));
	  sprintf(tmpBuffer, "(x-set-background-color \"%s\")",
		  background);
	  tkEmacsPtr->flags |= WAIT_FOR_RESULT;
	  TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer);
	  TkEmacsWaitForResult(tkEmacsPtr);
	  ckfree((char *) tmpBuffer);
	}
      }
    }
    if (oldTkEmacsBorderWidth != tkEmacsPtr->borderWidth) {
      tmpBuffer = ckalloc(60);
      sprintf(tmpBuffer, "(x-set-internal-border-width %ld)",
	      tkEmacsPtr->borderWidth);
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer);
      TkEmacsWaitForResult(tkEmacsPtr);
      TkEmacsSendEvent(tkEmacsPtr, CONFIGURE_EVENT);
      Tk_DoWhenIdle(TkEmacsUpdateScrollbars, (ClientData) tkEmacsPtr);
      ckfree((char *) tmpBuffer);
    }
    if (tkEmacsPtr->tkEmacsCursorColor != NULL &&
	strlen(tkEmacsPtr->tkEmacsCursorColor) > 0 &&
	oldTkEmacsCursorColor != tkEmacsPtr->tkEmacsCursorColor) {
      tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsCursorColor));
      sprintf(tmpBuffer, "(x-set-cursor-color \"%s\")",
	      tkEmacsPtr->tkEmacsCursorColor);
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer);
      TkEmacsWaitForResult(tkEmacsPtr);
      ckfree((char *) tmpBuffer);
    }
    if (tkEmacsPtr->tkEmacsFile != NULL &&
	strlen(tkEmacsPtr->tkEmacsFile) > 0 &&
	oldTkEmacsFile != tkEmacsPtr->tkEmacsFile) {
      tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsFile));
      sprintf(tmpBuffer, "(find-file \"%s\")",
	      tkEmacsPtr->tkEmacsFile);
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer);
      TkEmacsWaitForResult(tkEmacsPtr);
      ckfree((char *) tmpBuffer);
    }
    if (tkEmacsPtr->tkEmacsFont != NULL &&
	strlen(tkEmacsPtr->tkEmacsFont) > 0 &&
	oldTkEmacsFont != tkEmacsPtr->tkEmacsFont) {
      tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsFont));
      sprintf(tmpBuffer, "(x-set-font \"%s\")",
	      tkEmacsPtr->tkEmacsFont);
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer);
      TkEmacsWaitForResult(tkEmacsPtr);
      TkEmacsSendEvent(tkEmacsPtr, CONFIGURE_EVENT);
      Tk_DoWhenIdle(TkEmacsUpdateScrollbars, (ClientData) tkEmacsPtr);
      ckfree((char *) tmpBuffer);
    }
    if (tkEmacsPtr->tkEmacsForeground != NULL &&
	strlen(tkEmacsPtr->tkEmacsForeground) > 0 &&
	oldTkEmacsForeground != tkEmacsPtr->tkEmacsForeground) {
      tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsForeground));
      sprintf(tmpBuffer, "(x-set-foreground-color \"%s\")",
	      tkEmacsPtr->tkEmacsForeground);
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(interp, tkEmacsPtr, CMD_PREFIX, tmpBuffer);
      TkEmacsWaitForResult(tkEmacsPtr);
      ckfree((char *) tmpBuffer);
    }
    if (oldTkEmacsPollInterval != tkEmacsPtr->pollInterval) {
      if (tkEmacsPtr->timerToken != NULL) {
	Tk_DeleteTimerHandler(tkEmacsPtr->timerToken);
	tkEmacsPtr->timerToken = NULL;
      }
      if (tkEmacsPtr->pollInterval > 0) {
	tkEmacsPtr->timerToken =
	  Tk_CreateTimerHandler(tkEmacsPtr->pollInterval * TO_SECONDS,
			    TkEmacsTimerProc, (ClientData) tkEmacsPtr);
      }
    }
    TkEmacsSendEvent(tkEmacsPtr, EXPOSE_EVENT);
  }

  if (Tk_IsMapped(tkEmacsPtr->tkwin)) {
    if (tkEmacsPtr->pid != 0 &&
	tkEmacsPtr->emacsWindowId != 0) {
      if (oldemacsWindowId != tkEmacsPtr->emacsWindowId) {
	TkEmacsSendEvent(tkEmacsPtr, MAP_EVENT);
	TkEmacsSendEvent(tkEmacsPtr, CONFIGURE_EVENT);
      }
      TkEmacsSendEvent(tkEmacsPtr, EXPOSE_EVENT);
    } else {
      if (oldemacsWindowId != 0) {
	XSync(Tk_Display(tkEmacsPtr->tkwin), True);
      }
    }
  }
  if (Tk_IsMapped(tkEmacsPtr->tkwin) &&
      !(tkEmacsPtr->flags & REDRAW_PENDING)) {
    tkEmacsPtr->flags |= REDRAW_PENDING|CLEAR_NEEDED;
    Tk_DoWhenIdle(TkEmacsDisplay, (ClientData) tkEmacsPtr);
  }
#endif
  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsDisplay --
 *
 *	This procedure is invoked to display a tkEmacs widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Commands are output to X to display the tkEmacs in its
 *	current mode.
 *
 *----------------------------------------------------------------------
 */

static void
TkEmacsDisplay(clientData)
     ClientData clientData;	/* Information about widget. */
{
  register TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;
  register Tk_Window tkwin = tkEmacsPtr->tkwin;

  tkEmacsPtr->flags &= ~REDRAW_PENDING;
  if (tkEmacsPtr->pid == 0 || tkEmacsPtr->emacsWindowId == 0 ||
      tkEmacsPtr->tkwin == NULL || !Tk_IsMapped(tkwin)) {
    return;
  }

  if (tkEmacsPtr->flags & CLEAR_NEEDED) {
    XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
    tkEmacsPtr->flags &= ~CLEAR_NEEDED;
  }
  TkEmacsSendEvent(tkEmacsPtr, EXPOSE_EVENT);
  if ((tkEmacsPtr->border != NULL)
      && (tkEmacsPtr->relief != TK_RELIEF_FLAT)) {
    Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
		       tkEmacsPtr->border, 0, 0, Tk_Width(tkwin),
		       Tk_Height(tkwin), tkEmacsPtr->borderWidth,
		       tkEmacsPtr->relief);
  }
}

/*
 *--------------------------------------------------------------
 *
 * TkEmacsEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher on
 *	structure changes to a tkEmacs.  For tkEmacss with 3D
 *	borders, this procedure is also invoked for exposures.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the window gets deleted, internal structures get
 *	cleaned up.  When it gets exposed, it is redisplayed.
 *
 *--------------------------------------------------------------
 */

static void
TkEmacsEventProc(clientData, eventPtr)
     ClientData clientData;	/* Information about window. */
     register XEvent *eventPtr;	/* Information about event. */
{
  register TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;

  if (tkEmacsPtr->pid == 0 || tkEmacsPtr->emacsWindowId == 0) {
    return;
  }
  if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
    if ((tkEmacsPtr->relief != TK_RELIEF_FLAT) &&
	(tkEmacsPtr->tkwin != NULL) &&
	!(tkEmacsPtr->flags & REDRAW_PENDING)) {
      tkEmacsPtr->flags |= REDRAW_PENDING;
      Tk_DoWhenIdle(TkEmacsDisplay, (ClientData) tkEmacsPtr);
    } else {
      XSendEvent(Tk_Display(tkEmacsPtr->tkwin), tkEmacsPtr->emacsWindowId,
		 True, ExposureMask, eventPtr);
    }
  } else if (eventPtr->type == DestroyNotify) {
    Tcl_DeleteCommand(tkEmacsPtr->interp, Tk_PathName(tkEmacsPtr->tkwin));
    tkEmacsPtr->tkwin = NULL;
    if (tkEmacsPtr->flags & REDRAW_PENDING) {
      Tk_CancelIdleCall(TkEmacsDisplay, (ClientData) tkEmacsPtr);
    }
    Tk_CancelIdleCall(TkEmacsMap, (ClientData) tkEmacsPtr);
    Tk_EventuallyFree((ClientData) tkEmacsPtr, DestroyTkEmacs);
  } else {
    if (eventPtr->type == MapNotify) {
      TkEmacsMap((ClientData) tkEmacsPtr);
    } else if (eventPtr->type == EnterNotify ||
	       eventPtr->type == ButtonPress) {
      Tcl_VarEval(tkEmacsPtr->interp, "focus", (char *) NULL);
      if (strcmp(tkEmacsPtr->widgetPathName,
		 tkEmacsPtr->interp->result) != 0) {
	if (tkEmacsPtr->focusPath != NULL) {
	  ckfree((char *) tkEmacsPtr->focusPath);
	}
	tkEmacsPtr->focusPath =
	  ckalloc(strlen(tkEmacsPtr->interp->result) + 1);
	strcpy(tkEmacsPtr->focusPath, tkEmacsPtr->interp->result);
	Tcl_VarEval(tkEmacsPtr->interp, "focus ",
		    tkEmacsPtr->widgetPathName, (char *) NULL);
      }
    } else if (eventPtr->type == LeaveNotify) {
      if (tkEmacsPtr->focusPath != NULL) {
        Tcl_VarEval(tkEmacsPtr->interp, "focus ",
		    tkEmacsPtr->focusPath, (char *) NULL);
	ckfree((char *) tkEmacsPtr->focusPath);
	tkEmacsPtr->focusPath =	NULL;
      }
    }
    XSendEvent(Tk_Display(tkEmacsPtr->tkwin), tkEmacsPtr->emacsWindowId,
	       True, 0xfff, eventPtr);
  }
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsMap --
 *
 *	This procedure is invoked as a when-idle handler to map a
 *	newly-created emacs widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The emacs widget given by the clientData argument is mapped.
 *
 *----------------------------------------------------------------------
 */

static void
TkEmacsMap(clientData)
    ClientData clientData;		/* Pointer to widget structure. */
{
  XWindowAttributes windowAttr;
  TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;

  if (!(tkEmacsPtr->flags & MAP_WINDOW)) {
    return;
  }
  /*
   * Wait for all other background events to be processed before
   * mapping window.  This ensures that the window's correct geometry
   * will have been determined before it is first mapped, so that the
   * window manager doesn't get a false idea of its desired geometry.
   */
  Tk_Preserve((ClientData) tkEmacsPtr);
  
  while (1) {
    if (Tk_DoOneEvent(TK_IDLE_EVENTS) == 0) {
      break;
    }
    /*
     * After each event, make sure that the window still exists
     * and quit if the window has been destroyed.
     */
    if (tkEmacsPtr->tkwin == NULL) {
      Tk_Release((ClientData) tkEmacsPtr);
      return;
    }
  }
  
  tkEmacsPtr->flags &= ~MAP_WINDOW;
  TkEmacsSendEvent(tkEmacsPtr, MAP_EVENT);
  if (Tk_IsMapped(tkEmacsPtr->tkwin) &&
      !(tkEmacsPtr->flags & REDRAW_PENDING)) {
    tkEmacsPtr->flags |= REDRAW_PENDING|CLEAR_NEEDED;
    Tk_DoWhenIdle(TkEmacsDisplay, (ClientData) tkEmacsPtr);
  }
  if (tkEmacsPtr->timerToken != NULL) {
    Tk_DeleteTimerHandler(tkEmacsPtr->timerToken);
    tkEmacsPtr->timerToken = NULL;
  }
  if (tkEmacsPtr->pollInterval > 0) {
    tkEmacsPtr->timerToken =
      Tk_CreateTimerHandler(tkEmacsPtr->pollInterval * TO_SECONDS,
			    TkEmacsTimerProc, (ClientData) tkEmacsPtr);
  }

  XGetWindowAttributes(Tk_Display(tkEmacsPtr->tkwin),
		       Tk_WindowId(tkEmacsPtr->tkwin), &windowAttr);
  Tk_GeometryRequest(tkEmacsPtr->tkwin, windowAttr.width,
		     windowAttr.height);

  Tk_DoWhenIdle(TkEmacsSetupEmacs, (ClientData) tkEmacsPtr);
  TkEmacsSendEvent(tkEmacsPtr, CONFIGURE_EVENT);
  Tk_MapWindow(tkEmacsPtr->tkwin);
  Tk_Release((ClientData) tkEmacsPtr);
}

/*
 *--------------------------------------------------------------
 *
 * TkEmacsTimerProc --
 *
 *	This procedure is invoked to pol the scrollbar 
 *      parameters.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *      The scroolbars are updated.
 *
 *--------------------------------------------------------------
 */

static void
TkEmacsTimerProc(clientData)
     ClientData clientData;	/* Information about window. */
{
  register TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;

  TkEmacsUpdateScrollbars((ClientData) tkEmacsPtr);
  if (tkEmacsPtr->pollInterval > 0) {
    tkEmacsPtr->timerToken =
      Tk_CreateTimerHandler(tkEmacsPtr->pollInterval * TO_SECONDS,
			    TkEmacsTimerProc, (ClientData) tkEmacsPtr);
  }
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsAcceptFileHandler --
 *
 *	This procedure is called when data is waiting at the socket
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	evaluates given parameter
 *
 *----------------------------------------------------------------------
 */

static int
TkEmacsAcceptFileHandler(clientData, interp, argc, argv)
     ClientData clientData;		/* The emacs widget. */
     Tcl_Interp *interp;		/* Current interpreter. */
     int argc;				/* Number of arguments. */
     char **argv;			/* Argument strings. */
{
  TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;

  if (argc != 3) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		     " condition fileId\"", (char *) NULL);
    return TCL_ERROR;
  }
    
  /* accept connections */
  if (tkEmacsPtr->fileHandle2 != NULL) {
    /* accept to prevent blocking */
    Tcl_VarEval(tkEmacsPtr->interp, "accept ", argv[2], (char *) NULL);
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "only one connection per emacs widget.",
		     (char *) NULL);
    return TCL_ERROR;
  }
  if (Tcl_VarEval(tkEmacsPtr->interp, "accept ", argv[2],
		  (char *) NULL) != TCL_OK) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "could not accept connection at:",
		     tkEmacsPtr->fileHandle1, (char *) NULL);
    return TCL_ERROR;
  }

  tkEmacsPtr->fileHandle2 =
    ckalloc(strlen(tkEmacsPtr->interp->result) + 1);
  strcpy(tkEmacsPtr->fileHandle2, tkEmacsPtr->interp->result);
  
  if (TkEmacsReadData(tkEmacsPtr, interp,
		      tkEmacsPtr->fileHandle2) != TCL_OK) {
    return TCL_ERROR;
  }
  
  /* establish filehandler for accepted socket */
  if (Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		  tkEmacsPtr->fileHandle2, " r",
		  " TkEmacsReadFileHandler",
		  Tk_PathName(tkEmacsPtr->tkwin),
		  (char *) NULL) != TCL_OK) {
    return TCL_ERROR;
  }

  /* update */
  if (Tcl_VarEval(tkEmacsPtr->interp, "update ",
		  (char *) NULL) != TCL_OK) {
    return TCL_ERROR;
  }
  
  /* remove first handle */
  if (Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		  tkEmacsPtr->fileHandle1, (char *) NULL) != TCL_OK) {
    return TCL_ERROR;
  }
  if (Tcl_VarEval(tkEmacsPtr->interp, "close ",
		  tkEmacsPtr->fileHandle1, (char *) NULL) != TCL_OK) {
    return TCL_ERROR;
  }
  ckfree((char *) tkEmacsPtr->fileHandle1);
  tkEmacsPtr->fileHandle1 = NULL;

  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsReadData --
 *
 *	Read data from socket, and eval
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	evaluates given parameter
 *
 *----------------------------------------------------------------------
 */

static int
TkEmacsReadData(tkEmacsPtr, interp, fileHandle)
     TkEmacs *tkEmacsPtr;		/* The emacs widget. */
     Tcl_Interp *interp;		/* Current interpreter. */
     char *fileHandle;                  /* The file handle. */
{
  int beginType = NO_TYPE;
  
  char *command = (char *) NULL;

  if (tkEmacsPtr->result != NULL) {
    ckfree((char *) tkEmacsPtr->result);
  }
  tkEmacsPtr->result = ckalloc(2);
  strcpy(tkEmacsPtr->result, "");
  /* read data */
  if (Tcl_VarEval(interp, "gets ", fileHandle, (char *) NULL) !=
      TCL_OK) {
    Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		fileHandle, (char *) NULL);
    Tcl_VarEval(tkEmacsPtr->interp, "close ", fileHandle,
		(char *) NULL);
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "could not read from: ",
		     fileHandle, (char *) NULL);
    return TCL_ERROR;
  }

  if (interp->result == NULL || interp->result == '\0' ||
      strlen(interp->result) == 0) {
    if (Tcl_VarEval(interp, "eof ", fileHandle, (char *) NULL) != TCL_OK) {
      Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		  fileHandle, (char *) NULL);
      Tcl_VarEval(tkEmacsPtr->interp, "close ", fileHandle,
		  (char *) NULL);
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "could not read from: ",
		       fileHandle, (char *) NULL);
      return TCL_ERROR;
    }
    if (strcmp(interp->result, "1") == 0) {
      if (Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		      fileHandle, (char *) NULL) != TCL_OK) {
	return TCL_ERROR;
      }
      if (Tcl_VarEval(tkEmacsPtr->interp, "close ", fileHandle,
		      (char *) NULL) != TCL_OK) {
	return TCL_ERROR;
      }
      return TCL_OK;
    }
    return TCL_OK;
  }

  if (!TK_CMD(interp->result) &&
      !TK_RET(interp->result) &&
      !TK_ERR(interp->result) &&
      !TK_RST(interp->result) &&
      !TK_END(interp->result)) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "malformed command:no prefix", (char *) NULL);
    return TCL_OK;
  }
  
  while (1) {
    if (tkEmacsPtr->debug) {
      fprintf(stderr, "recv:%s<\n", interp->result);
    }
    if (interp->result == NULL ||
	*(interp->result) == '\0' ||
	strlen(interp->result) == 0) {
      if (command != NULL) {
	ckfree((char *) command);
	command = (char *) NULL;
      }
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "empty line", (char *) NULL);
      return TCL_ERROR;
    }

    if (TK_CMD(interp->result) && command == NULL && beginType == NO_TYPE) {
      beginType = CMD_TYPE;
      command = ckalloc(strlen(interp->result) + 1);
      *command = '\0';
      if (strlen(interp->result) > PREFIX_OFFSET) {
	strcpy(command, interp->result + PREFIX_OFFSET);
      }
    } else {
      if (TK_RET(interp->result) && command == NULL && beginType == NO_TYPE) {
	beginType = RET_TYPE;
	command = ckalloc(strlen(interp->result) + 1);
	*command = '\0';
	if (strlen(interp->result) > PREFIX_OFFSET) {
	  strcpy(command, interp->result + PREFIX_OFFSET);
	}
      } else {
	if (TK_ERR(interp->result)) {
	  if (command == NULL && beginType == NO_TYPE) {
	    command = ckalloc(strlen(interp->result) + 1);
	    *command = '\0';
	    if (strlen(interp->result) > strlen(ERR_PREFIX)) {
	      strcpy(command, interp->result + strlen(ERR_PREFIX));
	    }
	    if (strlen(tkEmacsPtr->errorCallback) > 0) {
	      if (Tcl_VarEval(tkEmacsPtr->interp,
			      tkEmacsPtr->errorCallback,
			      " ", tkEmacsPtr->widgetPathName, " {",
			      command, "}", (char *) NULL) != TCL_OK) {
		fprintf(stderr, "%s\n", tkEmacsPtr->result);
	      }
	    }
	    if (command != NULL) {
	      ckfree((char *) command);
	    }
	    if (tkEmacsPtr->result != NULL) {
	      ckfree((char *) tkEmacsPtr->result);
	    }
	    tkEmacsPtr->result = ckalloc(2);
	    strcpy(tkEmacsPtr->result, "");
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, tkEmacsPtr->result, (char *) NULL);
	    tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
	    return TCL_OK;
	  } else {
	    if (strlen(interp->result) > strlen(ERR_PREFIX)) {
	      if (strlen(tkEmacsPtr->errorCallback) > 0) {
		if (Tcl_VarEval(tkEmacsPtr->interp,
				tkEmacsPtr->errorCallback, 
				" ", tkEmacsPtr->widgetPathName, " {",
				interp->result + strlen(ERR_PREFIX), "}",
				(char *) NULL) != TCL_OK) {
		  fprintf(stderr, "%s\n", tkEmacsPtr->result);
		}
	      }
	    }
	  }
	} else {
	  if (TK_END(interp->result) && beginType != NO_TYPE) {
	    if (command != NULL) {
	      if (beginType == CMD_TYPE) {
		if (Tcl_Eval(interp, command, 0, (char **) NULL) != TCL_OK) {
		  if (command != NULL) {
		    ckfree((char *) command);
		  }
		  return TCL_ERROR;
		}
	      } else {
		if (beginType == RET_TYPE) {
		  if (tkEmacsPtr->result != NULL) {
		    ckfree((char *) tkEmacsPtr->result);
		  }
		  tkEmacsPtr->result = ckalloc(strlen(command) + 1);
		  strcpy(tkEmacsPtr->result, command);
		  Tcl_ResetResult(interp);
		  Tcl_AppendResult(interp, command, (char *) NULL);
		  tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
		} else {
		  Tcl_ResetResult(interp);
		  Tcl_AppendResult(interp, "unknown protocol token",
				   (char *) NULL);
		  if (command != NULL) {
		    ckfree((char *) command);
		  }
		  tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
		  return TCL_ERROR;
		}
	      }
	      ckfree((char *) command);
	    }
	    return TCL_OK;
	  } else {
	    if (TK_RST(interp->result)) {
	      if (tkEmacsPtr->result != NULL) {
		ckfree((char *) tkEmacsPtr->result);
	      }
	      tkEmacsPtr->result = ckalloc(2);
	      strcpy(tkEmacsPtr->result, "");
	      Tcl_ResetResult(interp);
	      Tcl_AppendResult(interp, "protocol reset", (char *) NULL);
	      if (command != NULL) {
		ckfree((char *) command);
	      }
	      tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
	      return TCL_OK;
	    } else {
	      if (command != NULL &&
		  ((beginType == CMD_TYPE && TK_CMD(interp->result)) ||
		   (beginType == RET_TYPE && TK_RET(interp->result)))) {
		if (strlen(interp->result) > PREFIX_OFFSET) {
		  command =
		    realloc(command,
			    strlen(command) + strlen(interp->result) + 2);
		  strcat(command, "\n");
		  strcat(command, interp->result + PREFIX_OFFSET);
		} else {
		  command =
		    realloc(command, strlen(command) + 2);
		  strcat(command, "\n");
		}
	      } else {
		if (command != NULL) {
		  ckfree((char *) command);
		}
		Tcl_ResetResult(interp);
		Tcl_AppendResult(interp, "protocol error", (char *) NULL);
		return TCL_ERROR;
	      }
	    }
	  }
	}
      }
    }

    if (Tcl_VarEval(interp, "gets ", fileHandle, (char *) NULL) != TCL_OK) {
      if (command != NULL) {
	ckfree((char *) command);
      }
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "could not read from: ", fileHandle,
		       (char *) NULL);
      return TCL_ERROR;
    }
  }
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsReadFileHandler --
 *
 *	This procedure is called when data is waiting at the socket
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	evaluates given parameter
 *
 *----------------------------------------------------------------------
 */

static int
TkEmacsReadFileHandler(clientData, interp, argc, argv)
     ClientData clientData;		/* The emacs widget. */
     Tcl_Interp *interp;		/* Current interpreter. */
     int argc;				/* Number of arguments. */
     char **argv;			/* Argument strings. */
{
  TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;
  
  if (argc != 3) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		     " condition fileId\"", (char *) NULL);
    return TCL_ERROR;
  }

  if (TkEmacsReadData(tkEmacsPtr, interp, argv[2]) != TCL_OK) {
    return TCL_ERROR;
  }
  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsSendEvent --
 *
 *	This procedure send an event to emacs
 *
 * Side effects:
 *	None
 *
 * Results:
 *      None
 *
 *----------------------------------------------------------------------
 */

static void
TkEmacsSendEvent(tkEmacsPtr, type)
     TkEmacs *tkEmacsPtr;	/* The emacs widget. */
     int type;                  /* The event type to send. */
{
  XEvent xevent;	        /* Information about event. */
  XExposeEvent *xexpose;	/* Information about expose event. */
  XConfigureEvent *xconfig;	/* Information about config event. */
  XMapEvent *xmap;	        /* Information about map event. */

  switch(type) {
  case CONFIGURE_EVENT:
    xevent.type = ConfigureNotify;
    xconfig = (XConfigureEvent*) &xevent;
    xconfig->display = Tk_Display(tkEmacsPtr->tkwin);
    xconfig->event = Tk_WindowId(tkEmacsPtr->tkwin);
    xconfig->window = Tk_WindowId(tkEmacsPtr->tkwin);
    xconfig->x = 0;
    xconfig->y = 0;
    xconfig->width = Tk_Width(tkEmacsPtr->tkwin);
    xconfig->height = Tk_Height(tkEmacsPtr->tkwin);
    xconfig->border_width = tkEmacsPtr->borderWidth;
    xconfig->above = None;
    xconfig->override_redirect = True;
    break;
  case EXPOSE_EVENT:
    xevent.type = Expose;
    xexpose = (XExposeEvent*) &xevent;
    xexpose->count = 0;
    xexpose->display = Tk_Display(tkEmacsPtr->tkwin);
    xexpose->window = Tk_WindowId(tkEmacsPtr->tkwin);
    xexpose->x = 0;
    xexpose->y = 0;
    xexpose->width = Tk_Width(tkEmacsPtr->tkwin);
    xexpose->height = Tk_Height(tkEmacsPtr->tkwin);
    break;
  case MAP_EVENT:
    xevent.type = MapNotify;
    xmap = (XMapEvent*) &xevent;
    xmap->display = Tk_Display(tkEmacsPtr->tkwin);
    xmap->event = Tk_WindowId(tkEmacsPtr->tkwin);
    xmap->window = Tk_WindowId(tkEmacsPtr->tkwin);
    xmap->override_redirect = True;
    break;
  }
  
  XSendEvent(Tk_Display(tkEmacsPtr->tkwin), tkEmacsPtr->emacsWindowId,
	     True, 0xfff, &xevent);
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsSendString --
 *
 *	This procedure loops until there is a result
 *
 * Side effects:
 *	None
 *
 * Results:
 *      The error code
 *
 *----------------------------------------------------------------------
 */

static int
TkEmacsSendString(interp, tkEmacsPtr, prefix, buffer)
     Tcl_Interp *interp;	/* Current interpreter. */
     TkEmacs *tkEmacsPtr;	/* The emacs widget. */
     char *prefix;              /* The prefix to send. */
     char *buffer;              /* The string to send. */
{
  char *tmpBuffer = (char *) NULL;
  char *tmpPos = buffer;
  char *oldTmpPos = buffer;

  if (tkEmacsPtr->fileHandle2 == NULL ||
      strlen(tkEmacsPtr->fileHandle2) == 0) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "there is no handle to write to: >",
		     buffer, "<", (char *) NULL);
    tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
    return TCL_ERROR;
  }

  if (strcmp(prefix, RST_PREFIX) == 0) {
    tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
    if (Tcl_VarEval(interp, "puts ", tkEmacsPtr->fileHandle2,
		    " {", RST_PREFIX, "}",
		    (char *) NULL) != TCL_OK) { 
      tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
      return TCL_ERROR;
    }
    return TCL_OK;
  }
  if (buffer != NULL && strlen(buffer) > 0) {
    tmpBuffer = ckalloc(2);
    *tmpBuffer = '\0';
    while ((tmpPos = strchr(oldTmpPos, '\n')) != (char *) NULL) {
      if (oldTmpPos != NULL && *oldTmpPos != '\0') {
	tmpBuffer = realloc(tmpBuffer,
			    strlen(tmpBuffer) + (tmpPos - oldTmpPos) + 7);
	strcat(tmpBuffer, CMD_PREFIX);
	strncat(tmpBuffer, oldTmpPos, tmpPos - oldTmpPos);
	strcat(tmpBuffer, "\n");
	if (tkEmacsPtr->debug) {
	  fprintf(stderr, "send:%s%.*s<\n", CMD_PREFIX, tmpPos-oldTmpPos,
		  oldTmpPos);
	}
      } else {
	tmpBuffer = realloc(tmpBuffer, strlen(tmpBuffer) + 7);
	strcat(tmpBuffer, CMD_PREFIX);
	strcat(tmpBuffer, "\n");
	if (tkEmacsPtr->debug) {
	  fprintf(stderr, "send:%s<\n", CMD_PREFIX);
	}
      }
      oldTmpPos = tmpPos;
      if (oldTmpPos != NULL && *oldTmpPos == '\n') {
	oldTmpPos++;
      }
      if (oldTmpPos == NULL || *oldTmpPos == '\0') {
	break;
      }
    }
    if (oldTmpPos != NULL && *oldTmpPos != '\0') {
      tmpBuffer = realloc(tmpBuffer,
			  strlen(tmpBuffer) + strlen(oldTmpPos) + 30);
      strcat(tmpBuffer, CMD_PREFIX);
      strcat(tmpBuffer, oldTmpPos);
      strcat(tmpBuffer, "\n");
      if (tkEmacsPtr->debug) {
	fprintf(stderr, "send:%s%s<\n", CMD_PREFIX, oldTmpPos);
      }
    }
    strcat(tmpBuffer, END_PREFIX);
    if (tkEmacsPtr->debug) {
      fprintf(stderr, "send:%s<\n", END_PREFIX);
    }

    if (Tcl_VarEval(interp, "puts ", tkEmacsPtr->fileHandle2,
		    " {", tmpBuffer, "}",
		    (char *) NULL) != TCL_OK) { 
      ckfree((char *) tmpBuffer);
      tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
      return TCL_ERROR;
    }
    ckfree((char *) tmpBuffer);
  } else {
    tkEmacsPtr->flags &= ~WAIT_FOR_RESULT;
  }
  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsSetupEmacs --
 *
 *	This procedure is to initially setup an external tkEmacs
 *
 * Side effects:
 *	An tkEmacs is confgured
 *
 *----------------------------------------------------------------------
 */

static void
TkEmacsSetupEmacs(clientData)
     ClientData clientData;		/* The emacs widget. */
{
  TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;
  char *background = (char *) NULL;
  char *tmpBuffer = (char *) NULL;

  if (tkEmacsPtr->pid != 0 &&
      tkEmacsPtr->emacsWindowId != 0) {
    if (tkEmacsPtr->tkEmacsStart != NULL) {
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
			tkEmacsPtr->tkEmacsStart);
      TkEmacsWaitForResult(tkEmacsPtr);
    }
  }

  tmpBuffer = ckalloc(60);
  if (tkEmacsPtr->tkEmacsAdvise) {
    strcpy(tmpBuffer, "(tk-advise-change-view-functions)");
  } else {
    strcpy(tmpBuffer, "(tk-unadvise-change-view-functions)");
  }
  tkEmacsPtr->flags |= WAIT_FOR_RESULT;
  TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		    tmpBuffer); 
  TkEmacsWaitForResult(tkEmacsPtr);

  sprintf(tmpBuffer, "(x-set-internal-border-width %ld)",
	  tkEmacsPtr->borderWidth);
  tkEmacsPtr->flags |= WAIT_FOR_RESULT;
  TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		    tmpBuffer); 
  TkEmacsWaitForResult(tkEmacsPtr);
  TkEmacsSendEvent(tkEmacsPtr, CONFIGURE_EVENT);
  Tk_DoWhenIdle(TkEmacsUpdateScrollbars, (ClientData) tkEmacsPtr);
  ckfree((char *) tmpBuffer);

  if (Tk_ConfigureInfo(tkEmacsPtr->interp, tkEmacsPtr->tkwin,
		       configSpecs, (char *) tkEmacsPtr,
		       "-background", 0) == TCL_OK) {
    if ((background = strrchr(tkEmacsPtr->interp->result, ' '))
	!= (char *) NULL) {
      background++;
      tmpBuffer = ckalloc(40 + strlen(background));
      sprintf(tmpBuffer, "(x-set-background-color \"%s\")",
	      background);
      tkEmacsPtr->flags |= WAIT_FOR_RESULT;
      TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
			tmpBuffer);
      TkEmacsWaitForResult(tkEmacsPtr);
      ckfree((char *) tmpBuffer);
    }
  }

  if (tkEmacsPtr->tkEmacsCursorColor != NULL &&
      strlen(tkEmacsPtr->tkEmacsCursorColor) > 0) {
    tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsCursorColor));
    sprintf(tmpBuffer, "(x-set-cursor-color \"%s\")",
	    tkEmacsPtr->tkEmacsCursorColor);
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		      tmpBuffer);
    TkEmacsWaitForResult(tkEmacsPtr);
    ckfree((char *) tmpBuffer);
  }
  
  if (tkEmacsPtr->tkEmacsFile != NULL &&
      strlen(tkEmacsPtr->tkEmacsFile) > 0) {
    tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsFile));
    sprintf(tmpBuffer, "(find-file \"%s\")",
	    tkEmacsPtr->tkEmacsFile);
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		      tmpBuffer);
    TkEmacsWaitForResult(tkEmacsPtr);
    ckfree((char *) tmpBuffer);
  }
  
  if (tkEmacsPtr->tkEmacsFont != NULL &&
      strlen(tkEmacsPtr->tkEmacsFont) > 0) {
    tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsFont));
    sprintf(tmpBuffer, "(x-set-font \"%s\")",
	    tkEmacsPtr->tkEmacsFont);
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		      tmpBuffer);
    TkEmacsWaitForResult(tkEmacsPtr);
    TkEmacsSendEvent(tkEmacsPtr, CONFIGURE_EVENT);
    Tk_DoWhenIdle(TkEmacsUpdateScrollbars, (ClientData) tkEmacsPtr);
    ckfree((char *) tmpBuffer);
  }
  
  if (tkEmacsPtr->tkEmacsForeground != NULL &&
      strlen(tkEmacsPtr->tkEmacsForeground) > 0) {
    tmpBuffer = ckalloc(40 + strlen(tkEmacsPtr->tkEmacsForeground));
    sprintf(tmpBuffer, "(x-set-foreground-color \"%s\")",
	    tkEmacsPtr->tkEmacsForeground);
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		      tmpBuffer);
    TkEmacsWaitForResult(tkEmacsPtr);
    ckfree((char *) tmpBuffer);
  }
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsStartEmacs --
 *
 *	This procedure is to start an external tkEmacs
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	An tkEmacs is started
 *
 *----------------------------------------------------------------------
 */

static int
TkEmacsStartEmacs(interp, tkEmacsPtr)
     Tcl_Interp *interp;	/* Current interpreter. */
     TkEmacs *tkEmacsPtr;	/* Pointer to tkEmacs structure. */
{
  char tmpVarBuffer[100];
  char *background = (char *) NULL;
  char *execName = (char *) NULL;
  char *port = (char *) NULL;
  int argc = 0;
  int counter = 0;
  char *argv[40];
  char *tmpBuffer = (char *) NULL;

  if (tkEmacsPtr->tkEmacsCommand == NULL ||
      strlen(tkEmacsPtr->tkEmacsCommand) == 0) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "no -command resource specified",
		     (char *) NULL);
    return TCL_ERROR;
  }

  if (tkEmacsPtr->tkEmacsLispFile == NULL ||
      strlen(tkEmacsPtr->tkEmacsLispFile) == 0) {
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "no -lispfile resource specified",
		     (char *) NULL);
    return TCL_ERROR;
  }

  if (tkEmacsPtr->fileHandle1 == NULL &&
      tkEmacsPtr->fileHandle2 == NULL) {
    /* Check if interpreter already contains the file handler */
    tmpBuffer = ckalloc(50 + strlen(Tk_PathName(tkEmacsPtr->tkwin)));
    sprintf(tmpBuffer, "info commands TkEmacsAcceptFileHandler%s",
	    Tk_PathName(tkEmacsPtr->tkwin));
    if (Tcl_Eval(interp, tmpBuffer, 0, (char **) NULL) != TCL_OK) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "the interpreter makes problems :-(",
		       (char *) NULL);
      return TCL_ERROR;
    }
    if (strlen(interp->result) == 0 ||
	strcmp(interp->result, "TkEmacsAcceptFileHandler") != 0) {
      sprintf(tmpBuffer, "TkEmacsAcceptFileHandler%s",
	      Tk_PathName(tkEmacsPtr->tkwin));
      Tcl_CreateCommand(interp, tmpBuffer, TkEmacsAcceptFileHandler,
			(ClientData) tkEmacsPtr, (void (*)()) NULL);
    }
    sprintf(tmpBuffer, "info commands TkEmacsReadFileHandler%s",
	    Tk_PathName(tkEmacsPtr->tkwin));
    if (Tcl_Eval(interp, tmpBuffer, 0, (char **) NULL) != TCL_OK) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "the interpreter makes problems :-(",
		       (char *) NULL);
      return TCL_ERROR;
    }
    if (strlen(interp->result) == 0 ||
	strcmp(interp->result, "TkEmacsReadFileHandler") != 0) {
      sprintf(tmpBuffer, "TkEmacsReadFileHandler%s",
	      Tk_PathName(tkEmacsPtr->tkwin));
      Tcl_CreateCommand(interp, tmpBuffer, TkEmacsReadFileHandler,
			(ClientData) tkEmacsPtr, (void (*)()) NULL);
    }

    /* make listening socket */
    if (Tcl_VarEval(tkEmacsPtr->interp, "connect -server \"\" 0",
		    (char *) NULL) != TCL_OK) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "could not establish listening socket",
		       (char *) NULL);
      return TCL_ERROR;
    }
    tkEmacsPtr->fileHandle1 =
      ckalloc(strlen(tkEmacsPtr->interp->result) + 1);
    strcpy(tkEmacsPtr->fileHandle1, tkEmacsPtr->interp->result);

    /* establish filehandler for socket */
    if (Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		    tkEmacsPtr->fileHandle1, " r",
		    " TkEmacsAcceptFileHandler",
		    Tk_PathName(tkEmacsPtr->tkwin),
		    (char *) NULL) != TCL_OK) {
      Tcl_ResetResult(interp);
      Tcl_AppendResult(interp, "could not establish filehandler for: ",
		       tkEmacsPtr->fileHandle1, (char *) NULL);
      return TCL_ERROR;
    }
    ckfree((char *) tmpBuffer);
  }

  /* which port did we get */
  sprintf(tmpVarBuffer, "connect_info(%s)", tkEmacsPtr->fileHandle1);
  port = Tcl_GetVar(tkEmacsPtr->interp, tmpVarBuffer, TCL_GLOBAL_ONLY);
  if (port == NULL) {
    port = tkEmacsPtr->interp->result;
  }
#if defined(DEBUG_PROT)
  fprintf(stderr, "tkemacs port: %s\n", port);
#endif
  
#if !defined(DEBUG_PROT)
  if (Tk_ConfigureInfo(tkEmacsPtr->interp, tkEmacsPtr->tkwin, configSpecs,
		       (char *) tkEmacsPtr, "-background", 0) == TCL_OK) {
    if ((background = strrchr(tkEmacsPtr->interp->result, ' '))
	!= (char *) NULL) {
      background++;
    }
  }

  execName = Tcl_TildeSubst(interp, tkEmacsPtr->tkEmacsCommand);
  argv[argc] = ckalloc(strlen(execName) + 1);
  strcpy(argv[argc++], execName);
  argv[argc] = ckalloc(9);
  strcpy(argv[argc++], "-display");
  argv[argc] = ckalloc(strlen(Tk_DisplayName(tkEmacsPtr->tkwin)) + 1);
  strcpy(argv[argc++], Tk_DisplayName(tkEmacsPtr->tkwin));
  if (tkEmacsPtr->tkEmacsHeight > 0 && tkEmacsPtr->tkEmacsWidth > 0) {
    argv[argc] = ckalloc(10);
    strcpy(argv[argc++], "-geometry");
    argv[argc] = ckalloc(40);
    sprintf(argv[argc++], "%dx%d", tkEmacsPtr->tkEmacsWidth,
	    tkEmacsPtr->tkEmacsHeight);
  }
  argv[argc] = ckalloc(3);
  strcpy(argv[argc++], "-l");
  argv[argc] = ckalloc(strlen(tkEmacsPtr->tkEmacsLispFile) + 1);
  strcpy(argv[argc++], tkEmacsPtr->tkEmacsLispFile);
  argv[argc] = ckalloc(3);
  strcpy(argv[argc++], "-b");
  argv[argc] = ckalloc(2);
  strcpy(argv[argc++], "0");
  argv[argc] = ckalloc(4);
  strcpy(argv[argc++], "-ib");
  argv[argc] = ckalloc(40);
  sprintf(argv[argc++], "%d", tkEmacsPtr->borderWidth);
  argv[argc] = ckalloc(10);
  strcpy(argv[argc++], "-tkwidget");
  argv[argc] = ckalloc(40);
  sprintf(argv[argc++], "%ld", Tk_WindowId(tkEmacsPtr->tkwin));
  argv[argc] = ckalloc(strlen(Tk_PathName(tkEmacsPtr->tkwin)) + 1);
  strcpy(argv[argc++], Tk_PathName(tkEmacsPtr->tkwin));
  argv[argc] = ckalloc(10);
  strcpy(argv[argc++], "localhost");
  argv[argc] = ckalloc(strlen(port) + 1);
  strcpy(argv[argc++], port);
  if (background != NULL) {
    argv[argc] = ckalloc(4);
    strcpy(argv[argc++], "-bg");
    argv[argc] = ckalloc(strlen(background) + 1);
    strcpy(argv[argc++], background);
  }
  if (tkEmacsPtr->tkEmacsCursorColor != NULL) {
    argv[argc] = ckalloc(4);
    strcpy(argv[argc++], "-cr");
    argv[argc] = ckalloc(strlen(tkEmacsPtr->tkEmacsCursorColor) + 1);
    strcpy(argv[argc++], tkEmacsPtr->tkEmacsCursorColor);
  }
  if (tkEmacsPtr->tkEmacsFont != NULL) {
    argv[argc] = ckalloc(6);
    strcpy(argv[argc++], "-font");
    argv[argc] = ckalloc(strlen(tkEmacsPtr->tkEmacsFont) + 1);
    strcpy(argv[argc++], tkEmacsPtr->tkEmacsFont);
  }
  if (tkEmacsPtr->tkEmacsForeground != NULL) {
    argv[argc] = ckalloc(4);
    strcpy(argv[argc++], "-fg");
    argv[argc] = ckalloc(strlen(tkEmacsPtr->tkEmacsForeground) + 1);
    strcpy(argv[argc++], tkEmacsPtr->tkEmacsForeground);
  }
  argv[argc] = NULL;

  if (tkEmacsPtr->debug) {
    fprintf(stderr, "emacs cmd: ");
    while (counter < argc) {
      fprintf(stderr, " %s", argv[counter++]);
    }
    fprintf(stderr, "<\n");
  }

  /* start emacs */
  tkEmacsPtr->pid = Tcl_Fork();
  if (tkEmacsPtr->pid == -1) {  /* error */
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "could not fork", (char *) NULL);
    return TCL_ERROR;
  } else {
    if (tkEmacsPtr->pid == 0) { /* child */
      if (execvp(execName, argv) == -1) {
	fprintf(stderr, "could not start:%s\n", execName);
	exit(1);
      }
    }
  }

  argc--;
  while (argc >= 0) {
    ckfree((char *) argv[argc--]);
  }
#endif
  tkEmacsPtr->flags |= MAP_WINDOW;
  
  return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsStopEmacs --
 *
 *	This procedure is to stop an external tkEmacs
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	An tkEmacs is stopped
 *
 *----------------------------------------------------------------------
 */

static int
TkEmacsStopEmacs(tkEmacsPtr)
     TkEmacs *tkEmacsPtr;	/* Pointer to tkEmacs structure. */
{
  int pid[2];
  int status = 0;
  char *tmpBuffer = (char *) NULL;

  if (tkEmacsPtr->timerToken != NULL) {
    Tk_DeleteTimerHandler(tkEmacsPtr->timerToken);
    tkEmacsPtr->timerToken = NULL;
  }

  if (tkEmacsPtr->emacsWindowId != 0) {
    if (tkEmacsPtr->tkEmacsEnd != NULL) {
      TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
			tkEmacsPtr->tkEmacsEnd);
    }
    TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		      "(tk-kill-emacs)");
  }

  tkEmacsPtr->emacsWindowId = 0L;
  if (tkEmacsPtr->pid != 0) {
    pid[0] = tkEmacsPtr->pid;
    Tcl_WaitPids(1, &pid[0], &status);
  }
  tkEmacsPtr->pid = 0;
  
  if (tkEmacsPtr->fileHandle1 != NULL) {
    /* remove second handle */
    Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		tkEmacsPtr->fileHandle1, (char *) NULL);
    ckfree((char *) tkEmacsPtr->fileHandle1);
    tkEmacsPtr->fileHandle1 = NULL;
  }
  tmpBuffer = ckalloc(50 + strlen(tkEmacsPtr->widgetPathName));
  sprintf(tmpBuffer, "TkEmacsAcceptFileHandler%s", tkEmacsPtr->widgetPathName);
  Tcl_DeleteCommand(tkEmacsPtr->interp, tmpBuffer);
  if (tkEmacsPtr->fileHandle2 != NULL) {
    /* remove second handle */
    Tcl_VarEval(tkEmacsPtr->interp, "filehandler ",
		tkEmacsPtr->fileHandle2, (char *) NULL);
    ckfree((char *) tkEmacsPtr->fileHandle2);
    tkEmacsPtr->fileHandle2 = NULL;
  }
  sprintf(tmpBuffer, "TkEmacsReadFileHandler%s", tkEmacsPtr->widgetPathName);
  Tcl_DeleteCommand(tkEmacsPtr->interp, tmpBuffer);
  ckfree((char *) tmpBuffer);
  return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * TkEmacsUpdateScrollbars --
 *
 *	This procedure is invoked to update the scrollbar 
 *      parameters.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *      The scroolbars are updated.
 *
 *--------------------------------------------------------------
 */

static void
TkEmacsUpdateScrollbars(clientData)
     ClientData clientData;	/* Information about window. */
{
  register TkEmacs *tkEmacsPtr = (TkEmacs *) clientData;
  char *firstPos = (char *) NULL;
  char *scndPos = (char *) NULL;
  char *thirdPos = (char *) NULL;
  
  if (tkEmacsPtr->pid == 0 || tkEmacsPtr->emacsWindowId == 0) {
    return;
  }

  if (tkEmacsPtr->xScrollCmd != NULL &&
      strlen(tkEmacsPtr->xScrollCmd) > 0) {
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		      "(tk-get-x-view-info)");
    TkEmacsWaitForResult(tkEmacsPtr);
    if ((firstPos = strchr(tkEmacsPtr->result, ' ')) != (char *) NULL) {
      if ((firstPos + 1) && (firstPos + 1) != (char *) NULL &&
	  (scndPos = strchr(firstPos + 1, ' ')) != (char *) NULL) {
	if ((scndPos + 1) && (scndPos + 1) != (char *) NULL &&
	    (thirdPos = strchr(scndPos + 1, ' ')) != (char *) NULL) {
	  if ((thirdPos + 1) && (thirdPos + 1) != (char *) NULL &&
	      strchr(thirdPos + 1, ' ') == (char *) NULL) {
	    if (Tcl_VarEval(tkEmacsPtr->interp, tkEmacsPtr->xScrollCmd,
			    " ", tkEmacsPtr->result,
			    (char *) NULL) != TCL_OK) {
	      fprintf(stderr, "%s\n", tkEmacsPtr->interp->result);
	      return;
	    }
	  }
	}
      }
    }
  }
  if (tkEmacsPtr->yScrollCmd != NULL &&
      strlen(tkEmacsPtr->yScrollCmd) > 0) {
    tkEmacsPtr->flags |= WAIT_FOR_RESULT;
    TkEmacsSendString(tkEmacsPtr->interp, tkEmacsPtr, CMD_PREFIX,
		      "(tk-get-y-view-info)");
    TkEmacsWaitForResult(tkEmacsPtr);
    if ((firstPos = strchr(tkEmacsPtr->result, ' ')) != (char *) NULL) {
      if ((firstPos + 1) && (firstPos + 1) != (char *) NULL &&
	  (scndPos = strchr(firstPos + 1, ' ')) != (char *) NULL) {
	if ((scndPos + 1) && (scndPos + 1) != (char *) NULL &&
	    (thirdPos = strchr(scndPos + 1, ' ')) != (char *) NULL) {
	  if ((thirdPos + 1) && (thirdPos + 1) != (char *) NULL &&
	      strchr(thirdPos + 1, ' ') == (char *) NULL) {
	    if (Tcl_VarEval(tkEmacsPtr->interp, tkEmacsPtr->yScrollCmd,
			    " ", tkEmacsPtr->result,
			    (char *) NULL) != TCL_OK) {
	      fprintf(stderr, "%s\n", tkEmacsPtr->interp->result);
	      return;
	    }
	  }
	}
      }
    }
  }
}

/*
 *----------------------------------------------------------------------
 *
 * TkEmacsWaitForResult --
 *
 *	This procedure loops until there is a result
 *
 * Side effects:
 *	None
 *
 * Results:
 *      None
 *
 *----------------------------------------------------------------------
 */

static void
TkEmacsWaitForResult(tkEmacsPtr)
     TkEmacs *tkEmacsPtr;		/* The emacs widget. */
{
  struct timeval startTime;
  struct timeval curTime;

  if (tkEmacsPtr->timeout > 0) {
    (void) gettimeofday(&startTime, (struct timezone *) NULL);
    startTime.tv_sec += tkEmacsPtr->timeout;
  }
  
  while (tkEmacsPtr->flags & WAIT_FOR_RESULT) {
    if (tkEmacsPtr->timeout > 0) {
      (void) gettimeofday(&curTime, (struct timezone *) NULL);
      if ((startTime.tv_sec < curTime.tv_sec) ||
	  ((startTime.tv_sec == curTime.tv_sec) &&
	   (startTime.tv_usec < curTime.tv_usec))) {
	if (strlen(tkEmacsPtr->errorCallback) > 0) {
	  if (Tcl_VarEval(tkEmacsPtr->interp,
			  tkEmacsPtr->errorCallback,
			  " ", tkEmacsPtr->widgetPathName,
			  " connection timeout",
			  (char *) NULL) != TCL_OK) {
	    fprintf(stderr, "%s\n", tkEmacsPtr->result);
	  }
	}
	if (tkEmacsPtr->result != NULL) {
	  ckfree((char *) tkEmacsPtr->result);
	}
	tkEmacsPtr->result = ckalloc(2);
	strcpy(tkEmacsPtr->result, "");
      }
    }
    Tk_DoOneEvent(0);
  }
}

/*
 *----------------------------------------------------------------------
 *
 * TK_CMD --
 *
 *	This procedure checks if the string contains a protocol begin.
 *
 * Results:
 *	Boolean indicating if protocol begin was found.
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

static int
TK_CMD(string)
     char *string;     /* The string to check */
{
  char *position = string;

  if (*position == 'C' && *(position + 1) == 'M' &&
      *(position + 2) == 'D' && *(position + 3) == ':' &&
      *(position + 4) == ' ') {
    return 1;
  }
  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TK_RET --
 *
 *	This procedure checks if the string contains a protocol return.
 *
 * Results:
 *	Boolean indicating if protocol end was found.
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

static int
TK_RET(string)
     char *string;     /* The string to check */
{
  char *position = string;

  if (*position == 'R' && *(position + 1) == 'E' &&
      *(position + 2) == 'T' && *(position + 3) == ':' &&
      *(position + 4) == ' ') {
    return 1;
  }
  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TK_ERR --
 *
 *	This procedure checks if the string contains a protocol error.
 *
 * Results:
 *	Boolean indicating if protocol end was found.
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

static int
TK_ERR(string)
     char *string;     /* The string to check */
{
  char *position = string;

  if (*position == 'E' && *(position + 1) == 'R' &&
      *(position + 2) == 'R' && *(position + 3) == ':' &&
      *(position + 4) == ' ') {
    return 1;
  }
  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TK_RST --
 *
 *	This procedure checks if the string contains a protocol reset.
 *
 * Results:
 *	Boolean indicating if protocol reset was found.
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

static int
TK_RST(string)
     char *string;     /* The string to check */
{
  char *position = string;

  if (*position == 'R' && *(position + 1) == 'S' &&
      *(position + 2) == 'T' && *(position + 3) == ':') {
    return 1;
  }
  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TK_END --
 *
 *	This procedure checks if the string contains a protocol end.
 *
 * Results:
 *	Boolean indicating if protocol end was found.
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

static int
TK_END(string)
     char *string;     /* The string to check */
{
  char *position = string;

  if (*position == 'E' && *(position + 1) == 'N' &&
      *(position + 2) == 'D' && *(position + 3) == ':') {
    return 1;
  }
  return 0;
}

