#!/usr/local/bin/wish -f
# Copyright (c) 1995
#	Ipsilon Networks, Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#      This product includes software developed by Ipsilon Networks, Inc.
# 4. The name of Ipsilon Networks, Inc., may not be used to endorse or
#    promote products derived from this software without specific prior
#    written permission.
#
# THIS SOFTWARE IS PROVIDED BY IPSILON NETWORKS, INC., ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL IPSILON NETWORKS, INC., BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

# $Id: tracelook,v 1.20 1995/11/17 22:34:25 minshall Exp minshall $

# CONFIGURATION SECTION
#
# This is a horribly primitive site-configuartion method.
# awk and xgraph sometimes are known by different names
# at different sites.  Allow them to be configured.

set AWK awk
set XGRAPH xgraph

# little routine to allow mild configuration of invoking XGRAPH
proc bg_xgraph_open { nolines markers } \
{
	global LIB XGRAPH

	return [open "|$LIB/bg.tcl $XGRAPH $nolines $markers -x Secs -y KB" w]
}


# Hopefully, this works in both Tk 4.0 *and* 3.x.


proc tcpdtimetosecs tcpdtime \
{
	# convert time to seconds
	set t [split $tcpdtime ":."]
	regsub {^0*([0-9])} [lindex $t 0] {\1} hours
	regsub {^0*([0-9])} [lindex $t 1] {\1} minutes
	regsub {^0*([0-9])} [lindex $t 2] {\1} seconds
	set tim [expr $hours*3600 + $minutes*60 + $seconds]
	return $tim.[lindex $t 3]
}

proc saveit {window wfile args} \
{
	global tfile

	switch -exact -- [lindex $args 0] {
	tcpconnection {
		set hosta [lindex $args 1]
		set porta [lindex $args 2]
		set hostb [lindex $args 3]
		set portb [lindex $args 4]
		exec tcpdump -w $wfile -r $tfile tcp \
				and host $hosta and port $porta \
				and host $hostb and port $portb
		}
	tcphost {
		set host [lindex $args 1]
		exec tcpdump -w $wfile -r $tfile tcp and host $host
		}
	tcp {
		exec tcpdump -w $wfile -r $tfile tcp
		}
	default {
		puts [format "saveit: unknown type %s" [lindex $args 0]]
		}
	}
}

proc getfilename {title cmd list} \
{
	global getfilename
	global getfilename_name

	if {![info exists getfilename]} {
		set getfilename 0
		frame .getfilename
	}
	incr getfilename
	set x $getfilename
	toplevel .getfilename.$x

	frame .getfilename.$x.file
	label .getfilename.$x.file.label -text $title
	entry .getfilename.$x.file.entry -textvar getfilename_name
	# the \ before "$getfilename_name" below is to keep getfilename_name
	# from being evaulated prior to the OK button being pressed
	bind .getfilename.$x.file.entry <Return> \
		"$cmd .getfilename.$x \$getfilename_name $list; \
						destroy .getfilename.$x"
	pack .getfilename.$x.file.label .getfilename.$x.file.entry \
							-side top -fill x

	frame .getfilename.$x.ctl
	# the \ before "$getfilename_name" below is to keep getfilename_name
	# from being evaulated prior to the OK button being pressed
	button .getfilename.$x.ctl.ok -text OK \
		    -command "$cmd .getfilename.$x \$getfilename_name $list; \
				       destroy .getfilename.$x"
	button .getfilename.$x.ctl.cancel -text Cancel \
			-command "destroy .getfilename.$x"
	pack .getfilename.$x.ctl.cancel .getfilename.$x.ctl.ok -side right
	pack .getfilename.$x.file .getfilename.$x.ctl -side top -fill x
}

proc save id \
{
	global conns_array
	global tk_version

	# "save" to a new tcpdump file

	# this may take a while, so put up a new cursor
	set oldcursor [lindex [.hosts.$id configure -cursor] 4]
	.hosts.$id configure -cursor watch
	update idletasks

	# we could save the whole file (tcp portion),
	# or just things for a given host,
	# or things for just a given connection.

	set connlist [.hosts.$id.lists.conns.list curselection]
	set hostlist [.hosts.$id.lists.hosts.list curselection]
	if {$connlist != ""} {
		set index [lindex $connlist 0]
		set conn $conns_array($index)
		set hosta [lindex $conn 0]
		set porta [lindex $conn 1]
		set hostb [lindex $conn 5]
		set portb [lindex $conn 6]
		getfilename "tcp: $hosta.$porta <> $hostb.$portb" saveit \
			[list tcpconnection $hosta $porta $hostb $portb]
	} elseif {$hostlist != ""} {
		if {$tk_version >= 4.0} {
		    scan [selection get] "%s %d/%d" host pkts bytes
		} else {
		    scan [selection get] "{%s %d/%d}" host pkts bytes
		}
		getfilename "tcp: $host" saveit [list tcphost $host]
	} else {
		getfilename "tcp:" saveit tcp
	}

	# restore original cursor
	.hosts.$id configure -cursor $oldcursor
}


proc view id \
{
	global view
	global tfile timezero nflag Nflag Sflag
	global conns_array
	global tk_version

	# this may take a while, so put up a new cursor
	set oldcursor [lindex [.hosts.$id configure -cursor] 4]
	.hosts.$id configure -cursor watch
	update idletasks

	# we could look at the whole file (tcp portion),
	# or just at things for a given host,
	# or at things for just a given connection.

	set connlist [.hosts.$id.lists.conns.list curselection]
	set hostlist [.hosts.$id.lists.hosts.list curselection]
	if {$connlist != ""} {
		set index [lindex $connlist 0]
		set conn $conns_array($index)
		set hosta [lindex $conn 0]
		set porta [lindex $conn 1]
		set hostb [lindex $conn 5]
		set portb [lindex $conn 6]
		set title "$hosta.$porta <> $hostb.$portb in $tfile"
		set f [open "|tcpdump $Sflag $Nflag $nflag -r $tfile tcp \
				and host $hosta and port $porta \
				and host $hostb and port $portb" r]
	} elseif {$hostlist != ""} {
		if {$tk_version >= 4.0} {
		    scan [selection get] "%s %d/%d" host pkts bytes
		} else {
		    scan [selection get] "{%s %d/%d}" host pkts bytes
		}
		set title "$host in $tfile"
		set f [open "|tcpdump $Sflag $Nflag $nflag -r $tfile tcp and host $host" r]
	} else {
		set title "$tfile"
		set f [open "|tcpdump $Sflag $Nflag $nflag -r $tfile tcp" r]
	}

	if {![info exists view]} {
		set view 0
		frame .view
	}
	incr view
	set x $view

	toplevel .view.$x
	wm minsize .view.$x 100 50
	wm title .view.$x $title

	button .view.$x.dismiss -text Dismiss -command "destroy .view.$x"
	pack .view.$x.dismiss -anchor ne

	text .view.$x.view -wrap none -borderwidth 4 \
		-yscrollcommand ".view.$x.scrolly set"
	pack .view.$x.view -side left -expand true -fill both -anchor nw
	scrollbar .view.$x.scrolly \
		-command ".view.$x.view yview"
	pack .view.$x.scrolly -side right -fill y

	.view.$x.view delete 1.0 end

	while {[gets $f line] >= 0} {
		set aslist [split $line]
		set time [expr [tcpdtimetosecs [lindex $aslist 0]] - $timezero]
		set aslist [lreplace $aslist 0 0 [format "%0.6f" $time]]
		.view.$x.view insert end [join $aslist]
		.view.$x.view insert end "\n"
	}
	close $f

	.view.$x.view configure -state disabled

	# restore original cursor
	.hosts.$id configure -cursor $oldcursor
}


# there is extra work done here.  we run tcpdump for *each*
# variable we are graphing, but we call it only two different
# ways.  the clear alternative would be to create (in [xdoxgraph],
# maybe) two tmp files, then run awk on one or the other of those.

proc doxone { which label lowseq shost sport dhost dport xgraph } \
{
	global tfile timezero nflag Nflag Sflag
	global LIB
	global AWK

	flush $xgraph

	puts $xgraph ""
	puts $xgraph "\"$label\""

	# need to flush so above [puts] are in order with following [exec]
	flush $xgraph

	set f [open "|$AWK -f $LIB/$which.awk >@$xgraph" w]

	puts $f [format "LOWVAL %7.6f\t%0.3f" $timezero \
					[format "%f" [expr $lowseq/1024]]]
#	puts [format "LOWVAL %7.6f\t%0.3f" $timezero \
#					[format "%f" [expr $lowseq/1024]]]
#	flush stdout
	flush $f

	# to keep $Nflag, $nflag from consuming argv[] space, even
	# when set to "", we need to put the string together,
	# then eval it
	set cmd "exec tcpdump $Nflag $nflag -S -r $tfile tcp and \
				src host $shost and src port $sport and \
				dst host $dhost and dst port $dport >@$f"
	eval $cmd
	close $f
}




proc xdoxgraph {x index} \
{
	global showwindowa showseqhigha showseqlowa showacka
	global showwindowb showseqhighb showseqlowb showackb
	global markers nolines
	global conns_array
	global LIB
	global XGRAPH

	# this may take a while, so put up busy cursor
	set oldcursor [lindex [.doxgraph.$x configure -cursor] 4]
	.doxgraph.$x configure -cursor watch
	update idletasks

	set conn $conns_array($index)
	set hosta [lindex $conn 0]
	set porta [lindex $conn 1]
	set lowseqa [lindex $conn 2]
	set hostb [lindex $conn 5]
	set portb [lindex $conn 6]
	set lowseqb [lindex $conn 7]

	if {$hosta == $hostb} {
		set sidea "$hosta.$porta"
		set sideb "$hostb.$portb"
	} else {
		set sidea $hosta
		set sideb $hostb
	}

	set f [bg_xgraph_open $nolines $markers]

	puts $f "TitleText: $hosta.$porta <> $hostb.$portb"

	if {$showwindowa} {
		doxone window "$sidea window" $lowseqb \
			$hosta $porta $hostb $portb $f
	}

	if {$showwindowb} {
		doxone window "$sideb window" $lowseqa \
			$hostb $portb $hosta $porta $f
	}

	if {$showseqhigha} {
		doxone seqhigh "$sidea seqhigh" $lowseqa \
			$hosta $porta $hostb $portb $f
	}

	if {$showseqhighb} {
		doxone seqhigh "$sideb seqhigh" $lowseqb \
			$hostb $portb $hosta $porta $f
	}

	if {$showseqlowa} {
		doxone seqlow "$sidea seqlow" $lowseqa \
			$hosta $porta $hostb $portb $f
	}

	if {$showseqlowb} {
		doxone seqlow "$sideb seqlow" $lowseqb \
			$hostb $portb $hosta $porta $f
	}

	if {$showacka} {
		doxone ack "$sidea ack" $lowseqb \
			$hosta $porta $hostb $portb $f
	}

	if {$showackb} {
		doxone ack "$sideb ack" $lowseqa \
			$hostb $portb $hosta $porta $f
	}

	close $f

	# is this necessary?
	# restore old cursor
	.doxgraph.$x configure -cursor $oldcursor

	wm withdraw .doxgraph.$x
}


proc doxgraph {path y} \
{
	global doxgraph
	global showwindow showseqhigh showseqlow showack
	global markers nolines
	global conns_array

	set index [$path nearest $y]
	set conn $conns_array($index)
	set hosta [lindex $conn 0]
	set porta [lindex $conn 1]
	set lowseqa [lindex $conn 2]
	set hostb [lindex $conn 5]
	set portb [lindex $conn 6]
	set lowseqb [lindex $conn 7]

	if {$hosta == $hostb} {
		set sidea "$hosta.$porta"
		set sideb "$hostb.$portb"
	} else {
		set sidea $hosta
		set sideb $hostb
	}

	if {![info exists doxgraph]} {
		set doxgraph 0
		frame .doxgraph
	}
	incr doxgraph
	set x $doxgraph

	toplevel .doxgraph.$x

	frame .doxgraph.$x.ctl
	button .doxgraph.$x.ctl.ok -text OK \
			-command "xdoxgraph $x $index"
	button .doxgraph.$x.ctl.cancel -text Cancel \
			-command "destroy .doxgraph.$x"
	pack .doxgraph.$x.ctl.cancel .doxgraph.$x.ctl.ok -side right
	pack .doxgraph.$x.ctl -fill x

	frame .doxgraph.$x.which -relief groove -borderwidth 4
	label .doxgraph.$x.which.label -text "which variables to graph"
	checkbutton .doxgraph.$x.which.windowa \
			-text "$sidea window" -variable showwindowa -anchor w
	checkbutton .doxgraph.$x.which.seqhigha \
			-text "$sidea seqhigh" -variable showseqhigha -anchor w
	checkbutton .doxgraph.$x.which.seqlowa \
			-text "$sidea seqlow" -variable showseqlowa -anchor w
	checkbutton .doxgraph.$x.which.acka \
			-text "$sidea ack" -variable showacka -anchor w
	checkbutton .doxgraph.$x.which.windowb \
			-text "$sideb window" -variable showwindowb -anchor w
	checkbutton .doxgraph.$x.which.seqhighb \
			-text "$sideb seqhigh" -variable showseqhighb -anchor w
	checkbutton .doxgraph.$x.which.seqlowb \
			-text "$sideb seqlow" -variable showseqlowb -anchor w
	checkbutton .doxgraph.$x.which.ackb \
			-text "$sideb ack" -variable showackb -anchor w
	pack .doxgraph.$x.which.label
	pack .doxgraph.$x.which.windowa .doxgraph.$x.which.seqhigha \
			.doxgraph.$x.which.seqlowa .doxgraph.$x.which.acka \
			-side top -fill x
	pack .doxgraph.$x.which.windowb .doxgraph.$x.which.seqhighb \
			.doxgraph.$x.which.seqlowb .doxgraph.$x.which.ackb \
			-side top -fill x
	pack .doxgraph.$x.which

	frame .doxgraph.$x.how -relief groove -borderwidth 4
	label .doxgraph.$x.how.label -text "xgraph options"
	pack .doxgraph.$x.how.label
	checkbutton .doxgraph.$x.how.nolines -anchor w \
			-text "no lines" \
			-variable nolines -offvalue "" -onvalue -nl
	pack .doxgraph.$x.how.nolines -side top -fill x
	frame .doxgraph.$x.how.markers -borderwidth 2
	label .doxgraph.$x.how.markers.label -text "marker style"
	radiobutton .doxgraph.$x.how.markers.none -anchor w \
			-text none -variable markers -value " "
	radiobutton .doxgraph.$x.how.markers.m -anchor w \
			-text markers -variable markers -value -m
	radiobutton .doxgraph.$x.how.markers.sm -anchor w \
			-text "style markers" -variable markers -value -M
	radiobutton .doxgraph.$x.how.markers.p -anchor w \
			-text "pixel markers" -variable markers -value -p
	radiobutton .doxgraph.$x.how.markers.lp -anchor w \
			-text "large pixel markers" -variable markers -value -P
	pack .doxgraph.$x.how.markers.label
	pack .doxgraph.$x.how.markers.none \
			.doxgraph.$x.how.markers.m .doxgraph.$x.how.markers.sm \
			.doxgraph.$x.how.markers.p .doxgraph.$x.how.markers.lp \
			-side top -fill x
	pack .doxgraph.$x.how.markers
	pack .doxgraph.$x.how -fill x
}

proc conns id \
{
	global tfile nflag Nflag Sflag
	global conns_array
	global LIB
	global AWK
	global tk_version

	# list all the conns

	# this may take a while, put up a notice...
	set oldcursor [lindex [.hosts.$id configure -cursor] 4]
	.hosts.$id configure -cursor watch
	update idletasks

	.hosts.$id.lists.conns.list delete 0 end

	# if there is one host selected, display only its connections
	set hostlist [.hosts.$id.lists.hosts.list curselection]
	if {$hostlist != ""} {
		if {$tk_version >= 4.0} {
		    scan [selection get] "%s %d/%d" host pkts bytes
		} else {
		    scan [selection get] "{%s %d/%d}" host pkts bytes
		}
		.hosts.$id.lists.conns.label configure \
					-text "Connections of $host"
		set f [open "|tcpdump -S $Nflag $nflag -r $tfile \
					    tcp and host $host | \
					    $AWK -f $LIB/tcpconns.awk" r]
	} else {
		# display all connections
		set host ""
		.hosts.$id.lists.conns.label configure -text "Connections"
		set f [open "|tcpdump -S $Nflag $nflag -r $tfile tcp | \
				$AWK -f $LIB/tcpconns.awk" r]
	}

	set index 0
	while {[gets $f line] >= 0} {
		set aslist [split $line " "]
		if {$host == [lindex $aslist 0]} {
			set mhost [lindex $aslist 0]
			set mport [lindex $aslist 1]
			set phost [lindex $aslist 5]
			set pport [lindex $aslist 6]
		} else {
			set phost [lindex $aslist 0]
			set pport [lindex $aslist 1]
			set mhost [lindex $aslist 5]
			set mport [lindex $aslist 6]
		}
		set pkts [expr [lindex $aslist 3]+[lindex $aslist 8]]
		set bytes [expr [lindex $aslist 4]+[lindex $aslist 9]]
		set conns_array($index) $aslist
		incr index
		if {$host != ""} {
			.hosts.$id.lists.conns.list insert end \
				"./$mport $phost/$pport $pkts/$bytes"
		} else {
			.hosts.$id.lists.conns.list insert end \
				"$mhost/$mport $phost/$pport $pkts/$bytes"
		}
	}
	close $f

	bind .hosts.$id.lists.conns.list <Double-Button-1> "doxgraph %W %y"

	pack .hosts.$id.lists.conns.list

	# restore old cursor
	.hosts.$id configure -cursor $oldcursor
}

proc hosts {} \
{
	global tfile nflag Nflag Sflag
	global hosts
	global LIB
	global AWK
	global tk_version

	# find all the hosts

	if {![info exists hosts]} {
		set hosts 0
		frame .hosts
	}
	incr hosts
	set x $hosts

	toplevel .hosts.$x
	wm minsize .hosts.$x 100 50
	wm title .hosts.$x "hosts in $tfile"

	frame .hosts.$x.buttons
	button .hosts.$x.buttons.dismiss -text "Dismiss" \
			-command "destroy ."
#			-command "destroy .hosts.$x"
	if {$tk_version >= 4.0} {
	    button .hosts.$x.buttons.deselect -text "Deselect" \
		    -command ".hosts.$x.lists.hosts.list select clear 0 end; \
			      .hosts.$x.lists.conns.list select clear 0 end; \
			      conns $x"
	} else {
	    button .hosts.$x.buttons.deselect -text "Deselect" \
		    -command ".hosts.$x.lists.hosts.list select clear; \
			      .hosts.$x.lists.conns.list select clear; \
			      conns $x"
	}
	button .hosts.$x.buttons.view -text "View" \
			-command "view $x"

	button .hosts.$x.buttons.save -text "Save" \
			-command "save $x"

	pack .hosts.$x.buttons.dismiss -side right -fill x -expand true
	pack .hosts.$x.buttons.deselect -side right -fill x -expand true
	pack .hosts.$x.buttons.save -side right -fill x -expand true
	pack .hosts.$x.buttons.view -side right -fill x -expand true
	pack .hosts.$x.buttons -side top -fill x

	frame .hosts.$x.lists
	frame .hosts.$x.lists.hosts -relief groove -borderwidth 4
	label .hosts.$x.lists.hosts.label -text "Hosts..."
	frame .hosts.$x.lists.conns -relief groove -borderwidth 4
	label .hosts.$x.lists.conns.label -text "Connections"

	listbox .hosts.$x.lists.hosts.list \
			-relief groove -borderwidth 2 \
			-yscrollcommand ".hosts.$x.lists.hosts.scroll set"
	scrollbar .hosts.$x.lists.hosts.scroll \
			-command ".hosts.$x.lists.hosts.list yview"
	listbox .hosts.$x.lists.conns.list \
			-relief groove -borderwidth 2 \
			-yscrollcommand ".hosts.$x.lists.conns.scroll set"
	if {$tk_version >= 4.0} {
	    .hosts.$x.lists.hosts.list config -selectmode single
	    .hosts.$x.lists.conns.list config -selectmode single
	} else {
	    tk_listboxSingleSelect .hosts.$x.lists.hosts.list
	    tk_listboxSingleSelect .hosts.$x.lists.conns.list
	}
	scrollbar .hosts.$x.lists.conns.scroll \
			-command ".hosts.$x.lists.conns.list yview"

	set f [open "|tcpdump $Nflag $nflag -r $tfile tcp \
					| $AWK -f $LIB/tcphosts.awk" r]

	while {[gets $f line] >= 0} {
		set host [lindex $line 0]
		set pkts [expr [lindex $line 1] + [lindex $line 2]]
		set bytes [expr [lindex $line 3] + [lindex $line 4]]
		.hosts.$x.lists.hosts.list insert end "$host $pkts/$bytes"
	}
	close $f

	bind .hosts.$x.lists.hosts.list <ButtonRelease-1> "conns $x"

	pack .hosts.$x.lists.hosts.label -side top -fill x
	pack .hosts.$x.lists.hosts.list -side left -fill both -expand true
	pack .hosts.$x.lists.hosts.scroll -side right -fill y
	pack .hosts.$x.lists.conns.label -side top -fill x
	pack .hosts.$x.lists.conns.list -side left -fill both -expand true
	pack .hosts.$x.lists.conns.scroll -side right -fill y
	pack .hosts.$x.lists.hosts .hosts.$x.lists.conns \
					-side left -fill both -expand true
	pack .hosts.$x.lists -expand true -fill both

	conns $x
}

# MAIN PROGRAM


# file independent initializations
set showwindowa 0
set showseqhigha 1
set showseqlowa 1
set showacka 0
set showwindowb 1
set showseqhighb 0
set showseqlowb 0
set showackb 1

set markers "-m"
set nolines ""

set nflag ""
set Nflag ""
set Sflag ""

foreach arg $argv {
	switch -- $arg {
	-nflag	{set nflag "-n"}
	-Nflag	{set Nflag "-N"}
	-Sflag  {set Sflag "-S"}
	default {
			if {[string index $arg 0] == "-"} {
				puts "$arg unknown option"
				set badopt 1
			} elseif {[info exists tfile]} {
				puts "multiple input files specified"
				set badopt 1
			} else {
				set tfile $arg
			}
		}
	}
}

if {[info exists badopt] || ![info exists tfile]} {
	puts "$argv0 \[-nflag\] \[-Nflag\] \[-Sflag\] tcpdumpfile"
	exit 1
}

if {![file isfile $tfile]} {
	puts "$tfile does not exist"
	exit 1
}

if {![file readable $tfile]} {
	puts "$tfile unreadable"
	exit 1
}

# search for our awk scripts and bg.tcl...
set lpath "."
if {[info exists env(PATH)]} {
	set asstring $env(PATH)
	set aslist [split $asstring ":"]
	set lpath [concat $lpath $aslist]

	# now, heuristic time -- look for "lib" directories parallel
	# to each of the "bin" directories in path...

	set path $aslist
	set libpath ""
	foreach bindir $path {
		set aslist [split $bindir "/"]
		set lastelt [expr [llength $aslist]-1]
		if {[lindex $aslist $lastelt] == "bin"} {
			set libdir [join \
				[lreplace $aslist $lastelt $lastelt "lib"] "/"]
			set libpath [concat $libpath $libdir]
		}
	}
	set lpath [concat $lpath $libpath]
}
if {[info exists env(LIBPATH)]} {
	set asstring $env(LIBPATH)
	set aslist [split $asstring ":"]
	set lpath [concat $lpath $aslist]
}

set us [file root [file tail $argv0]]
foreach libdir $lpath {
	if {[file isdirectory "$libdir/$us"]} {
		set LIB "$libdir/$us"
		break;
	} elseif {[file exists "$libdir/ack.awk"]} {
		set LIB $libdir
		break;
	}
}
if {![info exists LIB]} {
	puts stderr "Unable to locate awk and tcl scripts!"
	exit 1
}

# figure out lowest time in tcpdump file
set f [open "|tcpdump -n -r $tfile" r]
set line [gets $f]
catch "close $f"
set aslist [split $line " "]
set timezero [tcpdtimetosecs [lindex $aslist 0]]

wm withdraw .
hosts
