Index: tolsh/tolsh.vfs/lib/tcllib1.10/counter/counter.tcl
===================================================================
--- tolsh/tolsh.vfs/lib/tcllib1.10/counter/counter.tcl	(revision 8597867d776844b87bb3647ebb6b5d2d706e4001)
+++ tolsh/tolsh.vfs/lib/tcllib1.10/counter/counter.tcl	(revision 8597867d776844b87bb3647ebb6b5d2d706e4001)
@@ -0,0 +1,1265 @@
+# counter.tcl --
+#
+#   Procedures to manage simple counters and histograms.
+#
+# Copyright (c) 1998-2000 by Ajuba Solutions.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: counter.tcl,v 1.23 2005/09/30 05:36:38 andreas_kupries Exp $
+
+package require Tcl 8.2
+
+namespace eval ::counter {
+
+    # Variables of name counter::T-$tagname
+    # are created as arrays to support each counter.
+
+    # Time-based histograms are kept in sync with each other,
+    # so these variables are shared among them.
+    # These base times record the time corresponding to the first bucket 
+    # of the per-minute, per-hour, and per-day time-based histograms.
+
+    variable startTime
+    variable minuteBase
+    variable hourBase
+    variable hourEnd
+    variable dayBase
+    variable hourIndex
+    variable dayIndex
+
+    # The time-based histogram uses an after event and a list
+    # of counters to do mergeing on.
+
+    variable tagsToMerge
+    if {![info exists tagsToMerge]} {
+    set tagsToMerge {}
+    }
+    variable mergeInterval
+
+    namespace export init reset count exists get names start stop
+    namespace export histHtmlDisplay histHtmlDisplayRow histHtmlDisplayBarChart
+}
+
+# ::counter::init --
+#
+#   Set up a counter.
+#
+# Arguments:
+#   tag The identifier for the counter.  Pass this to counter::count
+#   args    option values pairs that define characteristics of the counter:
+#       See the man page for definitons.
+#
+# Results:
+#   None.
+#
+# Side Effects:
+#   Initializes state about a counter.
+
+proc ::counter::init {tag args} {
+    upvar #0 counter::T-$tag counter
+    if {[info exists counter]} {
+    unset counter
+    }
+    set counter(N) 0    ;# Number of samples
+    set counter(total) 0
+    set counter(type) {}
+
+    # With an empty type the counter is a simple accumulator
+    # for which we can compute an average.  Here we loop through
+    # the args to determine what additional counter attributes
+    # we need to maintain in counter::count
+
+    foreach {option value} $args {
+    switch -- $option {
+        -timehist {
+        variable tagsToMerge
+        variable secsPerMinute
+        variable startTime
+        variable minuteBase
+        variable hourBase
+        variable dayBase
+        variable hourIndex
+        variable dayIndex
+
+        upvar #0 counter::H-$tag histogram
+        upvar #0 counter::Hour-$tag hourhist
+        upvar #0 counter::Day-$tag dayhist
+
+        # Clear the histograms.
+
+        for {set i 0} {$i < 60} {incr i} {
+            set histogram($i) 0
+        }
+        for {set i 0} {$i < 24} {incr i} {
+            set hourhist($i) 0
+        }
+        if {[info exists dayhist]} {
+            unset dayhist
+        }
+        set dayhist(0) 0
+
+        # Clear all-time high records
+
+        set counter(maxPerMinute) 0
+        set counter(maxPerHour) 0
+        set counter(maxPerDay) 0
+
+        # The value associated with -timehist is the number of seconds
+        # in each bucket.  Normally this is 60, but for
+        # testing, we compress minutes.  The value is limited at
+        # 60 because the per-minute buckets are accumulated into
+        # per-hour buckets later.
+
+        if {$value == "" || $value == 0 || $value > 60} {
+            set value 60
+        }
+
+        # Histogram state variables.
+        # All time-base histograms share the same bucket size
+        # and starting times to keep them all synchronized.
+        # So, we only initialize these parameters once.
+
+        if {![info exists secsPerMinute]} {
+            set secsPerMinute $value
+
+            set startTime [clock seconds]
+            set dayIndex 0
+
+            set dayStart [clock scan [clock format $startTime \
+                -format 00:00]]
+            
+            # Figure out what "hour" we are
+
+            set delta [expr {$startTime - $dayStart}]
+            set hourIndex [expr {$delta / ($secsPerMinute * 60)}]
+            set day [expr {$hourIndex / 24}]
+            set hourIndex [expr {$hourIndex % 24}]
+
+            set hourBase [expr {$dayStart + $day * $secsPerMinute * 60 * 24}]
+            set minuteBase [expr {$hourBase + $hourIndex * 60 * $secsPerMinute}]
+
+            set partialHour [expr {$startTime -
+            ($hourBase + $hourIndex * 60 * $secsPerMinute)}]
+            set secs [expr {(60 * $secsPerMinute) - $partialHour}]
+            if {$secs <= 0} {
+            set secs 1
+            }
+
+            # After the first timer, the event occurs once each "hour"
+
+            set mergeInterval [expr {60 * $secsPerMinute * 1000}]
+            after [expr {$secs * 1000}] [list counter::MergeHour $mergeInterval]
+        }
+        if {[lsearch $tagsToMerge $tag] < 0} {
+            lappend tagsToMerge $tag
+        }
+
+        # This records the last used slots in order to zero-out the
+        # buckets that are skipped during idle periods.
+
+        set counter(lastMinute) -1
+
+        # The following is referenced when bugs cause histogram
+        # hits outside the expect range (overflow and underflow)
+
+        set counter(bucketsize)  0
+        }
+        -group {
+        # Cluster a set of counters with a single total
+
+        upvar #0 counter::H-$tag histogram
+        if {[info exists histogram]} {
+            unset histogram
+        }
+        set counter(group) $value
+        }
+        -lastn {
+        # The lastN samples are kept if a vector to form a running average.
+
+        upvar #0 counter::V-$tag vector
+        set counter(lastn) $value
+        set counter(index) 0
+        if {[info exists vector]} {
+            unset vector
+        }
+        for {set i 0} {$i < $value} {incr i} {
+            set vector($i) 0
+        }
+        }
+        -hist {
+        # A value-based histogram with buckets for different values.
+
+        upvar #0 counter::H-$tag histogram
+        if {[info exists histogram]} {
+            unset histogram
+        }
+        set counter(bucketsize) $value
+        set counter(mult) 1
+        }
+        -hist2x {
+        upvar #0 counter::H-$tag histogram
+        if {[info exists histogram]} {
+            unset histogram
+        }
+        set counter(bucketsize) $value
+        set counter(mult) 2
+        }
+        -hist10x {
+        upvar #0 counter::H-$tag histogram
+        if {[info exists histogram]} {
+            unset histogram
+        }
+        set counter(bucketsize) $value
+        set counter(mult) 10
+        }
+        -histlog {
+        upvar #0 counter::H-$tag histogram
+        if {[info exists histogram]} {
+            unset histogram
+        }
+        set counter(bucketsize) $value
+        }
+        -simple {
+        # Useful when disabling predefined -timehist or -group counter
+        }
+        default {
+        return -code error "Unsupported option $option.\
+        Must be -timehist, -group, -lastn, -hist, -hist2x, -hist10x, -histlog, or -simple."
+        }
+    }
+    if {[string length $option]} {
+        # In case an option doesn't change the type, but
+        # this feature of the interface isn't used, etc.
+
+        lappend counter(type) $option
+    }
+    }
+
+    # Instead of supporting a counter that could have multiple attributes,
+    # we support a single type to make counting more efficient.
+
+    if {[llength $counter(type)] > 1} {
+    return -code error "Multiple type attributes not supported.  Use only one of\
+        -timehist, -group, -lastn, -hist, -hist2x, -hist10x, -histlog, -disabled."
+    }
+    return ""
+}
+
+# ::counter::reset --
+#
+#   Reset a counter.
+#
+# Arguments:
+#   tag The identifier for the counter.
+#
+# Results:
+#   None.
+#
+# Side Effects:
+#   Deletes the counter and calls counter::init again for it.
+
+proc ::counter::reset {tag args} {
+    upvar #0 counter::T-$tag counter
+
+    # Layer reset on top of init.  Here we figure out what
+    # we need to pass into the init procedure to recreate it.
+
+    switch -- $counter(type) {
+    ""  {
+        set args ""
+    }
+    -group {
+        upvar #0 counter::H-$tag histogram
+        if {[info exists histogram]} {
+        unset histogram
+        }
+        set args [list -group $counter(group)]
+    }
+    -lastn {
+        upvar #0 counter::V-$tag vector
+        if {[info exists vector]} {
+        unset vector
+        }
+        set args [list -lastn $counter(lastn)]
+    }
+    -hist -
+    -hist10x -
+    -histlog -
+    -hist2x {
+        upvar #0 counter::H-$tag histogram
+        if {[info exists histogram]} {
+        unset histogram
+        }
+        set args [list $counter(type) $counter(bucketsize)]
+    }
+    -timehist {
+        foreach h [list counter::H-$tag counter::Hour-$tag counter::Day-$tag] {
+        upvar #0 $h histogram
+        if {[info exists histogram]} {
+            unset histogram
+        }
+        }
+        set args [list -timehist $counter::secsPerMinute]
+    }
+    default {#ignore}
+    }
+    unset counter
+    eval {counter::init $tag} $args
+    set counter(resetDate) [clock seconds]
+    return ""
+}
+
+# ::counter::count --
+#
+#   Accumulate statistics.
+#
+# Arguments:
+#   tag The counter identifier.
+#   delta   The increment amount.  Defaults to 1.
+#   arg For -group types, this is the histogram index.
+#
+# Results:
+#   None
+#
+# Side Effects:
+#   Accumlate statistics.
+
+proc ::counter::count {tag {delta 1} args} {
+    upvar #0 counter::T-$tag counter
+    set counter(total) [expr {$counter(total) + $delta}]
+    incr counter(N)
+
+    # Instead of supporting a counter that could have multiple attributes,
+    # we support a single type to make counting a skosh more efficient.
+
+#    foreach option $counter(type) {
+    switch -- $counter(type) {
+        ""  {
+        # Simple counter
+        return
+        }
+        -group {
+        upvar #0 counter::H-$tag histogram
+        set subIndex [lindex $args 0]
+        if {![info exists histogram($subIndex)]} {
+            set histogram($subIndex) 0
+        }
+        set histogram($subIndex) [expr {$histogram($subIndex) + $delta}]
+        }
+        -lastn {
+        upvar #0 counter::V-$tag vector
+        set vector($counter(index)) $delta
+        set counter(index) [expr {($counter(index) +1)%$counter(lastn)}]
+        }
+        -hist {
+        upvar #0 counter::H-$tag histogram
+        set bucket [expr {int($delta / $counter(bucketsize))}]
+        if {![info exists histogram($bucket)]} {
+            set histogram($bucket) 0
+        }
+        incr histogram($bucket)
+        }
+        -hist10x -
+        -hist2x {
+        upvar #0 counter::H-$tag histogram
+        set bucket 0
+        for {set max $counter(bucketsize)} {$delta > $max} \
+            {set max [expr {$max * $counter(mult)}]} {
+            incr bucket
+        }
+        if {![info exists histogram($bucket)]} {
+            set histogram($bucket) 0
+        }
+        incr histogram($bucket)
+        }
+        -histlog {
+        upvar #0 counter::H-$tag histogram
+        set bucket [expr {int(log($delta)*$counter(bucketsize))}]
+        if {![info exists histogram($bucket)]} {
+            set histogram($bucket) 0
+        }
+        incr histogram($bucket)
+        }
+        -timehist {
+        upvar #0 counter::H-$tag histogram
+        variable minuteBase
+        variable secsPerMinute
+
+        set minute [expr {([clock seconds] - $minuteBase) / $secsPerMinute}]
+        if {$minute > 59} {
+            # this occurs while debugging if the process is
+            # stopped at a breakpoint too long.
+            set minute 59
+        }
+
+        # Initialize the current bucket and 
+        # clear any buckets we've skipped since the last sample.
+        
+        if {$minute != $counter(lastMinute)} {
+            set histogram($minute) 0
+            for {set i [expr {$counter(lastMinute)+1}]} \
+                {$i < $minute} \
+                {incr i} {
+            set histogram($i) 0
+            }
+            set counter(lastMinute) $minute
+        }
+        set histogram($minute) [expr {$histogram($minute) + $delta}]
+        }
+        default {#ignore}
+    }
+#   }
+    return
+}
+
+# ::counter::exists --
+#
+#   Return true if the counter exists.
+#
+# Arguments:
+#   tag The counter identifier.
+#
+# Results:
+#   1 if it has been defined.
+#
+# Side Effects:
+#   None.
+
+proc ::counter::exists {tag} {
+    upvar #0 counter::T-$tag counter
+    return [info exists counter]
+}
+
+# ::counter::get --
+#
+#   Return statistics.
+#
+# Arguments:
+#   tag The counter identifier.
+#   option  What statistic to get
+#   args    Needed by some options.
+#
+# Results:
+#   With no args, just the counter value.
+#
+# Side Effects:
+#   None.
+
+proc ::counter::get {tag {option -total} args} {
+    upvar #0 counter::T-$tag counter
+    switch -- $option {
+    -total {
+        return $counter(total)
+    }
+    -totalVar {
+        return ::counter::T-$tag\(total)
+    }
+    -N {
+        return $counter(N)
+    }
+    -avg {
+        if {$counter(N) == 0} {
+        return 0
+        } else {
+        return [expr {$counter(total) / double($counter(N))}]
+        }
+    }
+    -avgn {
+        if {$counter(type) != "-lastn"} {
+        return -code error "The -avgn option is only supported for -lastn counters."
+        }
+        upvar #0 counter::V-$tag vector
+        set sum 0
+        for {set i 0} {($i < $counter(N)) && ($i < $counter(lastn))} {incr i} {
+        set sum [expr {$sum + $vector($i)}]
+        }
+        if {$i == 0} {
+        return 0
+        } else {
+        return [expr {$sum / double($i)}]
+        }
+    }
+    -hist {
+        upvar #0 counter::H-$tag histogram
+        if {[llength $args]} {
+        # Return particular bucket
+        set bucket [lindex $args 0]
+        if {[info exists histogram($bucket)]} {
+            return $histogram($bucket)
+        } else {
+            return 0
+        }
+        } else {
+        # Dump the whole histogram
+
+        set result {}
+        if {$counter(type) == "-group"} {
+            set sort -dictionary
+        } else {
+            set sort -integer
+        }
+        foreach x [lsort $sort [array names histogram]] {
+            lappend result $x $histogram($x)
+        }
+        return $result
+        }
+    }
+    -histVar {
+        return ::counter::H-$tag
+    }
+    -histHour {
+        upvar #0 counter::Hour-$tag histogram
+        set result {}
+        foreach x [lsort -integer [array names histogram]] {
+        lappend result $x $histogram($x)
+        }
+        return $result
+    }
+    -histHourVar {
+        return ::counter::Hour-$tag
+    }
+    -histDay {
+        upvar #0 counter::Day-$tag histogram
+        set result {}
+        foreach x [lsort -integer [array names histogram]] {
+        lappend result $x $histogram($x)
+        }
+        return $result
+    }
+    -histDayVar {
+        return ::counter::Day-$tag
+    }
+    -maxPerMinute {
+        return $counter(maxPerMinute)
+    }
+    -maxPerHour {
+        return $counter(maxPerHour)
+    }
+    -maxPerDay {
+        return $counter(maxPerDay)
+    }
+    -resetDate {
+        if {[info exists counter(resetDate)]} {
+        return $counter(resetDate)
+        } else {
+        return ""
+        }
+    }
+    -all {
+        return [array get counter]
+    }
+    default {
+        return -code error "Invalid option $option.\
+        Should be -all, -total, -N, -avg, -avgn, -hist, -histHour,\
+        -histDay, -totalVar, -histVar, -histHourVar, -histDayVar -resetDate."
+    }
+    }
+}
+
+# ::counter::names --
+#
+#   Return the list of defined counters.
+#
+# Arguments:
+#   none
+#
+# Results:
+#   A list of counter tags.
+#
+# Side Effects:
+#   None.
+
+proc ::counter::names {} {
+    set result {}
+    foreach v [info vars ::counter::T-*] {
+    if {[info exists $v]} {
+        # Declared arrays might not exist, yet
+        # strip prefix from name
+        set v [string range $v [string length "::counter::T-"] end]
+        lappend result $v
+    }
+    }
+    return $result
+}
+
+# ::counter::MergeHour --
+#
+#   Sum the per-minute histogram into the next hourly bucket.
+#   On 24-hour boundaries, sum the hourly buckets into the next day bucket.
+#   This operates on all time-based histograms.
+#
+# Arguments:
+#   none
+#
+# Results:
+#   none
+#
+# Side Effects:
+#   See description.
+
+proc ::counter::MergeHour {interval} {
+    variable hourIndex
+    variable minuteBase
+    variable hourBase
+    variable tagsToMerge
+    variable secsPerMinute
+
+    after $interval [list counter::MergeHour $interval]
+    if {![info exists hourBase] || $hourIndex == 0} {
+    set hourBase $minuteBase
+    }
+    set minuteBase [clock seconds]
+
+    foreach tag $tagsToMerge {
+    upvar #0 counter::T-$tag counter
+    upvar #0 counter::H-$tag histogram
+    upvar #0 counter::Hour-$tag hourhist
+
+    # Clear any buckets we've skipped since the last sample.
+
+    for {set i [expr {$counter(lastMinute)+1}]} {$i < 60} {incr i} {
+        set histogram($i) 0
+    }
+    set counter(lastMinute) -1
+
+    # Accumulate into the next hour bucket.
+
+    set hourhist($hourIndex) 0
+    set max 0
+    foreach i [array names histogram] {
+        set hourhist($hourIndex) [expr {$hourhist($hourIndex) + $histogram($i)}]
+        if {$histogram($i) > $max} {
+        set max $histogram($i)
+        }
+    }
+    set perSec [expr {$max / $secsPerMinute}]
+    if {$perSec > $counter(maxPerMinute)} {
+        set counter(maxPerMinute) $perSec
+    }
+    }
+    set hourIndex [expr {($hourIndex + 1) % 24}]
+    if {$hourIndex == 0} {
+    counter::MergeDay
+    }
+
+}
+# ::counter::MergeDay --
+#
+#   Sum the per-minute histogram into the next hourly bucket.
+#   On 24-hour boundaries, sum the hourly buckets into the next day bucket.
+#   This operates on all time-based histograms.
+#
+# Arguments:
+#   none
+#
+# Results:
+#   none
+#
+# Side Effects:
+#   See description.
+
+proc ::counter::MergeDay {} {
+    variable dayIndex
+    variable dayBase
+    variable hourBase
+    variable tagsToMerge
+    variable secsPerMinute
+
+    # Save the hours histogram into a bucket for the last day
+    # counter(day,$day) is the starting time for that day bucket
+
+    if {![info exists dayBase]} {
+    set dayBase $hourBase
+    }
+    foreach tag $tagsToMerge {
+    upvar #0 counter::T-$tag counter
+    upvar #0 counter::Day-$tag dayhist
+    upvar #0 counter::Hour-$tag hourhist
+    set dayhist($dayIndex) 0
+    set max 0
+    for {set i 0} {$i < 24} {incr i} {
+        if {[info exists hourhist($i)]} {
+        set dayhist($dayIndex) [expr {$dayhist($dayIndex) + $hourhist($i)}]
+        if {$hourhist($i) > $max} { 
+            set max $hourhist($i) 
+        }
+        }
+    }
+    set perSec [expr {double($max) / ($secsPerMinute * 60)}]
+    if {$perSec > $counter(maxPerHour)} {
+        set counter(maxPerHour) $perSec
+    }
+    }
+    set perSec [expr {double($dayhist($dayIndex)) / ($secsPerMinute * 60 * 24)}]
+    if {$perSec > $counter(maxPerDay)} {
+    set counter(maxPerDay) $perSec
+    }
+    incr dayIndex
+}
+
+# ::counter::histHtmlDisplay --
+#
+#   Create an html display of the histogram.
+#
+# Arguments:
+#   tag The counter tag
+#   args    option, value pairs that affect the display:
+#       -title  Label to display above bar chart
+#       -unit   minutes, hours, or days select time-base histograms.
+#           Specify anything else for value-based histograms.
+#       -images URL of /images directory.
+#       -gif    Image for normal histogram bars
+#       -ongif  Image for the active histogram bar
+#       -max    Maximum number of value-based buckets to display
+#       -height Pixel height of the highest bar
+#       -width  Pixel width of each bar
+#       -skip   Buckets to skip when labeling value-based histograms
+#       -format Format used to display labels of buckets.
+#       -text   If 1, a text version of the histogram is dumped,
+#           otherwise a graphical one is generated.
+#
+# Results:
+#   HTML for the display as a complete table.
+#
+# Side Effects:
+#   None.
+
+proc ::counter::histHtmlDisplay {tag args} {
+    append result "<p>\n<table border=0 cellpadding=0 cellspacing=0>\n"
+    append result [eval {counter::histHtmlDisplayRow $tag} $args]
+    append result </table>
+    return $result
+}
+
+# ::counter::histHtmlDisplayRow --
+#
+#   Create an html display of the histogram.
+#
+# Arguments:
+#   See counter::histHtmlDisplay
+#
+# Results:
+#   HTML for the display.  Ths is one row of a 2-column table,
+#   the calling page must define the <table> tag.
+#
+# Side Effects:
+#   None.
+
+proc ::counter::histHtmlDisplayRow {tag args} {
+    upvar #0 counter::T-$tag counter
+    variable secsPerMinute
+    variable minuteBase
+    variable hourBase
+    variable dayBase
+    variable hourIndex
+    variable dayIndex
+
+    array set options [list \
+    -title  $tag \
+    -unit   "" \
+    -images /images \
+    -gif    Blue.gif \
+    -ongif  Red.gif \
+    -max    -1 \
+    -height 100 \
+    -width  4 \
+    -skip   4 \
+    -format %.2f \
+    -text   0
+    ]
+    array set options $args
+
+    # Support for self-posting pages that can clear counters.
+
+    append result "<!-- resetCounter [ncgi::value resetCounter] -->"
+    if {[ncgi::value resetCounter] == $tag} {
+    counter::reset $tag
+    return "<!-- Reset $tag counter -->"
+    }
+
+    switch -glob -- $options(-unit) {
+    min* {
+        upvar #0 counter::H-$tag histogram
+        set histname counter::H-$tag
+        if {![info exists minuteBase]} {
+        return "<!-- No time-based histograms defined -->"
+        }
+        set time $minuteBase
+        set secsForMax $secsPerMinute
+        set periodMax $counter(maxPerMinute)
+        set curIndex [expr {([clock seconds] - $minuteBase) / $secsPerMinute}]
+        set options(-max) 60
+        set options(-min) 0
+    }
+    hour* {
+        upvar #0 counter::Hour-$tag histogram
+        set histname counter::Hour-$tag
+        if {![info exists hourBase]} {
+        return "<!-- Hour merge has not occurred -->"
+        }
+        set time $hourBase
+        set secsForMax [expr {$secsPerMinute * 60}]
+        set periodMax $counter(maxPerHour)
+        set curIndex [expr {$hourIndex - 1}]
+        if {$curIndex < 0} {
+        set curIndex 23
+        }
+        set options(-max) 24
+        set options(-min) 0
+    }
+    day* {
+        upvar #0 counter::Day-$tag histogram
+        set histname counter::Day-$tag
+        if {![info exists dayBase]} {
+        return "<!-- Hour merge has not occurred -->"
+        }
+        set time $dayBase
+        set secsForMax [expr {$secsPerMinute * 60 * 24}]
+        set periodMax $counter(maxPerDay)
+        set curIndex dayIndex
+        set options(-max) $dayIndex
+        set options(-min) 0
+    }
+    default {
+        # Value-based histogram with arbitrary units.
+
+        upvar #0 counter::H-$tag histogram
+        set histname counter::H-$tag
+
+        set unit $options(-unit)
+        set curIndex ""
+        set time ""
+    }
+    }
+    if {! [info exists histogram]} {
+    return "<!-- $histname doesn't exist -->\n"
+    }
+
+    set max 0
+    set maxName 0
+    foreach {name value} [array get histogram] {
+    if {$value > $max} {
+        set max $value
+        set maxName $name
+    }
+    }
+
+    # Start 2-column HTML display.  A summary table at the left, the histogram on the right.
+
+    append result "<tr><td valign=top>\n"
+
+    append result "<table bgcolor=#EEEEEE>\n"
+    append result "<tr><td colspan=2 align=center>[html::font]<b>$options(-title)</b></font></td></tr>\n"
+    append result "<tr><td>[html::font]<b>Total</b></font></td>"
+    append result "<td>[html::font][format $options(-format) $counter(total)]</font></td></tr>\n"
+
+    if {[info exists secsForMax]} {
+
+    # Time-base histogram
+
+    set string {}
+    set t $secsForMax
+    set days [expr {$t / (60 * 60 * 24)}]
+    if {$days == 1} {
+        append string "1 Day "
+    } elseif {$days > 1} {
+        append string "$days Days "
+    }
+    set t [expr {$t - $days * (60 * 60 * 24)}]
+    set hours [expr {$t / (60 * 60)}]
+    if {$hours == 1} {
+        append string "1 Hour "
+    } elseif {$hours > 1} {
+        append string "$hours Hours "
+    }
+    set t [expr {$t - $hours * (60 * 60)}]
+    set mins [expr {$t / 60}]
+    if {$mins == 1} {
+        append string "1 Minute "
+    } elseif {$mins > 1} {
+        append string "$mins Minutes "
+    }
+    set t [expr {$t - $mins * 60}]
+    if {$t == 1} {
+        append string "1 Second "
+    } elseif {$t > 1} {
+        append string "$t Seconds "
+    }
+    append result "<tr><td>[html::font]<b>Bucket Size</b></font></td>"
+    append result "<td>[html::font]$string</font></td></tr>\n"
+
+    append result "<tr><td>[html::font]<b>Max Per Sec</b></font></td>"
+    append result "<td>[html::font][format %.2f [expr {$max/double($secsForMax)}]]</font></td></tr>\n"
+
+    if {$periodMax > 0} {
+        append result "<tr><td>[html::font]<b>Best Per Sec</b></font></td>"
+        append result "<td>[html::font][format %.2f $periodMax]</font></td></tr>\n"
+    }
+    append result "<tr><td>[html::font]<b>Starting Time</b></font></td>"
+    switch -glob -- $options(-unit) {
+        min* {
+        append result "<td>[html::font][clock format $time \
+            -format %k:%M:%S]</font></td></tr>\n"
+        }
+        hour* {
+        append result "<td>[html::font][clock format $time \
+            -format %k:%M:%S]</font></td></tr>\n"
+        }
+        day* {
+        append result "<td>[html::font][clock format $time \
+            -format "%b %d %k:%M"]</font></td></tr>\n"
+        }
+        default {#ignore}
+    }
+
+    } else {
+
+    # Value-base histogram
+
+    set ix [lsort -integer [array names histogram]]
+
+    set mode [expr {$counter(bucketsize) * $maxName}]
+    set first [expr {$counter(bucketsize) * [lindex $ix 0]}]
+    set last [expr {$counter(bucketsize) * [lindex $ix end]}]
+
+    append result "<tr><td>[html::font]<b>Average</b></font></td>"
+    append result "<td>[html::font][format $options(-format) [counter::get $tag -avg]]</font></td></tr>\n"
+
+    append result "<tr><td>[html::font]<b>Mode</b></font></td>"
+    append result "<td>[html::font]$mode</font></td></tr>\n"
+
+    append result "<tr><td>[html::font]<b>Minimum</b></font></td>"
+    append result "<td>[html::font]$first</font></td></tr>\n"
+
+    append result "<tr><td>[html::font]<b>Maximum</b></font></td>"
+    append result "<td>[html::font]$last</font></td></tr>\n"
+
+    append result "<tr><td>[html::font]<b>Unit</b></font></td>"
+    append result "<td>[html::font]$unit</font></td></tr>\n"
+
+    append result "<tr><td colspan=2 align=center>[html::font]<b>"
+    append result "<a href=[ncgi::urlStub]?resetCounter=$tag>Reset</a></td></tr>\n"
+
+    if {$options(-max) < 0} {
+        set options(-max) [lindex $ix end]
+    }
+    if {![info exists options(-min)]} {
+        set options(-min) [lindex $ix 0]
+    }
+    }
+
+    # End table nested inside left-hand column
+
+    append result </table>\n
+    append result </td>\n
+    append result "<td valign=bottom>\n"
+
+
+    # Display the histogram
+
+    if {$options(-text)} {
+    } else {
+    append result [eval \
+        {counter::histHtmlDisplayBarChart $tag histogram $max $curIndex $time} \
+        [array get options]]
+    }
+
+    # Close the right hand column, but leave our caller's table open.
+
+    append result </td></tr>\n
+
+    return $result
+}
+
+# ::counter::histHtmlDisplayBarChart --
+#
+#   Create an html display of the histogram.
+#
+# Arguments:
+#   tag     The counter tag.
+#   histVar     The name of the histogram array
+#   max     The maximum counter value in a histogram bucket.
+#   curIndex    The "current" histogram index, for time-base histograms.
+#   time        The base, or starting time, for the time-based histograms.
+#   args        The array get of the options passed into histHtmlDisplay
+#
+# Results:
+#   HTML for the bar chart.
+#
+# Side Effects:
+#   See description.
+
+proc ::counter::histHtmlDisplayBarChart {tag histVar max curIndex time args} {
+    upvar #0 counter::T-$tag counter
+    upvar 1 $histVar histogram
+    variable secsPerMinute
+    array set options $args
+
+    append result "<table cellpadding=0 cellspacing=0 bgcolor=#eeeeee><tr>\n"
+
+    set ix [lsort -integer [array names histogram]]
+
+    for {set t $options(-min)} {$t < $options(-max)} {incr t} {
+    if {![info exists histogram($t)]} {
+        set value 0
+    } else {
+        set value $histogram($t)
+    }
+    if {$max == 0 || $value == 0} {
+        set height 1
+    } else {
+        set percent [expr {round($value * 100.0 / $max)}]
+        set height [expr {$percent * $options(-height) / 100}]
+    }
+    if {$t == $curIndex} {
+        set img src=$options(-images)/$options(-ongif)
+    } else {
+        set img src=$options(-images)/$options(-gif)
+    }
+    append result "<td valign=bottom><img $img height=$height\
+        width=$options(-width) title=$value alt=$value></td>\n"
+    }
+    append result "</tr>"
+
+    # Count buckets outside the range requested
+
+    set overflow 0
+    set underflow 0
+    foreach t [lsort -integer [array names histogram]] {
+    if {($options(-max) > 0) && ($t > $options(-max))} {
+        incr overflow
+    }
+    if {($options(-min) >= 0) && ($t < $options(-min))} {
+        incr underflow
+    }
+    }
+
+    # Append a row of labels at the bottom.
+
+    set colors {black #CCCCCC}
+    set bgcolors {#CCCCCC black}
+    set colori 0
+    if {$counter(type) != "-timehist"} {
+
+    # Label each bucket with its value
+    # This is probably wrong for hist2x and hist10x
+
+    append result "<tr>"
+    set skip $options(-skip)
+    if {![info exists counter(mult)]} {
+        set counter(mult) 1
+    }
+
+    # These are tick marks
+
+    set img src=$options(-images)/$options(-gif)
+    append result "<tr>"
+    for {set i $options(-min)} {$i < $options(-max)} {incr i} {
+        if {(($i % $skip) == 0)} {
+        append result "<td valign=bottom><img $img height=3 \
+            width=1></td>\n"
+        } else {
+        append result "<td valign=bottom></td>"
+        }
+    }
+    append result </tr>
+
+    # These are the labels
+
+    append result "<tr>"
+    for {set i $options(-min)} {$i < $options(-max)} {incr i} {
+        if {$counter(type) == "-histlog"} {
+        if {[catch {expr {int(log($i) * $counter(bucketsize))}} x]} {
+            # Out-of-bounds
+            break
+        }
+        } else {
+        set x [expr {$i * $counter(bucketsize) * $counter(mult)}]
+        }
+        set label [format $options(-format) $x]
+        if {(($i % $skip) == 0)} {
+        set color [lindex $colors $colori]
+        set bg [lindex $bgcolors $colori]
+        set colori [expr {($colori+1) % 2}]
+        append result "<td colspan=$skip><font size=1 color=$color>$label</font></td>"
+        }
+    }
+    append result </tr>
+    } else {
+    switch -glob -- $options(-unit) {
+        min*    {
+        if {$secsPerMinute != 60} {
+            set format %k:%M:%S
+            set skip 12
+        } else {
+            set format %k:%M
+            set skip 4
+        }
+        set deltaT $secsPerMinute
+        set wrapDeltaT [expr {$secsPerMinute * -59}]
+        }
+        hour*   {
+        if {$secsPerMinute != 60} {
+            set format %k:%M
+            set skip 4
+        } else {
+            set format %k
+            set skip 2
+        }
+        set deltaT [expr {$secsPerMinute * 60}]
+        set wrapDeltaT [expr {$secsPerMinute * 60 * -23}]
+        }
+        day* {
+        if {$secsPerMinute != 60} {
+            set format "%m/%d %k:%M"
+            set skip 10
+        } else {
+            set format %k
+            set skip $options(-skip)
+        }
+        set deltaT [expr {$secsPerMinute * 60 * 24}]
+        set wrapDeltaT 0
+        }
+        default {#ignore}
+    }
+    # These are tick marks
+
+    set img src=$options(-images)/$options(-gif)
+    append result "<tr>"
+    foreach t [lsort -integer [array names histogram]] {
+        if {(($t % $skip) == 0)} {
+        append result "<td valign=bottom><img $img height=3 \
+            width=1></td>\n"
+        } else {
+        append result "<td valign=bottom></td>"
+        }
+    }
+    append result </tr>
+
+    set lastLabel ""
+    append result "<tr>"
+    foreach t [lsort -integer [array names histogram]] {
+
+        # Label each bucket with its time
+
+        set label [clock format $time -format $format]
+        if {(($t % $skip) == 0) && ($label != $lastLabel)} {
+        set color [lindex $colors $colori]
+        set bg [lindex $bgcolors $colori]
+        set colori [expr {($colori+1) % 2}]
+        append result "<td colspan=$skip><font size=1 color=$color>$label</font></td>"
+        set lastLabel $label
+        }
+        if {$t == $curIndex} {
+        incr time $wrapDeltaT
+        } else {
+        incr time $deltaT
+        }
+    }
+    append result </tr>\n
+    }
+    append result "</table>"
+    if {$underflow > 0} {
+    append result "<br>Skipped $underflow samples <\
+        [expr {$options(-min) * $counter(bucketsize)}]\n"
+    }
+    if {$overflow > 0} {
+    append result "<br>Skipped $overflow samples >\
+        [expr {$options(-max) * $counter(bucketsize)}]\n"
+    }
+    return $result
+}
+
+# ::counter::start --
+#
+#   Start an interval timer.  This should be pre-declared with
+#   type either -hist, -hist2x, or -hist20x
+#
+# Arguments:
+#   tag     The counter identifier.
+#   instance    There may be multiple intervals outstanding
+#           at any time.  This serves to distinquish them.
+#
+# Results:
+#   None
+#
+# Side Effects:
+#   Records the starting time for the instance of this interval.
+
+proc ::counter::start {tag instance} {
+    upvar #0 counter::Time-$tag time
+    # clock clicks can return negative values if the sign bit is set
+    # Here we turn it into a 31-bit counter because we only want
+    # relative differences
+    set msec [expr {[clock clicks -milliseconds] & 0x7FFFFFFF}]
+    set time($instance) [list $msec [clock seconds]]
+}
+
+# ::counter::stop --
+#
+#   Record an interval timer.
+#
+# Arguments:
+#   tag     The counter identifier.
+#   instance    There may be multiple intervals outstanding
+#           at any time.  This serves to distinquish them.
+#   func        An optional function used to massage the time
+#           stamp before putting into the histogram.
+#
+# Results:
+#   None
+#
+# Side Effects:
+#   Computes the current interval and adds it to the histogram.
+
+proc ::counter::stop {tag instance {func ::counter::Identity}} {
+    upvar #0 counter::Time-$tag time
+
+    if {![info exists time($instance)]} {
+	# Extra call. Ignore so we can debug error cases.
+	return
+    }
+    set msec [expr {[clock clicks -milliseconds] & 0x7FFFFFFF}]
+    set now [list $msec [clock seconds]]
+    set delMicros [expr {[lindex $now 0] - [lindex $time($instance) 0]}]
+    if {$delMicros < 0} {
+      # Microsecond counter wrapped.
+      set delMicros [expr {0x7FFFFFFF - [lindex $time($instance) 0] +
+                            [lindex $now 0]}]
+    }
+    set delSecond [expr {[lindex $now 1] - [lindex $time($instance) 1]}]
+    unset time($instance)
+
+    # It is quite possible that the millisecond counter is much
+    # larger than 1000, so we just use it unless our microsecond
+    # calculation is screwed up.
+
+    if {$delMicros >= 0} {
+      counter::count $tag [$func [expr {$delMicros / 1000.0}]]
+    } else {
+      counter::count $tag [$func $delSecond]
+    }
+}
+
+# ::counter::Identity --
+#
+#   Return its argument.  This is used as the default function
+#   to apply to an interval timer.
+#
+# Arguments:
+#   x       Some value.
+#
+# Results:
+#   $x
+#
+# Side Effects:
+#   None
+
+
+proc ::counter::Identity {x} {
+    return $x
+}
+
+package provide counter 2.0.4
