66 package require Tcl 8.2
67 package provide cmdline 1.3.1
69 namespace eval ::cmdline {
70 namespace export getArgv0 getopt getKnownOpt getfiles getoptions \
105 proc ::cmdline::getopt {argvVar optstring optVar valVar} {
106 upvar 1 $argvVar argsList
107 upvar 1 $optVar option
108 upvar 1 $valVar value
111 set result [
getKnownOpt argsList $optstring option value]
154 proc ::cmdline::getKnownOpt {argvVar optstring optVar valVar} {
155 upvar 1 $argvVar argsList
156 upvar 1 $optVar option
157 upvar 1 $valVar value
165 if {[
llength $argsList] != 0} {
169 switch -glob -- [
set arg [
lindex $argsList 0]] {
171 set argsList [
lrange $argsList 1 end]
175 set option [
string range $arg 1 end]
176 if {[
string equal [
string range $option 0 0] "-"]} {
177 set option [
string range $arg 2 end]
181 set idx [
string first "=" $option 1]
183 set _val [
string range $option [
expr {$idx+1}] end]
184 set option [
string range $option 0 [
expr {$idx-1}]]
187 if {[lsearch -exact $optstring $option] != -1} {
191 set argsList [
lrange $argsList 1 end]
192 }
elseif {[lsearch -exact $optstring "$option.arg"] != -1} {
194 set argsList [
lrange $argsList 1 end]
196 if {[
info exists _val]} {
198 }
elseif {[
llength $argsList]} {
199 set value [
lindex $argsList 0]
200 set argsList [
lrange $argsList 1 end]
202 set value "Option \"$option\" requires an argument"
207 set value "Illegal option \"-$option\""
247 proc ::cmdline::getoptions {arglistVar optlist {usage options:}} {
248 upvar 1 $arglistVar argv
252 set argc [
llength $argv]
253 while {[
set err [
getopt argv $opts opt arg]]} {
258 set result($opt) $arg
260 if {[
info exists result(?)] || [
info exists result(help)]} {
263 return [
array get result]
289 proc ::cmdline::getKnownOptions {arglistVar optlist {usage options:}} {
290 upvar 1 $arglistVar argv
299 set unknownOptions [list]
301 set argc [
llength $argv]
302 while {[
set err [
getKnownOpt argv $opts opt arg]]} {
308 lappend unknownOptions [
lindex $argv 0]
309 set argv [
lrange $argv 1 end]
310 while {([
llength $argv] != 0) \
311 && ![
string match "-*" [
lindex $argv 0]]} {
312 lappend unknownOptions [
lindex $argv 0]
313 set argv [
lrange $argv 1 end]
315 }
elseif {$err == -2} {
319 set result($opt) $arg
325 set argv [
concat $unknownOptions $argv]
327 if {[
info exists result(?)] || [
info exists result(help)]} {
330 return [
array get result]
353 proc ::cmdline::GetOptionDefaults {optlist defaultArrayVar} {
354 upvar 1 $defaultArrayVar result
357 foreach opt $optlist {
358 set name [
lindex $opt 0]
359 if {[regsub -- {\.secret$} $name {} name] == 1} {
363 if {[regsub -- {\.arg$} $name {} name] == 1} {
367 set default [
lindex $opt 1]
368 set result($name) $default
389 proc ::cmdline::usage {optlist {usage {options:}}} {
391 foreach opt [
concat $optlist \
392 {{- "Forcibly stop option processing"} {help "Print this message"} {? "Print this message"}}] {
393 set name [
lindex $opt 0]
394 if {[regsub -- {\.secret$} $name {} name] == 1} {
398 if {[regsub -- {\.arg$} $name {} name] == 1} {
399 set default [
lindex $opt 1]
400 set comment [
lindex $opt 2]
401 append str [
format " %-20s %s <%s>\n" "-$name value" \
404 set comment [
lindex $opt 1]
405 append str [
format " %-20s %s\n" "-$name" $comment]
430 proc ::cmdline::getfiles {patterns quiet} {
432 if {$::tcl_platform(platform) == "windows"} {
433 foreach pattern $patterns {
434 set pat [
file join $pattern]
435 set files [glob -nocomplain -- $pat]
438 puts stdout "warning: no files match \"$pattern\""
441 foreach file $files {
450 foreach file $result {
453 set fullPath [
file join [
pwd] $file]
455 if {[
file isfile $fullPath]} {
456 lappend files $fullPath
457 }
elseif {! $quiet} {
458 puts stdout "warning: no files match \"$file\""
476 proc ::cmdline::getArgv0 {} {
479 set name [
file tail $argv0]
480 return [
file rootname $name]
502 namespace eval ::cmdline {
503 namespace export typedGetopt typedGetoptions typedUsage
516 catch {
string is . .} charclasses
518 regexp -- {must be (.+)$} $charclasses dummy charclasses
519 regsub -all -- {, (or )?} $charclasses {|} charclasses
609 proc ::cmdline::typedGetopt {argvVar optstring optVar argVar} {
612 upvar $argvVar argsList
623 if {[
llength $argsList] != 0} {
627 switch -glob -- [
set arg [
lindex $argsList 0]] {
629 set argsList [
lrange $argsList 1 end]
636 foreach str $optstring {
637 lappend optstr [
file rootname $str]
640 set _opt [
string range $arg 1 end]
644 set opt [
lindex $optstring $i]
646 set quantifier "none"
647 if {[regexp -- {\.[^.]+([?+*])$} $opt dummy quantifier]} {
648 set opt [
string range $opt 0 end-1]
651 if {[
string first . $opt] == -1} {
654 set argsList [
lrange $argsList 1 end]
656 }
elseif {[regexp -- "\\.(arg|$charclasses)\$" $opt dummy charclass]
657 || [regexp -- {\.\(([^)]+)\)} $opt dummy charclass]} {
659 if {[
string equal arg $charclass]} {
662 }
elseif {[regexp -- "^($charclasses)\$" $charclass]} {
668 set argsList [
lrange $argsList 1 end]
669 set opt [
file rootname $opt]
672 if {[
llength $argsList] == 0
673 || [
string equal "--" [
lindex $argsList 0]]} {
674 if {[
string equal "--" [
lindex $argsList 0]]} {
675 set argsList [
lrange $argsList 1 end]
679 if {$type == "arg"} {
681 }
elseif {$type == "oneof"} {
682 set oneof ", one of $charclass"
686 if {$quantifier == "?"} {
690 }
elseif {$quantifier == "+"} {
692 if {[
llength $optarg] < 1} {
694 set optarg "Option requires at least one $charclass argument$oneof -- $opt"
698 }
elseif {$quantifier == "*"} {
702 set optarg "Option requires $charclass argument$oneof -- $opt"
708 }
elseif {($type == "arg") || (($type == "oneof") && [
string first "|[
lindex $argsList 0]|" "|$charclass|"] != -1) || (($type == "class") && [
string is $charclass [
lindex $argsList 0]])} {
711 lappend optarg [
lindex $argsList 0]
712 set argsList [
lrange $argsList 1 end]
715 if {$type == "arg"} {
717 }
elseif {$type == "oneof"} {
718 set oneof ", one of $charclass"
721 set optarg "Option requires $charclass argument$oneof -- $opt"
725 if {$quantifier == "?"} {
731 if {![regexp -- {[+*]} $quantifier]} {
737 "Illegal option type specification: must be one of $charclasses" \
741 set optarg "Illegal option -- $_opt"
809 proc ::cmdline::typedGetoptions {arglistVar optlist {usage options:}} {
812 upvar 1 $arglistVar argv
815 foreach opt $optlist {
816 set name [
lindex $opt 0]
817 if {[regsub -- {\.secret$} $name {} name] == 1} {
820 if {[regsub -- {\.multi$} $name {} name] == 1} {
823 regsub -- {\..*$} $name {} temp
827 if {[regsub -- "\\.(arg|$charclasses|\\(.+).?\$" $name {} name] == 1} {
831 set dflt [
lindex $opt 1]
833 set defaults($name) $dflt
837 set argc [
llength $argv]
838 while {[
set err [
typedGetopt argv $opts opt arg]]} {
840 if {[
info exists result($opt)]
841 && [
info exists multi($opt)]} {
847 set result($opt) "$result($opt) $arg"
850 set result($opt) "$arg"
852 }
elseif {($err == -1) || ($err == -3)} {
854 }
elseif {$err == -2 && ![
info exists defaults($opt)]} {
858 if {[
info exists result(?)] || [
info exists result(help)]} {
861 foreach {opt dflt} [
array get defaults] {
862 if {![
info exists result($opt)]} {
863 set result($opt) $dflt
866 return [
array get result]
881 proc ::cmdline::typedUsage {optlist {usage {options:}}} {
885 foreach opt [
concat $optlist \
886 {{help "Print this message"} {? "Print this message"}}] {
887 set name [
lindex $opt 0]
888 if {[regsub -- {\.secret$} $name {} name] == 1} {
892 if {[regsub -- {\.multi$} $name {} name] == 1} {
896 if {[regexp -- "\\.(arg|$charclasses)\$" $name dummy charclass]
897 || [regexp -- {\.\(([^)]+)\)} $opt dummy charclass]} {
898 regsub -- "\\..+\$" $name {} name
899 set comment [
lindex $opt 2]
900 set default "<[
lindex $opt 1]>"
901 if {$default == "<>"} {
904 append str [
format " %-20s %s %s\n" "-$name $charclass" \
907 set comment [
lindex $opt 1]
908 append str [
format " %-20s %s\n" "-$name" $comment]
929 proc ::cmdline::prefixSearch {list pattern} {
932 if {[
set pos [::lsearch -exact $list $pattern]] > -1} {
938 set slist [lsort $list]
939 if {[
set pos [::lsearch -glob $slist $pattern*]] > -1} {
942 set check [
lindex $slist [
expr {$pos + 1}]]
943 if {[
string first $pattern $check] != 0} {
944 return [::lsearch -exact $list [
lindex $slist $pos]]
961 proc ::cmdline::Error {message args} {
962 return -code error -errorcode [
linsert $args 0 CMDLINE] $message