#!/usr/X11R6/bin/wish4.2 -f
### $Id: omnimoni.tcl,v 1.0 1995/11/12 06:00:48 rvm Exp rvm $
### $Author: rvm $
### OmniMoni verion:  $Revision: 1.0 $

###############################################################################
###############################################################################
##
## OmniMoni is a highly configurable, realtime, information monitoring system.
## OmniMoni, Copyright (C) 1995  Rainer Mager
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the Free
## Software Foundation; either version 2 of the License, or (at your option)
## any later version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
## more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 675 Mass
## Ave, Cambridge, MA 02139, USA.
##
###############################################################################
###############################################################################
##
## Notes:
##
## - This file works best with a tab size of 4 and a window width of 100.
## - All global variables have their initital letter capitalized and use an
##   underbar to seperate words.
## - Boolean types and global counters have all letters capatalized.
## - All of my "procedures" have their initial letter capitalized.
##
###############################################################################
###############################################################################



#######################
### Procedure list: ###
#######################
###
##################
### Error handling
##################
### tkerror        - Handle miscellaneous tk errors.  Hopefully, this'll never get called.
### MyCatch        - Catch executing a procedure and do something with a possible error message.
### CheckColor     - Check if a given color is a recognized one.
###
#############
### Functions
#############
### IndentLine     - Indent a line any number of times.
### VarSub         - Check a string for variables and if any exist go through substituting them.
### AbsSort        - Compare two numbers' absolute values.
### SortPackArray  - Sort an array of arrays according to a value in the inner array.
### SetDebug       - Set the debug level given an integer.
###
#########
### Input
#########
### LoadConfigFile - Load in the configuration file.
### GetENVs        - Check and get any of the environmental variables that are recognized.
### GetCLAs        - Check the command line arguments for the one's I want and maybe print help.
###
##########
### Output
##########
### Blurb          - Print a message to the terminal while doing word wrap.
### Debug          - Check debug level and maybe print a message.
###
##################
### Initialization
##################
### DoGlobals      - Initialize those global variables that require initial values.
### InitAll        - Initialize everything and then begin.
###
############
### Creation
############
### ParseTop       - Recursive procedure to check the top level and do initial pass stuff on it.
### ParseWindow    - Parse a WINDOW section of the config file.
### ParseConfigure - Change any global configuration options in the config file.
### ParseGroup     - Parse a GROUP section of the config file.
### ParseLabel     - Parse a LABEL section of the config file.
### ParseGraph     - Parse a GRAPH section of a config file.
### ParsePlot      - Parse a PLOT section of the config file.
### CreateWidgets  - Recursively create all widgets in a given WINDOW.
### CreateLabel    - Create a text label (message).
### DoBinds        - Put some bindings on a given window.
###
################
### User Event Driven
################
### DemandUpdate   - Cause widget (and all ancestors) to do an update immediately.
### TogglePack     - Toggle unpacked or packed state of a widget.
### ToggleIconify  - Toggle a WINDOW iconified or not.
### TogglePlot     - Toggle showing or not showing, a line in a graph.
### Unpack         - Unpack a widget and remember who it was packed after before being unpacked.
### Repack         - Repack and unpacked widget back to where it was originally.
###
#####################
### Time Event Driven
#####################
### UpdateSeconds  - Update everything and then wait the update time and do it again.
### UpdateWidgets  - Recursively update all widgets in a given WINDOW.
### UpdatePlot     - Update all PLOT lines in a GRAPH.
### EvalSpecial    - Evaluate the special symbols in a dynamic label or a graph.
###
############
### Graphics
############
### ScrollCanvas   - Scroll the graph with the left mouse button.
### ReHash         - Redraw all hash marks to new scale of graph.



######################
######################
### Error Handling ###
######################
######################

#########################################################################
### Handle miscellaneous tk errors.  Hopefully, this'll never get called.

proc tkerror { err_msg } {
	Blurb "Warning: a Tk error occurred, program execution will continue." -9
	Debug 16 "\nThe actual Tk error was:\n$err_msg\n"
}

### END tkerror
###############


###############################################################################
### Catch executing a procedure and do something with a possible error message.

proc MyCatch { command err_msg } {
	global Substitutes
	if {[catch $command out]} {
		# if there was an error
		Debug 16 "The actual error message was:\n\n$out\n\n"
		switch [string index $err_msg [expr [string length $err_msg] - 1]] {
			"!" {
				# display just my error message with 7 spaces for "Error: "
				Blurb $err_msg -7
				exit 1
			}
			":" {
				# display my and Tk's error messages with 0
				Blurb "$err_msg  $out" -7
				exit 1
			}
		}
		# not critical so just display just my error message with 9 spaced for "Warning: "
		Blurb $err_msg -9
	}
	return $out
}

### END MyCatch
###############


###############################################
### Check if a given color is a recognized one.

proc CheckColor { color } {
	# this probably isn't terribly efficient, but I don't know of another way to do it
	frame .dummy-test-frame
	set error [catch ".dummy-test-frame config -background $color"]
	destroy .dummy-test-frame
	return $error
}

### END CheckColor
##################



#################
#################
### Functions ###
#################
#################

######################################
### Indent a line any number of times.

proc IndentLine { indent } {
	global Indent_Chars
	for {set temp 0} {$temp < $indent} {incr temp} {
		puts -nonewline $Indent_Chars
	}
}

### END IndentLine
##################


###############################################################################
### Check a string for variables and if any exist go through substituting them.

proc VarSub { string } {
	global Substitutes
	set to_return ""
	while {[regexp -indices {\$[A-Z]} $string index]} {
		# while there are more variables
		if {[string index $string [expr [lindex $index 0] - 1]] == "/"} {
			# if it is backslash quoted
			append to_return [string range $string 0 [lindex $index 1]]
			set string [string range $string [expr [lindex $index 1] + 1] end]
		} else {
			set begin [lindex $index 0]
			set count $begin
			while {[regexp {[A-Z]} [string index $string [incr count]]]} {
				# while more letters in this one do nothing
			}
			set end [expr $count - 1]
			set var_name [string range $string [expr $begin + 1] $end]
			if {[info exists Substitutes($var_name)]} {
				# remember what's before variable
				append to_return [string range $string 0 [expr $begin - 1]]
				# remember variable
				append to_return $Substitutes($var_name)
				# reset to rest for next check
				set string [string range $string [expr $end + 1] end]
			} else {
				Blurb "Error: variable ``$var_name'' referenced with no definition!" -7
				exit 1
			}
		}
	}
	append to_return $string
	return $to_return
}

### END VarSub
##############


#########################################
### Compare two numbers' absolute values.

proc AbsSort { first second } {
	set first [expr abs([lindex $first 0])]
	set second [expr abs([lindex $second 0])]
	if {$first == $second} {
		return 0
	} elseif {$first > $second} {
		return 1
	} else {
		return -1
	}
}

### END AbsSort
###############


####################################################################
### Sort an array of arrays according to a value in the inner array.

proc SortPackArray { first second } {
	global sort_variable
	# this has to be a second global command to get the first value
	global [set sort_variable]
	if {[set [set sort_variable]($first)] < [set [set sort_variable]($second)]} {
		return -1
	} elseif {[set [set sort_variable]($first)] > [set [set sort_variable]($second)]} {
		return 1
	} else {
		return 0
	}
}

### END SortPackArray
#####################


#########################################
### Set the debug level given an integer.

proc SetDebug { mode } {
	global Debug
	# calculate the new debug mode
	set Debug [expr $Debug ^ 1<<[expr $mode - 1]]
	Blurb "Debug mode set to $Debug." 0
}

### END SetDebug
################



#############
#############
### Input ###
#############
#############

###################################
### Load in the configuration file.

proc LoadConfigFile { file_name } {
	if {[file readable $file_name]} {
		# if file is readable
		set file [open $file_name]
		while {![eof $file]} {
			# read the next 1k block
			append temp [read $file 1024]
		}
		close $file
		return $temp
	} else {
		Blurb "Error: could not find or read the file ``$file_name''!" -7
		exit 1
	}
}

### END LoadConfigFile
######################


#########################################################################
### Check and get any of the environmental variables that are recognized.

proc GetENVs {} {
	global OmniConfigFile Indent_Chars Update_Delta env LIB_PATH
	foreach var "OMNIMONI_CONFIG OMNIMONI_INDENT OMNIMONI_UPDATE" {
		if {[info exists env($var)]} {
			switch $var {
				OMNIMONI_CONFIG {
					set OmniConfigFile $env(OMNIMONI_CONFIG)
				}
				OMNIMONI_INDENT {
					set Indent_Chars $env(OMNIMONI_INDENT)
				}
				OMNIMONI_UPDATE {
					set Update_Delta $env(OMNIMONI_UPDATE)
				}
				OMNIMONI_LIB {
					# where the GIFs are
					set LIB_PATH $env(OMNIMONI_LIB)
				}
			}
		}
	}
}

### END GetENVs
###############


###############################################################################
### Check the command line arguments for the one's I want and maybe print help.

proc GetCLAs {} {
	global argv0 argc argv Debug Update_Delta OmniConfigFile Indent_Chars OM_title \
			Iconify OM_legal1 OM_legal2 Substitutes
	if {$argc > 0} {
		# if there are any args
		for {set count 0} {$count < $argc} {incr count} {
			# go through them all
			switch -- [lindex $argv $count] {
				--assign -
				-a {
					set var_name [lindex $argv [incr count]]
					if {[regexp {[^A-Z]} $var_name]} {
						Blurb "Warning: variable names must only contain capital letter, \
								ignoring ``$var_name''." -9
						incr count
					} else {
						set Substitutes($var_name) [lindex $argv [incr count]]
						Debug 32 "Variable ``$var_name'' set to ``$Substitutes($var_name)''.\n"
					}
				}
				--verbose -
				-v {
					if {![catch "expr [lindex $argv [incr count]] + 1"]} {
						# if it's a number
						set Debug [lindex $argv $count]
					} else {
						Blurb "Warning:  tried to set Debug level to non-number, ignoring!" -9
					}
				}
				--indent -
				-i {
					set Indent_Chars [lindex $argv [incr count]]
				}
				--file -
				-f {
					set OmniConfigFile [lindex $argv [incr count]]
				}
				--update -
				-u {
					if {[catch "expr [lindex $argv [incr count]] + 1"]} {
						Blurb "Warning: tried to set UPDATE to non-number, ignoring!" -9
					} else {
						set Update_Delta [lindex $argv $count]
						if {!($Update_Delta > 0)} {
							set Update_Delta 1
							Blurb "Warning: UPDATE must be positive, setting to 1!" -9
						}
					}
				}
				--iconify -
				-c {
					set Iconify 1
				}
				--help {
					Blurb $OM_legal1 9
					Blurb $OM_legal2 9
					Blurb "" 0
					Blurb "Usage: $argv0 \[<-a variable value> <-d #> <-i string> <-f filename> \
							<-u seconds> --help -l --debug\]" 7
					Blurb "" 0
					Blurb "-a var val .. assign ``val'' to variable ``var'' to be substituted \
							in the configuration file" 14
					Blurb "-v 0-255 .... or'd verbosity level(s) out of possible 8 \[0\]" 14
					Blurb "-i string ... characters used for indents during debugging \[four \
							spaces\]" 14
					Blurb "-f file ..... use ``file'' instead of the default \[~/.omnimoni\]" 14
					Blurb "-u time ..... seconds between checks for updates \[1\]" 14
					Blurb "-c .......... flag to iconify main window on startup \[off\]" 14
					Blurb "--help ...... show this help information" 14
					Blurb "--debug ..... show what the -v verbosity level mean" 14
					Blurb "-l .......... show legal notices about the program" 14
					exit 0
				}
				--debug {
					Blurb $OM_legal1 9
					Blurb $OM_legal2 9
					Blurb "" 1
					Blurb "OmniMoni supports 255 different verbose settings that can be helpful \
							when debugging your configuration file.  Specifically there are 8 \
							different settings which can be used in any combination with each \
							other.  The total verbosity level is determined by thinking of each \
							setting as a bit, setting 1 being the least significant bit and \
							setting 8 a as the most significant." 0
					Blurb "" 0
					Blurb "The settings supported are:" 0
					Blurb "" 0
					Blurb "1 - Display what is parsed in the configuration file as it happens." 4
					Blurb "2 - Display what values are being set for the various widgets." 4
					Blurb "3 - Set all labels as static instead of calculating thier values." 4
					Blurb "4 - Display what is packed and unpacked as it happens." 4
					Blurb "5 - Display actual Tk error messages when they occur." 4
					Blurb "6 - Display variable substitutions and default settings." 4
					Blurb "7 - Make all widgets appear as they are created instead all at once \
							at the end." 4
					Blurb "8 - Display what is happening during updates." 4
					Blurb "" 0
					Blurb "Note that some of these settings, 1 and 8, can produce a lot of output \
							to the point where it will probably slow down program execution. \
							Also, using option 3 will cause GRAPHs to stop updating." 0
					Blurb "" 0
					Blurb "These setting can be changed interactively by pressing CONTROL-\# \
							where \# is the number of the level to toggle." 0
					exit 0
				}
				--legal -
				-l {
					Blurb "OmniMoni is a highly configurable, realtime, information monitoring \
							system. OmniMoni, Copyright (C) 1995  Rainer Mager" 0
					Blurb "" 0
					Blurb "This program is free software; you can redistribute it and/or modify \
							it under the terms of the GNU General Public License as published by \
							the Free Software Foundation; either version 2 of the License, or \
							(at your option) any later version." 0
					Blurb "" 0
					Blurb "This program is distributed in the hope that it will be useful, but \
							WITHOUT ANY WARRANTY; without even the implied warranty of \
							MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU \
							General Public License for more details." 0
					Blurb "" 0
					Blurb "You should have received a copy of the GNU General Public License \
							along with this program; if not, write to the Free Software \
							Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA." 0
					exit 0
				}
				default {
					Blurb $OM_legal1 -9
					Blurb $OM_legal2 -9
					Blurb "" -1
					Blurb "Unknown option: [lindex $argv $count]" -16
					Blurb "" -1
					Blurb "Usage: $argv0 \[<-a variable value> <-v #> <-i string> <-f filename> \
							<-u seconds> -c --help -l --debug\]" -7
					exit 1
				}
			}
		}
	}
}

### END GetCLAs
###############



##############
##############
### Output ###
##############
##############


###########################################################
### Print a message to the terminal while doing word wrap.

proc Blurb { message indent } {
	if {![catch {set size [exec stty size]}]} {
		# if the stty command worked
		set columns [lindex $size 1]
	} else {
		set columns 80
	}
	# a little margin for readability
	incr columns -2
	set line ""
	set space ""
	for {set i 0} {$i < abs($indent)} {incr i} {
		# generate the string of spaces for indention
		set space "$space "
	}
	set error [catch {
		# first we need to back-slash quote all double quotes
		set temp $message
		regsub \" $temp \\\" message
		# now do the word wrapping
		foreach word $message {
			if {[string length $line] + [string length $word] < $columns} {
				set line "$line$word "
			} else {
				if {$indent < 0} {
					# if it's negative then stderr
					puts stderr $line
				} else {
					# else to stdout
					puts $line
				}
				set line "$space$word "
			}
		}
	}]
	if {$error} {
		Blurb "Error: confusing syntax problem, check braces and quotes!" -7
		exit 1
	}
	puts $line
}

### END Blurb
#############


################################################
### Check debug level and maybe print a message.

proc Debug { level string } {
	global Debug Indent
	# Debug levels are:
	# 1 - displays what is being done while parsing the config file
	# 2 - displays the values that are assigned to each field
	# 4 - makes all labels static and displays the label code instead of the result in the widgets
	# 8 - displays what widgets are Unpacked and Repacked
	# 16 - displays actual Tk error messages
	# 32 - displays variable substitutions and defaults
	# 64 - pack during creation, not after all created
	# 128 - shows what is happening during updates
	if {$string != {}} {
		# if there is a string given
		if {$Debug & $level} {
			# if the level bit is set on
			if {$level == 1 || $level == 128} {
				# these two debug levels need special text formatting
				if {[string first ">---<" $string] != -1} {
					puts -nonewline $string
				} elseif {[set first_half [string first "->" $string]] > 0} {
					# if there is a "->" in the string
					puts -nonewline [string range $string 0 $first_half]
					puts -nonewline [format "%3d>" [incr Indent]]
					puts -nonewline [string range $string [expr $first_half + 2] end]
				} elseif {[set first_half [string first "<-" $string]] > 0} {
					# elseif there is a "<-" in the string
					puts -nonewline [string range $string 0 $first_half]
					puts -nonewline [format "%3d-" $Indent]
					puts -nonewline [string range $string [expr $first_half + 2] end]
					incr Indent -1
				} else {
					Blurb "This message should never be seen (2)!" 0
				}
			} elseif {$level == 2} {
				# this debug level is also special
				set start 0
				while {[string match {*.*=*} $string] == 1} {
					# if the format is "string1.string2=string3"
					set begin [expr [string first "." $string] + 1]
					incr start $begin
					set string [string range $string $begin end]
				}
				puts [IndentLine $Indent]$string
			} else {
				# else nothing special in string
				puts -nonewline $string
			}
		}
	} else {
		# else not a string so just return whether level was set or not
		return [expr $Debug & $level]
	}
}

### END Debug
#############



######################
######################
### Initialization ###
######################
######################

#################################################################
### Initialize those global variables that require initial values.

proc DoGlobals {} {
	global OM_title OM_legal1 OM_legal2 LIB_PATH
	set revision {$Revision: 1.0 $}
	### OM_title			label of the program, used in the window's label
	set OM_title "OmniMoni v[lindex [string trim $revision {$}] 1]"
	### OM_legal			basic legal information, used in help and intro window
	set OM_legal1 "$OM_title, Copyright (C) 1995  Rainer Mager"
	set OM_legal2 "OmniMoni comes with ABSOLUTELY NO WARRANTY; for details use ``--legal'' option."

	global Debug Indent Indent_Chars Update_Delta Elapsed_Seconds OmniConfigFile
	### Debug				debug level, check the Debug procedure for more info
	set Debug 0
	### Indent				level of indention for debug level 2
	set Indent 0
	### Indent_Chars		characters for indenting debug level 2
	set Indent_Chars "    "
	### Update_Delta		number of seconds between checking for updates
	set Update_Delta 1
	### Elapsed_Seconds		number of seconds the program has been running so far
	set Elapsed_Seconds 0
	### OmniConfigFile		file name of the configuration file
	if {[file exists "~/.omnimoni"]} {
	    set OmniConfigFile "~/.omnimoni"
	} else {
	    set OmniConfigFile "/usr/lib/omnimoni/default.omni"
	    }
	### LIB_PATH			path to the GIF files
	set LIB_PATH "/usr/lib/omnimoni"

	global Iconify To_Unpack PACK_ORDER Graph_Sort
	### Iconify				boolean to iconfiy the main window at startup or not
	set Iconify 0
	### To_Unpack			list of widgets to be unpacked after initial pass
	set To_Unpack ""
	### PACK_ORDER			what order to pack things at creation
	set PACK_ORDER 1
	### Graph_Sort			used to sort the lines in a plot
	set Graph_Sort ""

	global Defaults
	set Defaults(LABEL-PLACE) "top"
	set Defaults(LABEL-EXTRA) ""
	set Defaults(COMMAND) ""
	set Defaults(PLACE) "top"
	set Defaults(UPDATE) 10
	set Defaults(EXTRA) ""
	set Defaults(WIDTH) 320
	set Defaults(HEIGHT) 200
	set Defaults(HASHES) 0
	set Defaults(ZEROCOL) "\#fff"
	set Defaults(HASHCOL) "\#999"
	set Defaults(HISTORY) 0
	set Defaults(DIRECTION) "left"
	set Defaults(COLOR) "\#fff"
	set Defaults(TYPE) "lined"
}

### END DoGlobals
#################


#########################################
### Initialize everything and then begin.

proc InitAll {} {
	global OM_title OmniConfigFile OM_legal1 OM_legal2 Update_Delta Indent Windows Iconify \
			Toggle_Iconify To_Unpack LIB_PATH
	# this is needed for some of those graphs
	set tcl_precision 17
	DoGlobals
	GetENVs
	# CLAs come second to override ENVs
	GetCLAs
	# make window how I like it
	wm resizable . no no
	wm title . $OM_title
	wm iconname . OmniMoni
	wm protocol . WM_DELETE_WINDOW exit
	. config -bg \#116
	# put a picture or a message in there
	if {[file exists $LIB_PATH/omnimoni.big.gif]} {
		canvas .intro
		image create photo omnimoni-big -file $LIB_PATH/omnimoni.big.gif
		.intro create image 0 0 -image omnimoni-big -anchor nw
		.intro config -width [image width omnimoni-big] -height [image height omnimoni-big]
	} else {
		message .intro -justify center -aspect 450 \
				-font "-Adobe-Helvetica-Bold-R-Normal--*-120-*" \
				-text "$OM_legal1\n$OM_legal2\nPlease wait.  Setting up..."
		.intro configure -bg \#116 -fore \#c34
	}
	pack .intro
	# put up the intro message whine they wait
	update
	# make the top level stuff
	MyCatch "menu .pack_ -tearoff 0" "Error: could not create main menu!"
	DoBinds ""
	# go parse the config file
	ParseTop [LoadConfigFile $OmniConfigFile]
	# create everything that was parsed
	set Indent -1
	foreach window [array names Windows] {
		MyCatch "menu .pack_$window -tearoff 0" \
				"Error: could not create menu for WINDOW ``$window''!"
		DoBinds $window
		CreateWidgets $window
		if {$Toggle_Iconify(.$window)} {
			wm deiconify .$window
		}
	}
	set Indent 0
	# unpack everything that is set to be unpacked by default
	foreach widget $To_Unpack {
		Unpack .$widget
	}
	unset To_Unpack
	# change the message to something more subtle
	if {[image names] == "omnimoni-big"} {
		image delete omnimoni-big
	}
	if {[file exists $LIB_PATH/omnimoni.small.gif]} {
		image create photo omnimoni-small -file $LIB_PATH/omnimoni.small.gif
		.intro create image 0 0 -image omnimoni-small -anchor nw
		.intro config -width [image width omnimoni-small] -height [image height omnimoni-small]
	} else {
		.intro configure -aspect 5000 -text "$OM_legal1"
	}
	# get rid of the into window if we're supposed to
	if {$Iconify} {
		wm iconify .
	}
	unset Iconify
	# start the time going
	UpdateSeconds $Update_Delta
}

### END InitAll
###############



################
################
### Creation ###
################
################

###############################################################################
### Recursive procedure to check the top level and do initial pass stuff on it.

proc ParseTop { config_file } {
	MyCatch "llength {$config_file}" \
			"Error: syntax problem in the top level of the config file:"
	for {set index_count 0} {$index_count < [llength $config_file]} {incr index_count 2} {
		# go through each "command argument" pair
		set key_word [lindex $config_file $index_count]
		set arguments [lindex $config_file [expr $index_count + 1]]
		switch -- $key_word {
			"\#" -
			COMMENT {
				# take the next section as comments
				Debug 1 "COMMENT\n"
			}
			!WINDOW -
			WINDOW {			
				MyCatch "llength {$arguments}" "Error: syntax problem in WINDOW in \
						the top level:"
				if {[string index $key_word 0] == "!"} {
					ParseWindow $arguments 1
				} else {
					ParseWindow $arguments 0
				}
			}
			INCLUDE {
				MyCatch "llength {$arguments}" "Error: syntax problem in INCLUDE in \
						the top level:"
				set arguments [VarSub $arguments]
				set config_file [concat [lrange $config_file 0 [expr $index_count - 1]] \
						[LoadConfigFile $arguments] \
						[lrange $config_file [expr $index_count +2] end]]
				incr index_count -2
			}
			CONFIGURE {
				MyCatch "llength {$arguments}" "Error: syntax problem in CONFIGURE in \
						the top level:"
				ParseConfigure $arguments
			}
			default {
				# if this piece is a single piece
				Blurb "Warning: found a section beginning with ``$key_word'', ignoring section!" -9
			}
		}
	}
}

### END ParseTop
################


##############################################
### Parse a WINDOW section of the config file.

proc ParseWindow { section DONT_PACK } {
	global PACK_ORDER Windows OM_title Windows Toggle_Iconify Debug
	# take the next bracketed section as a group command
	MyCatch "llength {$section}" "Error: syntax problem in WINDOW section:"
	if {[lindex $section 0] != "NAME"} {
		# every window must have a name as the first command
		Blurb "Error: the first item in a WINDOW must by the group's NAME! You had \
				``[lindex $section 0]'' instead!" -7
		exit 1
	}
	set real_window_name [lindex $section 1]
	if {[string match {*[.{}]*} $real_window_name] == 1} {
		# check for weird characters
		Blurb "Error: the NAME of WINDOW, ``$real_window_name'', can not have any of the \
				characters ``\{'', ``\}'', or ``.''!" -7
		exit 1
	}
	set lower_window_name [string tolower $real_window_name]
	if {[winfo exists .$lower_window_name]} {
		# check it it already exists
		Blurb "Error: the WINDOW, ``$real_window_name'', already exists, can not create another one!" -7
		exit 1
	}
	Debug 1 "Entered WINDOW       -> $real_window_name\n"
	MyCatch "toplevel .$lower_window_name" "Error: could not create WINDOW ``$real_window_name''!"
	# make this window the way I like it
	wm resizable .$lower_window_name no no
	wm title .$lower_window_name $OM_title
	.pack_ add checkbutton -label "$real_window_name" -command "ToggleIconify \
			.$lower_window_name" -variable Toggle_Iconify(.$lower_window_name)
	if {$DONT_PACK} {
		# if we're to unpack this window at start up
		set Windows($lower_window_name) 0
		set Toggle_Iconify(.$lower_window_name) 0	
		ToggleIconify .$lower_window_name
	} else {
		set Windows($lower_window_name) $PACK_ORDER
		set Toggle_Iconify(.$lower_window_name) 1
	}
	if {![Debug 64 ""]} {
		# if we're not to show everything as it is created
		wm iconify .$lower_window_name
	}
	for {set index_count 2} {$index_count < [llength $section]} {incr index_count 2} {
		# go through each "command argument" pair
		set key_word [lindex $section $index_count]
		set arguments [lindex $section [expr $index_count + 1]]
		switch -- $key_word {
			"\#" -
			COMMENT {
				# take the next bracketed section as comments
				Debug 1 "COMMENT\n"
			}
			CONFIGURE {
				MyCatch "llength {$arguments}" "Error: syntax problem in CONFIGURE in \
						WINDOW, ``$real_window_name'':"
				ParseConfigure $arguments
			}
			LABEL {
				MyCatch "llength {$arguments}" "Error: syntax problem in LABEL in \
						WINDOW, ``$real_window_name'':"
				set arguments [VarSub $arguments]
				wm title .$lower_window_name $arguments
			}
			PLACE {
				set arguments [VarSub $arguments]
				if {[string index [set x [lindex $arguments 0]] 0] != "-"} {
					set x "+$x"
				}
				if {[string index [set y [lindex $arguments 1]] 0] != "-"} {
					set y "+$y"
				}
				MyCatch "wm geometry .$lower_window_name $x$y" "Warning: could not set PLACE \
						for WINDOW, ``$real_window_name''."
			}
			!GROUP -
			GROUP {
				MyCatch "llength {$arguments}" "Error: syntax problem in GROUP in \
						WINDOW, ``$real_window_name'':"
				if {[string index $key_word 0] == "!"} {
					ParseGroup $lower_window_name $arguments 1
				} else {
					ParseGroup $lower_window_name $arguments 0
				}
			}
			!GRAPH -
			GRAPH {			
				MyCatch "llength {$arguments}" "Error: syntax problem in GRAPH in \
						WINDOW, ``$real_window_name'':"
				if {[string index $key_word 0] == "!"} {
					ParseGraph $lower_window_name $arguments 1
				} else {
					ParseGraph $lower_window_name $arguments 0
				}
			}
			default {
				# if this piece is a single piece
				Blurb "Warning: found a section beginning with ``$key_word'', ignoring section!" -9
			}
		}
	}
	Debug 1 "Left WINDOW          <- $real_window_name\n"
}

### END ParseWindow
###################


###############################################################
### Change any global configuration options in the config file.

proc ParseConfigure { opt_list } {
	global OmniConfigFile Update_Delta Defaults Substitutes
	MyCatch "llength {$opt_list}" "Error:  syntax problem in CONFIGURE section:"
	for {set index_count 0} {$index_count < [llength $opt_list]} {incr index_count 2} {
		set key_word [lindex $opt_list $index_count]
		set arguments [lindex $opt_list [expr $index_count + 1]]
		Debug 1 "Configured option    >---< $key_word\n"
		switch $key_word {
			ASSIGN {
				MyCatch "llength {$arguments}" "Error: syntax problem in ASSIGN in \
						CONFIGURE:"
				set arguments [VarSub $arguments]
				set var_name [lindex $arguments 0]
				set value [lindex $arguments 1]
				if {[regexp {[^A-Z]} $var_name]} {
					Blurb "Warning: variable names must only contain capital letter, ignoring \
							``$var_name''." -9
				} else {
					if {![info exists Substitutes($var_name)]} {
						set Substitutes($var_name) $value
						Debug 32 "Variable ``$var_name'' set to ``$value''.\n"
					} else {
						Debug 32 "Variable ``$var_name'' not set to ``$value'' because it was \
								previously defined.\n"
					}
				}
			}
			"\#" -
			COMMENT {
				# take the next bracketed section as comments
				Debug 1 "COMMENT\n"
			}
			DEFAULT {
				MyCatch "llength {$arguments}" "Error: syntax problem in DEFAULT in \
						CONFIGURE:"
				set default [lindex $arguments 0]
				set value [VarSub [lindex $arguments 1]]
				switch $default {
					PLACE -
					LABEL-PLACE {
						if {$value != "left" && $value != "right" && \
								$value != "top" && $value != "bottom"} {
							Blurb "Warning: $default when setting DEFAULT must be \
									one of ``left'', ``right'', ``top'', or ``bottom'', not \
									``$value''!" -9
						} else {
							set Defaults($default) "$value"
							Debug 32 "Set DEFAULT, ``$default'' to ``$value''.\n"
						}
					}
					UPDATE -
					WIDTH -
					HEIGHT -
					HASHES -
					HISTORY {
						if {[catch "expr $value + 1"]} {
							Blurb "Warning: $default when setting DEFAULT must be a number, not \
									``$value''!" -9
						} else {
							set Defaults($default) "$value"
							Debug 32 "Set DEFAULT, ``$default'' to ``$value''.\n"
						}
					}
					COLOR -
					HASHCOL -
					ZEROCOL {
						if {[CheckColor $value]} {
							Blurb "Warning: $default, ``$value'', when setting DEFAULT is \
									not a recognized color!" -9
						} else {
							set Defaults($default) "$value"
							Debug 32 "Set DEFAULT, ``$default'' to ``$value''.\n"
						}
					}
					TYPE {
						if {$value != "lined" && $value != "solid"} {
							Blurb "Warning: PLOT TYPE, ``$value'', must be ``lined'' or \
									``solid'' when setting DEFAULT!" -9
						} else {
							set Defaults($default) "$value"
							Debug 32 "Set DEFAULT, ``$default'' to ``$value''.\n"
						}
					}
					DIRECTION {
						if {$value != "left" && $value != "right" && \
								$value != "up" && $value != "down"} {
							Blurb "Warning: DIRECTION when setting DEFAULT must be one of \
									``left'', ``right'', ``up'', or ``down'', not ``$value''!" -9
						} else {
							set Defaults($default) "$value"
							Debug 32 "Set DEFAULT, ``$default'' to ``$value''.\n"
						}
					}
					DELTA {
						if {[catch "expr $value + 1"]} {
							Blurb "Warning: tried to set UPDATE to non-number, ignoring!" -9
						} else {
							global Update_Delta
							set Update_Delta $value
							if {!($Update_Delta > 0)} {
								set Update_Delta 1
								Blurb "Warning: UPDATE must be positive, setting to 1!" -9
							}
						}
					}
					EXTRA -
					LABEL-EXTRA -
					COMMAND -
					TYPE {
						set Defaults($default) "$value"
						Debug 32 "Set DEFAULT, ``$default'' to ``$value''.\n"
					}
					default {
						Blurb "Error: tried to set an unrecognized DEFAULT, ``$default''!" -7
						exit 1
					}
				}
			}
			EXTRA {
				MyCatch "llength {$arguments}" "Error: syntax problem in EXTRA in \
						CONFIGURE:"
				set arguments [VarSub $arguments]
				MyCatch "option add [lindex $arguments 0] [lindex $arguments 1]" \
					"Warning: could not set MISC option ``[lindex $arguments 0]'' to \
					``[lindex $arguments 1]''."
			}
			INCLUDE {
				MyCatch "llength {$arguments}" "Error: syntax problem in INCLUDE in \
						CONFIGURE:"
				set arguments [VarSub $arguments]
				set opt_list [concat [lrange $opt_list 0 [expr $index_count - 1]] \
						[LoadConfigFile $arguments] \
						[lrange $opt_list [expr $index_count +2] end]]
				incr index_count -2
			}
			default {
				Blurb "Error: tried to configure an unrecognized option, ``$key_word''!" -7
				exit 1
			}
		}
	}
}

### END ParseConfigure
######################


#############################################
### Parse a GROUP section of the config file.

proc ParseGroup { parent_name section DONT_PACK } {
	global Defaults PACK_ORDER To_Unpack Times Last_Time
	MyCatch "llength {$section}" "Error: syntax problem in GROUP section:"
	if {$parent_name == ""} {
		set parent_name "top_frames"
	}
	if {[lindex $section 0] != "NAME"} {
		Blurb "Error: the first item in a GROUP must by the group's NAME! \
				You had ``[lindex $section 0]'' instead!" -7
		exit 1
	}
	set real_frame_name [lindex $section 1]
	if {[string match {*[.{}]*} $real_frame_name] == 1} {
		Blurb "Error: the NAME of GROUP, ``$real_frame_name'', can not have any of the \
				characters ``\{'', ``\}'', or ``.''!" -7
		exit 1
	}
	set lower_frame_name [string tolower $real_frame_name]
	set frame_name $parent_name.$lower_frame_name
	if {[info exists Times($frame_name)]} {
		# if this group already exists
		Blurb "Error: the GROUP, ``$real_frame_name'', already exists, can not create \
				another one!" -7
		exit 1
	}
	set Times($frame_name) 0
	set Last_Time($frame_name) 0
	# add the name of this frame into the ones for its parent
	global [set parent_name] [set frame_name]
	set [set frame_name](NAME) $real_frame_name
	if {$DONT_PACK} {
		lappend To_Unpack $frame_name
	}
	set [set parent_name]($lower_frame_name) $PACK_ORDER
	incr PACK_ORDER
	Debug 1 "Entered GROUP        -> $parent_name.$real_frame_name\n"
	set [set frame_name](TYPE) "GROUP"
	for {set index_count 2} {$index_count < [llength $section]} {incr index_count 2} {
		set key_word [lindex $section $index_count]
		set arguments [lindex $section [expr $index_count + 1]]
		switch $key_word {
			"\#" -
			COMMENT {
				# take the next bracketed section as comments
				Debug 1 "COMMENT\n"
			}
			CONFIGURE {
				MyCatch "llength {$arguments}" "Error: syntax problem in CONFIGURE in \
						GROUP, ``$real_frame_name'':"
				ParseConfigure $arguments
			}
			LABEL -
			!LABEL {
				MyCatch "llength {$arguments}" "Error: syntax problem in LABEL in \
						GROUP, ``$real_frame_name'':"
				if {[string index $key_word 0] == "!"} {
					ParseLabel $frame_name $arguments 1
				} else {
					ParseLabel $frame_name $arguments 0
				}
			}
			COMMAND {
				set arguments [VarSub $arguments]
				set [set frame_name](COMMAND) $arguments
			}
			UPDATE {
				set arguments [VarSub $arguments]
				if {[catch "expr $arguments + 1"]} {
					Blurb "Warning: GROUP UPDATE, ``$arguments'', must be a number for \
							``$parent_name.$real_frame_name''!  Setting to DEFAULT, \
							$Defaults(UPDATE)." -9
				} else {
					set [set frame_name](UPDATE) $arguments
				}
			}
			PLACE {
				set arguments [VarSub $arguments]
				if {$arguments == "left" || $arguments == "right" || \
						$arguments == "top" || $arguments == "bottom"} {
					set [set frame_name](PLACE) $arguments
				} else {
					Blurb "Warning: PLACE for GROUP, ``$parent_name.$real_frame_name'' must be \
							one of ``left'', ``right'', ``top'', or ``bottom'', not \
							``$arguments''! Setting to DEFAULT, ``$Defaults(PLACE)''." -9
				}
			}
			EXTRA {
				MyCatch "llength {$arguments}" "Error: syntax problem in EXTRA in \
						GROUP, ``$real_frame_name'':"
				set arguments [VarSub $arguments]
				set [set frame_name](EXTRA) $arguments
			}
			GROUP -
			!GROUP {
				MyCatch "llength {$arguments}" "Error: syntax problem in GROUP in \
						GROUP, ``$real_frame_name'':"
				if {[string index $key_word 0] == "!"} {
					ParseGroup $frame_name $arguments 1
				} else {
					ParseGroup $frame_name $arguments 0
				}
			}
			GRAPH -
			!GRAPH {
				MyCatch "llength {$arguments}" "Error: syntax problem in GRAPH in \
						GROUP, ``$real_frame_name'':"
				if {[string index $key_word 0] == "!"} {
					ParseGraph $frame_name $arguments 1
				} else {
					ParseGraph $frame_name $arguments 0
				}
			}
			INCLUDE {
				MyCatch "llength {$arguments}" "Error: syntax problem in INCLUDE in \
						GROUP, ``$real_frame_name'':"
				set arguments [VarSub $arguments]
				set section [concat [lrange $section 0 [expr $index_count - 1]] \
						[LoadConfigFile $arguments] \
						[lrange $section [expr $index_count +2] end]]
				incr index_count -2
			}
			default {
				Blurb "Warning: found unrecognized word, ``$key_word'', in GROUP \
						``$parent_name.$real_frame_name''!" -9
			}
		}
	}
	foreach item "COMMAND UPDATE PLACE EXTRA" {
		if {![info exists [set frame_name]($item)]} {
			set [set frame_name]($item) $Defaults($item)
		}
	}
	Debug 1 "Left group           <- $parent_name.$real_frame_name\n"
}

### END ParseGroup
###################


#############################################
### Parse a LABEL section of the config file.

proc ParseLabel { parent_name section DONT_PACK } {
	global Defaults PACK_ORDER To_Unpack Times Last_Time
	MyCatch "llength {$section}" "Error: syntax problem in LABEL section:"
	if {[lindex $section 0] != "NAME"} {
		Blurb "Error: the first item in a LABEL must by the label's NAME! \
				You had ``[lindex $section 0]'' instead!" -7
		exit 1
	}
	set real_label_name [lindex $section 1]
	if {[string match {*[.{}]*} $real_label_name] == 1} {
		Blurb "Error: the NAME of LABEL, ``$real_label_name'', can not have any of the \
				characters ``\{'', ``\}'', or ``.''!" -7
		exit 1
	}
	set lower_label_name [string tolower $real_label_name]
	set label_name $parent_name.$lower_label_name
	if {[info exists Times($label_name)]} {
		# if this label already exists
		Blurb "Error: the LABEL, ``$real_label_name'', already exists, can no create \
				another one!" -7
		exit 1
	}
	set Times($label_name) 0
	set Last_Time($label_name) 0
	# add the name of this frame into the ones for its parent
	global [set parent_name] [set label_name]
	set [set label_name](NAME) $real_label_name
	if {$DONT_PACK} {
		lappend To_Unpack $label_name
	}
	set [set parent_name]($lower_label_name) $PACK_ORDER
	incr PACK_ORDER
	Debug 1 "Configured LABEL     -> $parent_name.$real_label_name\n"
	set [set label_name](TYPE) "LABEL"
	for {set index_count 2} {$index_count < [llength $section]} {incr index_count 2} {
		set key_word [lindex $section $index_count]
		set arguments [lindex $section [expr $index_count + 1]]
		switch $key_word {
			"\#" -
			COMMENT {
				# take the next bracketed section as comments
				Debug 1 "COMMENT\n"
			}
			STATIC {
				MyCatch "llength {$arguments}" "Error: syntax problem in STATIC in \
						LABEL, ``$real_label_name'':"
				set arguments [VarSub $arguments]
				if {[info exists FOUND_STRING]} {
					Blurb "Error: can only have one string in LABEL, ``$parent_name'', \
							you already have a $FOUND_STRING, \
							``[set [set label_name]($FOUND_STRING)]'' \
							before the STATIC, ``$arguments''!" -7
					exit 1
				} else {
					set [set label_name](STATIC) $arguments
					set FOUND_STRING "STATIC"
				}
			}
			EXPRESSION {
				MyCatch "llength {$arguments}" "Error: syntax problem in EXPRESSION in \
						LABEL, ``$real_label_name'':"
				set arguments [VarSub $arguments]
				if {[info exists FOUND_STRING]} {
					Blurb "Error: can only have one string in LABEL, ``$parent_name'', \
							you already have a $FOUND_STRING, \
							``[set [set label_name]($FOUND_STRING)]'' \
							before the EXPRESSION, ``$arguments''!" -7
					exit 1
				} else {
					set [set label_name](EXPRESSION) $arguments
					set FOUND_STRING "EXPRESSION"
				}
			}
			PLACE {
				set arguments [VarSub $arguments]
				if {$arguments == "left" || $arguments == "right" || \
						$arguments == "top" || $arguments == "bottom"} {
					set [set label_name](PLACE) $arguments
				} else {
					Blurb "Warning: PLACE for LABEL, ``$parent_name'' must be \
							one of ``left'', ``right'', ``top'', or ``bottom'', not \
							``$arguments''! Setting to DEFAULT, ``$Defaults(LABEL-PLACE)''." -9
					set [set label_name](PLACE) $Defaults(LABEL-PLACE)
				}
			}
			EXTRA {
				MyCatch "llength {$arguments}" "Error: syntax problem in EXTRA in \
						LABEL, ``$real_label_name'':"
				set arguments [VarSub $arguments]
				set [set label_name](EXTRA) $arguments
			}
			default {
				Blurb "Error: found unrecognized word, ``$key_word'', in LABEL \
						``$parent_name''!" -7
				exit 1
			}
		}
	}
	set FOUND_STRING 0
	foreach item "STATIC EXPRESSION" {
		if {[info exists [set label_name]($item)]} {
			set FOUND_STRING 1
		}
	}
	if {!$FOUND_STRING} {
		Blurb "Error: you must specify one string in the LABEL, ``$real_label_name``, \
				in the GROUP, ``$parent_name''!" -7
		exit 1
	}
	foreach item "PLACE EXTRA" {
		if {![info exists [set label_name]($item)]} {
			set [set label_name]($item) $Defaults(LABEL-$item)
		}
	}
}

### END ParseLabel
##################
		

###########################################
### Parse a GRAPH section of a config file.

proc ParseGraph { parent_name section DONT_PACK } {
	global Times Last_Time PACK_ORDER Defaults To_Unpack
	# take the next bracketed section as a group command
	MyCatch "llength {$section}" "Error: syntax problem in GRAPH section:"
	if {[lindex $section 0] != "NAME"} {
		Blurb "Error: the first item in a GRAPH must by the graph's NAME! \
				You had ``[lindex $section 0]'' instead!" -7
		exit 1
	}
	set real_graph_name [lindex $section 1]
	if {[string match {*[.{}]*} $real_graph_name] == 1} {
		Blurb "Error: the NAME of GRAPH, ``$real_graph_name'', can not have any of the \
				characters ``\{'', ``\}'', or ``.''!" -7
		exit 1
	}
	set lower_graph_name [string tolower $real_graph_name]
	set graph_name $parent_name.$lower_graph_name
	if {[info exists Times($graph_name)]} {
		# if this graph already exists
		Blurb "Error: the GRAPH, ``$real_graph_name'', already exists, can not create \
				another one!" -7
		exit 1
	}
	set Times($graph_name) 0
	set Last_Time($graph_name) 0
	# add the name of this graph into the ones for its parent
	global [set parent_name]
	global [set graph_name]
	set [set graph_name](NAME) $real_graph_name
	if {$DONT_PACK} {
		lappend To_Unpack $graph_name
	}
	set [set parent_name]($lower_graph_name) $PACK_ORDER
	incr PACK_ORDER
	Debug 1 "Entered GRAPH        -> $parent_name.$real_graph_name\n"
	set [set graph_name](TYPE) "GRAPH"
	for {set index_count 2} {$index_count < [llength $section]} {incr index_count 2} {
		set key_word [lindex $section $index_count]
		set arguments [lindex $section [expr $index_count + 1]]
		switch $key_word {
			"\#" -
			COMMENT {
				# take the next bracketed section as comments
				Debug 1 "COMMENT\n"
			}
			CONFIGURE {
				MyCatch "llength {$arguments}" "Error: syntax problem in CONFIGURE in \
						GRAPH, ``$real_graph_name'':"
				ParseConfigure $arguments
			}
			WIDTH {
				MyCatch "llength {$arguments}" "Error: syntax problem in WIDTH in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				if {[catch "expr $arguments + 0"] || $arguments < 5} {
					Blurb "Warning: the WIDTH of GRAPH ``$parent_name.$real_graph_name'' \
							must be a number at least 5, not ``$arguments''! Setting WIDTH \
							to 5." -9
					set [set graph_name](WIDTH) 5
				} else {
					set [set graph_name](WIDTH) $arguments
				}
			}
			HEIGHT {
				MyCatch "llength {$arguments}" "Error: syntax problem in HEIGHT in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				if {[catch "expr $arguments + 0"] || $arguments < 5} {
					Blurb "Warning: the HEIGHT of GRAPH ``$parent_name.$real_graph_name'' \
							must be a number at least 5, not ``$arguments''! Setting HEIGHT \
							to 5." -9
					set [set graph_name](HEIGHT) 5
				} else {
					set [set graph_name](HEIGHT) $arguments
				}
			}
			HASHES {
				MyCatch "llength {$arguments}" "Error: syntax problem in HASHES in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				if {[catch "expr $arguments + 0"]} {
					Blurb "Warning: the HASHES of GRAPH ``$parent_name.$real_graph_name'' \
							must be a number, not ``$arguments''! Setting to the DEFAULT, \
							``$Defaults(HASHES)''." -9
				} else {
					set [set graph_name](HASHES) $arguments
				}
			}
			ZEROCOL {
				MyCatch "llength {$arguments}" "Error: syntax problem in ZEROCOL in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				if {![CheckColor $arguments]} {
					set [set graph_name](ZEROCOL) $arguments
				} else {
					Blurb "Warning: ZEROCOLOR, ``$arguments'', for GRAPH \
							``$parent_name.$real_graph_name'' is not a recognized color! \
							Setting to DEFAULT, ``$Defaults(ZEROCOL)''." -9
				}
			}
			HASHCOL {
				MyCatch "llength {$arguments}" "Error: syntax problem in HASHCOL in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				if {![CheckColor $arguments]} {
					set [set graph_name](HASHCOL) $arguments
				} else {
					Blurb "Warning: HASHCOLOR, ``$arguments'', for GRAPH \
							``$parent_name.$real_graph_name'' is not a recognized color! \
							Setting to DEFAULT, ``$Defaults(HASHCOL)''." -9
				}
			}
			COMMAND {
				set arguments [VarSub $arguments]
				set [set graph_name](COMMAND) $arguments
			}
			UPDATE {
				set arguments [VarSub $arguments]
				if {[catch "expr $arguments + 1"]} {
					Blurb "Warning: GRAPH UPDATE, ``$arguments'', must be a number for \
							``$parent_name.$real_graph_name''!  Setting to DEFAULT, \
							$Defaults(UPDATE)." -9
				} else {
					set [set graph_name](UPDATE) $arguments
				}
			}
			DIRECTION {
				MyCatch "llength {$arguments}" "Error: syntax problem in DIRECTION in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				if {$arguments == "left" || $arguments == "right" || \
						$arguments == "up" || $arguments == "down"} {
					set [set graph_name](DIRECTION) $arguments
				} else {
					Blurb "Warning: DIRECTION for PLOT, ``$parent_name.$real_graph_name'' \
							must be one of ``left'', ``right'', ``up'', or ``down'', not \
							``$arguments''!  Setting to the DEFAULT ``$Defaults(DIRECTION)''." -9
					set [set graph_name](DIRECTION) $Defaults(DIRECTION)
				}
			}
			HISTORY {
				MyCatch "llength {$arguments}" "Error: syntax problem in HISTORY in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				if {[catch "expr $arguments + 1"]} {
					Blurb "Warning: GRAPH HISTORY, ``$arguments'', must be a number for \
							``$parent_name.$real_graph_name''!  Setting to DEFAULT, \
							$Defaults(HISTORY)." -9
				} else {
					set [set graph_name](HISTORY) $arguments
				}
			}
			PLACE {
				set arguments [VarSub $arguments]
				if {$arguments == "left" || $arguments == "right" || \
						$arguments == "top" || $arguments == "bottom"} {
					set [set graph_name](PLACE) $arguments
				} else {
					Blurb "Warning: PLACE for GRAPH, ``$parent_name.$real_graph_name'' \
							must be one of ``left'', ``right'', ``top'', or ``bottom'', not \
							``$arguments''! Setting to the DEFAULT, ``$Defaults(PLACE)''." -7
				}
			}
			EXTRA {
				MyCatch "llength {$arguments}" "Error: syntax problem in EXTRA in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				set [set graph_name](EXTRA) $arguments
			}
			PLOT -
			!PLOT {
				MyCatch "llength {$arguments}" "Error: syntax problem in PLOT in \
						GRAPH, ``$real_graph_name'':"
				if {[string index $key_word 0] == "!"} {
					ParsePlot $graph_name $arguments 1
				} else {
					ParsePlot $graph_name $arguments 0
				}
			}
			INCLUDE {
				MyCatch "llength {$arguments}" "Error: syntax problem in INCLUDE in \
						GRAPH, ``$real_graph_name'':"
				set arguments [VarSub $arguments]
				set section [concat [lrange $section 0 [expr $index_count - 1]] \
						[LoadConfigFile $arguments] \
						[lrange $section [expr $index_count +2] end]]
				incr index_count -2
			}
			default {
				Blurb "Warning: found unrecognized word, ``$key_word'', in GROUP \
						``$parent_name.$real_graph_name''!" -9
			}
		}
	}
	foreach item "COMMAND UPDATE PLACE EXTRA DIRECTION WIDTH HEIGHT HASHES ZEROCOL \
			HASHCOL HISTORY" {
		if {![info exists [set graph_name]($item)]} {
			set [set graph_name]($item) $Defaults($item)
		}
	}
	if {[set [set graph_name](DIRECTION)] == "up" || \
			[set [set graph_name](DIRECTION)] == "down"} {
		if {[set [set graph_name](HISTORY)] < [set [set graph_name](HEIGHT)]} {
			Blurb "Warning: HISTORY must be greater that or equal to HEIGHT in GRAPH \
					``$parent_name.$real_graph_name''! Setting HISTORY to \
					[set [set graph_name](HEIGHT)]." -9
			set [set graph_name](HISTORY) [set [set graph_name](HEIGHT)]
		}
	} else {
		if {[set [set graph_name](HISTORY)] < [set [set graph_name](WIDTH)]} {
			Blurb "Warning: HISTORY must be greater that or equal to WIDTH in GRAPH \
					``$parent_name.$real_graph_name''! Setting HISTORY to \
					[set [set graph_name](WIDTH)]." -9
			set [set graph_name](HISTORY) [set [set graph_name](WIDTH)]
		}
	}	
	Debug 1 "Left graph           <- $parent_name.$real_graph_name\n"
}

### END ParseGraph
##################


############################################
### Parse a PLOT section of the config file.

proc ParsePlot { parent_name section DONT_PACK } {
	global [set parent_name] Times Last_Time Defaults
	MyCatch "llength {$section}" "Error: syntax problem in PLOT section:"
	if {[lindex $section 0] != "NAME"} {
		Blurb "Error: the first item in a PLOT must by the PLOT's NAME! \
				You had ``[lindex $section 0]'' instead!" -7
		exit 1
	}
	set real_plot_name [lindex $section 1]
	if {[string match {*[.{}]*} $real_plot_name] == 1} {
		Blurb "Error: the NAME of PLOT, ``$real_plot_name'', can not have any of the \
				characters ``\{'', ``\}'', or ``.''!" -7
		exit 1
	}
	set lower_plot_name [string tolower $real_plot_name]
	set plot_name $parent_name.[string tolower $real_plot_name]
	global [set parent_name]
	global [set plot_name]
	if {[info exists [set plot_name](NAME)]} {
		# if this plot already exists
		Blurb "Error: the PLOT, ``$real_plot_name'', already exists, can not create \
				another one!" -7
		exit 1
	}
	set Times($plot_name) 1
	set Last_Time($plot_name) 0
	# add the name of this plot into the ones for its parent
	set [set plot_name](NAME) $real_plot_name
	if {$DONT_PACK} {
		set [set parent_name]($lower_plot_name) 0
	} else {
		set [set parent_name]($lower_plot_name) 1
	}
	for {set index_count 2} {$index_count < [llength $section]} {incr index_count 2} {
		set key_word [lindex $section $index_count]
		set arguments [lindex $section [expr $index_count + 1]]
		switch $key_word {
			"\#" -
			COMMENT {
				# take the next bracketed section as comments
				Debug 1 "COMMENT\n"
			}
			EXPRESSION {
				MyCatch "llength {$arguments}" "Error: syntax problem in CONFIGURE in \
						PLOT, ``$real_plot_name'':"
				set arguments [VarSub $arguments]
				if {[info exists [set plot_name](EXPRESSION)]} {
					Blurb "Error: can only have one EXPRESSION in PLOT, \
							``$parent_name.$real_plot_name'', you already have one, \
							``[set [set plot_name]($FOUND_VALUE)]''!" -7
					exit 1
				} else {
					set [set plot_name](EXPRESSION) $arguments
				}
			}
			COLOR {
				MyCatch "llength {$arguments}" "Error: syntax problem in COLOR in \
						PLOT, ``$real_plot_name'':"
				set arguments [VarSub $arguments]
				if {![CheckColor \{$arguments\}]} {
					set [set plot_name](COLOR) $arguments
				} else {
					Blurb "Warning: COLOR, ``$arguments'', for PLOT \
							``$parent_name.$real_plot_name'' is not a recognized color! \
							Setting to DEFAULT, ``$Defaults(COLOR)''." -9
				}
			}
			TYPE {
				MyCatch "llength {$arguments}" "Error: syntax problem in TYPE in \
						PLOT, ``$real_plot_name'':"
				set arguments [VarSub $arguments]
				if {$arguments != "lined" && $arguments != "solid"} {
					Blurb "Warning: PLOT TYPE, ``$arguments'', must be ``lined'' or ``solid'' \
							in PLOT ``$parent_name.$real_plot_name''! Setting to DEFAULT, \
							``$Defaults(TYPE)''" -9
				} else {
					set [set plot_name](TYPE) $arguments
				}
			}
			default {
				Blurb "Warning: found unrecognized word, ``$key_word'', in PLOT \
						``$parent_name.$real_plot_name''!" -9
			}
		}
	}
	foreach item "COLOR TYPE" {
		if {![info exists [set plot_name]($item)]} {
			set [set plot_name]($item) $Defaults($item)
		}
	}
}

### END ParsePlot
#################


#####################################################
### Recursively create all widgets in a given WINDOW.

proc CreateWidgets { variable } {
	global sort_variable [set variable] Indent Debug Toggle_Pack Graph_Maxes Graph_Mins \
			Graph_Scale To_Unpack
	incr Indent
	set widget_list ""
	set sort_variable $variable
	foreach item [lsort [array names [set variable]]] {
		if {[set [set variable]($item)] != ""} {
			# if this item has a value
			if {[string tolower $item] == $item} {
				# if it is a GROUP or GRAPH or PLOT
				lappend widget_list $item
			} else {
				Debug 2 "$variable.$item = [set [set variable]($item)]"
			}
		}
	}
	foreach item [lsort -command SortPackArray $widget_list] {
		Debug 2 "$variable.\b\b->$item = [set [set variable]($item)]"
		# go through the widgets in order of the config file
		global [set variable].[set item]
		if {[lsearch -exact $To_Unpack $variable.$item] != -1} {
			# if this one is to be unpacked
			# set it's value to 0 so we know it is (will be) unpacked
			set [set variable]($item) 0
		}
		if {[set [set variable].[set item](TYPE)] == "GROUP"} {
			set frame_name $variable.$item
			set menu_name .pack_$variable.$item
			set frame_label [set [set frame_name](NAME)]
			MyCatch "frame .$frame_name" "Error: could not create GROUP frame, \
					``$frame_name''!"
			pack .$frame_name -fill both -expand yes -side [set [set frame_name](PLACE)]
			foreach extra [set [set frame_name](EXTRA)] {
				MyCatch ".$frame_name configure \{[lindex $extra 0]\} \{[lindex $extra 1]\}" \
						"Warning: could not configure EXTRA option ``[lindex $extra 0]'' as \
						``[lindex $extra 1]'' for GROUP ``$variable.$frame_label''."
			}
			# mouse B1 demands update on label
			bind .$frame_name <Button-2> "Unpack .$frame_name"
			.pack_$variable add cascade -label $frame_label -menu $menu_name
			MyCatch "menu $menu_name -tearoff 0" \
					"Error: could not create menu for ``$frame_name''!"
			$menu_name add checkbutton -label GROUP -variable Toggle_Pack(.$frame_name) \
					-command "TogglePack .$frame_name"
			set Toggle_Pack(.$frame_name) 1
			set label_name ".[set frame_name].label"
		} elseif {[set [set variable].[set item](TYPE)] == "LABEL"} {
			set label_name $variable.$item
			set menu_name .pack_$variable
			CreateLabel $label_name
			foreach extra [set [set label_name](EXTRA)] {
				MyCatch ".$label_name configure \{[lindex $extra 0]\} \{[lindex $extra 1]\}" \
						"Warning: could not configure EXTRA option ``[lindex $extra 0]'' as \
						``[lindex $extra 1]'' for LABEL ``$label_name''."
			}
			$menu_name add checkbutton -label [set [set label_name](NAME)] -variable \
					Toggle_Pack(.$label_name) -command "TogglePack .$label_name"
			set Toggle_Pack(.$label_name) 1			
		} elseif {[set [set variable].[set item](TYPE)] == "GRAPH"} {
			global Graph_Scale Graph_Maxes Graph_Mins
			set Graph_Maxes($variable.$item) 0.0
			set Graph_Mins($variable.$item) 0.0
			set Graph_Scale($variable.$item) "1.0 1.0"
			set graph_name $variable.$item
			set parent_menu .pack_$variable
			set menu_name .pack_$graph_name
			set graph_label [set [set graph_name](NAME)]
			set direction [set [set graph_name](DIRECTION)]
			set width [set [set graph_name](WIDTH)]
			set height [set [set graph_name](HEIGHT)]
			set history [set [set graph_name](HISTORY)]
			set extra [set [set graph_name](EXTRA)]
			set place [set [set graph_name](PLACE)]
			MyCatch "canvas .$graph_name -width $width -height $height -xscrollincrement 1 \
					-yscrollincrement 1 -highlightthickness 0" \
					"Error: could not create GRAPH, ``$graph_name''!"
			pack .$graph_name -side $place
			# mouse B1 demands update on label
			bind .$graph_name <Button-2> "Unpack .$graph_name"
			$parent_menu add cascade -label $graph_label -menu $menu_name
			MyCatch "menu $menu_name -tearoff 0" \
					"Error: could not create menu for ``$graph_name''!"
			$menu_name add checkbutton -label GRAPH -variable Toggle_Pack(.$graph_name) \
					-command "TogglePack .$graph_name"
			set Toggle_Pack(.$graph_name) 1
			bind .$graph_name <Button-1> "[list ScrollCanvas .$graph_name mark \
					$direction $width $height $history %x %y]"
			bind .$graph_name <B1-Motion> "[list ScrollCanvas .$graph_name dragto \
					$direction $width $height $history %x %y]"
			if {$direction == "left" || $direction == "right"} {
				bind .$graph_name <Double-1> "[list .$graph_name xview moveto 0]"
			} else {
				bind .$graph_name <Double-1> "[list .$graph_name yview moveto 0]"
			}
			bind .$graph_name <Button-2> "Unpack .$graph_name"
			set Graph_Maxes($graph_name) 0
			set Graph_Mins($graph_name) 0
			set Graph_Scale($graph_name) "1.0 1.0"
			foreach arg $extra {
				MyCatch ".$graph_name configure \{[lindex $arg 0]\} \{[lindex $arg 1]\}" \
						"Warning: could not configure EXTRA option ``[lindex $arg 0]'' \
						as ``[lindex $arg 1]'' for GRAPH ``$graph_name''."
			}
			# this is necessary to put 0,0 is the upper left of the canvas
			set bw [lindex [.$graph_name config -borderwidth] 4]
			.$graph_name xview scroll -$bw units
			.$graph_name yview scroll -$bw units
		} elseif {[set [set variable].[set item](TYPE)] == "solid" || \
				[set [set variable].[set item](TYPE)] == "lined"} {
			set plot_name $variable.$item
			set menu_name .pack_$variable.$item
			set plot_label [set [set plot_name](NAME)]
			.pack_$variable add checkbutton -label $plot_label -variable \
					[set variable]($item) -command "TogglePlot [list $variable $item]"
		} else {
			Blurb "This message should never be seen (1)!" 0
		}
		if {[Debug 64 ""]} {
			update idletasks
		}
		CreateWidgets $variable.$item
	}
	incr Indent -1
}

### END CreateWidgets
#####################


##################################
### Create a text label (message).

proc CreateLabel { label_name } {
	global Last_Output [set label_name]
 	set label_widget ".$label_name"
 	set label_side [set [set label_name](PLACE)]
 	set label_args [set [set label_name](EXTRA)]
	MyCatch "message $label_widget -justify center -aspect 5000" \
			"Error: could not create LABEL ``$label_name''!"
	# mouse b2 unpack this label
	bind $label_widget <Button-2> "Unpack .$label_name"
	if {[info exists [set label_name](EXPRESSION)]} {
		# if label is a dynamic EXPRESSION
		if {[string match {*=*} [set [set label_name](EXPRESSION)]]} {
			# if there's an = sign in it
			# add it to the Last_Val array
			set Last_Output($label_name) {}
		}
		Debug 1 "Packed EXPRESSION    >---< $label_name\n"
		# mouse B1 demands update on label
		bind $label_widget <Button-1> "DemandUpdate $label_name"
 	} else {
		MyCatch "$label_widget configure -text \{[set [set label_name](STATIC)]\}" \
				"Error: could not label LABEL ``$label_name'' as \
				``[set [set label_name](STATIC)]''!"
		Debug 1 "Packed STATIC label  >---< $label_name\n"
	}
	
	foreach arg $label_args {
		MyCatch "$label_widget configure \{[lindex $arg 0]\} \{[lindex $arg 1]\}" \
				"Warning: could not configure EXTRA option ``[lindex $arg 0]'' as \
				``[lindex $arg 1]'' for LABEL ``$label_name''."
	}
	MyCatch "pack $label_widget -fill both -expand yes -side $label_side" \
			"Error: could not pack LABEL ``$label_name'' at the ``$label_side''!"
}

### END CreateLabel
###################



########################################
### Put some bindings on a given window.

proc DoBinds { window } {
	global Toggle_Iconify
	bind .$window <Button-3> ".pack_$window post %X %Y"
	bind Menu <Button-1> "tkMenuEscape %W"
	bind Menu <Button-2> "tkMenuEscape %W"
	bind .$window <Q> "exit"
	bind .$window <Z> "wm iconify .$window; set Toggle_Iconify(.$window) 0"
	bind .$window <plus> {
		set Update_Delta [expr $Update_Delta + 1]
		Blurb "Update delta set to $Update_Delta." 0
		after [expr int($Update_Delta * 1000)] "UpdateSeconds $Update_Delta"
	}
	bind .$window <Control-plus> {
		set Update_Delta [expr $Update_Delta + 60]
		Blurb "Update delta set to $Update_Delta." 0
		after [expr int($Update_Delta * 1000)] "UpdateSeconds $Update_Delta"
	}
	bind .$window <minus> {
		set Update_Delta [expr $Update_Delta - 1]
		if {!($Update_Delta > 0)} {
			set Update_Delta 1
		}
		Blurb "Update delta set to $Update_Delta." 0
		after [expr int($Update_Delta * 1000)] "UpdateSeconds $Update_Delta"
	}
	bind .$window <Control-minus> {
		set Update_Delta [expr $Update_Delta - 60]
		if {!($Update_Delta > 0)} {
			set Update_Delta 1
		}
		Blurb "Update delta set to $Update_Delta." 0
		after [expr int($Update_Delta * 1000)] "UpdateSeconds $Update_Delta"
	}
	bind .$window <Control-KeyPress-1> "SetDebug %K"
	bind .$window <Control-KeyPress-2> "SetDebug %K"
	bind .$window <Control-KeyPress-3> "SetDebug %K"
	bind .$window <Control-KeyPress-4> "SetDebug %K"
	bind .$window <Control-KeyPress-5> "SetDebug %K"
	bind .$window <Control-KeyPress-6> "SetDebug %K"
	bind .$window <Control-KeyPress-7> "SetDebug %K"
	bind .$window <Control-KeyPress-8> "SetDebug %K"
}

### END DoBinds
###############



#########################
#########################
### User Event Driven ###
#########################
#########################

#################################################################
### Cause widget (and all ancestors) to do an update immediately.

proc DemandUpdate { frame_name } {
	global Times
	Debug 128 "\nDemanding Update >---< $frame_name\n"
    while { $frame_name != {} } {
		# strip from last . on
		set old_frame_name $frame_name
		set Times($frame_name) 0
		set frame_name [string range $frame_name 0 [expr \
				[string last . $frame_name] - 1]]
	}
	UpdateWidgets $old_frame_name ""
}

### END DemandUpdate
####################


################################################
### Toggle unpacked or packed state of a widget.

proc TogglePack { widget_name } {
	global Toggle_Pack
	if {$Toggle_Pack($widget_name)} {
		# if has been Unpacked
		Repack $widget_name
	} else {
		Unpack $widget_name
	}
}

### END TogglePack
##################


#####################################
### Toggle a WINDOW iconified or not.

proc ToggleIconify { window_name } {
	global Toggle_Iconify
	if {!$Toggle_Iconify($window_name)} {
		Debug 8 "Iconified WINDOW $window_name\n"
		wm iconify $window_name
	} else {
		Debug 8 "Deiconified WINDOW $window_name\n"
		wm deiconify $window_name
	}
}

### END ToggleIconify
#####################


#####################################################
### Toggle showing or not showing, a line in a graph.

proc TogglePlot { frame_name id } {
	global [set frame_name] [set frame_name].[set id]
	if {![set [set frame_name]($id)]} {
		# if was unmarked
		Debug 8 "Unpacked PLOT $id\n"
		# change the color to clear
		.$frame_name itemconfigure $id -fill ""
	} else {
		Debug 8 "Repacked PLOT $id\n"
		.$frame_name itemconfigure $id -fill [set [set frame_name].[set id](COLOR)]
	}
}

### END TogglePlot
##################


###############################################################################
### Unpack a widget and remember who it was packed after before being unpacked.

proc Unpack { widget_name } {
	global Unpacked Toggle_Pack OM_legal1 OM_legal2
	# stop the bindings while I do this
	bind $widget_name <Button-2> {}
	set Toggle_Pack($widget_name) 0
	set pack_info [pack info $widget_name]
	set ancestor [string range $widget_name 0 [expr [string first . \
			[string trimleft $widget_name .]]]]
	# find its parent
	set parent [string range $widget_name 0 [expr [string last . $widget_name] - 1]]
	# find its position in the pack list
	set list_index [lsearch -exact [pack slaves $parent] $widget_name]
	if {$list_index == 0} {
		# if it was the first widget
		# it was packed after nothing
		set packed_after first$parent
	} else {
		# find what it was packed after
		set packed_after [lindex [pack slaves $parent] [expr $list_index - 1]]
	}
	foreach var [array names Unpacked] {
		if {[lindex $Unpacked($var) 0] == $packed_after} {
			# was it unpacked after $packed_after
			# then we should be unpacked after it
			set packed_after $var
			break
		}
	}
	# remember the side and who after
	set Unpacked($widget_name) "$packed_after $pack_info"
	pack forget $widget_name

	set parent_array [string trimleft $parent .]
	global [set parent_array]
	if {[string first "label" $widget_name] == -1 && \
		[string first "graph" $widget_name] == -1} {
		set [set parent_array]([string trimleft [file extension $widget_name] .]) 0
	}
	Debug 8 "Unpacked $widget_name\n"
	bind $widget_name <Button-2> "Unpack $widget_name"
	if {[pack slaves $ancestor] == ""} {
		message $ancestor.intro -justify center -aspect 5000 -font \
				"-Adobe-Helvetica-Bold-R-Normal--*-120-*" -text "$OM_legal1"
		$ancestor.intro configure -bg \#116 -fore \#c34
		pack $ancestor.intro -fill both -expand yes
	}
}

### END Unpack
##############


###############################################################
### Repack and unpacked widget back to where it was originally.

proc Repack { widget_name } {
	global Unpacked
	set pack_after [lindex $Unpacked($widget_name) 0]
	set pack_info [lrange $Unpacked($widget_name) 1 end]
	while {[info exists Unpacked($pack_after)]} {
		# check if after has been unpacked
		# then point to after's after
		set pack_after [lindex $Unpacked($pack_after) 0]
	}
	set ancestor [string range $widget_name 0 [expr [string first . \
			[string trimleft $widget_name .]]]]
	set parent [string range $widget_name 0 [expr [string last . $widget_name] - 1]]
	if {[pack slaves $ancestor] == "$ancestor.intro"} {
		pack forget $ancestor.intro
		destroy $ancestor.intro
	}
	if {$pack_after == "first$parent"} {
		# if it needs to be packed first
		# find first in pack list
		set pack_before [lindex [pack slaves $parent] 0]
		if {$pack_before == {}} {
			eval "pack $widget_name -expand yes $pack_info"
		} else {
			eval "pack $widget_name $pack_info -before $pack_before"
		}
	} else {
		eval "pack $widget_name $pack_info -after $pack_after"
	}

	set parent_array [string trimleft $parent .]
	global [set parent_array]
	if {[string first "label" $widget_name] == -1 && \
		[string first "graph" $widget_name] == -1} {
		set [set parent_array]([string trimleft [file extension $widget_name] .]) 1
	}

	unset Unpacked($widget_name)
	Debug 8 "Repacked $widget_name\n"
}

### END Repack
##############



#########################
#########################
### Time Event Driven ###
#########################
#########################

####################################################################
### Update everything and then wait the update time and do it again.

proc UpdateSeconds { old_update_delta } {
	global Elapsed_Seconds Update_Delta  Windows
	if {$old_update_delta == $Update_Delta} {
		set Elapsed_Seconds [expr $Elapsed_Seconds + $Update_Delta]
		Debug 128 "\nIncreased Time   >---< $Elapsed_Seconds Seconds\n"
		foreach window [array names Windows] {
			UpdateWidgets $window ""
		}
		after [expr int($Update_Delta * 1000)] "UpdateSeconds $Update_Delta"
	}
}

### END undateSeconds
#####################


#####################################################
### Recursively update all widgets in a given WINDOW.

proc UpdateWidgets { variable output } {
	global sort_variable [set variable] Indent Debug Toggle_Pack Times Elapsed_Seconds
	set sort_variable $variable
	set frame_list ""
	foreach item [array names $variable] {
		if {[set [set variable]($item)] != 0} {
			# if this item has a value (not unpacked)
			if {[string tolower $item] == $item} {
				# if it is a GROUP or GRAPH or LABEL or PLOT
				lappend frame_list $item
			}
		} else {
			Debug 128 "Skipping item    >---< $variable.$item\n"
		}
	}
	foreach item $frame_list {
		# go through the frames
		global [set variable].[set item]
		# pass on $output if not regenerated
		set command_out $output
		if {$Times($variable.$item) <= $Elapsed_Seconds} {
			set frame_name $variable.$item
			set label_name ".[set frame_name].label"
			if {[set [set variable].[set item](TYPE)] == "GROUP"} {
				Debug 128 "Updating GROUP   -> $frame_name\n"
				if {[info exists [set frame_name](COMMAND)] && \
						[set [set frame_name](COMMAND)] != "" && \
						![Debug 4 ""]} {
					# if there is a COMMAND for this group
					foreach command \{[set [set frame_name](COMMAND)]\} {
						append command_out [MyCatch "exec sh -c [list $command]" \
								"Warning: could not execute COMMAND ``$command'' in \
								``$frame_name''."]
					}
				}
				UpdateWidgets $frame_name $command_out
				Debug 128 "Finished GROUP   <- $frame_name\n"
				set Times($frame_name) [expr $Elapsed_Seconds + [set [set frame_name](UPDATE)]]
			} elseif {[set [set variable].[set item](TYPE)] == "LABEL"} {
				set label_name $variable.$item
				if {[info exists [set label_name](EXPRESSION)]} {
					MyCatch ".$label_name configure -text \{[EvalSpecial \
							[set [set label_name](EXPRESSION)] $command_out \
							$label_name]\}" \
							"Warning: could not update EXPRESSION for ``$label_name''."
					Debug 128 "Updated LABEL    >---< $label_name\n"
				}
			} elseif {[set [set variable].[set item](TYPE)] == "GRAPH"} {
				Debug 128 "Updating GRAPH   -> $frame_name\n"
				if {![Debug 4 ""]} {
					# if were not in static label debug mode
					global Graph_Maxes Graph_Mins Graph_Scale Graph_Sort Graph_History
					if {[info exists [set frame_name](COMMAND)] && \
							[set [set frame_name](COMMAND)] != "" && \
							![Debug 4 ""]} {
						# if there is a COMMAND for this group
						foreach command \{[set [set frame_name](COMMAND)]\} {
							append command_out [MyCatch "exec sh -c [list $command]" \
									"Warning: could not execute COMMAND ``$command'' in \
									``$frame_name''."]
						}
					}
					unset Graph_Sort
					set Graph_Sort(dummy) ""
					# unscale from previous time
					eval ".$frame_name scale all 0 0 $Graph_Scale($frame_name)"
					set old_max $Graph_Maxes($frame_name)
					set old_min $Graph_Mins($frame_name)
					set history [set [set frame_name](HISTORY)]
					set direction [set [set frame_name](DIRECTION)]
					set width [set [set frame_name](WIDTH)]
					set height [set [set frame_name](HEIGHT)]
					UpdatePlot $frame_name $command_out
					# if we're actually getting numbers
					if {[info exists Graph_Sort(solid)]} {
						# if there were solid lines
						foreach item [lsort -command AbsSort $Graph_Sort(solid)] {
							# lower it to bottom
							.$frame_name lower [lindex $item 1]
						}
					}
					if {[info exists Graph_Sort(lined)]} {
						# if there were lined linse
						foreach item [lsort -command AbsSort $Graph_Sort(lined)] {
							# raise it to top
							.$frame_name raise [lindex $item 1]
						}
					}
					if {[llength $Graph_History($frame_name)] > $history} {
						# if we're over history
						set leaving_max [lindex [lindex $Graph_History($frame_name) 0] 0]
						set leaving_min [lindex [lindex $Graph_History($frame_name) 0] 1]
						set leaving_group [lindex [lindex $Graph_History($frame_name) 0] 2]
						# forget the one who is leaving
						set Graph_History($frame_name) [lrange $Graph_History($frame_name) 1 end]
						# delete everyone who's leaving
						eval .$frame_name delete $leaving_group
						set local_max 0
						set local_min 0
						if {$leaving_max >= $Graph_Maxes($frame_name) || \
								$leaving_min <= $Graph_Mins($frame_name)} {
							# if leaving was a max or min
							foreach old_one $Graph_History($frame_name) {
								# go through everyone still left
								if {[lindex $old_one 0] > $local_max} {
									set local_max [lindex $old_one 0]
								}
								if {[lindex $old_one 1] < $local_min} {
									set local_min [lindex $old_one 1]
								}
							}
							set Graph_Maxes($frame_name) $local_max
							set Graph_Mins($frame_name) $local_min
						}
					}
					# find new scale values depending on max or mins from above
					switch $direction {
						up -
						down {
							set yscale 1
							if {[set g_width [expr double($Graph_Maxes($frame_name) - \
									$Graph_Mins($frame_name))]] == 0} {
								# if no width then no change
								set xscale 1
							} else {
								set xscale [expr double(($width - 2) / $g_width)]
								set Graph_Scale($frame_name) "[expr 1.0 / $xscale] 1.0"
							}
							.$frame_name xview moveto 0
							.$frame_name xview scroll \
									[expr 1 - [set [set frame_name](WIDTH)] - \
									int($Graph_Mins($frame_name) * $xscale)] units
						}
						left -
						right {
							set xscale 1
							if {[set g_height [expr double($Graph_Maxes($frame_name) - \
									$Graph_Mins($frame_name))]] == 0} {
								# if no height then no change
								set yscale 1
							} else {
								set yscale [expr ($height - 2) / $g_height]
								set Graph_Scale($frame_name) "1.0 [expr 1.0 / $yscale]"
							}
							.$frame_name yview moveto 0
							.$frame_name yview scroll \
									[expr 1 - [set [set frame_name](HEIGHT)] - \
									int($Graph_Mins($frame_name) * $yscale)] units
						}
					}
					.$frame_name scale all 0 0 $xscale $yscale
				}
				Debug 128 "Finished GRAPH   <- $frame_name\n"
				set Times($frame_name) [expr $Elapsed_Seconds + [set [set frame_name](UPDATE)]]
			} else {
				Blurb "This message should never be seen (3)!" 0
			}
		}
	}
}

### END UpdateWidgets
#####################


#####################################
### Update all PLOT lines in a GRAPH.

proc UpdatePlot { variable command_out } {
	global [set variable] Graph_Last Graph_Scale Graph_Maxes Graph_Mins Graph_Sort Graph_History
	# just in case there aren't any PLOTs in this GRAPH
	set plot_list ""
	# init these for first use
	set local_max 0
	set local_min 0
	set graph_group ""
	foreach item [array names $variable] {
		if {[string tolower $item] == $item} {
			# if it is a PLOT name
			lappend plot_list $item
		}
	}
	foreach item $plot_list {
		# go through the PLOTs
		global [set variable].[set item]
		Debug 128 "Updated PLOT     >---< $variable.[set [set variable].[set item](NAME)]\n"
		set color [set [set variable].[set item](COLOR)]
		set type [set [set variable].[set item](TYPE)]
		set height [set [set variable](HEIGHT)]
		set width [set [set variable](WIDTH)]
		set direction [set [set variable](DIRECTION)]
		if {[info exists [set variable].[set item](EXPRESSION)]} {
			set value [EvalSpecial [set [set variable].[set item](EXPRESSION)] \
					$command_out $variable.$item]
		}	
		if {[set [set variable]($item)] != 1} {
			set color ""
		}
		if {![catch "expr $value + 1"]} {
			# if it's a number
			if {$value > $local_max} {
				set local_max $value
			}
			if {$value < $local_min} {
				set local_min $value
			}
			if {$value > $Graph_Maxes($variable)} {
				set Graph_Maxes($variable) $value
				ReHash $variable $height $width $direction [set [set variable](HISTORY)] \
						[set [set variable](HASHES)] [set [set variable](ZEROCOL)] \
						[set [set variable](HASHCOL)]
			}
			if {$value < $Graph_Mins($variable)} {
				set Graph_Mins($variable) $value
				ReHash $variable $height $width $direction [set [set variable](HISTORY)] \
						[set [set variable](HASHES)] [set [set variable](ZEROCOL)] \
						[set [set variable](HASHCOL)]
			}
			if {![info exists Graph_Last] || \
					![info exists Graph_Last($variable.$item)]} {
				# find point plotted last time
				set Graph_Last($variable.$item) $value
			}
			switch $direction {
				up -
				down {
					if {$direction == "up"} {
						set y1 [expr $height - 1]
						set y2_lined [expr $height - 2]
						set ymove -1
					} else {
						set y1 0
						set y2_lined 1
						set ymove 1
					}
					set x1 [expr -$value]
					if {$type == "solid"} {
						set y2 $y1
						set x2 0
					} else {
						set y2 $y2_lined
						set x2 [expr -$Graph_Last($variable.$item)]
					}
					# remember this value for next time
					set Graph_Last($variable.$item) $value
					set xmove 0
				}
				left -
				right {
					if {$direction == "left"} {
						set x1 [expr $width - 1]
						set x2_lined [expr $width - 2]
						set xmove -1
					} else {
						set x1 0
						set x2_lined 1
						set xmove 1
					}
					set y1 [expr -$value]
					if {$type == "solid"} {
						set x2 $x1
						set y2 0
					} else {
						set x2 $x2_lined
						set y2 [expr -$Graph_Last($variable.$item)]
					}
					# remember this value for next time
					set Graph_Last($variable.$item) $value
					set ymove 0
				}
			}
			.$variable move $item $xmove $ymove
			set temp [.$variable create line $x1 $y1 $x2 $y2 \
					-fill $color -tags $item -capstyle round]
			lappend Graph_Sort($type) "$value $temp"
			lappend graph_group $temp
		}
	}
	# remember extremes
	lappend Graph_History($variable) "$local_max $local_min \{$graph_group\}"
}


### END UpdatePlot
##################


###############################################################
### Evaluate the special symbols in a dynamic label or a graph.

proc EvalSpecial { symbols output frame_name } {
	# The following special symbols exist:
	# $n - n is a integer - gives the nth field
	# %n - n is a integer - gives the nth field starting the last field
	# $n{$m} | $n{%m} | %n{$m} | %n{%m} - n and m are integers - get the mth field out of the
	#			nth field.  Assumes the nth field was enclosed in {}.
	# =$n | =%n - n is a integer - gives the nth field from the previous run of this command
	# :$n$m | :$n%m | :%n$m | :%n%m - n and m are integers - gives the range of fields from n to m
	# |c - c is a character - will replace every instance of character c with a space
	# 's' - s is a string - will return the result of the regexp s
	global Last_Output Times Last_Time Elapsed_Seconds
	if {![Debug 4 ""]} {
		foreach piece $symbols {
			# for each part of the math equation
			# check the first character
			switch -- [string index $piece 0] {
				"$" {
					if {[string index $piece 1] == "s"} {
						# if it is "$s"
						# find the last seconds delta
						set piece [expr $Elapsed_Seconds - $Last_Time($frame_name)]
						set Last_Time($frame_name) $Elapsed_Seconds
					} elseif {[string index $piece [expr [string length $piece] - 1]] == "\}"} {
						# else if has a {...}
						set part [string trimleft [string range $piece 0 [expr \
								[string first "\{" $piece] - 1]] "\$"]
						if {![catch "expr $part + 1"]} {
							# if it's a number
							set new_symbols [string trim [string range $piece \
									[string first "\{" $piece] end] "\{\}"]
							set piece [EvalSpecial $new_symbols [lindex $output $part] \
									$frame_name]
						}
					} else {
						# else it's a normal $n
						set inx [string trimleft $piece \$]
						if {![catch "expr $inx + 1"]} {
							# if it's a number
							# find what the $number points to
							set piece [lindex $output $inx]
						}
					}
				}
				"%" {
					if {[string index $piece 1] == "s"} {
						# if it is "%s"
						# find the last seconds delta
						set piece [expr $Elapsed_Seconds - $Last_Time($frame_name)]
						set Last_Time($frame_name) $Elapsed_Seconds
					} elseif {[string index $piece [expr [string length $piece] - 1]] == "\}"} {
						# else if has a {...}
						set part [string trimleft [string range $piece 0 [expr \
								[string first "\{" $piece] - 1]] "%"]
						if {![catch "expr $part + 1"]} {
							# if it's a number
							set new_symbols [string trim [string range $piece \
									[string first "\{" $piece] end] "\{\}"]
							set piece [EvalSpecial $new_symbols [lindex $output \
								[expr [llength $output] - ($part + 1)]] $frame_name]
						}
					} else {
						# else it's a normal %n
						set inx [string trimleft $piece %]
						if {![catch "expr $inx + 1"]} {
							# if it's a number
							# find field from right
							set piece [lindex $output [expr [llength $output] - ($inx + 1)]]
						}
					}
				}
				":" {
					set trim [string trimleft $piece ":"]
					set split [split [string trimleft $trim "\$%"] "\$%"]
					set num1 [lindex $split 0]
					set num2 [lindex $split 1]
					if {![catch "expr $num1 + $num2"]} {
						# if both are numbers
						if {[string index $trim 0] == "\$"} {
							set b $num1
						} elseif {[string index $trim 0] == "%"} {
							set b [expr [llength $output] - $num1]
						}
						if {[string first "\$" [string trimleft $trim "\$%"]] != -1} {
							set e $num2
						} elseif {[string first "%" [string trimleft $trim "\$%"]] != -1} {
							set e [expr [llength $output] - ($num2 + 1)]
						}
						if {[info exists b] && [info exists e]} {
							set piece [lrange $output $b $e]
						}
					}
				}
				"|" {
					# can't just use Tcl's split because that'll create null fields if two \
							split chars back to back.
					set each_char [split $output {}]
					set output ""
					foreach char $each_char {
						if {$char != [string index $piece 1]} {
							append output $char
						} else {
							append output " "
						}
					}
					set piece ""
				}
				"=" {
					set trim [string trimleft $piece "="]
					# recurse to next piece
					if {[info exists Last_Output($frame_name)]} {
						set piece [EvalSpecial $trim $Last_Output($frame_name) $frame_name]
					}
					# remember old output
					set Last_Output($frame_name) $output
				}
				"'" {
					set exp [string range $piece 1 [expr [string length $piece] - 2]]
					set findings ""
					set result [regexp $exp $output findings]
					set output $findings
					set piece ""
				}
			}
			append math $piece
		}
		if {[catch "expr $math" result]} {
			# if parsing the equation fails
			set return_val $math
		} else {
			set return_val $result
		}
	} else {
		# else is debug level 4
		set return_val $symbols
	}
	return $return_val
}

### END EvalSpecial
###################



################
################
### Graphics ###
################
################

################################################
### Scroll the graph with the left mouse button.

proc ScrollCanvas { frame_name type direction width height history x y } {
	set bw [lindex [$frame_name config -borderwidth] 4]
	switch $direction {
		up {
			# mark or dragto the new y position
			$frame_name scan $type 0 $y
			if {[$frame_name canvasy 0] > [expr 0 - $bw]} {
				# if it dragged too far up
				$frame_name yview moveto 0
			}
			if {[$frame_name canvasy 0] < [expr $height - $history - $bw]} {
				# if it dragged too far down
				$frame_name yview moveto 0		
				$frame_name yview scroll [expr $height - $history] units
			}
		}
		down {
			# mark or dragto the new y position
			$frame_name scan $type 0 $y
			if {[$frame_name canvasy 0] < [expr 0 - $bw]} {
				# if we dragged too far down
				$frame_name yview moveto 0
				$frame_name scan mark 0 $y
			}
			if {[$frame_name canvasy 0] > [expr $history - $height - $bw]} {
				# if we dragged too far up
				$frame_name yview moveto 0
				$frame_name yview scroll [expr $history - $height] units
				$frame_name scan mark 0 $y
			}
		}
		left {
			# mark or dragto the new x position
			$frame_name scan $type $x 0
			if {[$frame_name canvasx 0] > [expr 0 - $bw]} {
				# if we dragged too far left
				$frame_name xview moveto 0
				$frame_name scan mark $x 0
			}
			if {[$frame_name canvasx 0] < [expr $width - $history - $bw]} {
				# if we dragged too far right
				$frame_name xview moveto 0
				$frame_name xview scroll [expr $width - $history] units
				$frame_name scan mark $x 0
			}
		}
		right {
			# mark or dragto the new x position
			$frame_name scan $type $x 0
			if {[$frame_name canvasx 0] < [expr 0 - $bw]} {
				# if we dragged too far right
				$frame_name xview moveto 0
				$frame_name scan mark $x 0
			}
			if {[$frame_name canvasx 0] > [expr $history - $width - $bw]} {
				# if we dragged too far left
				$frame_name xview moveto 0
				$frame_name xview scroll [expr $history - $width] units
				$frame_name scan mark $x 0
			}
		}
	}
}

### END ScrollCanvas
####################


################################################
### Redraw all hash marks to new scale of graph.

proc ReHash { frame_name height width direction history hash_value zero_color hash_color } {
	global Graph_Maxes Graph_Mins
	.$frame_name addtag below_hashes below hashes
	.$frame_name delete hashes
	if {$hash_value} {
		switch $direction {
			up -
			down {
				if {$direction == "up"} {
					set start_y [expr $height - $history]
					set end_y $height
				} else {
					set start_y 0
					set end_y $history
				}
				for {set hash $Graph_Mins($frame_name)} \
						{$hash <= [expr $Graph_Maxes($frame_name) + $hash_value]} \
						{set hash [expr $hash + $hash_value]} {
					# for min val to max val
					# find the val rounded to hash_val
					set mark [expr -($hash - fmod($hash, $hash_value))]
					if {$mark != 0} {
						.$frame_name create line $mark $start_y $mark $end_y \
								-fill $hash_color -tags hashes
					}
				}
				.$frame_name create line 0 $start_y 0 $end_y -fill $zero_color -tags hashes
			}
			left -
			right {
				if {$direction == "left"} {
					set start_x [expr $width - $history]
					set end_x $width
				} else {
					set start_x 0
					set end_x $history
				}
				for {set hash $Graph_Mins($frame_name)} \
						{$hash <= [expr $Graph_Maxes($frame_name) + $hash_value]} \
						{set hash [expr $hash + $hash_value]} {
					# for min val to max val
					# find the val rounded to hash_val
					set mark [expr -($hash - fmod($hash, $hash_value))]
					if {$mark != 0} {
						.$frame_name create line $start_x $mark $end_x $mark \
								-fill $hash_color -tags hashes
					}
				}
				.$frame_name create line $start_x 0 $end_x 0 -fill $zero_color -tags hashes
			}
		}
		if {[.$frame_name gettags below_hashes] != ""} {
			# if there was anything below hashes
			# put these hashes above them
			.$frame_name raise hashes below_hashes
			.$frame_name dtag below_hashes
		} else {
			# put these on the bottom
			.$frame_name raise hashes
		}
	}
}

### END ReHash
##############



#####################
#####################
### Here we go... ###
#####################
#####################

InitAll

### END OmniMoni
################



###################
###################
### Emacs Stuff ###
###################
###################
### Emacs variables...
###
### Local Variables:
### mode:tcl
### tab-width:4
### minormode:line-number
### End:
