Index: ChangeLog
===================================================================
--- ChangeLog	(revision 68f1afe3f8a958184afe0832cf6c7231cc0332d2)
+++ ChangeLog	(revision 5df4b9a67df0d131296e49d30888f53288371879)
@@ -1,2 +1,36 @@
+1.2.1
+ * Fix building with Zephyr support. -nelhage
+ * Support --with-stack-protector in 'configure'. -nelhage
+
+1.2
+ * Fix some typos in source and messages. -adehnert
+ * Support an explicit --with{out,}-zephyr configure option. -nelhage
+ * Display a nicer error in :blist if .anyone doesn't exist. -geofft
+ * Don't zcrypt shift-R replies to zcrypted messages -adehnert
+ * Export a time_t for messages to perl as 'unix_time' -nelhage
+ * Get rid of cryptic numeric messages after IRC commands. -ezyang
+ * IRC: Include IRC admin messages in the 'irc' filter. -geofft
+ * Fix M-LEFT and M-RIGHT bindings. -nelhage
+ * Fix replycmd to use right class/instance -adehnert
+ * Allow SIGINT to interrupt getting the Zephyr buddy list -nelhage
+ * Break perlwrap.pm into multiple files. -nelhage
+ * Handle errors in perlwrap.pm better. -nelhage
+ * Don't attempt to send a Zephyr logout if we never initialized Zephyr. -nelhage
+ * Display personals better in OneLine mode. -adehnert
+ * Display context for pings -adehnert
+ * Support --program-{prefix,suffix,transform}. -nelhage
+ * Send instanced pings and give useful error messages -adehnert
+ * Add <message,*,%me%> to default Barnowl subs. -adehnert
+ * Maintain instance when using shift-R on personals -adehnert
+ * Improve handling of outgoing instanced personals -adehnert
+ * Don't require personals to be -i personal. -geofft
+ * Display context for personals, so as to make <message,*,%me> usable. -geofft
+ * Implement mark and swap in the message list. -asedeno
+ * Fix handling of C-SPACE. -nelhage
+ * Fix some warnings. -nelhage
+ * Handle SIGINT, and make ^C interrupt searches. -nelhage
+ * Fix the usage line for punt/unpunt -nelhage
+ * Small bugfixes to release scripts. -nelhage
+
 1.1.1
  * Fix bogus errors 'subscribing to login messages'. -nelhage
Index: codelist.pl
===================================================================
--- codelist.pl	(revision 09489b896891e6c4e2b0a2a7d622c25749410cf1)
+++ codelist.pl	(revision 0982901b3611e8a795d8e1630bcbfe1925feefe5)
@@ -8,4 +8,5 @@
 
 foreach $file (@ARGV) {
+    next if $file eq 'perlglue.c';
     open(FILE, $file);
 
Index: commands.c
===================================================================
--- commands.c	(revision 73ba824882e1f01df772d9031f2e27b1d7f26362)
+++ commands.c	(revision 2fc8397e0117350e37ccc1b1b83ebcc9b94686d2)
@@ -881,4 +881,29 @@
 		  "", ""),
 
+  OWLCMD_VOID_CTX("edit:set-mark", owl_editwin_set_mark,
+		  OWL_CTX_EDIT,
+		  "sets the mark",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:exchange-point-and-mark", owl_editwin_exchange_point_and_mark,
+		  OWL_CTX_EDIT,
+		  "exchanges the point and the mark",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:copy-region-as-kill", owl_editwin_copy_region_as_kill,
+		  OWL_CTX_EDIT,
+		  "copy the text between the point and the mark",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:kill-region", owl_editwin_kill_region,
+		  OWL_CTX_EDIT,
+		  "kill text between the point and the mark",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:yank", owl_editwin_yank,
+		  OWL_CTX_EDIT,
+		  "insert the current text from the kill buffer",
+		  "", ""),
+
   OWLCMD_VOID_CTX("editline:done", owl_command_editline_done, 
 		  OWL_CTX_EDITLINE,
@@ -913,4 +938,16 @@
 		  "runs 'editmulti:done'.\n"\
 		  "Otherwise runs 'edit:delete-next-char'\n"),
+
+  OWLCMD_VOID_CTX("editmulti:forward-paragraph", owl_editwin_forward_paragraph,
+                  OWL_CTX_EDITMULTI,
+                  "Move forward to end of paragraph.",
+                  "",
+                  "Move the point to the end of the current paragraph"),
+
+  OWLCMD_VOID_CTX("editmulti:backward-paragraph", owl_editwin_backward_paragraph,
+                  OWL_CTX_EDITMULTI,
+                  "Move backward to the start of paragraph.",
+                  "",
+                  "Move the point to the start of the current paragraph"),
 
   /****************************************************************/
@@ -2658,5 +2695,5 @@
   owl_function_makemsg("Command cancelled.");
 
-  if(e->echochar == 0) {
+  if(owl_editwin_get_echochar(e) == 0) {
     hist=owl_editwin_get_history(e);
     owl_history_store(hist, owl_editwin_get_text(e));
Index: configure.ac
===================================================================
--- configure.ac	(revision c08a72568ffb178aa943639541c5ce61a7fd77d6)
+++ configure.ac	(revision c08a72568ffb178aa943639541c5ce61a7fd77d6)
@@ -1,5 +1,5 @@
 dnl $Id$
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT([BarnOwl],[1.1.2],[bug-barnowl@mit.edu])
+AC_INIT([BarnOwl],[1.3],[bug-barnowl@mit.edu])
 AM_INIT_AUTOMAKE([-Wall foreign])
 
@@ -42,15 +42,26 @@
         AC_MSG_RESULT(no)
 fi
-PROTECT_CFLAGS=${PROTECT_CFLAGS-"-fstack-protector"}
-SAVE_CFLAGS=$CFLAGS
-CFLAGS="$CFLAGS $PROTECT_CFLAGS"
-AC_MSG_CHECKING(whether protection cflags work)
-AC_COMPILE_IFELSE(int i;,
-    [AC_MSG_RESULT(yes)],
-    [AC_MSG_RESULT(no)
-    CFLAGS=$SAVE_CFLAGS])
 
+AC_ARG_WITH([stack-protector],
+  [AS_HELP_STRING([--with-stack-protector],
+    [Enable gcc -fstack-protector])],
+  [],
+  [with_stack_protector=check])
 
-AC_CHECK_LIB(ssp, __stack_chk_guard)
+AS_IF([test "x$with_stack_protector" != xno],
+  [
+    SAVE_CFLAGS=$CFLAGS
+    CFLAGS="$CFLAGS -fstack-protector"
+    AC_MSG_CHECKING(whether protection cflags work)
+    AC_COMPILE_IFELSE(int i;,
+        [AC_MSG_RESULT(yes)],
+        [AC_MSG_RESULT(no)
+        CFLAGS=$SAVE_CFLAGS
+        if test "x$with_stack_protector" != xcheck; then
+          AC_MSG_FAILURE([--with-stack-protector selected, but gcc does support it.])
+        fi])
+    AC_CHECK_LIB(ssp, __stack_chk_guard)
+  ])
+
 AC_CHECK_LIB(ncursesw, initscr,, AC_MSG_ERROR(No libncursesw found.))
 AC_CHECK_LIB(com_err, com_err)
@@ -60,7 +71,25 @@
 AC_CHECK_LIB(des425, des_cbc_encrypt,,AC_CHECK_LIB(crypto,DES_cbc_encrypt))
 AC_CHECK_LIB(resolv, res_search)
-dnl AC_CHECK_LIB(zephyr, ZGetSender,, AC_MSG_ERROR(No zephyr library found.))
-AC_CHECK_LIB(zephyr, ZGetSender)
-AC_CHECK_LIB(zephyr, ZInitLocationInfo, AC_DEFINE([HAVE_LIBZEPHYR_ZINITLOCATIONINFO], [], [Have ZInitLocationInfo]),)
+
+AC_ARG_WITH([zephyr],
+  [AS_HELP_STRING([--with-libzephyr],
+    [Disable Zephyr support])],
+  [],
+  [with_zephyr=check])
+
+AS_IF([test "x$with_zephyr" != xno],
+  [AC_CHECK_LIB([zephyr], [ZGetSender],
+   [LIBS="$LIBS -lzephyr"
+    AC_DEFINE([HAVE_LIBZEPHYR], [1],
+                [Define if you have libzephyr])
+    AC_CHECK_LIB([zephyr], [ZInitLocationInfo],
+      AC_DEFINE([HAVE_LIBZEPHYR_ZINITLOCATIONINFO], [1],
+                [Have ZInitLocationInfo]),)
+   ],
+   [if test "x$with_zephyr" != xcheck; then
+      AC_MSG_FAILURE(
+        [--with-libzephyr was given, but libzephyr does not seem to be available.])
+    fi
+   ])])
 
 AC_CHECK_FUNCS(use_default_colors resizeterm des_string_to_key des_key_sched des_ecb_encrypt)
Index: editwin.c
===================================================================
--- editwin.c	(revision 50e671ca58bb9d2a3f7ca0cac269bc966a246a95)
+++ editwin.c	(revision 72ab15f8f49554cca076d3ea0271bfe01fce95c0)
@@ -7,5 +7,101 @@
 static const char fileIdent[] = "$Id$";
 
-#define INCR 5000
+#define VALID_EXCURSION	(0x9a2b4729)
+
+typedef struct _owl_editwin_excursion { /*noproto*/
+  int valid;
+  int index;
+  int mark;
+  int goal_column;
+  int lock;
+  struct _owl_editwin_excursion *next;
+} oe_excursion;
+
+struct _owl_editwin { /*noproto*/
+  char *buff;
+  owl_history *hist;
+  int bufflen;
+  int allocated;
+  int index;
+  int mark;
+  char *killbuf;
+  int goal_column;
+  int topindex;
+  int cursorx;
+  int winlines, wincols, fillcol, wrapcol;
+  WINDOW *curswin;
+  int style;
+  int lock;
+  int dotsend;
+  int echochar;
+  oe_excursion *excursions;
+
+  char *command;
+  void (*callback)(struct _owl_editwin*);
+  void *cbdata;
+};
+
+static void oe_reframe(owl_editwin *e);
+static void oe_save_excursion(owl_editwin *e, oe_excursion *x);
+static void oe_release_excursion(owl_editwin *e, oe_excursion *x);
+static void oe_restore_excursion(owl_editwin *e, oe_excursion *x);
+static void oe_restore_mark_only(owl_editwin *e, oe_excursion *x);
+static int oe_count_glyphs(char *s);
+static int oe_char_width(gunichar c, int column);
+static int oe_region_width(owl_editwin *e, int start, int end, int width);
+static int oe_find_display_line(owl_editwin *e, int *x, int index);
+static void oe_insert_char(owl_editwin *e, gunichar c);
+static int owl_editwin_limit_maxcols(int v, int maxv);
+static int owl_editwin_check_dotsend(owl_editwin *e);
+static int owl_editwin_is_char_in(owl_editwin *e, char *set);
+static gunichar owl_editwin_get_char_at_point(owl_editwin *e);
+static int owl_editwin_replace_internal(owl_editwin *e, int replace, char *s);
+static char *oe_copy_buf(owl_editwin *e, char *buf, int len);
+static int oe_copy_region(owl_editwin *e);
+static char *oe_chunk(owl_editwin *e, int start, int end);
+
+#define INCR 4096
+
+#define WHITESPACE " \n\t"
+
+owl_editwin *owl_editwin_allocate(void)
+{
+  owl_editwin *e;
+  e = owl_malloc(sizeof(owl_editwin));
+  memset(e, 0, sizeof(*e));
+  return e;
+}
+
+static int oe_count_glyphs(char *s)
+{
+  int count = 0;
+  char *p;
+
+  for(p = s; *p != 0; p = g_utf8_find_next_char(p, NULL))
+    if (!g_unichar_ismark(g_utf8_get_char(p)))
+      count++;
+
+  return count;
+}
+
+static inline void oe_set_index(owl_editwin *e, int index)
+{
+  if (index != e->index) {
+    e->goal_column = -1;
+    e->cursorx = -1;
+  }
+  e->index = index;
+}
+
+static inline void oe_set_mark(owl_editwin *e, int mark)
+{
+  e->mark = mark;
+}
+
+void owl_editwin_set_mark(owl_editwin *e)
+{
+  oe_set_mark(e, e->index);
+  /* owl_function_makemsg("Mark set."); */
+}
 
 /* initialize the editwin e.
@@ -14,17 +110,19 @@
 void owl_editwin_init(owl_editwin *e, WINDOW *win, int winlines, int wincols, int style, owl_history *hist)
 {
-  e->buff=owl_malloc(INCR);  
+  e->buff=owl_malloc(INCR);
   e->buff[0]='\0';
   e->bufflen=0;
   e->hist=hist;
   e->allocated=INCR;
-  e->buffx=0;
-  e->buffy=0;
-  e->topline=0;
-  e->winlines=winlines;
-  e->wincols=wincols;
-  e->fillcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxfillcols(&g));
-  e->wrapcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxwrapcols(&g));
-  e->curswin=win;
+  oe_set_index(e, 0);
+  oe_set_mark(e, -1);
+  if (e->killbuf != NULL)
+    free(e->killbuf);
+  e->killbuf = NULL;
+  e->goal_column = -1;
+  e->cursorx = -1;
+  e->topindex = 0;
+  e->excursions = NULL;
+  owl_editwin_set_curswin(e, win, winlines, wincols);
   e->style=style;
   if ((style!=OWL_EDITWIN_STYLE_MULTILINE) &&
@@ -69,9 +167,4 @@
 }
 
-void owl_editwin_set_history(owl_editwin *e, owl_history *h)
-{
-  e->hist=h;
-}
-
 owl_history *owl_editwin_get_history(owl_editwin *e)
 {
@@ -84,27 +177,32 @@
 }
 
-void owl_editwin_set_command(owl_editwin *e, char *command) {
+void owl_editwin_set_command(owl_editwin *e, char *command)
+{
   if(e->command) owl_free(e->command);
   e->command = owl_strdup(command);
 }
 
-char *owl_editwin_get_command(owl_editwin *e) {
+char *owl_editwin_get_command(owl_editwin *e)
+{
   if(e->command) return e->command;
   return "";
 }
 
-void owl_editwin_set_callback(owl_editwin *e, void (*cb)(owl_editwin*)) {
+void owl_editwin_set_callback(owl_editwin *e, void (*cb)(owl_editwin*))
+{
   e->callback = cb;
 }
 
-void (*owl_editwin_get_callback(owl_editwin *e))(owl_editwin*) {
+void (*owl_editwin_get_callback(owl_editwin *e))(owl_editwin*)
+{
   return e->callback;
 }
 
-void owl_editwin_set_cbdata(owl_editwin *e, void *data) {
+void owl_editwin_set_cbdata(owl_editwin *e, void *data)
+{
   e->cbdata = data;
 }
 
-void* owl_editwin_get_cbdata(owl_editwin *e) {
+void *owl_editwin_get_cbdata(owl_editwin *e) {
   return e->cbdata;
 }
@@ -121,6 +219,7 @@
 }
 
-int owl_editwin_limit_maxcols(int v, int maxv)
-{
+static int owl_editwin_limit_maxcols(int v, int maxv)
+{
+  /* maxv > 5 ? MAX(v, vax) : v */
   if (maxv > 5 && v > maxv) {
     return(maxv);
@@ -135,18 +234,10 @@
 void owl_editwin_set_locktext(owl_editwin *e, char *text)
 {
-  
-  int x, y;
-
-  x=e->buffx;
-  y=e->buffy;
-  e->buffx=0;
-  e->buffy=0;
-  owl_editwin_overwrite_string(e, text);
-  owl_editwin_overwrite_char(e, '\0');
-  e->lock=strlen(text);
-  /* if (text[e->lock-1]=='\n') e->lock--; */
-  /*  e->buffx=x; */
-  /*  e->buffy=y; */
-  _owl_editwin_set_xy_by_index(e, e->lock);
+  oe_set_index(e, 0);
+  e->lock = 0;
+  owl_editwin_replace(e, e->bufflen, text);
+  e->buff[e->bufflen] = 0;
+  e->lock=e->bufflen;
+  oe_set_index(e, e->lock);
   owl_editwin_redisplay(e, 0);
 }
@@ -159,7 +250,6 @@
 void owl_editwin_new_style(owl_editwin *e, int newstyle, owl_history *h)
 {
-  char *ptr;
-
-  owl_editwin_set_history(e, h);
+  e->hist = h;
+
   if (e->style==newstyle) return;
 
@@ -170,13 +260,7 @@
 
     /* nuke everything after the first line */
-    if (e->bufflen > 0) {
-      ptr=strchr(e->buff, '\n')-1;
-      if (ptr) {
-	e->bufflen=ptr - e->buff;
-	e->buff[e->bufflen]='\0';
-	e->buffx=0;
-	e->buffy=0;
-      }
-    }
+    owl_editwin_move_to_top(e);
+    owl_editwin_move_to_end_of_line(e);
+    owl_editwin_replace(e, oe_count_glyphs(e->buff + e->index),  "");
   }
 }
@@ -195,16 +279,13 @@
 {
 
-  int lock;
+  int lock = e->lock;
   int dotsend=e->dotsend;
   char *locktext=NULL;
   char echochar=e->echochar;
 
-  lock=0;
-  if (e->lock > 0) {
-    lock=1;
-
-    locktext=owl_malloc(e->lock+20);
-    strncpy(locktext, e->buff, e->lock);
-    locktext[e->lock]='\0';
+  if (lock > 0) {
+    locktext = owl_malloc(lock+1);
+    strncpy(locktext, e->buff, lock);
+    locktext[lock] = 0;
   }
 
@@ -222,24 +303,213 @@
   }
 
-  if (locktext) owl_free(locktext);
-  owl_editwin_adjust_for_locktext(e);
-}
-
-/* malloc more space for the buffer */
-void _owl_editwin_addspace(owl_editwin *e)
-{
-  e->buff=owl_realloc(e->buff, e->allocated+INCR);
-  if (!e->buff) {
-    /* error */
-    return;
-  }
-  e->allocated+=INCR;
+  if (locktext)
+    owl_free(locktext);
+
+  oe_set_index(e, lock);
 }
 
 void owl_editwin_recenter(owl_editwin *e)
 {
-  e->topline=e->buffy-(e->winlines/2);
-  if (e->topline<0) e->topline=0;
-  if (e->topline>owl_editwin_get_numlines(e)) e->topline=owl_editwin_get_numlines(e);
+  e->topindex = -1;
+}
+
+static void oe_save_excursion(owl_editwin *e, oe_excursion *x)
+{
+  x->index = e->index;
+  x->mark = e->mark;
+  x->goal_column = e->goal_column;
+  x->lock = e->lock;
+
+  x->valid = VALID_EXCURSION;
+  x->next = e->excursions;
+  e->excursions = x;
+}
+
+static void oe_release_excursion(owl_editwin *e, oe_excursion *x)
+{
+  oe_excursion *p;
+
+  x->valid = 0;
+  if (e->excursions == NULL)
+    /* XXX huh. */ ;
+  else if (e->excursions == x)
+    e->excursions = x->next;
+  else {
+    for (p = e->excursions; p->next != NULL; p = p->next)
+      if (p->next == x) {
+	p->next = p->next->next;
+	break;
+      }
+    /* and if we ran off the end? XXX */
+  }
+}
+
+static void oe_restore_excursion(owl_editwin *e, oe_excursion *x)
+{
+  if (x->valid == VALID_EXCURSION) {
+    oe_set_index(e, x->index);
+    e->goal_column = x->goal_column;
+    e->mark = x->mark;
+    e->lock = x->lock;
+
+    oe_release_excursion(e, x);
+  }
+}
+
+static void oe_restore_mark_only(owl_editwin *e, oe_excursion *x)
+{
+  if (x->valid == VALID_EXCURSION) {
+    e->mark = x->mark;
+
+    oe_release_excursion(e, x);
+  }
+}
+
+/* External interface to oe_save_excursion */
+owl_editwin_excursion *owl_editwin_begin_excursion(owl_editwin *e)
+{
+  owl_editwin_excursion *x = owl_malloc(sizeof *x);
+  oe_save_excursion(e, x);
+  return x;
+}
+
+void owl_editwin_end_excursion(owl_editwin *e, owl_editwin_excursion *x)
+{
+  oe_restore_excursion(e, x);
+  owl_free(x);
+}
+
+static inline char *oe_next_point(owl_editwin *e, char *p)
+{
+  char *boundary = e->buff + e->bufflen + 1;
+  char *q;
+
+  q = g_utf8_find_next_char(p, boundary);
+  while (q && g_unichar_ismark(g_utf8_get_char(q)))
+    q = g_utf8_find_next_char(q, boundary);
+
+  if (q == p)
+    return NULL;
+  return q;
+}
+
+static inline char *oe_prev_point(owl_editwin *e, char *p)
+{
+  char *boundary = e->buff + e->lock;
+
+  p = g_utf8_find_prev_char(boundary, p);
+  while (p && g_unichar_ismark(g_utf8_get_char(p)))
+    p = g_utf8_find_prev_char(boundary, p);
+
+  return p;
+}
+
+static int oe_char_width(gunichar c, int column)
+{
+  int cw;
+
+  if (c == 9) /* TAB */
+    return TABSIZE - column % TABSIZE;
+
+  cw = mk_wcwidth(c);
+
+  if (cw < 0) /* control characters */
+    cw = 0;
+
+  return cw;
+}
+
+static int oe_find_display_line(owl_editwin *e, int *x, int index)
+{
+  int width = 0, cw;
+  gunichar c;
+  char *p;
+
+  while(1) {
+    /* note the position of the dot */
+    if (x != NULL && index == e->index && width < e->wincols)
+      *x = width;
+
+    /* get the current character */
+    c = g_utf8_get_char(e->buff + index);
+
+    /* figure out how wide it is */
+    cw = oe_char_width(c, width);
+
+    if (width + cw > e->wincols) {
+      if (x != NULL && *x == width)
+	*x = -1;
+      break;
+    }
+    width += cw;
+
+    if (c == '\n') {
+      if (width < e->wincols)
+	++index; /* skip the newline */
+      break;
+    }
+
+    /* find the next character */
+    p = oe_next_point(e, e->buff + index);
+    if (p == NULL) { /* we ran off the end */
+      if (x != NULL && e->index > index)
+	*x = width + 1;
+      break;
+    }
+    index = p - e->buff;
+
+  }
+  return index;
+}
+
+static void oe_reframe(owl_editwin *e) {
+  oe_excursion x;
+  int goal = e->winlines / 2;
+  int index;
+  int count = 0;
+  int point;
+  int n, i;
+  int last;
+
+  oe_save_excursion(e, &x);
+  /* step back line-by-line through the buffer until we have >= goal lines of
+     display text */
+  e->lock = 0; /* we can (must) tread on the locktext */
+
+  point = e->index;
+  last = -1;
+  while (count < goal) {
+    index = e->index;
+    owl_editwin_move_to_beginning_of_line(e);
+    if (last == e->index)
+      break;
+    last = e->index;
+    for (n = 0, i = e->index; i < index; n++)
+      i = oe_find_display_line(e, NULL, i);
+    count += n == 0 ? 1 : n;
+    if (count < goal)
+      owl_editwin_point_move(e, -1);
+  }
+
+  e->topindex = e->index;
+  /* if we overshot, backtrack */
+  for (n = 0; n < (count - goal); n++)
+    e->topindex = oe_find_display_line(e, NULL, e->topindex);
+
+  oe_restore_excursion(e, &x);
+}
+
+static void oe_addnec(owl_editwin *e, int count)
+{
+  int i;
+
+  for (i = 0; i < count; i++)
+    waddch(e->curswin, e->echochar);
+}
+
+static void oe_mvaddnec(owl_editwin *e, int y, int x, int count)
+{
+  wmove(e->curswin, y, x);
+  oe_addnec(e, count);
 }
 
@@ -248,104 +518,143 @@
 void owl_editwin_redisplay(owl_editwin *e, int update)
 {
-  
-  char *ptr1, *ptr2, *ptr3, *buff;
-  int i;
-
-  werase(e->curswin);
-  wmove(e->curswin, 0, 0);
-
-  /* start at topline */
-  ptr1 = e->buff;
-  for (i = 0; i < e->topline; i++) {
-    ptr2 = strchr(ptr1, '\n');
-    if (!ptr2) {
-      /* we're already on the last line */
-      break;
+  int x = -1, y = -1, t;
+  int line, index, lineindex, times = 0;
+
+  do {
+    werase(e->curswin);
+
+    if (e->topindex == -1 || e->index < e->topindex)
+      oe_reframe(e);
+
+    line = 0;
+    index = e->topindex;
+    while(line < e->winlines) {
+      lineindex = index;
+      t = -1;
+      index = oe_find_display_line(e, &t, lineindex);
+      if (x == -1 && t != -1)
+	x = t, y = line;
+      if (index - lineindex) {
+	if (!e->echochar)
+	  mvwaddnstr(e->curswin, line, 0,
+		     e->buff + lineindex,
+		     index - lineindex);
+	else {
+	  if(lineindex < e->lock) {
+	    mvwaddnstr(e->curswin, line, 0,
+		       e->buff + lineindex,
+		       MIN(index - lineindex,
+			   e->lock - lineindex));
+	    if (e->lock < index)
+	      oe_addnec(e,
+			oe_region_width(e, e->lock, index,
+					oe_region_width(e, lineindex, e->lock, 0)));
+	  } else
+	    oe_mvaddnec(e, line, 0, oe_region_width(e, line, index, 0));
+	}
+      }
+      line++;
     }
-    ptr1 = ptr2 + 1;
-  }
-  /* ptr1 now stores the starting point */
-
-  /* find the ending point and store it in ptr3 */
-  ptr2 = ptr1;
-  ptr3 = ptr1;
-  for (i = 0; i < e->winlines; i++) {
-    ptr3 = strchr(ptr2, '\n');
-    if (!ptr3) {
-      /* we've hit the last line */
-      /* print everything to the end */
-      ptr3 = e->buff + e->bufflen - 1;
-      ptr3--;
-      break;
+    if (x == -1)
+	e->topindex = -1; /* force a reframe */
+    times++;
+  } while(x == -1 && times < 3);
+
+  wmove(e->curswin, y, x);
+  e->cursorx = x;
+
+  wnoutrefresh(e->curswin);
+  if (update == 1)
+    doupdate();
+}
+
+static inline void oe_fixup(int *target, int start, int end, int change) {
+  if (*target > start) {
+    if (*target < end)
+      *target = end;
+    else
+      *target += change;
+  }
+}
+
+int owl_editwin_replace_region(owl_editwin *e, char *s)
+{
+  oe_excursion x;
+  oe_save_excursion(e, &x);
+  int ret;
+
+  if(e->index > e->mark) {
+    owl_editwin_exchange_point_and_mark(e);
+  }
+
+  ret = owl_editwin_replace_internal(e, e->mark - e->index, s);
+
+  oe_restore_excursion(e, &x);
+
+  return ret;
+}
+
+/* replace 'replace' characters at the point with s, returning the change in size */
+int owl_editwin_replace(owl_editwin *e, int replace, char *s)
+{
+  int start, end, i;
+  char *p;
+
+  if (!g_utf8_validate(s, -1, NULL)) {
+    owl_function_debugmsg("owl_editwin_insert_string: received non-utf-8 string.");
+    return 0;
+  }
+
+  start = e->index;
+  for (i = 0, p = e->buff + start; i < replace && p != NULL; i++)
+    p = oe_next_point(e, p);
+  if (p != NULL)
+    end = p - e->buff;
+  else
+    end = e->bufflen;
+
+  return owl_editwin_replace_internal(e, end - start, s);
+}
+
+static int owl_editwin_replace_internal(owl_editwin *e, int replace, char *s)
+{
+  int start, end, free, need, size, change;
+  oe_excursion *x;
+  char *p;
+
+  start = e->index;
+  end   = start + replace;
+
+  free = e->allocated - e->bufflen + end - start;
+
+  need = strlen(s) - free;
+  if (need > 0) {
+    size = e->allocated + need + INCR - (need % INCR);
+    p = owl_realloc(e->buff, size);
+    if (p == NULL) {
+      /* XXX signal impending doom somehow and don't do anything */
+      return 0;
     }
-    ptr2 = ptr3 + 1;
-  }
-  ptr3 += 2;
-
-  buff = owl_malloc(ptr3 - ptr1 + 50);
-  strncpy(buff, ptr1, ptr3 - ptr1);
-  buff[ptr3 - ptr1] = '\0';
-  if (e->echochar == '\0') {
-    waddstr(e->curswin, buff);
-  } else {
-    /* translate to echochar, *except* for the locktext */
-    int len;
-    int dolocklen = e->lock - (ptr1 - e->buff);
-    char *locktext;
-    char tmp = e->buff[dolocklen];
-
-    e->buff[dolocklen] = '\0';
-    locktext = owl_strdup(e->buff);
-    e->buff[dolocklen] = tmp;
-
-    waddstr(e->curswin, locktext);
-    
-    len = strlen(buff);
-    for (i = 0; i < len-dolocklen; i++) {
-      waddch(e->curswin, e->echochar);
-    }
-  }
-  wmove(e->curswin, e->buffy-e->topline, e->buffx + _owl_editwin_cursor_adjustment(e));
-  wnoutrefresh(e->curswin);
-  if (update == 1) {
-    doupdate();
-  }
-  owl_free(buff);
-}
-
-/* Remove n bytes at cursor. */
-void _owl_editwin_remove_bytes(owl_editwin *e, int n) /*noproto*/
-{
-  int i = _owl_editwin_get_index_from_xy(e) + n;
-  for (; i < e->bufflen; i++) {
-    e->buff[i-n] = e->buff[i];
-  }
-  
-  e->bufflen -= n;
-  e->buff[e->bufflen] = '\0';
-}
-
-/* Insert n bytes at cursor.*/
-void _owl_editwin_insert_bytes(owl_editwin *e, int n) /*noproto*/
-{
-  int i, z;
-
-  if ((e->bufflen + n) > (e->allocated - 5)) {
-    _owl_editwin_addspace(e);
-  }
-
-  z = _owl_editwin_get_index_from_xy(e);
-
-  if(z != e->bufflen) {
-    for (i = e->bufflen + n - 1; i > z; i--) {
-      e->buff[i] = e->buff[i - n];
-    }
-  }
-
-  e->bufflen += n;
-  e->buff[e->bufflen] = '\0';
-
-}
-
+    e->buff = p;
+    e->allocated = size;
+  }
+
+  memmove(e->buff + start + strlen(s), e->buff + end, e->bufflen + 1 - end);
+  memcpy(e->buff + start, s, strlen(s));
+  change = start - end + strlen(s);
+  e->bufflen += change;
+  e->index += strlen(s);
+
+  /* fix up the mark */
+  oe_fixup(&e->mark, start, end, change);
+  oe_fixup(&e->topindex, start, end, change);
+  /* fix up any saved points after the replaced area */
+  for (x = e->excursions; x != NULL; x = x->next) {
+    oe_fixup(&x->index, start, end, change);
+    oe_fixup(&x->mark, start, end, change);
+  }
+
+  return change;
+}
 
 /* linewrap the word just before the cursor.
@@ -353,173 +662,29 @@
  * returns -1 if we could not wrap.
  */
-int _owl_editwin_linewrap_word(owl_editwin *e)
-{
-  int x, y;
-  int i;
-  char *ptr1, *start;
+static void _owl_editwin_linewrap_word(owl_editwin *e)
+{
+  oe_excursion x;
   gunichar c;
 
-  /* saving values */
-  x = e->buffx;
-  y = e->buffy;
-  start = e->buff + e->lock;
-
-  ptr1 = e->buff + _owl_editwin_get_index_from_xy(e);
-  ptr1 = g_utf8_find_prev_char(start, ptr1);
-
-  while (ptr1) {
-    c = g_utf8_get_char(ptr1);
-    if (owl_util_can_break_after(c)) {
-      if (c != ' ') {
-        i = ptr1 - e->buff;
-        _owl_editwin_set_xy_by_index(e, i);
-        _owl_editwin_insert_bytes(e, 1);
-        /* _owl_editwin_insert_bytes may move e->buff. */
-        ptr1 = e->buff + i;
-      }
-      *ptr1 = '\n';
-      return 0;
+  oe_save_excursion(e, &x);
+
+  while (owl_editwin_point_move(e, -1)) {
+    c = owl_editwin_get_char_at_point(e);
+    if (owl_util_can_break_after(c) || c == '\n') {
+      if (c != '\n')
+        owl_editwin_replace(e, c != ' ' ? 0 : 1, "\n");
+      break;
     }
-    else if (c == '\n') {
-      return 0;
-    }
-    ptr1 = g_utf8_find_prev_char(start, ptr1);
-  }
-  return -1;
-}
-
-/* insert a character at the current point (shift later
- * characters over)
- */
-void owl_editwin_insert_char(owl_editwin *e, gunichar c)
-{
-  int z, i, ret, len;
-  char tmp[6];
-  memset(tmp, '\0', 6);
-
-  /* \r is \n */
-  if (c == '\r') {
-    c = '\n';
-  }
-
-  if (c == '\n' && e->style == OWL_EDITWIN_STYLE_ONELINE) {
-    /* perhaps later this will change some state that allows the string
-       to be read */
-    return;
-  }
-
-  g_unichar_to_utf8(c, tmp);
-  len = strlen(tmp);
-
-  /* make sure there is enough memory for the new text */
-  if ((e->bufflen + len) > (e->allocated - 5)) {
-    _owl_editwin_addspace(e);
-  }
-
-  /* get the insertion point */
-  z = _owl_editwin_get_index_from_xy(e);
-
-  /* If we're going to insert at the last column do word wrapping, unless it's a \n */
-  if ((e->buffx + 1 == e->wrapcol) && (c != '\n')) {
-    ret = _owl_editwin_linewrap_word(e);
-    if (ret == -1) {
-      /* we couldn't wrap, insert a hard newline instead */
-      owl_editwin_insert_char(e, '\n');
-    }
-  }
-
-  /* shift all the other characters right */
-  _owl_editwin_insert_bytes(e, len);
-
-  /* insert the new character */
-  for(i = 0; i < len; i++) {
-    e->buff[z + i] = tmp[i];
-  }
-
-  /* advance the cursor */
-  z += len;
-  _owl_editwin_set_xy_by_index(e, z);
-}
-
-/* overwrite the character at the current point with 'c' */
-void owl_editwin_overwrite_char(owl_editwin *e, gunichar c)
-{
-  int z, oldlen, newlen, i;
-  char tmp[6];
-  memset(tmp, '\0', 6);
-
-  /* \r is \n */
-  if (c == '\r') {
-    c = '\n';
-  }
-  
-  if (c == '\n' && e->style == OWL_EDITWIN_STYLE_ONELINE) {
-    /* perhaps later this will change some state that allows the string
-       to be read */
-    return;
-  }
-
-  g_unichar_to_utf8(c, tmp);
-  newlen = strlen(tmp);
-
-  z = _owl_editwin_get_index_from_xy(e);
-  {
-    char *t = g_utf8_find_next_char(e->buff + z, NULL);
-    oldlen = (t ? (t - (e->buff + z)) : 0);
-  }
-
-  /* only if we are at the end of the buffer do we create new space here */
-  if (z == e->bufflen) {
-    if ((e->bufflen+newlen) > (e->allocated-5)) {
-      _owl_editwin_addspace(e);
-    }
-  }
-  /* if not at the end of the buffer, adjust based in char size difference. */ 
-  else if (oldlen > newlen) {
-    _owl_editwin_remove_bytes(e, oldlen-newlen);
-  }
-  else /* oldlen < newlen */ {
-    _owl_editwin_insert_bytes(e, newlen-oldlen);
-  }
-  /* Overwrite the old char*/
-  for (i = 0; i < newlen; i++) {
-    e->buff[z+i] = tmp[i];
-  }
-       
-  /* housekeeping */
-  if (z == e->bufflen) {
-    e->bufflen += newlen;
-    e->buff[e->bufflen] = '\0';
-  }
-  
-  /* advance the cursor */
-  z += newlen;
-  _owl_editwin_set_xy_by_index(e, z);
+  }
+
+  oe_restore_excursion(e, &x);
 }
 
 /* delete the character at the current point, following chars
  * shift left.
- */ 
+ */
 void owl_editwin_delete_char(owl_editwin *e)
 {
-  int z;
-  char *p1, *p2;
-  gunichar c;
-
-  if (e->bufflen == 0) return;
-  
-  /* get the deletion point */
-  z = _owl_editwin_get_index_from_xy(e);
-
-  if (z == e->bufflen) return;
-
-  p1 = e->buff + z;
-  p2 = g_utf8_next_char(p1);
-  c = g_utf8_get_char(p2);
-  while (g_unichar_ismark(c)) {
-    p2 = g_utf8_next_char(p2);
-    c = g_utf8_get_char(p2);
-  }
-  _owl_editwin_remove_bytes(e, p2-p1);
+  owl_editwin_replace(e, 1, "");
 }
 
@@ -527,48 +692,36 @@
  * advance the pointer.  If point is at beginning of buffer do
  * nothing.  If point is after the last character swap point-1 with
- * point-2.  (Behaves as observed in tcsh and emacs).  
+ * point-2.  (Behaves as observed in tcsh and emacs).
  */
 void owl_editwin_transpose_chars(owl_editwin *e)
 {
-  int z;
-  char *p1, *p2, *p3, *tmp;
+  char *middle, *end, *start, *tmp;
 
   if (e->bufflen == 0) return;
-  
-  /* get the cursor point */
-  z = _owl_editwin_get_index_from_xy(e);
-
-  if (z == e->bufflen) {
-    /* point is after last character */
-    z--;
-  }  
-
-  if (z - 1 < e->lock) {
-    /* point is at beginning of buffer, do nothing */
+
+  if (e->index == e->bufflen)
+    owl_editwin_point_move(e, -1);     /* point is after last character */
+
+  if (owl_editwin_at_beginning_of_buffer(e))
+    return;     /* point is at beginning of buffer, do nothing */
+
+  /* Transpose two utf-8 unicode glyphs. */
+  middle = e->buff + e->index;
+
+  end = oe_next_point(e, middle);
+  if (end == NULL)
     return;
-  }
-
-  /* Transpose two utf-8 unicode glyphs. */
-  p1 = e->buff + z;
-
-  p2 = g_utf8_find_next_char(p1, NULL);
-  while (p2 != NULL && g_unichar_ismark(g_utf8_get_char(p2))) {
-    p2 = g_utf8_find_next_char(p2, NULL);
-  }
-  if (p2 == NULL) return;
-
-  p3 = g_utf8_find_prev_char(e->buff, p1);
-  while (p3 != NULL && g_unichar_ismark(g_utf8_get_char(p3))) {
-    p3 = g_utf8_find_prev_char(p3, NULL);
-  }
-  if (p3 == NULL) return;
-
-  tmp = owl_malloc(p2 - p3 + 5);
-  *tmp = '\0';
-  strncat(tmp, p1, p2 - p1);
-  strncat(tmp, p3, p1 - p3);
-  strncpy(p3, tmp, p2 - p3);
-  owl_free(tmp);
-  _owl_editwin_set_xy_by_index(e, p3 - e->buff);
+
+  start = oe_prev_point(e, middle);
+  if (start == NULL)
+    return;
+
+  tmp = owl_malloc((end - start) + 1);
+  tmp[(end - start)] = 0;
+  memcpy(tmp, middle, end - middle);
+  memcpy(tmp + (end - middle), start, middle - start);
+
+  owl_editwin_point_move(e, -1);
+  owl_editwin_replace(e, 2, tmp);
 }
 
@@ -576,161 +729,163 @@
  * right
  */
-void owl_editwin_insert_string(owl_editwin *e, char *string)
+void owl_editwin_insert_string(owl_editwin *e, char *s)
+{
+  owl_editwin_replace(e, 0, s);
+}
+
+/* We assume index is not set to point to a mid-char */
+static gunichar owl_editwin_get_char_at_point(owl_editwin *e)
+{
+  return g_utf8_get_char(e->buff + e->index);
+}
+
+void owl_editwin_exchange_point_and_mark(owl_editwin *e) {
+  int tmp;
+
+  if (e->mark != -1) {
+    tmp = e->mark;
+    owl_editwin_set_mark(e);
+    oe_set_index(e, tmp);
+  }
+}
+
+int owl_editwin_point_move(owl_editwin *e, int delta)
 {
   char *p;
-  gunichar c;
-  if (!g_utf8_validate(string, -1, NULL)) {
-    owl_function_debugmsg("owl_editwin_insert_string: received non-utf-8 string.");
-    return;
-  }
-  p = string;
-  c = g_utf8_get_char(p);
-  while (c) {
-    _owl_editwin_process_char(e, c);
-    p = g_utf8_next_char(p);
-    c = g_utf8_get_char(p);
-  }
-}
-
-/* write 'string' at the current point, overwriting text that is
- * already there
- */
-
-void owl_editwin_overwrite_string(owl_editwin *e, char *string)
+  int change, d = 0;
+
+  change = MAX(delta, - delta);
+  p = e->buff + e->index;
+
+  while (d < change && p != NULL) {
+    if (delta > 0)
+      p = oe_next_point(e, p);
+    else
+      p = oe_prev_point(e, p);
+    if (p != NULL) {
+      oe_set_index(e, p - e->buff);
+      d++;
+    }
+  }
+
+  return delta > 0 ? d : -d;
+}
+
+int owl_editwin_at_beginning_of_buffer(owl_editwin *e) {
+  if (e->index == e->lock)
+    return 1;
+
+  return 0;
+}
+
+int owl_at_end_of_buffer(owl_editwin *e) {
+  if (e->index == e->bufflen)
+    return 1;
+
+  return 0;
+}
+
+int owl_editwin_at_beginning_of_line(owl_editwin *e) /*noproto*/
+{
+  oe_excursion x;
+  int ret;
+
+  if (owl_editwin_at_beginning_of_buffer(e))
+    return 1;
+
+  oe_save_excursion(e, &x);
+  owl_editwin_point_move(e, -1);
+  ret = (owl_editwin_get_char_at_point(e) == '\n');
+  oe_restore_excursion(e, &x);
+
+  return ret;
+}
+
+static int owl_editwin_is_char_in(owl_editwin *e, char *set)
 {
   char *p;
-  gunichar c;
-
-  if (!g_utf8_validate(string, -1, NULL)) {
-    owl_function_debugmsg("owl_editwin_overwrite_string: received non-utf-8 string.");
-    return;
-  }
-  p = string;
-  c = g_utf8_get_char(p);
-  while (c) {
-    owl_editwin_overwrite_char(e, c);
-    p = g_utf8_next_char(p);
-    c = g_utf8_get_char(p);
-  }
-}
-
-/* get the index into e->buff for the current cursor
- * position.
- */
-int _owl_editwin_get_index_from_xy(owl_editwin *e)
-{
-  int i;
-  char *ptr1, *ptr2;
-  gunichar c;
-
-  if (e->bufflen == 0) return(0);
-  
-  /* first go to the yth line */
-  ptr1 = e->buff;
-  for (i = 0; i < e->buffy; i++) {
-    ptr2= strchr(ptr1, '\n');
-    if (!ptr2) {
-      /* we're already on the last line */
+
+  for (p = set; *p != 0; p = g_utf8_find_next_char(p, NULL))
+    if (owl_editwin_get_char_at_point(e) == g_utf8_get_char(p))
+      return 1;
+  return 0;
+}
+
+int owl_editwin_move_if_in(owl_editwin *e, int delta, char *set)
+{
+  int change, distance = 0;
+  while (owl_editwin_is_char_in(e, set)) {
+    change = owl_editwin_point_move(e, delta);
+    distance += change;
+    if (change == 0)
       break;
+  }
+  return distance;
+}
+
+int owl_editwin_move_if_not_in(owl_editwin *e, int delta, char *set)
+{
+  int change, distance = 0;
+  while (!owl_editwin_is_char_in(e, set)) {
+    change = owl_editwin_point_move(e, delta);
+    distance += change;
+    if (change == 0)
+      break;
+  }
+  return distance;
+}
+
+int owl_editwin_move_to_beginning_of_line(owl_editwin *e)
+{
+  int distance = 0;
+
+  if (!owl_editwin_at_beginning_of_line(e)) {
+    /* move off the \n if were at the end of a line */
+    distance += owl_editwin_point_move(e, -1);
+    distance += owl_editwin_move_if_not_in(e, -1, "\n");
+    if (distance && !owl_editwin_at_beginning_of_buffer(e))
+      distance += owl_editwin_point_move(e, 1);
+  }
+  e->goal_column = 0; /* subtleties */
+
+  return distance;
+}
+
+int owl_editwin_move_to_end_of_line(owl_editwin *e)
+{
+  return owl_editwin_move_if_not_in(e, 1, "\n");
+}
+
+int owl_editwin_line_move(owl_editwin *e, int delta)
+{
+  int goal_column, change, ll, distance;
+  int count = 0;
+
+  change = MAX(delta, -delta);
+
+  goal_column = e->goal_column;
+  distance = owl_editwin_move_to_beginning_of_line(e);
+  goal_column = goal_column == -1 ? -distance : goal_column;
+
+  while(count < change) {
+    if (delta > 0) {
+      distance += owl_editwin_move_if_not_in(e, 1, "\n");
+      distance += owl_editwin_point_move(e, 1);
+    } else {
+      /* I really want to assert delta < 0 here */
+      distance += owl_editwin_point_move(e, -1); /* to the newline on
+						    the previous line */
+      distance += owl_editwin_move_to_beginning_of_line(e);
     }
-    ptr1 = ptr2 + 1;
-  }
-
-  /* now go to the xth cell */
-  ptr2 = ptr1;
-  i = 0;
-  while (ptr2 != NULL && i < e->buffx && (ptr2 - e->buff) < e->bufflen) {
-    c = g_utf8_get_char(ptr2);
-    i += (c == '\n' ? 1 : mk_wcwidth(c));
-    ptr2 = g_utf8_next_char(ptr2);
-  }
-  while(ptr2 != NULL && g_unichar_ismark(g_utf8_get_char(ptr2))) {
-    ptr2 = g_utf8_next_char(ptr2);
-  }
-  if (ptr2 == NULL) return e->bufflen;
-  return(ptr2 - e->buff);
-}
-
-/* We assume x,y are not set to point to a mid-char */
-gunichar _owl_editwin_get_char_at_xy(owl_editwin *e)
-{
-  return g_utf8_get_char(e->buff + _owl_editwin_get_index_from_xy(e));
-}
-
-
-void _owl_editwin_set_xy_by_index(owl_editwin *e, int index)
-{
-  char *ptr1, *ptr2, *target;
-  gunichar c;
-
-  e->buffx = 0;
-  e->buffy = 0;
-
-  ptr1 = e->buff;
-  target = ptr1 + index;
-  /* target sanitizing */
-  if ((target[0] & 0x80) && (~target[0] & 0x40)) {
-    /* middle of a utf-8 character, back up to previous character. */
-    target = g_utf8_find_prev_char(e->buff, target);
-  }
-  c = g_utf8_get_char(target);
-  while (g_unichar_ismark(c) && target > e->buff) {
-    /* Adjust the target off of combining characters and the like. */
-    target = g_utf8_find_prev_char(e->buff, target);
-    c = g_utf8_get_char(target);
-  }
-  /* If we start with a mark, something is wrong.*/
-  if (g_unichar_ismark(c)) return;
-
-  /* Now our target should be acceptable. */
-  ptr2 = strchr(ptr1, '\n');
-  while (ptr2 != NULL && ptr2 < target) {
-    e->buffy++;
-    ptr1 = ptr2 + 1;
-    ptr2 = strchr(ptr1, '\n');
-  }
-  ptr2 = ptr1;
-  while (ptr2 != NULL && ptr2 < target) {
-    c = g_utf8_get_char(ptr2);
-    e->buffx += mk_wcwidth(c);
-    ptr2 = g_utf8_next_char(ptr2);
-  }
-}
-
-int _owl_editwin_cursor_adjustment(owl_editwin *e)
-{
-  char *ptr1, *ptr2;
-  gunichar c;
-  int x, i;
-
-  /* Find line */
-  ptr1 = e->buff;
-  ptr2 = strchr(ptr1, '\n');
-  for (i = 0; ptr2 != NULL && i < e->buffy; i++) {
-    ptr1 = ptr2 + 1;
-    ptr2 = strchr(ptr1, '\n');
-  }
-  ptr2 = ptr1;
-
-  /* Find char */
-  x = 0;
-  while (ptr2 != NULL && x < e->buffx) {
-    if (*ptr2 == '\n') return 0;
-    c = g_utf8_get_char(ptr2);
-    x += mk_wcwidth(c);
-    ptr2 = g_utf8_next_char(ptr2);
-  }
-  
-  /* calculate x offset */
-  return x - e->buffx;
-}
-
-void owl_editwin_adjust_for_locktext(owl_editwin *e)
-{
-  /* if we happen to have the cursor over locked text
-   * move it to be out of the locktext region */
-  if (_owl_editwin_get_index_from_xy(e) < e->lock) {
-    _owl_editwin_set_xy_by_index(e, e->lock);
-  }
+    count++;
+  }
+
+  distance += (ll = owl_editwin_move_to_end_of_line(e));
+  if (ll > goal_column)
+    distance += owl_editwin_point_move(e, goal_column - ll);
+
+  e->goal_column = goal_column;
+
+  return distance;
 }
 
@@ -740,354 +895,298 @@
    * and shift later chars left
    */
-  if (_owl_editwin_get_index_from_xy(e) > e->lock) {
-    owl_editwin_key_left(e);
+  if(owl_editwin_point_move(e, -1))
     owl_editwin_delete_char(e);
-  }
-  owl_editwin_adjust_for_locktext(e);
 }
 
 void owl_editwin_key_up(owl_editwin *e)
 {
-  if (e->buffy > 0) e->buffy--;
-  if (e->buffx >= owl_editwin_get_numcells_on_line(e, e->buffy)) {
-    e->buffx=owl_editwin_get_numcells_on_line(e, e->buffy);
-  }
-
-  /* do we need to scroll? */
-  if (e->buffy-e->topline < 0) {
-    e->topline-=e->winlines/2;
-  }
-
-  owl_editwin_adjust_for_locktext(e);
+  owl_editwin_line_move(e, -1);
 }
 
 void owl_editwin_key_down(owl_editwin *e)
 {
-  /* move down if we can */
-  if (e->buffy+1 < owl_editwin_get_numlines(e)) e->buffy++;
-
-  /* if we're past the last character move back */
-  if (e->buffx >= owl_editwin_get_numcells_on_line(e, e->buffy)) {
-    e->buffx=owl_editwin_get_numcells_on_line(e, e->buffy);
-  }
-
-  /* do we need to scroll? */
-  if (e->buffy-e->topline > e->winlines) {
-    e->topline+=e->winlines/2;
-  }
-
-  /* adjust for locktext */
-  owl_editwin_adjust_for_locktext(e);
+  owl_editwin_line_move(e, 1);
 }
 
 void owl_editwin_key_left(owl_editwin *e)
 {
-  int i;
-  char * p;
-  i = _owl_editwin_get_index_from_xy(e);
-  p = e->buff + i;
-  p = g_utf8_find_prev_char(e->buff, p);
-  while (p && g_unichar_ismark(g_utf8_get_char(p))) {
-    p = g_utf8_find_prev_char(e->buff, p);
-  }
-  if (p == NULL) p = e->buff;
-  _owl_editwin_set_xy_by_index(e, p - e->buff);
-
-  if (e->buffy - e->topline < 0) {
-    e->topline -= e->winlines / 2;
-  }
-
-  /* make sure to avoid locktext */
-  owl_editwin_adjust_for_locktext(e);
+  owl_editwin_point_move(e, -1);
 }
 
 void owl_editwin_key_right(owl_editwin *e)
 {
-  int i;
-  char * p;
-  i = _owl_editwin_get_index_from_xy(e);
-  p = e->buff + i;
-  p = g_utf8_find_next_char(p, NULL);
-  while (p && g_unichar_ismark(g_utf8_get_char(p))) {
-    p = g_utf8_find_next_char(p, NULL);
-  }
-  if (p == NULL) {
-    _owl_editwin_set_xy_by_index(e, e->bufflen);
-  }
-  else {
-    _owl_editwin_set_xy_by_index(e, p - e->buff);
-  }
-
-  /* do we need to scroll down? */
-  if (e->buffy - e->topline >= e->winlines) {
-    e->topline += e->winlines / 2;
-  }
+  owl_editwin_point_move(e, 1);
+}
+
+int owl_editwin_forward_word(owl_editwin *e)
+{
+  int distance;
+  /* if we're starting on a space, find the first non-space */
+  distance = owl_editwin_move_if_in(e, 1, WHITESPACE);
+
+  /* now find the end of this word */
+  distance += owl_editwin_move_if_not_in(e, 1, WHITESPACE);
+
+  return distance;
 }
 
 void owl_editwin_move_to_nextword(owl_editwin *e)
 {
-  int i, x;
-  gunichar c = '\0';
-
-  /* if we're starting on a space, find the first non-space */
-  i=_owl_editwin_get_index_from_xy(e);
-  if (e->buff[i]==' ') {
-    for (x=i; x<e->bufflen; x++) {
-      if (e->buff[x]!=' ' && e->buff[x]!='\n') {
-	_owl_editwin_set_xy_by_index(e, x);
-	break;
+  owl_editwin_forward_word(e);
+}
+
+/* go backwards to the last non-space character
+ */
+int owl_editwin_backward_word(owl_editwin *e)
+{
+  oe_excursion x;
+  int distance = 0;
+  int further = 0;
+  int beginning;
+  /* if in middle of word, beginning of word */
+
+  /* if at beginning of a word, find beginning of previous word */
+
+  if (owl_editwin_is_char_in(e, WHITESPACE)) {
+    /* if in whitespace past end of word, find a word , the find the beginning*/
+    distance += owl_editwin_move_if_in(e, -1, WHITESPACE); /* leaves us on the last
+							      character of the word */
+    oe_save_excursion(e, &x);
+    /* are we at the beginning of a word? */
+    owl_editwin_point_move(e, -1);
+    beginning = owl_editwin_is_char_in(e, WHITESPACE);
+    oe_restore_excursion(e, &x);
+    if (beginning)
+      return distance;
+   } else {
+    /* in the middle of the word; */
+    oe_save_excursion(e, &x);
+    further += owl_editwin_point_move(e, -1);
+    if (owl_editwin_is_char_in(e, WHITESPACE)) { /* we were at the beginning */
+      distance += owl_editwin_backward_word(e); /* previous case */
+      oe_release_excursion(e, &x);
+      return distance + further;
+    } else {
+      oe_restore_excursion(e, &x);
+    }
+  }
+  distance += owl_editwin_move_if_not_in(e, -1, WHITESPACE);
+  /* will go past */
+  if (e->index > e->lock)
+    distance += owl_editwin_point_move(e, 1);
+  return distance;
+}
+
+void owl_editwin_move_to_previousword(owl_editwin *e)
+{
+  owl_editwin_backward_word(e);
+}
+
+void owl_editwin_delete_nextword(owl_editwin *e)
+{
+  oe_excursion x;
+
+  oe_save_excursion(e, &x);
+  oe_set_mark(e, e->index);
+  owl_editwin_forward_word(e);
+  owl_editwin_kill_region(e);
+  oe_restore_mark_only(e, &x);
+}
+
+void owl_editwin_delete_previousword(owl_editwin *e)
+{
+  oe_excursion x;
+
+  oe_save_excursion(e, &x);
+  oe_set_mark(e, e->index);
+  owl_editwin_backward_word(e);
+  owl_editwin_kill_region(e);
+  oe_restore_mark_only(e, &x);
+}
+
+void owl_editwin_move_to_line_end(owl_editwin *e)
+{
+  owl_editwin_move_to_end_of_line(e);
+}
+
+void owl_editwin_delete_to_endofline(owl_editwin *e)
+{
+  oe_excursion x;
+  int distance;
+
+  oe_save_excursion(e, &x);
+  owl_editwin_set_mark(e);
+  distance = owl_editwin_move_to_end_of_line(e);
+  if (distance)
+    owl_editwin_kill_region(e);
+  else
+    owl_editwin_replace(e, 1, "");
+  oe_restore_excursion(e, &x);
+}
+
+void owl_editwin_yank(owl_editwin *e)
+{
+  if (e->killbuf != NULL)
+    owl_editwin_replace(e, 0, e->killbuf);
+}
+
+static char *oe_copy_buf(owl_editwin *e, char *buf, int len)
+{
+  char *p;
+
+  p = owl_malloc(len + 1);
+
+  if (p != NULL) {
+    owl_free(e->killbuf);
+    e->killbuf = p;
+    memcpy(e->killbuf, buf, len);
+    e->killbuf[len] = 0;
+  }
+
+  return p;
+}
+
+static int oe_copy_region(owl_editwin *e)
+{
+  char *p;
+  int start, end;
+
+  if (e->mark == -1)
+    return 0;
+
+  start = MIN(e->index, e->mark);
+  end = MAX(e->index, e->mark);
+
+  p = oe_copy_buf(e, e->buff + start, end - start);
+  if (p != NULL)
+    return end - start;
+  return 0;
+}
+
+void owl_editwin_copy_region_as_kill(owl_editwin *e)
+{
+  oe_copy_region(e);
+}
+
+void owl_editwin_kill_region(owl_editwin *e)
+{
+  if (e->index > e->mark)
+    owl_editwin_exchange_point_and_mark(e);
+
+  owl_editwin_replace(e, oe_copy_region(e), "");
+}
+
+void owl_editwin_move_to_line_start(owl_editwin *e)
+{
+  owl_editwin_move_to_beginning_of_line(e);
+}
+
+void owl_editwin_move_to_end(owl_editwin *e)
+{
+  oe_set_index(e, e->bufflen);
+}
+
+void owl_editwin_move_to_top(owl_editwin *e)
+{
+  oe_set_index(e, e->lock);
+}
+
+void owl_editwin_backward_paragraph(owl_editwin *e)
+{
+  owl_editwin_point_move(e, -1);
+  for (; e->index >= e->lock; owl_editwin_point_move(e, -1)) {
+    if (e->index <= e->lock ||
+        ((e->buff[e->index] == '\n') && (e->buff[e->index - 1]=='\n')))
+      break;
+  }
+}
+
+void owl_editwin_forward_paragraph(owl_editwin *e)
+{
+  owl_editwin_point_move(e, 1);
+  /* scan forward to the start of the next paragraph */
+  for(; e->index < e->bufflen; owl_editwin_point_move(e, 1)) {
+    if (e->buff[e->index -1] == '\n' && e->buff[e->index] == '\n')
+      break;
+  }
+}
+
+int owl_editwin_current_column(owl_editwin *e)
+{
+  oe_excursion x;
+  int lineindex;
+
+  oe_save_excursion(e, &x);
+  owl_editwin_move_to_beginning_of_line(e);
+  lineindex = e->index;
+  oe_restore_excursion(e, &x);
+  return oe_region_width(e, lineindex, e->index, 0);
+}
+
+void owl_editwin_fill_paragraph(owl_editwin *e)
+{
+  oe_excursion x;
+  gunichar ch;
+  int sentence;
+
+  oe_save_excursion(e, &x);
+
+  /* Mark the end of the paragraph */
+  owl_editwin_forward_paragraph(e);
+  /* Skip the trailing newline */
+  owl_editwin_point_move(e, -1);
+  owl_editwin_set_mark(e);
+
+  owl_editwin_backward_paragraph(e);
+
+  /* Don't mess with the leading newline */
+  if (owl_editwin_get_char_at_point(e) == '\n')
+    owl_editwin_point_move(e, 1);
+
+  /*
+   * First pass: Scan forward replacing all series of spaces with ' '
+   * (or nothing after CJK ideograms)
+   */
+  sentence = 0;
+  for(;e->index < e->mark; owl_editwin_point_move(e, 1)) {
+    /* bail if we hit a trailing dot on the buffer */
+    if (strcmp(e->buff + e->index, "\n.") == 0) {
+      owl_editwin_set_mark(e);
+      break;
+    }
+
+    ch = owl_editwin_get_char_at_point(e);
+
+    if (owl_util_can_break_after(ch) || ch == '\n') {
+      if (g_unichar_isspace(ch)) {
+        owl_editwin_replace(e, 1, " ");
+      }
+
+      if (sentence && g_unichar_isspace(owl_editwin_get_char_at_point(e)))
+        owl_editwin_point_move(e, 1);
+
+      while(g_unichar_isspace(owl_editwin_get_char_at_point(e))
+            && e->index < e->mark) {
+        owl_editwin_delete_char(e);
       }
     }
-  }
-
-  /* find the next space, newline or end of line and go
-     there, if already at the end of the line, continue on to the next */
-  i=owl_editwin_get_numcells_on_line(e, e->buffy);
-  c = _owl_editwin_get_char_at_xy(e);
-  if (e->buffx < i) {
-    /* move right till end of line */
-    while (e->buffx < i) {
-      owl_editwin_key_right(e);
-      c = _owl_editwin_get_char_at_xy(e);
-      if (c == ' ') return;
-      if (e->buffx == i) return;
-    }
-  } else if (e->buffx == i) {
-    /* try to move down */
-    if (e->style==OWL_EDITWIN_STYLE_MULTILINE) {
-      if (e->buffy+1 < owl_editwin_get_numlines(e)) {
-	e->buffx=0;
-	e->buffy++;
-	owl_editwin_move_to_nextword(e);
-      }
-    }
-  }
-}
-
-/* go backwards to the last non-space character
- */
-void owl_editwin_move_to_previousword(owl_editwin *e)
-{
-  int i;
-  gunichar c;
-  char *ptr1, *ptr2;
-
-  /* are we already at the beginning of the word? */
-  c = _owl_editwin_get_char_at_xy(e);
-  i = _owl_editwin_get_index_from_xy(e);
-  ptr1 = e->buff + i;
-  if (*ptr1 != ' ' && *ptr1 != '\n' && *ptr1 != '\0' ) {
-    ptr1 = g_utf8_find_prev_char(e->buff, ptr1);
-    c = g_utf8_get_char(ptr1);
-    if (c == ' ' || c == '\n') {
-      owl_editwin_key_left(e);      
-    }
-  }
-
-  /* are we starting on a space character? */
-  i = _owl_editwin_get_index_from_xy(e);
-  while (i > e->lock && (e->buff[i] == ' ' || e->buff[i] == '\n' || e->buff[i] == '\0')) {
-    /* find the first non-space */
-    owl_editwin_key_left(e);      
-    i = _owl_editwin_get_index_from_xy(e);
-  }
-
-  /* find the last non-space */
-  ptr1 = e->buff + _owl_editwin_get_index_from_xy(e);
-  while (ptr1 >= e->buff + e->lock) {
-    ptr2 = g_utf8_find_prev_char(e->buff, ptr1);
-    if (!ptr2) break;
-    
-    c = g_utf8_get_char(ptr2);
-    if (c == ' ' || c == '\n'){
-      break;
-    }
-    owl_editwin_key_left(e);
-    ptr1 = e->buff + _owl_editwin_get_index_from_xy(e);
-  }
-}
-
-
-void owl_editwin_delete_nextword(owl_editwin *e)
-{
-  char *ptr1, *start;
-  gunichar c;
-
-  if (e->bufflen==0) return;
-
-  start = ptr1 = e->buff + _owl_editwin_get_index_from_xy(e);
-  /* if we start out on a space character then jump past all the
-     spaces up first */
-  while (*ptr1 == ' ' || *ptr1 == '\n') {
-    ++ptr1;
-  }
-
-  /* then jump past the next word */
-  
-  while (ptr1 && ptr1 - e->buff < e->bufflen) {
-    c = g_utf8_get_char(ptr1);
-    if (c == ' ' || c == '\n' || c == '\0') break;
-    ptr1 = g_utf8_find_next_char(ptr1, NULL);
-  }
-
-  if (ptr1) { /* We broke on a space, */
-    ptr1 = g_utf8_find_next_char(ptr1, NULL);
-    if (ptr1) { /* and there's a character after it, */
-      /* nuke everything back to our starting point. */
-      _owl_editwin_remove_bytes(e, ptr1 - start);
-      return;
-    }
-  }
-  
-  /* If we get here, we ran out of string, drop what's left. */
-  *start = '\0';
-  e->bufflen = start - e->buff;
-}
-
-void owl_editwin_delete_previousword(owl_editwin *e)
-{
-  /* go backwards to the last non-space character, then delete chars */
-  int startpos, endpos;
-
-  startpos = _owl_editwin_get_index_from_xy(e);
-  owl_editwin_move_to_previousword(e);
-  endpos = _owl_editwin_get_index_from_xy(e);
-  _owl_editwin_remove_bytes(e, startpos-endpos);
-}
-
-void owl_editwin_delete_to_endofline(owl_editwin *e)
-{
-  int i;
-
-  if (owl_editwin_get_numchars_on_line(e, e->buffy) > e->buffx) {
-    /* normal line */
-    i=_owl_editwin_get_index_from_xy(e);
-    while(i < e->bufflen) {
-      if (e->buff[i]!='\n') {
-	owl_editwin_delete_char(e);
-      } else if ((e->buff[i]=='\n') && (i==e->bufflen-1)) {
-	owl_editwin_delete_char(e);
-      } else {
-	return;
-      }
-    }
-  } else if (e->buffy+1 < owl_editwin_get_numlines(e)) {
-    /* line with cursor at the end but not on very last line */
-    owl_editwin_key_right(e);
-    owl_editwin_backspace(e);
-  }
-}
-
-void owl_editwin_move_to_line_end(owl_editwin *e)
-{
-  e->buffx=owl_editwin_get_numcells_on_line(e, e->buffy);
-}
-
-void owl_editwin_move_to_line_start(owl_editwin *e)
-{
-  e->buffx=0;
-  owl_editwin_adjust_for_locktext(e);
-}
-
-void owl_editwin_move_to_end(owl_editwin *e)
-{
-  /* go to last char */
-  e->buffy=owl_editwin_get_numlines(e)-1;
-  e->buffx=owl_editwin_get_numcells_on_line(e, e->buffy);
-  owl_editwin_key_right(e);
-
-  /* do we need to scroll? */
-  /*
-  if (e->buffy-e->topline > e->winlines) {
-    e->topline+=e->winlines/2;
-  }
-  */
-  owl_editwin_recenter(e);
-}
-
-void owl_editwin_move_to_top(owl_editwin *e)
-{
-  _owl_editwin_set_xy_by_index(e, 0);
-
-  /* do we need to scroll? */
-  e->topline=0;
-
-  owl_editwin_adjust_for_locktext(e);
-}
-
-void owl_editwin_fill_paragraph(owl_editwin *e)
-{
-  int i, save;
-
-  /* save our starting point */
-  save=_owl_editwin_get_index_from_xy(e);
-
-  /* scan back to the beginning of this paragraph */
-  for (i=save; i>=e->lock; i--) {
-    if ( (i<=e->lock) ||
-	 ((e->buff[i]=='\n') && (e->buff[i-1]=='\n'))) {
-      _owl_editwin_set_xy_by_index(e, i+1);
-      break;
-    }
-  }
-
-  /* main loop */
-  while (1) {
-    i = _owl_editwin_get_index_from_xy(e);
-
-    /* bail if we hit the end of the buffer */
-    if (i >= e->bufflen || e->buff[i] == '\0') break;
-
-    /* bail if we hit the end of the paragraph */
-    if (e->buff[i] == '\n' && e->buff[i+1] == '\n') break;
-
-    /* bail if we hit a trailing dot on the buffer */
-    if (e->buff[i] == '\n' && e->buff[i+1] == '.'
-        && ((i+2) >= e->bufflen || e->buff[i+2] == '\0'))
-      break;
-
+
+    if(ch == '.' || ch == '!' || ch == '?')
+      sentence = 1;
+    else
+      sentence = 0;
+  }
+
+  owl_editwin_backward_paragraph(e);
+
+  /* Now go through inserting newlines as needed */
+  while(e->index < e->mark) {
     /* if we've travelled too far, linewrap */
-    if ((e->buffx) >= e->fillcol) {
-      int len = e->bufflen;
+    if (owl_editwin_current_column(e) >= e->fillcol)
       _owl_editwin_linewrap_word(e);
-      /* we may have added a character. */
-      if (i < save) save += e->bufflen - len;
-      _owl_editwin_set_xy_by_index(e, i);
-    }
-
-    /* did we hit the end of a line too soon? */
-    /* asedeno: Here we replace a newline with a space. We may want to
-       consider removing the space if the characters to either side
-       are CJK ideograms.*/
-    i = _owl_editwin_get_index_from_xy(e);
-    if (e->buff[i] == '\n' && e->buffx < e->fillcol - 1) {
-      /* ********* we need to make sure we don't pull in a word that's too long ***********/
-      e->buff[i]=' ';
-    }
-
-    /* fix spacing */
-    i = _owl_editwin_get_index_from_xy(e);
-    if (e->buff[i] == ' ' && e->buff[i+1] == ' ') {
-      if (e->buff[i-1] == '.' || e->buff[i-1] == '!' || e->buff[i-1] == '?') {
-	owl_editwin_key_right(e);
-      } else {
-	owl_editwin_delete_char(e);
-	/* if we did this ahead of the save point, adjust it. Changing
-           by one is fine here because we're only removing an ASCII
-           space. */
-	if (i < save) save--;
-      }
-    } else {
-      owl_editwin_key_right(e);
-    }
-  }
-
-  /* put cursor back at starting point */
-  _owl_editwin_set_xy_by_index(e, save);
-
-  /* do we need to scroll? */
-  if (e->buffy-e->topline < 0) {
-    e->topline-=e->winlines/2;
-  }
+    owl_editwin_point_move(e, 1);
+  }
+
+  oe_restore_excursion(e, &x);
 }
 
@@ -1095,63 +1194,115 @@
 int owl_editwin_is_at_end(owl_editwin *e)
 {
-  int cur=_owl_editwin_get_index_from_xy(e);
-  return (only_whitespace(e->buff+cur));
-}
-
-int owl_editwin_check_dotsend(owl_editwin *e)
-{
-  char *p, *p_n, *p_p;
-  gunichar c;
+  return (only_whitespace(e->buff + e->index));
+}
+
+static int owl_editwin_check_dotsend(owl_editwin *e)
+{
+  int zdot;
+  oe_excursion x;
 
   if (!e->dotsend) return(0);
-
-  p = g_utf8_find_prev_char(e->buff, e->buff + e->bufflen);
-  p_n = g_utf8_find_next_char(p, NULL);
-  p_p = g_utf8_find_prev_char(e->buff, p);
-  c = g_utf8_get_char(p);
-  while (p != NULL) {
-    if (*p == '.'
-	&& p_p != NULL && (*p_p == '\n' || *p_p == '\r')
-	&& p_n != NULL && (*p_n == '\n' || *p_n == '\r')) {
-      e->bufflen = p - e->buff;
-      e->buff[e->bufflen] = '\0';
-      return(1);
-    }
-    if (c != '\0' && !g_unichar_isspace(c)) return(0);
-    p_n = p;
-    p = p_p;
-    c = g_utf8_get_char(p);
-    p_p = g_utf8_find_prev_char(e->buff, p);
-  }
-  return(0);
+  if (e->index != e->bufflen) return (0);
+
+  oe_save_excursion(e, &x);
+
+  owl_editwin_point_move(e, -3);
+
+  zdot = (strcmp(e->buff + e->index, "\n.\n") == 0);
+
+  oe_restore_excursion(e, &x);
+
+  return zdot;
 }
 
 void owl_editwin_post_process_char(owl_editwin *e, owl_input j)
 {
-  /* check if we need to scroll down */
-  if (e->buffy-e->topline >= e->winlines) {
-    e->topline+=e->winlines/2;
-  }
+  /* XXX force a redisplay? */
   if ((j.ch==13 || j.ch==10) && owl_editwin_check_dotsend(e)) {
     owl_command_editmulti_done(e);
     return;
   }
-  owl_editwin_redisplay(e, 0);  
-}
-
-void _owl_editwin_process_char(owl_editwin *e, gunichar j)
-{
-  if (!(g_unichar_iscntrl(j) && (j != 10) && (j != 13))) {
-    owl_editwin_insert_char(e, j);
-  }
-}
-
+  owl_editwin_redisplay(e, 0);
+}
+
+static int oe_region_width(owl_editwin *e, int start, int end, int offset)
+{
+  char *p;
+  int width = offset;
+  
+  for(p = e->buff + start;
+      p < e->buff + end;
+      p = g_utf8_find_next_char(p, NULL))
+    width += oe_char_width(g_utf8_get_char(p), width);
+
+  return width - offset;
+}
+
+static void oe_insert_char(owl_editwin *e, gunichar c)
+{
+  oe_excursion x;
+  char tmp[7];
+  int replaced = -1;
+
+  if (c == '\r') /* translate CRs to NLs */
+    c = '\n';
+
+  if (!g_unichar_iscntrl(c) || c == '\n' || c== '\t' ) {
+    memset(tmp, 0, 7);
+
+    if (c == '\n' && e->style == OWL_EDITWIN_STYLE_ONELINE) {
+      return;
+    }
+
+    if (e->cursorx != -1 && e->cursorx + oe_char_width(c, e->cursorx) > e->wrapcol) {
+      /* XXX this is actually wrong:
+       * + If the line has been been wrapped, we can be past the wrap column but
+       *   e->cursorx be much smaller.
+       * + If the user went back and inserted a bunch of stuff in the middle of
+       *   the line, there may be more than one word past the wrap column.
+       */
+      oe_save_excursion(e, &x);
+
+      if (c == ' ' || c == '\t') {
+	owl_editwin_point_move(e, -1);
+	replaced = -owl_editwin_move_if_in(e, -1, " \t");
+	if (!replaced) {
+	  c = '\n';
+	  replaced = -1;
+	}
+      } else {
+	while(!owl_editwin_at_beginning_of_line(e)) {
+	  owl_editwin_point_move(e, -1);
+	  if (owl_util_can_break_after(owl_editwin_get_char_at_point(e))) {
+	    replaced = -owl_editwin_move_if_in(e, -1, " \t");
+	    break;
+	  }
+	}
+	if (owl_editwin_at_beginning_of_line(e))
+	  replaced = -1;
+      }
+      if (replaced && !owl_editwin_at_beginning_of_line(e))
+	owl_editwin_point_move(e, 1);
+      if (replaced >= 0) {
+	owl_editwin_replace(e, replaced, "\n");
+      }
+      oe_restore_excursion(e, &x);
+    }
+
+    if (replaced >= 0 && (c == ' ' || c == '\t'))
+      return; /* our work here is done */
+
+    g_unichar_to_utf8(c, tmp);
+    owl_editwin_replace(e, 0, tmp);
+  }
+}
 
 void owl_editwin_process_char(owl_editwin *e, owl_input j)
 {
-  if (j.ch == ERR) return;
+  if (j.ch == ERR)
+    return;
   /* Ignore ncurses control characters. */
-  if (j.ch < 0x100) { 
-    _owl_editwin_process_char(e, j.uch);
+  if (j.ch < 0x100) {
+    oe_insert_char(e, j.uch);
   }
 }
@@ -1162,67 +1313,56 @@
 }
 
-int owl_editwin_get_numchars_on_line(owl_editwin *e, int line)
-{
-  int i;
-  char *ptr1, *ptr2;
-
-  if (e->bufflen==0) return(0);
+char *owl_editwin_get_region(owl_editwin *e)
+{
+  int start, end;
+  start = e->index;
+  end   = e->mark;
+  if(start > end) {
+    int tmp = end;
+    end = start;
+    start = tmp;
+  }
+
+  return oe_chunk(e, start, end);
+}
+
+int owl_editwin_get_echochar(owl_editwin *e)
+{
+  return e->echochar;
+}
+
+static char *oe_chunk(owl_editwin *e, int start, int end)
+{
+  char *p;
   
-  /* first go to the yth line */
-  ptr1=e->buff;
-  for (i=0; i<line; i++) {
-    ptr2=strchr(ptr1, '\n');
-    if (!ptr2) {
-      /* we're already on the last line */
-      return(0);
-    }
-    ptr1=ptr2+1;
-  }
-
-  /* now count characters */
-  i = 0;
-  ptr2 = ptr1;
-  while (ptr2 - e->buff < e->bufflen
-	 && *ptr2 != '\n') {
-    ++i;
-    ptr2 = g_utf8_next_char(ptr2);
-  }
-  return i;
-}
-
-int owl_editwin_get_numcells_on_line(owl_editwin *e, int line)
-{
-  int i;
-  char *ptr1, *ptr2;
-  gunichar c;
-
-  if (e->bufflen==0) return(0);
-  
-  /* first go to the yth line */
-  ptr1=e->buff;
-  for (i=0; i<line; i++) {
-    ptr2=strchr(ptr1, '\n');
-    if (!ptr2) {
-      /* we're already on the last line */
-      return(0);
-    }
-    ptr1=ptr2+1;
-  }
-
-  /* now count cells */
-  i = 0;
-  ptr2 = ptr1;
-  while (ptr2 - e->buff < e->bufflen
-	 && *ptr2 != '\n') {
-    c = g_utf8_get_char(ptr2);
-    i += mk_wcwidth(c);
-    ptr2 = g_utf8_next_char(ptr2);
-  }
-  return i;
-}
-
-int owl_editwin_get_numlines(owl_editwin *e)
-{
-  return(owl_text_num_lines(e->buff));
-}
-
+  p = owl_malloc(end - start + 1);
+  memcpy(p, e->buff + start, end - start);
+  p[end - start] = 0;
+
+  return p;
+}
+
+/*
+ * The only guarantee made about these values is that comparisons
+ * between them, as well as comparison between multiple calls to these
+ * functions without modifying the editwin in-between, are meaningful.
+ */
+
+int owl_editwin_get_point(owl_editwin *e)
+{
+  return e->index;
+}
+
+int owl_editwin_get_mark(owl_editwin *e)
+{
+  return e->mark;
+}
+
+
+
+/*
+ * Local Variables:
+ * mode:C
+ * c-basic-offset:2
+ * End:
+ */
Index: functions.c
===================================================================
--- functions.c	(revision 8daf50422d4a92e3fe48ca219c98d4691c38b71e)
+++ functions.c	(revision e6d989fc78e8d6130f9158587574264f40a26895)
@@ -224,5 +224,5 @@
 
 /* Create an outgoing zephyr message and return a pointer to it.  Does
- * not put it on the global queue, use owl_function_add_message() for
+ * not put it on the global queue, use owl_global_messagequeue_addmsg() for
  * that.
  */
@@ -246,5 +246,5 @@
  * message or NULL if we're not logged into AIM (and thus unable to
  * create the message).  Does not put it on the global queue.  Use
- * owl_function_add_message() for that .
+ * owl_global_messagequeue_addmsg() for that .
  */
 owl_message *owl_function_make_outgoing_aim(char *body, char *to)
@@ -267,5 +267,5 @@
 /* Create an outgoing loopback message and return a pointer to it.
  * Does not append it to the global queue, use
- * owl_function_add_message() for that.
+ * owl_global_messagequeue_addmsg() for that.
  */
 owl_message *owl_function_make_outgoing_loopback(char *body)
@@ -2570,5 +2570,5 @@
  *
  * if the curmsg is a personal zephyr return a filter name
- *    to the zephyr converstaion with that user.
+ *    to the zephyr conversation with that user.
  * If the curmsg is a zephyr class message, instance foo, recip *,
  *    return a filter name to the class, inst.
@@ -3108,5 +3108,12 @@
       ret=owl_zephyr_get_anyone_list(&anyone, filename);
       if (ret) {
-        owl_fmtext_append_normal(&fm, "  Error opening file for zephyr buddies.\n");
+        if (errno == ENOENT) {
+          owl_fmtext_append_normal(&fm, " You have not added any zephyr buddies.  Use the\n");
+          owl_fmtext_append_normal(&fm, " command ':addbuddy zephyr ");
+          owl_fmtext_append_bold(  &fm, "<username>");
+          owl_fmtext_append_normal(&fm, "'.\n");
+        } else {
+          owl_fmtext_append_normal(&fm, " Could not read zephyr buddies from the .anyone file.\n");
+        }
       } else {
         j=owl_list_get_size(&anyone);
@@ -3594,4 +3601,5 @@
   _owl_function_mark_message(m);
   owl_global_set_curmsg(&g, owl_view_get_nearest_to_msgid(v, marked_id));
+  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
   owl_mainwin_redisplay(owl_global_get_mainwin(&g));
   owl_global_set_direction_downwards(&g);
Index: global.c
===================================================================
--- global.c	(revision bd783db003615b2ead513d7f7668b768e1303358)
+++ global.c	(revision a556caa6cc599f80f613bcf8239a5c6550f1049c)
@@ -47,5 +47,6 @@
   g->rightshift=0;
 
-  owl_editwin_init(&(g->tw), NULL, owl_global_get_typwin_lines(g), g->cols, OWL_EDITWIN_STYLE_ONELINE, NULL);
+  g->tw = owl_editwin_allocate();
+  owl_editwin_init(g->tw, NULL, owl_global_get_typwin_lines(g), g->cols, OWL_EDITWIN_STYLE_ONELINE, NULL);
 
   owl_keyhandler_init(&g->kh);
@@ -147,5 +148,5 @@
   g->typwin=newwin(typwin_lines, cols, g->recwinlines+2, 0);
 
-  owl_editwin_set_curswin(&(g->tw), g->typwin, typwin_lines, g->cols);
+  owl_editwin_set_curswin(g->tw, g->typwin, typwin_lines, g->cols);
 
   idlok(g->typwin, FALSE);
@@ -261,5 +262,5 @@
 
 owl_editwin *owl_global_get_typwin(owl_global *g) {
-  return(&(g->tw));
+  return(g->tw);
 }
 
@@ -478,5 +479,5 @@
   owl_mainwin_redisplay(&(g->mw));
   sepbar(NULL);
-  owl_editwin_redisplay(&(g->tw), 0);
+  owl_editwin_redisplay(g->tw, 0);
   owl_function_full_redisplay(&g);
 
Index: help.c
===================================================================
--- help.c	(revision 799b60ea3145bfa2ad773b09544d31a3e30e77ff)
+++ help.c	(revision e6d989fc78e8d6130f9158587574264f40a26895)
@@ -58,5 +58,5 @@
      "\n"
      "    M-n           View zephyrs in selected conversation\n"
-     "    M-N           View zephyrs in selected converstaion of instance\n"
+     "    M-N           View zephyrs in selected conversation of instance\n"
      "    M-p           View only personal zephyrs\n"
      "    V             Change to back to home view ('all' by default)\n"
Index: keys.c
===================================================================
--- keys.c	(revision 4f0a2eece694b5cf4e0ad47414690608372f6362)
+++ keys.c	(revision 2fc8397e0117350e37ccc1b1b83ebcc9b94686d2)
@@ -45,7 +45,9 @@
   BIND_CMD("M-O 3 C",     "edit:move-next-word", "");
   BIND_CMD("M-RIGHT",     "edit:move-next-word", "");
+  BIND_CMD("M-[ 1 ; 3 D", "edit:move-next-word", "");
   BIND_CMD("M-b",         "edit:move-prev-word", "");
   BIND_CMD("M-O 3 D",     "edit:move-prev-word", "");
   BIND_CMD("M-LEFT",      "edit:move-prev-word", "");
+  BIND_CMD("M-[ 1 ; 3 C", "edit:move-next-word", "");
 
   BIND_CMD("LEFT",        "edit:move-left", "");
@@ -82,4 +84,11 @@
   BIND_CMD("M-q",         "edit:fill-paragraph", "");
 
+  BIND_CMD("C-@",         "edit:set-mark", "");
+  BIND_CMD("C-x C-x",     "edit:exchange-point-and-mark", "");
+
+  BIND_CMD("M-w",         "edit:copy-region-as-kill", "");
+  BIND_CMD("C-w",         "edit:kill-region", "");
+  BIND_CMD("C-y",         "edit:yank", "");
+
   BIND_CMD("C-l",         "( edit:recenter ; redisplay )", "");
 
@@ -100,4 +109,7 @@
   BIND_CMD("M-[ B",   "editmulti:move-down-line", "");
   BIND_CMD("C-n",     "editmulti:move-down-line", "");
+
+  BIND_CMD("M-}",     "editmulti:forward-paragraph", "");
+  BIND_CMD("M-{",     "editmulti:backward-paragraph", "");
 
   /* This would be nice, but interferes with C-c to cancel */
Index: owl.c
===================================================================
--- owl.c	(revision 1be5823abb73677c4de976106654c954318ec5e2)
+++ owl.c	(revision 301c975a7a71ae32c393a1f07001ae97eb19afca)
@@ -145,5 +145,5 @@
   /* turn ISTRIP off */
   tcgetattr(0, &tio);
-  tio.c_iflag &= ~ISTRIP;
+  tio.c_iflag &= ~(ISTRIP|IEXTEN);
   tio.c_cc[VQUIT] = 0;
   tcsetattr(0, TCSAFLUSH, &tio);
Index: owl.h
===================================================================
--- owl.h	(revision 37f27bc0e54077fc0d554781c838adc50188236d)
+++ owl.h	(revision 37f27bc0e54077fc0d554781c838adc50188236d)
@@ -419,22 +419,6 @@
 } owl_history;
 
-typedef struct _owl_editwin {
-  char *buff;
-  owl_history *hist;
-  int bufflen;
-  int allocated;
-  int buffx, buffy;
-  int topline;
-  int winlines, wincols, fillcol, wrapcol;
-  WINDOW *curswin;
-  int style;
-  int lock;
-  int dotsend;
-  int echochar;
-
-  char *command;
-  void (*callback)(struct _owl_editwin*);
-  void *cbdata;
-} owl_editwin;
+typedef struct _owl_editwin owl_editwin;
+typedef struct _owl_editwin_excursion owl_editwin_excursion;
 
 typedef struct _owl_keybinding {
@@ -551,5 +535,5 @@
   int config_format;
   void *buffercbdata;
-  owl_editwin tw;
+  owl_editwin *tw;
   owl_viewwin vw;
   void *perl;
Index: perl/lib/BarnOwl.pm
===================================================================
--- perl/lib/BarnOwl.pm	(revision fd8dfe732ac4f414b1f6e8e97af41c7f9d170dd2)
+++ perl/lib/BarnOwl.pm	(revision 22b54a7e11a865e233b7487d805219989c6e31aa)
@@ -18,4 +18,5 @@
 use BarnOwl::Style;
 use BarnOwl::Timer;
+use BarnOwl::Editwin;
 
 =head1 NAME
@@ -316,4 +317,55 @@
 }
 
+=head2 Modify filters by appending text
+
+=cut
+
+BarnOwl::new_command("filterappend",
+    sub { filter_append_helper('appending', '', @_); },
+    {
+        summary => "append '<text>' to filter",
+        usage => "filterappend <filter> <text>",
+    });
+
+BarnOwl::new_command("filterand",
+    sub { filter_append_helper('and-ing', 'and', @_); },
+    {
+        summary => "append 'and <text>' to filter",
+        usage => "filterand <filter> <text>",
+    });
+
+BarnOwl::new_command("filteror",
+    sub { filter_append_helper('or-ing', 'or', @_); },
+    {
+        summary => "append 'or <text>' to filter",
+        usage => "filteror <filter> <text>",
+    });
+
+=head3 filter_append_helper ACTION SEP FUNC FILTER APPEND_TEXT
+
+Helper to append to filters.
+
+=cut
+
+sub filter_append_helper
+{
+    my $action = shift;
+    my $sep = shift;
+    my $func = shift;
+    my $filter = shift;
+    my @append = @_;
+    my $oldfilter = BarnOwl::getfilter($filter);
+    chomp $oldfilter;
+    my $newfilter = join(' ', $oldfilter, $sep, @_);
+    my $msgtext = "To filter '$filter' $action\n'".join(' ', @append)."' to get\n'$newfilter'";
+    if (BarnOwl::getvar('showfilterchange') eq 'on') {
+        BarnOwl::admin_message("Filter", $msgtext);
+    }
+    BarnOwl::filter($filter, $newfilter);
+    return;
+}
+BarnOwl::new_variable_bool("showfilterchange",
+                           { default => 1,
+                             summary => 'Show modifications to filters by filterappend and friends.'});
 
 1;
Index: perl/lib/BarnOwl/Editwin.pm
===================================================================
--- perl/lib/BarnOwl/Editwin.pm	(revision c6ecf5c7b947ccf3045a8217895bcb3707cc40dd)
+++ perl/lib/BarnOwl/Editwin.pm	(revision c6ecf5c7b947ccf3045a8217895bcb3707cc40dd)
@@ -0,0 +1,39 @@
+use warnings;
+use strict;
+
+=head1 NAME
+
+BarnOwl::Editwin
+
+=head1 DESCRIPTION
+
+Functions for interfacing with the BarnOwl editwin. Most of the
+functions in this module are defined in perlglue.xs; This module
+exists to provide Exporter hooks and documentation for them.
+
+=cut
+
+package BarnOwl::Editwin;
+use base qw(Exporter);
+
+our @EXPORT_OK = qw(text_before_point text_after_point replace
+                    point_move replace_region get_region
+                    save_excursion current_column point mark);
+
+sub text_before_point {
+    save_excursion {
+        BarnOwl::command('edit:set-mark');
+        BarnOwl::command('edit:move-to-buffer-start');
+        get_region();
+    }
+}
+
+sub text_after_point {
+    save_excursion {
+        BarnOwl::command('edit:set-mark');
+        BarnOwl::command('edit:move-to-buffer-end');
+        get_region();
+    }
+}
+
+1;
Index: perl/lib/BarnOwl/Message.pm
===================================================================
--- perl/lib/BarnOwl/Message.pm	(revision fd8dfe732ac4f414b1f6e8e97af41c7f9d170dd2)
+++ perl/lib/BarnOwl/Message.pm	(revision d1ae4a421ba5874c2899090ebdd7079e9bbddc25)
@@ -22,4 +22,5 @@
 sub direction   { return shift->{"direction"}; }
 sub time        { return shift->{"time"}; }
+sub unix_time   { return shift->{"unix_time"}; }
 sub id          { return shift->{"id"}; }
 sub body        { return shift->{"body"}; }
Index: perl/lib/BarnOwl/Message/Zephyr.pm
===================================================================
--- perl/lib/BarnOwl/Message/Zephyr.pm	(revision bc8275e9e97b201c2d62ad8acd8eb4d672542c89)
+++ perl/lib/BarnOwl/Message/Zephyr.pm	(revision 30e7ffdcb0c653baff90c99a801d2bf2cdd7a46d)
@@ -179,5 +179,7 @@
 
     my $cmd;
-    if(lc $self->opcode eq 'crypt') {
+    if(lc $self->opcode eq 'crypt' and ( not $sender or $self->is_private)) {
+        # Responses to zcrypted messages should be zcrypted, so long as we
+        # aren't switching to personals
         $cmd = 'zcrypt';
     } else {
Index: perl/lib/BarnOwl/Style/Default.pm
===================================================================
--- perl/lib/BarnOwl/Style/Default.pm	(revision ee183beb7b1f02ed315026b95d9d72e0913908bd)
+++ perl/lib/BarnOwl/Style/Default.pm	(revision 5550eb09288cd1e20bd0d23ec60bba36e0019f0d)
@@ -3,4 +3,6 @@
 
 package BarnOwl::Style::Default;
+use POSIX qw(strftime);
+
 ################################################################################
 # Branching point for various formatting functions in this style.
@@ -40,6 +42,5 @@
     my $self = shift;
     my $m = shift;
-    my ($time) = $m->time =~ /(\d\d:\d\d)/;
-    return $time;
+    return strftime('%H:%M', localtime($m->unix_time));
 }
 
Index: perl/modules/IRC/lib/BarnOwl/Module/IRC.pm
===================================================================
--- perl/modules/IRC/lib/BarnOwl/Module/IRC.pm	(revision 1b62a5524238e97ee512527a1d5bb8d65416c381)
+++ perl/modules/IRC/lib/BarnOwl/Module/IRC.pm	(revision 48f7d1233a2fe7a8f8255a71e7944b8f22589667)
@@ -60,5 +60,5 @@
     register_commands();
     register_handlers();
-    BarnOwl::filter('irc type ^IRC$');
+    BarnOwl::filter('irc type ^IRC$ or ( type ^admin$ and adminheader ^IRC$ )');
 }
 
@@ -375,4 +375,5 @@
     $conn->conn->disconnect;
     delete $ircnets{$conn->alias};
+    return;
 }
 
@@ -387,4 +388,5 @@
         BarnOwl::start_edit_win("/msg -a " . $conn->alias . " $to", sub {process_msg($conn, $to, @_)});
     }
+    return;
 }
 
@@ -415,4 +417,5 @@
        );
     BarnOwl::queue_message($msg);
+    return;
 }
 
@@ -423,4 +426,5 @@
     $target ||= shift;
     $conn->conn->mode($target, @_);
+    return;
 }
 
@@ -432,4 +436,5 @@
     push @{$channels{$chan}}, $conn;
     $conn->conn->join($chan, @_);
+    return;
 }
 
@@ -440,4 +445,5 @@
     $channels{$chan} = [grep {$_ ne $conn} @{$channels{$chan} || []}];
     $conn->conn->part($chan);
+    return;
 }
 
@@ -447,4 +453,5 @@
     my $nick = shift or die("Usage: $cmd <new nick>\n");
     $conn->conn->nick($nick);
+    return;
 }
 
@@ -455,4 +462,5 @@
     $conn->names_tmp([]);
     $conn->conn->names($chan);
+    return;
 }
 
@@ -462,4 +470,5 @@
     my $who = shift || die("Usage: $cmd <user>\n");
     $conn->conn->whois($who);
+    return;
 }
 
@@ -468,4 +477,5 @@
     my $conn = shift;
     $conn->conn->motd;
+    return;
 }
 
@@ -477,4 +487,5 @@
     }
     BarnOwl::popless_ztext($message);
+    return;
 }
 
@@ -485,4 +496,5 @@
     BarnOwl::error("WHO $cmd $conn $who");
     $conn->conn->who($who);
+    return;
 }
 
@@ -492,4 +504,5 @@
     my $type = shift || die("Usage: $cmd <chiklmouy> [server] \n");
     $conn->conn->stats($type, @_);
+    return;
 }
 
@@ -499,4 +512,5 @@
     my $chan = shift;
     $conn->conn->topic($chan, @_ ? join(" ", @_) : undef);
+    return;
 }
 
@@ -505,4 +519,5 @@
     my $conn = shift;
     $conn->conn->sl(join(" ", @_));
+    return;
 }
 
Index: perl/modules/IRC/lib/BarnOwl/Module/IRC/Connection.pm
===================================================================
--- perl/modules/IRC/lib/BarnOwl/Module/IRC/Connection.pm	(revision 32620ac3c3d440bebbc1c828ae41b98d769a0d12)
+++ perl/modules/IRC/lib/BarnOwl/Module/IRC/Connection.pm	(revision 7c83a328fd31c2e251bf67957dd817f91ffada3d)
@@ -273,5 +273,5 @@
         $self->whois_tmp
     );
-    $self->whois_tmp([]);
+    $self->whois_tmp('');
 }
 
Index: perlconfig.c
===================================================================
--- perlconfig.c	(revision fd8dfe732ac4f414b1f6e8e97af41c7f9d170dd2)
+++ perlconfig.c	(revision a556caa6cc599f80f613bcf8239a5c6550f1049c)
@@ -87,4 +87,5 @@
   }
   (void)hv_store(h, "time", strlen("time"), newSVpv(owl_message_get_timestr(m),0),0);
+  (void)hv_store(h, "unix_time", strlen("unix_time"), newSViv(m->time), 0);
   (void)hv_store(h, "id", strlen("id"), newSViv(owl_message_get_id(m)),0);
   (void)hv_store(h, "deleted", strlen("deleted"), newSViv(owl_message_is_delete(m)),0);
@@ -481,5 +482,5 @@
 void owl_perlconfig_edit_callback(owl_editwin *e)
 {
-  SV *cb = (SV*)(e->cbdata);
+  SV *cb = (SV*)owl_editwin_get_cbdata(e);
   SV *text;
   dSP;
@@ -508,5 +509,5 @@
 
   SvREFCNT_dec(cb);
-  e->cbdata = NULL;
+  owl_editwin_set_cbdata(e, NULL);
 }
 
Index: perlglue.xs
===================================================================
--- perlglue.xs	(revision fd8dfe732ac4f414b1f6e8e97af41c7f9d170dd2)
+++ perlglue.xs	(revision c6ecf5c7b947ccf3045a8217895bcb3707cc40dd)
@@ -429,2 +429,89 @@
 		owl_function_debugmsg("Freeing timer %p", t);
 				owl_select_remove_timer(t);
+
+MODULE = BarnOwl		PACKAGE = BarnOwl::Editwin
+
+int
+replace(count, string)
+	int count;
+	char *string;
+	CODE:
+		RETVAL = owl_editwin_replace(owl_global_get_typwin(&g), count, string);
+	OUTPUT:
+		RETVAL
+
+int
+point_move(delta)
+	int delta;
+	CODE:
+		RETVAL = owl_editwin_point_move(owl_global_get_typwin(&g), delta);
+	OUTPUT:
+		RETVAL
+
+int
+replace_region(string)
+	char *string;
+	CODE:
+		RETVAL = owl_editwin_replace_region(owl_global_get_typwin(&g), string);
+	OUTPUT:
+		RETVAL
+
+char *
+get_region()
+	PREINIT:
+		char *region;
+	CODE:
+		region = owl_editwin_get_region(owl_global_get_typwin(&g));
+		RETVAL = region;
+	OUTPUT:
+		RETVAL
+	CLEANUP:
+		owl_free(region);
+
+SV *
+save_excursion(sub)
+	SV *sub;
+	PROTOTYPE: &
+	PREINIT:
+		int count;
+		owl_editwin_excursion *x;
+	CODE:
+	{
+		x = owl_editwin_begin_excursion(owl_global_get_typwin(&g));
+		count = call_sv(sub, G_SCALAR|G_EVAL|G_NOARGS);
+		owl_editwin_end_excursion(owl_global_get_typwin(&g), x);
+
+		if(SvTRUE(ERRSV)) {
+			croak(NULL);
+		}
+
+		SPAGAIN;
+		if(count == 1)
+			RETVAL = SvREFCNT_inc(POPs);
+		else
+			XSRETURN_UNDEF;
+
+	}
+	OUTPUT:
+		RETVAL
+
+int
+current_column()
+	CODE:
+		RETVAL = owl_editwin_current_column(owl_global_get_typwin(&g));
+	OUTPUT:
+		RETVAL
+
+int
+point()
+	CODE:
+		RETVAL = owl_editwin_get_point(owl_global_get_typwin(&g));
+	OUTPUT:
+		RETVAL
+
+int
+mark()
+	CODE:
+		RETVAL = owl_editwin_get_mark(owl_global_get_typwin(&g));
+	OUTPUT:
+		RETVAL
Index: scripts/locker-build
===================================================================
--- scripts/locker-build	(revision db98968bc3b30ba528ad63029aaba1addb187104)
+++ scripts/locker-build	(revision db98968bc3b30ba528ad63029aaba1addb187104)
@@ -88,5 +88,5 @@
         --prefix="/mit/barnowl/builds/barnowl-$VERS" --mandir=/mit/barnowl/man \
         --program-suffix="-$VERS" \
-        PROTECT_CFLAGS=-fno-stack-protector \
+        --with-zephyr --without-stack-protector \
         || die "configure failed"
 
Index: tester.c
===================================================================
--- tester.c	(revision eab5aa1d624e6d9d8f97cc7a604bb8750746e999)
+++ tester.c	(revision 3e8ff1e00f28cf98e081e0425ccd467e2b6ef910)
@@ -7,5 +7,5 @@
 owl_global g;
 
-#define OWL_UTIL_NTESTS  6
+#define OWL_UTIL_NTESTS  9
 #define OWL_DICT_NTESTS  19
 #define OWL_OBARRAY_NTESTS  6
Index: text.c
===================================================================
--- text.c	(revision f34dd65b3adbc2eedc045a674dd89b7e20257680)
+++ text.c	(revision 3e8ff1e00f28cf98e081e0425ccd467e2b6ef910)
@@ -125,4 +125,43 @@
 
   return(out2);
+}
+
+#define OWL_TAB_WIDTH 8
+
+/* Caller must free return */
+char *owl_text_expand_tabs(char *in)
+{
+  int ntabs = 0;
+  char *p = in;
+  char *ret, *out;
+  int col;
+
+  while(*p) {
+    if (*(p++) == '\t') ntabs++;
+  }
+
+  ret = owl_malloc(strlen(in) + 1 + OWL_TAB_WIDTH * ntabs);
+
+  p = in;
+  out = ret;
+
+  col = 0;
+  while(*p) {
+    switch(*p) {
+    case '\t':
+      do {*(out++) = ' '; col++; } while (col % OWL_TAB_WIDTH);
+      break;
+    case '\n':
+      col = -1;
+    default:
+      col++;
+      *(out++) = *p;
+    }
+    p++;
+  }
+
+  *out = 0;
+
+  return ret;
 }
 
Index: util.c
===================================================================
--- util.c	(revision 7bf51d5fcadf9407c899e794b37a7e99cd50e18b)
+++ util.c	(revision 3e8ff1e00f28cf98e081e0425ccd467e2b6ef910)
@@ -789,4 +789,13 @@
 	      !strcmp("meep", skiptokens("foo 'bar quux' meep", 2)));
 
+  FAIL_UNLESS("expand_tabs 1",
+              !strcmp("        hi", owl_text_expand_tabs("\thi")));
+
+  FAIL_UNLESS("expand_tabs 2",
+              !strcmp("        hi\nword    tab", owl_text_expand_tabs("\thi\nword\ttab")));
+
+  FAIL_UNLESS("expand_tabs 3",
+              !strcmp("                2 tabs", owl_text_expand_tabs("\t\t2 tabs")));
+
   /* if (numfailed) printf("*** WARNING: failures encountered with owl_util\n"); */
   printf("# END testing owl_util (%d failures)\n", numfailed);
Index: zwrite.c
===================================================================
--- zwrite.c	(revision 3ef779baa1843d5eb90748ba235decc2d937ea50)
+++ zwrite.c	(revision a52d13aca9a922645adbf472d974d139c11126a4)
@@ -9,10 +9,11 @@
 int owl_zwrite_create_from_line(owl_zwrite *z, char *line)
 {
-  int argc, badargs, myargc;
+  int argc, badargs, myargc, i, len;
   char **argv, **myargv;
+  char *msg = NULL;
 
   badargs=0;
   
-  /* start with null entires */
+  /* start with null entries */
   z->realm=NULL;
   z->class=NULL;
@@ -82,5 +83,5 @@
 	break;
       }
-      /* we must already have users or a class or an instance*/
+      /* we must already have users or a class or an instance */
       if (owl_list_get_size(&(z->recips))<1 && (!z->class) && (!z->inst)) {
 	badargs=1;
@@ -91,13 +92,17 @@
       myargv++;
       myargc--;
-      z->message = owl_strdup("");
+      len = 0;
+      for (i=0;i<myargc;i++) {
+        len += strlen(myargv[i-1]) + 1;
+      }
+      msg = owl_malloc(len);
+      msg[0] = '\0';
       while (myargc) {
-	z->message=owl_realloc(z->message, strlen(z->message)+strlen(myargv[0])+5);
-	strcat(z->message, myargv[0]);
-	strcat(z->message, " ");
-	myargc--;
-	myargv++;
-      }
-      z->message[strlen(z->message)-1]='\0'; /* remove last space */
+        strcat(msg, myargv[0]);
+        strcat(msg, " ");
+        myargc--;
+        myargv++;
+      }
+      msg[strlen(msg)-1] = '\0';
       break;
     } else if (!strcmp(myargv[0], "-C")) {
@@ -136,5 +141,10 @@
   if (z->opcode==NULL) z->opcode=owl_strdup("");
   /* z->message is allowed to stay NULL */
-  
+
+  if(msg) {
+    owl_zwrite_set_message(z, msg);
+    owl_free(msg);
+  }
+
   return(0);
 }
@@ -222,5 +232,5 @@
   int i, j;
   char *toline = NULL;
-  char *tmp = NULL;
+  char *tmp = NULL, *tmp2;
 
   if (z->message) owl_free(z->message);
@@ -240,10 +250,14 @@
     }
     tmp = owl_validate_utf8(msg);
-    z->message=owl_sprintf("%s\n%s", toline, tmp);
+    tmp2 = owl_text_expand_tabs(tmp);
+    z->message=owl_sprintf("%s\n%s", toline, tmp2);
     owl_free(toline);
+    owl_free(tmp);
+    owl_free(tmp2);
   } else {
-    z->message=owl_validate_utf8(msg);
-  }
-  if (tmp) owl_free(tmp);
+    tmp=owl_validate_utf8(msg);
+    z->message=owl_text_expand_tabs(tmp);
+    owl_free(tmp);
+  }
 }
 
