Index: ChangeLog
===================================================================
--- ChangeLog (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ ChangeLog (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,371 @@
+1.2.0-pre-erikdevel-17
+	Integrated change to fix problem with popup blinking on new zephyrs.
+	C-l and resizes will now refresh an open viewwin (eg, help).
+	Updated doc/code.txt to include info about filters, commands,
+	        contexts, and keybindings.
+	Ooops.  "delete view" had accidentally been misbound to M-d.
+	        Switched the binding back to M-D.
+	Exec commands cleaned up to not have buffer-size limitations
+	        and to not mess up spaces.  exec also returns a string
+		of the output now.
+	
+1.2.0-pre-erikdevel-16
+	Integrated changes from 1.1.3, and added docs for "zlocate -d"
+		and new show commands.
+	Show with arguments produces help on show.
+	Fix a bug in readconfig caught by efence (where we'd try to read before 
+	        the beginning of a string if it was empty).
+	
+1.1.2-erikdevel-15
+        The perl command doesn't do makemsg directly, but instead
+	     returns the string and it will get printed if it 
+	     was run interactively.
+
+1.1.2-erikdevel-14
+	*** NOTE: I probably missed some things here...
+	New framework for command handling.
+	New framework for keymap handling.
+	Added commands for everything that is bound 
+	     to a key (do 'show commands' to get the full list).
+	Added 'multi' and '(' commands to allow multiple commands
+	     to be specified on a line.		     
+	Added user keybindings with bindkey command.
+	Added command aliases (eg, "alias foo bar").
+	Added undelete command that parallels the delete command.
+	Added additional options to delete command.
+	The reply command now takes arguments.
+	Added 'edit:insert-text' command.
+	Added 'show zpunts' to show active punt filters.
+	Added 'show variable <name>' and 'show variables'.
+	Added 'show command <name>' and 'show commands'.
+	Added 'show keymap <name>' and 'show keymaps'.
+	Added 'M-u' to undelete all messages in current view.
+	Fixed dotsend so that the zephyr will still send if there
+	     is whitespace after the dot but not on the same line.
+	     This should resolve an issue where dotsend wouldn't work
+	     if you'd gone up and edited a zephyr.
+	
+1.1.3
+	'show subs' and 'show subscriptions' are now the same as 'getsubs'
+	zlocate now takes an optional -d argument
+	'show terminal' / 'show term'
+	'>' / last doesn't set the last message at the top of the screen now
+	implemented _followlast as an unsupported feature
+	include 'default' in the 'show colors' list
+	added help for 'zpunt' and 'zunpunt'
+	changed the bug address in the startup message
+	can now do 'show status'
+	can now do 'show version'
+	'status' / 'show status' includes the owl version number now
+	'show terminal' includes whether the terminal can change colors
+	fixed off by one bugs in paging / scrolling viewwin
+	don't downcase the sender when getting the log name for personals
+	support @owl::fields as well as @fields
+	downcase class/inst filter names in auto filters
+
+1.1.2
+	Fixed memory mishandling bug
+	Fixed bug in redfining the filter attached to the current view
+	M-n will narrow to message, instance on non-personal, class
+	     MESSAGE messages
+	M-N behavies like M-n except that on class messages it narrows
+	    to class and instance
+	line wrap earlier, to account for tabbing
+	fixed typo in help
+	'status' command now displays info on terminal color support
+	zephyr @ formatting is now case independant
+	added support for color terminals
+	zephyr @color(foo) now works
+	'D' for deleted messages is now not bold, unless it's the current
+  	  message
+	F1 displays the help screen
+	added filter colors
+	added the 'colorview' command
+	added the 'show colors' command
+	users who don't have a .zephyr.subs get a simpler format for
+  	  incoming messages
+        If colors are available 'show filters' will show a filter in the
+	  color associated with it.
+	Added the zpunt and zunpunt commands
+	Lines in the subs file starting with '-' are zpunted
+	Include login/logout messages in auto user filters
+	'V' changes to the home view ('all' by default)
+	
+1.1.1
+	Fixed perl, aperl, and pperl commands to deal with quoting 
+	      and spaces in a saner manner.
+	Removed all owl_get_* methods for booleans and switched
+	      cases where they were used to owl_is_* 
+	Changes to owlconf.erik to use some new features.
+	Increased the size of the help buffer (as it 
+	      was overflowing and truncating the help message).
+	Variables prefixed with a _ are not shown in help
+	      or by printallvars (and prefixed Not Yet Implemented 
+	      variables with this).
+	Fix typo in help
+	include stdio.h in functions.c
+	remove stale "q to quit" from bottom of info message
+	fix downward scrolling more than a page
+	use authentication for zlocate, by default
+	fixed buffer over run in info command on long messages
+	call 'perl <file>' from Makefile to avoid hardcoding perl paths
+	in Makefile don't build owl_prototypes.h unless necessary
+	store the time for admin messages
+	display admin message time in 'info' command
+	fixed an editwin M-> last character bug
+		
+1.1
+	reply is a normal function now
+	'R' does reply to sender
+	'T' tells you how many messages were marked for deletion
+	local realm removed from login / logout messages
+	added command history
+	better runtime / starttime reporting in 'status' command
+	leave the pointer near the current message after expunge
+	C-l recenters editwin
+	implemented zlocate
+	@italic works the same as @i
+	on reply only quote class / instance when necessary
+	C-r allows you to edit the reply line
+	don't use unecessary options in reply line
+	display 'info' errors in msgwin, not popup
+	impelemnted aexec, pexec commands
+	the zsig now goes through ztext formatting
+	messages have id numbers now
+	'info' prints the msgid
+	added the 'filter' command
+	added the 'view' command
+	added the 'show filter' command
+	added the 'viewclass' (and 'vc') commands
+	added the 'viewuser' (and 'vu') commands
+	M-n will filter to the current class or user
+	'v' starts a view command
+	M-D will delete all messages in current view
+	added the 'delete' (and 'del') command
+	load-subs with no argument loads the default subs file
+	'<truncated>' is now when the *current* message is truncated
+	the reply-lockout filter (with default) specifices messages that
+           cannot be replied to.
+	in the configfile owl::receive_msg is run whenever a message is
+	  received
+	added the beep command
+	added the contributors file
+	declare ZGetSubscriptions and ZGetLocations since the includes
+          don't seem to
+	fixed bug in displaying last line in popwin if no final '\n'
+	'T' uses the 'trash' filter now
+	zaway_msg, zaway_msg_default and zaway are all user variables now.
+	zsig variable overrides zsigproc
+	If there's no appendtosepbar don't interfear with the sepbar
+	Changed: owl_message_get_numlines will return 0 of m is NULL
+	Added login messages to messages marked by owl_function_delete_automsgs
+	Added owl_function_delete_by_id(id) which acts independent of view
+	Added "-id <id>" option to delete command
+	Fixed an arg checking bug in delete command
+	Added owl::id to perl namespace with message id
+	Fixed a memory corruption bug in readconfig.c (where right 
+	      after the strdup to "out", we'd strcat a \n onto the end.
+	      This would be triggered whenever owl::format_msg returned
+	      a string not ending in a newline
+	Added 'X' keybinding which expunges and then switches to 
+	      a view defined by the variable "view_home" which defaults 
+	      to "all"
+	Consolidated readconfig.c somewhat to remove duplication.
+	      owl_config_execute now returns a string.
+	Added an example config file that does vt-style formatting.
+	      (examples/owlconf.vtformat)
+	Added the 'perl', 'aperl', and 'pperl' commands which will
+	      evaluate perl expressions.
+        Fixed bug where pclose zsigproc would cause zombies
+	Can set zsigproc or zsig to "" to disable
+	Added support for multiple browsers (galeon and none were added).
+	      Configure with the "webbrowser" variable.
+	Changing typewinsize height triggers resize event.
+	Added zsig variable which will be used if no zsigproc and non-empty.
+	Added "make test" rule to Makefile which will run regression tests,
+	      and added regression testing framework to tester
+	Fixed codelist.pl to ignore static declarations.
+	Added dict.c which contains string->ptr dictionary routines
+	      and the owl_dict type.
+	      These include regression tests.
+	Overhaul/rewrite of variable handling.  Variables are now managed
+	      in an owl_vardict (in g.vars) which contains a dictionary
+	      of owl_variable's.  Each owl_variable has dispatch functions
+	      for validating values, setting it and getting it,
+	      and for setting it to and from string values.
+	      The variable.c file contains the list of variables.
+	      Stubs for the owl_global_<varname>_get functions and friends
+	      are generated from variable.c by stubgen.pl.
+	      The help.c messages for variables now calls into variable.c
+	      so all information about most variables is in one place.   
+	Cleaned out code from global.c and command.c that was made obselete
+	      by variable overhaul.
+	The set command now takes a -q option to not log a message.
+	Fixed a bug where set and print with no arguments would
+	      print "Undefined variable" in addition 
+	      to running owl_function_printallvars.
+	debug is now a variable that can be turned on and off.
+	Fixed mail,inbox message parsing in examples/owlconf.erik
+	Made zaway_msg and zaway_msg_default into variables 
+	Changed owl_function_makemsg and owl_function_debugmsg 
+	       to use varargs (ie, so they can now take a format
+	       string with args).
+	Don't allow " and \ characters in URLs with the "w" command.
+	Removed lots of build warnings.
+	Popwins are wider by default so help messages fit better.
+	Added an atokenize_free function.
+        Fixes to work with an older version of libzephyr.
+	Added dependencies on header files to Makefile.in
+	Added pageup and pagedown key bindings to message list
+	Added pageup and pagedown to viewwin
+	Added configfile section to doc/intro.txt (from example config file)
+	Added appendtosepbar variable which may contain text which will
+	      be appended to the sepbar.  This allows the configfile
+	      to put information about pings and logins into
+	      the sepbar.  (It may be worth also providing a variable
+	      which enables this by default, but for now this allows
+	      for experimenting with what works well.)
+	Added doc/code.txt which gives a brief overview of the code.
+	Added tags makefile rule and added TAGS to distclean rule.
+	
+1.0.1
+	fix frees in loadsubs and loadloginsubs
+	don't return in owl_free
+	
+1.0
+	'print' and 'set' with no arguments prints all variables
+	Added the 'unsubscribe' and 'unsub' command
+	Renamed the 'unsub' command to 'unsuball'
+	Added the 'getsubs' command which is like zctl ret
+	Fixed bug in logging messages sent to more than one recipient
+	Support '-C', '-O', and '-n' options to zwrite
+	Fixed bug in owl_editwin_delete_char when there are no later chars
+	  after the cursor
+	Make "more" and "truncated" work in the status bar
+	enable printing of zsigproc and loginsubs variables
+	only allow message scrolling if the message is actually off the
+	  screen
+	'T' will mark all automated message for deletion
+	'P' will go to the next personal message
+	'M-P' will go to the previous personal message
+	replying to a login message goes to the user now
+	added a status command
+	added the intro doc to the release
+	fixed off by one bug in viewwin
+	added complete online help
+	pass $owl::realm in configfile
+	fixed editwin wordwrapping on the last line
+	fixed editwin problem with key_right past the last char
+	print an error and quit if the configfile can't be parsed
+	got rid of owl_mainwin_calculate_topmsg
+	fixed off by one error in calculating topmsg upwards
+	you can now reply to an admin message
+	don't display an error about keypress on window resize
+	
+0.11
+	fixed bug in viewing messages longer than the screen
+	indicate in the sepbar if there is a non zero vert offset
+	send on '.' on a line by itself
+	added disable-ctrl-d variable
+	fixed bug where C-k did not delete the last \n in the buffer
+	make non-character meta keys work
+	use ZSendNotice instead of ZSendList
+	implemented <, >, M-< and M-> in viewwin
+	removed the spaces at the bottom of viewwin
+	added 'about' command
+	fixed bug using 'M' with no current message
+	changed message object to use char *'s to save on memory
+	change malloc, realloc, strdup and free to use owl hooks so that
+	   debugging can be added
+		
+0.10.1
+	fixed a trailing space bug in the parser
+	impelemented the "burning ears" feature
+	have admin messages do ztext parsing
+	fixed bug in reporting which M- key was pressed
+	C-g will now cancel commands like C-c
+	
+0.10
+	implemented owl_function_full_redisplay().
+	C-l uses owl_function_full_redisplay().
+	when a popwin exists to a full redisplay.  (fixes bug)
+	improved the owl_editwin_process_char logic
+	removed all unnecessary wrefresh's and replaced with wnoutrefesh
+	owl_editwin_redisplay now takes an argument to optionally doupdate()
+	improved the cut-and-paste speed by not doing a usleep the first
+	  time through the loop after getting a keypress.
+	nuked typwin.c and associated stuff.  It's useless now.
+	added viewwin code for paging windows
+	curly braces work for zephyr formatting
+	@i in zephyr formatting will be displayed as underlined text
+	turned off idlok
+	implemented viewwin
+	implemented viewwi in popwin for pageable popwins
+	help, info now use pageable popwins
+	bound 'M' to bring the current message up in a popwin
+	return, space bar, 'b' and backspace now scroll within a message
+	turned off resize message
+	C-v and M-v page the main window
+	implemented owl_message_is_mail
+	some build cleanup
+
+	
+0.9
+	added owl_message_is_personal and have things use it
+	added owl_message_is_private
+	fixed 'print personalbell' and have 'set personalbell'
+	   print a message
+	bold only on message_is_personal
+	display the realm if not local
+	implemented M-f, M-b, M-d, M-<, M-> in editwin
+	implemnted word wrapping in editwin
+	implemented M-q (paragraph-fill) in editwin
+	fixed bug that caused owl to segfault logging a 'weird' class
+	M-x is a keysym for ':'
+	added smart bolding and userclue
+	fixed a bug causing pings to beep even if rxping is off
+	
+0.8.1
+	fixed bug in logging code
+	
+0.8
+	implemented personal logging
+	implemented class logging
+	implemented resize of typewin
+	fixed the backspace problem
+	-v command line option prints the version number
+	
+0.7
+	load-subs will report error opening file
+	skip comment lines in loadsubs and loadloginsubs
+	changed internal references to rxping and txping
+	fix replying to a blank instance
+	added subscribe command
+	subscribe to login messages from .anyone by default
+	'loginsubs' variarble controlls automated login messages
+	redisplay the editwin after a resize
+	leave the cursor in the editwin if active
+	fix problems in the build system
+	added displayoutgoing variable
+	temporarily removed error printing for zlog in / out
+	
+0.61
+	fixed bug in "message sent to <foo>" for zwrite
+	
+0.6
+	help updated
+	zaway key set to caps A
+	support zephyring other realms
+	rxping variable for receiving pings
+	txping variable for sending pings
+	function in place to resize typwin
+	C-l to refresh
+	personal bell variable
+	beta message now an admin message
+	
+0.5
+	Added the debug command and flag
+	Fixed bug in printing fields in info command
+	Added owl_fmtext_append_ztext and use it
+	Better formating for pings and login zephyrs
+	make tester depends on proto
Index: Makefile.in
===================================================================
--- Makefile.in (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ Makefile.in (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,45 @@
+CC=@CC@
+LIBS=@LIBS@
+#CFLAGS=@CFLAGS@ @DEFS@
+CFLAGS=@CFLAGS@
+LDFLAGS=@LDFLAGS@
+
+OBJS=list.o message.o mainwin.o popwin.o zephyr.o messagelist.o commands.o \
+     global.o text.o fmtext.o editwin.o util.o logging.o readconfig.o keys.o \
+     functions.o zwrite.o viewwin.o help.o filter.o regex.o history.o view.o \
+     dict.o variable.o varstubs.o filterelement.o \
+     keypress.o keymap.o keybinding.o cmd.o context.o
+
+AUTOGEN=owl_prototypes.h varstubs.c
+
+owl: $(AUTOGEN) $(OBJS) owl.o
+	$(CC) -o owl owl.o $(OBJS) $(LDFLAGS) $(LIBS)
+
+tester: $(AUTOGEN) $(OBJS) tester.o
+	$(CC) -o tester tester.o $(OBJS) $(LDFLAGS) $(LIBS)
+
+test: tester
+	./tester reg
+
+clean:
+	$(RM) *~ *.o owl tester core $(AUTOGEN) owl_prototypes.h.new
+
+distclean: clean
+	$(RM) config.cache config.log config.status Makefile config.h TAGS
+
+proto: owl_prototypes.h
+
+
+varstubs.c: variable.c stubgen.pl
+	perl stubgen.pl > varstubs.c
+
+# Only move owl_prototypes.h into place if the new one is different
+owl_prototypes.h: codelist.pl varstubs.c $(OBJS:.o=.c)
+	perl codelist.pl > owl_prototypes.h.new
+	@cmp -s owl_prototypes.h.new owl_prototypes.h || echo 'Interfaces changed!'
+	@cmp -s owl_prototypes.h.new owl_prototypes.h || mv -f owl_prototypes.h.new owl_prototypes.h
+
+tags:
+	etags *.[ch]
+
+*.o:: owl.h config.h owl_prototypes.h
Index: NEWS
===================================================================
--- NEWS (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ NEWS (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,278 @@
+What's new in owl version 1.1
+-----------------------------
+* Generic filtering now exists
+
+* The filter command creates a filter
+
+* The view command narrows the view to a filter
+
+* The viewclass / vc command automatically narrows the view
+  to a particular class
+
+* The viewuser / vu command automatically narrows the view
+  to a particular user
+
+* M-n automatically narrows the view to whatever is appropriate for
+  the current message
+
+* v will start a view command
+
+* X will expunge and change the view to the view_home variable value
+
+* R will reply to sender
+
+* C-r and C-R will let the user edit the reply line
+
+* T now uses the 'trash' filter
+
+* T tells you how many messages were marked for deletion
+
+* The local realm has been removed from login/logout messages
+
+* Command history works
+
+* Prettier status command
+
+* Leave the pointer near the current message after an expunge
+
+* C-l recenters the typing window
+
+* zlocate is implemented
+
+* @italic works like @i
+
+* Unecessary quoting and options in reply lines have been removed
+
+* Errors in the 'info' command don't show up in a popup now
+
+* Implemented the aexec, pexec commands
+
+* The zsig now gets zephyr formatting
+
+* Messages now have message id's. They are displayed in 'info' and are
+  passed to the config as $owl::id.
+
+* The 'show' command is implemented to display info about filters.
+
+* M-D marks for deletion all messages in the current view
+
+* added the delete command
+
+* load-subs with no argument loads the default subscription file
+
+* "Truncated" is now displayed when the *current* message is truncated
+  not when the last displayed message is truncated.
+
+* The reply-lockout filter locks out replies to some classes
+
+* In the configfile owl::receive_msg is run when a message is
+  received.
+
+* Added the beep command
+
+* Added the contributors file
+
+* zaway, zaway_msg and zaway_msg_default are all user variables now
+
+* There is a zsig variable that overrides other zsig mechanisims
+
+* The appendtosepbar variable exists to allow the config to display
+  things in the sepbar
+
+* login messages are included as 'trash' messages by default now
+
+* Fixed a memory corruption bug in readconfig.c
+
+* There are example configs in the distribution now
+
+* Implemented the perl, aperl and pperl commands
+
+* FIxed bug with zsigproc zombies
+
+* Can disable zsigproc after setting it now
+
+* There is support for multiple browsers with the 'webbrowser'
+  variable.
+
+* Set now takes a -q option to not echo
+
+* debug is now a variable that can be turned on and off
+
+* Don't allow " and \ characters in URLs passed to a webbrowser
+
+* Build system cleaned up some
+
+* Pop up windows are wider by default
+
+* Fix to work with older versions of libzephyr
+
+* PageUp and PageDown work in the main window and in popups
+
+* Added doc/code.txt describing the code layout
+
+
+What's new in owl version 1.0
+-----------------------------
+* 'print' and 'set' with no arguments prints all variables
+
+* The 'unsub' command renamed to 'unsuball'
+
+* 'unsubscribe' (and 'unsub') command
+
+* 'getsubs' command to print subscriptions
+
+* Bug fixed in logging messages to more than one recipient
+
+* Support for zwrite options -C, -O and -n
+
+* Fixed owl_editwin_delete_char bug when there are no chars after the
+  cursor
+
+* Display 'more' and 'truncated' in the status bar
+
+* Enable printing of the 'zsigproc' and 'loginsubs' variables
+
+* Only allow message scrolling if the message is longer than the
+  screen
+
+* 'P' (capitolized) will advance the pointer to the next personal
+  message.  'M-P' will move the pointer to the previous personal
+  message.
+
+* 'T' will mark certain "trash" messages for deletion.  This includes:
+  mail notifications, pings and admin messages.
+
+* Replying to a login/logout message sends as a personal message to
+  the user
+
+* Replying to an admin message that's an outgoing message copy sends
+  as a personal message to the user
+
+* 'status' command
+
+* intro document added to the release
+
+* The local realm is trimmed everywhere, including in logging
+
+* Fixed off by one bug in scrolling popups
+
+* Complete on line help added
+
+* Pass the realm to the config file in $owl::realm
+
+* Word wrap and scroll at the bottom right corner of the editing
+  window
+
+* Fixed a bug in editwing window key_right past the last character
+
+* Print an error and don't start owl if the configfile can't be
+  parsed
+
+* Fix bug where the only message displayed might not be the current
+  message
+
+* Don't display the M-^Z key error on resize
+
+What's new in owl version 0.11
+------------------------------
+* Bug fixed in viewing messages longer than a screen
+
+* Indicate in the sepbar if there is a vertical offset present
+
+* '.' on a line by itself will send a zephyr
+
+* disable-ctrl-d variable will disable sending on C-d
+
+* bug fixed that kept C-k from deleting a final newline
+
+* make non-character meta keys work
+
+* fixed phatom zephyr problem
+
+* <, >, M-< and M-> work in popups now
+
+* removed extra spaces at bottom of a popup
+
+* Added "about" command
+
+* Fixed bug using 'M' with no current message
+
+* Log personal messages to 'all' as well as the personal file
+
+* Messages take up less memory
+
+What's new in owl version 0.10
+------------------------------
+* Scrollable / pageable popup windows.  They act like the 'less' or
+  'more' programs.
+
+* Scrolling within a message, to facilitate reading messages longer
+  than the screen size.
+
+* The command parser deals with quoting.  i.e. zwrite -c "foo class"
+  now works.
+
+* The slow cut and paste bug is fixed.
+
+* C-v and M-v will page down and up in the main window, respectively.
+
+* A bug in redrawing the popup windows is fixed.
+
+* An error is displayed when non-functional keys are pressed.
+
+* @i() formatting is displayed as underlined in curses.
+
+* Curly braces work for zephyr formatting.
+
+* The popup window size is relatively larger on small terminals
+
+* A bug in having meta keys work on eight bit input xterms is fixed.
+
+* The cursor location is more consistent.
+
+
+What's new in owl version 0.9
+-----------------------------
+* Many emacs meta keys work
+
+  M-q, M-f, M-b, M-d, M-< and M-> now work in the typing window
+  M-< and M-> also work in the main window
+  M-x in the main window now enters command mode, just like ':'
+
+* The typing window will automatically word-wrap when hitting the
+  end of the line
+
+* SmaatBolding (TM)
+
+  Users without a .zephyr.subs won't have personal messages
+  displayed in all bold
+
+* Realm displayed for interrealm zephyrs
+
+* 'personalbell' variable printing fixed
+
+* bug with ringing terminal bell on pings fixed
+
+
+What's new in owl version 0.8
+-----------------------------
+* personal logging
+  
+  "set logging on" will turn on personal logging.  By default messages
+  will go to ~/zlog/people.  This path can be changed by setting the
+  "logpath" variable.
+
+* class logging
+
+  "set classlogging on" will turn on class logging.  By default
+  messages will go to ~/zlog/class.  This path can be changed by
+  setting the "classlogpath" variable.
+
+* variable for resizeing the typing window
+
+  "set typewinsize <n>" will change the number of lines in the typing
+  window to n.
+
+* fixed bugs with backspace
+
+* -v command line option prints the version number
Index: cmd.c
===================================================================
--- cmd.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ cmd.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,257 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "owl.h"
+
+extern owl_cmd commands_to_init[];
+
+/**************************************************************************/
+/***************************** COMMAND DICT *******************************/
+/**************************************************************************/
+
+int owl_cmddict_setup(owl_cmddict *cd) {
+  if (0 != owl_cmddict_init(cd)) return(-1);
+  if (0 != owl_cmddict_add_from_list(cd, commands_to_init)) return(-1);
+  return(0);
+}
+
+int owl_cmddict_init(owl_cmddict *cd) {
+  if (owl_dict_create(cd)) return(-1);
+  return(0);
+}
+
+/* for bulk initialization at startup */
+int owl_cmddict_add_from_list(owl_cmddict *cd, owl_cmd *cmds) {
+  owl_cmd *cur, *cmd;
+  for (cur = cmds; cur->name != NULL; cur++) {  
+    cmd = owl_malloc(sizeof(owl_cmd));
+    owl_cmd_create_from_template(cmd, cur);
+    owl_dict_insert_element(cd, cmd->name, (void*)cmd, NULL);
+  }
+  return 0;
+}
+
+/* free the list with owl_cmddict_namelist_free */
+void owl_cmddict_get_names(owl_cmddict *d, owl_list *l) {
+  owl_dict_get_keys(d, l);
+}
+
+owl_cmd *owl_cmddict_find(owl_cmddict *d, char *name) {
+  return (owl_cmd*)owl_dict_find_element(d, name);
+}
+
+void owl_cmddict_namelist_free(owl_list *l) {
+  owl_list_free_all(l, owl_free);
+}
+
+/* creates a new command alias */
+int owl_cmddict_add_alias(owl_cmddict *cd, char *alias_from, char *alias_to) {
+  owl_cmd *cmd;
+  cmd = owl_malloc(sizeof(owl_cmd));
+  owl_cmd_create_alias(cmd, alias_from, alias_to);
+  owl_dict_insert_element(cd, cmd->name, (void*)cmd, (void(*)(void*))owl_cmd_free);    
+  return(0);
+}
+
+char *owl_cmddict_execute(owl_cmddict *cd, owl_context *ctx, char *cmdbuff) {
+  char **argv;
+  int argc;
+  char *tmpbuff;
+  char *retval = NULL;
+  owl_cmd *cmd;
+
+  tmpbuff=strdup(cmdbuff);
+  argv=owl_parseline(tmpbuff, &argc);
+  if (argc < 0) {
+    owl_free(tmpbuff);
+    sepbar(NULL);
+    owl_function_makemsg("Unbalanced quotes");
+    return NULL;
+  } 
+  
+  if (argc < 1) return(NULL);
+
+  if (!strcmp(argv[0], "")) {
+  } else if (NULL != (cmd = (owl_cmd*)owl_dict_find_element(cd, argv[0]))) {
+    retval = owl_cmd_execute(cmd, cd, ctx, argc, argv, cmdbuff);
+  } else {
+    owl_function_makemsg("Unknown command '%s'.", cmdbuff);
+  }
+  owl_parsefree(argv, argc);
+  owl_free(tmpbuff);
+  sepbar(NULL);
+  return retval;
+}
+
+/*********************************************************************/
+/***************************** COMMAND *******************************/
+/*********************************************************************/
+
+/* sets up a new command based on a template, copying strings */
+int owl_cmd_create_from_template(owl_cmd *cmd, owl_cmd *templ) {
+  *cmd = *templ;
+  if (!templ->name) return(-1);
+  cmd->name = owl_strdup(templ->name);
+  if (cmd->summary)     cmd->summary     = owl_strdup(templ->summary);
+  if (cmd->usage)       cmd->usage       = owl_strdup(templ->usage);
+  if (cmd->description) cmd->description = owl_strdup(templ->description);
+  if (cmd->cmd_aliased_to) cmd->cmd_aliased_to = owl_strdup(templ->cmd_aliased_to);
+  return(0);
+}
+
+int owl_cmd_create_alias(owl_cmd *cmd, char *name, char *aliased_to) {
+  memset(cmd, 0, sizeof(owl_cmd));
+  cmd->name = owl_strdup(name);
+  cmd->cmd_aliased_to = owl_strdup(aliased_to);
+  cmd->summary = owl_malloc(strlen(name)+strlen(OWL_CMD_ALIAS_SUMMARY_PREFIX)+2);
+  strcpy(cmd->summary, OWL_CMD_ALIAS_SUMMARY_PREFIX);
+  strcat(cmd->summary, name);
+  return(0);
+}
+
+void owl_cmd_free(owl_cmd *cmd) {
+  if (cmd->name) owl_free(cmd->name);
+  if (cmd->summary) owl_free(cmd->summary);
+  if (cmd->usage) owl_free(cmd->usage);
+  if (cmd->description) owl_free(cmd->description);
+}
+
+int owl_cmd_is_context_valid(owl_cmd *cmd, owl_context *ctx) { 
+  if (owl_context_matches(ctx, cmd->validctx)) return 1;
+  else return 0;
+}
+
+char *owl_cmd_execute(owl_cmd *cmd, owl_cmddict *cd, owl_context *ctx, int argc, char **argv, char *cmdbuff) {
+  static int alias_recurse_depth = 0;
+  int ival=0;
+  char *cmdbuffargs, *newcmd, *rv=NULL;
+
+  if (argc < 1) return(NULL);
+
+  /* Recurse if this is an alias */
+  if (cmd->cmd_aliased_to) {
+    if (alias_recurse_depth++ > 50) {
+      owl_function_makemsg("Alias loop detected for '%s'.", cmdbuff);
+    } else {
+      cmdbuffargs = skiptokens(cmdbuff, 1);
+      newcmd = owl_malloc(strlen(cmd->cmd_aliased_to)+strlen(cmdbuffargs)+2);
+      strcpy(newcmd, cmd->cmd_aliased_to);
+      strcat(newcmd, " ");
+      strcat(newcmd, cmdbuffargs);
+      rv = owl_function_command(newcmd);
+      owl_free(newcmd);
+    } 
+    alias_recurse_depth--;
+    return rv;
+  }
+
+  /* Do validation and conversions */
+  if (cmd->cmd_ctxargs_fn || cmd->cmd_ctxv_fn || cmd->cmd_ctxi_fn) {
+    if (!owl_cmd_is_context_valid(cmd, ctx)) {
+      owl_function_makemsg("Invalid context for command '%s'.", cmdbuff);
+      return NULL;
+    }
+  }
+
+  if ((argc != 1) && (cmd->cmd_v_fn || cmd->cmd_ctxv_fn)) {
+    owl_function_makemsg("Wrong number of arguments for %s command.", argv[0]);
+    return NULL;
+  }
+
+  if (cmd->cmd_i_fn || cmd->cmd_ctxi_fn) {
+      char *ep = "x";
+      if (argc != 2) {
+	owl_function_makemsg("Wrong number of arguments for %s command.", argv[0]);
+	return NULL;
+      }
+      ival = strtol(argv[1], &ep, 10);
+      if (*ep || ep==argv[1]) {
+	owl_function_makemsg("Invalid argument '%s' for %s command.", argv[1], argv[0]);
+	return(NULL);
+      }
+  }
+
+  if (cmd->cmd_args_fn) {
+    return cmd->cmd_args_fn(argc, argv, cmdbuff);
+  } else if (cmd->cmd_v_fn) {    
+    cmd->cmd_v_fn();
+  } else if (cmd->cmd_i_fn) {
+    cmd->cmd_i_fn(ival);
+  } else if (cmd->cmd_ctxargs_fn) {
+    return cmd->cmd_ctxargs_fn(owl_context_get_data(ctx), argc, argv, cmdbuff);
+  } else if (cmd->cmd_ctxv_fn) {    
+    cmd->cmd_ctxv_fn(owl_context_get_data(ctx));
+  } else if (cmd->cmd_ctxi_fn) {
+    cmd->cmd_ctxi_fn(owl_context_get_data(ctx), ival);
+  }
+
+  return NULL;
+}
+
+/* returns a reference */
+char *owl_cmd_get_summary(owl_cmd *cmd) {
+  return cmd->summary;
+}
+
+/* returns a summary line describing this keymap.  the caller must free. */
+char *owl_cmd_describe(owl_cmd *cmd) {
+  char *s;
+  int slen;
+  if (!cmd || !cmd->name || !cmd->summary) return NULL;
+  slen = strlen(cmd->name)+strlen(cmd->summary)+30;
+  s = owl_malloc(slen);
+  snprintf(s, slen-1, "%-25s - %s", cmd->name, cmd->summary);
+  return s;
+}
+
+
+
+void owl_cmd_get_help(owl_cmddict *d, char *name, owl_fmtext *fm) {
+  char *indent, *s;
+  owl_cmd *cmd;
+
+  if (!name || (cmd = owl_dict_find_element(d, name)) == NULL) {
+    owl_fmtext_append_bold(fm, "OWL HELP\n\n");
+    owl_fmtext_append_normal(fm, "No such command...\n");
+    return;
+  }
+
+  owl_fmtext_append_bold(fm, "OWL HELP\n\n");
+  owl_fmtext_append_bold(fm, "NAME\n\n");
+  owl_fmtext_append_normal(fm, OWL_TABSTR);
+  owl_fmtext_append_normal(fm, cmd->name);
+
+  if (cmd->summary && *cmd->summary) {
+    owl_fmtext_append_normal(fm, " - ");
+    owl_fmtext_append_normal(fm, cmd->summary);
+  }
+  owl_fmtext_append_normal(fm, "\n");
+
+  if (cmd->usage && *cmd->usage) {
+    s = cmd->usage;
+    indent = owl_malloc(strlen(s)+(owl_text_num_lines(s)+3)*OWL_TAB+1);
+    owl_text_indent(indent, s, OWL_TAB);
+    owl_fmtext_append_bold(fm, "\nSYNOPSIS\n");
+    owl_fmtext_append_normal(fm, indent);
+    owl_fmtext_append_normal(fm, "\n");
+    owl_free(indent);
+  } else {
+    owl_fmtext_append_bold(fm, "\nSYNOPSIS\n");
+    owl_fmtext_append_normal(fm, OWL_TABSTR);
+    owl_fmtext_append_normal(fm, cmd->name);
+    owl_fmtext_append_normal(fm, "\n");
+  }
+
+  if (cmd->description && *cmd->description) {
+    s = cmd->description;
+    indent = owl_malloc(strlen(s)+(owl_text_num_lines(s)+3)*OWL_TAB+1);
+    owl_text_indent(indent, s, OWL_TAB);
+    owl_fmtext_append_bold(fm, "\nDESCRIPTION\n");
+    owl_fmtext_append_normal(fm, indent);
+    owl_fmtext_append_normal(fm, "\n");
+    owl_free(indent);
+  }
+
+  owl_fmtext_append_normal(fm, "\n\n");  
+}
Index: codelist.pl
===================================================================
--- codelist.pl (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ codelist.pl (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,24 @@
+if ($#ARGV eq -1) {
+    @ARGV=`ls *.c`;
+    chop(@ARGV);
+}
+
+foreach $file (@ARGV) {
+    open(FILE, $file);
+
+    print "/* -------------------------------- $file -------------------------------- */\n";
+    while (<FILE>) {
+	if (/^\S/
+	    && /\{\s*$/
+	    && !/\}/
+	    && !/^\{/
+	    && !/^#include/
+	    && !/^static/
+	    && !/^#define/
+	    && !/\/\*/)
+	{s/\s+\{/\;/; print "extern "; print;}
+	    
+    }
+    close(FILE);
+    print "\n";
+}
Index: commands.c
===================================================================
--- commands.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ commands.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,1374 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "owl.h"
+
+/* fn is "char *foo(int argc, char **argv, char *buff)" */
+#define OWLCMD_ARGS(name, fn, ctx, summary, usage, description) \
+        { name, summary, usage, description, ctx, \
+          NULL, fn, NULL, NULL, NULL, NULL, NULL }
+
+/* fn is "void foo(void)" */
+#define OWLCMD_VOID(name, fn, ctx, summary, usage, description) \
+        { name, summary, usage, description, ctx, \
+          NULL, NULL, fn, NULL, NULL, NULL, NULL }
+
+/* fn is "void foo(int)" */
+#define OWLCMD_INT(name, fn, ctx, summary, usage, description) \
+        { name, summary, usage, description, ctx, \
+          NULL, NULL, NULL, fn, NULL, NULL, NULL }
+
+#define OWLCMD_ALIAS(name, actualname) \
+        { name, OWL_CMD_ALIAS_SUMMARY_PREFIX actualname, "", "", OWL_CTX_ANY, \
+          actualname, NULL, NULL, NULL, NULL, NULL, NULL }
+
+/* fn is "char *foo(void *ctx, int argc, char **argv, char *buff)" */
+#define OWLCMD_ARGS_CTX(name, fn, ctx, summary, usage, description) \
+        { name, summary, usage, description, ctx, \
+          NULL, NULL, NULL, NULL, ((char*(*)(void*,int,char**,char*))fn), NULL, NULL }
+
+/* fn is "void foo(void)" */
+#define OWLCMD_VOID_CTX(name, fn, ctx, summary, usage, description) \
+        { name, summary, usage, description, ctx, \
+          NULL, NULL, NULL, NULL, NULL, ((void(*)(void*))(fn)), NULL }
+
+/* fn is "void foo(int)" */
+#define OWLCMD_INT_CTX(name, fn, ctx, summary, usage, description) \
+        { name, summary, usage, description, ctx, \
+          NULL, NULL, NULL, NULL, NULL, NULL, ((void(*)(void*,int))fn) }
+
+
+owl_cmd commands_to_init[]
+  = {
+  OWLCMD_ARGS("zlog", owl_command_zlog, OWL_CTX_ANY,
+	      "send a login or logout notification",
+	      "zlog in\nzlog out",
+	      "zlog in will send a login notification, zlog out will send a\n"
+	      "logout notification.  By default a login notification is sent\n"
+	      "when owl is started and a logout notification is sent when owl\n"
+	      "is exited.  This behavior can be changed with the 'startuplogin'\n"
+	      "and 'shudownlogout' variables.\n"),
+
+  OWLCMD_VOID("quit", owl_command_quit, OWL_CTX_ANY,
+	      "exit owl",
+	      "",
+	      "Exit owl and run any shutdown activities."),
+  OWLCMD_ALIAS("exit", "quit"),
+  OWLCMD_ALIAS("q",    "quit"),
+  
+  OWLCMD_ARGS("start-command", owl_command_start_command, OWL_CTX_INTERACTIVE,
+	      "prompts the user to enter a command",
+	      "start-command [initial-value]",
+	      "Initializes the command field to initial-value."),
+
+  OWLCMD_ARGS("alias", owl_command_alias, OWL_CTX_ANY,
+	      "creates a command alias",
+	      "alias <new_command> <old_command>",
+	      "Creates a command alias from new_command to old_command.\n"
+	      "Any arguments passed to <new_command> will be appended to\n"
+	      "<old_command> before it is executed.\n"),
+
+  OWLCMD_ARGS("bindkey", owl_command_bindkey, OWL_CTX_ANY,
+	      "creates a binding in a keymap",
+	      "bindkey <keymap> <keyseq> command <command>",
+	      "Binds a key sequence to a command within a keymap.\n"
+	      "Use 'show keymaps' to see the existing keymaps.\n"
+	      "Key sequences may be things like M-C-t or NPAGE.\n"),
+
+  OWLCMD_ARGS("zwrite", owl_command_zwrite, OWL_CTX_INTERACTIVE,
+	      "send a zephyr",
+	      "zwrite [-n] [-C] [-c class] [-i instance] [-r realm] [-O opcde] [<user> ...] ",
+	      "Zwrite send a zephyr to the one or more users specified.\n\n"
+	      "The following options are available:\n\n"
+	      "-n    Do not send a ping message.\n\n"
+	      "-C    If the message is sent to more than one user include a\n"
+	      "      \"cc:\" line in the text\n\n"
+	      "-c class\n"
+	      "      Send to the specified zephyr class\n\n"
+	      "-i instance\n"
+	      "      Send to the specified zephyr instance\n\n"
+	      "-r realm\n"
+	      "      Send to a foreign realm\n"
+	      "-O opcode\n"
+	      "      Send to the specified opcode\n"),
+  
+  OWLCMD_ARGS("reply", owl_command_reply,  OWL_CTX_INTERACTIVE,
+	      "reply to the current message",
+	      "reply [-e] [ sender | all ]",
+	      "If -e is specified, the zwrite command line is presented to\n"
+	      "allow editing.\n\n"
+	      "If 'sender' is specified, reply to the sender.\n\n"
+	      "If 'all' or no args are specified, reply publically to the\n"
+	      "same class/instance for non-personal messages and to the\n"
+	      "sender for personal messages.\n"),
+
+  OWLCMD_ARGS("set", owl_command_set, OWL_CTX_ANY,
+	      "set a variable value",
+	      "set [-q] <variable> <value>\n"
+	      "set",
+	      "Set the named variable to the specified value.  If no\n"
+	      "arguments are used print the value of all variables.\n"
+	      "If -q is specified, is silent and doesn't print a message.\n"),
+
+  OWLCMD_ARGS("print", owl_command_print, OWL_CTX_ANY,
+	      "print a variable value",
+	      "print <variable>\n"
+	      "print",
+	      "Print the value of the named variable.  If no arugments\n"
+	      "are used print the value of all variables.\n"),
+
+  OWLCMD_VOID("version", owl_command_version, OWL_CTX_ANY,
+	      "print the version of the running owl", "", ""),
+
+  OWLCMD_ARGS("subscribe", owl_command_subscribe, OWL_CTX_ANY,
+	      "subscribe to a zephyr class, instance, recipient",
+	      "subscribe <class> <instance> [recipient]",
+	      "Subscribe the specified class and instance.  If the recipient\n"
+	      "is not listed on the command line it defaults\n"
+	      "to * (the wildcard recipient).\n"),
+  OWLCMD_ALIAS("sub", "subscribe"),
+
+  OWLCMD_ARGS("unsubscribe", owl_command_unsubscribe, OWL_CTX_ANY,
+	      "unsubscribe from a zephyr class, instance, recipient",
+	      "unsubscribe <class> <instance> [recipient]",
+	      "Unsubscribe from the specified class and instance.  If the\n"
+	      "recipient is not listed on the command line it defaults\n"
+	      "to * (the wildcard recipient).\n"),
+  OWLCMD_ALIAS("unsub", "unsubscribe"),
+
+  OWLCMD_VOID("unsuball", owl_command_unsuball, OWL_CTX_ANY,
+	      "unsubscribe from all zephyrs", "", ""),
+  
+  OWLCMD_VOID("getsubs", owl_command_getsubs, OWL_CTX_ANY,
+	      "print all current subscriptions",
+	      "getsubs",
+	      "getsubs retrieves the current subscriptions from the server\n"
+	      "and displays them.\n"),
+
+  OWLCMD_ARGS("zpunt", owl_command_zpunt, OWL_CTX_ANY,
+	      "suppress a given zephyr triplet",
+	      "zpunt <class> <instance> [recipient]\n"
+	      "zpunt <instance>",
+	      "The zpunt command will supress message to the specified\n"
+	      "zephyr triplet.  In the second usage messages as supressed\n"
+	      "for class MESSAGE and the named instance.\n\n"
+	      "SEE ALSO:  zunpunt, show zpunts\n"),
+
+  OWLCMD_ARGS("zunpunt", owl_command_zunpunt, OWL_CTX_ANY,
+	      "undo a previous zpunt",
+	      "zunpunt <class> <instance> [recipient]\n"
+	      "zunpunt <instance>",
+	      "The zunpunt command will allow messages that were previosly\n"
+	      "suppressed to be received again.\n\n"
+	      "SEE ALSO:  zpunt, show zpunts\n"),
+
+  OWLCMD_VOID("info", owl_command_info, OWL_CTX_INTERACTIVE,
+	      "display detailed information about the current message",
+	      "", ""),
+  
+  OWLCMD_ARGS("help", owl_command_help, OWL_CTX_INTERACTIVE,
+	      "display help on using owl",
+	      "help [command]", ""),
+  
+  OWLCMD_VOID("recv:shiftleft", owl_command_shift_left, OWL_CTX_INTERACTIVE,
+	      "scrolls receive window to the left", "", ""),
+
+  OWLCMD_VOID("recv:shiftright", owl_command_shift_right, OWL_CTX_INTERACTIVE,
+	      "scrolls receive window to the left", "", ""),
+
+  OWLCMD_VOID("recv:pagedown", owl_function_mainwin_pagedown, 
+	      OWL_CTX_INTERACTIVE,
+	      "scrolls down by a page", "", ""),
+
+  OWLCMD_VOID("recv:pageup", owl_function_mainwin_pageup, OWL_CTX_INTERACTIVE,
+	      "scrolls up by a page", "", ""),
+
+  OWLCMD_INT ("recv:scroll", owl_function_page_curmsg, OWL_CTX_INTERACTIVE,
+	      "scrolls current message up or down", 
+	      "recv:scroll <numlines>", 
+	      "Scrolls the current message up or down by <numlines>.\n"
+	      "Scrolls up if <numlines> is negative, else scrolls down.\n"),
+
+  OWLCMD_VOID("next", owl_command_next, OWL_CTX_INTERACTIVE,
+	      "move the pointer to the next message", "", ""),
+  OWLCMD_ALIAS("recv:next", "next"),
+
+  OWLCMD_VOID("prev", owl_command_prev, OWL_CTX_INTERACTIVE,
+	      "move the pointer to the previous message", "", ""),
+  OWLCMD_ALIAS("recv:prev", "prev"),
+
+  OWLCMD_VOID("next-notdel", owl_command_next_notdeleted, OWL_CTX_INTERACTIVE,
+	      "move the pointer to the next non-deleted message", "", ""),
+  OWLCMD_ALIAS("recv:next-notdel", "next-notdel"),
+
+  OWLCMD_VOID("prev-notdel", owl_command_prev_notdeleted, OWL_CTX_INTERACTIVE,
+	      "move the pointer to the previous non-deleted message", "", ""),
+  OWLCMD_ALIAS("recv:prev-notdel", "prev-notdel"),
+
+  OWLCMD_VOID("recv:next-personal", owl_function_next_personal, 
+	      OWL_CTX_INTERACTIVE,
+	      "move the pointer to the next personal message", "", ""),
+
+  OWLCMD_VOID("recv:prev-personal", owl_function_prev_personal, 
+	      OWL_CTX_INTERACTIVE,
+	      "move the pointer to the previous personal message", "", ""),
+
+  OWLCMD_VOID("first", owl_command_first, OWL_CTX_INTERACTIVE,
+	      "move the pointer to the first message", "", ""),
+  OWLCMD_ALIAS("recv:first", "first"),
+
+  OWLCMD_VOID("last", owl_command_last, OWL_CTX_INTERACTIVE,
+	      "move the pointer to the last message", "", ""),
+  OWLCMD_ALIAS("recv:last", "last"),
+
+  OWLCMD_VOID("expunge", owl_command_expunge, OWL_CTX_INTERACTIVE,
+	      "remove all messages marked for deletion", "", ""),
+
+  OWLCMD_VOID("resize", owl_command_resize, OWL_CTX_ANY,
+	      "resize the window to the current screen size", "", ""),
+
+  OWLCMD_VOID("redisplay", owl_command_redisplay, OWL_CTX_ANY,
+	      "redraw the entire window", "", ""),
+
+  OWLCMD_VOID("suspend", owl_command_suspend, OWL_CTX_ANY,
+	      "suspend owl", "", ""),
+
+  OWLCMD_ARGS("echo", owl_command_echo, OWL_CTX_ANY,
+	      "pops up a message in popup window",
+	      "echo [args .. ]\n\n", ""),
+
+  OWLCMD_ARGS("exec", owl_command_exec, OWL_CTX_ANY,
+	      "run a command from the shell",
+	      "exec [args .. ]", ""),
+
+  OWLCMD_ARGS("aexec", owl_command_aexec, OWL_CTX_INTERACTIVE,
+	      "run a command from the shell and display in an admin message",
+	      "aexec [args .. ]", ""),
+
+  OWLCMD_ARGS("pexec", owl_command_pexec, OWL_CTX_INTERACTIVE,
+	      "run a command from the shell and display in a popup window",
+	      "pexec [args .. ]", ""),
+
+  OWLCMD_ARGS("perl", owl_command_perl, OWL_CTX_ANY,
+	      "run a perl expression",
+	      "perl [args .. ]", ""),
+
+  OWLCMD_ARGS("aperl", owl_command_aperl, OWL_CTX_INTERACTIVE,
+	      "run a perl expression and display in an admin message",
+	      "aperl [args .. ]", ""),
+
+  OWLCMD_ARGS("pperl", owl_command_pperl, OWL_CTX_INTERACTIVE,
+	      "run a perl expression and display in a popup window",
+	      "pperl [args .. ]", ""),
+
+  OWLCMD_ARGS("multi", owl_command_multi, OWL_CTX_ANY,
+	      "runs multiple ;-separated commands",
+	      "multi <command1> ( ; <command2> )*\n",
+	      "Runs multiple semicolon-separated commands in order.\n"
+	      "Note quoting isn't supported here yet.\n"
+	      "If you want to do something fancy, use perl.\n"),
+
+  OWLCMD_ARGS("(", owl_command_multi, OWL_CTX_ANY,
+	      "runs multiple ;-separated commands",
+	      "'(' <command1> ( ; <command2> )* ')'\n",
+	      "Runs multiple semicolon-separated commands in order.\n"
+	      "You must have a space before the final ')'\n"
+	      "Note quoting isn't supported here yet.\n"
+	      "If you want to do something fancy, use perl.\n"),
+
+  OWLCMD_VOID("pop-message", owl_command_pop_message, OWL_CTX_RECWIN,
+	      "pops up a message in a window", "", ""),
+
+  OWLCMD_VOID("openurl", owl_command_openurl, OWL_CTX_INTERACTIVE,
+	      "opens up a URL from the current message",
+	      "", 
+	      "Uses the 'webbrowser' variable to determine\n"
+	      "which browser to use.  Currently, 'netscape'\n"
+	      "and 'galeon' are supported.\n"),
+
+  OWLCMD_ARGS("zaway", owl_command_zaway, OWL_CTX_INTERACTIVE,
+	      "running a command from the shell",
+	      "zaway [ on | off | toggle ]\n"
+	      "zaway <message>",
+	      "Turn on or off the default zaway message.  If a message is\n"
+	      "specified turn on zaway with that message\n"),
+
+  OWLCMD_ARGS("load-subs", owl_command_loadsubs, OWL_CTX_ANY,
+	      "load subscriptions from a file",
+	      "load-subs <file>\n", ""),
+
+  OWLCMD_VOID("about", owl_command_about, OWL_CTX_INTERACTIVE,
+	      "print information about owl", "", ""),
+
+  OWLCMD_VOID("status", owl_command_status, OWL_CTX_ANY,
+	      "print status information about the running owl", "", ""),
+  
+  OWLCMD_ARGS("zlocate", owl_command_zlocate, OWL_CTX_INTERACTIVE,
+	      "locate a user",
+	      "zlocate [-d] <user>", 
+	      "Performs a zlocate on a user and puts the result into\n"
+	      "a popwin.  If -d is specified, does not authenticate\n"
+	      "the lookup request.\n"),
+  
+  OWLCMD_ARGS("filter", owl_command_filter, OWL_CTX_ANY,
+	      "create a message filter",
+	      "filter <name> [ -c color ] [ <expression> ... ]",
+	      "The filter command creates a filter with the specified name,\n"
+	      "or if one already exists it is replaced.  Example filter\n"
+	      "syntax would be:\n\n"
+	      "     filter myfilter -c red ( class ^foobar$ ) or ( class ^quux$ and instance ^bar$ )\n\n"
+	      "Valid matching fields are class, instance, recipient, sender,\n"
+	      "opcode and realm. Valid operations are 'and', 'or' and 'not'.\n"
+	      "Spaces must be present before and after parenthesis.  If the\n"
+	      "optional color argument is used it specifies the color that\n"
+	      "messages matching this filter should be displayed in.\n\n"
+
+	      "SEE ALSO: view, viewclass, viewuser\n"),
+
+  OWLCMD_ARGS("colorview", owl_command_colorview, OWL_CTX_INTERACTIVE,
+	      "change the color on the current filter",
+	      "colorview <color>",
+	      "The color of messages in the current filter will be changed\n"
+	      "to <color>.  Use the 'show colors' command for a list\n"
+	      "of valid colors.\n\n"
+	      "SEE ALSO: 'show colors'\n"),
+
+  OWLCMD_ARGS("view", owl_command_view, OWL_CTX_INTERACTIVE,
+	      "view messages matching a filter",
+	      "view <filter>\n"
+	      "view -d <expression>\n"
+	      "view --home",
+	      "In the first usage, The view command sets the current view to\n"
+	      "the specified filter.   This causes only messages matching\n"
+	      "that filter to be displayed.\n"
+	      "\n"
+	      "In the second usage a filter expression\n"
+	      "and be specified directly\n"
+	      "after -d.  Owl will build an internal filter based on this\n"
+	      "filter and change the current view to use it.\n\n"
+	      "If --home is specified, switches to the view specified by\n"
+	      "the 'view_home' variable.\n\n"
+	      "SEE ALSO: filter, viewclass, viewuser\n"),
+
+  OWLCMD_ARGS("smartnarrow", owl_command_smartnarrow, OWL_CTX_INTERACTIVE,
+	      "view only messages similar to the current message",
+	      "smartnarrow [-i]",
+	      "If the curmsg is a personal message narrow\n"
+	      "   to the converstaion with that user.\n"
+	      "If the curmsg is a class message, instance foo, recip *\n"
+	      "   message, narrow to the class, inst.\n"
+	      "If the curmsg is a class message then narrow\n"
+	      "    to the class.\n"
+	      "If the curmsg is a class message and '-i' is specied\n"
+	      "    then narrow to the class, instance\n"),
+
+  OWLCMD_ARGS("viewclass", owl_command_viewclass, OWL_CTX_INTERACTIVE,
+	      "view messages matching a particular class",
+	      "viewclass <class>",
+	      "The viewclass command will automatically create a filter\n"
+	      "matching the specified class and switch the current view\n"
+	      "to it.\n\n"
+	      "SEE ALSO: filter, view, viewuser\n"),
+  OWLCMD_ALIAS("vc", "viewclass"),
+
+  OWLCMD_ARGS("viewuser", owl_command_viewuser, OWL_CTX_INTERACTIVE,
+	      "view messages matching a particular user",
+	      "viewuser <user>",
+	      "The viewuser command will automatically create a filter\n"
+	      "matching the specified user and switch the current\n"
+	      "view to it.\n\n"
+	      "SEE ALSO: filter, view, viewclass\n"),
+  OWLCMD_ALIAS("vu", "viewuser"),
+
+  OWLCMD_ARGS("show", owl_command_show, OWL_CTX_INTERACTIVE,
+	      "show information",
+	      "show variables\n"
+	      "show variable <variable>\n"
+	      "show filters\n"
+	      "show filter <filter>\n"
+	      "show keymaps\n"
+	      "show keymap <keymap>\n"
+	      "show commands\n"
+	      "show command <command>\n"
+	      "show subs\n"
+	      "show subscriptions\n"
+	      "show zpunts\n"
+	      "show colors\n"
+	      "show terminal\n"
+	      "show version\n"
+	      "show status\n",
+
+	      "Show colors will display a list of valid colors for the\n"
+	      "     terminal."
+	      "Show filters will list the names of all filters.\n"
+	      "Show filter <filter> will show the definition of a particular\n"
+	      "     filter.\n\n"
+	      "Show zpunts will show the active zpunt filters.\n\n"
+	      "Show keymaps will list the names of all keymaps.\n"
+	      "Show keymap <keymap> will show the key bindings in a keymap.\n\n"
+	      "Show commands will list the names of all keymaps.\n"
+	      "Show command <command> will provide information about a command.\n\n"
+	      "Show variables will list the names of all variables.\n\n"
+	      "SEE ALSO: filter, view, alias, bindkey, help\n"),
+  
+  OWLCMD_ARGS("delete", owl_command_delete, OWL_CTX_INTERACTIVE,
+	      "mark a message for deletion",
+	      "delete [ -id msgid ]\n"
+	      "delete view\n"
+	      "delete trash",
+	      "If no message id is specified the current message is marked\n"
+	      "for deletion.  Otherwise the message with the given message\n"
+	      "id is marked for deltion.\n"
+	      "If 'trash' is specified, deletes all trash/auto messages\n"
+	      "in the current view.\n"
+	      "If 'view' is specified, deletes all messages in the\n"
+	      "current view.\n"),
+  OWLCMD_ALIAS("del", "delete"),
+
+  OWLCMD_ARGS("undelete", owl_command_undelete, OWL_CTX_INTERACTIVE,
+	      "unmark a message for deletion",
+	      "undelete [ -id msgid ]\n"
+	      "undelete view",
+	      "If no message id is specified the current message is\n"
+	      "unmarked for deletion.  Otherwise the message with the\n"
+	      "given message id is marked for undeltion.\n"
+	      "If 'view' is specified, undeletes all messages\n"
+	      "in the current view.\n"),
+  OWLCMD_ALIAS("undel", "undelete"),
+
+  OWLCMD_VOID("beep", owl_command_beep, OWL_CTX_ANY,
+	      "ring the terminal bell",
+	      "beep",
+	      "Beep will ring the terminal bell.\n"
+	      "If the variable 'bell' has been\n"
+	      "set to 'off' this command does nothing.\n"),
+
+  OWLCMD_ARGS("debug", owl_command_debug, OWL_CTX_ANY,
+	      "prints a message into the debug log",
+	      "debug <message>", ""),
+
+  /****************************************************************/
+  /************************* EDIT-SPECIFIC ************************/
+  /****************************************************************/
+
+  OWLCMD_VOID_CTX("edit:move-next-word", owl_editwin_move_to_nextword, 
+		  OWL_CTX_EDIT,
+		  "moves cursor forward a word",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:move-prev-word", owl_editwin_move_to_previousword, 
+		  OWL_CTX_EDIT,
+		  "moves cursor backwards a word",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:move-to-buffer-start", owl_editwin_move_to_top,
+		  OWL_CTX_EDIT,
+		  "moves cursor to the top left (start) of the buffer",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:move-to-buffer-end", owl_editwin_move_to_end, 
+		  OWL_CTX_EDIT,
+		  "moves cursor to the bottom right (end) of the buffer",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:move-to-line-end", owl_editwin_move_to_line_end, 
+		  OWL_CTX_EDIT,
+		  "moves cursor to the end of the line",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:move-to-line-start", owl_editwin_move_to_line_start, 
+		  OWL_CTX_EDIT,
+		  "moves cursor to the beginning of the line",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:move-left", owl_editwin_key_left, 
+		  OWL_CTX_EDIT,
+		  "moves the cursor left by a character",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:move-right", owl_editwin_key_right,
+		  OWL_CTX_EDIT,
+		  "moves the cursor right by a character",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:delete-next-word", owl_editwin_delete_nextword,
+		  OWL_CTX_EDIT,
+		  "deletes the word to the right of the cursor",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:delete-prev-char", owl_editwin_backspace,
+		  OWL_CTX_EDIT,
+		  "deletes the character to the left of the cursor",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:delete-next-char", owl_editwin_delete_char, 
+		  OWL_CTX_EDIT,
+		  "deletes the character to the right of the cursor",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:delete-to-line-end", owl_editwin_delete_to_endofline,
+		  OWL_CTX_EDIT,
+		  "deletes from the cursor to the end of the line",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:delete-all", owl_editwin_clear, 
+		  OWL_CTX_EDIT,
+		  "deletes all of the contents of the buffer",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:fill-paragraph", owl_editwin_fill_paragraph, 
+		  OWL_CTX_EDIT,
+		  "fills the current paragraph to line-wrap well",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:recenter", owl_editwin_recenter, 
+		  OWL_CTX_EDIT,
+		  "recenters the buffer",
+		  "", ""),
+
+  OWLCMD_ARGS_CTX("edit:insert-text", owl_command_edit_insert_text, 
+		  OWL_CTX_EDIT,
+		  "inserts text into the buffer",
+		  "edit:insert-text <text>", ""),
+
+  OWLCMD_VOID_CTX("edit:cancel", owl_command_edit_cancel, 
+		  OWL_CTX_EDIT,
+		  "cancels the current command",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:history-next", owl_command_edit_history_next, 
+		  OWL_CTX_EDITLINE,
+		  "replaces the text with the previous history",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("edit:history-prev", owl_command_edit_history_prev, 
+		  OWL_CTX_EDITLINE,
+		  "replaces the text with the previous history",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("editline:done", owl_command_editline_done, 
+		  OWL_CTX_EDITLINE,
+		  "completes the command (eg, executes command being composed)",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("editmulti:move-up-line", owl_editwin_key_up, 
+		  OWL_CTX_EDITMULTI,
+		  "moves the cursor up one line",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("editmulti:move-down-line", owl_editwin_key_down, 
+		  OWL_CTX_EDITMULTI,
+		  "moves the cursor down one line",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("editmulti:done", owl_command_editmulti_done, 
+		  OWL_CTX_EDITMULTI,
+		  "completes the command (eg, sends message being composed)",
+		  "", ""),
+
+  /****************************************************************/
+  /********************** POPLESS-SPECIFIC ************************/
+  /****************************************************************/
+
+  OWLCMD_VOID_CTX("popless:scroll-down-page", owl_viewwin_pagedown, 
+		  OWL_CTX_POPLESS,
+		  "scrolls down one page",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("popless:scroll-down-line", owl_viewwin_linedown, 
+		  OWL_CTX_POPLESS,
+		  "scrolls down one line",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("popless:scroll-up-page", owl_viewwin_pageup, 
+		  OWL_CTX_POPLESS,
+		  "scrolls up one page",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("popless:scroll-up-line", owl_viewwin_lineup, 
+		  OWL_CTX_POPLESS,
+		  "scrolls up one line",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("popless:scroll-to-top", owl_viewwin_top, 
+		  OWL_CTX_POPLESS,
+		  "scrolls to the top of the buffer",
+		  "", ""),
+
+  OWLCMD_VOID_CTX("popless:scroll-to-bottom", owl_viewwin_bottom, 
+		  OWL_CTX_POPLESS,
+		  "scrolls to the bottom of the buffer",
+		  "", ""),
+
+  OWLCMD_INT_CTX ("popless:scroll-right", owl_viewwin_right, 
+		  OWL_CTX_POPLESS,
+		  "scrolls right in the buffer",
+		  "popless:scroll-right <num-chars>", ""),
+
+  OWLCMD_INT_CTX ("popless:scroll-left", owl_viewwin_left, 
+		  OWL_CTX_POPLESS,
+		  "scrolls left in the buffer",
+		  "popless:scroll-left <num-chars>", ""),
+
+  OWLCMD_VOID_CTX("popless:quit", owl_command_popless_quit, 
+		  OWL_CTX_POPLESS,
+		  "exits the popless window",
+		  "", ""),
+
+  /* This line MUST be last! */
+  { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
+};
+
+void owl_command_info() {
+  owl_function_info();
+}
+
+char *owl_command_help(int argc, char **argv, char *buff) {
+  if (argc!=2) {
+    owl_help();
+    return NULL;
+  }
+  
+  owl_function_help_for_command(argv[1]);
+  return NULL;
+}
+
+void owl_command_about() {
+  owl_function_about();
+}
+
+void owl_command_version() {
+  char buff[1024];
+
+  sprintf(buff, "Owl version %s", OWL_VERSION_STRING);
+  owl_function_makemsg(buff);
+}
+
+void owl_command_next() {
+  owl_function_nextmsg();
+}
+
+void owl_command_prev() {
+  owl_function_prevmsg();
+}
+
+void owl_command_next_notdeleted() {
+  owl_function_nextmsg_notdeleted();
+}
+
+void owl_command_prev_notdeleted() {
+  owl_function_prevmsg_notdeleted();
+}
+
+char *owl_command_smartnarrow(int argc, char **argv, char *buff) {
+  if (argc == 1) {
+    owl_function_smartnarrow(0);
+  } else if (argc == 2 && !strcmp(argv[1], "-i")) {
+    owl_function_smartnarrow(1);
+  } else {
+    owl_function_makemsg("Wrong number of arguments for %s", argv[0]);    
+  }
+  return NULL;
+}
+
+void owl_command_expunge() {
+  owl_function_expunge();
+}
+
+void owl_command_first() {
+  owl_global_set_rightshift(&g, 0);
+  owl_function_firstmsg();
+}
+
+void owl_command_last() {
+  owl_function_lastmsg();
+}
+
+void owl_command_resize() {
+  owl_function_resize();
+}
+
+void owl_command_redisplay() {
+  owl_function_full_redisplay();
+  owl_global_set_needrefresh(&g);
+}
+
+void owl_command_shift_right() {
+  owl_function_shift_right();
+}
+
+void owl_command_shift_left() {
+  owl_function_shift_left();
+}
+
+void owl_command_unsuball() {
+  owl_function_unsuball();
+}
+
+char *owl_command_loadsubs(int argc, char **argv, char *buff) {
+  if (argc == 2) {
+    owl_function_loadsubs(argv[1]);
+  } else if (argc == 1) {
+    owl_function_loadsubs(NULL);
+  } else {
+    owl_function_makemsg("Wrong number of arguments for load-subs.");
+    return NULL;
+  }
+  return NULL;
+}
+
+void owl_command_suspend() {
+  owl_function_suspend();
+}
+
+char *owl_command_start_command(int argc, char **argv, char *buff) {
+  buff = skiptokens(buff, 1);
+  owl_function_start_command(buff);
+  return NULL;
+}
+
+char *owl_command_zaway(int argc, char **argv, char *buff) {
+  
+  if ((argc==1) ||
+      ((argc==2) && !strcmp(argv[1], "on"))) {
+    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
+    owl_function_zaway_on();
+    return NULL;
+  }
+
+  if (argc==2 && !strcmp(argv[1], "off")) {
+    owl_function_zaway_off();
+    return NULL;
+  }
+
+  if (argc==2 && !strcmp(argv[1], "toggle")) {
+    owl_function_zaway_toggle();
+    return NULL;
+  }
+
+  buff = skiptokens(buff, 1);
+  owl_global_set_zaway_msg(&g, buff);
+  owl_function_zaway_on();
+  return NULL;
+}
+
+
+char *owl_command_set(int argc, char **argv, char *buff) {
+  char *var, *val;
+  int  silent=0;
+
+  if (argc == 1) {
+    owl_function_printallvars();
+    return NULL;
+  } else if (argc == 4 && !strcmp("-q",argv[1])) {
+    silent = 1;
+    var=argv[2];
+    val=argv[3];
+  } else if (argc == 3) {
+    var=argv[1];
+    val=argv[2];
+  } else {
+    owl_function_makemsg("Wrong number of arguments for set command");
+    return NULL;
+  }
+
+  owl_variable_set_fromstring(owl_global_get_vardict(&g), var, val, !silent);
+  return NULL;
+}
+
+char *owl_command_print(int argc, char **argv, char *buff) {
+  char *var;
+  char valbuff[1024];
+
+  if (argc==1) {
+    owl_function_printallvars();
+    return NULL;
+  } else if (argc!=2) {
+    owl_function_makemsg("Wrong number of arguments for print command");
+    return NULL;
+  }
+
+  var=argv[1];
+    
+  if (0 == owl_variable_get_tostring(owl_global_get_vardict(&g), 
+				     var, valbuff, 1024)) {
+    owl_function_makemsg("%s = '%s'", var, valbuff);
+  } else {
+    owl_function_makemsg("Unknown variable '%s'.", var);
+  }
+  return NULL;
+}
+
+
+char *owl_command_exec(int argc, char **argv, char *buff) {
+  return owl_function_exec(argc, argv, buff, 0);
+}
+
+char *owl_command_pexec(int argc, char **argv, char *buff) {
+  return owl_function_exec(argc, argv, buff, 1);
+}
+
+char *owl_command_aexec(int argc, char **argv, char *buff) {
+  return owl_function_exec(argc, argv, buff, 2);
+}
+
+char *owl_command_perl(int argc, char **argv, char *buff) {
+  return owl_function_perl(argc, argv, buff, 0);
+}
+
+char *owl_command_pperl(int argc, char **argv, char *buff) {
+  return owl_function_perl(argc, argv, buff, 1);
+}
+
+char *owl_command_aperl(int argc, char **argv, char *buff) {
+  return owl_function_perl(argc, argv, buff, 2);
+}
+
+char *owl_command_multi(int argc, char **argv, char *buff) {
+  char *lastrv = NULL, *newbuff;
+  char **commands;
+  int  ncommands, i;
+  if (argc < 2) {
+    owl_function_makemsg("Invalid arguments to 'multi' command.");    
+    return NULL;
+  }
+  newbuff = owl_strdup(buff);
+  newbuff = skiptokens(newbuff, 1);
+  if (!strcmp(argv[0], "(")) {
+    for (i=strlen(newbuff)-1; i>=0; i--) {
+      if (newbuff[i] == ')') {
+	newbuff[i] = '\0';
+	break;
+      } else if (newbuff[i] != ' ') {
+	owl_function_makemsg("Invalid arguments to 'multi' command.");    
+	owl_free(newbuff);
+	return NULL;
+      }
+    }
+  }
+  commands = atokenize(newbuff, ";", &ncommands);
+  for (i=0; i<ncommands; i++) {
+    if (lastrv) {
+      owl_free(lastrv);
+    }
+    lastrv = owl_function_command(commands[i]);
+  }
+  atokenize_free(commands, ncommands);
+  return lastrv;
+}
+
+
+char *owl_command_alias(int argc, char **argv, char *buff) {
+  if (argc < 3) {
+    owl_function_makemsg("Invalid arguments to 'alias' command.");
+    return NULL;
+  }
+  buff = skiptokens(buff, 2);
+  owl_function_command_alias(argv[1], buff);
+  return (NULL);
+}
+
+
+char *owl_command_bindkey(int argc, char **argv, char *buff) {
+  owl_keymap *km;
+  int ret;
+
+  if (argc < 5 || strcmp(argv[3], "command")) {
+    owl_function_makemsg("Usage: bindkey <keymap> <binding> command <cmd>", argv[3], argc);
+    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;
+  }
+  buff = skiptokens(buff, 4);
+  ret = owl_keymap_create_binding(km, argv[2], buff, NULL, "*user*");
+  if (ret!=0) {
+    owl_function_makemsg("Unable to bind '%s' in keymap '%s' to '%s'.",
+			 argv[2], argv[1], buff);
+    return NULL;
+  }
+  return NULL;
+}
+
+void owl_command_quit() {
+  owl_function_quit();
+}
+
+char *owl_command_debug(int argc, char **argv, char *buff) {
+  if (argc<2) {
+    owl_function_makemsg("Need at least one argument to debug command");
+    return NULL;
+  }
+
+  if (!owl_global_is_debug_fast(&g)) {
+    owl_function_makemsg("Debugging is not turned on");
+    return NULL;
+  }
+
+  owl_function_debugmsg(argv[1]);
+  return NULL;
+}
+
+char *owl_command_ktest(int argc, char **argv, char *buff) {
+  owl_function_popless_text("foobar");
+  return NULL;
+}
+
+char *owl_command_zlog(int argc, char **argv, char *buff) {
+  if (argc != 2) {
+    owl_function_makemsg("Wrong number of arguments for zlog command");
+    return NULL;
+  }
+
+  if (!strcmp(argv[1], "in")) {
+    owl_function_zlog_in();
+  } else if (!strcmp(argv[1], "out")) {
+    owl_function_zlog_out();
+  } else {
+    owl_function_makemsg("Invalid subcommand for zlog");
+  }
+  return NULL;
+}
+
+
+void owl_command_zlog_out() {
+  owl_function_zlog_out();
+}
+
+
+char *owl_command_subscribe(int argc, char **argv, char *buff) {
+  char *recip="";
+
+  if (argc<3) {
+    owl_function_makemsg("Not enough arguments to the subscribe command");
+    return NULL;
+  } else if (argc>4) {
+    owl_function_makemsg("Too many arguments to the subscribe command");
+    return NULL;
+  }
+
+  if (argc==3) {
+    recip="";
+  } else if (argc==4) {
+    recip=argv[3];
+  }
+
+  owl_function_subscribe(argv[1], argv[2], recip);
+  return NULL;
+}
+
+
+char *owl_command_unsubscribe(int argc, char **argv, char *buff) {
+  char *recip="";
+
+  if (argc<3) {
+    owl_function_makemsg("Not enough arguments to the unsubscribe command");
+    return NULL;
+  } else if (argc>4) {
+    owl_function_makemsg("Too many arguments to the unsubscribe command");
+    return NULL;
+  }
+
+  if (argc==3) {
+    recip="";
+  } else if (argc==4) {
+    recip=argv[3];
+  }
+
+  owl_function_unsubscribe(argv[1], argv[2], recip);
+  return NULL;
+}
+
+char *owl_command_echo(int argc, char **argv, char *buff) {
+  buff = skiptokens(buff, 1);
+  owl_function_popless_text(buff);
+  return NULL;
+}
+
+void owl_command_getsubs() {
+  owl_function_getsubs();
+}
+
+void owl_command_status() {
+  owl_function_status();
+}
+
+char *owl_command_zwrite(int argc, char **argv, char *buff) {
+  char *tmpbuff;
+  if (argc < 2) {
+    owl_function_makemsg("Not enough arguments to the zwrite command.");
+  } else {
+    tmpbuff = owl_strdup(buff);
+    owl_function_zwrite_setup(tmpbuff);
+    owl_global_set_buffercommand(&g, tmpbuff);
+    owl_free(tmpbuff);
+  }
+  return NULL;
+}
+
+char *owl_command_reply(int argc, char **argv, char *buff) {
+  int edit=0;
+  
+  if (argc==2 && !strcmp("-e", argv[1])) {
+    edit=1;
+    argv++;
+  }
+
+  if ((argc==1) || (argc==2 && !strcmp(argv[1], "all"))) {    
+    owl_function_reply(0, !edit);
+  } else if ((argc==1) || (argc==2 && !strcmp(argv[1], "sender"))) {
+    owl_function_reply(1, !edit);
+  } else {
+    owl_function_makemsg("Invalid arguments to the reply command.");
+  }
+  return NULL;
+}
+
+char *owl_command_filter(int argc, char **argv, char *buff) {
+  owl_function_create_filter(argc, argv);
+  return NULL;
+}
+
+char *owl_command_zlocate(int argc, char **argv, char *buff) {
+  if (argc==2) {
+    owl_function_zlocate(argv[1], 1);
+  } else if (argc==3) {
+    if (!strcmp(argv[1], "-d")) {
+      owl_function_zlocate(argv[2], 0);
+    } else {
+      owl_function_makemsg("Invalid option to the zlocate command");
+      return NULL;
+    }
+  } else {
+    owl_function_makemsg("Wrong number of arguments for zlocate command");
+    return NULL;
+  }
+  return NULL;
+}
+
+char *owl_command_view(int argc, char **argv, char *buff) {
+  if (argc<2) {
+    owl_function_makemsg("Wrong number of arguments to view command");
+    return NULL;
+  }
+
+  if (argc == 2 && !strcmp(argv[1], "--home")) {
+    owl_function_change_view(owl_global_get_view_home(&g));
+    return NULL;
+  }
+
+  /* is it a dynamic filter? */
+  if (!strcmp(argv[1], "-d")) {
+    char **myargv;
+    int i;
+
+    myargv=owl_malloc((argc*sizeof(char *))+50);
+    myargv[0]="";
+    myargv[1]="owl-dynamic";
+    for (i=2; i<argc; i++) {
+      myargv[i]=argv[i];
+    }
+    owl_function_create_filter(argc, myargv);
+    owl_function_change_view("owl-dynamic");
+    owl_free(myargv);
+    return NULL;
+  }
+
+  /* otherwise it's a normal view command */
+  if (argc>2) {
+    owl_function_makemsg("Wrong number of arguments to view command");
+    return NULL;
+  }
+  owl_function_change_view(argv[1]);
+  return NULL;
+}
+
+
+char *owl_command_show(int argc, char **argv, char *buff) {
+
+  if (argc<2) {
+    owl_function_help_for_command("show");
+    return NULL;
+  }
+
+  if (!strcmp(argv[1], "filter") || !strcmp(argv[1], "filters")) {
+    if (argc==2) {
+      owl_function_show_filters();
+    } else {
+      owl_function_show_filter(argv[2]);
+    }
+  } else if (argc==2 
+	     && (!strcmp(argv[1], "zpunts") || !strcmp(argv[1], "zpunted"))) {
+    owl_function_show_zpunts();
+  } else if (!strcmp(argv[1], "command") || !strcmp(argv[1], "commands")) {
+    if (argc==2) {
+      owl_function_show_commands();
+    } else {
+      owl_function_show_command(argv[2]);
+    }
+  } else if (!strcmp(argv[1], "variable") || !strcmp(argv[1], "variables")) {
+    if (argc==2) {
+      owl_function_show_variables();
+    } else {
+      owl_function_show_variable(argv[2]);
+    }
+  } else if (!strcmp(argv[1], "keymap") || !strcmp(argv[1], "keymaps")) {
+    if (argc==2) {
+      owl_function_show_keymaps();
+    } else {
+      owl_function_show_keymap(argv[2]);
+    }
+  } else if (!strcmp(argv[1], "colors")) {
+    owl_function_show_colors();
+  } else if (!strcmp(argv[1], "subs") || !strcmp(argv[1], "subscriptions")) {
+    owl_function_getsubs();
+  } else if (!strcmp(argv[1], "terminal") || !strcmp(argv[1], "term")) {
+    owl_function_show_term();
+  } else if (!strcmp(argv[1], "version")) {
+    owl_function_about();
+  } else if (!strcmp(argv[1], "status")) {
+    owl_function_status();
+  } else {
+    owl_function_makemsg("Unknown subcommand for 'show' command (see 'help show' for allowed args)");
+    return NULL;
+  }
+  return NULL;
+}
+
+char *owl_command_viewclass(int argc, char **argv, char *buff) {
+  if (argc!=2) {
+    owl_function_makemsg("Wrong number of arguments to viewclass command");
+    return NULL;
+  }
+  owl_function_fastclassinstfilt(argv[1], NULL);
+  return NULL;
+}
+
+char *owl_command_viewuser(int argc, char **argv, char *buff) {
+  if (argc!=2) {
+    owl_function_makemsg("Wrong number of arguments to viewuser command");
+    return NULL;
+  }
+  owl_function_fastuserfilt(argv[1]);
+  return NULL;
+}
+
+
+void owl_command_pop_message(void) {
+  owl_function_curmsg_to_popwin();
+}
+
+void owl_command_openurl(void) {
+  owl_function_openurl();
+}
+
+char *owl_command_delete(int argc, char **argv, char *buff) {
+  if (argc==1) {
+    owl_function_deletecur();
+    return NULL;
+  }
+
+  if (argc==2 && !strcmp(argv[1], "view")) {
+    owl_function_delete_curview_msgs(1);
+    return NULL;
+  }
+
+  if (argc==2 && !strcmp(argv[1], "trash")) {
+    owl_function_delete_automsgs();
+    return NULL;
+  }
+
+  if (argc==3 && !strcmp(argv[1], "-id")) {
+    owl_function_delete_by_id(atoi(argv[2]), 1);
+    return NULL;
+  }
+
+  owl_function_makemsg("Unknown arguments to delete command");
+  return NULL;
+}
+
+char *owl_command_undelete(int argc, char **argv, char *buff) {
+  if (argc==1) {
+    owl_function_undeletecur();
+    return NULL;
+  }
+
+  if (argc==2 && !strcmp(argv[1], "view")) {
+    owl_function_delete_curview_msgs(0);
+    return NULL;
+  }
+
+  if (argc==3 && !strcmp(argv[1], "-id")) {
+    owl_function_delete_by_id(atoi(argv[2]), 0);
+    return NULL;
+  }
+
+  owl_function_makemsg("Unknown arguments to delete command");
+  return NULL;
+}
+
+void owl_command_beep() {
+  owl_function_beep();
+}
+
+char *owl_command_colorview(int argc, char **argv, char *buff) {
+  if (argc!=2) {
+    owl_function_makemsg("Wrong number of arguments to colorview command");
+    return NULL;
+  }
+  owl_function_color_current_filter(argv[1]);
+  return NULL;
+}
+
+char *owl_command_zpunt(int argc, char **argv, char *buff) {
+  owl_command_zpunt_and_zunpunt(argc, argv, 0);
+  return NULL;
+}
+
+char *owl_command_zunpunt(int argc, char **argv, char *buff) {
+  owl_command_zpunt_and_zunpunt(argc, argv, 1);
+  return NULL;
+}
+
+
+void owl_command_zpunt_and_zunpunt(int argc, char **argv, int type) {
+  /* if type==0 then zpunt
+   * if type==1 then zunpunt
+   */
+  char *class, *inst, *recip;
+
+  class="message";
+  inst="";
+  recip="*";
+
+  if (argc==1) {
+    /* show current punt filters */
+    owl_function_show_zpunts();
+    return;
+  } else if (argc==2) {
+    inst=argv[1];
+  } else if (argc==3) {
+    class=argv[1];
+    inst=argv[2];
+  } else if (argc==4) {
+    class=argv[1];
+    inst=argv[2];
+    recip=argv[3];
+  } else {
+    owl_function_makemsg("Wrong number of arguments to the zpunt command");
+    return;
+  }
+
+  owl_function_zpunt(class, inst, recip, type);
+  if (type==0) {
+    owl_function_makemsg("<%s, %s, %s> added to punt list.", class, inst, recip);
+  } else if (type==1) {
+    owl_function_makemsg("<%s, %s, %s> removed from punt list.", class, inst, recip);
+  }
+}
+
+
+/*********************************************************************/
+/************************** EDIT SPECIFIC ****************************/
+/*********************************************************************/
+
+void owl_command_edit_cancel(owl_editwin *e) {
+  owl_function_makemsg("Command cancelled.");
+  owl_editwin_fullclear(e);
+  owl_global_set_needrefresh(&g);
+  wnoutrefresh(owl_editwin_get_curswin(e));
+  owl_global_set_typwin_inactive(&g);
+  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_ONELINE);
+}
+
+void owl_command_edit_history_prev(owl_editwin *e) {
+  owl_history *hist;
+  char *ptr;
+
+  hist=owl_global_get_history(&g);
+  if (!owl_history_is_touched(hist)) {
+    owl_history_store(hist, owl_editwin_get_text(e));
+    owl_history_set_partial(hist);
+  }
+  ptr=owl_history_get_prev(hist);
+  if (ptr) {
+    owl_editwin_clear(e);
+    owl_editwin_insert_string(e, ptr);
+    owl_editwin_redisplay(e, 0);
+    owl_global_set_needrefresh(&g);
+  } else {
+    owl_function_beep();
+  }
+}
+
+void owl_command_edit_history_next(owl_editwin *e) {
+  owl_history *hist;
+  char *ptr;
+
+  hist=owl_global_get_history(&g);
+  ptr=owl_history_get_next(hist);
+  if (ptr) {
+    owl_editwin_clear(e);
+    owl_editwin_insert_string(e, ptr);
+    owl_editwin_redisplay(e, 0);
+    owl_global_set_needrefresh(&g);
+  } else {
+    owl_function_beep();
+  }
+}
+
+char *owl_command_edit_insert_text(owl_editwin *e, int argc, char **argv, char *buff) {
+  buff = skiptokens(buff, 1);
+  owl_editwin_insert_string(e, buff);
+  owl_editwin_redisplay(e, 0);
+  owl_global_set_needrefresh(&g);  
+  return NULL;
+}
+
+void owl_command_editline_done(owl_editwin *e) {
+  owl_history *hist=owl_global_get_history(&g);
+  char *rv;
+
+  owl_global_set_typwin_inactive(&g);
+  owl_history_store(hist, owl_editwin_get_text(e));
+  owl_history_reset(hist);
+  rv = owl_function_command(owl_editwin_get_text(e));
+  
+  /* if we're still in ONELINE mode we can clear the buffer */
+  if (owl_editwin_get_style(e)==OWL_EDITWIN_STYLE_ONELINE) {
+    owl_editwin_fullclear(e);
+  }
+ 
+  wnoutrefresh(owl_editwin_get_curswin(e));
+  owl_global_set_needrefresh(&g);
+
+  if (rv) {
+    owl_function_makemsg("%s", rv);
+    owl_free(rv);
+  }
+}
+
+void owl_command_editmulti_done(owl_editwin *e) {
+  owl_function_run_buffercommand();
+  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_ONELINE);
+  owl_editwin_fullclear(e);
+  owl_global_set_typwin_inactive(&g);
+  owl_global_set_needrefresh(&g);
+  wnoutrefresh(owl_editwin_get_curswin(e));
+}
+
+
+/*********************************************************************/
+/*********************** POPLESS SPECIFIC ****************************/
+/*********************************************************************/
+
+void owl_command_popless_quit(owl_viewwin *vw) {
+  owl_popwin_close(owl_global_get_popwin(&g));
+  owl_viewwin_free(vw);
+  owl_global_set_needrefresh(&g);
+}
+
Index: config.h.in
===================================================================
--- config.h.in (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ config.h.in (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,48 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you have the <strings.h> header file.  */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/ioctl.h> header file.  */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the com_err library (-lcom_err).  */
+#undef HAVE_LIBCOM_ERR
+
+/* Define if you have the des425 library (-ldes425).  */
+#undef HAVE_LIBDES425
+
+/* Define if you have the k5crypto library (-lk5crypto).  */
+#undef HAVE_LIBK5CRYPTO
+
+/* Define if you have the krb4 library (-lkrb4).  */
+#undef HAVE_LIBKRB4
+
+/* Define if you have the krb5 library (-lkrb5).  */
+#undef HAVE_LIBKRB5
+
+/* Define if you have the ncurses library (-lncurses).  */
+#undef HAVE_LIBNCURSES
+
+/* Define if you have the nsl library (-lnsl).  */
+#undef HAVE_LIBNSL
+
+/* Define if you have the socket library (-lsocket).  */
+#undef HAVE_LIBSOCKET
+
+/* Define if you have the zephyr library (-lzephyr).  */
+#undef HAVE_LIBZEPHYR
+
+/* Define if libzephyr contains ZInitLocationInfo */
+#undef HAVE_LIBZEPHYR_ZINITLOCATIONINFO
+
+#undef TERMINFO
Index: configure
===================================================================
--- configure (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ configure (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,1971 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=owl.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='	'
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:531: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:561: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+	continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:612: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:644: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 655 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:660: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:686: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:691: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:700: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:719: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+
+if test "$GCC" = yes; then
+     CFLAGS="$CFLAGS -Wall -g";
+fi
+
+echo $ac_n "checking for /usr/athena/include""... $ac_c" 1>&6
+echo "configure:756: checking for /usr/athena/include" >&5
+if test -d /usr/athena/include; then
+	CFLAGS=${CFLAGS}\ -I/usr/athena/include
+	echo "$ac_t""yes" 1>&6
+else
+	echo "$ac_t""no" 1>&6
+fi
+echo $ac_n "checking for /usr/athena/lib""... $ac_c" 1>&6
+echo "configure:764: checking for /usr/athena/lib" >&5
+if test -d /usr/athena/lib; then
+	LDFLAGS=-L/usr/athena/lib\ ${LDFLAGS}
+	echo "$ac_t""yes" 1>&6
+else
+	echo "$ac_t""no" 1>&6
+fi
+
+
+
+
+
+
+echo $ac_n "checking for initscr in -lncurses""... $ac_c" 1>&6
+echo "configure:778: checking for initscr in -lncurses" >&5
+ac_lib_var=`echo ncurses'_'initscr | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lncurses  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 786 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char initscr();
+
+int main() {
+initscr()
+; return 0; }
+EOF
+if { (eval echo configure:797: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo ncurses | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lncurses $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for initscr in -lcurses""... $ac_c" 1>&6
+echo "configure:823: checking for initscr in -lcurses" >&5
+ac_lib_var=`echo curses'_'initscr | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lcurses  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 831 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char initscr();
+
+int main() {
+initscr()
+; return 0; }
+EOF
+if { (eval echo configure:842: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo curses | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lcurses $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+{ echo "configure: error: No curses library found." 1>&2; exit 1; }
+fi
+
+fi
+
+echo $ac_n "checking for com_err in -lcom_err""... $ac_c" 1>&6
+echo "configure:873: checking for com_err in -lcom_err" >&5
+ac_lib_var=`echo com_err'_'com_err | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lcom_err  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 881 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char com_err();
+
+int main() {
+com_err()
+; return 0; }
+EOF
+if { (eval echo configure:892: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo com_err | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lcom_err $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
+echo "configure:920: checking for gethostbyname in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lnsl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 928 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:939: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lnsl $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:967: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 975 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:986: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lsocket $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for krb5_derive_key in -lk5crypto""... $ac_c" 1>&6
+echo "configure:1014: checking for krb5_derive_key in -lk5crypto" >&5
+ac_lib_var=`echo k5crypto'_'krb5_derive_key | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lk5crypto  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1022 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char krb5_derive_key();
+
+int main() {
+krb5_derive_key()
+; return 0; }
+EOF
+if { (eval echo configure:1033: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo k5crypto | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lk5crypto $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for des425_req_act_vno in -ldes425""... $ac_c" 1>&6
+echo "configure:1061: checking for des425_req_act_vno in -ldes425" >&5
+ac_lib_var=`echo des425'_'des425_req_act_vno | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldes425  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1069 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char des425_req_act_vno();
+
+int main() {
+des425_req_act_vno()
+; return 0; }
+EOF
+if { (eval echo configure:1080: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo des425 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-ldes425 $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for krb5_get_credentials in -lkrb5""... $ac_c" 1>&6
+echo "configure:1108: checking for krb5_get_credentials in -lkrb5" >&5
+ac_lib_var=`echo krb5'_'krb5_get_credentials | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lkrb5  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1116 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char krb5_get_credentials();
+
+int main() {
+krb5_get_credentials()
+; return 0; }
+EOF
+if { (eval echo configure:1127: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo krb5 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lkrb5 $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for krb_sendauth in -lkrb4""... $ac_c" 1>&6
+echo "configure:1155: checking for krb_sendauth in -lkrb4" >&5
+ac_lib_var=`echo krb4'_'krb_sendauth | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lkrb4  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1163 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char krb_sendauth();
+
+int main() {
+krb_sendauth()
+; return 0; }
+EOF
+if { (eval echo configure:1174: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo krb4 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lkrb4 $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for ZGetSender in -lzephyr""... $ac_c" 1>&6
+echo "configure:1202: checking for ZGetSender in -lzephyr" >&5
+ac_lib_var=`echo zephyr'_'ZGetSender | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lzephyr  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1210 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char ZGetSender();
+
+int main() {
+ZGetSender()
+; return 0; }
+EOF
+if { (eval echo configure:1221: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo zephyr | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lzephyr $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+{ echo "configure: error: No zephyr library found." 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking for ZInitLocationInfo in -lzephyr""... $ac_c" 1>&6
+echo "configure:1250: checking for ZInitLocationInfo in -lzephyr" >&5
+ac_lib_var=`echo zephyr'_'ZInitLocationInfo | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lzephyr  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1258 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char ZInitLocationInfo();
+
+int main() {
+ZInitLocationInfo()
+; return 0; }
+EOF
+if { (eval echo configure:1269: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_LIBZEPHYR_ZINITLOCATIONINFO 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1294: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 1309 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1315: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 1326 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1332: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 1343 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1349: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1374: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1379 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1387: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1404 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1422 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1443 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1454: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6
+echo "configure:1478: checking for sys/wait.h that is POSIX.1 compatible" >&5
+if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1483 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+int main() {
+int s;
+wait (&s);
+s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+; return 0; }
+EOF
+if { (eval echo configure:1499: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_header_sys_wait_h=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_sys_wait_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6
+if test $ac_cv_header_sys_wait_h = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_SYS_WAIT_H 1
+EOF
+
+fi
+
+for ac_hdr in strings.h sys/ioctl.h unistd.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1523: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1528 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1533: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+FOO=`perl -MExtUtils::Embed -e ccopts`
+echo Adding perl CFLAGS ${FOO}
+CFLAGS=${CFLAGS}\ ${FOO}
+
+FOO=`perl -MExtUtils::Embed -e ldopts`
+echo Adding perl LDFLAGS ${FOO}
+LDFLAGS=${LDFLAGS}\ ${FOO}
+
+
+ac_safe=`echo "/usr/share/terminfo" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for /usr/share/terminfo""... $ac_c" 1>&6
+echo "configure:1571: checking for /usr/share/terminfo" >&5
+if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; }
+else
+  if test -r /usr/share/terminfo; then
+    eval "ac_cv_file_$ac_safe=yes"
+  else
+    eval "ac_cv_file_$ac_safe=no"
+  fi
+fi
+fi
+if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define TERMINFO "/usr/share/terminfo"
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+
+ac_safe=`echo "/usr/share/lib/terminfo" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for /usr/share/lib/terminfo""... $ac_c" 1>&6
+echo "configure:1596: checking for /usr/share/lib/terminfo" >&5
+if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; }
+else
+  if test -r /usr/share/lib/terminfo; then
+    eval "ac_cv_file_$ac_safe=yes"
+  else
+    eval "ac_cv_file_$ac_safe=no"
+  fi
+fi
+fi
+if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define TERMINFO "/usr/share/lib/terminfo"
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+{ echo "configure: error: No terminfo found for this system" 1>&2; exit 1; }
+fi
+
+fi
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[ 	]*VPATH[ 	]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ 	]*\)#\([ 	]*define[ 	][ 	]*\)'
+ac_dB='\([ 	][ 	]*\)[^ 	]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ 	]*\)#\([ 	]*\)undef\([ 	][ 	]*\)'
+ac_uB='\([ 	]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ 	]*\)#\([ 	]*\)undef\([ 	][ 	]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ 	]*#[ 	]*undef[ 	][ 	]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
Index: configure.in
===================================================================
--- configure.in (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ configure.in (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,98 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(owl.c)
+
+AC_CONFIG_HEADER(config.h)
+
+AC_PROG_CC
+
+dnl If we're using GCC, enable all warnings
+if test "$GCC" = yes; then
+     CFLAGS="$CFLAGS -Wall -g";
+fi
+
+dnl Check for Athena
+AC_MSG_CHECKING(for /usr/athena/include)
+if test -d /usr/athena/include; then
+	CFLAGS=${CFLAGS}\ -I/usr/athena/include
+	AC_MSG_RESULT(yes)
+else
+	AC_MSG_RESULT(no)
+fi
+AC_MSG_CHECKING(for /usr/athena/lib)
+if test -d /usr/athena/lib; then
+	LDFLAGS=-L/usr/athena/lib\ ${LDFLAGS}
+	AC_MSG_RESULT(yes)
+else
+	AC_MSG_RESULT(no)
+fi
+
+dnl A hack for myself to use gnu curses
+dnl AC_MSG_CHECKING(for mit gnu hack)
+dnl if test -d /mit/gnu/include; then
+dnl 	CFLAGS=${CFLAGS}\ -I/mit/gnu/include
+dnl 	AC_MSG_RESULT(yes)
+dnl else
+dnl 	AC_MSG_RESULT(no)
+dnl fi
+
+
+dnl Solaris ships with a broken curses.h
+dnl AC_MSG_CHECKING(for solaris broken curses.h)
+dnl if grep "Sun Microsystems" /usr/include/curses.h > /dev/null 2> /dev/null; then
+dnl   if test  "`grep -c getwc\(stdin\) /usr/include/widec.h`" = "2" ; then
+dnl     rm -rf includefix
+dnl     mkdir includefix
+dnl     sed -e 's/<widec.h>/\"widec.h\"/' /usr/include/curses.h > ./includefix/curses.h
+dnl  
+dnl     sed -e 's/<wchar.h>/\"wchar.h\"/' \
+dnl         -e 's/^#define.putwchar.*//' \
+dnl         -e 's/^#define.getwchar().*//' \
+dnl         -e 's/^#define.getwc(p).*//' \
+dnl         -e 's/^#define.putwc(x, p).*//' \
+dnl         /usr/include/widec.h > includefix/widec.h
+dnl     CFLAGS=-I./includefix\ ${CFLAGS}
+dnl     AC_MSG_RESULT(yes)
+dnl   fi
+dnl else
+dnl   AC_MSG_RESULT(no)
+dnl fi
+
+
+
+AC_CHECK_LIB(ncurses, initscr,,
+   AC_CHECK_LIB(curses, initscr,, AC_MSG_ERROR(No curses library found.)))
+AC_CHECK_LIB(com_err, com_err)
+AC_CHECK_LIB(nsl, gethostbyname)
+AC_CHECK_LIB(socket, socket)
+AC_CHECK_LIB(k5crypto, krb5_derive_key)
+AC_CHECK_LIB(des425, des425_req_act_vno)
+AC_CHECK_LIB(krb5, krb5_get_credentials)
+AC_CHECK_LIB(krb4, krb_sendauth)
+AC_CHECK_LIB(zephyr, ZGetSender,, AC_MSG_ERROR(No zephyr library found.))
+AC_CHECK_LIB(zephyr, ZInitLocationInfo, AC_DEFINE(HAVE_LIBZEPHYR_ZINITLOCATIONINFO),)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(strings.h sys/ioctl.h unistd.h)
+
+dnl Add CFLAGS for embeded perl
+FOO=`perl -MExtUtils::Embed -e ccopts`
+echo Adding perl CFLAGS ${FOO}
+CFLAGS=${CFLAGS}\ ${FOO}
+
+dnl Add LDFLAGS for embeded perl
+FOO=`perl -MExtUtils::Embed -e ldopts`
+echo Adding perl LDFLAGS ${FOO}
+LDFLAGS=${LDFLAGS}\ ${FOO}
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_CHECK_FILE(/usr/share/terminfo, AC_DEFINE(TERMINFO, "/usr/share/terminfo"),
+  AC_CHECK_FILE(/usr/share/lib/terminfo, AC_DEFINE(TERMINFO, "/usr/share/lib/terminfo"),
+  AC_MSG_ERROR(No terminfo found for this system)))
+
+dnl Checks for library functions.
+dnl AC_PROG_GCC_TRADITIONAL
+dnl AC_CHECK_FUNCS(gethostname strdup)
+
+AC_OUTPUT(Makefile)
Index: context.c
===================================================================
--- context.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ context.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,75 @@
+#include "owl.h"
+#include <string.h>
+
+#define SET_ACTIVE(ctx, new) ctx->mode = ((ctx->mode)&~OWL_CTX_ACTIVE_BITS)|new
+#define SET_MODE(ctx, new) ctx->mode = ((ctx->mode)&~OWL_CTX_MODE_BITS)|new
+
+int owl_context_init(owl_context *ctx) {
+  ctx->mode = OWL_CTX_STARTUP;
+  ctx->data = NULL;
+  return 0;
+}
+
+
+/* returns whether test matches the current context */
+int owl_context_matches(owl_context *ctx, int test) {
+  owl_function_debugmsg(", current: 0x%04x test: 0x%04x\n", ctx->mode, test);
+  if ((((ctx->mode&OWL_CTX_MODE_BITS) & test)
+       || !(test&OWL_CTX_MODE_BITS))
+      && 
+      (((ctx->mode&OWL_CTX_ACTIVE_BITS) & test) 
+       || !(test&OWL_CTX_ACTIVE_BITS))) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+void *owl_context_get_data(owl_context *ctx) {
+  return ctx->data;
+}
+
+int owl_context_get_mode(owl_context *ctx) {
+  return ctx->mode & OWL_CTX_MODE_BITS;
+}
+
+int owl_context_get_active(owl_context *ctx) {
+  return ctx->mode & OWL_CTX_ACTIVE_BITS;
+}
+
+int owl_context_is_startup(owl_context *ctx) {
+  return (ctx->mode & OWL_CTX_STARTUP)?1:0;
+}
+
+
+void owl_context_set_startup(owl_context *ctx) {
+  SET_MODE(ctx, OWL_CTX_STARTUP);
+}
+
+void owl_context_set_readconfig(owl_context *ctx) {
+  SET_MODE(ctx, OWL_CTX_READCONFIG);
+}
+
+void owl_context_set_interactive(owl_context *ctx) {
+  SET_MODE(ctx, OWL_CTX_INTERACTIVE);
+}
+
+void owl_context_set_popless(owl_context *ctx, owl_viewwin *vw) {
+  ctx->data = (void*)vw;
+  SET_ACTIVE(ctx, OWL_CTX_POPLESS);
+}
+
+void owl_context_set_recv(owl_context *ctx) {
+  SET_ACTIVE(ctx, OWL_CTX_RECV);
+}
+
+void owl_context_set_editmulti(owl_context *ctx, owl_editwin *ew) {
+  ctx->data = (void*)ew;
+  SET_ACTIVE(ctx, OWL_CTX_EDITMULTI);
+}
+
+void owl_context_set_editline(owl_context *ctx, owl_editwin *ew) {
+  ctx->data = (void*)ew;
+  SET_ACTIVE(ctx, OWL_CTX_EDITLINE);
+}
+
Index: dict.c
===================================================================
--- dict.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ dict.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,183 @@
+/* Dictionary data abstraction.  
+ * Maps from strings to pointers.
+ * Stores as a sorted list of key/value pairs.
+ * O(n) on inserts and deletes.
+ * O(n) on searches, although it should switch to a binary search for O(log n)
+ */
+
+#include "owl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define INITSIZE 30
+#define GROWAT 2
+#define GROWBY 1.5
+
+int owl_dict_create(owl_dict *d) {
+  d->size=0;
+  d->els=(owl_dict_el *)owl_malloc(INITSIZE*sizeof(owl_dict_el));
+  d->avail=INITSIZE;
+  if (d->els==NULL) return(-1);
+  return(0);
+}
+
+int owl_dict_get_size(owl_dict *d) {
+  return(d->size);
+}
+
+/* Finds the position of an element with key k, or of the element where
+ * this element would logically go, and stores the index in pos.
+ * TODO: optimize to do a binary search.
+ * Returns 1 if found, else 0. */
+int _owl_dict_find_pos(owl_dict *d, char *k, int *pos) {
+  int i;
+  for (i=0; (i<d->size) && strcmp(k,d->els[i].k)>0; i++);
+  *pos = i;
+  if (i>=d->size || strcmp(k,d->els[i].k)) {
+    return(0);
+  } else {
+    return(1);
+  }
+}
+
+/* returns the value corresponding to key k */
+void *owl_dict_find_element(owl_dict *d, char *k) {
+  int found, pos;
+  found = _owl_dict_find_pos(d, k, &pos);
+  if (!found) {
+    return(NULL);
+  }
+  return(d->els[pos].v);
+}
+
+/* creates a list and fills it in with keys.  duplicates the keys, 
+ * so they will need to be freed by the caller. */
+int owl_dict_get_keys(owl_dict *d, owl_list *l) {
+  int i;
+  char *dupk;
+  if (owl_list_create(l)) return(-1);
+  for (i=0; i<d->size; i++) {
+    if ((dupk = owl_strdup(d->els[i].k)) == NULL) return(-1);
+    owl_list_append_element(l, (void*)dupk);
+  }
+  return(0);
+}
+
+void owl_dict_noop_free(void *x) {
+  return;
+}
+
+/* Returns 0 on success.  Will copy the key but make 
+   a reference to the value.  Will clobber an existing 
+   entry with the same key iff free_on_replace!=NULL,
+   and will run free_on_replace on the old element.
+   Will return -2 if replace=NULL and match was found.
+*/
+int owl_dict_insert_element(owl_dict *d, char *k, void *v, void (*free_on_replace)(void *old)) {
+  int pos, found;
+  char *dupk;
+  found = _owl_dict_find_pos(d, k, &pos);
+  if (found && free_on_replace) {
+    free_on_replace(d->els[pos].v);
+    d->els[pos].v = v;
+    return(0);
+  } else if (found && !free_on_replace) {
+    return(-2);
+  } else {
+    if ((d->size+1) > (d->avail/GROWAT)) {
+      d->els=owl_realloc(d->els, d->avail*GROWBY*sizeof(void *));
+      d->avail=d->avail*GROWBY;
+      if (d->els==NULL) return(-1);
+    }
+    if ((dupk = owl_strdup(k)) == NULL) return(-1);
+    if (pos!=d->size) {
+      /* shift forward to leave us a slot */
+      memmove((void*)(d->els+pos+1), (void*)(d->els+pos), 
+	      sizeof(owl_dict_el)*(d->size-pos));
+    }
+    d->size++;
+    d->els[pos].k = dupk;
+    d->els[pos].v = v;    
+    return(0);
+  }
+}
+
+/* Doesn't free the value of the element, but does
+ * return it so the caller can free it. */
+void *owl_dict_remove_element(owl_dict *d, char *k) {
+  int i;
+  int pos, found;
+  void *v;
+  found = _owl_dict_find_pos(d, k, &pos);
+  if (!found) return(NULL);
+  owl_free(d->els[pos].k);
+  v = d->els[pos].v;
+  for (i=pos; i<d->size-1; i++) {
+    d->els[i]=d->els[i+1];
+  }
+  d->size--;
+  return(v);
+}
+
+/* elefree should free the value as well */
+void owl_dict_free_all(owl_dict *d, void (*elefree)(void *)) {
+  int i;
+
+  for (i=0; i<d->size; i++) {
+    owl_free(d->els[i].k);
+    if (elefree) (elefree)(d->els[i].v);
+  }
+  if (d->els) owl_free(d->els);
+}
+
+void owl_dict_free_simple(owl_dict *d) {
+  owl_dict_free_all(d, NULL);
+}
+
+
+
+/************* REGRESSION TESTS **************/
+#ifdef OWL_INCLUDE_REG_TESTS
+
+#define FAIL_UNLESS(desc,pred) printf("\t%-4s: %s\n", (pred)?"ok":(numfailed++,"FAIL"), desc)
+
+int owl_dict_regtest(void) {
+  owl_dict d;
+  owl_list l;
+  int numfailed=0;
+  char *av="aval", *bv="bval", *cv="cval", *dv="dval";
+
+  printf("BEGIN testing owl_dict\n");
+  FAIL_UNLESS("create", 0==owl_dict_create(&d));
+  FAIL_UNLESS("insert b", 0==owl_dict_insert_element(&d, "b", (void*)bv, owl_dict_noop_free));
+  FAIL_UNLESS("insert d", 0==owl_dict_insert_element(&d, "d", (void*)dv, owl_dict_noop_free));
+  FAIL_UNLESS("insert a", 0==owl_dict_insert_element(&d, "a", (void*)av, owl_dict_noop_free));
+  FAIL_UNLESS("insert c", 0==owl_dict_insert_element(&d, "c", (void*)cv, owl_dict_noop_free));
+  FAIL_UNLESS("reinsert d (no replace)", -2==owl_dict_insert_element(&d, "d", (void*)dv, 0));
+  FAIL_UNLESS("find a", (void*)av==owl_dict_find_element(&d, "a"));
+  FAIL_UNLESS("find b", (void*)bv==owl_dict_find_element(&d, "b"));
+  FAIL_UNLESS("find c", (void*)cv==owl_dict_find_element(&d, "c"));
+  FAIL_UNLESS("find d", (void*)dv==owl_dict_find_element(&d, "d"));
+  FAIL_UNLESS("find e (non-existent)", NULL==owl_dict_find_element(&d, "e"));
+  FAIL_UNLESS("remove d", (void*)dv==owl_dict_remove_element(&d, "d"));
+  FAIL_UNLESS("find d (post-removal)", NULL==owl_dict_find_element(&d, "d"));
+
+  FAIL_UNLESS("get_size", 3==owl_dict_get_size(&d));
+  FAIL_UNLESS("get_keys", 0==owl_dict_get_keys(&d, &l));
+  FAIL_UNLESS("get_keys result size", 3==owl_list_get_size(&l));
+  
+  /* these assume the returned keys are sorted */
+  FAIL_UNLESS("get_keys result val",0==strcmp("a",owl_list_get_element(&l,0)));
+  FAIL_UNLESS("get_keys result val",0==strcmp("b",owl_list_get_element(&l,1)));
+  FAIL_UNLESS("get_keys result val",0==strcmp("c",owl_list_get_element(&l,2)));
+
+  owl_list_free_all(&l, owl_free);
+  owl_dict_free_all(&d, NULL);
+
+  if (numfailed) printf("*** WARNING: failures encountered with owl_dict\n");
+  printf("END testing owl_dict (%d failures)\n", numfailed);
+  return(numfailed);
+}
+
+#endif /* OWL_INCLUDE_REG_TESTS */
Index: doc/code.txt
===================================================================
--- doc/code.txt (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ doc/code.txt (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,196 @@
+
+TYPES / CLASSES / SOURCE FILES
+------------------------------
+
+cmd:	     Underlying implementation of command table management
+	     and command dispatching.  Also handles the implementation
+	     of command aliases.
+
+commands:    Dispatching for commands and handling of their arguments.
+	     (Commands are the interface exported to the user.)
+	     Many commands tend to be backed by functions.
+	     In general, a command takes a string as an argument
+	     and optionally returns a string.
+	     At the top of the file is a table mapping
+	     command names and help to functions implementing them.
+	     The standard entrypoint for executing a command
+	     is owl_function_command("foo");
+	     Commands are only active within specific contexts,
+	     and attempts to call them from invalid contexts will fail.	     
+
+context:     A context specifies the current state of owl, in terms 
+	     of which modal window is active and which point
+	     in its life owl is in (eg, in startup, or running).
+	     This is implemented as a bitmask and there is 
+	     some hierarchy.  Commands may restrict themselves
+	     to only running in a limited number of contexts
+	     to prevent commands from being executed at points
+	     when they not make sense.  Also, the data from
+	     the active context (eg, a pointer to an active window)
+	     may be passed to a command.
+
+dict:	     Simple dictionary abstraction mapping from strings to pointers.
+
+editwin:     Text editing window (both multiline and single line).
+	     Sometimes also referred to as typewin.
+
+filter:	     Patterns which match messages.  These may 
+	     contain multiple filterelements which may be 
+	     combined together (eg, by "and" and "or").
+	     
+filterelement: An element of a filter which matches on some 
+	       attribute of a message.
+
+fmtext:      Formatted text routines (handles things like @i{foo}).
+	     These are particularly useful for building up
+	     text regions that are to be rendered on-screen,
+	     as they resize memory as needed, and they have 
+	     routines for cropping as needed.
+
+functions:   Where most features are implemented.
+	     Users should always interact with functions through commands.
+	     In general, functions are abstract entrypoints into the
+	     system and attempt to hide access to global state.
+
+global:	     Global state and variables and toplevel objects.
+	     owl.h defines "g" as a singleton instance of owl_global.
+	     Where possible/appropriate, most accesses to global data should
+	     be from a limited number of files (eg, from owl.c and 
+	     functions.c).  Consider whether you really need to before
+	     adding in uses of global.
+
+help:	     Help strings for commands and key bindings.
+	     Most of this is now in keys.c, commands.c, and variables.c,
+	     along with the definition of commands and keybindings.
+
+keys:	     Default key binding definitions for all keymaps.
+	     This also includes default actions for keymaps.
+
+keybinding:  Binding between a sequence of keypresses and a command.
+	     When executed, this executes the commands.  The sequence
+	     of keypresses is kept as a stack.  Keybindings are a part
+	     of keymaps.
+
+keypress:    Utility routines for translating between keypress values and
+	     and human-readable key names.
+
+keymap:      Contains both keymap and keyhandler.  A keymap is contains a
+	     list of keybindings, a sub-keymap, and optionally a
+	     default handler function.  The sub-keymap is a more
+	     general keymap which is consulted if the specific keymap
+	     doesn't contain a match.  (For example, the "global"
+	     keymap is the ancestor of all other keymaps.)  The
+	     keyhandler is a collection of keymaps which handles
+	     checking for key matches within keymaps.  It maintains a
+	     stack of keypresses and compares them against the
+	     bindings in keymaps.  It also handles ESC as a prefix for
+	     Meta.  At any one time, there is exactly one active
+	     keymap which determines where keybindings are looked for
+	     (along with its submaps).
+
+list:	     Simple list abstraction.  (Uses realloc to resize the list.)
+
+logging:     Interface to incoming / outgoing zephyr logging.
+
+mainwin:     Window that displays the list of messages.
+	     (Sometimes also referred to as recwin.)
+
+message:     Abstraction to messages.  Currently, messages
+	     are either of type zephyr or of type admin.
+
+messagelist: List of messages.
+
+owl.c:	     main() and signal handlers and other initial setup.
+	     Also contains the main loop, which is roughly:
+	     - handle scheduled resizes, and anything that might result
+	     - while zephyrs are pending, grab incoming zephyrs 
+	       and handle them (which includes formatting them
+	       with either perl extension or default formatter
+	       as part of owl_message_create_from_zephyr).
+	     - updates mainwin display if there are new zephyrs
+	     - displays and updates popwins and the terminal as necessary
+	     - sends characters to the popwin, recwin/mainwin, 
+	       or typewin/editwin
+
+
+owl.h:	     Prototypes for all types, as well as global constants.
+
+owl_prototypes.h:  Autogenerated prototypes for all functions.
+		   Created by codelist.pl.
+
+popwin:      Modal pop-up window container. 
+	     Usually contains a viewwin for read-only scrolling text.
+
+readconfig:  Perl extension interface.
+
+text:	     Text formatting utilities (ie, indenting, truncating, etc)
+
+util:	     Misc utility functions that don't fit anywhere yet:
+	     - sepbar rendering 
+	     - tokenizing and parsing utilities
+	     - downstr
+	     - stristr
+	     - owl_malloc/free/realloc
+
+variable:    Interface to setting and getting variables.
+	     Current variable types include bool, int, string, and other.
+	     There's also an enum type which is variant of int.
+	     Variables can be created and customized here as well.
+
+varstubs.c:  Autogenerated headers for accessing global variables
+
+view:	     A collection of messages determined by a filter.
+	     Many operations may be performed on the members
+	     of a view, and a view can be narrowed-to for display.
+
+viewwin:     Read-only scrolling text displayed in a modal popwin.
+	     This is also sometimes called "popless".
+
+zephyr:	     Routines for interfacing to zephyr.
+
+zwrite:	     Outgoing zephyrs.  Sends pings on creation,
+	     handles command arguments, etc.
+
+
+===========================================================================
+
+CURSES WINDOWS
+--------------
+
+The four curses windows on the screen are
+
+  recwin - receiving window
+  sepwin - seperator window
+  msgwin - message window
+  typwin - typing window
+
+
+===========================================================================
+
+
+MISC THINGS
+-----------
+
+userclue:  right now userclue is just used to decide if you sub to
+	   classes other than the default.  If you don't it doesn't bother
+	   making your personal messages bold since there's no point in
+	   making every message bold.
+
+
+
+===========================================================================
+
+Conventions and Design Criteria
+-------------------------------
+
+	There are no hard rules for memory allocation.  In general I
+	have the caller allocate memory for objects themselves and any
+	memory the object creates gets freed with object_free().
+	Functions should document if the caller needs to free
+	something, and this should be the exception to the rule.
+
+	Owl should be generally useful out-of-the-box without 
+	extensive configuration, for most people's needs.  
+	People shouldn't have to spend days tweaking
+	with config files before being happy switching to it.
+
Index: doc/contributors
===================================================================
--- doc/contributors (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ doc/contributors (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,41 @@
+Owl was primarly written by James Kretchmar at the Massachusetts
+Institute of Technogloy.  The following people have also made major
+contributions.
+
+Erik Nygren
+   Complete overhaul of the variable system
+   perl, aperl and pperl commands
+   Multiple browser support
+   Support for older versions of lib zephyr
+   Many bug fixes and features
+   Code documentation
+   Example config files
+
+Stephen Gildea
+   Extensive testing and feedback
+
+Mark Eichin
+   Bug fixes
+   Debian packaging
+
+Greg Hudson
+   Hack to work around waiting for HMACKs
+
+David Resnick
+   Build fixes and cleanup
+ 
+Marc Horowitz
+   Embedded perl advice
+
+Jeremy Daniel
+   Bug fixes and patches
+   First version of color support
+
+Andy Ellis, Erin Panttaja, Gisele Proulx
+Ron Hoffmann, Jag Patel, Tara Holm
+Heather Wakefield, Emiliy Havens,
+Matt Braun, Jeff Schiller
+   Beta testing
+
+Anne LaVin
+   ASCII Art Owl
Index: doc/intro.txt
===================================================================
--- doc/intro.txt (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ doc/intro.txt (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,227 @@
+Owl is a tty, curses-based zephyr client.  This is a quick guide to
+learning how to use it.
+
+====================
+OVERVIEW OF FEATURES
+====================
+o) As a tty client it can be run over telnet, rlogin or text ssh sessions
+
+o) It uses a perl configuration file for setting preferences and
+   formatting messages
+
+o) emacs style editing of messages
+
+o) It is easy to use and runs without a configfile.
+
+o) There will future support for AIM and ICQ
+
+o) There will be future support for advanced sorting of messages
+
+===============
+GETTING STARTED
+===============
+Owl will run happily without a configfile, so to get started just run
+it.  Owl will take over the terminal window it's started in.
+
+The Screen
+----------
+There are three main parts to the screen.  The large top portion of
+the screen is where zephyrs are displayed.  The status bar separates
+this area from the one below and displays some status information.
+The space below that is used to type in messages and is also used by
+owl to give warnings and information to the user.
+
+Sending a Zephyr
+----------------
+To send a zephyr to yourself hit the 'z' key.  This will start a
+zwrite command, which you can finish by typing the name of the user
+you wish to send to, followed by enter.  Begin typing your message.
+You will notice that most emacs-style editing is available.  When you
+are ready to send type Control-D.  If you wish to cancel type
+Control-C.
+
+If instead of a user you wish to send to class/instance simply supply
+-c and -i arguments to the zwrite command.
+
+Manipulating Messages
+---------------------
+When there are one or more zephyrs in the message window, one of them
+will be the 'current' message.  Owl will indicate which one it is with
+an arrow to the left of the message.  The following keys will move you
+between messages
+
+	n		move to the next non-deleted message
+	p		move to the previous non-deleted message
+	C-n , down	move to the next message
+	C-p , up	move to the previous message
+	<		move to the first message
+	>		move to the last message
+	right		scroll the screen to the right
+	left		scroll the screen to the left
+	P		move to the next personal message
+	M-P		move to the previous personal message
+	T		Mark all 'trash' messages for deletion
+
+When you are ready to delete a message you can mark it for deletion
+and a 'D' will appear to the left of the message.  Messages will not
+actually be removed until you perform an expunge.
+
+	d		mark a message for deletion
+	u		unmark a message for deletion
+	x		expunge deleted messages
+	T		mark all automated messages for deletion
+
+If you would like to respond to a zephyr sent to you there is a reply
+shortcut.  This will reply to a personal zephyr, or if the zephyr is
+sent to a zephyr class it will reply to the zephyr class.
+
+	r		reply to a zephyr
+
+You can scroll within the current message using:
+
+	SPACE		page down
+	b		page up
+	RETURN		line down
+	BACKSPACE	line up
+
+The pointer will change to indicate that the message is not starting
+at the first line.
+
+Two other keys that relate to the current message:
+
+	i 		print detailed information about the message
+	w 		instruct netscape to visit a URL in the message
+
+
+Other Functions
+----------------
+Some other funcions that can be performed with a single keystroke:
+
+	A		toggle zaway
+	h		print the help screen
+	C-l		refresh and resize the screen
+	C-z		suspend
+
+Command Mode
+------------
+Owl has a command mode where you can enter more detailed commands for
+owl to process.  To enter command mode:
+
+	:		begin command mode
+
+Owl will give you a command prompt and you can begin typing your
+command.  Type Enter to execute the command, Control-C to cancel.
+Currently support commands are:
+
+	quit , exit	exit owl
+	zwrite		send a zephyr
+	reply		reply to the current zephyr
+	set		set a variable
+	print		print a variable
+	zlog [in|out]	send a login or logout message
+	version		print the owl version number
+	subscribe	subscribe to a class, instance, recipient
+	unsubscribe	unsubscribe from a class, instance, recipient
+	unsuball	unsubscribe from all messagse
+	getsubs		print a list of current subscriptions
+	zlocate		locate a user
+	info		print detailed information about a message
+	help		print the help screen
+	next		move to the next message
+	prev		move to the previous message
+	next-notdel	move to the next non-deleted message
+	prev-notdel	move to the previous not-deleted message
+	expunge		expunge messages marked for deletion
+	first		move to the first message
+	last		move to the last message
+	resize		resize owl to the current window
+	suspend		suspend owl
+	exec		run a shell command
+	zaway [on|off|<msg>]  Turn zaway on, off, or set the message
+	load-subs	load subscriptions from a file
+	status		print info about owl status
+	about		print info about owl
+
+Variables
+---------
+The 'set' and 'print' commands let set and print the values of owl
+variables.  The currently supported variables are these:
+
+Variables:                                                                                        
+     name            settings  default      meaning                                                  
+     --------------- --------  -------      -------                                                  
+     appendtosepbar  <string>            string to append to the end of the sepbar                
+     bell            on,off    on        enable / disable the terminal bell                       
+     burningears     on,off    off       [NOT YET IMPLEMENTED] beep on messages matching patterns 
+     classlogging    on,off    off       turn class logging on or off                             
+     classpath       <path>    ~/zlog/class
+				         path for logging class zephyrs                           
+     debug           on,off    on        whether debugging is enabled                             
+     debug_file      <path>    /var/tmp/owldebug
+					 path for logging debug messages when debugging is enabled
+     disable-ctrl-d  on,off    off       don't send zephyrs on C-d                                
+												  
+     displayoutgoing on,off    on        display outgoing messages                                
+     logging         on,off    off       turn personal logging on or off                          
+     loginsubs       on,off    on        load logins from .anyone on startup                      
+     logpath         <path>    ~/zlog/people
+					 path for logging personal zephyrs                        
+     personalbell    on,off    off       ring the terminal bell when personal messages are receive
+     rxping          on,off    off       display received pings                                   
+     shutdownlogout  on,off    on        send a logout message when owl exits                     
+     startuplogin    on,off    on        send a login message when owl starts                     
+     summarymode     on,off    off       [NOT YET IMPLEMENTED]                                    
+     txping          on,off    on        send pings                                               
+     typewinsize     int > 0   8         number of lines in the typing window                     
+     webbrowser  none,netscape,galeon netscape web browser to use to launch URLs             
+     zaway_msg         <string>  "I'm sorry..."
+					   current zaway msg for responding to zephyrs when away    
+     zaway_msg_default <string>  "I'm sorry..."
+				           default zaway msg for responding to zephyrs when away  
+     zsig              <string>            zephyr signature                                         
+     zsigproc          <path>    <null>    name of a program to run that will generate zsigs        
+
+
+================
+THE CONFIG FILE
+================
+
+*** WARNING: This interface may change substantially in the near future ***
+
+This file is interpreted by the perl interpreter.
+If you wish to execute an owl command use the
+function owl::command().  i.e.
+
+     owl::command("set zsigproc \"/mit/kretch/bin/getzsig foo\"");
+
+will set the owl variable zsigproc.  Note that commands will currently
+be executed in order after the called configuration subroutine exits.
+
+Subroutines created with the names below will be executed at the
+specified times:
+
+    subroutine name    properties
+    ---------------    ----------
+    owl::startup()     run when owl first starts
+    owl::shutdown()    run when owl exits
+    owl::format_msg()  run when a new message arrives, the return
+                          value is used to display the message on the
+                          screen
+    owl::receive_msg() run when a message is received, and after
+		       it has been added to the message list
+
+
+The following variables will be set each time a message is recevied:
+
+    $owl::class, $owl::instance, $owl::recipient,
+    $owl::sender, $owl::opcode, $owl::zsig,
+    $owl::msg, $owl::time, $owl::host, @owl::fields, $owl::id
+
+The "appendtosepbar" variable may be set in owl::format_msg()
+to set text to be appended to sepbar that separates the received
+message list from the edit window.
+
+
+===================
+FURTHER INFORMATION
+===================
Index: doc/owl.1
===================================================================
--- doc/owl.1 (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ doc/owl.1 (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,59 @@
+.TH owl 1 "21 May 2002"
+.SH NAME
+owl \- tty based zephyr client
+.SH SYNOPSIS
+.B owl
+[ \-n
+]
+[ \-c
+.I configfile
+]
+[ \-t
+.I tty
+]
+[ \-v
+]
+
+.br
+.SH DESCRIPTION
+.B Owl
+is a fully integrated tty based zephyr client for sending and
+receiving zephyrs.  It is curses-based, allows for emacs style editing
+of outgoing messages and uses a perl configuration language for
+setting options and customizing message formatting.  Owl will also run
+happily without a configuration file.
+
+Once owl is started typing 'h' will display a help screen.  Typing ':'
+enters command mode, allowing the user to type an owl command line.
+
+.PP
+.SH USE
+The following command line options are avilable when running owl:
+
+.B \-n
+.IP
+Do not subscribe to messages on startup.  By default owl subscribes to
+the default subscriptions and to anything found in ~/.zephyr.subs
+.LP
+
+.B \-c \fIconfigfile\fP
+.IP
+Specifiy an alternate config file for owl to use.  By default it looks
+for ~/.owlconf
+.LP
+
+.B \-t \fItty\fP
+.IP
+Specifiy the tty name to use for the zephyr location.
+.LP
+
+.B \-v
+.IP
+Print the version number of owl and exit.
+.LP
+
+.SH AUTHOR
+Written by James Kretchmar at the Massachusetts Institute of
+Technology.
+Comments, questions, and bug reports may be mailed to
+\fBbug-ktools@mit.edu\fP.
Index: editwin.c
===================================================================
--- editwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ editwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,812 @@
+#include "owl.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define INCR 5000
+
+void owl_editwin_init(owl_editwin *e, WINDOW *win, int winlines, int wincols, int style) {
+  /* initialize the editwin e.
+   * 'win' is an already initialzed curses window that will be used by editwin
+   */
+  e->buff=owl_malloc(INCR);
+  e->buff[0]='\0';
+  e->bufflen=0;
+  e->allocated=INCR;
+  e->buffx=0;
+  e->buffy=0;
+  e->topline=0;
+  e->winlines=winlines;
+  e->wincols=wincols;
+  if (wincols > 80) {
+    e->fillcol=80-9;
+  } else {
+    e->fillcol=wincols-9;
+  }
+  e->wrapcol=wincols-9;
+  e->curswin=win;
+  e->style=style;
+  if ((style!=OWL_EDITWIN_STYLE_MULTILINE) &&
+      (style!=OWL_EDITWIN_STYLE_ONELINE)) {
+    e->style=OWL_EDITWIN_STYLE_MULTILINE;
+  }
+  e->lock=0;
+  e->dotsend=0;
+  if (win) werase(win);
+}
+
+void owl_editwin_set_curswin(owl_editwin *e, WINDOW *w, int winlines, int wincols) {
+  e->curswin=w;
+  e->winlines=winlines;
+  e->wincols=wincols;
+  if (wincols > 80) {
+    e->fillcol=80-8;
+  } else {
+    e->fillcol=wincols-8;
+  }
+  e->wrapcol=wincols-9;
+}
+
+WINDOW *owl_editwin_get_curswin(owl_editwin *e) {
+  return e->curswin;
+}
+
+void owl_editwin_set_dotsend(owl_editwin *e) {
+  e->dotsend=1;
+}
+
+void owl_editwin_set_locktext(owl_editwin *e, char *text) {
+  /* set text to be 'locked in' at the beginning of the buffer, any
+     previous text (including locked text) will be overwritten */
+  
+  int x, y;
+
+  x=e->buffx;
+  y=e->buffy;
+  e->buffx=0;
+  e->buffy=0;
+  owl_editwin_overwrite_string(e, text);
+  e->lock=strlen(text);
+  /* if (text[e->lock-1]=='\n') e->lock--; */
+  e->buffx=x;
+  e->buffy=y;
+  owl_editwin_adjust_for_locktext(e);
+  owl_editwin_redisplay(e, 0);
+}
+
+int owl_editwin_get_style(owl_editwin *e) {
+  return(e->style);
+}
+
+void owl_editwin_new_style(owl_editwin *e, int newstyle) {
+  char *ptr;
+  
+  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 */
+    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;
+      }
+    }
+  }
+}
+
+void owl_editwin_fullclear(owl_editwin *e) {
+  /* completly reinitialize the buffer */
+  owl_free(e->buff);
+  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style);
+}
+
+void owl_editwin_clear(owl_editwin *e) {
+  /* clear all text except for locktext and put the cursor at the beginning */
+  int lock;
+  char *locktext=NULL;
+  
+  lock=0;
+  if (e->lock > 0) {
+    lock=1;
+
+    locktext=owl_malloc(e->lock+20);
+    strncpy(locktext, e->buff, e->lock);
+    locktext[e->lock]='\0';
+  }
+
+  owl_free(e->buff);
+  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style);
+
+  if (lock > 0) {
+    owl_editwin_set_locktext(e, locktext);
+  }
+
+  if (locktext) owl_free(locktext);
+  owl_editwin_adjust_for_locktext(e);
+}
+
+
+void _owl_editwin_addspace(owl_editwin *e) {
+  /* malloc more space for the buffer */
+  e->buff=owl_realloc(e->buff, e->allocated+INCR);
+  if (!e->buff) {
+    /* error */
+    return;
+  }
+  e->allocated+=INCR;
+}
+
+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);
+}
+
+void owl_editwin_redisplay(owl_editwin *e, int update) {
+  /* regenerate the text on the curses window */
+  /* if update == 1 then do a doupdate(), otherwise do not */
+  
+  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;
+    }
+    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;
+    }
+    ptr2=ptr3+1;
+  }
+  ptr3+=2;
+
+  buff=owl_malloc(ptr3-ptr1+50);
+  strncpy(buff, ptr1, ptr3-ptr1);
+  buff[ptr3-ptr1]='\0';
+  waddstr(e->curswin, buff);
+  wmove(e->curswin, e->buffy-e->topline, e->buffx);
+  wnoutrefresh(e->curswin);
+  if (update==1) {
+    doupdate();
+  }
+  owl_free(buff);
+}
+
+
+int _owl_editwin_linewrap_word(owl_editwin *e) {
+  /* linewrap the word just before the cursor.
+   * returns 0 on success
+   * returns -1 if we could not wrap.
+   */
+
+  int i, z;
+
+  z=_owl_editwin_get_index_from_xy(e);
+  /* move back and line wrap the previous word */
+  for (i=z-1; ; i--) {
+    /* move back until you find a space or hit the beginning of the line */
+    if (e->buff[i]==' ') {
+      /* replace the space with a newline */
+      e->buff[i]='\n';
+      e->buffy++;
+      e->buffx=z-i-1;
+      /* were we on the last line */
+      return(0);
+    } else if (e->buff[i]=='\n' || i<=e->lock) {
+      /* we hit the begginning of the line or the buffer, we cannot
+       * wrap.
+       */
+      return(-1);
+    }
+  }
+}
+
+void owl_editwin_insert_char(owl_editwin *e, char c) {
+  /* insert a character at the current point (shift later
+   * characters over) */
+  
+  int z, i, ret;
+
+  /* \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;
+  }
+
+  /* make sure there is enough memory for the new text */
+  if ((e->bufflen+1) > (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');
+    }
+  }
+
+  z=_owl_editwin_get_index_from_xy(e);
+  /* shift all the other characters right */
+  for (i=e->bufflen; i>z; i--) {
+    e->buff[i]=e->buff[i-1];
+  }
+
+  /* insert the new one */
+  e->buff[z]=c;
+
+  /* housekeeping */
+  e->bufflen++;
+  e->buff[e->bufflen]='\0';
+
+  /* advance the cursor */
+  if (c=='\n') {
+    e->buffx=0;
+    e->buffy++;
+  } else {
+    e->buffx++;
+  }
+}
+
+void owl_editwin_overwrite_char(owl_editwin *e, char c) {
+  /* overwrite the character at the current point with 'c' */
+  int z;
+  
+  /* \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;
+  }
+
+  z=_owl_editwin_get_index_from_xy(e);
+
+  /* only if we are at the end of the buffer do we create new space */
+  if (z==e->bufflen) {
+    if ((e->bufflen+1) > (e->allocated-5)) {
+      _owl_editwin_addspace(e);
+    }
+  }
+  
+  e->buff[z]=c;
+
+  /* housekeeping if we are at the end of the buffer */
+  if (z==e->bufflen) {
+    e->bufflen++;
+    e->buff[e->bufflen]='\0';
+  }
+
+  /* advance the cursor */
+  if (c=='\n') {
+    e->buffx=0;
+    e->buffy++;
+  } else {
+    e->buffx++;
+  }
+
+}
+
+void owl_editwin_delete_char(owl_editwin *e) {
+  /* delete the character at the current point, following chars
+   * shift left.
+   */ 
+  int z, i;
+
+  if (e->bufflen==0) return;
+  
+  /* get the deletion point */
+  z=_owl_editwin_get_index_from_xy(e);
+
+  if (z==e->bufflen) return;
+
+  for (i=z; i<e->bufflen; i++) {
+    e->buff[i]=e->buff[i+1];
+  }
+  e->bufflen--;
+  e->buff[e->bufflen]='\0';
+}
+
+void owl_editwin_insert_string(owl_editwin *e, char *string) {
+  /* insert 'string' at the current point, later text is shifted
+   * right
+   */
+  int i, j;
+
+  j=strlen(string);
+  for (i=0; i<j; i++) {
+    owl_editwin_insert_char(e, string[i]);
+  }
+}
+
+void owl_editwin_overwrite_string(owl_editwin *e, char *string) {
+  int i, j;
+  /* write 'string' at the current point, overwriting text that is
+   * already there
+   */
+
+  j=strlen(string);
+  for (i=0; i<j; i++) {
+    owl_editwin_overwrite_char(e, string[i]);
+  }
+}
+
+int _owl_editwin_get_index_from_xy(owl_editwin *e) {
+  /* get the index into e->buff for the current cursor
+   * position.
+   */
+  int i;
+  char *ptr1, *ptr2;
+
+  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 */
+      break;
+    }
+    ptr1=ptr2+1;
+  }
+
+  /* now go to the xth character */
+  ptr2=strchr(ptr1, '\n');
+  if (!ptr2) {
+    ptr2=e->buff+e->bufflen;
+  }
+
+  if ((ptr2-ptr1) < e->buffx) {
+    ptr1=ptr2-1;
+  } else {
+    ptr1+=e->buffx;
+  }
+
+  /* printf("DEBUG: index is %i\r\n", ptr1-e->buff); */
+  return(ptr1-e->buff);
+}
+
+void _owl_editwin_set_xy_by_index(owl_editwin *e, int index) {
+  int z, i;
+
+  z=_owl_editwin_get_index_from_xy(e);
+  if (index>z) {
+    for (i=0; i<index-z; i++) {
+      owl_editwin_key_right(e);
+    }
+  } else if (index<z) {
+    for (i=0; i<z-index; i++) {
+      owl_editwin_key_left(e);
+    }
+  }
+}
+
+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);
+  }
+}
+
+void owl_editwin_backspace(owl_editwin *e) {
+  /* delete the char before the current one
+   * and shift later chars left
+   */
+  if (_owl_editwin_get_index_from_xy(e) > e->lock) {
+    owl_editwin_key_left(e);
+    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_numchars_on_line(e, e->buffy)) {
+    e->buffx=owl_editwin_get_numchars_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);
+}
+
+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_numchars_on_line(e, e->buffy)) {
+    e->buffx=owl_editwin_get_numchars_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);
+}
+
+void owl_editwin_key_left(owl_editwin *e) {
+  /* move left if we can, and maybe up a line */
+  if (e->buffx>0) {
+    e->buffx--;
+  } else if (e->buffy>0) {
+    e->buffy--;
+    e->buffx=owl_editwin_get_numchars_on_line(e, e->buffy);
+  }
+
+  /* do we need to scroll up? */
+  if (e->buffy-e->topline < 0) {
+    e->topline-=e->winlines/2;
+  }
+
+  /* make sure to avoid locktext */
+  owl_editwin_adjust_for_locktext(e);
+}
+
+void owl_editwin_key_right(owl_editwin *e) {
+  int i;
+
+  /* move right if we can, and skip down a line if needed */
+  i=owl_editwin_get_numchars_on_line(e, e->buffy);
+  if (e->buffx < i) {
+    e->buffx++;
+    /*  } else if (e->buffy+1 < owl_editwin_get_numlines(e)) { */
+  } else if (_owl_editwin_get_index_from_xy(e) < e->bufflen) {
+    if (e->style==OWL_EDITWIN_STYLE_MULTILINE) {
+      e->buffx=0;
+      e->buffy++;
+    }
+  }
+
+  /* do we need to scroll down? */
+  if (e->buffy-e->topline >= e->winlines) {
+    e->topline+=e->winlines/2;
+  }
+}
+
+void owl_editwin_move_to_nextword(owl_editwin *e) {
+  int i, x;
+
+  /* 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;
+      }
+    }
+  }
+
+  /* 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_numchars_on_line(e, e->buffy);
+  if (e->buffx < i) {
+    /* move right till end of line */
+    while (e->buffx < i) {
+      e->buffx++;
+      if (e->buff[_owl_editwin_get_index_from_xy(e)]==' ') 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);
+      }
+    }
+  }
+}
+
+void owl_editwin_move_to_previousword(owl_editwin *e) {
+  /* go backwards to the last non-space character */
+  int i, x;
+
+  /* are we already at the beginning of the word? */
+  i=_owl_editwin_get_index_from_xy(e);
+  if ( (e->buff[i]!=' ' && e->buff[i]!='\n' && e->buff[i]!='\0') &&
+       (e->buff[i-1]==' ' || e->buff[i-1]=='\n') ) {
+    owl_editwin_key_left(e);
+  }
+    
+  /* are we starting on a space character? */
+  i=_owl_editwin_get_index_from_xy(e);
+  if (e->buff[i]==' ' || e->buff[i]=='\n' || e->buff[i]=='\0') {
+    /* find the first non-space */
+    for (x=i; x>=e->lock; x--) {
+      if (e->buff[x]!=' ' && e->buff[x]!='\n' && e->buff[x]!='\0') {
+	_owl_editwin_set_xy_by_index(e, x);
+	break;
+      }
+    }
+  }
+
+  /* find the last non-space */
+  i=_owl_editwin_get_index_from_xy(e);
+  for (x=i; x>=e->lock; x--) {
+    if (e->buff[x-1]==' ' || e->buff[x-1]=='\n') {
+      _owl_editwin_set_xy_by_index(e, x);
+      break;
+    }
+  }
+  _owl_editwin_set_xy_by_index(e, x);
+}
+
+
+void owl_editwin_delete_nextword(owl_editwin *e) {
+  int z;
+
+  if (e->bufflen==0) return;
+
+  /* if we start out on a space character then gobble all the spaces
+     up first */
+  while (1) {
+    z=_owl_editwin_get_index_from_xy(e);
+    if (e->buff[z]==' ' || e->buff[z]=='\n') {
+      owl_editwin_delete_char(e);
+    } else {
+      break;
+    }
+  }
+
+  /* then nuke the next word */
+  while (1) {
+    z=_owl_editwin_get_index_from_xy(e);
+    if (e->buff[z+1]==' ' || e->buff[z+1]=='\n' || e->buff[z+1]=='\0') break;
+    owl_editwin_delete_char(e);
+  }
+  owl_editwin_delete_char(e);
+}
+
+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_numchars_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_numchars_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;
+  }
+}
+
+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) break;
+
+    /* bail if we hit the end of the paragraph */
+    if (e->buff[i]=='\n' && e->buff[i+1]=='\n') break;
+
+    /* if we've travelled too far, linewrap */
+    if ((e->buffx) >= e->fillcol) {
+      _owl_editwin_linewrap_word(e);
+    }
+
+    /* did we hit the end of a line too soon? */
+    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 */
+	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;
+  }
+}
+
+int owl_editwin_check_dotsend(owl_editwin *e) {
+  int i;
+
+  if (!e->dotsend) return(0);
+  for (i=e->bufflen-1; i>0; i--) {
+    if (e->buff[i] == '.' 
+	&& (e->buff[i-1] == '\n' || e->buff[i-1] == '\r')
+	&& (e->buff[i+1] == '\n' || e->buff[i+1] == '\r')) {
+      e->bufflen = i;
+      e->buff[i] = '\0';
+      return(1);
+    }
+    if (e->buff[i] != '\r'
+	&& e->buff[i] != '\n'
+	&& e->buff[i] != ' ') {
+      return(0);
+    }
+  }
+  return(0);
+
+#if 0  /* old implementation */
+  if (e->bufflen>=2) {
+    if ((e->buff[e->bufflen-1]=='\n') &&
+	(e->buff[e->bufflen-2]=='.') &&
+	((e->bufflen==2) || (e->buff[e->bufflen-3]=='\n'))) {
+      e->buff[e->bufflen-2]='\0';
+      e->bufflen-=2;
+      owl_editwin_redisplay(e, 0);
+      return(1);
+    }
+  }
+  return(0);
+#endif
+}
+
+void owl_editwin_post_process_char(owl_editwin *e, int j) {
+  /* check if we need to scroll down */
+  if (e->buffy-e->topline >= e->winlines) {
+    e->topline+=e->winlines/2;
+  }
+  if ((j==13 || j==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, int j) {
+  if (j == ERR) return;
+  if (j>127 || ((j<32) && (j!=10) && (j!=13))) {
+    return;
+  } else {
+    owl_editwin_insert_char(e, j);
+  }
+}
+
+char *owl_editwin_get_text(owl_editwin *e) {
+  return(e->buff+e->lock);
+}
+
+int owl_editwin_get_numchars_on_line(owl_editwin *e, int line) {
+  int i;
+  char *ptr1, *ptr2;
+
+  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 go to the xth character */
+  ptr2=strchr(ptr1, '\n');
+  if (!ptr2) {
+    return(e->buff + e->bufflen - ptr1);
+  }
+  return(ptr2-ptr1); /* don't count the newline for now */
+}
+
+int owl_editwin_get_numlines(owl_editwin *e) {
+  return(owl_text_num_lines(e->buff));
+}
+
Index: examples/owlconf.erik
===================================================================
--- examples/owlconf.erik (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ examples/owlconf.erik (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,183 @@
+### The owlconf config file   -*- perl -*-  
+###  $Id$
+###
+### This file is interpreted by the perl interpreter.
+### If you wish to execute an owl command use the
+### function owl::command().  i.e.
+###
+###      owl::command("set zsigproc /mit/kretch/bin/getzsig");
+###
+### will set the owl variable zsigproc.  Subroutines created with
+### the names below will be executed at the specified times:
+###
+###     subroutine name    properties
+###     ---------------    ----------
+###     owl::startup()     run when owl first starts
+###     owl::shutdown()    run when owl exits
+###     owl::format_msg()  run when a new message arrives, the return
+###                           value is used to display the message on the
+###                           screen
+###     owl::receive_msg() run when a message is received, and after
+###	  	           it has been added to the message list
+###
+###
+### The following variables will be set each time a message is recevied:
+###
+###     $owl::class, $owl::instance, $owl::recipient,
+###     $owl::sender, $owl::opcode, $owl::zsig,
+###     $owl::msg, $owl::time, $owl::host, @owl::fields, $owl::id
+###    
+
+# tokens for sepbar are:
+#    .username = ping
+#    >username = login
+#    <username = logout
+#    :username = personal message
+#    M         = mail received
+my @sepbartokens = ();
+
+
+# adds a sepbartoken and also updates the appendtosepbar variable
+sub sepbartokens_add {
+    my ($token) = @_;
+    $token =~ s/"//g;  # "
+    unshift @sepbartokens, $token;
+    pop @sepbartokens if (@sepbartokens > 80);
+    sepbartokens_set();
+}
+
+# trims a sepbartoken from the list also updates the appendtosepbar variable
+sub sepbartokens_trim {
+    my ($token) = @_;
+    @sepbartokens = map { if ($_ ne $token) { $_; } else { (); } } @sepbartokens;
+    sepbartokens_set();
+}
+
+# trims a sepbartoken from the list also updates the appendtosepbar variable
+sub sepbartokens_set {
+    owl::command(sprintf "set -q appendtosepbar \"%s\"", (join " ", @sepbartokens));
+}
+
+sub owl::startup {
+    owl::command("set -q logging off");
+    owl::command("set -q zsigproc /home/nygren/bin/owlzsigs");
+    owl::command("set -q startuplogin off");
+    owl::command("set -q shutdownlogout off");
+    #owl::command("set personalbell on");
+    owl::command("set -q rxping on");
+    owl::command("set -q typewinsize 5");
+    if ($ENV{"DISPLAY"} eq ":0") {
+      owl::command("set -q webbrowser galeon");	
+    }
+    owl::command("filter me recipient ".$ENV{"USER"});
+    owl::command("filter help class help");
+    owl::command("filter quiet not ( class ^greed|geek|help|login$ or instance white-magic )");    
+
+    owl::command("alias z zwrite");
+    owl::command("alias zc zwrite -c");
+
+    owl::command("bindkey editmulti \"C-c C-c\" command editmulti:done");
+    owl::command("bindkey recv M-k command ( smartnarrow ; delete view )");
+    owl::command("bindkey recv M-l command ( expunge ; view all )");
+    owl::command("bindkey recv M-K command ( smartnarrow ; delete view ; expunge ; view all )");
+    owl::command(q(bindkey edit M-s command perl owl::command("edit:insert-text <".("scritch"x int(1+rand(5))).">")));
+
+}
+
+sub owl::shutdown {
+#    not doing anything at the moment...
+}
+
+# run to format a message
+sub owl::format_msg {
+    my $out, $tmp;
+
+    $owl::sender=~s/\@ATHENA\.MIT\.EDU$//;
+    $owl::sender=~s/\@local-realm$//;
+
+    if (uc($owl::opcode) eq "PING") {
+	return("\@bold(PING) from \@bold($owl::sender)\n");
+    } elsif (uc($owl::class) eq "LOGIN") {
+	if (uc($owl::opcode) eq "USER_LOGIN") {
+	    $out="\@bold(LOGIN)";
+	} elsif (uc($owl::opcode) eq "USER_LOGOUT") {
+	    $out="\@bold(LOGOUT)";
+	} else {
+	    $out="\@bold(UNKNOWN)";
+	}
+	$out.=" for \@bold($owl::sender) at $fields[0] on $fields[2]\n";
+	return($out);
+    } elsif (uc($owl::class) eq "MAIL" and uc($owl::instance) eq "INBOX") {
+	$out = "\@bold(MAIL) ";
+	if ($owl::msg =~ /^From:\s+(.+)\s*$/m) { $out .= "From $1 "; }
+	if ($owl::msg =~ /^To:\s+(.+)\s*$/m) { $out .= "To $1 "; }
+	if ($owl::msg =~ /^Subject:\s+(.+)\s*$/m) { $out .= "Subject $1 "; }
+	return($out."\n");
+    }
+
+    $out = sprintf "[mit,%s,%s] / %s / %s", lc($owl::class), 
+                   lc($owl::instance), $owl::time, lc($owl::host);
+    if ($owl::opcode ne "") {$out.=" op:$owl::opcode";}
+    $out.="\n";
+    $out.= "  \@bold($owl::sender)> ";
+    if ($owl::zsig ne "") {
+	my $zsig = $owl::zsig;
+	$zsig =~ s/(\n.*)+$/ [...]/;
+	if (length($zsig)+5+length($owl::sender) > 70) {
+	    $out.="# ...";
+	} else {
+	    $out.="# $zsig";
+	}
+    }
+    $out.="\n";		
+
+    # indent it
+    $tmp=$owl::msg;
+    $tmp=~s/^/    /g;
+    $tmp=~s/\n/\n    /g;
+    $out.=$tmp;
+
+    # make personal messages bold
+    if (uc($owl::class) eq "MESSAGE" &&
+	uc($owl::instance) eq "PERSONAL") {
+	$out="\@bold{".$out."}";
+    }
+
+    return($out."\n");
+}
+
+# run when a message is received, and after
+# it has been added to the message list.
+sub owl::receive_msg() {
+    my $out, $tmp;
+
+    $owl::sender=~s/\@ATHENA\.MIT\.EDU$//;
+    $owl::sender=~s/\@local-realm$//;
+
+    if (uc($owl::opcode) eq "PING") {
+	sepbartokens_add(".$owl::sender");
+        owl::command("delete -id $owl::id");
+    } elsif (uc($owl::class) eq "LOGIN") {
+        owl::command("delete -id $owl::id");
+	if (uc($owl::opcode) eq "USER_LOGIN") {
+	    sepbartokens_add(">$owl::sender");
+	    sepbartokens_trim("<$owl::sender");
+	} elsif (uc($owl::opcode) eq "USER_LOGOUT") {
+	    sepbartokens_add("<$owl::sender");
+	    sepbartokens_trim(">$owl::sender");
+	} 
+    } elsif (uc($owl::class) eq "MAIL") {
+        owl::command("delete -id $owl::id");
+	sepbartokens_add("M");
+    }
+
+
+    # make personal messages bold
+    if (uc($owl::class) eq "MESSAGE" &&
+	uc($owl::instance) eq "PERSONAL") {
+	sepbartokens_trim(".$owl::sender");
+	sepbartokens_add(":$owl::sender");
+    }
+
+    return 1;
+}
Index: examples/owlconf.vtformat
===================================================================
--- examples/owlconf.vtformat (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ examples/owlconf.vtformat (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,141 @@
+### The owlconf config file   -*- perl -*-  
+###  $Id$
+###
+### This file is interpreted by the perl interpreter.
+### If you wish to execute an owl command use the
+### function owl::command().  i.e.
+###
+###      owl::command("set zsigproc /mit/kretch/bin/getzsig");
+###
+### will set the owl variable zsigproc.  Subroutines created with
+### the names below will be executed at the specified times:
+###
+###     subroutine name    properties
+###     ---------------    ----------
+###     owl::startup()     run when owl first starts
+###     owl::shutdown()    run when owl exits
+###     owl::format_msg()  run when a new message arrives, the return
+###                           value is used to display the message on the
+###                           screen
+###
+### The following variables will be set each time a message is recevied:
+###
+###     $owl::class, $owl::instance, $owl::recipient,
+###     $owl::sender, $owl::opcode, $owl::zsig,
+###     $owl::msg, $owl::time, $owl::host, @owl::fields
+###    
+
+# classes will be appreviated this way
+my %class_abbrev = (
+		   "message"         => "",
+		   "spleen"          => "sp",
+		   "ccare"           => "cc",
+		   "implied-consent" => "ic",
+		   "syseng"          => "se",
+		   "install"         => "i",
+		   );
+
+sub owl::startup {
+    owl::command("set logging off");
+    #owl::command("set zsigproc /home/nygren/bin/owlzsigs");
+    owl::command("set startuplogin off");
+    owl::command("set shutdownlogout off");
+    #owl::command("set personalbell on");
+    owl::command("set rxping on");
+    owl::command("set typewinsize 5");
+}
+
+sub owl::shutdown {
+#    system("/mit/kretch/bin/zkill > /dev/null 2> /dev/null");
+}
+
+sub owl::format_msg {
+    my ($out, $tmp);
+
+    $owl::sender=~s/\@ATHENA\.MIT\.EDU$//;
+    $owl::sender=~s/\@local-realm$//;
+
+    if (uc($owl::opcode) eq "PING") {
+	return("\@bold(PING) from \@bold($owl::sender)\n");
+    } elsif (uc($owl::class) eq "LOGIN") {
+	if (uc($owl::opcode) eq "USER_LOGIN") {
+	    $out="\@bold(LOGIN)";
+	} elsif (uc($owl::opcode) eq "USER_LOGOUT") {
+	    $out="\@bold(LOGOUT)";
+	} else {
+	    $out="\@bold(UNKNOWN)";
+	}
+	$out.=" for \@bold($owl::sender) at $fields[0] on $fields[2]\n";
+	return($out);
+    } elsif (uc($owl::class) eq "MAIL") {
+	$out = "\@bold(MAIL) ";
+	if ($owl::msg =~ /^From:\s+(.+)\s*$/m) { $out .= "From $1 "; }
+	if ($owl::msg =~ /^To:\s+(.+)\s*$/m) { $out .= "To $1 "; }
+	if ($owl::msg =~ /^Subject:\s+(.+)\s*$/m) { $out .= "Subject $1 "; }
+	return($out."\n");
+    }
+
+    my $channel = $owl::class;
+    if (defined $class_abbrev{lc($owl::class)}) {
+	$channel = $class_abbrev{lc($owl::class)};
+    }
+    if (lc($owl::class) ne "message" and lc($owl::instance) eq "personal") {
+	$owl::instance = "";
+    }
+    $channel .= "[".$owl::instance."]";
+    $channel = substr($channel,0,13);
+
+    $header = sprintf "%-8s %-13s ", lc($owl::sender), lc($channel);
+
+    if ($owl::msg =~ /=/) {
+	# indent it
+	$tmp=$owl::msg;
+	$tmp=~s/^/    /g;
+	$tmp=~s/\n/\n    /g;
+	$out.=$header."\n".$tmp;
+    } else {
+	# fill it
+	my $pagewidth = 74;
+	$out .= $header;
+	$out .= fill_text($owl::msg, $pagewidth, 22, 1);
+    }
+
+    # note: no zsig added in this version
+
+    # make personal messages bold
+    if (uc($owl::class) eq "MESSAGE" &&
+	uc($owl::instance) eq "PERSONAL") {
+	$out="\@bold{".$out."}\n";
+    }
+
+    return($out);
+}
+
+
+
+sub fill_text {
+    my ($in, $width, $indent, $unindent_first) = @_;
+    $indent = 0 if (@_ < 3);
+    my $unindent = $indent if ($unindent_first);
+    my @words = split " ", $in;
+    my $out = "";
+    my $outline = "";
+    if (!$unindent_first) {
+	my $outline = " "x$indent;
+    }
+    for my $w (@words) {
+	if (($outline ne "") 
+	    && (length($outline)+length($w) > $width-$unindent)) {
+	    $out .= $outline."\n";
+	    $outline = " "x$indent;
+	    $unindent = 0;
+	}
+	if ($outline =~ /.*\.$/) {
+	    $outline .= "  ";
+	} elsif ($outline ne "") {
+	    $outline .= " ";
+	}
+	$outline .= $w;
+    }
+    $out .= $outline . "\n";    
+}
Index: filter.c
===================================================================
--- filter.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ filter.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,353 @@
+#include <string.h>
+#include "owl.h"
+
+int owl_filter_init_fromstring(owl_filter *f, char *name, char *string) {
+  char **argv;
+  int argc, out;
+
+  argv=owl_parseline(string, &argc);
+  out=owl_filter_init(f, name, argc, argv);
+  /* owl_parsefree(argv, argc); */
+  return(out);
+}
+
+int owl_filter_init(owl_filter *f, char *name, int argc, char **argv) {
+  int i, error;
+  owl_filterelement *fe;
+   
+  f->name=owl_strdup(name);
+  f->polarity=0;
+  f->color=OWL_COLOR_DEFAULT;
+  owl_list_create(&(f->fes));
+
+  /* first take arguments that have to come first */
+  /* set the color */
+  if (argc>=2 && !strcmp(argv[0], "-c")) {
+    f->color=owl_util_string_to_color(argv[1]);
+    argc-=2;
+    argv+=2;
+  }
+
+  /* then deal with the expression */
+  for (i=0; i<argc; i++) {
+    error=0;
+    fe=owl_malloc(sizeof(owl_filterelement));
+
+    /* all the 0 argument possibilities */
+    if (!strcmp(argv[i], "(")) {
+      owl_filterelement_create_openbrace(fe);
+    } else if (!strcmp(argv[i], ")")) {
+      owl_filterelement_create_closebrace(fe);
+    } else if (!strcasecmp(argv[i], "and")) {
+      owl_filterelement_create_and(fe);
+    } else if (!strcasecmp(argv[i], "or")) {
+      owl_filterelement_create_or(fe);
+    } else if (!strcasecmp(argv[i], "not")) {
+      owl_filterelement_create_not(fe);
+
+    } else if (i==argc-1) {
+      error=1;
+    } else {
+      if (!strcasecmp(argv[i], "class") ||
+	  !strcasecmp(argv[i], "instance") ||
+	  !strcasecmp(argv[i], "sender") ||
+	  !strcasecmp(argv[i], "recipient") ||
+	  !strcasecmp(argv[i], "opcode") ||
+	  !strcasecmp(argv[i], "realm") ||
+	  !strcasecmp(argv[i], "type")) {
+	owl_filterelement_create_re(fe, argv[i], argv[i+1]);
+	i++;
+      } else {
+	error=1;
+      }
+    }
+
+    if (!error) {
+      owl_list_append_element(&(f->fes), fe);
+    } else {
+      owl_free(fe);
+      owl_filter_free(f);
+      return(-1);
+    }
+
+  }
+  return(0);
+}
+
+char *owl_filter_get_name(owl_filter *f) {
+  return(f->name);
+}
+
+void owl_filter_set_polarity_match(owl_filter *f) {
+  f->polarity=0;
+}
+
+void owl_filter_set_polarity_unmatch(owl_filter *f) {
+  f->polarity=1;
+}
+
+void owl_filter_set_color(owl_filter *f, int color) {
+  f->color=color;
+}
+
+int owl_filter_get_color(owl_filter *f) {
+  return(f->color);
+}
+
+int owl_filter_message_match(owl_filter *f, owl_message *m) {
+  int i, j, tmp;
+  owl_list work_fes, *fes;
+  owl_filterelement *fe;
+  char *field, *match;
+
+  /* create the working list */
+  fes=&(f->fes);
+  owl_list_create(&work_fes);
+  j=owl_list_get_size(fes);
+  for (i=0; i<j; i++) {
+    owl_list_append_element(&work_fes, owl_list_get_element(fes, i));
+  }
+
+  /* first go thru and turn all RE elements into true or false */
+  match="";
+  for (i=0; i<j; i++) {
+    fe=owl_list_get_element(&work_fes, i);
+    if (!owl_filterelement_is_re(fe)) continue;
+    field=owl_filterelement_get_field(fe);
+    if (!strcasecmp(field, "class")) {
+      match=owl_message_get_class(m);
+    } else if (!strcasecmp(field, "instance")) {
+      match=owl_message_get_instance(m);
+    } else if (!strcasecmp(field, "sender")) {
+      match=owl_message_get_sender(m);
+    } else if (!strcasecmp(field, "recipient")) {
+      match=owl_message_get_recipient(m);
+    } else if (!strcasecmp(field, "opcode")) {
+      match=owl_message_get_opcode(m);
+    } else if (!strcasecmp(field, "realm")) {
+      match=owl_message_get_realm(m);
+    } else if (!strcasecmp(field, "type")) {
+      if (owl_message_is_zephyr(m)) {
+	match="zephyr";
+      } else if (owl_message_is_admin(m)) {
+	match="admin";
+      } else {
+	match="";
+      }
+    }
+    
+    tmp=owl_regex_compare(owl_filterelement_get_re(fe), match);
+    if (!tmp) {
+      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_true(&g));
+    } else {
+      owl_list_replace_element(&work_fes, i, owl_global_get_filterelement_false(&g));
+    }
+  }
+
+  
+  /* call the recursive helper */
+  i=_owl_filter_message_match_recurse(f, m, &work_fes, 0, owl_list_get_size(&(f->fes))-1);
+
+  tmp=0;
+  /* now we should have one value */
+  for (i=0; i<j; i++) {
+    fe=owl_list_get_element(&work_fes, i);
+    if (owl_filterelement_is_null(fe)) continue;
+    if (owl_filterelement_is_true(fe)) {
+      tmp=1;
+      break;
+    }
+    if (owl_filterelement_is_false(fe)) {
+      tmp=0;
+      break;
+    }
+  }  
+
+  if (f->polarity) {
+    tmp=!tmp;
+  }
+  owl_list_free_simple(&work_fes);
+  return(tmp);
+}
+
+int _owl_filter_message_match_recurse(owl_filter *f, owl_message *m, owl_list *fes, int start, int end) {
+  int a=0, b=0, i, x, y, z, score, ret, type;
+  owl_filterelement *fe, *tmpfe=NULL;
+
+  /* deal with parens first */
+  for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
+    score=x=y=0;
+    for (i=start; i<=end; i++) {
+      fe=owl_list_get_element(fes, i);
+      if (owl_filterelement_is_openbrace(fe)) {
+	if (score==0) x=i;
+	score++;
+      } else if (owl_filterelement_is_closebrace(fe)) {
+	score--;
+	if (score<0) {
+	  /* unblanaced parens */
+	  return(-1);
+	} else if (score==0) {
+	  y=i; /* this is the matching close paren */
+	  break;
+	}
+      }
+    }
+    if (score>0) {
+      /* unblanaced parens */
+      return(-1);
+    }
+
+    if (y>0) {
+      /* null out the parens */
+      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
+      owl_list_replace_element(fes, y, owl_global_get_filterelement_null(&g));
+
+      /* simplify the part that was in between */
+      ret=_owl_filter_message_match_recurse(f, m, fes, x+1, y-1);
+      if (ret<0) return(-1);
+
+      /* there may be more, so we continue */
+      continue;
+    } else {
+      /* otherwise we're done with this part */
+      break;
+    }
+  }
+  if (i==OWL_FILTER_MAX_DEPTH) {
+    return(-1);
+  }
+
+  /* and / or / not */
+  for (i=0; i<OWL_FILTER_MAX_DEPTH; i++) {
+    type=score=x=y=z=0;
+    for (i=start; i<=end; i++) {
+      fe=owl_list_get_element(fes, i);
+      if (owl_filterelement_is_null(fe)) continue;
+      if (score==0) {
+	if (owl_filterelement_is_value(fe)) {
+	  x=i;
+	  score=1;
+	  type=1;
+	} else if (owl_filterelement_is_not(fe)) {
+	  x=i;
+	  score=1;
+	  type=2;
+	}
+      } else if (score==1) {
+	if (type==1) {
+	  if (owl_filterelement_is_and(fe) || owl_filterelement_is_or(fe)) {
+	    score=2;
+	    y=i;
+	  } else {
+	    x=y=z=score=0;
+	  }
+	} else if (type==2) {
+	  if (owl_filterelement_is_value(fe)) {
+	    /* it's a valid "NOT expr" */
+	    y=i;
+	    break;
+	  }
+	}
+      } else if (score==2) {
+	if (owl_filterelement_is_value(fe)) {
+	  /* yes, it's a good match */
+	  z=i;
+	  break;
+	} else {
+	  x=y=z=score=0;
+	}
+      }
+    }
+
+    /* process and / or */
+    if ((type==1) && (z>0)) {
+      fe=owl_list_get_element(fes, x);
+      if (owl_filterelement_is_true(fe)) {
+	a=1;
+      } else if (owl_filterelement_is_false(fe)) {
+	a=0;
+      }
+
+      fe=owl_list_get_element(fes, z);
+      if (owl_filterelement_is_true(fe)) {
+	b=1;
+      } else if (owl_filterelement_is_false(fe)) {
+	b=0;
+      }
+
+      fe=owl_list_get_element(fes, y);
+      if (owl_filterelement_is_and(fe)) {
+	if (a && b) {
+	  tmpfe=owl_global_get_filterelement_true(&g);
+	} else {
+	  tmpfe=owl_global_get_filterelement_false(&g);
+	}
+      } else if (owl_filterelement_is_or(fe)) {
+	if (a || b) {
+	  tmpfe=owl_global_get_filterelement_true(&g);
+	} else {
+	  tmpfe=owl_global_get_filterelement_false(&g);
+	}
+      }
+      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
+      owl_list_replace_element(fes, y, tmpfe);
+      owl_list_replace_element(fes, z, owl_global_get_filterelement_null(&g));
+    } else if ((type==2) && (y>0)) { /* process NOT */
+      fe=owl_list_get_element(fes, y);
+      owl_list_replace_element(fes, x, owl_global_get_filterelement_null(&g));
+      if (owl_filterelement_is_false(fe)) {
+	owl_list_replace_element(fes, y, owl_global_get_filterelement_true(&g));
+      } else {
+	owl_list_replace_element(fes, y, owl_global_get_filterelement_false(&g));
+      }
+    } else {
+      break;
+    }
+  }
+  return(0);
+
+}
+
+void owl_filter_print(owl_filter *f, char *out) {
+  int i, j;
+  owl_filterelement *fe;
+  char *tmp;
+
+  strcpy(out, owl_filter_get_name(f));
+  strcat(out, ": ");
+
+  if (f->color!=OWL_COLOR_DEFAULT) {
+    strcat(out, "-c ");
+    strcat(out, owl_util_color_to_string(f->color));
+    strcat(out, " ");
+  }
+
+  j=owl_list_get_size(&(f->fes));
+  for (i=0; i<j; i++) {
+    fe=owl_list_get_element(&(f->fes), i);
+    tmp=owl_filterelement_to_string(fe);
+    strcat(out, tmp);
+    owl_free(tmp);
+  }
+  strcat(out, "\n");
+}
+
+int owl_filter_equiv(owl_filter *a, owl_filter *b) {
+  char buff[LINE], buff2[LINE];
+
+  owl_filter_print(a, buff);
+  owl_filter_print(b, buff2);
+
+  if (!strcmp(buff, buff2)) return(1);
+  return(0);
+}
+
+void owl_filter_free(owl_filter *f) {
+  void (*func)();
+
+  func=&owl_filterelement_free;
+  
+  if (f->name) owl_free(f->name);
+  owl_list_free_all(&(f->fes), func);
+}
Index: filterelement.c
===================================================================
--- filterelement.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ filterelement.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,151 @@
+#include "owl.h"
+
+#define OWL_FILTERELEMENT_NULL        0
+#define OWL_FILTERELEMENT_TRUE        1
+#define OWL_FILTERELEMENT_FALSE       2
+#define OWL_FILTERELEMENT_OPENBRACE   3
+#define OWL_FILTERELEMENT_CLOSEBRACE  4
+#define OWL_FILTERELEMENT_AND         5
+#define OWL_FILTERELEMENT_OR          6
+#define OWL_FILTERELEMENT_NOT         7
+#define OWL_FILTERELEMENT_RE          8
+
+
+void owl_filterelement_create_null(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_NULL;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_openbrace(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_OPENBRACE;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_closebrace(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_CLOSEBRACE;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_and(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_AND;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_or(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_OR;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_not(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_NOT;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_true(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_TRUE;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_false(owl_filterelement *fe) {
+  fe->type=OWL_FILTERELEMENT_FALSE;
+  fe->field=NULL;
+}
+
+void owl_filterelement_create_re(owl_filterelement *fe, char *field, char *re) {
+  fe->type=OWL_FILTERELEMENT_RE;
+  fe->field=owl_strdup(field);
+  owl_regex_create(&(fe->re), re);
+}
+
+void owl_filterelement_free(owl_filterelement *fe) {
+  if (fe->field) owl_free(fe->field);
+}
+
+int owl_filterelement_is_null(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_NULL) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_openbrace(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_OPENBRACE) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_closebrace(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_CLOSEBRACE) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_and(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_AND) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_or(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_OR) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_not(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_NOT) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_true(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_TRUE) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_false(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_FALSE) return(1);
+  return(0);
+}
+
+int owl_filterelement_is_re(owl_filterelement *fe) {
+  if (fe->type==OWL_FILTERELEMENT_RE) return(1);
+  return(0);
+}
+
+owl_regex *owl_filterelement_get_re(owl_filterelement *fe) {
+  return(&(fe->re));
+}
+
+char *owl_filterelement_get_field(owl_filterelement *fe) {
+  return(fe->field);
+}
+
+int owl_filterelement_is_value(owl_filterelement *fe) {
+  if ( (fe->type==OWL_FILTERELEMENT_TRUE) ||
+       (fe->type==OWL_FILTERELEMENT_FALSE) ||
+       (fe->type==OWL_FILTERELEMENT_RE) ) {
+    return(1);
+  }
+  return(0);
+}
+
+
+char *owl_filterelement_to_string(owl_filterelement *fe) {
+  /* return must be freed by caller */
+  
+  if (owl_filterelement_is_openbrace(fe)) {
+    return(owl_strdup("( "));
+  } else if (owl_filterelement_is_closebrace(fe)) {
+    return(owl_strdup(") "));
+  } else if (owl_filterelement_is_and(fe)) {
+    return(owl_strdup("and "));
+  } else if (owl_filterelement_is_or(fe)) {
+    return(owl_strdup("or "));
+  } else if (owl_filterelement_is_not(fe)) {
+    return(owl_strdup("not "));
+  } else if (owl_filterelement_is_true(fe)) {
+    return(owl_strdup("true "));
+  } else if (owl_filterelement_is_false(fe)) {
+    return(owl_strdup("false "));
+  } else if (owl_filterelement_is_re(fe)) {
+    char *buff;
+    buff=owl_malloc(LINE);
+    sprintf(buff, "%s %s ", fe->field, owl_regex_get_string(&(fe->re)));
+    return(buff);
+  }
+  return(owl_strdup(""));
+}
Index: fmtext.c
===================================================================
--- fmtext.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ fmtext.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,499 @@
+#include "owl.h"
+#include <stdlib.h>
+#include <string.h>
+
+void owl_fmtext_init_null(owl_fmtext *f) {
+  f->textlen=0;
+  f->textbuff=owl_strdup("");
+  f->fmbuff=owl_malloc(5);
+  f->colorbuff=owl_malloc(5);
+  f->fmbuff[0]=OWL_FMTEXT_ATTR_NONE;
+  f->colorbuff[0]=OWL_COLOR_DEFAULT;
+}
+
+
+void _owl_fmtext_set_attr(owl_fmtext *f, int attr, int first, int last) {
+  int i;
+  for (i=first; i<=last; i++) {
+    f->fmbuff[i]=(unsigned char) attr;
+  }
+}
+
+void _owl_fmtext_set_color(owl_fmtext *f, int color, int first, int last) {
+  int i;
+  for (i=first; i<=last; i++) {
+    f->colorbuff[i]=(unsigned char) color;
+  }
+}
+
+
+void owl_fmtext_append_attr(owl_fmtext *f, char *text, int attr, int color) {
+  int newlen;
+
+  newlen=strlen(f->textbuff)+strlen(text);
+  f->textbuff=owl_realloc(f->textbuff, newlen+2);
+  f->fmbuff=owl_realloc(f->fmbuff, newlen+2);
+  f->colorbuff=owl_realloc(f->colorbuff, newlen+2);
+
+  strcat(f->textbuff, text);
+  _owl_fmtext_set_attr(f, attr, f->textlen, newlen);
+  _owl_fmtext_set_color(f, color, f->textlen, newlen);
+  f->textlen=newlen;
+}
+
+
+void owl_fmtext_append_normal(owl_fmtext *f, char *text) {
+  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT);
+}
+
+void owl_fmtext_append_normal_color(owl_fmtext *f, char *text, int color) {
+  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, color);
+}
+
+
+void owl_fmtext_append_bold(owl_fmtext *f, char *text) {
+  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT);
+}
+
+
+void owl_fmtext_append_reverse(owl_fmtext *f, char *text) {
+  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE, OWL_COLOR_DEFAULT);
+}
+
+
+void owl_fmtext_append_reversebold(owl_fmtext *f, char *text) {
+  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE | OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT);
+}
+
+
+void owl_fmtext_addattr(owl_fmtext *f, int attr) {
+  /* add the attribute to all text */
+  int i, j;
+
+  j=f->textlen;
+  for (i=0; i<j; i++) {
+    f->fmbuff[i] |= attr;
+  }
+}
+
+void owl_fmtext_colorize(owl_fmtext *f, int color) {
+  /* everywhere the color is OWL_COLOR_DEFAULT, change it to be 'color' */
+  int i, j;
+
+  j=f->textlen;
+  for(i=0; i<j; i++) {
+    if (f->colorbuff[i]==OWL_COLOR_DEFAULT) f->colorbuff[i] = color;
+  }
+}
+
+
+void owl_fmtext_append_ztext(owl_fmtext *f, char *text) {
+  int stacksize, curattrs, curcolor;
+  char *ptr, *txtptr, *buff, *tmpptr;
+  int attrstack[32], chrstack[32];
+
+  curattrs=OWL_FMTEXT_ATTR_NONE;
+  curcolor=OWL_COLOR_DEFAULT;
+  stacksize=0;
+  txtptr=text;
+  while (1) {
+    ptr=strpbrk(txtptr, "@{[<()>]}");
+    if (!ptr) {
+      /* add all the rest of the text and exit */
+      owl_fmtext_append_attr(f, txtptr, curattrs, curcolor);
+      return;
+    } else if (ptr[0]=='@') {
+      /* add the text up to this point then deal with the stack */
+      buff=owl_malloc(ptr-txtptr+20);
+      strncpy(buff, txtptr, ptr-txtptr);
+      buff[ptr-txtptr]='\0';
+      owl_fmtext_append_attr(f, buff, curattrs, curcolor);
+      owl_free(buff);
+
+      /* update pointer to point at the @ */
+      txtptr=ptr;
+
+      /* now the stack */
+
+      /* if we've hit our max stack depth, print the @ and move on */
+      if (stacksize==32) {
+	owl_fmtext_append_attr(f, "@", curattrs, curcolor);
+	txtptr++;
+	continue;
+      }
+
+      /* if it's an @@, print an @ and continue */
+      if (txtptr[1]=='@') {
+	owl_fmtext_append_attr(f, "@", curattrs, curcolor);
+	txtptr+=2;
+	continue;
+      }
+	
+      /* if there's no opener, print the @ and continue */
+      tmpptr=strpbrk(txtptr, "(<[{ ");
+      if (!tmpptr || tmpptr[0]==' ') {
+	owl_fmtext_append_attr(f, "@", curattrs, curcolor);
+	txtptr++;
+	continue;
+      }
+
+      /* check what command we've got, push it on the stack, start
+	 using it, and continue ... unless it's a color command */
+      buff=owl_malloc(tmpptr-ptr+20);
+      strncpy(buff, ptr, tmpptr-ptr);
+      buff[tmpptr-ptr]='\0';
+      if (!strcasecmp(buff, "@bold")) {
+	attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
+	chrstack[stacksize]=tmpptr[0];
+	stacksize++;
+	curattrs|=OWL_FMTEXT_ATTR_BOLD;
+	txtptr+=6;
+	owl_free(buff);
+	continue;
+      } else if (!strcasecmp(buff, "@b")) {
+	attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
+	chrstack[stacksize]=tmpptr[0];
+	stacksize++;
+	curattrs|=OWL_FMTEXT_ATTR_BOLD;
+	txtptr+=3;
+	owl_free(buff);
+	continue;
+      } else if (!strcasecmp(buff, "@i")) {
+	attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
+	chrstack[stacksize]=tmpptr[0];
+	stacksize++;
+	curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
+	txtptr+=3;
+	owl_free(buff);
+	continue;
+      } else if (!strcasecmp(buff, "@italic")) {
+	attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
+	chrstack[stacksize]=tmpptr[0];
+	stacksize++;
+	curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
+	txtptr+=8;
+	owl_free(buff);
+	continue;
+
+	/* if it's a color read the color, set the current color and
+           continue */
+      } else if (!strcasecmp(buff, "@color") && owl_global_get_hascolors(&g)) {
+	owl_free(buff);
+	txtptr+=7;
+	tmpptr=strpbrk(txtptr, "@{[<()>]}");
+	if (tmpptr &&
+	    ((txtptr[-1]=='(' && tmpptr[0]==')') ||
+	     (txtptr[-1]=='<' && tmpptr[0]=='>') ||
+	     (txtptr[-1]=='[' && tmpptr[0]==']') ||
+	     (txtptr[-1]=='{' && tmpptr[0]=='}'))) {
+
+	  /* grab the color name */
+	  buff=owl_malloc(tmpptr-txtptr+20);
+	  strncpy(buff, txtptr, tmpptr-txtptr);
+	  buff[tmpptr-txtptr]='\0';
+
+	  /* set it as the current color */
+	  curcolor=owl_util_string_to_color(buff);
+	  owl_free(buff);
+	  txtptr=tmpptr+1;
+	  continue;
+
+	} else {
+
+	}
+
+      } else {
+	/* if we didn't understand it, we'll print it.  This is different from zwgc
+	   but zwgc seems to be smarter about some screw cases than I am */
+	owl_fmtext_append_attr(f, "@", curattrs, curcolor);
+	txtptr++;
+	continue;
+      }
+
+    } else if (ptr[0]=='}' || ptr[0]==']' || ptr[0]==')' || ptr[0]=='>') {
+      /* add the text up to this point first */
+      buff=owl_malloc(ptr-txtptr+20);
+      strncpy(buff, txtptr, ptr-txtptr);
+      buff[ptr-txtptr]='\0';
+      owl_fmtext_append_attr(f, buff, curattrs, curcolor);
+      owl_free(buff);
+
+      /* now deal with the closer */
+      txtptr=ptr;
+
+      /* first, if the stack is empty we must bail (just print and go) */
+      if (stacksize==0) {
+	buff=owl_malloc(5);
+	buff[0]=ptr[0];
+	buff[1]='\0';
+	owl_fmtext_append_attr(f, buff, curattrs, curcolor);
+	owl_free(buff);
+	txtptr++;
+	continue;
+      }
+
+      /* if the closing char is what's on the stack, turn off the
+         attribue and pop the stack */
+      if ((ptr[0]==')' && chrstack[stacksize-1]=='(') ||
+	  (ptr[0]=='>' && chrstack[stacksize-1]=='<') ||
+	  (ptr[0]==']' && chrstack[stacksize-1]=='[') ||
+	  (ptr[0]=='}' && chrstack[stacksize-1]=='{')) {
+	int i;
+	stacksize--;
+	curattrs=OWL_FMTEXT_ATTR_NONE;
+	for (i=0; i<stacksize; i++) {
+	  curattrs|=attrstack[i];
+	}
+	txtptr+=1;
+	continue;
+      } else {
+	/* otherwise print and continue */
+	buff=owl_malloc(5);
+	buff[0]=ptr[0];
+	buff[1]='\0';
+	owl_fmtext_append_attr(f, buff, curattrs, curcolor);
+	owl_free(buff);
+	txtptr++;
+	continue;
+      }
+    } else {
+      /* we've found an unattached opener, print everything and move on */
+      buff=owl_malloc(ptr-txtptr+20);
+      strncpy(buff, txtptr, ptr-txtptr+1);
+      buff[ptr-txtptr+1]='\0';
+      owl_fmtext_append_attr(f, buff, curattrs, curcolor);
+      owl_free(buff);
+      txtptr=ptr+1;
+      continue;
+    }
+  }
+
+}
+
+void owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in, int start, int stop) {
+  int newlen, i;
+
+  newlen=strlen(f->textbuff)+(stop-start+1);
+  f->textbuff=owl_realloc(f->textbuff, newlen+1);
+  f->fmbuff=owl_realloc(f->fmbuff, newlen+1);
+  f->colorbuff=owl_realloc(f->colorbuff, newlen+1);
+
+  strncat(f->textbuff, in->textbuff+start, stop-start+1);
+  f->textbuff[newlen]='\0';
+  for (i=start; i<=stop; i++) {
+    f->fmbuff[f->textlen+(i-start)]=in->fmbuff[i];
+    f->colorbuff[f->textlen+(i-start)]=in->colorbuff[i];
+  }
+  f->textlen=newlen;
+}
+
+void owl_fmtext_append_spaces(owl_fmtext *f, int nspaces) {
+  int i;
+  for (i=0; i<nspaces; i++) {
+    owl_fmtext_append_normal(f, " ");
+  }
+}
+
+/* requires that the list values are strings or NULL.
+ * joins the elements together with join_with. 
+ * If format_fn is specified, passes it the list element value
+ * and it will return a string which this needs to free. */
+void owl_fmtext_append_list(owl_fmtext *f, owl_list *l, char *join_with, char *(format_fn)(void*)) {
+  int i, size;
+  void *elem;
+  char *text;
+
+  size = owl_list_get_size(l);
+  for (i=0; i<size; i++) {
+    elem = (char*)owl_list_get_element(l,i);
+    if (elem && format_fn) {
+      text = format_fn(elem);
+      if (text) {
+	owl_fmtext_append_normal(f, text);
+	owl_free(text);
+      }
+    } else if (elem) {
+      owl_fmtext_append_normal(f, elem);
+    }
+    if ((i < size-1) && join_with) {
+      owl_fmtext_append_normal(f, join_with);
+    }
+  }
+}
+
+
+void owl_fmtext_print_plain(owl_fmtext *f, char *buff) {
+  strcpy(buff, f->textbuff);
+}
+
+
+void owl_fmtext_curs_waddstr(owl_fmtext *f, WINDOW *w) {
+  char *tmpbuff;
+  int position, trans1, trans2, len, lastsame;
+
+  tmpbuff=owl_malloc(f->textlen+10);
+
+  position=0;
+  len=f->textlen;
+  while (position<=len) {
+    /* find the last char with the current format and color */
+    trans1=owl_util_find_trans(f->fmbuff+position, len-position);
+    trans2=owl_util_find_trans(f->colorbuff+position, len-position);
+
+    if (trans1<trans2) {
+      lastsame=position+trans1;
+    } else {
+      lastsame=position+trans2;
+    }
+
+    /* set the format */
+    wattrset(w, A_NORMAL);
+    if (f->fmbuff[position] & OWL_FMTEXT_ATTR_BOLD) {
+      wattron(w, A_BOLD);
+    }
+    if (f->fmbuff[position] & OWL_FMTEXT_ATTR_REVERSE) {
+      wattron(w, A_REVERSE);
+    }
+    if (f->fmbuff[position] & OWL_FMTEXT_ATTR_UNDERLINE) {
+      wattron(w, A_UNDERLINE);
+    }
+
+    /* set the color */
+    /* warning, this is sort of a hack */
+    if (owl_global_get_hascolors(&g)) {
+      if (f->colorbuff[position]!=OWL_COLOR_DEFAULT) {
+	wattron(w, COLOR_PAIR(f->colorbuff[position]));
+      }
+    }
+
+    /* add the text */
+    strncpy(tmpbuff, f->textbuff + position, lastsame-position+1);
+    tmpbuff[lastsame-position+1]='\0';
+    waddstr(w, tmpbuff);
+
+    position=lastsame+1;
+  }
+  owl_free(tmpbuff);
+}
+
+
+int owl_fmtext_truncate_lines(owl_fmtext *in, int aline, int lines, owl_fmtext *out) {
+  /* start with line aline (where the first line is 0) and print
+   *  'lines' lines
+   */
+  char *ptr1, *ptr2;
+  int i, offset;
+
+  /* initialize out */
+  owl_fmtext_init_null(out);
+  
+  /* find the starting line */
+  ptr1=in->textbuff;
+  if (aline!=0) {
+    for (i=0; i<aline; i++) {
+      ptr1=strchr(ptr1, '\n');
+      if (!ptr1) return(-1);
+      ptr1++;
+    }
+  }
+  /* ptr1 now holds the starting point */
+
+  /* copy in the next 'lines' lines */
+  if (lines<1) return(-1);
+
+  for (i=0; i<lines; i++) {
+    ptr2=strchr(ptr1, '\n');
+    offset=ptr1-in->textbuff;
+    if (!ptr2) {
+      owl_fmtext_append_fmtext(out, in, offset, in->textlen-1);
+      return(-1);
+    }
+    owl_fmtext_append_fmtext(out, in, offset, (ptr2-ptr1)+offset);
+    ptr1=ptr2+1;
+  }
+  return(0);
+}
+
+
+void owl_fmtext_truncate_cols(owl_fmtext *in, int acol, int bcol, owl_fmtext *out) {
+  char *ptr1, *ptr2, *last;
+  int len, offset;
+  
+  /* the first column is column 0 */
+
+  /* the message is expected to end in a new line for now */
+
+  owl_fmtext_init_null(out);
+
+  last=in->textbuff+in->textlen-1;
+  ptr1=in->textbuff;
+  while (ptr1<=last) {
+    ptr2=strchr(ptr1, '\n');
+    if (!ptr2) {
+      /* but this shouldn't happen if we end in a \n */
+      break;
+    }
+    
+    if (ptr2==ptr1) {
+      owl_fmtext_append_normal(out, "\n");
+      ptr1++;
+      continue;
+    }
+
+    /* we need to check that we won't run over here */
+    len=bcol-acol;
+    if (len > (ptr2-(ptr1+acol))) {
+      len=ptr2-(ptr1+acol);
+    }
+    if (len>last-ptr1) {
+      len-=(last-ptr1);
+    }
+    if (len<=0) {
+      owl_fmtext_append_normal(out, "\n");
+      ptr1=ptr2+1;
+      continue;
+    }
+
+    offset=ptr1-in->textbuff;
+    owl_fmtext_append_fmtext(out, in, offset+acol, offset+acol+len);
+
+    ptr1=ptr2+1;
+  }
+}
+
+
+int owl_fmtext_num_lines(owl_fmtext *f) {
+  int lines, i;
+
+  lines=0;
+  for (i=0; f->textbuff[i]!='\0'; i++) {
+    if (f->textbuff[i]=='\n') lines++;
+  }
+
+  /* if the last char wasn't a \n there's one more line */
+  if (f->textbuff[i-1]!='\n') lines++;
+
+  return(lines);
+}
+
+
+char *owl_fmtext_get_text(owl_fmtext *f) {
+  return(f->textbuff);
+}
+
+void owl_fmtext_free(owl_fmtext *f) {
+  if (f->textbuff) owl_free(f->textbuff);
+  if (f->fmbuff) owl_free(f->fmbuff);
+  if (f->colorbuff) owl_free(f->colorbuff);
+}
+
+
+void owl_fmtext_copy(owl_fmtext *dst, owl_fmtext *src) {
+  dst->textlen=src->textlen;
+  dst->textbuff=owl_malloc(src->textlen+5);
+  dst->fmbuff=owl_malloc(src->textlen+5);
+  dst->colorbuff=owl_malloc(src->textlen+5);
+  memcpy(dst->textbuff, src->textbuff, src->textlen);
+  memcpy(dst->fmbuff, src->fmbuff, src->textlen);
+  memcpy(dst->colorbuff, src->colorbuff, src->textlen);
+}
Index: functions.c
===================================================================
--- functions.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ functions.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,2008 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <com_err.h>
+#include <time.h>
+#include "owl.h"
+
+void owl_function_noop(void) {
+  return;
+}
+
+char *owl_function_command(char *cmdbuff) {
+  owl_function_debugmsg("executing command: %s", cmdbuff);
+  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
+			     owl_global_get_context(&g), cmdbuff);
+}
+
+void owl_function_command_norv(char *cmdbuff) {
+  char *rv;
+  rv = owl_function_command(cmdbuff);
+  if (rv) owl_free(rv);
+}
+
+void owl_function_command_alias(char *alias_from, char *alias_to) {
+  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
+}
+
+owl_cmd *owl_function_get_cmd(char *name) {
+  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
+}
+
+void owl_function_show_commands() {
+  owl_list l;
+  owl_fmtext fm;
+
+  owl_fmtext_init_null(&fm);
+  owl_fmtext_append_bold(&fm, "Commands:  ");
+  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
+  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
+  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
+  owl_fmtext_append_normal(&fm, "\n");
+  owl_function_popless_fmtext(&fm);
+  owl_cmddict_namelist_free(&l);
+  owl_fmtext_free(&fm);
+}
+
+char *owl_function_cmd_describe(void *name) {
+  owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
+  if (cmd) return owl_cmd_describe(cmd);
+  else return(NULL);
+}
+
+void owl_function_show_command(char *name) {
+  owl_function_help_for_command(name);
+}
+
+void owl_function_adminmsg(char *header, char *body) {
+  owl_message *m;
+  int followlast;
+
+  followlast=owl_global_should_followlast(&g);
+  m=owl_malloc(sizeof(owl_message));
+  owl_message_create_admin(m, header, body);
+  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
+  owl_view_consider_message(owl_global_get_current_view(&g), m);
+
+  if (followlast) owl_function_lastmsg_noredisplay();
+
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
+    owl_popwin_refresh(owl_global_get_popwin(&g));
+  }
+  
+  wnoutrefresh(owl_global_get_curs_recwin(&g));
+  owl_global_set_needrefresh(&g);
+}
+
+void owl_function_adminmsg_outgoing(char *header, char *body, char *zwriteline) {
+  owl_message *m;
+  int followlast;
+  
+  followlast=owl_global_should_followlast(&g);
+  m=owl_malloc(sizeof(owl_message));
+  owl_message_create_admin(m, header, body);
+  owl_message_set_admin_outgoing(m, zwriteline);
+  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
+  owl_view_consider_message(owl_global_get_current_view(&g), m);
+
+  if (followlast) owl_function_lastmsg_noredisplay();
+
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
+    owl_popwin_refresh(owl_global_get_popwin(&g));
+  }
+  
+  wnoutrefresh(owl_global_get_curs_recwin(&g));
+  owl_global_set_needrefresh(&g);
+}
+
+void owl_function_zwrite_setup(char *line) {
+  owl_editwin *e;
+  char buff[1024];
+  owl_zwrite z;
+  int ret;
+
+  /* check the arguments */
+  ret=owl_zwrite_create_from_line(&z, line);
+  if (ret) {
+    owl_function_makemsg("Error in zwrite arugments");
+    owl_zwrite_free(&z);
+    return;
+  }
+
+  /* send a ping if necessary */
+  if (owl_global_is_txping(&g)) {
+    owl_zwrite_send_ping(&z);
+  }
+  owl_zwrite_free(&z);
+
+  /* create and setup the editwin */
+  e=owl_global_get_typwin(&g);
+  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE);
+
+  if (!owl_global_is_lockout_ctrld(&g)) {
+    owl_function_makemsg("Type your zephyr below.  End with ^D or a dot on a line by itself.  ^C will quit.");
+  } else {
+    owl_function_makemsg("Type your zephyr below.  End with a dot on a line by itself.  ^C will quit.");
+  }
+
+  owl_editwin_clear(e);
+  owl_editwin_set_dotsend(e);
+  strcpy(buff, "----> ");
+  strcat(buff, line);
+  strcat(buff, "\n");
+  owl_editwin_set_locktext(e, buff);
+
+  /* make it active */
+  owl_global_set_typwin_active(&g);
+}
+
+void owl_function_zwrite(char *line) {
+  char *tmpbuff, buff[1024];
+  owl_zwrite z;
+  int i, j;
+
+  /* create the zwrite and send the message */
+  owl_zwrite_create_from_line(&z, line);
+  owl_zwrite_send_message(&z, owl_editwin_get_text(owl_global_get_typwin(&g)));
+  owl_function_makemsg("Waiting for ack...");
+
+  /* display the message as an admin message in the receive window */
+  if (owl_global_is_displayoutgoing(&g) && owl_zwrite_is_personal(&z)) {
+    tmpbuff=owl_malloc(strlen(owl_editwin_get_text(owl_global_get_typwin(&g)))+1024);
+    owl_zwrite_get_recipstr(&z, buff);
+    sprintf(tmpbuff, "Message sent to %s", buff);
+    owl_function_adminmsg_outgoing(tmpbuff, owl_editwin_get_text(owl_global_get_typwin(&g)), line);
+    owl_free(tmpbuff);
+  }
+
+  /* log it if we have logging turned on */
+  if (owl_global_is_logging(&g) && owl_zwrite_is_personal(&z)) {
+    j=owl_zwrite_get_numrecips(&z);
+    for (i=0; i<j; i++) {
+      owl_log_outgoing(owl_zwrite_get_recip_n(&z, i),
+		       owl_editwin_get_text(owl_global_get_typwin(&g)));
+    }
+  }
+
+  /* free the zwrite */
+  owl_zwrite_free(&z);
+}
+
+
+
+void owl_function_nextmsg() {
+  int curmsg;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+  
+  curmsg=owl_global_get_curmsg(&g);
+  curmsg++;
+  if (curmsg>owl_view_get_size(v)-1) {
+    curmsg=owl_view_get_size(v)-1;
+    if (curmsg<0) curmsg=0;
+    owl_function_beep();
+    owl_function_makemsg("already at last message");
+  }
+  owl_global_set_curmsg(&g, curmsg);
+  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
+
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_direction_downwards(&g);
+}
+
+
+void owl_function_prevmsg() {
+  int curmsg;
+  
+  curmsg=owl_global_get_curmsg(&g);
+  curmsg--;
+  if (curmsg<0) {
+    curmsg=0;
+    owl_function_beep();
+    owl_function_makemsg("already at first message");
+  }
+  owl_global_set_curmsg(&g, curmsg);
+  owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_direction_upwards(&g);
+}
+
+
+void owl_function_nextmsg_notdeleted() {
+  int curmsg;
+  owl_message *m;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+
+  curmsg=owl_global_get_curmsg(&g);
+  while (1) {
+    curmsg++;
+
+    /* if we're out of bounds get in bounds and stop */
+    if (curmsg>owl_view_get_size(v)-1) {
+      curmsg=owl_view_get_size(v)-1;
+      if (curmsg<0) curmsg=0;
+      owl_function_makemsg("already at last non-deleted message");
+      break;
+    }
+
+    /* if this one is not deleted we can stop where we are */
+    m=owl_view_get_element(v, curmsg);
+    if (!owl_message_is_delete(m)) {
+      break;
+    }
+  }
+  
+  owl_global_set_curmsg(&g, curmsg);
+  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_direction_downwards(&g);
+}
+
+
+void owl_function_prevmsg_notdeleted() {
+  int curmsg;
+  owl_message *m;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+
+  curmsg=owl_global_get_curmsg(&g);
+  while(1) {
+    curmsg--;
+
+    /* if we're out of bounds get in bounds and stop */
+    if (curmsg<0) {
+      curmsg=0;
+      owl_function_makemsg("already at first non-deleted message");
+      break;
+    }
+
+    /* if this one is not deleted we can stop where we are */
+    m=owl_view_get_element(v, curmsg);
+    if (!owl_message_is_delete(m)) {
+      break;
+    }
+  }
+
+  owl_global_set_curmsg(&g, curmsg);
+  owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_direction_upwards(&g);
+}
+
+
+void owl_function_deletecur() {
+  int curmsg;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+
+  /* bail if there's no current message */
+  if (owl_view_get_size(v) < 1) {
+    owl_function_makemsg("No current message to delete");
+    return;
+  }
+
+  /* mark the message for deletion */
+  curmsg=owl_global_get_curmsg(&g);
+  owl_view_delete_element(v, curmsg);
+
+  /* move the poiner in the appropriate direction to the next undeleted msg */
+  if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
+    owl_command_prev_notdeleted();
+  } else {
+    owl_command_next_notdeleted();
+  }
+}
+
+
+void owl_function_undeletecur() {
+  int curmsg;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+  
+  if (owl_view_get_size(v) < 1) {
+    owl_function_makemsg("No current message to undelete");
+    return;
+  }
+  curmsg=owl_global_get_curmsg(&g);
+
+  owl_view_undelete_element(v, curmsg);
+
+  if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
+    if (curmsg>0) {
+      owl_command_prev();
+    } else {
+      owl_command_next();
+    }
+  } else {
+    owl_command_next();
+  }
+
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+}
+
+
+void owl_function_expunge() {
+  int curmsg;
+  owl_message *m;
+  owl_messagelist *ml;
+  owl_view *v;
+  int i, j;
+
+  curmsg=owl_global_get_curmsg(&g);
+  v=owl_global_get_current_view(&g);
+  ml=owl_global_get_msglist(&g);
+
+  /* first try to move to an undeleted message in the view*/
+  m=owl_view_get_element(v, curmsg);
+  if (owl_message_is_delete(m)) {
+    /* try to find the next undeleted message */
+    j=owl_view_get_size(v);
+    for (i=curmsg; i<j; i++) {
+      if (!owl_message_is_delete(owl_view_get_element(v, i))) {
+	owl_global_set_curmsg(&g, i);
+	break;
+      }
+    }
+
+    /* if we weren't successful try to find one backwards */
+    curmsg=owl_global_get_curmsg(&g);
+    if (owl_message_is_delete(owl_view_get_element(v, curmsg))) {
+      for (i=curmsg; i>0; i--) {
+	if (!owl_message_is_delete(owl_view_get_element(v, i))) {
+	  owl_global_set_curmsg(&g, i);
+	  break;
+	}
+      }
+    }
+  }
+
+  /* expunge the message list */
+  owl_messagelist_expunge(ml);
+
+  /* update all views (we only have one right now) */
+  owl_view_recalculate(v);
+
+  if (curmsg>owl_view_get_size(v)-1) {
+    owl_global_set_curmsg(&g, owl_view_get_size(v)-1);
+    if (owl_global_get_curmsg(&g)<0) {
+      owl_global_set_curmsg(&g, 0);
+    }
+    owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
+  }
+
+  /* if there are no messages set the direction to down in case we
+     delete everything upwards */
+  owl_global_set_direction_downwards(&g);
+  
+  owl_function_makemsg("Messages expunged");
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+}
+
+
+void owl_function_firstmsg() {
+  owl_global_set_curmsg(&g, 0);
+  owl_global_set_topmsg(&g, 0);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_direction_downwards(&g);
+}
+
+void owl_function_lastmsg_noredisplay() {
+  int curmsg;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+  
+  curmsg=owl_view_get_size(v)-1;
+  if (curmsg<0) curmsg=0;
+  owl_global_set_curmsg(&g, curmsg);
+  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_direction_downwards(&g);
+}
+
+void owl_function_lastmsg() {
+  owl_function_lastmsg_noredisplay();
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));  
+}
+
+void owl_function_shift_right() {
+  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_needrefresh(&g);
+}
+
+
+void owl_function_shift_left() {
+  int shift;
+
+  shift=owl_global_get_rightshift(&g);
+  if (shift>=10) {
+    owl_global_set_rightshift(&g, shift-10);
+    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+    owl_global_set_needrefresh(&g);
+  } else {
+    owl_function_beep();
+    owl_function_makemsg("Already full left");
+  }
+}
+
+
+void owl_function_unsuball() {
+  unsuball();
+  owl_function_makemsg("Unsubscribed from all messages.");
+}
+
+void owl_function_loadsubs(char *file) {
+  int ret;
+  ret=owl_zephyr_loadsubs(file);
+  if (ret==0) {
+    owl_function_makemsg("Subscribed to messages from file.");
+  } else if (ret==-1) {
+    owl_function_makemsg("Could not open file.");
+  } else {
+    owl_function_makemsg("Error subscribing to messages from file.");
+  }
+}
+
+void owl_function_suspend() {
+  endwin();
+  printf("\n");
+  kill(getpid(), SIGSTOP);
+
+  /* resize to reinitialize all the windows when we come back */
+  owl_command_resize();
+}
+
+void owl_function_zaway_toggle() {
+  if (!owl_global_is_zaway(&g)) {
+    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
+    owl_function_zaway_on();
+  } else {
+    owl_function_zaway_off();
+  }
+}
+
+void owl_function_zaway_on() {
+  owl_global_set_zaway_on(&g);
+  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
+}
+
+void owl_function_zaway_off() {
+  owl_global_set_zaway_off(&g);
+  owl_function_makemsg("zaway off");
+}
+
+void owl_function_quit() {
+  char *ret;
+  
+  /* zlog out if we need to */
+  if (owl_global_is_shutdownlogout(&g)) {
+    owl_function_zlog_out();
+  }
+
+  /* execute the commands in shutdown */
+  ret = owl_config_execute("owl::shutdown();");
+  if (ret) owl_free(ret);
+
+  /* final clean up */
+  unsuball();
+  ZClosePort();
+  endwin();
+  owl_function_debugmsg("Quitting Owl");
+  exit(0);
+}
+
+
+void owl_function_zlog_in() {
+  char *exposure, *eset;
+  int ret;
+
+  eset=EXPOSE_REALMVIS;
+  exposure=ZGetVariable("exposure");
+  if (exposure==NULL) {
+    eset=EXPOSE_REALMVIS;
+  } else if (!strcasecmp(exposure,EXPOSE_NONE)) {
+    eset = EXPOSE_NONE;
+  } else if (!strcasecmp(exposure,EXPOSE_OPSTAFF)) {
+    eset = EXPOSE_OPSTAFF;
+  } else if (!strcasecmp(exposure,EXPOSE_REALMVIS)) {
+    eset = EXPOSE_REALMVIS;
+  } else if (!strcasecmp(exposure,EXPOSE_REALMANN)) {
+    eset = EXPOSE_REALMANN;
+  } else if (!strcasecmp(exposure,EXPOSE_NETVIS)) {
+    eset = EXPOSE_NETVIS;
+  } else if (!strcasecmp(exposure,EXPOSE_NETANN)) {
+    eset = EXPOSE_NETANN;
+  }
+   
+  ret=ZSetLocation(eset);
+  if (ret != ZERR_NONE) {
+    /*
+      char buff[LINE];
+      sprintf(buff, "Error setting location: %s", error_message(ret));
+      owl_function_makemsg(buff);
+    */
+  }
+}
+
+void owl_function_zlog_out() {
+  int ret;
+  
+  ret=ZUnsetLocation();
+  if (ret != ZERR_NONE) {
+    /*
+      char buff[LINE];
+      sprintf(buff, "Error unsetting location: %s", error_message(ret));
+      owl_function_makemsg(buff);
+    */
+  }
+}
+
+
+void owl_function_makemsg(char *fmt, ...) {
+  va_list ap;
+  char buff[2048];
+
+  va_start(ap, fmt);
+  werase(owl_global_get_curs_msgwin(&g));
+  
+  vsnprintf(buff, 2048, fmt, ap);
+  owl_function_debugmsg("makemsg: %s", buff);
+  waddstr(owl_global_get_curs_msgwin(&g), buff);  
+  wnoutrefresh(owl_global_get_curs_msgwin(&g));
+  owl_global_set_needrefresh(&g);
+  va_end(ap);
+}
+
+void owl_function_errormsg(char *fmt, ...) {
+  va_list ap;
+  char buff[2048];
+
+  va_start(ap, fmt);
+  werase(owl_global_get_curs_msgwin(&g));
+  
+  vsnprintf(buff, 2048, fmt, ap);
+  owl_function_debugmsg("ERROR: %s", buff);
+  waddstr(owl_global_get_curs_msgwin(&g), buff);  
+  waddstr(owl_global_get_curs_msgwin(&g), "ERROR: ");  
+  wnoutrefresh(owl_global_get_curs_msgwin(&g));
+  owl_global_set_needrefresh(&g);
+  va_end(ap);
+}
+
+
+void owl_function_openurl() {
+  /* visit the first url in the current message */
+  owl_message *m;
+  owl_view *v;
+  char *ptr1, *ptr2, *text, url[LINE], tmpbuff[LINE];
+  int webbrowser;
+
+  webbrowser = owl_global_get_webbrowser(&g);
+
+  if (webbrowser < 0 || webbrowser == OWL_WEBBROWSER_NONE) {
+    owl_function_makemsg("No browser selected");
+    return;
+  }
+
+  v=owl_global_get_current_view(&g);
+  
+  if (owl_view_get_size(v)==0) {
+    owl_function_makemsg("No current message selected");
+    return;
+  }
+
+  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
+  text=owl_message_get_text(m);
+
+  /* First look for a good URL */  
+  if ((ptr1=strstr(text, "http://"))!=NULL) {
+    ptr2=strpbrk(ptr1, " \n\t");
+    if (ptr2) {
+      strncpy(url, ptr1, ptr2-ptr1+1);
+      url[ptr2-ptr1+1]='\0';
+    } else {
+      strcpy(url, ptr1);
+    }
+
+    /* if we had <http strip a trailing > */
+    if (ptr1>text && ptr1[-1]=='<') {
+      if (url[strlen(url)-1]=='>') {
+	url[strlen(url)-1]='\0';
+      }
+    }
+  } else if ((ptr1=strstr(text, "https://"))!=NULL) {
+    /* Look for an https URL */  
+    ptr2=strpbrk(ptr1, " \n\t");
+    if (ptr2) {
+      strncpy(url, ptr1, ptr2-ptr1+1);
+      url[ptr2-ptr1+1]='\0';
+    } else {
+      strcpy(url, ptr1);
+    }
+    
+    /* if we had <http strip a trailing > */
+    if (ptr1>text && ptr1[-1]=='<') {
+      if (url[strlen(url)-1]=='>') {
+	url[strlen(url)-1]='\0';
+      }
+    }
+  } else if ((ptr1=strstr(text, "www."))!=NULL) {
+    /* if we can't find a real url look for www.something */
+    ptr2=strpbrk(ptr1, " \n\t");
+    if (ptr2) {
+      strncpy(url, ptr1, ptr2-ptr1+1);
+      url[ptr2-ptr1+1]='\0';
+    } else {
+      strcpy(url, ptr1);
+    }
+  } else {
+    owl_function_beep();
+    owl_function_makemsg("Could not find URL to open.");
+    return;
+  }
+
+  /* Make sure there aren't any quotes or \'s in the url */
+  for (ptr1 = url; *ptr1; ptr1++) {
+    if (*ptr1 == '"' || *ptr1 == '\\') {
+      owl_function_beep();
+      owl_function_makemsg("URL contains invalid characters.");
+      return;
+    }
+  }
+  
+  /* NOTE: There are potentially serious security issues here... */
+
+  /* open the page */
+  owl_function_makemsg("Opening %s", url);
+  if (webbrowser == OWL_WEBBROWSER_NETSCAPE) {
+    snprintf(tmpbuff, LINE, "netscape -remote \"openURL(%s)\" > /dev/null 2> /dev/null", url);
+    system(tmpbuff); 
+  } else if (webbrowser == OWL_WEBBROWSER_GALEON) {
+    snprintf(tmpbuff, LINE, "galeon \"%s\" > /dev/null 2> /dev/null &", url);
+    system(tmpbuff); 
+  }
+}
+
+void owl_function_calculate_topmsg(int direction) {
+  int recwinlines, y, savey, i, j, topmsg, curmsg, foo;
+  owl_mainwin *mw;
+  owl_view *v;
+
+  mw=owl_global_get_mainwin(&g);
+
+  topmsg=owl_global_get_topmsg(&g);
+  curmsg=owl_global_get_curmsg(&g);
+  v=owl_global_get_current_view(&g);
+  recwinlines=owl_global_get_recwin_lines(&g);
+
+  if (owl_view_get_size(v) < 1) {
+    return;
+  }
+  
+  /* Find number of lines from top to bottom of curmsg (store in savey) */
+  savey=0;
+  for (i=topmsg; i<=curmsg; i++) {
+    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
+  }
+
+  /* If the direction is DOWNWARDS but we're off the bottom of the
+   *  screen, then set the topmsg to curmsg and scroll UPWARDS
+   */
+  if (direction == OWL_DIRECTION_DOWNWARDS) {
+    if (savey > recwinlines) {
+      topmsg=curmsg;
+      savey=owl_message_get_numlines(owl_view_get_element(v, i));
+      direction=OWL_DIRECTION_UPWARDS;
+    }
+  }
+
+  /* If our bottom line is less than 1/4 down the screen then scroll up */
+  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
+    if (savey < (recwinlines / 4)) {
+      y=0;
+      for (j=curmsg; j>=0; j--) {
+	foo=owl_message_get_numlines(owl_view_get_element(v, j));
+	/* will we run the curmsg off the screen? */
+	if ((foo+y) >= recwinlines) {
+	  j++;
+	  if (j>curmsg) j=curmsg;
+	  break;
+	}
+	/* have saved 1/2 the screen space? */
+	y+=foo;
+	if (y > (recwinlines / 2)) break;
+      }
+      if (j<0) j=0;
+      owl_global_set_topmsg(&g, j);
+      return;
+    }
+  }
+
+  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
+    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
+    if (savey > ((recwinlines * 3)/4)) {
+      y=0;
+      /* count lines from the top until we can save 1/2 the screen size */
+      for (j=topmsg; j<curmsg; j++) {
+	y+=owl_message_get_numlines(owl_view_get_element(v, j));
+	if (y > (recwinlines / 2)) break;
+      }
+      if (j==curmsg) {
+	j--;
+      }
+      owl_global_set_topmsg(&g, j+1);
+      return;
+    }
+  }
+}
+
+
+void owl_function_resize() {
+  owl_global_set_resize_pending(&g);
+}
+
+
+void owl_function_run_buffercommand() {
+  char *buff;
+
+  buff=owl_global_get_buffercommand(&g);
+  if (!strncmp(buff, "zwrite ", 7)) {
+
+    owl_function_zwrite(buff);
+  }
+}
+
+void owl_function_debugmsg(char *fmt, ...) {
+  FILE *file;
+  time_t now;
+  char buff1[LINE], buff2[LINE];
+  va_list ap;
+  va_start(ap, fmt);
+
+  if (!owl_global_is_debug_fast(&g)) return;
+
+  file=fopen(owl_global_get_debug_file(&g), "a");
+  if (!file) return;
+
+  now=time(NULL);
+  strcpy(buff1, ctime(&now));
+  buff1[strlen(buff1)-1]='\0';
+
+  owl_global_get_runtime_string(&g, buff2);
+  
+  fprintf(file, "[%i -  %s - %s]: ", (int) getpid(), buff1, buff2);
+  vfprintf(file, fmt, ap);
+  fprintf(file, "\n");
+  fclose(file);
+
+  va_end(ap);
+}
+
+
+void owl_function_refresh() {
+  owl_function_resize();
+}
+
+void owl_function_beep() {
+  if (owl_global_is_bell(&g)) {
+    beep();
+  }
+}
+
+
+void owl_function_subscribe(char *class, char *inst, char *recip) {
+  int ret;
+
+  ret=owl_zephyr_sub(class, inst, recip);
+  if (ret) {
+    owl_function_makemsg("Error subscribing.");
+  } else {
+    owl_function_makemsg("Subscribed.");
+  }
+}
+
+
+void owl_function_unsubscribe(char *class, char *inst, char *recip) {
+  int ret;
+
+  ret=owl_zephyr_unsub(class, inst, recip);
+  if (ret) {
+    owl_function_makemsg("Error subscribing.");
+  } else {
+    owl_function_makemsg("Unsubscribed.");
+  }
+}
+
+
+void owl_function_set_cursor(WINDOW *win) {
+  wnoutrefresh(win);
+}
+
+
+void owl_function_full_redisplay() {
+  redrawwin(owl_global_get_curs_recwin(&g));
+  redrawwin(owl_global_get_curs_sepwin(&g));
+  redrawwin(owl_global_get_curs_typwin(&g));
+  redrawwin(owl_global_get_curs_msgwin(&g));
+
+  wnoutrefresh(owl_global_get_curs_recwin(&g));
+  wnoutrefresh(owl_global_get_curs_sepwin(&g));
+  wnoutrefresh(owl_global_get_curs_typwin(&g));
+  wnoutrefresh(owl_global_get_curs_msgwin(&g));
+  
+  sepbar("");
+  owl_function_makemsg("");
+
+  owl_global_set_needrefresh(&g);
+}
+
+
+void owl_function_popless_text(char *text) {
+  owl_popwin *pw;
+  owl_viewwin *v;
+
+  pw=owl_global_get_popwin(&g);
+  v=owl_global_get_viewwin(&g);
+
+  owl_popwin_up(pw);
+  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
+			owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
+			text);
+  owl_popwin_refresh(pw);
+  owl_viewwin_redisplay(v, 0);
+  owl_global_set_needrefresh(&g);
+}
+
+
+void owl_function_popless_fmtext(owl_fmtext *fm) {
+  owl_popwin *pw;
+  owl_viewwin *v;
+
+  pw=owl_global_get_popwin(&g);
+  v=owl_global_get_viewwin(&g);
+
+  owl_popwin_up(pw);
+  owl_viewwin_init_fmtext(v, owl_popwin_get_curswin(pw),
+		   owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
+		   fm);
+  owl_popwin_refresh(pw);
+  owl_viewwin_redisplay(v, 0);
+  owl_global_set_needrefresh(&g);
+}
+
+void owl_function_about() {
+  char buff[5000];
+
+  sprintf(buff, "This is owl version %s\n", OWL_VERSION_STRING);
+  strcat(buff, "\nOwl was written by James Kretchmar at the Massachusetts\n");
+  strcat(buff, "Institute of Technology.  The first version, 0.5, was\n");
+  strcat(buff, "released in March 2002\n");
+  strcat(buff, "\n");
+  strcat(buff, "The name 'owl' was chosen in reference to the owls in the\n");
+  strcat(buff, "Harry Potter novels, who are tasked with carrying messages\n");
+  strcat(buff, "between Witches and Wizards.\n");
+  strcat(buff, "\n");
+  strcat(buff, "Copyright 2002 Massachusetts Institute of Technology\n");
+  strcat(buff, "\n");
+  strcat(buff, "Permission to use, copy, modify, and distribute this\n");
+  strcat(buff, "software and its documentation for any purpose and without\n");
+  strcat(buff, "fee is hereby granted, provided that the above copyright\n");
+  strcat(buff, "notice and this permission notice appear in all copies\n");
+  strcat(buff, "and in supporting documentation.  No representation is\n");
+  strcat(buff, "made about the suitability of this software for any\n");
+  strcat(buff, "purpose.  It is provided \"as is\" without express\n");
+  strcat(buff, "or implied warranty.\n");
+  owl_function_popless_text(buff);
+}
+
+void owl_function_info() {
+  owl_message *m;
+  ZNotice_t *n;
+  char buff[2048], tmpbuff[1024];
+  char *ptr;
+  int i, j, fields, len;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+  
+  if (owl_view_get_size(v)==0) {
+    owl_function_makemsg("No message selected\n");
+    return;
+  }
+
+  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
+  if (!owl_message_is_zephyr(m)) {
+    sprintf(buff,   "Owl Message Id: %i\n", owl_message_get_id(m));
+    sprintf(buff, "%sTime          : %s\n", buff, owl_message_get_timestr(m));
+    owl_function_popless_text(buff);
+    return;
+  }
+
+  n=owl_message_get_notice(m);
+
+  sprintf(buff,   "Owl Msg ID: %i\n", owl_message_get_id(m));
+  sprintf(buff, "%sClass     : %s\n", buff, n->z_class);
+  sprintf(buff, "%sInstance  : %s\n", buff, n->z_class_inst);
+  sprintf(buff, "%sSender    : %s\n", buff, n->z_sender);
+  sprintf(buff, "%sRecip     : %s\n", buff, n->z_recipient);
+  sprintf(buff, "%sOpcode    : %s\n", buff, n->z_opcode);
+  strcat(buff,    "Kind      : ");
+  if (n->z_kind==UNSAFE) {
+    strcat(buff, "UNSAFE\n");
+  } else if (n->z_kind==UNACKED) {
+    strcat(buff, "UNACKED\n");
+  } else if (n->z_kind==ACKED) {
+    strcat(buff, "ACKED\n");
+  } else if (n->z_kind==HMACK) {
+    strcat(buff, "HMACK\n");
+  } else if (n->z_kind==HMCTL) {
+    strcat(buff, "HMCTL\n");
+  } else if (n->z_kind==SERVACK) {
+    strcat(buff, "SERVACK\n");
+  } else if (n->z_kind==SERVNAK) {
+    strcat(buff, "SERVNAK\n");
+  } else if (n->z_kind==CLIENTACK) {
+    strcat(buff, "CLIENTACK\n");
+  } else if (n->z_kind==STAT) {
+    strcat(buff, "STAT\n");
+  } else {
+    strcat(buff, "ILLEGAL VALUE\n");
+  }
+  sprintf(buff, "%sTime      : %s\n", buff, owl_message_get_timestr(m));
+  sprintf(buff, "%sHost      : %s\n", buff, owl_message_get_hostname(m));
+  sprintf(buff, "%sPort      : %i\n", buff, n->z_port);
+  strcat(buff,    "Auth      : ");
+  if (n->z_auth == ZAUTH_FAILED) {
+    strcat(buff, "FAILED\n");
+  } else if (n->z_auth == ZAUTH_NO) {
+    strcat(buff, "NO\n");
+  } else if (n->z_auth == ZAUTH_YES) {
+    strcat(buff, "YES\n");
+  } else {
+    sprintf(buff, "%sUnknown State (%i)\n", buff, n->z_auth);
+  }
+  sprintf(buff, "%sCheckd Ath: %i\n", buff, n->z_checked_auth);
+  sprintf(buff, "%sMulti notc: %s\n", buff, n->z_multinotice);
+  sprintf(buff, "%sNum other : %i\n", buff, n->z_num_other_fields);
+  sprintf(buff, "%sMsg Len   : %i\n", buff, n->z_message_len);
+
+  sprintf(buff, "%sFields    : %i\n", buff, owl_zephyr_get_num_fields(n));
+
+  fields=owl_zephyr_get_num_fields(n);
+  for (i=0; i<fields; i++) {
+    sprintf(buff, "%sField %i   : ", buff, i+1);
+
+    ptr=owl_zephyr_get_field(n, i+1, &len);
+    if (!ptr) break;
+    if (len<30) {
+      strncpy(tmpbuff, ptr, len);
+      tmpbuff[len]='\0';
+    } else {
+      strncpy(tmpbuff, ptr, 30);
+      tmpbuff[30]='\0';
+      strcat(tmpbuff, "...");
+    }
+
+    /* just for testing for now */
+    for (j=0; j<strlen(tmpbuff); j++) {
+      if (tmpbuff[j]=='\n') tmpbuff[j]='~';
+      if (tmpbuff[j]=='\r') tmpbuff[j]='!';
+    }
+
+    strcat(buff, tmpbuff);
+    strcat(buff, "\n");
+  }
+  sprintf(buff, "%sDefault Fm: %s\n", buff, n->z_default_format);
+	
+  owl_function_popless_text(buff);
+}
+
+
+void owl_function_curmsg_to_popwin() {
+  owl_popwin *pw;
+  owl_view *v;
+  owl_message *m;
+
+  v = owl_global_get_current_view(&g);
+
+  pw=owl_global_get_popwin(&g);
+
+  if (owl_view_get_size(v)==0) {
+    owl_function_makemsg("No current message");
+    return;
+  }
+
+  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
+  owl_function_popless_fmtext(owl_message_get_fmtext(m));
+}
+
+
+void owl_function_page_curmsg(int step) {
+  /* scroll down or up within the current message IF the message is truncated */
+
+  int offset, curmsg, lines;
+  owl_view *v;
+  owl_message *m;
+
+  offset=owl_global_get_curmsg_vert_offset(&g);
+  v=owl_global_get_current_view(&g);
+  if (owl_view_get_size(v)==0) return;
+  curmsg=owl_global_get_curmsg(&g);
+  m=owl_view_get_element(v, curmsg);
+  lines=owl_message_get_numlines(m);
+
+  if (offset==0) {
+    /* Bail if the curmsg isn't the last one displayed */
+    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
+      owl_function_makemsg("The entire message is already displayed");
+      return;
+    }
+    
+    /* Bail if we're not truncated */
+    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
+      owl_function_makemsg("The entire message is already displayed");
+      return;
+    }
+  }
+  
+  
+  /* don't scroll past the last line */
+  if (step>0) {
+    if (offset+step > lines-1) {
+      owl_global_set_curmsg_vert_offset(&g, lines-1);
+    } else {
+      owl_global_set_curmsg_vert_offset(&g, offset+step);
+    }
+  }
+
+  /* would we be before the beginning of the message? */
+  if (step<0) {
+    if (offset+step<0) {
+      owl_global_set_curmsg_vert_offset(&g, 0);
+    } else {
+      owl_global_set_curmsg_vert_offset(&g, offset+step);
+    }
+  }
+  
+  /* redisplay */
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_global_set_needrefresh(&g);
+}
+
+void owl_function_resize_typwin(int newsize) {
+  owl_global_set_typwin_lines(&g, newsize);
+  owl_function_resize();
+}
+
+void owl_function_typwin_grow() {
+  int i;
+
+  i=owl_global_get_typwin_lines(&g);
+  owl_function_resize_typwin(i+1);
+}
+
+void owl_function_typwin_shrink() {
+  int i;
+
+  i=owl_global_get_typwin_lines(&g);
+  if (i>2) {
+    owl_function_resize_typwin(i-1);
+  }
+}
+
+void owl_function_mainwin_pagedown() {
+  int i;
+
+  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
+  if (i<0) return;
+
+  owl_global_set_curmsg(&g, i);
+  owl_function_nextmsg();
+}
+
+void owl_function_mainwin_pageup() {
+  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
+  owl_function_prevmsg();
+}
+
+void owl_function_getsubs() {
+  int ret, num, i, one;
+  ZSubscription_t sub;
+  char *buff;
+
+  one = 1;
+
+  ret=ZRetrieveSubscriptions(0, &num);
+  if (ret == ZERR_TOOMANYSUBS) {
+
+  }
+
+  buff=malloc(num*200);
+  strcpy(buff, "");
+  for (i=0; i<num; i++) {
+    if ((ret = ZGetSubscriptions(&sub, &one)) != ZERR_NONE) {
+      /* deal with error */
+    } else {
+      sprintf(buff, "%s<%s,%s,%s>\n", buff, sub.zsub_class, sub.zsub_classinst, sub.zsub_recipient);
+    }
+  }
+
+  owl_function_popless_text(buff);
+  free(buff);
+  ZFlushSubscriptions();
+}
+
+#define PABUFLEN 5000
+void owl_function_printallvars() {
+  char buff[PABUFLEN], *pos, *name;
+  owl_list varnames;
+  int i, numvarnames, rem;
+
+  pos = buff;
+  pos += sprintf(pos, "%-20s = %s\n", "VARIABLE", "VALUE");
+  pos += sprintf(pos, "%-20s   %s\n",  "--------", "-----");
+  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
+  rem = (buff+PABUFLEN)-pos-1;
+  numvarnames = owl_list_get_size(&varnames);
+  for (i=0; i<numvarnames; i++) {
+    name = owl_list_get_element(&varnames, i);
+    if (name && name[0]!='_') {
+      rem = (buff+PABUFLEN)-pos-1;    
+      pos += snprintf(pos, rem, "\n%-20s = ", name);
+      rem = (buff+PABUFLEN)-pos-1;    
+      owl_variable_get_tostring(owl_global_get_vardict(&g), name, pos, rem);
+      pos = buff+strlen(buff);
+    }
+  }
+  rem = (buff+PABUFLEN)-pos-1;    
+  snprintf(pos, rem, "\n");
+  owl_variable_dict_namelist_free(&varnames);
+  
+  owl_function_popless_text(buff);
+}
+
+void owl_function_show_variables() {
+  owl_list varnames;
+  owl_fmtext fm;  
+  int i, numvarnames;
+  char *varname;
+
+  owl_fmtext_init_null(&fm);
+  owl_fmtext_append_bold(&fm, 
+      "Variables: (use 'show variable <name>' for details)\n");
+  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
+  owl_variable_get_summaryheader(&fm);
+  numvarnames = owl_list_get_size(&varnames);
+  for (i=0; i<numvarnames; i++) {
+    varname = owl_list_get_element(&varnames, i);
+    if (varname && varname[0]!='_') {
+      owl_variable_get_summary(owl_global_get_vardict(&g), varname, &fm);
+    }
+  }
+  owl_variable_dict_namelist_free(&varnames);
+  owl_function_popless_fmtext(&fm);
+  owl_fmtext_free(&fm);
+}
+
+void owl_function_show_variable(char *name) {
+  owl_fmtext fm;  
+
+  owl_fmtext_init_null(&fm);
+  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
+  owl_function_popless_fmtext(&fm);
+  owl_fmtext_free(&fm);  
+}
+
+/* note: this applies to global message list, not to view.
+ * If flag is 1, deletes.  If flag is 0, undeletes. */
+void owl_function_delete_by_id(int id, int flag) {
+  owl_messagelist *ml;
+  owl_message *m;
+  ml = owl_global_get_msglist(&g);
+  m = owl_messagelist_get_by_id(ml, id);
+  if (m) {
+    if (flag == 1) {
+      owl_message_mark_delete(m);
+    } else if (flag == 0) {
+      owl_message_unmark_delete(m);
+    }
+    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+    owl_global_set_needrefresh(&g);
+  } else {
+    owl_function_makemsg("No message with id %d: unable to mark for (un)delete",id);
+  }
+}
+
+void owl_function_delete_automsgs() {
+  /* mark for deletion all messages in the current view that match the
+   * 'trash' filter */
+
+  int i, j, count;
+  owl_message *m;
+  owl_view *v;
+  owl_filter *f;
+  char buff[LINE];
+
+  /* get the trash filter */
+  f=owl_global_get_filter(&g, "trash");
+  if (!f) {
+    owl_function_makemsg("No trash filter defined");
+    return;
+  }
+
+  v=owl_global_get_current_view(&g);
+
+  count=0;
+  j=owl_view_get_size(v);
+  for (i=0; i<j; i++) {
+    m=owl_view_get_element(v, i);
+    if (owl_filter_message_match(f, m)) {
+      count++;
+      owl_message_mark_delete(m);
+    }
+  }
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  sprintf(buff, "%i messages marked for deletion", count);
+  owl_function_makemsg(buff);
+  owl_global_set_needrefresh(&g);
+}
+
+void owl_function_next_personal() {
+  int i, j, curmsg, found;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+  j=owl_view_get_size(v);
+  curmsg=owl_global_get_curmsg(&g);
+  found=0;
+  for (i=curmsg+1; i<j; i++) {
+    if (owl_message_is_personal(owl_view_get_element(v, i))) {
+      owl_global_set_curmsg(&g, i);
+      owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
+      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+      owl_global_set_direction_downwards(&g);
+      found=1;
+      break;
+    }
+  }
+  if (!found) {
+    owl_function_makemsg("No next personal message found");
+  }
+}
+
+void owl_function_prev_personal() {
+  int i, curmsg, found;
+  owl_view *v;
+
+  v=owl_global_get_current_view(&g);
+  curmsg=owl_global_get_curmsg(&g);
+  found=0;
+  for (i=curmsg-1; i>=0; i--) {
+    if (owl_message_is_personal(owl_view_get_element(v, i))) {
+      owl_global_set_curmsg(&g, i);
+      owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
+      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+      owl_global_set_direction_upwards(&g);
+      found=1;
+      break;
+    }
+  }
+  if (!found) {
+    owl_function_makemsg("No previous personal message found");
+  }
+}
+
+
+void owl_function_status() {
+  char buff[5000];
+  time_t start;
+  int up, days, hours, minutes;
+
+  start=owl_global_get_starttime(&g);
+
+  sprintf(buff, "Version: %s\n", OWL_VERSION_STRING);
+  sprintf(buff, "%sScreen size: %i lines, %i columns\n", buff, owl_global_get_lines(&g), owl_global_get_cols(&g));
+  sprintf(buff, "%sStartup Arugments: %s\n", buff, owl_global_get_startupargs(&g));
+  sprintf(buff, "%sStartup Time: %s", buff, ctime(&start));
+
+  up=owl_global_get_runtime(&g);
+  days=up/86400;
+  up-=days*86400;
+  hours=up/3600;
+  up-=hours*3600;
+  minutes=up/60;
+  up-=minutes*60;
+  sprintf(buff, "%sRun Time: %i days %2.2i:%2.2i:%2.2i\n", buff, days, hours, minutes, up);
+
+  if (owl_global_get_hascolors(&g)) {
+    sprintf(buff, "%sColor: Yes, %i color pairs.\n", buff, owl_global_get_colorpairs(&g));
+  } else {
+    strcat(buff, "Color: No.\n");
+  }
+  
+  owl_function_popless_text(buff);
+}
+
+void owl_function_show_term() {
+  owl_fmtext fm;
+  char buff[LINE];
+
+  owl_fmtext_init_null(&fm);
+  sprintf(buff, "Terminal Lines: %i\nTerminal Columns: %i\n",
+	  owl_global_get_lines(&g),
+	  owl_global_get_cols(&g));
+  owl_fmtext_append_normal(&fm, buff);
+
+  if (owl_global_get_hascolors(&g)) {
+    owl_fmtext_append_normal(&fm, "Color: Yes\n");
+    sprintf(buff, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
+    owl_fmtext_append_normal(&fm, buff);
+    sprintf(buff, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
+    owl_fmtext_append_normal(&fm, buff);
+  } else {
+    owl_fmtext_append_normal(&fm, "Color: No\n");
+  }
+
+  owl_function_popless_fmtext(&fm);
+  owl_fmtext_free(&fm);
+}
+
+
+void owl_function_reply(int type, int enter) {
+  /* if type = 0 then normal reply.
+   * if type = 1 then it's a reply to sender
+   * if enter = 0 then allow the command to be edited
+   * if enter = 1 then don't wait for editing
+   */
+  char buff[1024];
+  owl_message *m;
+  owl_filter *f;
+  
+  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
+    owl_function_makemsg("No message selected");
+  } else {
+    char *class, *inst, *to;
+    
+    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
+
+    /* first check if we catch the reply-lockout filter */
+    f=owl_global_get_filter(&g, "reply-lockout");
+    if (f) {
+      if (owl_filter_message_match(f, m)) {
+	owl_function_makemsg("Sorry, replies to this message have been disabled by the reply-lockout filter");
+	return;
+      }
+    }
+    
+    if (owl_message_is_admin(m)) {
+      if (owl_message_get_admintype(m)==OWL_MESSAGE_ADMINTYPE_OUTGOING) {
+	owl_function_zwrite_setup(owl_message_get_zwriteline(m));
+	owl_global_set_buffercommand(&g, owl_message_get_zwriteline(m));
+      } else {
+	owl_function_makemsg("You cannot reply to this admin message");
+      }
+    } else {
+      if (owl_message_is_login(m)) {
+	class="MESSAGE";
+	inst="PERSONAL";
+	to=owl_message_get_sender(m);
+      } else if (type==1) {
+	class="MESSAGE";
+	inst="PERSONAL";
+	to=owl_message_get_sender(m);
+      } else {
+	class=owl_message_get_class(m);
+	inst=owl_message_get_instance(m);
+	to=owl_message_get_recipient(m);
+	if (!strcmp(to, "") || !strcmp(to, "*")) {
+	  to="";
+	} else if (to[0]=='@') {
+	  /* leave it, to get the realm */
+	} else {
+	  to=owl_message_get_sender(m);
+	}
+      }
+      
+      /* create the command line */
+      strcpy(buff, "zwrite");
+      if (strcasecmp(class, "message")) {
+	sprintf(buff, "%s -c %s%s%s", buff, owl_getquoting(class), class, owl_getquoting(class));
+      }
+      if (strcasecmp(inst, "personal")) {
+	sprintf(buff, "%s -i %s%s%s", buff, owl_getquoting(inst), inst, owl_getquoting(inst));
+      }
+      if (*to != '\0') {
+	char *tmp;
+	tmp=pretty_sender(to);
+	sprintf(buff, "%s %s", buff, tmp);
+	owl_free(tmp);
+      }
+
+      if (enter) {
+	owl_history_store(owl_global_get_history(&g), buff);
+	owl_function_zwrite_setup(buff);
+	owl_global_set_buffercommand(&g, buff);
+      } else {
+	owl_function_start_command(buff);
+      }
+    }
+  }
+}
+
+void owl_function_zlocate(char *user, int auth) {
+  char buff[LINE], myuser[LINE];
+  char *ptr;
+
+  strcpy(myuser, user);
+  ptr=strchr(myuser, '@');
+  if (!ptr) {
+    strcat(myuser, "@");
+    strcat(myuser, ZGetRealm());
+  }
+
+  owl_zephyr_zlocate(myuser, buff, auth);
+  owl_function_popless_text(buff);
+}
+
+void owl_function_start_command(char *line) {
+  int i, j;
+  owl_editwin *tw;
+
+  tw=owl_global_get_typwin(&g);
+  owl_global_set_typwin_active(&g);
+  owl_editwin_set_locktext(tw, "command: ");
+  owl_global_set_needrefresh(&g);
+
+  j=strlen(line);
+  for (i=0; i<j; i++) {
+    owl_editwin_process_char(tw, line[i]);
+  }
+  owl_editwin_redisplay(tw, 0);
+}
+
+char *owl_function_exec(int argc, char **argv, char *buff, int type) {
+  /* if type == 1 display in a popup
+   * if type == 2 display an admin messages
+   * if type == 0 return output
+   * else display in a popup
+   */
+  char *newbuff, *redirect = " 2>&1 < /dev/null";
+  char *out, buff2[1024];
+  int size;
+  FILE *p;
+
+  if (argc<2) {
+    owl_function_makemsg("Wrong number of arguments to the pexec command");
+    return NULL;
+  }
+
+  buff = skiptokens(buff, 1);
+  newbuff = owl_malloc(strlen(buff)+strlen(redirect)+1);
+  strcpy(newbuff, buff);
+  strcat(newbuff, redirect);
+
+  p=popen(newbuff, "r");
+  out=owl_malloc(1024);
+  size=1024;
+  strcpy(out, "");
+  while (fgets(buff2, 1024, p)!=NULL) {
+    size+=1024;
+    out=owl_realloc(out, size);
+    strcat(out, buff2);
+  }
+  pclose(p);
+
+  if (type==1) {
+    owl_function_popless_text(out);
+  } else if (type==0) {
+    return out;
+  } else if (type==2) {
+    owl_function_adminmsg(buff, out);
+  } else {
+    owl_function_popless_text(out);
+  }
+  owl_free(out);
+  return NULL;
+}
+
+
+char *owl_function_perl(int argc, char **argv, char *buff, int type) {
+  /* if type == 1 display in a popup
+   * if type == 2 display an admin messages
+   * if type == 0 return output
+   * else display in a popup
+   */
+  char *perlout;
+
+  if (argc<2) {
+    owl_function_makemsg("Wrong number of arguments to perl command");
+    return NULL;
+  }
+
+  /* consume first token (argv[0]) */
+  buff = skiptokens(buff, 1);
+
+  perlout = owl_config_execute(buff);
+  if (perlout) { 
+    if (type==1) {
+      owl_function_popless_text(perlout);
+    } else if (type==2) {
+      owl_function_adminmsg(buff, perlout);
+    } else if (type==0) {
+      return perlout;
+    } else {
+      owl_function_popless_text(perlout);
+    }
+    owl_free(perlout);
+  }
+  return NULL;
+}
+
+
+void owl_function_change_view(char *filtname) {
+  owl_view *v;
+  owl_filter *f;
+
+  v=owl_global_get_current_view(&g);
+  f=owl_global_get_filter(&g, filtname);
+  if (!f) {
+    owl_function_makemsg("Unknown filter");
+    return;
+  }
+
+  owl_view_free(v);
+  owl_view_create(v, f);
+
+  owl_global_set_curmsg(&g, 0);
+  owl_global_set_curmsg_vert_offset(&g, 0);
+  owl_global_set_direction_downwards(&g);
+  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+}
+
+void owl_function_create_filter(int argc, char **argv) {
+  owl_filter *f;
+  owl_view *v;
+  int ret, inuse=0;
+
+  if (argc < 2) {
+    owl_function_makemsg("Wrong number of arguments to filter command");
+    return;
+  }
+
+  v=owl_global_get_current_view(&g);
+
+  /* don't touch the all filter */
+  if (!strcmp(argv[1], "all")) {
+    owl_function_makemsg("You may not change the 'all' filter.");
+    return;
+  }
+
+  /* deal with the case of trying change the filter color */
+  if (argc==4 && !strcmp(argv[2], "-c")) {
+    f=owl_global_get_filter(&g, argv[1]);
+    if (!f) {
+      owl_function_makemsg("The filter '%s' does not exist.", argv[1]);
+      return;
+    }
+    owl_filter_set_color(f, owl_util_string_to_color(argv[3]));
+    owl_global_set_needrefresh(&g);
+    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+    return;
+  }
+
+  /* create the filter and check for errors */
+  f=owl_malloc(sizeof(owl_filter));
+  ret=owl_filter_init(f, argv[1], argc-2, argv+2);
+  if (ret==-1) {
+    owl_free(f);
+    owl_function_makemsg("Invalid filter syntax");
+    return;
+  }
+
+  /* if the named filter is in use by the current view, remember it */
+  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
+    inuse=1;
+  }
+
+  /* if the named filter already exists, nuke it */
+  if (owl_global_get_filter(&g, argv[1])) {
+    owl_global_remove_filter(&g, argv[1]);
+  }
+
+  /* add the filter */
+  owl_global_add_filter(&g, f);
+
+  /* if it was in use by the current view then update */
+  if (inuse) {
+    owl_function_change_view(argv[1]);
+  }
+  owl_global_set_needrefresh(&g);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+}
+
+void owl_function_show_filters() {
+  owl_list *l;
+  owl_filter *f;
+  int i, j;
+  owl_fmtext fm;
+
+  owl_fmtext_init_null(&fm);
+
+  l=owl_global_get_filterlist(&g);
+  j=owl_list_get_size(l);
+
+  owl_fmtext_append_bold(&fm, "Filters:\n");
+
+  for (i=0; i<j; i++) {
+    f=owl_list_get_element(l, i);
+    owl_fmtext_append_normal(&fm, "   ");
+    if (owl_global_get_hascolors(&g)) {
+      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_color(f));
+    } else {
+      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
+    }
+    owl_fmtext_append_normal(&fm, "\n");
+  }
+  owl_function_popless_fmtext(&fm);
+  owl_fmtext_free(&fm);
+}
+
+void owl_function_show_filter(char *name) {
+  owl_filter *f;
+  char buff[5000];
+
+  f=owl_global_get_filter(&g, name);
+  if (!f) {
+    owl_function_makemsg("There is no filter with that name");
+    return;
+  }
+  owl_filter_print(f, buff);
+  owl_function_popless_text(buff);
+}
+
+void owl_function_show_zpunts() {
+  owl_filter *f;
+  owl_list *fl;
+  char buff[5000];
+  owl_fmtext fm;
+  int i, j;
+
+  owl_fmtext_init_null(&fm);
+
+  fl=owl_global_get_puntlist(&g);
+  j=owl_list_get_size(fl);
+  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
+
+  for (i=0; i<j; i++) {
+    f=owl_list_get_element(fl, i);
+    owl_filter_print(f, buff);
+    owl_fmtext_append_normal(&fm, buff);
+  }
+  owl_function_popless_fmtext(&fm);
+  owl_fmtext_free(&fm);
+}
+
+void owl_function_fastclassinstfilt(char *class, char *instance) {
+  /* narrow to the current class, instance.  If instance is null then
+     just narrow to the current class */
+  owl_list *fl;
+  owl_filter *f;
+  char *argbuff, *filtname;
+  int len;
+
+  fl=owl_global_get_filterlist(&g);
+
+  /* name for the filter */
+  len=strlen(class)+30;
+  if (instance) len+=strlen(instance);
+  filtname=owl_malloc(len);
+  if (!instance) {
+    sprintf(filtname, "class-%s", class);
+  } else {
+    sprintf(filtname, "class-%s-instance-%s", class, instance);
+  }
+  downstr(filtname);
+
+  /* if it already exists then go with it.  This lets users override */
+  if (owl_global_get_filter(&g, filtname)) {
+    owl_function_change_view(filtname);
+    owl_free(filtname);
+    return;
+  }
+
+  /* create the new filter */
+  argbuff=owl_malloc(len+20);
+  sprintf(argbuff, "( class ^%s$ )", class);
+  if (instance) {
+    sprintf(argbuff, "%s and ( instance ^%s$ )", argbuff, instance);
+  }
+
+  f=owl_malloc(sizeof(owl_filter));
+  owl_filter_init_fromstring(f, filtname, argbuff);
+
+  /* add it to the global list */
+  owl_global_add_filter(&g, f);
+
+  /* set the current view to use it */
+  owl_function_change_view(filtname);
+
+  owl_free(argbuff);
+  owl_free(filtname);
+}
+
+void owl_function_fastuserfilt(char *user) {
+  owl_filter *f;
+  char *argbuff, *longuser, *shortuser, *filtname;
+
+  /* stick the local realm on if it's not there */
+  longuser=long_sender(user);
+  shortuser=pretty_sender(user);
+
+  /* name for the filter */
+  filtname=owl_malloc(strlen(shortuser)+20);
+  sprintf(filtname, "user-%s", shortuser);
+
+  /* if it already exists then go with it.  This lets users override */
+  if (owl_global_get_filter(&g, filtname)) {
+    owl_function_change_view(filtname);
+    owl_free(filtname);
+    return;
+  }
+
+  /* create the new-internal filter */
+  f=owl_malloc(sizeof(owl_filter));
+
+  argbuff=owl_malloc(strlen(longuser)+200);
+  sprintf(argbuff, "( ( class ^message$ ) and ( instance ^personal$ ) and ( sender ^%s$ ) )", longuser);
+  sprintf(argbuff, "%s or ( ( type ^admin$ ) and ( recipient %s ) )", argbuff, shortuser);
+  sprintf(argbuff, "%s or ( ( class ^login$ ) and ( sender ^%s$ ) )", argbuff, longuser);
+
+  owl_filter_init_fromstring(f, filtname, argbuff);
+
+  /* add it to the global list */
+  owl_global_add_filter(&g, f);
+
+  /* set the current view to use it */
+  owl_function_change_view(filtname);
+
+  /* free stuff */
+  owl_free(argbuff);
+  owl_free(filtname);
+  owl_free(longuser);
+  owl_free(shortuser);
+    
+}
+
+/* If flag is 1, marks for deletion.  If flag is 0,
+ * unmarks for deletion. */
+void owl_function_delete_curview_msgs(int flag) {
+  owl_view *v;
+  int i, j;
+
+  v=owl_global_get_current_view(&g);
+  j=owl_view_get_size(v);
+  for (i=0; i<j; i++) {
+    if (flag == 1) {
+      owl_message_mark_delete(owl_view_get_element(v, i));
+    } else if (flag == 0) {
+      owl_message_unmark_delete(owl_view_get_element(v, i));
+    }
+  }
+
+  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
+
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));  
+}
+
+void owl_function_smartnarrow(int type) {
+  /* if the curmsg is a personal message narrow
+   *    to the converstaion with that user.
+   * If the curmsg is a class message, instance foo, recip *
+   *    message, narrow to the class, inst.
+   * If the curmsg is a class message and type==0 then narrow
+   *    to the class
+   * If the curmsg is a class message and type==1 then narrow
+   *    to the class, instance
+   */
+  owl_view *v;
+  owl_message *m;
+  char *sender;
+  
+  v=owl_global_get_current_view(&g);
+  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
+
+  if (owl_view_get_size(v)==0) {
+    owl_function_makemsg("No message selected\n");
+    return;
+  }
+
+  /* for now we skip admin messages. */
+  if (owl_message_is_admin(m)) {
+    owl_function_makemsg("Narrowing on an admin message has not been implemented yet.  Check back soon.");
+    return;
+  }
+
+  /* narrow personal and login messages to the sender */
+  if (owl_message_is_personal(m) || owl_message_is_login(m)) {
+    if (owl_message_is_zephyr(m)) {
+      sender=pretty_sender(owl_message_get_sender(m));
+      owl_function_fastuserfilt(sender);
+      free(sender);
+    }
+    return;
+  }
+
+  /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
+  if (!strcasecmp(owl_message_get_class(m), "message") &&
+      !owl_message_is_personal(m)) {
+    owl_function_fastclassinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
+    return;
+  }
+
+  /* otherwise narrow to the class */
+  if (type==0) {
+    owl_function_fastclassinstfilt(owl_message_get_class(m), NULL);
+  } else if (type==1) {
+    owl_function_fastclassinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
+  }
+}
+
+void owl_function_color_current_filter(char *color) {
+  owl_filter *f;
+  char *name;
+
+  name=owl_view_get_filtname(owl_global_get_current_view(&g));
+  f=owl_global_get_filter(&g, name);
+  if (!f) {
+    owl_function_makemsg("Unknown filter");
+    return;
+  }
+
+  /* don't touch the all filter */
+  if (!strcmp(name, "all")) {
+    owl_function_makemsg("You may not change the 'all' filter.");
+    return;
+  }
+
+  /* deal with the case of trying change the filter color */
+  owl_filter_set_color(f, owl_util_string_to_color(color));
+  owl_global_set_needrefresh(&g);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+}
+
+void owl_function_show_colors() {
+  owl_fmtext fm;
+
+  owl_fmtext_init_null(&fm);
+  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT);
+  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED);
+  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN);
+  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW);
+  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE);
+  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA);
+  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN);
+  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE);
+
+  owl_function_popless_fmtext(&fm);
+  owl_fmtext_free(&fm);
+}
+
+void owl_function_zpunt(char *class, char *inst, char *recip, int direction) {
+  /* add the given class, inst, recip to the punt list for filtering.
+   *   if direction==0 then punt
+   *   if direction==1 then unpunt */
+  owl_filter *f;
+  owl_list *fl;
+  char *buff;
+  int ret, i, j;
+
+  fl=owl_global_get_puntlist(&g);
+
+  /* first, create the filter */
+  f=malloc(sizeof(owl_filter));
+  buff=malloc(strlen(class)+strlen(inst)+strlen(recip)+100);
+  if (!strcmp(recip, "*")) {
+    sprintf(buff, "class ^%s$ and instance ^%s$", class, inst);
+  } else {
+    sprintf(buff, "class ^%s$ and instance ^%s$ and recipient %s", class, inst, recip);
+  }
+  owl_function_debugmsg("About to filter %s", buff);
+  ret=owl_filter_init_fromstring(f, "punt-filter", buff);
+  owl_free(buff);
+  if (ret) {
+    owl_function_makemsg("Error creating filter for zpunt");
+    owl_filter_free(f);
+    return;
+  }
+
+  /* Check for an identical filter */
+  j=owl_list_get_size(fl);
+  for (i=0; i<j; i++) {
+    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
+      /* if we're punting, then just silently bow out on this duplicate */
+      if (direction==0) {
+	owl_filter_free(f);
+	return;
+      }
+
+      /* if we're unpunting, then remove this filter from the puntlist */
+      if (direction==1) {
+	owl_filter_free(owl_list_get_element(fl, i));
+	owl_list_remove_element(fl, i);
+	return;
+      }
+    }
+  }
+
+  /* If we're punting, add the filter to the global punt list */
+  if (direction==0) {
+    owl_list_append_element(fl, f);
+  }
+}
+
+void owl_function_activate_keymap(char *keymap) {
+  if (!owl_keyhandler_activate(owl_global_get_keyhandler(&g), keymap)) {
+    owl_function_makemsg("Unable to activate keymap '%s'", keymap);
+  }
+}
+
+
+void owl_function_show_keymaps() {
+  owl_list l;
+  owl_fmtext fm;
+
+  owl_fmtext_init_null(&fm);
+  owl_fmtext_append_bold(&fm, "Keymaps:   ");
+  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
+  owl_keyhandler_get_keymap_names(owl_global_get_keyhandler(&g), &l);
+  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
+  owl_fmtext_append_normal(&fm, "\n");
+  owl_function_popless_fmtext(&fm);
+  owl_keyhandler_keymap_namelist_free(&l);
+  owl_fmtext_free(&fm);
+}
+
+char *owl_function_keymap_summary(void *name) {
+  owl_keymap *km 
+    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
+  if (km) return owl_keymap_summary(km);
+  else return(NULL);
+}
+
+/* TODO: implement for real */
+void owl_function_show_keymap(char *name) {
+  owl_fmtext  fm;
+  owl_keymap *km;
+
+  owl_fmtext_init_null(&fm);
+  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
+  if (km) {
+    owl_keymap_get_details(km, &fm);
+  } else {
+    owl_fmtext_append_normal(&fm, "No such keymap...\n");
+  }  
+  owl_function_popless_fmtext(&fm);
+  owl_fmtext_free(&fm);
+}
+
+
+void owl_function_help_for_command(char *cmdname) {
+  owl_fmtext   fm;
+
+  owl_fmtext_init_null(&fm);
+  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
+  owl_function_popless_fmtext(&fm);  
+  owl_fmtext_free(&fm);
+}
Index: global.c
===================================================================
--- global.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ global.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,593 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include "owl.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+void owl_global_init(owl_global *g) {
+  struct hostent *hent;
+  char hostname[MAXHOSTNAMELEN];
+  char buff[MAXPATHLEN];
+
+  gethostname(hostname, MAXHOSTNAMELEN);
+  hent=gethostbyname(hostname);
+  if (!hent) {
+    strcpy(g->thishost, "localhost");
+  } else {
+    strcpy(g->thishost, hent->h_name);
+  }
+
+  owl_context_init(&g->ctx);
+  owl_context_set_startup(&g->ctx);
+  g->curmsg=0;
+  g->topmsg=0;
+  g->needrefresh=1;
+
+  owl_variable_dict_setup(&(g->vars));
+  owl_cmddict_setup(&(g->cmds));
+
+  g->lines=LINES;
+  g->cols=COLS;
+
+  g->rightshift=0;
+
+  owl_editwin_init(&(g->tw), NULL, owl_global_get_typwin_lines(g), g->cols, OWL_EDITWIN_STYLE_ONELINE);
+
+  owl_keyhandler_init(&g->kh);
+  owl_keys_setup_keymaps(&g->kh);
+
+  owl_list_create(&(g->filterlist));
+  owl_list_create(&(g->puntlist));
+  g->curmsg_vert_offset=0;
+  g->resizepending=0;
+  g->typwinactive=0;
+  g->direction=OWL_DIRECTION_DOWNWARDS;
+  g->zaway=0;
+  if (has_colors()) {
+    g->hascolors=1;
+  }
+  g->colorpairs=COLOR_PAIRS;
+  g->debug=OWL_DEBUG;
+  g->starttime=time(NULL); /* assumes we call init only a start time */
+  strcpy(g->buffercommand, "");
+
+  owl_global_set_config_format(g, 0);
+  owl_global_set_userclue(g, OWL_USERCLUE_NONE);
+  owl_global_set_no_have_config(g);
+  owl_history_init(&(g->hist));
+  g->nextmsgid=0;
+
+  owl_filterelement_create_true(&(g->fe_true));
+  owl_filterelement_create_false(&(g->fe_false));
+  owl_filterelement_create_null(&(g->fe_null));
+
+  _owl_global_setup_windows(g);
+
+  /* Fill in some variables which don't have constant defaults */
+  /* TODO: come back later and check passwd file first */
+  strcpy(g->homedir, getenv("HOME"));
+  sprintf(buff, "%s/zlog/people/", owl_global_get_homedir(g));
+  owl_global_set_logpath(g, buff);
+  sprintf(buff, "%s/zlog/class/", owl_global_get_homedir(g));
+  owl_global_set_classlogpath(g, buff);
+  
+
+  owl_messagelist_create(&(g->msglist));
+  owl_mainwin_init(&(g->mw));
+  owl_popwin_init(&(g->pw));
+}
+
+void _owl_global_setup_windows(owl_global *g) {
+  int lines, cols, typwin_lines;
+
+  lines=g->lines;
+  cols=g->cols;
+  typwin_lines = owl_global_get_typwin_lines(g);
+
+  /* set the new window sizes */
+  g->recwinlines=g->lines-(typwin_lines+2);
+
+  /* create the new windows */
+  g->recwin=newwin(g->recwinlines, cols, 0, 0);
+  g->sepwin=newwin(1, cols, g->recwinlines, 0);
+  g->msgwin=newwin(1, cols, g->recwinlines+1, 0);
+  g->typwin=newwin(typwin_lines, cols, g->recwinlines+2, 0);
+
+  owl_editwin_set_curswin(&(g->tw), g->typwin, typwin_lines, g->cols);
+
+  idlok(g->typwin, FALSE);
+  idlok(g->recwin, FALSE);
+  idlok(g->sepwin, FALSE);
+  idlok(g->msgwin, FALSE);
+
+  nodelay(g->typwin, 1);
+  keypad(g->typwin, TRUE);
+  wmove(g->typwin, 0, 0);
+
+  meta(g->typwin, TRUE);
+}
+
+owl_context *owl_global_get_context(owl_global *g) {
+  return(&g->ctx);
+}
+			 
+int owl_global_get_lines(owl_global *g) {
+  return(g->lines);
+}
+
+int owl_global_get_cols(owl_global *g) {
+  return(g->cols);
+}
+
+int owl_global_get_recwin_lines(owl_global *g) {
+  return(g->recwinlines);
+}
+
+/* curmsg */
+
+int owl_global_get_curmsg(owl_global *g) {
+  return(g->curmsg);
+}
+
+void owl_global_set_curmsg(owl_global *g, int i) {
+  g->curmsg=i;
+  /* we will reset the vertical offset from here */
+  /* we might want to move this out to the functions later */
+  owl_global_set_curmsg_vert_offset(g, 0);
+}
+
+/* topmsg */
+
+int owl_global_get_topmsg(owl_global *g) {
+  return(g->topmsg);
+}
+
+void owl_global_set_topmsg(owl_global *g, int i) {
+  g->topmsg=i;
+}
+
+/* windows */
+
+owl_mainwin *owl_global_get_mainwin(owl_global *g) {
+  return(&(g->mw));
+}
+
+owl_popwin *owl_global_get_popwin(owl_global *g) {
+  return(&(g->pw));
+}
+
+/* msglist */
+
+owl_messagelist *owl_global_get_msglist(owl_global *g) {
+  return(&(g->msglist));
+}
+
+/* keyhandler */
+
+owl_keyhandler *owl_global_get_keyhandler(owl_global *g) {
+  return(&(g->kh));
+}
+
+/* curses windows */
+
+WINDOW *owl_global_get_curs_recwin(owl_global *g) {
+  return(g->recwin);
+}
+
+WINDOW *owl_global_get_curs_sepwin(owl_global *g) {
+  return(g->sepwin);
+}
+
+WINDOW *owl_global_get_curs_msgwin(owl_global *g) {
+  return(g->msgwin);
+}
+
+WINDOW *owl_global_get_curs_typwin(owl_global *g) {
+  return(g->typwin);
+}
+
+/* typwin */
+
+owl_editwin *owl_global_get_typwin(owl_global *g) {
+  return(&(g->tw));
+}
+
+/* buffercommand */
+
+void owl_global_set_buffercommand(owl_global *g, char *command) {
+  strcpy(g->buffercommand, command);
+}
+
+char *owl_global_get_buffercommand(owl_global *g) {
+  return(g->buffercommand);
+}
+
+/* refresh */
+
+int owl_global_is_needrefresh(owl_global *g) {
+  if (g->needrefresh==1) return(1);
+  return(0);
+}
+
+void owl_global_set_needrefresh(owl_global *g) {
+  g->needrefresh=1;
+}
+
+void owl_global_set_noneedrefresh(owl_global *g) {
+  g->needrefresh=0;
+}
+
+/* variable dictionary */
+
+owl_vardict *owl_global_get_vardict(owl_global *g) {
+  return &(g->vars);
+}
+
+/* command dictionary */
+
+owl_cmddict *owl_global_get_cmddict(owl_global *g) {
+  return &(g->cmds);
+}
+
+/* rightshift */
+
+void owl_global_set_rightshift(owl_global *g, int i) {
+  g->rightshift=i;
+}
+
+int owl_global_get_rightshift(owl_global *g) {
+  return(g->rightshift);
+}
+
+/* typwin */
+
+int owl_global_is_typwin_active(owl_global *g) {
+  if (g->typwinactive==1) return(1);
+  return(0);
+}
+
+void owl_global_set_typwin_active(owl_global *g) {
+  g->typwinactive=1;
+}
+
+void owl_global_set_typwin_inactive(owl_global *g) {
+  g->typwinactive=0;
+}
+
+/* resize */
+
+void owl_global_set_resize_pending(owl_global *g) {
+  g->resizepending=1;
+}
+
+char *owl_global_get_homedir(owl_global *g) {
+  return(g->homedir);
+}
+
+int owl_global_get_direction(owl_global *g) {
+  return(g->direction);
+}
+
+void owl_global_set_direction_downwards(owl_global *g) {
+  g->direction=OWL_DIRECTION_DOWNWARDS;
+}
+
+void owl_global_set_direction_upwards(owl_global *g) {
+  g->direction=OWL_DIRECTION_UPWARDS;
+}
+
+/* perl stuff */
+
+void owl_global_set_perlinterp(owl_global *g, void *p) {
+  g->perl=p;
+}
+
+void *owl_global_get_perlinterp(owl_global *g) {
+  return(g->perl);
+}
+
+int owl_global_is_config_format(owl_global *g) {
+  if (g->config_format) return(1);
+  return(0);
+}
+
+void owl_global_set_config_format(owl_global *g, int state) {
+  if (state==1) {
+    g->config_format=1;
+  } else {
+    g->config_format=0;
+  }
+}
+
+void owl_global_set_have_config(owl_global *g) {
+  g->haveconfig=1;
+}
+
+void owl_global_set_no_have_config(owl_global *g) {
+  g->haveconfig=0;
+}
+
+int owl_global_have_config(owl_global *g) {
+  if (g->haveconfig) return(1);
+  return(0);
+}
+
+void owl_global_resize(owl_global *g, int x, int y) {
+  /* resize the screen.  If x or y is 0 use the terminal size */
+  struct winsize size;
+    
+  if (!g->resizepending) return;
+
+  /* delete the current windows */
+  delwin(g->recwin);
+  delwin(g->sepwin);
+  delwin(g->msgwin);
+  delwin(g->typwin);
+  if (!isendwin()) {
+    endwin();
+  }
+
+  refresh();
+
+  /* get the new size */
+  ioctl(STDIN_FILENO, TIOCGWINSZ, &size);
+  if (x==0) {
+    g->lines=size.ws_row;
+  } else {
+    g->lines=x;
+  }
+
+  if (y==0) {
+    g->cols=size.ws_col;
+  } else {
+    g->cols=y;
+  }
+
+  /* resizeterm(size.ws_row, size.ws_col); */
+
+  /* re-initialize the windows */
+  _owl_global_setup_windows(g);
+
+  /* refresh stuff */
+  g->needrefresh=1;
+  owl_mainwin_redisplay(&(g->mw));
+  sepbar(NULL);
+
+  if (owl_global_is_typwin_active(g)) {
+    owl_editwin_redisplay(&(g->tw), 0);
+  }	
+  /* TODO: this should handle other forms of popwins */
+  if (owl_popwin_is_active(owl_global_get_popwin(g)) 
+      && owl_global_get_viewwin(g)) {
+    owl_popwin_refresh(owl_global_get_popwin(g));
+    owl_viewwin_redisplay(owl_global_get_viewwin(g), 0);
+  }
+
+  /*
+  char buff[1024];
+  sprintf(buff, "New size is %i lines, %i cols.\n", size.ws_row, size.ws_col);
+  owl_function_makemsg(buff);
+  */
+  owl_function_makemsg("");
+
+  g->resizepending=0;
+}
+
+/* tty (not fully implemented yet) */
+
+void owl_global_set_tty(owl_global *g, char *tty) {
+  if (tty) {
+    strcpy(g->thistty, tty);
+  } else if (getenv("DISPLAY")) {
+    strcpy(g->thistty, getenv("DISPLAY"));
+  } else if (ttyname(fileno(stdout))) {
+    strcpy(g->thistty, ttyname(fileno(stdout)));
+    if (!strncmp(g->thistty, "/dev/", 5)) {
+      strcpy(g->thistty, g->thistty+5);
+    }
+  } else {
+    strcpy(g->thistty, "unknown");
+  }
+    
+#ifdef HAVE_LIBZEPHYR_ZINITLOCATIONINFO
+  ZInitLocationInfo(g->thishost, g->thistty); 
+#endif
+}
+
+
+/* debug */
+
+int owl_global_is_debug_fast(owl_global *g) {
+  if (g->debug) return(1);
+  return(0);
+}
+
+/* starttime */
+
+time_t owl_global_get_starttime(owl_global *g) {
+  return(g->starttime);
+}
+
+time_t owl_global_get_runtime(owl_global *g) {
+  return(time(NULL)-g->starttime);
+}
+
+void owl_global_get_runtime_string(owl_global *g, char *buff) {
+  time_t diff;
+
+  diff=time(NULL)-owl_global_get_starttime(g);
+
+  /* print something nicer later */   
+  sprintf(buff, "%i seconds", (int) diff);
+}
+
+/* userclue */
+
+void owl_global_set_userclue(owl_global *g, int clue) {
+  g->userclue=clue;
+}
+
+void owl_global_add_userclue(owl_global *g, int clue) {
+  g->userclue|=clue;
+}
+
+int owl_global_get_userclue(owl_global *g) {
+  return(g->userclue);
+}
+
+int owl_global_is_userclue(owl_global *g, int clue) {
+  if (g->userclue & clue) return(1);
+  return(0);
+}
+
+/* viewwin */
+
+owl_viewwin *owl_global_get_viewwin(owl_global *g) {
+  return(&(g->vw));
+}
+
+
+/* vert offset */
+
+int owl_global_get_curmsg_vert_offset(owl_global *g) {
+  return(g->curmsg_vert_offset);
+}
+
+void owl_global_set_curmsg_vert_offset(owl_global *g, int i) {
+  g->curmsg_vert_offset=i;
+}
+
+/* startup args */
+
+void owl_global_set_startupargs(owl_global *g, int argc, char **argv) {
+  int i;
+
+  strcpy(g->startupargs, "");
+  for (i=0; i<argc; i++) {
+    sprintf(g->startupargs, "%s%s ", g->startupargs, argv[i]);
+  }
+  g->startupargs[strlen(g->startupargs)-1]='\0';
+}
+
+char *owl_global_get_startupargs(owl_global *g) {
+  return(g->startupargs);
+}
+
+/* history */
+
+owl_history *owl_global_get_history(owl_global *g) {
+  return(&(g->hist));
+}
+
+/* filterlist */
+
+owl_list *owl_global_get_filterlist(owl_global *g) {
+  return(&(g->filterlist));
+}
+
+owl_filter *owl_global_get_filter(owl_global *g, char *name) {
+  int i, j;
+  owl_filter *f;
+
+  j=owl_list_get_size(&(g->filterlist));
+  for (i=0; i<j; i++) {
+    f=owl_list_get_element(&(g->filterlist), i);
+    if (!strcmp(name, owl_filter_get_name(f))) {
+      return(f);
+    }
+  }
+  return(NULL);
+}
+
+void owl_global_add_filter(owl_global *g, owl_filter *f) {
+  owl_list_append_element(&(g->filterlist), f);
+}
+
+void owl_global_remove_filter(owl_global *g, char *name) {
+  int i, j;
+  owl_filter *f;
+
+  j=owl_list_get_size(&(g->filterlist));
+  for (i=0; i<j; i++) {
+    f=owl_list_get_element(&(g->filterlist), i);
+    if (!strcmp(name, owl_filter_get_name(f))) {
+      owl_filter_free(f);
+      owl_list_remove_element(&(g->filterlist), i);
+      break;
+    }
+  }
+}
+
+/* nextmsgid */
+
+int owl_global_get_nextmsgid(owl_global *g) {
+  return(g->nextmsgid++);
+}
+
+/* current view */
+
+owl_view *owl_global_get_current_view(owl_global *g) {
+  return(&(g->current_view));
+}
+
+owl_filterelement *owl_global_get_filterelement_true(owl_global *g) {
+  return(&(g->fe_true));
+}
+
+owl_filterelement *owl_global_get_filterelement_false(owl_global *g) {
+  return(&(g->fe_false));
+}
+
+owl_filterelement *owl_global_get_filterelement_null(owl_global *g) {
+  return(&(g->fe_null));
+}
+
+/* has colors */
+
+int owl_global_get_hascolors(owl_global *g) {
+  if (g->hascolors) return(1);
+  return(0);
+}
+
+/* color pairs */
+
+int owl_global_get_colorpairs(owl_global *g) {
+  return(g->colorpairs);
+}
+
+/* puntlist */
+
+owl_list *owl_global_get_puntlist(owl_global *g) {
+  return(&(g->puntlist));
+}
+
+int owl_global_message_is_puntable(owl_global *g, owl_message *m) {
+  owl_list *pl;
+  int i, j;
+
+  pl=owl_global_get_puntlist(g);
+  j=owl_list_get_size(pl);
+  for (i=0; i<j; i++) {
+    if (owl_filter_message_match(owl_list_get_element(pl, i), m)) return(1);
+  }
+  return(0);
+}
+
+int owl_global_should_followlast(owl_global *g) {
+  owl_view *v;
+  
+  if (!owl_global_is__followlast(g)) return(0);
+  
+  v=owl_global_get_current_view(g);
+  
+  if (owl_global_get_curmsg(g)==owl_view_get_size(v)-1) return(1);
+  return(0);
+}
Index: help.c
===================================================================
--- help.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ help.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,120 @@
+#include "owl.h"
+#include <string.h>
+
+void owl_help() {
+  owl_fmtext fm;
+  char *varname;
+  owl_list varnames;
+  int i, numvarnames;
+
+  owl_fmtext_init_null(&fm);
+  owl_fmtext_append_bold
+     (&fm, 
+      "OWL HELP\n\n");
+
+  owl_fmtext_append_normal
+     (&fm, 
+      "  For help on a specific command use 'help <command>'\n"
+      "  For information on advanced keys, do 'M-x show keymaps'.\n"
+      "  For information on advanced commands, do 'M-x show commands'.\n"
+      "  For information on variables, do 'M-x show variables'.\n\n");
+
+  owl_fmtext_append_bold
+     (&fm, 
+      "  Basic Keys:\n"
+      );
+  owl_fmtext_append_normal
+     (&fm, 
+      "    n             Move to next non-deleted message\n"
+      "    p             Move to previous non-deleted message\n"
+      "    C-n , down    Move to next message\n"
+      "    C-p , up      Move to previous message\n"
+      "    < , >         Move to first, last message\n"
+      "    right , left  Scroll screen left or right\n"
+      "    C-v           Page down\n"
+      "    M-v           Page up\n"
+      "    i             Print more information about a message\n"
+      "    P             Move to the next personal message\n"
+      "    M-P           Move to the preivous personal message\n"
+      "\n"
+      "    d             Mark message for deletion\n"
+      "    u             Undelete a message marked for deletion\n"
+      "    x             Expunge deleted messages\n"
+      "    X             Expunge deleted messages and switch view\n"
+      "    T             Mark all 'trash' messages for deletion\n"
+      "\n"
+      "    z             Start a zwrite command\n"
+      "    r             Reply to the current message\n"
+      "    R             Reply to sender\n"
+      "    C-r           Reply but allow editing of reply line\n"
+      "\n"
+      "    M-n           View zephyrs to selected conversation\n"
+      "    v             Start a view command\n"
+      "\n"
+      "    A             Toggle zaway\n"
+      "    w             Open a URL in the message in netscape\n"
+      "    C-l           Refresh the screen\n"
+      "    C-z           Suspend\n"
+      "    h             Print this help message\n"
+      "    : , M-x       Enter one of the commands below\n"
+      "\n\n"
+      );
+  owl_fmtext_append_bold
+     (&fm, 
+      "  Basic Commands:\n"
+      );
+  owl_fmtext_append_normal
+     (&fm, 
+      "    quit, exit    Exit owl\n"
+      "    help          Get help about commands\n"
+      "    show          Show information about owl (see detailed help)\n"
+      "\n"
+      "    zwrite        Send a zephyr\n"
+      "    reply         Reply to the current zephyr\n"
+      "\n"
+      "    zlog          Send a login or login notification\n"
+      "    subscribe     Subscribe to a zephyr class or instance\n"
+      "    unsubscribe   Unsubscribe to a zephyr class or instance\n"
+      "    unsuball      Unsubscribe from all zephyr classes\n"
+      "    getsubs       Print a list of current subscriptions\n"
+      "    zlocate       Locate a user\n"
+      "    info          Print detailed information about the current message\n"
+      "    filter        Create a message filter\n"
+      "    view          View messages matching a filter\n"
+      "    viewuser      View messages to or from a particular user\n"
+      "    viewclass     View messages to a particular class\n"
+      "    expunge       Expunge messages marked for deletion\n"
+      "    first         Move to the first message\n"
+      "    last          Move to the last message\n"
+      "    zaway         Turn zaway on or off, or set the message\n"
+      "    load-subs     Load zephyr subscriptions from a file\n"
+      "\n"
+      "    set           Set a variable (see list below)\n"
+      "    print         Print a variable's value (variables listed below)\n"
+      "\n"
+      "    about         Print information about owl\n"
+      "    status        Print status information about the running owl\n"
+      "    version       Print the version number of owl\n"
+      "\n");
+  
+  /* help for variables */
+  owl_fmtext_append_bold(&fm, 
+      "Variables:\n");
+  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
+  owl_variable_get_summaryheader(&fm);
+  numvarnames = owl_list_get_size(&varnames);
+  for (i=0; i<numvarnames; i++) {
+    varname = owl_list_get_element(&varnames, i);
+    if (varname && varname[0]!='_') {
+      owl_variable_get_summary(owl_global_get_vardict(&g), varname, &fm);
+    }
+  }
+  owl_variable_dict_namelist_free(&varnames);
+
+  owl_fmtext_append_normal(&fm, "\n");
+
+  owl_function_popless_fmtext(&fm);
+
+  owl_fmtext_free(&fm);
+}
+
Index: history.c
===================================================================
--- history.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ history.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,66 @@
+#include "owl.h"
+
+void owl_history_init(owl_history *h) {
+  owl_list_create(&(h->hist));
+  h->cur=0;
+  h->touched=0;
+  h->partial=0;
+}
+
+char *owl_history_get_prev(owl_history *h) {
+  h->touched=1;
+
+  if (owl_list_get_size(&(h->hist))==0) return(NULL);
+
+  if (h->cur == owl_list_get_size(&(h->hist))-1) {
+    return(NULL);
+  }
+
+  h->cur++;
+  return(owl_list_get_element(&(h->hist), h->cur));
+}
+
+char *owl_history_get_next(owl_history *h) {
+  if (owl_list_get_size(&(h->hist))==0) return(NULL);
+
+  if (h->cur==0) {
+    return(NULL);
+  }
+
+  h->cur--;
+  return(owl_list_get_element(&(h->hist), h->cur));
+}
+
+void owl_history_store(owl_history *h, char *line) {
+  int size;
+
+  /* if partial is set, remove the first entry first */
+  if (h->partial) {
+    owl_list_remove_element(&(h->hist), 0);
+  }
+    
+  /* if we've reached the max history size, pop off the last element */
+  size=owl_list_get_size(&(h->hist));
+  if (size>OWL_HISTORYSIZE) {
+    owl_free(owl_list_get_element(&(h->hist), size-1));
+    owl_list_remove_element(&(h->hist), size-1);
+  }
+
+  /* add the new line */
+  owl_list_prepend_element(&(h->hist), owl_strdup(line));
+}
+
+void owl_history_set_partial(owl_history *h) {
+  h->partial=1;
+}
+
+void owl_history_reset(owl_history *h) {
+  h->cur=0;
+  h->touched=0;
+  h->partial=0;
+}
+
+int owl_history_is_touched(owl_history *h) {
+  if (h->touched) return(1);
+  return(0);
+}
Index: keybinding.c
===================================================================
--- keybinding.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ keybinding.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,127 @@
+#include <ctype.h>
+#include <string.h>
+#include "owl.h"
+
+
+/*
+ * TODO: Idea for allowing functions to be user-specified --- 
+ *      Have function have a context bitmask that says where it 
+ *      can be used, and have keymaps also have one, and compare
+ *      the two when setting.
+ *      
+ */
+
+/* sets up a new keybinding for a command */
+int owl_keybinding_init(owl_keybinding *kb, char *keyseq, char *command, void (*function_fn)(void), char *desc) {
+  char **ktokens;
+  int    nktokens, i;
+  
+  owl_function_debugmsg("creating binding for <%s> with desc: <%s>\n", keyseq, desc);
+  if (command && !function_fn) {
+    kb->type = OWL_KEYBINDING_COMMAND;
+  } else if (!command && function_fn) {
+    kb->type = OWL_KEYBINDING_FUNCTION;
+  } else {
+    return(-1);
+  }
+
+  ktokens = atokenize(keyseq, " ", &nktokens);
+  if (!ktokens) return(-1);
+  if (nktokens > OWL_KEYMAP_MAXSTACK) {
+    atokenize_free(ktokens, nktokens);
+    return(-1);
+  }
+  kb->j = owl_malloc((nktokens+1)*sizeof(int));
+  for (i=0; i<nktokens; i++) {
+    kb->j[i] = owl_keypress_fromstring(ktokens[i]);
+    if (kb->j[i] == ERR) { 
+      atokenize_free(ktokens, nktokens);
+      owl_free(kb->j);
+      return(-1);
+    }
+  }
+  kb->j[i] = 0;
+
+  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);
+}
+
+/* Releases data associated with a keybinding */
+void owl_keybinding_free(owl_keybinding *kb) {
+  if (kb->j) owl_free(kb->j);
+  if (kb->desc) owl_free(kb->desc);
+  if (kb->command) owl_free(kb->command);
+}
+
+/* Releases data associated with a keybinding, and the kb itself */
+void owl_keybinding_free_all(owl_keybinding *kb) {
+  owl_keybinding_free(kb);
+  owl_free(kb);
+}
+
+/* executes a keybinding */
+void owl_keybinding_execute(owl_keybinding *kb, int j) {
+  if (kb->type == OWL_KEYBINDING_COMMAND && kb->command) {
+    owl_function_command_norv(kb->command);
+  } else if (kb->type == OWL_KEYBINDING_FUNCTION && kb->function_fn) {
+    kb->function_fn();
+  }
+}
+
+/* returns 0 on success */
+int owl_keybinding_stack_tostring(int *j, char *buff, int bufflen) {
+  char *pos = buff;
+  int   rem = bufflen;
+  int   i, n;
+
+  for (i=0; j[i]; i++) {
+    owl_keypress_tostring(j[i], 0, pos, rem-1);
+    if (j[i+1]) strcat(pos, " ");
+    n = strlen(pos);
+    pos += n;
+    rem -= n;
+  }
+  return 0;
+}
+
+/* returns 0 on success */
+int owl_keybinding_tostring(owl_keybinding *kb, char *buff, int bufflen) {
+  return owl_keybinding_stack_tostring(kb->j, buff, bufflen);
+}
+
+char *owl_keybinding_get_desc(owl_keybinding *kb) {
+  return kb->desc;
+}
+
+/* returns 0 on no match, 1 on subset match, and 2 on complete match */
+int owl_keybinding_match(owl_keybinding *kb, int *kpstack) {
+  int *kbstack = kb->j;
+  
+  while (*kbstack && *kpstack) {
+    if (*kbstack != *kpstack) {
+      return 0;
+    }
+    kbstack++; kpstack++;
+  }
+  if (!*kpstack && !*kbstack) {
+    return 2;
+  } else if (!*kpstack) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/* returns 1 if keypress sequence is the same */
+int owl_keybinding_equal(owl_keybinding *kb1, owl_keybinding *kb2) {
+  int *j1 = kb1->j;
+  int *j2 = kb2->j;
+  while (*j1 && *j2) {
+    if (*(j1++) != *(j2++))  return(0);
+  }
+  if (*j1 != *j2) return(0);
+  return(1);
+}
Index: keymap.c
===================================================================
--- keymap.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ keymap.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,271 @@
+#include <string.h>
+#include "owl.h"
+
+/* returns 0 on success */
+int owl_keymap_init(owl_keymap *km, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int)) {
+  if (!name || !desc) return(-1);
+  if ((km->name = owl_strdup(name)) == NULL) return(-1);
+  if ((km->desc = owl_strdup(desc)) == NULL) return(-1);
+  if (0 != owl_list_create(&km->bindings)) return(-1);
+  km->submap = NULL;
+  km->default_fn = default_fn;
+  km->prealways_fn = prealways_fn;
+  km->postalways_fn = postalways_fn;
+  return(0);
+}
+
+/* note that this will free the memory for the bindings! */
+void owl_keymap_free(owl_keymap *km) {
+  owl_free(km->name);
+  owl_free(km->desc);
+  owl_list_free_all(&km->bindings, (void(*)(void*))owl_keybinding_free_all);
+}
+
+void owl_keymap_set_submap(owl_keymap *km, owl_keymap *submap) {
+  km->submap = submap;
+}
+
+/* creates and adds a key binding */
+int owl_keymap_create_binding(owl_keymap *km, char *keyseq, char *command, void (*function_fn)(void), char *desc) {
+  owl_keybinding *kb, *curkb;
+  int i;
+
+  if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1);
+  if (0 != owl_keybinding_init(kb, keyseq, command, function_fn, desc)) {
+    owl_free(kb);
+    return(-1);
+  }
+  /* see if another matching binding, and if so remove it.
+   * otherwise just add this one. 
+   */
+  for (i = owl_list_get_size(&km->bindings)-1; i>=0; i--) {
+    curkb = (owl_keybinding*)owl_list_get_element(&km->bindings, i);
+    if (owl_keybinding_equal(curkb, kb)) {
+      owl_list_remove_element(&km->bindings, i);
+      owl_keybinding_free_all(curkb);
+    }
+  }
+  return owl_list_append_element(&km->bindings, kb);  
+}
+
+/* returns a summary line describing this keymap.  the caller must free. */
+char *owl_keymap_summary(owl_keymap *km) {
+  char *s;
+  int slen;
+  if (!km || !km->name || !km->desc) return NULL;
+  slen = strlen(km->name)+strlen(km->desc)+20;
+  s = owl_malloc(slen);
+  snprintf(s, slen-1, "%-15s - %s", km->name, km->desc);
+  return s;
+}
+
+/* Appends details about the keymap to fm */
+void owl_keymap_get_details(owl_keymap *km, owl_fmtext *fm) {
+  int i, nbindings; 
+  owl_keybinding *kb;
+  
+  owl_fmtext_append_bold(fm, "KEYMAP - ");
+  owl_fmtext_append_bold(fm, km->name);
+  owl_fmtext_append_normal(fm, "\n");
+  if (km->desc) {
+    owl_fmtext_append_normal(fm, OWL_TABSTR "Purpose:    ");
+    owl_fmtext_append_normal(fm, km->desc);
+    owl_fmtext_append_normal(fm, "\n");
+  }
+  if (km->submap) {
+    owl_fmtext_append_normal(fm, OWL_TABSTR "Has submap: ");
+    owl_fmtext_append_normal(fm, km->submap->name);
+    owl_fmtext_append_normal(fm, "\n");
+  }
+    owl_fmtext_append_normal(fm, "\n");
+  if (km->default_fn) {
+    owl_fmtext_append_normal(fm, OWL_TABSTR 
+     "Has a default keypress handler (default_fn).\n");
+  }
+  if (km->prealways_fn) {
+    owl_fmtext_append_normal(fm, OWL_TABSTR
+     "Executes a function (prealways_fn) on every keypress.\n");
+  }
+  if (km->postalways_fn) {
+    owl_fmtext_append_normal(fm, OWL_TABSTR 
+     "Executes a function (postalways_fn) after handling every keypress.\n");
+  }
+
+  owl_fmtext_append_bold(fm, "\nKey bindings:\n\n");  
+  nbindings = owl_list_get_size(&km->bindings);
+  for (i=0; i<nbindings; i++) {
+    char buff[100];
+    owl_cmd *cmd;
+    char *tmpdesc, *desc = "";
+
+    kb = owl_list_get_element(&km->bindings, i);
+    owl_keybinding_tostring(kb, buff, 100);
+    owl_fmtext_append_normal(fm, OWL_TABSTR);
+    owl_fmtext_append_normal(fm, buff);
+    owl_fmtext_append_spaces(fm, 11-strlen(buff));
+    owl_fmtext_append_normal(fm, " - ");
+    if (kb->desc && *kb->desc) {
+      desc = kb->desc;
+    } else if ((cmd=owl_function_get_cmd(kb->command))
+	       && (tmpdesc = owl_cmd_get_summary(cmd))) {
+      desc = tmpdesc;
+    }
+    owl_fmtext_append_normal(fm, desc);
+    if (kb->command && *(kb->command)) {
+      owl_fmtext_append_normal(fm, "   [");
+      owl_fmtext_append_normal(fm, kb->command);
+      owl_fmtext_append_normal(fm, "]");
+    } 
+    owl_fmtext_append_normal(fm, "\n");
+  }
+}
+
+
+
+/***********************************************************************/
+/***************************** KEYHANDLER ******************************/
+/***********************************************************************/
+
+/* NOTE: keyhandler has private access to the internals of keymap */
+
+int owl_keyhandler_init(owl_keyhandler *kh) {
+  if (0 != owl_dict_create(&kh->keymaps)) return(-1); 
+  kh->active = NULL;
+  owl_keyhandler_reset(kh);
+  return(0);
+}
+
+/* adds a new keymap */
+void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km) {
+  owl_dict_insert_element(&kh->keymaps, km->name, km, NULL);
+}
+
+owl_keymap *owl_keyhandler_create_and_add_keymap(owl_keyhandler *kh, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int)) {
+  owl_keymap *km;
+  km = (owl_keymap*)owl_malloc(sizeof(owl_keymap));
+  if (!km) return NULL;
+  owl_keymap_init(km, name, desc, default_fn, prealways_fn, postalways_fn);
+  owl_keyhandler_add_keymap(kh, km);
+  return km;
+}
+
+/* resets state and clears out key stack */
+void owl_keyhandler_reset(owl_keyhandler *kh) {
+  kh->in_esc = 0;
+  memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int));
+  kh->kpstackpos = -1;
+}
+
+owl_keymap *owl_keyhandler_get_keymap(owl_keyhandler *kh, char *mapname) {
+  return (owl_keymap*)owl_dict_find_element(&kh->keymaps, mapname);
+}
+
+/* free the list with owl_cmddict_namelist_free */
+void owl_keyhandler_get_keymap_names(owl_keyhandler *kh, owl_list *l) {
+  owl_dict_get_keys(&kh->keymaps, l);
+}
+
+void owl_keyhandler_keymap_namelist_free(owl_list *l) {
+  owl_list_free_all(l, owl_free);
+}
+
+
+
+/* sets the active keymap, which will also reset any key state.
+ * returns the new keymap, or NULL on failure. */
+owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, char *mapname) {
+  owl_keymap *km;
+  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
+  km = (owl_keymap*)owl_dict_find_element(&kh->keymaps, mapname);
+  if (!km) return(NULL);
+  owl_keyhandler_reset(kh);
+  kh->active = km;
+  return(km);
+}
+
+/* processes a keypress.  returns 0 if the keypress was handled,
+ * 1 if not handled, -1 on error, and -2 if j==ERR. */
+int owl_keyhandler_process(owl_keyhandler *kh, int j) {
+  owl_keymap     *km;
+  owl_keybinding *kb;
+  int i, match;
+
+  if (!kh->active) {
+    owl_function_makemsg("No active keymap!!!");
+    return(-1);
+  }
+  if (j==ERR) {
+	return(-1);
+  }
+
+  owl_function_debugmsg("processkey: got key %d, active keymap %s, stack at %d",
+			j, kh->active->name, kh->kpstackpos);
+
+  /* deal with ESC prefixing */
+  if (!kh->in_esc && j==27) {
+    kh->in_esc = 1;
+    return(0);
+  }
+  if (kh->in_esc) {
+    j = META(j);
+    kh->in_esc = 0;
+  }
+  
+  kh->kpstack[++(kh->kpstackpos)] = j;
+  if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) {
+    owl_keyhandler_reset(kh);
+    owl_function_makemsg("Too many prefix keys pressed...");
+    return(-1);
+  }
+
+  /* deal with the always_fn for the map and submaps */
+  for (km=kh->active; km; km=km->submap) {
+    if (km->prealways_fn) {
+      km->prealways_fn(j);
+    }
+  }
+
+  /* search for a match.  goes through active keymap and then
+   * through submaps... TODO:  clean this up so we can pull
+   * keyhandler and keymap apart.  */
+  for (km=kh->active; km; km=km->submap) {
+    for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) {
+      kb = (owl_keybinding*)owl_list_get_element(&km->bindings, i);
+      match = owl_keybinding_match(kb, kh->kpstack);
+      if (match == 1) {		/* subset match */
+	owl_function_debugmsg("processkey: found subset match in %s", km->name);
+	return(0);
+      } else if (match == 2) {	/* exact match */
+	owl_function_debugmsg("processkey: found exact match in %s", km->name);
+	owl_keybinding_execute(kb, j);
+	owl_keyhandler_reset(kh);
+	if (km->postalways_fn) {
+	  km->postalways_fn(j);
+	}
+	return(0);
+      }
+    }
+  }
+
+  /* see if a default action exists for the active keymap */
+  if (kh->active->default_fn && kh->kpstackpos<1) {
+    owl_function_debugmsg("processkey: executing default_fn");
+    kh->active->default_fn(j);
+    owl_keyhandler_reset(kh);
+    if (kh->active->postalways_fn) {
+      kh->active->postalways_fn(j);
+    }
+    return(0);
+  }
+
+  owl_keyhandler_invalidkey(kh);
+  /* unable to handle */
+  return(1);  
+}
+
+void owl_keyhandler_invalidkey(owl_keyhandler *kh) {
+    char kbbuff[500];
+    owl_keybinding_stack_tostring(kh->kpstack, kbbuff, 500);
+    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
+    owl_keyhandler_reset(kh);
+}
Index: keypress.c
===================================================================
--- keypress.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ keypress.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,199 @@
+#include <ctype.h>
+#include <string.h>
+#include "owl.h"
+
+static struct _owl_keypress_specialmap {
+  int   kj;
+  char *ks;
+} specialmap[] = {
+   { KEY_CODE_YES, 	"CODE_YES" }, 
+   { KEY_MIN, 		"MIN" }, 
+   { KEY_BREAK, 	"BREAK" }, 
+   { KEY_DOWN, 		"DOWN" }, 
+   { KEY_UP, 		"UP" }, 
+   { KEY_LEFT, 		"LEFT" }, 
+   { KEY_RIGHT, 	"RIGHT" }, 
+   { KEY_HOME, 		"HOME" }, 
+   { KEY_BACKSPACE, 	"BACKSPACE" }, 
+   { KEY_F0, 		"F0" }, 
+   { KEY_F(1), 		"F1" }, 
+   { KEY_F(2), 		"F2" }, 
+   { KEY_F(3), 		"F3" }, 
+   { KEY_F(4), 		"F4" }, 
+   { KEY_F(5), 		"F5" }, 
+   { KEY_F(6), 		"F6" }, 
+   { KEY_F(7), 		"F7" }, 
+   { KEY_F(8), 		"F8" }, 
+   { KEY_F(9), 		"F9" }, 
+   { KEY_F(10),		"F10" }, 
+   { KEY_F(11),		"F11" }, 
+   { KEY_F(12),		"F12" }, 
+   { KEY_DL, 		"DL" }, 
+   { KEY_IL, 		"IL" }, 
+   { KEY_DC, 		"DC" }, 
+   { KEY_IC, 		"IC" }, 
+   { KEY_EIC, 		"EIC" }, 
+   { KEY_CLEAR, 	"CLEAR" }, 
+   { KEY_EOS, 		"EOS" }, 
+   { KEY_EOL, 		"EOL" }, 
+   { KEY_SF, 		"SF" }, 
+   { KEY_SR, 		"SR" }, 
+   { KEY_NPAGE, 	"NPAGE" }, 
+   { KEY_PPAGE, 	"PPAGE" }, 
+   { KEY_STAB, 		"STAB" }, 
+   { KEY_CTAB, 		"CTAB" }, 
+   { KEY_CATAB, 	"CATAB" }, 
+   { KEY_ENTER, 	"ENTER" }, 
+   { KEY_SRESET, 	"SRESET" }, 
+   { KEY_RESET, 	"RESET" }, 
+   { KEY_PRINT, 	"PRINT" }, 
+   { KEY_LL, 		"LL" }, 
+   { KEY_A1, 		"A1" }, 
+   { KEY_A3, 		"A3" }, 
+   { KEY_B2, 		"B2" }, 
+   { KEY_C1, 		"C1" }, 
+   { KEY_C3, 		"C3" }, 
+   { KEY_BTAB, 		"BTAB" }, 
+   { KEY_BEG, 		"BEG" }, 
+   { KEY_CANCEL, 	"CANCEL" }, 
+   { KEY_CLOSE, 	"CLOSE" }, 
+   { KEY_COMMAND, 	"COMMAND" }, 
+   { KEY_COPY, 	        "COPY" }, 
+   { KEY_CREATE, 	"CREATE" }, 
+   { KEY_END, 	        "END" }, 
+   { KEY_EXIT, 	        "EXIT" }, 
+   { KEY_FIND, 	        "FIND" }, 
+   { KEY_HELP, 	        "HELP" }, 
+   { KEY_MARK, 	        "MARK" }, 
+   { KEY_MESSAGE, 	"MESSAGE" }, 
+   { KEY_MOVE, 	        "MOVE" }, 
+   { KEY_NEXT, 	        "NEXT" }, 
+   { KEY_OPEN, 	        "OPEN" }, 
+   { KEY_OPTIONS, 	"OPTIONS" }, 
+   { KEY_PREVIOUS, 	"PREVIOUS" }, 
+   { KEY_REDO, 	        "REDO" }, 
+   { KEY_REFERENCE, 	"REFERENCE" }, 
+   { KEY_REFRESH, 	"REFRESH" }, 
+   { KEY_REPLACE, 	"REPLACE" }, 
+   { KEY_RESTART, 	"RESTART" }, 
+   { KEY_RESUME, 	"RESUME" }, 
+   { KEY_SAVE, 	        "SAVE" }, 
+   { KEY_SBEG, 	        "SBEG" }, 
+   { KEY_SCANCEL, 	"SCANCEL" }, 
+   { KEY_SCOMMAND, 	"SCOMMAND" }, 
+   { KEY_SCOPY, 	"SCOPY" }, 
+   { KEY_SCREATE, 	"SCREATE" }, 
+   { KEY_SDC, 	        "SDC" }, 
+   { KEY_SDL, 	        "SDL" }, 
+   { KEY_SELECT, 	"SELECT" }, 
+   { KEY_SEND, 	        "SEND" }, 
+   { KEY_SEOL, 	        "SEOL" }, 
+   { KEY_SEXIT, 	"SEXIT" }, 
+   { KEY_SFIND, 	"SFIND" }, 
+   { KEY_SHELP, 	"SHELP" }, 
+   { KEY_SHOME, 	"SHOME" }, 
+   { KEY_SIC, 	        "SIC" }, 
+   { KEY_SLEFT, 	"SLEFT" }, 
+   { KEY_SMESSAGE, 	"SMESSAGE" }, 
+   { KEY_SMOVE, 	"SMOVE" }, 
+   { KEY_SNEXT, 	"SNEXT" }, 
+   { KEY_SOPTIONS, 	"SOPTIONS" }, 
+   { KEY_SPREVIOUS, 	"SPREVIOUS" }, 
+   { KEY_SPRINT, 	"SPRINT" }, 
+   { KEY_SREDO, 	"SREDO" }, 
+   { KEY_SREPLACE, 	"SREPLACE" }, 
+   { KEY_SRIGHT, 	"SRIGHT" }, 
+   { KEY_SRSUME, 	"SRSUME" }, 
+   { KEY_SSAVE, 	"SSAVE" }, 
+   { KEY_SSUSPEND, 	"SSUSPEND" }, 
+   { KEY_SUNDO, 	"SUNDO" }, 
+   { KEY_SUSPEND, 	"SUSPEND" }, 
+   { KEY_UNDO, 	        "UNDO" }, 
+   { KEY_MOUSE, 	"MOUSE" }, 
+   { KEY_RESIZE, 	"RESIZE" }, 
+   { KEY_MAX, 	        "MAX" }, 
+   { ' ', 	        "SPACE" }, 
+   { 27, 	        "ESCAPE" }, 
+   { 127, 	        "DELETE" }, 
+   { '\r', 	        "CR" }, 
+   { '\n', 	        "LF" }, 
+   { 0,                 NULL }
+};
+
+/* returns 0 on success */
+int owl_keypress_tostring(int j, int esc, char *buff, int bufflen) {
+  char kb[64], kb2[2];
+  struct _owl_keypress_specialmap *sm;
+
+  *kb = '\0';
+  for (sm = specialmap; sm->kj!=0; sm++) {
+    if (j == META(sm->kj) || (esc && j == sm->kj)) {
+      strcat(kb, "M-");
+      strcat(kb, sm->ks);
+      break;
+    } else if (j == sm->kj) {
+      strcat(kb, sm->ks);
+      break;
+    }
+  }
+  if (!*kb) {
+    if (j&META(0)) {
+      strcat(kb, "M-");
+      j &= ~META(0);
+    }
+    if ((CTRL(j) == j)) {
+      strcat(kb, "C-");
+      j |= 0x60;
+
+    }
+    if (isascii(j)) {
+      kb2[0] = j;
+      kb2[1] = 0;
+      strcat(kb, kb2);    
+    }
+  }  
+  if (!*kb) {
+    /* not a valid key */
+    strncpy(buff, "INVALID", bufflen);
+    return(-1);
+  }
+  strncpy(buff, kb, bufflen);
+  return(0);
+}
+
+
+/* returns ERR on failure, else a keycode */
+int owl_keypress_fromstring(char *kb) {
+  struct _owl_keypress_specialmap *sm;
+  int ismeta=0, isctrl=0;
+  int j = ERR;
+
+  while (*kb && kb[1] == '-' && (kb[0] == 'C' || kb[0] == 'M')) {
+    if ((kb[0] == 'C') && (kb[1] == '-')) {
+      isctrl = 1;
+      kb+=2;
+    }
+    if ((kb[0] == 'M') && (kb[1] == '-')) {
+      ismeta = 1;
+      kb+=2;
+    }
+  }
+  if (isascii(kb[0]) && !kb[1]) {
+    j = kb[0];
+  } else {
+    for (sm = specialmap; sm->kj!=0; sm++) {
+      if (!strcmp(sm->ks, kb)) {
+	j = sm->kj;
+      }
+    }
+  }
+  if (j==ERR) return(ERR);
+  if (isctrl) {
+    j = CTRL(j);
+  }
+  if (ismeta) {
+    j = META(j);
+  }
+  return(j);
+}
+
Index: keys.c
===================================================================
--- keys.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ keys.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,290 @@
+#include "owl.h"
+
+
+#define BIND_CMD(kpress, command, desc) \
+         owl_keymap_create_binding(km, kpress, command, NULL, desc);
+
+#define BIND_FNV(kpress, fn, desc) \
+         owl_keymap_create_binding(km, kpress, NULL, fn, desc);
+
+
+/* sets up the default keymaps */
+void owl_keys_setup_keymaps(owl_keyhandler *kh) {
+  owl_keymap *km, *km_global, *km_editwin, *km_mainwin,
+    *km_ew_multi, *km_ew_onel, *km_viewwin;
+
+  
+  /****************************************************************/
+  /*************************** GLOBAL *****************************/
+  /****************************************************************/
+
+  km_global = km = owl_keyhandler_create_and_add_keymap(kh, "global",
+       "System-wide default key bindings", 
+       owl_keys_default_invalid, NULL, NULL);
+  BIND_CMD("C-z",      "suspend",            "Suspend owl");
+
+  /****************************************************************/
+  /***************************** EDIT *****************************/
+  /****************************************************************/
+
+  km_editwin = km = owl_keyhandler_create_and_add_keymap(kh, "edit",
+       "Text editing and command window", 
+       owl_keys_editwin_default, NULL, owl_keys_editwin_postalways);
+  owl_keymap_set_submap(km_editwin, km_global);
+  BIND_CMD("F1",          "help",            "");
+  BIND_CMD("HELP",        "help",            "");
+  BIND_CMD("M-[ 2 8 ~",   "help",            "");
+
+  BIND_CMD("C-c",         "edit:cancel", "");
+  BIND_CMD("C-g",         "edit:cancel", "");
+
+  BIND_CMD("M-f",         "edit:move-next-word", "");
+  BIND_CMD("M-O 3 C",     "edit:move-next-word", "");
+  BIND_CMD("M-b",         "edit:move-prev-word", "");
+  BIND_CMD("M-O 3 D",     "edit:move-prev-word", "");
+
+  BIND_CMD("LEFT",        "edit:move-left", "");
+  BIND_CMD("C-b",         "edit:move-left", "");
+  BIND_CMD("RIGHT",       "edit:move-right", "");
+  BIND_CMD("C-f",         "edit:move-right", "");
+
+  BIND_CMD("M-<",         "edit:move-to-buffer-start", "");
+  BIND_CMD("HOME",        "edit:move-to-buffer-start", "");
+  BIND_CMD("M->",         "edit:move-to-buffer-end", "");
+  BIND_CMD("END",         "edit:move-to-buffer-end", "");
+
+  BIND_CMD("C-a",         "edit:move-to-line-start", "");
+  BIND_CMD("C-e",         "edit:move-to-line-end", "");
+
+  BIND_CMD("M-d",         "edit:delete-next-word", "");
+  BIND_CMD("M-[ 3 ; 3 ~", "edit:delete-next-word", "");
+
+  BIND_CMD("C-h",         "edit:delete-prev-char", "");
+  BIND_CMD("BACKSPACE",   "edit:delete-prev-char", "");
+  BIND_CMD("DC",          "edit:delete-prev-char", "");
+  BIND_CMD("DELETE",      "edit:delete-prev-char", "");
+
+  BIND_CMD("C-k",         "edit:delete-to-line-end", "");
+
+  BIND_CMD("M-q",         "edit:fill-paragraph", "");
+
+  BIND_CMD("C-l",         "( edit:recenter ; redisplay )", "");
+
+  BIND_CMD("C-d",     "edit:delete-next-char", "");
+
+
+  /****************************************************************/
+  /**************************** EDITMULTI *************************/
+  /****************************************************************/
+
+  km_ew_multi = km = owl_keyhandler_create_and_add_keymap(kh, "editmulti",
+       "Multi-line text editing", 
+       owl_keys_editwin_default, NULL, owl_keys_editwin_postalways);
+  owl_keymap_set_submap(km_ew_multi, km_editwin);
+
+  BIND_CMD("UP",      "editmulti:move-up-line", "");
+  BIND_CMD("C-p",     "editmulti:move-up-line", "");
+  BIND_CMD("DOWN",    "editmulti:move-down-line", "");
+  BIND_CMD("C-n",     "editmulti:move-down-line", "");
+
+  /* This would be nice, but interferes with C-c to cancel */
+  /*BIND_CMD("C-c C-c", "editmulti:done", "sends the zephyr");*/
+
+  /* note that changing "disable-ctrl-d" will change this to 
+   * edit:delete-next-char */
+  BIND_CMD("C-d",     "editmulti:done", "sends the zephyr");
+
+
+  /****************************************************************/
+  /**************************** EDITLINE **************************/
+  /****************************************************************/
+
+  km_ew_onel = km = owl_keyhandler_create_and_add_keymap(kh, "editline",
+       "Single-line text editing", 
+       owl_keys_editwin_default, NULL, owl_keys_editwin_postalways);
+  owl_keymap_set_submap(km_ew_onel, km_editwin);
+
+  BIND_CMD("C-u",         "edit:delete-all", "Clears the entire line");
+
+  BIND_CMD("UP",          "edit:history-prev", "");
+  BIND_CMD("C-p",         "edit:history-prev", "");
+  BIND_CMD("M-p",         "edit:history-prev", "");
+
+  BIND_CMD("DOWN",        "edit:history-next", "");
+  BIND_CMD("C-n",         "edit:history-next", "");
+  BIND_CMD("M-n",         "edit:history-next", "");
+
+  BIND_CMD("LF",          "editline:done", "executes the command");
+  BIND_CMD("CR",          "editline:done", "executes the command");
+
+
+  /****************************************************************/
+  /**************************** POPLESS ***************************/
+  /****************************************************************/
+
+  km_viewwin = km = owl_keyhandler_create_and_add_keymap(kh, "popless",
+       "Pop-up window (eg, help)", 
+       owl_keys_default_invalid, NULL, owl_keys_popless_postalways);
+  owl_keymap_set_submap(km_viewwin, km_global);
+
+  BIND_CMD("SPACE",       "popless:scroll-down-page", "");
+  BIND_CMD("NPAGE",       "popless:scroll-down-page", "");
+  BIND_CMD("M-n",         "popless:scroll-down-page", "");
+
+  BIND_CMD("b",           "popless:scroll-up-page", "");
+  BIND_CMD("PPAGE",       "popless:scroll-up-page", "")
+  BIND_CMD("M-p",         "popless:scroll-up-page", "");
+
+  BIND_CMD("CR",          "popless:scroll-down-line", "");
+  BIND_CMD("LF",          "popless:scroll-down-line", "");
+  BIND_CMD("DOWN",        "popless:scroll-down-line", "");
+  BIND_CMD("C-n",         "popless:scroll-down-page", "");
+
+  BIND_CMD("UP",          "popless:scroll-up-line", "");
+  BIND_CMD("C-h",         "popless:scroll-up-line", "");
+  BIND_CMD("C-p",         "popless:scroll-up-line", "");
+  BIND_CMD("DELETE",      "popless:scroll-up-line", "");
+  BIND_CMD("BACKSPACE",   "popless:scroll-up-line", "");
+  BIND_CMD("DC",          "popless:scroll-up-line", "");
+
+  BIND_CMD("RIGHT",       "popless:scroll-right 10", "scrolls right");
+  BIND_CMD("LEFT",        "popless:scroll-left  10", "scrolls left");
+
+  BIND_CMD("HOME",        "popless:scroll-to-top", "");
+  BIND_CMD("<",           "popless:scroll-to-top", "");
+  BIND_CMD("M-<",         "popless:scroll-to-top", "");
+
+  BIND_CMD(">",           "popless:scroll-to-bottom", "");
+  BIND_CMD("M->",         "popless:scroll-to-bottom", "");
+
+  BIND_CMD("q",           "popless:quit", "");
+  BIND_CMD("C-c",         "popless:quit", "");
+  BIND_CMD("C-g",         "popless:quit", "");
+
+  BIND_CMD("C-l",         "redisplay", "");
+
+
+  /****************************************************************/
+  /***************************** RECV *****************************/
+  /****************************************************************/
+
+  km_mainwin = km = owl_keyhandler_create_and_add_keymap(kh, "recv",
+	"Main window / message list",
+        owl_keys_default_invalid, owl_keys_recwin_prealways, NULL);
+  owl_keymap_set_submap(km_mainwin, km_global);
+  BIND_CMD("C-x C-c", "quit",           "");
+  BIND_CMD("F1",      "help",           "");
+  BIND_CMD("h",       "help",           "");
+  BIND_CMD("HELP",    "help",           "");
+  BIND_CMD("M-[ 2 8 ~",   "help",       "");
+
+  BIND_CMD(":",       "start-command",  "start a new command");
+  BIND_CMD("M-x",     "start-command",  "start a new command");
+
+  BIND_CMD("x",       "expunge",        "");
+  BIND_CMD("u",       "undelete",       "");
+  BIND_CMD("M-u",     "undelete view",  "undelete a message marked for deletion");
+  BIND_CMD("d",       "delete",         "mark message for deletion");
+  BIND_CMD("M-D",     "delete view",    "mark all messages in view for deletion");
+
+  BIND_CMD("X",   "( expunge ; view --home )", "expunge deletions and switch to home view");
+
+  BIND_CMD("v",   "start-command view ", "start a view command");
+  BIND_CMD("V",   "view --home",      "change to the home view ('all' by default)");
+  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("LEFT",   "recv:shiftleft", "");
+  BIND_CMD("RIGHT",  "recv:shiftright","");
+  BIND_CMD("DOWN",   "recv:next",      "");
+  BIND_CMD("C-n",    "recv:next",      "");
+  BIND_CMD("UP",     "recv:prev",      "");
+  BIND_CMD("n",      "recv:next-notdel", "");
+  BIND_CMD("p",      "recv:prev-notdel", "");
+  BIND_CMD("C-p",    "recv:prev",        "");
+  BIND_CMD("P",      "recv:next-personal", "");
+  BIND_CMD("M-P",    "recv:prev-personal", "");
+  BIND_CMD("M-<",    "recv:first",     "");
+  BIND_CMD("<",      "recv:first",     "");
+  BIND_CMD("M->",    "recv:last",      "");
+  BIND_CMD(">",      "recv:last",      "");
+  BIND_CMD("C-v",    "recv:pagedown",  "");
+  BIND_CMD("NPAGE",  "recv:pagedown",  "");
+  BIND_CMD("M-v",    "recv:pageup",    "");
+  BIND_CMD("PPAGE",  "recv:pageup",    "");
+
+  BIND_CMD("SPACE",     "recv:scroll  10", "scroll message down a page");
+  BIND_CMD("CR",        "recv:scroll   1", "scroll message down a line");
+  BIND_CMD("LF",        "recv:scroll   1", "scroll message down a line");
+  BIND_CMD("C-h"  ,     "recv:scroll  -1", "scroll message up a line");
+  BIND_CMD("DELETE",    "recv:scroll  -1", "scroll message up a line");
+  BIND_CMD("BACKSPACE", "recv:scroll  -1", "scroll message up a line");
+  BIND_CMD("DC",        "recv:scroll  -1", "scroll message up a line");
+  BIND_CMD("b",         "recv:scroll -10", "scroll message up a page");
+
+  BIND_CMD("C-l",       "redisplay",       "");
+
+  BIND_CMD("i",   "info",             "");
+  BIND_CMD("M",   "pop-message",      "");
+  BIND_CMD("T",   "delete trash",     "mark all 'trash' messages for deletion");
+
+  BIND_CMD("A",   "zaway toggle",     "toggles zaway on and off");
+
+  BIND_CMD("z",   "start-command zwrite ", "start a zwrite command");
+  BIND_CMD("r",   "reply",            "reply to the current message");
+  BIND_CMD("R",   "reply sender",     "reply to sender of the current message");
+  BIND_CMD("C-r", "reply -e",         "reply to the current message, but allow editing of recipient");
+  BIND_CMD("C-R", "reply -e sender",  "reply to sender of the current message, but allow editing of recipient");
+		  
+  BIND_CMD("w",   "openurl",          "open a URL using a webbrowser");
+
+  BIND_CMD("C-c",  "",                "no effect in this mode");
+  BIND_CMD("C-g",  "",                "no effect in this mode");
+
+
+  /**********************/
+
+  owl_function_activate_keymap("recv");
+
+}
+
+
+/****************************************************************/
+/********************* Support Functions ************************/
+/****************************************************************/
+
+void owl_keys_recwin_prealways(int j) {
+  /* Clear the message line on subsequent key presses */
+  owl_function_makemsg("");
+}
+
+void owl_keys_editwin_default(int j) {
+  owl_editwin *e;
+  if (NULL != (e=owl_global_get_typwin(&g))) {
+    owl_editwin_process_char(e, j);
+  }
+}
+
+void owl_keys_editwin_postalways(int j) {
+  owl_editwin *e;
+  if (NULL != (e=owl_global_get_typwin(&g))) {
+    owl_editwin_post_process_char(e, j);
+  }  
+  owl_global_set_needrefresh(&g);
+}
+
+void owl_keys_popless_postalways(int j) {
+  owl_viewwin *v = owl_global_get_viewwin(&g);
+  owl_popwin *pw = owl_global_get_popwin(&g);
+
+  if (pw && owl_popwin_is_active(pw) && v) {
+    owl_viewwin_redisplay(v, 1);
+  }  
+}
+
+void owl_keys_default_invalid(int j) {
+  if (j==ERR) return;
+  if (j==410) return;
+  owl_keyhandler_invalidkey(owl_global_get_keyhandler(&g));
+}
+
Index: list.c
===================================================================
--- list.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ list.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,84 @@
+#include "owl.h"
+#include <stdlib.h>
+
+#define INITSIZE 30
+#define GROWAT 2
+#define GROWBY 1.5
+
+int owl_list_create(owl_list *l) {
+  l->size=0;
+  l->list=(void **)owl_malloc(INITSIZE*sizeof(void *));
+  l->avail=INITSIZE;
+  if (l->list==NULL) return(-1);
+  return(0);
+}
+
+int owl_list_get_size(owl_list *l) {
+  return(l->size);
+}
+
+void *owl_list_get_element(owl_list *l, int n) {
+  if (n>l->size-1) return(NULL);
+  return(l->list[n]);
+}
+
+int owl_list_append_element(owl_list *l, void *element) {
+  if ((l->size+1) > (l->avail/GROWAT)) {
+    l->list=owl_realloc(l->list, l->avail*GROWBY*sizeof(void *));
+    l->avail=l->avail*GROWBY;
+    if (l->list==NULL) return(-1);
+  }
+
+  l->list[l->size]=element;
+  l->size++;
+  return(0);
+}
+
+int owl_list_prepend_element(owl_list *l, void *element) {
+  int i;
+ 
+  if ((l->size+1) > (l->avail/GROWAT)) {
+    l->list=owl_realloc(l->list, l->avail*GROWBY*sizeof(void *));
+    l->avail=l->avail*GROWBY;
+    if (l->list==NULL) return(-1);
+  }
+
+  for (i=l->size; i>0; i--) {
+    l->list[i]=l->list[i-1];
+  }
+  l->list[0]=element;
+  l->size++;
+  return(0);
+}
+
+int owl_list_remove_element(owl_list *l, int n) {
+  int i;
+
+  if (n>l->size-1) return(-1);
+  for (i=n; i<l->size-1; i++) {
+    l->list[i]=l->list[i+1];
+  }
+  l->size--;
+  return(0);
+}
+
+/* todo: might leak memory */
+int owl_list_replace_element(owl_list *l, int n, void *element) {
+  if (n>l->size-1) return(-1);
+
+  l->list[n]=element;
+  return(0);
+}
+
+void owl_list_free_all(owl_list *l, void (*elefree)(void *)) {
+  int i;
+
+  for (i=0; i<l->size; i++) {
+    (elefree)(l->list[i]);
+  }
+  owl_free(l->list);
+}
+
+void owl_list_free_simple(owl_list *l) {
+  if (l->list) owl_free(l->list);
+}
Index: logging.c
===================================================================
--- logging.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ logging.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,145 @@
+#include "owl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/param.h>
+
+void owl_log_outgoing(char *to, char *text) {
+  FILE *file;
+  char filename[MAXPATHLEN];
+  char *tobuff, *ptr;
+
+  tobuff=owl_malloc(strlen(to)+20);
+  strcpy(tobuff, to);
+
+  /* chop off a local realm */
+  ptr=strchr(tobuff, '@');
+  if (ptr && !strncmp(ptr+1, ZGetRealm(), strlen(ZGetRealm()))) {
+    *ptr='\0';
+  }
+
+  sprintf(filename, "%s/zlog/people/%s", owl_global_get_homedir(&g), tobuff);
+  file=fopen(filename, "a");
+  if (!file) {
+    owl_function_makemsg("Unable to open file for outgoing logging");
+    return;
+  }
+  fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text);
+  if (text[strlen(text)-1]!='\n') {
+    fprintf(file, "\n");
+  }
+  fclose(file);
+
+  sprintf(filename, "%s/zlog/people/all", owl_global_get_homedir(&g));
+  file=fopen(filename, "a");
+  if (!file) {
+    owl_function_makemsg("Unable to open file for outgoing logging");
+    return;
+  }
+  fprintf(file, "OUTGOING (owl): %s\n%s\n", tobuff, text);
+  if (text[strlen(text)-1]!='\n') {
+    fprintf(file, "\n");
+  }
+  fclose(file);
+
+  owl_free(tobuff);
+}
+
+void owl_log_incoming(owl_message *m) {
+  FILE *file, *allfile;
+  char filename[MAXPATHLEN], allfilename[MAXPATHLEN];
+  char *frombuff, *ptr, *from, *buff, *tmp;
+  int len, ch, i, personal;
+
+  /* check for nolog */
+  if (!strcasecmp(owl_message_get_opcode(m), "nolog") ||
+      !strcasecmp(owl_message_get_instance(m), "nolog")) return;
+
+  if (owl_message_is_personal(m)) {
+    personal=1;
+    if (!owl_global_is_logging(&g)) return;
+  } else {
+    personal=0;
+    if (!owl_global_is_classlogging(&g)) return;
+  }
+
+  if (personal) {
+    from=frombuff=owl_strdup(owl_message_get_sender(m));
+    /* chop off a local realm */
+    ptr=strchr(frombuff, '@');
+    if (ptr && !strncmp(ptr+1, ZGetRealm(), strlen(ZGetRealm()))) {
+      *ptr='\0';
+    }
+  } else {
+    from=frombuff=owl_strdup(owl_message_get_class(m));
+  }
+  
+  /* check for malicious sender formats */
+  len=strlen(frombuff);
+  if (len<1 || len>35) from="weird";
+  if (strchr(frombuff, '/')) from="weird";
+
+  ch=frombuff[0];
+  if (!isalnum(ch)) from="weird";
+
+  for (i=0; i<len; i++) {
+    if (frombuff[i]<'!' || frombuff[i]>'~') from="weird";
+  }
+
+  if (!strcmp(frombuff, ".") || !strcasecmp(frombuff, "..")) from="weird";
+
+  if (!personal) {
+    if (strcmp(from, "weird")) downstr(from);
+  }
+
+  /* create the filename */
+  if (personal) {
+    sprintf(filename, "%s/%s", owl_global_get_logpath(&g), from);
+    sprintf(allfilename, "%s/all", owl_global_get_logpath(&g));
+  } else {
+    sprintf(filename, "%s/%s", owl_global_get_classlogpath(&g), from);
+  }
+  
+  file=fopen(filename, "a");
+  if (!file) {
+    owl_function_makemsg("Unable to open file for incoming logging");
+    return;
+  }
+
+  allfile=NULL;
+  if (personal) {
+    allfile=fopen(allfilename, "a");
+    if (!allfile) {
+      owl_function_makemsg("Unable to open file for incoming logging");
+      return;
+    }
+  }
+
+  tmp=pretty_sender(owl_message_get_sender(m));
+  
+  fprintf(file, "Class: %s Instance: %s", owl_message_get_class(m), owl_message_get_instance(m));
+  if (strcmp(owl_message_get_opcode(m), "")) fprintf(file, " Opcode: %s", owl_message_get_opcode(m));
+  fprintf(file, "\n");
+  fprintf(file, "Time: %s Host: %s\n", owl_message_get_timestr(m), owl_message_get_hostname(m));
+  ptr=owl_zephyr_get_zsig(owl_message_get_notice(m), &i);
+  buff=owl_malloc(i+10);
+  memcpy(buff, ptr, i);
+  buff[i]='\0';
+  fprintf(file, "From: %s <%s>\n\n", buff, tmp);
+  fprintf(file, "%s\n", owl_message_get_body(m));
+  fclose(file);
+
+  if (personal) {
+    fprintf(allfile, "Class: %s Instance: %s", owl_message_get_class(m), owl_message_get_instance(m));
+    if (strcmp(owl_message_get_opcode(m), "")) fprintf(allfile, " Opcode: %s", owl_message_get_opcode(m));
+    fprintf(allfile, "\n");
+    fprintf(allfile, "Time: %s Host: %s\n", owl_message_get_timestr(m), owl_message_get_hostname(m));
+    fprintf(allfile, "From: %s <%s>\n\n", buff, tmp);
+    fprintf(allfile, "%s\n", owl_message_get_body(m));
+    fclose(allfile);
+  }
+
+  owl_free(tmp);
+  owl_free(buff);
+  owl_free(frombuff);
+}
Index: mainwin.c
===================================================================
--- mainwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ mainwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,133 @@
+#include "owl.h"
+
+void owl_mainwin_init(owl_mainwin *mw) {
+  mw->curtruncated=0;
+  mw->lastdisplayed=-1;
+}
+
+void owl_mainwin_redisplay(owl_mainwin *mw) {
+  owl_message *m;
+  int i, j, p, q, lines, isfull;
+  int x, y, savey, recwinlines, start;
+  int topmsg, curmsg, color;
+  WINDOW *recwin;
+  owl_view *v;
+  owl_list *filtlist;
+  owl_filter *f;
+
+  recwin=owl_global_get_curs_recwin(&g);
+  topmsg=owl_global_get_topmsg(&g);
+  curmsg=owl_global_get_curmsg(&g);
+  v=owl_global_get_current_view(&g);
+
+  werase(recwin);
+
+  recwinlines=owl_global_get_recwin_lines(&g);
+  topmsg=owl_global_get_topmsg(&g);
+
+  /* if there are no messages, just draw a blank screen */
+  if (owl_view_get_size(v)==0) {
+    owl_global_set_topmsg(&g, 0);
+    mw->curtruncated=0;
+    mw->lastdisplayed=-1;
+    wnoutrefresh(recwin);
+    owl_global_set_needrefresh(&g);
+    return;
+  }
+
+  /* write the messages out */
+  isfull=0;
+  mw->curtruncated=0;
+  j=owl_view_get_size(v);
+  for (i=topmsg; i<j; i++) {
+    if (isfull) break;
+    m=owl_view_get_element(v, i);
+
+    /* hold on to y in case this is the current message or deleted */
+    getyx(recwin, y, x);
+    savey=y;
+
+    /* if it's the current message, account for a vert_offset */
+    if (i==owl_global_get_curmsg(&g)) {
+      start=owl_global_get_curmsg_vert_offset(&g);
+      lines=owl_message_get_numlines(m)-start;
+    } else {
+      start=0;
+      lines=owl_message_get_numlines(m);
+    }
+
+    /* if we match filters set the color */
+    color=OWL_COLOR_DEFAULT;
+    filtlist=owl_global_get_filterlist(&g);
+    q=owl_list_get_size(filtlist);
+    for (p=0; p<q; p++) {
+      f=owl_list_get_element(filtlist, p);
+      if (owl_filter_message_match(f, m)) {
+	if (owl_filter_get_color(f)!=OWL_COLOR_DEFAULT) color=owl_filter_get_color(f);
+      }
+    }
+
+    /* if we'll fill the screen print a partial message */
+    if ((y+lines > recwinlines) && (i==owl_global_get_curmsg(&g))) mw->curtruncated=1;
+    if (y+lines > recwinlines-1) {
+      isfull=1;
+      owl_message_curs_waddstr(m, owl_global_get_curs_recwin(&g),
+			       start,
+			       start+recwinlines-y,
+			       owl_global_get_rightshift(&g),
+			       owl_global_get_cols(&g)+owl_global_get_rightshift(&g)-1,
+			       color);
+    } else {
+      /* otherwise print the whole thing */
+      owl_message_curs_waddstr(m, owl_global_get_curs_recwin(&g),
+			       start,
+			       start+lines,
+			       owl_global_get_rightshift(&g),
+			       owl_global_get_cols(&g)+owl_global_get_rightshift(&g)-1,
+			       color);
+    }
+
+
+    /* is it the current message and/or deleted? */
+    getyx(recwin, y, x);
+    wattrset(recwin, A_NORMAL);
+    if (owl_global_get_rightshift(&g)==0) {   /* this lame and should be fixed */
+      if (m==owl_view_get_element(v, curmsg)) {
+	wmove(recwin, savey, 0);
+	wattron(recwin, A_BOLD);	
+	if (owl_global_get_curmsg_vert_offset(&g)>0) {
+	  waddstr(recwin, "+");
+	} else {
+	  waddstr(recwin, "-");
+	}
+	if (!owl_message_is_delete(m)) {
+	  waddstr(recwin, ">");
+	} else {
+	  waddstr(recwin, "D");
+	}
+	wmove(recwin, y, x);
+	wattroff(recwin, A_BOLD);
+      } else if (owl_message_is_delete(m)) {
+	wmove(recwin, savey, 0);
+	waddstr(recwin, " D");
+	wmove(recwin, y, x);
+      }
+    }
+    wattroff(recwin, A_BOLD);
+  }
+  mw->lastdisplayed=i-1;
+
+  wnoutrefresh(recwin);
+  owl_global_set_needrefresh(&g);
+}
+
+
+int owl_mainwin_is_curmsg_truncated(owl_mainwin *mw) {
+  if (mw->curtruncated) return(1);
+  return(0);
+}
+
+int owl_mainwin_get_last_msg(owl_mainwin *mw) {
+  /* return the number of the last message displayed. -1 if none */
+  return(mw->lastdisplayed);
+}
Index: message.c
===================================================================
--- message.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ message.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,610 @@
+#include <zephyr/zephyr.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include "owl.h"
+
+void owl_message_create_admin(owl_message *m, char *header, char *text) {
+  char *indent;
+  time_t t;
+
+  m->id=owl_global_get_nextmsgid(&g);
+  m->type=OWL_MESSAGE_TYPE_ADMIN;
+  m->admintype=OWL_MESSAGE_ADMINTYPE_GENERIC;
+  m->delete=0;
+  m->sender=owl_strdup("-owl");
+  m->class=owl_strdup("");
+  m->inst=owl_strdup("");
+  m->recip=owl_strdup("");
+  m->opcode=owl_strdup("");
+  m->realm=owl_strdup("");
+  strcpy(m->hostname, "");
+  m->zwriteline=strdup("");
+
+  /* save the time */
+  t=time(NULL);
+  m->time=owl_strdup(ctime(&t));
+  m->time[strlen(m->time)-1]='\0';
+
+  m->body=owl_strdup(text);
+
+  /* do something to make it clear the notice shouldn't be used for now */
+
+  indent=owl_malloc(strlen(text)+owl_text_num_lines(text)*OWL_MSGTAB+10);
+  owl_text_indent(indent, text, OWL_MSGTAB);
+  owl_fmtext_init_null(&(m->fmtext));
+  owl_fmtext_append_normal(&(m->fmtext), OWL_TABSTR);
+  owl_fmtext_append_bold(&(m->fmtext), "OWL ADMIN ");
+  owl_fmtext_append_ztext(&(m->fmtext), header);
+  owl_fmtext_append_normal(&(m->fmtext), "\n");
+  owl_fmtext_append_ztext(&(m->fmtext), indent);
+  if (text[strlen(text)-1]!='\n') {
+    owl_fmtext_append_normal(&(m->fmtext), "\n");
+  }
+
+  owl_free(indent);
+}
+
+void owl_message_create_from_zephyr(owl_message *m, ZNotice_t *n) {
+  struct hostent *hent;
+  int k;
+  char *ptr;
+
+  m->id=owl_global_get_nextmsgid(&g);
+  m->type=OWL_MESSAGE_TYPE_ZEPHYR;
+  
+  /* first save the full notice */
+  memcpy(&(m->notice), n, sizeof(ZNotice_t));
+
+  /* a little gross, we'll reaplace \r's with ' ' for now */
+  owl_zephyr_hackaway_cr(&(m->notice));
+  
+  m->delete=0;
+
+  /* set other info */
+  m->sender=owl_strdup(n->z_sender);
+  m->class=owl_strdup(n->z_class);
+  m->inst=owl_strdup(n->z_class_inst);
+  m->recip=owl_strdup(n->z_recipient);
+  if (n->z_opcode) {
+    m->opcode=owl_strdup(n->z_opcode);
+  } else {
+    n->z_opcode=owl_strdup("");
+  }
+
+  if ((ptr=strchr(n->z_recipient, '@'))!=NULL) {
+    m->realm=owl_strdup(ptr+1);
+  } else {
+    m->realm=owl_strdup(ZGetRealm());
+  }
+
+  m->zwriteline=strdup("");
+
+  ptr=owl_zephyr_get_message(n, &k);
+  m->body=owl_malloc(k+10);
+  memcpy(m->body, ptr, k);
+  m->body[k]='\0';
+
+  /* save the hostname */
+  owl_function_debugmsg("About to do get hostbyaddr");
+  hent=gethostbyaddr((char *) &(n->z_uid.zuid_addr), sizeof(n->z_uid.zuid_addr), AF_INET);
+  if (hent && hent->h_name) {
+    strcpy(m->hostname, hent->h_name);
+  } else {
+    strcpy(m->hostname, inet_ntoa(n->z_sender_addr));
+  }
+
+  /* save the time */
+  m->time=owl_strdup(ctime((time_t *) &n->z_time.tv_sec));
+  m->time[strlen(m->time)-1]='\0';
+
+  /* create the formatted message */
+  if (owl_global_is_config_format(&g)) {
+    _owl_message_make_text_from_config(m);
+  } else if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES)) {
+    _owl_message_make_text_from_notice_standard(m);
+  } else {
+    _owl_message_make_text_from_notice_simple(m);
+  }
+
+}
+
+
+void _owl_message_make_text_from_config(owl_message *m) {
+  char *body, *indent;
+  
+  owl_fmtext_init_null(&(m->fmtext));
+
+  /* get body from the config */
+  body=owl_config_getmsg(m, 1);
+  
+  /* indent */
+  indent=owl_malloc(strlen(body)+owl_text_num_lines(body)*OWL_TAB+10);
+  owl_text_indent(indent, body, OWL_TAB);
+
+  /* fmtext_append.  This needs to change */
+  owl_fmtext_append_ztext(&(m->fmtext), indent);
+
+  owl_free(indent);
+  owl_free(body);
+}
+
+void _owl_message_make_text_from_notice_standard(owl_message *m) {
+  char *body, *indent, *ptr;
+  char frombuff[1024];
+  char zsigbuff[LINE];
+  ZNotice_t *n;
+  int len;
+
+  /* get the body */
+  n=&(m->notice);
+  ptr=(owl_zephyr_get_message(n, &len));
+  body=owl_malloc(len+20);
+  strncpy(body, ptr, len);
+  body[len]='\0';
+
+  /* add a newline if we need to */
+  if (body[0]!='\0' && body[strlen(body)-1]!='\n') {
+    strcat(body, "\n");
+  }
+
+  /* do the indenting into indent */
+  indent=owl_malloc(strlen(body)+owl_text_num_lines(body)*OWL_MSGTAB+10);
+  owl_text_indent(indent, body, OWL_MSGTAB);
+
+  /* edit the from addr for printing */
+  strcpy(frombuff, m->sender);
+  ptr=strchr(frombuff, '@');
+  if (ptr && !strncmp(ptr+1, ZGetRealm(), strlen(ZGetRealm()))) {
+    *ptr='\0';
+  }
+
+  /* set the message for printing */
+  owl_fmtext_init_null(&(m->fmtext));
+  owl_fmtext_append_normal(&(m->fmtext), OWL_TABSTR);
+
+  if (!strcasecmp(n->z_opcode, "ping")) {
+    owl_fmtext_append_bold(&(m->fmtext), "PING");
+    owl_fmtext_append_normal(&(m->fmtext), " from ");
+    owl_fmtext_append_bold(&(m->fmtext), frombuff);
+    owl_fmtext_append_normal(&(m->fmtext), "\n");
+  } else if (!strcasecmp(n->z_class, "login")) {
+    char *ptr, host[LINE], tty[LINE];
+    int len;
+
+    ptr=owl_zephyr_get_field(n, 1, &len);
+    strncpy(host, ptr, len);
+    host[len]='\0';
+    ptr=owl_zephyr_get_field(n, 3, &len);
+    strncpy(tty, ptr, len);
+    tty[len]='\0';
+    
+    if (!strcasecmp(n->z_opcode, "user_login")) {
+      owl_fmtext_append_bold(&(m->fmtext), "LOGIN");
+    } else if (!strcasecmp(n->z_opcode, "user_logout")) {
+      owl_fmtext_append_bold(&(m->fmtext), "LOGOUT");
+    }
+    owl_fmtext_append_normal(&(m->fmtext), " for ");
+    ptr=pretty_sender(n->z_class_inst);
+    owl_fmtext_append_bold(&(m->fmtext), ptr);
+    owl_free(ptr);
+    owl_fmtext_append_normal(&(m->fmtext), " at ");
+    owl_fmtext_append_normal(&(m->fmtext), host);
+    owl_fmtext_append_normal(&(m->fmtext), " ");
+    owl_fmtext_append_normal(&(m->fmtext), tty);
+    owl_fmtext_append_normal(&(m->fmtext), "\n");
+  } else {
+    owl_fmtext_append_normal(&(m->fmtext), m->class);
+    owl_fmtext_append_normal(&(m->fmtext), " / ");
+    owl_fmtext_append_normal(&(m->fmtext), m->inst);
+    owl_fmtext_append_normal(&(m->fmtext), " / ");
+    owl_fmtext_append_bold(&(m->fmtext), frombuff);
+    if (strcasecmp(owl_message_get_realm(m), ZGetRealm())) {
+      owl_fmtext_append_normal(&(m->fmtext), " {");
+      owl_fmtext_append_normal(&(m->fmtext), owl_message_get_realm(m));
+      owl_fmtext_append_normal(&(m->fmtext), "} ");
+    }
+    if (n->z_opcode[0]!='\0') {
+      owl_fmtext_append_normal(&(m->fmtext), " [");
+      owl_fmtext_append_normal(&(m->fmtext), n->z_opcode);
+      owl_fmtext_append_normal(&(m->fmtext), "] ");
+    }
+
+    /* stick on the zsig */
+    _owl_message_get_zsig(m, zsigbuff, LINE);
+    if (zsigbuff[0]!='\0') {
+      owl_fmtext_append_normal(&(m->fmtext), "    (");
+      owl_fmtext_append_ztext(&(m->fmtext), zsigbuff);
+      owl_fmtext_append_normal(&(m->fmtext), ")");
+    }
+    owl_fmtext_append_normal(&(m->fmtext), "\n");
+    owl_fmtext_append_ztext(&(m->fmtext), indent);
+
+    /* make personal messages bold for smaat users */
+    if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES)) {
+      if (owl_message_is_personal(m)) {
+	owl_fmtext_addattr((&m->fmtext), OWL_FMTEXT_ATTR_BOLD);
+      }
+    }
+  }
+
+  owl_free(body);
+  owl_free(indent);
+}
+
+void _owl_message_make_text_from_notice_simple(owl_message *m) {
+  char *body, *indent, *ptr;
+  char frombuff[1024];
+  char zsigbuff[LINE];
+  ZNotice_t *n;
+  int len;
+
+  /* get the body */
+  n=&(m->notice);
+  ptr=(owl_zephyr_get_message(n, &len));
+  body=owl_malloc(len+20);
+  strncpy(body, ptr, len);
+  body[len]='\0';
+
+  /* add a newline if we need to */
+  if (body[0]!='\0' && body[strlen(body)-1]!='\n') {
+    strcat(body, "\n");
+  }
+
+  /* do the indenting into indent */
+  indent=owl_malloc(strlen(body)+owl_text_num_lines(body)*OWL_MSGTAB+10);
+  owl_text_indent(indent, body, OWL_MSGTAB);
+
+  /* edit the from addr for printing */
+  strcpy(frombuff, m->sender);
+  ptr=strchr(frombuff, '@');
+  if (ptr && !strncmp(ptr+1, ZGetRealm(), strlen(ZGetRealm()))) {
+    *ptr='\0';
+  }
+
+  /* set the message for printing */
+  owl_fmtext_init_null(&(m->fmtext));
+  owl_fmtext_append_normal(&(m->fmtext), OWL_TABSTR);
+
+  if (!strcasecmp(n->z_opcode, "ping")) {
+    owl_fmtext_append_bold(&(m->fmtext), "PING");
+    owl_fmtext_append_normal(&(m->fmtext), " from ");
+    owl_fmtext_append_bold(&(m->fmtext), frombuff);
+    owl_fmtext_append_normal(&(m->fmtext), "\n");
+  } else if (!strcasecmp(n->z_class, "login")) {
+    char *ptr, host[LINE], tty[LINE];
+    int len;
+
+    ptr=owl_zephyr_get_field(n, 1, &len);
+    strncpy(host, ptr, len);
+    host[len]='\0';
+    ptr=owl_zephyr_get_field(n, 3, &len);
+    strncpy(tty, ptr, len);
+    tty[len]='\0';
+    
+    if (!strcasecmp(n->z_opcode, "user_login")) {
+      owl_fmtext_append_bold(&(m->fmtext), "LOGIN");
+    } else if (!strcasecmp(n->z_opcode, "user_logout")) {
+      owl_fmtext_append_bold(&(m->fmtext), "LOGOUT");
+    }
+    owl_fmtext_append_normal(&(m->fmtext), " for ");
+    ptr=pretty_sender(n->z_class_inst);
+    owl_fmtext_append_bold(&(m->fmtext), ptr);
+    owl_free(ptr);
+    owl_fmtext_append_normal(&(m->fmtext), " at ");
+    owl_fmtext_append_normal(&(m->fmtext), host);
+    owl_fmtext_append_normal(&(m->fmtext), " ");
+    owl_fmtext_append_normal(&(m->fmtext), tty);
+    owl_fmtext_append_normal(&(m->fmtext), "\n");
+  } else {
+    owl_fmtext_append_normal(&(m->fmtext), "From: ");
+    if (strcasecmp(m->class, "message")) {
+      owl_fmtext_append_normal(&(m->fmtext), "Class ");
+      owl_fmtext_append_normal(&(m->fmtext), m->class);
+      owl_fmtext_append_normal(&(m->fmtext), " / Instance ");
+      owl_fmtext_append_normal(&(m->fmtext), m->inst);
+      owl_fmtext_append_normal(&(m->fmtext), " / ");
+    }
+    owl_fmtext_append_normal(&(m->fmtext), frombuff);
+    if (strcasecmp(owl_message_get_realm(m), ZGetRealm())) {
+      owl_fmtext_append_normal(&(m->fmtext), " {");
+      owl_fmtext_append_normal(&(m->fmtext), owl_message_get_realm(m));
+      owl_fmtext_append_normal(&(m->fmtext), "} ");
+    }
+
+    /* stick on the zsig */
+    _owl_message_get_zsig(m, zsigbuff, LINE);
+    if (zsigbuff[0]!='\0') {
+      owl_fmtext_append_normal(&(m->fmtext), "    (");
+      owl_fmtext_append_ztext(&(m->fmtext), zsigbuff);
+      owl_fmtext_append_normal(&(m->fmtext), ")");
+    }
+    owl_fmtext_append_normal(&(m->fmtext), "\n");
+    owl_fmtext_append_ztext(&(m->fmtext), indent);
+
+    /* make personal messages bold for smaat users */
+    if (owl_global_is_userclue(&g, OWL_USERCLUE_CLASSES)) {
+      if (owl_message_is_personal(m)) {
+	owl_fmtext_addattr((&m->fmtext), OWL_FMTEXT_ATTR_BOLD);
+      }
+    }
+  }
+
+  owl_free(body);
+  owl_free(indent);
+}
+
+void _owl_message_get_zsig(owl_message *m, char *buff, int size) {
+  char *ptr;
+  ZNotice_t n;
+  int len;
+  /* just a hackish thing for now.  We'll only present the first line
+     or the first 'size'. characters.  If the message is not
+     appropriate for having a zsig we'll return an empty string */
+  n=m->notice;
+
+
+  /* bail if it shouldn't have a zsig */
+  buff[0]='\0';
+  if (!strcasecmp(n.z_opcode, "ping")) {
+    return;
+  }
+
+  /* find the right length to copy */
+  len=strlen(n.z_message);
+  if (size < len) {
+    len=size;
+  }
+  if ((ptr=strchr(n.z_message, '\n'))!=NULL) {
+    if ((ptr-n.z_message) < len) {
+      len=ptr-n.z_message;
+    }
+  }
+
+  /* copy */
+  strncpy(buff, n.z_message, len);
+  buff[len]='\0';
+}
+
+
+int owl_message_get_numlines(owl_message *m) {
+  if (m == NULL) return(0);
+  return(owl_fmtext_num_lines(&(m->fmtext)));
+}
+
+
+void owl_message_mark_delete(owl_message *m) {
+  if (m == NULL) return;
+  m->delete=1;
+  /* _owl_message_make_text_from_notice(m); */
+}
+
+
+void owl_message_unmark_delete(owl_message *m) {
+  if (m == NULL) return;
+  m->delete=0;
+}
+
+
+int owl_message_set_admintype(owl_message *m, int admintype) {
+  if (m->type != OWL_MESSAGE_TYPE_ADMIN) return(-1);
+  m->admintype=admintype;
+  return(0);
+}
+
+
+int owl_message_get_admintype(owl_message *m) {
+  return(m->admintype);
+}
+
+void owl_message_set_admin_outgoing(owl_message *m, char *zwriteline) {
+  owl_message_set_admintype(m, OWL_MESSAGE_ADMINTYPE_OUTGOING);
+  if (m->zwriteline) {
+    owl_free(m->zwriteline);
+    m->zwriteline=owl_strdup(zwriteline);
+  }
+}
+
+char *owl_message_get_zwriteline(owl_message *m) {
+  return(m->zwriteline);
+}
+
+int owl_message_is_delete(owl_message *m) {
+  if (m == NULL) return(0);
+  if (m->delete==1) return(1);
+  return(0);
+}
+
+ZNotice_t *owl_message_get_notice(owl_message *m) {
+  return(&(m->notice));
+}
+
+
+void owl_message_free(owl_message *m) {
+  if (owl_message_is_zephyr(m)) {
+    ZFreeNotice(&(m->notice));
+  }
+  if (m->sender) owl_free(m->sender);
+  if (m->recip) owl_free(m->recip);
+  if (m->class) owl_free(m->class);
+  if (m->inst) owl_free(m->inst);
+  if (m->opcode) owl_free(m->opcode);
+  if (m->time) owl_free(m->time);
+  if (m->realm) owl_free(m->realm);
+  if (m->body) owl_free(m->body);
+  if (m->zwriteline) owl_free(m->zwriteline);
+ 
+  owl_fmtext_free(&(m->fmtext));
+}
+
+
+char *owl_message_get_hostname(owl_message *m) {
+  return(m->hostname);
+}
+
+
+void owl_message_curs_waddstr(owl_message *m, WINDOW *win, int aline, int bline, int acol, int bcol, int color) {
+  owl_fmtext a, b;
+
+  owl_fmtext_truncate_lines(&(m->fmtext), aline, bline-aline+1, &a);
+  owl_fmtext_truncate_cols(&a, acol, bcol, &b);
+  if (color!=OWL_COLOR_DEFAULT) {
+    owl_fmtext_colorize(&b, color);
+  }
+  owl_fmtext_curs_waddstr(&b, win);
+
+  owl_fmtext_free(&a);
+  owl_fmtext_free(&b);
+}
+
+owl_fmtext *owl_message_get_fmtext(owl_message *m) {
+  return(&(m->fmtext));
+}
+
+void owl_message_set_class(owl_message *m, char *class) {
+  if (m->class) owl_free(m->class);
+  m->class=owl_strdup(class);
+}
+
+
+char *owl_message_get_class(owl_message *m) {
+  return(m->class);
+}
+
+void owl_message_set_instance(owl_message *m, char *inst) {
+  if (m->inst) owl_free(m->inst);
+  m->inst=owl_strdup(inst);
+}
+
+char *owl_message_get_instance(owl_message *m) {
+  return(m->inst);
+}
+
+void owl_message_set_sender(owl_message *m, char *sender) {
+  if (m->sender) owl_free(m->sender);
+  m->sender=owl_strdup(sender);
+}
+
+char *owl_message_get_sender(owl_message *m) {
+  return(m->sender);
+}
+
+void owl_message_set_recipient(owl_message *m, char *recip) {
+  if (m->recip) owl_free(m->recip);
+  m->recip=owl_strdup(recip);
+}
+
+char *owl_message_get_recipient(owl_message *m) {
+  /* this is very, very stupid for outgoing messages, we need to
+     fix that. */
+     
+  if (m->type==OWL_MESSAGE_TYPE_ZEPHYR) {
+    return(m->recip);
+  } else if (m->type==OWL_MESSAGE_TYPE_ADMIN &&
+	     m->admintype==OWL_MESSAGE_ADMINTYPE_OUTGOING) {
+    return(m->zwriteline);
+  } else {
+    return(m->recip);
+  }
+}
+
+void owl_message_set_realm(owl_message *m, char *realm) {
+  if (m->realm) owl_free(m->realm);
+  m->realm=owl_strdup(realm);
+}
+
+char *owl_message_get_realm(owl_message *m) {
+  return(m->realm);
+}
+
+void owl_message_set_opcode(owl_message *m, char *opcode) {
+  if (m->opcode) free(m->opcode);
+  m->opcode=owl_strdup(opcode);
+}
+
+char *owl_message_get_opcode(owl_message *m) {
+  return(m->opcode);
+}
+
+char *owl_message_get_timestr(owl_message *m) {
+  return(m->time);
+}
+
+int owl_message_is_admin(owl_message *m) {
+  if (m->type==OWL_MESSAGE_TYPE_ADMIN) return(1);
+  return(0);
+}
+
+int owl_message_is_zephyr(owl_message *m) {
+  if (m->type==OWL_MESSAGE_TYPE_ZEPHYR) return(1);
+  return(0);
+}
+
+char *owl_message_get_text(owl_message *m) {
+  return(owl_fmtext_get_text(&(m->fmtext)));
+}
+
+char *owl_message_get_body(owl_message *m) {
+  return(m->body);
+}
+
+int owl_message_is_personal(owl_message *m) {
+  if (!strcasecmp(owl_message_get_class(m), "message") &&
+      !strcasecmp(owl_message_get_instance(m), "personal") &&
+      !strcmp(owl_message_get_recipient(m), ZGetSender())) {
+    return(1);
+  }
+  return(0);
+}
+
+int owl_message_is_private(owl_message *m) {
+  if (!strcmp(owl_message_get_recipient(m), ZGetSender())) return(1);
+  return(0);
+}
+
+int owl_message_is_mail(owl_message *m) {
+  if (!strcasecmp(owl_message_get_class(m), "mail") && owl_message_is_private(m)) {
+    return(1);
+  }
+  return(0);
+}
+
+int owl_message_is_ping(owl_message *m) {
+  if (!strcasecmp(owl_message_get_opcode(m), "ping")) return(1);
+  return(0);
+}
+
+int owl_message_is_login(owl_message *m) {
+  if (!strcasecmp(owl_message_get_class(m), "login")) return(1);
+  return(0);
+  /* is this good enough? */
+}
+
+int owl_message_is_burningears(owl_message *m) {
+  /* we should add a global to cache the short zsender */
+  char sender[LINE], *ptr;
+
+  /* if the message is from us or to us, it doesn't count */
+  if (!strcasecmp(ZGetSender(), owl_message_get_sender(m))) return(0);
+  if (!strcasecmp(ZGetSender(), owl_message_get_recipient(m))) return(0);
+
+  strcpy(sender, ZGetSender());
+  ptr=strchr(sender, '@');
+  if (ptr) *ptr='\0';
+
+  if (stristr(owl_message_get_body(m), sender)) {
+    return(1);
+  }
+  return(0);
+}
+
+int owl_message_get_id(owl_message *m) {
+  return(m->id);
+}
+					
Index: messagelist.c
===================================================================
--- messagelist.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ messagelist.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,78 @@
+#include "owl.h"
+#include <stdlib.h>
+#include <string.h>
+
+int owl_messagelist_create(owl_messagelist *ml) {
+  owl_list_create(&(ml->list));
+  return(0);
+}
+
+int owl_messagelist_get_size(owl_messagelist *ml) {
+  return(owl_list_get_size(&(ml->list)));
+}
+
+void *owl_messagelist_get_element(owl_messagelist *ml, int n) {
+  return(owl_list_get_element(&(ml->list), n));
+}
+
+owl_message *owl_messagelist_get_by_id(owl_messagelist *ml, int id) {
+  /* return the message with id == 'id'.  If it doesn't exist return NULL. */
+  /* we could make this much more efficient at some point */
+  int i, j;
+  owl_message *m;
+
+  j=owl_list_get_size(&(ml->list));
+  for (i=0; i<j; i++) {
+    m=owl_list_get_element(&(ml->list), i);
+    if (owl_message_get_id(m)==id) return(m);
+
+    /* the message id's have to be sequential.  If we've passed the
+       one we're looking for just bail */
+    if (owl_message_get_id(m) > id) return(NULL);
+  }
+  return(NULL);
+}
+
+int owl_messagelist_append_element(owl_messagelist *ml, void *element) {
+  return(owl_list_append_element(&(ml->list), element));
+}
+
+/* do we really still want this? */
+int owl_messagelist_delete_element(owl_messagelist *ml, int n) {
+  /* mark a message as deleted */
+  owl_message_mark_delete(owl_list_get_element(&(ml->list), n));
+  return(0);
+}
+
+int owl_messagelist_undelete_element(owl_messagelist *ml, int n) {
+  /* mark a message as deleted */
+  owl_message_unmark_delete(owl_list_get_element(&(ml->list), n));
+  return(0);
+}
+
+int owl_messagelist_expunge(owl_messagelist *ml) {
+  /* expunge deleted messages */
+  int i, j;
+  owl_list newlist;
+  owl_message *m;
+
+  owl_list_create(&newlist);
+  /*create a new list without messages marked as deleted */
+  j=owl_list_get_size(&(ml->list));
+  for (i=0; i<j; i++) {
+    m=owl_list_get_element(&(ml->list), i);
+    if (owl_message_is_delete(m)) {
+      owl_message_free(m);
+    } else {
+      owl_list_append_element(&newlist, m);
+    }
+  }
+
+  /* free the old list */
+  owl_list_free_simple(&(ml->list));
+
+  /* copy the new list to the old list */
+  memcpy(&(ml->list), &newlist, sizeof(owl_list));
+
+  return(0);
+}
Index: owl.c
===================================================================
--- owl.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ owl.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,373 @@
+/* Written by James Kretchmar, MIT
+ *
+ * Copyright 2001 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice and this
+ * permission notice appear in all copies and in supporting
+ * documentation.  No representation is made about the suitability of
+ * this software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zephyr/zephyr.h>
+#include <com_err.h>
+#include <signal.h>
+#include <sys/param.h>
+#include "owl.h"
+
+int main(int argc, char **argv, char **env) {
+  WINDOW *recwin, *sepwin, *typwin, *msgwin;
+  owl_editwin *tw;
+  owl_popwin *pw;
+  int j, ret, initialsubs, debug, newzephyrs, argcsave, followlast;
+  struct sigaction sigact;
+  char *configfile, *tty, *perlout;
+  char buff[LINE], startupmsg[LINE];
+  char **argvsave;
+  owl_filter *f;
+
+  argcsave=argc;
+  argvsave=argv;
+  configfile=NULL;
+  tty=NULL;
+  debug=0;
+  if (argc>0) {
+    argv++;
+    argc--;
+  }
+  while (argc>0) {
+    if (!strcmp(argv[0], "-n")) {
+      initialsubs=0;
+      argv++;
+      argc--;
+    } else if (!strcmp(argv[0], "-c")) {
+      if (argc<2) {
+	fprintf(stderr, "Too few arguments to -c\n");
+	usage();
+	exit(1);
+      }
+      configfile=argv[1];
+      argv+=2;
+      argc-=2;
+    } else if (!strcmp(argv[0], "-t")) {
+      if (argc<2) {
+	fprintf(stderr, "Too few arguments to -t\n");
+	usage();
+	exit(1);
+      }
+      tty=argv[1];
+      argv+=2;
+      argc-=2;
+    } else if (!strcmp(argv[0], "-d")) {
+      debug=1;
+      argv++;
+      argc--;
+    } else if (!strcmp(argv[0], "-v")) {
+      printf("This is owl version %s\n", OWL_VERSION_STRING);
+      exit(0);
+    } else {
+      fprintf(stderr, "Uknown argument\n");
+      usage();	      
+      exit(1);
+    }
+  }
+
+  /* zephyr init */
+  if ((ret = ZInitialize()) != ZERR_NONE) {
+    com_err("owl",ret,"while initializing");
+    exit(1);
+  }
+  if ((ret = ZOpenPort(NULL)) != ZERR_NONE) {
+    com_err("owl",ret,"while opening port");
+    exit(1);
+  }
+
+  /* signal handler */
+  sigact.sa_handler=sig_handler;
+  sigemptyset(&sigact.sa_mask);
+  sigact.sa_flags=0;
+  sigaction(SIGWINCH, &sigact, NULL);
+  sigaction(SIGALRM, &sigact, NULL);
+
+  /* screen init */
+  sprintf(buff, "TERMINFO=%s", TERMINFO);
+  putenv(buff);
+  initscr();
+  start_color();
+  use_default_colors();
+  raw();
+  noecho();
+
+#if 0 /* Removed to prevent flashing of popwin */
+  intrflush(stdscr,FALSE);
+  keypad(stdscr,TRUE);
+  nodelay(stdscr,1);
+  meta(stdscr,TRUE);
+#endif /*0*/
+
+  /* define simple color pairs */
+  if (has_colors() && COLOR_PAIRS>=8) {
+    init_pair(OWL_COLOR_BLACK,   COLOR_BLACK,   -1);
+    init_pair(OWL_COLOR_RED,     COLOR_RED,     -1);
+    init_pair(OWL_COLOR_GREEN,   COLOR_GREEN,   -1);
+    init_pair(OWL_COLOR_YELLOW,  COLOR_YELLOW,  -1);
+    init_pair(OWL_COLOR_BLUE,    COLOR_BLUE,    -1);
+    init_pair(OWL_COLOR_MAGENTA, COLOR_MAGENTA, -1);
+    init_pair(OWL_COLOR_CYAN,    COLOR_CYAN,    -1);
+    init_pair(OWL_COLOR_WHITE,   COLOR_WHITE,   -1);
+  }
+    
+  /* owl init */
+  owl_global_init(&g);
+  if (debug) owl_global_set_debug_on(&g);
+  owl_global_set_startupargs(&g, argcsave, argvsave);
+  owl_global_set_tty(&g, tty);
+  owl_context_set_readconfig(owl_global_get_context(&g));
+
+  /* setup the default filters */
+  f=malloc(sizeof(owl_filter));
+  owl_filter_init_fromstring(f, "personal", "( class ^message$ and instance ^personal$ ) or ( type ^admin$ and recipient .+ )"); /* fix to use admintype */
+  owl_list_append_element(owl_global_get_filterlist(&g), f);
+
+  f=malloc(sizeof(owl_filter));
+  owl_filter_init_fromstring(f, "trash", "class ^mail$ or opcode ^ping$ or type ^admin$ or class ^login$");
+  owl_list_append_element(owl_global_get_filterlist(&g), f);
+
+  f=malloc(sizeof(owl_filter));
+  owl_filter_init_fromstring(f, "reply-lockout", "class ^noc or class ^mail$");
+  owl_list_append_element(owl_global_get_filterlist(&g), f);
+
+  f=malloc(sizeof(owl_filter));
+  owl_filter_init_fromstring(f, "all", "class .*");
+  owl_list_append_element(owl_global_get_filterlist(&g), f);
+
+  /* set the current view */
+  owl_view_create(owl_global_get_current_view(&g), f);
+
+  /* read the config file */
+  ret=owl_readconfig(configfile);
+  if (ret) {
+    endwin();
+    printf("\nError parsing configfile\n");
+    exit(1);
+  }
+  perlout = owl_config_execute("owl::startup();");
+  if (perlout) owl_free(perlout);
+  owl_function_debugmsg("-- Owl Startup --");
+  
+  /* hold on to the window names for convenience */
+  msgwin=owl_global_get_curs_msgwin(&g);
+  recwin=owl_global_get_curs_recwin(&g);
+  sepwin=owl_global_get_curs_sepwin(&g);
+  typwin=owl_global_get_curs_typwin(&g);
+
+  tw=owl_global_get_typwin(&g);
+
+  wrefresh(sepwin);
+
+  /* load zephyr subs */
+  ret=owl_zephyr_loadsubs(NULL);
+  if (ret!=-1) {
+    owl_global_add_userclue(&g, OWL_USERCLUE_CLASSES);
+  }
+
+  /* load login subs */
+  if (owl_global_is_loginsubs(&g)) {
+    loadloginsubs(NULL);
+  }
+
+  /* zlog in if we need to */
+  if (owl_global_is_startuplogin(&g)) {
+    owl_function_zlog_in();
+  }
+
+  /* welcome message */
+  sprintf(buff, "Welcome to owl version %s.\n", OWL_VERSION_STRING);
+  strcpy(startupmsg, "-------------------------------------------------------------------------\n");
+  strcat(startupmsg, buff);
+  strcat(startupmsg, "Press 'h' for on line help.                                   ^ ^        \n");
+  strcat(startupmsg, "                                                              OvO        \n");
+  strcat(startupmsg, "Please report any bugs or suggestions to bug-owl@mit.edu     (   )       \n");
+  strcat(startupmsg, "--------------------------------------------------------------m-m--------\n");
+  
+  owl_function_adminmsg("", startupmsg);
+  /* owl_function_makemsg(buff); */
+  sepbar(NULL);
+  
+  owl_context_set_interactive(owl_global_get_context(&g));
+
+  /* main loop */
+  while (1) {
+
+    /* if a resize has been scheduled, deal with it */
+    owl_global_resize(&g, 0, 0);
+
+    /* these are here in case a resize changes the windows */
+    msgwin=owl_global_get_curs_msgwin(&g);
+    recwin=owl_global_get_curs_recwin(&g);
+    sepwin=owl_global_get_curs_sepwin(&g);
+    typwin=owl_global_get_curs_typwin(&g);
+
+    followlast=owl_global_should_followlast(&g);
+
+    /* grab incoming zephyrs */
+    newzephyrs=0;
+    while(ZPending()) {
+      ZNotice_t notice;
+      struct sockaddr_in from;
+      owl_message *m;
+
+      ZReceiveNotice(&notice, &from);
+
+      /* is this an ack from a zephyr we sent? */
+      if (owl_zephyr_notice_is_ack(&notice)) {
+	owl_zephyr_handle_ack(&notice);
+	continue;
+      }
+      
+      /* if it's a ping and we're not viewing pings then skip it */
+      if (!owl_global_is_rxping(&g) && !strcasecmp(notice.z_opcode, "ping")) {
+	continue;
+      }
+
+      /* create the new message */
+      m=owl_malloc(sizeof(owl_message));
+      owl_message_create_from_zephyr(m, &notice);
+      
+      /* if it's on the puntlist then, nuke it and continue */
+      if (owl_global_message_is_puntable(&g, m)) {
+	owl_message_free(m);
+	continue;
+      }
+
+      /* otherwise add it to the global list */
+      owl_messagelist_append_element(owl_global_get_msglist(&g), m);
+      newzephyrs=1;
+
+      /* let the config know the new message has been received */
+      owl_config_getmsg(m, 0);
+
+      /* add it to any necessary views; right now there's only the current view */
+      owl_view_consider_message(owl_global_get_current_view(&g), m);
+
+      /* do we need to autoreply? */
+      if (owl_global_is_zaway(&g)) {
+	owl_zephyr_zaway(m);
+      }
+
+      /* ring the bell if it's a personal */
+      if (owl_global_is_personalbell(&g) && owl_message_is_personal(m)) {
+	owl_function_beep();
+	owl_global_set_needrefresh(&g);
+      }
+
+      /* check for burning ears message */
+      /* this is an unsupported feature */
+      if (owl_global_is_burningears(&g) && owl_message_is_burningears(m)) {
+	char buff[LINE];
+	sprintf(buff, "@i(Burning ears message on class %s)", owl_message_get_class(m));
+	/* owl_function_makemsg(buff); */
+	owl_function_adminmsg(buff, "");
+	owl_function_beep();
+      }
+
+      /* log the zephyr if we need to */
+      if (owl_global_is_logging(&g) || owl_global_is_classlogging(&g)) {
+	owl_log_incoming(m);
+      }
+    }
+
+    /* follow the last message if we're supposed to */
+    if (newzephyrs && followlast) {
+      owl_function_lastmsg_noredisplay();
+    }
+    
+    /* redisplay if necessary */
+    if (newzephyrs) {
+      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+      sepbar(NULL);
+      if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
+	owl_popwin_refresh(owl_global_get_popwin(&g));
+	/* TODO: this is a broken kludge */
+	if (owl_global_get_viewwin(&g)) {
+	  owl_viewwin_redisplay(owl_global_get_viewwin(&g), 0);
+	}
+      }
+      owl_global_set_needrefresh(&g);
+    }
+
+    /* if a popwin just came up, refresh it */
+    pw=owl_global_get_popwin(&g);
+    if (owl_popwin_is_active(pw) && owl_popwin_needs_first_refresh(pw)) {
+      owl_popwin_refresh(pw);
+      owl_popwin_no_needs_first_refresh(pw);
+      owl_global_set_needrefresh(&g);
+      /* TODO: this is a broken kludge */
+      if (owl_global_get_viewwin(&g)) {
+	owl_viewwin_redisplay(owl_global_get_viewwin(&g), 0);
+      }
+    }
+
+    /* update the terminal if we need to */
+    if (owl_global_is_needrefresh(&g)) {
+      /* leave the cursor in the appropriate window */
+      if (owl_global_is_typwin_active(&g)) {
+	owl_function_set_cursor(typwin);
+      } else {
+	owl_function_set_cursor(sepwin);
+      }
+      doupdate();
+      owl_global_set_noneedrefresh(&g);
+    }
+
+    /* handle all keypresses */
+    j=wgetch(typwin);
+    if (j==ERR) {
+      usleep(10);
+      continue;
+    }
+    /* find and activate the current keymap.
+     * TODO: this should really get fixed by activating
+     * keymaps as we switch between windows... 
+     */
+    if (pw && owl_popwin_is_active(pw) && owl_global_get_viewwin(&g)) {
+      owl_context_set_popless(owl_global_get_context(&g), 
+			      owl_global_get_viewwin(&g));
+      owl_function_activate_keymap("popless");
+    } else if (owl_global_is_typwin_active(&g) 
+	       && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_ONELINE) {
+      owl_context_set_editline(owl_global_get_context(&g), tw);
+      owl_function_activate_keymap("editline");
+    } else if (owl_global_is_typwin_active(&g) 
+	       && owl_editwin_get_style(tw)==OWL_EDITWIN_STYLE_MULTILINE) {
+      owl_context_set_editmulti(owl_global_get_context(&g), tw);
+      owl_function_activate_keymap("editmulti");
+    } else {
+      owl_context_set_recv(owl_global_get_context(&g));
+      owl_function_activate_keymap("recv");
+    }
+    /* now actually handle the keypress */
+    ret = owl_keyhandler_process(owl_global_get_keyhandler(&g), j);
+    if (ret!=0 && ret!=1) {
+      owl_function_makemsg("Unable to handle keypress");
+    }
+  }
+
+}
+
+void sig_handler(int sig) {
+  if (sig==SIGWINCH) {
+    /* we can't inturrupt a malloc here, so it just sets a flag */
+    owl_function_resize();
+  }
+}
+
+void usage() {
+  fprintf(stderr, "Usage: owl [-n] [-d] [-v] [-c <configfile>] [-t <ttyname>]\n");
+}
Index: owl.h
===================================================================
--- owl.h (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ owl.h (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,385 @@
+#ifndef INC_OWL_H
+#define INC_OWL_H
+
+#include <zephyr/zephyr.h>
+#include <curses.h>
+#include <sys/param.h>
+#include <EXTERN.h>
+#include <netdb.h>
+#include <regex.h>
+#include "config.h"
+
+#define OWL_VERSION         1.2.1-pre-1
+#define OWL_VERSION_STRING "1.2.1-pre-1"
+
+#define OWL_DEBUG 0
+#define OWL_DEBUG_FILE "/var/tmp/owldebug"
+
+#define OWL_FMTEXT_ATTR_NONE      0
+#define OWL_FMTEXT_ATTR_BOLD      1
+#define OWL_FMTEXT_ATTR_REVERSE   2
+#define OWL_FMTEXT_ATTR_UNDERLINE 4
+
+#define OWL_COLOR_BLACK     0
+#define OWL_COLOR_RED       1
+#define OWL_COLOR_GREEN     2
+#define OWL_COLOR_YELLOW    3
+#define OWL_COLOR_BLUE      4
+#define OWL_COLOR_MAGENTA   5
+#define OWL_COLOR_CYAN      6
+#define OWL_COLOR_WHITE     7
+#define OWL_COLOR_DEFAULT   8
+
+#define OWL_EDITWIN_STYLE_MULTILINE 0
+#define OWL_EDITWIN_STYLE_ONELINE   1
+
+#define OWL_MESSAGE_TYPE_ADMIN  0
+#define OWL_MESSAGE_TYPE_ZEPHYR 1
+
+#define OWL_MESSAGE_ADMINTYPE_GENERIC  0
+#define OWL_MESSAGE_ADMINTYPE_OUTGOING 1
+#define OWL_MESSAGE_ADMINTYPE_NOTADMIN 2
+
+#define OWL_DIRECTION_NONE      0
+#define OWL_DIRECTION_DOWNWARDS 1
+#define OWL_DIRECTION_UPWARDS   2
+
+#define OWL_TAB               3  /* This *HAS* to be the size of TABSTR below */
+#define OWL_TABSTR        "   "
+#define OWL_MSGTAB            7
+#define OWL_TYPWIN_SIZE       8
+#define OWL_HISTORYSIZE       50
+
+/* Indicate current state, as well as what is allowed */
+#define OWL_CTX_ANY          0xffff
+/* Only one of these may be active at a time... */
+#define OWL_CTX_MODE_BITS    0x000f
+#define OWL_CTX_STARTUP      0x0001
+#define OWL_CTX_READCONFIG   0x0002
+#define OWL_CTX_INTERACTIVE  0x0004
+/* Only one of these may be active at a time... */
+#define OWL_CTX_ACTIVE_BITS  0xfff0
+#define OWL_CTX_POPWIN       0x00f0
+#define OWL_CTX_POPLESS      0x0010
+#define OWL_CTX_RECWIN       0x0f00
+#define OWL_CTX_RECV         0x0100
+#define OWL_CTX_TYPWIN       0xf000
+#define OWL_CTX_EDIT         0x3000
+#define OWL_CTX_EDITLINE     0x1000
+#define OWL_CTX_EDITMULTI    0x2000
+
+#define OWL_USERCLUE_NONE       0
+#define OWL_USERCLUE_CLASSES    1
+#define OWL_USERCLUE_FOOBAR     2
+#define OWL_USERCLUE_BAZ        4
+
+#define OWL_WEBBROWSER_NONE     0
+#define OWL_WEBBROWSER_NETSCAPE 1
+#define OWL_WEBBROWSER_GALEON   2
+
+#define OWL_VARIABLE_OTHER      0
+#define OWL_VARIABLE_INT        1
+#define OWL_VARIABLE_BOOL       2
+#define OWL_VARIABLE_STRING     3
+
+#define OWL_FILTER_MAX_DEPTH    300
+
+#define OWL_KEYMAP_MAXSTACK 20
+
+#define OWL_KEYBINDING_COMMAND   1 /* command string */
+#define OWL_KEYBINDING_FUNCTION  2 /* function taking no args */
+
+#define OWL_DEFAULT_ZAWAYMSG "I'm sorry, but I am currently away from the terminal and am\nnot able to receive your message.\n"
+
+#define OWL_INCLUDE_REG_TESTS  1  /* whether to build in regression tests */
+
+#define OWL_CMD_ALIAS_SUMMARY_PREFIX "command alias to "
+
+#ifndef CTRL
+#define CTRL(key) ((key)&037)
+#endif
+#ifndef META
+#define META(key) ((key)|0200)
+#endif
+
+#define LINE 2048
+
+typedef struct _owl_variable {
+  char *name;
+  int   type;  /* OWL_VARIABLE_* */
+  void *pval_default;  /* for types other and string */
+  int   ival_default;  /* for types int and bool     */
+  char *validsettings;		/* documentation of valid settings */
+  char *docstring;		/* documentation of valid settings */
+  void *val;                    /* current value */
+  int  (*validate_fn)(struct _owl_variable *v, void *newval);
+                                /* returns 1 if newval is valid */
+  int  (*set_fn)(struct _owl_variable *v, void *newval); 
+                                /* sets the variable to a value
+				 * of the appropriate type.
+				 * unless documented, this 
+				 * should make a copy. 
+				 * returns 0 on success. */
+  int  (*set_fromstring_fn)(struct _owl_variable *v, char *newval);
+                                /* sets the variable to a value
+				 * of the appropriate type.
+				 * unless documented, this 
+				 * should make a copy. 
+				 * returns 0 on success. */
+  void *(*get_fn)(struct _owl_variable *v);
+				/* returns a reference to the current value.
+				 * WARNING:  this approach is hard to make
+				 * thread-safe... */
+  int  (*get_tostring_fn)(struct _owl_variable *v, 
+			  char *buf, int bufsize, void *val); 
+                                /* converts val to a string 
+				 * and puts into buf */
+  void  (*free_fn)(struct _owl_variable *v);
+				/* frees val as needed */
+} owl_variable;
+
+typedef struct _owl_fmtext {
+  int textlen;
+  char *textbuff;
+  char *fmbuff;
+  char *colorbuff;
+} owl_fmtext;
+
+typedef struct _owl_list {
+  int size;
+  int avail;
+  void **list;
+} owl_list;
+
+typedef struct _owl_dict_el {
+  char *k;			/* key   */
+  void *v;			/* value */
+} owl_dict_el;
+
+typedef struct _owl_dict {
+  int size;
+  int avail;
+  owl_dict_el *els;		/* invariant: sorted by k */
+} owl_dict;
+typedef owl_dict owl_vardict;	/* dict of variables */
+typedef owl_dict owl_cmddict;	/* dict of commands */
+
+typedef struct _owl_context {
+  int   mode;
+  void *data;		/* determined by mode */
+} owl_context;
+
+typedef struct _owl_cmd {	/* command */
+  char *name;
+
+  char *summary;		/* one line summary of command */
+  char *usage;			/* usage synopsis */
+  char *description;		/* long description of command */
+
+  int validctx;			/* bitmask of valid contexts */
+
+  /* we should probably have a type here that says which of
+   * the following is valid, and maybe make the below into a union... */
+
+  /* Only one of these may be non-NULL ... */
+
+  char *cmd_aliased_to;		/* what this command is aliased to... */
+  
+  /* These don't take any context */
+  char *(*cmd_args_fn)(int argc, char **argv, char *buff);  
+				/* takes argv and the full command as buff.
+				 * caller must free return value if !NULL */
+  void (*cmd_v_fn)(void);	/* takes no args */
+  void (*cmd_i_fn)(int i);	/* takes an int as an arg */
+
+  /* The following also take the active context if it's valid */
+  char *(*cmd_ctxargs_fn)(void *ctx, int argc, char **argv, char *buff);  
+				/* takes argv and the full command as buff.
+				 * caller must free return value if !NULL */
+  void (*cmd_ctxv_fn)(void *ctx);	        /* takes no args */
+  void (*cmd_ctxi_fn)(void *ctx, int i);	/* takes an int as an arg */
+} owl_cmd;
+
+
+typedef struct _owl_zwrite {
+  char class[LINE];
+  char inst[LINE];
+  char realm[LINE];
+  char opcode[LINE];
+  owl_list recips;
+  int cc;
+  int noping;
+} owl_zwrite;
+
+typedef struct _owl_message {
+  int id;
+  int type;
+  int admintype;
+  ZNotice_t notice;
+  owl_fmtext fmtext;
+  int delete;
+  char hostname[MAXHOSTNAMELEN];
+  char *sender;
+  char *recip;
+  char *class;
+  char *inst;
+  char *opcode;
+  char *time;
+  char *realm;
+  char *body;
+  char *zwriteline;
+} owl_message;
+
+typedef struct _owl_mainwin {
+  int curtruncated;
+  int lastdisplayed;
+} owl_mainwin;
+
+typedef struct _owl_editwin {
+  char *buff;
+  int bufflen;
+  int allocated;
+  int buffx, buffy;
+  int topline;
+  int winlines, wincols, fillcol, wrapcol;
+  WINDOW *curswin;
+  int style;
+  int lock;
+  int dotsend;
+} owl_editwin;
+
+typedef struct _owl_viewwin {
+  owl_fmtext fmtext;
+  int textlines;
+  int topline;
+  int rightshift;
+  int winlines, wincols;
+  WINDOW *curswin;
+} owl_viewwin;
+  
+typedef struct _owl_popwin {
+  WINDOW *borderwin;
+  WINDOW *popwin;
+  int lines;
+  int cols;
+  int active;
+  int needsfirstrefresh;
+  void (*handler) (int ch);
+} owl_popwin;
+
+typedef struct _owl_messagelist {
+  owl_list list;
+} owl_messagelist;
+
+typedef struct _owl_regex {
+  int negate;
+  char *string;
+  regex_t re;
+} owl_regex;
+
+typedef struct _owl_filterelement {
+  int type;
+  char *field;
+  owl_regex re;
+} owl_filterelement;
+
+typedef struct _owl_filter {
+  char *name;
+  int polarity;
+  owl_list fes; /* filterelements */
+  int color;
+} owl_filter;
+
+typedef struct _owl_view {
+  owl_filter *filter;
+  owl_messagelist ml;
+} owl_view;
+
+typedef struct _owl_history {
+  owl_list hist;
+  int cur;
+  int touched;
+  int partial;
+} owl_history;
+
+typedef struct _owl_keybinding {
+  int  *j;			/* keypress stack (0-terminated) */  
+  int   type;			/* command or function? */
+  char *desc;			/* description (or "*user*") */
+  char *command;		/* command, if of type command */
+  void (*function_fn)(void);	/* function ptr, if of type function */
+} owl_keybinding;
+
+typedef struct _owl_keymap {
+  char     *name;		/* name of keymap */
+  char     *desc;		/* description */
+  owl_list  bindings;		/* key bindings */
+  struct _owl_keymap *submap;	/* submap */
+  void (*default_fn)(int j);	/* default action (takes a keypress) */
+  void (*prealways_fn)(int j);	/* always called before a keypress is received */
+  void (*postalways_fn)(int j);	/* always called after keypress is processed */
+} owl_keymap;
+
+typedef struct _owl_keyhandler {
+  owl_dict  keymaps;		/* dictionary of keymaps */
+  owl_keymap *active;		/* currently active keymap */
+  int	    in_esc;		/* escape pressed? */
+  int       kpstack[OWL_KEYMAP_MAXSTACK+1]; /* current stack of keypresses */
+  int       kpstackpos;		/* location in stack (-1 = none) */
+} owl_keyhandler;
+
+typedef struct _owl_global {
+  owl_mainwin mw;
+  owl_popwin pw;
+  owl_history hist;
+  owl_keyhandler kh;
+  owl_list filterlist;
+  owl_list puntlist;
+  owl_vardict vars;
+  owl_cmddict cmds;
+  owl_context ctx;
+  int lines, cols;
+  int curmsg, topmsg;
+  int curmsg_vert_offset;
+  owl_view current_view;
+  owl_messagelist msglist;
+  WINDOW *recwin, *sepwin, *msgwin, *typwin;
+  int needrefresh;
+  int rightshift;
+  int resizepending;
+  int recwinlines;
+  int typwinactive;
+  char thishost[LINE];
+  char thistty[LINE];
+  char homedir[LINE];
+  int direction;
+  int zaway;
+  char *cur_zaway_msg;
+  int haveconfig;
+  int config_format;
+  char buffercommand[1024];
+  owl_editwin tw;
+  owl_viewwin vw;
+  void *perl;
+  int debug;
+  int starttime;
+  char startupargs[LINE];
+  int userclue;
+  int nextmsgid;
+  int hascolors;
+  int colorpairs;
+  owl_filterelement fe_true;
+  owl_filterelement fe_false;
+  owl_filterelement fe_null;
+} owl_global;
+
+/* globals */
+owl_global g;
+
+#include "owl_prototypes.h"
+
+/* these are missing from the zephyr includes for some reason */
+int ZGetSubscriptions(ZSubscription_t *, int *);
+int ZGetLocations(ZLocations_t *,int *);
+
+#endif /* INC_OWL_H */
Index: popwin.c
===================================================================
--- popwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ popwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,112 @@
+#include "owl.h"
+
+int owl_popwin_init(owl_popwin *pw) {
+  pw->active=0;
+  pw->needsfirstrefresh=0;
+  pw->lines=0;
+  pw->cols=0;
+  return(0);
+}
+
+int owl_popwin_up(owl_popwin *pw) {
+  int glines, gcols, startcol, startline;
+
+  /* calculate the size of the popwin */
+  glines=owl_global_get_lines(&g);
+  gcols=owl_global_get_cols(&g);
+  if (glines > 24) {
+    pw->lines=glines/2;
+    startline=glines/4;
+  } else {
+    pw->lines=(glines*3)/4;
+    startline=glines/8;
+  }
+
+  pw->cols=(gcols*15)/16;
+  startcol=gcols/32;
+  if (pw->cols > 100) {
+    pw->cols=100;
+    startcol=(gcols-100)/2;
+  }
+
+  pw->borderwin=newwin(pw->lines, pw->cols, startline, startcol);
+  pw->popwin=newwin(pw->lines-2, pw->cols-2, startline+1, startcol+1);
+  pw->needsfirstrefresh=1;
+  
+  meta(pw->popwin,TRUE);
+  nodelay(pw->popwin, 1);
+  keypad(pw->popwin, TRUE);
+
+  werase(pw->popwin);
+  werase(pw->borderwin);
+  box(pw->borderwin, 0, 0);
+  wnoutrefresh(pw->popwin);
+  wnoutrefresh(pw->borderwin);
+  owl_global_set_needrefresh(&g);
+  pw->active=1;
+  return(0);
+}
+
+int owl_popwin_display_text(owl_popwin *pw, char *text) {
+  waddstr(pw->popwin, text);
+  wnoutrefresh(pw->popwin);
+  touchwin(pw->borderwin);
+  wnoutrefresh(pw->borderwin);
+  owl_global_set_needrefresh(&g);
+  return(0);
+}	      
+
+int owl_popwin_close(owl_popwin *pw) {
+  delwin(pw->popwin);
+  delwin(pw->borderwin);
+  pw->active=0;
+  owl_global_set_needrefresh(&g);
+  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
+  owl_function_full_redisplay(&g);
+  return(0);
+}
+
+int owl_popwin_is_active(owl_popwin *pw) {
+  if (pw->active==1) return(1);
+  return(0);
+}
+
+int owl_popwin_refresh(owl_popwin *pw) {
+  /* this will refresh the border as well as the text area */
+  touchwin(pw->borderwin);
+  touchwin(pw->popwin);
+
+  wnoutrefresh(pw->borderwin);
+  wnoutrefresh(pw->popwin);
+  owl_global_set_needrefresh(&g);
+  return(0);
+}
+
+void owl_popwin_set_handler(owl_popwin *pw, void (*func)(int ch)) {
+  pw->handler=func;
+}
+
+void owl_popwin_unset_handler(owl_popwin *pw) {
+  pw->handler=NULL;
+}
+
+WINDOW *owl_popwin_get_curswin(owl_popwin *pw) {
+  return(pw->popwin);
+}
+
+int owl_popwin_get_lines(owl_popwin *pw) {
+  return(pw->lines-2);
+}
+
+int owl_popwin_get_cols(owl_popwin *pw) {
+  return(pw->cols-2);
+}
+
+int owl_popwin_needs_first_refresh(owl_popwin *pw) {
+  if (pw->needsfirstrefresh) return(1);
+  return(0);
+}
+
+void owl_popwin_no_needs_first_refresh(owl_popwin *pw) {
+  pw->needsfirstrefresh=0;
+}
Index: readconfig.c
===================================================================
--- readconfig.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ readconfig.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,213 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "owl.h"
+#include <perl.h>
+
+int owl_readconfig(char *file) {
+  int ret;
+  PerlInterpreter *p;
+  char buff[1024], filename[1024];
+  char *embedding[5];
+  struct stat statbuff;
+
+  if (file==NULL) {
+    sprintf(filename, "%s/%s", getenv("HOME"), ".owlconf");
+  } else {
+    strcpy(filename, file);
+  }
+
+  embedding[0]="";
+  embedding[1]=filename;
+  embedding[2]=0;
+
+  /* create and initialize interpreter */
+  p=perl_alloc();
+  owl_global_set_perlinterp(&g, (void*)p);
+  perl_construct(p);
+
+  owl_global_set_no_have_config(&g);
+
+  ret=stat(filename, &statbuff);
+  if (ret) {
+    return(0);
+  }
+
+  ret=perl_parse(p, NULL, 2, embedding, NULL);
+  if (ret) return(-1);
+
+  ret=perl_run(p);
+  if (ret) return(-1);
+
+  owl_global_set_have_config(&g);
+
+  /* create variables */
+  perl_get_sv("owl::id", TRUE);
+  perl_get_sv("owl::class", TRUE);
+  perl_get_sv("owl::instance", TRUE);
+  perl_get_sv("owl::recipient", TRUE);
+  perl_get_sv("owl::sender", TRUE);
+  perl_get_sv("owl::realm", TRUE);
+  perl_get_sv("owl::opcode", TRUE);
+  perl_get_sv("owl::zsig", TRUE);
+  perl_get_sv("owl::msg", TRUE);
+  perl_get_sv("owl::time", TRUE);
+  perl_get_sv("owl::host", TRUE);
+  perl_get_av("owl::fields", TRUE);
+  
+  /* load in owl_command() */
+  strcpy(buff, "sub owl::command {                           \n");
+  strcat(buff, "  my $command = shift;                       \n");
+  strcat(buff, "  push @owl::commands, $command;             \n");
+  strcat(buff, "}                                            \n");
+  perl_eval_pv(buff, FALSE);
+
+
+  /* check if we have the formatting function */
+  if (perl_get_cv("owl::format_msg", FALSE)) {
+    owl_global_set_config_format(&g, 1);
+  }
+  return(0);
+}
+
+
+/* caller is responsible for freeing returned string */
+char *owl_config_execute(char *line) {
+  STRLEN n_a;
+  SV *command, *response;
+  AV *commands;
+  int numcommands, i;
+  char *out, *preout;
+
+  if (!owl_global_have_config(&g)) return NULL;
+
+  /* execute the subroutine */
+  response = perl_eval_pv(line, FALSE);
+
+  preout=SvPV(response, n_a);
+  /* leave enough space in case we have to add a newline */
+  out = owl_malloc(strlen(preout)+2);
+  strcpy(out, preout);
+  if (!strlen(out) || out[strlen(out)-1]!='\n') {
+    strcat(out, "\n");
+  }
+
+  /* execute all the commands, cleaning up the arrays as we go */
+  commands=perl_get_av("owl::commands", TRUE);
+  numcommands=av_len(commands)+1;
+  for (i=0; i<numcommands; i++) {
+    command=av_shift(commands);
+    owl_function_command_norv(SvPV(command, n_a));
+  }
+  av_undef(commands);
+
+  return(out);
+}
+
+char *owl_config_getmsg(owl_message *m, int mode) {
+  /* if mode==1 we are doing message formatting.  The returned
+   * formatted message needs to be freed by the caller.
+   *
+   * if mode==0 we are just doing the message-has-been-received
+   * thing.
+  */
+
+  int i, j, len;
+  char *ptr, *ptr2;
+  ZNotice_t *n;
+
+  if (!owl_global_have_config(&g)) return("");
+
+  /* set owl::msg */
+  n=owl_message_get_notice(m);
+  ptr=owl_zephyr_get_message(n, &len);
+  ptr2=owl_malloc(len+20);
+  memcpy(ptr2, ptr, len);
+  ptr2[len]='\0';
+  if (ptr2[len-1]!='\n') {
+    strcat(ptr2, "\n");
+  }
+  sv_setpv(perl_get_sv("owl::msg", TRUE), ptr2);
+  owl_free(ptr2);
+
+  /* set owl::zsig */
+  ptr=owl_zephyr_get_zsig(n, &len);
+  if (len>0) {
+    ptr2=owl_malloc(len+20);
+    memcpy(ptr2, ptr, len);
+    ptr2[len]='\0';
+    if (ptr2[len-1]=='\n') {  /* do we really need this? */
+      ptr2[len-1]='\0';
+    }
+    sv_setpv(perl_get_sv("owl::zsig", TRUE), ptr2);
+    owl_free(ptr2);
+  } else {
+    sv_setpv(perl_get_sv("owl::zsig", TRUE), "");
+  }
+
+  /* set owl::type */
+  if (owl_message_is_zephyr(m)) {
+    sv_setpv(perl_get_sv("owl::type", TRUE), "zephyr");
+  } else if (owl_message_is_admin(m)) {
+    sv_setpv(perl_get_sv("owl::type", TRUE), "admin");
+  } else {
+    sv_setpv(perl_get_sv("owl::type", TRUE), "unknown");
+  }
+
+  /* set everything else */
+  sv_setpv(perl_get_sv("owl::class", TRUE), owl_message_get_class(m));
+  sv_setpv(perl_get_sv("owl::instance", TRUE), owl_message_get_instance(m));
+  sv_setpv(perl_get_sv("owl::sender", TRUE), owl_message_get_sender(m));
+  sv_setpv(perl_get_sv("owl::realm", TRUE), owl_message_get_realm(m));
+  sv_setpv(perl_get_sv("owl::recipient", TRUE), owl_message_get_recipient(m));
+  sv_setpv(perl_get_sv("owl::opcode", TRUE), owl_message_get_opcode(m));
+  sv_setpv(perl_get_sv("owl::time", TRUE), owl_message_get_timestr(m));
+  sv_setpv(perl_get_sv("owl::host", TRUE), owl_message_get_hostname(m));
+  sv_setiv(perl_get_sv("owl::id", TRUE), owl_message_get_id(m));
+
+  /* free old @fields ? */
+  /* I don't think I need to do this, but ask marc to make sure */
+  /*
+  j=av_len(perl_get_av("fields", TRUE));
+  for (i=0; i<j; i++) {
+    tmpsv=av_pop(perl_get_av("fields", TRUE));
+    SvREFCNT_dec(tmpsv);
+  }
+  */
+
+  /* set owl::fields */
+  av_clear(perl_get_av("owl::fields", TRUE));
+  j=owl_zephyr_get_num_fields(n);
+  for (i=0; i<j; i++) {
+    ptr=owl_zephyr_get_field(n, i+1, &len);
+    ptr2=owl_malloc(len+10);
+    memcpy(ptr2, ptr, len);
+    ptr2[len]='\0';
+    av_push(perl_get_av("owl::fields", TRUE), newSVpvn(ptr2, len));
+    owl_free(ptr2);
+  }
+
+  /* for backwards compatibilty, because I'm an idiot */
+  av_clear(perl_get_av("fields", TRUE));
+  j=owl_zephyr_get_num_fields(n);
+  for (i=0; i<j; i++) {
+    ptr=owl_zephyr_get_field(n, i+1, &len);
+    ptr2=owl_malloc(len+10);
+    memcpy(ptr2, ptr, len);
+    ptr2[len]='\0';
+    av_push(perl_get_av("fields", TRUE), newSVpvn(ptr2, len));
+    owl_free(ptr2);
+  }
+
+  /* run the procedure corresponding to the mode */
+  if (mode==1) {
+    return(owl_config_execute("owl::format_msg();"));
+  } else {
+    ptr=owl_config_execute("owl::receive_msg();");
+    if (ptr) owl_free(ptr);
+    return(NULL);
+  }
+}
Index: regex.c
===================================================================
--- regex.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ regex.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,72 @@
+#include <string.h>
+#include "owl.h"
+
+void owl_regex_init(owl_regex *re) {
+  re->negate=0;
+  re->string=NULL;
+}
+
+int owl_regex_create(owl_regex *re, char *string) {
+  int ret;
+  char buff1[LINE], buff2[LINE];
+  char *ptr;
+  
+  re->string=owl_strdup(string);
+
+  ptr=string;
+  re->negate=0;
+  if (string[0]=='!') {
+    ptr++;
+    re->negate=1;
+  }
+
+  /* set the regex */
+  ret=regcomp(&(re->re), ptr, REG_EXTENDED|REG_ICASE);
+  if (ret) {
+    regerror(ret, NULL, buff1, LINE);
+    sprintf(buff2, "Error in regular expression: %s", buff1);
+    owl_function_makemsg(buff2);
+    owl_free(re->string);
+    return(-1);
+  }
+
+  return(0);
+}
+
+int owl_regex_compare(owl_regex *re, char *string) {
+  int out, ret;
+
+  /* if the regex is not set we match */
+  if (!owl_regex_is_set(re)) {
+    return(0);
+  }
+  
+  ret=regexec(&(re->re), string, 0, NULL, 0);
+  out=ret;
+  if (re->negate) {
+    out=!out;
+  }
+  return(out);
+}
+
+int owl_regex_is_set(owl_regex *re) {
+  if (re->string) return(1);
+  return(0);
+}
+
+char *owl_regex_get_string(owl_regex *re) {
+  return(re->string);
+}
+
+void owl_regex_copy(owl_regex *a, owl_regex *b) {
+  b->negate=a->negate;
+  b->string=owl_strdup(a->string);
+  memcpy(&(b->re), &(a->re), sizeof(regex_t));
+}
+
+void owl_regex_free(owl_regex *re) {
+  if (re->string) owl_free(re->string);
+
+  /* do we need to free the regular expression? */
+  
+}
Index: stubgen.pl
===================================================================
--- stubgen.pl (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ stubgen.pl (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,41 @@
+if ($#ARGV eq -1) {
+    @ARGV=`ls *.c`;
+    chop(@ARGV);
+}
+
+print qq(/* THIS FILE WAS AUTOGENERATED BY STUBGEN.PL --- DO NOT EDIT BY HAND!!! */\n\n);
+print qq(#include "owl.h");
+
+foreach $file (@ARGV) {
+    open(FILE, $file);
+
+    print "/* -------------------------------- $file -------------------------------- */\n";
+    while (<FILE>) {
+	if (m|^\s*OWLVAR_([A-Z_0-9]+)\s*\(\s*"([^"]+)"\s*/\*\s*%OwlVarStub:?([a-z0-9_]+)?\s*\*/|) {   # "
+    my $vartype = $1;
+    my $varname = $2;
+    my $altvarname = $2;
+    $altvarname = $3 if ($3);
+    if ($vartype =~ /^BOOL/) {
+	print "void owl_global_set_${altvarname}_on(owl_global *g) {\n";
+	print "  owl_variable_set_bool_on(&g->vars, \"$varname\");\n}\n";
+	print "void owl_global_set_${altvarname}_off(owl_global *g) {\n";
+	print "  owl_variable_set_bool_off(&g->vars, \"$varname\");\n}\n";
+	print "int owl_global_is_$altvarname(owl_global *g) {\n";
+	print "  return owl_variable_get_bool(&g->vars, \"$varname\");\n}\n";
+    } elsif ($vartype =~ /^PATH/ or $vartype =~ /^STRING/) {
+	print "void owl_global_set_$altvarname(owl_global *g, char *text) {\n";
+	print "  owl_variable_set_string(&g->vars, \"$varname\", text);\n}\n";
+	print "char *owl_global_get_$altvarname(owl_global *g) {\n";
+	print "  return owl_variable_get_string(&g->vars, \"$varname\");\n}\n";
+    } elsif ($vartype =~ /^INT/ or $vartype =~ /^ENUM/) {
+	print "void owl_global_set_$altvarname(owl_global *g, int n) {\n";
+	print "  owl_variable_set_int(&g->vars, \"$varname\", n);\n}\n";
+	print "int owl_global_get_$altvarname(owl_global *g) {\n";
+	print "  return owl_variable_get_int(&g->vars, \"$varname\");\n}\n";
+    } 
+    }
+    }
+    close(FILE);
+    print "\n";
+}
Index: tester.c
===================================================================
--- tester.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ tester.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,156 @@
+#include "owl.h"
+#include <unistd.h>
+#include <stdlib.h>
+
+void screeninit() {
+  char buff[1024];
+  
+  sprintf(buff, "TERMINFO=%s", TERMINFO);
+  putenv(buff);
+
+  initscr();
+  start_color();
+  /* cbreak(); */
+  raw();
+  noecho();
+  intrflush(stdscr,FALSE);
+  keypad(stdscr,TRUE);
+  nodelay(stdscr,1);
+  clear();
+  refresh();
+  meta(stdscr, TRUE);
+}
+
+void test1() {
+  int j;
+  owl_editwin e;
+
+  screeninit();
+
+  owl_editwin_init(&e, stdscr, LINES, COLS, OWL_EDITWIN_STYLE_MULTILINE);
+  /* owl_editwin_set_locktext(&e, "Here is some locktext:\n");*/
+  doupdate();
+  while (1) {
+    usleep(50);
+
+    j=getch();
+
+    if (j==ERR) continue;
+
+    if (j==3) break;
+
+    if (j==27) {
+      j=getch();
+      if (j==ERR) continue;
+      owl_editwin_process_char(&e, j);
+      doupdate();
+    } else {
+      owl_editwin_process_char(&e, j);
+      doupdate();
+    }
+  }
+  endwin();
+  printf("Had:\n%s", owl_editwin_get_text(&e));
+}
+
+void test2(char *in) {
+  owl_fmtext t;
+
+  screeninit();
+
+  owl_fmtext_init_null(&t);
+  owl_fmtext_append_ztext(&t, in);
+  owl_fmtext_curs_waddstr(&t, stdscr);
+  wrefresh(stdscr);
+  sleep(5000);
+  endwin();
+}
+
+void test3() {
+  ZNotice_t *n;
+
+  printf("%i\n", sizeof(n->z_uid.zuid_addr));
+  /* gethostbyaddr((char *) &(n->z_uid.zuid_addr), sizeof(n->z_uid.zuid_addr), AF_INET); */
+}
+
+void colorinfo() {
+  char buff[1024];
+  
+  screeninit();
+  sprintf(buff, "Have %i COLOR_PAIRS\n", COLOR_PAIRS);
+  addstr(buff);
+  refresh();
+  sleep(10);
+  endwin();  
+}
+
+void test4() {
+  int j;
+  char buff[1024];
+
+  screeninit();
+  
+  while (1) {
+    usleep(100);
+
+    j=getch();
+
+    if (j==ERR) continue;
+
+    if (j==3) break;
+    sprintf(buff, "%o\n", j);
+    addstr(buff);
+  }
+  endwin();
+}
+
+void test_keypress() {
+  int j, rev;
+  char buff[1024], buff2[64];
+
+  screeninit();
+  
+  while (1) {
+    usleep(100);
+
+    j=wgetch(stdscr);
+
+    if (j==ERR) continue;
+
+    if (j==3) break;
+    if (0 == owl_keypress_tostring(j, 0, buff2, 1000)) {
+      rev = owl_keypress_fromstring(buff2);
+      sprintf(buff, "%s : 0x%x 0%o %d %d %s\n", buff2, j, j, j, rev,
+	      (j==rev?"matches":"*** WARNING: Does Not Reverse"));
+    } else {
+      sprintf(buff, "UNKNOWN : 0x%x 0%o %d\n", j, j, j);
+    }
+      addstr(buff);
+  }
+  endwin();
+}
+
+
+int main(int argc, char **argv, char **env) {
+  int numfailures=0;
+  if (argc==2 && 0==strcmp(argv[1],"reg")) {
+    numfailures += owl_dict_regtest();
+    numfailures += owl_variable_regtest();
+    if (numfailures) {
+      fprintf(stderr, "*** WARNING: %d failures total\n", numfailures);
+    }
+    return(numfailures);
+  } else if (argc==2 && 0==strcmp(argv[1],"test1")) {
+    test1();
+  } else if (argc==2 && 0==strcmp(argv[1],"colorinfo")) {
+    colorinfo();
+  } else if (argc==2 && 0==strcmp(argv[1],"test4")) {
+    test4();
+  } else if (argc==2 && 0==strcmp(argv[1],"keypress")) {
+    test_keypress();
+  } else {
+    fprintf(stderr, "No test specified.  Current options are: reg test1\n");
+  }
+  return(0);
+}
+
Index: text.c
===================================================================
--- text.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ text.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "owl.h"
+
+int owl_text_truncate_lines(char *out, char *in, int aline, int lines) {
+  /* start with line aline (where the first line is 1) and print
+   *  'lines' lines
+   */
+  char *ptr1, *ptr2;
+  int i;
+
+  strcpy(out, "");
+  
+  if (aline==0) aline=1; /* really illegal use */
+
+  /* find the starting line */
+  ptr1=in;
+  if (aline!=1) {
+     for (i=0; i<aline-1; i++) {
+      ptr1=strchr(ptr1, '\n');
+      if (!ptr1) return(-1);
+      ptr1++;
+    }
+  }
+  /* ptr1 now holds the starting point */
+
+  /* copy in the next 'lines' lines */
+  if (lines<1) return(-1);
+  
+  for (i=0; i<lines; i++) {
+    ptr2=strchr(ptr1, '\n');
+    if (!ptr2) {
+      strcat(out, ptr1);
+      return(-1);
+    }
+    strncat(out, ptr1, ptr2-ptr1+1);
+    ptr1=ptr2+1;
+  }
+  return(0);
+}
+
+void owl_text_truncate_cols(char *out, char *in, int acol, int bcol) {
+  char *ptr1, *ptr2, *tmpbuff, *last;
+  int len;
+  
+  /* the first column is column 0 */
+
+  /* the message is expected to end in a new line for now */
+
+  tmpbuff=owl_malloc(strlen(in)+20);
+
+  strcpy(tmpbuff, "");
+  last=in+strlen(in)-1;
+  ptr1=in;
+  while (ptr1<last) {
+    ptr2=strchr(ptr1, '\n');
+    if (!ptr2) {
+      /* but this shouldn't happen if we end in a \n */
+      break;
+    }
+    
+    if (ptr2==ptr1) {
+      strcat(tmpbuff, "\n");
+      ptr1++;
+      continue;
+    }
+
+    /* we need to check that we won't run over here */
+    if ( (ptr2-ptr1) < (bcol-acol) ) {
+      len=ptr2-(ptr1+acol);
+    } else {
+      len=bcol-acol;
+    }
+    if ((ptr1+len)>=last) {
+      len-=last-(ptr1+len);
+    }
+
+    strncat(tmpbuff, ptr1+acol, len);
+    strcat(tmpbuff, "\n");
+
+    ptr1=ptr2+1;
+  }
+  strcpy(out, tmpbuff);
+  owl_free(tmpbuff);
+}
+
+
+void owl_text_indent(char *out, char *in, int n) {
+  char *ptr1, *ptr2, *last;
+  int i;
+
+  strcpy(out, "");
+
+  last=in+strlen(in)-1;
+  ptr1=in;
+  while (ptr1<=last) {
+    for (i=0; i<n; i++) {
+      strcat(out, " ");
+    }
+    ptr2=strchr(ptr1, '\n');
+    if (!ptr2) {
+      strcat(out, ptr1);
+      break;
+    } else {
+      strncat(out, ptr1, ptr2-ptr1+1);
+    }
+    ptr1=ptr2+1;
+  }
+}
+
+
+int owl_text_num_lines(char *in) {
+  int lines, i;
+
+  lines=0;
+  for (i=0; in[i]!='\0'; i++) {
+    if (in[i]=='\n') lines++;
+  }
+
+  /* if the last char wasn't a \n there's one more line */
+  if (in[i-1]!='\n') lines++;
+
+  return(lines);
+}
+
Index: util.c
===================================================================
--- util.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ util.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,386 @@
+#include "owl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+
+void sepbar(char *in) {
+  char buff[1024];
+  WINDOW *sepwin;
+  owl_messagelist *ml;
+  owl_view *v;
+  int x, y, i;
+  char *foo, *appendtosepbar;
+
+  sepwin=owl_global_get_curs_sepwin(&g);
+  ml=owl_global_get_msglist(&g);
+  v=owl_global_get_current_view(&g);
+
+  werase(sepwin);
+  wattron(sepwin, A_REVERSE);
+  whline(sepwin, ACS_HLINE, owl_global_get_cols(&g));
+
+  wmove(sepwin, 0, 2);  
+
+  if (owl_messagelist_get_size(ml)==0) {
+    strcpy(buff, " (-/-) ");
+  } else {
+    sprintf(buff, " (%i/%i/%i) ", owl_global_get_curmsg(&g)+1,
+	    owl_view_get_size(v),
+	    owl_messagelist_get_size(ml));
+  }
+  waddstr(sepwin, buff);
+
+  foo=owl_view_get_filtname(v);
+  if (strcmp(foo, "all")) wattroff(sepwin, A_REVERSE);
+  waddstr(sepwin, " ");
+  waddstr(sepwin, owl_view_get_filtname(v));
+  waddstr(sepwin, " ");
+  if (strcmp(foo, "all")) wattron(sepwin, A_REVERSE);
+
+  if (owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    waddstr(sepwin, " <truncated> ");
+    wattroff(sepwin, A_BOLD);
+  }
+
+  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
+  if ((i != -1) &&
+      (i < owl_view_get_size(v)-1)) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    waddstr(sepwin, " <more> ");
+    wattroff(sepwin, A_BOLD);
+  }
+
+  if (owl_global_get_rightshift(&g)>0) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    sprintf(buff, " right: %i ", owl_global_get_rightshift(&g));
+    waddstr(sepwin, buff);
+  }
+
+  if (owl_global_is_zaway(&g)) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    wattroff(sepwin, A_REVERSE);
+    waddstr(sepwin, " ZAWAY ");
+    wattron(sepwin, A_REVERSE);
+    wattroff(sepwin, A_BOLD);
+  }
+
+  if (owl_global_get_curmsg_vert_offset(&g)) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    wattroff(sepwin, A_REVERSE);
+    waddstr(sepwin, " SCROLL ");
+    wattron(sepwin, A_REVERSE);
+    wattroff(sepwin, A_BOLD);
+  }
+  
+  if (in) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    waddstr(sepwin, in);
+  }
+
+  appendtosepbar = owl_global_get_appendtosepbar(&g);
+  if (appendtosepbar && *appendtosepbar) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    waddstr(sepwin, " ");
+    waddstr(sepwin, owl_global_get_appendtosepbar(&g));
+    waddstr(sepwin, " ");
+  }
+
+  getyx(sepwin, y, x);
+  wmove(sepwin, y, owl_global_get_cols(&g)-1);
+    
+  wattroff(sepwin, A_BOLD);
+  wattroff(sepwin, A_REVERSE);
+  wnoutrefresh(sepwin);
+}
+
+
+void pophandler_quit(int ch) {
+  if (ch=='q') {
+    owl_popwin_close(owl_global_get_popwin(&g));
+  }
+}
+
+char **atokenize(char *buffer, char *sep, int *i) {
+  /* each element of return must be freed by user */
+  char **args;
+  char *workbuff, *foo;
+  int done=0, first=1, count=0;
+
+  workbuff=owl_malloc(strlen(buffer)+1);
+  memcpy(workbuff, buffer, strlen(buffer)+1);
+
+  args=NULL;
+  while (!done) {
+    if (first) {
+      first=0;
+      foo=(char *)strtok(workbuff, sep);
+    } else {
+      foo=(char *)strtok(NULL, sep);
+    }
+    if (foo==NULL) {
+      done=1;
+    } else {
+      args=(char **)owl_realloc(args, sizeof(char *) * (count+1));
+      args[count]=owl_malloc(strlen(foo)+1);
+      strcpy(args[count], foo);
+      count++;
+    }
+  }
+  *i=count;
+  owl_free(workbuff);
+  return(args);
+}
+
+/* skips n tokens and returns where that would be.
+ * TODO: handle quotes. */
+char *skiptokens(char *buff, int n) {
+  int inquotes=0;
+  while (*buff && n>0) {
+      while (*buff == ' ') buff++;
+      while (*buff && (inquotes || *buff != ' ')) { 
+	if (*buff == '"') inquotes=!inquotes;
+	buff++;
+      }
+      while (*buff == ' ') buff++;
+      n--;
+  }
+  return buff;
+}
+
+void atokenize_free(char **tok, int nels) {
+  int i;
+  for (i=0; i<nels; i++) {
+    owl_free(tok[i]);
+  }
+  owl_free(tok);
+}
+
+
+void owl_parsefree(char **argv, int argc) {
+  int i;
+
+  if (!argv) return;
+  
+  for (i=0; i<argc; i++) {
+    if (argv[i]) owl_free(argv[i]);
+  }
+  owl_free(argv);
+}
+
+char **owl_parseline(char *line, int *argc) {
+  /* break a command line up into argv, argc.  The caller must free
+     the returned values.  If there is an error argc will be set to
+     -1, argv will be NULL and the caller does not need to free
+     anything */
+
+  char **argv;
+  int i, len, between=1;
+  char *curarg;
+  char quote;
+
+  argv=owl_malloc(sizeof(char *));
+  len=strlen(line);
+  curarg=owl_malloc(len+10);
+  strcpy(curarg, "");
+  quote='\0';
+  *argc=0;
+  for (i=0; i<len+1; i++) {
+    /* find the first real character */
+    if (between) {
+      if (line[i]==' ' || line[i]=='\t' || line[i]=='\0') {
+	continue;
+      } else {
+	between=0;
+	i--;
+	continue;
+      }
+    }
+
+    /* deal with a quote character */
+    if (line[i]=='"' || line[i]=="'"[0]) {
+      /* if this type of quote is open, close it */
+      if (quote==line[i]) {
+	quote='\0';
+	continue;
+      }
+
+      /* if no quoting is open then open with this */
+      if (quote=='\0') {
+	quote=line[i];
+	continue;
+      }
+
+      /* if another type of quote is open then treat this as a literal */
+      curarg[strlen(curarg)+1]='\0';
+      curarg[strlen(curarg)]=line[i];
+      continue;
+    }
+
+    /* if it's not a space or end of command, then use it */
+    if (line[i]!=' ' && line[i]!='\t' && line[i]!='\n' && line[i]!='\0') {
+      curarg[strlen(curarg)+1]='\0';
+      curarg[strlen(curarg)]=line[i];
+      continue;
+    }
+
+    /* otherwise, if we're not in quotes, add the whole argument */
+    if (quote=='\0') {
+      /* add the argument */
+      argv=owl_realloc(argv, sizeof(char *)*((*argc)+1));
+      argv[*argc]=owl_malloc(strlen(curarg)+2);
+      strcpy(argv[*argc], curarg);
+      *argc=*argc+1;
+      strcpy(curarg, "");
+      between=1;
+      continue;
+    }
+
+    /* if it is a space and we're in quotes, then use it */
+    curarg[strlen(curarg)+1]='\0';
+    curarg[strlen(curarg)]=line[i];
+  }
+
+  /* check for unbalanced quotes */
+  if (quote!='\0') {
+    owl_parsefree(argv, *argc);
+    *argc=-1;
+    return(NULL);
+  }
+
+  return(argv);
+}
+
+
+
+int owl_util_find_trans(char *in, int len) {
+  /* return the index of the last char before a change from the first
+     one */
+  int i;
+  for (i=1; i<len; i++) {
+    if (in[i] != in[0]) return(i-1);
+  }
+  return(i);
+}
+
+
+void downstr(char *foo) {
+  int i;
+  for (i=0; foo[i]!='\0'; i++) {
+    foo[i]=tolower(foo[i]);
+  }
+}
+
+char *stristr(char *a, char *b) {
+  char *x, *y, *ret;
+
+  if ((x=owl_strdup(a))==NULL) return(NULL);
+  if ((y=owl_strdup(b))==NULL) return(NULL);
+  downstr(x);
+  downstr(y);
+  ret=strstr(x, y);
+  owl_free(x);
+  owl_free(y);
+  return(ret);
+}
+
+
+void *owl_malloc(size_t size) {
+  return(malloc(size));
+}
+
+void owl_free(void *ptr) {
+  free(ptr);
+}
+
+char *owl_strdup(const char *s1) {
+  return(strdup(s1));
+}
+
+void *owl_realloc(void *ptr, size_t size) {
+  return(realloc(ptr, size));
+}
+
+char *pretty_sender(char *in) {
+  char *out, *ptr;
+  
+  /* the caller must free the return */
+  out=owl_strdup(in);
+  ptr=strchr(out, '@');
+  if (ptr) {
+    if (!strcasecmp(ptr+1, ZGetRealm())) {
+      *ptr='\0';
+    }
+  }
+  return(out);
+}
+
+char *long_sender(char *in) {
+  char *out, *ptr;
+
+  /* the caller must free the return */
+  out=owl_malloc(strlen(in)+100);
+  strcpy(out, in);
+  ptr=strchr(out, '@');
+  if (ptr) return(out);
+  sprintf(out, "%s@%s", out, ZGetRealm());
+  return(out);
+}
+		  
+
+char *owl_getquoting(char *line) {
+  if (line[0]=='\0') return("'");
+  if (strchr(line, '\'')) return("\"");
+  if (strchr(line, '"')) return("'");
+  if (strchr(line, ' ')) return("'");
+  return("");
+}
+
+
+int owl_util_string_to_color(char *color) {
+  if (!strcasecmp(color, "black")) {
+    return(OWL_COLOR_BLACK);
+  } else if (!strcasecmp(color, "red")) {
+    return(OWL_COLOR_RED);
+  } else if (!strcasecmp(color, "green")) {
+    return(OWL_COLOR_GREEN);
+  } else if (!strcasecmp(color, "yellow")) {
+    return(OWL_COLOR_YELLOW);
+  } else if (!strcasecmp(color, "blue")) {
+    return(OWL_COLOR_BLUE);
+  } else if (!strcasecmp(color, "magenta")) {
+    return(OWL_COLOR_MAGENTA);
+  } else if (!strcasecmp(color, "cyan")) {
+    return(OWL_COLOR_CYAN);
+  } else if (!strcasecmp(color, "white")) {
+    return(OWL_COLOR_WHITE);
+  } else if (!strcasecmp(color, "default")) {
+    return(OWL_COLOR_DEFAULT);
+  }
+  return(OWL_COLOR_DEFAULT);
+}
+
+char *owl_util_color_to_string(int color) {
+  if (color==OWL_COLOR_BLACK)   return("black");
+  if (color==OWL_COLOR_RED)     return("red");
+  if (color==OWL_COLOR_GREEN)   return("green");
+  if (color==OWL_COLOR_YELLOW)  return("yellow");
+  if (color==OWL_COLOR_BLUE)    return("blue");
+  if (color==OWL_COLOR_MAGENTA) return("magenta");
+  if (color==OWL_COLOR_CYAN)    return("cyan");
+  if (color==OWL_COLOR_WHITE)   return("white");
+  if (color==OWL_COLOR_DEFAULT) return("default");
+  return("Unknown color");
+}
Index: variable.c
===================================================================
--- variable.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ variable.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,721 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "owl.h"
+
+#define OWLVAR_BOOL(name,default,docstring) \
+        { name, OWL_VARIABLE_BOOL, NULL, default, "on,off", docstring, NULL, \
+        NULL, NULL, NULL, NULL, NULL }
+
+#define OWLVAR_BOOL_FULL(name,default,docstring,validate,set,get) \
+        { name, OWL_VARIABLE_BOOL, NULL, default, "on,off", docstring, NULL, \
+        validate, set, NULL, get, NULL }
+
+#define OWLVAR_INT(name,default,docstring) \
+        { name, OWL_VARIABLE_INT, NULL, default, "<int>", docstring, NULL, \
+        NULL, NULL, NULL, NULL, NULL, NULL }
+
+#define OWLVAR_INT_FULL(name,default,docstring,validset,validate,set,get) \
+        { name, OWL_VARIABLE_INT, NULL, default, validset, docstring, NULL, \
+        validate, set, NULL, get, NULL, NULL }
+
+#define OWLVAR_PATH(name,default,docstring) \
+        { name, OWL_VARIABLE_STRING, default, 0, "<path>", docstring,  NULL, \
+        NULL, NULL, NULL, NULL, NULL, NULL }
+
+#define OWLVAR_STRING(name,default,docstring) \
+        { name, OWL_VARIABLE_STRING, default, 0, "<string>", docstring, NULL, \
+        NULL, NULL, NULL, NULL, NULL, NULL }
+
+/* enums are really integers, but where validset is a comma-separated
+ * list of strings which can be specified.  The tokens, starting at 0,
+ * correspond to the values that may be specified. */
+#define OWLVAR_ENUM(name,default,docstring,validset) \
+        { name, OWL_VARIABLE_INT, NULL, default, validset, docstring, NULL, \
+        owl_variable_enum_validate, \
+        NULL, owl_variable_enum_set_fromstring, \
+        NULL, owl_variable_enum_get_tostring, \
+        NULL }
+
+static owl_variable variables_to_init[] = {
+
+  OWLVAR_BOOL( "personalbell" /* %OwlVarStub */, 0,
+	       "ring the terminal bell when personal messages are received" ),
+
+  OWLVAR_BOOL( "bell" /* %OwlVarStub */, 1,
+	       "enable / disable the terminal bell" ),
+
+  OWLVAR_BOOL_FULL( "debug" /* %OwlVarStub */, OWL_DEBUG,
+		    "whether debugging is enabled",
+		    NULL, owl_variable_debug_set, NULL),
+
+  OWLVAR_BOOL( "startuplogin" /* %OwlVarStub */, 1,
+	       "send a login message when owl starts" ),
+
+  OWLVAR_BOOL( "shutdownlogout" /* %OwlVarStub */, 1,
+	       "send a logout message when owl exits" ),
+
+  OWLVAR_BOOL( "rxping" /* %OwlVarStub */, 0,
+	       "display received pings" ),
+
+  OWLVAR_BOOL( "txping" /* %OwlVarStub */, 1,
+	       "send pings" ),
+
+  OWLVAR_BOOL( "displayoutgoing" /* %OwlVarStub */, 1,
+	       "display outgoing messages" ),
+
+  OWLVAR_BOOL( "loginsubs" /* %OwlVarStub */, 1,
+	       "load logins from .anyone on startup" ),
+
+  OWLVAR_BOOL( "logging" /* %OwlVarStub */, 0,
+	       "turn personal logging on or off" ),
+
+  OWLVAR_BOOL( "classlogging" /* %OwlVarStub */, 0,
+	       "turn class logging on or off" ),
+
+  OWLVAR_BOOL_FULL( "disable-ctrl-d" /* %OwlVarStub:lockout_ctrld */, 0,
+		    "don't send zephyrs on C-d",
+		    NULL, owl_variable_disable_ctrl_d_set, NULL),
+
+  OWLVAR_BOOL( "_burningears" /* %OwlVarStub:burningears */, 0,
+	       "[NOT YET IMPLEMENTED] beep on messages matching patterns" ),
+
+  OWLVAR_BOOL( "_summarymode" /* %OwlVarStub:summarymode */, 0,
+	       "[NOT YET IMPLEMENTED]" ),
+
+  OWLVAR_PATH( "logpath" /* %OwlVarStub */, "~/zlog/people",
+	       "path for logging personal zephyrs" ),
+
+  OWLVAR_PATH( "classlogpath" /* %OwlVarStub:classlogpath */, "~/zlog/class",
+	       "path for logging class zephyrs" ),
+
+  OWLVAR_PATH( "debug_file" /* %OwlVarStub */, OWL_DEBUG_FILE,
+	       "path for logging debug messages when debugging is enabled" ),
+  
+  OWLVAR_PATH( "zsigproc" /* %OwlVarStub:zsig_exec */, NULL,
+	       "name of a program to run that will generate zsigs" ),
+
+  OWLVAR_STRING( "zsig" /* %OwlVarStub */, "",
+	         "zephyr signature" ),
+
+  OWLVAR_STRING( "appendtosepbar" /* %OwlVarStub */, "",
+	         "string to append to the end of the sepbar" ),
+
+  OWLVAR_BOOL( "zaway" /* %OwlVarStub */, 0,
+	       "turn zaway on or off" ),
+
+  OWLVAR_STRING( "zaway_msg" /* %OwlVarStub */, 
+		 OWL_DEFAULT_ZAWAYMSG,
+	         "zaway msg for responding to zephyrs when away" ),
+
+  OWLVAR_STRING( "zaway_msg_default" /* %OwlVarStub */, 
+		 OWL_DEFAULT_ZAWAYMSG,
+	         "default zaway message" ),
+
+  OWLVAR_STRING( "view_home" /* %OwlVarStub */, "all",
+	         "home view to switch to after 'X'" ),
+
+  OWLVAR_INT_FULL( "typewinsize" /* %OwlVarStub:typwin_lines */, 
+		   OWL_TYPWIN_SIZE,
+		  "number of lines in the typing window", "int > 0",
+		   owl_variable_int_validate_gt0,
+		   owl_variable_typewinsize_set,
+		   NULL /* use default for get */
+		   ),
+
+  OWLVAR_ENUM( "webbrowser" /* %OwlVarStub */, OWL_WEBBROWSER_NETSCAPE,
+	       "web browser to use to launch URLs",
+	       "none,netscape,galeon" ),
+
+  OWLVAR_BOOL( "_followlast" /* %OwlVarStub */, 0,
+	       "enable automatic following of the last zephyr" ),
+
+  /* This MUST be last... */
+  { NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
+};
+
+/**************************************************************************/
+/*********************** SPECIFIC TO VARIABLES ****************************/
+/**************************************************************************/
+
+
+/* commonly useful */
+
+int owl_variable_int_validate_gt0(owl_variable *v, void *newval) {
+  if (newval == NULL) return(0);
+  else if (*(int*)newval < 1) return(0);
+  else return (1);
+}
+
+int owl_variable_int_validate_positive(owl_variable *v, void *newval) {
+  if (newval == NULL) return(0);
+  else if (*(int*)newval < 0) return(0);
+  else return (1);
+}
+
+/* typewinsize */
+
+int owl_variable_typewinsize_set(owl_variable *v, void *newval) {
+  int rv;
+  rv = owl_variable_int_set_default(v, newval);
+  if (0 == rv) owl_function_resize();
+  return(rv);
+}
+
+/* debug (cache value in g->debug) */
+
+int owl_variable_debug_set(owl_variable *v, void *newval) {
+  if (newval && (*(int*)newval == 1 || *(int*)newval == 0)) {
+    g.debug = *(int*)newval;
+  }
+  return owl_variable_bool_set_default(v, newval);
+}
+
+/* note that changing the value of this will clobber 
+ * any user setting of this */
+int owl_variable_disable_ctrl_d_set(owl_variable *v, void *newval) {
+  if (newval && !owl_context_is_startup(owl_global_get_context(&g))
+      && (*(int*)newval == 1 || *(int*)newval == 0)) {
+    if (*(int*)newval) {
+      owl_function_command_norv("bindkey editmulti C-d command edit:delete-next-char");
+    } else {
+      owl_function_command_norv("bindkey editmulti C-d command editmulti:done");
+    }
+  }  
+  return owl_variable_bool_set_default(v, newval);  
+}
+
+
+/**************************************************************************/
+/****************************** GENERAL ***********************************/
+/**************************************************************************/
+
+int owl_variable_dict_setup(owl_vardict *vd) {
+  owl_variable *cur;
+  if (owl_dict_create(vd)) return(-1);
+  for (cur = variables_to_init; cur->name != NULL; cur++) {
+    switch (cur->type) {
+    case OWL_VARIABLE_OTHER:
+      cur->set_fn(cur, cur->pval_default);
+      break;
+    case OWL_VARIABLE_STRING:
+      if (!cur->validate_fn) 
+	cur->validate_fn = owl_variable_string_validate_default;
+      if (!cur->set_fn) 
+	cur->set_fn = owl_variable_string_set_default;
+      if (!cur->set_fromstring_fn) 
+	cur->set_fromstring_fn = owl_variable_string_set_fromstring_default;
+      if (!cur->get_fn) 
+	cur->get_fn = owl_variable_get_default;
+      if (!cur->get_tostring_fn) 
+	cur->get_tostring_fn = owl_variable_string_get_tostring_default;      
+      if (!cur->free_fn) 
+	cur->free_fn = owl_variable_free_default;
+      cur->set_fn(cur, cur->pval_default);
+      break;
+    case OWL_VARIABLE_BOOL:
+      if (!cur->validate_fn) 
+	cur->validate_fn = owl_variable_bool_validate_default;
+      if (!cur->set_fn) 
+	cur->set_fn = owl_variable_bool_set_default;
+      if (!cur->set_fromstring_fn) 
+	cur->set_fromstring_fn = owl_variable_bool_set_fromstring_default;
+      if (!cur->get_fn) 
+	cur->get_fn = owl_variable_get_default;
+      if (!cur->get_tostring_fn) 
+	cur->get_tostring_fn = owl_variable_bool_get_tostring_default;      
+      if (!cur->free_fn) 
+	cur->free_fn = owl_variable_free_default;
+      cur->val = owl_malloc(sizeof(int));
+      cur->set_fn(cur, &cur->ival_default);
+      break;
+    case OWL_VARIABLE_INT:
+      if (!cur->validate_fn) 
+	cur->validate_fn = owl_variable_int_validate_default;
+      if (!cur->set_fn) 
+	cur->set_fn = owl_variable_int_set_default;
+      if (!cur->set_fromstring_fn) 
+	cur->set_fromstring_fn = owl_variable_int_set_fromstring_default;
+      if (!cur->get_fn) 
+	cur->get_fn = owl_variable_get_default;
+      if (!cur->get_tostring_fn) 
+	cur->get_tostring_fn = owl_variable_int_get_tostring_default;      
+      if (!cur->free_fn) 
+	cur->free_fn = owl_variable_free_default;
+      cur->val = owl_malloc(sizeof(int));
+      cur->set_fn(cur, &cur->ival_default);
+      break;
+    default:
+      fprintf(stderr, "owl_variable_setup: invalid variable type\n");
+      return(-2);
+    }
+    owl_dict_insert_element(vd, cur->name, (void*)cur, NULL);
+  }
+  return 0;
+}
+
+void owl_variable_dict_free(owl_vardict *d) {
+  owl_dict_free_all(d, (void(*)(void*))owl_variable_free);
+}
+
+/* free the list with owl_variable_dict_namelist_free */
+void owl_variable_dict_get_names(owl_vardict *d, owl_list *l) {
+  owl_dict_get_keys(d, l);
+}
+
+void owl_variable_dict_namelist_free(owl_list *l) {
+  owl_list_free_all(l, owl_free);
+}
+
+void owl_variable_free(owl_variable *v) {
+  if (v->free_fn) v->free_fn(v);
+}
+
+
+char *owl_variable_get_docstring(owl_variable *v) {
+  return v->docstring;
+}
+
+char *owl_variable_get_validsettings(owl_variable *v) {
+  if (v->validsettings) {
+    return v->validsettings;
+  } else {
+    return "";
+  }
+}
+
+/* functions for getting and setting variable values */
+
+/* returns 0 on success, prints a status msg if msg is true */
+int owl_variable_set_fromstring(owl_vardict *d, char *name, char *value, int msg) {
+  owl_variable *v;
+  char buff2[1024];
+  if (!name) return(-1);
+  v = owl_dict_find_element(d, name);
+  if (v == NULL) {
+    if (msg) owl_function_makemsg("Unknown variable %s", name);
+    return -1;
+  }
+  if (!v->set_fromstring_fn) {
+    if (msg) owl_function_makemsg("Variable %s is read-only", name);
+    return -1;
+    
+  }
+  if (0 != v->set_fromstring_fn(v, value)) {
+    if (msg) owl_function_makemsg("Unable to set %s (must be %s)", name, 
+				  owl_variable_get_validsettings(v));
+    return -1;
+  }
+  if (msg && v->get_tostring_fn) {
+    v->get_tostring_fn(v, buff2, 1024, v->val);
+    owl_function_makemsg("%s = '%s'", name, buff2);
+  }    
+  return 0;
+}
+ 
+int owl_variable_set_string(owl_vardict *d, char *name, char *newval) {
+  owl_variable *v;
+  if (!name) return(-1);
+  v = owl_dict_find_element(d, name);
+  if (v == NULL || !v->set_fn) return(-1);
+  if (v->type!=OWL_VARIABLE_STRING) return(-1);
+  return v->set_fn(v, (void*)newval);
+}
+ 
+int owl_variable_set_int(owl_vardict *d, char *name, int newval) {
+  owl_variable *v;
+  if (!name) return(-1);
+  v = owl_dict_find_element(d, name);
+  if (v == NULL || !v->set_fn) return(-1);
+  if (v->type!=OWL_VARIABLE_INT && v->type!=OWL_VARIABLE_BOOL) return(-1);
+  return v->set_fn(v, &newval);
+}
+ 
+int owl_variable_set_bool_on(owl_vardict *d, char *name) {
+  return owl_variable_set_int(d,name,1);
+}
+
+int owl_variable_set_bool_off(owl_vardict *d, char *name) {
+  return owl_variable_set_int(d,name,0);
+}
+
+int owl_variable_get_tostring(owl_vardict *d, char *name, char *buf, int bufsize) {
+  owl_variable *v;
+  if (!name) return(-1);
+  v = owl_dict_find_element(d, name);
+  if (v == NULL || !v->get_tostring_fn) return(-1);
+  return v->get_tostring_fn(v, buf, bufsize, v->val);
+}
+
+int owl_variable_get_default_tostring(owl_vardict *d, char *name, char *buf, int bufsize) {
+  owl_variable *v;
+  if (!name) return(-1);
+  v = owl_dict_find_element(d, name);
+  if (v == NULL || !v->get_tostring_fn) return(-1);
+  if (v->type == OWL_VARIABLE_INT || v->type == OWL_VARIABLE_BOOL) {
+    return v->get_tostring_fn(v, buf, bufsize, &(v->ival_default));
+  } else {
+    return v->get_tostring_fn(v, buf, bufsize, v->pval_default);
+  }
+}
+
+/* returns a reference */
+void *owl_variable_get(owl_vardict *d, char *name, int require_type) {
+  owl_variable *v;
+  if (!name) return(NULL);
+  v = owl_dict_find_element(d, name);
+  if (v == NULL || !v->get_fn || v->type != require_type) return(NULL);
+  return v->get_fn(v);
+}
+
+/* returns a reference */
+char *owl_variable_get_string(owl_vardict *d, char *name) {
+  return (char*)owl_variable_get(d,name, OWL_VARIABLE_STRING);
+}
+
+/* returns a reference */
+void *owl_variable_get_other(owl_vardict *d, char *name) {
+  return (char*)owl_variable_get(d,name, OWL_VARIABLE_OTHER);
+}
+
+int owl_variable_get_int(owl_vardict *d, char *name) {
+  int *pi;
+  pi = (int*)owl_variable_get(d,name,OWL_VARIABLE_INT);
+  if (!pi) return(-1);
+  return(*pi);
+}
+
+int owl_variable_get_bool(owl_vardict *d, char *name) {
+  int *pi;
+  pi = (int*)owl_variable_get(d,name,OWL_VARIABLE_BOOL);
+  if (!pi) return(-1);
+  return(*pi);
+}
+
+#define OWL_VARIABLE_HELP_FMT "     %-15s %-9s %-9s %s\n"
+
+/* appends to the end of the fmtext */
+void owl_variable_get_summaryheader(owl_fmtext *fm) {
+  char tmpbuff[512];
+  snprintf(tmpbuff, 512,  
+	   OWL_VARIABLE_HELP_FMT OWL_VARIABLE_HELP_FMT,
+	   "name",            "settings", "default", "meaning",
+	   "---------------", "--------", "-------", "-------");
+  owl_fmtext_append_bold(fm, tmpbuff);
+}
+
+void owl_variable_get_summary(owl_vardict *d, char *name, owl_fmtext *fm) {
+  char defaultbuf[10];
+  char buf[1024];
+  int buflen = 1023;
+  owl_variable *v;
+
+  if (!name
+      || (v = owl_dict_find_element(d, name)) == NULL 
+      || !v->get_fn) {
+    snprintf(buf, buflen, "     No such variable '%s'\n", name);     
+    owl_fmtext_append_normal(fm, buf);
+    return;
+  }
+  if (v->type == OWL_VARIABLE_INT || v->type == OWL_VARIABLE_BOOL) {
+    v->get_tostring_fn(v, defaultbuf, 10, &(v->ival_default));
+  } else {
+    v->get_tostring_fn(v, defaultbuf, 10, v->pval_default);
+  }
+  snprintf(buf, buflen, OWL_VARIABLE_HELP_FMT, 
+		  v->name, 
+		  owl_variable_get_validsettings(v),
+		  defaultbuf,
+		  owl_variable_get_docstring(v));
+  owl_fmtext_append_normal(fm, buf);
+}
+
+void owl_variable_get_help(owl_vardict *d, char *name, owl_fmtext *fm) {
+  char buff[1024];
+  int bufflen = 1023;
+  owl_variable *v;
+
+  if (!name
+      || (v = owl_dict_find_element(d, name)) == NULL 
+      || !v->get_fn) {
+    owl_fmtext_append_normal(fm, "No such variable...\n");
+    return;
+  }
+
+  owl_fmtext_append_bold(fm, "OWL VARIABLE\n\n");
+  owl_fmtext_append_normal(fm, OWL_TABSTR);
+  owl_fmtext_append_normal(fm, name);
+  owl_fmtext_append_normal(fm, " - ");
+  owl_fmtext_append_normal(fm, v->docstring);
+  owl_fmtext_append_normal(fm, "\n\n");
+
+  owl_fmtext_append_normal(fm, "Current:        ");
+  owl_variable_get_tostring(d, name, buff, bufflen);
+  owl_fmtext_append_normal(fm, buff);
+  owl_fmtext_append_normal(fm, "\n\n");
+
+
+  if (v->type == OWL_VARIABLE_INT || v->type == OWL_VARIABLE_BOOL) {
+    v->get_tostring_fn(v, buff, bufflen, &(v->ival_default));
+  } else {
+    v->get_tostring_fn(v, buff, bufflen, v->pval_default);
+  }
+  owl_fmtext_append_normal(fm, "Default:        ");
+  owl_fmtext_append_normal(fm, buff);
+  owl_fmtext_append_normal(fm, "\n\n");
+
+  owl_fmtext_append_normal(fm, "Valid Settings: ");
+  owl_fmtext_append_normal(fm, owl_variable_get_validsettings(v));
+  owl_fmtext_append_normal(fm, "\n\n");
+}
+
+
+
+
+/**************************************************************************/
+/*********************** GENERAL TYPE-SPECIFIC ****************************/
+/**************************************************************************/
+
+/* default common functions */
+
+void *owl_variable_get_default(owl_variable *v) {
+  return v->val;
+}
+
+void owl_variable_free_default(owl_variable *v) {
+  if (v->val) owl_free(v->val);
+}
+
+/* default functions for booleans */
+
+int owl_variable_bool_validate_default(owl_variable *v, void *newval) {
+  if (newval == NULL) return(0);
+  else if (*(int*)newval==1 || *(int*)newval==0) return(1);
+  else return (0);
+}
+
+int owl_variable_bool_set_default(owl_variable *v, void *newval) {
+  if (v->validate_fn) {
+    if (!v->validate_fn(v, newval)) return(-1);
+  }
+  *(int*)v->val = *(int*)newval;
+  return(0);
+}
+
+int owl_variable_bool_set_fromstring_default(owl_variable *v, char *newval) {
+  int i;
+  if (!strcmp(newval, "on")) i=1;
+  else if (!strcmp(newval, "off")) i=0;
+  else return(-1);
+  return (v->set_fn(v, &i));
+}
+
+int owl_variable_bool_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val) {
+  if (val == NULL) {
+    snprintf(buf, bufsize, "<null>");
+    return -1;
+  } else if (*(int*)val == 0) {
+    snprintf(buf, bufsize, "off");
+    return 0;
+  } else if (*(int*)val == 1) {
+    snprintf(buf, bufsize, "on");
+    return 0;
+  } else {
+    snprintf(buf, bufsize, "<invalid>");
+    return -1;
+  }
+}
+
+/* default functions for integers */
+
+int owl_variable_int_validate_default(owl_variable *v, void *newval) {
+  if (newval == NULL) return(0);
+  else return (1);
+}
+
+int owl_variable_int_set_default(owl_variable *v, void *newval) {
+  if (v->validate_fn) {
+    if (!v->validate_fn(v, newval)) return(-1);
+  }
+  *(int*)v->val = *(int*)newval;
+  return(0);
+}
+
+int owl_variable_int_set_fromstring_default(owl_variable *v, char *newval) {
+  int i;
+  char *ep = "x";
+  i = strtol(newval, &ep, 10);
+  if (*ep || ep==newval) return(-1);
+  return (v->set_fn(v, &i));
+}
+
+int owl_variable_int_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val) {
+  if (val == NULL) {
+    snprintf(buf, bufsize, "<null>");
+    return -1;
+  } else {
+    snprintf(buf, bufsize, "%d", *(int*)val);
+    return 0;
+  } 
+}
+
+/* default functions for enums (a variant of integers) */
+
+int owl_variable_enum_validate(owl_variable *v, void *newval) {  
+  char **enums;
+  int nenums, val;
+  if (newval == NULL) return(0);
+  enums = atokenize(v->validsettings, ",", &nenums);
+  if (enums == NULL) return(0);
+  atokenize_free(enums, nenums);
+  val = *(int*)newval;
+  if (val < 0 || val >= nenums) {
+    return(0);
+  }
+  return(1);
+}
+
+int owl_variable_enum_set_fromstring(owl_variable *v, char *newval) {
+  char **enums;
+  int nenums, i, val=-1;
+  if (newval == NULL) return(-1);
+  enums = atokenize(v->validsettings, ",", &nenums);
+  if (enums == NULL) return(-1);
+  for (i=0; i<nenums; i++) {
+    if (0==strcmp(newval, enums[i])) {
+      val = i;
+    }
+  }
+  atokenize_free(enums, nenums);
+  if (val == -1) return(-1);
+  return (v->set_fn(v, &val));
+}
+
+int owl_variable_enum_get_tostring(owl_variable *v, char* buf, int bufsize, void *val) {
+  char **enums;
+  int nenums, i;
+
+  if (val == NULL) {
+    snprintf(buf, bufsize, "<null>");
+    return -1;
+  }
+  enums = atokenize(v->validsettings, ",", &nenums);
+  i = *(int*)val;
+  if (i<0 || i>=nenums) {
+    snprintf(buf, bufsize, "<invalid:%d>",i);
+    atokenize_free(enums, nenums);
+    return(-1);
+  }
+  snprintf(buf, bufsize, "%s", enums[i]);
+  return 0;
+}
+
+/* default functions for stringeans */
+
+int owl_variable_string_validate_default(struct _owl_variable *v, void *newval) {
+  if (newval == NULL) return(0);
+  else return (1);
+}
+
+int owl_variable_string_set_default(owl_variable *v, void *newval) {
+  if (v->validate_fn) {
+    if (!v->validate_fn(v, newval)) return(-1);
+  }
+  if (v->val) owl_free(v->val);
+  v->val = owl_strdup(newval);
+  return(0);
+}
+
+int owl_variable_string_set_fromstring_default(owl_variable *v, char *newval) {
+  return (v->set_fn(v, newval));
+}
+
+int owl_variable_string_get_tostring_default(owl_variable *v, char* buf, int bufsize, void *val) {
+  if (val == NULL) {
+    snprintf(buf, bufsize, "<null>");
+    return -1;
+  } else {
+    snprintf(buf, bufsize, "%s", (char*)val);
+    return 0;
+  }
+}
+
+
+
+/**************************************************************************/
+/************************* REGRESSION TESTS *******************************/
+/**************************************************************************/
+
+#ifdef OWL_INCLUDE_REG_TESTS
+
+#define FAIL_UNLESS(desc,pred) printf("\t%-4s: %s\n", (pred)?"ok":(numfailed++,"FAIL"), desc)
+
+int owl_variable_regtest(void) {
+  owl_vardict vd;
+  int numfailed=0;
+  char buf[1024];
+
+  printf("BEGIN testing owl_variable\n");
+  FAIL_UNLESS("setup", 0==owl_variable_dict_setup(&vd));
+
+  FAIL_UNLESS("get bool", 0==owl_variable_get_bool(&vd,"personalbell"));
+  FAIL_UNLESS("get bool (no such)", -1==owl_variable_get_bool(&vd,"mumble"));
+  FAIL_UNLESS("get bool as string 1", 0==owl_variable_get_tostring(&vd,"personalbell", buf, 1024));
+  FAIL_UNLESS("get bool as string 2", 0==strcmp(buf,"off"));
+  FAIL_UNLESS("set bool 1", 0==owl_variable_set_bool_on(&vd,"personalbell"));
+  FAIL_UNLESS("get bool 2", 1==owl_variable_get_bool(&vd,"personalbell"));
+  FAIL_UNLESS("set bool 3", 0==owl_variable_set_fromstring(&vd,"personalbell","off",0));
+  FAIL_UNLESS("get bool 4", 0==owl_variable_get_bool(&vd,"personalbell"));
+  FAIL_UNLESS("set bool 5", -1==owl_variable_set_fromstring(&vd,"personalbell","xxx",0));
+  FAIL_UNLESS("get bool 6", 0==owl_variable_get_bool(&vd,"personalbell"));
+
+
+  FAIL_UNLESS("get string", 0==strcmp("~/zlog/people", owl_variable_get_string(&vd,"logpath")));
+  FAIL_UNLESS("set string 7", 0==owl_variable_set_string(&vd,"logpath","whee"));
+  FAIL_UNLESS("get string", 0==strcmp("whee", owl_variable_get_string(&vd,"logpath")));
+
+  FAIL_UNLESS("get int", 8==owl_variable_get_int(&vd,"typewinsize"));
+  FAIL_UNLESS("get int (no such)", -1==owl_variable_get_int(&vd,"mmble"));
+  FAIL_UNLESS("get int as string 1", 0==owl_variable_get_tostring(&vd,"typewinsize", buf, 1024));
+  FAIL_UNLESS("get int as string 2", 0==strcmp(buf,"8"));
+  FAIL_UNLESS("set int 1", 0==owl_variable_set_int(&vd,"typewinsize",12));
+  FAIL_UNLESS("get int 2", 12==owl_variable_get_int(&vd,"typewinsize"));
+  FAIL_UNLESS("set int 1b", -1==owl_variable_set_int(&vd,"typewinsize",-3));
+  FAIL_UNLESS("get int 2b", 12==owl_variable_get_int(&vd,"typewinsize"));
+  FAIL_UNLESS("set int 3", 0==owl_variable_set_fromstring(&vd,"typewinsize","9",0));
+  FAIL_UNLESS("get int 4", 9==owl_variable_get_int(&vd,"typewinsize"));
+  FAIL_UNLESS("set int 5", -1==owl_variable_set_fromstring(&vd,"typewinsize","xxx",0));
+  FAIL_UNLESS("set int 6", -1==owl_variable_set_fromstring(&vd,"typewinsize","",0));
+  FAIL_UNLESS("get int 7", 9==owl_variable_get_int(&vd,"typewinsize"));
+
+  FAIL_UNLESS("get enum", OWL_WEBBROWSER_NETSCAPE==owl_variable_get_int(&vd,"webbrowser"));
+  FAIL_UNLESS("get enum as string 1", 0==owl_variable_get_tostring(&vd,"webbrowser", buf, 1024));
+  FAIL_UNLESS("get enum as string 2", 0==strcmp(buf,"netscape"));
+  FAIL_UNLESS("set enum 1", 0==owl_variable_set_int(&vd,"webbrowser",OWL_WEBBROWSER_GALEON));
+  FAIL_UNLESS("get enum 2", OWL_WEBBROWSER_GALEON==owl_variable_get_int(&vd,"webbrowser"));
+  FAIL_UNLESS("set enum 1b", -1==owl_variable_set_int(&vd,"webbrowser",-3));
+  FAIL_UNLESS("set enum 1b", -1==owl_variable_set_int(&vd,"webbrowser",209));
+  FAIL_UNLESS("get enum 2b", OWL_WEBBROWSER_GALEON==owl_variable_get_int(&vd,"webbrowser"));
+  FAIL_UNLESS("set enum 3", 0==owl_variable_set_fromstring(&vd,"webbrowser","none",0));
+  FAIL_UNLESS("get enum 4", OWL_WEBBROWSER_NONE==owl_variable_get_int(&vd,"webbrowser"));
+  FAIL_UNLESS("set enum 5", 0==owl_variable_set_fromstring(&vd,"webbrowser","netscape",0));
+  FAIL_UNLESS("get enum 6", OWL_WEBBROWSER_NETSCAPE==owl_variable_get_int(&vd,"webbrowser"));
+  FAIL_UNLESS("set enum 7", -1==owl_variable_set_fromstring(&vd,"webbrowser","xxx",0));
+  FAIL_UNLESS("set enum 8", -1==owl_variable_set_fromstring(&vd,"webbrowser","",0));
+  FAIL_UNLESS("set enum 9", -1==owl_variable_set_fromstring(&vd,"webbrowser","netscapey",0));
+  FAIL_UNLESS("get enum 10", OWL_WEBBROWSER_NETSCAPE==owl_variable_get_int(&vd,"webbrowser"));
+
+
+
+  owl_variable_dict_free(&vd);
+
+  if (numfailed) printf("*** WARNING: failures encountered with owl_variable\n");
+  printf("END testing owl_variable (%d failures)\n", numfailed);
+  return(numfailed);
+}
+
+
+#endif /* OWL_INCLUDE_REG_TESTS */
Index: view.c
===================================================================
--- view.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ view.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,62 @@
+#include "owl.h"
+
+void owl_view_create(owl_view *v, owl_filter *f) {
+  v->filter=f;
+  owl_messagelist_create(&(v->ml));
+  owl_view_recalculate(v);
+}
+
+void owl_view_consider_message(owl_view *v, owl_message *m) {
+  /* if the message matches the filter then add to view */
+  if (owl_filter_message_match(v->filter, m)) {
+    owl_messagelist_append_element(&(v->ml), m);
+  }
+}
+
+void owl_view_recalculate(owl_view *v) {
+  /* add all the global messages that match the filter */
+  int i, j;
+  owl_messagelist *gml;
+  owl_messagelist *ml;
+  owl_message *m;
+
+  gml=owl_global_get_msglist(&g);
+  ml=&(v->ml);
+
+  /* nuke the old list */
+  owl_list_free_simple((owl_list *) ml);
+  owl_messagelist_create(&(v->ml));
+
+  /* find all the messages we want */
+  j=owl_messagelist_get_size(gml);
+  for (i=0; i<j; i++) {
+    m=owl_messagelist_get_element(gml, i);
+    if (owl_filter_message_match(v->filter, m)) {
+      owl_messagelist_append_element(ml, m);
+    }
+  }
+}
+
+owl_message *owl_view_get_element(owl_view *v, int index) {
+  return(owl_messagelist_get_element(&(v->ml), index));
+}
+
+void owl_view_delete_element(owl_view *v, int index) {
+  owl_messagelist_delete_element(&(v->ml), index);
+}
+
+void owl_view_undelete_element(owl_view *v, int index) {
+  owl_messagelist_undelete_element(&(v->ml), index);
+}
+
+int owl_view_get_size(owl_view *v) {
+  return(owl_messagelist_get_size(&(v->ml)));
+}
+
+char *owl_view_get_filtname(owl_view *v) {
+  return(owl_filter_get_name(v->filter));
+}
+
+void owl_view_free(owl_view *v) {
+  owl_list_free_simple((owl_list *) &(v->ml));
+}
Index: viewwin.c
===================================================================
--- viewwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ viewwin.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,120 @@
+#include <string.h>
+#include "owl.h"
+
+#define BOTTOM_OFFSET 1
+
+void owl_viewwin_init_text(owl_viewwin *v, WINDOW *win, int winlines, int wincols, char *text) {
+  /* initialize the viewwin e.
+   * 'win' is an already initialzed curses window that will be used by viewwin
+   */
+
+  owl_fmtext_init_null(&(v->fmtext));
+  if (text) {
+    owl_fmtext_append_normal(&(v->fmtext), text);
+    if (text[strlen(text)-1]!='\n') {
+      owl_fmtext_append_normal(&(v->fmtext), "\n");
+    }
+    v->textlines=owl_fmtext_num_lines(&(v->fmtext));
+  }
+  v->topline=0;
+  v->rightshift=0;
+  v->winlines=winlines;
+  v->wincols=wincols;
+  v->curswin=win;
+}
+
+void owl_viewwin_init_fmtext(owl_viewwin *v, WINDOW *win, int winlines, int wincols, owl_fmtext *fmtext) {
+  /* initialize the viewwin e.
+   * 'win' is an already initialzed curses window that will be used by viewwin
+   */
+
+  owl_fmtext_copy(&(v->fmtext), fmtext);
+  v->textlines=owl_fmtext_num_lines(&(v->fmtext));
+  v->topline=0;
+  v->rightshift=0;
+  v->winlines=winlines;
+  v->wincols=wincols;
+  v->curswin=win;
+}
+
+void owl_viewwin_set_curswin(owl_viewwin *v, WINDOW *w, int winlines, int wincols) {
+  v->curswin=w;
+  v->winlines=winlines;
+  v->wincols=wincols;
+}
+
+void owl_viewwin_redisplay(owl_viewwin *v, int update) {
+  /* regenerate text on the curses window. */
+  /* if update == 1 then do a doupdate() */
+
+  owl_fmtext fm1, fm2;
+  
+  werase(v->curswin);
+  wmove(v->curswin, 0, 0);
+
+  owl_fmtext_truncate_lines(&(v->fmtext), v->topline, v->winlines-BOTTOM_OFFSET, &fm1);
+  owl_fmtext_truncate_cols(&fm1, v->rightshift, v->wincols-1+v->rightshift, &fm2);
+
+  owl_fmtext_curs_waddstr(&fm2, v->curswin);
+
+  /* print the message at the bottom */
+  wmove(v->curswin, v->winlines-1, 0);
+  wattrset(v->curswin, A_REVERSE);
+  if (v->textlines - v->topline > v->winlines-BOTTOM_OFFSET - 1) {
+    waddstr(v->curswin, "--More-- (Space to see more, 'q' to quit)");
+  } else {
+    waddstr(v->curswin, "--End-- (Press 'q' to quit)");
+  }
+  wattroff(v->curswin, A_REVERSE);
+  wnoutrefresh(v->curswin);
+
+  if (update==1) {
+    doupdate();
+  }
+}
+
+void owl_viewwin_pagedown(owl_viewwin *v) {
+  v->topline+=v->winlines - BOTTOM_OFFSET;
+  if ( (v->topline+v->winlines-BOTTOM_OFFSET) > v->textlines) {
+    v->topline = v->textlines - v->winlines + BOTTOM_OFFSET;
+  }
+}
+
+void owl_viewwin_linedown(owl_viewwin *v) {
+  v->topline++;
+  if ( (v->topline+v->winlines-BOTTOM_OFFSET) > v->textlines) {
+    v->topline = v->textlines - v->winlines + BOTTOM_OFFSET;
+  }
+}
+
+void owl_viewwin_pageup(owl_viewwin *v) {
+  v->topline-=v->winlines;
+  if (v->topline<0) v->topline=0;
+}
+
+void owl_viewwin_lineup(owl_viewwin *v) {
+  v->topline--;
+  if (v->topline<0) v->topline=0;
+}
+
+void owl_viewwin_right(owl_viewwin *v, int n) {
+  v->rightshift+=n;
+}
+
+void owl_viewwin_left(owl_viewwin *v, int n) {
+  v->rightshift-=n;
+  if (v->rightshift<0) v->rightshift=0;
+}
+
+void owl_viewwin_top(owl_viewwin *v) {
+  v->topline=0;
+  v->rightshift=0;
+}
+
+void owl_viewwin_bottom(owl_viewwin *v) {
+  v->topline = v->textlines - v->winlines + BOTTOM_OFFSET;
+}
+
+void owl_viewwin_free(owl_viewwin *v) {
+  owl_fmtext_free(&(v->fmtext));
+}
Index: zephyr.c
===================================================================
--- zephyr.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ zephyr.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,454 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <zephyr/zephyr.h>
+#include <com_err.h>
+#include <pwd.h>
+#include "owl.h"
+
+int owl_zephyr_loadsubs(char *filename) {
+  /* return 0  on success
+   *        -1 on file error
+   *        -2 on subscription error
+   */
+  FILE *file;
+  char *tmp, *start;
+  char buffer[1024], subsfile[1024];
+  ZSubscription_t subs[3001];
+  int count, ret, i;
+
+  ret=0;
+  
+  if (filename==NULL) {
+    sprintf(subsfile, "%s/%s", owl_global_get_homedir(&g), ".zephyr.subs");
+  } else {
+    strcpy(subsfile, filename);
+  }
+  
+  /* need to redo this to do chunks, not just bail after 3000 */
+  count=0;
+  file=fopen(subsfile, "r");
+  if (file) {
+    while ( fgets(buffer, 1024, file)!=NULL ) {
+      if (buffer[0]=='#' || buffer[0]=='\n' || buffer[0]=='\n') continue;
+
+      if (buffer[0]=='-') {
+	start=buffer+1;
+      } else {
+	start=buffer;
+      }
+      
+      if (count >= 3000) break; /* also tell the user */
+
+      /* add it to the list of subs */
+      if ((tmp=(char *) strtok(start, ",\n\r"))==NULL) continue;
+      subs[count].zsub_class=owl_strdup(tmp);
+      if ((tmp=(char *) strtok(NULL, ",\n\r"))==NULL) continue;
+      subs[count].zsub_classinst=owl_strdup(tmp);
+      if ((tmp=(char *) strtok(NULL, " \t\n\r"))==NULL) continue;
+      subs[count].zsub_recipient=owl_strdup(tmp);
+
+      /* if it started with '-' then add it to the global punt list */
+      if (buffer[0]=='-') {
+	owl_function_zpunt(subs[count].zsub_class, subs[count].zsub_classinst, subs[count].zsub_recipient, 0);
+      }
+      
+      count++;
+    }
+    fclose(file);
+  } else {
+    count=0;
+    ret=-1;
+  }
+
+  /* sub with defaults */
+  if (ZSubscribeTo(subs,count,0) != ZERR_NONE) {
+    fprintf(stderr, "Error subbing\n");
+    ret=-2;
+  }
+
+  /* free stuff */
+  for (i=0; i<count; i++) {
+    owl_free(subs[i].zsub_class);
+    owl_free(subs[i].zsub_classinst);
+    owl_free(subs[i].zsub_recipient);
+  }
+
+  return(ret);
+}
+
+int loadloginsubs(char *filename) {
+  FILE *file;
+  ZSubscription_t subs[3001];
+  char subsfile[1024], buffer[1024];
+  int count, ret, i;
+
+  ret=0;
+
+  if (filename==NULL) {
+    sprintf(subsfile, "%s/%s", owl_global_get_homedir(&g), ".anyone");
+  } else {
+    strcpy(subsfile, filename);
+  }
+
+  /* need to redo this to do chunks, not just bag out after 3000 */
+  count=0;
+  file=fopen(subsfile, "r");
+  if (file) {
+    while ( fgets(buffer, 1024, file)!=NULL ) {
+      if (buffer[0]=='#' || buffer[0]=='\n' || buffer[0]=='\n') continue;
+      
+      if (count >= 3000) break; /* also tell the user */
+
+      buffer[strlen(buffer)-1]='\0';
+      subs[count].zsub_class="login";
+      subs[count].zsub_recipient="*";
+      if (strchr(buffer, '@')) {
+	subs[count].zsub_classinst=owl_strdup(buffer);
+      } else {
+	subs[count].zsub_classinst=owl_malloc(1024);
+	sprintf(subs[count].zsub_classinst, "%s@%s", buffer, ZGetRealm());
+      }
+
+      count++;
+    }
+    fclose(file);
+  } else {
+    count=0;
+    ret=-1;
+  }
+
+  /* sub with defaults */
+  if (ZSubscribeTo(subs,count,0) != ZERR_NONE) {
+    fprintf(stderr, "Error subbing\n");
+    ret=-2;
+  }
+
+  /* free stuff */
+  for (i=0; i<count; i++) {
+    owl_free(subs[i].zsub_classinst);
+  }
+
+  return(ret);
+}
+
+void unsuball() {
+  int ret;
+  
+  ret=ZCancelSubscriptions(0);
+  if (ret != ZERR_NONE) {
+    com_err("owl",ret,"while unsubscribing");
+  }
+}
+
+int owl_zephyr_sub(char *class, char *inst, char *recip) {
+  ZSubscription_t subs[5];
+  int ret;
+
+  subs[0].zsub_class=class;
+  subs[0].zsub_classinst=inst;
+  subs[0].zsub_recipient=recip;
+
+  if (ZSubscribeTo(subs,1,0) != ZERR_NONE) {
+    fprintf(stderr, "Error subbing\n");
+    ret=-2;
+  }
+  return(0);
+}
+
+
+int owl_zephyr_unsub(char *class, char *inst, char *recip) {
+  ZSubscription_t subs[5];
+  int ret;
+
+  subs[0].zsub_class=class;
+  subs[0].zsub_classinst=inst;
+  subs[0].zsub_recipient=recip;
+
+  if (ZUnsubscribeTo(subs,1,0) != ZERR_NONE) {
+    fprintf(stderr, "Error unsubbing\n");
+    ret=-2;
+  }
+  return(0);
+}
+
+
+void zlog_in() {
+  int ret;
+
+  ret=ZSetLocation(EXPOSE_NETVIS);
+  /* do something on failure */
+}
+
+void zlog_out() {
+  int ret;
+
+  ret=ZUnsetLocation();
+  /* do something on failure */
+}
+
+
+char *owl_zephyr_get_field(ZNotice_t *n, int j, int *k) {
+  /* return a pointer to the Jth field, place the length in k.  If the
+     field doesn't exist return an emtpy string */
+  int i, count, save;
+
+  count=save=0;
+  for (i=0; i<n->z_message_len; i++) {
+    if (n->z_message[i]=='\0') {
+      count++;
+      if (count==j) {
+	/* just found the end of the field we're looking for */
+	*k=i-save;
+	return(n->z_message+save);
+      } else {
+	save=i+1;
+      }
+    }
+  }
+  /* catch the last field */
+  if (count==j-1) {
+    *k=n->z_message_len-save;
+    return(n->z_message+save);
+  }
+  
+  *k=0;
+  return("");
+}
+
+
+int owl_zephyr_get_num_fields(ZNotice_t *n) {
+  int i, fields;
+
+  fields=1;
+  for (i=0; i<n->z_message_len; i++) {
+    if (n->z_message[i]=='\0') fields++;
+  }
+  
+  return(fields);
+}
+
+
+char *owl_zephyr_get_message(ZNotice_t *n, int *k) {
+  /* return a pointer to the message, place the message length in k */
+  if (!strcasecmp(n->z_opcode, "ping")) {
+    *k=0;
+    return("");
+  }
+
+  return(owl_zephyr_get_field(n, 2, k));
+}
+
+
+char *owl_zephyr_get_zsig(ZNotice_t *n, int *k) {
+  /* return a pointer to the zsig if there is one */
+
+  if (n->z_message_len==0) {
+    *k=0;
+    return("");
+  }
+  *k=strlen(n->z_message);
+  return(n->z_message);
+}
+
+
+int send_zephyr(char *opcode, char *zsig, char *class, char *instance, char *recipient, char *message) {
+  int ret;
+  ZNotice_t notice;
+  char *ptr;
+  struct passwd *pw;
+  char zsigtmp[LINE];
+  char *zsigexec, *zsigowlvar, *zsigzvar;
+  
+  zsigexec = owl_global_get_zsig_exec(&g);
+  zsigowlvar = owl_global_get_zsig(&g);
+  zsigzvar = ZGetVariable("zwrite-signature");
+
+  if (zsig) {
+    strcpy(zsigtmp, zsig);
+  } else if (zsigowlvar && *zsigowlvar) {
+    strncpy(zsigtmp, zsigowlvar, LINE);
+  } else if (zsigexec && *zsigexec) {
+    FILE *file;
+    char buff[LINE];
+    strcpy(zsigtmp, "");
+    file=popen(zsigexec, "r");
+    if (!file) {
+      if (zsigzvar && *zsigzvar) {
+	strncpy(zsigtmp, zsigzvar, LINE);
+      }
+    } else {
+      while (fgets(buff, LINE, file)) { /* wrong sizing */
+	strcat(zsigtmp, buff);
+      }
+      pclose(file);
+      if (zsigtmp[strlen(zsigtmp)-1]=='\n') {
+	zsigtmp[strlen(zsigtmp)-1]='\0';
+      }
+    }
+  } else if (zsigzvar) {
+    strncpy(zsigtmp, zsigzvar, LINE);
+  } else if (((pw=getpwuid(getuid()))!=NULL) && (pw->pw_gecos)) {
+    strncpy(zsigtmp, pw->pw_gecos, LINE);
+    ptr=strchr(zsigtmp, ',');
+    if (ptr) {
+      ptr[0]='\0';
+    }
+  } else {
+    strcpy(zsigtmp, "");
+  }
+    
+  memset(&notice, 0, sizeof(notice));
+
+  notice.z_kind=ACKED;
+  notice.z_port=0;
+  notice.z_class=class;
+  notice.z_class_inst=instance;
+  if (!strcmp(recipient, "*") || !strcmp(recipient, "@")) {
+    notice.z_recipient="";
+  } else {
+    notice.z_recipient=recipient;
+  }
+  notice.z_default_format="Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold{$1 <$sender>}\n\n$2";
+  notice.z_sender=NULL;
+  if (opcode) notice.z_opcode=opcode;
+
+  notice.z_message_len=strlen(zsigtmp)+1+strlen(message);
+  notice.z_message=owl_malloc(notice.z_message_len+10);
+  strcpy(notice.z_message, zsigtmp);
+  memcpy(notice.z_message+strlen(zsigtmp)+1, message, strlen(message));
+
+  /* ret=ZSendNotice(&notice, ZAUTH); */
+  ret=ZSrvSendNotice(&notice, ZAUTH, send_zephyr_helper);
+  
+  /* free then check the return */
+  owl_free(notice.z_message);
+  ZFreeNotice(&notice);
+  if (ret!=ZERR_NONE) {
+    owl_function_makemsg("Error sending zephyr");
+    return(ret);
+  }
+
+  return(0);
+}
+
+Code_t send_zephyr_helper(ZNotice_t *notice, char *buf, int len, int wait) {
+  return(ZSendPacket(buf, len, 0));
+}
+
+void send_ping(char *to) {
+  send_zephyr("PING", "", "MESSAGE", "PERSONAL", to, "");
+}
+
+void owl_zephyr_handle_ack(ZNotice_t *retnotice) {
+  char buff[LINE];
+  char *tmp;
+  
+  /* if it's an HMACK ignore it */
+  if (retnotice->z_kind == HMACK) return;
+  
+  if (retnotice->z_kind == SERVNAK) {
+    owl_function_makemsg("Authorization failure sending zephyr");
+  } else if ((retnotice->z_kind != SERVACK) || !retnotice->z_message_len) {
+    owl_function_makemsg("Detected server failure while receiving acknowledgement");
+  } else if (!strcmp(retnotice->z_message, ZSRVACK_SENT)) {
+    if (!strcasecmp(retnotice->z_opcode, "ping")) {
+      return;
+    } else if (!strcasecmp(retnotice->z_class, "message") &&
+	       !strcasecmp(retnotice->z_class_inst, "personal")) {
+      tmp=pretty_sender(retnotice->z_recipient);
+      sprintf(buff, "Message sent to %s.", tmp);
+      free(tmp);
+    } else {
+      sprintf(buff, "Message sent to -c %s -i %s\n", retnotice->z_class, retnotice->z_class_inst);
+    }
+    owl_function_makemsg(buff);
+  } else if (!strcmp(retnotice->z_message, ZSRVACK_NOTSENT)) {
+    if (strcasecmp(retnotice->z_class, "message")) {
+      sprintf(buff, "Not logged in or not subscribing to class %s, instance %s",
+	      retnotice->z_class, retnotice->z_class_inst);
+      owl_function_makemsg(buff);
+    } else {
+      owl_function_makemsg("Not logged in or subscribing to messages.");
+    }
+  } else {
+    char buff[1024];
+    sprintf(buff, "Internal error on ack (%s)", retnotice->z_message);
+    owl_function_makemsg(buff);
+  }
+}
+
+int owl_zephyr_notice_is_ack(ZNotice_t *n) {
+  if (n->z_kind == SERVNAK || n->z_kind == SERVACK || n->z_kind == HMACK) {
+    if (!strcasecmp(n->z_class, LOGIN_CLASS)) return(0);
+    return(1);
+  }
+  return(0);
+}
+  
+void owl_zephyr_zaway(owl_message *m) {
+  char *tmpbuff;
+  
+  /* bail if it doesn't look like a message we should reply to */
+  if (strcasecmp(owl_message_get_class(m), "message")) return;
+  if (strcasecmp(owl_message_get_recipient(m), ZGetSender())) return;
+  if (!strcasecmp(owl_message_get_sender(m), "")) return;
+  if (!strcasecmp(owl_message_get_opcode(m), "ping")) return;
+  if (!strcasecmp(owl_message_get_opcode(m), "auto")) return;
+  if (!strcasecmp(owl_message_get_notice(m)->z_message, "Automated reply:")) return;
+  if (!strcasecmp(owl_message_get_sender(m), ZGetSender())) return;
+  /* add one to not send to ourselves */
+
+  send_zephyr("",
+	      "Automated reply:",
+	      owl_message_get_class(m),
+	      owl_message_get_instance(m),
+	      owl_message_get_sender(m),
+	      owl_global_get_zaway_msg(&g));
+
+  /* display the message as an admin message in the receive window */
+  tmpbuff=owl_malloc(strlen(owl_global_get_zaway_msg(&g))+LINE);
+  sprintf(tmpbuff, "Message sent to %s", owl_message_get_sender(m));
+  owl_function_adminmsg(tmpbuff, owl_global_get_zaway_msg(&g));
+  owl_free(tmpbuff);
+}
+
+
+void owl_zephyr_hackaway_cr(ZNotice_t *n) {
+  /* replace \r's with ' '.  Gross-ish */
+  int i;
+
+  for (i=0; i<n->z_message_len; i++) {
+    if (n->z_message[i]=='\r') {
+      n->z_message[i]=' ';
+    }
+  }
+}
+
+void owl_zephyr_zlocate(char *user, char *out, int auth) {
+  int ret, numlocs;
+  int one = 1;
+  ZLocations_t locations;
+  char *myuser;
+  
+  strcpy(out, "");
+
+  ret=ZLocateUser(user,&numlocs,auth?ZAUTH:ZNOAUTH);
+  if (ret != ZERR_NONE) {
+    owl_function_makemsg("Error locating user");
+  }
+
+  if (numlocs==0) {
+    strcpy(out, "Hidden or not logged-in\n");
+    return;
+  }
+    
+  for (;numlocs;numlocs--) {
+    ZGetLocations(&locations,&one);
+    myuser=pretty_sender(user);
+    sprintf(out, "%s%s: %s\t%s\t%s\n", out, myuser, locations.host, locations.tty, locations.time);
+    owl_free(myuser);
+  }
+}
Index: zwrite.c
===================================================================
--- zwrite.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
+++ zwrite.c (revision 7d4fbcdd0df83802711454fdafce2c9d592bf431)
@@ -0,0 +1,210 @@
+#include <string.h>
+#include "owl.h"
+
+int owl_zwrite_create_from_line(owl_zwrite *z, char *line) {
+  int argc, badargs, myargc;
+  char **argv, **myargv;
+
+  badargs=0;
+  
+  /* set the defaults */
+  strcpy(z->realm, "");
+  strcpy(z->class, "message");
+  strcpy(z->inst, "personal");
+  strcpy(z->opcode, "");
+  z->cc=0;
+  z->noping=0;
+  owl_list_create(&(z->recips));
+
+  /* parse the command line for options */
+  argv=myargv=owl_parseline(line, &argc);
+  if (argc<0) {
+    owl_function_makemsg("Unbalanced quotes");
+    return(-1);
+  }
+  myargc=argc;
+  myargc--;
+  myargv++;
+  while (myargc) {
+    if (!strcmp(myargv[0], "-c")) {
+      if (myargc<2) {
+	badargs=1;
+	break;
+      }
+      strcpy(z->class, myargv[1]);
+      myargv+=2;
+      myargc-=2;
+    } else if (!strcmp(myargv[0], "-i")) {
+      if (myargc<2) {
+	badargs=1;
+	break;
+      }
+      strcpy(z->inst, myargv[1]);
+      myargv+=2;
+      myargc-=2;
+    } else if (!strcmp(myargv[0], "-r")) {
+      if (myargc<2) {
+	badargs=1;
+	break;
+      }
+      strcpy(z->realm, myargv[1]);
+      myargv+=2;
+      myargc-=2;
+    } else if (!strcmp(myargv[0], "-O")) {
+      if (myargc<2) {
+	badargs=1;
+	break;
+      }
+      strcpy(z->opcode, myargv[1]);
+      myargv+=2;
+      myargc-=2;
+    } else if (!strcmp(myargv[0], "-C")) {
+      z->cc=1;
+      myargv++;
+      myargc--;
+    } else if (!strcmp(myargv[0], "-n")) {
+      z->noping=1;
+      myargv++;
+      myargc--;
+    } else {
+      /* anything unattached is a recipient */
+      owl_list_append_element(&(z->recips), strdup(myargv[0]));
+      myargv++;
+      myargc--;
+    }
+  }
+
+  owl_parsefree(argv, argc);
+
+  if (badargs) {
+    return(-1);
+  }
+
+  if (!strcasecmp(z->class, "message") &&
+      !strcasecmp(z->inst, "personal") &&
+      owl_list_get_size(&(z->recips))==0) {
+    /* do the makemsg somewhere else */
+    owl_function_makemsg("You must specify a recipient");
+    return(-1);
+  }
+
+  return(0);
+}
+
+void owl_zwrite_send_ping(owl_zwrite *z) {
+  int i, j;
+  char to[LINE];
+
+  if (z->noping) return;
+  
+  if (strcasecmp(z->class, "message") ||
+      strcasecmp(z->inst, "personal")) {
+    return;
+  }
+
+  /* if there are no recipients we won't send a ping, which
+     is what we want */
+  j=owl_list_get_size(&(z->recips));
+  for (i=0; i<j; i++) {
+    if (strcmp(z->realm, "")) {
+      sprintf(to, "%s@%s", (char *) owl_list_get_element(&(z->recips), i), z->realm);
+    } else {
+      strcpy(to, owl_list_get_element(&(z->recips), i));
+    }
+    send_ping(to);
+  }
+
+}
+
+void owl_zwrite_send_message(owl_zwrite *z, char *msg) {
+  int i, j;
+  char to[LINE];
+
+  j=owl_list_get_size(&(z->recips));
+  if (j>0) {
+    char *tmpmsg=NULL;
+    char toline[LINE];
+
+    if (z->cc) {
+      strcpy(toline, "CC: ");
+      for (i=0; i<j; i++) {
+	if (strcmp(z->realm, "")) {
+	  sprintf(toline, "%s%s@%s ", toline, (char *) owl_list_get_element(&(z->recips), i), z->realm);
+	} else {
+	  sprintf(toline, "%s%s ", toline, (char *) owl_list_get_element(&(z->recips), i));
+	}
+      }
+      tmpmsg=owl_malloc(strlen(msg)+strlen(toline)+30);
+      sprintf(tmpmsg, "%s\n%s", toline, msg);
+    }
+
+    for (i=0; i<j; i++) {
+      if (strcmp(z->realm, "")) {
+	sprintf(to, "%s@%s", (char *) owl_list_get_element(&(z->recips), i), z->realm);
+      } else {
+	strcpy(to, owl_list_get_element(&(z->recips), i));
+      }
+      if (z->cc) {
+	send_zephyr(z->opcode, NULL, z->class, z->inst, to, tmpmsg);
+      } else {
+	send_zephyr(z->opcode, NULL, z->class, z->inst, to, msg);
+      }
+    }
+    if (z->cc) {
+      owl_free(tmpmsg);
+    }
+  } else {
+    sprintf(to, "@%s", z->realm);
+    send_zephyr(z->opcode, NULL, z->class, z->inst, to, msg);
+  }
+}
+
+char *owl_zwrite_get_class(owl_zwrite *z) {
+  return(z->class);
+}
+
+char *owl_zwrite_get_instance(owl_zwrite *z) {
+  return(z->inst);
+}
+
+char *owl_zwrite_get_realm(owl_zwrite *z) {
+  return(z->realm);
+}
+
+void owl_zwrite_get_recipstr(owl_zwrite *z, char *buff) {
+  int i, j;
+
+  strcpy(buff, "");
+  j=owl_list_get_size(&(z->recips));
+  for (i=0; i<j; i++) {
+    strcat(buff, owl_list_get_element(&(z->recips), i));
+    strcat(buff, " ");
+  }
+  buff[strlen(buff)-1]='\0';
+}
+
+int owl_zwrite_get_numrecips(owl_zwrite *z) {
+  return(owl_list_get_size(&(z->recips)));
+}
+
+char *owl_zwrite_get_recip_n(owl_zwrite *z, int n) {
+  return(owl_list_get_element(&(z->recips), n));
+}
+
+int owl_zwrite_is_personal(owl_zwrite *z) {
+  /* return true if at least one of the recipients is personal */
+  int i, j;
+  char *foo;
+
+  j=owl_list_get_size(&(z->recips));
+  for (i=0; i<j; i++) {
+    foo=owl_list_get_element(&(z->recips), i);
+    if (foo[0]!='@') return(1);
+  }
+  return(0);
+}
+  
+
+void owl_zwrite_free(owl_zwrite *z) {
+  owl_list_free_all(&(z->recips), &owl_free);
+}
