#!/usr/bin/perl
#!/usr/bin/perl

require 5;

require Tk::Table;
require Tk::HList;
require Tk::FileSelect;
require Tk::Dialog;

$host = 'localhost';
$OID = '.1.3.6.1';

# default session options
%session_opts = (Community    => "public",
		 RemotePort   => 161,
		 Timeout      => 5000000,
		 Retries      => 5,
		 Version      => 1,
		 AuthProto    => 'MD5',
		 PrivProto    => 'DES',
		 AuthPass     => '',
		 PrivPass     => '',
		 Context      => 'default',
		 SecName      => 'initial',
		 SecLevel     => 'authNoPriv',
		 UseLongNames => 1);

while($#ARGV > -1 && $ARGV[0] =~ /^-/) {
    $_ = shift;
    $session_opts{'RemotePort'} = shift if (/-p/);
    $findoid = shift if (/-o/);
}
$host = shift if ($#ARGV > -1);
$session_opts{'Community'} = shift if ($#ARGV > -1);

$graphtime=5;
$graphdelta=1;
@graphcolors=qw(blue red green yellow purple);

@displayInfo=qw(type access status units hint moduleID enums indexes);

use Tk;
require Tk::HList;
use SNMP;
$SNMP::save_descriptions=1;
$SNMP::use_long_names=1;
$SNMP::use_enums=1;
SNMP::initMib();
$SNMP::verbose = 1;

my $tmpbd = 3;

$top = MainWindow->new();
$top->title("tkmib");

#Menus
$MenuFrame = $top->Frame(-relief => "raised",-borderwidth => 2);
$MenuFrame->pack(-fill => "x",-expand => 1);
$FileMenuBut = $MenuFrame->Menubutton(-text => "File",
				      -menuitems =>
      [
#       [Button => "Save Output", -command => [\&saveOutput]],
       [Button => "Quit",        -command => [\&exit]]
       ]);
$FileMenuBut->pack(-side => 'left');

$MibMenuBut = $MenuFrame->Menubutton(-text => "Mib",
				      -menuitems =>
      [[Button => "Find a mib node", 
	-command => sub { my $var;
			  entryBox("Find a Mib Node", 
				   "Enter a mib node name to search for:",
				   \$var, \&findANode );}],
       [Button => "Load a New Mib File", -command => [\&loadNewMibFile]],
       [Button => "Load a New Mib Module", 
	-command => sub { my $var;
			  entryBox("Load a Module", 
				   "Enter a SNMP MIB module name to load:",
				   \$var, \&loadIt);}]
       ]);
$MibMenuBut->pack(-side => 'left');

$OptMenuBut = $MenuFrame->Menubutton(-text => "Options",
				     -menuitems =>
      [[Cascade => "~Display"],
       [Cascade => "Use SNMP Version", -menuitems =>
	[
	 [Radiobutton => '1', -variable => \$session_opts{'Version'}],
	 [Radiobutton => '2c', -variable => \$session_opts{'Version'}],
	 [Radiobutton => '3', -variable => \$session_opts{'Version'}]
	]
       ], # ends version number specification
       [Cascade => "SNMPv1/2c options", -menuitems =>
	[
	 [Button => "Community Name", 
	  -command => [\&entryBox,"Community Name", "Community name to use:", 
		       \$session_opts{'Community'}]]
	]
       ],
       [Cascade => "SNMP3 options", -menuitems =>
	[
	 [Button => "Security Name", 
	  -command => [\&entryBox,"Security Name", "Security Name to use:", 
		       \$session_opts{'SecName'}]],
         [Cascade => "Security Level", -menuitems =>
          [
	   [Radiobutton => 'noAuthNoPriv',
	    -variable => \$session_opts{'SecLevel'}],
           [Radiobutton => 'authNoPriv', 
	    -variable => \$session_opts{'SecLevel'}],
	   [Radiobutton => 'authPriv', 
            -variable => \$session_opts{'SecLevel'}]
          ]
         ],
	 [Button => "Authentication Passphrase", 
	  -command => [\&entryBox,"Authentication Passphrase", 
		       "Authentication Passphrase to use:", 
		       \$session_opts{'AuthPass'}]],
         [Cascade => "Authentication Type", -menuitems =>
          [
	   [Radiobutton => 'MD5',
	    -variable => \$session_opts{'AuthProto'}],
	   [Radiobutton => 'SHA',
	    -variable => \$session_opts{'AuthProto'}],
          ]
         ],
	 [Button => "Privacy Passphrase", 
	  -command => [\&entryBox,"Privacy Passphrase", 
		       "Privacy Passphrase to use:", 
		       \$session_opts{'PrivPass'}]],
         [Cascade => "Privacy Type", -menuitems =>
          [
	   [Radiobutton => 'DES',
	    -variable => \$session_opts{'PrivProto'}],
          ]
         ],
	]
       ],
       [Button => "Time between graph polls", 
	-command => sub { entryBox("graph polls", "Time between graph polls:", 
				    \$graphtime);}],
       [Button => "Port number", 
	-command => sub { entryBox("Port Number", "SNMP Port number to use:", 
				    \$session_opts{'RemotePort'});}],
       [Button => "TimeOut", 
	-command => sub { entryBox("Time Out", "Timeout for SNMP requests:", 
				    \$session_opts{'Timeout'});}],
       [Button => "Retries", 
	-command => sub { entryBox("Retries", 
				   "Number of Times to Retransmit Requests:", 
				    \$session_opts{'Retries'});}],
       ])->pack(-side => 'left');
        
$tmp = $OptMenuBut->cget(-menu);
$OptMenuWidgets = $tmp->entrycget("Display", -menu);

# $OptMenuBut->cascade(-label => 'sub');
#$sub1 = $OptMenuBut->Optionmenu(-text => 'options', -options => ['hehe',["hi", "there"]]);
#$sub1->pack();
# $OptMenuBut->entryconfigure('sub', -menu => $sub1);

$hlist=$top->Scrolled(qw(HList -itemtype imagetext -browsecmd main::showInfo
			 -command main::showChildren -width 80 -height 15));
$hlist->pack(-side => 'top', -expand => 1, -fill => 'both');
my $sFrame = $top->Frame(-relief => 'raised', -borderwidth => $tmpbd);
$sFrame->pack(-side => 'top', -fill => 'x');
$sFrame->Label(-text => 'OID: ', -relief => 'raised', -borderwidth => $tmpbd)
    ->pack(-side => 'left');
$mibOID = $sFrame->Entry(-textvariable => \$OID, -relief => 'flat');
$mibOID->pack(-side => 'left');
$mibTextOID = $sFrame->Label(-text => '');
$mibTextOID->pack(-side => 'right');

$dispFrame=$top->Frame(-relief => 'raised', -borderwidth => $tmpbd);
$dispFrame->pack(-side => 'top', -fill =>'x');
for($i=0;$i<= $#displayInfo;$i++) {
    createRow($i);
    optionalWidget($i,$OptMenuWidgets);
}
    
$descrFrame=$top->Frame(-relief => 'raised', -borderwidth => $tmpbd);
$descrFrame->pack(-side => 'top', -fill =>'x');
$descrFrame->Label(-text => 'Description:', -anchor => 'w')->pack(-side => 'top',
								-fill => 'x');
$descr = $descrFrame->Scrolled(qw(Text -width 80 -height 4));
$descr->pack(-side => 'top', -fill => 'x');

$bFrame = $top->Frame(-relief => 'raised', -borderwidth => $tmpbd);
$bFrame->pack(-side => 'top', -fill => 'x');
$hostEntry = $bFrame->Entry(-textvariable => \$host, -width => 12);
$hostEntry->pack(-side => 'left');
$bFrame->Button(-text => 'graph', -command => \&snmpgraph)->pack(-side => 'right');
$bFrame->Button(-text => 'table', -command => \&snmptable)->pack(-side => 'right');
$bFrame->Button(-text => 'walk', -command => \&snmpwalk)->pack(-side => 'right');
$bFrame->Button(-text => 'getnext', -command => \&snmpgetnext)->pack(-side => 'right');
$bFrame->Button(-text => 'get', -command => \&snmpget)->pack(-side => 'right');
$stopBut = $bFrame->Button(-text => 'stop', -command => sub { stop(1) },
			   -state => 'disabled');
$stopBut->pack(-side => 'right');
$oFrame = $top->Frame(-borderwidth => $tmpbd, -relief => 'raised');
$oFrame->pack(-side => 'top', -fill => 'both');
$output = $oFrame->Scrolled(qw(Text -width 80 -height 14));
$output->pack(-side => 'top', -fill => 'both', -expand => 1);

$tmpFrame = $top->Frame(-relief => 'raised', -borderwidth => $tmpbd);
$tmpFrame->pack(-side => 'top', -fill => 'x');
$tmpFrame->Label(-text => "Status:  ", -anchor => 'w')
#		 -relief => 'raised', -borderwidth => $tmpbd)
    ->pack(-side => 'left');
$status = $tmpFrame->Label(-anchor => 'w');
$status->pack(-side => 'left', -fill => 'x');

# initialize the browser
foreach $i (qw(.1 .1.3 .1.3.6 .1.3.6.1)) {
    addMibOID($i);
}
showChildren("$OID");
if (defined($findoid)) {
    findANode($findoid);
}

MainLoop();

sub printit {
    while($a = shift) {
	print ": $a\n";
    }
}

sub insertresult {
    my $oid = shift;
    my $val = shift;
    $output->insert('end', $oid, "oid:$oid");
    $output->tagBind("oid:$oid", '<1>', [sub{shift; 
					     my $oid = shift;
					     findANode($oid);
					     my $tag = SNMP::translateObj($oid);
					     showInfo($tag);},$oid]);
    $output->insert('end', " = ");
    $output->insert('end', $val, "value:$val");
    $output->insert('end', "\n");
}

sub insertvar {
    my $var = shift;
    my $name;
    if ($var->tag !~ /[a-z]/) {
	$name = SNMP::translateObj($var->tag);
    } else {
	$name = "$var->[$SNMP::Varbind::tag_f].$var->[$SNMP::Varbind::iid_f]";
    }

    insertresult($name,"$var->[$SNMP::Varbind::val_f]");
}

sub snmpsetup {
    my $oid = $OID;
    my $tag = SNMP::translateObj($oid);
    my $sess = new SNMP::Session(DestHost => $host, %session_opts);
    print "session_opts:\n";
    foreach $i (keys(%session_opts)) {
	print "  $i => $session_opts{$i}\n";
    }
    print "\n";
    my $var = new SNMP::Varbind([$oid]);
    if (!defined($var)) {
	print "ack:  $@ $SNMP::ErrorStr $!\n";
    }
    stop(0);
    initText();
    print "oid: $oid\n";
    return ($oid, $sess, $var);
}

sub initText {
    if (ref($output) eq "Tk::Frame" && defined($$output{'_#text'})) {
	$output->delete('0.0','end');
    } else {
	$output->destroy();
	$output = $oFrame->Scrolled(qw(Text -width 80 -height 14));
	$output->pack(-side => 'top', -fill => 'both', -expand => 1);
    }
}

sub initTable {
    $output->destroy();
    $oFrame->packPropagate(0);
    $output = $oFrame->Table(-columns => shift, -width => 80, -height => 14,
			     -fixedrows => 2, -fixedcolumns => 2);
    $output->pack(-side => 'top', -fill => 'both', -expand => 1);
}

sub initCanvas {
    $output->destroy();
    $oFrame->packPropagate(0);
    $output = $oFrame->Scrolled(qw(Canvas -width 80c -height 14c));
    $output->pack(-side => 'top', -fill => 'both', -expand => 1);
}

sub snmpget {
    (my $oid, my $sess, my $var) = snmpsetup();
    print "getting:  $host $community $oid\n";
    $status->configure(-text => "getting:  $host $community $oid");
    $top->update();
    my $val = $sess->get($var);
    if ($sess->{ErrorStr}) {
	$status->configure(-text => $sess->{ErrorStr});
    } else {
	insertvar($var);
	$status->configure(-text => "");
    }
}

sub snmpgetnext {
    (my $oid, my $sess, my $var) = snmpsetup();
    print "getnexting:  $host $community $oid\n";
    $status->configure(-text => "get next:  $host $community $oid");
    $top->update();
    my $val = $sess->getnext($var);
    if ($sess->{ErrorStr}) {
	$status->configure(-text => $sess->{ErrorStr});
    } else {
	insertvar($var);
	$status->configure(-text => "");
    }
}

sub snmpwalk {
    (my $oid, my $sess, my $var) = snmpsetup();
    $status->configure(-text => "walking:  $host $community $oid");
    $top->update();
    while (!$sess->{ErrorStr} && !$stopit) {
	my $val = $sess->getnext($var);
	last if (!defined($var->tag) ||
		 $sess->{ErrorStr} ||
	         SNMP::translateObj($var->tag) !~ /^$oid/);
	insertvar($var);
	$top->update();
    }
    if ($sess->{ErrorStr}) {
	$status->configure(-text => $sess->{ErrorStr});
	$output->insert('end',"$sess->{ErrorStr} ($sess->{ErrorNum})\n");
    } else {
	$status->configure(-text => "");
    }
    stop(1);
}

sub snmptable {
    (my $oid, my $sess, my $var) = snmpsetup();
    $status->configure(-text => "collecting data:  $host $community $oid");
    $top->update();
    my (@tb, @tags, @index);
    while (!$sess->{ErrorStr} && !$stopit) {
	my $val = $sess->getnext($var);
	last if (!defined($var->tag) ||
		 $sess->{ErrorStr} ||
	         SNMP::translateObj($var->tag) !~ /^$oid/);
	$newoid = "$var->[$SNMP::Varbind::tag_f].$var->[$SNMP::Varbind::iid_f]";
	insertvar($var);
	$top->update();
	$newoid =~ /([^\.]+)\.([0-9\.]+)$/;
	if (!grep(/$1/,@tags)) {
	    push @tags,$1;
	}
	if (!grep(/$2/,@index)) {
	    push @index,$2;
	}
	$tb{$2}{$1} = $var->val;
    }
    initTable($#tags+1);
    for(my $k=0;$k <= $#tags;$k++) {
	$output->put(1,$k+2,$tags[$k]);
    }
    $output->put(1,1,"Index");
    for(my $i=0;$i <= $#index;$i++) {
	$output->put($i+2,1,$index[$i]);
    }
    for(my $i=0;$i <= $#index; $i++) {
	for(my $k=0;$k <= $#tags;$k++) {
	    $output->put($i+2,$k+2,$tb{$index[$i]}{$tags[$k]});
	}
    }
    $status->configure(-text => "");
    stop(1);
}

sub snmpgraph {
    ($graphoid, $graphsess, my $graphvar) = snmpsetup();
    $top->update();
    %graphtb = ();
    @graphvars = ();
    initCanvas();
    $gcount=0;
    $max=-1;
    $min=2**32-1;
    updateGraph();
    $output->repeat($graphtime*1000, \&updateGraph);
}

sub updateGraph() {
    $status->configure(-text => "collecting data:  $host $community $graphoid");
    print "updating\n";
    my $oid = $graphoid;
    my $tag = SNMP::translateObj($graphoid,0);
    my $var = new SNMP::Varbind([$oid]);
    $graphsess->{ErrorStr} = "";    
    while (!$graphsess->{ErrorStr} && !$stopit) {
	my $val = $graphsess->getnext($var);
	if ($#graphvars == -1 && SNMP::translateObj($var->tag) !~ /^$oid/) {
	    # if an exact oid, do a get instead.
	    $var = new SNMP::Varbind([$oid]);
	    $val = $graphsess->get($var);
	}
	if ($graphsess->{ErrorStr} ||
	    !defined($var->tag) ||
	    SNMP::translateObj($var->tag) !~ /^$oid/) {
	    last;
	}
	my $newoid = SNMP::translateObj("$var->[$SNMP::Varbind::tag_f].$var->[$SNMP::Varbind::iid_f]");
	$top->update();
	$newoid =~ /$oid\.([0-9\.]+)$/;
	if (defined($1)) {
	    if (!grep(/$1/,@graphvars)) {
		push @graphvars,$1;
	    }
	    if ($graphdelta) {
		if ($gcount > 0) {
		    $graphtb{$1}[$gcount-1] = $var->val - $prev{$1};
		}
		$prev{$1} = $var->val;
	    } else {
		$graphtb{$1}[$gcount] = $var->val;
	    }
	    $max = $graphtb{$1}[$#{$graphtb{$1}}] 
		if ($#{$graphtb{$1}} >= 0 &&
		    $graphtb{$1}[$#{$graphtb{$1}}] > $max);
	    $min = $graphtb{$1}[$#{$graphtb{$1}}] 
		if ($#{$graphtb{$1}} >= 0 &&
		    $graphtb{$1}[$#{$graphtb{$1}}] < $min);
	}
    }
    if ($gcount > 1) {
	$output->delete('all');
	my $canvas = $$output{'SubWidget'}{'canvas'};
	my $h=$canvas->cget(-height);
	foreach $i (@graphvars) {
	    my @a = ();
	    for(my $j=0; $j <= $#{$graphtb{$i}}; $j++) {
		$a[$j*2] = $j;
		$a[$j*2+1] = $h-(($h-3)*($graphtb{$i}[$j]-$min))/($max-$min)-3;
	    }
	    $output->createLine(@a, -fill => $graphcolors[$i%$#graphcolors]);
	}
	$output->create('text',5, $h-3, -text => "$max");
	$output->create('text',5, 3, -text => "$min");
    }
    $gcount++;
    $status->configure(-text => "sleeping for $graphtime seconds");
}

sub addMibOID {
    my $i = shift;
    $i = ".$i" if ($i !~ /^\./);
    my $name = SNMP::translateObj($i,1);
    if (defined($name)) {
	$name =~ s/.*\.([^.]+)$/$1/;
    } else {
	return;
    }
    $i =~ s/^\.//;
    $hlist->add($i, -text => $name);
}

sub showInfo {
    my $full = shift;
    $full = ".$full" if ($full !~ /^\./);
    my $oid = $full;
    my $tag = $oid;

    if ($tag =~ /^[.0-9]+$/) {
	# strip off index in case there is one
	$tag = SNMP::translateObj("$oid");
	$tag = ".iso.org.dod.internet.private.$tag" if $tag =~ /^enterprises/;
    } else {
	$full = SNMP::translateObj("$oid");
    }

    $tag =~ s/[.0-9]+$//;
    $oid = SNMP::translateObj($tag);
	
    if (!defined($last) || "$last" ne $oid) {
	updateInfo($oid);
    }
    $OID = $full;
    $mibOID->configure(-textvariable => \$OID);
    $mibOID->update();
    $last = $oid;
}
	
sub showAllChildren {
    my $id = shift;
    $id =~ s/^\.//;
    my @pieces = split(/\./,$id);
    my ($i, $lastvalid);
    for($i = 0; $i <= $#pieces; $i++) {
	my $a = join(".", @pieces[0..$i]);
	if ($hlist->infoExists($a) && !($hlist->infoChildren($a))) {
	    showChildren(join(".", $a));
	}
	if ($hlist->infoExists($a)) {
	    $lastvalid = $a;
	} else {
	    last;
	}
    }
    $hlist->see($lastvalid);
    $hlist->selectionClear($hlist->selectionGet);
    $hlist->selectionSet($lastvalid);
}

sub showChildren {
    $OID = shift;
    $OID =~ s/^\.//;
    my $oid = $OID;
    $mibOID->configure(-textvariable => \$OID);
    if ($hlist->infoChildren($oid)) {
	my @a = $hlist->infoChildren($oid);
	my $i;
	foreach $i (@a) {
	    $hlist->deleteEntry($i);
	}
    } else {
	$oid = ".$oid";
	my $mib = $SNMP::MIB{$oid};
	if (defined($mib)) {
	    my $children = $$mib{'children'}; 
	    if (ref($children) eq "ARRAY") {
		foreach $i (sort {$$a{'subID'} <=> $$b{'subID'}} @{$children}) {
		    addMibOID($$i{'objectID'});
		}
	    } else {
		$status->configure(-text => SNMP::translateObj($oid,1) . 
				   " has no children");
		return;
	    }
	}
    }
    $status->configure(-text => "");
}

sub updateInfo {
    $OID = shift;
    my $oid = $OID;
    my $mib = $SNMP::MIB{"$oid"};
    if (!defined($mib->{'description'}) || $mib->{'description'} eq "") {
	$oid =~ s/[.0-9]+$//;
	$mib = $SNMP::MIB{"$oid"};
    }
    if (defined($mib)) {
	$mibOID->configure(-text => $mib->{'objectID'});
	$mibTextOID->configure(-text => 
			     SNMP::translateObj($mib->{'objectID'},1));
	$descr->delete('0.0','end');
	if (defined($mib->{'description'}) && 
	    $mib->{'description'} ne "") {
	    my $desc = $mib->{'description'};
	    $desc =~ s/\n[ \t]+/\n/g;
 	    $desc =~ s/^\n//;
	    $descr->insert('end',$desc);
	}
	for($i=0; $i<= $#displayInfo;$i++) {
	    $dpyInfo[$i] = $mib->{$displayInfo[$i]};
	    if (ref($dpyInfo[$i]) eq HASH) {
		my %hash = %{$dpyInfo[$i]};
		$dpyInfo[$i] = "";
		foreach $j (sort { $hash{$a} <=> $hash{$b} } keys(%hash)) {
		    $dpyInfo[$i] .= "$j = $hash{$j},";
		}
	    } elsif (ref($dpyInfo[$i]) eq ARRAY) {
		$dpyInfo[$i] = join(", ", @{$dpyInfo[$i]});
	    }
	}
    }
}

sub optionalWidget {
    my $num = shift;
    my $menu = shift;
    my $state = 1;
    $menu->checkbutton(-label => $displayInfo[$num], 
		       -variable => \$state,
		       -command => [\&toggleWidgetShown, $num]);
}

sub createRow {
    my $i = shift;
    $dispFrame->Label(-text => $displayInfo[$i], 
		     -anchor => 'w', -borderwidth => $tmpbd)
	->grid(-column => ($i%2)*2, -row => int($i/2), -sticky => 'w');
    $dpyInfo[$i] = "" if (!defined($dpyInfo[$i]));
    my $ent = $dispFrame->Entry(-textvariable => \$dpyInfo[$i], -width => 40, 
		      -relief => 'flat', -borderwidth => $tmpbd);
    $ent->grid(-column => ($i%2)*2 + 1, -row => int($i/2), -sticky => 'w');
}

sub toggleWidgetShown {
    my $self = shift;
    my @widgets = $dispFrame->gridSlaves(-row => $self);
    my $i;
    if ($#widgets >= 0) {
	for($i = 0; $i <= $#widgets; $i++) {
	    $widgets[$i]->gridForget;
	    $widgets[$i]->destroy();
	}
    } else {
	createRow($self);
    }
}

sub loadNewMibFile {
    my $sel = $top->FileSelect();
    my $file = $sel->Show();
    if (defined($file)) {
      SNMP::addMibFiles($file);
	showChildren("1.3.6.1");
	showChildren("1.3.6.1");
    }
}

sub loadNewMibModule {
    my $tmptop = MainWindow->new();
    my $var = "";
    $tmptop->Label(-text => "Enter a SNMP MIB module name")
	->pack(-side => 'top');
    my $e = $tmptop->Entry(-textvariable => \$var);
    $e->pack(-side => 'top');
    $e->bind('<Return>',[\&loadIt,\$var,$tmptop]);
    my $f = $tmptop->Frame();
    $f->pack(-side => 'top');
    $f->Button(-text => 'Ok', -command => [\&loadIt,"",\$var,$tmptop])
	->pack(-side => 'left');
    $f->Button(-text => 'Cancel', -command => [sub { my $wid = shift;
						     $wid->destroy(); },
					       $tmptop])
	->pack(-side => 'left');
}

sub loadIt {
    my $var = shift;
    if ($var ne "") {
        my $ret = SNMP::loadModules($var);
	if ($ret) {
	    showChildren("1.3.6.1");
	    showChildren("1.3.6.1");
	    return 1;
	} else {
	    $status->configure(-text => "Failed reading module $var");
	    return 0;
	}
    }
    return 0;
}

sub stop {
    $stopit = shift;
    if ($stopit) {
	$stopBut->configure(-state => 'disabled');
    } else {
	$stopBut->configure(-state => 'normal');
    }
}

sub entryBox {
    my $title = shift;
    my $text = shift;
    my $var = shift;
    my $callback = shift;
    my $top = MainWindow->new();
    my $newvar = $$var if defined($var);
    $top->title($title);
    my $f = $top->Frame();
    $f->pack(-side => 'top');
    $f->Label(-text => $text)->pack(-side => 'left');
    my $e = $f->Entry(-textvariable => \$newvar);
    $e->pack(-side => 'left');
    $f = $top->Frame();
    $f->pack(-side => 'bottom');
    my $b = $f->Button(-text => 'Ok', 
		       -command => [sub { my $w = shift;
					  my $v1 = shift;
					  my $v2 = shift;
					  my $call = shift;
					  my $ret = 1;
					  $$v1 = $$v2 if defined($v1);
					  $ret = $call->($$v2) 
					      if defined($call);
					  $w->destroy() if ($ret);}, $top, $var,
				    \$newvar, $callback]);
    $b->pack(-side => 'left');
    $e->bind('<Return>',[$b,'invoke']);
    $b = $f->Button(-text => 'Cancel', -command => [sub { my $w = shift;
							  $w->destroy();}, $top
						    ]);
    $b->pack(-side => 'right');
    $e->bind('<Escape>',[$b,'invoke']);
    
}

sub findANode {     
    my $val = shift;
    my $tag = SNMP::translateObj($val);
    if ($tag) {
	showAllChildren($tag);
	return 1;
    } else {
	$top->Dialog(-text => "$val not found")->Show();
	return 0;
    }
}
