Death: UCD-SNMP
Birth: NET-SNMP
  (new agent API merged to the main branch)


git-svn-id: file:///home/hardaker/lib/sf-bkups/net-snmp-convert-svnrepo/trunk@5901 06827809-a52a-0410-b366-d66718629ded
diff --git a/agent/mibgroup/agentx/master_request.c b/agent/mibgroup/agentx/master_request.c
index f7c97a0..f46f9ec 100644
--- a/agent/mibgroup/agentx/master_request.c
+++ b/agent/mibgroup/agentx/master_request.c
@@ -55,12 +55,13 @@
 
 #include "protocol.h"
 #include "client.h"
-#include "master.h"
-#include "master_admin.h"
 #include "snmp_agent.h"
+#include "agent_handler.h"
 #include "snmp_vars.h"
 #include "var_struct.h"
 #include "mibII/sysORTable.h"
+#include "master.h"
+#include "master_admin.h"
 
 #define VARLIST_ITERATION	10
 
diff --git a/agent/mibgroup/mibII/icmp.c b/agent/mibgroup/mibII/icmp.c
index d5ed42e..7057f3a 100644
--- a/agent/mibgroup/mibII/icmp.c
+++ b/agent/mibgroup/mibII/icmp.c
@@ -63,10 +63,6 @@
 #if HAVE_WINSOCK_H
 #include <winsock.h>
 #endif
-#if HAVE_DMALLOC_H
-#include <dmalloc.h>
-#endif
-
 #include "tools.h"
 #ifdef solaris2
 #include "kernel_sunos5.h"
@@ -91,6 +87,10 @@
 #include "icmp.h"
 #include "sysORTable.h"
 
+#if HAVE_DMALLOC_H
+#include <dmalloc.h>
+#endif
+
 #ifndef MIB_STATS_CACHE_TIMEOUT
 #define MIB_STATS_CACHE_TIMEOUT	5
 #endif
diff --git a/agent/mibgroup/mibII/tcpTable.c b/agent/mibgroup/mibII/tcpTable.c
index cf684fd..0e25bdf 100644
--- a/agent/mibgroup/mibII/tcpTable.c
+++ b/agent/mibgroup/mibII/tcpTable.c
@@ -115,10 +115,6 @@
 #include <sys/tcpipstats.h>
 #endif
 
-#if HAVE_DMALLOC_H
-#include <dmalloc.h>
-#endif
-
 #include "auto_nlist.h"
 #include "mibincl.h"
 
@@ -130,6 +126,10 @@
 #include "tcp.h"
 #include "tcpTable.h"
 
+#if HAVE_DMALLOC_H
+#include <dmalloc.h>
+#endif
+
 	/*********************
 	 *
 	 *  Kernel & interface information,
diff --git a/agent/mibgroup/mibII/udpTable.c b/agent/mibgroup/mibII/udpTable.c
index 2805d80..6cf48e4 100644
--- a/agent/mibgroup/mibII/udpTable.c
+++ b/agent/mibgroup/mibII/udpTable.c
@@ -81,11 +81,6 @@
 #include <inet/mib2.h>
 #endif
 
-#if HAVE_DMALLOC_H
-#include <dmalloc.h>
-#endif
-
-
 #ifdef solaris2
 #include "kernel_sunos5.h"
 #else
@@ -115,6 +110,10 @@
 #include <sys/sysctl.h>
 #endif
 
+#if HAVE_DMALLOC_H
+#include <dmalloc.h>
+#endif
+
 	/*********************
 	 *
 	 *  Kernel & interface information,
diff --git a/agent/mibgroup/ucd-snmp/file.c b/agent/mibgroup/ucd-snmp/file.c
index 0da7d2b..afe1cec 100644
--- a/agent/mibgroup/ucd-snmp/file.c
+++ b/agent/mibgroup/ucd-snmp/file.c
@@ -23,9 +23,6 @@
 #include <string.h>
 #endif
 
-#if HAVE_DMALLOC_H
-#include <dmalloc.h>
-#endif
 #if HAVE_STRING_H
 #include <string.h>
 #endif
diff --git a/local/mib2c b/local/mib2c
index 939bced..3f124ee 100755
--- a/local/mib2c
+++ b/local/mib2c
@@ -25,6 +25,7 @@
 
 $configfile="mib2c.conf";
 $debug=0;
+$quiet=0;
 $nostats = 0;
 
 sub usage {
@@ -35,6 +36,7 @@
     print "  mibNode\tThe name of the top level mib node you want to\n\t\tgenerate code for.  By default, the code will be stored in\n\t\tmibNode.c and mibNode.h (use the -f flag to change this)\n\n";
     print "  -d\t\tdebugging output (dont do it.  trust me.)\n\n";
     print "  -s\t\tDon't display statistics at the end\n\n";
+    print "  -S VAR=VAL\tSet $VAR variable to $VAL\n";
     1;
 }	
 
@@ -42,273 +44,248 @@
     $_ = shift;
     $configfile = shift if (/^-c/);
     $debug = 1 if (/^-d/);
+    if (/-S/) {
+	my $expr = shift;
+	my ($var, $val) = ($expr =~ /([^=]*)=(.*)/);
+	die "no variable specified for -S flag." if (!$var);
+	$vars{$var} = $val;
+    }
+    $quiet = 1 if (/^-q/);
     $nostats = 1 if (/^-s/);
     usage && exit(1) if (/^-h/);
     $outputName = shift if (/^-f/);
     $oid = $_ if (/^[^-]/);
 }
  
-read_config($configfile);
 #
 # internal conversion tables
 #
 
-%accessToUCD = qw(ReadOnly RONLY ReadWrite RWRITE 
-		  WriteOnly RWRITE Create RWRITE);
+%accessToIsWritable = qw(ReadOnly 0 ReadWrite 1 
+			 WriteOnly 1 Create 1);
+%perltoctypes = qw(OCTETSTR   ASN_OCTET_STR
+		   INTEGER    ASN_INTEGER
+		   INTEGER32  ASN_INTEGER
+		   UNSIGNED32 ASN_UNSIGNED
+		   OBJECTID   ASN_OBJECT_ID
+		   COUNTER64  ASN_COUNTER64
+		   COUNTER    ASN_COUNTER
+		   NETADDR    ASN_COUNTER
+		   UINTEGER   ASN_UINTEGER
+		   IPADDR     ASN_IPADDRESS
+		   BITS       ASN_IPADDRESS
+		   TICKS      ASN_TIMETICKS
+		   GAUGE      ASN_GAUGE
+		   OPAQUE     ASN_OPAQUE);
 
-#  The lengths of the defined 'variableN' structures
-@varLengths = (2,4,7,8,13);
 
-if (!defined($oid)) {
-    print STDERR "You didn\'t specify a mib oid to convert!\n";
-    usage();
-    exit(1);
+my $mibnode = $SNMP::MIB{$oid};
+die "you didn't give me a valid OID to start with" if (!$mibnode);
+
+# setup
+$outputName = $mibnode->{'label'} if (!defined($outputName));
+$vars{'name'} = $outputName;
+$vars{'oid'} = $oid;
+
+# loop through mib nodes, remembering stuff.
+setup_data($mibnode);
+
+# process .conf file
+$fh = new IO::File;
+$fh->open("$configfile");
+process();
+$fh->close;
+
+foreach $i (keys(%written)) {
+    next if ($i eq "-");
+    print STDERR "running indent on $i\n" if (!$quiet);
+    system("indent -orig -nbc -bap -nut $i");
 }
 
-$mib = $SNMP::MIB{$oid};
-$_ = $commaoid = $fulloid = $mib->{'objectID'};
-if (!defined ($fulloid)) {
-    print STDERR "Couldn\'t find mib reference: $oid\n";
-    exit(1);
+sub tocommas {
+    my $oid = $_[0];
+    $oid =~ s/\./,/g;
+    $oid =~ s/^\s*,//;
+    return $oid;
 }
-s/[^.]//g;
-$commaoid =~ s/\./,/g;
-$commaoid =~ s/^,//g;
 
-$outputName = $mib->{'label'} if (!defined($outputName));
-$OUTPUTNAME = uc($outputName);
-$vroutine="$outputName";
-print "outputting to $outputName.c and $outputName.h ...\n";
-
-#============================================
-#
-#   Walk the MIB tree, and construct strings
-#     holding the various fragments of code needed.
-#
-#   'loadMib' returns the length of the longest OID suffix
-#     encountered.
-#
-#   The variables constructed and used are:
-#
-#   (in the header file)
-#	functionInfo :	A list of definitions for the table-handling functions,
-#			and routines for SETtable variables.
-#			(The main scalar handling routine is handled implicitly)
-#
-#   (in the code file)
-#	structinfo :	The contents of the variableN structure listing
-#			the variables handled, including type, access level,
-#			OID suffix and 'magic number'
-#
-#	caseStatements:	A hash array (indexed by variable routine name)
-#			containing the body of the switch statement
-#			used for returning the appropriate values.
-#			At a minimum, this consists of the various 'case' labels
-#			If full type information is available (from mib2c.conf)
-#			then this will also include a default initialiser,
-#			and setting of a 'write_method' (if appropriate).
-#
-#	writeFuncs:	A list of function skeletons for setting variables
-#			(for variables with suitable access levels).
-#			Note that this list will not include functions
-#			for variables which don't provide type information
-#			in the mib2c.conf file (even if such variables are
-#			defined as writeable in the variableN structure).
-#
-#============================================
-$count = 0;
-$depth = loadMib($mib,0)-1;
-
-#  Determine which 'variableN' structure is needed
-for($varlen = 0; $varlen <= $#varLengths; $varlen++) {
-  last if ($depth <= $varLengths[$varlen]);
+sub oidlength {
+    return scalar ($_[0] =~ /\./);
 }
-$varlen = $varLengths[$varlen];
 
-#============================================
+# replaces $VAR type expressions and $VAR.subcomponent expressions
+# with data from the mib tree and loop variables.
+# possible uses:
 #
-#	Table-handling routines.
+#   $var               -- as defined by loops, etc.
+#   ${var}otherstuff   -- appending text to variable contents
+#   $var.uc            -- all upper case version of $var
 #
-#============================================
-foreach $vtable (@table_list) {
-    foreach $ptable (@processtable) {
-	$variables{$ptable}{'processed'} .= 
-	    (eval "\"$variables{$ptable}{'code'}\"") . "\n\n";
+# Mib components, $var must first expand to a mib node name:
+#
+#   $var.objectID      -- dotted full OID
+#   $var.commaoid      -- comma separated OID for array init
+#   $var.subid         -- last number component of oid
+#   $var.oidlength     -- length of the oid
+#   $var.type          -- node's ASN_XXX type
+#   $var.settable      -- 1 if it's writable, 0 if not
+#   $var.access        -- node's access type
+#   $var.status        -- node's status
+#   $var.syntax        -- node's syntax
+sub process_vars {
+    my $it = shift;
+
+    # mib substitutions ($var.type -> $mibnode->{'type'})
+    $it =~ s/\$(\w+)\.(uc)/uc($vars{$1})/eg; # make something uppercase
+    $it =~ s/\$(\w+)\.(commaoid)/tocommas($SNMP::MIB{$vars{$1}}{objectID})/eg; # lowercase
+    $it =~ s/\$(\w+)\.(oidlength)/oidlength($SNMP::MIB{$vars{$1}}{objectID})/eg; # lowercase
+    $it =~ s/\$(\w+)\.(perltype)/$SNMP::MIB{$vars{$1}}{type}/g; # lowercase
+    $it =~ s/\$(\w+)\.(type)/$perltoctypes{$SNMP::MIB{$vars{$1}}{$2}}/g; # lowercase
+    $it =~ s/\$(\w+)\.(subid)/$SNMP::MIB{$vars{$1}}{subID}/g; # lowercase
+    $it =~ s/\$(\w+)\.(settable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(ReadWrite|Create|Writeonly)\/)?1:0)/eg; # lowercase
+    $it =~ s/\$(\w+)\.(objectID|label|subID|access|status|syntax)/$SNMP::MIB{$vars{$1}}{$2}/g;
+
+    # normal variable substitions
+    $it =~ s/\$\{(\w+)\}/$vars{$1}/g;
+    $it =~ s/\$(\w+)/$vars{$1}/g;
+    return $it;
+}
+
+# process various types of statements
+#
+# which include:
+#   @open FILE@
+#     writes generated output to FILE
+#   @foreach $VAR table@
+#     repeat iterate over code until @end@ setting $VAR to all known tables
+#   @foreach $VAR column@
+#     repeat iterate over code until @end@ setting $VAR to all known
+#     columns within a given table.  Obviously this must be called
+#     within a foreach-table clause.
+#   @foreach $VAR index@
+#     repeat iterate over code until @end@ setting $VAR to all known
+#     indexes within a given table.  Obviously this must be called
+#     within a foreach-table clause.
+#   @eval $VAR = expression@
+#     evaluates expression and assigns the results to $VAR
+#   @perleval STUFF@
+#     evaluates STUFF directly in perl.  Note that all mib2c variables
+#     interpereted within .conf files are in $vars{NAME}.
+#   @skip@
+#     skips everything till the appropriately matched @end@
+#   @if expression@
+#      evaluates expression, and if expression is true processes
+#      contained part until appropriate @end@ is reached.
+sub skippart {
+    my $endcount = 1;
+    while(<$fh>) {
+	if (/\@end\@/) {
+	    return if ($endcount == 1);
+	    $endcount--;
+	}
+	if (/\@else\@/) {
+	    return if ($endcount == 1);
+	}
+	if (/\@(foreach|if)/) {
+	    $endcount++;
+	}
     }
-    $var_routines .= 
-	(eval "\"$variables{'code-var_table'}{'code'}\"") . "\n\n";
 }
 
-#============================================
-#
-#   Output the header file
-#
-#============================================
-open(DOTH,">$outputName.h");
-print DOTH (eval "\"$variables{'code-dot-h'}{'code'}\"") . "\n";
-close(DOTH);
 
-#============================================
-#
-#   Output the code file:
-#	Initialisation and main variable routine.
-#
-#============================================
-
-open(DOTC,">$outputName.c");
-print DOTC (eval "\"$variables{'code-main-part'}{'code'}\"") . "\n\n";;
-close(DOTC);
-
-#============================================
-#
-# Everyone loves statistics.
-#
-#============================================
-print "  depth: $depth\n";
-print "  Number of Lines Created:\n";
-system("wc -l $outputName.c $outputName.h");
-print "Done.\n\n";
-print "NOTE: the code that has been created for you is merely a starting template.\n";
-print "      You will have to modify it in order to make it work properly.\n";
-
-#============================================
-#
-#  loadMib:
-#	Recursive routine to walk the mib,
-#	and construct the various code fragment strings.
-#
-#============================================
-sub loadMib {
-    my $mib = shift;
-    my $i;
-    my $depth = shift;
-    $depth = $depth + 1;
-    my $name = $mib->{'label'};
-    my $NAME = uc($name);
-    print "doing $mib->{label} : $mib->{objectID}\n" if $debug;
-    if (defined($mib->{'access'}) && 
-	$mib->{'access'} =~ /ReadOnly|ReadWrite|WriteOnly|Create|NoAccess/) {
-	$count = $count + 1;
-	$subid = $mib->{'objectID'};
-	$subid =~ s/$fulloid\.//;
-	$subid =~ s/\./,/g;
-	if (!defined($variables{$mib->{'type'}})  && !defined($mib->{'indexes'})) {
-	    print STDERR "unknown type:  $mib->{type} for $mib->{label} $mib->{'access'}\n";
-	    print STDERR "unknown type:  no information generated for $mib->{label}\n";
+sub process {
+    while(<$fh>) {
+	if (/^\#\#/) {
+	    # noop, it's a comment
+	} elsif (/\@open\s+([^\@]+)\@/) {
+	    my $spec = process_vars($1);
+	    $out->close() if ($out);
+	    $out = new IO::File;
+	    $out->open(">$spec") || die "failed to open $spec";
+	    print STDERR "writing to $spec\n" if (!$quiet);
+	    $written{$spec} = '1';
+	} elsif (/\@end\@/) {
+	    return;
+	} elsif (/\@if\s+([^@]*)\@/) {
+	    if (eval(process_vars($1))) {
+		process();
+	    } else {
+		skippart();
+	    }
+	} elsif (/\@eval\s+\$(\w+)\s*=\s*([^\@]*)/) {
+	    my ($v, $e) = ($1, $2);
+	    my $e = process_vars($e);
+	    $vars{$v} = eval($e);
+	} elsif (/\@perleval\s*(.*)\@/) {
+	    eval($1);
+	} elsif (/\@skip\@/) {
+	    skippart();
+	} elsif (/\@\s*foreach\s+\$([^\@]+)\s+tables*\s*\@/) {
+	    my $var = $1;
+	    my $startpos = tell(C);
+	    my $table;
+	    foreach $table (keys %tables) {
+		seek(C, $startpos, 0); # go to top of section.
+		my $oldvar = $vars{$var};
+		$vars{$var} = $table;
+		my $oldtable = $currenttable;
+		$currenttable = $table;
+		process();
+		$vars{$var} = $oldvar;
+		$currenttable = $oldtable;
+	    }
+	} elsif (/\@\s*foreach\s+\$([^\@]+)\s+(column|index)\s*\@/) {
+	    my ($var, $type) = ($1, $2);
+	    my $startpos = $fh->tell();
+	    my $column;
+	    foreach $column (@{$tables{$currenttable}{$type}}) {
+#		print "looping on $var for $type -> $column\n";
+		$fh->seek($startpos, 0); # go to top of section.
+		my $oldvar = $vars{$var};
+		$vars{$var} = $column;
+		my $oldcolumn = $currentcolumn;
+		$currentcolumn = $column;
+		process();
+		$vars{$var} = $oldvar;
+		$currentcolumn = $oldcolumn;
+	    }
 	} else {
-	    foreach $i (@process) {
-		next if (defined($variables{$i}{'skipif'}) &&
-			 eval $variables{$i}{'skipif'});
-		my $result = (eval "\"$variables{$i}{'code'}\"");
-		$variables{$i}{'processed'} .= "$result\n";
-		$variables{$vroutine}{$i}{'processed'} .= "$result\n";
-	    }
-	}
-	if (defined($mib->{'indexes'})) {
-	    print "indexes: ", join(", ",@{$mib->{'indexes'}}),"\n" if ($debug);
-	    $variables{$vroutine}{'indexes'} = $mib->{'indexes'};
-	    foreach $i (@{$mib->{'indexes'}}) {
-		$variables{$vroutine}{$i}{'isanindex'} = 1;
-	    }
+	    die "no output file specified" if (!$out);
+	    print $out process_vars($_);
 	}
     }
-    my $children = $$mib{'children'}; 
-    my $i;
-    my $newdepth = $depth;
-    foreach $i (sort {$a->{subID} <=> $b->{subID}} @{$children}) {
-	if ( $name =~ /Table$/ ) {
-	    $vroutine="$name";
-	    push @table_list, $name;
-	    $newdepth = max(loadMib($i, $depth), $newdepth);
-	    $vroutine="$outputName";
+}
+
+sub setup_data {
+    my $mib = shift;
+    if ($mib->{label} =~ /Table$/) {
+	my $tablename = $mib->{label};
+	my $entry = $mib->{children};
+	my $columns = $entry->[0]{children};
+	foreach my $col (sort { $a->{'subID'} <=> $b->{'subID'} } @$columns) {
+	    # store by numeric key so we can sort them later
+	    push @{$tables{$tablename}{'column'}}, $col->{'label'};
 	}
-	else {
-	    $newdepth = max(loadMib($i, $depth), $newdepth);
+	foreach my $index (@{$entry->[0]{'indexes'}}) {
+	    my $node = $SNMP::MIB{$index} || 
+		die "can't find info about index $index in table $tablename\n";
+	    push @{$tables{$tablename}{'index'}}, $index;
+	}
+    } else {
+	my $children = $mib->{children};
+	my $i;
+	for($i = 0; $i <= $#$children; $i++) {
+	    setup_data($children->[$i]);
 	}
     }
-    return $newdepth;
+}
+
+sub min {
+    return $_[0] if ($_[0] < $_[1]);
+    return $_[1];
 }
 
 sub max {
-    my $x = shift;
-    my $y = shift;
-    return ($x > $y) ? $x : $y;
-}
-
-
-sub read_config() {
-    my $configfile = shift;
-    my ($type, $lasttoken);
-    my $fh = new FileHandle;
-    $configfile = "/usr/local/share/snmp/" . $configfile if (!(-f $configfile));
-    if ( $fh->open("<$configfile") ) {
-	while(<$fh>) {
-	    next if (/^\s*\#/ || /^\s*$/);
-	    if (/^\s*type:\s*(.*)/) {
-		if (defined($type) && defined($lasttoken) && 
-		    defined ($variables{$type}{$lasttoken})) {
-		    chomp($variables{$type}{$lasttoken});
-		}
-		$type = $1;
-		chomp($type);
-	    } elsif (/include:\s*(.*)/) {
-		read_config($1);
-	    } elsif (/process:\s*(.*)/) {
-		push (@process, $1);
-	    } elsif (/processtable:\s*(.*)/) {
-		push (@processtable, $1);
-	    } elsif (/delete:\s*(.*)/) {
-		delete($variables{$type}{$1});
-	    } elsif (/copy:\s*(.*)/) {
-		my $item;
-		my $arg = $1;
-		chomp($arg);
-		foreach $item (keys(%{$variables{$arg}})) {
-		    $variables{$type}{$item} = $variables{$arg}{$item};
-		}
-	    } else {
-		if (/\s*([^:]*):(\s*.*)/) {
-		    if (!defined($variables{$type}{$1})) {
-			if (defined($type) && defined($lasttoken) && 
-			    defined ($variables{$type}{$lasttoken})) {
-			    chomp($variables{$type}{$lasttoken});
-			}
-			$variables{$type}{$1} = $2;
-			$lasttoken = $1;
-			$variables{$type}{$1} =~ s/^\t+//;
-		    } else {
-			# duplicate entry:  tack it on.
-			my ($x, $y) = ($1, $2);
-			$y =~ s/^\t+//;
-			$variables{$type}{$x} .= "\n" . $y;
-		    }
-		} else {
-		    # continuation line, it started with spaces or a +
-		    s/^\s*//;
-		    s/^\+//; 
-		    $variables{$type}{$lasttoken} .= "\n" . $_;
-		    chomp($variables{$type}{$lasttoken});
-		}
-	    }
-	}
-	$fh->close();
-    } else {
-	warn "Config file ($configfile) not found.\n";
-    }
-}
-
-sub evalstr {
-    my $str = shift;
-#    if ($str !~ /^\"/) {
-	$str = "\"$str\"";  # surround by quotes.
-#    }
-    eval ($str);            # should return a string, with variables expanded
-}
-
-sub evalrstr {
-    my $rstr = shift;
-#    if ($str !~ /^\"/) {
-#	$$rstr = "\"" . $$rstr . "\"";  # surround by quotes.
-#    }
-    eval ("\"$$rstr\"");            # should return a string, with variables expanded
+    return $_[0] if ($_[0] > $_[1]);
+    return $_[1];
 }
diff --git a/local/mib2c.array-auto.conf b/local/mib2c.array-auto.conf
new file mode 100644
index 0000000..a05fad4
--- /dev/null
+++ b/local/mib2c.array-auto.conf
@@ -0,0 +1,146 @@
+## -*- c -*-
+######################################################################
+## Do the .h file
+######################################################################
+@open include/${name}.h@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *        $Id$
+ */
+#ifndef $name.uc_H
+#define $name.uc_H
+
+/** function declarations */
+void init_$name(void);
+@foreach $i table@
+
+#include "${i}_user.h"
+
+void initialize_table_$i(void);
+NodeHandler ${i}_handler;
+@end@
+@foreach $i table@
+
+/* column number definitions for table $i */
+@foreach $c column@
+#define COLUMN_$c.uc		$c.subid
+@end@
+@end@
+
+#endif /** $name.uc_H */
+######################################################################
+## Do the .c file
+######################################################################
+@open auto/${name}.c@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *       $Id$
+ */
+
+#ifdef IN_UCD_SNMP_SOURCE
+/** If we're compiling this file inside the ucd-snmp source tree */
+
+/** This should always be included first before anything else */
+#include <config.h>
+
+/** minimal include directives */
+#include "mibincl.h"
+#include "util_funcs.h"
+
+#else /** !IN_UCD_SNMP_SOURCE */
+
+#include <ucd-snmp/ucd-snmp-config.h>
+#include <ucd-snmp/ucd-snmp-includes.h>
+#include <ucd-snmp/ucd-snmp-agent-includes.h>
+
+#endif /** !IN_UCD_SNMP_SOURCE */
+
+#include "${name}.h"
+
+
+@foreach $i table@
+/* Initialize the $i table by defining it's contents and how it's structured */
+void
+initialize_table_$i(void)
+{
+    static table_array_callbacks cb;
+    static oid ${i}_oid[] = {$i.commaoid};
+    size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
+    table_registration_info *table_info;
+    handler_registration *my_handler;
+
+    /** create the table structure itself */
+    table_info = SNMP_MALLOC_TYPEDEF(table_registration_info);
+
+    /* if your table is read only, it's easiest to change the
+       HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */
+    my_handler = create_handler_registration("$i",
+                                             table_array_helper_handler,
+                                             ${i}_oid,
+                                             ${i}_oid_len,
+                                             HANDLER_CAN_RWRITE);
+            
+    if (!my_handler || !table_info) {
+        snmp_log(LOG_ERR, "malloc failed in "
+                 "initialize_table_${i}_handler\n");
+        return; /** mallocs failed */
+    }
+
+    /***************************************************
+     * Setting up the table's definition
+     */
+    @foreach $idx index@
+        /** index: $idx */
+        table_helper_add_index(table_info, $idx.type);
+    @end@
+
+    @eval $minv = 0xffffffff@
+    @eval $maxv = 0@
+    @foreach $c column@
+    @if ! $c.noaccess@
+    @eval $minv = min($minv, $c.subid)@
+    @end@
+    @eval $maxv = max($maxv, $c.subid)@
+    @end@
+    table_info->min_column = $minv;
+    table_info->max_column = $maxv;
+
+    /***************************************************
+     * registering the table with the master agent
+     */
+    cb.get_value = ${i}_get_value;
+
+#ifdef ${i}_SET_HANDLING
+#ifdef ${i}_ROW_CREATION
+    cb.create_row = ${i}_create_row;
+    cb.delete_row = ${i}_delete_row;
+#else
+    cb.create_row = cb.delete_row= NULL;
+#endif
+    cb.set_reserve1 = ${i}_set_reserve1;
+    cb.set_reserve2 = ${i}_set_reserve2;
+    cb.set_action = ${i}_set_action;
+    cb.set_commit = ${i}_set_commit;
+    cb.set_free = ${i}_set_free;
+    cb.set_undo = ${i}_set_undo;
+#else
+    cb.set_reserve1 = cb.set_reserve2 = cb.set_action = cb.set_commit = 
+        cb.set_free = cb.set_undo = NULL;
+#endif
+    DEBUGMSGTL(("initialize_table_$i",
+                "Registering table $i "
+                "as a table array\n"));
+    register_table_array(my_handler, table_info, &cb, 1);
+}
+@end@
+
+/** Initialzies the $name module */
+void
+init_$name(void)
+{
+    /** here we initialize all the tables we're planning on supporting */
+    @foreach $i table@
+        initialize_table_$i();
+    @end@
+}
+@end@
diff --git a/local/mib2c.array-user.conf b/local/mib2c.array-user.conf
new file mode 100644
index 0000000..5642fe7
--- /dev/null
+++ b/local/mib2c.array-user.conf
@@ -0,0 +1,1249 @@
+## -*- c -*-
+######################################################################
+## Do the .h file
+## @perleval $vars{shortname} =~ s/([A-Z])[a-z]+/$1/g@
+######################################################################
+@foreach $i table@
+@open ${i}.h@
+@eval $hack = "Id"
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *        $Id$
+ *
+ * $$hack:$
+ *
+ * Yes, there is lots of code here that you might not use. But it is much
+ * easier to remove code than to add it!
+ */
+#ifndef $i.uc_H
+#define $i.uc_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <net-snmp/oid_array.h>
+#include <net-snmp/table_array.h>
+
+    /**
+     * un-comment this line if this table has any external indexes.
+     * This warning, and the rest, may be deleted once you've written
+     * the appropriate sections of code.
+     */
+    /** #define ${i}_EXTERNAL_INDEX */
+
+typedef struct ${i}_context_s {
+    oid_array_header index; /** THIS MUST BE FIRST!!! */
+
+    /*************************************************************
+     * You can store data internally in this structure.
+     *
+     * TODO: You'll probably have to fix a few types here...
+     */
+#ifdef ${i}_EXTERNAL_INDEX
+#warning "TODO: add code for external index!"
+#endif
+    @foreach $c column@
+        /** $c.syntax = $c.type */
+        @eval $have_type = 0@
+        @if "$c.type" eq "ASN_OCTET_STR"@
+            @eval $have_type = 1@
+            @eval $o_len = "65535"@
+            @if "$c.syntax" eq "DisplayString"@
+                @eval $o_len = "255"@
+            @end@
+            @if "$c.syntax" eq "SnmpAdminString"@
+                @eval $o_len = "255"@
+            @end@
+            char $c[$o_len];
+            long ${c}_len;
+        @end@
+        @if "$c.type" eq "ASN_OBJECT_ID"@
+            @eval $have_type = 1@
+            oid $c[MAX_OID_LEN];
+            long ${c}_len;
+        @end@
+        @if "$c.type" eq "ASN_UNSIGNED"@
+            @eval $have_type = 1@
+            unsigned long $c;
+        @end@
+        @if "$c.type" eq "ASN_TIMETICKS"@
+            @eval $have_type = 1@
+            unsigned long $c;
+        @end@
+        @if "$c.type" eq "ASN_IPADDRESS"@
+            @eval $have_type = 1@
+            unsigned long $c;
+        @end@
+        @if "$c.type" eq "ASN_UINTEGER"@
+            @eval $have_type = 1@
+            unsigned long $c;
+        @end@
+        @if "$c.type" eq "ASN_COUNTER"@
+            @eval $have_type = 1@
+            unsigned long $c;
+        @end@
+        @if $have_type == 0@
+            long $c;
+        @end@
+
+    @end@
+
+    /*
+     * OR
+     *
+     * Keep a pointer to your data
+     */
+    void * data;
+
+    /*
+     *add anything else you want here
+     */
+
+} ${i}_context;
+
+/*************************************************************
+ * function declarations
+ */
+void init_$i(void);
+void initialize_table_$i(void);
+const ${i}_context * ${i}_get_by_idx(oid_array_header *);
+int ${i}_get_value(request_info *, oid_array_header *, table_request_info *);
+
+
+/*************************************************************
+ * oid declarations
+ */
+extern oid ${i}_oid[];
+extern size_t ${i}_oid_len;
+
+/*************************************************************
+ * column number definitions for table $i
+ */
+@foreach $c column@
+#define COLUMN_$c.uc $c.subid
+@if "$c.syntax" eq "RowStatus"@
+   @eval $rs_name = "$c"@
+@end@
+@if "$c.syntax" eq "StorageType"@
+   @eval $st_name = "$c"@
+@end@
+@end@
+
+/* comment out the following line if you dont handle SET-REQUEST for $i */
+#define ${i}_SET_HANDLING
+
+/* comment out the following line if you cant create new rows */
+#define ${i}_ROW_CREATION
+
+/* comment out the following line if you dont want the secondary
+ * index binary tree. */
+#define ${i}_TREE
+
+@if "$rs_name" ne ""@
+/* uncommend the following line if you allow modifications to an
+ * active row */
+/** define ${i}_CAN_MODIFY_ACTIVE_ROW */
+
+@end@
+#ifdef ${i}_SET_HANDLING
+void ${i}_set_reserve1( array_group * );
+void ${i}_set_reserve2( array_group * );
+void ${i}_set_action( array_group * );
+void ${i}_set_commit( array_group * );
+void ${i}_set_free( array_group * );
+void ${i}_set_undo( array_group * );
+#endif
+
+#ifdef ${i}_ROW_CREATION
+oid_array_header * ${i}_create_row( oid_array_header* );
+oid_array_header * ${i}_duplicate_row( oid_array_header* );
+oid_array_header * ${i}_delete_row( oid_array_header* );
+#endif
+
+#ifdef ${i}_TREE
+${i}_context * ${i}_get( const char *name, int len );
+#endif
+
+/**
+  And just for your enjoyment:
+  
+    @foreach $c column@
+name                 : $c
+uppercase            : $c.uc
+dotted full OID      : $c.objectID
+comma separated OID  : $c.commaoid
+last component of oid: $c.subid
+length of the oid    : $c.oidlength
+node's ASN_XXX type  : $c.type
+1 if it's writable   : $c.settable
+node's access type   : $c.access
+node's status        : $c.status
+node's syntax        : $c.syntax
+
+    @end@
+*/
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /** $name.uc_H */
+@end@
+######################################################################
+## Do the .c file
+######################################################################
+@foreach $i table@
+@open ${name}.c@
+@eval $hack = "Id"@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *       $Id$
+ *
+ * $$hack:$
+ *
+ *
+ * For help understanding NET-SNMP in general, please check the 
+ *     documentation and FAQ at:
+ *
+ *     http://www.net-snmp.org/
+ *
+ *
+ * For help understanding this code, the agent and how it processes
+ *     requests, please check the following references.
+ *
+ *     http://www.net-snmp.org/tutorial/
+ *     http://www.net-snmp.org/tutorial/toolkit/
+ *     http://www.net-snmp.org/tutorial/agent/
+ *
+ *
+ * And if all else fails, send a detailed message to the developers
+ *     describing the problem you are having to:
+ *
+ *    net-snmp-coders@lists.sourceforge.net
+ *
+ * Yes, there is lots of code here that you might not use. But it is much
+ * easier to remove code than to add it!
+ */
+#ifdef IN_UCD_SNMP_SOURCE
+/** If we're compiling this file inside the ucd-snmp source tree */
+
+/** This should always be included first before anything else */
+#include <config.h>
+
+/** minimal include directives */
+#include "mibincl.h"
+#include "util_funcs.h"
+#include "table.h"
+
+#else /** !IN_UCD_SNMP_SOURCE */
+
+#include <ucd-snmp/ucd-snmp-config.h>
+#include <ucd-snmp/ucd-snmp-includes.h>
+#include <ucd-snmp/ucd-snmp-agent-includes.h>
+
+#endif /** !IN_UCD_SNMP_SOURCE */
+
+#include <assert.h>
+
+#include "${i}.h"
+
+static     handler_registration *my_handler = NULL;
+
+oid ${i}_oid[] = {$i.commaoid};
+size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
+
+
+#ifdef ${i}_TREE
+/************************************************************
+ * keep binary tree to find context by name
+ */
+static int ${i}_cmp( void *lhs, void *rhs );
+static ${i}_context * ${i}_find( ${i}_context *item );
+static ${i}_context * ${i}_insert( ${i}_context *item );
+static ${i}_context * ${i}_delete( ${i}_context * item );
+static void * tree = NULL;
+
+/************************************************************
+ * compare two context pointers here. Return -1 if lhs < rhs,
+ * 0 if lhs == rhs, and 1 if lhs > rhs.
+ */
+static int
+${i}_cmp( void *lhs, void *rhs )
+{
+    ${i}_context *context_l =
+        (${i}_context *)lhs;
+    ${i}_context *context_r =
+        (${i}_context *)rhs;
+
+    /*
+     * check primary key, then secondary. Add your own code if
+     * there are more than 2 indexes
+     */
+    int rc;
+    rc = strncmp( context_l->xxName, context_r->xxName,
+                    SNMP_MIN(context_l->xxName_len, context_r->xxName_len) );
+
+    if(rc)
+        return rc;
+
+    /*
+     *  secondary keys
+     */
+    if(context_l->yy < context_r->yy) 
+        return -1;
+
+    return (context_l->yy == context_r->yy) ? 0 : 1;
+}
+
+/************************************************************
+ * search tree
+ */
+${i}_context *
+${i}_get( const char *name, int len )
+{
+    /** XXX TODO: handle null termination */
+    ${i}_context tmp;
+    if(len > sizeof(tmp.xxName))
+        return NULL;
+
+    strncpy( tmp.xxName, name, sizeof(tmp.xxName) );
+    tmp.xxName_len = len;
+    
+    return ${i}_find( &tmp );
+}
+#endif
+
+
+/************************************************************
+/* Initialzies the $name module
+ */
+void
+init_$name(void)
+{
+    initialize_table_$i();
+
+    /*
+     * TODO: perform and startup stuff here
+     */
+}
+
+#ifdef ${i}_SET_HANDLING
+
+/*
+ * the *_extract_index routine
+ */
+int
+${i}_extract_index( ${i}_context * ctx, oid_array_header * hdr )
+{
+    /*
+     * temporary local storage for extracting oid index
+     */
+    @eval $first_idx = ""@
+    @foreach $idx index@
+        @if "$first_idx" eq ""@
+        @eval $first_idx = $idx@
+        @end@
+    struct variable_list var_$idx;
+    @end@
+    int err = 0;
+
+    /*
+     * copy index, if provided
+     */
+    if(hdr) {
+        assert(ctx->index.idx == NULL);
+        if(snmp_clone_mem( (void*)&ctx->index.idx, hdr->idx,
+                           hdr->idx_len * sizeof(oid) )) {
+            return -1;
+        }
+        ctx->index.idx_len = hdr->idx_len;
+    }
+
+    /**
+     * Create variable to hold each component of the index
+     */
+#ifdef ${i}_EXTERNAL_INDEX
+#warning "TODO: add code for external index!"
+#endif
+    @foreach $idx index@
+    memset( &var_$idx, 0x00, sizeof(var_$idx) );
+    var_${idx}.type = $idx.type;
+    var_$idx.next_variable = &var_XX;
+
+    @end@
+
+    /*
+     * parse the oid into the individual components
+     */
+    parse_oid_indexes( hdr->idx, hdr->idx_len, &var_$first_idx );
+
+    /*
+     * copy components into the context structure
+     */
+#ifdef ${i}_EXTERNAL_INDEX
+#warning "TODO: add code for external index!"
+#endif
+    @foreach $idx index@
+    @eval $have_type = 0@
+    @if "$idx.type" eq "ASN_OCTET_STR"@
+    @eval $have_type = 1@
+    if(var_$idx.val_len > sizeof(ctx->$idx))
+       err = -1;
+    else
+        memcpy( ctx->$idx, var_$idx.val.string, var_$idx.val_len );
+    ctx->${idx}_len = var_$idx.val_len;
+    @end@
+    @if "$idx.type" eq "ASN_OBJECT_ID"@
+    @eval $have_type = 1@
+    memcpy( ctx->$idx, var_$idx.val.string, var_$idx.val_len );
+    ctx->$idx_len = var_$idx.val_len;
+    @end@
+    @if $have_type == 0@
+    ctx->$idx = *var_$idx.val.integer;
+    @end@
+
+    @end@
+
+    /*
+     * TODO: check any syntax requrements for indexes here
+     */
+
+
+    /*
+     * parsing may have allocated memory. free it.
+     */
+    snmp_reset_var_buffers( &var_$first_idx );
+
+    return err;
+}
+
+@if "$rs_name" ne ""@
+/************************************************************
+ * the *_can_activate routine is called routine is called
+ * when a row is changed to determine if all the values
+ * set are consistent with the row's rules for  a row status
+ * of ACTIVE.
+ *
+ * return 1 if the row could be ACTIVE
+ * return 0 if the row is not ready for the ACTIVE state
+ */
+int ${i}_can_activate(${i}_context *ctx_old,
+                      ${i}_context *ctx_new,
+                      array_group * ag)
+{
+    /*
+     * TODO: check for activation requirements here
+     */
+    return 1;
+}
+
+/************************************************************
+ * the *_can_deactivate routine is called when a row that is
+ * currently ACTIVE is set to a state other than ACTIVE. If
+ * there are conditions in which a row should not be allowed
+ * to transition out of the ACTIVE state (such as the row being
+ * referred to by another row or table), check for them here.
+ *
+ * return 1 if the row can set to a non-ACTIVE state
+ * return 0 if the row must remain in the ACTIVE state
+ */
+int ${i}_can_deactivate(${i}_context *ctx_old,
+                        ${i}_context *ctx_new,
+                        array_group * ag)
+{
+    /*
+     * TODO: check for deactivation requirements here
+     */
+    return 1;
+}
+
+@end@
+/************************************************************
+ * the *_can_delete routine is call to determine if a row
+ * can be deleted.
+ *
+ * return 1 if the row can be deleted
+ * return 0 if the row cannot be deleted
+ */
+int ${i}_can_delete(${i}_context *ctx_old,
+                    ${i}_context *ctx_new,
+                    array_group * ag)
+{
+    /*
+     * probably shouldn't delete a row that we can't
+     * deactivate.
+     */
+    if(${i}_can_deactivate(ctx_old,ctx_new,ag))
+        return 1;
+
+    /*
+     * TODO: check for other deletion requirements here
+     */
+    return 0;
+}
+
+#ifdef ${i}_ROW_CREATION
+/************************************************************
+ * the *_create_row routine is called by the table handler
+ * to create a new row for a given index. If you need more
+ * information (such as column values) to make a decision
+ * on creating rows, you must create an initial row here
+ * (to hold the column values), and you can examine the
+ * situation in more detail in the *_set_reserve1 or later
+ * states of set processing. Simple check for a NULL ctx_old
+ * in those states and do detailed creation checking there.
+ *
+ * returns a newly allocated ${i}_context
+ *   structure if the specified indexes are not illegal
+ * returns NULL for errors or illegal index values.
+ */
+oid_array_header * ${i}_create_row( oid_array_header* hdr)
+{
+    ${i}_context * ctx =
+        (${i}_context*)calloc(1,sizeof(*ctx));
+    if(!ctx)
+        return NULL;
+        
+    /*
+     * TODO: check indexes, if necessary.
+     */
+    if(${i}_extract_index( ctx, hdr )) {
+        free(ctx->index.idx);
+        free(ctx);
+        return NULL;
+    }
+
+    /*
+     * TODO: initialize any default values here. This is also
+     * first place you really should allocate any memory for
+     * yourself to use.  If you allocated memory earlier,
+     * make sure you free it for earlier error cases!
+     */
+    /**
+     @foreach $c column@
+     @if $c.settable@
+     ctx->$c = 0;
+     @end@
+     @end@
+    */
+
+    return (oid_array_header *)ctx;
+}
+
+/************************************************************
+ * the *_duplicate row routine
+ */
+oid_array_header * ${i}_duplicate_row( oid_array_header* hdr)
+{
+    ${i}_context * ctx_old = (${i}_context *)hdr;
+    ${i}_context * ctx_new;
+    if(!ctx_old)
+        return;
+
+    ctx_new = (${i}_context*)calloc(1,sizeof(*ctx_new));
+    if(!ctx_new)
+        return NULL;
+        
+    /*
+     * copy index, if provided
+     */
+    assert(ctx_old->index.idx != NULL);
+    if(snmp_clone_mem( (void*)&ctx_new->index.idx, ctx_old->index.idx,
+                           ctx_old->index.idx_len * sizeof(oid) )) {
+        free(ctx_new);
+        ctx_new = NULL;
+        return NULL;
+    }
+    ctx_new->index.idx_len = ctx_old->index.idx_len;
+    
+
+    /*
+     * copy components into the context structure
+     */
+#ifdef ${i}_EXTERNAL_INDEX
+#warning "TODO: add code for external index!"
+#endif
+    @foreach $c column@
+    @eval $have_type = 0@
+    @if "$c.type" eq "ASN_OCTET_STR"@
+    @eval $have_type = 1@
+    memcpy( ctx_new->$c, ctx_old->$c, ctx_old->${c}_len );
+    ctx_new->${c}_len = ctx_old->${c}_len;
+    @end@
+    @if "$c.type" eq "ASN_OBJECT_ID"@
+    @eval $have_type = 1@
+    memcpy( ctx_old->$c, ctx_new->$c, ctx_old->${c}_len );
+    ctx_new->${c}_len = ctx_old->${c}_len;
+    @end@
+    @if $have_type == 0@
+    ctx_new->$c = ctx_old->$c;
+    @end@
+
+    @end@
+    return (oid_array_header*) ctx_new;
+}
+
+/************************************************************
+ * the *_delete_row method is called to delete a row.
+ */
+oid_array_header * ${i}_delete_row( oid_array_header* hdr)
+{
+    ${i}_context * ctx =
+        (${i}_context*)hdr;
+
+    if(ctx->index.idx)
+        free(ctx->index.idx);
+
+    /*
+     * TODO: release any memory you allocated here...
+     */
+
+    /*
+     * release header
+     */
+    free( ctx );
+
+    return NULL;
+}
+#endif
+
+
+/************************************************************
+ * RESERVE is used to check the syntax of all the variables
+ * provided, that the values being set are sensible and consistent,
+ * and to allocate any resources required for performing the SET.
+ * After this stage, the expectation is that the set ought to
+ * succeed, though this is not guaranteed. (In fact, with the UCD
+ * agent, this is done in two passes - RESERVE1, and
+ * RESERVE2, to allow for dependancies between variables).
+ *
+ * AFTER calling this routine, the agent will call duplicate_row
+ * to create a copy of the row.
+ *
+ * next state -> SET_RESERVE2 || SET_FREE
+ */
+void ${i}_set_reserve1( array_group *ag )
+{
+    ${i}_context *ctx_new =
+            (${i}_context *)ag->new_row;
+    ${i}_context *ctx_old =
+            (${i}_context *)ag->old_row;
+    struct variable_list *var;
+    array_group_item *current;
+
+    /*
+     * TODO: loop through columns, check syntax and lengths. For
+     * columns which have no dependencies, you could also move
+     * the value/range checking here to attempt to catch error
+     * cases as early as possible.
+     */
+    for( current = ag->list; current; current = current->next ) {
+
+        var = current->ri->requestvb;
+
+        switch(current->tri->colnum) {
+
+        @foreach $c column@
+        @if $c.settable@
+        case COLUMN_$c.uc:
+            /** $c.syntax = $c.type */
+            if( var->type != $c.type ) {
+                set_mode_request_error(MODE_SET_BEGIN, current->ri,
+                                       SNMP_ERR_WRONGTYPE );
+            }
+            @if "$c.type" eq "ASN_OCTET_STR"@
+                else if( var->val_len > sizeof(ctx_new->$c) ) {
+            @end@
+            @if "$c.type" eq "ASN_OBJECT_ID"@
+                else if( var->val_len > sizeof(ctx_new->$c) ) {
+            @end@
+            @if "$c.type" ne "ASN_OCTET_STR"@
+                else if( var->val_len != sizeof(ctx_new->$c) ) {
+            @end@
+                set_mode_request_error(MODE_SET_BEGIN, current->ri,
+                                       SNMP_ERR_WRONGLENGTH );
+            }
+        break;
+
+        @end@
+        @end@
+        default: /** We shouldn't get here */
+            snmp_log(LOG_ERR, "unknown column in "
+                     "${i}_set_reserve1\n");
+        }
+
+        ag->status = SNMP_MAX( ag->status, current->ri->status );
+    }
+
+    /*
+     * done with all the columns. Could check row related
+     * requirements here.
+     */
+}
+
+void ${i}_set_reserve2( array_group *ag )
+{
+    struct variable_list *var;
+    ${i}_context *ctx_new = (${i}_context *)ag->new_row;
+    ${i}_context *ctx_old = (${i}_context *)ag->old_row;
+    array_group_item *current;
+
+    @if "$st_name" ne ""@
+    STORAGETYPE_DECLARE;
+    @end@
+    @if "$rs_name" ne ""@
+    ROWSTATUS_DECLARE;
+
+    @end@
+    /*
+     * TODO: loop through columns, check for valid
+     * values and any range constraints.
+     */
+    for( current = ag->list; current; current = current->next ) {
+
+        var = current->ri->requestvb;
+
+        switch(current->tri->colnum) {
+
+        @foreach $c column@
+        @if $c.settable@
+        case COLUMN_$c.uc:
+            /** $c.syntax = $c.type */
+            @eval $have_check = 0@
+            @if "$c" eq "$st_name"@
+                @eval $have_check = 1@
+                STORAGETYPE_VALIDATE( var, current->ri );
+            @end@
+            @if "$c" eq "$rs_name"@
+                @eval $have_check = 1@
+                ROWSTATUS_VALIDATE( var, current->ri );
+            @end@
+            @if $have_check == 0@
+                @if "$c.type" eq "ASN_IPADDRESS"@
+                    @eval $have_check = 1@
+                    if ( XXX_check_ip( *var->val.integer ) ) {
+                @end@
+                @if "$c.type" eq "ASN_OBJECT_ID"@
+                    @eval $have_check = 1@
+                    if ( XXX_check_oid( var ) ) {
+                @end@
+                @if "$c.type" eq "ASN_OCTET_STR"@
+                    @eval $have_check = 1@
+                    if ( XXX_check_value( var->val.string, XXX ) ) {
+                @end@
+                @if "$c.syntax" eq "TruthValue"@
+                    @eval $have_check = 1@
+                    /** TruthValue allowable values */
+                    if ((*var->val.integer != 2) &&
+                        (*var->val.integer != 1)) {
+                @end@
+                @if $have_check != 1@
+                    if ( *var->val.integer != XXX ) {
+                @end@
+                    /** or SNMP_ERR_INCONSISTENTVALUE */
+                    set_mode_request_error(MODE_SET_BEGIN, current->ri,
+                                           SNMP_ERR_BADVALUE );
+                }
+            @end@
+        break;
+
+        @end@
+        @end@
+        default: /** We shouldn't get here */
+            snmp_log(LOG_ERR, "unknown column in "
+                     "${i}_set_reserve2\n");
+        }
+    }
+
+    /*
+     * done with all the columns. Could check row related
+     * requirements here.
+     */
+    @if "$st_name" ne ""@
+    STORAGETYPE_CHECK( ctx_new->$st_name );
+    @end@
+    @if "$rs_name" ne ""@
+    ROWSTATUS_CHECK( ctx_new->$rs_name, ctx_new->$st_name, ag->list->ri );
+    @end@
+}
+
+/************************************************************
+ * Assuming that the RESERVE phase was successful, the next
+ * stage is indicated by the action value ACTION. This is used
+ * to actually implement the set operation. However, this must
+ * either be done into temporary (persistent) storage, or the
+ * previous value stored similarly, in case any of the subsequent
+ * ACTION calls fail.
+ *
+ * BEFORE calling this routine the agent will replace the old_row
+ * in the array with the new_row.
+ */
+void ${i}_set_action( array_group *ag )
+{
+    struct variable_list *var;
+    ${i}_context *ctx_new = (${i}_context *)ag->new_row;
+    ${i}_context *ctx_old = (${i}_context *)ag->old_row;
+    array_group_item *current;
+
+    @if "$rs_name" ne ""@
+    request_info * row_status_vb = NULL;
+    int            row_err = 0;
+    @end@
+
+    assert( ctx_old || ctx_new );
+
+    /*
+     * TODO: loop through columns, copy varbind values
+     * to context structure for the row.
+     */
+    for( current = ag->list; current; current = current->next ) {
+
+        var = current->ri->requestvb;
+
+        switch(current->tri->colnum) {
+
+        @foreach $c column@
+        @if $c.settable@
+        case COLUMN_$c.uc:
+            /** $c.syntax = $c.type */
+            @eval $have_type = 0@
+            @if "$c.type" eq "ASN_OCTET_STR"@
+            @eval $have_type = 1@
+            memcpy(ctx_new->$c,var->val.string,var->val_len);
+            ctx_new->${c}_len = var->val_len;
+            @end@
+            @if "$c.type" eq "ASN_OBJECT_ID"@
+            @eval $have_type = 1@
+            memcpy(ctx_new->$c,var->val.objid,var->val_len);
+            ctx_new->${c}_len = var->val_len;
+            @end@
+            @if "$rs_name" eq "$c"@
+                row_status_vb = current->ri;
+            @end@
+            @if $have_type == 0@
+            ctx_new->$c = *var->val.integer;
+            @end@
+        break;
+
+        @end@
+        @end@
+        default: /** We shouldn't get here */
+            snmp_log(LOG_ERR, "unknown column in "
+                     "${i}_set_action\n");
+        }
+    }
+
+    /*
+     * done with all the columns. Could check row related
+     * requirements here.
+     */
+    @if "$rs_name" ne ""@
+    if(!row_status_vb)
+        row_status_vb = ag->list->ri;
+#ifndef ${i}_CAN_MODIFY_ACTIVE_ROW
+    if( ctx_old && RS_IS_ACTIVE(ctx_old->$rs_name) &&
+        ctx_new && RS_IS_ACTIVE(ctx_new->$rs_name) ) {
+            row_err = 1;
+    }
+#endif
+
+    /*
+     * check activation/deactivation
+     */
+    if( ctx_new ) {
+        /*
+         * is this row ready to be active?
+         */
+        int row_ready = ${i}_can_activate(ctx_old,ctx_new,ag);
+
+        /*
+         * either a new row, or change to old row
+         */
+        if( RS_IS_GOING_ACTIVE(ctx_new->$rs_name) ) {
+            if( ! row_ready ) {
+                row_err = 1;
+            }
+            ctx_new->$rs_name = RS_ACTIVE;
+        }
+        else {
+            if(ctx_old) {
+                /*
+                 * change
+                 */
+                if( RS_IS_ACTIVE(ctx_old->$rs_name) ) {
+                    /*
+                     * check preqs for deactivation
+                     */
+                    if(! ${i}_can_deactivate(ctx_old,ctx_new,ag)) {
+                        row_err = 1;
+                    }
+                }
+            }
+            else {
+                /*
+                 * new row
+                 */
+            }
+
+            if( row_ready )
+                ctx_new->$rs_name = RS_NOTINSERVICE;
+            else
+                ctx_new->$rs_name = RS_NOTREADY;
+        }
+    }
+    else {
+        /*
+         * check pre-reqs for delete row
+         */
+        if(! ${i}_can_delete(ctx_old,ctx_new,ag)) {
+            row_err = 1;
+        }
+    }
+    if(row_err) {
+        set_mode_request_error(MODE_SET_BEGIN, row_status_vb,
+                               SNMP_ERR_INCONSISTENTVALUE);
+        return;
+    }
+
+    @end@
+    /*
+     * TODO: if you have dependencies on other tables, this would be
+     * a good place to check those, too.
+     */
+
+#ifdef ${i}_TREE
+    /*
+     * update secondary index
+     */
+    if(ctx_old)
+       ${i}_delete( ctx_old );
+    if(ctx_new)
+       ${i}_insert( ctx_new );
+#endif
+}
+
+/************************************************************
+ * Only once the ACTION phase has completed successfully, can
+ * the final COMMIT phase be run. This is used to complete any
+ * writes that were done into temporary storage, and then release
+ * any allocated resources. Note that all the code in this phase
+ * should be "safe" code that cannot possibly fail (cue
+ * hysterical laughter). The whole intent of the ACTION/COMMIT
+ * division is that all of the fallible code should be done in
+ * the ACTION phase, so that it can be backed out if necessary.
+ */
+void ${i}_set_commit( array_group *ag )
+{
+    struct variable_list *var;
+    ${i}_context *ctx_new = (${i}_context *)ag->new_row;
+    ${i}_context *ctx_old = (${i}_context *)ag->old_row;
+    array_group_item *current;
+
+    /*
+     * loop through columns
+     */
+    for( current = ag->list; current; current = current->next ) {
+
+        var = current->ri->requestvb;
+
+        switch(current->tri->colnum) {
+
+        @foreach $c column@
+        @if $c.settable@
+        case COLUMN_$c.uc:
+            /** $c.syntax = $c.type */
+        break;
+
+        @end@
+        @end@
+        default: /** We shouldn't get here */
+            snmp_log(LOG_ERR, "unknown column in "
+                     "${i}_set_commit\n");
+        }
+    }
+
+    /*
+     * done with all the columns. Could check row related
+     * requirements here.
+     */
+}
+
+/************************************************************
+ * If either of the RESERVE calls fail, the write routines
+ * are called again with the FREE action, to release any resources
+ * that have been allocated. The agent will then return a failure
+ * response to the requesting application.
+ *
+ * AFTER calling this routine, the agent will delete old_row.
+ */
+void ${i}_set_free( array_group *ag )
+{
+    struct variable_list *var;
+    ${i}_context *ctx_new = (${i}_context *)ag->new_row;
+    ${i}_context *ctx_old = (${i}_context *)ag->old_row;
+    array_group_item *current;
+
+    /*
+     * loop through columns
+     */
+    for( current = ag->list; current; current = current->next ) {
+
+        var = current->ri->requestvb;
+
+        switch(current->tri->colnum) {
+
+        @foreach $c column@
+        @if $c.settable@
+        case COLUMN_$c.uc:
+            /** $c.syntax = $c.type */
+        break;
+
+        @end@
+        @end@
+        default: /** We shouldn't get here */
+            snmp_log(LOG_ERR, "unknown column in "
+                     "${i}_set_free\n");
+        }
+    }
+
+    /*
+     * done with all the columns. Could check row related
+     * requirements here.
+     */
+}
+
+/************************************************************
+ * If the ACTION phase does fail (for example due to an apparently
+ * valid, but unacceptable value, or an unforeseen problem), then
+ * the list of write routines are called again, with the UNDO
+ * action. This requires the routine to reset the value that was
+ * changed to its previous value (assuming it was actually changed),
+ * and then to release any resources that had been allocated. As
+ * with the FREE phase, the agent will then return an indication
+ * of the error to the requesting application.
+ *
+ * BEFORE calling this routine, the agent will remove the new row
+ * from the table and replace it with the old row.
+ *
+ * AFTER calling this routing, the agent will delete the new_row.
+ */
+void ${i}_set_undo( array_group *ag )
+{
+    struct variable_list *var;
+    ${i}_context *ctx_new = (${i}_context *)ag->new_row;
+    ${i}_context *ctx_old = (${i}_context *)ag->old_row;
+    array_group_item *current;
+
+    /*
+     * loop through columns
+     */
+    for( current = ag->list; current; current = current->next ) {
+
+        var = current->ri->requestvb;
+
+        switch(current->tri->colnum) {
+
+        @foreach $c column@
+        @if $c.settable@
+        case COLUMN_$c.uc:
+            /** $c.syntax = $c.type */
+        break;
+
+        @end@
+        @end@
+        default: /** We shouldn't get here */
+            snmp_log(LOG_ERR, "unknown column in "
+                     "${i}_set_undo\n");
+        }
+    }
+
+    /*
+     * done with all the columns. Could check row related
+     * requirements here.
+     */
+#ifdef ${i}_TREE
+    /*
+     * update secondary index
+     */
+    if(ctx_new)
+        ${i}_delete( ctx_new );
+    if(ctx_old)
+        ${i}_insert( ctx_old );
+#endif
+}
+
+#endif /** ${i}_SET_HANDLING */
+
+
+#ifdef ${i}_TREE
+static ${i}_context *
+${i}_find( ${i}_context *item )
+{
+    ${i}_context **item_ptr =
+        (${i}_context **)
+        tfind( item, &tree, ${i}_cmp );
+
+    return item_ptr ? *item_ptr : NULL;
+}
+
+static ${i}_context *
+${i}_insert( ${i}_context *item )
+{
+    ${i}_context **item_ptr =
+        (${i}_context **)
+        tsearch( item, &tree, ${i}_cmp );
+
+    return item_ptr ? *item_ptr : NULL;
+}
+
+static ${i}_context *
+${i}_delete( ${i}_context *item )
+{
+    ${i}_context **item_ptr =
+        (${i}_context **)
+        tdelete( item, &tree, ${i}_cmp );
+
+    return item_ptr ? *item_ptr : NULL;
+}
+
+#endif
+
+/************************************************************
+ *
+ * Initialize the $i table by defining it's contents and how it's structured
+ */
+void
+initialize_table_$i(void)
+{
+    static table_array_callbacks cb;
+    table_registration_info *table_info;
+
+    if(my_handler) {
+        snmp_log(LOG_ERR, "initialize_table_${i}_handler called again\n");
+        return;
+    }
+
+    memset(&cb, 0x00, sizeof(cb));
+
+    /** create the table structure itself */
+    table_info = SNMP_MALLOC_TYPEDEF(table_registration_info);
+
+    /* if your table is read only, it's easiest to change the
+       HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */
+    my_handler = create_handler_registration("$i",
+                                             table_array_helper_handler,
+                                             ${i}_oid,
+                                             ${i}_oid_len,
+                                             HANDLER_CAN_RWRITE);
+            
+    if (!my_handler || !table_info) {
+        snmp_log(LOG_ERR, "malloc failed in "
+                 "initialize_table_${i}_handler\n");
+        return; /** mallocs failed */
+    }
+
+    /***************************************************
+     * Setting up the table's definition
+     */
+    /*
+     * TODO: add any external indexes here.
+     */
+#ifdef ${i}_EXTERNAL_INDEX
+#warning "TODO: add code for external index!"
+#endif
+
+    /*
+     * internal indexes
+     */
+    @foreach $idx index@
+        /** index: $idx */
+        table_helper_add_index(table_info, $idx.type);
+    @end@
+
+    @eval $minv = 0xffffffff@
+    @eval $maxv = 0@
+    @foreach $c column@
+    @if ! $c.noaccess@
+    @eval $minv = min($minv, $c.subid)@
+    @end@
+    @eval $maxv = max($maxv, $c.subid)@
+    @end@
+    table_info->min_column = $minv;
+    table_info->max_column = $maxv;
+
+    /***************************************************
+     * registering the table with the master agent
+     */
+    cb.get_value = ${i}_get_value;
+
+#ifdef ${i}_SET_HANDLING
+#ifdef ${i}_ROW_CREATION
+    cb.create_row = ${i}_create_row;
+    cb.duplicate_row = ${i}_duplicate_row;
+    cb.delete_row = ${i}_delete_row;
+#endif
+    cb.set_reserve1 = ${i}_set_reserve1;
+    cb.set_reserve2 = ${i}_set_reserve2;
+    cb.set_action = ${i}_set_action;
+    cb.set_commit = ${i}_set_commit;
+    cb.set_free = ${i}_set_free;
+    cb.set_undo = ${i}_set_undo;
+#endif
+    DEBUGMSGTL(("initialize_table_$i",
+                "Registering table $i "
+                "as a table array\n"));
+    register_table_array(my_handler, table_info, &cb, 1);
+}
+
+/************************************************************
+ * ${i}_get_value
+ */
+int ${i}_get_value(
+            request_info *request,
+            oid_array_header *item,
+            table_request_info *table_info )
+{
+    struct variable_list *var = request->requestvb;
+    ${i}_context *context = (${i}_context *)item;
+
+    switch(table_info->colnum) {
+
+        @foreach $c column@
+            @eval $have_type = 0@
+        case COLUMN_$c.uc:
+            /** $c.syntax = $c.type */
+            snmp_set_var_typed_value(var, $c.type,
+                         (char*)&context->$c,
+            @if "$c.type" eq "ASN_OBJECT_ID"@
+                @eval $have_type = 1@
+                         context->${c}_len );
+            @end@
+            @if "$c.type" eq "ASN_OCTET_STR"@
+                @eval $have_type = 1@
+                         context->${c}_len );
+            @end@
+            @if $have_type == 0@
+                         sizeof(context->$c) );
+            @end@
+        break;
+
+        @end@
+    default: /** We shouldn't get here */
+        snmp_log(LOG_ERR, "unknown column in "
+                 "${i}_get_value\n");
+    }
+}
+
+const ${i}_context *
+${i}_get_by_idx(oid_array_header * hdr)
+{
+    return (const ${i}_context *)
+    table_array_get_by_index( my_handler, hdr );
+}
+
+@end@
+@end@
+@end@
diff --git a/local/mib2c.create-dataset.conf b/local/mib2c.create-dataset.conf
new file mode 100644
index 0000000..ab08ace
--- /dev/null
+++ b/local/mib2c.create-dataset.conf
@@ -0,0 +1,95 @@
+## -*- c -*-
+######################################################################
+## Do the .h file
+######################################################################
+@open ${name}.h@
+/* Note: this file originally auto-generated by mib2c */
+#ifndef $name.uc_H
+#define $name.uc_H
+
+/* function declarations */
+void init_$name(void);
+@foreach $i table@
+void initialize_table_$i(void);
+NodeHandler ${i}_handler;
+@end@
+@foreach $i table@
+
+/* column number definitions for table $i */
+    @foreach $c column@
+       #define COLUMN_$c.uc		$c.subid
+    @end@
+@end@
+#endif /* $name.uc_H */
+######################################################################
+## Do the .c file
+######################################################################
+@open ${name}.c@
+/* Note: this file originally auto-generated by mib2c */
+@foreach $i table@
+/** Initialize the $i table by defining it's contents and how it's structured */
+void
+initialize_table_$i(void)
+{
+    static oid ${i}_oid[] = {$i.commaoid};
+    size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
+    table_data_set *table_set;
+
+    /* create the table structure itself */
+    table_set = create_table_data_set("$i");
+
+    /***************************************************
+     * Adding indexes
+     */
+    @foreach $idx index@
+        /*
+         * declaring the $idx index
+         */
+        DEBUGMSGTL(("initialize_table_$i",
+	            "adding index $idx.label of type $idx.type to table $i\n"));
+        table_dataset_add_index(table_set, $idx.type);
+    @end@
+
+    @foreach $c column@
+      /* adding column $c of type $c.type and access of $c.access */
+        DEBUGMSGTL(("initialize_table_$i",
+	            "adding column $c.label (#$c.subid) of type $c.type to table $i\n"));		 
+	table_set_add_default_row(table_set, COLUMN_$c.uc, $c.type, $c.settable);
+    @end@
+
+    /* registering the table with the master agent */
+    /* note: if you don't need a subhandler to deal with any aspects
+       of the request, change ${i}_handler to "NULL" */
+    register_table_data_set(create_handler_registration("$i", ${i}_handler,
+                                                        ${i}_oid,
+                                                        ${i}_oid_len,
+                                                        HANDLER_CAN_RWRITE),
+                            table_set, NULL);
+}
+@end@
+
+/** Initialzies the $name module */
+void
+init_$name(void)
+{
+
+  /* here we initialize all the tables we're planning on supporting */
+  @foreach $i table@
+    initialize_table_$i();
+  @end@
+}
+@foreach $i table@
+
+/** handles requests for the $i table, if anything else needs to be done */
+int
+${i}_handler(
+    mib_handler               *handler,
+    handler_registration      *reginfo,
+    agent_request_info        *reqinfo,
+    request_info              *requests) {
+    /* perform anything here that you need to do.  The requests have
+       already been processed by the master table_dataset handler, but
+       this gives you chance to act on the request in some other way
+       if need be. */
+    return SNMP_ERR_NOERROR;
+}
diff --git a/local/mib2c.iterate.conf b/local/mib2c.iterate.conf
new file mode 100644
index 0000000..59c8be1
--- /dev/null
+++ b/local/mib2c.iterate.conf
@@ -0,0 +1,241 @@
+## -*- c -*-
+######################################################################
+## Do the .h file
+######################################################################
+@open ${name}.h@
+/* Note: this file originally auto-generated by mib2c */
+#ifndef $name.uc_H
+#define $name.uc_H
+
+/* function declarations */
+void init_$name(void);
+@foreach $i table@
+void initialize_table_$i(void);
+NodeHandler ${i}_handler;
+
+FirstDataPoint  ${i}_get_first_data_point;
+NextDataPoint   ${i}_get_next_data_point;
+@end@
+@foreach $i table@
+
+/* column number definitions for table $i */
+    @foreach $c column@
+       #define COLUMN_$c.uc		$c.subid
+    @end@
+@end@
+#endif /* $name.uc_H */
+######################################################################
+## Do the .c file
+######################################################################
+@open ${name}.c@
+/* Note: this file originally auto-generated by mib2c */
+
+#include <config.h>
+
+#include "mibincl.h"
+#include "snmp_loging.h"
+#include <helpers/table.h>
+#include <helpers/table_iterator.h>
+#include "${name}.h"
+
+@foreach $i table@
+/** Initialize the $i table by defining it's contents and how it's structured */
+void
+initialize_table_$i(void)
+{
+    static oid ${i}_oid[] = {$i.commaoid};
+    size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
+    table_registration_info *table_info;
+    handler_registration *my_handler;
+
+    /* create the table structure itself */
+    table_info = SNMP_MALLOC_TYPEDEF(table_registration_info);
+
+    /* if your table is read only, it's easiest to change the
+       HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */
+    my_handler = create_handler_registration("$i",
+                                             ${i}_handler,
+                                             ${i}_oid,
+                                             sizeof(${i}_oid)/sizeof(oid),
+                                             HANDLER_CAN_RWRITE);
+            
+    if (!my_handler || !table_info)
+        return; /* mallocs failed */
+
+    /***************************************************
+     * Setting up the table's definition
+     */
+    @foreach $idx index@
+        table_helper_add_index(table_info, $idx.type); /* index: $idx */
+    @end@
+
+    @eval $minv = 0xffffffff@
+    @eval $maxv = 0@
+    @foreach $c column@
+        @eval $minv = min($minv, $c.subid)@
+        @eval $maxv = max($maxv, $c.subid)@
+    @end@
+    table_info->min_column = $minv;
+    table_info->max_column = $maxv;
+    table_info->get_first_data_point = ${i}_get_first_data_point;
+    table_info->get_next_data_point = ${i}_get_next_data_point;
+
+    /***************************************************
+     * registering the table with the master agent
+     */
+    DEBUGMSGTL(("initialize_table_$i",
+                "Registering table $i as a table iterator\n"));		 
+    register_table_iterator(my_handler, table_info);
+}
+@end@
+
+/** Initialzies the $name module */
+void
+init_$name(void)
+{
+
+  /* here we initialize all the tables we're planning on supporting */
+  @foreach $i table@
+    initialize_table_$i();
+  @end@
+}
+@foreach $i table@
+
+/** returns the first data point within the $i table data.
+
+    Set the my_loop_context variable to the first data point structure
+    of your choice (from which you can find the next one).  This could
+    be anything from the first node in a linked list, to an integer
+    pointer containing the beginning of an array variable.
+
+    Set the my_data_context variable to something to be returned to
+    you later that will provide you with the data to return in a given
+    row.  This could be the same pointer as what my_loop_context is
+    set to, or something different.
+
+    The put_index_data variable contains a list of snmp variable
+    bindings, one for each index in your table.  Set the values of
+    each appropriately according to the data matching the first row
+    and return the put_index_data variable at the end of the function.
+*/
+struct variable_list *
+${i}_get_first_data_point(void **my_loop_context, void **my_data_context,
+                          struct variable_list *put_index_data) {
+
+    struct variable_list *vptr;
+
+    *my_loop_context = /* XXX */;
+    *my_data_context = /* XXX */;
+
+    vptr = put_index_data;
+    
+    @foreach $idx index@
+    snmp_set_var_value(vptr, (u_char *) /* XXX: $idx data */, /* XXX: length of $idx data */);
+    vptr = vptr->next_variable;
+    @end@
+
+    return put_index_data;
+}
+
+/** functionally the same as ${i}_get_first_data_point, but
+   my_loop_context has already been set to a previous value and should
+   be updated to the next in the list.  For example, if it was a
+   linked list, you might want to cast it and the return
+   my_loop_context->next.  The my_data_context pointer should be set
+   to something you need later and the indexes in put_index_data
+   updated again. */
+
+struct variable_list *
+${i}_get_next_data_point(void **my_loop_context, void **my_data_context,
+                         struct variable_list *put_index_data) {
+
+    struct variable_list *vptr;
+
+    *my_loop_context = /* XXX */;
+    *my_data_context = /* XXX */;
+
+    vptr = put_index_data;
+    
+    @foreach $idx index@
+    snmp_set_var_value(vptr, (u_char *) /* XXX: $idx data */, /* XXX: length of $idx data */);
+    vptr = vptr->next_variable;
+    @end@
+
+    return put_index_data;
+}
+
+/** handles requests for the $i table, if anything else needs to be done */
+int
+${i}_handler(
+    mib_handler               *handler,
+    handler_registration      *reginfo,
+    agent_request_info        *reqinfo,
+    request_info              *requests) {
+
+    table_request_info *table_info;
+    struct variable_list *var;
+    
+    while(requests) {
+        var = requests->requestvb;
+        if (requests->processed != 0)
+            continue;
+
+        /* perform anything here that you need to do.  The requests have
+           already been processed by the master table_dataset handler, but
+           this gives you chance to act on the request in some other way
+           if need be. */
+
+        /* the following extracts the my_data_context pointer set in
+           the loop functions above.  You can then use the results to
+           help return data for the columns of the $i table in question */
+        /* XXX */ = (/* XXX */ *) extract_iterator_context(requests);
+        if (/* XXX */ == NULL) {
+            if (reqinfo->mode == MODE_GET) {
+                set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE);
+                continue;
+            }
+            /* XXX: no row existed, if you support creation and this is a
+               set, start dealing with it here, else continue */
+        }
+
+        /* extracts the information about the table from the request */
+        table_info = extract_table_info(requests);
+        /* table_info->colnum contains the column number requested */
+        /* table_info->indexes contains a linked list of snmp variable
+           bindings for the indexes of the table.  Values in the list
+           have been set corresponding to the indexes of the
+           request */
+        if (table_info==NULL) {
+            requests = requests->next;
+            continue;
+        }
+
+        switch(reqinfo->mode) {
+            /* the table_iterator helper should change all GETNEXTs
+               into GETs for you automatically, so you don't have to
+               worry about the GETNEXT case.  Only GETs and SETs need
+               to be dealt with here */
+            case MODE_GET:
+                switch(table_info->colnum) {
+                    @foreach $c column@
+                    case COLUMN_$c.uc:
+                        snmp_set_var_typed_value(var, $c.type, (u_char *) /* XXX: column data */, /* XXX: column data length */);
+                        break;
+
+                    @end@
+                    default:
+                        /* We shouldn't get here */
+                        snmp_log(LOG_ERR, "problem encountered in ${i}_handler: unknown column\n");
+                }
+                break;
+
+            case MODE_SET_RESERVE1:
+                /* set handling... */
+
+            default:
+                snmp_log(LOG_ERR, "problem encountered in ${i}_handler: unsupported mode\n");
+        }
+        requests = requests->next;
+    }
+    return SNMP_ERR_NOERROR;
+}
diff --git a/local/mib2c.vartypes.conf b/local/mib2c.vartypes.conf
index 6626355..2290612 100644
--- a/local/mib2c.vartypes.conf
+++ b/local/mib2c.vartypes.conf
@@ -87,8 +87,6 @@
 ############################################################################
 type:		BITS
 copy:		OCTETSTR
-delete:		asnType
-asnType:	ASN_BIT_STR
 ############################################################################
 type:		GAUGE
 copy:		INTEGER
diff --git a/man/Makefile.in b/man/Makefile.in
index 53e8418..d031ec0 100644
--- a/man/Makefile.in
+++ b/man/Makefile.in
@@ -56,13 +56,20 @@
 	cat $(srcdir)/default_store.3.top default_store.3.h $(srcdir)/default_store.3.bot > default_store.3
 
 install:  installdirs $(MAN1) $(MAN1G) $(MAN3) $(MAN5) $(MAN5G) $(MAN8)
-	@for i in $(MAN1) ; do $(INSTALL) $(srcdir)/$$i $(man1dir) ; echo "install:  installed $$i in $(man1dir)" ; done
-	@for i in $(MAN1G) ; do $(INSTALL) $$i $(man1dir) ; echo "install:  installed $$i in $(man1dir)" ; done
-	@for i in $(MAN3) ; do $(INSTALL) $(srcdir)/$$i $(man3dir) ; echo "install:  installed $$i in $(man3dir)" ; done
-	@for i in $(MAN3G) ; do $(INSTALL) $$i $(man3dir) ; echo "install:  installed $$i in $(man3dir)" ; done
-	@for i in $(MAN5) ; do $(INSTALL) $(srcdir)/$$i $(man5dir) ; echo "install:  installed $$i in $(man5dir)" ; done
-	@for i in $(MAN5G) ; do $(INSTALL) $$i $(man5dir) ; echo "install:  installed $$i in $(man5dir)" ; done
-	@for i in $(MAN8) ; do $(INSTALL) $(srcdir)/$$i $(man8dir) ; echo "install:  installed $$i in $(man8dir)" ; done
+	@$(INSTALL) $(MAN1) $(man1dir)
+	@for i in $(MAN1) ; do echo "install:  installed $$i in $(man1dir)" ; done
+	@$(INSTALL) $(MAN1G) $(man1dir)
+	@for i in $(MAN1G) ; do echo "install:  installed $$i in $(man1dir)" ; done
+	@$(INSTALL) $(MAN3) $(man3dir)
+	@for i in $(MAN3) ; do echo "install:  installed $$i in $(man3dir)" ; done
+	@$(INSTALL) $(MAN3G) $(man3dir)
+	@for i in $(MAN3G) ; do echo "install:  installed $$i in $(man3dir)" ; done
+	@$(INSTALL) $(MAN5) $(man5dir)
+	@for i in $(MAN5) ; do echo "install:  installed $$i in $(man5dir)" ; done
+	@$(INSTALL) $(MAN5G) $(man5dir)
+	@for i in $(MAN5G) ; do echo "install:  installed $$i in $(man5dir)" ; done
+	@$(INSTALL) $(MAN8) $(man8dir)
+	@for i in $(MAN8) ; do echo "install:  installed $$i in $(man8dir)" ; done
 
 html: $(MANALL)
 	for i in $(MANALL); do base=`echo $$i | sed 's/.[0-9]$$//;'` ; echo "<HTML><BODY bgcolor=\"#ffffff\" background=\"../ucd-snmp-bg3.gif\"><PRE>" > $$base.html; nroff -man $$i | man2html -bare -title "$$base manual page" >> $$base.html; done
diff --git a/mibs/Makefile.in b/mibs/Makefile.in
index ec86ba6..567ff25 100644
--- a/mibs/Makefile.in
+++ b/mibs/Makefile.in
@@ -39,7 +39,8 @@
 all:
 
 install: installdirs
-	@for i in $(MIBS) ; do $(INSTALL) $(srcdir)/$$i $(mibdir) ; echo "install: installed $$i in $(mibdir)" ; done
+	@$(INSTALL) $(MIBS) $(mibdir)
+	@for i in $(MIBS) ; do echo "install: installed $$i in $(mibdir)" ; done
 
 installdirs:
 	@$(SHELL) $(srcdir)/../mkinstalldirs $(mibdir)
diff --git a/mibs/NET-SNMP-AGENT-MIB.txt b/mibs/NET-SNMP-AGENT-MIB.txt
new file mode 100644
index 0000000..9d455ef
--- /dev/null
+++ b/mibs/NET-SNMP-AGENT-MIB.txt
@@ -0,0 +1,71 @@
+NET-SNMP-AGENT-MIB DEFINITIONS ::= BEGIN
+
+--
+-- defines control and monitoring structures for the net-snmp agent.
+--
+
+IMPORTS
+    OBJECT-TYPE, NOTIFICATION-TYPE, MODULE-IDENTITY,
+    Integer32, Opaque, enterprises
+        FROM SNMPv2-SMI
+
+    TEXTUAL-CONVENTION, DisplayString, TruthValue
+	FROM SNMPv2-TC;
+
+netSnmpAgentMib MODULE-IDENTITY
+    LAST-UPDATED "200101170000Z"
+    ORGANIZATION "www.net-snmp.org"
+    CONTACT-INFO    
+	 "Defines control and monitoring structures for the net-snmp agent.
+
+         postal:    Wes Hardaker
+                    P.O. Box 382
+                    Davis CA  95617
+
+         email:     net-snmp-coders@lists.sourceforge.net"
+    DESCRIPTION
+	"Defines control and monitoring structures for the net-snmp agent."
+    REVISION	 "200111030000Z"
+    DESCRIPTION
+	"First revision."
+    ::= { enterprises 8072 999 1 }
+
+nsTransactionTable OBJECT-TYPE
+    SYNTAX      SEQUENCE OF NsTransactionEntry
+    MAX-ACCESS  not-accessible
+    STATUS      current
+    DESCRIPTION
+	"lists currently outstanding transactions in the net-snmp agent."
+    ::= { netSnmpAgentMib 1 }
+
+nsTransactionEntry OBJECT-TYPE
+    SYNTAX      NsTransactionEntry
+    MAX-ACCESS  not-accessible
+    STATUS      current
+    DESCRIPTION
+	"A row describing a given transaction."
+    INDEX   { nsTransactionID }
+    ::= {nsTransactionTable 1 }
+
+NsTransactionEntry ::= SEQUENCE {
+  nsTransactionID   INTEGER,
+  nsTransactionMode INTEGER
+}
+
+nsTransactionID OBJECT-TYPE
+    SYNTAX      INTEGER
+    MAX-ACCESS  not-accessible
+    STATUS      current
+    DESCRIPTION
+	"The internal identifier for a given transaction."
+    ::= { nsTransactionEntry 1 }
+
+nsTransactionMode OBJECT-TYPE
+    SYNTAX      Integer32
+    MAX-ACCESS  read-only
+    STATUS      current
+    DESCRIPTION
+	"The mode number for the current operation being performed."
+    ::= { nsTransactionEntry 2 }
+
+END
diff --git a/perl/SNMP/SNMP.xs b/perl/SNMP/SNMP.xs
index 0f9a9ad..a7db40b 100644
--- a/perl/SNMP/SNMP.xs
+++ b/perl/SNMP/SNMP.xs
@@ -3085,7 +3085,7 @@
                        tmp_sv=newSVpv((char*)str_buf, len);
                        av_store(varbind, VARBIND_VAL_F, tmp_sv);
 		       if (sv_timestamp)
-                          av_store(varbind, VARBIND_TIME_F, sv_timestamp);
+                          av_store(varbind, VARBIND_TYPE_F, sv_timestamp);
                        XPUSHs(sv_mortalcopy(tmp_sv));
                     } else {
                        av_store(varbind, VARBIND_VAL_F, &sv_undef);
@@ -3264,7 +3264,7 @@
                     tmp_sv = newSVpv((char*)str_buf, len);
                     av_store(varbind, VARBIND_VAL_F, tmp_sv);
 		    if (sv_timestamp)
-                       av_store(varbind, VARBIND_TIME_F, sv_timestamp);
+                       av_store(varbind, VARBIND_TYPE_F, sv_timestamp);
                     XPUSHs(sv_mortalcopy(tmp_sv));
                  } else {
 		    /* Return undef for this variable. */
@@ -3463,7 +3463,7 @@
                     tmp_sv = newSVpv((char*)str_buf, len);
 		    av_store(varbind, VARBIND_VAL_F, tmp_sv);
 		    if (sv_timestamp)
-		       av_store(varbind, VARBIND_TIME_F, SvREFCNT_inc(sv_timestamp));
+		       av_store(varbind, VARBIND_TYPE_F, SvREFCNT_inc(sv_timestamp));
 
 		    rv = newRV_noinc((SV *)varbind);
 		    sv_bless(rv, gv_stashpv("SNMP::Varbind",0));
diff --git a/snmplib/Makefile.in b/snmplib/Makefile.in
index 0de2bc6..bacdb35 100644
--- a/snmplib/Makefile.in
+++ b/snmplib/Makefile.in
@@ -12,6 +12,8 @@
 INSTALLHEADERS=\
 	asn1.h \
 	callback.h \
+	data_list.h \
+	oid_array.h \
 	default_store.h \
 	int64.h \
 	keytools.h \
@@ -42,6 +44,7 @@
 	snmp_debug.c tools.c  snmp_logging.c			\
 	snmpv3.c lcd_time.c keytools.c		                \
 	scapi.c callback.c default_store.c snmp_alarm.c		\
+	data_list.c oid_array.c					\
 	mt_support.c snmp_enum.c snmp-tc.c			\
 	snprintf.c strtol.c strtoul.c				\
 	snmp_transport.c @transport_src_list@                   \
@@ -53,6 +56,7 @@
 	snmp_debug.o tools.o  snmp_logging.o	   	  	\
 	snmpv3.o lcd_time.o keytools.o	  	                \
 	scapi.o callback.o default_store.o snmp_alarm.o	  	\
+	data_list.o oid_array.o					\
 	mt_support.o snmp_enum.o snmp-tc.o			\
 	snprintf.o strtol.o strtoul.o				\
 	snmp_transport.o @transport_obj_list@                   \
@@ -64,6 +68,7 @@
 	snmp_debug.lo tools.lo  snmp_logging.lo	   	  	\
 	snmpv3.lo lcd_time.lo keytools.lo	  	        \
 	scapi.lo callback.lo default_store.lo snmp_alarm.lo	\
+	data_list.lo oid_array.lo					\
 	mt_support.lo snmp_enum.lo snmp-tc.lo			\
 	snprintf.lo strtol.lo strtoul.lo			\
 	snmp_transport.lo @transport_lobj_list@                 \
@@ -78,6 +83,7 @@
 all: $(TARG)
 
 libsnmp.$(LIB_EXTENSION)$(LIB_VERSION):    ${TOBJS}
+	wait
 	$(LIB_LD_CMD) libsnmp.$(LIB_EXTENSION)$(LIB_VERSION) ${TOBJS}
 	$(RANLIB) libsnmp.$(LIB_EXTENSION)$(LIB_VERSION)
 
@@ -86,7 +92,8 @@
 
 install:	$(TARG) installdirs
 	@for i in $(TARG) ; do $(INSTALL) $$i $(libdir) ; done
-	@for i in $(INSTALLHEADERS) ; do $(INSTALL) $(srcdir)/$$i $(includedir) ; echo "install:  installed $$i in $(includedir)" ; done
+	@$(INSTALL) $(INSTALLHEADERS) $(includedir)
+	@for i in $(INSTALLHEADERS) ; do echo "install:  installed $$i in $(includedir)" ; done
 	$(LIB_LDCONFIG_CMD)
 
 ../lib/libsnmp.$(LIB_EXTENSION)$(LIB_VERSION):	libsnmp.$(LIB_EXTENSION)$(LIB_VERSION)
diff --git a/snmplib/mib.c b/snmplib/mib.c
index 18812c1..510a998 100644
--- a/snmplib/mib.c
+++ b/snmplib/mib.c
@@ -81,6 +81,8 @@
 #include "snmp_debug.h"
 #include "default_store.h"
 #include "snmp_logging.h"
+#include "tools.h"
+#include "snmp_client.h"
 
 static struct tree * _sprint_objid(char *buf, oid *objid, size_t objidlen);
 
@@ -3158,6 +3160,273 @@
   return buf;
 }
 
+/* takes the value in VAR and turns it into an OID segment in VAR->NAME */
+/* returns SNMPERR_SUCCESS or SNMPERR_GENERR */
+int
+build_oid_segment(struct variable_list *var) {
+  int i;
+  
+  switch(var->type) {
+      case ASN_INTEGER:
+      case ASN_COUNTER:
+      case ASN_GAUGE:
+      case ASN_TIMETICKS:
+          var->name_length = 1;
+          var->name = (oid *) malloc(sizeof(oid));
+          if (var->name == NULL)
+              return SNMPERR_GENERR;
+          var->name[0] = *(var->val.integer);
+          break;
+
+      case ASN_PRIV_IMPLIED_OBJECT_ID:
+          var->name_length = var->val_len/sizeof(oid);
+          var->name = (oid *) malloc(sizeof(oid) * (var->name_length));
+          if (var->name == NULL)
+              return SNMPERR_GENERR;
+
+          for(i = 0; i < (int)var->name_length; i++)
+              var->name[i] = var->val.objid[i];
+          break;
+
+      case ASN_OBJECT_ID:
+          var->name_length = var->val_len/sizeof(oid) + 1;
+          var->name = (oid *) malloc(sizeof(oid) * (var->name_length));
+          if (var->name == NULL)
+              return SNMPERR_GENERR;
+
+          var->name[0] = var->name_length-1;
+          for(i = 0; i < (int)var->name_length-1; i++)
+              var->name[i+1] = var->val.objid[i];
+          break;
+        
+      case ASN_PRIV_IMPLIED_OCTET_STR:
+          var->name_length = var->val_len;
+          var->name = (oid *) malloc(sizeof(oid) * (var->name_length));
+          if (var->name == NULL)
+              return SNMPERR_GENERR;
+
+          for(i = 0; i < (int)var->val_len; i++)
+              var->name[i] = (oid) var->val.string[i];
+          break;
+
+      case ASN_OPAQUE:
+      case ASN_OCTET_STR:
+          var->name_length = var->val_len + 1;
+          var->name = (oid *) malloc(sizeof(oid) * (var->name_length));
+          if (var->name == NULL)
+              return SNMPERR_GENERR;
+
+          var->name[0] = (oid) var->val_len;
+          for(i = 0; i < (int)var->val_len; i++)
+              var->name[i+1] = (oid) var->val.string[i];
+          break;
+      
+      default:
+          DEBUGMSGTL(("build_oid_segment",
+                      "invalid asn type: %d\n", var->type));
+          return SNMPERR_GENERR;
+  }
+  
+  if (var->name_length > MAX_OID_LEN) {
+      DEBUGMSGTL(("build_oid_segment",
+                  "Something terribly wrong, namelen = %d\n", var->name_length));
+      return SNMPERR_GENERR;
+  }
+
+  return SNMPERR_SUCCESS;
+}
+
+      
+int build_oid_noalloc(oid *in, size_t in_len, size_t *out_len,
+											oid *prefix, size_t prefix_len,
+											struct variable_list *indexes)
+{
+	struct variable_list *var;
+  
+	if (prefix) {
+		if (in_len < prefix_len)
+			return SNMPERR_GENERR;
+		memcpy(in, prefix, prefix_len * sizeof(oid));
+		*out_len = prefix_len;
+	} else {
+		*out_len = 0;
+	}
+    
+	for(var = indexes; var != NULL; var = var->next_variable) {
+		if (build_oid_segment(var) != SNMPERR_SUCCESS)
+			return SNMPERR_GENERR;
+		if (var->name_length + *out_len < in_len) {
+			memcpy(&(in[*out_len]), var->name, sizeof(oid) * var->name_length);
+			*out_len += var->name_length;
+		} else {
+			return SNMPERR_GENERR;
+		}
+	}
+    
+	DEBUGMSGTL(("build_oid_noalloc", "generated: "));
+	DEBUGMSGOID(("build_oid_noalloc", in, *out_len));
+	DEBUGMSG(("build_oid_noalloc", "\n"));
+	return SNMPERR_SUCCESS;
+}
+
+int build_oid(oid **out, size_t *out_len,
+              oid *prefix, size_t prefix_len,
+              struct variable_list *indexes)
+{
+	oid tmpout[MAX_OID_LEN];
+  
+	if (build_oid_noalloc(tmpout,sizeof(tmpout), out_len,
+												prefix, prefix_len, indexes) != SNMPERR_SUCCESS)
+		return SNMPERR_GENERR;
+	
+	snmp_clone_mem((void **) out, (void *) tmpout, *out_len*sizeof(oid));
+
+	return SNMPERR_SUCCESS;
+}
+
+/* vblist_out must contain a pre-allocated string of variables into
+   which indexes can be extracted based on the previously existing
+   types in the variable chain
+   returns:
+      SNMPERR_GENERR  on error
+      SNMPERR_SUCCESS on success
+*/
+
+int parse_oid_indexes(oid *oidIndex, size_t oidLen,
+                      struct variable_list *data)
+{
+  struct variable_list *var = data;
+  
+  while(var && oidLen > 0) {
+
+	if(parse_one_oid_index(&oidIndex, &oidLen, var, 0) != SNMPERR_SUCCESS)
+	  break;
+
+	var = var->next_variable;
+  }
+  
+  if (var != NULL || oidLen != 0)
+      return SNMPERR_GENERR;
+  return SNMPERR_SUCCESS;
+}
+
+
+int parse_one_oid_index(oid **oidStart, size_t *oidLen,
+                      struct variable_list *data, int complete)
+{
+  struct variable_list *var = data;
+  oid tmpout[MAX_OID_LEN];
+  int i, itmp;
+
+  oid *oidIndex = *oidStart;
+
+  if (var == NULL || ( (*oidLen == 0) && (complete==0) ) )
+      return SNMPERR_GENERR;
+  else {
+    switch(var->type) {
+      case ASN_INTEGER:
+      case ASN_COUNTER:
+      case ASN_GAUGE:
+      case ASN_TIMETICKS:
+          if (*oidLen) {
+          snmp_set_var_value(var, (u_char *) oidIndex++, sizeof(long));
+			--(*oidLen);
+		  } else {
+			snmp_set_var_value(var, (u_char *) oidLen, sizeof(long));
+		  }
+          DEBUGMSGTL(("parse_oid_indexes",
+                      "Parsed int(%d): %d\n", var->type, *var->val.integer));
+          break;
+
+        case ASN_OBJECT_ID:
+        case ASN_PRIV_IMPLIED_OBJECT_ID:
+            if (var->type == ASN_PRIV_IMPLIED_OBJECT_ID) {
+                itmp = *oidLen;
+            } else {
+							if (*oidLen) {
+                itmp = (long) *oidIndex++;
+                --(*oidLen);
+							} else {
+								itmp = 0;
+							}
+                if ( (itmp > (int)*oidLen) && (complete==0) )
+                    return SNMPERR_GENERR;
+            }
+
+			if (itmp > (int)*oidLen) {
+			  memcpy( tmpout, oidIndex, sizeof(oid)*(*oidLen) );
+			  memset( &tmpout[ *oidLen ], 0x00, sizeof(oid)*(itmp-*oidLen) );
+			  snmp_set_var_value(var, (u_char *) tmpout, sizeof(oid)*itmp);
+				oidIndex += *oidLen;
+				(*oidLen) = 0;
+			} else {
+            snmp_set_var_value(var, (u_char *) oidIndex, sizeof(oid)*itmp);
+						oidIndex += itmp;
+						(*oidLen) -= itmp;
+			}
+
+            DEBUGMSGTL(("parse_oid_indexes", "Parsed oid: "));
+            DEBUGMSGOID(("parse_oid_indexes",
+                         var->val.objid, var->val_len/sizeof(oid)));
+            DEBUGMSG(("parse_oid_indexes", "\n"));
+            break;
+
+        case ASN_OPAQUE:
+        case ASN_OCTET_STR:
+        case ASN_PRIV_IMPLIED_OCTET_STR:
+            if (var->type == ASN_PRIV_IMPLIED_OCTET_STR) {
+                itmp = *oidLen;
+            } else {
+							if (*oidLen) {
+                itmp = (long) *oidIndex++;
+                --(*oidLen);
+							} else {
+								itmp = 0;
+							}
+                if ( (itmp > (int)*oidLen) && (complete==0) )
+                    return SNMPERR_GENERR;
+            }
+
+            /* we handle this one ourselves since we don't have
+               pre-allocated memory to copy from using
+               snmp_set_var_value() */
+
+            if (itmp == 0)
+                break;        /* zero length strings shouldn't malloc */
+        
+            /* malloc by size+1 to allow a null to be appended. */
+            var->val_len = itmp;
+            var->val.string = (u_char *) calloc(1,itmp+1);
+            if (var->val.string == NULL)
+                return SNMPERR_GENERR;
+
+						if (itmp > (int)(*oidLen) ) {
+            for(i = 0; i < *oidLen; ++i)
+                var->val.string[i] = (u_char) *oidIndex++;
+            for(i = 0; i < itmp; ++i)
+			  var->val.string[i] = '\0';
+						(*oidLen) = 0;
+						} else {
+							for(i = 0; i < itmp; ++i)
+                var->val.string[i] = (u_char) *oidIndex++;
+							(*oidLen) -= itmp;
+						}
+            var->val.string[itmp] = '\0';
+
+            DEBUGMSGTL(("parse_oid_indexes",
+                        "Parsed str(%d): %s\n", var->type, var->val.string));
+            break;
+
+        default:
+            DEBUGMSGTL(("parse_oid_indexes",
+                        "invalid asn type: %d\n", var->type));
+            return SNMPERR_GENERR;
+    }
+  }
+  (*oidStart) = oidIndex;
+  return SNMPERR_SUCCESS;
+}
+
 int
 dump_realloc_oid_to_string(oid *objid, size_t objidlen,
 			   u_char **buf, size_t *buf_len, size_t *out_len,
@@ -4678,6 +4947,58 @@
   return NULL;
 }
 
+int
+mib_to_asn_type(int mib_type) 
+{
+    switch(mib_type) {
+        case TYPE_OBJID:
+            return ASN_OBJECT_ID;
+            
+        case TYPE_OCTETSTR:
+        case TYPE_IPADDR:
+            return ASN_OCTET_STR;
+            
+        case TYPE_NETADDR:
+            return ASN_IPADDRESS;
+
+        case TYPE_INTEGER32:
+        case TYPE_INTEGER:
+            return ASN_INTEGER;
+            
+        case TYPE_COUNTER:
+            return ASN_COUNTER;
+            
+        case TYPE_GAUGE:
+            return ASN_COUNTER;
+            
+        case TYPE_TIMETICKS:
+            return ASN_TIMETICKS;
+            
+        case TYPE_OPAQUE:
+            return ASN_OPAQUE;
+            
+        case TYPE_NULL:
+            return ASN_NULL;
+
+        case TYPE_COUNTER64:
+            return ASN_COUNTER64;
+
+        case TYPE_BITSTRING:
+            return ASN_BIT_STR;
+
+        case TYPE_UINTEGER:
+        case TYPE_UNSIGNED32:
+            return ASN_UINTEGER;
+
+        case TYPE_NSAPADDRESS:
+            return ASN_NSAP;
+            
+    }
+    return -1;
+}
+
+    
+
 #ifdef CMU_COMPATIBLE
 
 int mib_TxtToOid(char *Buf, oid **OidP, size_t *LenP)
diff --git a/snmplib/mib.h b/snmplib/mib.h
index ff1e76f..25a7bee 100644
--- a/snmplib/mib.h
+++ b/snmplib/mib.h
@@ -32,6 +32,8 @@
 SOFTWARE.
 ******************************************************************/
 
+#include <stdio.h> /* for FILE */
+
 #ifdef CMU_COMPATIBLE
 
 struct	mib_system {
@@ -358,6 +360,16 @@
                     const char *, const char *);
 #endif
 
+int parse_one_oid_index(oid **oidStart, size_t *oidLen,
+						struct variable_list *data, int complete);
+int parse_oid_indexes(oid *oidIndex, size_t oidLen, struct variable_list *data);
+int build_oid_noalloc(oid *in, size_t in_len, size_t *out_len,
+											oid *prefix, size_t prefix_len,
+											struct variable_list *indexes);
+int build_oid(oid **out, size_t *out_len, oid *prefix, size_t prefix_len,
+              struct variable_list *indexes);
+int build_oid_segment(struct variable_list *var);
+    
 int sprint_realloc_hexstring	(u_char **buf, size_t *buf_len,
 				 size_t *out_len, int allow_realloc,
 				 const u_char *, size_t);
@@ -474,6 +486,7 @@
 void snmp_out_toggle_options_usage(const char *, FILE *);
 char *snmp_in_toggle_options(char *);
 void snmp_in_toggle_options_usage(const char *, FILE *);
+int mib_to_asn_type(int mib_type);
 
 #ifdef __cplusplus
 }
diff --git a/snmplib/read_config.c b/snmplib/read_config.c
index 9737fe4..1f8266b 100644
--- a/snmplib/read_config.c
+++ b/snmplib/read_config.c
@@ -1136,6 +1136,7 @@
       }
     } else {
       readfrom = copy_nword(readfrom, (char *)*str, *len);
+      *len = strlen(*str);
     }
   }
 
@@ -1199,6 +1200,9 @@
    Returns: character pointer to the next token in the configuration line.
             NULL if none left.
             NULL if an unknown type.
+
+            dataptr is expected to match a pointer type being read
+            (int *, u_int *, char **, oid **)
 */
 char *read_config_read_data(int type, char *readfrom, void *dataptr, size_t *len) {
   int *intp;
@@ -1236,8 +1240,59 @@
   return NULL;
 }
 
-/* read_config_read_data():
-   reads data of a given type from a token(s) on a configuration line.
+/* read_config_read_memory():
+
+   similar to read_config_read_data, but expects a generic memory
+   pointer rather than a specific type of pointer.  Len is expected to
+   be the amount of available memory.
+*/
+char *read_config_read_memory(int type, char *readfrom,
+                              char *dataptr, size_t *len) {
+  int *intp;
+  unsigned int *uintp;
+
+  if (!dataptr || !readfrom)
+      return NULL;
+  
+  switch(type) {
+    case ASN_INTEGER:
+      if (*len < sizeof(int))
+          return NULL;
+      intp = (int *) dataptr;
+      *intp = atoi(readfrom);
+      *len = sizeof(int);
+      readfrom = skip_token(readfrom);
+      return readfrom;
+      
+    case ASN_UNSIGNED:
+      if (*len < sizeof(unsigned int))
+          return NULL;
+      uintp = (unsigned int *) dataptr;
+      *uintp = strtoul(readfrom, NULL, 0);
+      *len = sizeof(unsigned int);
+      readfrom = skip_token(readfrom);
+      return readfrom;
+
+    case ASN_OCTET_STR:
+    case ASN_BIT_STR:
+    case ASN_PRIV_IMPLIED_OCTET_STR:
+      return read_config_read_octet_string(readfrom, (u_char **) &dataptr, len);
+
+    case ASN_PRIV_IMPLIED_OBJECT_ID:
+    case ASN_OBJECT_ID:
+        readfrom = read_config_read_objid(readfrom, (oid **) &dataptr, len);
+        *len *= sizeof(oid);
+        return readfrom;
+
+    default:
+      DEBUGMSGTL(("read_config_read_memory","Fail: Unknown type: %d", type));
+      return NULL;
+  }
+  return NULL;
+}
+
+/* read_config_store_data():
+   stores data of a given type to a configuration line.
 
    Returns: character pointer to the next token in the configuration line.
             NULL if none left.
diff --git a/snmplib/read_config.h b/snmplib/read_config.h
index 87bfe0e..2f1c352 100644
--- a/snmplib/read_config.h
+++ b/snmplib/read_config.h
@@ -84,6 +84,7 @@
 char *read_config_read_objid(char *readfrom, oid **objid, size_t *len);
 char *read_config_save_objid(char *saveto, oid *objid, size_t len);
 char *read_config_read_data(int type, char *readfrom, void *dataptr, size_t *len);
+char *read_config_read_memory(int type, char *readfrom, char *dataptr, size_t *len);
 char *read_config_store_data(int type, char *storeto, void *dataptr, size_t *len);
 void  read_config_store(const char *type, const char *line);
 void  read_app_config_store(const char *line);
diff --git a/snmplib/snmp-tc.h b/snmplib/snmp-tc.h
index 6623a9d..c022c10 100644
--- a/snmplib/snmp-tc.h
+++ b/snmplib/snmp-tc.h
@@ -15,6 +15,7 @@
 #define TV_FALSE 2
 
 /* RowStatus */
+#define RS_NONEXISTENT    0
 #define RS_ACTIVE	        1
 #define RS_NOTINSERVICE	        2
 #define RS_NOTREADY	        3
@@ -22,13 +23,21 @@
 #define RS_CREATEANDWAIT	5
 #define RS_DESTROY		6
 
+#define RS_IS_GOING_ACTIVE( x ) ( x == RS_CREATEANDGO || x == RS_ACTIVE )
+#define RS_IS_ACTIVE( x ) ( x == RS_ACTIVE )
+#define RS_IS_NOT_ACTIVE( x ) ( ! RS_GOING_ACTIVE(x) )
+
 /* StorageType */
+#define ST_NONE 0
 #define ST_OTHER	1
 #define ST_VOLATILE	2
 #define ST_NONVOLATILE	3
 #define ST_PERMANENT	4
 #define ST_READONLY	5
 
+char check_rowstatus_transition( int old_val, int new_val, int storage_type );
+char check_storage_transition( int old_val, int new_val );
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/snmplib/snmpUnixDomain.h b/snmplib/snmpUnixDomain.h
index 2da2eb1..d4e5fae 100644
--- a/snmplib/snmpUnixDomain.h
+++ b/snmplib/snmpUnixDomain.h
@@ -17,6 +17,12 @@
 
 snmp_transport		*snmp_unix_transport	(struct sockaddr_un *addr,
 						 int local);
+int		snmp_unix_recv	(snmp_transport *t, void *buf, int size,
+				 void **opaque, int *olength);
+int		snmp_unix_send	(snmp_transport *t, void *buf, int size,
+				 void **opaque, int *olength);
+int		snmp_unix_close	(snmp_transport *t);
+int		snmp_unix_accept	(snmp_transport *t);
 
 /*  "Constructor" for transport domain object.  */
 
diff --git a/snmplib/snmp_api.c b/snmplib/snmp_api.c
index 96975ec..b0ef766 100644
--- a/snmplib/snmp_api.c
+++ b/snmplib/snmp_api.c
@@ -188,6 +188,8 @@
     int (*hook_realloc_build)(struct snmp_session *, struct snmp_pdu *,
 			      u_char **, size_t *, size_t *);
     int (*check_packet) (u_char *, size_t);
+   struct snmp_pdu * (*hook_create_pdu)  (snmp_transport *,
+                             void *, size_t);
 
     u_char *packet;
     size_t packet_len, packet_size;
@@ -1178,7 +1180,36 @@
   struct session_list *slp;
   slp = (struct session_list *)snmp_sess_add_ex(in_session, transport,
 						fpre_parse, NULL, fpost_parse,
-						NULL, NULL, NULL);
+						NULL, NULL, NULL, NULL);
+  if (slp == NULL) {
+    return NULL;
+  }
+
+  snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
+  slp->next = Sessions;
+  Sessions = slp;
+  snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
+
+  return (slp->session);
+}
+
+struct snmp_session *snmp_add_full(
+struct snmp_session *in_session,
+snmp_transport *transport,
+int (*fpre_parse) (struct snmp_session *, snmp_transport *, void *, int),
+int (*fparse) (struct snmp_session *, struct snmp_pdu *, u_char *, size_t),
+int (*fpost_parse) (struct snmp_session *, struct snmp_pdu *, int),
+int (*fbuild) (struct snmp_session *, struct snmp_pdu *, u_char *, size_t *),
+int (*frbuild)(struct snmp_session *, struct snmp_pdu *, u_char **,
+	       size_t *, size_t *),
+int (*fcheck) (u_char *, size_t),
+struct snmp_pdu * (*fcreate_pdu) (snmp_transport *, void *, size_t))
+{	
+  struct session_list *slp;
+  slp = (struct session_list *)snmp_sess_add_ex(in_session, transport,
+						fpre_parse, fparse, fpost_parse,
+						fbuild, frbuild,
+                                                fcheck, fcreate_pdu);
   if (slp == NULL) {
     return NULL;
   }
@@ -1202,7 +1233,8 @@
 int (*fbuild) (struct snmp_session *, struct snmp_pdu *, u_char *, size_t *),
 int (*frbuild)(struct snmp_session *, struct snmp_pdu *, u_char **,
 	       size_t *, size_t *),
-int (*fcheck) (u_char *, size_t))
+int (*fcheck) (u_char *, size_t),
+struct snmp_pdu * (*fcreate_pdu) (snmp_transport *, void *, size_t))
 {	
   struct session_list *slp;
 
@@ -1227,6 +1259,7 @@
   slp->internal->hook_build   = fbuild;
   slp->internal->hook_realloc_build = frbuild;
   slp->internal->check_packet = fcheck;
+  slp->internal->hook_create_pdu = fcreate_pdu;
 
   slp->session->rcvMsgMaxSize = transport->msgMaxSize;
 
@@ -1251,7 +1284,7 @@
 int (*fpost_parse) (struct snmp_session *, struct snmp_pdu *, int))
 {	
   return snmp_sess_add_ex(in_session, transport, fpre_parse, NULL,
-			  fpost_parse, NULL, NULL, NULL);
+			  fpost_parse, NULL, NULL, NULL, NULL);
 }
 
 
@@ -4105,6 +4138,27 @@
     free((char *)pdu);
 }
 
+struct snmp_pdu *
+snmp_create_sess_pdu(snmp_transport *transport, void *opaque, size_t olength) 
+{
+    struct snmp_pdu *pdu;
+    pdu = (struct snmp_pdu *)calloc(1,sizeof(struct snmp_pdu));
+    if (pdu == NULL) {
+        DEBUGMSGTL(("sess_process_packet", "can't malloc space for PDU\n"));
+        return NULL;
+    }
+
+    /*  Save the transport-level data specific to this reception (e.g. UDP
+      source address).  */
+
+    pdu->transport_data        = opaque;
+    pdu->transport_data_length = olength;
+    pdu->tDomain		     = transport->domain;
+    pdu->tDomainLen	     = transport->domain_length;
+    return pdu;
+}
+
+
 /*  This function processes a complete (according to asn_check_packet or the
     AgentX equivalent) packet, parsing it into a PDU and calling the relevant
     callbacks.  On entry, packetptr points at the packet in the session's
@@ -4149,19 +4203,15 @@
     }
   }
 
-  pdu = (struct snmp_pdu *)calloc(1,sizeof(struct snmp_pdu));
-  if (pdu == NULL) {
-    DEBUGMSGTL(("sess_process_packet", "can't malloc space for PDU\n"));
-    return -1;
+  if (isp->hook_create_pdu) {
+      pdu = isp->hook_create_pdu(transport, opaque, olength);
+  } else {
+      pdu = snmp_create_sess_pdu(transport, opaque, olength);
   }
-
-  /*  Save the transport-level data specific to this reception (e.g. UDP
-      source address).  */
-
-  pdu->transport_data        = opaque;
-  pdu->transport_data_length = olength;
-  pdu->tDomain		     = transport->domain;
-  pdu->tDomainLen	     = transport->domain_length;
+  if (!pdu) {
+      snmp_log(LOG_ERR, "pdu failed to be created\n");
+      return -1;
+  }
 
   if (isp->hook_parse) {
     ret = isp->hook_parse(sp, pdu, packetptr, length);
@@ -4412,7 +4462,8 @@
 	nslp = (struct session_list *)snmp_sess_add_ex(sp, new_transport,
 				isp->hook_pre, isp->hook_parse, 
 				isp->hook_post, isp->hook_build,
-				isp->hook_realloc_build, isp->check_packet);
+				isp->hook_realloc_build, isp->check_packet,
+                                isp->hook_create_pdu);
 
 	if (nslp != NULL) {
 	  nslp->next = Sessions;
@@ -5019,6 +5070,12 @@
     }
 }
 
+int
+snmp_oid_ncompare(const oid *in_name1, const oid*in_name2, int len)
+{
+    return snmp_oid_compare(in_name1, len, in_name2, len);
+}
+
 /* lexicographical compare two object identifiers.
  * Returns -1 if name1 < name2,
  *          0 if name1 = name2,
diff --git a/snmplib/snmp_api.h b/snmplib/snmp_api.h
index ee6ebb9..ac72c8b 100644
--- a/snmplib/snmp_api.h
+++ b/snmplib/snmp_api.h
@@ -743,7 +743,8 @@
 					 snmp_pdu *, u_char *, size_t *), 
 			  int (*frbuild)(struct snmp_session *, struct
 				   snmp_pdu *, u_char **, size_t *, size_t *), 
-			  int (*fcheck) (u_char *, size_t));
+			  int (*fcheck) (u_char *, size_t),
+                          struct snmp_pdu * (*fcreate_pdu) (struct _snmp_transport *, void *, size_t));
 
 void   *snmp_sess_add	(struct snmp_session *, struct _snmp_transport *,
 			 int (*fpre_parse) (struct snmp_session *,
@@ -758,6 +759,16 @@
 						 void *, int),
 			      int (*fpost_parse) (struct snmp_session *,
 						  struct snmp_pdu *, int));
+struct snmp_session *snmp_add_full(struct snmp_session *in_session,
+                                   struct _snmp_transport *transport,
+                                   int (*fpre_parse) (struct snmp_session *, struct _snmp_transport *, void *, int),
+                                   int (*fparse) (struct snmp_session *, struct snmp_pdu *, u_char *, size_t),
+                                   int (*fpost_parse) (struct snmp_session *, struct snmp_pdu *, int),
+                                   int (*fbuild) (struct snmp_session *, struct snmp_pdu *, u_char *, size_t *),
+                                   int (*frbuild)(struct snmp_session *, struct snmp_pdu *, u_char **, size_t *, size_t *),
+                                   int (*fcheck) (u_char *, size_t),
+                                   struct snmp_pdu * (*fcreate_pdu) (struct _snmp_transport *, void *, size_t)
+);
 
 /* use return value from snmp_sess_open as void * parameter */
 
@@ -832,7 +843,13 @@
 #define  STAT_SNMP_STATS_START               STAT_SNMPINPKTS
 #define  STAT_SNMP_STATS_END                 STAT_SNMPOUTTRAPS
 
-#define  MAX_STATS                           41
+/* target mib counters */
+#define  STAT_SNMPUNAVAILABLECONTEXTS	     41
+#define  STAT_SNMPUNKNOWNCONTEXTS	     42
+#define  STAT_TARGET_STATS_START             STAT_SNMPUNAVAILABLECONTEXTS
+#define  STAT_TARGET_STATS_END               STAT_SNMPUNKNOWNCONTEXTS
+
+#define  MAX_STATS                           43
 
 #ifdef __cplusplus
 }
diff --git a/snmplib/snmp_client.c b/snmplib/snmp_client.c
index b94c9e2..001d35b 100644
--- a/snmplib/snmp_client.c
+++ b/snmplib/snmp_client.c
@@ -265,6 +265,28 @@
 
 
 /*
+ * Walks through a list of varbinds and frees and allocated memory,
+ * restoring pointers to local buffers
+ */
+void
+snmp_reset_var_buffers( struct variable_list * var )
+{
+    while( var ) {
+        if(var->name != var->name_loc) {
+            free(var->name);
+            var->name = var->name_loc;
+            var->name_length = 0;
+        }
+        if(var->val.string != var->buf) {
+            free(var->val.string);
+            var->val.string = var->buf;
+            var->val_len = 0;
+        }
+        var = var->next_variable;
+    }
+}
+
+/*
  * Creates and allocates a clone of the input PDU,
  * but does NOT copy the variables.
  * This function should be used with another function,
@@ -571,7 +593,8 @@
         vp->name = (oid *)malloc(len);
         if (!vp->name) return 1;
     }
-    memmove(vp->name, objid, len);
+    if (objid)
+        memmove(vp->name, objid, len);
     vp->name_length = name_length;
     return 0;
 }
@@ -584,6 +607,38 @@
     return snmp_set_var_value(newvar, val_str, val_len);
 }
 
+int
+count_varbinds( struct variable_list *var_ptr )
+{
+  int count = 0;
+
+  for (  ; var_ptr != NULL ; var_ptr = var_ptr->next_variable )
+	count++;
+
+  return count;
+}
+
+int
+count_varbinds_of_type( struct variable_list *var_ptr, int type )
+{
+  int count = 0;
+  
+  for (;  var_ptr != NULL ; var_ptr = var_ptr->next_variable )
+      if (var_ptr->type == type)
+          count++;
+
+  return count;
+}
+
+struct variable_list *
+find_varbind_of_type( struct variable_list *var_ptr, int type )
+{
+  for (;  var_ptr != NULL && var_ptr->type != type ;
+       var_ptr = var_ptr->next_variable );
+
+  return var_ptr;
+}
+
 /*
  * Add some value to SNMP variable.
  * If the value is large, additional memory is allocated.
@@ -613,8 +668,12 @@
         }
         memmove(newvar->val.string, val_str, val_len);
         newvar->val_len = val_len;
+    } else if (val_str) {
+        /* NULL STRING != NULL ptr */
+        newvar->val.string = newvar->buf;
+        newvar->val.string[0] = '\0';
+        newvar->val_len = 0;
     }
-
     return 0;
 }
 
diff --git a/snmplib/snmp_client.h b/snmplib/snmp_client.h
index 011a43c..c338bbd 100644
--- a/snmplib/snmp_client.h
+++ b/snmplib/snmp_client.h
@@ -53,7 +53,12 @@
                              u_char *val_str, size_t val_len);
 void snmp_replace_var_types(struct variable_list *vbl, int old_type,
                             int new_type);
+void snmp_reset_var_buffers( struct variable_list * var );
 void snmp_reset_var_types(struct variable_list *vbl, int new_type);
+int count_varbinds( struct variable_list *var_ptr );
+int count_varbinds_of_type( struct variable_list *var_ptr, int type );
+struct variable_list *find_varbind_of_type( struct variable_list *var_ptr,
+                                            int type );
 
 struct variable_list* snmp_add_null_var (struct snmp_pdu *, oid *, size_t);
 struct snmp_pdu	*snmp_pdu_create (int);
diff --git a/snmplib/snmp_transport.c b/snmplib/snmp_transport.c
index fa2c6d8..22b41e6 100644
--- a/snmplib/snmp_transport.c
+++ b/snmplib/snmp_transport.c
@@ -29,6 +29,7 @@
 #include "snmp_api.h"
 #include "snmp_debug.h"
 #include "snmp_logging.h"
+#include "tools.h"
 
 
 /*  Our list of supported transport domains.  */
@@ -333,3 +334,49 @@
   free(mystring);
   return NULL;
 }
+
+
+/** adds a transport to a linked list of transports.
+    Returns 1 on failure, 0 on success */
+int
+snmp_transport_add_to_list(snmp_transport_list **transport_list,
+                           snmp_transport *transport) {
+    snmp_transport_list *newptr = SNMP_MALLOC_TYPEDEF(snmp_transport_list);
+
+    if (!newptr)
+        return 1;
+
+    newptr->next = *transport_list;
+    newptr->transport = transport;
+    
+    *transport_list = newptr;
+
+    return 0;
+}
+
+
+/**  removes a transport from a linked list of transports.
+     Returns 1 on failure, 0 on success */
+int
+snmp_transport_remove_from_list(snmp_transport_list **transport_list,
+                                snmp_transport *transport) {
+    snmp_transport_list *ptr = *transport_list, *lastptr = NULL;
+
+    while(ptr && ptr->transport != transport) {
+        lastptr = ptr;
+        ptr = ptr->next;
+    }
+
+    if (!ptr)
+        return 1;
+
+    if (lastptr)
+        lastptr->next = ptr->next;
+    else
+        *transport_list = ptr->next;
+    
+    free(ptr);
+
+    return 0;
+}
+
diff --git a/snmplib/snmp_transport.h b/snmplib/snmp_transport.h
index a0c89e4..4bb6948 100644
--- a/snmplib/snmp_transport.h
+++ b/snmplib/snmp_transport.h
@@ -79,11 +79,15 @@
   char *(*f_fmtaddr) (struct _snmp_transport *, void *, int);
 } snmp_transport;
 
+typedef struct snmp_transport_list_s {
+   snmp_transport *transport;
+   struct snmp_transport_list_s *next;
+} snmp_transport_list;
+
 typedef struct _snmp_tdomain {
   const oid		*name;
   size_t		 name_length;
   const char 	       **prefix;
-
   snmp_transport	*(*f_create)(const char *, int);
 
   struct _snmp_tdomain	*next;
@@ -92,6 +96,11 @@
 
 /*  Some utility functions.  */
 
+int snmp_transport_add_to_list(snmp_transport_list **transport_list,
+                               snmp_transport *transport);
+int snmp_transport_remove_from_list(snmp_transport_list **transport_list,
+                                    snmp_transport *transport);
+    
 
 /*  Return an exact (deep) copy of t, or NULL if there is a memory allocation
     problem (for instance).  */
diff --git a/snmplib/tools.c b/snmplib/tools.c
index def2ec4..0b62dc9 100644
--- a/snmplib/tools.c
+++ b/snmplib/tools.c
@@ -714,3 +714,26 @@
 
   return 1;
 }
+
+
+	/*
+	 * Time-related utility functions
+	 */
+
+		/* Return the number of timeTicks since the given marker */
+int
+marker_tticks( marker_t pm )
+{
+    int res;
+    marker_t now = atime_newMarker();
+
+    res = atime_diff( pm, now );
+    free( now );
+    return res/10;      /* atime_diff works in msec, not csec */
+}
+
+int timeval_tticks( struct timeval *tv )
+{
+    return marker_tticks((marker_t)tv);
+}
+
diff --git a/snmplib/tools.h b/snmplib/tools.h
index ddc1f94..6401365 100644
--- a/snmplib/tools.h
+++ b/snmplib/tools.h
@@ -148,6 +148,9 @@
 long atime_diff( marker_t first, marker_t second );
 int atime_ready( marker_t pm, int deltaT);
 
+int marker_tticks( marker_t pm );
+int timeval_tticks( struct timeval *tv );
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/snmplib/vacm.c b/snmplib/vacm.c
index 8e482de..ac87acc 100644
--- a/snmplib/vacm.c
+++ b/snmplib/vacm.c
@@ -537,7 +537,13 @@
         if ((securityModel == vp->securityModel || vp->securityModel == SNMP_SEC_MODEL_ANY)
 	    && securityLevel >= vp->securityLevel
 	    && !memcmp(vp->groupName, group, glen+1)
-	    && !memcmp(vp->contextPrefix, context, clen+1))
+            && ((vp->contextMatch == CONTEXT_MATCH_EXACT
+                 && clen == vp->contextPrefix[0]
+                 && (memcmp(vp->contextPrefix, context, clen+1) == 0))
+                || (vp->contextMatch == CONTEXT_MATCH_PREFIX
+                    && clen >= vp->contextPrefix[0]
+                    && (memcmp(vp->contextPrefix + 1, context + 1,
+                               vp->contextPrefix[0]) == 0))))
 	  return vp;
     }
     return NULL;