Browse Source

safe interp fixes

master
Julian Noble 6 months ago
parent
commit
ac5a496f92
  1. 7
      src/modules/argparsingtest-999999.0a1.0.tm
  2. 5
      src/modules/canaryspace-999999.0a1.0.tm
  3. 35
      src/modules/natsort-0.1.1.6.tm
  4. 47
      src/modules/punk-0.1.tm
  5. 1539
      src/modules/punk/ansi-999999.0a1.0.tm
  6. 297
      src/modules/punk/args-999999.0a1.0.tm
  7. 118
      src/modules/punk/assertion-999999.0a1.0.tm
  8. 11
      src/modules/punk/cap/handlers/templates-999999.0a1.0.tm
  9. 994
      src/modules/punk/char-999999.0a1.0.tm
  10. 65
      src/modules/punk/config-0.1.tm
  11. 501
      src/modules/punk/experiment-999999.0a1.0.tm
  12. 3
      src/modules/punk/experiment-buildversion.txt
  13. 163
      src/modules/punk/lib-999999.0a1.0.tm
  14. 12
      src/modules/punk/ns-999999.0a1.0.tm
  15. 38
      src/modules/punk/overlay-0.1.tm
  16. 46
      src/modules/punk/repl-0.1.tm
  17. 2
      src/modules/punkcheck-0.1.0.tm
  18. 48
      src/modules/shellfilter-0.1.9.tm
  19. 115
      src/modules/textblock-999999.0a1.0.tm

7
src/modules/argparsingtest-999999.0a1.0.tm

@ -46,8 +46,11 @@
#[list_begin itemized]
package require Tcl 8.6-
package require punk::args
package require struct::set
#*** !doctools
#[item] [package {Tcl 8.6}]
#[item] [package {punk::args}]
# #package require frobz
# #*** !doctools
@ -240,7 +243,7 @@ namespace eval argparsingtest {
-3 -default 3 -type integer
*values
} $args]
return [dict get $argd opts]
return [tcl::dict::get $argd opts]
}
proc test1_punkargs_validate_without_ansi {args} {
set argd [punk::args::get_dict {
@ -259,7 +262,7 @@ namespace eval argparsingtest {
-3 -default 3 -type integer -validate_without_ansi true
*values
} $args]
return [dict get $argd opts]
return [tcl::dict::get $argd opts]
}
package require opt

5
src/modules/canaryspace-999999.0a1.0.tm

@ -23,7 +23,10 @@
# Meta description and so may need to be comprised mainly of fully qualified commands.
# @@ Meta End
#usage example
#% use canaryspace
# using the repl, enter commands that may use the calling context and ensure there are no unexpected canaryspace emissions on stderr.
# (expect only a single CANARYSPACE output for entered command if it is at global level.)
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements

35
src/modules/natsort-0.1.1.6.tm

@ -1427,24 +1427,27 @@ namespace eval natsort {
return 0
}
}
set is_namematch [called_directly_namematch]
set is_inodematch [called_directly_inodematch]
####
#review - reliability of mechanisms to determine direct calls
# we don't want application being called when being used as a library, but we need it to run if called directly or from symlinks etc
#-- choose a policy and leave the others commented.
#set is_called_directly $is_namematch
#set is_called_directly $is_inodematch
set is_called_directly [expr {$is_namematch || $is_inodematch}]
#set is_called_directly [expr {$is_namematch && $is_inodematch}]
###
#puts stdout "called_directly_name: [called_directly_namematch] called_directly_inode: [called_directly_inodematch]"
if {![interp issafe]} {
set is_namematch [called_directly_namematch]
set is_inodematch [called_directly_inodematch]
####
#review - reliability of mechanisms to determine direct calls
# we don't want application being called when being used as a library, but we need it to run if called directly or from symlinks etc
#-- choose a policy and leave the others commented.
#set is_called_directly $is_namematch
#set is_called_directly $is_inodematch
set is_called_directly [expr {$is_namematch || $is_inodematch}]
#set is_called_directly [expr {$is_namematch && $is_inodematch}]
###
#puts stdout "called_directly_name: [called_directly_namematch] called_directly_inode: [called_directly_inodematch]"
} else {
#safe interp
set is_called_directly 0
}
#
proc test_pass_fail_message {pass {additional ""}} {

47
src/modules/punk-0.1.tm

@ -160,13 +160,6 @@ namespace eval punk {
#variable re_headvar1 {([a-zA-Z:@.(),]+?)(?![^(]*\))(,.*)*$}
proc get_repl_runid {} {
if {[tsv::exists repl runid]} {
return [tsv::get repl runid]
} else {
return 0
}
}
#ordinary emission of chunklist when no repl
proc emit_chunklist {chunklist} {
@ -1009,7 +1002,7 @@ namespace eval punk {
}
#set assigned [dict values $leveldata]
set pairs [list]
dict for {k v} $leveldata {lappend pairs [list $k $v]}
tcl::dict::for {k v} $leveldata {lappend pairs [list $k $v]}
set assigned [lindex [list $pairs [unset pairs]] 0]
} elseif {[string is integer -strict $index]} {
if {[catch {llength $leveldata} len]} {
@ -1696,7 +1689,7 @@ namespace eval punk {
set action ?mismatch-not-a-dict
} else {
set pairs [list]
dict for {k v} $leveldata {lappend pairs [list $k $v]}
tcl::dict::for {k v} $leveldata {lappend pairs [list $k $v]}
set assigned [lindex [list $pairs [unset pairs]] 0]
}
}
@ -4541,6 +4534,37 @@ namespace eval punk {
}
}
# ---------------------------
# commands that should be aliased in safe interps that need to use punk repl
#
proc get_repl_runid {} {
if {[interp issafe]} {
if {[info commands ::tsv::exists] eq ""} {
puts stderr "punk::get_repl_runid cannot operate directly in safe interp - install the appropriate punk aliases"
error "punk::get_repl_runid punk repl aliases not installed"
}
#if safe interp got here - there must presumably be a direct set of aliases on tsv::* commands
}
if {[tsv::exists repl runid]} {
return [tsv::get repl runid]
} else {
return 0
}
}
#ensure we don't get into loop in unknown when in safe interp - which won't have tsv
proc set_repl_last_unknown {args} {
if {[interp issafe]} {
if {[info commands ::tsv::set] eq ""} {
puts stderr "punk::set_repl_last_unknown cannot operate directly in safe interp - install an alias to tsv::set repl last_unknown"
return
}
#tsv::* somehow working - possibly custom aliases for tsv functionality ? review
}
tsv::set repl last_unknown {*}$args
}
# ---------------------------
proc configure_unknown {} {
#-----------------------------
#these are critical e.g core behaviour or important for repl displaying output correctly
@ -4557,7 +4581,8 @@ namespace eval punk {
package require base64
#set ::punk::last_run_display [list]
#set ::repl::last_unknown [lindex $args 0] ;#jn
tsv::set repl last_unknown [lindex $args 0] ;#REVIEW
#tsv::set repl last_unknown [lindex $args 0] ;#REVIEW
punk::set_repl_last_unknown [lindex $args 0]
}][info body ::unknown]
@ -6229,7 +6254,7 @@ namespace eval punk {
set script_extensions [list]
set extension_lookup [dict create]
dict for {lang langinfo} $scriptconfig {
tcl::dict::for {lang langinfo} $scriptconfig {
set extensions [dict get $langinfo extensions]
lappend script_extensions {*}$extensions
foreach e $extensions {

1539
src/modules/punk/ansi-999999.0a1.0.tm

File diff suppressed because it is too large Load Diff

297
src/modules/punk/args-999999.0a1.0.tm

@ -186,11 +186,19 @@
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#All ensemble commands are slower in a safe interp as they aren't compiled the same way
#https://core.tcl-lang.org/tcl/tktview/1095bf7f75
#as this is needed in safe interps too, and the long forms tcl::dict::for tcl::string::map are no slower in a normal interp - we use the long form here.
#(As at 2024-06 There are many tcl8.6/8.7 interps in use which are affected by this and it's unknown when/whether it will be fixed)
#ensembles: array binary chan clock dict encoding info namespace string
#possibly file too, although that is generally hidden/modified in a safe interp
#*** !doctools
#[subsection dependencies]
#[para] packages used by punk::args
#[list_begin itemized]
package require Tcl 8.6-
#*** !doctools
#[item] [package {Tcl 8.6-}]
@ -210,11 +218,11 @@ package require Tcl 8.6-
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args::class {
tcl::namespace::eval punk::args::class {
#*** !doctools
#[subsection {Namespace punk::args::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
@ -243,13 +251,13 @@ namespace eval punk::args::class {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args {
namespace export {[a-z]*}
tcl::namespace::eval punk::args {
tcl::namespace::export {[a-z]*}
variable argspec_cache
variable argspecs
variable id_counter
set argspec_cache [dict create]
set argspecs [dict create]
set argspec_cache [tcl::dict::create]
set argspecs [tcl::dict::create]
set id_counter 0
#*** !doctools
@ -265,12 +273,12 @@ namespace eval punk::args {
#review - check if there is a built-into-tcl way to do this quickly
#for now we will just key using the whole string
set cache_key $optionspecs
if {[dict exists $argspec_cache $cache_key]} {
return [dict get $argspec_cache $cache_key]
if {[tcl::dict::exists $argspec_cache $cache_key]} {
return [tcl::dict::get $argspec_cache $cache_key]
}
set optionspecs [string map [list \r\n \n] $optionspecs]
set optspec_defaults [dict create\
set optionspecs [tcl::string::map [list \r\n \n] $optionspecs]
set optspec_defaults [tcl::dict::create\
-type string\
-optional 1\
-allow_ansi 1\
@ -279,7 +287,7 @@ namespace eval punk::args {
-nocase 0\
-multiple 0\
]
set valspec_defaults [dict create\
set valspec_defaults [tcl::dict::create\
-type string\
-optional 0\
-allow_ansi 1\
@ -295,10 +303,10 @@ namespace eval punk::args {
#todo - detect if anything in the spec uses -allow_ansi 0, -validate_without_ansi 1 or -strip_ansi 1 and set a flag indicating if punk::ansi::ta::detect should be run on the argslist
set opt_required [list]
set val_required [list]
set arg_info [dict create]
set opt_defaults [dict create]
set arg_info [tcl::dict::create]
set opt_defaults [tcl::dict::create]
set opt_names [list] ;#defined opts
set val_defaults [dict create]
set val_defaults [tcl::dict::create]
set opt_solos [list]
#first process dashed and non-dashed argspecs without regard to whether non-dashed are at the beginning or end
set val_names [list]
@ -309,21 +317,21 @@ namespace eval punk::args {
set linelist [split $optionspecs \n]
set lastindent ""
foreach ln $linelist {
if {[string trim $ln] eq ""} {continue}
if {[tcl::string::trim $ln] eq ""} {continue}
regexp {(\s*).*} $ln _all lastindent
break ;#break at first non-empty
}
#puts "indent1:[ansistring VIEW $lastindent]"
set in_record 0
foreach rawline $linelist {
set recordsofar [string cat $linebuild $rawline]
if {![info complete $recordsofar]} {
set recordsofar [tcl::string::cat $linebuild $rawline]
if {![tcl::info::complete $recordsofar]} {
#append linebuild [string trimleft $rawline] \n
if {$in_record} {
if {[string length $lastindent]} {
if {[tcl::string::length $lastindent]} {
#trim only the whitespace corresponding to last indent - not all whitespace on left
if {[string first $lastindent $rawline] == 0} {
set trimmedline [string range $rawline [string length $lastindent] end]
if {[tcl::string::first $lastindent $rawline] == 0} {
set trimmedline [tcl::string::range $rawline [tcl::string::length $lastindent] end]
append linebuild $trimmedline \n
} else {
append linebuild $rawline \n
@ -340,10 +348,10 @@ namespace eval punk::args {
}
} else {
set in_record 0
if {[string length $lastindent]} {
if {[tcl::string::length $lastindent]} {
#trim only the whitespace corresponding to last indent - not all whitespace on left
if {[string first $lastindent $rawline] == 0} {
set trimmedline [string range $rawline [string length $lastindent] end]
if {[tcl::string::first $lastindent $rawline] == 0} {
set trimmedline [tcl::string::range $rawline [tcl::string::length $lastindent] end]
append linebuild $trimmedline
} else {
append linebuild $rawline
@ -361,19 +369,19 @@ namespace eval punk::args {
set val_max -1 ;#-1 for no limit
set spec_id ""
foreach ln $records {
set trimln [string trim $ln]
switch -- [string index $trimln 0] {
set trimln [tcl::string::trim $ln]
switch -- [tcl::string::index $trimln 0] {
"" - # {continue}
}
set linespecs [lassign $trimln argname]
if {$argname ne "*id" && [llength $linespecs] %2 != 0} {
error "punk::args::get_dict - bad optionspecs line for record '$argname' Remaining items on line must be in paired option-value format - received '$linespecs'"
}
set firstchar [string index $argname 0]
set secondchar [string index $argname 1]
set firstchar [tcl::string::index $argname 0]
set secondchar [tcl::string::index $argname 1]
if {$firstchar eq "*" && $secondchar ne "*"} {
set starspecs $linespecs
switch -- [string range $argname 1 end] {
switch -- [tcl::string::range $argname 1 end] {
id {
#id line must have single entry - a unique id assigned by the user - an id will be allocated if no id line present or the value is "auto"
if {[llength $starspecs] != 1} {
@ -398,10 +406,10 @@ namespace eval punk::args {
}
-minlen - -maxlen - -range - -choices - -choicelabels {
#review - only apply to certain types?
dict set optspec_defaults $k $v
tcl::dict::set optspec_defaults $k $v
}
-nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels {
dict unset optspec_defaults $k
tcl::dict::unset optspec_defaults $k
}
-type -
-optional -
@ -410,7 +418,7 @@ namespace eval punk::args {
-strip_ansi -
-multiple {
#allow overriding of defaults for options that occur later
dict set optspec_defaults $k $v
tcl::dict::set optspec_defaults $k $v
}
default {
error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts"
@ -431,17 +439,17 @@ namespace eval punk::args {
}
-minlen - -maxlen - -range - -choices - -choicelabels {
#review - only apply to certain types?
dict set valspec_defaults $k $v
tcl::dict::set valspec_defaults $k $v
}
-nominlen - -nomaxlen - -norange - -nochoices - -nochoicelabels {
dict unset valspec_defaults $k
tcl::dict::unset valspec_defaults $k
}
-type -
-allow_ansi -
-validate_without_ansi -
-strip_ansi -
-multiple {
dict set valspec_defaults $k $v
tcl::dict::set valspec_defaults $k $v
}
default {
error "punk::args::Get_argspecs - unrecognised key '$k' in *opts line. Known keys: -anyopts"
@ -457,16 +465,16 @@ namespace eval punk::args {
continue
} elseif {$firstchar eq "-"} {
set argspecs $linespecs
dict set argspecs -ARGTYPE option
tcl::dict::set argspecs -ARGTYPE option
lappend opt_names $argname
set is_opt 1
} else {
if {$firstchar eq "*"} {
#allow basic ** escaping for literal argname that begins with *
set argname [string range $argname 1 end]
set argname [tcl::string::range $argname 1 end]
}
set argspecs $linespecs
dict set argspecs -ARGTYPE value
tcl::dict::set argspecs -ARGTYPE value
lappend val_names $argname
set is_opt 0
}
@ -478,20 +486,20 @@ namespace eval punk::args {
switch -- $spec {
-type {
#normalize here so we don't have to test during actual args parsing in main function
switch -- [string tolower $specval] {
switch -- [tcl::string::tolower $specval] {
int - integer {
dict set merged -type int
tcl::dict::set merged -type int
}
bool - boolean {
dict set merged -type bool
tcl::dict::set merged -type bool
}
char - character {
dict set merged -type char
tcl::dict::set merged -type char
}
"" - none {
if {$is_opt} {
dict set merged -type none
dict set merged -default 0 ;#-default 0 can still be overridden if -default appears after -type - we'll allow it.
tcl::dict::set merged -type none
tcl::dict::set merged -default 0 ;#-default 0 can still be overridden if -default appears after -type - we'll allow it.
lappend opt_solos $argname
} else {
#-solo only valid for flags
@ -499,12 +507,12 @@ namespace eval punk::args {
}
}
default {
dict set merged -type [string tolower $specval]
tcl::dict::set merged -type [tcl::string::tolower $specval]
}
}
}
-default - -solo - -range - -choices - -choicelabels - -minlen - -maxlen - -nocase - -optional - -multiple - -validate_without_ansi - -allow_ansi - -strip_ansi - -help - -ARGTYPE {
dict set merged $spec $specval
tcl::dict::set merged $spec $specval
}
default {
set known_argspecs [list -default -type -range -choices -nocase -optional -multiple -validate_without_ansi -allow_ansi -strip_ansi -help]
@ -514,40 +522,40 @@ namespace eval punk::args {
}
set argspecs $merged
#if {$is_opt} {
set argchecks [dict remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen
set argchecks [tcl::dict::remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen
#} else {
# set argchecks [dict remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen
# set argchecks [tcl::dict::remove $argspecs -type -default -multiple -strip_ansi -validate_without_ansi -allow_ansi] ;#leave things like -range -minlen
#}
dict set arg_info $argname $argspecs
dict set arg_checks $argname $argchecks
if {![dict get $argspecs -optional]} {
tcl::dict::set arg_info $argname $argspecs
tcl::dict::set arg_checks $argname $argchecks
if {![tcl::dict::get $argspecs -optional]} {
if {$is_opt} {
lappend opt_required $argname
} else {
lappend val_required $argname
}
}
if {[dict exists $argspecs -default]} {
if {[tcl::dict::exists $argspecs -default]} {
if {$is_opt} {
dict set opt_defaults $argname [dict get $argspecs -default]
tcl::dict::set opt_defaults $argname [tcl::dict::get $argspecs -default]
} else {
dict set val_defaults $argname [dict get $argspecs -default]
tcl::dict::set val_defaults $argname [tcl::dict::get $argspecs -default]
}
}
}
#confirm any valnames before last don't have -multiple key
foreach valname [lrange $val_names 0 end-1] {
if {[dict get $arg_info $valname -multiple]} {
if {[tcl::dict::get $arg_info $valname -multiple]} {
error "bad key -multiple on argument spec for '$valname'. Only the last value argument specification can be marked -multiple"
}
}
if {$spec_id eq "" || [string tolower $spec_id] eq "auto"} {
if {$spec_id eq "" || [tcl::string::tolower $spec_id] eq "auto"} {
variable id_counter
set spec_id "autoid_[incr id_counter]"
}
set result [dict create\
set result [tcl::dict::create\
id $spec_id\
arg_info $arg_info\
arg_checks $arg_checks\
@ -566,28 +574,28 @@ namespace eval punk::args {
valspec_defaults $valspec_defaults\
proc_info $proc_info\
]
dict set argspec_cache $cache_key $result
dict set argspecs $spec_id $optionspecs
tcl::dict::set argspec_cache $cache_key $result
tcl::dict::set argspecs $spec_id $optionspecs
return $result
}
proc get_spec {id} {
variable argspecs
if {[dict exists $argspecs $id]} {
return [dict get $argspecs $id]
if {[tcl::dict::exists $argspecs $id]} {
return [tcl::dict::get $argspecs $id]
}
return
}
proc get_spec_ids {{match *}} {
variable argspecs
return [dict keys $argspecs $match]
return [tcl::dict::keys $argspecs $match]
}
#for use within get_dict only
#This mechanism gets less-than-useful results for oo methods
#e.g {$obj}
proc Get_caller {} {
set cmdinfo [dict get [info frame -3] cmd]
set cmdinfo [tcl::dict::get [tcl::info::frame -3] cmd]
#puts "-->$cmdinfo"
set caller [regexp -inline {\S+} $cmdinfo]
if {$caller eq "namespace"} {
@ -681,7 +689,7 @@ namespace eval punk::args {
set argspecs [Get_argspecs $optionspecs]
dict with argspecs {} ;#turn keys into vars
tcl::dict::with argspecs {} ;#turn keys into vars
#puts "-arg_info->$arg_info"
set flagsreceived [list]
@ -692,12 +700,12 @@ namespace eval punk::args {
set maxidx [expr {[llength $arglist]-1}]
for {set i 0} {$i <= $maxidx} {incr i} {
set a [lindex $arglist $i]
if {![string match -* $a]} {
if {![tcl::string::match -* $a]} {
#we can't treat as first positional arg - as it comes before the eopt indicator --
error "punk::args::get_dict bad options for [Get_caller]. Expected flag (leading -) at position $i got:$rawargs"
}
#TODO!
if {[dict get $arg_info $a -type] ne "none"} {
if {[tcl::dict::get $arg_info $a -type] ne "none"} {
if {[incr i] > $maxidx} {
error "punk::args::get_dict bad options for [Get_caller]. No value supplied for last option $a which is not marked with -solo 1"
}
@ -714,20 +722,20 @@ namespace eval punk::args {
set maxidx [expr {[llength $rawargs]-1}]
for {set i 0} {$i <= $maxidx} {incr i} {
set a [lindex $rawargs $i]
if {![string match -* $a]} {
if {![tcl::string::match -* $a]} {
#assume beginning of positional args
incr i -1
break
}
if {![catch {tcl::prefix match -message "options for %caller%. Unexpected option" $opt_names $a } fullopt]} {
if {[dict get $arg_info $fullopt -type] ne "none"} {
if {[tcl::dict::get $arg_info $fullopt -type] ne "none"} {
#non-solo
set flagval [lindex $rawargs $i+1]
if {[dict get $arg_info $fullopt -multiple]} {
dict lappend opts $fullopt $flagval
tcl::dict::lappend opts $fullopt $flagval
} else {
dict set opts $fullopt $flagval
tcl::dict::set opts $fullopt $flagval
}
#incr i to skip flagval
if {[incr i] > $maxidx} {
@ -735,15 +743,15 @@ namespace eval punk::args {
}
} else {
#type none (solo-flag)
if {[dict get $arg_info $fullopt -multiple]} {
if {[dict get $opts $fullopt] == 0} {
if {[tcl::dict::get $arg_info $fullopt -multiple]} {
if {[tcl::dict::get $opts $fullopt] == 0} {
#review - what if default at time opt was specified is not zero? we will end up with more items in the list than the number of times the flag was specified
dict set opts $fullopt 1
tcl::dict::set opts $fullopt 1
} else {
dict lappend opts $fullopt 1
tcl::dict::lappend opts $fullopt 1
}
} else {
dict set opts $fullopt 1
tcl::dict::set opts $fullopt 1
}
}
lappend flagsreceived $fullopt ;#dups ok
@ -751,12 +759,12 @@ namespace eval punk::args {
if {$opt_any} {
set newval [lindex $rawargs $i+1]
#opt was unspecified but is allowed due to *opt -any 1 - 'adhoc/passthrough' option
dict set arg_info $a $optspec_defaults ;#use default settings for unspecified opt
if {[dict get $arg_info $a -type] ne "none"} {
if {[dict get $arg_info $a -multiple]} {
dict lappend opts $a $newval
tcl::dict::set arg_info $a $optspec_defaults ;#use default settings for unspecified opt
if {[tcl::dict::get $arg_info $a -type] ne "none"} {
if {[tcl::dict::get $arg_info $a -multiple]} {
tcl::dict::lappend opts $a $newval
} else {
dict set opts $a $newval
tcl::dict::set opts $a $newval
}
lappend flagsreceived $a ;#adhoc flag as supplied
if {[incr i] > $maxidx} {
@ -764,19 +772,19 @@ namespace eval punk::args {
}
} else {
#review -we can't provide a way to allow unspecified -type none flags through reliably/unambiguously unless all remaining unspecified options are -type none
if {[dict get $arg_info $a -multiple]} {
if {![dict exists $opts $a]} {
dict set opts $a 1
if {[tcl::dict::get $arg_info $a -multiple]} {
if {![tcl::dict::exists $opts $a]} {
tcl::dict::set opts $a 1
} else {
dict lappend opts $a 1
tcl::dict::lappend opts $a 1
}
} else {
dict set opts $a 1
tcl::dict::set opts $a 1
}
}
} else {
#delay Get_caller so only called in the unhappy path
set errmsg [string map [list %caller% [Get_caller]] $fullopt]
set errmsg [tcl::string::map [list %caller% [Get_caller]] $fullopt]
error $errmsg
}
}
@ -800,20 +808,20 @@ namespace eval punk::args {
break
}
if {$valname ne ""} {
if {[dict get $arg_info $valname -multiple]} {
dict lappend values_dict $valname $val
if {[tcl::dict::get $arg_info $valname -multiple]} {
tcl::dict::lappend values_dict $valname $val
set in_multiple $valname
} else {
dict set values_dict $valname $val
tcl::dict::set values_dict $valname $val
}
lappend valnames_received $valname
} else {
if {$in_multiple ne ""} {
dict lappend values_dict $in_multiple $val
tcl::dict::lappend values_dict $in_multiple $val
#name already seen
} else {
dict set values_dict $validx $val
dict set arg_info $validx $valspec_defaults
tcl::dict::set values_dict $validx $val
tcl::dict::set arg_info $validx $valspec_defaults
lappend valnames_received $validx
}
}
@ -844,6 +852,11 @@ namespace eval punk::args {
#The aim is to allow a wrapper function to specify a default value for an option (and/or other changes/restrictions to a particular option or two) - without having to re-specify all the options for the underlying function.
#without full respecification in the wrapper - we can't know that a supplied prefix is unambiguous at the next level
#For this reason we need to pass on full-length opts for any defined options in the wrapper even if anyopts is true
#safe interp note - cannot avoid struct::set difference ensemble as it could be c or tcl implementation and we don't have an option to call directly?
#example timing difference:
#struct::set difference {x} {a b}
#normal interp 0.18 u2 vs save interp 9.4us
if {[llength [set missing [struct::set difference $opt_required $flagsreceived]]]} {
error "Required option missing for [Get_caller]. missing flags $missing are marked with -optional false - so must be present in full-length form"
}
@ -853,24 +866,24 @@ namespace eval punk::args {
#todo - allow defaults outside of choices/ranges
#check types,ranges,choices
set opts_and_values [dict merge $opts $values_dict]
#set combined_defaults [dict merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash
set opts_and_values [tcl::dict::merge $opts $values_dict]
#set combined_defaults [tcl::dict::merge $val_defaults $opt_defaults] ;#can be no key collisions - we don't allow a value key beginning with dash - opt names must begin with dash
#puts "---opts_and_values:$opts_and_values"
#puts "---arg_info:$arg_info"
dict for {argname v} $opts_and_values {
set thisarg [dict get $arg_info $argname]
#set thisarg_keys [dict keys $thisarg]
set thisarg_checks [dict get $arg_checks $argname]
set is_multiple [dict get $thisarg -multiple]
set is_allow_ansi [dict get $thisarg -allow_ansi]
set is_validate_without_ansi [dict get $thisarg -validate_without_ansi]
set is_strip_ansi [dict get $thisarg -strip_ansi]
set has_default [dict exists $thisarg -default]
tcl::dict::for {argname v} $opts_and_values {
set thisarg [tcl::dict::get $arg_info $argname]
#set thisarg_keys [tcl::dict::keys $thisarg]
set thisarg_checks [tcl::dict::get $arg_checks $argname]
set is_multiple [tcl::dict::get $thisarg -multiple]
set is_allow_ansi [tcl::dict::get $thisarg -allow_ansi]
set is_validate_without_ansi [tcl::dict::get $thisarg -validate_without_ansi]
set is_strip_ansi [tcl::dict::get $thisarg -strip_ansi]
set has_default [tcl::dict::exists $thisarg -default]
if {$has_default} {
set defaultval [dict get $thisarg -default]
set defaultval [tcl::dict::get $thisarg -default]
}
set type [dict get $thisarg -type]
set has_choices [dict exists $thisarg -choices]
set type [tcl::dict::get $thisarg -type]
set has_choices [tcl::dict::exists $thisarg -choices]
if {$is_multiple} {
set vlist $v
@ -916,20 +929,22 @@ namespace eval punk::args {
switch -- $type {
any {}
string {
if {[dict size $thisarg_checks]} {
if {[tcl::dict::size $thisarg_checks]} {
foreach e_check $vlist_check {
dict for {checkopt checkval} $thisarg_checks {
#safe jumptable test
#dict for {checkopt checkval} $thisarg_checks {}
tcl::dict::for {checkopt checkval} $thisarg_checks {
switch -- $checkopt {
-minlen {
# -1 for disable is as good as zero
if {[string length $e_check] < $checkval} {
error "Option $argname for [Get_caller] requires string with -minlen $checkval. Received len:[string length $e_check] value:'$e_check'"
if {[tcl::string::length $e_check] < $checkval} {
error "Option $argname for [Get_caller] requires string with -minlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'"
}
}
-maxlen {
if {$checkval ne "-1"} {
if {[string length $e_check] > $checkval} {
error "Option $argname for [Get_caller] requires string with -maxlen $checkval. Received len:[string length $e_check] value:'$e_check'"
if {[tcl::string::length $e_check] > $checkval} {
error "Option $argname for [Get_caller] requires string with -maxlen $checkval. Received len:[tcl::string::length $e_check] value:'$e_check'"
}
}
}
@ -942,10 +957,10 @@ namespace eval punk::args {
package require ansi
}
int {
if {[dict exists $thisarg -range]} {
lassign [dict get $thisarg -range] low high
if {[tcl::dict::exists $thisarg -range]} {
lassign [tcl::dict::get $thisarg -range] low high
foreach e $vlist e_check $vlist_check {
if {![string is integer -strict $e_check]} {
if {![tcl::string::is integer -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e'"
}
if {$e_check < $low || $e_check > $high} {
@ -954,7 +969,7 @@ namespace eval punk::args {
}
} else {
foreach e_check $vlist_check {
if {![string is integer -strict $e_check]} {
if {![tcl::string::is integer -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'integer'. Received: '$e_check'"
}
}
@ -962,11 +977,13 @@ namespace eval punk::args {
}
double {
foreach e $vlist e_check $vlist_check {
if {![string is double -strict $e_check]} {
if {![tcl::string::is double -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'double'. Received: '$e'"
}
if {[dict size $thisarg_checks]} {
dict for {checkopt checkval} $thisarg_checks {
if {[tcl::dict::size $thisarg_checks]} {
#safe jumptable test
#dict for {checkopt checkval} $thisarg_checks {}
tcl::dict::for {checkopt checkval} $thisarg_checks {
switch -- $checkopt {
-range {
#todo - small-value double comparisons with error-margin? review
@ -982,7 +999,7 @@ namespace eval punk::args {
}
bool {
foreach e_check $vlist_check {
if {![string is boolean -strict $e_check]} {
if {![tcl::string::is boolean -strict $e_check]} {
error "Option $argname for [Get_caller] requires type 'boolean'. Received: '$e_check'"
}
}
@ -1001,7 +1018,7 @@ namespace eval punk::args {
wordchar -
xdigit {
foreach e $vlist e_check $vlist_check {
if {![string is $type $e_check]} {
if {![tcl::string::is $type $e_check]} {
error "Option $argname for [Get_caller] requires type '$type'. Received: '$e'"
}
}
@ -1011,7 +1028,7 @@ namespace eval punk::args {
existingfile -
existingdirectory {
foreach e $vlist e_check $vlist_check {
if {!([string length $e_check]>0 && ![regexp {[\"*?<>\;]} $e_check])} {
if {!([tcl::string::length $e_check]>0 && ![regexp {[\"*?<>\;]} $e_check])} {
#what about special file names e.g on windows NUL ?
error "Option $argname for [Get_caller] requires type '$type'. Received: '$e' which doesn't look like it could be a file or directory"
}
@ -1032,7 +1049,7 @@ namespace eval punk::args {
}
char {
foreach e $vlist e_check $vlist_check {
if {[string length $e_check] != 1} {
if {[tcl::string::length $e_check] != 1} {
error "Option $argname for [Get_caller] requires type 'character'. Received: '$e' which is not a single character"
}
}
@ -1040,13 +1057,13 @@ namespace eval punk::args {
}
if {$has_choices} {
#todo -choicelabels
set choices [dict get $thisarg -choices]
set nocase [dict get $thisarg -nocase]
set choices [tcl::dict::get $thisarg -choices]
set nocase [tcl::dict::get $thisarg -nocase]
foreach e $vlist e_check $vlist_check {
if {$nocase} {
set casemsg "(case insensitive)"
set choices_test [string tolower $choices]
set v_test [string tolower $e_check]
set choices_test [tcl::string::tolower $choices]
set v_test [tcl::string::tolower $e_check]
} else {
set casemsg "(case sensitive)"
set v_test $e_check
@ -1060,24 +1077,24 @@ namespace eval punk::args {
}
if {$is_strip_ansi} {
set stripped_list [lmap e $vlist {punk::ansi::stripansi $e}] ;#no faster or slower, but more concise than foreach
if {[dict get $thisarg -multiple]} {
if {[dict get $thisarg -ARGTYPE] eq "option"} {
dict set opts $argname $stripped_list
if {[tcl::dict::get $thisarg -multiple]} {
if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} {
tcl::dict::set opts $argname $stripped_list
} else {
dict set values_dict $argname $stripped_list
tcl::dict::set values_dict $argname $stripped_list
}
} else {
if {[dict get $thisarg -ARGTYPE] eq "option"} {
dict set opts $argname [lindex $stripped_list 0]
if {[tcl::dict::get $thisarg -ARGTYPE] eq "option"} {
tcl::dict::set opts $argname [lindex $stripped_list 0]
} else {
dict set values_dict [lindex $stripped_list 0]
tcl::dict::set values_dict [lindex $stripped_list 0]
}
}
}
}
#maintain order of opts $opts values $values as caller may use lassign.
return [dict create opts $opts values $values_dict]
return [tcl::dict::create opts $opts values $values_dict]
}
#proc sample1 {p1 args} {
@ -1099,9 +1116,9 @@ namespace eval punk::args {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args::lib {
namespace export *
namespace path [namespace parent]
tcl::namespace::eval punk::args::lib {
tcl::namespace::export *
tcl::namespace::path [tcl::namespace::parent]
#*** !doctools
#[subsection {Namespace punk::args::lib}]
#[para] Secondary functions that are part of the API
@ -1126,7 +1143,7 @@ namespace eval punk::args::lib {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval punk::args::system {
tcl::namespace::eval punk::args::system {
#*** !doctools
#[subsection {Namespace punk::args::system}]
#[para] Internal functions that are not part of the API
@ -1136,7 +1153,7 @@ namespace eval punk::args::system {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::args [namespace eval punk::args {
package provide punk::args [tcl::namespace::eval punk::args {
variable pkg punk::args
variable version
set version 999999.0a1.0

118
src/modules/punk/assertion-999999.0a1.0.tm

@ -69,11 +69,11 @@ package require Tcl 8.6-
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::assertion::class {
tcl::namespace::eval punk::assertion::class {
#*** !doctools
#[subsection {Namespace punk::assertion::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
if {[tcl::info::commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
@ -100,16 +100,16 @@ namespace eval punk::assertion::class {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#keep 2 namespaces for assertActive and assertInactive so there is introspection available via namespace origin
namespace eval punk::assertion::primary {
namespace export *
tcl::namespace::eval punk::assertion::primary {
#tcl::namespace::export {[a-z]*}
tcl::namespace::export assertActive assertInactive
proc assertActive {expr args} {
set code [catch {uplevel 1 [list expr $expr]} res]
if {$code} {
return -code $code $res
}
if {![string is boolean -strict $res]} {
if {![tcl::string::is boolean -strict $res]} {
return -code error "invalid boolean expression: $expr"
}
@ -124,28 +124,40 @@ namespace eval punk::assertion::primary {
upvar ::punk::assertion::CallbackCmd CallbackCmd
# Might want to catch this
namespace eval :: $CallbackCmd [list $msg]
tcl::namespace::eval :: $CallbackCmd [list $msg]
}
proc assertInactive args {}
}
namespace eval punk::assertion::secondary {
namespace export *
tcl::namespace::eval punk::assertion::secondary {
tcl::namespace::export *
#we need to actually define these procs here, (not import then re-export) - or namespace origin will report the original source namespace - which isn't what we want.
proc assertActive {expr args} [info body ::punk::assertion::primary::assertActive]
proc assertActive {expr args} [tcl::info::body ::punk::assertion::primary::assertActive]
proc assertInactive args {}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::assertion {
tcl::namespace::eval punk::assertion {
variable CallbackCmd [list return -code error]
namespace import ::punk::assertion::primary::assertActive
#puts --------AAA
#*very* slow in safe interp - why?
#tcl::namespace::import ::punk::assertion::primary::assertActive
proc do_ns_import {} {
uplevel 1 [list tcl::namespace::import ::punk::assertion::primary::assertActive]
}
do_ns_import
#puts --------BBB
rename assertActive assert
namespace export *
}
tcl::namespace::eval punk::assertion {
tcl::namespace::export *
#variable xyz
#*** !doctools
@ -177,7 +189,7 @@ namespace eval punk::assertion {
set n [llength $args]
if {$n > 1} {
return -code error "wrong # args: should be\
\"[lindex [info level 0] 0] ?command?\""
\"[lindex [tcl::info::level 0] 0] ?command?\""
}
if {$n} {
set cb [lindex $args 0]
@ -187,41 +199,41 @@ namespace eval punk::assertion {
}
proc active {{on_off ""}} {
set nscaller [uplevel 1 [list namespace current]]
set which_assert [namespace eval $nscaller {namespace which assert}]
set nscaller [uplevel 1 [list tcl::namespace::current]]
set which_assert [tcl::namespace::eval $nscaller {tcl::namespace::which assert}]
#puts "nscaller:'$nscaller'"
#puts "which_assert: $which_assert"
if {$on_off eq ""} {
if {$which_assert eq ""} {return 0}
set assertorigin [namespace origin $which_assert]
set assertorigin [tcl::namespace::origin $which_assert]
#puts "ns which assert: $which_assert"
#puts "ns origin assert: $assertorigin"
return [expr {"assertActive" eq [namespace tail $assertorigin]}]
return [expr {"assertActive" eq [tcl::namespace::tail $assertorigin]}]
}
if {![string is boolean -strict $on_off]} {
if {![tcl::string::is boolean -strict $on_off]} {
error "invalid boolean value : $on_off"
} else {
set info_command [namespace eval $nscaller {info commands assert}]
set info_command [tcl::namespace::eval $nscaller {tcl::info::commands assert}]
if {$on_off} {
#Enable it in calling namespace
if {"assert" eq $info_command} {
#There is an assert command reachable - due to namespace path etc, it could be in another namespace entirely - (not necessarily in an ancestor namespace of the namespace's tree structure)
if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} {
namespace eval $nscaller {
set assertorigin [namespace origin assert]
tcl::namespace::eval $nscaller {
set assertorigin [tcl::namespace::origin assert]
set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin]
switch -- $assertorigin_ns {
::punk::assertion {
#original import - switch to primary origin
rename assert {}
namespace import ::punk::assertion::primary::assertActive
tcl::namespace::import ::punk::assertion::primary::assertActive
rename assertActive assert
}
::punk::assertion::primary - ::punk::assertion::secondary {
#keep using from same origin ns
rename assert {}
namespace import ${assertorigin_ns}::assertActive
tcl::namespace::import ${assertorigin_ns}::assertActive
rename assertActive assert
}
default {
@ -232,10 +244,10 @@ namespace eval punk::assertion {
return 1
} else {
#assert is available, but isn't in the calling namespace - we should enable it in a way that is distinguishable from case where assert was explicitly imported to this namespace
namespace eval $nscaller {
set assertorigin [namespace origin assert]
if {[string match ::punk::assertion::* $assertorigin]} {
namespace import ::punk::assertion::secondary::assertActive
tcl::namespace::eval $nscaller {
set assertorigin [tcl::namespace::origin assert]
if {[tcl::string::match ::punk::assertion::* $assertorigin]} {
tcl::namespace::import ::punk::assertion::secondary::assertActive
rename assertActive assert
} else {
error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin"
@ -254,20 +266,20 @@ namespace eval punk::assertion {
if {"assert" eq $info_command} {
if {$which_assert eq [punk::assertion::system::nsjoin ${nscaller} assert]} {
#assert is present in callers NS
namespace eval $nscaller {
set assertorigin [namespace origin assert]
tcl::namespace::eval $nscaller {
set assertorigin [tcl::namespace::origin assert]
set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin]
switch -glob -- $assertorigin_ns {
::punk::assertion {
#original import
rename assert {}
namespace import punk::assertion::primary::assertInactive
tcl::namespace::import punk::assertion::primary::assertInactive
rename assertInactive assert
}
::punk::assertion::primary - ::punk::assertion::secondary {
#keep using from same origin ns
rename assert {}
namespace import ${assertorigin_ns}::assertInactive
tcl::namespace::import ${assertorigin_ns}::assertInactive
rename assertInactive assert
}
default {
@ -278,11 +290,11 @@ namespace eval punk::assertion {
return 0
} else {
#assert not present in callers NS - first install of secondary (if assert is from punk::assertion::*)
namespace eval $nscaller {
set assertorigin [namespace origin assert]
tcl::namespace::eval $nscaller {
set assertorigin [tcl::namespace::origin assert]
set assertorigin_ns [punk::assertion::system::nsprefix $assertorigin]
if {[string match ::punk::assertion::* $assertorigin]} {
namespace import ::punk::assertion::secondary::assertInactive
if {[tcl::string::match ::punk::assertion::* $assertorigin]} {
tcl::namespace::import ::punk::assertion::secondary::assertInactive
rename assertInactive assert
} else {
error "The reachable assert command at '$which_assert' is not from punk::assertion package. Import punk::assertion::assert - or use the enable mechanism from the package associated with $assertorigin"
@ -310,9 +322,9 @@ namespace eval punk::assertion {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::assertion::lib {
namespace export *
namespace path [namespace parent]
tcl::namespace::eval punk::assertion::lib {
tcl::namespace::export *
tcl::namespace::path [tcl::namespace::parent]
#*** !doctools
#[subsection {Namespace punk::assertion::lib}]
#[para] Secondary functions that are part of the API
@ -337,7 +349,7 @@ namespace eval punk::assertion::lib {
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval punk::assertion::system {
tcl::namespace::eval punk::assertion::system {
#*** !doctools
#[subsection {Namespace punk::assertion::system}]
#[para] Internal functions that are not part of the API
@ -346,33 +358,33 @@ namespace eval punk::assertion::system {
#nsprefix/nstail are string functions - they do not concern themselves with what namespaces are present in the system
proc nsprefix {{nspath {}}} {
#normalize the common case of ::::
set nspath [string map [list :::: ::] $nspath]
set rawprefix [string range $nspath 0 end-[string length [nstail $nspath]]]
set nspath [tcl::string::map [list :::: ::] $nspath]
set rawprefix [tcl::string::range $nspath 0 end-[tcl::string::length [nstail $nspath]]]
if {$rawprefix eq "::"} {
return $rawprefix
} else {
if {[string match *:: $rawprefix]} {
return [string range $rawprefix 0 end-2]
if {[tcl::string::match *:: $rawprefix]} {
return [tcl::string::range $rawprefix 0 end-2]
} else {
return $rawprefix
}
#return [string trimright $rawprefix :]
#return [tcl::string::trimright $rawprefix :]
}
}
#see also punk::ns - keep in sync
proc nstail {nspath args} {
#normalize the common case of ::::
set nspath [string map [list :::: ::] $nspath]
set mapped [string map [list :: \u0FFF] $nspath]
set nspath [tcl::string::map [list :::: ::] $nspath]
set mapped [tcl::string::map [list :: \u0FFF] $nspath]
set parts [split $mapped \u0FFF]
set defaults [list -strict 0]
set opts [dict merge $defaults $args]
set strict [dict get $opts -strict]
set opts [tcl::dict::merge $defaults $args]
set strict [tcl::dict::get $opts -strict]
if {$strict} {
foreach p $parts {
if {[string match :* $p]} {
if {[tcl::string::match :* $p]} {
error "nstail unpaired colon ':' in $nspath"
}
}
@ -381,7 +393,7 @@ namespace eval punk::assertion::system {
return [lindex $parts end]
}
proc nsjoin {prefix name} {
if {[string match ::* $name]} {
if {[tcl::string::match ::* $name]} {
if {"$prefix" ne ""} {
error "nsjoin: won't join non-empty prefix to absolute namespace path '$name'"
}
@ -400,7 +412,7 @@ namespace eval punk::assertion::system {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::assertion [namespace eval punk::assertion {
package provide punk::assertion [tcl::namespace::eval punk::assertion {
variable pkg punk::assertion
variable version
set version 999999.0a1.0

11
src/modules/punk/cap/handlers/templates-999999.0a1.0.tm

@ -85,8 +85,19 @@ namespace eval punk::cap::handlers::templates {
module {
set provide_statement [package ifneeded $pkg [package require $pkg]]
set tmfile [lindex $provide_statement end]
if {[interp issafe]} {
#default safe interp can't use file exists/normalize etc.. but safe interp may have a policy/alias set allowing file access to certain paths - so test if file exists is usable
if {[catch {file exists $tmfile} tm_exists]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING (expected in most safe interps) - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
flush stderr
return 0
}
} else {
set tm_exists [file exists $tmfile]
}
if {![file exists $tmfile]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
flush stderr
return 0
}

994
src/modules/punk/char-999999.0a1.0.tm

File diff suppressed because it is too large Load Diff

65
src/modules/punk/config-0.1.tm

@ -1,10 +1,5 @@
package provide punk::config [namespace eval punk::config {
variable version
set version 0.1
}]
namespace eval punk::config {
tcl::namespace::eval punk::config {
variable loaded
variable startup ;#include env overrides
variable running
@ -33,38 +28,42 @@ namespace eval punk::config {
#defaults
dict set startup configset .punkshell
dict set startup exec_unknown true ;#whether to use exec instead of experimental shellfilter::run
#dict set startup color_stdout [list cyan bold] ;#not a good idea to default
dict set startup color_stdout [list]
tcl::dict::set startup configset .punkshell
tcl::dict::set startup exec_unknown true ;#whether to use exec instead of experimental shellfilter::run
#tcl::dict::set startup color_stdout [list cyan bold] ;#not a good idea to default
tcl::dict::set startup color_stdout [list]
#This wraps the stderr stream as it comes in with Ansi - probably best to default to empty.. but it's useful.
dict set startup color_stderr [list red bold]
tcl::dict::set startup color_stderr [list red bold]
dict set startup syslog_stdout "127.0.0.1:514"
dict set startup syslog_stderr "127.0.0.1:514"
dict set startup syslog_active 0
tcl::dict::set startup syslog_stdout "127.0.0.1:514"
tcl::dict::set startup syslog_stderr "127.0.0.1:514"
tcl::dict::set startup syslog_active 0
#default file logs to logs folder at same location as exe if writable, or empty string
dict set startup logfile_stdout ""
dict set startup logfile_stderr ""
set exename [info nameofexecutable]
tcl::dict::set startup logfile_stdout ""
tcl::dict::set startup logfile_stderr ""
set exename ""
catch {
#catch for safe interps
#safe base will return empty string, ordinary safe interp will raise error
set exename [tcl::info::nameofexecutable]
}
if {$exename ne ""} {
set exefolder [file dirname [info nameofexecutable]]
set exefolder [file dirname $exename]
set log_folder $exefolder/logs
dict set startup scriptlib $exefolder/scriptlib
dict set startup apps $exefolder/../../punkapps
tcl::dict::set startup scriptlib $exefolder/scriptlib
tcl::dict::set startup apps $exefolder/../../punkapps
if {[file exists $log_folder]} {
if {[file isdirectory $log_folder] && [file writable $log_folder]} {
dict set startup logfile_stdout $log_folder/repl-exec-stdout.txt
dict set startup logfile_stderr $log_folder/repl-exec-stderr.txt
tcl::dict::set startup logfile_stdout $log_folder/repl-exec-stdout.txt
tcl::dict::set startup logfile_stderr $log_folder/repl-exec-stderr.txt
}
}
} else {
#probably a safe interp - which cannot access info nameofexecutable even if access given to the location via punk::island
#review - todo?
dict set startup scriptlib ""
dict set startup apps ""
tcl::dict::set startup scriptlib ""
tcl::dict::set startup apps ""
}
@ -95,16 +94,20 @@ namespace eval punk::config {
set f [set ::env($evar)]
if {$f ne "default"} {
#e.g PUNK_SCRIPTLIB -> scriptlib
set varname [string tolower [string range $evar 5 end]]
dict set startup $varname $f
set varname [tcl::string::tolower [tcl::string::range $evar 5 end]]
tcl::dict::set startup $varname $f
}
}
}
unset -nocomplain evar
unset -nocomplain vars
set running [dict create]
set running [dict merge $running $startup]
set running [tcl::dict::create]
set running [tcl::dict::merge $running $startup]
}
package provide punk::config [tcl::namespace::eval punk::config {
variable version
set version 0.1
}]

501
src/modules/punk/experiment-999999.0a1.0.tm

@ -0,0 +1,501 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'pmix make' or bin/punkmake to update from <pkg>-buildversion.txt
# module template: shellspy/src/decktemplates/vendor/punk/modules/template_module-0.0.2.tm
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2024
#
# @@ Meta Begin
# Application punk::experiment 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin shellspy_module_punk::experiment 0 999999.0a1.0]
#[copyright "2024"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require punk::experiment]
#[keywords module]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of punk::experiment
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by punk::experiment
#[list_begin itemized]
package require Tcl 8.6-
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::experiment::class {
#*** !doctools
#[subsection {Namespace punk::experiment::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::experiment {
namespace export {[a-z]*} ;# Convention: export all lowercase
#variable xyz
#*** !doctools
#[subsection {Namespace punk::experiment}]
#[para] Core API functions for punk::experiment
#[list_begin definitions]
#proc sample1 {p1 n args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [arg n] [opt {option value...}]]
# #[para]Description of sample1
# #[para] Arguments:
# # [list_begin arguments]
# # [arg_def tring p1] A description of string argument p1.
# # [arg_def integer n] A description of integer argument n.
# # [list_end]
# return "ok"
#}
variable o_opts_table [dict create\
]
variable o_opts_table_defaults [dict create\
-test 1\
-test2 etc\
-test3 333\
-test4 444\
]
set topt_keys [dict keys $o_opts_table_defaults]
set topt_switchkeys [list -test - -test2 - -test3 - -test4]
proc configure args [string map [list %topt_keys% $topt_keys %topt_switchkeys% $topt_switchkeys] {
variable o_opts_table
variable o_opts_table_defaults
if {![llength $args]} {
return $o_opts_table
}
if {[llength $args] == 1} {
if {[lindex $args 0] in [list %topt_keys%]} {
#query single option
set k [lindex $args 0]
set val [dict get $o_opts_table $k]
set returndict [dict create option $k value $val ansireset "\x1b\[m"]
set infodict [dict create]
switch -- $k {
-ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder_body - -ansiborder_footer {
dict set infodict debug [ansistring VIEW $val]
}
-framemap_body - -framemap_header - -framelimits_body - -framelimits_header {
dict set returndict effective [dict get $o_opts_table_effective $k]
}
}
dict set returndict info $infodict
return $returndict
#return [dict create option $k value $val ansireset "\x1b\[m" info $infodict]
} else {
error "textblock::table configure - unrecognised option '[lindex $args 0]'. Known values [dict keys $o_opts_table_defaults]"
}
}
if {[llength $args] %2 != 0} {
error "[namespace current]::table configure - unexpected argument count. Require name value pairs"
}
foreach {k v} $args {
switch -- $k {
%topt_switchkeys% {}
default {
error "[namespace current]::table configure - unrecognised option '$k'. Known values [dict keys $o_opts_table_defaults]"
}
}
#if {$k ni [dict keys $o_opts_table_defaults]} {
# error "[namespace current]::table configure - unrecognised option '$k'. Known values [dict keys $o_opts_table_defaults]"
#}
}
set checked_opts [list]
foreach {k v} $args {
switch -- $k {
-ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder-body - -ansiborder_footer {
set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]"
set ansi_codes [list] ;
foreach {pt code} $parts {
if {$pt ne ""} {
#we don't expect plaintext in an ansibase
error "Unable to interpret $k value as ansi SGR codes. Plaintext detected. Consider using for example: '\[punk::ansi::a+ green]' (or alias '\[a+ green]') to build ansi. debug view: [punk::ansi::ansistring VIEW $v]"
}
if {$code ne ""} {
lappend ansi_codes $code
}
}
set ansival [punk::ansi::codetype::sgr_merge_singles $ansi_codes]
lappend checked_opts $k $ansival
}
-frametype - -frametype_header - -frametype_body {
#frametype will raise an error if v is not a valid custom dict or one of the known predefined types such as light,heavy,double etc
lassign [textblock::frametype $v] _cat category _type ftype
lappend checked_opts $k $v
}
-framemap_body - -framemap_header {
#upvar ::textblock::class::opts_table_defaults tdefaults
#set default_bmap [dict get $tdefaults -framemap_body]
#todo - check keys and map
if {[llength $v] == 1} {
if {$v eq "default"} {
upvar ::textblock::class::opts_table_defaults tdefaults
set default_map [dict get $tdefaults $k]
lappend checked_opts $k $default_map
} else {
error "textblock::table::configure invalid $k value $v. Expected the value 'default' or a dict e.g topleft {hl *}"
}
} else {
foreach {subk subv} $v {
switch -- $subk {
topleft - topinner - topright - topsolo - middleleft - middleinner - middleright - middlesolo - bottomleft - bottominner - bottomright - bottomsolo - onlyleft - onlyinner - onlyright - onlysolo {}
default {
error "textblock::table::configure invalid $subk. Known values {topleft topinner topright topsolo middleleft middleinner middleright middlesolo bottomleft bottominner bottomright bottomsolo onlyleft onlyinner onlyright onlysolo}"
}
}
dict for {seg subst} $subv {
switch -- $seg {
hl - hlt - hlb - vl - vll - vlr - trc - tlc - blc - brc {}
default {
error "textblock::table::configure invalid $subk value $seg. Known values {hl hlt hlb vl vll vlr trc tlc blc brc}"
}
}
}
}
lappend checked_opts $k $v
}
}
-framelimits_body - -framelimits_header {
set specific_framelimits [list]
foreach fl $v {
switch -- $fl {
"default" {
lappend specific_framelimits trc hlt tlc vll blc hlb brc vlr
}
hl {
lappend specific_framelimits hlt hlb
}
vl {
lappend specific_framelimits vll vlr
}
hlt - hlb - vll - vlr - trc - tlc - blc - brc {
lappend specific_framelimits $fl
}
default {
error "textblock::table::configure invalid $k '$fl'. Known values {hl hlb hlt vl vll vlr trc tlc blc brc} (or default for all)"
}
}
}
lappend checked_opts $k $specific_framelimits
}
-ansireset {
if {$v eq "\uFFEF"} {
set RST "\x1b\[m" ;#[a]
lappend checked_opts $k $RST
} else {
error "textblock::table::configure -ansireset is read-only. It is present only to prevent unwanted colourised output in configure commands"
}
}
-show_hseps {
if {![string is boolean $v]} {
error "textblock::table::configure invalid $k '$v'. Must be a boolean or empty string"
}
lappend checked_opts $k $v
#these don't affect column width calculations
}
-show_edge {
if {![string is boolean $v]} {
error "textblock::table::configure invalid $k '$v'. Must be a boolean or empty string"
}
lappend checked_opts $k $v
#these don't affect column width calculations - except if table -minwidth/-maxwidth come into play
set o_calculated_column_widths [list] ;#invalidate cached column widths - a recalc will be forced when needed
}
-show_vseps {
#we allow empty string - so don't use -strict boolean check
if {![string is boolean $v]} {
error "textblock::table::configure invalid $k '$v'. Must be a boolean or empty string"
}
#affects width calculations
set o_calculated_column_widths [list]
lappend checked_opts $k $v
}
-minwidth - -maxwidth {
set o_calculated_column_widths [list]
lappend checked_opts $k $v
}
default {
lappend checked_opts $k $v
}
}
}
#all options checked - ok to update o_opts_table and o_opts_table_effective
#set o_opts_table [dict merge $o_opts_table $checked_opts]
foreach {k v} $args {
#yes in safe
switch -- $k {
-framemap_header - -framemap_body {
#framemaps don't require setting every key to update.
#e.g configure -framemaps {topleft <map>}
#needs to merge with existing unspecified keys such as topright middleleft etc.
if {$v eq "default"} {
dict set o_opts_table $k default
} else {
if {[dict get $o_opts_table $k] eq "default"} {
dict set o_opts_table $k $v
} else {
dict set o_opts_table $k [dict merge [dict get $o_opts_table $k] $v]
}
}
}
default {
dict set o_opts_table $k $v
}
}
}
#use values from checked_opts for the effective opts
dict for {k v} $checked_opts {
switch -- $k {
-framemap_body - -framemap_header {
set existing [dict get $o_opts_table_effective $k]
#set updated $existing
#dict for {subk subv} $v {
# dict set updated $subk $subv
#}
#dict set o_opts_table_effective $k $updated
dict set o_opts_table_effective $k [dict merge $existing $v]
}
-framelimits_body - -framelimits_header {
#my Set_effective_framelimits
dict set o_opts_table_effective $k $v
}
default {
dict set o_opts_table_effective $k $v
}
}
}
#ansireset exception
dict set o_opts_table -ansireset [dict get $o_opts_table_effective -ansireset]
return $o_opts_table
}]
proc test1 {args} {
set result [list]
dict for {k v} $args {
switch -- $k {
-a - -b - -c - -d - -e - -f - -g - -h - -i - -j - -k - -l - -m - -n - -o - -p - -q - -r - -s - -t - -u - -v - -w - -x - -y - -z {
switch -- $k {
-a - -b - -c {
lappend result "dfor-switcharm1-switcharm1-$k"
}
default {
lappend result "dfor-switcharm1-switchdefault-$k"
}
}
}
default {
switch -- $k {
-1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 {
lappend result "dfor-switchdefault-switcharm1-$k"
}
default {
lappend result "dfor-switchdefault-switchdefault-$k"
}
}
}
}
}
return $result
}
proc test2 {args} {
set result [list]
foreach {k v} $args {
switch -- $k {
-a - -b - -c - -d - -e - -f - -g - -h - -i - -j - -k - -l - -m - -n - -o - -p - -q - -r - -s - -t - -u - -v - -w - -x - -y - -z {
switch -- $k {
-a - -b - -c {
lappend result "dfor-switcharm1-switcharm1-$k"
}
default {
lappend result "dfor-switcharm1-switchdefault-$k"
}
}
}
default {
switch -- $k {
-1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 {
lappend result "dfor-switchdefault-switcharm1-$k"
}
default {
lappend result "dfor-switchdefault-switchdefault-$k"
}
}
}
}
}
return $result
}
proc test3 {args} {
set result [list]
for {set i 0} {$i < [llength $args]} {incr i} {
set k [lindex $args $i]
switch -- $k {
-a - -b - -c - -d - -e - -f - -g - -h - -i - -j - -k - -l - -m - -n - -o - -p - -q - -r - -s - -t - -u - -v - -w - -x - -y - -z {
switch -- $k {
-a - -b - -c {
lappend result "dfor-switcharm1-switcharm1-$k"
}
default {
lappend result "dfor-switcharm1-switchdefault-$k"
}
}
}
default {
switch -- $k {
-1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 {
lappend result "dfor-switchdefault-switcharm1-$k"
}
default {
lappend result "dfor-switchdefault-switchdefault-$k"
}
}
}
}
}
return $result
}
oo::class create c1 {
method test1 args [info body ::punk::experiment::test1]
method test2 args [info body ::punk::experiment::test2]
method test3 args [info body ::punk::experiment::test2]
}
c1 create obj1
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::experiment ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::experiment::lib {
namespace export {[a-z]*} ;# Convention: export all lowercase
namespace path [namespace parent]
#*** !doctools
#[subsection {Namespace punk::experiment::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]]
# #[para]Description of utility1
# return 1
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::experiment::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval punk::experiment::system {
#*** !doctools
#[subsection {Namespace punk::experiment::system}]
#[para] Internal functions that are not part of the API
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::experiment [namespace eval punk::experiment {
variable pkg punk::experiment
variable version
set version 999999.0a1.0
}]
return
#*** !doctools
#[manpage_end]

3
src/modules/punk/experiment-buildversion.txt

@ -0,0 +1,3 @@
0.1.0
#First line must be a semantic version number
#all other lines are ignored.

163
src/modules/punk/lib-999999.0a1.0.tm

@ -66,11 +66,11 @@ package require Tcl 8.6-
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::lib::class {
tcl::namespace::eval punk::lib::class {
#*** !doctools
#[subsection {Namespace punk::lib::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
if {[info commands [tcl::namespace::current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
@ -96,46 +96,46 @@ namespace eval punk::lib::class {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::lib::ensemble {
tcl::namespace::eval punk::lib::ensemble {
#wiki.tcl-lang.org/page/ensemble+extend
# extend an ensemble-like routine with the routines in some namespace
proc extend {routine extension} {
if {![string match ::* $routine]} {
set resolved [uplevel 1 [list ::namespace which $routine]]
set resolved [uplevel 1 [list ::tcl::namespace::which $routine]]
if {$resolved eq {}} {
error [list {no such routine} $routine]
}
set routine $resolved
}
set routinens [namespace qualifiers $routine]
set routinens [tcl::namespace::qualifiers $routine]
if {$routinens eq {::}} {
set routinens {}
}
set routinetail [namespace tail $routine]
set routinetail [tcl::namespace::tail $routine]
if {![string match ::* $extension]} {
set extension [uplevel 1 [
list [namespace which namespace] current]]::$extension
list [tcl::namespace::which namespace] current]]::$extension
}
if {![namespace exists $extension]} {
if {![tcl::namespace::exists $extension]} {
error [list {no such namespace} $extension]
}
set extension [namespace eval $extension [
list [namespace which namespace] current]]
set extension [tcl::namespace::eval $extension [
list [tcl::namespace::which namespace] current]]
namespace eval $extension [
list [namespace which namespace] export *]
tcl::namespace::eval $extension [
list [tcl::namespace::which namespace] export *]
while 1 {
set renamed ${routinens}::${routinetail}_[info cmdcount]
if {[namespace which $renamed] eq {}} break
if {[tcl::namespace::which $renamed] eq {}} break
}
rename $routine $renamed
namespace eval $extension [
tcl::namespace::eval $extension [
list namespace ensemble create -command $routine -unknown [
list apply {{renamed ensemble routine args} {
list $renamed $routine
@ -147,7 +147,7 @@ namespace eval punk::lib::ensemble {
}
}
namespace eval punk::lib::compat {
tcl::namespace::eval punk::lib::compat {
#*** !doctools
#[subsection {Namespace punk::lib::compat}]
#[para] compatibility functions for features that may not be available in earlier Tcl versions
@ -315,8 +315,8 @@ namespace eval punk::lib::compat {
}
# Bind [string insert] to [::tcl::string::insert].
namespace ensemble configure string -map [dict replace\
[namespace ensemble configure string -map]\
tcl::namespace::ensemble configure string -map [tcl::dict::replace\
[tcl::namespace::ensemble configure string -map]\
insert ::tcl::string::insert]
}
#*** !doctools
@ -327,7 +327,7 @@ namespace eval punk::lib::compat {
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::lib {
namespace export *
tcl::namespace::export *
#variable xyz
#*** !doctools
@ -368,29 +368,29 @@ namespace eval punk::lib {
#capture - use uplevel 1 or namespace eval depending on context
set capture [uplevel 1 {
apply { varnames {
set capturevars [dict create]
set capturearrs [dict create]
set capturevars [tcl::dict::create]
set capturearrs [tcl::dict::create]
foreach fullv $varnames {
set v [namespace tail $fullv]
set v [tcl::namespace::tail $fullv]
upvar 1 $v var
if {[info exists var]} {
if {(![array exists var])} {
dict set capturevars $v $var
tcl::dict::set capturevars $v $var
} else {
dict set capturearrs capturedarray_$v [array get var]
tcl::dict::set capturearrs capturedarray_$v [array get var]
}
} else {
#A variable can show in the results for 'info vars' but still not 'exist'. e.g a 'variable x' declaration in the namespace where the variable has never been set
}
}
return [dict create vars $capturevars arrs $capturearrs]
return [tcl::dict::create vars $capturevars arrs $capturearrs]
} } [info vars]
} ]
# -- --- ---
set cvars [dict get $capture vars]
set carrs [dict get $capture arrs]
set cvars [tcl::dict::get $capture vars]
set carrs [tcl::dict::get $capture arrs]
set apply_script ""
foreach arrayalias [dict keys $carrs] {
foreach arrayalias [tcl::dict::keys $carrs] {
set realname [string range $arrayalias [string first _ $arrayalias]+1 end]
append apply_script [string map [list %realname% $realname %arrayalias% $arrayalias] {
array set %realname% [set %arrayalias%][unset %arrayalias%]
@ -409,9 +409,9 @@ namespace eval punk::lib {
foreach $varnames $list {
lappend result {*}[apply\
[list\
[concat $varnames [dict keys $cvars] [dict keys $carrs] ]\
[concat $varnames [tcl::dict::keys $cvars] [tcl::dict::keys $carrs] ]\
$apply_script\
] {*}[subst $values] {*}[dict values $cvars] {*}[dict values $carrs] ]
] {*}[subst $values] {*}[tcl::dict::values $cvars] {*}[tcl::dict::values $carrs] ]
}
return $result
}
@ -456,8 +456,8 @@ namespace eval punk::lib {
error {wrong # args: should be "dict_getdef dictValue ?key ...? key default"}
}
set keys [lrange $args -1 end-1]
if {[dict exists $dictValue {*}$keys]} {
return [dict get $dictValue {*}$keys]
if {[tcl::dict::exists $dictValue {*}$keys]} {
return [tcl::dict::get $dictValue {*}$keys]
} else {
return [lindex $args end]
}
@ -566,7 +566,7 @@ namespace eval punk::lib {
} else {
#we still don't know the actual integer index for an index such as end-x or int-int without parsing and evaluating ourself.
#we can return the value - but only in a way that won't collide with our -1 out-of-range indicator
return [dict create value [lindex $resultlist 0]]
return [tcl::dict::create value [lindex $resultlist 0]]
}
}
@ -661,17 +661,17 @@ namespace eval punk::lib {
if {[llength $argopts]%2 !=0} {
error "[namespace current]::hex2dec arguments prior to list_largeHex must be option/value pairs - received '$argopts'"
}
set opts [dict create\
set opts [tcl::dict::create\
-validate 1\
-empty_as_hex "INVALID set -empty_as_hex to a hex string e.g FF if empty values should be replaced"\
]
set known_opts [dict keys $opts]
set known_opts [tcl::dict::keys $opts]
foreach {k v} $argopts {
dict set opts [tcl::prefix match -message "options for hex2dec. Unexpected option" $known_opts $k] $v
tcl::dict::set opts [tcl::prefix match -message "options for hex2dec. Unexpected option" $known_opts $k] $v
}
# -- --- --- ---
set opt_validate [dict get $opts -validate]
set opt_empty [dict get $opts -empty_as_hex]
set opt_validate [tcl::dict::get $opts -validate]
set opt_empty [tcl::dict::get $opts -empty_as_hex]
# -- --- --- ---
set list_largeHex [lmap h $list_largeHex[unset list_largeHex] {string map [list _ ""] [string trim $h]}]
@ -710,21 +710,21 @@ namespace eval punk::lib {
if {[llength $argopts]%2 !=0} {
error "[namespace current]::dec2hex arguments prior to list_decimals must be option/value pairs - received '$argopts'"
}
set defaults [dict create\
set defaults [tcl::dict::create\
-width 1\
-case upper\
-empty_as_decimal "INVALID set -empty_as_decimal to a number if empty values should be replaced"\
]
set known_opts [dict keys $defaults]
set fullopts [dict create]
set known_opts [tcl::dict::keys $defaults]
set fullopts [tcl::dict::create]
foreach {k v} $argopts {
dict set fullopts [tcl::prefix match -message "options for [namespace current]::dec2hex. Unexpected option" $known_opts $k] $v
tcl::dict::set fullopts [tcl::prefix match -message "options for [tcl::namespace::current]::dec2hex. Unexpected option" $known_opts $k] $v
}
set opts [dict merge $defaults $fullopts]
set opts [tcl::dict::merge $defaults $fullopts]
# -- --- --- ---
set opt_width [dict get $opts -width]
set opt_case [dict get $opts -case]
set opt_empty [dict get $opts -empty_as_decimal]
set opt_width [tcl::dict::get $opts -width]
set opt_case [tcl::dict::get $opts -case]
set opt_empty [tcl::dict::get $opts -empty_as_decimal]
# -- --- --- ---
@ -933,35 +933,35 @@ namespace eval punk::lib {
proc sieve n {
set primes [list]
if {$n < 2} {return $primes}
set nums [dict create]
set nums [tcl::dict::create]
for {set i 2} {$i <= $n} {incr i} {
dict set nums $i ""
tcl::dict::set nums $i ""
}
set next 2
set limit [expr {sqrt($n)}]
while {$next <= $limit} {
for {set i $next} {$i <= $n} {incr i $next} {dict unset nums $i}
for {set i $next} {$i <= $n} {incr i $next} {tcl::dict::unset nums $i}
lappend primes $next
dict for {next -} $nums break
tcl::dict::for {next -} $nums break
}
return [concat $primes [dict keys $nums]]
return [concat $primes [tcl::dict::keys $nums]]
}
proc sieve2 n {
set primes [list]
if {$n < 2} {return $primes}
set nums [dict create]
set nums [tcl::dict::create]
for {set i 2} {$i <= $n} {incr i} {
dict set nums $i ""
tcl::dict::set nums $i ""
}
set next 2
set limit [expr {sqrt($n)}]
while {$next <= $limit} {
for {set i $next} {$i <= $n} {incr i $next} {dict unset nums $i}
for {set i $next} {$i <= $n} {incr i $next} {tcl::dict::unset nums $i}
lappend primes $next
#dict for {next -} $nums break
set next [lindex $nums 0]
}
return [concat $primes [dict keys $nums]]
return [concat $primes [tcl::dict::keys $nums]]
}
proc hasglobs {str} {
@ -1002,7 +1002,7 @@ namespace eval punk::lib {
#[para]This function merges the two dicts whilst maintaining the key order of main followed by defaults.
#1st merge (inner merge) with wrong values taking precedence - but right key-order - then (outer merge) restore values
return [dict merge [dict merge $main $defaults] $main]
return [tcl::dict::merge [tcl::dict::merge $main $defaults] $main]
}
proc askuser {question} {
@ -1044,7 +1044,7 @@ namespace eval punk::lib {
set answer [gets stdin]
}
} finally {
fconfigure stdin -blocking [dict get $stdin_state -blocking]
fconfigure stdin -blocking [tcl::dict::get $stdin_state -blocking]
}
return $answer
}
@ -1162,13 +1162,13 @@ namespace eval punk::lib {
}
proc list_as_lines2 {args} {
#eat or own dogfood version - shows the implementation is simpler - but unfortunately not suitable for a simple function like this which should be as fast as possible?
lassign [dict values [punk::args::get_dict {
lassign [tcl::dict::values [punk::args::get_dict {
-joinchar -default \n
*values -min 1 -max 1
} $args]] opts values
puts "opts:$opts"
puts "values:$values"
return [join [dict get $values 0] [dict get $opts -joinchar]]
return [join [tcl::dict::get $values 0] [tcl::dict::get $opts -joinchar]]
}
proc lines_as_list {args} {
@ -1189,7 +1189,7 @@ namespace eval punk::lib {
} else {
set opts [lrange $args 0 end-1]
}
#set opts [dict merge {-block {}} $opts]
#set opts [tcl::dict::merge {-block {}} $opts]
set bposn [lsearch $opts -block]
if {$bposn < 0} {
lappend opts -block {}
@ -1203,11 +1203,11 @@ namespace eval punk::lib {
#-anyopts 1 avoids having to know what to say if odd numbers of options passed etc
#we don't have to decide what is an opt vs a value
#even if the caller provides the argument -block without a value the next function's validation will report a reasonable error because there is now nothing in $values (consumed by -block)
lassign [dict values [punk::args::get_dict {
lassign [tcl::dict::values [punk::args::get_dict {
*opts -any 1
-block -default {}
} $args]] opts valuedict
tailcall linelist {*}$opts {*}[dict values $valuedict]
tailcall linelist {*}$opts {*}[tcl::dict::values $valuedict]
}
# important for pipeline & match_assign
@ -1222,7 +1222,7 @@ namespace eval punk::lib {
set text [string map [list \r\n \n] $text] ;#review - option?
set arglist [lrange $args 0 end-1]
set opts [dict create\
set opts [tcl::dict::create\
-block {trimhead1 trimtail1}\
-line {}\
-commandprefix ""\
@ -1232,7 +1232,7 @@ namespace eval punk::lib {
foreach {o v} $arglist {
switch -- $o {
-block - -line - -commandprefix - -ansiresets - -ansireplays {
dict set opts $o $v
tcl::dict::set opts $o $v
}
default {
error "linelist: Unrecognized option '$o' usage:$usage"
@ -1240,7 +1240,7 @@ namespace eval punk::lib {
}
}
# -- --- --- --- --- ---
set opt_block [dict get $opts -block]
set opt_block [tcl::dict::get $opts -block]
if {[llength $opt_block]} {
foreach bo $opt_block {
switch -- $bo {
@ -1272,7 +1272,7 @@ namespace eval punk::lib {
# -- --- --- --- --- ---
set opt_line [dict get $opts -line]
set opt_line [tcl::dict::get $opts -line]
set tl_left 0
set tl_right 0
set tl_both 0
@ -1299,11 +1299,11 @@ namespace eval punk::lib {
set tl_both 1
}
# -- --- --- --- --- ---
set opt_commandprefix [dict get $opts -commandprefix]
set opt_commandprefix [tcl::dict::get $opts -commandprefix]
# -- --- --- --- --- ---
set opt_ansiresets [dict get $opts -ansiresets]
set opt_ansiresets [tcl::dict::get $opts -ansiresets]
# -- --- --- --- --- ---
set opt_ansireplays [dict get $opts -ansireplays]
set opt_ansireplays [tcl::dict::get $opts -ansireplays]
if {$opt_ansireplays} {
if {$opt_ansiresets eq "auto"} {
set opt_ansiresets 1
@ -1604,8 +1604,29 @@ namespace eval punk::lib {
}
#we are interested in seeing jumpTable line and following lines up until next line starting with "Command" or bracketed number e.g (164)
proc show_jump_tables {procname} {
set data [tcl::unsupported::disassemble proc $procname]
proc show_jump_tables {args} {
#avoiding use of 'info cmdtype' as unavaliable in safe interps as at 2024-06.
if {[llength $args] == 1} {
set data [tcl::unsupported::disassemble proc [lindex $args 0]]
} elseif {[llength $args] == 2} {
#review - this looks for direct methods on the supplied object/class, and then tries to disassemble method on the supplied class or class of supplied object if it isn't a class itself.
#not sure if this handles more complex hierarchies or mixins etc.
lassign $args obj method
if {![info object isa object $obj]} {
error "show_jump_tables unable to examine '$args'. $obj is not an oo object"
}
#classes are objects too and can have direct methods
if {$method in [info object methods $obj]} {
set data [tcl::unsupported::disassemble objmethod $obj $method]
} else {
if {![info object isa class $obj]} {
set obj [info object class $obj]
}
set data [tcl::unsupported::disassemble method $obj $method]
}
} else {
error "show_jump_tables expected a procname or a class/object and method"
}
set result ""
set in_jt 0
foreach ln [split $data \n] {
@ -1888,7 +1909,7 @@ namespace eval punk::lib::system {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::lib [namespace eval punk::lib {
package provide punk::lib [tcl::namespace::eval punk::lib {
variable pkg punk::lib
variable version
set version 999999.0a1.0

12
src/modules/punk/ns-999999.0a1.0.tm

@ -994,7 +994,19 @@ namespace eval punk::ns {
}
#info cmdtype available in 8.7+
#safe interps also seem to have it disabled for some reason
proc cmdtype {cmd} {
if {[interp issafe]} {
if {[catch {::tcl::info::cmdtype $cmd} result]} {
if {[info commands ::cmdtype] ne ""} {
#hack - look for an alias that may have been specifically enabled to bring this back
tailcall ::cmdtype $cmd
}
return na
} else {
return $result
}
}
if {[info commands ::tcl::info::cmdtype] ne ""} {
tailcall info cmdtype $cmd
}

38
src/modules/punk/overlay-0.1.tm

@ -41,41 +41,41 @@ namespace eval ::punk::overlay {
#}
namespace eval $routine [
list namespace ensemble configure $routine -unknown [
list apply {{base ensemble subcommand args} {
list ${base}::_redirected $ensemble $subcommand
::list namespace ensemble configure $routine -unknown [
::list ::apply {{base ensemble subcommand args} {
::list ${base}::_redirected $ensemble $subcommand
}} $base
]
]
punk::mix::util::namespace_import_pattern_to_namespace_noclobber ::punk::mix::util::* ${routine}::util
#namespace eval ${routine}::util {
#namespace import ::punk::mix::util::*
#::namespace import ::punk::mix::util::*
#}
punk::mix::util::namespace_import_pattern_to_namespace_noclobber ${base}::lib::* ${routine}::lib
#namespace eval ${routine}::lib [string map [list <base> $base] {
# namespace import <base>::lib::*
# ::namespace import <base>::lib::*
#}]
namespace eval ${routine}::lib [string map [list <base> $base <routine> $routine] {
if {[namespace exists <base>::lib]} {
set current_paths [namespace path]
if {[::namespace exists <base>::lib]} {
::set current_paths [namespace path]
if {"<routine>" ni $current_paths} {
lappend current_paths <routine>
::lappend current_paths <routine>
}
namespace path $current_paths
::namespace path $current_paths
}
}]
namespace eval $routine {
set exportlist [list]
foreach cmd [info commands [namespace current]::*] {
set c [namespace tail $cmd]
if {![string match _* $c]} {
lappend exportlist $c
::set exportlist [::list]
::foreach cmd [::info commands [::namespace current]::*] {
::set c [::namespace tail $cmd]
if {![::string match _* $c]} {
::lappend exportlist $c
}
}
namespace export {*}$exportlist
::namespace export {*}$exportlist
}
return $routine
@ -122,18 +122,18 @@ namespace eval ::punk::overlay {
#let child namespace 'lib' resolve parent namespace and thus util::xxx
namespace eval ${cmdnamespace}::lib [string map [list <cmdns> $cmdnamespace] {
set nspaths [namespace path]
::set nspaths [::namespace path]
if {"<cmdns>" ni $nspaths} {
lappend nspaths <cmdns>
::lappend nspaths <cmdns>
}
namespace path $nspaths
::namespace path $nspaths
}]
set imported_commands [list]
set nscaller [uplevel 1 [list namespace current]]
if {[catch {
#review - noclobber?
namespace eval ${nscaller}::temp_import [list namespace import ${cmdnamespace}::*]
namespace eval ${nscaller}::temp_import [list ::namespace import ${cmdnamespace}::*]
foreach cmd [info commands ${nscaller}::temp_import::*] {
set cmdtail [namespace tail $cmd]
if {$cmdtail eq "_default"} {

46
src/modules/punk/repl-0.1.tm

@ -41,6 +41,7 @@ package require textblock
if {![info exists ::env(SHELL)]} {
set ::env(SHELL) punk86
}
@ -1278,6 +1279,8 @@ proc repl::repl_handler {inputchan prompt_config} {
#if it's been set to raw - assume it is deliberately done this way as the user could have alternatively called punk::mode raw or punk::console::enableVirtualTerminal
#by not doing this automatically - we assume the caller has a reason.
} else {
#JMN FIX!
#this returns 0 in rawmode on 8.6 after repl thread changes
set rawmode [set ::punk::console::is_raw]
}
@ -1332,9 +1335,10 @@ proc repl::repl_handler {inputchan prompt_config} {
set cols 3
if {[string is integer -strict $rows]} {
set RED [punk::ansi::a+ red bold]; set RST [punk::ansi::a]
set msg "${RED}line-length Tcl windows channel bug? Hit enter to continue$RST"
set msg "${RED}fblocked $inputchan is true. (line-length Tcl windows channel bug?) Hit enter to continue$RST"
set msglen [ansistring length $msg]
punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg
#punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg
puts stderr $msg
}
after 100
}
@ -2494,6 +2498,12 @@ namespace eval repl {
#package require shellrun
package require textblock
#md5 uses open so can't be directly called in a safe interp
#it will need to delegate to a call here in the main interp of the codethread using an installed alias
set md5version [package require md5]
#we also need to 'package provide md5 $md5version' in the safe interp itself so that it won't override
#punk::configure_unknown ;#must be called because we hacked the tcl 'unknown' proc
#child codethread (outside of code interp) needs to know details of the calling repl
@ -2534,6 +2544,28 @@ namespace eval repl {
thread::send %replthread% [list punk::console::colour {*}$args]
interp eval code [list punk::console::colour {*}$args]
}
proc mode args {
thread::send %replthread% [list punk::console::mode {*}$args]
interp eval code [list ::punk::console::mode {*}$args]
}
proc cmdtype cmd {
code invokehidden tcl:info:cmdtype $cmd
}
#punk repl tsv wrappers
proc set_repl_last_unknown args {
tsv::set repl last_unknown {*}$args
}
proc get_repl_runid args {
if {[tsv::exists repl runid]} {
return [tsv::get repl runid]
} else {
return 0
}
}
proc md5 args {
::md5::md5 {*}$args
}
}
namespace eval ::repl::interpextras {
#install using safe::setLogCmd
@ -2633,6 +2665,7 @@ namespace eval repl {
interp share {} [shellfilter::stack::item_tophandle stderr] code
}
code alias ::md5::md5 ::repl::interphelpers::md5
code alias exit ::repl::interphelpers::quit
} elseif {$safe == 2} {
safe::interpCreate code -nested 1
@ -2687,6 +2720,9 @@ namespace eval repl {
#review - exit should do something slightly different
# see ::safe::interpDelete
code alias exit ::repl::interphelpers::quit
code alias ::md5::md5 ::repl::interphelpers::md5
interp eval code [list package provide md5 $md5version]
} else {
interp create code
interp eval code {
@ -2706,8 +2742,14 @@ namespace eval repl {
code alias quit ::repl::interphelpers::quit
code alias editbuf ::repl::interphelpers::editbuf
code alias colour ::repl::interphelpers::colour
code alias mode ::repl::interphelpers::mode
#code alias after ::repl::interphelpers::do_after
code alias ::punk::set_repl_last_unknown ::repl::interphelpers::set_repl_last_unknown
code alias ::punk::get_repl_runid ::repl::interphelpers::get_repl_runid
code alias cmdtype ::repl::interphelpers::cmdtype
#temporary debug aliases - deliberate violation of safety provided by safe interp
code alias escapeeval ::repl::interphelpers::escapeeval

2
src/modules/punkcheck-0.1.0.tm

@ -1294,7 +1294,7 @@ namespace eval punkcheck {
dict unset config -call-depth-internal
dict unset config -max_depth
dict unset config -subdirlist
dict for {k v} $config {
tcl::dict::for {k v} $config {
if {$v eq "\uFFFF"} {
dict unset config $k
}

48
src/modules/shellfilter-0.1.9.tm

@ -329,11 +329,14 @@ namespace eval shellfilter::chan {
}
}
method initialize {ch mode} {
return [list initialize finalize write]
return [list initialize finalize write flush clear]
}
method finalize {ch} {
my destroy
}
method clear {ch} {
return
}
method watch {ch events} {
# must be present but we ignore it because we do not
# post any events
@ -341,6 +344,9 @@ namespace eval shellfilter::chan {
#method read {ch count} {
# return ?
#}
method flush {ch} {
return ""
}
method write {ch bytes} {
set stringdata [encoding convertfrom $o_enc $bytes]
foreach v $o_datavars {
@ -374,7 +380,7 @@ namespace eval shellfilter::chan {
}
}
method initialize {transform_handle mode} {
return [list initialize read write finalize]
return [list initialize read drain write flush clear finalize]
}
method finalize {transform_handle} {
::shellfilter::log::close $o_logsource
@ -384,12 +390,21 @@ namespace eval shellfilter::chan {
# must be present but we ignore it because we do not
# post any events
}
method clear {transform_handle} {
return
}
method drain {transform_handle} {
return ""
}
method read {transform_handle bytes} {
set logdata [encoding convertfrom $o_enc $bytes]
#::shellfilter::log::write $o_logsource $logdata
puts -nonewline $o_localchan $logdata
return $bytes
}
method flush {transform_handle} {
return ""
}
method write {transform_handle bytes} {
set logdata [encoding convertfrom $o_enc $bytes]
#::shellfilter::log::write $o_logsource $logdata
@ -524,18 +539,27 @@ namespace eval shellfilter::chan {
}
}
method initialize {transform_handle mode} {
return [list initialize read write finalize]
return [list initialize read write clear flush drain finalize]
}
method finalize {transform_handle} {
my destroy
}
method clear {transform_handle} {
return
}
method watch {transform_handle events} {
}
method drain {transform_handle} {
return ""
}
method read {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes]
set outstring [punk::ansi::stripansi $instring]
return [encoding convertto $o_enc $outstring]
}
method flush {transform_handle} {
return ""
}
method write {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes]
set outstring [punk::ansi::stripansi $instring]
@ -614,19 +638,33 @@ namespace eval shellfilter::chan {
}
}
method initialize {transform_handle mode} {
return [list initialize write finalize]
return [list initialize write flush read drain clear finalize]
}
method finalize {transform_handle} {
my destroy
}
method watch {transform_handle events} {
}
method clear {transform_handle} {
return
}
method flush {transform_handle} {
return ""
}
method write {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes]
set outstring "$o_do_colour$instring$o_do_normal"
#set outstring ">>>$instring"
return [encoding convertto $o_enc $outstring]
}
method drain {transform_handle} {
return ""
}
method read {transform_handle bytes} {
set instring [encoding convertfrom $o_enc $bytes]
set outstring "$o_do_colour$instring$o_do_normal"
return [encoding convertto $o_enc $outstring]
}
method meta_is_redirection {} {
return $o_is_junction
}
@ -2109,7 +2147,7 @@ namespace eval shellfilter {
error "Trailing arguments after any positional arguments must be in pairs of the form -argname argvalue. Valid flags are:'$valid_flags'"
}
set invalid_flags [list]
dict for {k -} $args {
foreach {k -} $args {
switch -- $k {
-timeout -
-outprefix -

115
src/modules/textblock-999999.0a1.0.tm

@ -184,13 +184,17 @@ namespace eval textblock {
onlysolo [list]\
]
#ensembles seem to be not compiled in safe interp
#https://core.tcl-lang.org/tcl/tktview/1095bf7f75
#as we want textblock to be usable in safe interps - use tcl::dict::for as a partial workaround
#This at least means the script argument, especially switch statements can get compiled.
#It does however affect all ensemble commands - so even 'dict get/set' etc won't be as efficient in a safe interp.
#e.g $t configure -framemap_body [table_edge_map " "]
proc table_edge_map {char} {
variable table_edge_parts
set map [list]
dict for {celltype parts} $table_edge_parts {
tcl::dict::for {celltype parts} $table_edge_parts {
set tmap [list]
foreach p $parts {
dict set tmap $p $char
@ -202,7 +206,7 @@ namespace eval textblock {
proc table_sep_map {char} {
variable table_hseps
set map [list]
dict for {celltype parts} $table_hseps {
tcl::dict::for {celltype parts} $table_hseps {
set tmap [list]
foreach p $parts {
dict set tmap $p $char
@ -214,7 +218,7 @@ namespace eval textblock {
proc header_edge_map {char} {
variable header_edge_parts
set map [list]
dict for {celltype parts} $header_edge_parts {
tcl::dict::for {celltype parts} $header_edge_parts {
set tmap [list]
foreach p $parts {
dict set tmap $p $char
@ -522,14 +526,18 @@ namespace eval textblock {
error "textblock::table::configure invalid $k value $v. Expected the value 'default' or a dict e.g topleft {hl *}"
}
} else {
dict for {subk subv} $v {
#safe jumptable test
#dict for {subk subv} $v {}
foreach {subk subv} $v {
switch -- $subk {
topleft - topinner - topright - topsolo - middleleft - middleinner - middleright - middlesolo - bottomleft - bottominner - bottomright - bottomsolo - onlyleft - onlyinner - onlyright - onlysolo {}
default {
error "textblock::table::configure invalid $subk. Known values {topleft topinner topright topsolo middleleft middleinner middleright middlesolo bottomleft bottominner bottomright bottomsolo onlyleft onlyinner onlyright onlysolo}"
}
}
dict for {seg subst} $subv {
#safe jumptable test
#dict for {seg subst} $subv {}
foreach {seg subst} $subv {
switch -- $seg {
hl - hlt - hlb - vl - vll - vlr - trc - tlc - blc - brc {}
default {
@ -632,7 +640,10 @@ namespace eval textblock {
}
}
#use values from checked_opts for the effective opts
dict for {k v} $checked_opts {
#safe jumptable test
#dict for {k v} $checked_opts {}
#foreach {k v} $checked_opts {}
tcl::dict::for {k v} $checked_opts {
switch -- $k {
-framemap_body - -framemap_header {
set existing [dict get $o_opts_table_effective $k]
@ -687,7 +698,7 @@ namespace eval textblock {
}
$m add columns [dict size $o_columndata]
$m add rows [dict size $o_rowdefs]
dict for {k v} $o_columndata {
tcl::dict::for {k v} $o_columndata {
$m set column $k $v
}
return $m
@ -950,7 +961,7 @@ namespace eval textblock {
if {$args_got_headers} {
#if the headerlist length for this column has shrunk,and it was the longest - we may now have excess entries in o_headerstates
set zero_heights [list]
dict for {hidx _v} $o_headerstates {
tcl::dict::for {hidx _v} $o_headerstates {
#pass empty string for exclude_column so we don't exclude our own column
if {[my header_height_calc $hidx ""] == 0} {
lappend zero_heights $hidx
@ -974,7 +985,7 @@ namespace eval textblock {
}
method header_count_calc {} {
set max_headers 0
dict for {k cdef} $o_columndefs {
tcl::dict::for {k cdef} $o_columndefs {
set num_headers [llength [dict get $cdef -headers]]
set max_headers [expr {max($max_headers,$num_headers)}]
}
@ -994,7 +1005,7 @@ namespace eval textblock {
} else {
set exclude_colidx [lindex [dict keys $o_columndefs] $exclude_column]
}
dict for {cidx cdef} $o_columndefs {
tcl::dict::for {cidx cdef} $o_columndefs {
if {$exclude_colidx == $cidx} {
continue
}
@ -1014,7 +1025,7 @@ namespace eval textblock {
method header_colspans {} {
set num_headers [my header_count_calc]
set colspans_by_header [dict create]
dict for {cidx cdef} $o_columndefs {
tcl::dict::for {cidx cdef} $o_columndefs {
set headerlist [dict get $cdef -headers]
set colspans_for_column [dict get $cdef -header_colspans]
for {set h 0} {$h < $num_headers} {incr h} {
@ -1076,7 +1087,7 @@ namespace eval textblock {
set result [dict create]
dict set result -colspans [dict get $colspans_by_header $hidx]
set header_row_items [list]
dict for {cidx cdef} $o_columndefs {
tcl::dict::for {cidx cdef} $o_columndefs {
set colheaders [dict get $cdef -headers]
set relevant_header [lindex $colheaders $hidx]
#The -headers element of the column def is allowed to have less elements than the total, even be empty. Number of headers is defined by the longest -header list in the set of columns
@ -1095,7 +1106,7 @@ namespace eval textblock {
switch -- $k {
-values {
set header_row_items [list]
dict for {cidx cdef} $o_columndefs {
tcl::dict::for {cidx cdef} $o_columndefs {
set colheaders [dict get $cdef -headers]
set relevant_header [lindex $colheaders $hidx]
#The -headers element of the column def is allowed to have less elements than the total, even be empty. Number of headers is defined by the longest -header list in the set of columns
@ -1127,14 +1138,16 @@ namespace eval textblock {
if {[llength $args] %2 != 0} {
error "textblock::table configure_header incorrect number of options following index_expression. Require name value pairs. Known options: [dict keys $o_opts_header_defaults]"
}
dict for {k v} $args {
foreach {k v} $args {
if {$k ni [dict keys $o_opts_header_defaults]} {
error "[namespace current]::table configure_row unknown option '$k'. Known options: [dict keys $o_opts_header_defaults]"
}
}
set checked_opts [list]
dict for {k v} $args {
#safe jumptable test
#dict for {k v} $args {}
foreach {k v} $args {
switch -- $k {
-ansibase {
set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]"
@ -1248,8 +1261,10 @@ namespace eval textblock {
}
#configured opts all good
dict for {k v} $checked_opts {
#safe jumptable test
#dict for {k v} $checked_opts {}
#foreach {k v} $checked_opts {}
tcl::dict::for {k v} $checked_opts {
switch -- $k {
-values {
set c 0
@ -1342,7 +1357,9 @@ namespace eval textblock {
if {[llength $args] %2 !=0} {
error "[namespace current]::table::add_row unexpected argument count. Require name value pairs. Known options: [dict keys $defaults]"
}
dict for {k v} $args {
#safe jumptable test
#dict for {k v} $args {}
foreach {k v} $args {
switch -- $k {
-minheight - -maxheight - -ansibase - -ansireset {}
default {
@ -1363,7 +1380,7 @@ namespace eval textblock {
}
} else {
if {![llength $valuelist]} {
dict for {k coldef} $o_columndefs {
tcl::dict::for {k coldef} $o_columndefs {
lappend valuelist [dict get $coldef -defaultvalue]
}
}
@ -1451,13 +1468,13 @@ namespace eval textblock {
if {[llength $args] %2 != 0} {
error "textblock::table configure_row incorrect number of options following index_expression. Require name value pairs. Known options: [dict keys $o_opts_row_defaults]"
}
dict for {k v} $args {
foreach {k v} $args {
if {$k ni [dict keys $o_opts_row_defaults]} {
error "[namespace current]::table configure_row unknown option '$k'. Known options: [dict keys $o_opts_row_defaults]"
}
}
set checked_opts [list]
dict for {k v} $args {
foreach {k v} $args {
switch -- $k {
-ansibase {
set parts [punk::ansi::ta::split_codes_single $v] ;#caller may have supplied separated codes eg "[a+ Yellow][a+ red]"
@ -1514,7 +1531,7 @@ namespace eval textblock {
set o_rowdefs [dict create]
set o_rowstates [dict create]
#The data values are stored by column regardless of whether added row by row
dict for {cidx records} $o_columndata {
tcl::dict::for {cidx records} $o_columndata {
dict set o_columndata $cidx [list]
#reset only the body fields in o_columnstates
dict set o_columnstates $cidx minwidthbodyseen 0
@ -1587,7 +1604,7 @@ namespace eval textblock {
-position "inner"\
-return "string"\
]
dict for {k v} $args {
foreach {k v} $args {
switch -- $k {
-position - -return {
dict set opts $k $v
@ -2387,7 +2404,7 @@ namespace eval textblock {
set defaults [dict create\
-usetables 1\
]
dict for {k v} $args {
foreach {k v} $args {
switch -- $k {
-usetables {}
default {
@ -2403,13 +2420,13 @@ namespace eval textblock {
#puts stdout "columndefs: $o_columndefs"
puts stdout "columndefs:"
if {!$opt_usetables} {
dict for {k v} $o_columndefs {
tcl::dict::for {k v} $o_columndefs {
puts " $k $v"
}
} else {
set t [textblock::class::table new]
$t add_column -headers "Col"
dict for {col coldef} $o_columndefs {
tcl::dict::for {col coldef} $o_columndefs {
foreach property [dict keys $coldef] {
if {$property eq "-ansireset"} {
continue
@ -2422,7 +2439,7 @@ namespace eval textblock {
#build our inner tables first so we can sync widths
set col_header_tables [dict create]
set max_widths [dict create 0 0 1 0 2 0 3 0] ;#max inner table column widths
dict for {col coldef} $o_columndefs {
tcl::dict::for {col coldef} $o_columndefs {
set row [list $col]
set colheaders [dict get $coldef -headers]
#inner table probably overkill here ..but just as easy
@ -2451,14 +2468,18 @@ namespace eval textblock {
}
}
dict for {col coldef} $o_columndefs {
#safe jumptable test
#dict for {col coldef} $o_columndefs {}
tcl::dict::for {col coldef} $o_columndefs {
set row [list $col]
dict for {property val} $coldef {
#safe jumptable test
#dict for {property val} $coldef {}
tcl::dict::for {property val} $coldef {
switch -- $property {
-ansireset {continue}
-headers {
set htable [dict get $col_header_tables $col]
dict for {innercol maxw} $max_widths {
tcl::dict::for {innercol maxw} $max_widths {
$htable configure_column $innercol -minwidth $maxw -blockalign left
}
lappend row [$htable print]
@ -2481,7 +2502,7 @@ namespace eval textblock {
}
puts stdout "columnstates: $o_columnstates"
puts stdout "headerstates: $o_headerstates"
dict for {k coldef} $o_columndefs {
tcl::dict::for {k coldef} $o_columndefs {
if {[dict exists $o_columndata $k]} {
set headerlist [dict get $coldef -headers]
set coldata [dict get $o_columndata $k]
@ -2732,7 +2753,7 @@ namespace eval textblock {
-cached 1\
]
#-colspan is relevant to header/footer data only
dict for {k v} $args {
foreach {k v} $args {
switch -- $k {
-headers - -footers - -colspan - -data - -cached {
dict set opts $k $v
@ -2761,7 +2782,7 @@ namespace eval textblock {
set colheaders [dict get $o_columndefs $cidx -headers]
set all_colspans_by_header [my header_colspans]
set hlist [list]
dict for {hrow cspans} $all_colspans_by_header {
tcl::dict::for {hrow cspans} $all_colspans_by_header {
set s [lindex $cspans $cidx]
#todo - map 'all' entries to a number?
#we should build a version of header_colspans that does this
@ -2862,7 +2883,7 @@ namespace eval textblock {
set colspace_added [dict create]
set ordered_spans [dict create]
dict for {col spandata} [my spangroups] {
tcl::dict::for {col spandata} [my spangroups] {
set dwidth [my column_datawidth $col -data 1 -headers 0 -footers 0 -cached 1]
set minwidth [dict get $o_columndefs $col -minwidth]
set maxwidth [dict get $o_columndefs $col -maxwidth]
@ -2892,8 +2913,10 @@ namespace eval textblock {
}
}
}
dict for {spanid spandata} $ordered_spans {
#safe jumptable test
#dict for {spanid spandata} $ordered_spans {}
tcl::dict::for {spanid spandata} $ordered_spans {
lassign [split $spanid ,] startcol hrow
set memcols [dict get $spandata membercols] ;#dict with col and initial width - we ignore initial width, it's there in case we want to allocate space based on initial data width ratios
set colids [dict keys $memcols]
@ -3045,7 +3068,7 @@ namespace eval textblock {
set spaninfo [list]
set numcols [dict size $o_columndefs]
#note that 'all' can occur in positions other than column 0 - meaning all remaining
dict for {hrow rawspans} $spans_by_header {
tcl::dict::for {hrow rawspans} $spans_by_header {
set thiscol_spanval [lindex $rawspans $cidx]
if {$thiscol_spanval eq "all" || $thiscol_spanval > 0} {
set spanstartcol $cidx ;#own column
@ -3080,7 +3103,7 @@ namespace eval textblock {
set opts [dict create\
-algorithm $o_column_width_algorithm\
]
dict for {k v} $args {
foreach {k v} $args {
switch -- $k {
-algorithm {
dict set opts $k $v
@ -3317,7 +3340,7 @@ namespace eval textblock {
-compact 1\
-forcecolour 0\
]
dict for {k v} $args {
foreach {k v} $args {
switch -- $k {
-return - -compact - -forcecolour {
dict set opts $k $v
@ -4131,7 +4154,7 @@ namespace eval textblock {
#}
#2 - the more useful one?
dict for {b bdict} $blockinfo {
tcl::dict::for {b bdict} $blockinfo {
lappend r0 [dict get $blockinfo $b left0] [dict get $blockinfo $b right0]
lappend r1 [dict get $blockinfo $b left1] [dict get $blockinfo $b right1]
lappend r2 [dict get $blockinfo $b left2] [dict get $blockinfo $b right2]
@ -4145,7 +4168,7 @@ namespace eval textblock {
set t [textblock::list_as_table [expr {1 + (2 * [dict size $blockinfo])}] $rows -return object]
$t configure_column 0 -headers [list [dict get $opts -description] "within_ansi"] -ansibase $column_ansi
set col 1
dict for {b bdict} $blockinfo {
tcl::dict::for {b bdict} $blockinfo {
if {[dict exists $bheaders $b]} {
set hdr [dict get $bheaders $b]
} else {
@ -5651,7 +5674,7 @@ namespace eval textblock {
set termwidth 80
}
dict for {k v} $frame_cache {
tcl::dict::for {k v} $frame_cache {
lassign $v _f frame _used used
#set fwidth [textblock::widthtopline $frame]
#review - are cached frames uniform width lines?
@ -5819,7 +5842,9 @@ namespace eval textblock {
error "frame option -joins '$opt_joins' must contain only values from the set: left,right,up,down with targets heavy,light,ascii,altg,arc,double,block,block1,custom e.g down-light"
}
set is_boxmap_ok 1
dict for {boxelement subst} $opt_boxmap {
#safe jumptable test
#dict for {boxelement subst} $opt_boxmap {}
tcl::dict::for {boxelement subst} $opt_boxmap {
switch -- $boxelement {
hl - hlt - hlb - vl - vll - vlr - tlc - trc - blc - brc {}
default {
@ -6013,7 +6038,7 @@ namespace eval textblock {
#puts "---> $opt_boxmap"
#review - we handle double-wide in custom frames - what about for boxmaps?
dict for {boxelement sub} $opt_boxmap {
tcl::dict::for {boxelement sub} $opt_boxmap {
if {$boxelement eq "vl"} {
set vll $sub
set vlr $sub

Loading…
Cancel
Save