diff src/energize.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 9ee227acff29
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/energize.c	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,5442 @@
+/****************************************************************************
+ ***
+ ***	Copyright (c) 1990 by Sun/Lucid,	All Rights Reserved.
+ ***	Copyright (c) 1991-1993 by Lucid, Inc.  All Rights Reserved.
+ ***
+ *****************************************************************************/
+
+/* Synched up with: Not in FSF. */
+
+#include <config.h>
+
+#ifdef ENERGIZE		/* whole file */
+
+#include "lisp.h"
+
+/* Display Context for the icons */
+#include "console-x.h"
+#include <Xm/DialogS.h>
+#include "lwlib.h"
+#include "objects-x.h"
+
+#include "events.h"
+#include "opaque.h"
+#include "buffer.h"
+#include "extents.h"
+#include "process.h"
+#include "insdel.h"
+#include "window.h"
+#include "faces.h"
+
+/* Energize editor requests and I/O operations */
+#include "energize.h"
+
+#include "systime.h"
+#include "sysfile.h"
+#include "syssignal.h"
+
+#ifndef CBFileYourself
+/* This means that emacs is being compiled against the connection library
+   and headers that go with an Energize protocol less than 0.10.  We need
+   to do some slightly different things in this file because of that.
+
+   Note that if Emacs is compiled against the 3.0 version of the connection
+   library and associated headers, it can still talk to 2.1- or 2.5-level
+   servers.  But the opposite is not true.
+ */
+# define ENERGIZE_V2_HEADERS
+#endif
+
+/* The Connection library
+ */
+extern void CWriteQueryChoicesRequest ();
+extern void CWriteExecuteChoicesRequest ();
+extern void CWriteSheetRequest ();
+extern void CWriteSetControlRequest ();
+extern void CWriteChoice ();
+extern void CWriteProtocol ();
+extern int  CGetPortNumber ();
+
+
+/************** Typedefs and Structs ***********************/
+
+/* structure argument used by the next mapping function */
+typedef struct
+{
+  BufferInfo *binfo;
+  int n_extents;
+} binfo_and_n_extents;
+
+typedef struct
+{
+  BufferInfo*	binfo;
+  int		state;
+  int		tell_energize;
+} binfo_and_state;
+
+struct reply_wait
+{
+  int		serial;
+  EId		objectId;
+  EId		genericId;
+  EId		itemId;
+  char		answered_p;
+  char		status;
+  char*		message;
+  Lisp_Object	menu_result;
+  Lisp_Object	only_name;
+  struct reply_wait*	next;
+};
+
+static struct reply_wait *global_reply_wait;
+
+Lisp_Object Venergize_kernel_busy;
+Lisp_Object Qenergize_kernel_busy;
+Lisp_Object Venergize_attributes_mapping;
+int energize_extent_gc_threshold;
+Lisp_Object Venergize_kernel_busy_hook;
+Lisp_Object Qenergize_kernel_busy_hook;
+Lisp_Object Venergize_menu_update_hook;
+Lisp_Object Qenergize_menu_update_hook;
+Lisp_Object Qenergize;
+Lisp_Object Qenergize_auto_revert_buffer;
+
+
+static void set_energize_extent_data (EXTENT extent, void *data);
+
+static char *kernel_buffer_type_to_elisp_type (char *kernel_type);
+
+static CONST void *get_object (EId id, BufferInfo *binfo);
+static void put_object (EId id, BufferInfo *binfo, void *object);
+static void remove_object (EId id, BufferInfo *binfo);
+static void free_GDataclass (GDataClass *cl, BufferInfo *binfo);
+
+static void free_GenericData (GenericData *gen, BufferInfo *binfo);
+
+static void free_Energize_Extent_Data (Energize_Extent_Data *, BufferInfo *,
+				       enum Energize_Object_Free_Type);
+
+static BufferInfo *get_buffer_info_for_emacs_buffer (Lisp_Object emacs_buf,
+						     Editor *editor);
+static void put_buffer_info (EId id, Lisp_Object emacs_buf,
+			     BufferInfo *binfo, Editor *editor);
+
+static void handle_sheet_control_change (Widget, EId sheet_id, void* arg);
+static Connection *make_energize_connection (Editor *editor,
+					     int fdin, int fdout);
+static void close_energize_connection (void);
+Lisp_Object Fclose_connection_to_energize (void);
+static void mark_all_extents_as_unmodified (BufferInfo *binfo);
+Lisp_Object Fenergize_barf_if_buffer_locked (void);
+Lisp_Object Fconnected_to_energize_p (void);
+static int get_energize_connection_and_buffer_id (Lisp_Object buffer,
+						  void **conn_ptr,
+						  long *buffer_id_ptr);
+
+void restore_energize_extent_state (EXTENT);
+
+
+/**************************** Variables *****************************/
+
+/* debugging variable */
+int ignore_kernel;
+
+Lisp_Object Venergize_kernel_modification_hook;
+Lisp_Object Venergize_create_buffer_hook;
+Lisp_Object Qenergize_create_buffer_hook;
+
+Lisp_Object Qenergize_buffer_modified_hook;
+Lisp_Object Qbuffer_locked_by_energize;
+Lisp_Object Qenergize_user_input_buffer_mark;
+Lisp_Object Qenergize_make_many_buffers_visible;
+
+int inside_parse_buffer;
+
+/* List of all buffers currently managed by Energize.  This is
+Staticpro'ed so that they don't get GC'ed from under us. */
+static Lisp_Object Venergize_buffers_list;
+
+static Editor *energize_connection;
+static protocol_edit_options *peo;
+
+static int request_serial_number;
+
+extern int current_debuggerpanel_exposed_p;
+extern int desired_debuggerpanel_exposed_p;
+extern int debuggerpanel_sheet;
+
+/**************************** Macros *****************************/
+
+#define xnew(type) ((type*)xmalloc (sizeof (type)))
+
+#define BUFFER_NOTIFY_BACKGROUND_BIT_SET_P(buffer) 1
+
+#define get_extent_data(id,binfo) (Energize_Extent_Data*)get_object(id, binfo)
+#define get_class(id,binfo) (GDataClass*)get_object(id, binfo)
+#define get_generic(id,binfo) (GenericData*)get_object(id, binfo)
+
+#define put_extent_data(id,binfo,obj) put_object(id, binfo, obj)
+#define put_class(id,binfo,obj) put_object(id, binfo, obj)
+#define put_generic(id,binfo,obj) put_object(id, binfo, obj)
+
+#define remove_extent_data(id,binfo) remove_object(id, binfo)
+#define remove_class(id,binfo) remove_object(id, binfo)
+#define remove_generic(id,binfo) remove_object(id, binfo)
+
+#define DEBUGGER_PSHEET_NAME  "DEBUGGER_P_SHEET"
+
+/* special graphics attribute meaning "use what anyone else's attributes" */
+#define GA_NO_CHANGE 0
+/* this number should be bigger than any of the "real" GA's */
+#define GA_MAX 0x1000
+
+/**************************** Utilities *****************************/
+
+static int
+emacs_CWriteRequestBuffer (Connection* conn)
+{
+  int result;
+  /* don't kill emacs with SIGPIPE */
+  SIGTYPE (*old_sigpipe)() =
+    (SIGTYPE (*) ()) signal (SIGPIPE, SIG_IGN);
+
+  result = CWriteRequestBuffer (conn);	/* the real one; macroized later */
+  signal (SIGPIPE, old_sigpipe);
+  return result;
+}
+
+#define CWriteRequestBuffer emacs_CWriteRequestBuffer
+
+
+static Energize_Extent_Data *
+extent_to_data (Lisp_Object extent_obj)
+{
+  Energize_Extent_Data *ext = 0;
+
+  if (!EXTENTP (extent_obj))
+    return 0;
+  else
+    ext = energize_extent_data (XEXTENT (extent_obj));
+
+  if (ext)
+    {
+      if (EQ (ext->extent, extent_obj))
+	return ext;
+      else
+	abort ();
+    }
+  else
+    return 0;
+}
+
+
+static Lisp_Object
+data_to_extent (Energize_Extent_Data *ext)
+{
+  Lisp_Object extent = ext->extent;
+  assert (EXTENTP (extent));
+  return extent;
+}
+
+/* duplicate a string */
+static char*
+copy_string (char *s)
+{
+  if (!s)
+    return 0;
+  else
+    {
+      int len = strlen (s);
+      char *res = (char *) xmalloc (len + 1);
+      return strcpy (res, s);
+    }
+}
+
+/* Get objects from the hashtables */
+static CONST void *
+get_object_internal (EId id, c_hashtable table)
+{
+  void *res;
+  CONST void *found = gethash ((void*)id, table, &res);
+
+  if (found) CHECK_OBJECT (res);
+
+  return found ? res : 0;
+}
+
+static CONST void *
+get_object (EId id, BufferInfo *binfo)
+{
+  return get_object_internal (id, binfo->id_to_object);
+}
+
+static void
+put_object_internal (EId id, c_hashtable table, void *object)
+{
+  if (!PUT_ABLE_OBJECT (object))
+    error ("Can't put 0x%x in table", object);
+  CHECK_OBJECT (object);
+  puthash ((void*)id, object, table);
+}
+
+static void
+put_object (EId id, BufferInfo *binfo, void *object)
+{
+  put_object_internal (id, binfo->id_to_object, object);
+  return;
+}
+
+static void
+remove_object_internal (EId id, c_hashtable table)
+{
+  void *res;
+
+  if (gethash ((void*)id, table, &res))
+    {
+      if (OBJECT_FREE (res))
+	error ("Free'd object 0x%x still in table!", res);
+      remhash ((void*)id, table);
+    }
+  else if (id)
+    /* #### If this happens for Energize_Extent_Data as a result of extent
+       finalization, this aborts (because gc_in_progress).  These errors are
+       awfully bad, so probably they should just be abort()s anyway... */
+    error ("EId %d not in table!", id);
+}
+
+static void
+remove_object (EId id, BufferInfo *binfo)
+{
+  remove_object_internal (id, binfo->id_to_object);
+  return;
+}
+
+/* maphash_function called by free_buffer_info */
+static void
+free_object (void *key, void *contents, void *arg)
+{
+  BufferInfo *binfo = arg;
+
+  if (contents)
+    {
+      switch (OBJECT_SEAL (contents))
+	{
+	case BUF_INFO_SEAL:
+	  break;
+	case EXTENT_SEAL:
+	  free_Energize_Extent_Data ((Energize_Extent_Data *) contents,
+				     binfo, OFT_MAPHASH);
+	  break;
+	case GDATA_CLASS_SEAL:
+	  free_GDataclass ((GDataClass *) contents, binfo);
+	  break;
+	case GDATA_SEAL:
+	  free_GenericData ((GenericData *) contents, binfo);
+	  break;
+	default:
+	  error ("Bad argument 0x%x to free_object()", contents);
+	  return;
+	}
+    }
+}
+
+static GDataClass *
+alloc_GDataclass (EId id, BufferInfo *binfo)
+{
+  GDataClass *cl = xnew (GDataClass);
+  memset (cl, 0, sizeof (GDataClass));
+  cl->seal = GDATA_CLASS_SEAL;
+  cl->id = id;
+  put_class (cl->id, binfo, cl);
+  return cl;
+}
+
+static void
+free_GDataclass (GDataClass *cl, BufferInfo *binfo)
+{
+  if (cl)
+    {
+      remove_class (cl->id, binfo);
+      SET_OBJECT_FREE (cl);
+    }
+  return;
+}
+
+
+static GenericData *
+alloc_GenericData (EId id, GDataClass *cl, BufferInfo *binfo)
+{
+  GenericData *gen = xnew (GenericData);
+  gen->seal = GDATA_SEAL;
+  gen->id = id;
+  gen->cl = cl;
+/*  gen->image = 0;*/
+  gen->flags = 0;
+  gen->modified_state = 0;
+  put_generic (gen->id, binfo, gen);
+  return gen;
+}
+
+static void
+free_GenericData (GenericData *gen, BufferInfo *binfo)
+{
+  if (gen)
+    {
+      remove_generic (gen->id, binfo);
+      gen->cl = 0;
+      SET_OBJECT_FREE (gen);
+    }
+  return;
+}
+
+/* Called to flush the extent from the hash table when Energize tells us to
+   lose the extent.  This is NOT called from the extent GC finalization method,
+   because there would be a period before the next GC when we still had an
+   Energize ID that the server thought was dead, and could concievably reuse.
+
+   Since we protect extents from GC until Energize says they're done, if an
+   extent still has Energize data by the time it gets collected, something is
+   fucked.
+ */
+static void
+free_Energize_Extent_Data (Energize_Extent_Data *ext, BufferInfo *binfo,
+			   enum Energize_Object_Free_Type free_type)
+{
+  if (ext)
+    {
+      Lisp_Object extent_obj = data_to_extent (ext);
+
+      /* Remove the extent, remove the extent's pointer to the data,
+	 and the data's pointer to the extent. */
+      Fdetach_extent (extent_obj);
+      set_energize_extent_data (XEXTENT (extent_obj), 0);
+      ext->extent = Qnil; /* at this point, refs will abort */
+
+      /* Remove the data from the hash table, and mark it as dead. */
+      remove_extent_data (ext->id, binfo);
+      ext->id = 0;
+
+      /* don't free this "sub-guy" via maphash, as it will get taken care
+	 of during the course of the maphash without our doing anything */
+      if (free_type != OFT_MAPHASH)
+	{
+	  if (ext->extentType == CEGeneric)
+	    free_GenericData (ext->u.generic.gData, binfo);
+	}
+
+      SET_OBJECT_FREE (ext);
+    }
+  return;
+}
+
+static BufferInfo *
+alloc_BufferInfo (EId id, Lisp_Object name, Lisp_Object filename,
+		  char *class_str, Editor *editor, Window win, int nobjects)
+{
+  BufferInfo *binfo = xnew (BufferInfo);
+  Widget nw = 0;
+  Lisp_Object buffer = Qnil;
+
+  if (win)
+    {
+      char win_as_string [16];
+      nw = XtWindowToWidget (get_x_display (Qnil), win);
+      if (nw)
+	nw = XtParent (nw);
+      
+      if (nw)
+	sprintf (win_as_string, "w%x", nw);
+      else
+	sprintf (win_as_string, "0x%x", win);
+      binfo->frame =
+	Fx_create_frame (Qnil, Qnil, build_string (win_as_string));
+    }
+  else
+    binfo->frame = Qnil;
+
+  /* try to re-use a buffer with the same file name if one already exists.
+   * If a buffer already exists but is modified we should do a dialog and
+   * ask the user what to do.  For now I'll just use a new buffer in that case.
+   * ParseBuffer will erase the buffer.
+   */
+  if (!NILP (filename))
+    {
+      int offct = find_file_compare_truenames;
+      find_file_compare_truenames = 1;
+      buffer = Fget_file_buffer (filename);
+      find_file_compare_truenames = offct;
+
+      if (!NILP (buffer) && !NILP (Fbuffer_modified_p (buffer)))
+	buffer = Qnil;
+    }
+
+  if (NILP (buffer))
+    buffer = Fget_buffer_create (Fgenerate_new_buffer_name (name, Qnil));
+
+  binfo->seal = BUF_INFO_SEAL;
+  binfo->id = id;
+  binfo->flags = 0;
+  binfo->editor = editor;
+  binfo->id_to_object = make_hashtable (nobjects);
+  binfo->emacs_buffer = buffer;
+  binfo->modified_state = 0;
+  binfo->editable = 0;
+  binfo->output_mark = Qnil;
+  binfo->buffer_type = kernel_buffer_type_to_elisp_type (class_str);
+  binfo->p_sheet_ids = 0;
+  binfo->n_p_sheets = 0;
+  binfo->note_ids = 0;
+  binfo->n_notes = 0;
+#ifdef I18N4
+  binfo->wcmap.valid = 0;
+  binfo->wcmap.modiff_stamp = -1;
+  binfo->wcmap.map = NULL;
+#endif   
+  put_buffer_info (id, binfo->emacs_buffer, binfo, editor);
+
+  Venergize_buffers_list = Fcons (buffer, Venergize_buffers_list);
+
+#if 0
+ *  if (nw){
+ *    Lisp_Object window = Fframe_selected_window (binfo->frame);
+ *    Fset_window_buffer (window, binfo->emacs_buffer);
+ *    set_text_widget ((NoteWidget)nw,
+ *		     FRAME_X_SHELL_WIDGET (XFRAME (binfo->frame)));
+ *  }
+#endif
+
+  return binfo;
+}
+
+/* free a buffer_info */
+static void
+free_buffer_info (BufferInfo *binfo)
+{
+  maphash (free_object, binfo->id_to_object, (void *)binfo);
+  free_hashtable (binfo->id_to_object);
+  if (energize_connection && energize_connection->binfo_hash)
+    {
+      if (binfo->id)
+	remhash ((void *)binfo->id, energize_connection->binfo_hash);
+      if (!NILP (binfo->emacs_buffer))
+	{
+	  remhash (LISP_TO_VOID (binfo->emacs_buffer),
+		   energize_connection->binfo_hash);
+	}
+    }
+  binfo->id = 0;
+  binfo->emacs_buffer = Qnil;
+#ifdef I18N4
+  if (binfo->wcmap.valid) {
+    binfo->wcmap.valid= 0;
+    xfree(binfo->wcmap.map);
+  }
+#endif 
+  SET_OBJECT_FREE (binfo);
+}
+
+/* hash for BufferInfo structures */
+static BufferInfo*
+get_buffer_info_for_emacs_buffer (Lisp_Object emacs_buf, Editor *editor)
+{
+  BufferInfo *res;
+  if (!editor || !editor->binfo_hash)
+    return 0;
+  else
+    {
+      void *vbuf;
+      /* #### Probably should use a Lisp hash table for this; what if the last
+	 pointer to the buffer was in the editor struct? */
+      return (gethash (LISP_TO_VOID (emacs_buf),
+		       editor->binfo_hash,
+		       (void *) &res)
+	      ? res : 0);
+    }
+}
+
+
+
+/* Called by mark_buffer.  It is possible for the last remaining pointer to
+   an extent object to be in an Energize_Extent_Data structure that is pointed
+   at by the binfo->id_to_object table.  Since Energize may still reference
+   this object by its id (in fact, I think it may even "ressurect" a detached
+   extent) we must prevent the extent from being garbage collected.  Aside 
+   from the obvious lossage (that the extent itself would be trashed) this
+   would also cause us to free the Energize_Extent_Data which the server still
+   believes we have.  The buffers all get marked because they're on the
+   `Venergize_buffers_list'.
+
+   So, an Energize extent or buffer only ever gets collected when the server
+   has claimed that it is done with it (or when the connection is closed).
+
+   Of course, by keeping pointers to lisp objects in C structs under non-lisp
+   hash tables, we again build in the assumption that GC never relocates.
+ */
+
+/* FUCK!!  It's not standard-conforming to cast pointers to functions
+   to or from void*.  Give me a fucking break! */
+struct markobj_kludge_fmh 
+{
+  void (*markobj) (Lisp_Object);
+};
+
+static void
+mark_energize_buffer_data_extent_mapper (void *key, void *val, void *arg)
+{
+  if (OBJECT_SEAL (val) == EXTENT_SEAL)
+    {
+      struct markobj_kludge_fmh *fmh = arg;
+      struct Energize_Extent_Data *ext = (Energize_Extent_Data *) val;
+      /* Lisp_Object extent = data_to_extent (ext);  (will abort if marked) */
+      Lisp_Object extent = ext->extent;
+      assert (GC_EXTENTP (extent));
+      ((*fmh->markobj) (extent));
+    }
+}
+
+void
+mark_energize_buffer_data (struct buffer *b,
+			   void (*markobj) (Lisp_Object))
+{
+  struct markobj_kludge_fmh fmh;
+  Lisp_Object buffer;
+  BufferInfo *binfo;
+  if (!energize_connection || !energize_connection->binfo_hash)
+    return;
+  XSETBUFFER (buffer, b);
+  binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
+  if (! binfo)
+    return;
+  fmh.markobj = markobj;
+  maphash (mark_energize_buffer_data_extent_mapper, binfo->id_to_object, &fmh);
+}
+
+
+
+struct buffer_and_sheet_ids
+{
+  EId	buffer_id;
+  EId	sheet_id;
+};
+
+static void
+find_sheet_id (void* key, void* contents, void* arg)
+{
+  BufferInfo* binfo = (BufferInfo*)contents;
+  EId buffer_id = (EId)key;
+  struct buffer_and_sheet_ids* result = (struct buffer_and_sheet_ids*)arg;
+  int i;
+
+  if (!result->buffer_id)
+    for (i = 0; i < binfo->n_p_sheets; i++)
+      if (binfo->p_sheet_ids [i] == result->sheet_id)
+	{
+	  result->buffer_id = buffer_id;
+	  return;
+	}
+}
+
+static EId
+buffer_id_of_sheet (EId id)
+{
+  Editor *editor = energize_connection;
+  struct buffer_and_sheet_ids basi;
+  if (!energize_connection)
+    return 0;
+
+  basi.buffer_id = 0;
+  basi.sheet_id = id;
+  maphash (find_sheet_id, editor->binfo_hash, (void*)&basi);
+  return basi.buffer_id;
+}
+
+static long
+get_energize_buffer_id (Lisp_Object emacs_buf)
+{
+  Editor *editor = energize_connection;
+  BufferInfo *res;
+
+  if (!editor || !editor->binfo_hash)
+    return -1;
+  else if (!gethash (LISP_TO_VOID (emacs_buf), editor->binfo_hash, (void *)&res))
+    return -1;
+  else
+    return (long) res->id;
+}
+
+static char *
+kernel_buffer_type_to_elisp_type (char *kernel_type)
+{
+  struct buffer_type_struct *bts =
+    kernel_buffer_types_to_elisp_buffer_types_vector;
+  char *elisp_type = 0;
+
+  if (!kernel_type)
+    return UNINITIALIZED_BUFFER_TYPE;
+
+  while (bts->kernel_name)
+    {
+      if (!strcmp (bts->kernel_name, kernel_type))
+	{
+	  elisp_type = bts->elisp_name;
+	  break;
+	}
+      bts++;
+    }
+
+  if (!elisp_type)
+    return kernel_type;
+  else
+    return elisp_type;
+}
+
+static Lisp_Object
+get_buffer_type_for_emacs_buffer (Lisp_Object emacs_buf, Editor *editor)
+{
+  BufferInfo *binfo;
+  if (!(binfo = get_buffer_info_for_emacs_buffer (emacs_buf, editor)))
+    return Qnil;
+  else
+    {
+      if (!binfo->buffer_type) binfo->buffer_type =
+	UNINITIALIZED_BUFFER_TYPE;
+
+      return intern (binfo->buffer_type);
+    }
+}
+
+static Lisp_Object
+set_buffer_type_for_emacs_buffer (Lisp_Object emacs_buf, Editor *editor,
+				    Lisp_Object type)
+{
+  BufferInfo *binfo;
+  if (!(binfo = get_buffer_info_for_emacs_buffer (emacs_buf, editor)))
+    return Qnil;
+  else
+    {
+      char *type_string;
+
+      if (NILP (type)) return Qnil;
+
+      if (SYMBOLP (type))
+	XSETSTRING (type, XSYMBOL (type)->name);
+
+      if (STRINGP (type))
+	type_string = (char *)string_data (XSTRING (type));
+
+      type_string = copy_string (type_string);
+
+      if (!type_string) return Qnil;
+
+      binfo->buffer_type = type_string;
+
+      return intern (binfo->buffer_type);
+    }
+}
+
+static BufferInfo*
+get_buffer_info_for_id (EId id, Editor *editor)
+{
+  BufferInfo *res;
+  return (gethash ((void *)id, editor->binfo_hash, (void *)&res))?res:0;
+}
+
+static void
+put_buffer_info (EId id, Lisp_Object emacs_buf, BufferInfo *binfo,
+		 Editor *editor)
+{
+  puthash ((void *)id, binfo, editor->binfo_hash);
+  puthash (LISP_TO_VOID (emacs_buf), binfo, editor->binfo_hash);
+}
+
+static void
+remove_buffer_info (EId id, Lisp_Object emacs_buf, Editor *editor)
+{
+  void *vbuf;
+  remhash ((void *)id, editor->binfo_hash);
+  remhash (LISP_TO_VOID (emacs_buf), editor->binfo_hash);
+}
+
+
+/* Conversion between Energize and Emacs buffer positions */
+
+#if defined(I18N4)
+
+/* An emacs position is an index into the buffer...  In I18N, foreign
+   characters take up the same amount of space as ASCII characters.  Emacs
+   is using wide characters.  The first position is 1.
+
+   An energize position is a straight byte offset into the file when it's
+   saved onto disk.  Foreign characters take up more than one byte.  The
+   first position is 0. */
+
+#define WCMAP_SETSIZE(wcmap,n) {					      \
+  (wcmap).mapsize = (n);						      \
+  (wcmap).map = (WCharMapRec *) xrealloc((wcmap).map,			      \
+					 (n) * sizeof(WCharMapRec));	      \
+}
+#define WCMAP_ENLARGE(wcmap) WCMAP_SETSIZE(wcmap, 2*(wcmap.mapsize))
+
+#ifndef MB_LEN_MAX
+#define MB_LEN_MAX 10		/* arbitrarily large enough */
+#endif 
+
+static char wcsize_buf[MB_LEN_MAX];
+#define WCSIZE(wc)	     (isascii(wc) ? 1 : wctomb(wcsize_buf,wc))
+
+#define SANITY_CHECK_NOT
+#ifdef SANITY_CHECK
+static int sanity=0;
+#endif 
+
+static void
+sync_buffer_widechar_map (BufferInfo *binfo)
+{
+  /* #### - check this: */
+  assert (XBUFFER (binfo->emacs_buffer) == current_buffer);
+
+  if (!binfo->wcmap.valid)
+    {
+      binfo->wcmap.valid= 1;
+      binfo->wcmap.modiff_stamp= -1;
+      binfo->wcmap.map= NULL;
+      WCMAP_SETSIZE (binfo->wcmap, 1);
+    }
+
+  if (binfo->wcmap.modiff_stamp == BUF_MODIFF (current_buffer))
+    {
+      return;
+    }
+  else
+    {
+      int nbytes, maxpos,
+      pos = 0,			/* start at zero instead of 1 */
+      adjustment = 0,
+      mapindex= 0;
+      wchar_t *buf, t;
+
+#ifdef SANITY_CHECK
+      stderr_out ("rebuilding widechar map for %s\n", string_data (XSTRING (current_buffer->name)));
+#endif
+      
+      /* #### this is not gonna compile.  move_gap() is now a private function
+	 inside of insdel.c and it should stay that way. */
+      if (BUF_BEGV (current_buffer) < GPT && BUF_ZV (current_buffer) > GPT)
+	move_gap (current_buffer, BUF_BEGV (current_buffer));
+      binfo->wcmap.modiff_stamp = BUF_MODIFF (current_buffer);
+      
+      buf = BUF_BEG_ADDR (current_buffer);
+      maxpos= (BUF_Z (current_buffer) - 1);
+      wctomb (NULL, 0);		/* reset shift state of wctomb() */
+      binfo->wcmap.map[mapindex].pos= pos;
+      binfo->wcmap.map[mapindex].eucsize=
+	((pos<maxpos) ? (nbytes= WCSIZE(buf[pos])) : 1);
+      do {
+	while ((pos < maxpos) && (nbytes == WCSIZE(t = buf[pos])))
+	  ++pos;
+	binfo->wcmap.map[mapindex++].endpos= pos;
+	if (mapindex == binfo->wcmap.mapsize)
+	  WCMAP_ENLARGE(binfo->wcmap);
+	if (pos < maxpos)
+	  {
+	    binfo->wcmap.map[mapindex].pos= pos;
+	    binfo->wcmap.map[mapindex].eucsize= nbytes= WCSIZE(t);
+	  }
+      } while (pos < maxpos);
+      WCMAP_SETSIZE(binfo->wcmap, mapindex);
+    }
+}
+
+static EnergizePos
+EnergizePosForBufpos (Bufpos char_pos, BufferInfo *binfo)
+{
+  int mapindex;
+  WCharMapRec map;
+  EnergizePos byte_pos;
+
+  sync_buffer_widechar_map (binfo);
+
+  --char_pos;			/* preadjust emacs position */
+
+  mapindex=0, byte_pos=0;
+  while ((mapindex < binfo->wcmap.mapsize) &&
+	 (char_pos > (map= binfo->wcmap.map[mapindex++]).pos)) {
+    if (char_pos > map.endpos) {
+      byte_pos += ((map.endpos - map.pos) * map.eucsize);
+    } else {
+      byte_pos += ((char_pos - map.pos) * map.eucsize);
+    }
+  }
+  /* there should be a sanity check here */
+#ifdef CHECK_SANITY
+  if (!sanity) {
+    Bufpos check_pos;
+    sanity=1;
+    check_pos= BufposForEnergizePos(byte_pos, binfo);
+    sanity=0;
+
+    if (check_pos != char_pos) {
+      stderr_out ("ezpos(%d) = %d, Bufpos(%d) = %d\n",
+	      char_pos, byte_pos, byte_pos, check_pos);
+    }
+  }
+#endif 
+  return byte_pos;
+}
+
+static Bufpos
+BufposForEnergizePos (EnergizePos ez_pos, BufferInfo *binfo)
+{
+  int mapindex, x;
+  WCharMapRec map;
+  Bufpos char_pos;
+  EnergizePos byte_pos;
+
+  sync_buffer_widechar_map(binfo);
+
+  mapindex=0, byte_pos=0;
+  while ((mapindex < binfo->wcmap.mapsize) &&
+	 (byte_pos <= ez_pos)) {
+    map= binfo->wcmap.map[mapindex++];
+    x= (map.eucsize*(map.endpos-map.pos));
+    if (ez_pos > (byte_pos + x)) {
+      byte_pos += x;
+      char_pos = map.endpos;
+    } else {
+      char_pos = (map.pos + ((ez_pos - byte_pos)/map.eucsize));
+      break;
+    }
+  }
+  char_pos= (char_pos >= (1 << VALBITS)) ? BUF_Z (current_buffer) :
+    (char_pos + 1);
+#ifdef CHECK_SANITY
+  if (!sanity) {
+    EnergizePos check_pos;
+    sanity=1;
+    check_pos= EnergizePosForBufpos(char_pos);
+    sanity=0;
+    
+    if (check_pos != ez_pos) {
+      stderr_out ("Bufpos(%d) = %d, EnergizePosForBufpos(%d) = %d\n",
+	      ez_pos, char_pos, char_pos, check_pos);
+    }
+  }
+#endif 
+  return (char_pos);
+}
+
+#else /* !I18N4 */
+
+static Bufpos
+BufposForEnergizePos (EnergizePos energizePos, BufferInfo *binfo)
+{
+  return ((energizePos >= (1 << VALBITS)) ? BUF_Z (current_buffer) : 
+	  (energizePos + 1));
+}
+
+static EnergizePos
+EnergizePosForBufpos (Bufpos emacs_pos, BufferInfo *binfo)
+{
+  return (emacs_pos - 1);
+}
+
+#endif /* !I18N4 */
+
+
+DEFUN ("energize-update-menubar", Fenergize_update_menubar,
+       Senergize_update_menubar, 0, 1, 0 /*
+obsolete
+*/ )
+     (frame)
+     Lisp_Object frame;
+{
+  return Qnil;
+}
+
+
+DEFUN ("energize-extent-menu-p", Fenergize_extent_menu_p,
+       Senergize_extent_menu_p, 1, 1, 0 /*
+Whether the extent has a set of commands defined by Energize.
+*/ )
+     (extent_obj)
+     Lisp_Object extent_obj;
+{
+  CHECK_EXTENT (extent_obj);
+
+  if (NILP (Fconnected_to_energize_p ()))
+    return Qnil;
+  else
+    {
+      Energize_Extent_Data *ext = extent_to_data (extent_obj);
+      return (ext && ext->extentType == CEGeneric) ? Qt : Qnil;
+    }
+}
+
+
+/* Do what is needed so that the delayed requests will be notified by
+** the event loop */
+
+extern void mark_process_as_being_ready (struct Lisp_Process* process);
+
+static void
+notify_delayed_requests (void)
+{
+  if (energize_connection
+      && !NILP (energize_connection->proc)
+      && energize_connection->conn
+      && CRequestDelayedP (energize_connection->conn))
+    this function no longer exists. 
+    (Replaced by mark_what_as_being_ready, with different arguments.)
+    Rewrite this.
+    mark_process_as_being_ready (XPROCESS (energize_connection->proc));
+}
+
+
+/******************* IMAGE storage maintenance *******************/
+
+extern GLYPH image_instance_to_glyph (Lisp_Object);
+
+static c_hashtable image_cache;
+
+extern char *strdup ();
+extern Lisp_Object Fbuffer_file_name (Lisp_Object);
+
+
+extern Lisp_Object Fmake_face (Lisp_Object name);
+extern Lisp_Object Fface_foreground (Lisp_Object face, Lisp_Object frame);
+extern Lisp_Object Fface_background (Lisp_Object face, Lisp_Object frame);
+
+/* Don't let any of these get GCed, since we hold their GLYPH ids in
+   a non-lisp hash table (image_cache) . */
+static Lisp_Object Vall_energize_pixmaps;
+
+/* Parses an image from the image language */
+static GLYPH
+read_energize_image_data (Connection *conn, BufferInfo *binfo)
+{
+  ReqLen l;
+  char *s = CGetVstring (conn, &l);
+  char pix_name [255];
+  char buf [255];
+  int attr_number, pix_len;
+  char *file;
+  GLYPH result = 0;
+  /* It is bad news to pass the address of a short to gethash. */
+  int hashed = 0;
+
+  if (s[0] != 'f')
+    return 0;
+
+  if (gethash ((void *) s, image_cache, (void *) &hashed))
+    /* If we have already parsed this image spec (string=) then just return
+       the old glyph, instead of calling the lisp code, x_get_pixmap, and
+       XtGetSubResources again.	 The result may be 0 if there is no pixmap
+       file name in the resource database.
+     */
+    return (GLYPH) hashed;
+
+  if (3 != sscanf (s, "f %d p %d %s", &attr_number, &pix_len, pix_name))
+    {
+      sprintf (buf, "unparsable image: \"%s\"", s);
+      error (buf);
+    }
+
+  assert (pix_len == strlen (pix_name));
+
+  /* Read the pixmap file name for this image from the resource db */
+  {
+    XtResource resource [1];
+    resource[0].resource_name = pix_name;
+    resource[0].resource_class = XtCBitmap;
+    resource[0].resource_type = XtRString;
+    resource[0].resource_size = sizeof (char *);
+    resource[0].resource_offset = 0;
+    resource[0].default_type = XtRImmediate;
+    resource[0].default_addr = 0;
+    file = 0;
+    XtGetSubresources (FRAME_X_SHELL_WIDGET (XFRAME (Fselected_frame (Qnil))),
+		       (XtPointer) &file, "image", "Image", resource, 1, NULL,
+		       0);
+  }
+
+  if (! file)
+    result = 0;
+  else
+    {
+      Lisp_Object lfile = Qnil;
+      Lisp_Object p = Qnil;
+      struct gcpro gcpro1, gcpro2;
+      sprintf (buf, "attribute%d", attr_number);
+      GCPRO2 (lfile, p);
+      lfile = build_string (file);
+      p = Fmake_image_instance (lfile, Qnil); /* may gc */
+      result = image_instance_to_glyph (p);
+      Vall_energize_pixmaps = Fcons (Fcons (make_int (result), p),
+				     Vall_energize_pixmaps);
+      if (!EQ (p, glyph_to_image_instance (result)))
+	abort ();
+      UNGCPRO;
+
+      if (XIMAGE_INSTANCE (p)->depth == 0)
+	/* if depth is >0 then this is an XPM, and its colors are not
+	   controlled by the fg/bg of a face.  So don't bother making a
+	   face for it. */
+	{
+	  Lisp_Object face, fg, bg;
+	  struct face *c_face;
+	  /* #### review this */
+	  face = Fmake_face (intern (buf));
+	  fg = FACE_FOREGROUND (face, Qnil);
+	  bg = FACE_BACKGROUND (face, Qnil);
+	  Fcolorize_image_instance (p, fg, bg);
+	}
+    }
+
+  /* CGetVstring returns a pointer into the connection buffer; we need to
+     copy it to use it as a hash key. */
+  s = strdup (s);
+
+  hashed = result;
+  puthash ((void *) s, (void *) hashed, image_cache);
+  return result;
+}
+
+
+/* Parses Classes from the connection buffer.  Defines them for
+ * the buffer given as argument */
+static void
+read_energize_class_data (Connection *conn, unsigned int number,
+			  BufferInfo *binfo, unsigned int modify_ok)
+{
+  CClass *ptr;			/* pointer to class data in buffer */
+  GDataClass *cl;		/* unmodified class data */
+  GLYPH g;
+  int i;
+
+  for (i = 0; i < number; i++)
+    {
+      ptr = CGet (conn, CClass);
+      g = read_energize_image_data (conn, binfo);
+      cl = get_class (ptr->classId, binfo);
+
+      if (!cl)
+	cl = alloc_GDataclass (ptr->classId, binfo);
+      else if (!modify_ok)
+	message("Attempt to create class with existing Id %8x", ptr->classId);
+
+      if (ignore_kernel) continue;
+
+      /* if it did exist, we just clobber it */
+      if (cl->flags != ptr->flags)
+	{
+	  cl->flags = ptr->flags;
+	  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
+	}
+      if (cl->glyph != g)
+	{
+	  cl->glyph = g;
+	  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
+	}
+    }
+}
+
+/* Parse GenericData form the connection buffer.  Defines them for the buffer
+ * given as argument */
+static void
+read_energize_generic_data (Connection *conn, unsigned int number,
+		 BufferInfo *binfo, unsigned int modify_ok)
+{
+  CGeneric *ptr;
+  GenericData *gen;
+  GDataClass *cl;
+  GLYPH g;
+  int i;
+
+  for (i = 0; i < number; i++)
+    {
+      ptr = CGet (conn, CGeneric);
+      g = read_energize_image_data (conn, binfo);
+      gen = get_generic (ptr->genericId, binfo);
+      cl = get_class (ptr->classId, binfo);
+
+      if (!gen)
+	{
+	  /* create generic if it didn't already exist */
+
+	  if (!cl)
+	    {
+	      message ("Attempt to create generic %8x with undefined class %8x",
+		     ptr->genericId, ptr->classId);
+	      continue;
+	    }
+
+	  gen = alloc_GenericData (ptr->genericId, cl, binfo);
+	  gen->glyph = g;
+	  if (ptr->flags != 0xff) gen->flags = ptr->flags;
+	  gen->attribute = ptr->attribute;
+	}
+      else if (!modify_ok)
+	message("Attempt to create generic with existing id %8x",
+	      ptr->genericId);
+      else{
+	/* modify the generic */
+	int modified = 0;
+	if (cl != gen->cl)
+	  {
+	    modified = 1;
+	    gen->cl = cl;
+	  }
+	if (gen->glyph != g)
+	  {
+	    modified = 1;
+	    gen->glyph = g;
+	  }
+	if (ptr->flags != 0xff)
+	  {
+	    modified = 1;
+	    gen->flags = ptr->flags;
+	  }
+	if (gen->attribute != ptr->attribute)
+	  {
+	    modified = 1;
+	    gen->attribute = ptr->attribute;
+	  }
+	if (modified)
+	  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
+      }
+    }
+}
+
+
+static void
+insert_one_extent (CExtent* ptr, BufferInfo* binfo, int modify_ok)
+{
+  Energize_Extent_Data *ext;
+  GenericData *gen;
+  Bufpos extent_start;
+  Bufpos extent_end;
+  int set_endpoints_p = 1;
+  int created_ext_data = 0;
+  Lisp_Object buffer = binfo->emacs_buffer;
+
+  ext = get_extent_data (ptr->extentId, binfo);
+
+  if (!ext)
+    {
+      ext = (Energize_Extent_Data *) xmalloc (sizeof (Energize_Extent_Data));
+      created_ext_data = 1;
+      ext->seal = EXTENT_SEAL;
+      ext->id = ptr->extentId;
+      ext->extentType = -1;
+      ext->extent = Qnil;	/* not a normal value: set before we return */
+      ext->start_pixmap = 0;
+      ext->end_pixmap = 0;
+      ext->warn_modify = 0;
+      put_extent_data (ext->id, binfo, ext);
+    }
+  else if (!modify_ok)
+    message ("Creating extent with existing id %8x", ptr->extentId);
+
+  ext->extentType = ptr->extentType;
+
+  switch (ptr->extentType)
+    {
+    case CEAttribute:
+      ext->u.attr.attrValue = ptr->data;
+      break;
+
+    case CEAbbreviation:
+      ext->u.abbrev.isOpened = ptr->data;
+      break;
+
+    case CEWriteProtect:
+      break;
+
+    case CEGeneric:
+      gen = get_generic (ptr->data, binfo);
+      if (!gen)
+	{
+	  message ("NewExtents: Nonexistent generic data %8x", ptr->data);
+	  return;
+	}
+      ext->u.generic.gData = gen;
+      break;
+
+    default:
+      message ("Unknown extent type %d", ptr->extentType);
+      break;
+    }
+
+  /* instruct redisplay to recompute the frame */
+  BUF_FACECHANGE (XBUFFER (binfo->emacs_buffer))++;
+
+  /* ptr->startPosition == ptr->endPosition == ~0 means to not change
+   * the extent endpoints */
+  if (ptr->startPosition == ~0 && ptr->endPosition == ~0)
+    {
+      set_endpoints_p = 0;
+      extent_start = ~0;
+      extent_end = ~0;
+    }
+  else
+    {
+      struct buffer *b = XBUFFER (buffer);
+      extent_start = BufposForEnergizePos (ptr->startPosition, binfo);
+      extent_end = BufposForEnergizePos (ptr->endPosition, binfo);
+
+      /* We have to be careful to create the extents with endpoints
+	 which are in the buffer.
+
+	 Under certain obscure conditions involving changes made outside
+	 of Emacs (bug 19983), the server and the editor can have different
+	 ideas about where the extents are, so these numbers can be off
+	 temporarily (during the window between read_energize_extent_data
+	 and Qenergize_auto_revert_buffer in read_energize_buffer_data
+	 from ModifyBufferRType).
+       */
+
+      /* Don't allow 0-length extents, as they tend to disappear. */
+      if (extent_start >= extent_end)
+	extent_end = extent_start + 1;
+
+      /* Don't let them outside the buffer (even if we grew them). */
+      if (extent_start >= BUF_Z (b))  extent_start = BUF_Z (b) - 1;
+      if (extent_end   >= BUF_Z (b))  extent_end   = BUF_Z (b) - 1;
+      if (extent_start < BUF_BEG (b)) extent_start = BUF_BEG (b);
+      if (extent_end   < BUF_BEG (b)) extent_end   = BUF_BEG (b);
+
+      /* If they're 0-length again, then the extent must be at point-max.
+	 In that case, extent it backward (if possible) instead of forward.
+       */
+      if (extent_start >= extent_end
+	  && BUF_BEG (b) != BUF_Z (b))
+	extent_start = extent_end - 1;
+    }
+
+  /* no zero width extent */
+  if (set_endpoints_p && extent_start == extent_end)
+    extent_end += 1;
+
+  /* Now create the extent for the extent-data.  There is a 1:1 mapping between
+     these, and an extent-data points at an extent (and that extent points
+     back) until energize tells us to delete the extent.  This is the only
+     function in which ext->extent is ever not an extent. */
+  if (created_ext_data)
+    {
+      ext->extent = Fmake_extent (make_int (extent_start),
+				  make_int (extent_end), buffer);
+      set_energize_extent_data (XEXTENT (ext->extent), ext);
+      restore_energize_extent_state (XEXTENT (ext->extent));
+    }
+  else
+    {
+      if (!EQ (buffer, extent_buffer (XEXTENT (ext->extent))))
+	signal_simple_error_2 ("extent not part of buffer", ext->extent,
+			       buffer);
+
+      if (set_endpoints_p)
+	Fset_extent_endpoints (ext->extent, make_int (extent_start),
+			       make_int (extent_end), Qnil);
+      restore_energize_extent_state (XEXTENT (ext->extent));
+    }
+
+  if (energize_extent_data (XEXTENT (ext->extent)) != ext)
+    abort ();
+
+  extent_duplicable_p (XEXTENT (ext->extent)) = 1;
+  extent_unique_p (XEXTENT (ext->extent)) = 1;
+}
+
+
+/* Parse GenericData from the connection buffer.  Defines them for the buffer
+ * given as argument.  Creates the Emacs extents while parsing.
+ * Energize sends the extents ordered by increasing starting position.
+ I don't think the following is true any more:
+ * Emacs is __much__ faster at inserting them in decreasing starting position
+ * also for overlaps to work correctly the outmost extents have to be
+ * inserted first.  This is what the recursive function is trying to do.
+ */
+static void
+read_energize_extent_data (Connection *conn, unsigned int number,
+			   BufferInfo *binfo, unsigned int modify_ok,
+			   int extent_offset)
+{
+  CExtent* all_extents;
+  int i;
+
+  /* Gets the extents from the connection */
+  all_extents = CGetN (conn, CExtent, number);
+
+  /* adjusts the endpoints with the offset */
+  for (i = 0; i < number; i++)
+    {
+      if (all_extents [i].startPosition != ~0)
+	all_extents [i].startPosition += extent_offset;
+      if (all_extents [i].endPosition != ~0)
+	all_extents [i].endPosition += extent_offset;
+    }
+
+  /* inserts them all */
+  for (i = number - 1; i >= 0; i--)
+    {
+      insert_one_extent (all_extents + i, binfo, modify_ok);
+    }
+}
+
+/* Parses a CBuffer in the connection stream. If (delete_from != delete_to)
+   all characters in this range must be deleted.
+   */
+
+static int
+string_buffer_compare (char *string, int string_len,
+		       struct buffer *buf, Bufpos bufpos)
+{
+  /* !!#### needs to be rewritten for Mule */
+  Bufpos first_section_end = BUF_CEILING_OF (buf, bufpos);
+
+  /* degenerate case, which we consider to be "true" */
+  if (string_len == 0) return 0;
+
+  /* string won't fit in the buffer, so comparison fails */
+  if (BUF_Z (buf) < (bufpos + string_len)) return -1;
+
+  /* bad starting position, so comparison fails */
+  if (bufpos < BUF_BEG (buf)) return -1;
+
+  {
+    char *first_section_chars = (char *) BUF_BYTE_ADDRESS (buf, bufpos);
+    int comp = strncmp (string, first_section_chars,
+			first_section_end - bufpos);
+    
+    if (comp) return comp;
+  }
+
+  if (first_section_end < BUF_ZV (buf))
+    /* there is a second section */
+    {
+      char *second_section_chars =
+	(char *) BUF_BYTE_ADDRESS (buf, first_section_end);
+      int comp = strncmp (string + (first_section_end - bufpos),
+			  second_section_chars,
+			  BUF_ZV (buf) - first_section_end);
+
+      if (comp) return comp;
+    }
+
+  return 0;
+}
+
+/* called by unwind protect, from within ParseBuffer and HandleRemoveExtents */
+static Lisp_Object
+restore_buffer_state (Lisp_Object state_cons)
+{
+  BufferInfo *binfo;
+  Lisp_Object bufferId_obj = Fcar (state_cons);
+  unsigned int bufferId = (unsigned int) get_opaque_ptr (bufferId_obj);
+  Lisp_Object buffer_modified_state = Fcar (Fcdr (state_cons));
+  Lisp_Object clear_undo_list = Fcdr (Fcdr (state_cons));
+
+  if (bufferId != 0)
+    {
+      if (energize_connection
+	  && (binfo = get_buffer_info_for_id (bufferId, energize_connection))
+	  && !NILP (binfo->emacs_buffer))
+	{
+	  /* Always ignore what Energize tells us about the buffer read-only
+	     state.  For files Emacs knows better and for non-file buffers
+	     Emacs is hacking the read-only state anyway so let it be. */
+	  XBUFFER (binfo->emacs_buffer)->read_only = buffer_modified_state;
+	  if (!NILP (clear_undo_list))
+	    XBUFFER (binfo->emacs_buffer)->undo_list = Qnil;
+	}
+    }
+  else
+    /* this is just temporary */
+    message ("Bad bufferId cons cell!");
+  return Qnil;
+}
+
+/* #### this shit should be using generate-new-buffer */
+static void
+rename_the_buffer (Lisp_Object new_name)
+{
+  int count = 0;
+  char number [8];
+  struct gcpro gcpro1;
+
+  Lisp_Object name = new_name;
+  GCPRO1 (name);
+  while (!NILP (Fget_buffer (name)))
+    {
+      sprintf (number, "<%d>", ++count);
+      name = concat2 (new_name, build_string (number));
+    }
+  Frename_buffer (name, Qnil);
+  UNGCPRO;
+}
+
+static int
+destroy_if_energize_extent (EXTENT e, void* arg)
+{
+  struct Energize_Extent_Data *ext = energize_extent_data (e);
+  if (ext)
+    {
+      Lisp_Object extent;
+      Lisp_Object buffer;
+      BufferInfo *binfo = 0;
+      XSETEXTENT (extent, e);
+      buffer = extent_buffer (XEXTENT (extent));
+      Fdelete_extent (extent);
+      if (BUFFERP (buffer))
+	binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
+      if (binfo)
+	free_Energize_Extent_Data (ext, binfo, OFT_GC);
+      else
+	{
+	  /* #### partly duplicated in free_Energize_Extent_Data() */
+	  set_energize_extent_data (e, 0);
+	  ext->extent = Qnil;
+	}
+    }
+  return 0;
+}
+
+static void
+destroy_all_energize_extents (struct buffer *buf)
+{
+  map_extents (BUF_BEG (buf), BUF_Z (buf), destroy_if_energize_extent,
+	       NULL, make_buffer (buf), 0,
+	       ME_END_CLOSED | ME_MIGHT_MODIFY_EXTENTS);
+}
+
+static Lisp_Object
+restore_inside_parse_buffer (Lisp_Object val)
+{
+  inside_parse_buffer = XINT (val);
+  return (val);
+}
+
+static void
+hack_window_point (Lisp_Object window,
+		   Lisp_Object old_point,
+		   Lisp_Object old_start,
+		   int keep_start_p,
+		   BufferInfo *binfo)
+     /* If we've reverted a buffer, sometimes we want to make sure that
+	the window-point doesn't move. */
+{
+  if (NILP (window))
+    return;
+
+  Fset_marker (XWINDOW (window)->pointm, old_point, binfo->emacs_buffer);
+  if (NILP (binfo->output_mark) && keep_start_p)
+    {
+      Fset_marker (XWINDOW (window)->start, old_start, binfo->emacs_buffer);
+      XWINDOW (window)->force_start = 1;
+    }
+}
+
+static void
+read_energize_buffer_data (Connection *conn, CBuffer *cbu, Editor *editor,
+			   EnergizePos delete_from, EnergizePos delete_to,
+			   Window win, int relative_p)
+{
+  char *name;
+  ReqLen name_len;
+  char *pathname_str;
+  ReqLen pathname_len;
+  char *buffer_class_str;
+  ReqLen buffer_class_len;
+  Lisp_Object pathname = Qnil;
+  Lisp_Object pathname_directory = Qnil;
+  Lisp_Object buffer_name = Qnil;
+  Lisp_Object filename = Qnil;
+#if 1
+  Lisp_Object display_window = Qnil;
+#endif
+  BufferInfo *binfo;
+  int modifying_p = 0;
+  Bufpos previous_point;
+  Bufpos from;
+  Bufpos to;
+#if 1
+  Bufpos display_start = 1;
+#endif
+  char *text;
+  ReqLen text_len;
+  int get_chars_from_file = 0;
+  Lisp_Object modified_buffer_flag;
+  int speccount = specpdl_depth ();
+  int extent_offset;
+  Lisp_Object restore_buffer_state_cons;
+  int should_keep_window_start = 1;
+  int no_text_deleted = 0;
+
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+
+  /* For some reason calling the GC before parsing the buffer data
+     makes a better usage of memory and emacs leaks less when 
+     creating/deleting LE browser buffers.  
+     However you don't want to call GC all the tiem so we only do it if the request
+     will create more than a given number of extents. */
+  if (cbu->nExtent > energize_extent_gc_threshold)
+    garbage_collect_1 ();
+
+  record_unwind_protect (save_restriction_restore, save_restriction_save ());
+
+  Fwiden (Fcurrent_buffer ());
+
+  GCPRO4 (buffer_name, pathname, pathname_directory, filename);
+
+  name = CGetVstring (conn, &name_len);
+
+  /* read the pathname and buffer-class -- Editor Protocol > 0 */
+  pathname_str = CGetVstring (conn, &pathname_len);
+  buffer_class_str = CGetVstring (conn, &buffer_class_len);
+
+  if (name_len)
+    buffer_name = build_string (name);
+  if (pathname_len)
+    pathname = build_string (pathname_str);
+
+  /* set up pathname_directory */
+  if (!NILP (pathname))
+    {
+      if (NILP (Ffile_directory_p (pathname)))
+	pathname_directory = Ffile_name_directory (pathname);
+      else
+	pathname_directory = pathname;
+    }
+
+  /* make sure that pathname_directory ends with a '/', if it exists */
+  if (!NILP (pathname_directory))
+    {
+      Bufbyte *str = string_data (XSTRING (pathname_directory));
+      Bytecount size = string_length (XSTRING (pathname_directory));
+      if (str[size - 1] != '/')
+	{
+	  Lisp_Object tmp = make_string (str, size + 1);
+	  set_string_byte (XSTRING (tmp), size, '/');
+	  pathname_directory = tmp;
+	}
+    }
+
+
+  /* get or create the BufferInfo */
+  if (binfo = get_buffer_info_for_id (cbu->bufferId, editor))
+    modifying_p = 1;
+  else
+    {
+      if (NILP (buffer_name))
+	{
+	  char *dummy = "*Unnamed " IDENTITY_CRISIS " Buffer*";
+	  buffer_name = build_string (dummy);
+	}
+      /* create new buffer */
+      binfo = alloc_BufferInfo (cbu->bufferId, buffer_name, pathname,
+				buffer_class_str, editor, win,
+				cbu->nExtent + cbu->nClass + cbu->nGeneric);
+      XBUFFER (binfo->emacs_buffer)->read_only =
+	cbu->flags == CBReadOnly ? Qt : Qnil;
+    }
+
+  /* remember where we were in which buffer before we change things */
+  if (current_buffer != XBUFFER (binfo->emacs_buffer))
+    {
+      record_unwind_protect (save_excursion_restore, save_excursion_save ());
+      Fset_buffer (binfo->emacs_buffer);
+    }
+
+  /* set default-directory */
+  if (!NILP (pathname_directory))
+    {
+      if (!NILP (Ffile_directory_p (pathname_directory))
+	  && !NILP (Ffile_executable_p (pathname_directory)))
+	Fset (Qdefault_directory, pathname_directory);
+      /* Never set this to nil, that loses badly. */
+/*      else
+	Fset (Qdefault_directory, Qnil); */
+    }
+
+  /* set file name unless it's a directory */
+  if (!NILP (pathname) && NILP (Ffile_directory_p (pathname)))
+    {
+      filename = Fexpand_file_name (pathname, Qnil);
+      Fset (Qbuffer_file_name, filename);
+    }
+
+  /* set buffer name */
+  if (!NILP (buffer_name))
+    {
+      if (modifying_p
+	  && strcmp ((char*)string_data (XSTRING (buffer_name)),
+		     (char*)
+		     string_data (XSTRING (XBUFFER (binfo->emacs_buffer)->name))))
+	rename_the_buffer (buffer_name);
+    }
+
+  if (modifying_p)
+    {
+      run_hook (Venergize_kernel_modification_hook);
+      /* Make sure buffer is current after the hook */
+      Fset_buffer (binfo->emacs_buffer);
+    }
+
+  modified_buffer_flag = Fbuffer_modified_p (binfo->emacs_buffer);
+
+  /* enables buffer edits */
+  restore_buffer_state_cons =
+    Fcons (make_opaque_ptr ((void *) cbu->bufferId),
+	   Fcons (XBUFFER (binfo->emacs_buffer)->read_only, Qt));
+  record_unwind_protect (restore_buffer_state, restore_buffer_state_cons);
+  XBUFFER (binfo->emacs_buffer)->read_only = Qnil;
+
+  /* any changes here should take place "underneath" these hooks, I think */
+  specbind (Qenergize_buffer_modified_hook, Qnil);
+  specbind (Qfirst_change_hook, Qnil);
+  specbind (Qbefore_change_functions, Qnil);
+  specbind (Qbefore_change_function, Qnil); /* #### */
+  /* As energize does not use the after-change-function it's not useful to
+     bind it to NIL */
+  /* specbind (Qafter_change_functions, Qnil); */
+  /* specbind (Qafter_change_function, Qnil); #### */
+  specbind (Qinhibit_read_only, Qt);
+  record_unwind_protect (restore_inside_parse_buffer,
+			 make_int (inside_parse_buffer));
+  inside_parse_buffer = 1;
+  specbind (Qbuffer_undo_list, Qt);
+
+  XBUFFER (binfo->emacs_buffer)->undo_list = Qt;
+
+  /* BufposForEnergizePos uses the current-buffer */
+  from = BufposForEnergizePos (delete_from, binfo);
+  to = BufposForEnergizePos (delete_to, binfo);
+
+  /* See if we should get the characters from the file directly.
+     Only protocol 0.10+ will do this.
+   */
+#ifdef ENERGIZE_V2_HEADERS
+  get_chars_from_file = 0;
+#else
+  if (cbu->flags != 0xff)
+    get_chars_from_file = cbu->flags &  CBFileYourself;
+  else
+    get_chars_from_file = binfo->flags &  CBFileYourself;
+#endif
+  
+  /* Even when we get the chars from a file there is an empty text string */
+  if (get_chars_from_file)
+    {
+      text = CGetVstring (conn, &text_len);
+      text = NULL;
+      text_len = 0;
+    }
+  else
+    {
+      text = CGetVstring (conn, &text_len);
+    }
+  
+  /* updates the visited file modtime */
+  if (modifying_p && (from != to || text_len)
+      /* but only when we do not read the file ourselves */
+      && !get_chars_from_file)
+    Fset_buffer_modtime (binfo->emacs_buffer, Qnil);
+
+  if (!modifying_p)
+    {
+      /* clears the buffer in case we re-use a non-energize buffer */
+      previous_point = 1;
+      Fset_buffer (binfo->emacs_buffer);
+      buffer_delete_range (current_buffer, BUF_BEG (current_buffer),
+			   BUF_Z (current_buffer), 0);
+    }
+  else
+    {
+#if 1
+      display_window = Fget_buffer_window (binfo->emacs_buffer, Qnil, Qnil);
+#endif
+      previous_point = BUF_PT (current_buffer);
+
+#if 1
+      if (!NILP (display_window))
+	display_start =
+	  XINT (Fmarker_position (XWINDOW (display_window)->start));
+#endif
+
+      if (from != to)
+	{
+	  struct buffer *buf = XBUFFER (binfo->emacs_buffer);
+
+	  Fset_buffer (binfo->emacs_buffer);
+	  Fwiden (Fcurrent_buffer ());
+	  if (!NILP (binfo->output_mark)
+	      && marker_position (binfo->output_mark) >= from)
+	    Fset_marker (binfo->output_mark, make_int (from),
+			 binfo->emacs_buffer);
+	  if (((to - from) == text_len) && !get_chars_from_file &&
+	      !string_buffer_compare (text, text_len, buf, from))
+	    /* the new text is the same as the old text, don't clear
+	       the undo list*/
+	    {
+	      Fsetcdr (Fcdr (restore_buffer_state_cons), Qnil);
+	      no_text_deleted = 1;
+	      destroy_all_energize_extents (buf);
+	    }
+	  else
+	    {
+	      /* Do not keep window start if we actually delete text */
+	      should_keep_window_start = 0;
+	      Fset_buffer (binfo->emacs_buffer);
+	      destroy_all_energize_extents (buf);
+	      if (!get_chars_from_file)
+		buffer_delete_range (current_buffer, from, to, 0);
+	    }
+
+	  /* Do not clear the undo list if getting the chars from the file */
+	  if (get_chars_from_file)
+	    Fsetcdr (Fcdr (restore_buffer_state_cons), Qnil);
+	}
+      else if (!text_len && !get_chars_from_file)
+	/* if there is no text and we didn't delete anything,
+	   don't clear the undo_list slot */
+	Fsetcdr (Fcdr (restore_buffer_state_cons), Qnil);
+
+    }
+
+  /* buffer type */
+  if (cbu->flags != 0xff && cbu->flags != binfo->flags)
+    {
+      if (!modifying_p)
+	{
+	  if (cbu->flags == CBUserInput)
+	    {
+	      Lisp_Object buffer_local_variable_name =
+		Qenergize_user_input_buffer_mark;
+	      binfo->output_mark = Fmake_marker ();
+	      Fset_marker (binfo->output_mark, make_int (1),
+			   binfo->emacs_buffer);
+	      /* make sure that this guy doesn't get GC'd out from under us */
+	      Fmake_local_variable (buffer_local_variable_name);
+	      Fput (buffer_local_variable_name, Qpermanent_local, Qt);
+	      Fset (buffer_local_variable_name, binfo->output_mark);
+	      /* Make sure buffer is current after the hook */
+	      Fset_buffer (binfo->emacs_buffer);
+	    }
+	}
+      binfo->flags = cbu->flags;
+    }
+
+  if (get_chars_from_file && text_len != 0)
+    /* I think this is always true, but let's make sure - jwz */
+    abort ();
+
+  if (text_len)
+    {
+      if (!NILP (binfo->output_mark))
+	{
+	  Fset_buffer (binfo->emacs_buffer);
+	  if (XMARKER (binfo->output_mark)->buffer)
+	    Fgoto_char (binfo->output_mark, Fcurrent_buffer ());
+	  else
+	    /* This should not happen */
+	    Fgoto_char (make_int (BUF_ZV (XBUFFER (binfo->emacs_buffer))),
+			Fcurrent_buffer ());
+
+	  if (BUF_PT (current_buffer) <= previous_point)
+	    {
+#if 1
+	      display_start += text_len;
+#endif
+	      previous_point += text_len;
+	    }
+	  buffer_insert_raw_string (current_buffer, text, text_len);
+	  Fset_marker (binfo->output_mark, make_int (BUF_PT (current_buffer)),
+		       binfo->emacs_buffer);
+	}
+      else if (modifying_p)
+	{
+	  Fgoto_char (make_int (from), Fcurrent_buffer ());
+	  if (!no_text_deleted)
+	    buffer_insert_raw_string (current_buffer, text, text_len);
+	}
+      else
+	buffer_insert_raw_string (current_buffer, text, text_len);
+
+      previous_point = XINT (Fgoto_char (make_int (previous_point)),
+			     Fcurrent_buffer ());
+    }
+  else if (get_chars_from_file && !modifying_p)
+    {
+      /* !modifying_p means the buffer is being created - read the text
+	 from the file. */
+      Finsert_file_contents_internal (Fbuffer_file_name (binfo->emacs_buffer),
+				      /* #### coding system not correct */
+				      Qt, Qnil, Qnil, Qnil, Qnil, Qnil));
+    }
+
+  if (!relative_p)
+    extent_offset = 0;
+  else if (!NILP (binfo->output_mark))
+    extent_offset = EnergizePosForBufpos (XINT (Fmarker_position
+						     (binfo->output_mark)),
+					   binfo);
+  else
+    extent_offset = EnergizePosForBufpos (BUF_Z(XBUFFER(binfo->emacs_buffer)),
+					   binfo);
+  
+#if 1
+  if (text_len || !text)
+    hack_window_point (display_window,
+		       make_int (previous_point),
+		       make_int (display_start),
+		       should_keep_window_start,
+		       binfo);
+#endif
+
+
+  /* Classes, generics and extents */
+  /* make sure that we have enough room in the hash table */
+  expand_hashtable (binfo->id_to_object,
+		    cbu->nClass + cbu->nGeneric + cbu->nExtent);
+  read_energize_class_data (conn, cbu->nClass, binfo, modifying_p);
+  read_energize_generic_data (conn, cbu->nGeneric, binfo, modifying_p);
+  read_energize_extent_data (conn, cbu->nExtent, binfo, modifying_p, extent_offset);
+
+  /* Restore the modified bit */
+  Fset_buffer_modified_p (modified_buffer_flag, binfo->emacs_buffer);
+
+  if (get_chars_from_file && modifying_p)
+    {
+      /* modifying_p means the buffer already exists and the extents are
+	 being re-written.  It may be that the file has changed on disk,
+	 and the extents no longer correspond to the text in the buffer,
+	 which would be bad.  So, check the file on disk, and if it has
+	 changed, offer to revert.
+
+	 As this runs lisp code which may prompt the user, and consequently
+	 may accept process output, be careful to do this after we have
+	 finished reading the current request from the Energize connection.
+       */
+     if (NILP (Fverify_visited_file_modtime (binfo->emacs_buffer)))
+       {
+	 call1 (Qenergize_auto_revert_buffer, binfo->emacs_buffer);
+	 hack_window_point (display_window,
+			    make_int (previous_point),
+			    make_int (display_start),
+			    1,
+			    binfo);
+       }
+   }
+
+
+  /* restore modified hooks and globals, and return the previous buffer */
+  UNGCPRO;
+  unbind_to (speccount, Qnil);
+}
+
+
+static void
+cleanly_destroy_all_widgets (int count, LWLIB_ID *ids)
+{
+  /* This just calls lw_destroy_all_widgets, but is careful to make sure that
+     this doesn't cause the frames to shrink.  If one deletes psheets
+     (children of the "control" area of the MainWindow) without first
+     unmanaging the MainWindow, the frame resizes.  So first unmanage all
+     the MainWindows of all applicable frames, then remanage them.  This is
+     nasty, but...
+   */
+  Lisp_Object frmcons, devcons, concons;
+  int i, j;
+
+  if (count == 0)
+    return;
+
+  FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+    {
+      Lisp_Object frame = XCAR (frmcons);
+      struct frame *f = XFRAME (frame);
+
+      if (!FRAME_X_P (f))
+	continue;
+      /* Optimization: only unmanage the MainWindow if this frame is
+	 displaying one of the psheets in question.  (Special casing
+	 the debugger panel as usual...)
+       */
+      for (i = 0; i < count; i++)
+	if (ids [i] == debuggerpanel_sheet)
+	  {
+	    XtUnmanageChild (FRAME_X_CONTAINER_WIDGET (f));
+	    goto next_frame;
+	  }
+	else
+	  for (j = 0; j < FRAME_X_CURRENT_PSHEET_COUNT (f); j++)
+	    if (ids [i] == FRAME_X_CURRENT_PSHEETS (f) [j])
+	      {
+		XtUnmanageChild (FRAME_X_CONTAINER_WIDGET (f));
+		goto next_frame;
+	      }
+    next_frame: ;
+    }
+
+  for (i = 0; i < count; i++)
+    {
+      lw_destroy_all_widgets (ids [i]);
+      if (ids [i] == debuggerpanel_sheet)
+	{
+	  debuggerpanel_sheet = 0;
+	  desired_debuggerpanel_exposed_p = 0;
+	}
+    }
+
+  FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+    {
+      Lisp_Object frame = XCAR (frmcons);
+      struct frame *f = XFRAME (frame);
+
+      if (!FRAME_X_P (f))
+	continue;
+      XtManageChild (FRAME_X_CONTAINER_WIDGET (f));
+    }
+}
+
+
+/* kill an Energize buffer */
+static void
+forget_buffer (BufferInfo *binfo)
+{
+  int i;
+  Lisp_Object buffer = binfo->emacs_buffer;
+
+  remove_buffer_info (binfo->id, buffer, binfo->editor);
+  Venergize_buffers_list = Fdelq (buffer, Venergize_buffers_list);
+
+  /* if there was an associated frame */
+  if (!NILP (binfo->frame))
+    Fdelete_frame (binfo->frame, Qt);
+
+  if (binfo->n_p_sheets > 0)
+    {
+      /* Also delete the dialog boxes associated with the buffer. */
+      cleanly_destroy_all_widgets (binfo->n_p_sheets,
+				   (LWLIB_ID *) binfo->p_sheet_ids);
+    }
+
+  free_buffer_info (binfo);
+
+  XBUFFER (buffer)->undo_list = Qnil;
+  /* flush the buffer SOE before flushing the extents */
+  free_buffer_cached_stack (XBUFFER (buffer));
+  XBUFFER (buffer)->extents = Qnil;
+}
+
+/********************** Request-related utilities ************************/
+
+/* outputs a single extent in the connection buffer */
+static void
+write_energize_extent_data (Connection *conn, Energize_Extent_Data *ext,
+			    unsigned int start, unsigned int end)
+{
+  switch (ext->extentType)
+    {
+    case CEAttribute:
+      CWriteExtent (conn, CEAttribute, ext->id, start, end,
+		    (EId)ext->u.attr.attrValue);
+      break;
+
+    case CEAbbreviation:
+      CWriteExtent (conn, CEAbbreviation, ext->id, start, end,
+		    (EId)ext->u.abbrev.isOpened);
+      break;
+
+    case CEGeneric:
+      CWriteExtent (conn, CEGeneric, ext->id, start, end, 0);
+      break;
+
+    case CEWriteProtect:
+      CWriteExtent (conn, CEWriteProtect, ext->id, start, end, 0);
+      break;
+    }
+}
+
+/* Function called by map_extents in SaveBufferToEnergize. Outputs the
+   extents for the extents corresponding to Energize objects, and
+   increments the n_extents count. */
+
+static int
+write_energize_extent_data_mapper (EXTENT extent, void *arg)
+{
+  binfo_and_n_extents *bane = (binfo_and_n_extents*)arg;
+  Lisp_Object extent_obj;
+  Energize_Extent_Data *ext;
+
+  XSETEXTENT (extent_obj, extent);
+  ext = extent_to_data (extent_obj);
+  if (ext)
+    {
+      Bufpos first = XINT (Fextent_start_position (extent_obj));
+      Bufpos last = XINT (Fextent_end_position (extent_obj));
+      write_energize_extent_data (bane->binfo->editor->conn, ext,
+				  EnergizePosForBufpos (first, bane->binfo),
+				  EnergizePosForBufpos (last, bane->binfo));
+      bane->n_extents += 1;
+    }
+  return 0;
+}
+
+/* Sends a BufferSaved request to energize for binfo */
+static void
+write_energize_buffer_data (BufferInfo *binfo)
+{
+  Connection *conn = binfo->editor->conn;
+  EId bufferId = binfo->id;
+  CBuffer *cbu;
+  CEditorRequest *req;
+  struct buffer *cur_buff = current_buffer;
+  int speccount = specpdl_depth ();
+  Lisp_Object file_name;
+
+  binfo_and_n_extents bane;
+
+  /* selects the buffer as current */
+  Fset_buffer (binfo->emacs_buffer);
+
+  /* write header */
+  cbu = CWriteBufferSavedHeader (conn);
+  cbu->bufferId = bufferId;
+  cbu->flags = 0;
+  cbu->nClass = 0;
+  cbu->nGeneric = 0;
+
+  /* file name */
+  file_name = current_buffer->filename;
+  if (STRINGP (file_name))
+    CWriteVstring0 (conn, string_data (XSTRING (file_name)));
+  else
+    CWriteVstring0 (conn, "");
+  CWriteVstring0 (conn, "");	/* directory name */
+  CWriteVstring0 (conn, "");	/* buffer name */
+
+  /* write the text */
+#ifndef ENERGIZE_V2_HEADERS
+  if (binfo->flags & CBFileYourself)
+    {
+      /* Only the 0.10+ protocol will ask us to write the file directly. */
+      Lisp_Object start;
+      Lisp_Object end;
+      XSETINT (start, BUF_BEG (current_buffer));
+      XSETINT (end, BUF_Z (current_buffer));
+      Fwrite_region_internal (start, end,
+			      Fbuffer_file_name (binfo->emacs_buffer),
+			      /* #### coding system not correct */
+			      Qnil, Qt, Qnil);
+      CNeedOutputSize (conn, 9);
+      CWriteVstringLen (conn, NULL, 0);
+    }
+  else
+#endif /*  ENERGIZE_V2_HEADERS */
+    {
+      Lisp_Object string = make_string_from_buffer (current_buffer,
+						    BUF_BEG (current_buffer),
+						    BUF_Z (current_buffer));
+      CNeedOutputSize (conn, string_length (XSTRING (string)) + 9);
+      CWriteVstringLen (conn, string_data (XSTRING (string)),
+			string_length (XSTRING (string)));
+    }
+
+  /* write the extents */
+  bane.binfo = binfo;
+  bane.n_extents = 0;
+
+  /* Only write the extents when not filing ourselves */
+#ifndef ENERGIZE_V2_HEADERS
+  if (!(binfo->flags & CBFileYourself))
+#endif
+    {
+      map_extents (BUF_BEG (current_buffer), BUF_Z (current_buffer),
+		   write_energize_extent_data_mapper, &bane,
+		   binfo->emacs_buffer, 0, ME_END_CLOSED);
+      
+    }
+
+  /* update nextent in request's header */
+  req = (CEditorRequest *)conn->header;
+  req->buffersaved.buffer.nExtent = bane.n_extents;
+  CWriteLength (conn);
+  CWriteRequestBuffer (conn);
+
+  /* sets the flags so that we will warn Energize about more modifications */
+  binfo->modified_state = 0;
+
+  /* Mark the buffer as non editable so that we will ask Energize about it
+     before modifying it again */
+  binfo->editable = 0;
+
+  /* restores the buffer as current */
+  set_buffer_internal (cur_buff);
+  unbind_to (speccount, Qnil);
+}
+
+static unsigned long
+energize_extent_data_id (Energize_Extent_Data *ext)
+{
+  return ext ? ext->id : 0;
+}
+
+
+/********************** Menu ("keywords") operations **********************/
+
+static int
+something_answered_p (void* arg)
+{
+  struct reply_wait* rw = (struct reply_wait*)arg;
+  return rw->answered_p || !energize_connection || !energize_connection->conn;
+}
+
+
+static void
+push_wait (struct reply_wait* rw)
+{
+  rw->next = global_reply_wait;
+  global_reply_wait = rw;
+}
+
+static Lisp_Object
+remove_wait (Lisp_Object obj)
+{
+  struct reply_wait* gw;
+  struct reply_wait* previous;
+  struct reply_wait* rw = (struct reply_wait *) get_opaque_ptr (obj);
+
+  for (previous = 0, gw = global_reply_wait;
+       gw != rw;
+       previous = gw, gw = gw->next);
+  if (previous)
+    previous->next = gw->next;
+  else
+    global_reply_wait = gw->next;
+  return Qnil;
+}
+
+static struct reply_wait*
+find_wait_reply (int serial)
+{
+  struct reply_wait* gw;
+  for (gw = global_reply_wait; gw && gw->serial != serial; gw = gw->next);
+  return gw;
+}
+
+
+static int
+wait_for_reply (struct reply_wait* rw)
+{
+  int speccount = specpdl_depth ();
+  rw->answered_p = 0;
+  push_wait (rw);
+  record_unwind_protect (remove_wait, make_opaque_ptr (rw));
+  wait_delaying_user_input (something_answered_p, rw);
+  unbind_to (speccount, Qnil);
+  return rw->answered_p;
+}
+
+/* gets the menu for the buffer/extent pair at the head of the request buffer.
+   returns the propose choice request if succeeds, nil otherwise (kernel
+   connection closed, or not connected)
+ */
+
+static Lisp_Object
+get_energize_menu (Lisp_Object buffer, Lisp_Object extent_obj, int selection_p,
+		   Lisp_Object only_name)
+{
+  Connection*	conn;
+  EId	buffer_id;
+  EId	extent_id;
+  Lisp_Object result;
+  struct reply_wait rw;
+  struct gcpro gcpro1, gcpro2;
+
+  if (!get_energize_connection_and_buffer_id (buffer,
+					      (void **) &conn,
+					      (long *) &buffer_id))
+    return Qnil;
+
+  if (EXTENTP (extent_obj))
+    extent_id = energize_extent_data_id (extent_to_data (extent_obj));
+  else
+    extent_id = 0;
+
+  CWriteQueryChoicesRequest (conn, buffer_id, extent_id);
+  conn->header->data =
+    selection_p ? CEChasCharSelection | CEChasObjectSelection : 0;
+  conn->header->serial = ++request_serial_number;
+  CWriteRequestBuffer (conn);
+
+  /* wait for the acknowledge */
+  rw.serial = request_serial_number;
+  rw.objectId = buffer_id;
+  rw.genericId = extent_id;
+  rw.menu_result = Qnil;
+  rw.only_name = only_name;
+
+  GCPRO2 (rw.menu_result, rw.only_name);
+  wait_for_reply (&rw);
+  result = rw.menu_result;
+  UNGCPRO;
+  return result;
+}
+
+
+static void
+execute_energize_menu (Lisp_Object buffer, Energize_Extent_Data* ext,
+		       char* name, EId item_id, EId flags,
+		       Lisp_Object selection, Lisp_Object no_confirm)
+{
+  Connection*	conn;
+  EId	buffer_id;
+  EId	extent_id;
+  BufferInfo*	binfo;
+  struct reply_wait rw;
+
+  if (!get_energize_connection_and_buffer_id (buffer, (void**)&conn,
+					      (long*)&buffer_id))
+    return;
+
+  extent_id = energize_extent_data_id (ext);
+
+  if ((flags & CKBuffer) && !NILP (Fbuffer_modified_p (buffer)))
+    {
+      /* saves buffer if requested and needed */
+      binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
+      if (binfo)
+	write_energize_buffer_data (binfo);
+    }
+
+  CWriteExecuteChoicesRequest (conn, buffer_id, extent_id, item_id, 0, 0);
+  /* send the menu name */
+  if (energize_connection->minor >= 7)
+    CWriteVstring0 (conn, name);
+  conn->header->serial = ++request_serial_number;
+  conn->header->data = 0;
+  if (STRINGP (selection))
+    {
+      conn->header->data |= CEChasCharSelection;
+      CWriteVstringLen (conn, string_data (XSTRING (selection)),
+			string_length (XSTRING (selection)));
+    }
+  else if (VECTORP (selection))
+    {
+      int i;
+      EId data;
+      conn->header->data |= CEChasObjectSelection;
+
+      /* writes the length */
+      data = vector_length (XVECTOR (selection));
+      CWrite (conn, EId, &data);
+
+      /* writes the elements */
+      for (i = 0; i < vector_length (XVECTOR (selection)); i++)
+	{
+	  if (CONSP (vector_data (XVECTOR (selection)) [i]))
+	    data = lisp_to_word (vector_data (XVECTOR (selection)) [i]);
+	  else
+	    data = XINT (vector_data (XVECTOR (selection)) [i]);
+	  CWrite (conn, EId, &data);
+	}
+    }
+  else if (CONSP (selection))
+    {
+      Lisp_Object type = Fcar (selection);
+      Lisp_Object value = Fcdr (selection);
+      if (EQ (type, intern ("ENERGIZE_OBJECT"))
+	  && STRINGP (value))
+	{
+	  conn->header->data |= CEChasObjectSelection;
+	  CWriteN (conn, char, string_data (XSTRING (value)),
+		   string_length (XSTRING (value)));
+	}
+    }
+  else if (!NILP (selection))
+    error ("unrecognized energize selection");
+  
+  if (!NILP (no_confirm))
+    conn->header->data |= CECnoConfirm;
+  CWriteLength (conn);
+  CWriteRequestBuffer (conn);
+
+  /* wait for the acknowledge */
+  rw.serial = request_serial_number;
+  rw.objectId = buffer_id;
+  rw.genericId = extent_id;
+  rw.itemId = item_id;
+  rw.message = 0;
+
+  if (wait_for_reply (&rw) && !rw.status)
+    {
+      char message [128];
+      if (energize_connection && energize_connection->conn)
+	sprintf (message, IDENTITY_CRISIS " command failed: %.80s",
+		 (rw.message ? rw.message : "(null)"));
+      else
+	sprintf (message, "Connection to " IDENTITY_CRISIS " was closed.");
+      if (rw.message)
+	xfree (rw.message);
+      error (message);
+    }
+  else
+    {
+      if (rw.message)
+	xfree (rw.message);
+      if (!energize_connection)
+	error ("Connection to " IDENTITY_CRISIS " was closed.");
+    }
+ }
+
+/* Returns a list of vectors representing the menu choices.  Next request
+   in connection must be a ProposeChoices.  The list is
+   (buffer extent <item1> ... <itemN>).	 <itemI> is (name id1 id2 flags).
+   Idi is (high .  low).  We build the list in reverse order and nreverse
+   it.	If (only_name != 0), we only return the item of named only_name as
+   a vector.  */
+
+static Lisp_Object
+list_choices (Lisp_Object buffer, Lisp_Object extent_obj,
+	      Lisp_Object only_name, CProposeChoicesRequest* creq)
+{
+  Connection *conn;
+  int i;
+  Lisp_Object item_list;
+  Lisp_Object item;
+  struct Lisp_Vector *v;
+  struct gcpro gcpro1, gcpro2, gcpro3;
+  CChoice *choice;
+  ReqLen name_length;
+  char *name;
+  char *arg_name;
+
+  if (energize_connection && energize_connection->conn)
+    conn = energize_connection->conn;
+  else
+    return Qnil;
+
+  if (!creq || creq->head.reqType != ProposeChoicesRType)
+    {
+      CSkipRequest (conn);
+      return Qnil;
+    }
+
+  item = Qnil;
+  item_list = Qnil;
+
+  GCPRO3 (only_name, item_list, item);
+
+  for (i = 0; i < (int)(creq->nChoices); i++)
+    {
+      choice = CGet (conn, CChoice);
+      name = CGetVstring (conn, &name_length);
+      if (!name_length)
+	continue;
+
+      /* the argument, if passed, is another string after the NUL (!)
+       * this is a quick hack to provide cheap arguments to menus entries */
+      arg_name = strchr (name, 0240);
+      if (arg_name)
+	{
+	  *arg_name= 0;
+	  arg_name += 1;
+	}
+
+      if (!NILP (only_name))
+	{
+	  if (!strcmp ((char*) string_data (XSTRING (only_name)), name))
+	    {
+	      if (NILP (item))
+		{
+		  item = make_vector (5, Qnil);
+		  v = XVECTOR (item);
+		  v->contents [0] = only_name;
+		}
+	      v->contents [1] = word_to_lisp (choice->choiceId);
+	      v->contents [2] = Qnil;
+	      v->contents [3] = make_int (choice->flags);
+	      v->contents [4] = arg_name ? build_string (arg_name) : Qnil;
+	    }
+	}
+      else
+	{
+	  item = make_vector (5, Qnil);
+	  v = XVECTOR (item);
+	  v->contents [0] = build_string (name);
+	  v->contents [1] = word_to_lisp (choice->choiceId);
+	  v->contents [2] = Qnil;
+	  v->contents [3] = make_int (choice->flags);
+	  v->contents [4] = arg_name ? build_string (arg_name) : Qnil;
+	  item_list = Fcons (item, item_list); /* pushes in the list */
+	}
+    }
+
+  if (NILP (only_name))
+    item_list = Fcons (buffer, Fcons (extent_obj, Fnreverse (item_list)));
+  UNGCPRO;
+
+  return NILP (only_name) ? item_list : item;
+}
+
+DEFUN ("energize-list-menu", Fenergize_list_menu,
+       Senergize_list_menu, 3, 4, 0 /*
+Request the set of menu options from the Energize server that are
+appropriate to the buffer and the extent.  Extent can be (), in which case
+the options are requested for the whole buffer.	 Selection-p tells
+if the selection is available on the dislpay emacs is using. 
+Returns the options as
+a list that can be passed to energize-activate-menu.  Items
+in the list can also be passed to energize-execute-menu-item.
+The list is (buffer extent or () <item1> ... <itemN>).
+where <itemI> is (name id1 id2 flags); idI is (high . low).
+If optional argument only-name is provided only the item with name only-name
+is returned, or () if no such item exists.
+*/ )
+     (buffer, extent_obj, selection_p, only_name)
+     Lisp_Object buffer, extent_obj, selection_p, only_name;
+{
+  Lisp_Object res;
+  CHECK_BUFFER (buffer);
+
+  if (!energize_connection || !energize_connection->conn) return Qnil;
+
+  if (!NILP (only_name))
+    CHECK_STRING (only_name);
+
+  res = get_energize_menu (buffer, extent_obj, !NILP (selection_p),
+			   only_name);
+  notify_delayed_requests ();
+  return res;
+}
+
+DEFUN ("energize-execute-menu-item", Fenergize_execute_menu_item,
+       Senergize_execute_menu_item, 3, 5, 0 /*
+Item is a vector received by energize-list-menu.  Sends a request to
+execute the code associated to this menu inside the Energize server.
+Optional fourth argument is a string or a vector to be used as the selection
+for entry disabled because they need the selection.
+Optional fifth argument, if non NIL, tells Energize to not request 
+confirmation before executing the command.
+*/ )
+(buffer, extent_obj, item, selection, no_confirm)
+Lisp_Object buffer, extent_obj, item, selection, no_confirm;
+{
+  struct Lisp_Vector *v;
+
+  if (!energize_connection || !energize_connection->conn) return Qnil;
+
+  CHECK_BUFFER (buffer);
+  CHECK_VECTOR (item);
+  v = XVECTOR (item);
+
+  if (vector_length (v) != 4)
+    error ("Bad menu item to energize-execute-menu-item");
+
+  /* ignore the flags for now */
+  execute_energize_menu (buffer, extent_to_data (extent_obj),
+			 (char*)string_data (XSTRING (v->contents [0])),
+			 lisp_to_word (v->contents [1]),
+			 XINT (v->contents [3]),
+			 selection,
+			 no_confirm);
+
+  return Qt;
+}
+
+DEFUN ("energize-execute-command-internal", Fenergize_execute_command_internal,
+       Senergize_execute_command_internal, 3, 5, 0 /*
+Command is a string naming an energize command.	 Sends a request to
+execute this command inside the Energize server.
+Optional fourth argument is a string or a vector to be used as the selection.
+Optional fifth argument, if non NIL, tells Energize to not request 
+confirmation before executing the command.
+
+See also 'energize-list-menu'.
+*/ )
+     (buffer, extent_obj, command, selection, no_confirm)
+     Lisp_Object buffer, extent_obj, command, selection, no_confirm;
+{
+  if (!energize_connection || !energize_connection->conn) return Qnil;
+
+  CHECK_BUFFER (buffer);
+  CHECK_STRING (command);
+
+  execute_energize_menu (buffer, extent_to_data (extent_obj),
+			 (char*)string_data (XSTRING (command)), 0, 0, selection,
+			 no_confirm);
+
+  return Qt;
+}
+
+/********************************* kill buffer interface ****************/
+
+DEFUN ("energize-buffer-type-internal",
+       Fenergize_buffer_type, Senergize_buffer_type,
+       1, 1, 0 /*
+Return a symbol denoting the buffer type if buffer is an Energize
+buffer, else it returns NIL.
+*/ )
+	(buffer)
+	Lisp_Object buffer;
+{
+  if (!energize_connection) return Qnil;
+
+  CHECK_BUFFER (buffer);
+  return get_buffer_type_for_emacs_buffer (buffer, energize_connection);
+}
+
+DEFUN ("set-energize-buffer-type-internal",
+       Fset_energize_buffer_type_internal,
+       Sset_energize_buffer_type_internal, 2, 2, 0 /*
+Return the type symbol which is the new buffer-type, if the buffer is
+an Energize buffer and the type is non-NIL symbol, else it returns NIL.
+*/ )
+   (buffer, type)
+    Lisp_Object buffer, type;
+{
+  BufferInfo *binfo;
+
+  if (!energize_connection || (NILP (type))) return Qnil;
+
+  CHECK_BUFFER (buffer);
+  CHECK_SYMBOL (type);
+
+  if (!(binfo =
+	get_buffer_info_for_emacs_buffer (buffer, energize_connection)))
+    return Qnil;
+  else
+    return
+      set_buffer_type_for_emacs_buffer (buffer, energize_connection, type);
+}
+
+DEFUN ("energize-buffer-p", Fenergize_buffer_p, Senergize_buffer_p, 1, 1, 0 /*
+Whether buffer is an Energize buffer.
+*/ )
+  (buffer)
+  Lisp_Object buffer;
+{
+  BufferInfo *binfo;
+
+  if (!energize_connection) return Qnil;
+
+  CHECK_BUFFER (buffer);
+  if (!(binfo =
+	get_buffer_info_for_emacs_buffer (buffer, energize_connection)))
+    return Qnil;
+  else
+    return Qt;
+}
+
+DEFUN ("energize-buffer-id", Fenergize_buffer_id, Senergize_buffer_id, 1, 1, 0 /*
+Return (high . low) if buffer is an Energize buffer, otherwise nil.
+*/ )
+     (buffer)
+     Lisp_Object buffer;
+{
+  BufferInfo *binfo;
+
+  if (!energize_connection) return Qnil;
+
+  CHECK_BUFFER (buffer);
+  if (!(binfo =
+	get_buffer_info_for_emacs_buffer (buffer, energize_connection)))
+    return Qnil;
+  else
+    return word_to_lisp (binfo->id);
+}
+
+DEFUN ("energize-request-kill-buffer", Fenergize_request_kill_buffer,
+       Senergize_request_kill_buffer, 1, 1, 0 /*
+Sends a request to energize for killing buffer.
+*/ )
+   (buffer)
+   Lisp_Object buffer;
+{
+  BufferInfo *binfo;
+
+  if (!energize_connection) return Qnil;
+
+  CHECK_BUFFER (buffer);
+  if (!(binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection)))
+    return Qnil;
+
+  /* Tell Energize about it if connected */
+  if (energize_connection->conn)
+    {
+      CWriteKillBufferHeader (energize_connection->conn, binfo->id);
+      CWriteRequestBuffer (energize_connection->conn);
+    }
+
+  /* Clears the internal state */
+  forget_buffer (binfo);
+
+  return Qnil;
+}
+
+/******************** Handle requests from the kernel *********************/
+
+#ifdef EMACS_BTL
+#include "cadillac-btl-extern.h"
+#endif
+
+/* turn logging on or off, etc. */
+static void
+handle_logging_request (Editor *editor, CLoggingRequest *creq)
+     /* I'm a lumberjack and I'm ok... */
+{
+  ReqLen name_len;
+  char* data_filename = CGetVstring (editor->conn, &name_len);
+
+#ifdef EMACS_BTL
+  {
+    char *execname =
+      (STRINGP (Vinvocation_directory))?
+	((char *) string_data (XSTRING (Vinvocation_directory))):0;
+
+    switch (creq->type)
+      {
+      case CLRInitBTL:
+	cadillac_terminate_logging (); /* #### rename me */
+	cadillac_initialize_backtrace_logging /* #### rename me */
+	  (data_filename, execname, (long) creq->limit, (long) creq->interval);
+	break;
+
+      case CLRInitPCL:
+	cadillac_terminate_logging (); /* #### rename me */
+	cadillac_initialize_pc_logging /* #### rename me */
+	  (data_filename, execname, (long) creq->limit, (long) creq->interval);
+	break;
+
+      case CLRStart:
+	cadillac_start_logging (); /* #### rename me */
+	break;
+
+      case CLRStop:
+	cadillac_stop_logging (); /* #### rename me */
+	break;
+
+      case CLRTerminate:
+	cadillac_terminate_logging (); /* #### rename me */
+	break;
+
+      case CLRSetLogSignal:
+	cadillac_set_log_signal (creq->signal); /* #### rename me */
+	break;
+
+      default:
+	error ("Bad logging request type %d", creq->type);
+      }
+  }
+#else
+  message ("Logging request, but no such code in image.");
+#endif
+}
+
+
+
+/* creates a new buffer */
+static void
+handle_new_buffer_request (Editor *editor, CNewBufferRequest *creq)
+{
+  read_energize_buffer_data (editor->conn, &creq->buffer, editor, 0, 0,
+			     creq->transientId, 0);
+  if (!NILP (Venergize_create_buffer_hook))
+    {
+      CBuffer *cbu = &creq->buffer;
+      BufferInfo *binfo = get_buffer_info_for_id (cbu->bufferId, editor);
+      Lisp_Object buffer;
+      if (binfo)
+	{
+	  Lisp_Object prev_frame;
+	  buffer = binfo->emacs_buffer;
+	  if (!NILP (binfo->frame))
+	    {
+	      prev_frame = Fselected_frame (Qnil);
+	      Fselect_frame (binfo->frame);
+	    }
+	  va_run_hook_with_args (Qenergize_create_buffer_hook, 1, buffer);
+	  if (!NILP (binfo->frame))
+	    Fselect_frame (prev_frame);
+	}
+    }
+}
+
+/* Modifies the contents of a buffer */
+static void
+handle_modify_buffer_request (Editor *editor, CModifyBufferRequest *creq)
+{
+  read_energize_buffer_data (editor->conn, &creq->newData, editor,
+			     creq->startPosition, creq->endPosition,
+			     0, creq->head.data);
+}
+
+static void
+make_buffer_and_extent_visible (Lisp_Object list, Lisp_Object go_there)
+{
+  call2 (Qenergize_make_many_buffers_visible, list, go_there);
+}
+
+/* pops a buffer and scroll to a extent: calls to lisp */
+static void
+handle_ensure_visible_request (Editor *editor, CEnsureVisibleRequest *creq)
+{
+  BufferInfo *binfo;
+  Energize_Extent_Data *ext;
+  Lisp_Object buffer_extent_list;
+  struct gcpro gcpro1;
+
+  buffer_extent_list = Qnil;
+  GCPRO1 (buffer_extent_list);
+
+  binfo = get_buffer_info_for_id (creq->bufferId, editor);
+  if (!binfo)
+    {
+      message ("EnsureVisibleRequest: unknown buffer");
+      goto finished;
+    }
+
+  if (!NILP (binfo->frame))
+    {
+      /* ignore ensure visible for postit note buffers */
+      goto finished;
+    }
+
+  if (creq->extentId)
+    {
+      ext = get_extent_data (creq->extentId, binfo);
+      if (!ext)
+	message ("EnsureVisibleRequest: ignoring unknown extent");
+    }
+  else
+    ext = 0;
+
+  buffer_extent_list = Fcons (ext ? data_to_extent (ext) : Qnil, Qnil);
+  buffer_extent_list = Fcons (binfo->emacs_buffer, buffer_extent_list);
+
+  make_buffer_and_extent_visible (buffer_extent_list, creq->head.data ? Qt : Qnil);
+
+ finished:
+  CSkipRequest (editor->conn);
+  UNGCPRO;
+}
+
+static void
+handle_ensure_many_visible_request (Editor *editor,
+				    CEnsureManyVisibleRequest *creq)
+{
+  BufferInfo *binfo;
+  Energize_Extent_Data *ext;
+  Lisp_Object buffer_extent_list;
+  int n;
+  EId buffer_id;
+  EId extent_id;
+  struct gcpro gcpro1;
+
+  buffer_extent_list = Qnil;
+  GCPRO1 (buffer_extent_list);
+
+  for (n = creq->head.data,
+       buffer_id = creq->bufferId,
+       extent_id = creq->extentId;
+       n;
+       n--,
+       buffer_id = n ? *(CGet (editor->conn, EId)) : 0,
+       extent_id = n ? *(CGet (editor->conn, EId)) : 0)
+    {
+      binfo = get_buffer_info_for_id (buffer_id, editor);
+      if (!binfo)
+	{
+	  message ("EnsureManyVisibleRequest: ignoring unknown buffer");
+	  continue;
+	}
+
+      if (!NILP (binfo->frame))
+	{
+	  /* silently ignore ensure visible for postit note buffers */
+	  continue;
+	}
+
+      if (extent_id)
+	{
+	  ext = get_extent_data (extent_id, binfo);
+	  if (!ext)
+	    message ("EnsureManyVisibleRequest: ignoring unknown extent");
+	}
+      else
+	ext = 0;
+
+      /* cons in reverse order and reverse the list before
+	 calling make_buffer_and_extent_visible */
+      buffer_extent_list = Fcons (binfo->emacs_buffer, buffer_extent_list);
+      buffer_extent_list = Fcons (ext ? data_to_extent (ext) : Qnil,
+				  buffer_extent_list);
+    }
+  buffer_extent_list = Fnreverse (buffer_extent_list);
+  make_buffer_and_extent_visible (buffer_extent_list, Qt);
+
+  UNGCPRO;
+}
+
+/* Update the cached menus, ie update the menubar for now. */
+static void
+handle_propose_choices_request (Editor *editor, CProposeChoicesRequest *req)
+{
+  BufferInfo* binfo;
+  Lisp_Object buffer = Qnil;
+  Lisp_Object extent = Qnil;
+  Lisp_Object choices = Qnil;
+  struct gcpro gcpro1, gcpro2, gcpro3;
+  struct reply_wait* rw;
+
+  GCPRO3 (buffer, extent, choices);
+
+  /* get the buffer */
+  binfo = get_buffer_info_for_id (req->objectId, editor);
+  if (binfo)
+    buffer = binfo->emacs_buffer;
+  else
+    buffer = Qnil;
+
+  /* get the extent */
+  if (binfo && req->genericId)
+    {
+      Energize_Extent_Data* ext = get_extent_data (req->genericId, binfo);
+      if (ext)
+	extent = data_to_extent (ext);
+      else
+	extent = Qnil;
+    }
+  else
+    extent = Qnil;
+
+  /* find if we were waiting for a reply */
+  rw = find_wait_reply (req->head.serial);
+
+  /* handle the request */
+  if (rw && rw->objectId == req->objectId && rw->genericId == req->genericId)
+    {
+      /* It's a reply for a get_energize_menu call */
+      rw->answered_p = True;
+      rw->status = 1;
+      rw->menu_result = list_choices (buffer, extent, rw->only_name, req);
+    }
+  else
+    {
+      /* It's a menu update, call the hook */
+      choices = list_choices (buffer, extent, Qnil, req);
+      va_run_hook_with_args (Qenergize_menu_update_hook, 1, choices);
+    }
+  UNGCPRO;
+}
+
+/* Kills a buffer */
+static void
+unmodify_buffer_and_kill_it (Lisp_Object buffer)
+{
+  int speccount = specpdl_depth ();
+
+  if (!BUFFER_LIVE_P (XBUFFER (buffer)))
+    return;
+
+  Fset_buffer_modified_p (Qnil, buffer);
+
+  /* kill it.  This will call the Energize hook to do the right thing */
+  Fkill_buffer (buffer);
+}
+
+static void
+handle_kill_buffer_request (Editor *editor, CKillBufferRequest *creq)
+{
+  BufferInfo *binfo;
+
+  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
+    {
+      message ("KillBufferVisibleRequest: unregistered buffer");
+      return;
+    }
+
+  unmodify_buffer_and_kill_it (binfo->emacs_buffer);
+}
+
+static void
+handle_remove_extents_request (Editor *editor, CRemoveExtentsRequest *creq)
+{
+  BufferInfo *binfo;
+  int i;
+  EId *ids;
+  Lisp_Object restore_buffer_state_cons;
+  int speccount = specpdl_depth ();
+
+  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
+    {
+      message ("RemoveExtentsRequest: unregistered buffer");
+      CSkipRequest (editor->conn);
+      return;
+    }
+
+  /* enable buffer edits */
+  restore_buffer_state_cons =
+    Fcons (make_opaque_ptr ((void *) creq->bufferId),
+	   Fcons (XBUFFER (binfo->emacs_buffer)->read_only, Qnil));
+
+  record_unwind_protect (restore_buffer_state, restore_buffer_state_cons);
+
+  XBUFFER (binfo->emacs_buffer)->read_only = Qnil;
+
+  /* save old hook values */
+  specbind (Qenergize_buffer_modified_hook, Qnil);
+
+  ids = CGetN (editor->conn, EId, creq->nExtent);
+  for (i = 0; i < creq->nExtent; i++)
+    {
+      Energize_Extent_Data *ext = get_extent_data (ids [i], binfo);
+      if (ext)
+	free_Energize_Extent_Data (ext, binfo, OFT_STANDALONE);
+    }
+
+  /* restore modified hooks and globals */
+  unbind_to (speccount, Qnil);
+}
+
+#ifndef ENERGIZE_V2_HEADERS
+static Lisp_Object
+save_to_energize_unwind (Lisp_Object closure)
+{
+  BITS32 buffer_id = (BITS32) cons_to_long (closure);
+  /* If the buffer ID is not 0, then the call to save-buffer
+     didn't complete normally - so tell Energize the save was aborted. */
+  if (buffer_id)
+    {
+      Editor *editor = energize_connection;
+      if (editor && editor->conn)  /* Maybe the kernel has gone away. */
+	{
+	  CWriteBufferSaveAbortedHeader (editor->conn, buffer_id);
+	  CWriteRequestBuffer (editor->conn);
+	}
+    }
+  return Qnil;
+}
+#endif /*  ENERGIZE_V2_HEADERS */
+
+
+/* handles a request to save a buffer from the kernel */
+static void
+handle_save_buffer_request (Editor *editor, CSaveBufferRequest *creq)
+{
+  BufferInfo *binfo;
+  int speccount = specpdl_depth ();
+  struct gcpro gcpro1;
+  Lisp_Object closure = Qnil;
+
+  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
+    {
+      message ("Server attempt to save a non registered buffer");
+      return;
+    }
+
+  if (!EQ (binfo->emacs_buffer, Fcurrent_buffer ()))
+    {
+      record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
+      Fset_buffer (binfo->emacs_buffer);
+    }
+
+  GCPRO1 (closure);
+  if (creq->head.data == CSExecuteSave)
+    {
+#ifndef ENERGIZE_V2_HEADERS
+      Lisp_Object closure = make_opaque_ptr ((void *) creq->bufferId);
+      record_unwind_protect (save_to_energize_unwind, closure);
+#endif /*  ENERGIZE_V2_HEADERS */
+      call0 (intern ("save-buffer"));
+#ifndef ENERGIZE_V2_HEADERS
+      /* clear out the id to tell the unwind-protect form that the save
+	 completed normally. */
+      set_opaque_ptr (closure, 0);
+#endif /*  ENERGIZE_V2_HEADERS */
+    }
+  else
+    write_energize_buffer_data (binfo);
+
+  UNGCPRO;
+  unbind_to (speccount, Qnil);
+}
+
+static void
+handle_set_modified_flag_request (Editor* editor,
+				  CSetModifiedFlagRequest* creq)
+{
+  BufferInfo *binfo;
+  int speccount = specpdl_depth ();
+
+  if (!(binfo = get_buffer_info_for_id (creq->bufferId, editor)))
+    {
+      message ("Server attempt to set modified flag of a non registered buffer");
+      return;
+    }
+
+  record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
+  specbind (Qenergize_buffer_modified_hook, Qnil);
+
+  /* Only set buffer modified time in older protocols
+     as we handle the file timestamps ourselves now for
+     CBFileYourself buffers. */
+#ifndef ENERGIZE_V2_HEADERS
+  if ((energize_connection->minor < 10) && !(binfo->flags &  CBFileYourself))
+#endif
+    {
+      Fset_buffer_modtime (binfo->emacs_buffer, Qnil);
+    }
+
+  Fset_buffer_modified_p (creq->state ? Qt : Qnil, binfo->emacs_buffer);
+  binfo->modified_state = creq->state;
+  /* Mark the buffer so that we ask permission to Energize when the
+   * user tries to modify it again */
+  binfo->editable = 0;
+  if (!creq->state)
+    mark_all_extents_as_unmodified (binfo);
+  unbind_to (speccount, Qnil);
+}
+
+
+/* handles requests regarding p_sheet associated to buffers */
+static void
+add_in_list_of_ids (int** ids, int* n_ids, int id)
+{
+  if (*n_ids == 0)
+    {
+      *n_ids = 1;
+      *ids = (int*)xmalloc (sizeof (int));
+    }
+  else
+    {
+      *n_ids += 1;
+      *ids = (int*)xrealloc (*ids, sizeof (int) * (*n_ids));
+    }
+  (*ids) [(*n_ids) - 1] = id;
+}
+
+static void
+remove_from_list_of_ids (int** ids, int* n_ids, int id)
+{
+  int i;
+  if (*n_ids)
+    {
+      /* look for id in *ids */
+      for (i = 0; i < (*n_ids) && (*ids) [i] != id; i++);
+      /* shift the remaining ones */
+      for (; i < (*n_ids) - 1; i++)
+	(*ids) [i] = (*ids) [i + 1];
+      /* decrease the count */
+      *n_ids -= 1;
+      /* free array if empty */
+      if (!*n_ids)
+	{
+	  xfree (*ids);
+	  *ids = 0;
+	}
+    }
+}
+
+extern void make_psheets_desired (struct frame *, Lisp_Object);
+
+static void
+handle_buffer_sheet_request (Editor *editor, CSheetRequest *sreq,
+			     EId buffer_id)
+{
+  BufferInfo *binfo;
+  char *name;
+  Connection *conn = editor->conn;
+
+  if (!(binfo = get_buffer_info_for_id (buffer_id, editor)))
+    {
+      message ("Server attempt to use p_sheet in a non registered buffer");
+      CSkipRequest (conn);
+      return;
+    }
+
+  name = CGetVstring (conn, (ReqLen *) 0);
+  switch ((CSheetRSubtype) sreq->head.data)
+    {
+    case CSCreate:
+      lw_register_widget (name, name, sreq->sheetId, NULL, NULL,
+			  handle_sheet_control_change, NULL);
+      add_in_list_of_ids (&binfo->p_sheet_ids, &binfo->n_p_sheets,
+			  sreq->sheetId);
+      if (!strcmp (name, DEBUGGER_PSHEET_NAME))
+	debuggerpanel_sheet = sreq->sheetId;
+      break;
+
+    case CSDelete:
+      remove_from_list_of_ids (&binfo->p_sheet_ids, &binfo->n_p_sheets,
+			       sreq->sheetId);
+      cleanly_destroy_all_widgets (1, &sreq->sheetId);
+      if (sreq->sheetId == debuggerpanel_sheet)
+	{
+	  desired_debuggerpanel_exposed_p = 0;
+	  debuggerpanel_sheet = 0;
+	}
+      break;
+
+    case CSHide:
+      {
+	Lisp_Object frmcons, devcons, concons;
+
+	if (sreq->sheetId == debuggerpanel_sheet)
+	  desired_debuggerpanel_exposed_p = 0;
+	else
+	  FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+	    {
+	      struct frame *frame = XFRAME (Fcar (frmcons));
+	      if (FRAME_X_P (frame))
+		make_psheets_desired (frame, Qnil);
+	    }
+      }
+      break;
+
+    case CSShow:
+      if (sreq->sheetId == debuggerpanel_sheet)
+	desired_debuggerpanel_exposed_p = 1;
+      else
+	{
+	  Lisp_Object frmcons, devcons, concons;
+
+	  FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+	    {
+	      struct frame *frame = XFRAME (Fcar (frmcons));
+	      if (FRAME_X_P (frame))
+		{
+		  struct window *window =
+		    XWINDOW (FRAME_SELECTED_WINDOW (frame));
+		  if (EQ (window->buffer, binfo->emacs_buffer))
+		    make_psheets_desired (frame, binfo->emacs_buffer);
+		}
+	    }
+	}
+      break;
+    }
+}
+
+
+
+/* show busy */
+
+static void
+show_all_menubars_busy (int busy)
+{
+  Lisp_Object frmcons, devcons, concons;
+
+  FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
+    {
+      struct frame *f = XFRAME (XCAR (frmcons));
+      if (FRAME_X_P (f))
+	{
+	  if (FRAME_X_MENUBAR_WIDGET (f))
+	    lw_show_busy (FRAME_X_MENUBAR_WIDGET (f), busy);
+	}
+    }
+}
+
+static void
+handle_show_busy_request (Editor *editor, CGenericRequest *preq)
+{
+  /* call the show busy routine of the	library for the menubar of
+   * all frames */
+  ReqLen len;
+
+  char* why = CGetVstring (editor->conn, &len);
+
+  show_all_menubars_busy (preq->head.data);
+  Venergize_kernel_busy = preq->head.data ? Qt : Qnil;
+  va_run_hook_with_args (Qenergize_kernel_busy_hook, 1, build_string (why));
+}
+
+/* This request creates, destroys, raises, or lowers a psheet or dialog box.
+ */
+static void
+handle_sheet_request (Connection* conn, CSheetRequest* sreq, Widget parent)
+{
+  char* name = CGetVstring (conn, NULL);
+
+  switch ((CSheetRSubtype)sreq->head.data)
+    {
+    case CSCreate:
+      lw_create_widget (name, name, sreq->sheetId, 0, parent,
+			!sreq->bufferId, 0, handle_sheet_control_change, 0);
+      break;
+    case CSDelete:
+      cleanly_destroy_all_widgets (1, &sreq->sheetId);
+      break;
+
+    case CSShow:
+      lw_pop_up_all_widgets (sreq->sheetId);
+      break;
+
+    case CSHide:
+      lw_pop_down_all_widgets (sreq->sheetId);
+      break;
+    }
+}
+
+/* This request changes slot values in the psheets/dialog boxes. */
+static void
+handle_set_control_request (Connection* conn, CGenericRequest* creq)
+{
+  CSetControlRequest* sreq = &creq->setcontrol;
+  widget_value val;
+  widget_value* contents;
+
+  unsigned long i;
+  unsigned long n = sreq->nChoices;
+
+  if (n > 0)
+    {
+      contents = (widget_value *) xmalloc (n * sizeof (widget_value));
+      memset (contents, 0, (n * sizeof (widget_value)));
+    }
+  else
+    contents = NULL;
+  memset (&val, 0, sizeof (val));
+  val.name = CGetVstring (conn, NULL);
+  val.enabled = !(sreq->flags & CKInactive);
+  val.selected = !!(sreq->flags & CKSelected);
+  val.change = VISIBLE_CHANGE;
+  val.contents = contents;
+
+  for (i = 0; i < n; i++)
+    {
+      widget_value* cur = &contents [i];
+      CChoice* choice = CGet (conn, CChoice);
+      cur->name = CGetVstring (conn, NULL);
+      cur->value = cur->name;
+      cur->key = NULL;
+      cur->enabled = !(choice->flags & CKInactive);
+      cur->selected = !!(choice->flags & CKSelected);
+      cur->change = VISIBLE_CHANGE;
+      cur->contents = NULL;
+      cur->call_data = NULL;
+      cur->next = i == n - 1 ? NULL : &contents [i + 1];
+      cur->toolkit_data = NULL;
+      if ((i == 0 && n == 1) || cur->selected)
+	{
+	  val.value = cur->name;
+	  if (!*val.value)
+	    val.value = NULL;
+	}
+    }
+  lw_modify_all_widgets (sreq->sheetId, &val, True);
+
+  if (contents)
+    xfree (contents);
+}
+
+static void
+handle_sheet_control_change (Widget widget, EId sheet_id, void* arg)
+{
+  Connection*	conn;
+  widget_value* val;
+  widget_value* cur;
+  widget_value* this_val = NULL;
+  widget_value* cancel = NULL;
+  char*		this_name;
+  int delete_window_p = (((int) arg) == -1);
+
+
+  if (!energize_connection)
+    return;
+
+  conn = energize_connection->conn;
+  if (!conn)
+    return;
+
+  this_name = XtName (widget);
+  val = lw_get_all_values (sheet_id);
+
+  if (delete_window_p)
+    /* Complete and utter kludge.  If this dbox was dismissed with the
+       WM close box (WM_DELETE_WINDOW, meaning the widget was destroyed)
+       then we look for a likely "cancel" button and pretend the user
+       clicked on that.	 Really the protocol should be extended for this.
+     */
+    for (cur = val; cur; cur = cur->next)
+      {
+	char *v = cur->value;
+	if (v &&
+	    ((strlen (v) >= 6 && !strncmp (v, "cancel", 6)) ||
+	     (strlen (v) >= 5 && !strncmp (v, "abort", 5))))
+	  cancel = cur;
+      }
+
+  /* first send all the edited widgets */
+  for (cur = val; cur; cur = cur->next)
+    {
+      /* do not send the widget that ran the callback */
+      if (!strcmp (cur->name, this_name))
+	this_val = cur;
+      else if (cur == cancel)
+	;
+      /* send the edited widgets */
+      else if (cur->edited)
+	{
+	  char* value = cur->value;
+	  unsigned int flags = 0;
+
+	  if (!cur->enabled)
+	    flags |= CKInactive;
+	  if (cur->selected)
+	    flags |= CKSelected;
+
+	  /* the kernel is brain dead and expect "1" and "0" as values
+	     for the checkbox objects.	So if value is NULL, make it be "0"
+	     or "1" depending on the selected state.  This is until we fix
+	     the kernel. */
+	  if (!value)
+	    value = cur->selected ? "1" : "0";
+
+	  CWriteSetControlRequest (conn, sheet_id, 0, cur->name, 1);
+	  CWriteChoice (conn, 0, flags, value, 0);
+	  CWriteLength (conn);
+	}
+    }
+
+  if (delete_window_p && !this_val)
+    {
+      this_val = cancel;
+/*	if (! this_val) abort (); */
+    }
+
+  /* Then send the widget that ran the callback */
+  if (this_val)
+    {
+      CWriteSetControlRequest (conn, sheet_id, 0, this_val->name, 1);
+      CWriteChoice (conn, 0, 0, this_val->value, 0);
+      CWriteLength (conn);
+      CWriteRequestBuffer (conn);
+    }
+}
+
+/******************** Low level connection stuff ************************/
+static void
+add_in_connection_input_buffer (Connection *conn, char *s, int l)
+{
+  /* Should be in connection.c */
+  if (conn->inread >= conn->infill)
+    conn->inread = conn->infill = conn->inbuffer;
+
+  CNeedInputSize (conn, l);
+  memcpy (conn->infill, s, l);
+  conn->infill += l;
+}
+
+static Lisp_Object
+process_one_energize_request (void)
+{
+  Editor *editor = energize_connection;
+  CEditorRequest *req;
+  int res = 0;
+
+  if (!editor) return make_int (res);
+
+  if (!editor->conn)
+    {
+      close_energize_connection ();
+      return make_int (res);
+    }
+
+  req = CReadEditorRequest (editor->conn);
+  if (!req)
+    {
+      switch (errno)
+	{
+	case EWOULDBLOCK:
+	  /* message ("ProcessEnergizeRequest: internal error EWOULDBLOCK"); */
+	  res = -1;
+	  break;
+
+	case 0:
+	case ECONNRESET:
+	  message ("Connection to " IDENTITY_CRISIS " was closed.");
+	  close_energize_connection ();
+	  break;
+
+	default:
+	  message
+	    ("System error on connection to " IDENTITY_CRISIS ", closing.");
+	  close_energize_connection ();
+	  break;
+	}
+    }
+  else
+    {
+      res = 1;
+      switch (req->head.reqType)
+	{
+	case RefuseConnectionRType:
+	  message (IDENTITY_CRISIS " connection refused");
+	  close_energize_connection ();
+	  break;
+
+	case AcceptConnectionRType:
+	  {
+	    CProtocol* proto = CGet (editor->conn, CProtocol);
+	    editor->major = proto->major;
+	    editor->minor = proto->minor;
+	    message (IDENTITY_CRISIS " connection accepted");
+	    CSkipRequest (editor->conn);
+	  }
+	  break;
+
+	case NewBufferRType:
+	  handle_new_buffer_request (editor, &req->newbuffer);
+	  break;
+
+	case QueryBufferRType:
+	  {
+	    EId buffer_id;
+	    struct reply_wait* rw = find_wait_reply (req->head.serial);
+	    CGetVstring (editor->conn, 0); /* skip directory */
+	    CGetVstring (editor->conn, 0); /* skip file */
+	    buffer_id = *CGet (editor->conn, EId);
+	    if (rw)
+	      {
+		rw->answered_p = 1;
+		rw->status = req->head.data;
+		rw->objectId = buffer_id;
+	      }
+	  }
+	  break;
+
+	case EnsureVisibleRType:
+	  handle_ensure_visible_request (editor, &req->ensurevisible);
+	  break;
+
+	case EnsureManyVisibleRType:
+	  handle_ensure_many_visible_request (editor, &req->ensuremanyvisible);
+	  break;
+
+	case ModifyBufferRType:
+	  handle_modify_buffer_request (editor, &req->modifybuffer);
+	  break;
+
+	case ProposeChoicesRType:
+	  handle_propose_choices_request (editor,
+					  &req->generic.proposechoices);
+	  break;
+
+	case ChoiceExecutedRType:
+	  {
+	    struct reply_wait* rw = find_wait_reply (req->head.serial);
+	    CChoiceExecutedRequest* ce = &req->generic.choiceexecuted;
+	    if (rw)
+	      {
+		rw->answered_p = 1;
+		rw->status = ce->head.data;
+		rw->message = CMakeVstring (editor->conn, 0);
+	      }
+	  }
+	  break;
+
+	case KillBufferRType:
+	  handle_kill_buffer_request (editor, &req->killbuffer);
+	  break;
+
+	case ModifiedBufferRType:
+	  {
+	    struct reply_wait* rw = find_wait_reply (req->head.serial);
+	    if (rw)
+	      {
+		rw->answered_p = 1;
+		if (rw->objectId == req->modifiedbuffer.bufferId)
+		  rw->status = req->modifiedbuffer.state;
+		else
+		  rw->status = CMBufferLocked;
+	      }
+	  }
+	  break;
+
+	case SetModifiedFlagRType:
+	  handle_set_modified_flag_request (editor, &req->setmodifiedflag);
+	  break;
+
+	case RemoveExtentsRType:
+	  handle_remove_extents_request (editor, &req->removeextents);
+	  break;
+
+	case RenumberExtentsRType:
+	  /* HandleDuplicateExtentRequest (editor, req); */
+	  break;
+
+#if 0
+	case DialogRType:
+	  /* HandleDialogRequest (editor, req, CurrentBuffer (editor)); */
+	  break;
+#endif
+
+	case SaveBufferRType:
+	  handle_save_buffer_request (editor, &req->savebuffer);
+	  break;
+
+	case SheetRType:{
+	  EId buffer_id = req->generic.sheet.bufferId;
+	  if (!buffer_id)
+	    buffer_id = buffer_id_of_sheet (req->generic.sheet.sheetId);
+	  if (buffer_id)
+	    handle_buffer_sheet_request (editor, &req->generic.sheet,
+					 buffer_id);
+	  else
+	    {
+	      CSheetRSubtype type = (CSheetRSubtype)req->head.data;
+	      if (type == CSDelete || type ==CSHide)
+		/* #### ??? this does nothing. */
+		Fselect_frame (Fselected_frame (Qnil));
+	      handle_sheet_request (editor->conn, &req->generic.sheet,
+				    FRAME_X_SHELL_WIDGET
+				    (XFRAME (Fselected_frame (Qnil))));
+	    }
+	}
+	  break;
+
+	case SetControlRType:
+	  handle_set_control_request (editor->conn, (CGenericRequest*) req);
+	  break;
+
+	case OpenPostitRType:
+	case KillPostitRType:
+	  message ("Don't know what to do with postit requests.");
+	  break;
+
+	case ShowBusyRType:
+	  handle_show_busy_request (editor, (CGenericRequest*)req);
+	  break;
+
+	case LoggingRType:
+	  handle_logging_request (editor, (CLoggingRequest*)req);
+	  break;
+
+#ifndef ENERGIZE_V2_HEADERS
+	case KernelEventRType:
+	  CSkipRequest (editor->conn);
+	  break;
+#endif
+
+	default:
+	  message("ProcessEnergizeRequest: can't handle request of type %d",
+		req->head.reqType);
+	}
+
+    }
+
+  return make_int (res);
+}
+
+static int inside_process_energize_request_1;
+
+/* this must be called ONLY by unwind_protect in process_energize_request_1 */
+static Lisp_Object
+post_handle_request (Lisp_Object ignored)
+{
+  if (inside_process_energize_request_1 <= 0)
+    abort ();
+  inside_process_energize_request_1--;
+  if (energize_connection && energize_connection->conn)
+    CSkipRequest (energize_connection->conn);
+  return Qnil;
+}
+
+static Lisp_Object
+pop_conn (Lisp_Object arg)
+{
+  Connection *old_conn = (Connection *) get_opaque_ptr (arg);
+  if (! old_conn)
+    abort ();
+  if (! energize_connection)
+    return Qnil;
+  if (energize_connection->conn == old_conn)
+    abort ();
+
+  if (CRequestDelayedP (energize_connection->conn))
+    /* We don't call the CWait* functions any more so this shouldn't happen.
+       But if it does someday, then we need to either copy the remaining
+       bits from new_conn to old_conn, or loop processing requests until
+       new_conn is drained.
+     */
+    abort ();
+
+  DeleteConnection (energize_connection->conn);
+  energize_connection->conn = old_conn;
+
+  return Qnil;
+}
+
+static Lisp_Object
+process_energize_request_1 ()
+{
+  Lisp_Object result;
+  int speccount = specpdl_depth ();
+
+  if (inside_process_energize_request_1)
+    {
+      /* When the energize process filter is called recursively, push a new
+	 connection object.  The read-pointer of the connection buffer could
+	 be in the middle of a request.	 However, we know that the fd itself
+	 is always pointing between requests.  So making a new connection is
+	 a way of skipping past the one request we were in the process of
+	 reading when we allowed process output to be handled recursively.
+       */
+      Connection *old_conn = energize_connection->conn;
+      Connection *new_conn =
+	make_energize_connection ((void *) energize_connection,
+				  old_conn->fdin, old_conn->fdout);
+      energize_connection->conn = new_conn;
+      record_unwind_protect (pop_conn, make_opaque_ptr (old_conn));
+    }
+
+  /* this must come after pop_conn() to get the right connection object */
+  record_unwind_protect (post_handle_request, Qnil);
+
+  inside_process_energize_request_1++;
+
+  result = process_one_energize_request ();
+  notify_delayed_requests ();
+
+  /* decrements inside_process_energize_request_1 and possibly replaces
+     energize_connection->conn with old_conn.
+   */
+  unbind_to (speccount, Qnil);
+
+  return result;
+}
+
+
+/******** Initialize Energize-related state and set up connection ********/
+
+static void
+setup_connection (Editor *ed, unsigned int id1, unsigned int id2)
+{
+  CEditorRequest *req = CWriteEditorRequest (ed->conn, QueryConnectionRType);
+
+  /* these 2 slots are ignored */
+  req->generic.queryconnection.major = 0;
+  req->generic.queryconnection.minor = 0;
+
+  req->generic.queryconnection.cadillacId1 = id1;
+  req->generic.queryconnection.cadillacId2 = id2;
+  req->generic.queryconnection.nProtocols = 1;
+  /* first numerical arg is major protocol number, second is minor */
+  CWriteProtocol (ed->conn, 0, 10, "editor");
+  CWriteLength (ed->conn);
+  CWriteRequestBuffer (ed->conn);
+}
+
+/* this is used as the readMethod of the energize connection, so that
+   the connection library won't do some buffering that messes us up.
+   It does this buffering only if conn->readMethod == read, so using
+   another function turns it off.
+ */
+static int
+my_read (int fd, char *buf, int nb)
+{
+  return read (fd, buf, nb);
+}
+
+static Connection *
+make_energize_connection (Editor *editor, int fdin, int fdout)
+{
+  Connection *conn = NewConnection ((void *)editor, fdin, fdout);
+  if (conn)
+    conn->readMethod = my_read;
+  return conn;
+}
+
+DEFUN ("handle-energize-request", Fhandle_energize_request,
+       Shandle_energize_request,
+       2, 2, 0 /*
+Filter called when a request is available from Energize.
+*/ )
+     (proc, string)
+     Lisp_Object proc, string;
+{
+  if (!NILP (string))
+    CHECK_STRING (string);
+
+  if (!energize_connection || !energize_connection->conn)
+    {
+      /* no need for a message here, Energize is dead */
+      return make_int (0);
+    }
+  if (!energize_connection || (!EQ (energize_connection->proc, proc)))
+    {
+      message ("Got " IDENTITY_CRISIS " request but not from current connection ");
+      return make_int (0);
+    }
+
+  if (!NILP (string))
+    add_in_connection_input_buffer (energize_connection->conn,
+				    (char *) string_data (XSTRING (string)),
+				    string_length (XSTRING (string)));
+
+  return process_energize_request_1 ();
+}
+
+
+Lisp_Object Venergize_process;
+
+/* Opens a network connection to Energize.
+ * server is a string.	It can end up with :<uid> or :<username>
+ * in which case the uid is added to the TCP port to get the connection */
+static void
+connect_to_energize (char *server_str, char *arg)
+{
+  struct Lisp_Process *proc;
+  Lisp_Object lp;
+  Lisp_Object fil;
+  char *host;
+  unsigned int port;
+  long flags;
+  int id1;
+  int id2;
+
+  if (CGetPortNumber (server_str, &host, &port))
+    {
+
+      lp = Fopen_network_stream_internal (build_string ("energize"),
+					  Qnil,
+					  build_string (host),
+					  make_int (port));
+      if (!NILP (lp))
+	{
+	  int infd, outfd;
+	  /* Don't ask the user for confirmation when exiting Emacs */
+	  Fprocess_kill_without_query (lp, Qnil);
+	  proc = XPROCESS (lp);
+	  energize_connection = xnew (Editor);
+	  get_process_file_descriptors (proc, &infd, &outfd);
+	  energize_connection->conn =
+	    make_energize_connection (energize_connection, infd, outfd);
+	  energize_connection->proc = lp;
+	  energize_connection->binfo_hash = make_hashtable (10);
+	  energize_connection->image_table = 0;
+	  energize_connection->gc_save = Qnil;
+	  energize_connection->major = 0;
+	  energize_connection->minor = 0;
+	  peo = allocate_edit_options (10);
+	  request_serial_number = 0;
+	  global_reply_wait = 0;
+
+	  if ((flags = fcntl (energize_connection->conn->fdin, F_GETFL, 0))
+	      == -1)
+	    abort ();
+
+#ifdef O_NONBLOCK
+	  if (fcntl (energize_connection->conn->fdin, F_SETFL,
+		     flags & ~O_NONBLOCK)
+	      == -1)
+#else
+	  if (fcntl (energize_connection->conn->fdin, F_SETFL,
+		     flags & ~O_NDELAY)
+	      == -1)
+#endif
+	    abort ();
+
+	  XSETSUBR (fil, &Shandle_energize_request);
+	  set_process_filter (lp, fil, 1);
+
+	  Venergize_kernel_busy = Qnil;
+
+	  id1 = 0;
+	  id2 = 0;
+	  if (arg)
+	    sscanf (arg, "%x,%x", &id1, &id2);
+
+	  Venergize_buffers_list = Qnil;
+
+	  setup_connection (energize_connection, id1, id2);
+
+	  Venergize_process = lp;
+	}
+      else
+	error ("couldn't connect to " IDENTITY_CRISIS " server");
+    }
+  else
+    error ("couldn't determine " IDENTITY_CRISIS " server port number");
+
+
+#ifdef ENERGIZE_V2_HEADERS
+  if (energize_connection->minor > 9)
+    {
+      close_energize_connection ();
+      error ("This Emacs doesn't understand " IDENTITY_CRISIS " version 3.");
+    }
+
+#endif /* ENERGIZE_V2_HEADERS */
+
+}
+
+
+/* Close the connection to energize.
+ * Kills all the energize related buffer */
+static void
+close_energize_connection ()
+{
+  Editor *ed = energize_connection;
+
+  if (ed)
+    /* make this function as paranoid as we can */
+    {
+      /* cleanup the busy state */
+      show_all_menubars_busy (False);
+      Venergize_kernel_busy = Qnil;
+      /* destroy all pop_up boxes */
+      lw_destroy_all_pop_ups ();
+
+      if (ed->conn)
+	DeleteConnection (ed->conn);
+      ed->conn = 0;
+
+      if (ed->binfo_hash)
+	{
+	  int speccount = specpdl_depth ();
+
+	  /* we are flushing everything, so we just ignore any change
+	     hooks and don't make an effort to delete extents since they
+	     are all going away */
+	  specbind (Qenergize_buffer_modified_hook, Qnil);
+	  specbind (Qinhibit_quit, Qt);
+	  call0 (intern ("de-energize-all-buffers"));
+	  unbind_to (speccount, Qnil);
+
+	  free_hashtable (ed->binfo_hash);
+	  ed->binfo_hash = 0;
+	}
+
+      /* Do this after de-energize-all-buffers or frame sizes thrash. */
+      debuggerpanel_sheet = 0;
+      desired_debuggerpanel_exposed_p = 0;
+
+      free_edit_options (peo);
+
+      if (ZEROP (ed->proc)) abort ();
+
+      if (!NILP (ed->proc))
+	Fdelete_process (ed->proc);
+      ed->proc = Qnil;
+
+      Venergize_buffers_list = Qnil;
+
+      /* now kill buffers created to satisfy requests on old connection */
+      xfree (ed);
+    }
+
+  /* mark as closed */
+  energize_connection = 0;
+  Venergize_process = Qnil;
+}
+
+
+DEFUN ("connect-to-energize-internal",
+       Fconnect_to_energize_internal, Sconnect_to_energize_internal, 0, 2, 0 /*
+Usage: (connect-to-energize-internal <server-name> <energizearg>)
+Energizearg representing two 32 bit Energize ids that will be passed
+to the Energize server when opening the Energize connection.
+Only one connection can be open at a time.
+*/ )
+
+  (server_name, energize_arg)
+  Lisp_Object server_name, energize_arg;
+{
+  unsigned char *server;
+  unsigned char *arg;
+
+  if (!NILP (energize_arg))
+    {
+      CHECK_STRING (energize_arg);
+      arg = string_data (XSTRING (energize_arg));
+    }
+  else
+    arg = 0;
+
+  if (!NILP (server_name))
+    {
+      CHECK_STRING (server_name);
+      server = string_data (XSTRING (server_name));
+    }
+  else
+    server = 0;
+
+  /* since we are going ahead with this, make sure that we are
+     really and truly disconnected first */
+  Fclose_connection_to_energize ();
+
+  connect_to_energize ((char *)server, (char *)arg);
+  return Qnil;
+}
+
+DEFUN ("close-connection-to-energize", Fclose_connection_to_energize,
+       Sclose_connection_to_energize, 0, 0, 0 /*
+Close the open Energize connection, if any.
+*/ )
+     ()
+{
+  if (!energize_connection) return Qnil;
+
+  close_energize_connection ();
+  return Qnil;
+}
+
+
+/* Extents stuff; this used to be in extents.c */
+
+static void
+set_extent_flags (EXTENT extent, Energize_Extent_Data *ext)
+{
+  /* clear every flag */
+  if (!EXTENT_LIVE_P (extent))
+    return;
+  extent_start_open_p (extent) = 0;
+  extent_end_open_p (extent) = 1;
+  extent_read_only_p (extent) = 0;
+  set_extent_mouse_face (extent, Qnil);
+  extent_unique_p (extent) = 0;
+  extent_duplicable_p (extent) = 0;
+  extent_invisible_p (extent) = 0;
+
+  set_extent_glyph (extent, 0, 0, GL_TEXT);
+  set_extent_glyph (extent, 0, 1, GL_TEXT);
+
+  if (ext)
+    {
+      ext->warn_modify = 0;
+
+      switch (ext->extentType)
+	{
+	case CEAttribute:
+	  break;
+
+	case CEAbbreviation:
+	  break;
+
+	case CEWriteProtect:
+	  extent_read_only_p (extent) = 1;
+	  break;
+
+	case CEGeneric:
+	  {
+	    GLYPH begin_glyph = 0;	/* always the class glyph */
+	    GLYPH end_glyph = 0;	/* always the instance glyph */
+
+	    /* if (ext->u.generic.gData->id)
+	         SET_EXTENT_FLAG (extent, EF_MENU);*/
+
+	    if (ext->u.generic.gData->glyph)
+	      end_glyph = ext->u.generic.gData->glyph;
+	    if (ext->u.generic.gData->cl && ext->u.generic.gData->cl->glyph)
+	      begin_glyph = ext->u.generic.gData->cl->glyph;
+
+#if 0
+	    if (begin_glyph && end_glyph)
+	      {
+		begin_glyph = end_glyph;
+		end_glyph = 0;
+	      }
+#endif
+
+	    if (begin_glyph)
+	      set_extent_glyph (extent, begin_glyph, 0, GL_TEXT);
+	    if (end_glyph)
+	      set_extent_glyph (extent, end_glyph, 1, GL_TEXT);
+
+	    if (ext->u.generic.gData->cl &&
+		(ext->u.generic.gData->cl->flags & CCElectric))
+	      set_extent_mouse_face (extent, Qhighlight);
+	    if (ext->u.generic.gData->cl &&
+		(ext->u.generic.gData->cl->flags & CCWarnModified))
+	      ext->warn_modify = 1;
+#if 0 /* #### some day (soon?)... */
+	    if (ext->u.generic.gData->cl &&
+		(ext->u.generic.gData->cl->flags & CCColumn))
+	      SET_EXTENT_FLAG (extent, EF_COLUMN);
+#endif
+	    extent_duplicable_p (extent) = 1;
+	    extent_unique_p (extent) = 1;
+	    break;
+	  }
+
+	default:
+	  break;
+	}
+    }
+}
+
+extern Lisp_Object Fset_extent_face (Lisp_Object extent, Lisp_Object name);
+
+static void
+set_extent_attributes_index (EXTENT extent, Energize_Extent_Data *ext)
+{
+  int graphic_attributes;
+
+  if (!ext)
+    extent_face_id (extent) = -1;
+  else
+    {
+      switch (ext->extentType)
+	{
+	case CEAttribute:
+	  graphic_attributes = ext->u.attr.attrValue;
+	  break;
+
+	case CEGeneric:
+	  graphic_attributes = ext->u.generic.gData->attribute;
+	  break;
+
+	case CEWriteProtect:
+	  /* this type has NO display attributes */
+	  extent_face_id (extent) = -1;
+	  return;
+
+	default:
+	  graphic_attributes = GA_NO_CHANGE;
+	}
+
+      if (graphic_attributes >= GA_MAX)
+	graphic_attributes = GA_NO_CHANGE;
+
+      {
+	/* The Venergize_attributes_mapping global is an alist of the form
+	   ( (<kernel-attribute-number> . <emacs-face-name> ) ... )
+	   */
+	Lisp_Object face = Fcdr (Fassq (make_int (graphic_attributes),
+					Venergize_attributes_mapping));
+	Lisp_Object e;
+	XSETEXTENT (e, extent);
+	if (NILP (face))
+	  message ("Unknown Energize attribute %d", graphic_attributes);
+	else if (EQ (face, Qdefault))
+	  Fset_extent_face (e, Qnil);
+	else
+	  Fset_extent_face (e, face);
+      }
+    }
+}
+
+void
+restore_energize_extent_state (EXTENT extent)
+{
+  Energize_Extent_Data *ext = energize_extent_data (extent);
+  if (!ext) return;
+  set_extent_flags (extent, ext);
+  set_extent_attributes_index (extent, ext);
+}
+
+DEFUN ("extent-to-generic-id", Fextent_to_generic_id, Sextent_to_generic_id,
+       1, 1, 0 /*
+Return Energize ID of buffer of EXTENT.
+*/ )
+     (extent_obj)
+     Lisp_Object extent_obj;
+{
+  CHECK_EXTENT (extent_obj);
+  return word_to_lisp (energize_extent_data_id
+		       (energize_extent_data (XEXTENT (extent_obj))));
+}
+
+
+
+/* buffer modified routines */
+static void
+send_buffer_modification_state (Editor *editor, BufferInfo *binfo, int flag)
+{
+  Connection *conn = editor->conn;
+  EId bufferId = binfo->id;
+
+  if (conn)
+    {
+      int state = (flag)?(CMBufferSetModified):(CMBufferSetUnmodified);
+      CWriteModifiedBufferHeader (conn, bufferId, state);
+      CWriteRequestBuffer (conn);
+    }
+}
+
+/* returns 1 if buffer is locked (non-editable),
+   0 if it isn't (editable), and -1 if it can't tell
+ */
+static int
+check_buffer_lock (Editor *editor, BufferInfo *binfo)
+{
+  Connection *conn = editor->conn;
+  struct reply_wait rw;
+
+  /* If permision already granted by kernel dont' ask again */
+  if (binfo->editable)
+    return 0;
+
+  /* If can't ask say we do not know */
+  if (!conn)
+    return -1;
+
+  /* Ask the kernel */
+  CWriteModifiedBufferHeader (conn, binfo->id, CMBufferQueryLock);
+  conn->header->serial = ++request_serial_number;
+  CWriteRequestBuffer (conn);
+
+  rw.serial = request_serial_number;
+  rw.objectId = binfo->id;
+  rw.status = -1;
+
+  if (!wait_for_reply (&rw))
+    return -1;
+
+  if (rw.status == CMBufferLocked)
+    {
+      /* Buffer is locked by kernel so we cannot edit it */
+      return 1;
+    }
+  else if (rw.status == CMBufferUnlocked)
+    {
+      /* Buffer is unlocked by kernel: edit permission granted! */
+      binfo->editable = 1;
+      return 0;
+    }
+  else
+    {
+      /* This should never happen */
+      return -1;
+    }
+}
+
+
+/* Ask the kernel if BUFFER is currently locked -- waits for answer */
+static Lisp_Object
+buffer_locked_p (Lisp_Object buffer)
+{
+  BufferInfo *binfo;
+
+  if (!energize_connection)
+    return Qnil;
+
+  if (NILP (buffer))
+    XSETBUFFER (buffer, current_buffer);
+
+  CHECK_BUFFER (buffer);
+
+  binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
+
+  if (!binfo)
+    {
+      /* Not an Energize buffer, return Qnil: can edit buffer */
+      return Qnil;
+    }
+  else
+    {
+      /* Energize buffer, check if editable */
+      return check_buffer_lock (energize_connection, binfo) == 0 ? Qnil : Qt;
+    }
+}
+
+
+
+/* Called by map_extents function called by Fenergize_send_buffer_modified
+ */
+static int
+notify_extent_modified (EXTENT extent, void *arg)
+{
+  /* arg is a binfo_and_state */
+  binfo_and_state *bans = (binfo_and_state*)arg;
+  Energize_Extent_Data *ext;
+  BufferInfo *binfo = bans->binfo;
+  Connection *conn = binfo->editor->conn;
+  EId *extent_id;
+  Lisp_Object extent_obj;
+
+  XSETEXTENT (extent_obj, extent);
+  if ((ext = extent_to_data (extent_obj))
+      && ext->warn_modify
+      && ext->extentType == CEGeneric
+      && ext->u.generic.gData
+      && ext->u.generic.gData->cl
+      && (ext->u.generic.gData->cl->flags & CCWarnModified)
+      && ext->u.generic.gData->modified_state != bans->state)
+    {
+      if (bans->tell_energize)
+	{
+	  CWriteModifiedExtentsHeader (conn, binfo->id, bans->state, 1);
+	  extent_id = CPut (conn, EId);
+	  *extent_id = ext->id;
+	  CWriteLength (conn);
+	  CWriteRequestBuffer (conn);
+	}
+      ext->u.generic.gData->modified_state = bans->state;
+    }
+  return 0;
+}
+
+static int
+ceiwme_lower_mf (EXTENT e, void *arg)
+{
+  Lisp_Object extent;
+  Energize_Extent_Data *ext;
+  XSETEXTENT (extent, e);
+  ext = extent_to_data (extent);
+  if (ext && ext->warn_modify)
+    *((EXTENT *) arg) = e;
+  return 0;
+}
+
+static int
+ceiwme_upper_mf (EXTENT e, void *arg)
+{
+  Lisp_Object extent;
+  Energize_Extent_Data *ext;
+  XSETEXTENT (extent, e);
+  ext = extent_to_data (extent);
+  if (ext && ext->warn_modify)
+    {
+      *((EXTENT *) arg) = e;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+static void
+coerce_endpoints_to_be_inside_warn_on_modify_extents (Bufpos *from_ptr,
+						      Bufpos *to_ptr,
+						      struct buffer *b)
+{
+  EXTENT lower_extent = 0;
+  EXTENT upper_extent = 0;
+  Bufpos lower_bound = *from_ptr;
+  Bufpos upper_bound = *to_ptr;
+  Lisp_Object tmp;
+
+  /* make sure that from <= to */
+  {
+    Bufpos tmp_int = max (lower_bound, upper_bound);
+    *from_ptr = lower_bound = min (lower_bound, upper_bound);
+    *to_ptr = upper_bound = tmp_int;
+  }
+
+  if (!BUFFER_NOTIFY_BACKGROUND_BIT_SET_P (b))
+    return;
+
+  map_extents (BUF_BEG (b), lower_bound, ceiwme_lower_mf,
+	       (void *) &lower_extent, make_buffer (b), 0, ME_END_CLOSED);
+  if (!lower_extent)
+    {
+      lower_bound = BUF_BEG (b);
+      map_extents (upper_bound, BUF_Z (b), ceiwme_upper_mf,
+		   (void *) &upper_extent, make_buffer (b), 0, ME_END_CLOSED);
+      if (!upper_extent)
+	upper_bound = BUF_Z (b);
+      else
+	{
+	  Bufpos xstart;
+	  XSETEXTENT (tmp, upper_extent);
+	  xstart = XINT (Fextent_start_position (tmp));
+	  upper_bound = max (upper_bound, xstart);
+	}
+    }
+  /* I forget why, but if we found an lower bound, we don't need to find
+     an upper bound */
+  else
+    {
+      Bufpos xstart;
+      XSETEXTENT (tmp, lower_extent);
+      xstart = XINT (Fextent_start_position (tmp));
+      lower_bound = min (lower_bound, xstart);
+    }
+
+  *from_ptr = lower_bound;
+  *to_ptr = upper_bound;
+  return;
+}
+
+static void
+mark_all_extents_as_unmodified (BufferInfo *binfo)
+{
+  binfo_and_state bans;
+  bans.binfo = binfo;
+  bans.state = FALSE;
+  bans.tell_energize = FALSE;
+
+  map_extents (BUF_BEG (XBUFFER (binfo->emacs_buffer)),
+	       BUF_Z (XBUFFER (binfo->emacs_buffer)),
+	       notify_extent_modified, &bans,
+	       binfo->emacs_buffer, 0, ME_END_CLOSED);
+}
+
+/* Send the BufferModified events for the current buffer.
+ * Handles both global buffer modified and extents modified. */
+DEFUN ("energize-send-buffer-modified", Fenergize_send_buffer_modified,
+       Senergize_send_buffer_modified,
+       3, 3, 0 /*
+Send a BufferModified request for the current buffer.
+*/ )
+     (state, from, to)
+     Lisp_Object state, from, to; /* dont use ANSI arglists in DEFUNs */
+{
+  int modifiedp = NILP (state)? 0 : 1;
+  Lisp_Object buffer;
+  BufferInfo *binfo;
+  Bufpos from_int = XINT (from);
+  Bufpos to_int = XINT (to);
+
+  if (!energize_connection || !energize_connection->conn) return Qnil;
+
+  XSETBUFFER (buffer, current_buffer);
+
+  Fenergize_barf_if_buffer_locked ();
+
+  if (binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection))
+    {
+      /* now make sure that from and to
+	 are inside some warn_on_modify extents somewhere */
+      coerce_endpoints_to_be_inside_warn_on_modify_extents
+	(&from_int, &to_int, current_buffer);
+      XSETINT (from, from_int);
+      XSETINT (to, to_int);
+
+      if (binfo->modified_state != modifiedp)
+	{
+	  send_buffer_modification_state (energize_connection, binfo,
+					  modifiedp);
+	  binfo->modified_state = modifiedp;
+	}
+#ifndef ENERGIZE_V2_HEADERS
+      if (!(binfo->flags & CBFileYourself))
+#endif
+	{
+	  if (modifiedp)
+	    {
+	      binfo_and_state bans;
+	      bans.binfo = binfo;
+	      bans.state = 1;
+	      bans.tell_energize = 1;
+	      map_extents (XINT (from), XINT (to),
+			   notify_extent_modified, &bans,
+			   binfo->emacs_buffer, 0,
+			   ME_END_CLOSED);
+	    }
+	  else
+	    mark_all_extents_as_unmodified (binfo);
+	}
+    }
+  return Qnil;
+}
+
+DEFUN ("energize-barf-if-buffer-locked", Fenergize_barf_if_buffer_locked,
+       Senergize_barf_if_buffer_locked, 0, 0, 0 /*
+Error if the buffer is locked.
+*/ )
+     ()
+{
+  Lisp_Object buffer;
+  XSETBUFFER (buffer, current_buffer);
+
+  if (!energize_connection || !energize_connection->conn)
+    return Qnil;
+
+  while (!NILP (buffer_locked_p (buffer)))
+    {
+      notify_delayed_requests ();
+      Fsignal (Qbuffer_locked_by_energize, (Fcons (buffer, Qnil)));
+    }
+  return Qnil;
+}
+
+
+DEFUN ("energize-send-region", Fenergize_send_region,
+       Senergize_send_region,
+       2, 2, 0 /*
+Send region as user input
+*/ )
+     (start, end)
+     Lisp_Object start, end;
+{
+  BufferInfo *binfo;
+  Lisp_Object b;
+  CEditorRequest *req;
+
+  if (!energize_connection || !energize_connection->conn)
+    error ("Not connected to " IDENTITY_CRISIS);
+
+  XSETBUFFER (b, current_buffer);
+  if (binfo = get_buffer_info_for_emacs_buffer (b, energize_connection))
+    {
+      Bufpos st, en;
+      Bufpos ceil;
+
+      get_buffer_range_char (current_buffer, start, end, &st, &en);
+
+      do
+	{
+	  ceil = BUF_CEILING_OF (current_buffer, st);
+
+	  req = CWriteEditorRequest (energize_connection->conn,
+				     UserTypedSomethingRType);
+	  req->usertypedsomething.bufferId = binfo->id;
+	  CWriteVstringLen
+	    (energize_connection->conn,
+	     (char *) BUF_BYTE_ADDRESS (current_buffer, st), ceil - st);
+	  CWriteLength (energize_connection->conn);
+	  CWriteRequestBuffer (energize_connection->conn);
+	  st = ceil;
+	} while (st < en);
+      return Qnil;
+    }
+  return Qnil;
+}
+
+DEFUN ("connected-to-energize-p", Fconnected_to_energize_p,
+       Sconnected_to_energize_p,
+       0, 0, 0 /*
+Return nil if no connection to Energize.
+*/ )
+     ()
+{
+  if (!energize_connection ||
+      !energize_connection->conn ||
+      !energize_connection->binfo_hash ||
+      !PROCESSP (energize_connection->proc))
+    return Qnil;
+  else
+    return Qt;
+}
+
+DEFUN ("energize-user-input-buffer-mark", Fenergize_user_input_buffer_mark,
+       Senergize_user_input_buffer_mark, 0, 1, 0 /*
+Return the mark associated to the given Energize buffer.
+*/ )
+     (buffer)
+     Lisp_Object buffer;
+{
+  BufferInfo *binfo;
+
+  XSETBUFFER (buffer, decode_buffer (buffer, 0));
+  if (!energize_connection) return Qnil;
+  if ((binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection)))
+    return binfo->output_mark;
+  else
+    return Qnil;
+}
+
+Lisp_Object
+energize_get_buffer_process (Lisp_Object buf)
+{
+  BufferInfo *binfo;
+
+  if (!BUFFERP (buf)) return Qnil;
+  if (!energize_connection) return Qnil;
+  binfo = get_buffer_info_for_emacs_buffer (buf, energize_connection);
+  if (!binfo) return Qnil;
+  if (! binfo->buffer_type) return Qnil;
+  if (!strcmp (binfo->buffer_type, "energize-debugger-buffer") ||
+      !strcmp (binfo->buffer_type, "energize-log-file-buffer"))
+    return Venergize_process;
+  return Qnil;
+}
+
+
+static int
+get_energize_connection_and_buffer_id (Lisp_Object buffer, void **conn_ptr,
+				       long *buffer_id_ptr)
+{
+  BufferInfo *binfo;
+
+  if (!energize_connection || !energize_connection->conn) return 0;
+
+  binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
+
+  *conn_ptr = (void *) energize_connection->conn;
+  *buffer_id_ptr = (long) binfo ? binfo->id : 0;
+  return 1;
+}
+
+static int
+get_energize_connection_and_current_buffer_id (void **conn_ptr,
+					       long *buffer_id_ptr)
+{
+  Lisp_Object lisp_buffer;
+  XSETBUFFER (lisp_buffer, current_buffer);
+
+  return get_energize_connection_and_buffer_id (lisp_buffer, conn_ptr,
+						buffer_id_ptr);
+}
+
+int *
+get_psheets_for_buffer (Lisp_Object buffer, int *count_ptr)
+{
+  BufferInfo *binfo;
+
+  if (!energize_connection || !energize_connection->conn) return 0;
+
+  binfo = get_buffer_info_for_emacs_buffer (buffer, energize_connection);
+  if (!binfo) return 0;
+
+  if (count_ptr) *count_ptr = binfo->n_p_sheets;
+  return binfo->p_sheet_ids;
+}
+
+void
+notify_energize_sheet_hidden (EId id)
+{
+  EId buffer_id = buffer_id_of_sheet (id);
+  if (!buffer_id)
+    return;
+
+  if (buffer_id && energize_connection && energize_connection->conn)
+    {
+      CWriteSheetRequest (energize_connection->conn,
+			  CSHide, id, buffer_id, "");
+      CWriteRequestBuffer (energize_connection->conn);
+    }
+}
+
+DEFUN ("energize-query-buffer", Fenergize_query_buffer,
+       Senergize_query_buffer, 1, 2, 0 /*
+Ask Energize to create a buffer containing the file filename.
+Returns the buffer or NIL if Energize cannot create the buffer.
+If second argument just-ask is T, just ask if Energize
+already knows about the file and returns T if yes, NIL otherwise.
+*/ )
+     (filename, just_ask)
+     Lisp_Object filename, just_ask;
+{
+  struct Lisp_String *filename_str;
+  CEditorRequest *creq;
+  char *dir_sep;
+  struct reply_wait rw;
+
+  if (!energize_connection || !energize_connection->conn)
+    return Qnil;
+
+  filename = Fexpand_file_name (filename, Qnil);
+  filename_str = XSTRING (filename);
+
+  dir_sep = (char *) strrchr ((char *) string_data (filename_str), '/');
+
+  creq = CWriteEditorRequest (energize_connection->conn, QueryBufferRType);
+  creq->head.data = !NILP (just_ask);
+  creq->head.serial = ++request_serial_number;
+  CWriteVstringLen (energize_connection->conn, (char *) string_data (filename_str),
+		    (dir_sep)? (dir_sep - (char *) string_data (filename_str)): 0);
+  CWriteVstringLen (energize_connection->conn,
+		    (char *) string_data (filename_str), string_length (filename_str));
+  CWriteLength (energize_connection->conn);
+  CWriteRequestBuffer (energize_connection->conn);
+
+  rw.serial = request_serial_number;
+  rw.objectId = 0;
+
+  if (!wait_for_reply (&rw))
+    return Qnil;
+
+  if (rw.status)
+    {
+      if (rw.objectId)
+	{
+	  BufferInfo* binfo = get_buffer_info_for_id (rw.objectId,
+						      energize_connection);
+	  return binfo ? binfo->emacs_buffer : Qt;
+	}
+      else
+	return Qt;
+    }
+  else
+    return Qnil;
+}
+
+
+DEFUN ("energize-protocol-level", Fenergize_protocol_level,
+       Senergize_protocol_level, 0, 0, 0 /*
+Return the Energize protocol level.
+*/ )
+     ()
+{
+  return
+    energize_connection
+      ? Fcons (make_int (energize_connection->major),
+	       make_int (energize_connection->minor))
+	: Qnil;
+}
+
+
+DEFUN ("energize-psheets-visible-p", Fenergize_psheets_visible_p,
+       Senergize_psheets_visible_p, 0, 1, 0 /*
+Whether the (optional) frame currently has open psheets.
+*/ )
+     (frame)
+     Lisp_Object frame;
+{
+  if (NILP (frame))
+    XSETFRAME (frame, XFRAME(Fselected_frame(Qnil)));
+  CHECK_FRAME (frame);
+  if (FRAME_X_CURRENT_PSHEETS (XFRAME (frame)))
+    return Qt;
+  return Qnil;
+}
+
+DEFUN ("energize-buffer-has-psheets-p", Fenergize_buffer_has_psheets_p,
+       Senergize_buffer_has_psheets_p, 0, 1, 0 /*
+Whether the buffer has psheets associated with it.
+*/ )
+     (buf)
+     Lisp_Object buf;
+{
+  int count;
+  if (NILP (buf))
+    buf = Fcurrent_buffer ();
+  CHECK_BUFFER (buf);
+  if (get_psheets_for_buffer (buf, &count))
+    return Qt;
+  return Qnil;
+}
+
+
+void
+make_psheets_desired (struct frame *f, Lisp_Object buffer)
+{
+  int count;
+  int *psheets;
+
+  if (NILP (buffer) || !(psheets = get_psheets_for_buffer (buffer, &count)))
+    {
+      FRAME_X_DESIRED_PSHEETS (f) = 0;
+      FRAME_X_DESIRED_PSHEET_COUNT (f) = 0;
+      FRAME_X_DESIRED_PSHEET_BUFFER (f) = Qnil;
+    }
+  else
+    {
+      /* Do not show the debugger panel in this function.  The
+       * debugger panel should never be listed in the visible psheets. */
+      extern int debuggerpanel_sheet;
+      
+      if (count == 1 && psheets [0] == debuggerpanel_sheet)
+	return;
+
+      FRAME_X_DESIRED_PSHEETS (f) = psheets;
+      FRAME_X_DESIRED_PSHEET_COUNT (f) = count;
+      FRAME_X_DESIRED_PSHEET_BUFFER (f) = buffer;
+    }
+
+  /* Garbage the frame so that the sheets get recomputed right away and not
+     the next time some display change happens.  Possibly redisplay should
+     notice this on its own without the garbaged flag.  But once redisplay
+     gets smarter about such things, all garbagers should be revisited.
+   */
+  MARK_FRAME_CHANGED (f);
+}
+
+Lisp_Object
+desired_psheet_buffer (struct frame *f)
+{
+  if (FRAME_X_P (f))
+    return FRAME_X_DESIRED_PSHEET_BUFFER (f);
+  else
+    return Qnil;
+}
+
+/* This function is invoked when the user clicks on the "sheet" button.
+ */
+DEFUN ("energize-toggle-psheet", Fenergize_toggle_psheet,
+       Senergize_toggle_psheet, 0, 0, "" /*
+
+*/ )
+     ()
+{
+  struct frame *frame = XFRAME(Fselected_frame(Qnil));
+  Lisp_Object buffer = Fwindow_buffer (Fselected_window (Qnil));
+  if (EQ (buffer, desired_psheet_buffer (frame)))
+    make_psheets_desired (frame, Qnil);
+  else
+    make_psheets_desired (frame, buffer);
+  return Qnil;
+}
+
+
+static void energize_show_menubar_of_buffer (Lisp_Object frame,
+					     Lisp_Object buffer,
+					     Lisp_Object psheets_too);
+
+/* This is called when a buffer becomes visible in some window.
+
+   Show the menubar associated with this buffer, and show the psheets as
+   well if this buffer is the last buffer whose psheets were visible in
+   this frame.
+ */
+void
+energize_buffer_shown_hook (struct window *window)
+{
+  struct frame* frame = XFRAME (window->frame);
+  Lisp_Object buffer = window->buffer;
+  Lisp_Object pbuf;
+
+  if (! FRAME_X_P (frame)) return;
+  pbuf = desired_psheet_buffer (frame);
+
+  if (!MINI_WINDOW_P (window))
+    energize_show_menubar_of_buffer (window->frame, buffer,
+				     (EQ (buffer, pbuf) ? Qt : Qnil));
+}
+
+
+static int
+find_buffer_in_different_window (window, buffer, not_in)
+     struct window* window;
+     Lisp_Object buffer;
+     struct window* not_in;
+{
+  Lisp_Object child;
+  if (!NILP (window->buffer))
+    {
+      /* a leaf window */
+      return (EQ (window->buffer, buffer) && (window != not_in));
+    }
+  else
+    {
+      /* a non leaf window, visit either the hchild or the vchild */
+      for (child = !NILP (window->vchild) ? window->vchild : window->hchild;
+	   !NILP (child);
+	   child = XWINDOW (child)->next)
+	{
+	  if (find_buffer_in_different_window (XWINDOW (child), buffer,
+					       not_in))
+	    return 1;
+	}
+      return 0;
+    }
+}
+
+/* returns 1 if the buffer is only visible in window on frame f */
+static int
+buffer_only_visible_in_this_window_p (Lisp_Object buffer, 
+				      struct frame* f, 
+				      struct window* window)
+{
+  return !find_buffer_in_different_window (XWINDOW (f->root_window), buffer,
+					   window);
+}
+
+/* This is called just before a buffer which is visible becomes invisible,
+   either because some other buffer is about to be made visible in its window,
+   or because that window is being deleted.
+
+   If this buffer's psheets are visible, hide them.
+ */
+void
+energize_buffer_hidden_hook (struct window *window)
+{
+  struct frame *f = XFRAME (window->frame);
+
+  if (! FRAME_X_P (f)) return;
+
+  /* hides the p_sheet if we are changing the buffer of the
+   * selected window of the frame and the p_sheet where displayed */
+  if (EQ (window->buffer, desired_psheet_buffer (f))
+      && buffer_only_visible_in_this_window_p (window->buffer, f, window))
+    make_psheets_desired (f, Qnil);
+}
+
+
+/* This is called just before the selected window is no longer the selected
+   window because some other window is being selected.  The given window is
+   not being deleted, it is merely no longer the selected one.
+
+   This doesn't do anything right now.
+ */
+void
+energize_window_deselected_hook (struct window *window)
+{
+}
+
+
+/* This is called just after a window has been selected.
+
+   Show the menubar associated with this buffer; leave the psheets as
+   they are.
+ */
+void
+energize_window_selected_hook (struct window *window)
+{
+  struct frame* frame = XFRAME (window->frame);
+  Lisp_Object buffer = window->buffer;
+
+  if (FRAME_X_P (frame) && !MINI_WINDOW_P (window))
+    energize_show_menubar_of_buffer (window->frame, buffer, Qnil);
+}
+
+
+
+int current_debuggerpanel_exposed_p;
+int desired_debuggerpanel_exposed_p;
+int debuggerpanel_sheet;
+
+static void
+energize_show_menubar_of_buffer (Lisp_Object frame,
+				 Lisp_Object buffer,
+				 Lisp_Object psheets_too)
+{
+  struct frame *f = decode_x_frame (frame);
+
+  if (! NILP (psheets_too))
+    {
+      Lisp_Object buffer;
+      XSETBUFFER (buffer, current_buffer);
+      make_psheets_desired (f, buffer);
+    }
+}
+
+
+/* edit-mode dialog box
+   This stuff really sucks
+ */
+
+static struct editmode {
+  int ok, external, view, edit, window, split;
+  char *other;
+} editmode;
+
+static void
+edit_mode_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
+{
+  widget_value *data;
+  char *name = (char *) client_data;
+
+  if ((int) client_data == -1) name = "cancel";	/* WM_DELETE_WINDOW */
+
+  if (!strcmp (XtName (widget), "otherText")) /* screw it */
+    ;
+  else if (!strcmp (name, "externalBox"))
+    {
+      /* make the text slot be active only if "other" is selected */
+      data = malloc_widget_value ();
+      data->name = "externalOther";
+      if (! lw_get_some_values (id, data)) abort ();
+      data->enabled = data->selected;
+      data->name = "otherText";
+      lw_modify_all_widgets (id, data, True);
+      free_widget_value (data);
+    }
+  else if (!strcmp (name, "cancel"))
+    {
+      editmode.ok = -1;
+      lw_destroy_all_widgets (id);
+    }
+  else if (!strcmp (name, "help"))
+    {
+      Lisp_Object v = Fmake_vector (make_int (3), Qt);
+      vector_data (XVECTOR (v)) [0] = build_string ("ok");
+      vector_data (XVECTOR (v)) [1] = list1 (Qignore);
+      Fpopup_dialog_box (list2 (build_string ("dbx_editmode_help"), v));
+    }
+  else if (!strcmp (name, "ok"))
+    {
+      editmode.ok = 1;
+      data = malloc_widget_value ();
+      data->name = "externalEmacs";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.external = 0;
+      data->name = "externalViXterm";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.external = 1;
+      data->name = "externalViCmdtool";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.external = 2;
+      data->name = "externalOther";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.external = 3;
+      data->name = "otherText";
+      if (! lw_get_some_values (id, data)) abort ();
+      editmode.other = data->value;
+
+      data->name = "emacsView";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.view = 0;
+      data->name = "viView";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.view = 1;
+      data->name = "lessView";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.view = 2;
+
+      data->name = "editEmacs";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.edit = 0;
+      data->name = "editVi";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.edit = 1;
+
+      data->name = "windowOne";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.window = 0;
+      data->name = "windowSeveral";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.window = 1;
+      data->name = "windowMany";
+      if (! lw_get_some_values (id, data)) abort ();
+      if (data->selected) editmode.window = 2;
+
+      data->name = "splitScreens";
+      if (! lw_get_some_values (id, data)) abort ();
+      editmode.split = !!data->selected;
+
+      free_widget_value (data);
+      lw_destroy_all_widgets (id);
+    }
+  else
+    {
+      abort ();
+    }
+}
+
+static int
+editmode_done (void *arg)
+{
+  return (editmode.ok != 0);
+}
+
+extern LWLIB_ID new_lwlib_id (void);
+
+DEFUN ("energize-edit-mode-prompt", Fenergize_edit_mode_prompt,
+       Senergize_edit_mode_prompt, 6, 6, 0 /*
+
+*/ )
+     (external, edit_mode, view_mode, other_text, window, split)
+     Lisp_Object external, edit_mode, view_mode, other_text, window, split;
+{
+  int dbox_id;
+  struct frame *f = selected_frame ();
+  widget_value *data;
+  Widget parent, dbox;
+  Lisp_Object frame = Qnil;
+
+  XSETFRAME (frame, f);
+  CHECK_X_FRAME (frame);
+  parent = FRAME_X_SHELL_WIDGET (f);
+
+  CHECK_INT (external);
+  CHECK_INT (edit_mode);
+  CHECK_INT (view_mode);
+  CHECK_INT (window);
+  CHECK_INT (split);
+  CHECK_STRING (other_text);
+
+  editmode.ok = 0;
+  editmode.external = XINT (external);
+  editmode.view = XINT (view_mode);
+  editmode.edit = XINT (edit_mode);
+  editmode.window = XINT (window);
+  editmode.split = XINT (split);
+  editmode.other = 0;
+
+  data = malloc_widget_value ();
+  data->name = "editmode";
+  data->value = "editmode";
+  data->enabled = 1;
+
+  dbox_id = new_lwlib_id ();
+  dbox = lw_create_widget ("editmode", "editmode", dbox_id, data, parent,
+			   1, 0, edit_mode_callback, 0);
+  data->value = 0;
+
+  data->name = "button1"; data->call_data = data->value = "ok";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = "button2"; data->call_data = data->value = "cancel";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = "button3"; data->call_data = data->value = "help";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = data->call_data = "externalBox";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = "otherText"; data->call_data = "otherText";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = "message"; data->value = "editmode";
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  data->selected = 1;
+  switch (editmode.external)
+    {
+    case 0: data->name = "externalEmacs"; break;
+    case 1: data->name = "externalViXterm"; break;
+    case 2: data->name = "externalViCmdtool"; break;
+    case 3: data->name = "externalOther"; break;
+    default: abort ();
+    }
+  lw_modify_all_widgets (dbox_id, data, True);
+  switch (editmode.view)
+    {
+    case 0: data->name = "emacsView"; break;
+    case 1: data->name = "viView"; break;
+    case 2: data->name = "lessView"; break;
+    default: abort ();
+    }
+  lw_modify_all_widgets (dbox_id, data, True);
+  switch (editmode.edit)
+    {
+    case 0: data->name = "editEmacs"; break;
+    case 1: data->name = "editVi"; break;
+    default: abort ();
+    }
+  lw_modify_all_widgets (dbox_id, data, True);
+  switch (editmode.window)
+    {
+    case 0: data->name = "windowOne"; break;
+    case 1: data->name = "windowSeveral"; break;
+    case 2: data->name = "windowMany"; break;
+    default: abort ();
+    }
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  data->name = "otherText";
+  data->selected = 0;
+  data->value = (char *) string_data (XSTRING (other_text));
+  data->enabled = (editmode.external == 3);
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  data->name = "splitScreens";
+  data->enabled = 1;
+  data->selected = editmode.split;
+  data->value = 0;
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  free_widget_value (data);
+
+  lw_pop_up_all_widgets (dbox_id);
+
+  wait_delaying_user_input (editmode_done, 0);
+
+  if (editmode.ok == -1)
+    return Fcons (external,
+		  list5 (edit_mode, view_mode, other_text, window, split));
+  else if (editmode.ok == 1)
+    return Fcons (make_int (editmode.external),
+		  list5 (make_int (editmode.view),
+			 make_int (editmode.edit),
+			 build_string (editmode.other ? editmode.other : ""),
+			 make_int (editmode.window),
+			 make_int (editmode.split)));
+  else
+    abort ();
+}
+
+static LWLIB_ID search_id;
+static int last_search_up_p;
+
+static void
+hide_search_dialog (Widget w, LWLIB_ID id)
+{
+#if 0
+  /* I'd like to do this, but the widget occasionally gets FUCKED */
+  XUnmapWindow (XtDisplay (w), XtWindow (w));
+#else
+  lw_destroy_all_widgets (id);
+#endif
+}
+
+
+static void
+search_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
+{
+  Widget parent = widget;
+  widget_value *data;
+  char *name = (char *) client_data;
+  Lisp_Object search, replace;
+  Lisp_Object case_sensitive_p, regexp_p, direction, match_word_p;
+  Lisp_Object device = Qnil;
+
+  if ((int) client_data == -1) name = "done";	/* WM_DELETE_WINDOW */
+
+  while (parent && XtClass (parent) != xmDialogShellWidgetClass)
+    parent = XtParent (parent);
+  if (! parent) abort ();
+
+  if (!strcmp (name, "done"))
+    {
+      hide_search_dialog (parent, id);
+      return;
+    }
+#if 0
+  else if (!strcmp (name, "help"))
+    {
+      Lisp_Object v = Fmake_vector (3, Qt);
+      vector_data (XVECTOR (v)) [0] = build_string ("ok");
+      vector_data (XVECTOR (v)) [1] = list1 (Qignore);
+      Fpopup_dialog_box (list2 (build_string ("dbx_search_help"), v));
+      return;
+    }
+#endif
+
+  {
+    struct device *d = get_device_from_display (XtDisplay (widget));
+    XSETDEVICE (device, d);
+    DEVICE_X_MOUSE_TIMESTAMP (d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (d);
+  }
+
+  if (!strcmp (name, "gotoStart"))
+    {
+      signal_special_Xt_user_event (device, Qcall_interactively,
+				    Qbeginning_of_buffer);
+    }
+  else if (!strcmp (name, "gotoEnd"))
+    {
+      signal_special_Xt_user_event (device, Qcall_interactively,
+				    Qend_of_buffer);
+    }
+  else if (!strcmp (name, "scrollForward"))
+    {
+      signal_special_Xt_user_event (device, Qcall_interactively,
+				    Qscroll_up);
+    }
+  else if (!strcmp (name, "scrollBack"))
+    {
+      signal_special_Xt_user_event (device, Qcall_interactively,
+				    Qdown_up);
+    }
+  else
+    {
+      data = malloc_widget_value ();
+      data->name = "searchText";
+      if (! lw_get_some_values (id, data)) abort ();
+      search = build_string (data->value);
+      data->name = "replaceText";
+      if (! lw_get_some_values (id, data)) abort ();
+      replace = build_string (data->value);
+      data->name = "regexpSearch";
+      if (! lw_get_some_values (id, data)) abort ();
+      regexp_p = (data->selected ? Qt : Qnil);
+      data->name = "caseSearch";
+      if (! lw_get_some_values (id, data)) abort ();
+      case_sensitive_p = (data->selected ? Qt : Qnil);
+      data->name = "matchWord";
+      if (! lw_get_some_values (id, data)) abort ();
+      match_word_p = (data->selected ? Qt : Qnil);
+      
+      data->name = "directionForward";
+      if (! lw_get_some_values (id, data)) abort ();
+      direction = data->selected ? Qt : Qnil;
+      
+      if (!strcmp (name, "search"))
+	replace = Qnil;
+      else if (!strcmp (name, "replace"))
+	;
+      else if (!strcmp (name, "replace_all"))
+	{
+	  replace = list1 (replace);
+	  /*	hide_search_dialog (parent, id); */
+	}
+      else
+	abort ();
+      
+      free_widget_value (data);
+      
+      signal_special_Xt_user_event (device,
+				    intern ("energize-search-internal"),
+				 (NILP (replace)
+				  ? list5 (case_sensitive_p, match_word_p,
+					   regexp_p, direction, search)
+				  : list6 (case_sensitive_p, match_word_p,
+					   regexp_p, direction, search,
+					   replace)));
+    }
+}
+
+
+DEFUN ("energize-search", Fenergize_search, Senergize_search, 0, 0, "" /*
+Pop up the search-and-replace dialog box.
+*/ )
+     ()
+{
+  int dbox_id;
+  struct frame *f = selected_frame ();
+  widget_value *data;
+  Widget parent, dbox;
+  Lisp_Object frame = Qnil;
+
+  XSETFRAME (frame, f);
+  CHECK_X_FRAME (frame);
+  parent = FRAME_X_SHELL_WIDGET (f);
+
+  data = malloc_widget_value ();
+
+  dbox_id = (search_id ? search_id : new_lwlib_id());
+  dbox = lw_create_widget ("search", "search", dbox_id, NULL, parent,
+			   1, 0, search_callback, 0);
+  data->enabled = 1;
+  data->value = 0;
+
+  data->name = "button1"; data->value = data->call_data = "search";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = "button2"; data->value = data->call_data = "replace";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = "button3"; data->value = data->call_data = "replace_all";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = "button4"; data->value = data->call_data = "done";
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  data->value = 0;
+  data->name = data->call_data = "gotoStart";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = data->call_data = "gotoEnd";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = data->call_data = "scrollBack";
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = data->call_data = "scrollForward";
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  data->value = 0;
+  data->name = data->call_data = "caseSearch";
+  data->selected = NILP (current_buffer->case_fold_search);
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  data->name = data->call_data = "directionForward";
+  data->selected = 1;
+  lw_modify_all_widgets (dbox_id, data, True);
+  data->name = data->call_data = "directionBackward";
+  data->selected = 0;
+  lw_modify_all_widgets (dbox_id, data, True);
+
+  free_widget_value (data);
+
+  lw_pop_up_all_widgets (dbox_id);
+  last_search_up_p = 0;
+  if (search_id)
+    {
+      Widget w = lw_get_widget (dbox_id, parent, True);
+      if (! w) abort ();
+      XMapRaised (XtDisplay (w), XtWindow (w));
+    }
+  else
+    {
+      search_id = dbox_id;
+    }
+
+  return Qnil;
+}
+
+
+
+/*************** Definition of Emacs Lisp-callable functions ***************/
+
+void
+syms_of_energize (void)
+{
+  defsubr (&Senergize_send_buffer_modified);
+  defsubr (&Senergize_list_menu);
+  defsubr (&Senergize_execute_menu_item);
+  defsubr (&Senergize_execute_command_internal);
+  defsubr (&Sconnect_to_energize_internal);
+  defsubr (&Sconnected_to_energize_p);
+  defsubr (&Sclose_connection_to_energize);
+  defsubr (&Shandle_energize_request);
+  defsubr (&Senergize_buffer_p);
+  defsubr (&Senergize_buffer_type);
+  defsubr (&Sset_energize_buffer_type_internal);
+  defsubr (&Senergize_buffer_id);
+  defsubr (&Senergize_request_kill_buffer);
+  defsubr (&Senergize_send_region);
+  defsubr (&Senergize_user_input_buffer_mark);
+  defsubr (&Senergize_update_menubar);
+  defsubr (&Senergize_extent_menu_p);
+  defsubr (&Senergize_query_buffer);
+  defsubr (&Senergize_barf_if_buffer_locked);
+  defsubr (&Senergize_psheets_visible_p);
+  defsubr (&Senergize_buffer_has_psheets_p);
+  defsubr (&Senergize_toggle_psheet);
+  defsubr (&Senergize_protocol_level);
+  defsubr (&Senergize_edit_mode_prompt);
+  defsubr (&Senergize_search);
+  defsubr (&Sextent_to_generic_id);
+
+  defsymbol (&Qenergize_create_buffer_hook, "energize-create-buffer-hook");
+  defsymbol (&Qenergize_buffer_modified_hook, "energize-buffer-modified-hook");
+
+  defsymbol (&Qenergize_kernel_busy, "energize-kernel-busy");
+
+  defsymbol (&Qenergize_kernel_busy_hook, "energize-kernel-busy-hook");
+  defsymbol (&Qenergize_menu_update_hook, "energize-menu-update-hook");
+  defsymbol (&Qbuffer_locked_by_energize, "buffer-locked-by-energize");
+  defsymbol (&Qenergize_user_input_buffer_mark,
+	     "energize-user-input-buffer-mark");
+  defsymbol (&Qenergize_make_many_buffers_visible,
+	     "energize-make-many-buffers-visible");
+  defsymbol (&Qenergize, "energize");
+  defsymbol (&Qenergize_auto_revert_buffer, "energize-auto-revert-buffer");
+}
+
+void
+vars_of_energize (void)
+{
+  energize_connection = 0;
+  inside_process_energize_request_1 = 0;
+
+  staticpro (&Venergize_buffers_list);
+  Venergize_buffers_list = Qnil;
+
+  staticpro (&Vall_energize_pixmaps);
+  Vall_energize_pixmaps = Qnil;
+
+  Fprovide (intern ("energize"));
+
+  search_id = 0;
+
+  DEFVAR_LISP ("energize-process", &Venergize_process /*
+The Lisp object representing the Energize connection, or nil
+*/ );
+  Venergize_process = Qnil;
+
+  DEFVAR_LISP ("energize-create-buffer-hook", &Venergize_create_buffer_hook /*
+Hook called when buffer is created by energize; takes 
+BUFFER as its only argument.
+*/ );
+  Venergize_create_buffer_hook = Qnil;
+
+
+  DEFVAR_LISP ("energize-kernel-modification-hook",
+	       &Venergize_kernel_modification_hook /*
+Hook called when a buffer is being modified by energize;
+takes no arguments.
+*/ );
+  Venergize_kernel_modification_hook = Qnil;
+
+  DEFVAR_BOOL ("ignore-kernel",
+	       &ignore_kernel /*
+Set when the kernel should be ignored -- for debugging.
+*/ );
+  ignore_kernel = 0;
+
+  DEFVAR_LISP ("energize-kernel-busy", &Venergize_kernel_busy /*
+True if the Energize kernel is busy.
+*/ );
+  Venergize_kernel_busy = Qnil;
+  DEFVAR_LISP ("energize-kernel-busy-hook", &Venergize_kernel_busy_hook /*
+Hook called when the Energize kernel becomes busy or non busy.
+*/ );
+  Venergize_kernel_busy_hook = Qnil;
+
+  DEFVAR_LISP ("energize-menu-update-hook", &Venergize_menu_update_hook /*
+Hook called when the Energize kernel updates the menubar.
+*/ );
+  Venergize_menu_update_hook = Qnil;
+
+  DEFVAR_LISP ("energize-attributes-mapping", &Venergize_attributes_mapping /*
+A-list to map kernel attributes indexes to Emacs attributes
+*/ );
+  Venergize_attributes_mapping = Qnil;
+
+  DEFVAR_INT ("energize-extent-gc-threshold", &energize_extent_gc_threshold /*
+Number of  extents in a ModifyBuffer request above which to do a GC
+*/ );
+  energize_extent_gc_threshold = 20;
+  
+  pure_put (Qbuffer_locked_by_energize, Qerror_conditions,
+	    list2 (Qbuffer_locked_by_energize, Qerror));
+  pure_put (Qbuffer_locked_by_energize, Qerror_message,
+	    build_string ("Buffer is currently locked by kernel"));
+}
+
+void
+complex_vars_of_energize (void)
+{
+  image_cache = make_strings_hashtable (50);
+}
+
+#endif /* ENERGIZE */