+ return $this->{addinfo};
+}
+
+sub diagset {
+ my $this = shift();
+ return $this->{diagset};
+}
+
+sub render {
+ my $this = shift();
+ my $res = "ZOOM error " . $this->code() . ' "' . $this->message() . '"';
+ $res .= ' (addinfo: "' . $this->addinfo() . '")' if $this->addinfo();
+ return $res;
+}
+
+# This means that untrapped exceptions render nicely.
+use overload '""' => \&render;
+
+# ----------------------------------------------------------------------------
+
+package ZOOM::Options;
+
+sub new {
+ my $class = shift();
+ my($p1, $p2) = @_;
+
+ my $opts;
+ if (@_ == 0) {
+ $opts = Net::Z3950::ZOOM::options_create();
+ } elsif (@_ == 1) {
+ $opts = Net::Z3950::ZOOM::options_create_with_parent($p1->_opts());
+ } elsif (@_ == 2) {
+ $opts = Net::Z3950::ZOOM::options_create_with_parent2($p1->_opts(),
+ $p2->_opts());
+ } else {
+ die "can't make $class object with more than 2 parents";
+ }
+
+ return bless {
+ _opts => $opts,
+ }, $class;
+}
+
+# PRIVATE to this class and ZOOM::Connection::create() and
+# ZOOM::Connection::package()
+#
+sub _opts {
+ my $this = shift();
+
+ my $_opts = $this->{_opts};
+ die "{_opts} undefined: has this Options block been destroy()ed?"
+ if !defined $_opts;
+
+ return $_opts;
+}
+
+sub option {
+ my $this = shift();
+ my($key, $value) = @_;
+
+ my $oldval = Net::Z3950::ZOOM::options_get($this->_opts(), $key);
+ Net::Z3950::ZOOM::options_set($this->_opts(), $key, $value)
+ if defined $value;
+
+ return $oldval;
+}
+
+sub option_binary {
+ my $this = shift();
+ my($key, $value) = @_;
+
+ my $dummylen = 0;
+ my $oldval = Net::Z3950::ZOOM::options_getl($this->_opts(),
+ $key, $dummylen);
+ Net::Z3950::ZOOM::options_setl($this->_opts(), $key,
+ $value, length($value))
+ if defined $value;
+
+ return $oldval;
+}
+
+# This is a bit stupid, since the scalar values that Perl returns from
+# option() can be used as a boolean; but it's just possible that some
+# applications will rely on ZOOM_options_get_bool()'s idiosyncratic
+# interpretation of what constitutes truth.
+#
+sub bool {
+ my $this = shift();
+ my($key, $default) = @_;
+
+ return Net::Z3950::ZOOM::options_get_bool($this->_opts(), $key, $default);
+}
+
+# .. and the next two are even more stupid
+sub int {
+ my $this = shift();
+ my($key, $default) = @_;
+
+ return Net::Z3950::ZOOM::options_get_int($this->_opts(), $key, $default);
+}
+
+sub set_int {
+ my $this = shift();
+ my($key, $value) = @_;
+
+ Net::Z3950::ZOOM::options_set_int($this->_opts(), $key, $value);
+}
+
+# ### Feel guilty. Feel very, very guilty. I've not been able to
+# get the callback memory-management right in "ZOOM.xs", with
+# the result that the values of $function and $udata passed into
+# this function, which are on the stack, have sometimes been
+# freed by the time they're used by __ZOOM_option_callback(),
+# with hilarious results. To avoid this, I copy the values into
+# module-scoped globals, and pass _those_ into the extension
+# function. To avoid overwriting those globals by subsequent
+# calls, I keep all the old ones, pushed onto the @_function and
+# @_udata arrays, which means that THIS FUNCTION LEAKS MEMORY
+# LIKE IT'S GOING OUT OF FASHION. Not nice. One day, I should
+# fix this, but for now there's more important fish to fry.
+#
+my(@_function, @_udata);
+sub set_callback {
+ my $o1 = shift();
+ my($function, $udata) = @_;
+
+ push @_function, $function;
+ push @_udata, $udata;
+ Net::Z3950::ZOOM::options_set_callback($o1->_opts(),
+ $_function[-1], $_udata[-1]);
+}
+
+sub destroy {
+ my $this = shift();
+
+ Net::Z3950::ZOOM::options_destroy($this->_opts());
+ $this->{_opts} = undef;