From 39ce3b7f5bd71a9502cf40f921ed874a18fc7d33 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Fri, 1 Aug 2025 14:34:47 +1000 Subject: [PATCH] update bootsupport and vfs --- src/bootsupport/modules/punk/args-0.2.tm | 23 ++++-- src/bootsupport/modules/punk/docgen-0.1.0.tm | 29 ++++++-- .../modules/punk/libunknown-0.1.tm | 2 +- src/bootsupport/modules/punk/mix/cli-0.3.1.tm | 34 +++++---- .../punk/mix/commandset/loadedlib-0.1.0.tm | 56 +++++++++++---- .../modules/punk/mix/commandset/repo-0.1.0.tm | 33 ++++++++- .../punk/mix/commandset/scriptwrap-0.1.0.tm | 2 +- src/bootsupport/modules/punk/path-0.1.0.tm | 37 +++++++++- src/bootsupport/modules/punk/repl-0.1.2.tm | 20 ++++-- src/bootsupport/modules/shellfilter-0.2.tm | 72 +++---------------- src/bootsupport/modules/textblock-0.1.3.tm | 1 - src/bootsupport/modules/tomlish-1.1.6.tm | 13 +++- .../src/bootsupport/modules/punk/args-0.2.tm | 23 ++++-- .../bootsupport/modules/punk/docgen-0.1.0.tm | 29 ++++++-- .../modules/punk/libunknown-0.1.tm | 2 +- .../bootsupport/modules/punk/mix/cli-0.3.1.tm | 34 +++++---- .../punk/mix/commandset/loadedlib-0.1.0.tm | 56 +++++++++++---- .../modules/punk/mix/commandset/repo-0.1.0.tm | 33 ++++++++- .../punk/mix/commandset/scriptwrap-0.1.0.tm | 2 +- .../bootsupport/modules/punk/path-0.1.0.tm | 37 +++++++++- .../bootsupport/modules/punk/repl-0.1.2.tm | 20 ++++-- .../bootsupport/modules/shellfilter-0.2.tm | 72 +++---------------- .../bootsupport/modules/textblock-0.1.3.tm | 1 - .../src/bootsupport/modules/tomlish-1.1.6.tm | 13 +++- .../src/bootsupport/modules/punk/args-0.2.tm | 23 ++++-- .../bootsupport/modules/punk/docgen-0.1.0.tm | 29 ++++++-- .../modules/punk/libunknown-0.1.tm | 2 +- .../bootsupport/modules/punk/mix/cli-0.3.1.tm | 34 +++++---- .../punk/mix/commandset/loadedlib-0.1.0.tm | 56 +++++++++++---- .../modules/punk/mix/commandset/repo-0.1.0.tm | 33 ++++++++- .../punk/mix/commandset/scriptwrap-0.1.0.tm | 2 +- .../bootsupport/modules/punk/path-0.1.0.tm | 37 +++++++++- .../bootsupport/modules/punk/repl-0.1.2.tm | 20 ++++-- .../bootsupport/modules/shellfilter-0.2.tm | 72 +++---------------- .../bootsupport/modules/textblock-0.1.3.tm | 1 - .../src/bootsupport/modules/tomlish-1.1.6.tm | 13 +++- .../_vfscommon.vfs/modules/picalc-0.1.0.tm | 21 +++--- .../_vfscommon.vfs/modules/poshinfo-0.1.0.tm | 39 +++++----- .../_vfscommon.vfs/modules/punk/args-0.2.tm | 21 ++++-- .../punk/mix/commandset/loadedlib-0.1.0.tm | 56 +++++++++++---- .../_vfscommon.vfs/modules/punk/path-0.1.0.tm | 37 +++++++++- .../_vfscommon.vfs/modules/shellfilter-0.2.tm | 9 ++- 42 files changed, 766 insertions(+), 383 deletions(-) diff --git a/src/bootsupport/modules/punk/args-0.2.tm b/src/bootsupport/modules/punk/args-0.2.tm index f2b3c002..fc438d57 100644 --- a/src/bootsupport/modules/punk/args-0.2.tm +++ b/src/bootsupport/modules/punk/args-0.2.tm @@ -222,7 +222,7 @@ package require Tcl 8.6- tcl::namespace::eval punk::args::register { #*** !doctools - #[subsection {Namespace punk::args}] + #[subsection {Namespace punk::args::register}] #[para] cooperative namespace punk::args::register #[para] punk::args aware packages may add their own namespace to the public list variable NAMESPACES before or after punk::args is loaded #[para] The punk::args package will then test for a public list variable ::PUNKARGS containing argument definitions when it needs to. @@ -2606,11 +2606,11 @@ tcl::namespace::eval punk::args { @values {set defaults_key VALSPEC_DEFAULTS} } if {[dict exists $opt_override $type]} { - append result \n "$type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]]" - dict set resultdict $type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]] + append result \n "$type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]]" + dict set resultdict $type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]] } else { - append result \n "$type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS]" - dict set resultdict $type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] + append result \n "$type [dict get $specdict FORMS $formname $defaults_key]" + dict set resultdict $type [dict get $specdict FORMS $formname $defaults_key] } } } @@ -2856,6 +2856,19 @@ tcl::namespace::eval punk::args { return $result } + lappend PUNKARGS [list { + @id -id ::punk::args::update_definitions + @cmd -name punk::args::update_definitions\ + -summary\ + ""\ + -help\ + "" + @values -min 1 + id + arglist -type list -help\ + "list containing arguments to be parsed as per the + argument specification identified by the supplied id." + }] #scanned_packages (list) #namespace_docpackages (dict) proc update_definitions {{nslist *}} { diff --git a/src/bootsupport/modules/punk/docgen-0.1.0.tm b/src/bootsupport/modules/punk/docgen-0.1.0.tm index cea2d287..f3808c15 100644 --- a/src/bootsupport/modules/punk/docgen-0.1.0.tm +++ b/src/bootsupport/modules/punk/docgen-0.1.0.tm @@ -40,15 +40,30 @@ namespace eval punk::docgen { set data [string map [list \r\n \n] $data] set in_doctools 0 set doctools "" + #foreach ln [split $data \n] { + # set ln [string trim $ln] + # if {$in_doctools && [string index $ln 0] != "#"} { + # set in_doctools 0 + # } elseif {[string range $ln 0 1] == "#*"} { + # #todo - process doctools ordering hints in tail of line + # set in_doctools 1 + # } elseif {$in_doctools} { + # append doctools [string range $ln 1 end] \n + # } + #} foreach ln [split $data \n] { set ln [string trim $ln] - if {$in_doctools && [string index $ln 0] != "#"} { - set in_doctools 0 - } elseif {[string range $ln 0 1] == "#*"} { - #todo - process doctools ordering hints in tail of line - set in_doctools 1 - } elseif {$in_doctools} { - append doctools [string range $ln 1 end] \n + if {$in_doctools} { + if {[string index $ln 0] != "#"} { + set in_doctools 0 + } else { + append doctools [string range $ln 1 end] \n + } + } else { + if {[string range $ln 0 1] == "#*" && [string first "!doctools" $ln] >=2} { + #todo - process doctools ordering hints in tail of line + set in_doctools 1 + } } } return $doctools diff --git a/src/bootsupport/modules/punk/libunknown-0.1.tm b/src/bootsupport/modules/punk/libunknown-0.1.tm index 1b15d45a..3b5d35b0 100644 --- a/src/bootsupport/modules/punk/libunknown-0.1.tm +++ b/src/bootsupport/modules/punk/libunknown-0.1.tm @@ -17,7 +17,7 @@ # doctools header # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools -#[manpage_begin shellspy_module_punk::libunknown 0.1] +#[manpage_begin shellspy_module_punk::libunknown 0 0.1] #[copyright "2025"] #[titledesc {Module API}] [comment {-- Name section and table of contents description --}] #[moddesc {-}] [comment {-- Description at end of page heading --}] diff --git a/src/bootsupport/modules/punk/mix/cli-0.3.1.tm b/src/bootsupport/modules/punk/mix/cli-0.3.1.tm index 33d17404..afd06d2a 100644 --- a/src/bootsupport/modules/punk/mix/cli-0.3.1.tm +++ b/src/bootsupport/modules/punk/mix/cli-0.3.1.tm @@ -1083,20 +1083,28 @@ namespace eval punk::mix::cli { switch -- $calltype { lib {} shell { - set kettleappfile [file dirname [info nameofexecutable]]/kettle - set kettlebatfile [file dirname [info nameofexecutable]]/kettle.bat - - if {(![file exists $kettleappfile]) && (![file exists $kettlebatfile])} { - error "deck kettle_call unable to find installed kettle application file '$kettleappfile' (or '$kettlebatfile' if on windows)" - } - if {[file exists $kettleappfile]} { - set kettlescript $kettleappfile - } - if {$::tcl_platform(platform) eq "windows"} { - if {[file exists $kettlebatfile]} { - set kettlescript $kettlebatfile - } + #on windows - could be kettle.bat or kettle.cmd - use auto_execok to find, whatever the platform. + #for now, restrict to version sitting next to exe - REVIEW + set exedir [file dirname [info nameofexecutable]] + set kettlescript [auto_execok $exedir/kettle] + if {$kettlescript eq ""} { + error "kettle_call unable to find installed kettle application file in '$exedir'" } + + #set kettleappfile [file dirname [info nameofexecutable]]/kettle + #set kettlebatfile [file dirname [info nameofexecutable]]/kettle.bat + + #if {(![file exists $kettleappfile]) && (![file exists $kettlebatfile])} { + # error "deck kettle_call unable to find installed kettle application file '$kettleappfile' (or '$kettlebatfile' if on windows)" + #} + #if {[file exists $kettleappfile]} { + # set kettlescript $kettleappfile + #} + #if {$::tcl_platform(platform) eq "windows"} { + # if {[file exists $kettlebatfile]} { + # set kettlescript $kettlebatfile + # } + #} } default { error "deck kettle_call 1st argument must be one of: 'lib' for direct use of kettle module or 'shell' to call as separate process" diff --git a/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm b/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm index 3fb1e001..dc105f2f 100644 --- a/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm @@ -35,23 +35,39 @@ namespace eval punk::mix::commandset::loadedlib { -highlight -type boolean -default 1 -help\ "Highlight which version is present with ansi underline and colour" -refresh -default 0 -type boolean -help "Re-scan the tm and library folders" - searchstrings -default * -multiple 1 -help\ + searchstring -default * -multiple 1 -help\ "Names to search for, may contain glob chars (* ?) e.g *lib* - If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. - eg name -> *name* - To search for an exact name prefix it with = - e.g =name -> name - " + If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. + eg name -> *name* + To search for an exact name prefix it with = + e.g =name -> name + If search is not prefixed with '=' the search is case insensitive." } proc search {args} { set argd [punk::args::get_by_id ::punk::mix::commandset::loadedlib::search $args] - set searchstrings [dict get $argd values searchstrings] + set searchstrings [dict get $argd values searchstring] set opts [dict get $argd opts] set opt_return [dict get $opts -return] set opt_highlight [dict get $opts -highlight] + set opt_refresh [dict get $opts -refresh] + + if {$opt_refresh} { + catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans + foreach tm_path [tcl::tm::list] { + set paths_below [punk::path::subfolders -recursive $tm_path] + foreach folder $paths_below { + set tail [file tail $folder] + if {[string match #modpod-* $tail] || [string match #tarjar-* $tail]} { + continue + } + if {[string match */_build/* $folder]} {continue} + set relpath [string tolower [punk::path::relative $tm_path $folder]] + set modpath [string map {/ ::} $relpath] + catch {package require ${modpath}::flobrudder99} + } + } + } - #REVIEW - this doesn't result in full scans - catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything set packages [package names] set matches [list] @@ -122,9 +138,25 @@ namespace eval punk::mix::commandset::loadedlib { } } } - proc loaded.search {searchstring} { - set search_result [search $searchstring] - set all_libs [split $search_result \n] + + punk::args::define { + @id -id ::punk::mix::commandset::loadedlib::loaded.search + @cmd -name "punk::mix::commandset::loadedlib loaded.search"\ + -summary\ + "Search loaded libraries."\ + -help "search all Tcl libraries currently loaded in your local interpreter. + ie those that have been loaded directly or indirectly by 'package require'." + }\ + @values\ + [punk::args::resolved_def -types values ::punk::mix::commandset::loadedlib::search searchstring] + + proc loaded.search {args} { + set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::loaded.search] + lassign [dict values $argd] leaders opts values + set searchstrings [dict get $values searchstring] + + set all_libs [search -return list -highlight 0 {*}$searchstrings] + set col1items [list] set col2items [list] set col3items [list] diff --git a/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm b/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm index 277e386e..15dc9187 100644 --- a/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm @@ -41,11 +41,27 @@ namespace eval punk::mix::commandset::repo { return $result } + lappend PUNKARGS [list { + @id -id ::punk::mix::commandset::repo::fossilize + @cmd -name punk::mix::commandset::repo::fossilize + -summary\ + "Initialise and check in a project to fossil (unimplemented)."\ + -help\ + "(unimplemented)" + }] proc fossilize { args} { #check if project already managed by fossil.. initialise and check in if not. puts stderr "unimplemented" } + lappend PUNKARGS [list { + @id -id ::punk::mix::commandset::repo::unfossilize + @cmd -name punk::mix::commandset::repo::unfossilize + -summary\ + "Remove/archive .fossil (unimplemented)."\ + -help\ + "(unimplemented)" + }] proc unfossilize {projectname args} { #remove/archive .fossil puts stderr "unimplemented" @@ -76,14 +92,27 @@ namespace eval punk::mix::commandset::repo { #punk::args lappend PUNKARGS [list { @id -id ::punk::mix::commandset::repo::fossil-move-repository - @cmd -name punk::mix::commandset::repo::fossil-move-repository -help\ + @cmd -name punk::mix::commandset::repo::fossil-move-repository + -summary\ + "Move a fossil repository database file."\ + -help\ "Move the fossil repository file (usually named with .fossil extension). This is an interactive function which will prompt for answers on stdin before proceeding. The move can be done even if there are open checkouts and will maintain - the link between checkout databases and the repository file." + the link between checkout databases and the repository file. + + The call can be made from within a folder containing fossil databases, + or from within one of the checkouts of the fossil database that is to + be moved. + " + + #todo? + #@values -min 0 -max 1 + #path }] proc fossil-move-repository {{path ""}} { + #path unused for now - todo - allow calling with a specific target rather than relying on cwd? set searchbase [pwd] set projectinfo [punk::repo::find_repos $searchbase] set projectbase [dict get $projectinfo closest] diff --git a/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm b/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm index 24168483..4f1af2bc 100644 --- a/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm @@ -77,7 +77,7 @@ namespace eval punk::mix::commandset::scriptwrap { #[para] Core API functions for punk::mix::commandset::scriptwrap #[list_begin definitions] - namespace export * + namespace export {[a-z]*} namespace eval fileline { namespace import ::punk::fileline::lib::* diff --git a/src/bootsupport/modules/punk/path-0.1.0.tm b/src/bootsupport/modules/punk/path-0.1.0.tm index 54ee4080..0268dd75 100644 --- a/src/bootsupport/modules/punk/path-0.1.0.tm +++ b/src/bootsupport/modules/punk/path-0.1.0.tm @@ -99,7 +99,7 @@ package require punk::args # Base namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punk::path { - namespace export * + namespace export {[a-z]*} #variable xyz #*** !doctools @@ -645,6 +645,41 @@ namespace eval punk::path { } return $ismatch } + punk::args::define { + @id -id ::punk::path::subfolders + @cmd -name punk::path::subfolders\ + -summary\ + "Listing of directories within supplied path."\ + -help\ + "List of folders below path. + The resulting list is unsorted." + @opts + -recursive -type none -help\ + "" + #todo -depth + @values -min 0 -max 1 + path -type directory -optional 1 -help\ + "Path of folder. If not supplied current directory is used." + } + proc subfolders {args} { + set argd [punk::args::parse $args withid ::punk::path::subfolders] + lassign [dict values $argd] leaders opts values received + set do_recursion [dict exists $received -recursive] + if {[dict exists $received path]} { + set path [dict get $values path] + } else { + set path [pwd] + } + set folders [glob -nocomplain -directory $path -types d *] + if {$do_recursion} { + foreach subdir $folders { + lappend folders {*}[subfolders -recursive $subdir] + } + } + return $folders + } + + #todo - treefolders with similar search caps as treefilenames punk::args::define { @id -id ::punk::path::treefilenames diff --git a/src/bootsupport/modules/punk/repl-0.1.2.tm b/src/bootsupport/modules/punk/repl-0.1.2.tm index fd84ec8d..731e263e 100644 --- a/src/bootsupport/modules/punk/repl-0.1.2.tm +++ b/src/bootsupport/modules/punk/repl-0.1.2.tm @@ -522,7 +522,14 @@ proc repl::start {inchan args} { set codethread "" set codethread_cond "" punk::console::mode line ;#review - revert to line mode on final exit - but we may be exiting a nested repl - puts "end repl::start" + set donevalue [set [namespace current]::done] + if {[lindex $donevalue 0] eq "quit"} { + puts "-->repl::start end $inchan $args result:'$donevalue'" + puts stderr "--> returning [lindex $donevalue 1]" + return [lindex $donevalue 1] + } + puts "-->repl::start end $inchan $args result:'$donevalue'" + puts stderr "__> returning 0" return 0 } proc repl::post_operations {} { @@ -570,7 +577,7 @@ proc repl::reopen_stdin {} { #todo - avoid putting this in gobal namespace? #collisions with other libraries apps? proc punk::repl::quit {args} { - set ::repl::done "quit {*}$args" + set ::repl::done [list quit {*}$args] #puts stderr "quit called" return "" ;#make sure to return nothing so "quit" doesn't land on stdout } @@ -800,8 +807,7 @@ proc repl::rputs {args} { } } set last_char_info_width 60 - #review - string shouldn't be truncated prior to stripcodes - could chop ansi codes! - #set summary "[::shellfilter::ansi::stripcodes [string range $out 0 $last_char_info_width]]" + #review - string shouldn't be truncated prior to ansistrip - could chop ansi codes! set out_plain_text [punk::ansi::ansistrip $out] set summary [string range $out_plain_text 0 $last_char_info_width] if {[string length $summary] > $last_char_info_width} { @@ -1610,6 +1616,8 @@ proc repl::repl_handler {inputchan prompt_config} { if {![llength $input_chunks_waiting($inputchan)]} { chan event $inputchan readable [list ::repl::repl_handler $inputchan $prompt_config] } else { + #review + #puts stderr "warning: after idle re-enable repl::repl_handler in thread: [thread::id]" after idle [list ::repl::repl_handler $inputchan $prompt_config] } #################################################### @@ -2695,6 +2703,7 @@ namespace eval repl { proc init {args} { + puts stderr "-->repl::init $args" if {![info exists ::argv0]} { #error out before we create a thread - punk requires this - review error "::argv0 not set" @@ -2900,7 +2909,7 @@ namespace eval repl { proc quit {args} { #child codethreads run in a 'code' interp therefore if they started another repl - it is within the 'code' interp in that thread # whereas the first repl launched in the process runs in root interp "" - thread::send -async %replthread% [list interp eval %replthread_interp% ::punk::repl::quit] + thread::send -async %replthread% [list interp eval %replthread_interp% [list ::punk::repl::quit {*}$args]] } proc editbuf args { thread::send %replthread% [list punk::repl::editbuf {*}$args] @@ -3623,6 +3632,7 @@ namespace eval repl { #temporary debug aliases - deliberate violation of safety provided by safe interp code alias escapeeval ::repl::interphelpers::escapeeval + code alias exit ::repl::interphelpers::quit #experiment #code alias ::shellfilter::stack ::shellfilter::stack diff --git a/src/bootsupport/modules/shellfilter-0.2.tm b/src/bootsupport/modules/shellfilter-0.2.tm index 9aa98332..e04e5107 100644 --- a/src/bootsupport/modules/shellfilter-0.2.tm +++ b/src/bootsupport/modules/shellfilter-0.2.tm @@ -152,65 +152,6 @@ namespace eval shellfilter::pipe { } - -namespace eval shellfilter::ansi { - #maint warning - - #ansistrip from punk::ansi is better/more comprehensive - proc stripcodes {text} { - #obsolete? - #single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~). - dict set escape_terminals CSI [list @ \\ ^ _ ` | ~ 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 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 "\{" "\}"] - #dict set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic - dict set escape_terminals OSC [list \007 \033\\] ;#note mix of 1 and 2-byte terminals - #we process char by char - line-endings whether \r\n or \n should be processed as per any other character. - #line endings can theoretically occur within an ansi escape sequence (review e.g title?) - set inputlist [split $text ""] - set outputlist [list] - - #self-contained 2 byte ansi escape sequences - review more? - set 2bytecodes_dict [dict create\ - "reset_terminal" "\033c"\ - "save_cursor_posn" "\u001b7"\ - "restore_cursor_posn" "\u001b8"\ - "cursor_up_one" "\u001bM"\ - ] - set 2bytecodes [dict values $2bytecodes_dict] - - set in_escapesequence 0 - #assumption - undertext already 'rendered' - ie no backspaces or carriagereturns or other cursor movement controls - set i 0 - foreach u $inputlist { - set v [lindex $inputlist $i+1] - set uv ${u}${v} - if {$in_escapesequence eq "2b"} { - #2nd byte - done. - set in_escapesequence 0 - } elseif {$in_escapesequence != 0} { - set escseq [dict get $escape_terminals $in_escapesequence] - if {$u in $escseq} { - set in_escapesequence 0 - } elseif {$uv in $escseq} { - set in_escapseequence 2b ;#flag next byte as last in sequence - } - } else { - #handle both 7-bit and 8-bit CSI and OSC - if {[regexp {^(?:\033\[|\u009b)} $uv]} { - set in_escapesequence CSI - } elseif {[regexp {^(?:\033\]|\u009c)} $uv]} { - set in_escapesequence OSC - } elseif {$uv in $2bytecodes} { - #self-contained e.g terminal reset - don't pass through. - set in_escapesequence 2b - } else { - lappend outputlist $u - } - } - incr i - } - return [join $outputlist ""] - } - -} namespace eval shellfilter::chan { set testobj ::shellfilter::chan::var if {$testobj ni [info commands $testobj]} { @@ -2851,8 +2792,10 @@ namespace eval shellfilter { - + #chan configure $inchan -buffering none -blocking 1 ;#test chan configure $inchan -buffering $inbuffering -blocking 0 ;#we are setting up a readable handler for this - so non-blocking ok + + chan configure $errchan -buffering $errbuffering #chan configure $outchan -blocking 0 chan configure $outchan -buffering $outbuffering ;#don't configure non-blocking. weird duplicate of *second* line occurs if you do. @@ -2888,7 +2831,14 @@ namespace eval shellfilter { # Ideally we need something like exec,open in tcl that interacts with transformed channels directly and emits as it runs, not only at termination # - and that at least appears like a terminal to the called command. #set rdout [open |[concat $commandlist [list 2>@stderr <@$inchan]] [list RDONLY]] - + + #REVIEW! + #if the child process takes a while to begin reading stdin - the data on stdin between when we stopped the parent chan event handler and when the child gets data, + #seems to stay buffered somewhere. It is then read by the parent, after the child returns. (ie not lost, but out-of-order) + #This can be apparent sometimes even with fast typing upon calling an executable. (e.g occasionally even vim - but seems to be timing based so might only happen first time if at all) + # see scriptlib/stdin_race.tcl etc test files. + #similar problem with python & perl - issue seems to be in libc or OS buffering behaviour for standard channels. + #note that zig (repo/jn/zig/stdin_race) seems to avoid this issue - todo - make zig based binary extension for open/exec? set rdout [open |[concat $commandlist [list 2>@$wrerr <@$inchan]] [list RDONLY]] diff --git a/src/bootsupport/modules/textblock-0.1.3.tm b/src/bootsupport/modules/textblock-0.1.3.tm index f2f4a3af..de0164cd 100644 --- a/src/bootsupport/modules/textblock-0.1.3.tm +++ b/src/bootsupport/modules/textblock-0.1.3.tm @@ -4899,7 +4899,6 @@ tcl::namespace::eval textblock { set textblock [punk::ansi::ansistripraw $textblock] } if {[tcl::string::last \n $textblock] >= 0} { - #return [tcl::mathfunc::max {*}[lmap v [split $textblock \n] {::punk::char::ansifreestring_width $v}]] set widths [lmap v [split $textblock \n] {::punk::char::ansifreestring_width $v}] set max [tcl::mathfunc::max {*}$widths] set min [tcl::mathfunc::min {*}$widths] diff --git a/src/bootsupport/modules/tomlish-1.1.6.tm b/src/bootsupport/modules/tomlish-1.1.6.tm index 7abbaeae..d7d1f131 100644 --- a/src/bootsupport/modules/tomlish-1.1.6.tm +++ b/src/bootsupport/modules/tomlish-1.1.6.tm @@ -2012,6 +2012,8 @@ namespace eval tomlish { #updatables #review - type changes from existing value?? set sourcedata [lindex $source_d_elements $arridx] + #todo - what happens when less source elements than in existing array? ie sourcedata is empty. + # switch -- $arrchild_type { STRING - LITERAL - FLOAT - INT - DATETIME - DATETIME-LOCAL - DATE-LOCAL - TIME-LOCAL { #basic types - no recursion needed @@ -2036,7 +2038,7 @@ namespace eval tomlish { } ITABLE - ARRAY { #recurse - puts stderr "update $tomlish_type within array" + puts stderr "update $arrchild_type within array" set nextd [tomlish::dict::path::get $d $arridx] set subrecord_tomlish [list $arrchild] set newsubrecord_tomlish [update_tomlish_from_dict $subrecord_tomlish $nextd] @@ -9240,8 +9242,15 @@ namespace eval tomlish::lib { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval tomlish::system { + #*** !doctools + #[subsection {Namespace tomlish::system}] + #[para] + #[list_begin definitions] + + #taken from punk::lib + #todo - change list argument to integer length proc lindex_resolve_basic {list index} { #*** !doctools #[call [fun lindex_resolve_basic] [arg list] [arg index]] @@ -9360,6 +9369,8 @@ namespace eval tomlish::system { } } + #*** !doctools + #[list_end] [comment {--- end definitions namespace tomlish::system ---}] } if {[info exists ::argc] && $::argc > 0} { diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.tm index f2b3c002..fc438d57 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/args-0.2.tm @@ -222,7 +222,7 @@ package require Tcl 8.6- tcl::namespace::eval punk::args::register { #*** !doctools - #[subsection {Namespace punk::args}] + #[subsection {Namespace punk::args::register}] #[para] cooperative namespace punk::args::register #[para] punk::args aware packages may add their own namespace to the public list variable NAMESPACES before or after punk::args is loaded #[para] The punk::args package will then test for a public list variable ::PUNKARGS containing argument definitions when it needs to. @@ -2606,11 +2606,11 @@ tcl::namespace::eval punk::args { @values {set defaults_key VALSPEC_DEFAULTS} } if {[dict exists $opt_override $type]} { - append result \n "$type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]]" - dict set resultdict $type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]] + append result \n "$type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]]" + dict set resultdict $type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]] } else { - append result \n "$type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS]" - dict set resultdict $type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] + append result \n "$type [dict get $specdict FORMS $formname $defaults_key]" + dict set resultdict $type [dict get $specdict FORMS $formname $defaults_key] } } } @@ -2856,6 +2856,19 @@ tcl::namespace::eval punk::args { return $result } + lappend PUNKARGS [list { + @id -id ::punk::args::update_definitions + @cmd -name punk::args::update_definitions\ + -summary\ + ""\ + -help\ + "" + @values -min 1 + id + arglist -type list -help\ + "list containing arguments to be parsed as per the + argument specification identified by the supplied id." + }] #scanned_packages (list) #namespace_docpackages (dict) proc update_definitions {{nslist *}} { diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm index cea2d287..f3808c15 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm @@ -40,15 +40,30 @@ namespace eval punk::docgen { set data [string map [list \r\n \n] $data] set in_doctools 0 set doctools "" + #foreach ln [split $data \n] { + # set ln [string trim $ln] + # if {$in_doctools && [string index $ln 0] != "#"} { + # set in_doctools 0 + # } elseif {[string range $ln 0 1] == "#*"} { + # #todo - process doctools ordering hints in tail of line + # set in_doctools 1 + # } elseif {$in_doctools} { + # append doctools [string range $ln 1 end] \n + # } + #} foreach ln [split $data \n] { set ln [string trim $ln] - if {$in_doctools && [string index $ln 0] != "#"} { - set in_doctools 0 - } elseif {[string range $ln 0 1] == "#*"} { - #todo - process doctools ordering hints in tail of line - set in_doctools 1 - } elseif {$in_doctools} { - append doctools [string range $ln 1 end] \n + if {$in_doctools} { + if {[string index $ln 0] != "#"} { + set in_doctools 0 + } else { + append doctools [string range $ln 1 end] \n + } + } else { + if {[string range $ln 0 1] == "#*" && [string first "!doctools" $ln] >=2} { + #todo - process doctools ordering hints in tail of line + set in_doctools 1 + } } } return $doctools diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm index 1b15d45a..3b5d35b0 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm @@ -17,7 +17,7 @@ # doctools header # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools -#[manpage_begin shellspy_module_punk::libunknown 0.1] +#[manpage_begin shellspy_module_punk::libunknown 0 0.1] #[copyright "2025"] #[titledesc {Module API}] [comment {-- Name section and table of contents description --}] #[moddesc {-}] [comment {-- Description at end of page heading --}] diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm index 33d17404..afd06d2a 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm @@ -1083,20 +1083,28 @@ namespace eval punk::mix::cli { switch -- $calltype { lib {} shell { - set kettleappfile [file dirname [info nameofexecutable]]/kettle - set kettlebatfile [file dirname [info nameofexecutable]]/kettle.bat - - if {(![file exists $kettleappfile]) && (![file exists $kettlebatfile])} { - error "deck kettle_call unable to find installed kettle application file '$kettleappfile' (or '$kettlebatfile' if on windows)" - } - if {[file exists $kettleappfile]} { - set kettlescript $kettleappfile - } - if {$::tcl_platform(platform) eq "windows"} { - if {[file exists $kettlebatfile]} { - set kettlescript $kettlebatfile - } + #on windows - could be kettle.bat or kettle.cmd - use auto_execok to find, whatever the platform. + #for now, restrict to version sitting next to exe - REVIEW + set exedir [file dirname [info nameofexecutable]] + set kettlescript [auto_execok $exedir/kettle] + if {$kettlescript eq ""} { + error "kettle_call unable to find installed kettle application file in '$exedir'" } + + #set kettleappfile [file dirname [info nameofexecutable]]/kettle + #set kettlebatfile [file dirname [info nameofexecutable]]/kettle.bat + + #if {(![file exists $kettleappfile]) && (![file exists $kettlebatfile])} { + # error "deck kettle_call unable to find installed kettle application file '$kettleappfile' (or '$kettlebatfile' if on windows)" + #} + #if {[file exists $kettleappfile]} { + # set kettlescript $kettleappfile + #} + #if {$::tcl_platform(platform) eq "windows"} { + # if {[file exists $kettlebatfile]} { + # set kettlescript $kettlebatfile + # } + #} } default { error "deck kettle_call 1st argument must be one of: 'lib' for direct use of kettle module or 'shell' to call as separate process" diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm index 3fb1e001..dc105f2f 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm @@ -35,23 +35,39 @@ namespace eval punk::mix::commandset::loadedlib { -highlight -type boolean -default 1 -help\ "Highlight which version is present with ansi underline and colour" -refresh -default 0 -type boolean -help "Re-scan the tm and library folders" - searchstrings -default * -multiple 1 -help\ + searchstring -default * -multiple 1 -help\ "Names to search for, may contain glob chars (* ?) e.g *lib* - If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. - eg name -> *name* - To search for an exact name prefix it with = - e.g =name -> name - " + If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. + eg name -> *name* + To search for an exact name prefix it with = + e.g =name -> name + If search is not prefixed with '=' the search is case insensitive." } proc search {args} { set argd [punk::args::get_by_id ::punk::mix::commandset::loadedlib::search $args] - set searchstrings [dict get $argd values searchstrings] + set searchstrings [dict get $argd values searchstring] set opts [dict get $argd opts] set opt_return [dict get $opts -return] set opt_highlight [dict get $opts -highlight] + set opt_refresh [dict get $opts -refresh] + + if {$opt_refresh} { + catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans + foreach tm_path [tcl::tm::list] { + set paths_below [punk::path::subfolders -recursive $tm_path] + foreach folder $paths_below { + set tail [file tail $folder] + if {[string match #modpod-* $tail] || [string match #tarjar-* $tail]} { + continue + } + if {[string match */_build/* $folder]} {continue} + set relpath [string tolower [punk::path::relative $tm_path $folder]] + set modpath [string map {/ ::} $relpath] + catch {package require ${modpath}::flobrudder99} + } + } + } - #REVIEW - this doesn't result in full scans - catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything set packages [package names] set matches [list] @@ -122,9 +138,25 @@ namespace eval punk::mix::commandset::loadedlib { } } } - proc loaded.search {searchstring} { - set search_result [search $searchstring] - set all_libs [split $search_result \n] + + punk::args::define { + @id -id ::punk::mix::commandset::loadedlib::loaded.search + @cmd -name "punk::mix::commandset::loadedlib loaded.search"\ + -summary\ + "Search loaded libraries."\ + -help "search all Tcl libraries currently loaded in your local interpreter. + ie those that have been loaded directly or indirectly by 'package require'." + }\ + @values\ + [punk::args::resolved_def -types values ::punk::mix::commandset::loadedlib::search searchstring] + + proc loaded.search {args} { + set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::loaded.search] + lassign [dict values $argd] leaders opts values + set searchstrings [dict get $values searchstring] + + set all_libs [search -return list -highlight 0 {*}$searchstrings] + set col1items [list] set col2items [list] set col3items [list] diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm index 277e386e..15dc9187 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm @@ -41,11 +41,27 @@ namespace eval punk::mix::commandset::repo { return $result } + lappend PUNKARGS [list { + @id -id ::punk::mix::commandset::repo::fossilize + @cmd -name punk::mix::commandset::repo::fossilize + -summary\ + "Initialise and check in a project to fossil (unimplemented)."\ + -help\ + "(unimplemented)" + }] proc fossilize { args} { #check if project already managed by fossil.. initialise and check in if not. puts stderr "unimplemented" } + lappend PUNKARGS [list { + @id -id ::punk::mix::commandset::repo::unfossilize + @cmd -name punk::mix::commandset::repo::unfossilize + -summary\ + "Remove/archive .fossil (unimplemented)."\ + -help\ + "(unimplemented)" + }] proc unfossilize {projectname args} { #remove/archive .fossil puts stderr "unimplemented" @@ -76,14 +92,27 @@ namespace eval punk::mix::commandset::repo { #punk::args lappend PUNKARGS [list { @id -id ::punk::mix::commandset::repo::fossil-move-repository - @cmd -name punk::mix::commandset::repo::fossil-move-repository -help\ + @cmd -name punk::mix::commandset::repo::fossil-move-repository + -summary\ + "Move a fossil repository database file."\ + -help\ "Move the fossil repository file (usually named with .fossil extension). This is an interactive function which will prompt for answers on stdin before proceeding. The move can be done even if there are open checkouts and will maintain - the link between checkout databases and the repository file." + the link between checkout databases and the repository file. + + The call can be made from within a folder containing fossil databases, + or from within one of the checkouts of the fossil database that is to + be moved. + " + + #todo? + #@values -min 0 -max 1 + #path }] proc fossil-move-repository {{path ""}} { + #path unused for now - todo - allow calling with a specific target rather than relying on cwd? set searchbase [pwd] set projectinfo [punk::repo::find_repos $searchbase] set projectbase [dict get $projectinfo closest] diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm index 24168483..4f1af2bc 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm @@ -77,7 +77,7 @@ namespace eval punk::mix::commandset::scriptwrap { #[para] Core API functions for punk::mix::commandset::scriptwrap #[list_begin definitions] - namespace export * + namespace export {[a-z]*} namespace eval fileline { namespace import ::punk::fileline::lib::* diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/path-0.1.0.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/path-0.1.0.tm index 54ee4080..0268dd75 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/path-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/path-0.1.0.tm @@ -99,7 +99,7 @@ package require punk::args # Base namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punk::path { - namespace export * + namespace export {[a-z]*} #variable xyz #*** !doctools @@ -645,6 +645,41 @@ namespace eval punk::path { } return $ismatch } + punk::args::define { + @id -id ::punk::path::subfolders + @cmd -name punk::path::subfolders\ + -summary\ + "Listing of directories within supplied path."\ + -help\ + "List of folders below path. + The resulting list is unsorted." + @opts + -recursive -type none -help\ + "" + #todo -depth + @values -min 0 -max 1 + path -type directory -optional 1 -help\ + "Path of folder. If not supplied current directory is used." + } + proc subfolders {args} { + set argd [punk::args::parse $args withid ::punk::path::subfolders] + lassign [dict values $argd] leaders opts values received + set do_recursion [dict exists $received -recursive] + if {[dict exists $received path]} { + set path [dict get $values path] + } else { + set path [pwd] + } + set folders [glob -nocomplain -directory $path -types d *] + if {$do_recursion} { + foreach subdir $folders { + lappend folders {*}[subfolders -recursive $subdir] + } + } + return $folders + } + + #todo - treefolders with similar search caps as treefilenames punk::args::define { @id -id ::punk::path::treefilenames diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm index fd84ec8d..731e263e 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm @@ -522,7 +522,14 @@ proc repl::start {inchan args} { set codethread "" set codethread_cond "" punk::console::mode line ;#review - revert to line mode on final exit - but we may be exiting a nested repl - puts "end repl::start" + set donevalue [set [namespace current]::done] + if {[lindex $donevalue 0] eq "quit"} { + puts "-->repl::start end $inchan $args result:'$donevalue'" + puts stderr "--> returning [lindex $donevalue 1]" + return [lindex $donevalue 1] + } + puts "-->repl::start end $inchan $args result:'$donevalue'" + puts stderr "__> returning 0" return 0 } proc repl::post_operations {} { @@ -570,7 +577,7 @@ proc repl::reopen_stdin {} { #todo - avoid putting this in gobal namespace? #collisions with other libraries apps? proc punk::repl::quit {args} { - set ::repl::done "quit {*}$args" + set ::repl::done [list quit {*}$args] #puts stderr "quit called" return "" ;#make sure to return nothing so "quit" doesn't land on stdout } @@ -800,8 +807,7 @@ proc repl::rputs {args} { } } set last_char_info_width 60 - #review - string shouldn't be truncated prior to stripcodes - could chop ansi codes! - #set summary "[::shellfilter::ansi::stripcodes [string range $out 0 $last_char_info_width]]" + #review - string shouldn't be truncated prior to ansistrip - could chop ansi codes! set out_plain_text [punk::ansi::ansistrip $out] set summary [string range $out_plain_text 0 $last_char_info_width] if {[string length $summary] > $last_char_info_width} { @@ -1610,6 +1616,8 @@ proc repl::repl_handler {inputchan prompt_config} { if {![llength $input_chunks_waiting($inputchan)]} { chan event $inputchan readable [list ::repl::repl_handler $inputchan $prompt_config] } else { + #review + #puts stderr "warning: after idle re-enable repl::repl_handler in thread: [thread::id]" after idle [list ::repl::repl_handler $inputchan $prompt_config] } #################################################### @@ -2695,6 +2703,7 @@ namespace eval repl { proc init {args} { + puts stderr "-->repl::init $args" if {![info exists ::argv0]} { #error out before we create a thread - punk requires this - review error "::argv0 not set" @@ -2900,7 +2909,7 @@ namespace eval repl { proc quit {args} { #child codethreads run in a 'code' interp therefore if they started another repl - it is within the 'code' interp in that thread # whereas the first repl launched in the process runs in root interp "" - thread::send -async %replthread% [list interp eval %replthread_interp% ::punk::repl::quit] + thread::send -async %replthread% [list interp eval %replthread_interp% [list ::punk::repl::quit {*}$args]] } proc editbuf args { thread::send %replthread% [list punk::repl::editbuf {*}$args] @@ -3623,6 +3632,7 @@ namespace eval repl { #temporary debug aliases - deliberate violation of safety provided by safe interp code alias escapeeval ::repl::interphelpers::escapeeval + code alias exit ::repl::interphelpers::quit #experiment #code alias ::shellfilter::stack ::shellfilter::stack diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.2.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.2.tm index 9aa98332..e04e5107 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.2.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/shellfilter-0.2.tm @@ -152,65 +152,6 @@ namespace eval shellfilter::pipe { } - -namespace eval shellfilter::ansi { - #maint warning - - #ansistrip from punk::ansi is better/more comprehensive - proc stripcodes {text} { - #obsolete? - #single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~). - dict set escape_terminals CSI [list @ \\ ^ _ ` | ~ 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 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 "\{" "\}"] - #dict set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic - dict set escape_terminals OSC [list \007 \033\\] ;#note mix of 1 and 2-byte terminals - #we process char by char - line-endings whether \r\n or \n should be processed as per any other character. - #line endings can theoretically occur within an ansi escape sequence (review e.g title?) - set inputlist [split $text ""] - set outputlist [list] - - #self-contained 2 byte ansi escape sequences - review more? - set 2bytecodes_dict [dict create\ - "reset_terminal" "\033c"\ - "save_cursor_posn" "\u001b7"\ - "restore_cursor_posn" "\u001b8"\ - "cursor_up_one" "\u001bM"\ - ] - set 2bytecodes [dict values $2bytecodes_dict] - - set in_escapesequence 0 - #assumption - undertext already 'rendered' - ie no backspaces or carriagereturns or other cursor movement controls - set i 0 - foreach u $inputlist { - set v [lindex $inputlist $i+1] - set uv ${u}${v} - if {$in_escapesequence eq "2b"} { - #2nd byte - done. - set in_escapesequence 0 - } elseif {$in_escapesequence != 0} { - set escseq [dict get $escape_terminals $in_escapesequence] - if {$u in $escseq} { - set in_escapesequence 0 - } elseif {$uv in $escseq} { - set in_escapseequence 2b ;#flag next byte as last in sequence - } - } else { - #handle both 7-bit and 8-bit CSI and OSC - if {[regexp {^(?:\033\[|\u009b)} $uv]} { - set in_escapesequence CSI - } elseif {[regexp {^(?:\033\]|\u009c)} $uv]} { - set in_escapesequence OSC - } elseif {$uv in $2bytecodes} { - #self-contained e.g terminal reset - don't pass through. - set in_escapesequence 2b - } else { - lappend outputlist $u - } - } - incr i - } - return [join $outputlist ""] - } - -} namespace eval shellfilter::chan { set testobj ::shellfilter::chan::var if {$testobj ni [info commands $testobj]} { @@ -2851,8 +2792,10 @@ namespace eval shellfilter { - + #chan configure $inchan -buffering none -blocking 1 ;#test chan configure $inchan -buffering $inbuffering -blocking 0 ;#we are setting up a readable handler for this - so non-blocking ok + + chan configure $errchan -buffering $errbuffering #chan configure $outchan -blocking 0 chan configure $outchan -buffering $outbuffering ;#don't configure non-blocking. weird duplicate of *second* line occurs if you do. @@ -2888,7 +2831,14 @@ namespace eval shellfilter { # Ideally we need something like exec,open in tcl that interacts with transformed channels directly and emits as it runs, not only at termination # - and that at least appears like a terminal to the called command. #set rdout [open |[concat $commandlist [list 2>@stderr <@$inchan]] [list RDONLY]] - + + #REVIEW! + #if the child process takes a while to begin reading stdin - the data on stdin between when we stopped the parent chan event handler and when the child gets data, + #seems to stay buffered somewhere. It is then read by the parent, after the child returns. (ie not lost, but out-of-order) + #This can be apparent sometimes even with fast typing upon calling an executable. (e.g occasionally even vim - but seems to be timing based so might only happen first time if at all) + # see scriptlib/stdin_race.tcl etc test files. + #similar problem with python & perl - issue seems to be in libc or OS buffering behaviour for standard channels. + #note that zig (repo/jn/zig/stdin_race) seems to avoid this issue - todo - make zig based binary extension for open/exec? set rdout [open |[concat $commandlist [list 2>@$wrerr <@$inchan]] [list RDONLY]] diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm index f2f4a3af..de0164cd 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/textblock-0.1.3.tm @@ -4899,7 +4899,6 @@ tcl::namespace::eval textblock { set textblock [punk::ansi::ansistripraw $textblock] } if {[tcl::string::last \n $textblock] >= 0} { - #return [tcl::mathfunc::max {*}[lmap v [split $textblock \n] {::punk::char::ansifreestring_width $v}]] set widths [lmap v [split $textblock \n] {::punk::char::ansifreestring_width $v}] set max [tcl::mathfunc::max {*}$widths] set min [tcl::mathfunc::min {*}$widths] diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/tomlish-1.1.6.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/tomlish-1.1.6.tm index 7abbaeae..d7d1f131 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/tomlish-1.1.6.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/tomlish-1.1.6.tm @@ -2012,6 +2012,8 @@ namespace eval tomlish { #updatables #review - type changes from existing value?? set sourcedata [lindex $source_d_elements $arridx] + #todo - what happens when less source elements than in existing array? ie sourcedata is empty. + # switch -- $arrchild_type { STRING - LITERAL - FLOAT - INT - DATETIME - DATETIME-LOCAL - DATE-LOCAL - TIME-LOCAL { #basic types - no recursion needed @@ -2036,7 +2038,7 @@ namespace eval tomlish { } ITABLE - ARRAY { #recurse - puts stderr "update $tomlish_type within array" + puts stderr "update $arrchild_type within array" set nextd [tomlish::dict::path::get $d $arridx] set subrecord_tomlish [list $arrchild] set newsubrecord_tomlish [update_tomlish_from_dict $subrecord_tomlish $nextd] @@ -9240,8 +9242,15 @@ namespace eval tomlish::lib { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval tomlish::system { + #*** !doctools + #[subsection {Namespace tomlish::system}] + #[para] + #[list_begin definitions] + + #taken from punk::lib + #todo - change list argument to integer length proc lindex_resolve_basic {list index} { #*** !doctools #[call [fun lindex_resolve_basic] [arg list] [arg index]] @@ -9360,6 +9369,8 @@ namespace eval tomlish::system { } } + #*** !doctools + #[list_end] [comment {--- end definitions namespace tomlish::system ---}] } if {[info exists ::argc] && $::argc > 0} { diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.tm index f2b3c002..fc438d57 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/args-0.2.tm @@ -222,7 +222,7 @@ package require Tcl 8.6- tcl::namespace::eval punk::args::register { #*** !doctools - #[subsection {Namespace punk::args}] + #[subsection {Namespace punk::args::register}] #[para] cooperative namespace punk::args::register #[para] punk::args aware packages may add their own namespace to the public list variable NAMESPACES before or after punk::args is loaded #[para] The punk::args package will then test for a public list variable ::PUNKARGS containing argument definitions when it needs to. @@ -2606,11 +2606,11 @@ tcl::namespace::eval punk::args { @values {set defaults_key VALSPEC_DEFAULTS} } if {[dict exists $opt_override $type]} { - append result \n "$type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]]" - dict set resultdict $type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]] + append result \n "$type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]]" + dict set resultdict $type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]] } else { - append result \n "$type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS]" - dict set resultdict $type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] + append result \n "$type [dict get $specdict FORMS $formname $defaults_key]" + dict set resultdict $type [dict get $specdict FORMS $formname $defaults_key] } } } @@ -2856,6 +2856,19 @@ tcl::namespace::eval punk::args { return $result } + lappend PUNKARGS [list { + @id -id ::punk::args::update_definitions + @cmd -name punk::args::update_definitions\ + -summary\ + ""\ + -help\ + "" + @values -min 1 + id + arglist -type list -help\ + "list containing arguments to be parsed as per the + argument specification identified by the supplied id." + }] #scanned_packages (list) #namespace_docpackages (dict) proc update_definitions {{nslist *}} { diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm index cea2d287..f3808c15 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/docgen-0.1.0.tm @@ -40,15 +40,30 @@ namespace eval punk::docgen { set data [string map [list \r\n \n] $data] set in_doctools 0 set doctools "" + #foreach ln [split $data \n] { + # set ln [string trim $ln] + # if {$in_doctools && [string index $ln 0] != "#"} { + # set in_doctools 0 + # } elseif {[string range $ln 0 1] == "#*"} { + # #todo - process doctools ordering hints in tail of line + # set in_doctools 1 + # } elseif {$in_doctools} { + # append doctools [string range $ln 1 end] \n + # } + #} foreach ln [split $data \n] { set ln [string trim $ln] - if {$in_doctools && [string index $ln 0] != "#"} { - set in_doctools 0 - } elseif {[string range $ln 0 1] == "#*"} { - #todo - process doctools ordering hints in tail of line - set in_doctools 1 - } elseif {$in_doctools} { - append doctools [string range $ln 1 end] \n + if {$in_doctools} { + if {[string index $ln 0] != "#"} { + set in_doctools 0 + } else { + append doctools [string range $ln 1 end] \n + } + } else { + if {[string range $ln 0 1] == "#*" && [string first "!doctools" $ln] >=2} { + #todo - process doctools ordering hints in tail of line + set in_doctools 1 + } } } return $doctools diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm index 1b15d45a..3b5d35b0 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/libunknown-0.1.tm @@ -17,7 +17,7 @@ # doctools header # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools -#[manpage_begin shellspy_module_punk::libunknown 0.1] +#[manpage_begin shellspy_module_punk::libunknown 0 0.1] #[copyright "2025"] #[titledesc {Module API}] [comment {-- Name section and table of contents description --}] #[moddesc {-}] [comment {-- Description at end of page heading --}] diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm index 33d17404..afd06d2a 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/cli-0.3.1.tm @@ -1083,20 +1083,28 @@ namespace eval punk::mix::cli { switch -- $calltype { lib {} shell { - set kettleappfile [file dirname [info nameofexecutable]]/kettle - set kettlebatfile [file dirname [info nameofexecutable]]/kettle.bat - - if {(![file exists $kettleappfile]) && (![file exists $kettlebatfile])} { - error "deck kettle_call unable to find installed kettle application file '$kettleappfile' (or '$kettlebatfile' if on windows)" - } - if {[file exists $kettleappfile]} { - set kettlescript $kettleappfile - } - if {$::tcl_platform(platform) eq "windows"} { - if {[file exists $kettlebatfile]} { - set kettlescript $kettlebatfile - } + #on windows - could be kettle.bat or kettle.cmd - use auto_execok to find, whatever the platform. + #for now, restrict to version sitting next to exe - REVIEW + set exedir [file dirname [info nameofexecutable]] + set kettlescript [auto_execok $exedir/kettle] + if {$kettlescript eq ""} { + error "kettle_call unable to find installed kettle application file in '$exedir'" } + + #set kettleappfile [file dirname [info nameofexecutable]]/kettle + #set kettlebatfile [file dirname [info nameofexecutable]]/kettle.bat + + #if {(![file exists $kettleappfile]) && (![file exists $kettlebatfile])} { + # error "deck kettle_call unable to find installed kettle application file '$kettleappfile' (or '$kettlebatfile' if on windows)" + #} + #if {[file exists $kettleappfile]} { + # set kettlescript $kettleappfile + #} + #if {$::tcl_platform(platform) eq "windows"} { + # if {[file exists $kettlebatfile]} { + # set kettlescript $kettlebatfile + # } + #} } default { error "deck kettle_call 1st argument must be one of: 'lib' for direct use of kettle module or 'shell' to call as separate process" diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm index 3fb1e001..dc105f2f 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm @@ -35,23 +35,39 @@ namespace eval punk::mix::commandset::loadedlib { -highlight -type boolean -default 1 -help\ "Highlight which version is present with ansi underline and colour" -refresh -default 0 -type boolean -help "Re-scan the tm and library folders" - searchstrings -default * -multiple 1 -help\ + searchstring -default * -multiple 1 -help\ "Names to search for, may contain glob chars (* ?) e.g *lib* - If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. - eg name -> *name* - To search for an exact name prefix it with = - e.g =name -> name - " + If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. + eg name -> *name* + To search for an exact name prefix it with = + e.g =name -> name + If search is not prefixed with '=' the search is case insensitive." } proc search {args} { set argd [punk::args::get_by_id ::punk::mix::commandset::loadedlib::search $args] - set searchstrings [dict get $argd values searchstrings] + set searchstrings [dict get $argd values searchstring] set opts [dict get $argd opts] set opt_return [dict get $opts -return] set opt_highlight [dict get $opts -highlight] + set opt_refresh [dict get $opts -refresh] + + if {$opt_refresh} { + catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans + foreach tm_path [tcl::tm::list] { + set paths_below [punk::path::subfolders -recursive $tm_path] + foreach folder $paths_below { + set tail [file tail $folder] + if {[string match #modpod-* $tail] || [string match #tarjar-* $tail]} { + continue + } + if {[string match */_build/* $folder]} {continue} + set relpath [string tolower [punk::path::relative $tm_path $folder]] + set modpath [string map {/ ::} $relpath] + catch {package require ${modpath}::flobrudder99} + } + } + } - #REVIEW - this doesn't result in full scans - catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything set packages [package names] set matches [list] @@ -122,9 +138,25 @@ namespace eval punk::mix::commandset::loadedlib { } } } - proc loaded.search {searchstring} { - set search_result [search $searchstring] - set all_libs [split $search_result \n] + + punk::args::define { + @id -id ::punk::mix::commandset::loadedlib::loaded.search + @cmd -name "punk::mix::commandset::loadedlib loaded.search"\ + -summary\ + "Search loaded libraries."\ + -help "search all Tcl libraries currently loaded in your local interpreter. + ie those that have been loaded directly or indirectly by 'package require'." + }\ + @values\ + [punk::args::resolved_def -types values ::punk::mix::commandset::loadedlib::search searchstring] + + proc loaded.search {args} { + set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::loaded.search] + lassign [dict values $argd] leaders opts values + set searchstrings [dict get $values searchstring] + + set all_libs [search -return list -highlight 0 {*}$searchstrings] + set col1items [list] set col2items [list] set col3items [list] diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm index 277e386e..15dc9187 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/repo-0.1.0.tm @@ -41,11 +41,27 @@ namespace eval punk::mix::commandset::repo { return $result } + lappend PUNKARGS [list { + @id -id ::punk::mix::commandset::repo::fossilize + @cmd -name punk::mix::commandset::repo::fossilize + -summary\ + "Initialise and check in a project to fossil (unimplemented)."\ + -help\ + "(unimplemented)" + }] proc fossilize { args} { #check if project already managed by fossil.. initialise and check in if not. puts stderr "unimplemented" } + lappend PUNKARGS [list { + @id -id ::punk::mix::commandset::repo::unfossilize + @cmd -name punk::mix::commandset::repo::unfossilize + -summary\ + "Remove/archive .fossil (unimplemented)."\ + -help\ + "(unimplemented)" + }] proc unfossilize {projectname args} { #remove/archive .fossil puts stderr "unimplemented" @@ -76,14 +92,27 @@ namespace eval punk::mix::commandset::repo { #punk::args lappend PUNKARGS [list { @id -id ::punk::mix::commandset::repo::fossil-move-repository - @cmd -name punk::mix::commandset::repo::fossil-move-repository -help\ + @cmd -name punk::mix::commandset::repo::fossil-move-repository + -summary\ + "Move a fossil repository database file."\ + -help\ "Move the fossil repository file (usually named with .fossil extension). This is an interactive function which will prompt for answers on stdin before proceeding. The move can be done even if there are open checkouts and will maintain - the link between checkout databases and the repository file." + the link between checkout databases and the repository file. + + The call can be made from within a folder containing fossil databases, + or from within one of the checkouts of the fossil database that is to + be moved. + " + + #todo? + #@values -min 0 -max 1 + #path }] proc fossil-move-repository {{path ""}} { + #path unused for now - todo - allow calling with a specific target rather than relying on cwd? set searchbase [pwd] set projectinfo [punk::repo::find_repos $searchbase] set projectbase [dict get $projectinfo closest] diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm index 24168483..4f1af2bc 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/commandset/scriptwrap-0.1.0.tm @@ -77,7 +77,7 @@ namespace eval punk::mix::commandset::scriptwrap { #[para] Core API functions for punk::mix::commandset::scriptwrap #[list_begin definitions] - namespace export * + namespace export {[a-z]*} namespace eval fileline { namespace import ::punk::fileline::lib::* diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/path-0.1.0.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/path-0.1.0.tm index 54ee4080..0268dd75 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/path-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/path-0.1.0.tm @@ -99,7 +99,7 @@ package require punk::args # Base namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punk::path { - namespace export * + namespace export {[a-z]*} #variable xyz #*** !doctools @@ -645,6 +645,41 @@ namespace eval punk::path { } return $ismatch } + punk::args::define { + @id -id ::punk::path::subfolders + @cmd -name punk::path::subfolders\ + -summary\ + "Listing of directories within supplied path."\ + -help\ + "List of folders below path. + The resulting list is unsorted." + @opts + -recursive -type none -help\ + "" + #todo -depth + @values -min 0 -max 1 + path -type directory -optional 1 -help\ + "Path of folder. If not supplied current directory is used." + } + proc subfolders {args} { + set argd [punk::args::parse $args withid ::punk::path::subfolders] + lassign [dict values $argd] leaders opts values received + set do_recursion [dict exists $received -recursive] + if {[dict exists $received path]} { + set path [dict get $values path] + } else { + set path [pwd] + } + set folders [glob -nocomplain -directory $path -types d *] + if {$do_recursion} { + foreach subdir $folders { + lappend folders {*}[subfolders -recursive $subdir] + } + } + return $folders + } + + #todo - treefolders with similar search caps as treefilenames punk::args::define { @id -id ::punk::path::treefilenames diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm index fd84ec8d..731e263e 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm @@ -522,7 +522,14 @@ proc repl::start {inchan args} { set codethread "" set codethread_cond "" punk::console::mode line ;#review - revert to line mode on final exit - but we may be exiting a nested repl - puts "end repl::start" + set donevalue [set [namespace current]::done] + if {[lindex $donevalue 0] eq "quit"} { + puts "-->repl::start end $inchan $args result:'$donevalue'" + puts stderr "--> returning [lindex $donevalue 1]" + return [lindex $donevalue 1] + } + puts "-->repl::start end $inchan $args result:'$donevalue'" + puts stderr "__> returning 0" return 0 } proc repl::post_operations {} { @@ -570,7 +577,7 @@ proc repl::reopen_stdin {} { #todo - avoid putting this in gobal namespace? #collisions with other libraries apps? proc punk::repl::quit {args} { - set ::repl::done "quit {*}$args" + set ::repl::done [list quit {*}$args] #puts stderr "quit called" return "" ;#make sure to return nothing so "quit" doesn't land on stdout } @@ -800,8 +807,7 @@ proc repl::rputs {args} { } } set last_char_info_width 60 - #review - string shouldn't be truncated prior to stripcodes - could chop ansi codes! - #set summary "[::shellfilter::ansi::stripcodes [string range $out 0 $last_char_info_width]]" + #review - string shouldn't be truncated prior to ansistrip - could chop ansi codes! set out_plain_text [punk::ansi::ansistrip $out] set summary [string range $out_plain_text 0 $last_char_info_width] if {[string length $summary] > $last_char_info_width} { @@ -1610,6 +1616,8 @@ proc repl::repl_handler {inputchan prompt_config} { if {![llength $input_chunks_waiting($inputchan)]} { chan event $inputchan readable [list ::repl::repl_handler $inputchan $prompt_config] } else { + #review + #puts stderr "warning: after idle re-enable repl::repl_handler in thread: [thread::id]" after idle [list ::repl::repl_handler $inputchan $prompt_config] } #################################################### @@ -2695,6 +2703,7 @@ namespace eval repl { proc init {args} { + puts stderr "-->repl::init $args" if {![info exists ::argv0]} { #error out before we create a thread - punk requires this - review error "::argv0 not set" @@ -2900,7 +2909,7 @@ namespace eval repl { proc quit {args} { #child codethreads run in a 'code' interp therefore if they started another repl - it is within the 'code' interp in that thread # whereas the first repl launched in the process runs in root interp "" - thread::send -async %replthread% [list interp eval %replthread_interp% ::punk::repl::quit] + thread::send -async %replthread% [list interp eval %replthread_interp% [list ::punk::repl::quit {*}$args]] } proc editbuf args { thread::send %replthread% [list punk::repl::editbuf {*}$args] @@ -3623,6 +3632,7 @@ namespace eval repl { #temporary debug aliases - deliberate violation of safety provided by safe interp code alias escapeeval ::repl::interphelpers::escapeeval + code alias exit ::repl::interphelpers::quit #experiment #code alias ::shellfilter::stack ::shellfilter::stack diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.2.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.2.tm index 9aa98332..e04e5107 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.2.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/shellfilter-0.2.tm @@ -152,65 +152,6 @@ namespace eval shellfilter::pipe { } - -namespace eval shellfilter::ansi { - #maint warning - - #ansistrip from punk::ansi is better/more comprehensive - proc stripcodes {text} { - #obsolete? - #single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~). - dict set escape_terminals CSI [list @ \\ ^ _ ` | ~ 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 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 "\{" "\}"] - #dict set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic - dict set escape_terminals OSC [list \007 \033\\] ;#note mix of 1 and 2-byte terminals - #we process char by char - line-endings whether \r\n or \n should be processed as per any other character. - #line endings can theoretically occur within an ansi escape sequence (review e.g title?) - set inputlist [split $text ""] - set outputlist [list] - - #self-contained 2 byte ansi escape sequences - review more? - set 2bytecodes_dict [dict create\ - "reset_terminal" "\033c"\ - "save_cursor_posn" "\u001b7"\ - "restore_cursor_posn" "\u001b8"\ - "cursor_up_one" "\u001bM"\ - ] - set 2bytecodes [dict values $2bytecodes_dict] - - set in_escapesequence 0 - #assumption - undertext already 'rendered' - ie no backspaces or carriagereturns or other cursor movement controls - set i 0 - foreach u $inputlist { - set v [lindex $inputlist $i+1] - set uv ${u}${v} - if {$in_escapesequence eq "2b"} { - #2nd byte - done. - set in_escapesequence 0 - } elseif {$in_escapesequence != 0} { - set escseq [dict get $escape_terminals $in_escapesequence] - if {$u in $escseq} { - set in_escapesequence 0 - } elseif {$uv in $escseq} { - set in_escapseequence 2b ;#flag next byte as last in sequence - } - } else { - #handle both 7-bit and 8-bit CSI and OSC - if {[regexp {^(?:\033\[|\u009b)} $uv]} { - set in_escapesequence CSI - } elseif {[regexp {^(?:\033\]|\u009c)} $uv]} { - set in_escapesequence OSC - } elseif {$uv in $2bytecodes} { - #self-contained e.g terminal reset - don't pass through. - set in_escapesequence 2b - } else { - lappend outputlist $u - } - } - incr i - } - return [join $outputlist ""] - } - -} namespace eval shellfilter::chan { set testobj ::shellfilter::chan::var if {$testobj ni [info commands $testobj]} { @@ -2851,8 +2792,10 @@ namespace eval shellfilter { - + #chan configure $inchan -buffering none -blocking 1 ;#test chan configure $inchan -buffering $inbuffering -blocking 0 ;#we are setting up a readable handler for this - so non-blocking ok + + chan configure $errchan -buffering $errbuffering #chan configure $outchan -blocking 0 chan configure $outchan -buffering $outbuffering ;#don't configure non-blocking. weird duplicate of *second* line occurs if you do. @@ -2888,7 +2831,14 @@ namespace eval shellfilter { # Ideally we need something like exec,open in tcl that interacts with transformed channels directly and emits as it runs, not only at termination # - and that at least appears like a terminal to the called command. #set rdout [open |[concat $commandlist [list 2>@stderr <@$inchan]] [list RDONLY]] - + + #REVIEW! + #if the child process takes a while to begin reading stdin - the data on stdin between when we stopped the parent chan event handler and when the child gets data, + #seems to stay buffered somewhere. It is then read by the parent, after the child returns. (ie not lost, but out-of-order) + #This can be apparent sometimes even with fast typing upon calling an executable. (e.g occasionally even vim - but seems to be timing based so might only happen first time if at all) + # see scriptlib/stdin_race.tcl etc test files. + #similar problem with python & perl - issue seems to be in libc or OS buffering behaviour for standard channels. + #note that zig (repo/jn/zig/stdin_race) seems to avoid this issue - todo - make zig based binary extension for open/exec? set rdout [open |[concat $commandlist [list 2>@$wrerr <@$inchan]] [list RDONLY]] diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm index f2f4a3af..de0164cd 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/textblock-0.1.3.tm @@ -4899,7 +4899,6 @@ tcl::namespace::eval textblock { set textblock [punk::ansi::ansistripraw $textblock] } if {[tcl::string::last \n $textblock] >= 0} { - #return [tcl::mathfunc::max {*}[lmap v [split $textblock \n] {::punk::char::ansifreestring_width $v}]] set widths [lmap v [split $textblock \n] {::punk::char::ansifreestring_width $v}] set max [tcl::mathfunc::max {*}$widths] set min [tcl::mathfunc::min {*}$widths] diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/tomlish-1.1.6.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/tomlish-1.1.6.tm index 7abbaeae..d7d1f131 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/tomlish-1.1.6.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/tomlish-1.1.6.tm @@ -2012,6 +2012,8 @@ namespace eval tomlish { #updatables #review - type changes from existing value?? set sourcedata [lindex $source_d_elements $arridx] + #todo - what happens when less source elements than in existing array? ie sourcedata is empty. + # switch -- $arrchild_type { STRING - LITERAL - FLOAT - INT - DATETIME - DATETIME-LOCAL - DATE-LOCAL - TIME-LOCAL { #basic types - no recursion needed @@ -2036,7 +2038,7 @@ namespace eval tomlish { } ITABLE - ARRAY { #recurse - puts stderr "update $tomlish_type within array" + puts stderr "update $arrchild_type within array" set nextd [tomlish::dict::path::get $d $arridx] set subrecord_tomlish [list $arrchild] set newsubrecord_tomlish [update_tomlish_from_dict $subrecord_tomlish $nextd] @@ -9240,8 +9242,15 @@ namespace eval tomlish::lib { } # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval tomlish::system { + #*** !doctools + #[subsection {Namespace tomlish::system}] + #[para] + #[list_begin definitions] + + #taken from punk::lib + #todo - change list argument to integer length proc lindex_resolve_basic {list index} { #*** !doctools #[call [fun lindex_resolve_basic] [arg list] [arg index]] @@ -9360,6 +9369,8 @@ namespace eval tomlish::system { } } + #*** !doctools + #[list_end] [comment {--- end definitions namespace tomlish::system ---}] } if {[info exists ::argc] && $::argc > 0} { diff --git a/src/vfs/_vfscommon.vfs/modules/picalc-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/picalc-0.1.0.tm index d13899ac..37244743 100644 --- a/src/vfs/_vfscommon.vfs/modules/picalc-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/picalc-0.1.0.tm @@ -46,7 +46,7 @@ #[list_begin itemized] package require Tcl 8.6- -package require punk::lib +package require punk::lib package require punk::args #*** !doctools #[item] [package {Tcl 8.6}] @@ -73,10 +73,18 @@ tcl::namespace::eval picalc { # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools #[subsection {Namespace picalc}] - #[para] Core API functions for picalc + #[para] Core API functions for picalc #[list_begin definitions] variable PUNKARGS + variable PUNKARGS_aliases + + lappend PUNKARGS [list { + @id -id "(package)picalc" + @package -name "picalc"\ + -help\ + "A test/learning experimental package for pi calculation" + }] @@ -475,15 +483,6 @@ tcl::namespace::eval picalc::lib { # == === === === === === === === === === === === === === === tcl::namespace::eval picalc { tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase - variable PUNKARGS - variable PUNKARGS_aliases - - lappend PUNKARGS [list { - @id -id "(package)picalc" - @package -name "picalc" -help\ - "Package - Description" - }] namespace eval argdoc { #namespace for custom argument documentation diff --git a/src/vfs/_vfscommon.vfs/modules/poshinfo-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/poshinfo-0.1.0.tm index 37117d6d..3c3c2c52 100644 --- a/src/vfs/_vfscommon.vfs/modules/poshinfo-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/poshinfo-0.1.0.tm @@ -21,7 +21,7 @@ #[manpage_begin punkshell_module_poshinfo 0 0.1.0] #[copyright "2024"] #[titledesc {poshinfo prompt theme tool}] [comment {-- Name section and table of contents description --}] -#[moddesc {POSH-related prompt tool}] [comment {-- Description at end of page heading --}] +#[moddesc {POSH-related prompt tool}] [comment {-- Description at end of page heading --}] #[require poshinfo] #[keywords module terminal console theme prompt {prompt theme} POSH] #[description] @@ -113,7 +113,7 @@ tcl::namespace::eval poshinfo { #*** !doctools #[subsection {Namespace poshinfo}] - #[para] Core API functions for poshinfo + #[para] Core API functions for poshinfo #[list_begin definitions] @@ -121,11 +121,11 @@ tcl::namespace::eval poshinfo { proc info_from_filename {fname} { #string based filename processing: we are deliberately avoiding test of file existence etc here if {$fname eq ""} { - error "poshinfo::info_from_filename unable to determine name from empty string" + error "poshinfo::info_from_filename unable to determine name from empty string" } if {[string first . $fname] < 0} { #theoretically we could have a file without dots - but it's more likely an error in this context - error "poshinfo::info_from_filename supplied value '$fname' doesn't look like a filename. Cowardly refusing to guess a shortname." + error "poshinfo::info_from_filename supplied value '$fname' doesn't look like a filename. Cowardly refusing to guess a shortname." } set ftail [file tail $fname] set rootname [file rootname $ftail] @@ -141,7 +141,7 @@ tcl::namespace::eval poshinfo { set shortname [join [lrange $parts 0 end-1] .] } else { if {$rootname eq "schema"} { - set type schema + set type schema } else { #review - we can't tell diff betw . and .. set type unknown @@ -181,7 +181,7 @@ tcl::namespace::eval poshinfo { dict lappend themes_dict $shortname $themeinfo } } - } + } } } return $themes_dict @@ -204,10 +204,10 @@ tcl::namespace::eval poshinfo { -format -default all -multiple 1 -choices {all yaml json}\ -help "File format of posh theme - based on file extension" -type -default all -multiple 1\ - -help "e.g omp" + -help "e.g omp" -as -default "table" -choices {list showlist dict showdict table tableobject plaintext}\ -help "return type of result" - @values -min 0 + @values -min 0 globs -multiple 1 -default * -help "" } proc themes {args} { @@ -226,7 +226,7 @@ tcl::namespace::eval poshinfo { set themeinfo [lindex $themeinfolist 0] if {("all" in $formats || [dict get $themeinfo format] in $formats) && ("all" in $types || [dict get $themeinfo type] in $types)} { dict set restricted_themes_dict $shortname $themeinfolist - } + } } unset themes_dict switch -- $return_as { @@ -266,7 +266,7 @@ tcl::namespace::eval poshinfo { omp {} unknown { set bg Web-red - } + } default { #we shouldn't be getting other values set bg Web-yellow @@ -274,7 +274,7 @@ tcl::namespace::eval poshinfo { } if {$posh_theme eq [file normalize $path]} { set fg web-limegreen - } + } if {"$fg$bg" ne ""} { $t configure_row [expr {[$t row_count]-1}] -ansibase [a+ {*}$fg {*}$bg] } @@ -287,7 +287,7 @@ tcl::namespace::eval poshinfo { return [join [lines_as_list -line trimline $pt] \n] } table { - set tabletext [$t print] + set tabletext [$t print] $t destroy return $tabletext } @@ -313,16 +313,9 @@ tcl::namespace::eval poshinfo::lib { #*** !doctools #[subsection {Namespace poshinfo::lib}] - #[para] Secondary functions that are part of the API + #[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 @@ -340,17 +333,17 @@ tcl::namespace::eval poshinfo::lib { #*** !doctools #[subsection {Namespace poshinfo::system}] - #[para] Internal functions that are not part of the API + #[para] Internal functions that are not part of the API #} # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ -## Ready +## Ready package provide poshinfo [tcl::namespace::eval poshinfo { variable pkg poshinfo variable version - set version 0.1.0 + set version 0.1.0 }] return diff --git a/src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm b/src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm index 93fcfe6d..fc438d57 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/args-0.2.tm @@ -2606,11 +2606,11 @@ tcl::namespace::eval punk::args { @values {set defaults_key VALSPEC_DEFAULTS} } if {[dict exists $opt_override $type]} { - append result \n "$type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]]" - dict set resultdict $type [dict merge [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] [dict get $opt_override $type]] + append result \n "$type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]]" + dict set resultdict $type [dict merge [dict get $specdict FORMS $formname $defaults_key] [dict get $opt_override $type]] } else { - append result \n "$type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS]" - dict set resultdict $type [dict get $specdict FORMS $formname LEADERSPEC_DEFAULTS] + append result \n "$type [dict get $specdict FORMS $formname $defaults_key]" + dict set resultdict $type [dict get $specdict FORMS $formname $defaults_key] } } } @@ -2856,6 +2856,19 @@ tcl::namespace::eval punk::args { return $result } + lappend PUNKARGS [list { + @id -id ::punk::args::update_definitions + @cmd -name punk::args::update_definitions\ + -summary\ + ""\ + -help\ + "" + @values -min 1 + id + arglist -type list -help\ + "list containing arguments to be parsed as per the + argument specification identified by the supplied id." + }] #scanned_packages (list) #namespace_docpackages (dict) proc update_definitions {{nslist *}} { diff --git a/src/vfs/_vfscommon.vfs/modules/punk/mix/commandset/loadedlib-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/mix/commandset/loadedlib-0.1.0.tm index 3fb1e001..dc105f2f 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/mix/commandset/loadedlib-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/mix/commandset/loadedlib-0.1.0.tm @@ -35,23 +35,39 @@ namespace eval punk::mix::commandset::loadedlib { -highlight -type boolean -default 1 -help\ "Highlight which version is present with ansi underline and colour" -refresh -default 0 -type boolean -help "Re-scan the tm and library folders" - searchstrings -default * -multiple 1 -help\ + searchstring -default * -multiple 1 -help\ "Names to search for, may contain glob chars (* ?) e.g *lib* - If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. - eg name -> *name* - To search for an exact name prefix it with = - e.g =name -> name - " + If no glob chars are explicitly specified, the searchstring will be wrapped with star globs. + eg name -> *name* + To search for an exact name prefix it with = + e.g =name -> name + If search is not prefixed with '=' the search is case insensitive." } proc search {args} { set argd [punk::args::get_by_id ::punk::mix::commandset::loadedlib::search $args] - set searchstrings [dict get $argd values searchstrings] + set searchstrings [dict get $argd values searchstring] set opts [dict get $argd opts] set opt_return [dict get $opts -return] set opt_highlight [dict get $opts -highlight] + set opt_refresh [dict get $opts -refresh] + + if {$opt_refresh} { + catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything REVIEW - this doesn't result in full scans + foreach tm_path [tcl::tm::list] { + set paths_below [punk::path::subfolders -recursive $tm_path] + foreach folder $paths_below { + set tail [file tail $folder] + if {[string match #modpod-* $tail] || [string match #tarjar-* $tail]} { + continue + } + if {[string match */_build/* $folder]} {continue} + set relpath [string tolower [punk::path::relative $tm_path $folder]] + set modpath [string map {/ ::} $relpath] + catch {package require ${modpath}::flobrudder99} + } + } + } - #REVIEW - this doesn't result in full scans - catch {package require frobznodule666} ;#ensure pkg system has loaded/searched for everything set packages [package names] set matches [list] @@ -122,9 +138,25 @@ namespace eval punk::mix::commandset::loadedlib { } } } - proc loaded.search {searchstring} { - set search_result [search $searchstring] - set all_libs [split $search_result \n] + + punk::args::define { + @id -id ::punk::mix::commandset::loadedlib::loaded.search + @cmd -name "punk::mix::commandset::loadedlib loaded.search"\ + -summary\ + "Search loaded libraries."\ + -help "search all Tcl libraries currently loaded in your local interpreter. + ie those that have been loaded directly or indirectly by 'package require'." + }\ + @values\ + [punk::args::resolved_def -types values ::punk::mix::commandset::loadedlib::search searchstring] + + proc loaded.search {args} { + set argd [punk::args::parse $args withid ::punk::mix::commandset::loadedlib::loaded.search] + lassign [dict values $argd] leaders opts values + set searchstrings [dict get $values searchstring] + + set all_libs [search -return list -highlight 0 {*}$searchstrings] + set col1items [list] set col2items [list] set col3items [list] diff --git a/src/vfs/_vfscommon.vfs/modules/punk/path-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/path-0.1.0.tm index 54ee4080..0268dd75 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/path-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/path-0.1.0.tm @@ -99,7 +99,7 @@ package require punk::args # Base namespace # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ namespace eval punk::path { - namespace export * + namespace export {[a-z]*} #variable xyz #*** !doctools @@ -645,6 +645,41 @@ namespace eval punk::path { } return $ismatch } + punk::args::define { + @id -id ::punk::path::subfolders + @cmd -name punk::path::subfolders\ + -summary\ + "Listing of directories within supplied path."\ + -help\ + "List of folders below path. + The resulting list is unsorted." + @opts + -recursive -type none -help\ + "" + #todo -depth + @values -min 0 -max 1 + path -type directory -optional 1 -help\ + "Path of folder. If not supplied current directory is used." + } + proc subfolders {args} { + set argd [punk::args::parse $args withid ::punk::path::subfolders] + lassign [dict values $argd] leaders opts values received + set do_recursion [dict exists $received -recursive] + if {[dict exists $received path]} { + set path [dict get $values path] + } else { + set path [pwd] + } + set folders [glob -nocomplain -directory $path -types d *] + if {$do_recursion} { + foreach subdir $folders { + lappend folders {*}[subfolders -recursive $subdir] + } + } + return $folders + } + + #todo - treefolders with similar search caps as treefilenames punk::args::define { @id -id ::punk::path::treefilenames diff --git a/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.tm b/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.tm index 6c446aa3..e04e5107 100644 --- a/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.tm +++ b/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.tm @@ -2792,7 +2792,7 @@ namespace eval shellfilter { - #chan configure $inchan -buffering none -blocking 1 ;test + #chan configure $inchan -buffering none -blocking 1 ;#test chan configure $inchan -buffering $inbuffering -blocking 0 ;#we are setting up a readable handler for this - so non-blocking ok @@ -2831,10 +2831,15 @@ namespace eval shellfilter { # Ideally we need something like exec,open in tcl that interacts with transformed channels directly and emits as it runs, not only at termination # - and that at least appears like a terminal to the called command. #set rdout [open |[concat $commandlist [list 2>@stderr <@$inchan]] [list RDONLY]] - + #REVIEW! #if the child process takes a while to begin reading stdin - the data on stdin between when we stopped the parent chan event handler and when the child gets data, #seems to stay buffered somewhere. It is then read by the parent, after the child returns. (ie not lost, but out-of-order) + #This can be apparent sometimes even with fast typing upon calling an executable. (e.g occasionally even vim - but seems to be timing based so might only happen first time if at all) + # see scriptlib/stdin_race.tcl etc test files. + #similar problem with python & perl - issue seems to be in libc or OS buffering behaviour for standard channels. + #note that zig (repo/jn/zig/stdin_race) seems to avoid this issue - todo - make zig based binary extension for open/exec? + set rdout [open |[concat $commandlist [list 2>@$wrerr <@$inchan]] [list RDONLY]] chan configure $rderr -buffering $errbuffering -blocking 0