#!/usr/share/sysman/bin/sysmansh

#############################################################################
# Name:
#	sysman_menu_main.tcl
#
# Abstract:
#	Main driver for the SysMan Menu. 
#
# Notes:
#	SM is the prefix we use to avoid variable/proc name collisions
#	(SM == SysMan Menu).
#
#
#############################################################################
#


# Init
   
    # ensure that even on pass through invokations the "." window
    # doesn't show up.

    if ![lempty [info commands wm]] {
	catch {wm iconify .}
    }


# Globals
    global SysmanOnCluster	;# Set by sysmansh

    # Status codes
    set SM_status(SUCCESS) 1
    set SM_status(FAILURE) 0
    set SM_status(WARNING) -1

    set SM_menu_parent ""	     ;# Holds string with current node of tree
    set SM_menu_level  0	     ;# Root of the tree is indent level 0
    set SM_menu_main   sysman_menu   ;# Name of our message catalog
    set _UIT_g_main    sysman_menu   ;# SUIT main object name
    set SM_focushost   ""	     ;# Default to cluster focus
    set SM_takesfocus  false	     ;# True is -takesfocus specified   

    set SM_display_accelerators false ;# Whether the menu name show accels.
    set SM_suit_initialized	false ;# SUIT not init'd yet
    set SM_menu_initialized	false ;# Menu data structs not built yet
    set SM_leaf_needs_focus_dialog    false  ;# Assume not a direct 
					       ;# invocation of a focus leaf


    #
    # Global list of all outstanding child pids
    #

    set SM_must_wait "true"
    set SM_last_exited_pid ""
    set SM_last_exited_status ""

    # The menu allows tree defintions to be present to that we can implement
    # task oriented tree vs OO oriented tree. This global holds the default
    # tree name. 
    # [pjw] this will get reset by command line interface (when implemented)
    # so that the user can choose which menu defintion/layout to use
    set SM_menu_def_root SM_SysmanMenu

    # SUIT currently doesn't have a feature to unmanage
    # a button. We want the feature of supressing the Find button 
    # when we are pruning the menu - aka captive mode. You
    # See a menu that is rooted at the branch specified on the command
    # line. Assume are not pruning until we parse the command line below
    set SM_captive_flag "false" 
     
#
# Procedure:
#	SM_GetUI - determine the SUIT ui name for a given value of 
#	SYSMANUI. We need this to properly invoke the Suitlet object
#	before SUIT has been fully initialized. Note that this
#	is NOT the same as the value of SysmanUi!
#
# Inputs:
#	sysmanui - the current value of sysmanui as set by sysmansh
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#	This code should be common/shared!
#
proc SM_GetUI { sysmanui } {

	set sysmanui [string toupper $sysmanui]
	switch -exact -- $sysmanui {
	   GUI { 
		set ui GUI
	   }
	   JAVA {
		set ui JAVA
	   }
	   CUI -
	   CURSES - 
	   default {
		set ui CURSES
	   }

	} ;# end switch


} ;# end SM_GetUI
#
# Procedure:
#	Child exit callback Handler -
#	Called from SIGCHLD trap. Catches child process exits and if they have
#       terminated with a non-zero status code, displays child error message.
#
#
# Inputs:
#       SIGCHLD ;-)
#
# Outputs:
# 	An info message box
# Returns:
#	none
# Notes:
#	Hope you have a strong constitution for this one. This is a tcl proc
#	that defines a otcl proc on the instace of our SUIT main object. 
#	However, when we are launching a suitlet directly from the command line
#	(sysman -accel foo or -menu bar) we are short circuiting SUIT
#	initialization. IOW, we don't got the stinking object.  Fortunately,
#	the contents of the otcl proc have no dependencies on the actual
#	object itself (no instvars are used, no $self references). 
#	We simply will create a dummy object with this name immediately before
#	the proc defintion. When the real sysman_menu object is created by
#	SUIT it will nuke this one (a Otcl'ism that we are relying on)
#	Initially this proc was in the cb.tcl file but it had to be moved
#	here cause that file contains all instprocs and again and we
#	can't source it it cause all those SUIT otcl objects don't exist. 
#	Also note that when the dummy object is destroyed, this proc
#	is destroyed with it. We recreate it with a call to this vanilla
#	tcl proc from the .cb file. 
#

proc childExitCB {self} {
   global SM_last_exited_pid SM_last_exited_status SM_must_wait
   set stat [$self status]


   # pjw: crufty code. What's the difference between stat having
   # three elements vs anything else? I don't know and can't believe
   # this is uncommented. It's related to the exit status
   # from bgexec in the suitlet object
   if {[llength $stat] != 3} {
      set exitpid [$self pid]
      set exitval 1
      menu_debug puts \
          "childExitCB: return list != 3. exitpid: $exitpid exitval: $exitval"

   } else {
      lassign $stat exitpid exitstr exitval
   }
   
   if {$exitval != 0} {
      # get error txt from suitlet instance.
      set exitstr [$self error]   

      # SM_must_wait is set when the initial menu invocation launches
      # a leaf. We don't want to just exit without reporting back
      # the status for a potentially failed leaf. Instead we make
      # sure to display and InfoMsg with the exit status
      if { $SM_must_wait == "true" } {
	 set waitflag 1
      } else {
	 set waitflag 0
      }
      
      # Display error with the task's name, the return code, and
      # the first line of the returned text
      SM_DisplayMsg error error_child_error_exit true $waitflag\
	  [$self set _task] \
	  $exitval $exitstr
      
   }

   menu_debug puts "In childExitCB: exitpid: $exitpid, exitval: $exitval"
   set SM_last_exited_pid	$exitpid
   set SM_last_exited_status	$exitval

} ;# end childExitCB 

#
# Procedure:
#	SM_DisplayMsg - display an error message to stderr or 
#	to a SUIT InfoMsg dialog
#
# Inputs:
#	msg_type - type of error error, warning, or info
#	name	 - the catalog name of the message
#	db_flag  - true if message is to be displayed to an InfoMsg db
#	wait     - if 1, SUIT blocks till the user presses OK
#	args	 - variable number of paramters to substitute into the 
#		   msg string
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#
proc SM_DisplayMsg { {msg_type {"error"} } name {db_flag {"true"}} \
			{wait 0} args } {


    global SM_menu_main
    global SM_info_msg_titles
    global SM_suit_initialized


    # First time through, fetch the dialog box titles we'll need
    if { ![info exists SM_info_msg_titles] } {
	set SM_info_msg_titles(info)    [SM_cat catgets msg_info_title_lbl]
	set SM_info_msg_titles(error)   [SM_cat catgets msg_error_title_lbl]
	set SM_info_msg_titles(warning) [SM_cat catgets msg_warning_title_lbl]
    }

    set msg [SM_cat catgets ${name}_msg]	;# Get the message

    if { [cequal $args ""] != 1} {		;# If there are parameters
        set msg [eval {format "$msg"}  $args]	;# Then substitute them
    }

    set use_infomsg false			;# Assume msg to stdout
    if { $db_flag == "true" } {			;# Unless caller want dialog
	set use_infomsg true
    }

    # We have certain code paths where SUIT is not yet initialized. 
    # Rather than go back and analyze them all to see if InfoMsg is
    # safe to use, we finesse it here
    if { $SM_suit_initialized == "false" } {
	set use_infomsg false
    }


    if { $use_infomsg == "true" } {

	set title $SM_info_msg_titles($msg_type)
	InfoMsg  $msg_type $msg $title $wait

    } else {
	puts stderr $msg

    }

} ;# end proc SM_DisplayMsg

#
# Procedure:
#	SM_DisplayInfoMsg - display an error in a SUIT info msg dialog
#
# Inputs:
#	msg_type - type of error error, warning, or info
#	name	 - the catalog name of the message
#	args	 - variable number of paramters to substitute into the 
#		   msg string
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#	The caller should *NOT* append _msg to the message name. It's appended 
#	in the general purpose routine below
#
proc SM_DisplayInfoMsg { msg_type name args } {

    eval {SM_DisplayMsg $msg_type $name true 0} $args


} ;# end proc SM_DisplayInfoMsg

#
# Procedure:
#	SM_DisplayTextMsg - display a message to stderr or stdout
#
# Inputs:
#	msg_type - type of error error, warning, or info
#	name	 - the catalog name of the message
#	args	 - variable number of paramters to substitute into the 
#		   msg string
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#	The caller should *NOT* append _msg to the message name. It's appended 
#	in the general purpose routine below
#
proc SM_DisplayTextMsg { msg_type  name args } {

    eval {SM_DisplayMsg $msg_type $name false 0} $args


} ;# end proc SM_DisplayTextMsg

#
# Procedure:
#	SM_Usage - print sysman command line usage
#
# Inputs:
#	None
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#
proc SM_Usage { } {

    puts "[SM_cat catgets info_usage_msg]"	;# Standard usage msg

} ;# end SM_Usage

#
# Procedure:
#	SM_MenuUsage - print SysMan Menu command line usage
#
# Inputs:
#	None
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#
proc SM_MenuUsage { } {

    puts "[SM_cat catgets info_menu_usage_msg]"	;# Standard usage msg

} ;# end SM_MenuUsage

#
# Procedure:
#	SM_PostInitialize - This function is called after SUIT has
#	initialized and processed our genUID definiton. It's called 
#	from sysman_menu initCB. Here we process any command line 
#	arguments pertinent to the the menu UI (i.e. ones that need 
#	SUIT to be initialized before we can act on them). 
#
# Inputs:
#	None
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#	I took a look at Sun' tcl page to see if anyone had done
#	a decent options parser for non-getopts style comamnd lines. 
#	There was one called foxTypedOpts.tcl that was decent but
#	poorly documented, the example was busted (though
#	easily fixable). The killer was the fact that it insisted on 
#	processing all the options. Things like:
#		sysmenu -accel foo -bar -x -y
#	generate errors that -bar -x and -y are not valid options. 
#	If they are at the end of the line, we need to pass them in to the 
#	suitlet. Mutual exclusion couldn't be represented but you
#	could post-process the returned option arrays to catch that. 
#	Things like sysmenu -menu -accel were considered legal. I want to 
#	to treat this as an error. It always put errors to stderr
#	and didn't use message catalogs. 
#
proc SM_PostInitialize {} {

    global argc argv
    global SM_menu_def_root
    global SM_arguments
    global SM_menu_main	
    global SM_menu_title
    global SM_startup_menu_item
    global SM_takesfocus
    global SM_leaf_needs_focus_dialog
    global SM_captive_flag
    global SM_last_exited_pid 
    global SM_last_exited_status

    menu_win instvar menu_title



    # The syntax of the command line is:
    #	sysmenu [ -ui cui | gui | java ] 
    #		[ -display displayname ]
    #		[ -geometry geom ]
    #		[ [-takesfocus] [-accel] <accelerator>  [arguments] ] 
    #		[ [-takesfocus] [-menu]  <name>	  [arguments] ]
    #		[-menu]
    #		-list
    #		-list_hierarchy (undocumented)
    #		-actions
    #		-help
    # The cases that get handled here are as follows:
    #   - If the argument to -accel or -menu was  a branch, 
    #	  then we process it here (crank up the menu expanded to that branch
    #   - If -menu_title is specified, we start a "captive" menu
    #   - If there are no arguments we bring up the main menu proper
    #   - If -takesfocus was specified, we bring up the Chose Focus Host dialog
    #	  [pjw] -takesfocus is now obsolete. A focus task automagically
    #	  gets the Choose Focus Host dialog


    if { $argc == 0 } {				;# If no arguments
	$SM_menu_def_root Execute		;# Build the tree 
	menu_win display			;# Get it on-screen
	return {}

    } elseif { $SM_leaf_needs_focus_dialog == "true"} { 

	# If we're here, then we are launching a focus-requring
	# SUITlet from the command line. We need to wait around 
	# til it exits so we can report the exit status. 

	set SM_must_wait "true"	;# Flag we need suitlet exit status
	$SM_startup_menu_item Execute	;# Launch it
	
	# Need to wait here for the cleanup routine to trigger
	# after handling the SIGCHLD.   Only wait if there
	# are outstanding entries in the array (we may have
	# already cleaned up)

	if {$SM_last_exited_pid == {}} {
	   vwait SM_last_exited_pid
	}

	exit $SM_last_exited_status
	

    } else {					 ;# else argument was a branch

	# pjw: Right now the policy is to unconditionally prune
	# when a branch us specified on the comamnd line. If we add
	# a switch to allow control of this behavior, all it has
	# to do is set captive_flag
	if { $SM_captive_flag == "true" } { 

	    # Prune the menu tree to include only the children of
	    # this branch. 
	    $SM_menu_def_root Prune_menu $SM_startup_menu_item

	    # We want to take the branch's title and use it as our
	    # title bar to give the illusion of a standalone 
	    # app. There is one special case gotcha. Some menu item names
	    # are in the context of the enclosing branch. For example:
	    #    - Networking 
	    #	      - Configuration
	    #		   | children of configuration
	    # In this case it's just "Configuration" cause you have
	    # the outer context of Networking. If you were to prune
	    # at the Configuration branch however, putting Configuration
	    # in the titlebar would be stupid. For cases like these
	    # we support a secondary message catalog string to 
	    # be uses as a title for a pruned menu. This message cat id
	    # is in the form <object_name>_title_lbl. If it's not 
	    # defined then then we just use the regular menu item string. 
	    menu_win set menu_title [$SM_startup_menu_item Get_item_text title]

	} ;# end if captive_flag is set

	$SM_startup_menu_item Make_parents_visible  ;# Expand branch's parents
						    ;# Find selection index
	set index [$SM_menu_def_root Get_item_index $SM_startup_menu_item]

	menu_debug puts "SM_PostInitialize: index $index"

        $SM_startup_menu_item Execute $index true ;# Display this branch
	menu_win display			  ;# Get it on-screen

    } ;# end if no args


} ;# End SM_PostInitialize


#
# Procedure:
#	SM_Init_environment - get the ball rolling by checking for 
#	customization related environment variables
#
# Inputs:
#	None
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#
proc SM_Init_environment {} {

    global env auto_path 
    global SM_menu_dir SM_menu_definition_file SM_menu_tasks_dir
    global SM_menu_mcat
    global SM_menu_ui_type
    global SM_menu_debug SM_menu_debug_level
    global SM_menu_suit_dir
    global SM_menu_def_root    
    global SM_menu_parent
    global SM_captive_flag
    global SysmanDir
    
    # Check out environment for devo customizations

    # Location of the source files for SysMan Menu
    if { [set menu_dir [array get env SYSMAN_MENU_DIR]] != {}} {
	set SM_menu_dir [lindex $menu_dir 1]

	# This is the only way to force sysmansh to use our classes
	# instead of the installed ones. Even making sure SM_menu_dir
	# is first in the auto_path is not enough to have it execute 
	# our local stuff. Use source to bend sysmansh to our iron will.
        puts "sysman_menu: using source files from $SM_menu_dir"
	source ${SM_menu_dir}/sysman_menu_class.tcl
	source ${SM_menu_dir}/sysman_menu_leaf_class.tcl
	source ${SM_menu_dir}/sysman_menu_branch_class.tcl

	# We can't source in cb file since our otcl suit objects
	# aren't defined yet. For that, insert the pwd into the autopath
	set auto_path [linsert $auto_path 0 $SM_menu_dir]

    } else {
	set SM_menu_dir "$SysmanDir/menu/src"
    }

    # Alternate menu defintion file. If the envar has /'s then assume
    # it's full path, else assume it's a file in SYSMAN_MENU_DIR
    if { [set menu_file [array get env SYSMAN_MENU_DEFINITION_FILE]] != {}} {
	set SM_menu_definition_file [lindex $menu_file 1]
	if {[string first {/} $SM_menu_definition_file] == -1} { ;#No slashes 
	    set SM_menu_definition_file \
		"$SM_menu_dir/$SM_menu_definition_file"
	} 
    } else {					;# Else envar is not defined
						;# Use system default menu
	set SM_menu_definition_file "$SM_menu_dir/sysman_menu_definition.tcl"
    }

    # Alternate directory for suitlets. 
    set SM_menu_tasks_dir "$SysmanDir/menu/tasks"
    if { [set tasks_dir [array get env SYSMAN_MENU_TASKS_DIR]] != {}} {
	set SM_menu_tasks_dir "[lindex $tasks_dir 1] $SM_menu_tasks_dir"
    }

    # We have two dependencies on SUIT infrastructure:
    # trees and message catalogs. We need to init those explicitly
    # since we need them before we have let SUIT formally
    # initialize itself. Allow alternate versions ot be used
    if { [set menu_dir [array get env SYSMAN_MENU_SUIT_DIR]] != {}} {
      set SM_menu_suit_dir [lindex $menu_dir 1]
    } else {
      set SM_menu_suit_dir "/usr/share/sysman/suit"
    }



    # Create debugging utility object
    SM_debug menu_debug SYSMAN_MENU_DEBUG

    # Retain legacy debugging until all the code switches over
    if { [menu_debug get] == "true" } {
	set SM_menu_debug "true"	;# This is the legacy debug global
	set SM_menu_debug_level 10
    } else {
	set SM_menu_debug "false"
    }

    # Save our execution domain
    set SM_menu_ui_type "cui"
    if { [set ui_type [array get env SYSMANUI]] != {} } {
	set SM_menu_ui_type [lindex $ui_type 1]
    }

    if { [menu_debug get] == "true" } {
	puts "menu_dir  = $SM_menu_dir"
	puts "tasks_dir = $SM_menu_tasks_dir"
	puts "menu_file = $SM_menu_definition_file"
	puts "ui domain = $SM_menu_ui_type"
	puts ""
    }
   
} ;# end proc Init_environment

#
# Procedure:
#	SM_Initialize - get the ball rolling by sourcing
#	in the base menu definiton and all the menu extensions
#
# Inputs:
#	nosource - true when we want to suppress sourcing in the files
#		   from the tasks directory. This is used when the user wants
#		   to define a custom SYSMAN_MENU_DEFINITION_FILE. If
#		   We source in all the tasks then we get errors cause
#		   the parent objects are not defined. 
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#	We call this routine *before* SUIT has been intialized. 
#	That means that we must use SM_DisplayTextMsg and not
#	SM_DisplayInfoMsg. 
#	This routine sets SM_menu_initialized to indicate that the 
#	menu object defintions have been processed (i.e. we've
#	built the menu tree data structures)
#
proc SM_Initialize { {nosource {false}}  } {

    global SM_menu_definition_file
    global SM_menu_tasks_dir
    global SM_menu_def_root
    global SM_menu_initialized


    set SM_menu_initialized "true"

    # Load the main menu description
    source $SM_menu_definition_file

    # If we are running in "captive mode" where the menu is acting 
    # as the top-level window for a set or related suitlets, then 
    # we don't want to source in the additional menu items
    if { $nosource == "false" } {

	# If there are directory permission errors in menu/tasks, 
	# for_recursive_glob barfs so be defensive. 
	set error [catch {
	    # Now source in any customizations to the menu. These
	    # are contained in files in the $SM_menu_tasks_dir having names
	    # in the form sysman_menu_definition*.tcl
	    for_recursive_glob f $SM_menu_tasks_dir \
				"sysman_menu_definition*.tcl" {

		if { [catch {source $f} result ] } {	;# If there is an error
		    SM_DisplayTextMsg error \
			error_bad_menu_definition $f $result
		}
	    }
	 } result]

	if { $error == 1 } {	;# If for_recursive_glob barfed
	    SM_DisplayTextMsg error error_cannot_recursive_glob
	}

    } ;# If nosource == false



    # Set initial tree's nodes expanded/collapsed state. 
    # Everything start collapsed except for the 
    $SM_menu_def_root set item_state "expanded"

    # Delete all branches that have no leaves
    # To aid in debugging and to let devo's see the entire tree
    # tree as we envision it, then don't do this step when 
    # SYSMAN_MENU_NO_PRUNE is defined
    if { [array get env SYSMAN_MENU_NO_PRUNE] == {} } {
	$SM_menu_def_root Prune_empty_branches
    }

    # menu_debug puts "[$SM_menu_def_root dump]"


} ;# End SM_Initialize

#
# Procedure:
#	SM_navigation_help - display and initial help screen for 
#	curses navigation
#
# Inputs:
#	None
# Outputs:
# 	None
# Returns:
#	None
# Notes:
#	Since this can be called before SUIT is initialized, 
#	the SysmanUi (and the SM_menu_ui_type) can be
#	"menu" or "cli" instead of "cui"
#
proc SM_navigation_help { {nosource {false}}  } {
    global SM_menu_ui_type

    # Navigation help is only for curses
    if { $SM_menu_ui_type == "cui"  || \
	 $SM_menu_ui_type == "menu" || \
	 $SM_menu_ui_type == "cli"        } {

	# Display the help message with a final:
	#     Press any key to continue...
	puts -nonewline "[SM_cat catgets info_curses_navigation_msg]"
	gets stdin
    }

    return

} ;# End SM_navigation_help


#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Do preliminary initialization and parsing of the command line. 
# Depending on the command line arguments, we don't need to initialize/start
# the menu suitlet at all. We typically DO need to construct the menu
# tree data structure however. For example:
#	- sysman -help should just display the usage and exit
#	- sysman -menu "foo" or -accel bar should just launch the 
#	  specified suitlet when foo and bar are leaves. 
# It's big win not to have to intialize SUIT for these cases. Otherwise,
# in this latter case, we end up doing the work twice - once for the 
# menu and once for the suitlet to launch. Also, by avoiding SUIT
# initialization, we avoid the Initializing... dialog (which 
# would be displayed once for the menu and once for the suitlet!)
# The gotchas to this approach are:
#	- we can't rely on the SUIT message catalog layer since
#	  it's not init'd yet. No big deal, just use the generic
#	  catgets functions. Only the actual UI uses the SUIT functions
#	- we need to run the Suitlet command. This is a proc that is 
#	  created dynamically at SUIT statup time. Instead of calling
#	  it, we will have to call the object that it's a wrapper for
#	  (which is a domain specific object). 
#
#----------------------------------------------------------------------

    # No matter what, we need message catalog support. Since
    # the Usage proc and and the menu processing routines will
    # need the catalog support up-front, init it now. Note that 
    # this is the general catalog support code and NOT the suit 
    # API over the general code 
    _Catalog SM_cat			;# Create catalog object
    SM_cat catopen sysman_menu		;# and then open the catalog

    # Query environment variables
    SM_Init_environment


    set SM_arguments ""			;# Start with no cli args



    # The syntax of the command line is:
    #	sysmenu [ -ui cui | gui | java ] 
    #		[ -display displayname ]
    #		[ -geometry geom ]
    #		[ [-takesfocus] [-accel] <accelerator>  [arguments] ] 
    #		[ [-takesfocus] [-menu]  <name>	  [arguments] ]
    #		[-menu]
    #		-menu_title <title>	[pjw: obsolete/unsupported]
    #		-list
    #		-help
    # sysmansh sucks off the -ui, -display, -geom arguments 
    # (several others not applicable to the menu) so we'll
    # never see them here. 
    # -menu_title is for internal application use only and not a documented
    # user option. 
    # -accel and -menu are mutually exclusive. We are going to 
    # be very restrictive here since we need to allow the 
    # menu to pass on arbitrary arguments. For example
    #	sysmenu netconfig -list -help -foo -bar
    # should result in all the arguments being passed to netconfig!
    # IOW, we should not be intercepting -list/-help even though they 
    # are valid args for us as well. We will do the following:
    #    - look at the command line arguments in order, from left to right
    #	 - if it's an argument that takes a parameter, then read the 
    #	   next token
    #    - if it's a argument that takes no parameters (-help -list), then 
    #	   then stop parsing and execute that function
    # -takesfocus is for cluster apps that take the -focushost argument. 
    # The menu will display a initial "Choose Focus Host" dialog box for the 
    # the user. Note that the order here is critical. -takesfocus
    # must come before -accel/-menu or lese it wil be passed as an 
    # argument to suitlet specified! Also, -takesfocus only applies to 
    # leaves. It doesn't make sense for a branch. 
    

    # If there are command line args and -menu is not alone on the
    # command line...
    if { $argc != 0 && !($argc == 1 && [lindex $argv 0] == "-menu") } {

	# Start by looking for the easy stuff - stuff that doesn't need
	# the menu tree data structures defined. This way you get
	# help or error messages as quickly as possible. 

	# If there is exactly one arg and it's for help
	# then the user entered sysman <help_args> so we give help
	# on the overall sysman command. The sysman shell script
	# transforms help_args to -command_help so we hit this case. 
	# We put this here just to avoid the extra inventory of having
	# the sysman shell script having to have a message catalog
	if { $argc == 1 && ([lindex $argv 0] == "-command_help") } {
	    SM_Usage
	    exit 1
	}
	# If there are two args and they are -menu <help args>
	# then we give help on the menu
	set help_args {-h -help -Help ?}
	if { $argc == 2 && ([lindex $argv 0] == "-menu") && \
	     ([lsearch -exact $help_args [lindex $argv 1]] != -1) } {
	    SM_MenuUsage
	    exit 1
	}
	

	# If it's an arg that requires parameters, make sure there is one
	# Note that -takesfocus doesn't take a parameter per se, it just
	# requires that additional params be specified
	set args_with_params  {-accel -menu_title -takesfocus}

	set arg [lindex $argv 0]		;# Get first arg

	# If only one argument is specified, then if it's an arg that 
	# takes a paramter, print an error message. 
	if { $argc == 1 && ([lsearch -exact $args_with_params $arg] != -1) } {

	    switch -- $arg {
		-accel {
		     SM_DisplayTextMsg error \
			    error_cli_syntax_accel_missing_param $arg
		}

		#-menu {
		#     SM_DisplayTextMsg error \
		#	    error_cli_syntax_name_missing_param $arg
		#}

		-menu_title {
		     SM_DisplayTextMsg error \
			    error_cli_syntax_title_missing_param $arg
		}

		-takesfocus {
		     SM_DisplayTextMsg error \
			    error_cli_syntax_takesfocus_missing_param $arg
		}
	    }

	    exit 1
	} ;# end if argc == 1...


	# OK, from here on in we need the menu tree to be built cause
	# we'll be looking for accelerators, menu names, or putting
	# up the UI. 

	# Finally, init the menu tree structure
	SM_Initialize 


#	# See if the first arg is -takesfocus and do sanity checks
#	# of remaining args
#	if { $arg == "-takesfocus" } {
#
#	    set SM_takesfocus "true"
#	    set argv [lrange $argv 1 end]	;# Strip -takesfocus from argv
#	    set argc [incr argc -1]
#
#	    # -takesfocus is ONLY applicable on a cluster! Allow
#	    # it to be specified on a standalone system but ignore it
#	    if { $SysmanOnCluster == 0 } {
#		set SM_takesfocus "false"
#	    }
#
#
#	    # At this point we expect:
#	    #	-accel <accel> | -menu "name" | <accel>
#	    # If -menu or -accel are specified without an argument, 
#	    # complain
#	    if { ([lindex $argv 0] == "-menu" || \
#		  [lindex $argv 0] == "-accel" ) && $argc == 1 } {
#
#		SM_DisplayTextMsg error \
#			    error_cli_syntax_takesfocus_missing_param $arg
#		exit 1		   
#	    }
#
#	    set arg [lindex $argv 0]		;# Get first arg again
#
#	} ;# end if arg == -takesfocus


	# If we reach here, then at least one arg has been specified, 
	# and if it needs a param it has one. The menu data structures
	# are init'd. 
	switch  -- $arg {

	    -accel {
		# -accel and -menu expect a parameter. -accel wants
		# the accelerator for a menu item. -menu wants the user visible
		# name of the menu item. 

		set SM_arguments [lrange $argv 2 end]	;# Save additional args

		# Find the menu item with this accel. 
		set accel [lindex $argv 1]		;# Get second argument
		set SM_startup_menu_item \
			[$SM_menu_def_root Find_accelerator $accel]

		if { $SM_startup_menu_item == "" } {	;# If unknown accel.
		    SM_DisplayTextMsg error info_usage_unknown_accel $accel
		    exit 1

		}

		set SM_captive_flag true		;# Enable pruning

	    }

	    -menu {

		# Find the item with this name 
		set name [lindex $argv 1]		;# Get second argument
		set SM_startup_menu_item \
			[$SM_menu_def_root Find_named_item $name]

		if { $SM_startup_menu_item == "" } {	;# If unknown name
		    SM_DisplayTextMsg error info_usage_unknown_name $name
		    exit 1
		}
		set SM_arguments [lrange $argv 2 end]	;# Save additional args
		set SM_captive_flag true		;# Enable pruning

	    }

            -actions {
                $SM_menu_def_root List_actions
                exit
            }

	    -list {
		$SM_menu_def_root List_menu
		exit
	    }

            -list_attributes {
		# This is an undocumented, internal use only function
		# to dump the menu tree along with each item's attributes
                $SM_menu_def_root List_menu "show_all_with_attributes"
                exit
            }
 
	    default {

		# If we reach here, neither -accel nor -menu was specified
		# and there is at least one argument - the first argument
		# is either an accelerator or a menu name. 
		# Save off additional app-specific args that come after this 
		# argument. 
		set SM_arguments [lrange $argv 1 end] ;# Save additional args

		# Assume it's an accel first:
		set SM_startup_menu_item \
			[$SM_menu_def_root Find_accelerator $arg]

		# If we can't find it as an accel, try named item
		if { $SM_startup_menu_item == "" } { 
		    set SM_startup_menu_item \
			[$SM_menu_def_root Find_named_item $arg]
		}

		# If it's still not found, we have an error. 
		if { $SM_startup_menu_item == "" } {
		    SM_DisplayTextMsg error info_usage_unknown_item $arg
		    exit 1
		} else {
		    set SM_captive_flag true		;# Enable pruning
		    # Now fall through to common code below
		} 
	    }


	}     ;# end switch $arg

    
        SM_navigation_help	;# If curses, display navigation help


	# At this point, check to see if we have located a valid menu
	# branch or leaf. For a leaf:
	#	- if it needs a focus specified, then we fall through 
	#	  and do the work in initCB after SUIT is init'd. 
	#	- otherwise we don't need to initialize
	#         the SUIT for the menu - just execute the target task. 
	# For a branch or
	# just the vanilla top level menu (sysman with no args) 
	# then just fall through to the suit defintion below and continue
	# processing in the main initCb


        # If the leaf takes focus and it's not specified on the command line
	# AND we are in a cluster then flag the fact that the Choose
	# Focus Host dialog is needed. 
	set focus [$SM_startup_menu_item set focus]


	if { ($focus == "both" || $focus == "member_specific") } {

	    if { $SysmanFocusHost == "" && $SysmanOnCluster == 1 } {
	
		# then flag we need the Choose Focus Host dialog
		set SM_leaf_needs_focus_dialog true
	    } 
	}


	# If we are a branch or need the Chose Focus Host dialog
	if { [$SM_startup_menu_item info class] == "Menu_branch" || \
	    $SM_leaf_needs_focus_dialog == "true" } {

	    set SM_must_wait "false"
	    # Fall through and display the branch in PostInitialize

#	    # Suppress splash screen for menu's display of Choose Focus Dialog
#	    if { $SM_leaf_needs_focus_dialog == "true"} {
#		set env(_SUIT_SPLASH_SCREEN) 0
#	    }

	} else {				;# else we're a leaf

	    # Skip SUIT init and launch directly

	    # There is one subtlety for leaf processing. If the leaf
	    # can't be executed for some reason we need to capture
	    # it's error output and display that to the user. Since we
	    # fork/exec it, we need to wait for the child to exit. Do this
	    # by setting the SM_must_wait which is read by childExitCB
	    # and forces a wait when displaying status
	    set SM_must_wait "true"

	    $SM_startup_menu_item Real_execute 	;# Execute the leaf

            # remove main window for this application as it 
	    # isn't needed.

	    if [cequal [info commands wm] "wm"] {
	       catch {destroy .}
	    }

	    # Need to wait here for the cleanup routine to trigger
	    # after handling the SIGCHLD.   Only wait if there
	    # are outstanding entries in the array (we may have
	    # already cleaned up)
	
	    if {$SM_last_exited_pid == {}} {
	       vwait SM_last_exited_pid
	    }

	    exit $SM_last_exited_status

	} ;# end if we're a branch or SM_leaf_needs_focus_dialog is true


    } else {			;# no command line args

	set argc 0		;# -menu counts as no args in this case
	
        SM_navigation_help	;# If curses, display navigation help

	# We used init the menu here but on slow systems the speed
	# of this proc gates the display of the SUIT splash screen. 
	# Instead, fall through to the SUIT code below (which displays
	# the splash screen) and then then init in main window's initCB
	#SM_Initialize		;# Init the menu tree structure

	# The menu will be displayed by the main window's initCB

    }  ;# end if there are command line args





#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# Define our SUIT interface for the user. This consists
# of a main dialog box containing a list box. We manage the contents
# of the list box in order to display and hierarchical tree (using
# identations levels). No code can occur after this SUIT window hierarchy 
# definition. At the end of this, SUIT goes into the equivalent
# of XtMainLoop. SUIT calls sysman_menu initCB which is where we
# pick up our command line processing
#
#----------------------------------------------------------------------

# When we are in -menu_title mode, we supress the find button
    if { $SM_captive_flag == "true" } {
	set menu_win_buttons { select help_on_item}
    } else {
	set menu_win_buttons { select find help_on_item}
    }

    Main sysman_menu {

	Window menu_win {

	    # Make sure our globals in SUIT's scope
	    global menu_win_buttons
	    global SysmanOnCluster
	    List menu_tree \
		 -height 13 \
		 -needSelection {select help_on_item} \
				-buttons $menu_win_buttons \
		 -doubleClk select \
		 -autoSort 0 \
		 -width 80
	} \
	    -wmclose exit \
	    -buttons {exit options help} \
	    -display 0 
	    ;# end sysman_menu_lst


	# Find task by keyword dialog box
	Window find_db {
	    Label intro
	    Text keyword \
		-width 30

	} \
	    -wmclose cancel \
	    -buttons { ok apply cancel help } \
	    -defaultb ok \
	    -display 0
	    ;# end find_db

	# The task dialog box displays a list of the tasks that were found
	# in the Find Task by Keyword dialog
	Window tasks_db {
	    Label intro

	    List tasks \
		 -height 13 \
		 -buttons {run help_on_item} \
		 -needSelection {run help_on_item}  \
		 -doubleClk run \
		 -autoSort 0
	} \
	    -wmclose ok \
	    -buttons { cancel  help } \
	    -defaultb cancel \
	    -display 0
	    ;# end tasks_db

	# Options dialog box
	Window options_db {
	    Boolean display_accelerators

	} \
	    -wmclose cancel \
	    -buttons { ok cancel help } \
	    -defaultb ok \
	    -display 0
	    ;# end options_db

	# The cluster members list box
	Window members_db {
	    List members_list \
		    -width 50 \
		    -doubleClk select \
		    -needSelection {select} \
		    -buttons {select} \
		    -autoSort 0
	} \
	    -display 0 \
	    -defaultb ok \
	    -buttons {ok cancel help}

	# If the user asks for help on a ptyiolet we display the man page
	Window manpage_db {
	    Text text -height 17 -width 80 -editable 0
	} -buttons {ok} \
	  -defaultb ok \
	  -display 0


    }  ;# end Main

