diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2016-09-19 20:38:49 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2016-09-19 20:38:49 +0300 |
commit | 67e638c55b844846e5ffb6a3b60c59f420d1e467 (patch) | |
tree | 67b14749c4e55a65fab7e05394f5750af0edeac6 | |
parent | a94076c0476cb9c0f9b0fb07ade3bc1f6facd2f8 (diff) | |
download | rex-67e638c55b844846e5ffb6a3b60c59f420d1e467.tar.gz rex-67e638c55b844846e5ffb6a3b60c59f420d1e467.tar.bz2 |
Revamp rc file support.
While improving rc file support and making it (hopefully) more
convenient, this commit alters the priority of rc ws. command
line settings. This will be reverted in future commits.
* rex (::getopt::parse): New option -postprocess. When given,
supplies the name of a function to be called after parsing command
line. The function returns a list of triplets: {option line file},
where option is a (list of) option and eventual argument, line and
file give the location of the corresponding statement in the rc
file. The list is then processed by calls to ::getopt::getopt,
one call per option.
(::config): Deprecate the old way of setting configuration
variables. Provide functions for each particular setting, that
make rc file look more or less like a traditional UNIX configuration
file, while still providing possibility for using full strength of
TCL.
Catch and process syntax errors.
(modetrans): New array. Translates various spellings of the mode
argument to ensure config(mode) is set uniformly.
-rwxr-xr-x | rex | 303 |
1 files changed, 269 insertions, 34 deletions
@@ -193,7 +193,7 @@ namespace eval ::getopt { variable opterr 1 variable optchar variable optopt - + proc init {} { variable optarg set optarg "" @@ -625,7 +625,7 @@ namespace eval ::getopt { } exit 0 } - + proc parse {args} { while {[llength $args] > 0} { set arg [lindex $args 0] @@ -637,7 +637,11 @@ namespace eval ::getopt { -before { set before [lindex $args 1] set args [lreplace $args [set args 0] 1] - } + } + -postprocess { + set postprocess [lindex $args 1] + set args [lreplace $args [set args 0] 1] + } -docstring { set docdict(docstring) [lindex $args 1] set args [lreplace $args [set args 0] 1] @@ -661,7 +665,7 @@ namespace eval ::getopt { default { break } } } - + if {[llength $args] != 3} { return -code error "bad number of arguments [llength $args]" } @@ -754,7 +758,7 @@ namespace eval ::getopt { } lappend param $argc $argv lappend param $shortopts - + getopt {*}$param { upvar before before if {[info exists before]} { @@ -772,6 +776,44 @@ namespace eval ::getopt { variable optind set argv [lrange $argv $optind end] set argc [expr $argc - $optind] + + if {[info exists postprocess]} { + foreach optitem [eval $postprocess] { + set opt [lindex $optitem 0] + + if {[info exists progname]} { + set loc "$progname:" + } else { + set loc "" + } + if {[llength $optitem] == 3} { + append loc "[lindex $optitem 2]:" + } + append loc [lindex $optitem 1] + + init + set param [list -progname $loc] + if {[info exists longopts]} { + lappend param -longopts $longopts + } + + lappend param 1 $opt + lappend param $shortopts + + getopt {*}$param { + upvar before before + if {[info exists before]} { + eval $before + } + upvar select select + switch -- $optchar {*}$select \ + ? { exit 1 } \ + default { + return -code error "option should have been recognized: $optchar" + } + } + } + } } } # ####################################################################### @@ -1282,7 +1324,8 @@ proc dbchanged {filename var} { upvar $var olddb readdb $filename newdb - foreach newkey [lsort -command keycmp [array names newdb]] oldkey [lsort -command keycmp [array names olddb]] { + foreach newkey [lsort -command keycmp [array names newdb]] \ + oldkey [lsort -command keycmp [array names olddb]] { if {$oldkey != $newkey} { return 1 } @@ -2141,7 +2184,157 @@ namespace eval ::hostproc { # ####################################################################### namespace eval ::config { variable initialized 0 - variable cfgvars {sudo hosts user password command program} + variable cfgvars {sudo hosts user password command} + variable update_hint + array set update_hint { + hosts {var { return "host $var" }} + sudo {var { if {$var} { + return "sudo on" + } else { + return "sudo off" + } + }} + user {var { return "user $var" }} + password {var { return "password $var" }} + } + + proc host {args} { + variable hosts + lappend hosts {*}$args + } + + proc ifmode {args} { + switch -- $::config(mode) {*}$args + } + + proc option {args} { + global config + set frame [info frame -1] + lappend opt $args + lappend opt [dict get $frame line] + if {[dict get $frame type] == "source"} { + lappend opt [dict get $frame file] + } + lappend config(postprocess) $opt + } + + proc timeout {args} { + set frame [info frame -1] + set locus [dict get $frame line] + if {[dict get $frame type] == "source"} { + set locus "[dict get $frame file]:$locus" + } + switch -- [llength $args] { + 0 { return $::timeout } + 1 { set ::timeout [lindex $args 1] } + default { + terror "$locus: usage: timeout ?value?" + } + } + } + + proc environ {args} { + set frame [info frame -1] + set locus [dict get $frame line] + if {[dict get $frame type] == "source"} { + set locus "[dict get $frame file]:$locus" + } + set mode set + foreach a $args { + switch -- $mode { + {set} { + switch -regexp -matchvar match -- $a { + {^-set$} {} + {^-unset$} { + set mode unset + } + {^(.+?)=(.*)$} { + set env([lindex $match 1]) [lindex $match 2] + } + default { + terror "$locus: that doesn't look like a variable assignment: $a" + } + } + } + {unset} { + switch -- $a { + {-set} { + set mode set + } + {-unset} {} + default { + unset -nocomplain env($a) + } + } + } + } + } + } + + # set_config_list NAME [-clear] list + proc set_config_list {name arglist} { + global config + + if {[llength $arglist] == 0} { + set frame [info frame -2] + set locus [dict get $frame line] + if {[dict get $frame type] == "source"} { + set locus "[dict get $frame file]:$locus" + } + terror "$locus: usage: $name ?-clear? list" + return + } + + if {[lindex $arglist 1] == "-clear" || [lindex $arglist 1] == "-i"} { + unset -nocomplain config($name) + set arglist [lrange $arglist 1 end] + } + + if {[llength $arglist] > 0} { + lappend config($name) {*}$arglist + } + } + + # earlycmd [-clear] list + proc earlycmd {args} { + set_config_list earlycmd $args + } + # shrc [-clear] list + proc shrc {args} { + set_config_list shrc $args + } + proc run-commands {args} { + set_config_list shrc $args + } + + # sudo on|off + proc sudo {args} { + set frame [info frame -2] + set locus [dict get $frame line] + if {[dict get $frame type] == "source"} { + set locus "[dict get $frame file]:$locus" + } + + if {[llength $arglist] != 1} { + terror "$locus: usage: sudo on|off" + return + } + switch -nocase -- [lindex $args 0] { + on setsudo + off { unset -nocomplain ::config(sudo) } + default { + terror "$locus: usage: sudo on|off" + } + } + } + + proc user {name} { + set ::config(user) $user + } + + proc password {text} { + set ::config(pass) [::passenc $text] + } proc read {file} { variable cfgvars @@ -2149,9 +2342,35 @@ namespace eval ::config { foreach var $cfgvars { variable $var } - source $file - set initialized 1 + if {[catch {source $file} result options] == 0} { + set initialized 1 + } else { + set locus "$file" + if {[dict get $options -code] == 1} { + append locus ":" [dict get $options -errorline] + } + terror "$locus: $result" + foreach {tok prm} [dict get $options -errorstack] { + switch -- $tok { + {CALL} { + if {[lindex $prm 0] == "::config::read"} { + break + } + if {[info exists prevcall]} { + if {$prevcall == "prm"} { + continue + } + } + terror "called from:" + puts stderr "$prm" + set prevcall prm + } + } + } + exit 1 + } } + proc exists {name} { variable $name info exists $name @@ -2164,8 +2383,14 @@ namespace eval ::config { global config variable cfgvars variable initialized + variable update_hint + foreach var $cfgvars { if {[exists $var]} { + terror -nosave "warning: your rc file sets obsolete variable \"$var\"" + if {[info exists update_hint($var)]} { + terror -nosave "warning: please change this to \"[apply $update_hint($var) [valueof $var]]\"" + } if {[info exists config($var)]} { if {$var == "hosts" && ![info exists config(option,ignore-hosts)]} { @@ -2175,7 +2400,7 @@ namespace eval ::config { debug 3 "config(hosts) = $config(hosts)" } } else { - eval { set config($var) [valueof $var] } + set config($var) [valueof $var] debug 3 "config($var) = $config($var)" } } @@ -2227,7 +2452,7 @@ proc hostlist_setup {} { global libpath global sysconfdir global usrconfdir - + if {[info exists config(hosts)] \ && [info exists config(exclude_hosts)] \ && [llength $config(exclude_hosts)] > 0} { @@ -2329,7 +2554,7 @@ proc common_config_setup {} { terror " $dir" } } elseif {[config_option verbose]} { - warning "the following directories doesn't exist:" + warning "the following directories don't exist:" foreach dir $missing { terror " $dir" } @@ -2351,7 +2576,10 @@ proc common_config_setup {} { } debug 2 "importing configuration settings" - ::config::export + ::config::export + if {[info exists config(postprocess)]} { + return $config(postprocess) + } } proc cleanup {} { @@ -2441,10 +2669,9 @@ proc rex_command args { } } - set config(mode) command - ::getopt::parse -usage {rex run [OPTIONS] PROGRAM [ARGS...]} \ -docstring {Runs PROGRAM on the given hosts.} \ + -postprocess common_config_setup \ -before {global config} argc argv { buffer-output,b {buffer output from servers} @@ -2510,8 +2737,6 @@ proc rex_command args { { set config(option,zsh-quirk) 1 } } - common_config_setup - # FIXME if {[config_option noop]} { updatedb @@ -2630,8 +2855,6 @@ proc rex_list {} { global config global env - set config(mode) list - ::getopt::parse \ -usage {rex list [groups]} \ -alias {{rex list [OPTIONS] hosts}} \ @@ -2641,16 +2864,17 @@ hostgroups along with their descriptions. In the second form, lists hostnames obtained as a result of applying the OPTIONS (at least one must be given).} \ + -postprocess common_config_setup \ -before {global config} argc argv { group,g=NAME {select host group} - { set config(option,hostgroup) $optarg } + { set config(option,hostgroup) $optarg } host,H=HOST {add HOST to the host list} { lappend config(hosts) {*}[split $optarg ","] } exclude-host,X=HOST {remove HOST from the host list} - { lappend config(exclude_hosts) {*}[split $optarg ","] } + { lappend config(exclude_hosts) {*}[split $optarg ","] } ignore-hosts,i {ignore the list of hosts read from the hostgroup file} { set config(option,ignore-hosts) 1 } @@ -2661,10 +2885,8 @@ the OPTIONS (at least one must be given).} \ 1 { switch -- [argcvshift] { groups listgroups hosts { + hostlist_setup if {[info exists config(option,hostgroup)]} { - common_config_setup - hostlist_setup - foreach host $config(hosts) { puts "$host" } @@ -2696,6 +2918,7 @@ proc rex_copy_from {} { ::getopt::parse \ -usage {rex rcp|copy-from [OPTIONS] HOST FILE [FILE...] DEST} \ -docstring {Copies FILEs from HOST to DEST on the local machine.} \ + -postprocess common_config_setup \ -before {global config} argc argv { interactive,I @@ -2718,7 +2941,6 @@ proc rex_copy_from {} { debug 2 "copy-from mode" } - common_config_setup exit [runcprev {*}$argv] } @@ -2731,6 +2953,7 @@ proc rex_copy_to {} { ::getopt::parse \ -usage {rex cp|copy [OPTIONS] FILE [FILE...] DEST} \ -docstring {Copies FILEs to DEST on each host.} \ + -postprocess common_config_setup \ -before {global config} argc argv { confirm,w {prompt and wait for confirmation before each host} @@ -2782,7 +3005,6 @@ proc rex_copy_to {} { } debug 2 "argv=[join $config(argv)]" - common_config_setup hostlist_setup if {$config(sudo) != ""} { @@ -2813,6 +3035,7 @@ proc rex_login {} { ::getopt::parse \ -usage {rex login [OPTIONS] HOST} \ -docstring {Log in to HOST.} \ + -postprocess common_config_setup \ -before {global config} argc argv { interactive,I @@ -2845,8 +3068,6 @@ proc rex_login {} { } set host [lshift argv] - common_config_setup - log_user [config_option log] debug 2 "logging in to $host" @@ -3080,7 +3301,24 @@ if {$argc == 0} { exit 1 } +array set modetrans { + run run + command run + + copy copy-to + cp copy-to + + copy-from copy-from + rcp copy-from + + list list + edit edit +} + set config(mode) [argcvshift] +if {[info exist modetrans(config(mode))]} { + set config(mode) $modetrans(config(mode)) +} trap { cleanup @@ -3088,14 +3326,11 @@ trap { } {SIGINT SIGQUIT SIGHUP SIGTERM} switch -- $config(mode) { - run - - command rex_command + run rex_command - cp - - copy rex_copy_to + copy-to rex_copy_to - copy-from - - rcp rex_copy_from + copy-from rex_copy_from login rex_login list rex_list |