Index: commands.c
===================================================================
--- commands.c	(revision d564c3d0a07e5929b3445a63f13cb97eb6b52715)
+++ commands.c	(revision 8a5b5a12327c205b798d00f5cc5505f787e55331)
@@ -1,2 +1,3 @@
+#include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -91,5 +92,14 @@
 	      "Use 'show keymaps' to see the existing keymaps.\n"
 	      "Key sequences may be things like M-C-t or NPAGE.\n\n"
-	      "Ex.: bindkey recv C-b command zwrite -c barnowl"),
+	      "Ex.: bindkey recv C-b command zwrite -c barnowl"
+              "SEE ALSO: bindkey"),
+
+  OWLCMD_ARGS("unbindkey", owl_command_unbindkey, OWL_CTX_ANY,
+	      "removes a binding in a keymap",
+	      "bindkey <keymap> <keyseq>",
+	      "Removes a binding of a key sequence within a keymap.\n"
+	      "Use 'show keymaps' to see the existing keymaps.\n"
+	      "Ex.: unbindkey recv H"
+              "SEE ALSO: bindkey"),
 
   OWLCMD_ARGS("zwrite", owl_command_zwrite, OWL_CTX_INTERACTIVE,
@@ -583,5 +593,5 @@
   OWLCMD_ARGS("smartnarrow", owl_command_smartnarrow, OWL_CTX_INTERACTIVE,
 	      "view only messages similar to the current message",
-	      "smartnarrow [-i | --instance]",
+	      "smartnarrow [-i | --instance]  [-r | --relatde]",
 	      "If the curmsg is a personal message narrow\n"
 	      "   to the conversation with that user.\n"
@@ -591,5 +601,7 @@
 	      "    to the class.\n"
 	      "If the curmsg is a class message and '-i' is specified\n"
-	      "    then narrow to the class and instance.\n"),
+	      "    then narrow to the class and instance.\n"
+	      "If '-r' or '--related' is specified, behave as though the\n"
+              "    'narrow-related' variable was inverted."),
 
   OWLCMD_ARGS("smartfilter", owl_command_smartfilter, OWL_CTX_INTERACTIVE,
@@ -897,13 +909,6 @@
 		  "", ""),
 
-  OWLCMD_VOID_CTX("editline:done", owl_command_editline_done, 
-		  OWL_CTX_EDITLINE,
-		  "completes the command (eg, executes command being composed)",
-		  "", ""),
-
-  OWLCMD_VOID_CTX("editresponse:done", owl_command_editresponse_done, 
-		  OWL_CTX_EDITRESPONSE,
-		  "completes the response to a question",
-		  "", ""),
+  OWLCMD_ALIAS   ("editline:done", "edit:done"),
+  OWLCMD_ALIAS   ("editresponse:done", "edit:done"),
 
   OWLCMD_VOID_CTX("edit:move-up-line", owl_editwin_key_up, 
@@ -918,6 +923,6 @@
 
   OWLCMD_VOID_CTX("edit:done", owl_command_edit_done, 
-		  OWL_CTX_EDITMULTI,
-		  "completes the command (eg, sends message being composed)",
+		  OWL_CTX_EDIT,
+		  "Finishes entering text in the editwin.",
 		  "", ""),
 
@@ -1236,8 +1241,8 @@
       argc-=2; argv+=2; 
     } else if (argc>=2 && !strcmp(argv[1], "--smart-filter")) {
-      filter = owl_function_smartfilter(0);
+      filter = owl_function_smartfilter(0, 0);
       argc-=2; argv+=2; 
     } else if (argc>=2 && !strcmp(argv[1], "--smart-filter-instance")) {
-      filter = owl_function_smartfilter(1);
+      filter = owl_function_smartfilter(1, 0);
       argc-=2; argv+=2; 
     } else {
@@ -1266,8 +1271,8 @@
       argc-=2; argv+=2; 
     } else if (argc>=2 && !strcmp(argv[1], "--smart-filter")) {
-      filter = owl_function_smartfilter(0);
+      filter = owl_function_smartfilter(0, 0);
       argc-=2; argv+=2; 
     } else if (argc>=2 && !strcmp(argv[1], "--smart-filter-instance")) {
-      filter = owl_function_smartfilter(1);
+      filter = owl_function_smartfilter(1, 0);
       argc-=2; argv+=2;  
    } else {
@@ -1285,15 +1290,42 @@
   char *filtname = NULL;
 
-  if (argc == 1) {
-    filtname = owl_function_smartfilter(0);
-  } else if (argc == 2 && (!strcmp(argv[1], "-i") || !strcmp(argv[1], "--instance"))) {
-    filtname = owl_function_smartfilter(1);
-  } else {
-    owl_function_makemsg("Wrong number of arguments for %s", argv[0]);    
-  }
+  char opt;
+  int instance = 0, related = 0, i;
+  char **tmp_argv = owl_malloc(sizeof(char *) * argc);
+
+  for (i = 0; i < argc; i++)
+    tmp_argv[i] = owl_strdup(argv[i]);
+
+  static struct option options[] = {
+    {"instance", 0, 0, 'i'},
+    {"related",  0, 0, 'r'},
+    {NULL,       0, 0, 0}};
+  while ((opt = getopt_long(argc, tmp_argv, "ir", options, NULL)) != -1) {
+    switch (opt) {
+      case 'i':
+        instance = 1;
+        break;
+      case 'r':
+        related = 1;
+        break;
+      default:
+        owl_function_makemsg("Wrong number of arguments for %s (%c)", argv[0], opt);
+        goto done;
+    }
+  }
+
+  for (i = 0; i < argc; i++)
+    owl_free(tmp_argv[i]);
+  owl_free(tmp_argv);
+
+  filtname = owl_function_smartfilter(instance, related);
+
   if (filtname) {
     owl_function_change_currentview_filter(filtname);
     owl_free(filtname);
   }
+
+done:
+  optind = 0; /* reset getopt */
   return NULL;
 }
@@ -1304,7 +1336,7 @@
 
   if (argc == 1) {
-    filtname = owl_function_smartfilter(0);
+    filtname = owl_function_smartfilter(0, 0);
   } else if (argc == 2 && (!strcmp(argv[1], "-i") || !strcmp(argv[1], "--instance"))) {
-    filtname = owl_function_smartfilter(1);
+    filtname = owl_function_smartfilter(1, 0);
   } else {
     owl_function_makemsg("Wrong number of arguments for %s", argv[0]);    
@@ -1672,4 +1704,32 @@
   return NULL;
 }
+
+
+char *owl_command_unbindkey(int argc, const char *const *argv, const char *buf)
+{
+  owl_keymap *km;
+  int ret;
+
+  if (argc < 3) {
+    owl_function_makemsg("Usage: bindkey <keymap> <binding>");
+    return NULL;
+  }
+  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), argv[1]);
+  if (!km) {
+    owl_function_makemsg("No such keymap '%s'", argv[1]);
+    return NULL;
+  }
+  ret = owl_keymap_remove_binding(km, argv[2]);
+  if (ret == -1) {
+    owl_function_makemsg("Unable to unbind '%s' in keymap '%s'.",
+			 argv[2], argv[1]);
+    return NULL;
+  } else if (ret == -2) {
+    owl_function_makemsg("No such binding '%s' in keymap '%s'.",
+                         argv[2], argv[1]);
+  }
+  return NULL;
+}
+
 
 void owl_command_quit(void)
@@ -2220,5 +2280,5 @@
     return NULL;
   }
-  filtname = owl_function_classinstfilt(argv[1], NULL);
+  filtname = owl_function_classinstfilt(argv[1], NULL, owl_global_is_narrow_related(&g));
   owl_function_change_currentview_filter(filtname);
   owl_free(filtname);
@@ -2332,5 +2392,5 @@
   }
 
-  filtname=owl_function_classinstfilt(argv[1], NULL);
+  filtname=owl_function_classinstfilt(argv[1], NULL, owl_global_is_narrow_related(&g));
   (void) owl_function_color_filter(filtname, argv[2], (argc == 4 ? argv[3] : NULL));
   return NULL;
@@ -2524,7 +2584,7 @@
   /* if we get two arguments, ask for the password */
   if (argc==2) {
-    owl_global_set_buffercommand(&g, argv[1]);
-    owl_global_set_buffercallback(&g, &owl_callback_aimlogin);
-    owl_function_start_password("AIM Password: ");
+    owl_editwin *e = owl_function_start_password("AIM Password: ");
+    owl_editwin_set_cbdata(e, owl_strdup(argv[1]), owl_free);
+    owl_editwin_set_callback(e, owl_callback_aimlogin);
     return(NULL);
   } else {
@@ -2655,10 +2715,9 @@
   }
 
-  owl_editwin_fullclear(e);
   owl_global_set_needrefresh(&g);
+  owl_global_pop_context(&g);
+
   owl_global_set_typwin_inactive(&g);
-  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_ONELINE, NULL);
-
-  owl_global_pop_context(&g);
+  owl_editwin_delete(e);
 }
 
@@ -2710,51 +2769,19 @@
 }
 
-void owl_command_editline_done(owl_editwin *e)
+void owl_command_edit_done(owl_editwin *e)
 {
   owl_history *hist=owl_editwin_get_history(e);
-  char *rv, *cmd;
-
-  owl_history_store(hist, owl_editwin_get_text(e));
-  owl_history_reset(hist);
-  owl_global_set_typwin_inactive(&g);
-  owl_global_pop_context(&g);
-  cmd = owl_strdup(owl_editwin_get_text(e));
-  owl_editwin_fullclear(e);
-  rv = owl_function_command(cmd);
-  owl_free(cmd);
-
-  owl_global_set_needrefresh(&g);
-
-  if (rv) {
-    owl_function_makemsg("%s", rv);
-    owl_free(rv);
-  }
-}
-
-
-void owl_command_editresponse_done(owl_editwin *e)
-{
-  owl_function_run_buffercommand();
-
-  owl_global_set_typwin_inactive(&g);
-  owl_global_pop_context(&g);
-  owl_editwin_fullclear(e);
-  owl_global_set_needrefresh(&g);
-}
-
-
-void owl_command_edit_done(owl_editwin *e)
-{
-  owl_history *hist=owl_editwin_get_history(e);
-
-  owl_history_store(hist, owl_editwin_get_text(e));
-  owl_history_reset(hist);
-
-  owl_function_run_buffercommand();
-  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_ONELINE, NULL);
-  owl_editwin_fullclear(e);
+
+  if (hist) {
+    owl_history_store(hist, owl_editwin_get_text(e));
+    owl_history_reset(hist);
+  }
+
   owl_global_set_typwin_inactive(&g);
   owl_global_pop_context(&g);
   owl_global_set_needrefresh(&g);
+
+  owl_editwin_do_callback(e);
+  owl_editwin_delete(e);
 }
 
Index: configure.ac
===================================================================
--- configure.ac	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
+++ configure.ac	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
@@ -98,4 +98,10 @@
 AC_MSG_NOTICE([Adding perl LIBS ${FOO}])
 LIBS=${LIBS}\ ${FOO}
+AC_CHECK_LIB([perl], [perl_alloc],, AC_MSG_ERROR([No libperl found]))
+
+AX_PROG_PERL_MODULES([Class::Accessor::Fast],,
+                     [AC_MSG_ERROR([cannot find perl module Class::Accessor::Fast.])])
+AX_PROG_PERL_MODULES([PAR],,
+                     [AC_MSG_WARN([PAR.pm not found. Loadable modules will be disabled.])])
 
 dnl Add CFLAGS and LDFLAGS for glib-2.0
@@ -135,4 +141,8 @@
 AC_PROG_INSTALL
 AC_PROG_RANLIB
+AC_CHECK_PROG([HAVE_ZIP], [zip], [yes], [no])
+if test "x${HAVE_ZIP}" = "xno"; then
+   AC_MSG_ERROR([cannot find a 'zip' binary.])
+fi
 
 AC_CONFIG_FILES([Makefile libfaim/Makefile perl/Makefile perl/modules/Makefile])
Index: editwin.c
===================================================================
--- editwin.c	(revision f449096479541df1434d1d796890d0bd2939f072)
+++ editwin.c	(revision 21dd3912e6d7e5c827618d283b392c71e3f9dff2)
@@ -35,6 +35,6 @@
   oe_excursion *excursions;
 
-  char *command;
   void (*callback)(struct _owl_editwin*);
+  void (*destroy_cbdata)(void *);
   void *cbdata;
 };
@@ -45,5 +45,4 @@
 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(const 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);
@@ -58,4 +57,5 @@
 static int oe_copy_region(owl_editwin *e);
 static char *oe_chunk(owl_editwin *e, int start, int end);
+static void oe_destroy_cbdata(owl_editwin *e);
 
 #define INCR 4096
@@ -63,5 +63,5 @@
 #define WHITESPACE " \n\t"
 
-owl_editwin *owl_editwin_allocate(void)
+static owl_editwin *owl_editwin_allocate(void)
 {
   owl_editwin *e;
@@ -75,23 +75,11 @@
   owl_free(e->buff);
   owl_free(e->killbuf);
-  owl_free(e->command);
   /* just in case someone forgot to clean up */
   while (e->excursions) {
     oe_release_excursion(e, e->excursions);
   }
+  oe_destroy_cbdata(e);
 
   owl_free(e);
-}
-
-static int oe_count_glyphs(const char *s)
-{
-  int count = 0;
-  const 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;
 }
 
@@ -116,8 +104,10 @@
 }
 
-/* initialize the editwin e.
- * 'win' is an already initialzed curses window that will be used by editwin
- */
-void owl_editwin_init(owl_editwin *e, WINDOW *win, int winlines, int wincols, int style, owl_history *hist)
+static void _owl_editwin_init(owl_editwin *e,
+                              WINDOW *win,
+                              int winlines,
+                              int wincols,
+                              int style,
+                              owl_history *hist)
 {
   e->buff=owl_malloc(INCR);
@@ -145,12 +135,13 @@
   e->echochar='\0';
 
-  /* We get initialized multiple times, but we need to hold on to
-     the callbacks, so we can't NULL them here. */
-  /*
-    e->command = NULL;
-    e->callback = NULL;
-    e->cbdata = NULL;
-  */
   if (win) werase(win);
+}
+
+owl_editwin *owl_editwin_new(WINDOW *win, int winlines, int wincols, int style, owl_history *hist)
+{
+  owl_editwin *e = owl_editwin_allocate();
+
+  _owl_editwin_init(e, win, winlines, wincols, style, hist);
+  return e;
 }
 
@@ -188,16 +179,4 @@
 }
 
-void owl_editwin_set_command(owl_editwin *e, const char *command)
-{
-  if(e->command) owl_free(e->command);
-  e->command = owl_strdup(command);
-}
-
-const 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*))
 {
@@ -210,7 +189,16 @@
 }
 
-void owl_editwin_set_cbdata(owl_editwin *e, void *data)
-{
+static void oe_destroy_cbdata(owl_editwin *e) {
+  if (e->destroy_cbdata)
+    e->destroy_cbdata(e->cbdata);
+  e->cbdata = NULL;
+  e->destroy_cbdata = NULL;
+}
+
+void owl_editwin_set_cbdata(owl_editwin *e, void *data, void (*destroy)(void *))
+{
+  oe_destroy_cbdata(e);
   e->cbdata = data;
+  e->destroy_cbdata = destroy;
 }
 
@@ -259,29 +247,4 @@
 }
 
-void owl_editwin_new_style(owl_editwin *e, int newstyle, owl_history *h)
-{
-  e->hist = h;
-
-  if (e->style==newstyle) return;
-
-  if (newstyle==OWL_EDITWIN_STYLE_MULTILINE) {
-    e->style=newstyle;
-  } else if (newstyle==OWL_EDITWIN_STYLE_ONELINE) {
-    e->style=newstyle;
-
-    /* nuke everything after the first line */
-    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),  "");
-  }
-}
-
-/* completly reinitialize the buffer */
-void owl_editwin_fullclear(owl_editwin *e)
-{
-  owl_free(e->buff);
-  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
-}
-
 /* clear all text except for locktext and put the cursor at the
  * beginning
@@ -302,5 +265,5 @@
 
   owl_free(e->buff);
-  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
+  _owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
 
   if (lock > 0) {
Index: functions.c
===================================================================
--- functions.c	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
+++ functions.c	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
@@ -278,5 +278,5 @@
 }
 
-void owl_function_start_edit_win(const char *line, void (*callback)(owl_editwin *), void *data)
+void owl_function_start_edit_win(const char *line, void (*callback)(owl_editwin *), void *data, void (*cleanup)(void *))
 {
   owl_editwin *e;
@@ -284,8 +284,6 @@
 
   /* create and setup the editwin */
-  e = owl_global_get_typwin(&g);
-  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE,
-			owl_global_get_msg_history(&g));
-  owl_editwin_clear(e);
+  e = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_MULTILINE,
+                                   owl_global_get_msg_history(&g));
   owl_editwin_set_dotsend(e);
   s = owl_sprintf("----> %s\n", line);
@@ -293,9 +291,6 @@
   owl_free(s);
 
-  /* make it active */
-  owl_global_set_typwin_active(&g);
-
-  owl_editwin_set_cbdata(owl_global_get_typwin(&g), data);
-  owl_global_set_buffercallback(&g, callback);
+  owl_editwin_set_cbdata(e, data, cleanup);
+  owl_editwin_set_callback(e, callback);
   owl_global_push_context(&g, OWL_CTX_EDITMULTI, e, "editmulti");
 }
@@ -313,6 +308,7 @@
 			 noun);
 
-  owl_function_start_edit_win(line, callback, NULL);
-  owl_global_set_buffercommand(&g, line);
+  owl_function_start_edit_win(line, callback,
+                              owl_strdup(line),
+                              owl_free);
 }
 
@@ -350,5 +346,6 @@
 
 void owl_callback_zwrite(owl_editwin *e) {
-  owl_function_zwrite(owl_editwin_get_command(e),
+  char *command = owl_editwin_get_cbdata(e);
+  owl_function_zwrite(command,
                       owl_editwin_get_text(e));
 }
@@ -459,5 +456,6 @@
 
 void owl_callback_aimwrite(owl_editwin *e) {
-  owl_function_aimwrite(owl_editwin_get_command(e),
+  char *command = owl_editwin_get_cbdata(e);
+  owl_function_aimwrite(command,
                         owl_editwin_get_text(e));
 }
@@ -894,5 +892,6 @@
 
 void owl_callback_aimlogin(owl_editwin *e) {
-  owl_function_aimlogin(owl_editwin_get_command(e),
+  char *user = owl_editwin_get_cbdata(e);
+  owl_function_aimlogin(user,
                         owl_editwin_get_text(e));
 }
@@ -1193,9 +1192,4 @@
 {
   owl_global_set_resize_pending(&g);
-}
-
-void owl_function_run_buffercommand(void)
-{
-  owl_editwin_do_callback(owl_global_get_typwin(&g));
 }
 
@@ -1917,12 +1911,21 @@
 }
 
+void owl_callback_command(owl_editwin *e)
+{
+  char *rv;
+  const char *line = owl_editwin_get_text(e);
+
+  rv = owl_function_command(line);
+   if (rv) {
+    owl_function_makemsg("%s", rv);
+    owl_free(rv);
+  }
+}
+
 void owl_function_start_command(const char *line)
 {
   owl_editwin *tw;
 
-  tw=owl_global_get_typwin(&g);
-  owl_global_set_typwin_active(&g);
-  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, 
-			owl_global_get_cmd_history(&g));
+  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
 
   owl_editwin_set_locktext(tw, "command: ");
@@ -1933,13 +1936,12 @@
 
   owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline");
-}
-
-void owl_function_start_question(const char *line)
+  owl_editwin_set_callback(tw, owl_callback_command);
+}
+
+owl_editwin *owl_function_start_question(const char *line)
 {
   owl_editwin *tw;
 
-  tw=owl_global_get_typwin(&g);
-  owl_global_set_typwin_active(&g);
-  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
+  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
 
   owl_editwin_set_locktext(tw, line);
@@ -1948,14 +1950,14 @@
   owl_editwin_redisplay(tw);
 
-  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editline");
-}
-
-void owl_function_start_password(const char *line)
+  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
+  return tw;
+}
+
+owl_editwin *owl_function_start_password(const char *line)
 {
   owl_editwin *tw;
 
-  tw=owl_global_get_typwin(&g);
-  owl_global_set_typwin_active(&g);
-  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
+  tw = owl_global_set_typwin_active(&g, OWL_EDITWIN_STYLE_ONELINE, NULL);
+
   owl_editwin_set_echochar(tw, '*');
 
@@ -1966,4 +1968,5 @@
 
   owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
+  return tw;
 }
 
@@ -2289,6 +2292,7 @@
  * instance is NULL then catch all messgaes in the class.  Returns the
  * name of the filter, which the caller must free.
+ * If 'related' is nonzero, encompass unclasses and .d classes as well.
  */
-char *owl_function_classinstfilt(const char *c, const char *i) 
+char *owl_function_classinstfilt(const char *c, const char *i, int related) 
 {
   owl_filter *f;
@@ -2297,14 +2301,21 @@
   char *class, *instance = NULL;
 
-  class = owl_util_baseclass(c);
-  if(i) {
-    instance = owl_util_baseclass(i);
+  if (related) {
+    class = owl_util_baseclass(c);
+    if (i) {
+      instance = owl_util_baseclass(i);
+    }
+  } else {
+    class = owl_strdup(c);
+    if (i) {
+      instance = owl_strdup(i);
+    }
   }
 
   /* name for the filter */
   if (!instance) {
-    filtname = owl_sprintf("class-%s", class);
-  } else {
-    filtname = owl_sprintf("class-%s-instance-%s", class, instance);
+    filtname = owl_sprintf("%sclass-%s", related ? "related-" : "", class);
+  } else {
+    filtname = owl_sprintf("%sclass-%s-instance-%s", related ? "related-" : "", class, instance);
   }
   /* downcase it */
@@ -2338,8 +2349,8 @@
   }
 
-  argbuff = owl_sprintf("class ^(un)*%s(\\.d)*$", tmpclass);
+  argbuff = owl_sprintf(related ? "class ^(un)*%s(\\.d)*$" : "class ^%s$", tmpclass);
   if (tmpinstance) {
     char *tmp = argbuff;
-    argbuff = owl_sprintf("%s and ( instance ^(un)*%s(\\.d)*$ )", tmp, tmpinstance);
+    argbuff = owl_sprintf(related ? "%s and ( instance ^(un)*%s(\\.d)*$ )" : "%s and instance ^%s$", tmp, tmpinstance);
     owl_free(tmp);
   }
@@ -2512,11 +2523,12 @@
  *    name to the AIM conversation with that user 
  */
-char *owl_function_smartfilter(int type)
+char *owl_function_smartfilter(int type, int invert_related)
 {
   const owl_view *v;
   const owl_message *m;
   char *zperson, *filtname=NULL;
-  const char *argv[1];
-  
+  const char *argv[2];
+  int related = owl_global_is_narrow_related(&g) ^ invert_related;
+
   v=owl_global_get_current_view(&g);
   m=owl_view_get_element(v, owl_global_get_curmsg(&g));
@@ -2562,5 +2574,5 @@
     /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
     if (!strcasecmp(owl_message_get_class(m), "message")) {
-      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
+      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
       return(filtname);
     }
@@ -2568,7 +2580,7 @@
     /* otherwise narrow to the class */
     if (type==0) {
-      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL);
+      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL, related);
     } else if (type==1) {
-      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
+      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m), related);
     }
     return(filtname);
@@ -2576,8 +2588,7 @@
 
   /* pass it off to perl */
-  if(type) {
-    argv[0] = "-i";
-  };
-  return owl_perlconfig_message_call_method(m, "smartfilter", type ? 1 : 0, argv);
+  argv[0] = type ? "1" : "0";
+  argv[1] = related ? "1" : "0";
+  return owl_perlconfig_message_call_method(m, "smartfilter", 2, argv);
 }
 
@@ -3418,52 +3429,35 @@
   int i, j;
   owl_list anyone;
-  owl_message *m;
   owl_zbuddylist *zbl;
+  GList **zaldlist;
+  GList *zaldptr;
+  ZAsyncLocateData_t *zald;
   const char *user;
-  ZLocations_t location[200];
-  int numlocs, ret;
 
   if (!owl_global_is_havezephyr(&g)) return;
-
-  zbl=owl_global_get_zephyr_buddylist(&g);
+  owl_global_set_pseudologin_notify(&g, notify);
+  zbl = owl_global_get_zephyr_buddylist(&g);
+  zaldlist = owl_global_get_zaldlist(&g);
+
+  /* Clear the existing ZALDs first. */
+  zaldptr = g_list_first(*zaldlist);
+  while (zaldptr) {
+    ZFreeALD(zaldptr->data);
+    owl_free(zaldptr->data);
+    zaldptr = g_list_next(zaldptr);
+  }
+  g_list_free(*zaldlist);
+  *zaldlist = NULL;
 
   owl_list_create(&anyone);
-  ret=owl_zephyr_get_anyone_list(&anyone, NULL);
-
-  j=owl_list_get_size(&anyone);
-  for (i=0; i<j; i++) {
-    user=owl_list_get_element(&anyone, i);
-    ret=ZLocateUser(zstr(user), &numlocs, ZAUTH);
-    if (ret!=ZERR_NONE) {
-      owl_function_error("Error getting location for %s", user);
-      continue;
-    }
-    numlocs=200;
-    ret=ZGetLocations(location, &numlocs);
-    if (ret==0) {
-      if ((numlocs>0) && !owl_zbuddylist_contains_user(zbl, user)) {
-	/* Send a PSEUDO LOGIN! */
-	if (notify) {
-	  m=owl_malloc(sizeof(owl_message));
-	  owl_message_create_pseudo_zlogin(m, 0, user, location[0].host, location[0].time, location[0].tty);
-	  owl_global_messagequeue_addmsg(&g, m);
-	}
-	owl_zbuddylist_adduser(zbl, user);
-	owl_function_debugmsg("owl_function_zephyr_buddy_check: login for %s ", user);
-      } else if ((numlocs==0) && owl_zbuddylist_contains_user(zbl, user)) {
-	/* I don't think this ever happens (if there are 0 locations we should get an error from
-	 * ZGetLocations)
-	 */
-	owl_function_error("owl_function_zephyr_buddy_check: exceptional case logout for %s ",user);
-      }
-    } else if ((ret==ZERR_NOLOCATIONS) && owl_zbuddylist_contains_user(zbl, user)) {
-      /* Send a PSEUDO LOGOUT! */
-      if (notify) {
-	m=owl_malloc(sizeof(owl_message));
-	owl_message_create_pseudo_zlogin(m, 1, user, "", "", "");
-	owl_global_messagequeue_addmsg(&g, m);
-      }
-      owl_zbuddylist_deluser(zbl, user);
-      owl_function_debugmsg("owl_function_zephyr_buddy_check: logout for %s ",user);
+  owl_zephyr_get_anyone_list(&anyone, NULL);
+  j = owl_list_get_size(&anyone);
+  for (i = 0; i < j; i++) {
+    user = owl_list_get_element(&anyone, i);
+    zald = owl_malloc(sizeof(ZAsyncLocateData_t));
+    if (ZRequestLocations(zstr(user), zald, UNACKED, ZAUTH) == ZERR_NONE) {
+      *zaldlist = g_list_append(*zaldlist, zald);
+    } else {
+      owl_free(zald);
     }
   }
Index: global.c
===================================================================
--- global.c	(revision 2ee9e8d643311c1b180ebed2ab20399c20cfb42a)
+++ global.c	(revision 98d296d62b216a1e2182e5ecebd0bb94beb7cc60)
@@ -45,6 +45,5 @@
   g->rightshift=0;
 
-  g->tw = owl_editwin_allocate();
-  owl_editwin_init(g->tw, NULL, owl_global_get_typwin_lines(g), g->cols, OWL_EDITWIN_STYLE_ONELINE, NULL);
+  g->tw = NULL;
 
   owl_keyhandler_init(&g->kh);
@@ -58,5 +57,4 @@
   g->curmsg_vert_offset=0;
   g->resizepending=0;
-  g->typwinactive=0;
   g->direction=OWL_DIRECTION_DOWNWARDS;
   g->zaway=0;
@@ -111,4 +109,7 @@
   owl_zbuddylist_create(&(g->zbuddies));
 
+  g->zaldlist = NULL;
+  g->pseudologin_notify = 0;
+
   owl_obarray_init(&(g->obarray));
 
@@ -171,5 +172,6 @@
   _owl_panel_set_window(&g->typpan, newwin(typwin_lines, cols, g->recwinlines+2, 0));
 
-  owl_editwin_set_curswin(g->tw, owl_global_get_curs_typwin(g), typwin_lines, g->cols);
+  if (g->tw)
+      owl_editwin_set_curswin(g->tw, owl_global_get_curs_typwin(g), typwin_lines, g->cols);
 
   idlok(owl_global_get_curs_typwin(g), FALSE);
@@ -324,22 +326,4 @@
 }
 
-/* buffercommand */
-
-void owl_global_set_buffercommand(owl_global *g, const char *command) {
-  owl_editwin_set_command(owl_global_get_typwin(g), command);
-}
-
-const char *owl_global_get_buffercommand(const owl_global *g) {
-  return owl_editwin_get_command(owl_global_get_typwin(g));
-}
-
-void owl_global_set_buffercallback(owl_global *g, void (*cb)(owl_editwin*)) {
-  owl_editwin_set_callback(owl_global_get_typwin(g), cb);
-}
-
-void (*owl_global_get_buffercallback(const owl_global *g))(owl_editwin*) {
-  return owl_editwin_get_callback(owl_global_get_typwin(g));
-}
-
 /* refresh */
 
@@ -381,23 +365,25 @@
 /* typwin */
 
-int owl_global_is_typwin_active(const owl_global *g) {
-  if (g->typwinactive==1) return(1);
-  return(0);
-}
-
-void owl_global_set_typwin_active(owl_global *g) {
-  int d = owl_global_get_typewindelta(g);
-  if (d > 0)
+owl_editwin *owl_global_set_typwin_active(owl_global *g, int style, owl_history *hist) {
+  int d;
+  d = owl_global_get_typewindelta(g);
+  if (d > 0 && style == OWL_EDITWIN_STYLE_MULTILINE)
       owl_function_resize_typwin(owl_global_get_typwin_lines(g) + d);
 
-  g->typwinactive=1;
+  g->tw = owl_editwin_new(owl_global_get_curs_typwin(g),
+                          owl_global_get_typwin_lines(g),
+                          g->cols,
+                          style,
+                          hist);
+  return g->tw;
 }
 
 void owl_global_set_typwin_inactive(owl_global *g) {
   int d = owl_global_get_typewindelta(g);
-  if (d > 0)
+  if (d > 0 && owl_editwin_get_style(g->tw) == OWL_EDITWIN_STYLE_MULTILINE)
       owl_function_resize_typwin(owl_global_get_typwin_lines(g) - d);
 
-  g->typwinactive=0;
+  werase(owl_global_get_curs_typwin(g));
+  g->tw = NULL;
 }
 
@@ -531,5 +517,9 @@
   owl_mainwin_redisplay(&(g->mw));
   sepbar(NULL);
-  owl_editwin_redisplay(g->tw);
+  if (g->tw)
+      owl_editwin_redisplay(g->tw);
+  else
+    werase(owl_global_get_curs_typwin(g));
+
   owl_function_full_redisplay();
 
@@ -968,4 +958,19 @@
 }
 
+GList **owl_global_get_zaldlist(owl_global *g)
+{
+  return &(g->zaldlist);
+}
+
+int owl_global_get_pseudologin_notify(owl_global *g)
+{
+  return g->pseudologin_notify;
+}
+
+void owl_global_set_pseudologin_notify(owl_global *g, int notify)
+{
+  g->pseudologin_notify = notify;
+}
+
 struct termios *owl_global_get_startup_tio(owl_global *g)
 {
Index: keybinding.c
===================================================================
--- keybinding.c	(revision 920201c28b634b172870ffe63419fc66ede53158)
+++ keybinding.c	(revision 8a921b5e61689654774fef672165f4c56c01036d)
@@ -14,7 +14,4 @@
 int owl_keybinding_init(owl_keybinding *kb, const char *keyseq, const char *command, void (*function_fn)(void), const char *desc)
 {
-  char **ktokens;
-  int    nktokens, i;
-  
   owl_function_debugmsg("owl_keybinding_init: creating binding for <%s> with desc: <%s>", keyseq, desc);
   if (command && !function_fn) {
@@ -25,4 +22,20 @@
     return(-1);
   }
+
+  if (owl_keybinding_make_keys(kb, keyseq) != 0) {
+    return(-1);
+  }
+
+  if (command) kb->command = owl_strdup(command);
+  kb->function_fn = function_fn;
+  if (desc) kb->desc = owl_strdup(desc);
+  else kb->desc = NULL;
+  return(0);
+}
+
+int owl_keybinding_make_keys(owl_keybinding *kb, const char *keyseq)
+{
+  char **ktokens;
+  int    nktokens, i;
 
   ktokens = atokenize(keyseq, " ", &nktokens);
@@ -42,11 +55,5 @@
   }
   kb->len = nktokens;
-
   atokenize_delete(ktokens, nktokens);
-
-  if (command) kb->command = owl_strdup(command);
-  kb->function_fn = function_fn;
-  if (desc) kb->desc = owl_strdup(desc);
-  else kb->desc = NULL;
   return(0);
 }
Index: keymap.c
===================================================================
--- keymap.c	(revision bb0d4398c98044f9ad40b838f5c410f0670c6bb0)
+++ keymap.c	(revision 8a921b5e61689654774fef672165f4c56c01036d)
@@ -51,5 +51,30 @@
   }
   return owl_list_append_element(&km->bindings, kb);  
-}
+
+}
+
+/* removes the binding associated with the keymap */
+int owl_keymap_remove_binding(owl_keymap *km, const char *keyseq)
+{
+  owl_keybinding *kb, *curkb;
+  int i;
+
+  if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1);
+  if (0 != owl_keybinding_make_keys(kb, keyseq)) {
+    owl_free(kb);
+    return(-1);
+  }
+
+  for (i = owl_list_get_size(&km->bindings)-1; i >= 0; i--) {
+    curkb = owl_list_get_element(&km->bindings, i);
+    if (owl_keybinding_equal(curkb, kb)) {
+      owl_list_remove_element(&km->bindings, i);
+      owl_keybinding_delete(curkb);
+      return(0);
+    }
+  }
+  return(-2);
+}
+
 
 /* returns a summary line describing this keymap.  the caller must free. */
Index: keys.c
===================================================================
--- keys.c	(revision 8830df47052c239516cb1ea423bdb6db94444218)
+++ keys.c	(revision 8a5b5a12327c205b798d00f5cc5505f787e55331)
@@ -246,4 +246,6 @@
   BIND_CMD("M-n", "smartnarrow",      "narrow to a view based on the current message");
   BIND_CMD("M-N", "smartnarrow -i",   "narrow to a view based on the current message, and consider instance pair");
+  BIND_CMD("M-m", "smartnarrow -r",   "like M-n but with 'narrow-related' temporarily flipped.");
+  BIND_CMD("M-M", "smartnarrow -ri",  "like M-N but with 'narrow-related' temporarily flipped.");
   BIND_CMD("M-p", "view personal", "");
   
Index: m4/ax_prog_perl_modules.m4
===================================================================
--- m4/ax_prog_perl_modules.m4	(revision 2d3ed95eba3d5651a17d8ec24cd534352c8a2070)
+++ m4/ax_prog_perl_modules.m4	(revision 2d3ed95eba3d5651a17d8ec24cd534352c8a2070)
@@ -0,0 +1,77 @@
+# ===========================================================================
+#      http://www.nongnu.org/autoconf-archive/ax_prog_perl_modules.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PROG_PERL_MODULES([MODULES], [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+#
+# DESCRIPTION
+#
+#   Checks to see if the given perl modules are available. If true the shell
+#   commands in ACTION-IF-TRUE are executed. If not the shell commands in
+#   ACTION-IF-FALSE are run. Note if $PERL is not set (for example by
+#   calling AC_CHECK_PROG, or AC_PATH_PROG), AC_CHECK_PROG(PERL, perl, perl)
+#   will be run.
+#
+#   MODULES is a space separated list of module names. To check for a
+#   minimum version of a module, append the version number to the module
+#   name, separated by an equals sign.
+#
+#   Example:
+#
+#     AX_PROG_PERL_MODULES( Text::Wrap Net::LDAP=1.0.3, ,
+#                           AC_MSG_WARN(Need some Perl modules)
+#
+# LICENSE
+#
+#   Copyright (c) 2009 Dean Povey <povey@wedgetail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 6
+
+AU_ALIAS([AC_PROG_PERL_MODULES], [AX_PROG_PERL_MODULES])
+AC_DEFUN([AX_PROG_PERL_MODULES],[dnl
+
+m4_define([ax_perl_modules])
+m4_foreach([ax_perl_module], m4_split(m4_normalize([$1])),
+	  [
+	   m4_append([ax_perl_modules],
+		     [']m4_bpatsubst(ax_perl_module,=,[ ])[' ])
+          ])
+
+# Make sure we have perl
+if test -z "$PERL"; then
+AC_CHECK_PROG(PERL,perl,perl)
+fi
+
+if test "x$PERL" != x; then
+  ax_perl_modules_failed=0
+  for ax_perl_module in ax_perl_modules; do
+    AC_MSG_CHECKING(for perl module $ax_perl_module)
+
+    # Would be nice to log result here, but can't rely on autoconf internals
+    $PERL -e "use $ax_perl_module; exit" > /dev/null 2>&1
+    if test $? -ne 0; then
+      AC_MSG_RESULT(no);
+      ax_perl_modules_failed=1
+   else
+      AC_MSG_RESULT(ok);
+    fi
+  done
+
+  # Run optional shell commands
+  if test "$ax_perl_modules_failed" = 0; then
+    :
+    $2
+  else
+    :
+    $3
+  fi
+else
+  AC_MSG_WARN(could not find perl)
+fi])dnl
Index: owl.c
===================================================================
--- owl.c	(revision f449096479541df1434d1d796890d0bd2939f072)
+++ owl.c	(revision 98d296d62b216a1e2182e5ecebd0bb94beb7cc60)
@@ -629,5 +629,5 @@
       /* leave the cursor in the appropriate window */
       if (!owl_popwin_is_active(owl_global_get_popwin(&g))
-	  && owl_global_is_typwin_active(&g)) {
+	  && owl_global_get_typwin(&g)) {
 	owl_function_set_cursor(typwin);
       } else {
Index: owl.h
===================================================================
--- owl.h	(revision 03c5bddef12e63f0cf2dacca911a8830b13d76e9)
+++ owl.h	(revision 98d296d62b216a1e2182e5ecebd0bb94beb7cc60)
@@ -559,5 +559,4 @@
   int resizepending;
   int recwinlines;
-  int typwinactive;
   char *thishost;
   char *homedir;
@@ -604,4 +603,6 @@
   owl_zbuddylist zbuddies;
   owl_timer zephyr_buddycheck_timer;
+  GList *zaldlist;
+  int pseudologin_notify;
   struct termios startup_tio;
   owl_obarray obarray;
Index: perl/lib/BarnOwl/Style/Default.pm
===================================================================
--- perl/lib/BarnOwl/Style/Default.pm	(revision ad0deddb4298f15386cb0fca17ff0b2265b9ba7d)
+++ perl/lib/BarnOwl/Style/Default.pm	(revision 0fe69d275af9f0d0a83259b7494566c102b17902)
@@ -168,5 +168,5 @@
     ? '@color(cyan)' : '';
 
-  my $chars = $oneline ? qr/[[:cntrl:]]/ : qr/[^[:print:]]|[\r\cK\f]/;
+  my $chars = $oneline ? qr/[[:cntrl:]]/ : qr/[^[:print:]\n]|[\r\cK\f]/;
 
   $s =~ s/($chars)/
@@ -179,5 +179,5 @@
 
 As above, but always be conservative, and replace with a '?' instead
-of something mmore elaborate.
+of something more elaborate.
 
 =cut
Index: perlconfig.c
===================================================================
--- perlconfig.c	(revision 8f2d9bf19fa0ce948c8f50406b95759c78a06046)
+++ perlconfig.c	(revision 1b1cd2cda4440db58f76fdfc5a6f5ab697a2bdf5)
@@ -574,7 +574,10 @@
   FREETMPS;
   LEAVE;
-
-  SvREFCNT_dec(cb);
-  owl_editwin_set_cbdata(e, NULL);
+}
+
+void owl_perlconfig_dec_refcnt(void *data)
+{
+  SV *v = data;
+  SvREFCNT_dec(v);
 }
 
Index: perlglue.xs
===================================================================
--- perlglue.xs	(revision 8c591789302e72f4d45fab6b792bfd08bfed194a)
+++ perlglue.xs	(revision 9186c75ec164683622b4eac28d412877f724cfef)
@@ -172,4 +172,5 @@
 	SV *callback
 	PREINIT:
+		owl_editwin *e;
 	CODE:
 	{
@@ -177,8 +178,10 @@
 			croak("Callback must be a subref");
 
-		owl_function_start_question(line);
-
-		owl_editwin_set_cbdata(owl_global_get_typwin(&g), newSVsv(callback));
-		owl_editwin_set_callback(owl_global_get_typwin(&g), owl_perlconfig_edit_callback);
+		e = owl_function_start_question(line);
+
+		owl_editwin_set_cbdata(e,
+				       newSVsv(callback),
+				       owl_perlconfig_dec_refcnt);
+		owl_editwin_set_callback(e, owl_perlconfig_edit_callback);
 	}
 
@@ -188,4 +191,5 @@
 	SV *callback
 	PREINIT:
+		owl_editwin *e;
 	CODE:
 	{
@@ -193,8 +197,10 @@
 			croak("Callback must be a subref");
 
-		owl_function_start_password(line);
-
-		owl_editwin_set_cbdata(owl_global_get_typwin(&g), newSVsv(callback));
-		owl_editwin_set_callback(owl_global_get_typwin(&g), owl_perlconfig_edit_callback);
+		e = owl_function_start_password(line);
+
+		owl_editwin_set_cbdata(e,
+				       newSVsv(callback),
+				       owl_perlconfig_dec_refcnt);
+		owl_editwin_set_callback(e, owl_perlconfig_edit_callback);
 	}
 
@@ -208,5 +214,8 @@
 			croak("Callback must be a subref");
 
-		owl_function_start_edit_win(line, owl_perlconfig_edit_callback, newSVsv(callback));
+		owl_function_start_edit_win(line,
+					    owl_perlconfig_edit_callback,
+					    newSVsv(callback),
+					    owl_perlconfig_dec_refcnt);
 	}
 
@@ -542,6 +551,13 @@
 	int count;
 	const char *string;
-	CODE:
-		RETVAL = owl_editwin_replace(owl_global_get_typwin(&g), count, string);
+	PREINIT:
+		owl_editwin *e;
+	CODE:
+		e = owl_global_get_typwin(&g);
+		if (e) {
+			RETVAL = owl_editwin_replace(e, count, string);
+		} else {
+			RETVAL = 0;
+		}
 	OUTPUT:
 		RETVAL
@@ -550,6 +566,13 @@
 point_move(delta)
 	int delta;
-	CODE:
-		RETVAL = owl_editwin_point_move(owl_global_get_typwin(&g), delta);
+	PREINIT:
+		owl_editwin *e;
+	CODE:
+		e = owl_global_get_typwin(&g);
+		if (e) {
+			RETVAL = owl_editwin_point_move(e, delta);
+		} else {
+			RETVAL = 0;
+		}
 	OUTPUT:
 		RETVAL
@@ -558,6 +581,13 @@
 replace_region(string)
 	const char *string;
-	CODE:
-		RETVAL = owl_editwin_replace_region(owl_global_get_typwin(&g), string);
+	PREINIT:
+		owl_editwin *e;
+	CODE:
+		e = owl_global_get_typwin(&g);
+		if (e) {
+			RETVAL = owl_editwin_replace_region(e, string);
+		} else {
+			RETVAL = 0;
+		}
 	OUTPUT:
 		RETVAL
@@ -567,6 +597,12 @@
 	PREINIT:
 		char *region;
-	CODE:
-		region = owl_editwin_get_region(owl_global_get_typwin(&g));
+		owl_editwin *e;
+	CODE:
+		e = owl_global_get_typwin(&g);
+		if (e) {
+			region = owl_editwin_get_region(owl_global_get_typwin(&g));
+		} else {
+			region = NULL;
+		}
 		RETVAL = region;
 	OUTPUT:
@@ -581,7 +617,12 @@
 	PREINIT:
 		int count;
+		owl_editwin *e;
 		owl_editwin_excursion *x;
 	CODE:
 	{
+		e = owl_global_get_typwin(&g);
+		if(!e)
+			croak("The edit window is not currently active!");
+
 		x = owl_editwin_begin_excursion(owl_global_get_typwin(&g));
 		PUSHMARK(SP);
@@ -605,6 +646,13 @@
 int
 current_column()
-	CODE:
-		RETVAL = owl_editwin_current_column(owl_global_get_typwin(&g));
+	PREINIT:
+		owl_editwin *e;
+	CODE:
+		e = owl_global_get_typwin(&g);
+		if (e) {
+			RETVAL = owl_editwin_current_column(e);
+		} else {
+			RETVAL = 0;
+		}
 	OUTPUT:
 		RETVAL
@@ -612,6 +660,13 @@
 int
 point()
-	CODE:
-		RETVAL = owl_editwin_get_point(owl_global_get_typwin(&g));
+	PREINIT:
+		owl_editwin *e;
+	CODE:
+		e = owl_global_get_typwin(&g);
+		if (e) {
+			RETVAL = owl_editwin_get_point(e);
+		} else {
+			RETVAL = 0;
+		}
 	OUTPUT:
 		RETVAL
@@ -619,6 +674,13 @@
 int
 mark()
-	CODE:
-		RETVAL = owl_editwin_get_mark(owl_global_get_typwin(&g));
-	OUTPUT:
-		RETVAL
+	PREINIT:
+		owl_editwin *e;
+	CODE:
+		e = owl_global_get_typwin(&g);
+		if (e) {
+			RETVAL = owl_editwin_get_mark(e);
+		} else {
+			RETVAL = 0;
+		}
+	OUTPUT:
+		RETVAL
Index: popexec.c
===================================================================
--- popexec.c	(revision 4cca5919980fa5979e05cba7e1e75e1eaeb78859)
+++ popexec.c	(revision 125fd21c2d564a4a7d0a027552b7156a4d694243)
@@ -96,4 +96,5 @@
   if (!pe->pid && !pe->winactive) {
     owl_select_remove_io_dispatch(d);
+    pe->dispatch = NULL;
     return;
   }
@@ -114,4 +115,5 @@
     }
     owl_select_remove_io_dispatch(d);
+    pe->dispatch = NULL;
     return;
   }
@@ -157,6 +159,7 @@
 
   pe->winactive = 0;
-  if (pe->dispatch->fd > 0) {
+  if (pe->dispatch) {
     owl_select_remove_io_dispatch(pe->dispatch);
+    pe->dispatch = NULL;
   }
   if (pe->pid) {
Index: scripts/add-changelog
===================================================================
--- scripts/add-changelog	(revision 68ab07cc360a4123bbb496e5190f0a734ae9867e)
+++ scripts/add-changelog	(revision 51dbfb51348a8cc7abe2836c37126ba000e82e96)
@@ -1,4 +1,9 @@
 #!/bin/sh
 version=$1; shift
+
+if [ -z "$version" ] || [ "$#" = 0 ];  then
+    echo "Usage: $0 VERSION REV-LIST-ARGS" > &2
+    exit 1;
+fi
 
 (
Index: scripts/do-release
===================================================================
--- scripts/do-release	(revision d771d1b92b8b63462925ff8f0ab7fc081ffd81b8)
+++ scripts/do-release	(revision 51dbfb51348a8cc7abe2836c37126ba000e82e96)
@@ -4,4 +4,16 @@
     echo "$@" >&2
     exit 1
+}
+
+usage() {
+    cat >&2 <<EOF
+Usage: %0 [options]
+Generate a barnowl release tarball.
+
+OPTIONS:
+  -f            Don't require a changelog entry for the new release.
+  --no-tag      Don't create and sign a git tag for the new release
+  --git         Do a beta release for the current git revision.
+EOF
 }
 
@@ -15,4 +27,5 @@
         --no-tag) no_tag=1 ;;
         --git) git=1 ;;
+        -h|--help) usage ;;
     esac
 done
Index: scripts/locker-build
===================================================================
--- scripts/locker-build	(revision 2b6622a602f1f8b0922ad96976b2abc20232db9c)
+++ scripts/locker-build	(revision 51dbfb51348a8cc7abe2836c37126ba000e82e96)
@@ -3,11 +3,4 @@
 #########################################################
 # Build script to build BarnOwl for the locker.
-
-# Usage: locker-build [-n] [-o OUTPUT-TGZ] SOURCE-TARBALL
-# -n is a dry-run, and drops to a shell in the build directory
-# -o does the install into a temporary directory and tars it into the
-#    specified tarball instead.
-# SOURCE-TARBALL is a source tarball, created by do-release
-
 die() {
     echo "$@" 2>&1;
@@ -22,5 +15,12 @@
 
 usage () {
-    echo "Usage: $0 [-n] [-o OUTPUT-TGZ] SOURCE-TARBALL"
+    cat >&2 <<EOF
+Usage: $0 [-n] [-o OUTPUT-TGZ] SOURCE-TARBALL
+ -n is a dry-run, and drops to a shell in the build directory
+ -o does the install into a temporary directory and tars it into the
+    specified tarball instead.
+
+SOURCE-TARBALL is a source tarball, created by do-release.
+EOF
     exit 2;
 }
Index: variable.c
===================================================================
--- variable.c	(revision 66a8cd6f82c4a4dc47992bd57b12563500abb001)
+++ variable.c	(revision 66e409c96288dd36cee1498652dc8d6dce9b4c45)
@@ -358,4 +358,11 @@
 	       "normal,top,neartop,center,paged,pagedcenter" ),
 
+  OWLVAR_BOOL( "narrow-related" /* %OwlVarStub:narrow_related */, 1,
+               "Make smartnarrow use broader filters",
+               "Causes smartfiler to narrow to messages \"related\" to \n"
+               "the current message, as well as ones to the same place.\n\n"
+               "for Zephyr, this controls whether to narrow to e.g. class-help or\n"
+               "class-help.d alone, or to related-class-help, which includes\n"
+               "help, unhelp, help.d, etc.\n\nDefault is true (include unclasses, etc.).\n" ),
 
   OWLVAR_BOOL( "_followlast" /* %OwlVarStub */, 0,
Index: zephyr.c
===================================================================
--- zephyr.c	(revision c230bc1b418cbace9d748df62640c26a59aa1622)
+++ zephyr.c	(revision f25812b2029878bb898c301ce9e06adee69692cf)
@@ -1299,4 +1299,69 @@
 }
 
+#ifdef HAVE_LIBZEPHYR
+void owl_zephyr_process_pseudologin(ZNotice_t *n)
+{
+  owl_message *m;
+  owl_zbuddylist *zbl;
+  GList **zaldlist;
+  GList *zaldptr;
+  ZAsyncLocateData_t *zald = NULL;
+  ZLocations_t location;
+  int numlocs, ret, notify;
+
+  /* Find a ZALD to match this notice. */
+  zaldlist = owl_global_get_zaldlist(&g);
+  zaldptr = g_list_first(*zaldlist);
+  while (zaldptr) {
+    if (ZCompareALDPred(n, zaldptr->data)) {
+      zald = zaldptr->data;
+      *zaldlist = g_list_remove(*zaldlist, zaldptr->data);
+      break;
+    }
+    zaldptr = g_list_next(zaldptr);
+  }
+  if (zald) {
+    /* Deal with notice. */
+    notify = owl_global_get_pseudologin_notify(&g);
+    zbl = owl_global_get_zephyr_buddylist(&g);
+    ret = ZParseLocations(n, zald, &numlocs, NULL);
+    if (ret == ZERR_NONE) {
+      if (numlocs > 0 && !owl_zbuddylist_contains_user(zbl, zald->user)) {
+        if (notify) {
+          numlocs = 1;
+          ret = ZGetLocations(&location, &numlocs);
+          if (ret == ZERR_NONE) {
+            /* Send a PSEUDO LOGIN! */
+            m = owl_malloc(sizeof(owl_message));
+            owl_message_create_pseudo_zlogin(m, 0, zald->user,
+                                             location.host,
+                                             location.time,
+                                             location.tty);
+            owl_global_messagequeue_addmsg(&g, m);
+          }
+          owl_zbuddylist_adduser(zbl, zald->user);
+          owl_function_debugmsg("owl_function_zephyr_buddy_check: login for %s ", zald->user);
+        }
+      } else if (numlocs == 0 && owl_zbuddylist_contains_user(zbl, zald->user)) {
+        /* Send a PSEUDO LOGOUT! */
+        if (notify) {
+          m = owl_malloc(sizeof(owl_message));
+          owl_message_create_pseudo_zlogin(m, 1, zald->user, "", "", "");
+          owl_global_messagequeue_addmsg(&g, m);
+        }
+        owl_zbuddylist_deluser(zbl, zald->user);
+        owl_function_debugmsg("owl_function_zephyr_buddy_check: logout for %s ", zald->user);
+      }
+    }
+    ZFreeALD(zald);
+    owl_free(zald);
+  }
+}
+#else
+void owl_zephyr_process_pseudologin(void *n)
+{
+}
+#endif
+
 /*
  * Process zephyrgrams from libzephyr's queue. To prevent starvation,
@@ -1333,4 +1398,11 @@
       }
 
+      /* if it is a LOCATE message, it's for pseudologins. */
+      if (strcmp(notice.z_opcode, LOCATE_LOCATE) == 0) {
+        owl_zephyr_process_pseudologin(&notice);
+        ZFreeNotice(&notice);
+        continue;
+      }
+
       /* create the new message */
       m=owl_malloc(sizeof(owl_message));
