Index: Makefile.am
===================================================================
--- Makefile.am	(revision c2662816286b30464feee0b8ba6ad4566b650c16)
+++ Makefile.am	(revision 353719a2ddec4d6a8458ee42af37514fedbd80bf)
@@ -39,5 +39,6 @@
 
 CODELIST_SRCS=message.c mainwin.c popwin.c zephyr.c messagelist.c \
-     commands.c global.c text.c fmtext.c editwin.c util.c logging.c \
+     commands.c global.c text.c fmtext.c editwin.c \
+     util.c logging.c \
      perlconfig.c keys.c functions.c zwrite.c viewwin.c help.c filter.c \
      regex.c history.c view.c dict.c variable.c filterelement.c pair.c \
@@ -45,5 +46,5 @@
      aim.c buddy.c buddylist.c style.c errqueue.c \
      zbuddylist.c popexec.c select.c wcwidth.c \
-     mainpanel.c msgwin.c sepbar.c editcontext.c signal.c
+     mainpanel.c msgwin.c sepbar.c editcontext.c signal.c closures.c
 
 NORMAL_SRCS = filterproc.c window.c windowcb.c
@@ -51,6 +52,6 @@
 BASE_SRCS = $(CODELIST_SRCS) $(NORMAL_SRCS)
 
-GEN_C = varstubs.c perlglue.c
-GEN_H = owl_prototypes.h
+GEN_C = varstubs.c perlglue.c gmarshal_funcs.c
+GEN_H = owl_prototypes.h gmarshal_funcs.h
 
 BUILT_SOURCES = $(GEN_C) $(GEN_H)
@@ -74,4 +75,9 @@
 	$(AM_V_GEN)perl $< $(sort $(filter-out $<,$+)) > $@
 
+gmarshal_funcs.h: marshal_types
+	glib-genmarshal --header $< > $@
+gmarshal_funcs.c: marshal_types
+	glib-genmarshal --body $< > $@
+
 # For emacs flymake-mode
 check-syntax: proto
Index: closures.c
===================================================================
--- closures.c	(revision 06e04a995f32e4c88df08bff7baf37fb5bc8a59c)
+++ closures.c	(revision 06e04a995f32e4c88df08bff7baf37fb5bc8a59c)
@@ -0,0 +1,28 @@
+#define OWL_PERL
+#include "owl.h"
+#include <dlfcn.h>
+#include <gperl.h>
+#include <gperl_marshal.h>
+
+gboolean (*gvalue_from_sv) (GValue * value, SV * sv) = NULL;
+SV * (*sv_from_gvalue) (const GValue * value) = NULL;
+GClosure * (*perl_closure_new) (SV * callback, SV * data, gboolean swap) = NULL;
+
+/* Caller does not own return value */
+const char *owl_closure_init(void)
+{
+  void *handle = NULL;
+  const char *res = "Unable to dlopen self - huh?";
+
+  handle = dlopen(NULL, RTLD_LAZY | RTLD_NOLOAD);
+  if(handle) {
+    gvalue_from_sv = dlsym(handle, "gperl_value_from_sv");
+    sv_from_gvalue = dlsym(handle, "gperl_sv_from_value");
+    perl_closure_new = dlsym(handle, "gperl_closure_new");
+    /* ... */
+    res = dlerror();
+    dlclose(handle);
+  }
+  return res;
+}
+
Index: commands.c
===================================================================
--- commands.c	(revision 8258ea514c05b11da3f68d25d8414e222496fa2d)
+++ commands.c	(revision 3b9ca713910ca1c21db2aa8293da541e66eaa97c)
@@ -1604,5 +1604,5 @@
   if (v == NULL) {
     if (!silent) owl_function_error("Unknown variable '%s'", var);
-  } else if (requirebool && owl_variable_get_type(v) != OWL_VARIABLE_BOOL) {
+  } else if (requirebool && !v->takes_on_off) {
     if (!silent) owl_function_error("Variable '%s' is not a boolean", var);
   } else {
@@ -1633,5 +1633,5 @@
   if (v == NULL) {
     if (!silent) owl_function_error("Unknown variable '%s'", var);
-  } else if (owl_variable_get_type(v) != OWL_VARIABLE_BOOL) {
+  } else if (!v->takes_on_off) {
     if (!silent) owl_function_error("Variable '%s' is not a boolean", var);
   } else {
Index: configure.ac
===================================================================
--- configure.ac	(revision 58b6ce5427395fc8c1887a2a8c69ea459a1fc9e5)
+++ configure.ac	(revision 06e04a995f32e4c88df08bff7baf37fb5bc8a59c)
@@ -127,4 +127,6 @@
 AX_PROG_PERL_MODULES([PAR],,
                      [AC_MSG_WARN([PAR.pm not found. Loadable modules will be disabled.])])
+AX_PROG_PERL_MODULES([ExtUtils::Depends],,
+                     [AC_MSG_ERROR([cannot find perl module ExtUtils::Depends])])
 
 dnl Add CFLAGS and LIBS for glib-2.0
@@ -139,4 +141,9 @@
    prefix="${ac_default_prefix}"
 fi
+
+dnl Add CFLAGS for glib-perl
+GLIB_PERL_CFLAGS=`perl -MExtUtils::Depends -e 'my $e = ExtUtils::Depends->new("BarnOwl","Glib"); my %h = $e->get_makefile_vars; print $h{"INC"}'`
+AC_MSG_NOTICE([Adding glib-perl CFLAGS ${GLIB_PERL_CFLAGS}])
+AM_CFLAGS="${GLIB_PERL_CFLAGS} ${AM_CFLAGS}"
 
 dnl Checks for typedefs, structures, and compiler characteristics.
Index: marshal_types
===================================================================
--- marshal_types	(revision 353719a2ddec4d6a8458ee42af37514fedbd80bf)
+++ marshal_types	(revision 353719a2ddec4d6a8458ee42af37514fedbd80bf)
@@ -0,0 +1,4 @@
+# set_fromstring_fn
+INT:STRING
+# get_tostring_fn
+STRING:VOID
Index: owl.h
===================================================================
--- owl.h	(revision c42a8d1091e249bf9e1e7a3a93c7e22c9f469414)
+++ owl.h	(revision bbc31e47b279ccc502d0452ffed590280f0527d6)
@@ -235,34 +235,36 @@
   char *name;
   int   type;  /* OWL_VARIABLE_* */
-  void *pval_default;  /* for types other and string */
-  int   ival_default;  /* for types int and bool     */
-  const char *validsettings;	/* documentation of valid settings */
+  char *default_str;            /* the default value as a string */
+  char *validsettings;	        /* documentation of valid settings */
   char *summary;		/* summary of usage */
   char *description;		/* detailed description */
-  void *val;                    /* current value */
-  int  (*validate_fn)(const struct _owl_variable *v, const void *newval);
+  bool takes_on_off;            /* allow passing on/off in argument-less set/unset */
+  GClosure *set_fromstring_fn;
+                                /* sets the variable to a value
+                                 * of the appropriate type.
+                                 * unless documented, this
+                                 * should make a copy.
+                                 * returns 0 on success. */
+  GClosure *get_tostring_fn;
+                                /* converts val to a string;
+                                 * caller must free the result */
+
+  /* These are only valid for OWL_VARIABLE_{INT,BOOL,STRING} */
+  GValue val;                   /* current value, if default get_fn/set_fn */
+
+  GCallback get_fn;
+                                /* returns a reference to the current value.
+                                 * WARNING:  this approach is hard to make
+                                 * thread-safe... */
+  GCallback validate_fn;
                                 /* returns 1 if newval is valid */
-  int  (*set_fn)(struct _owl_variable *v, const void *newval); 
+  GCallback set_fn;
                                 /* 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, const char *newval);
-                                /* sets the variable to a value
-				 * of the appropriate type.
-				 * unless documented, this 
-				 * should make a copy. 
-				 * returns 0 on success. */
-  const void *(*get_fn)(const struct _owl_variable *v);
-				/* returns a reference to the current value.
-				 * WARNING:  this approach is hard to make
-				 * thread-safe... */
-  CALLER_OWN char *(*get_tostring_fn)(const struct _owl_variable *v, const void *val);
-                                /* converts val to a string;
-				 * caller must free the result */
-  void (*delete_fn)(struct _owl_variable *v);
-				/* frees val as needed */
+                                 * of the appropriate type.
+                                 * unless documented, this
+                                 * should make a copy.
+                                 * returns 0 on success. */
 } owl_variable;
+
 
 typedef struct _owl_input {
@@ -614,3 +616,9 @@
 #endif
 
+/* We have to dynamically bind these ourselves */
+extern gboolean (*gvalue_from_sv) (GValue * value, SV * sv);
+extern SV * (*sv_from_gvalue) (const GValue * value);
+extern GClosure * (*perl_closure_new) (SV * callback, SV * data, gboolean swap);
+
+
 #endif /* INC_BARNOWL_OWL_H */
Index: perl/lib/BarnOwl.pm
===================================================================
--- perl/lib/BarnOwl.pm	(revision 374089acfb4f43fdb00e90ec924887d88fd84f5d)
+++ perl/lib/BarnOwl.pm	(revision 104a4eb952a68c48da10585deab68e021a648ca6)
@@ -18,4 +18,5 @@
                     new_command
                     new_variable_int new_variable_bool new_variable_string
+                    new_variable_enum
                     quote redisplay);
 our %EXPORT_TAGS = (all => \@EXPORT_OK);
@@ -402,8 +403,12 @@
 =head2 new_variable_string NAME [{ARGS}]
 
-Add a new owl variable, either an int, a bool, or a string, with the
+=head2 new_variable_enum NAME [{ARGS}]
+
+Add a new owl variable, either an int, a bool, a string, or an enum with the
 specified name.
 
-ARGS can optionally contain the following keys:
+For new_variable_enum, ARGS is required to contain a validsettings key pointing
+to an array reference. For all four, it can optionally contain the following
+keys:
 
 =over 4
@@ -423,32 +428,126 @@
 =back
 
+In addition, new_variable_string optionally accepts a string validsettings
+parameter, in case people want to set it to "<path>".
+
 =cut
 
 sub new_variable_int {
-    unshift @_, \&BarnOwl::Internal::new_variable_int, 0;
-    goto \&_new_variable;
+    my ($name, $args) = @_;
+    my $storage = defined($args->{default}) ? $args->{default} : 0;
+    BarnOwl::new_variable_full($name, {
+            %{$args},
+            get_tostring => sub { "$storage" },
+            set_fromstring => sub {
+                die "Expected integer" unless $_[0] =~ /^-?[0-9]+$/;
+                $storage = 0 + $_[0];
+            },
+            validsettings => "<int>",
+            takes_on_off => 0,
+        });
 }
 
 sub new_variable_bool {
-    unshift @_, \&BarnOwl::Internal::new_variable_bool, 0;
-    goto \&_new_variable;
+    my ($name, $args) = @_;
+    my $storage = defined($args->{default}) ? $args->{default} : 0;
+    BarnOwl::new_variable_full($name, {
+            %{$args},
+            get_tostring => sub { $storage ? "on" : "off" },
+            set_fromstring => sub {
+                die "Valid settings are on/off" unless $_[0] eq "on" || $_[0] eq "off";
+                $storage = $_[0] eq "on";
+            },
+            validsettings => "on,off",
+            takes_on_off => 1,
+        });
 }
 
 sub new_variable_string {
-    unshift @_, \&BarnOwl::Internal::new_variable_string, "";
-    goto \&_new_variable;
-}
-
-sub _new_variable {
-    my $func = shift;
-    my $default_default = shift;
+    my ($name, $args) = @_;
+    my $storage = defined($args->{default}) ? $args->{default} : "";
+    BarnOwl::new_variable_full($name, {
+            # Allow people to override this one if they /reaaally/ want to for
+            # some reason. Though we still reserve the right to interpret this
+            # value in interesting ways for tab-completion purposes.
+            validsettings => "<string>",
+            %{$args},
+            get_tostring => sub { $storage },
+            set_fromstring => sub { $storage = $_[0]; },
+            takes_on_off => 0,
+        });
+}
+
+sub new_variable_enum {
+    my ($name, $args) = @_;
+
+    # Gather the valid settings.
+    die "validsettings is required" unless defined($args->{validsettings});
+    my %valid;
+    map { $valid{$_} = 1 } @{$args->{validsettings}};
+
+    my $storage = (defined($args->{default}) ?
+                   $args->{default} :
+                   $args->{validsettings}->[0]);
+    BarnOwl::new_variable_full($name, {
+            %{$args},
+            get_tostring => sub { $storage },
+            set_fromstring => sub {
+                die "Invalid input" unless $valid{$_[0]};
+                $storage = $_[0];
+            },
+            validsettings => join(",", @{$args->{validsettings}})
+        });
+}
+
+=head2 new_variable_full NAME {ARGS}
+
+Create a variable, in full generality. The keyword arguments have types below:
+
+ get_tostring : ()  -> string
+ set_fromstring : string -> int
+ -- optional --
+ summary : string
+ description : string
+ validsettings : string
+ takes_on_off : int
+
+The get/set functions are required. Note that the caller manages storage for the
+variable. get_tostring/set_fromstring both convert AND store the value.
+set_fromstring dies on failure.
+
+If the variable takes parameters 'on' and 'off' (i.e. is boolean-looking), set
+takes_on_off to 1. This makes :set VAR and :unset VAR work. set_fromstring will
+be called with those arguments.
+
+=cut
+
+sub new_variable_full {
     my $name = shift;
     my $args = shift || {};
     my %args = (
-        summary     => "",
+        summary => "",
         description => "",
-        default     => $default_default,
+        takes_on_off => 0,
+        validsettings => "<string>",
         %{$args});
-    $func->($name, $args{default}, $args{summary}, $args{description});
+
+    die "get_tostring required" unless $args{get_tostring};
+    die "set_fromstring required" unless $args{set_fromstring};
+
+    # Strip off the bogus dummy argument. Aargh perl-Glib.
+    my $get_tostring_fn = sub { $args{get_tostring}->() };
+    my $set_fromstring_fn = sub {
+      my ($dummy, $val) = @_;
+      # Translate from user-supplied die-on-failure callback to expected
+      # non-zero on error. Less of a nuisance than interacting with ERRSV.
+      eval { $args{set_fromstring}->($val) };
+      # TODO: Consider changing B::I::new_variable to expect string|NULL with
+      # string as the error message. That can then be translated to a GError in
+      # owl_variable_set_fromstring. For now the string is ignored.
+      return ($@ ? -1 : 0);
+    };
+
+    BarnOwl::Internal::new_variable($name, $args{summary}, $args{description}, $args{validsettings},
+                                    $args{takes_on_off}, $get_tostring_fn, $set_fromstring_fn, undef);
 }
 
Index: perlconfig.c
===================================================================
--- perlconfig.c	(revision 6401db33a7a1c3156833113bbe6e9eec7a571406)
+++ perlconfig.c	(revision e5210c98551ad1835a4e926515106b3e2f04eab8)
@@ -331,4 +331,5 @@
   char *err;
   const char *args[4] = {"", "-e", "0;", NULL};
+  const char *dlerr;
   AV *inc;
   char *path;
@@ -388,4 +389,14 @@
   g_free(path);
 
+  /* Load up perl-Glib. */
+  eval_pv("use Glib;", FALSE);
+
+  /* Now, before BarnOwl tries to use them, get the relevant function pointers out. */
+  dlerr = owl_closure_init();
+  if (dlerr) {
+    return g_strdup(dlerr);
+  }
+
+  /* And now it's safe to import BarnOwl. */
   eval_pv("use BarnOwl;", FALSE);
 
Index: perlglue.xs
===================================================================
--- perlglue.xs	(revision 374089acfb4f43fdb00e90ec924887d88fd84f5d)
+++ perlglue.xs	(revision 3b9ca713910ca1c21db2aa8293da541e66eaa97c)
@@ -402,42 +402,33 @@
 	   }
 
-void
-new_variable_string(name, ival, summ, desc)
-	const char * name
-	const char * ival
-	const char * summ
-	const char * desc
-	CODE:
-	owl_variable_dict_newvar_string(owl_global_get_vardict(&g),
-					name,
-					summ,
-					desc,
-					ival);
-
-void
-new_variable_int(name, ival, summ, desc)
-	const char * name
-	int ival
-	const char * summ
-	const char * desc
-	CODE:
-	owl_variable_dict_newvar_int(owl_global_get_vardict(&g),
-				     name,
-				     summ,
-				     desc,
-				     ival);
-
-void
-new_variable_bool(name, ival, summ, desc)
-	const char * name
-	int ival
-	const char * summ
-	const char * desc
-	CODE:
-	owl_variable_dict_newvar_bool(owl_global_get_vardict(&g),
-				      name,
-				      summ,
-				      desc,
-				      ival);
+
+MODULE = BarnOwl		PACKAGE = BarnOwl::Internal
+
+void
+new_variable(name, summary, description, validsettings, takes_on_off, get_tostring_fn, set_fromstring_fn, data)
+    const char *name
+    const char *summary
+    const char *description
+    const char *validsettings
+    int takes_on_off
+    SV *get_tostring_fn
+    SV *set_fromstring_fn
+    SV *data
+    CODE:
+{
+        /* data is somewhat redundant given we can create closures, but oh
+         * well. Might be convenient sometimes. */
+	if(!SV_IS_CODEREF(get_tostring_fn)) {
+		croak("To-string function must be a coderef!");
+	}
+	if(!SV_IS_CODEREF(set_fromstring_fn)) {
+		croak("From-string function must be a coderef!");
+	}
+
+        owl_variable_dict_newvar_other(owl_global_get_vardict(&g),
+                                       name, summary, description, validsettings, takes_on_off,
+                                       perl_closure_new(get_tostring_fn, data, false),
+                                       perl_closure_new(set_fromstring_fn, data, false));
+}
 
 void
Index: t/variable.t
===================================================================
--- t/variable.t	(revision 4953c44b539a53767c2742d4b17e5c477471d88c)
+++ t/variable.t	(revision 4953c44b539a53767c2742d4b17e5c477471d88c)
@@ -0,0 +1,82 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use Test::More qw(no_plan);
+
+use BarnOwl;
+
+# Test the basic variable types.
+
+BarnOwl::new_variable_int("intvar", { default => 7 });
+is(BarnOwl::getvar("intvar"), "7", "intvar default");
+BarnOwl::set("-q", "intvar", "24");
+is(BarnOwl::getvar("intvar"), "24", "intvar set");
+BarnOwl::set("-q", "intvar", "string");
+is(BarnOwl::getvar("intvar"), "24", "intvar set bogus");
+BarnOwl::unset("-q", "intvar");
+is(BarnOwl::getvar("intvar"), "24", "intvar unset bogus");
+
+BarnOwl::new_variable_bool("boolvar", { default => 1 });
+is(BarnOwl::getvar("boolvar"), "on", "boolvar default");
+BarnOwl::set("-q", "boolvar", "off");
+is(BarnOwl::getvar("boolvar"), "off", "boolvar set");
+BarnOwl::set("-q", "boolvar", "bogus");
+is(BarnOwl::getvar("boolvar"), "off", "boolvar set bogus");
+BarnOwl::set("-q", "boolvar");
+is(BarnOwl::getvar("boolvar"), "on", "boolvar set");
+BarnOwl::unset("-q", "boolvar");
+is(BarnOwl::getvar("boolvar"), "off", "boolvar unset");
+
+BarnOwl::new_variable_string("strvar", { default => "monkey" });
+is(BarnOwl::getvar("strvar"), "monkey", "strvar default");
+BarnOwl::set("-q", "strvar", "cuttlefish");
+is(BarnOwl::getvar("strvar"), "cuttlefish", "strvar set");
+BarnOwl::set("-q", "strvar");
+is(BarnOwl::getvar("strvar"), "cuttlefish", "strvar set bogus");
+BarnOwl::unset("-q", "strvar");
+is(BarnOwl::getvar("strvar"), "cuttlefish", "strvar unset bogus");
+
+BarnOwl::new_variable_enum("enumvar", { validsettings => [qw/foo bar baz/], default => "bar" });
+is(BarnOwl::getvar("enumvar"), "bar", "enumvar default");
+BarnOwl::set("-q", "enumvar", "baz");
+is(BarnOwl::getvar("enumvar"), "baz", "enumvar set");
+BarnOwl::set("-q", "enumvar", "bogus");
+is(BarnOwl::getvar("enumvar"), "baz", "boolvar set bogus");
+BarnOwl::unset("-q", "enumvar");
+is(BarnOwl::getvar("enumvar"), "baz", "enumvar unset bogus");
+
+BarnOwl::new_variable_int("intvar2");
+is(BarnOwl::getvar("intvar2"), "0", "intvar default default");
+BarnOwl::new_variable_bool("boolvar2");
+is(BarnOwl::getvar("boolvar2"), "off", "boolvar default default");
+BarnOwl::new_variable_string("strvar2");
+is(BarnOwl::getvar("strvar2"), "", "strvar default default");
+BarnOwl::new_variable_enum("enumvar2", { validsettings => [qw/foo bar baz/] });
+is(BarnOwl::getvar("enumvar2"), "foo", "enumvar default default");
+
+# And test new_variable_full
+my $value = "foo";
+BarnOwl::new_variable_full("fullvar", {
+    validsettings => '<short-words>',
+    get_tostring => sub { "value is " . $value },
+    set_fromstring => sub {
+	die "Too long" unless $_[0] =~ /^...?$/;
+	$value = $_[0];
+    },
+    takes_on_off => 1
+});
+is(BarnOwl::getvar("fullvar"), "value is foo", "fullvar get");
+BarnOwl::set("-q", "fullvar", "bar");
+is(BarnOwl::getvar("fullvar"), "value is bar", "fullvar set");
+BarnOwl::set("-q", "fullvar");
+is(BarnOwl::getvar("fullvar"), "value is on", "fullvar set2");
+BarnOwl::unset("-q", "fullvar");
+is(BarnOwl::getvar("fullvar"), "value is off", "fullvar unset");
+BarnOwl::set("-q", "fullvar", "bogus");
+is(BarnOwl::getvar("fullvar"), "value is off", "fullvar set bogus");
+$value = "something really long";
+is(BarnOwl::getvar("fullvar"), "value is something really long", "fullvar set out-of-band");
+
+1;
+
Index: tester.c
===================================================================
--- tester.c	(revision 6a209963395d62d98c3d6b52daa6fb2e3054ae1b)
+++ tester.c	(revision 6a8b5190384f683113444387b65b59ef66bb779f)
@@ -337,8 +337,7 @@
   int numfailed=0;
   char *value;
-  const void *v;
 
   printf("# BEGIN testing owl_variable\n");
-  FAIL_UNLESS("setup", 0==owl_variable_dict_setup(&vd));
+  owl_variable_dict_setup(&vd);
 
   FAIL_UNLESS("get bool var", NULL != (var = owl_variable_get_var(&vd, "rxping")));
@@ -377,26 +376,39 @@
   FAIL_UNLESS("get int 7", 9 == owl_variable_get_int(var));
 
-  owl_variable_dict_newvar_string(&vd, "stringvar", "", "", "testval");
+  FAIL_UNLESS("get enum var", NULL != (var = owl_variable_get_var(&vd, "scrollmode")));
+  FAIL_UNLESS("get enum", OWL_SCROLLMODE_NORMAL == owl_variable_get_int(var));
+  FAIL_UNLESS("get enum as string",
+	      !strcmp((value = owl_variable_get_tostring(var)), "normal"));
+  g_free(value);
+  FAIL_UNLESS("set enum 1", 0 == owl_variable_set_int(var, OWL_SCROLLMODE_TOP));
+  FAIL_UNLESS("get enum 1", OWL_SCROLLMODE_TOP == owl_variable_get_int(var));
+  FAIL_UNLESS("set enum 2a", -1 == owl_variable_set_int(var, -1));
+  FAIL_UNLESS("set enum 2b", -1 == owl_variable_set_int(var, OWL_SCROLLMODE_PAGEDCENTER + 1));
+  FAIL_UNLESS("get enum 2", OWL_SCROLLMODE_TOP == owl_variable_get_int(var));
+  FAIL_UNLESS("set enum 3", 0 == owl_variable_set_fromstring(var, "center", 0));
+  FAIL_UNLESS("get enum 4", OWL_SCROLLMODE_CENTER == owl_variable_get_int(var));
+  FAIL_UNLESS("set enum 5", -1 == owl_variable_set_fromstring(var, "bogus", 0));
+  FAIL_UNLESS("set enum 6", -1 == owl_variable_set_fromstring(var, "", 0));
+  FAIL_UNLESS("get enum 7", OWL_SCROLLMODE_CENTER == owl_variable_get_int(var));
+
+  owl_variable_dict_newvar_string(&vd, "stringvar", "testval", "", "");
   FAIL_UNLESS("get new string var", NULL != (var = owl_variable_get_var(&vd, "stringvar")));
-  FAIL_UNLESS("get new string var", NULL != (v = owl_variable_get(var)));
   FAIL_UNLESS("get new string val", !strcmp("testval", owl_variable_get_string(var)));
   owl_variable_set_string(var, "new val");
   FAIL_UNLESS("update string val", !strcmp("new val", owl_variable_get_string(var)));
 
-  owl_variable_dict_newvar_int(&vd, "intvar", "", "", 47);
+  owl_variable_dict_newvar_int(&vd, "intvar", 47, "", "");
   FAIL_UNLESS("get new int var", NULL != (var = owl_variable_get_var(&vd, "intvar")));
-  FAIL_UNLESS("get new int var", NULL != (v = owl_variable_get(var)));
   FAIL_UNLESS("get new int val", 47 == owl_variable_get_int(var));
   owl_variable_set_int(var, 17);
   FAIL_UNLESS("update int val", 17 == owl_variable_get_int(var));
 
-  owl_variable_dict_newvar_bool(&vd, "boolvar", "", "", 1);
+  owl_variable_dict_newvar_bool(&vd, "boolvar", true, "", "");
   FAIL_UNLESS("get new bool var", NULL != (var = owl_variable_get_var(&vd, "boolvar")));
-  FAIL_UNLESS("get new bool var", NULL != (v = owl_variable_get(var)));
   FAIL_UNLESS("get new bool val", owl_variable_get_bool(var));
   owl_variable_set_bool_off(var);
   FAIL_UNLESS("update bool val", !owl_variable_get_bool(var));
 
-  owl_variable_dict_newvar_string(&vd, "nullstringvar", "", "", NULL);
+  owl_variable_dict_newvar_string(&vd, "nullstringvar", NULL, "", "");
   FAIL_UNLESS("get new string (NULL) var", NULL != (var = owl_variable_get_var(&vd, "nullstringvar")));
   FAIL_UNLESS("get string (NULL)", NULL == (value = owl_variable_get_tostring(var)));
@@ -405,4 +417,10 @@
   FAIL_UNLESS("get string (NULL) 2", NULL == (value = owl_variable_get_tostring(var)));
   g_free(value);
+
+  owl_variable_dict_newvar_enum(&vd, "enumvar", 0, "", "", "a,b,c,d");
+  FAIL_UNLESS("get new enum var", NULL != (var = owl_variable_get_var(&vd, "enumvar")));
+  FAIL_UNLESS("get new enum val", 0 == owl_variable_get_int(var));
+  owl_variable_set_fromstring(var, "c", 0);
+  FAIL_UNLESS("update enum val", 2 == owl_variable_get_int(var));
 
   owl_variable_dict_cleanup(&vd);
Index: variable.c
===================================================================
--- variable.c	(revision 8258ea514c05b11da3f68d25d8414e222496fa2d)
+++ variable.c	(revision 6a8b5190384f683113444387b65b59ef66bb779f)
@@ -1,61 +1,105 @@
 #include "owl.h"
 #include <stdio.h>
-
-#define OWLVAR_BOOL(name,default,summary,description) \
-        { g_strdup(name), OWL_VARIABLE_BOOL, NULL, default, "on,off", g_strdup(summary), g_strdup(description), NULL, \
-        NULL, NULL, NULL, NULL, NULL, NULL }
-
-#define OWLVAR_BOOL_FULL(name,default,summary,description,validate,set,get) \
-        { g_strdup(name), OWL_VARIABLE_BOOL, NULL, default, "on,off", g_strdup(summary), g_strdup(description), NULL, \
-        validate, set, NULL, get, NULL, NULL }
-
-#define OWLVAR_INT(name,default,summary,description) \
-        { g_strdup(name), OWL_VARIABLE_INT, NULL, default, "<int>", g_strdup(summary), g_strdup(description), NULL, \
-        NULL, NULL, NULL, NULL, NULL, NULL }
+#include "gmarshal_funcs.h"
+
+/* TODO(davidben): When we can require 2.30 and up, remove this. */
+#ifndef G_VALUE_INIT
+#define G_VALUE_INIT { 0, { { 0 } } }
+#endif
+
+typedef const char *(*get_string_t)(const owl_variable *);
+typedef int (*get_int_t)(const owl_variable *);
+typedef bool (*get_bool_t)(const owl_variable *);
+
+typedef int (*set_string_t)(owl_variable *, const char *);
+typedef int (*set_int_t)(owl_variable *, int);
+typedef int (*set_bool_t)(owl_variable *, bool);
+
+typedef int (*validate_string_t)(const owl_variable *, const char *);
+typedef int (*validate_int_t)(const owl_variable *, int);
+typedef int (*validate_bool_t)(const owl_variable *, bool);
+
+static void owl_variable_dict_newvar_bool_full(owl_vardict *vd,
+                                               const char *name,
+                                               bool default_val,
+                                               const char *summary,
+                                               const char *description,
+                                               validate_bool_t validate_fn,
+                                               set_bool_t set_fn,
+                                               get_bool_t get_fn);
+
+static void owl_variable_dict_newvar_string_full(owl_vardict *vd,
+                                                 const char *name,
+                                                 const char *default_val,
+                                                 const char *summary,
+                                                 const char *description,
+                                                 const char *validsettings,
+                                                 validate_string_t validate_fn,
+                                                 set_string_t set_fn,
+                                                 get_string_t get_fn);
+
+static void owl_variable_dict_newvar_int_full(owl_vardict *vd,
+                                              const char *name,
+                                              int default_val,
+                                              const char *summary,
+                                              const char *description,
+                                              const char *validsettings,
+                                              validate_int_t validate_fn,
+                                              set_int_t set_fn,
+                                              get_int_t get_fn);
+
+static void owl_variable_dict_newvar_enum_full(owl_vardict *vd,
+                                               const char *name,
+                                               int default_val,
+                                               const char *summary,
+                                               const char *description,
+                                               const char *validsettings,
+                                               validate_int_t validate_fn,
+                                               set_int_t set_fn,
+                                               get_int_t get_fn);
+
+#define OWLVAR_BOOL(name, default, summary, description) \
+        owl_variable_dict_newvar_bool(vd, name, default, summary, description)
+
+#define OWLVAR_BOOL_FULL(name, default, summary, description, validate, set, get) \
+        owl_variable_dict_newvar_bool_full(vd, name, default, summary, description, \
+                                           validate, set, get)
+
+#define OWLVAR_INT(name, default, summary, description) \
+        owl_variable_dict_newvar_int(vd, name, default, summary, description)
 
 #define OWLVAR_INT_FULL(name,default,summary,description,validset,validate,set,get) \
-        { g_strdup(name), OWL_VARIABLE_INT, NULL, default, validset, g_strdup(summary), g_strdup(description), NULL, \
-        validate, set, NULL, get, NULL, NULL }
-
-#define OWLVAR_PATH(name,default,summary,description) \
-        { g_strdup(name), OWL_VARIABLE_STRING, g_strdup(default), 0, "<path>", g_strdup(summary), g_strdup(description),  NULL, \
-        NULL, NULL, NULL, NULL, NULL, NULL }
-
-#define OWLVAR_STRING(name,default,summary,description) \
-        { g_strdup(name), OWL_VARIABLE_STRING, g_strdup(default), 0, "<string>", g_strdup(summary), g_strdup(description), NULL, \
-        NULL, NULL, NULL, NULL, NULL, NULL }
-
-#define OWLVAR_STRING_FULL(name,default,validset,summary,description,validate,set,get) \
-        { g_strdup(name), OWL_VARIABLE_STRING, g_strdup(default), 0, validset, g_strdup(summary), g_strdup(description), NULL, \
-        validate, set, NULL, get, NULL, NULL }
+        owl_variable_dict_newvar_int_full(vd, name, default, summary, description, \
+                                          validset, validate, set, get)
+
+#define OWLVAR_PATH(name, default, summary, description) \
+        owl_variable_dict_newvar_path(vd, name, default, summary, description)
+
+#define OWLVAR_STRING(name, default, summary, description) \
+        owl_variable_dict_newvar_string(vd, name, default, summary, description)
+
+#define OWLVAR_STRING_FULL(name, default, validset, summary, description, validate, set, get) \
+        owl_variable_dict_newvar_string_full(vd, name, default, summary, description, \
+                                             validset, validate, set, get)
 
 /* 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,summary,description,validset) \
-        { g_strdup(name), OWL_VARIABLE_INT, NULL, default, validset, g_strdup(summary), g_strdup(description), NULL, \
-        owl_variable_enum_validate, \
-        NULL, owl_variable_enum_set_fromstring, \
-        NULL, owl_variable_enum_get_tostring, \
-        NULL }
+#define OWLVAR_ENUM(name, default, summary, description, validset) \
+        owl_variable_dict_newvar_enum(vd, name, default, summary, description, validset)
 
 #define OWLVAR_ENUM_FULL(name,default,summary,description,validset,validate, set, get) \
-        { g_strdup(name), OWL_VARIABLE_INT, NULL, default, validset, g_strdup(summary), g_strdup(description), NULL, \
-        validate, \
-        set, owl_variable_enum_set_fromstring, \
-        get, owl_variable_enum_get_tostring, \
-        NULL }
-
-int owl_variable_add_defaults(owl_vardict *vd)
-{
-  owl_variable variables_to_init[] = {
-
+        owl_variable_dict_newvar_enum_full(vd, name, default, summary, description, \
+                                           validset, validate, set, get)
+
+void owl_variable_add_defaults(owl_vardict *vd)
+{
   OWLVAR_STRING( "personalbell" /* %OwlVarStub */, "off",
 		 "ring the terminal bell when personal messages are received",
 		 "Can be set to 'on', 'off', or the name of a filter which\n"
-		 "messages need to match in order to ring the bell"),
+		 "messages need to match in order to ring the bell");
 
   OWLVAR_BOOL( "bell" /* %OwlVarStub */, 1,
-	       "enable / disable the terminal bell", "" ),
+	       "enable / disable the terminal bell", "" );
 
   OWLVAR_BOOL_FULL( "debug" /* %OwlVarStub */, OWL_DEBUG,
@@ -63,32 +107,32 @@
 		    "If set to 'on', debugging messages are logged to the\n"
 		    "file specified by the debugfile variable.\n",
-		    NULL, owl_variable_debug_set, NULL),
+		    NULL, owl_variable_debug_set, NULL);
 
   OWLVAR_BOOL( "startuplogin" /* %OwlVarStub */, 1,
-	       "send a login message when BarnOwl starts", "" ),
+	       "send a login message when BarnOwl starts", "" );
 
   OWLVAR_BOOL( "shutdownlogout" /* %OwlVarStub */, 1,
-	       "send a logout message when BarnOwl exits", "" ),
+	       "send a logout message when BarnOwl exits", "" );
 
   OWLVAR_BOOL( "rxping" /* %OwlVarStub */, 0,
-	       "display received pings", "" ),
+	       "display received pings", "" );
 
   OWLVAR_BOOL( "txping" /* %OwlVarStub */, 1,
-	       "send pings", "" ),
+	       "send pings", "" );
 
   OWLVAR_BOOL( "sepbar_disable" /* %OwlVarStub */, 0,
-	       "disable printing information in the separator bar", "" ),
+	       "disable printing information in the separator bar", "" );
 
   OWLVAR_BOOL( "smartstrip" /* %OwlVarStub */, 1,
-	       "strip kerberos instance for reply", ""),
+	       "strip kerberos instance for reply", "");
 
   OWLVAR_BOOL( "newlinestrip" /* %OwlVarStub */, 1,
-	       "strip leading and trailing newlines", ""),
+	       "strip leading and trailing newlines", "");
 
   OWLVAR_BOOL( "displayoutgoing" /* %OwlVarStub */, 1,
-	       "display outgoing messages", "" ),
+	       "display outgoing messages", "" );
 
   OWLVAR_BOOL( "loginsubs" /* %OwlVarStub */, 1,
-	       "load logins from .anyone on startup", "" ),
+	       "load logins from .anyone on startup", "" );
 
   OWLVAR_BOOL( "logging" /* %OwlVarStub */, 0,
@@ -97,5 +141,5 @@
 	       "logged in the directory specified\n"
 	       "by the 'logpath' variable.  The filename in that\n"
-	       "directory is derived from the sender of the message.\n" ),
+	       "directory is derived from the sender of the message.\n" );
 
   OWLVAR_BOOL( "classlogging" /* %OwlVarStub */, 0,
@@ -105,5 +149,5 @@
 	       "by the 'classlogpath' variable.\n" 
 	       "The filename in that directory is derived from\n"
-	       "the name of the class to which the message was sent.\n" ),
+	       "the name of the class to which the message was sent.\n" );
 
   OWLVAR_ENUM( "loggingdirection" /* %OwlVarStub */, OWL_LOGGING_DIRECTION_BOTH,
@@ -114,9 +158,9 @@
 	       "is selected both incoming and outgoing messages are\n"
 	       "logged.",
-	       "both,in,out"),
+	       "both,in,out");
 
   OWLVAR_BOOL_FULL( "colorztext" /* %OwlVarStub */, 1,
                     "allow @color() in zephyrs to change color",
-                    NULL, NULL, owl_variable_colorztext_set, NULL),
+                    NULL, NULL, owl_variable_colorztext_set, NULL);
 
   OWLVAR_BOOL( "fancylines" /* %OwlVarStub */, 1,
@@ -124,9 +168,9 @@
 	       "If turned off, dashes, pipes and pluses will be used\n"
 	       "to draw lines on the screen.  Useful when the terminal\n"
-	       "is causing problems" ),
+	       "is causing problems" );
 
   OWLVAR_BOOL( "zcrypt" /* %OwlVarStub */, 1,
 	       "Do automatic zcrypt processing",
-	       "" ),
+	       "" );
 
   OWLVAR_BOOL_FULL( "pseudologins" /* %OwlVarStub */, 0,
@@ -136,5 +180,5 @@
 		    "but sent no login message, or a user is not present that sent no\n"
 		    "logout message, a pseudo login or logout message will be created\n",
-		    NULL, owl_variable_pseudologins_set, NULL),
+		    NULL, owl_variable_pseudologins_set, NULL);
 
   OWLVAR_BOOL( "ignorelogins" /* %OwlVarStub */, 0,
@@ -142,5 +186,5 @@
 	       "When this is enabled, BarnOwl will print login and logout notifications\n"
 	       "for AIM, zephyr, or other protocols.  If disabled BarnOwl will not print\n"
-	       "login or logout notifications.\n"),
+	       "login or logout notifications.\n");
 
   OWLVAR_STRING( "logfilter" /* %OwlVarStub */, "",
@@ -151,5 +195,5 @@
 		 "variables like logging, classlogging, loglogins, loggingdirection,\n"
 		 "etc.  If you want this variable to control all logging, make sure\n"
-		 "all other logging variables are in their default state.\n"),
+		 "all other logging variables are in their default state.\n");
 
   OWLVAR_BOOL( "loglogins" /* %OwlVarStub */, 0,
@@ -157,5 +201,5 @@
 	       "When this is enabled, BarnOwl will log login and logout notifications\n"
 	       "for AIM, zephyr, or other protocols.  If disabled BarnOwl will not print\n"
-	       "login or logout notifications.\n"),
+	       "login or logout notifications.\n");
 
   OWLVAR_ENUM_FULL( "disable-ctrl-d" /* %OwlVarStub:lockout_ctrld */, 1,
@@ -169,20 +213,20 @@
 		    "in the editmulti keymap.\n",
 		    "off,middle,on",
-		    NULL, owl_variable_disable_ctrl_d_set, NULL),
+		    NULL, owl_variable_disable_ctrl_d_set, NULL);
 
   OWLVAR_PATH( "logpath" /* %OwlVarStub */, "~/zlog/people",
 	       "path for logging personal zephyrs", 
 	       "Specifies a directory which must exist.\n"
-	       "Files will be created in the directory for each sender.\n"),
+	       "Files will be created in the directory for each sender.\n");
 
   OWLVAR_PATH( "classlogpath" /* %OwlVarStub:classlogpath */, "~/zlog/class",
 	       "path for logging class zephyrs",
 	       "Specifies a directory which must exist.\n"
-	       "Files will be created in the directory for each class.\n"),
+	       "Files will be created in the directory for each class.\n");
 
   OWLVAR_PATH( "debug_file" /* %OwlVarStub */, OWL_DEBUG_FILE,
 	       "path for logging debug messages when debugging is enabled",
 	       "This file will be logged to if 'debug' is set to 'on'.\n"
-               "BarnOwl will append a dot and the current process's pid to the filename."),
+               "BarnOwl will append a dot and the current process's pid to the filename.");
   
   OWLVAR_PATH( "zsigproc" /* %OwlVarStub:zsigproc */, NULL,
@@ -192,5 +236,5 @@
                "See the documentation for 'zsig' for more information about\n"
                "how the outgoing zsig is chosen."
-               ),
+               );
 
   OWLVAR_PATH( "newmsgproc" /* %OwlVarStub:newmsgproc */, NULL,
@@ -198,5 +242,5 @@
 	       "The named program will be run when BarnOwl receives new\n"
 	       "messages.  It will not be run again until the first\n"
-	       "instance exits"),
+	       "instance exits");
 
   OWLVAR_STRING( "zsender" /* %OwlVarStub */, "",
@@ -205,5 +249,5 @@
          "zephyrs.  If this is unset, it will use your Kerberos\n"
          "principal. Note that customizing the sender name will\n"
-         "cause your zephyrs to be sent unauthenticated."),
+         "cause your zephyrs to be sent unauthenticated.");
 
   OWLVAR_STRING( "zsigfunc" /* %OwlVarStub */, "BarnOwl::default_zephyr_signature()",
@@ -212,5 +256,5 @@
 		 "explicit zsig.  The default setting implements the policy\n"
 		 "described in the documentation for the 'zsig' variable.\n"
-		 "See also BarnOwl::random_zephyr_signature().\n"),
+		 "See also BarnOwl::random_zephyr_signature().\n");
 
   OWLVAR_STRING( "zsig" /* %OwlVarStub */, "",
@@ -219,5 +263,5 @@
 		 "unset, 'zsigproc' will be run to generate a zsig. If that is\n"
 		 "also unset, the 'zwrite-signature' zephyr variable will be\n"
-		 "used instead.\n"),
+		 "used instead.\n");
 
   OWLVAR_STRING( "appendtosepbar" /* %OwlVarStub */, "",
@@ -225,44 +269,44 @@
 		 "The sepbar is the bar separating the top and bottom\n"
 		 "of the BarnOwl screen.  Any string specified here will\n"
-		 "be displayed on the right of the sepbar\n"),
+		 "be displayed on the right of the sepbar\n");
 
   OWLVAR_BOOL( "zaway" /* %OwlVarStub */, 0,
-	       "turn zaway on or off", "" ),
+	       "turn zaway on or off", "" );
 
   OWLVAR_STRING( "zaway_msg" /* %OwlVarStub */, 
 		 OWL_DEFAULT_ZAWAYMSG,
-	         "zaway msg for responding to zephyrs when away", "" ),
+	         "zaway msg for responding to zephyrs when away", "" );
 
   OWLVAR_STRING( "zaway_msg_default" /* %OwlVarStub */, 
 		 OWL_DEFAULT_ZAWAYMSG,
-	         "default zaway message", "" ),
+	         "default zaway message", "" );
 
   OWLVAR_BOOL_FULL( "aaway" /* %OwlVarStub */, 0,
 		    "Set AIM away status",
 		    "",
-		    NULL, owl_variable_aaway_set, NULL),
+		    NULL, owl_variable_aaway_set, NULL);
 
   OWLVAR_STRING( "aaway_msg" /* %OwlVarStub */, 
 		 OWL_DEFAULT_AAWAYMSG,
-	         "AIM away msg for responding when away", "" ),
+	         "AIM away msg for responding when away", "" );
 
   OWLVAR_STRING( "aaway_msg_default" /* %OwlVarStub */, 
 		 OWL_DEFAULT_AAWAYMSG,
-	         "default AIM away message", "" ),
+	         "default AIM away message", "" );
 
   OWLVAR_STRING( "view_home" /* %OwlVarStub */, "all",
 	         "home view to switch to after 'X' and 'V'", 
-		 "SEE ALSO: view, filter\n" ),
+		 "SEE ALSO: view, filter\n" );
 
   OWLVAR_STRING( "alert_filter" /* %OwlVarStub */, "none",
 		 "filter on which to trigger alert actions",
-		 "" ),
+		 "" );
 
   OWLVAR_STRING( "alert_action" /* %OwlVarStub */, "nop",
 		 "BarnOwl command to execute for alert actions",
-		 "" ),
+		 "" );
 
   OWLVAR_STRING_FULL( "tty" /* %OwlVarStub */, "", "<string>", "tty name for zephyr location", "",
-		      NULL, owl_variable_tty_set, NULL),
+		      NULL, owl_variable_tty_set, NULL);
 
   OWLVAR_STRING( "default_style" /* %OwlVarStub */, "default",
@@ -275,5 +319,5 @@
 		 "   perl     - legacy perl interface\n"
 		 "\nSEE ALSO: style, show styles, view -s <style>\n"
-		 ),
+		 );
 
 
@@ -282,5 +326,5 @@
                  "This specifies the maximum number of columns for M-q to fill text\n"
                  "to.  If set to 0, M-q will wrap to the width of the window, and\n"
-                 "values less than 0 disable M-q entirely.\n"),
+                 "values less than 0 disable M-q entirely.\n");
 
   OWLVAR_INT(    "edit:maxwrapcols" /* %OwlVarStub:edit_maxwrapcols */, 70,
@@ -291,5 +335,5 @@
                  "\n"
                  "As a courtesy to recipients, it is recommended that outgoing\n"
-                 "Zephyr messages be no wider than 70 columns.\n"),
+                 "Zephyr messages be no wider than 70 columns.\n");
 
   OWLVAR_INT( "aim_ignorelogin_timer" /* %OwlVarStub */, 15,
@@ -298,5 +342,5 @@
 	      "AIM login before allowing the receipt of AIM login notifications.\n"
 	      "By default this is set to 15.  If you would like to view login\n"
-	      "notifications of buddies as soon as you login, set it to 0 instead."),
+	      "notifications of buddies as soon as you login, set it to 0 instead.");
 
 	      
@@ -311,5 +355,5 @@
 		   owl_variable_typewinsize_set,
 		   NULL /* use default for get */
-		   ),
+		   );
 
   OWLVAR_INT( "typewindelta" /* %OwlVarStub */, 0,
@@ -321,5 +365,5 @@
            "typewinsize to 1.\n\n"
            "This works a lot better with a non-default scrollmode;\n"
-           "try :set scrollmode pagedcenter.\n"),
+           "try :set scrollmode pagedcenter.\n");
 
   OWLVAR_ENUM( "scrollmode" /* %OwlVarStub */, OWL_SCROLLMODE_NORMAL,
@@ -350,5 +394,5 @@
 	       "                 the screen will be paged up or down and\n"
 	       "                 the cursor will be near the center.\n",
-	       "normal,top,neartop,center,paged,pagedcenter" ),
+	       "normal,top,neartop,center,paged,pagedcenter" );
 
   OWLVAR_BOOL( "narrow-related" /* %OwlVarStub:narrow_related */, 1,
@@ -358,5 +402,5 @@
                "for Zephyr, this controls whether to narrow to e.g. class-help or\n"
                "class-help.d alone, or to related-class-help, which includes\n"
-               "help, unhelp, help.d, etc.\n\nDefault is true (include unclasses, etc.).\n" ),
+               "help, unhelp, help.d, etc.\n\nDefault is true (include unclasses, etc.).\n" );
 
   OWLVAR_BOOL( "_followlast" /* %OwlVarStub */, 0,
@@ -365,5 +409,5 @@
 	       "continue to follow the last message if this is set.\n"
 	       "Note that this is currently risky as you might accidentally\n"
-	       "delete a message right as it came in.\n" ),
+	       "delete a message right as it came in.\n" );
 
   OWLVAR_STRING_FULL( "default_exposure" /* %OwlVarStub */, "",
@@ -374,5 +418,5 @@
                       "~/.zephyr.vars.\n"
                       "See the description of exposure for the values this can be.",
-                      NULL, owl_variable_default_exposure_set, owl_variable_default_exposure_get ),
+                      NULL, owl_variable_default_exposure_set, owl_variable_default_exposure_get );
 
   OWLVAR_STRING_FULL( "exposure" /* %OwlVarStub */, "",
@@ -430,17 +474,5 @@
                       "                     personal subscriptions will be entered for the\n"
                       "                     user.\n",
-                      NULL, owl_variable_exposure_set, NULL /* use default for get */ ),
-
-  /* This MUST be last... */
-  { NULL, 0, NULL, 0, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL }
-
-  };
-
-  int ret = owl_variable_dict_add_from_list(vd, variables_to_init);
-  owl_variable *var;
-  for (var = variables_to_init; var->name != NULL; var++)
-    owl_variable_cleanup(var);
-  return ret;
+                      NULL, owl_variable_exposure_set, NULL /* use default for get */ );
 }
 
@@ -452,20 +484,16 @@
 /* commonly useful */
 
-int owl_variable_int_validate_gt0(const owl_variable *v, const void *newval)
-{
-  if (newval == NULL) return(0);
-  else if (*(const int*)newval < 1) return(0);
-  else return (1);
-}
-
-int owl_variable_int_validate_positive(const owl_variable *v, const void *newval)
-{
-  if (newval == NULL) return(0);
-  else if (*(const int*)newval < 0) return(0);
-  else return (1);
+int owl_variable_int_validate_gt0(const owl_variable *v, int newval)
+{
+  return !(newval < 1);
+}
+
+int owl_variable_int_validate_positive(const owl_variable *v, int newval)
+{
+  return !(newval < 0);
 }
 
 /* typewinsize */
-int owl_variable_typewinsize_set(owl_variable *v, const void *newval)
+int owl_variable_typewinsize_set(owl_variable *v, int newval)
 {
   int rv;
@@ -476,26 +504,22 @@
 
 /* debug (cache value in g->debug) */
-int owl_variable_debug_set(owl_variable *v, const void *newval)
-{
-  if (newval && (*(const int*)newval == 1 || *(const int*)newval == 0)) {
-    g.debug = *(const int*)newval;
+int owl_variable_debug_set(owl_variable *v, bool newval)
+{
+  g.debug = newval;
+  return owl_variable_bool_set_default(v, newval);
+}
+
+/* When 'aaway' is changed, need to notify the AIM server */
+int owl_variable_aaway_set(owl_variable *v, bool newval)
+{
+  if (newval) {
+    owl_aim_set_awaymsg(owl_global_get_aaway_msg(&g));
+  } else {
+    owl_aim_set_awaymsg("");
   }
   return owl_variable_bool_set_default(v, newval);
 }
 
-/* When 'aaway' is changed, need to notify the AIM server */
-int owl_variable_aaway_set(owl_variable *v, const void *newval)
-{
-  if (newval) {
-    if (*(const int*)newval == 1) {
-      owl_aim_set_awaymsg(owl_global_get_aaway_msg(&g));
-    } else if (*(const int*)newval == 0) {
-      owl_aim_set_awaymsg("");
-    }
-  }
-  return owl_variable_bool_set_default(v, newval);
-}
-
-int owl_variable_colorztext_set(owl_variable *v, const void *newval)
+int owl_variable_colorztext_set(owl_variable *v, bool newval)
 {
   int ret = owl_variable_bool_set_default(v, newval);
@@ -510,18 +534,16 @@
 }
 
-int owl_variable_pseudologins_set(owl_variable *v, const void *newval)
+int owl_variable_pseudologins_set(owl_variable *v, bool newval)
 {
   static guint timer = 0;
   if (newval) {
-    if (*(const int*)newval == 1) {
-      owl_function_zephyr_buddy_check(0);
-      if (timer == 0) {
-        timer = g_timeout_add_seconds(180, owl_zephyr_buddycheck_timer, NULL);
-      }
-    } else {
-      if (timer != 0) {
-        g_source_remove(timer);
-        timer = 0;
-      }
+    owl_function_zephyr_buddy_check(0);
+    if (timer == 0) {
+      timer = g_timeout_add_seconds(180, owl_zephyr_buddycheck_timer, NULL);
+    }
+  } else {
+    if (timer != 0) {
+      g_source_remove(timer);
+      timer = 0;
     }
   }
@@ -531,10 +553,10 @@
 /* note that changing the value of this will clobber 
  * any user setting of this */
-int owl_variable_disable_ctrl_d_set(owl_variable *v, const void *newval)
-{
-  if (newval && !owl_context_is_startup(owl_global_get_context(&g))) {
-    if (*(const int*)newval == 2) {
+int owl_variable_disable_ctrl_d_set(owl_variable *v, int newval)
+{
+  if (!owl_context_is_startup(owl_global_get_context(&g))) {
+    if (newval == 2) {
       owl_function_command_norv("bindkey editmulti C-d command edit:delete-next-char");
-    } else if (*(const int*)newval == 1) {
+    } else if (newval == 1) {
       owl_function_command_norv("bindkey editmulti C-d command edit:done-or-delete");
     } else {
@@ -542,24 +564,24 @@
     }
   }  
-  return owl_variable_int_set_default(v, newval);  
-}
-
-int owl_variable_tty_set(owl_variable *v, const void *newval)
+  return owl_variable_int_set_default(v, newval);
+}
+
+int owl_variable_tty_set(owl_variable *v, const char *newval)
 {
   owl_zephyr_set_locationinfo(g_get_host_name(), newval);
-  return(owl_variable_string_set_default(v, newval));
-}
-
-int owl_variable_default_exposure_set(owl_variable *v, const void *newval)
+  return owl_variable_string_set_default(v, newval);
+}
+
+int owl_variable_default_exposure_set(owl_variable *v, const char *newval)
 {
   return owl_zephyr_set_default_exposure(newval);
 }
 
-const void *owl_variable_default_exposure_get(const owl_variable *v)
+const char *owl_variable_default_exposure_get(const owl_variable *v)
 {
   return owl_zephyr_get_default_exposure();
 }
 
-int owl_variable_exposure_set(owl_variable *v, const void *newval)
+int owl_variable_exposure_set(owl_variable *v, const char *newval)
 {
   int ret = owl_zephyr_set_exposure(newval);
@@ -573,78 +595,17 @@
 /**************************************************************************/
 
-int owl_variable_dict_setup(owl_vardict *vd) {
+void owl_variable_dict_setup(owl_vardict *vd) {
   owl_dict_create(vd);
-  return owl_variable_add_defaults(vd);
-}
-
-int owl_variable_dict_add_from_list(owl_vardict *vd, owl_variable *variables_to_init)
-{
-  owl_variable *var, *cur;
-  for (var = variables_to_init; var->name != NULL; var++) {
-    cur = g_new(owl_variable, 1);
-    *cur = *var;
-    /* strdup all the strings so we can delete them consistently. */
-    cur->name = g_strdup(var->name);
-    cur->summary = g_strdup(var->summary);
-    cur->description = g_strdup(var->description);
-    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->delete_fn)
-	cur->delete_fn = owl_variable_delete_default;
-      cur->pval_default = g_strdup(var->pval_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->delete_fn)
-	cur->delete_fn = owl_variable_delete_default;
-      cur->val = g_new(int, 1);
-      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->delete_fn)
-	cur->delete_fn = owl_variable_delete_default;
-      cur->val = g_new(int, 1);
-      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, cur, NULL);
-  }
-  return 0;
+  owl_variable_add_defaults(vd);
+}
+
+CALLER_OWN GClosure *owl_variable_make_closure(owl_variable *v,
+                                               GCallback fn,
+                                               GClosureMarshal marshal) {
+  GClosure *closure = g_cclosure_new_swap(fn, v, NULL);
+  g_closure_set_marshal(closure,marshal);
+  g_closure_ref(closure);
+  g_closure_sink(closure);
+  return closure;
 }
 
@@ -654,86 +615,148 @@
 }
 
-CALLER_OWN owl_variable *owl_variable_newvar(const char *name, const char *summary, const char *description)
-{
-  owl_variable * var = g_new0(owl_variable, 1);
+static owl_variable *owl_variable_newvar(int type, const char *name, const char *summary, const char *description, const char *validsettings) {
+  owl_variable *var = g_new0(owl_variable, 1);
+  var->type = type;
   var->name = g_strdup(name);
   var->summary = g_strdup(summary);
   var->description = g_strdup(description);
+  var->validsettings = g_strdup(validsettings);
   return var;
 }
 
-void owl_variable_update(owl_variable *var, const char *summary, const char *desc) {
-  g_free(var->summary);
-  var->summary = g_strdup(summary);
-  g_free(var->description);
-  var->description = g_strdup(desc);
-}
-
-void owl_variable_dict_newvar_string(owl_vardict *vd, const char *name, const char *summ, const char *desc, const char *initval)
-{
-  owl_variable *old = owl_variable_get_var(vd, name);
-  if (old && owl_variable_get_type(old) == OWL_VARIABLE_STRING) {
-    owl_variable_update(old, summ, desc);
-    g_free(old->pval_default);
-    old->pval_default = g_strdup(initval);
-  } else {
-    owl_variable * var = owl_variable_newvar(name, summ, desc);
-    var->type = OWL_VARIABLE_STRING;
-    var->validsettings = "<string>";
-    var->pval_default = g_strdup(initval);
-    var->set_fn = owl_variable_string_set_default;
-    var->set_fromstring_fn = owl_variable_string_set_fromstring_default;
-    var->get_fn = owl_variable_get_default;
-    var->get_tostring_fn = owl_variable_string_get_tostring_default;
-    var->delete_fn = owl_variable_delete_default;
-    var->set_fn(var, initval);
-    owl_variable_dict_add_variable(vd, var);
-  }
-}
-
-void owl_variable_dict_newvar_int(owl_vardict *vd, const char *name, const char *summ, const char *desc, int initval)
-{
-  owl_variable *old = owl_variable_get_var(vd, name);
-  if (old && owl_variable_get_type(old) == OWL_VARIABLE_INT) {
-    owl_variable_update(old, summ, desc);
-    old->ival_default = initval;
-  } else {
-    owl_variable * var = owl_variable_newvar(name, summ, desc);
-    var->type = OWL_VARIABLE_INT;
-    var->validsettings = "<int>";
-    var->ival_default = initval;
-    var->validate_fn = owl_variable_int_validate_default;
-    var->set_fn = owl_variable_int_set_default;
-    var->set_fromstring_fn = owl_variable_int_set_fromstring_default;
-    var->get_fn = owl_variable_get_default;
-    var->get_tostring_fn = owl_variable_int_get_tostring_default;
-    var->delete_fn = owl_variable_delete_default;
-    var->val = g_new(int, 1);
-    var->set_fn(var, &initval);
-    owl_variable_dict_add_variable(vd, var);
-  }
-}
-
-void owl_variable_dict_newvar_bool(owl_vardict *vd, const char *name, const char *summ, const char *desc, int initval)
-{
-  owl_variable *old = owl_variable_get_var(vd, name);
-  if (old && owl_variable_get_type(old) == OWL_VARIABLE_BOOL) {
-    owl_variable_update(old, summ, desc);
-    old->ival_default = initval;
-  } else {
-    owl_variable * var = owl_variable_newvar(name, summ, desc);
-    var->type = OWL_VARIABLE_BOOL;
-    var->validsettings = "on,off";
-    var->ival_default = initval;
-    var->validate_fn = owl_variable_bool_validate_default;
-    var->set_fn = owl_variable_bool_set_default;
-    var->set_fromstring_fn = owl_variable_bool_set_fromstring_default;
-    var->get_fn = owl_variable_get_default;
-    var->get_tostring_fn = owl_variable_bool_get_tostring_default;
-    var->delete_fn = owl_variable_delete_default;
-    var->val = g_new(int, 1);
-    var->set_fn(var, &initval);
-    owl_variable_dict_add_variable(vd, var);
-  }
+static void owl_variable_dict_newvar_int_full(owl_vardict *vd, const char *name, int default_val, const char *summary, const char *description, const char *validsettings, validate_int_t validate_fn, set_int_t set_fn, get_int_t get_fn)
+{
+  owl_variable *var = owl_variable_newvar(OWL_VARIABLE_INT, name, summary,
+                                          description, validsettings);
+  var->takes_on_off = false;
+  var->get_fn = G_CALLBACK(get_fn ? get_fn : owl_variable_int_get_default);
+  var->set_fn = G_CALLBACK(set_fn ? set_fn : owl_variable_int_set_default);
+  var->validate_fn = G_CALLBACK(validate_fn ? validate_fn : owl_variable_int_validate_default);
+
+  var->get_tostring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_int_get_tostring_default),
+      g_cclosure_user_marshal_STRING__VOID);
+  var->set_fromstring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_int_set_fromstring_default),
+      g_cclosure_user_marshal_INT__STRING);
+
+  g_value_init(&var->val, G_TYPE_INT);
+  owl_variable_set_int(var, default_val);
+
+  var->default_str = owl_variable_get_tostring(var);
+  owl_variable_dict_add_variable(vd, var);
+}
+
+void owl_variable_dict_newvar_int(owl_vardict *vd, const char *name, int default_val, const char *summary, const char *description) {
+  owl_variable_dict_newvar_int_full(vd, name, default_val, summary, description,
+                                    "<int>", NULL, NULL, NULL);
+}
+
+static void owl_variable_dict_newvar_bool_full(owl_vardict *vd, const char *name, bool default_val, const char *summary, const char *description, validate_bool_t validate_fn, set_bool_t set_fn, get_bool_t get_fn)
+{
+  owl_variable *var = owl_variable_newvar(OWL_VARIABLE_BOOL, name, summary,
+                                          description, "on,off");
+  var->takes_on_off = true;
+  var->get_fn = G_CALLBACK(get_fn ? get_fn : owl_variable_bool_get_default);
+  var->set_fn = G_CALLBACK(set_fn ? set_fn : owl_variable_bool_set_default);
+  var->validate_fn = G_CALLBACK(validate_fn ? validate_fn : owl_variable_bool_validate_default);
+
+  var->get_tostring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_bool_get_tostring_default),
+      g_cclosure_user_marshal_STRING__VOID);
+  var->set_fromstring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_bool_set_fromstring_default),
+      g_cclosure_user_marshal_INT__STRING);
+
+  g_value_init(&var->val, G_TYPE_BOOLEAN);
+  owl_variable_set_bool(var, default_val);
+
+  var->default_str = owl_variable_get_tostring(var);
+  owl_variable_dict_add_variable(vd, var);
+}
+
+void owl_variable_dict_newvar_bool(owl_vardict *vd, const char *name, bool default_val, const char *summary, const char *description) {
+  owl_variable_dict_newvar_bool_full(vd, name, default_val, summary, description,
+                                     NULL, NULL, NULL);
+}
+
+static void owl_variable_dict_newvar_string_full(owl_vardict *vd, const char *name, const char *default_val, const char *summary, const char *description, const char *validsettings, validate_string_t validate_fn, set_string_t set_fn, get_string_t get_fn)
+{
+  owl_variable *var = owl_variable_newvar(OWL_VARIABLE_STRING, name, summary,
+                                          description, validsettings);
+  var->takes_on_off = false;
+  var->get_fn = G_CALLBACK(get_fn ? get_fn : owl_variable_string_get_default);
+  var->set_fn = G_CALLBACK(set_fn ? set_fn : owl_variable_string_set_default);
+  var->validate_fn = G_CALLBACK(validate_fn ? validate_fn : owl_variable_string_validate_default);
+
+  var->get_tostring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_string_get_tostring_default),
+      g_cclosure_user_marshal_STRING__VOID);
+  var->set_fromstring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_string_set_fromstring_default),
+      g_cclosure_user_marshal_INT__STRING);
+
+  g_value_init(&var->val, G_TYPE_STRING);
+  owl_variable_set_string(var, default_val);
+
+  var->default_str = owl_variable_get_tostring(var);
+  owl_variable_dict_add_variable(vd, var);
+}
+
+void owl_variable_dict_newvar_string(owl_vardict *vd, const char *name, const char *default_val, const char *summary, const char *description) {
+  owl_variable_dict_newvar_string_full(vd, name, default_val, summary, description,
+                                       "<string>", NULL, NULL, NULL);
+}
+
+void owl_variable_dict_newvar_path(owl_vardict *vd, const char *name, const char *default_val, const char *summary, const char *description) {
+  owl_variable_dict_newvar_string_full(vd, name, default_val, summary, description,
+                                       "<path>", NULL, NULL, NULL);
+}
+
+static void owl_variable_dict_newvar_enum_full(owl_vardict *vd, const char *name, int default_val, const char *summary, const char *description, const char *validsettings, validate_int_t validate_fn, set_int_t set_fn, get_int_t get_fn)
+{
+  owl_variable *var = owl_variable_newvar(OWL_VARIABLE_INT, name, summary,
+                                          description, validsettings);
+  var->takes_on_off = false;
+  var->get_fn = G_CALLBACK(get_fn ? get_fn : owl_variable_int_get_default);
+  var->set_fn = G_CALLBACK(set_fn ? set_fn : owl_variable_int_set_default);
+  var->validate_fn = G_CALLBACK(validate_fn ? validate_fn : owl_variable_enum_validate);
+
+  var->get_tostring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_enum_get_tostring),
+      g_cclosure_user_marshal_STRING__VOID);
+  var->set_fromstring_fn = owl_variable_make_closure(
+      var, G_CALLBACK(owl_variable_enum_set_fromstring),
+      g_cclosure_user_marshal_INT__STRING);
+
+  g_value_init(&var->val, G_TYPE_INT);
+  owl_variable_set_int(var, default_val);
+
+  var->default_str = owl_variable_get_tostring(var);
+  owl_variable_dict_add_variable(vd, var);
+}
+
+void owl_variable_dict_newvar_enum(owl_vardict *vd, const char *name, int default_val, const char *summary, const char *description, const char *validset) {
+  owl_variable_dict_newvar_enum_full(vd, name, default_val, summary, description,
+                                     validset, NULL, NULL, NULL);
+}
+
+void owl_variable_dict_newvar_other(owl_vardict *vd, const char *name, const char *summary, const char *description, const char *validsettings, bool takes_on_off, GClosure *get_tostring_fn, GClosure *set_fromstring_fn)
+{
+  owl_variable *var = owl_variable_newvar(OWL_VARIABLE_OTHER, name, summary,
+                                          description, validsettings);
+  var->takes_on_off = takes_on_off;
+
+  var->get_tostring_fn = g_closure_ref(get_tostring_fn);
+  g_closure_sink(get_tostring_fn);
+
+  var->set_fromstring_fn = g_closure_ref(set_fromstring_fn);
+  g_closure_sink(set_fromstring_fn);
+
+  var->default_str = owl_variable_get_tostring(var);
+
+  /* Note: this'll overwrite any existing variable of that name, even a C one,
+     but it's consistent with previous behavior and commands. */
+  owl_variable_dict_add_variable(vd, var);
 }
 
@@ -747,17 +770,15 @@
 }
 
-void owl_variable_cleanup(owl_variable *v)
-{
-  if (v->delete_fn) v->delete_fn(v);
+void owl_variable_delete(owl_variable *v)
+{
   g_free(v->name);
   g_free(v->summary);
   g_free(v->description);
-  if (v->type == OWL_VARIABLE_STRING)
-    g_free(v->pval_default);
-}
-
-void owl_variable_delete(owl_variable *v)
-{
-  owl_variable_cleanup(v);
+  g_free(v->default_str);
+  g_free(v->validsettings);
+  g_value_unset(&(v->val));
+  g_closure_unref(v->get_tostring_fn);
+  g_closure_unref(v->set_fromstring_fn);
+
   g_free(v);
 }
@@ -785,46 +806,66 @@
   return v->type;
 }
-
-/* 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_variable *v, const char *value, int msg) {
   char *tostring;
-  if (!v->set_fromstring_fn) {
-    if (msg) owl_function_error("Variable %s is read-only", owl_variable_get_name(v));
-    return -1;   
-  }
-  if (0 != v->set_fromstring_fn(v, value)) {
+  GValue values[] = {G_VALUE_INIT, G_VALUE_INIT};
+  GValue return_box = G_VALUE_INIT;
+  int set_successfully;
+
+  g_value_init(&values[0], G_TYPE_POINTER);
+  g_value_set_pointer(&values[0], NULL);
+  g_value_init(&values[1], G_TYPE_STRING);
+  g_value_set_static_string(&values[1], value);
+  g_value_init(&return_box, G_TYPE_INT);
+  g_closure_invoke(v->set_fromstring_fn, &return_box, 2, values, NULL);
+
+  set_successfully = g_value_get_int(&return_box);
+  if (0 != set_successfully) {
     if (msg) owl_function_error("Unable to set %s (must be %s)", owl_variable_get_name(v),
                                 owl_variable_get_validsettings(v));
-    return -1;
+  } else if (msg) {
+    tostring = owl_variable_get_tostring(v);
+    if (tostring) {
+      owl_function_makemsg("%s = '%s'", owl_variable_get_name(v), tostring);
+    } else {
+      owl_function_makemsg("%s = <null>", owl_variable_get_name(v));
+    }
+    g_free(tostring);
   }
-  if (msg) {
-    tostring = v->get_tostring_fn(v, v->get_fn(v));
-    if (tostring)
-      owl_function_makemsg("%s = '%s'", owl_variable_get_name(v), tostring);
-    else
-      owl_function_makemsg("%s = <null>", owl_variable_get_name(v));
-    g_free(tostring);
-  }    
-  return 0;
+
+  g_value_unset(&return_box);
+  g_value_unset(&values[1]);
+  g_value_unset(&values[0]);
+  return set_successfully;
 }
  
 int owl_variable_set_string(owl_variable *v, const char *newval)
 {
-  if (v->type != OWL_VARIABLE_STRING) return -1;
-  return v->set_fn(v, newval);
-}
- 
+  g_return_val_if_fail(v->type == OWL_VARIABLE_STRING, -1);
+
+  set_string_t cb = (set_string_t) v->set_fn;
+  return cb(v, newval);
+}
+
 int owl_variable_set_int(owl_variable *v, int newval)
 {
-  if (v->type != OWL_VARIABLE_INT && v->type != OWL_VARIABLE_BOOL) return -1;
-  return v->set_fn(v, &newval);
-}
- 
+  g_return_val_if_fail(v->type == OWL_VARIABLE_INT, -1);
+
+  set_int_t cb = (set_int_t) v->set_fn;
+  return cb(v, newval);
+}
+
+int owl_variable_set_bool(owl_variable *v, bool newval) {
+  g_return_val_if_fail(v->type == OWL_VARIABLE_BOOL, -1);
+
+  set_bool_t cb = (set_bool_t) v->set_fn;
+  return cb(v, newval);
+}
+
 int owl_variable_set_bool_on(owl_variable *v)
 {
   if (v->type != OWL_VARIABLE_BOOL) return -1;
-  return owl_variable_set_int(v, true);
+  return owl_variable_set_bool(v, true);
 }
 
@@ -832,19 +873,28 @@
 {
   if (v->type != OWL_VARIABLE_BOOL) return -1;
-  return owl_variable_set_int(v, false);
+  return owl_variable_set_bool(v, false);
 }
 
 CALLER_OWN char *owl_variable_get_tostring(const owl_variable *v)
 {
-  return v->get_tostring_fn(v, v->get_fn(v));
-}
-
-CALLER_OWN char *owl_variable_get_default_tostring(const owl_variable *v)
-{
-  if (v->type == OWL_VARIABLE_INT || v->type == OWL_VARIABLE_BOOL) {
-    return v->get_tostring_fn(v, &(v->ival_default));
-  } else {
-    return v->get_tostring_fn(v, v->pval_default);
-  }
+  GValue instance = G_VALUE_INIT;
+  GValue tostring_box = G_VALUE_INIT;
+  char *ret = NULL;
+
+  g_value_init(&instance, G_TYPE_POINTER);
+  g_value_set_pointer(&instance, NULL);
+  g_value_init(&tostring_box, G_TYPE_STRING);
+  g_closure_invoke(v->get_tostring_fn, &tostring_box, 1, &instance, NULL);
+
+  ret = g_value_dup_string(&tostring_box);
+
+  g_value_unset(&tostring_box);
+  g_value_unset(&instance);
+  return ret;
+}
+
+const char *owl_variable_get_default_tostring(const owl_variable *v)
+{
+  return v->default_str;
 }
 
@@ -854,58 +904,35 @@
 }
 
-/* returns a reference */
-const void *owl_variable_get(const owl_variable *v)
-{
-  return v->get_fn(v);
-}
-
 const char *owl_variable_get_string(const owl_variable *v)
 {
-  if (owl_variable_get_type(v) != OWL_VARIABLE_STRING) {
-    owl_function_error("Variable '%s' is not a string.", owl_variable_get_name(v));
-    return NULL;
-  }
-  return owl_variable_get(v);
-}
-
-/* returns a reference */
-const void *owl_variable_get_other(const owl_variable *v)
-{
-  if (owl_variable_get_type(v) != OWL_VARIABLE_OTHER) {
-    owl_function_error("Variable '%s' is not type other.", owl_variable_get_name(v));
-    return NULL;
-  }
-  return owl_variable_get(v);
+  g_return_val_if_fail(v->type == OWL_VARIABLE_STRING, NULL);
+
+  get_string_t cb = (get_string_t) v->get_fn;
+  return cb(v);
 }
 
 int owl_variable_get_int(const owl_variable *v)
 {
-  if (owl_variable_get_type(v) != OWL_VARIABLE_INT) {
-    owl_function_error("Variable '%s' is an int.", owl_variable_get_name(v));
-    return -1;
-  }
-  const int *pi = owl_variable_get(v);
-  if (!pi) return -1;
-  return *pi;
-}
-
-int owl_variable_get_bool(const owl_variable *v)
-{
-  if (owl_variable_get_type(v) != OWL_VARIABLE_BOOL) {
-    owl_function_error("Variable '%s' is a boolean.", owl_variable_get_name(v));
-    return -1;
-  }
-  const int *pi = owl_variable_get(v);
-  if (!pi) return -1;
-  return *pi;
+  g_return_val_if_fail(v->type == OWL_VARIABLE_INT, 0);
+
+  get_int_t cb = (get_int_t) v->get_fn;
+  return cb(v);
+}
+
+bool owl_variable_get_bool(const owl_variable *v)
+{
+  g_return_val_if_fail(v->type == OWL_VARIABLE_BOOL, FALSE);
+
+  get_bool_t cb = (get_bool_t) v->get_fn;
+  return cb(v);
 }
 
 void owl_variable_describe(const owl_variable *v, owl_fmtext *fm)
 {
-  char *tostring = owl_variable_get_default_tostring(v);
+  const char *default_str = owl_variable_get_default_tostring(v);
   char *default_buf;
 
-  if (tostring)
-    default_buf = g_strdup_printf("'%s'", tostring);
+  if (default_str)
+    default_buf = g_strdup_printf("'%s'", default_str);
   else
     default_buf = g_strdup("<null>");
@@ -914,9 +941,9 @@
                             owl_variable_get_summary(v), default_buf);
   g_free(default_buf);
-  g_free(tostring);
 }
 
 void owl_variable_get_help(const owl_variable *v, owl_fmtext *fm) {
   char *tostring;
+  const char *default_str;
 
   owl_fmtext_append_bold(fm, "OWL VARIABLE\n\n");
@@ -933,8 +960,7 @@
   owl_fmtext_append_normal(fm, "\n\n");
 
-
-  tostring = owl_variable_get_default_tostring(v);
+  default_str = owl_variable_get_default_tostring(v);
   owl_fmtext_append_normal(fm, "Default:        ");
-  owl_fmtext_append_normal(fm, (tostring ? tostring : "<null>"));
+  owl_fmtext_append_normal(fm, (default_str ? default_str : "<null>"));
   owl_fmtext_append_normal(fm, "\n\n");
 
@@ -948,5 +974,4 @@
     owl_fmtext_append_normal(fm, "\n\n");
   }
-  g_free(tostring);
 }
 
@@ -960,44 +985,49 @@
 /* default common functions */
 
-const void *owl_variable_get_default(const owl_variable *v) {
-  return v->val;
-}
-
-void owl_variable_delete_default(owl_variable *v)
-{
-  g_free(v->val);
+const char *owl_variable_string_get_default(const owl_variable *v) {
+  return g_value_get_string(&(v->val));
+}
+
+int owl_variable_int_get_default(const owl_variable *v) {
+  return g_value_get_int(&(v->val));
+}
+
+bool owl_variable_bool_get_default(const owl_variable *v) {
+  return g_value_get_boolean(&(v->val));
 }
 
 /* default functions for booleans */
 
-int owl_variable_bool_validate_default(const owl_variable *v, const void *newval) {
-  if (newval == NULL) return(0);
-  else if (*(const int*)newval==1 || *(const int*)newval==0) return(1);
-  else return (0);
-}
-
-int owl_variable_bool_set_default(owl_variable *v, const void *newval) {
-  if (v->validate_fn) {
-    if (!v->validate_fn(v, newval)) return(-1);
+int owl_variable_bool_validate_default(const owl_variable *v, bool newval) {
+  return (newval == 1) || (newval == 0);
+}
+
+int owl_variable_bool_set_default(owl_variable *v, bool newval) {
+  if (!((validate_bool_t)v->validate_fn)(v, newval))
+    return -1;
+
+  g_value_set_boolean(&(v->val), newval);
+  return(0);
+}
+
+int owl_variable_bool_set_fromstring_default(owl_variable *v, const char *newval, void *dummy) {
+  bool i;
+  if (!strcmp(newval, "on")) {
+    i = true;
+  } else if (!strcmp(newval, "off")) {
+    i = false;
+  } else {
+    return(-1);
   }
-  *(int*)v->val = *(const int*)newval;
-  return(0);
-}
-
-int owl_variable_bool_set_fromstring_default(owl_variable *v, const 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));
-}
-
-CALLER_OWN char *owl_variable_bool_get_tostring_default(const owl_variable *v, const void *val)
-{
-  if (val == NULL) {
-    return NULL;
-  } else if (*(const int*)val == 0) {
+
+  return owl_variable_set_bool(v, i);
+}
+
+CALLER_OWN char *owl_variable_bool_get_tostring_default(const owl_variable *v, void *dummy)
+{
+  bool val = owl_variable_get_bool(v);
+  if (val == 0) {
     return g_strdup("off");
-  } else if (*(const int*)val == 1) {
+  } else if (val == 1) {
     return g_strdup("on");
   } else {
@@ -1008,44 +1038,39 @@
 /* default functions for integers */
 
-int owl_variable_int_validate_default(const owl_variable *v, const void *newval) {
-  if (newval == NULL) return(0);
-  else return (1);
-}
-
-int owl_variable_int_set_default(owl_variable *v, const void *newval) {
-  if (v->validate_fn) {
-    if (!v->validate_fn(v, newval)) return(-1);
-  }
-  *(int*)v->val = *(const int*)newval;
+int owl_variable_int_validate_default(const owl_variable *v, int newval)
+{
+  return (1);
+}
+
+int owl_variable_int_set_default(owl_variable *v, int newval) {
+  if (!((validate_int_t)v->validate_fn)(v, newval))
+    return -1;
+
+  g_value_set_int(&(v->val), newval);
   return(0);
 }
 
-int owl_variable_int_set_fromstring_default(owl_variable *v, const char *newval) {
+int owl_variable_int_set_fromstring_default(owl_variable *v, const char *newval, void *dummy) {
   int i;
   char *ep;
   i = strtol(newval, &ep, 10);
   if (*ep || ep==newval) return(-1);
-  return (v->set_fn(v, &i));
-}
-
-CALLER_OWN char *owl_variable_int_get_tostring_default(const owl_variable *v, const void *val)
-{
-  if (val == NULL) {
-    return NULL;
-  } else {
-    return g_strdup_printf("%d", *(const int*)val);
-  } 
+  return owl_variable_set_int(v, i);
+}
+
+CALLER_OWN char *owl_variable_int_get_tostring_default(const owl_variable *v, void *dummy)
+{
+  return g_strdup_printf("%d", owl_variable_get_int(v));
 }
 
 /* default functions for enums (a variant of integers) */
 
-int owl_variable_enum_validate(const owl_variable *v, const void *newval) {  
+int owl_variable_enum_validate(const owl_variable *v, int newval) {
   char **enums;
   int nenums, val;
-  if (newval == NULL) return(0);
   enums = g_strsplit_set(v->validsettings, ",", 0);
   nenums = g_strv_length(enums);
   g_strfreev(enums);
-  val = *(const int*)newval;
+  val = newval;
   if (val < 0 || val >= nenums) {
     return(0);
@@ -1054,5 +1079,5 @@
 }
 
-int owl_variable_enum_set_fromstring(owl_variable *v, const char *newval) {
+int owl_variable_enum_set_fromstring(owl_variable *v, const char *newval, void *dummy) {
   char **enums;
   int i, val=-1;
@@ -1066,8 +1091,8 @@
   g_strfreev(enums);
   if (val == -1) return(-1);
-  return (v->set_fn(v, &val));
-}
-
-CALLER_OWN char *owl_variable_enum_get_tostring(const owl_variable *v, const void *val)
+  return owl_variable_set_int(v, val);
+}
+
+CALLER_OWN char *owl_variable_enum_get_tostring(const owl_variable *v, void *dummy)
 {
   char **enums;
@@ -1075,10 +1100,7 @@
   char *tostring;
 
-  if (val == NULL) {
-    return NULL;
-  }
   enums = g_strsplit_set(v->validsettings, ",", 0);
   nenums = g_strv_length(enums);
-  i = *(const int*)val;
+  i = owl_variable_get_int(v);
   if (i<0 || i>=nenums) {
     g_strfreev(enums);
@@ -1092,25 +1114,25 @@
 /* default functions for stringeans */
 
-int owl_variable_string_validate_default(const struct _owl_variable *v, const void *newval) {
+int owl_variable_string_validate_default(const owl_variable *v, const char *newval) {
   if (newval == NULL) return(0);
   else return (1);
 }
 
-int owl_variable_string_set_default(owl_variable *v, const void *newval) {
-  if (v->validate_fn) {
-    if (!v->validate_fn(v, newval)) return(-1);
-  }
-  g_free(v->val);
-  v->val = g_strdup(newval);
+int owl_variable_string_set_default(owl_variable *v, const char *newval) {
+  if (!((validate_string_t)v->validate_fn)(v, newval))
+    return -1;
+
+  g_value_set_string(&(v->val), newval);
   return(0);
 }
 
-int owl_variable_string_set_fromstring_default(owl_variable *v, const char *newval) {
-  return (v->set_fn(v, newval));
-}
-
-CALLER_OWN char *owl_variable_string_get_tostring_default(const owl_variable *v, const void *val)
-{
-  return g_strdup((const char*)val);
-}
-
+int owl_variable_string_set_fromstring_default(owl_variable *v, const char *newval, void *dummy) 
+{
+  return owl_variable_set_string(v, newval);
+}
+
+CALLER_OWN char *owl_variable_string_get_tostring_default(const owl_variable *v, void *dummy)
+{
+  return g_strdup(owl_variable_get_string(v));
+}
+
