Browse Source

bootsupport: promote tcltestrun 0.4.0 (G-161) + sweep pending console 0.8.1 / shellfilter 0.2.5

make.tcl modules + make.tcl bootsupport outputs batch (punkcheck-managed):
- punk/tcltestrun 0.3.1 -> 0.4.0 (the G-161 multi-line banner parser -
  runtests.tcl's parent loads the bootsupport copy, so this promotion is
  what puts the tolerant parser in front of the runner)
- punk/console 0.8.0 -> 0.8.1 and shellfilter 0.2.4 -> 0.2.5: already
  committed in src/modules and already promoted into _vfscommon
  (ac032b96/8864e5ce); this bootsupport run swept the snapshots current
- punk/mix/templates-0.2.0.tm re-packed by the mint (modpod payload
  refresh, no source change)

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
11dbe02d02
  1. 123
      src/bootsupport/modules/punk/console-0.8.1.tm
  2. BIN
      src/bootsupport/modules/punk/mix/templates-0.2.0.tm
  3. 102
      src/bootsupport/modules/punk/tcltestrun-0.4.0.tm
  4. 72
      src/bootsupport/modules/shellfilter-0.2.5.tm

123
src/bootsupport/modules/punk/console-0.8.0.tm → src/bootsupport/modules/punk/console-0.8.1.tm

@ -7,7 +7,7 @@
# (C) 2023 # (C) 2023
# #
# @@ Meta Begin # @@ Meta Begin
# Application punk::console 0.8.0 # Application punk::console 0.8.1
# Meta platform tcl # Meta platform tcl
# Meta license <unspecified> # Meta license <unspecified>
# @@ Meta End # @@ Meta End
@ -17,7 +17,7 @@
# doctools header # doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools #*** !doctools
#[manpage_begin punkshell_module_punk::console 0 0.8.0] #[manpage_begin punkshell_module_punk::console 0 0.8.1]
#[copyright "2024"] #[copyright "2024"]
#[titledesc {punk console}] [comment {-- Name section and table of contents description --}] #[titledesc {punk console}] [comment {-- Name section and table of contents description --}]
#[moddesc {punk console}] [comment {-- Description at end of page heading --}] #[moddesc {punk console}] [comment {-- Description at end of page heading --}]
@ -2809,6 +2809,30 @@ namespace eval punk::console {
return [dict create columns 80 rows 24] return [dict create columns 80 rows 24]
} }
lappend PUNKARGS [list {
@id -id ::punk::console::size_result_valid
@cmd -name punk::console::size_result_valid -summary\
"True if a size-mechanism result is a well-formed {columns <int> rows <int>} dict."
@values -min 1 -max 1
sizedict -type any -help\
"Candidate result from a get_size_using_* mechanism."
}]
proc size_result_valid {sizedict} {
#see PUNKARGS id ::punk::console::size_result_valid
#Guard for size_via_query_mechanisms: a mechanism 'success' must be well-formed.
#Mechanisms can otherwise return malformed data without erroring and win the
#cached-mechanism loop silently (the 2026-08-03 tput single-capname regression
#class - and the ANSI mechanisms return {columns {} rows {}} on a query timeout).
if {[catch {dict size $sizedict} pairs] || $pairs != 2} {
return 0
}
expr {
[dict exists $sizedict columns] && [dict exists $sizedict rows]
&& [string is integer -strict [dict get $sizedict columns]]
&& [string is integer -strict [dict get $sizedict rows]]
}
}
#ANSI/tput size mechanisms with per-console-pair timing cache. Returns a dict #ANSI/tput size mechanisms with per-console-pair timing cache. Returns a dict
#{columns <int> rows <int>} or an empty dict when size cannot be determined. #{columns <int> rows <int>} or an empty dict when size cannot be determined.
#Used by the legacy get_size channel path and (as console_size_provider) by the base #Used by the legacy get_size channel path and (as console_size_provider) by the base
@ -2828,7 +2852,10 @@ namespace eval punk::console {
#see PUNKARGS id ::punk::console::size_via_query_mechanisms #see PUNKARGS id ::punk::console::size_via_query_mechanisms
if {[console_fact_get $inoutchannels is_vt52]} { if {[console_fact_get $inoutchannels is_vt52]} {
#vt52 doesn't support cursor save/restore or cursor position reports. #vt52 doesn't support cursor save/restore or cursor position reports.
if {![catch {get_size_using_tput $inoutchannels} sizedict]} { if {![catch {get_size_using_sttysize $inoutchannels} sizedict] && [size_result_valid $sizedict]} {
return $sizedict
}
if {![catch {get_size_using_tput $inoutchannels} sizedict] && [size_result_valid $sizedict]} {
return $sizedict return $sizedict
} }
return [dict create] return [dict create]
@ -2841,19 +2868,25 @@ namespace eval punk::console {
#we will also use the results for our initial return of the size. #we will also use the results for our initial return of the size.
set successful_mechs [list] set successful_mechs [list]
set sizedict [dict create] set sizedict [dict create]
if {![catch {get_size_using_cursorrestore $inoutchannels} result]} { if {![catch {get_size_using_cursorrestore $inoutchannels} result] && [size_result_valid $result]} {
lappend successful_mechs "cursorrestore" lappend successful_mechs "cursorrestore"
if {![dict size $sizedict]} { if {![dict size $sizedict]} {
set sizedict $result set sizedict $result
} }
} }
if {![catch {get_size_using_cursormove $inoutchannels} result]} { if {![catch {get_size_using_cursormove $inoutchannels} result] && [size_result_valid $result]} {
lappend successful_mechs "cursormove" lappend successful_mechs "cursormove"
if {![dict size $sizedict]} { if {![dict size $sizedict]} {
set sizedict $result set sizedict $result
} }
} }
if {![catch {get_size_using_tput $inoutchannels} result] } { if {![catch {get_size_using_sttysize $inoutchannels} result] && [size_result_valid $result]} {
lappend successful_mechs "sttysize"
if {![dict size $sizedict]} {
set sizedict $result
}
}
if {![catch {get_size_using_tput $inoutchannels} result] && [size_result_valid $result]} {
lappend successful_mechs "tput" lappend successful_mechs "tput"
if {![dict size $sizedict]} { if {![dict size $sizedict]} {
set sizedict $result set sizedict $result
@ -2879,7 +2912,7 @@ namespace eval punk::console {
} }
foreach mech [dict get $get_size_mechanism $inoutchannels] { foreach mech [dict get $get_size_mechanism $inoutchannels] {
if {![catch {get_size_using_$mech $inoutchannels} sizedict]} { if {![catch {get_size_using_$mech $inoutchannels} sizedict] && [size_result_valid $sizedict]} {
return $sizedict return $sizedict
} }
} }
@ -2971,6 +3004,11 @@ namespace eval punk::console {
#this mechanism is much faster than ansi cursor movements #this mechanism is much faster than ansi cursor movements
#REVIEW check if any x-platform anomalies with this method? #REVIEW check if any x-platform anomalies with this method?
#can -winsize key exist but contain erroneous info? We will check that we get 2 ints at least #can -winsize key exist but contain erroneous info? We will check that we get 2 ints at least
#NOTE (verified 2026-08-03, tcl 9.0.2): unix tty/serial channels do NOT expose -winsize
#(windows console channels do) - so on unix this mechanism currently always falls through.
#If a future tcl adds unix -winsize, be aware a stacked/reflected stdout (e.g the repl's
#shellfilter stack) masks the underlying tty's options - probing the base tty channel
#(or /dev/tty) would be needed for it to be seen under stacked channels.
lassign [dict get $outconf -winsize] cols lines lassign [dict get $outconf -winsize] cols lines
if {[string is integer -strict $cols] && [string is integer -strict $lines]} { if {[string is integer -strict $cols] && [string is integer -strict $lines]} {
return [dict create columns $cols rows $lines] return [dict create columns $cols rows $lines]
@ -2979,6 +3017,34 @@ namespace eval punk::console {
error "chan configure method of getting console size not supported or failed to get valid size info" error "chan configure method of getting console size not supported or failed to get valid size info"
} }
lappend PUNKARGS [list {
@id -id ::punk::console::get_size_using_sttysize
@cmd -name punk::console::get_size_using_sttysize -summary\
"Console size via 'stty size' on the input channel's tty (single fork) - errors when unavailable or input is not a tty."
@values -min 0 -max 1
inoutchannels -type list -default {stdin stdout} -optional 1 -help\
"Canonical {in out} channel pair (internal size mechanism - not spec-form aware)."
}]
proc get_size_using_sttysize {{inoutchannels {stdin stdout}}} {
#see PUNKARGS id ::punk::console::get_size_using_sttysize
#'stty size' emits "<rows> <cols>" from the tty on its stdin - a single fork with a
#fixed output shape (GNU, busybox and BSD stty all support it - not strictly POSIX).
#Preferred over tput for the exec-based fallback tier: tput multi-capname handling
#varies by implementation (see get_size_using_tput autodetect). Non-tty input makes
#stty error, which correctly drops this mechanism from size_via_query_mechanisms.
set in [lindex $inoutchannels 0]
set sttycmd [auto_execok stty]
if {$sttycmd eq ""} {
error "stty command not found - cannot use stty size method to get console size"
}
set sizeinfo [string trim [exec {*}$sttycmd size <@$in]]
lassign $sizeinfo lines cols
if {![string is integer -strict $lines] || ![string is integer -strict $cols] || $lines < 1 || $cols < 1} {
error "stty size returned '$sizeinfo' - not usable as '<rows> <cols>'"
}
return [dict create columns $cols rows $lines]
}
lappend PUNKARGS [list { lappend PUNKARGS [list {
@id -id ::punk::console::get_size_using_tput @id -id ::punk::console::get_size_using_tput
@cmd -name punk::console::get_size_using_tput -summary\ @cmd -name punk::console::get_size_using_tput -summary\
@ -2993,7 +3059,44 @@ namespace eval punk::console {
if {$tputcmd eq ""} { if {$tputcmd eq ""} {
error "tput command not found - cannot use tput method to get console size" error "tput command not found - cannot use tput method to get console size"
} }
lassign [exec {*}$tputcmd lines cols] lines cols #tput multi-capname behaviour varies by implementation: ncurses >= 6.1 and the BSD
#tputs emit one value per capname ("66\n266"), but older ncurses treats the second
#capname as an (ignored) parameter of the first and emits only "66" - which used to
#lassign cols to "" here and return 'columns {} rows 66' (2026-08-03 WSL get_size
#regression once the mechanism-timing cache ranked tput first). Autodetect by result
#shape on first use and remember per tput path - capable tputs stay single-fork,
#single-cap-only tputs settle into two forks per call.
variable tput_multicap
if {![info exists tput_multicap]} {set tput_multicap [dict create]}
if {[dict exists $tput_multicap $tputcmd] && ![dict get $tput_multicap $tputcmd]} {
set lines [string trim [exec {*}$tputcmd lines]]
set cols [string trim [exec {*}$tputcmd cols]]
} else {
set outdata [exec {*}$tputcmd lines cols]
set values [list]
foreach v [split $outdata \n] {
set v [string trim $v]
if {$v ne ""} {lappend values $v}
}
switch -- [llength $values] {
2 {
lassign $values lines cols
dict set tput_multicap $tputcmd 1
}
1 {
#single-cap-only tput evaluated 'lines' and ignored 'cols'
dict set tput_multicap $tputcmd 0
set lines [lindex $values 0]
set cols [string trim [exec {*}$tputcmd cols]]
}
default {
error "tput lines cols returned '$outdata' - unrecognised output shape"
}
}
}
if {![string is integer -strict $lines] || ![string is integer -strict $cols] || $lines < 1 || $cols < 1} {
error "tput returned lines='$lines' cols='$cols' - not usable as positive integers"
}
return [dict create columns $cols rows $lines] return [dict create columns $cols rows $lines]
} }
@ -5787,7 +5890,7 @@ namespace eval punk::console::system {
# Standalone debug run example (from this directory): # Standalone debug run example (from this directory):
# pwsh -nop -nol -f consolemode_server_async.ps1 test1 0 1 # pwsh -nop -nol -f consolemode_server_async.ps1 test1 0 1
# Protocol (one line per named-pipe connection): enableraw | disableraw | ping | exit # Protocol (one line per named-pipe connection): enableraw | disableraw | ping | exit
# MAINTENANCE: src/modules/punk/console-0.8.0.tm carries this file's text as # MAINTENANCE: src/modules/punk/console-0.8.1.tm carries this file's text as
# punk::console::system::ps_consolemode_script_embedded (the last-resort resolution for kits and # punk::console::system::ps_consolemode_script_embedded (the last-resort resolution for kits and
# unusual cwds). Keep the two in sync - the console testsuite (psfallback.test) fails when they diverge. # unusual cwds). Keep the two in sync - the console testsuite (psfallback.test) fails when they diverge.
; ;
@ -6906,7 +7009,7 @@ namespace eval ::punk::args::register {
## Ready ## Ready
package provide punk::console [namespace eval punk::console { package provide punk::console [namespace eval punk::console {
variable version variable version
set version 0.8.0 set version 0.8.1
}] }]
return return

BIN
src/bootsupport/modules/punk/mix/templates-0.2.0.tm

Binary file not shown.

102
src/bootsupport/modules/punk/tcltestrun-0.3.1.tm → src/bootsupport/modules/punk/tcltestrun-0.4.0.tm

@ -8,7 +8,7 @@
# (C) 2026 # (C) 2026
# #
# @@ Meta Begin # @@ Meta Begin
# Application punk::tcltestrun 0.3.1 # Application punk::tcltestrun 0.4.0
# Meta platform tcl # Meta platform tcl
# Meta license BSD # Meta license BSD
# @@ Meta End # @@ Meta End
@ -36,7 +36,12 @@ tcl::namespace::eval punk::tcltestrun {
"Parse a dict containing stderr and stdout keys into testresult summary data."\ "Parse a dict containing stderr and stdout keys into testresult summary data."\
-help\ -help\
"Parse a dict containing stderr and stdout keys into testresult summary data. "Parse a dict containing stderr and stdout keys into testresult summary data.
The dict is expected to be in the format returned by shellrun::runx from execution of a tcltest script" The dict is expected to be in the format returned by shellrun::runx from execution of a tcltest script.
Intended for streams produced with tcltest -verbose {body pass skip start error line usec} (the
runtests.tcl configuration). Failing tests whose descriptions contain embedded newlines (multi-line
opening banners) are parsed with full fidelity when the stream carries -verbose start events; without
start events such banners are not recognised and parsing degrades to the historic single-line-only
behaviour."
@leaders @leaders
@opts @opts
@values -min 1 -max 2 @values -min 1 -max 2
@ -81,12 +86,63 @@ tcl::namespace::eval punk::tcltestrun {
set test_case_pass [dict create] set test_case_pass [dict create]
#dict set test_case_pass microseconds 0 #dict set test_case_pass microseconds 0
set test_case_time [dict create] set test_case_time [dict create]
#most recently started test name, from "---- <name> start" lines (-verbose start).
#Anchor for multi-line opening-banner recognition (G-161): without start events the
#parser retains its historic single-line-banner-only behaviour.
set current_start_name ""
switch -- $what { switch -- $what {
stdout { stdout {
foreach ln [split $chunk \n] { foreach ln [split $chunk \n] {
set ln_trimright [string trimright $ln] set ln_trimright [string trimright $ln]
incr i incr i
set fail_stage [dict get $test_case_fail stage] set fail_stage [dict get $test_case_fail stage]
if {$fail_stage eq "banner"} {
#G-161: accumulating the remainder of a multi-line opening banner.
#tcltest emits the opening banner as ONE puts of
# ==== <name> <description> FAILED
#and trims only the description's ENDS, so a description containing
#embedded newlines makes the banner span physical lines: the first line
#starts "==== <name> " (recognised by the anchored entry point below) and
#the final line always ends " FAILED" (the description's last line is
#non-whitespace-terminated, so the space before FAILED is guaranteed).
if {[string match "* FAILED" $ln_trimright]} {
#banner complete - same state as a single-line opener
dict append test_case_fail test_openingtext "\n[string range $ln_trimright 0 end-7]"
dict set test_case_fail stage open
dict unset test_case_fail banner_lines
dict append results out "<stdout><$pkg> $ln" \n
continue
} elseif {[string match "==== Contents of test case:*" $ln]} {
#terminator line missed (pathological description content) - treat the
#banner as complete and take the open->contents transition so the
#parser cannot wedge waiting for a line that already went past
dict set test_case_fail stage contents
dict unset test_case_fail banner_lines
dict append results out "<stdout><$pkg> $ln" \n
continue
} elseif {[string match "++++ * took *" $ln_trimright]
|| [string match "++++ * PASSED" $ln_trimright]
|| [string match "++++ * SKIPPED: *" $ln_trimright]
|| [string match "---- * start" $ln_trimright]
|| [string match "*:*Total*Passed*Skipped*Failed*" $ln]
|| [dict get $test_case_fail banner_lines] >= 100} {
#a recognisable event line (or an implausibly long banner) while
#accumulating: this was not a banner after all - abandon capture and
#reprocess the current line through normal handling below.
#Already-buffered lines were ordinary output; they remain in 'out'
#where they were appended as they streamed.
dict set test_case_fail stage ""
dict set test_case_fail test_openingtext ""
dict unset test_case_fail banner_lines
set fail_stage ""
#deliberate fall-through - no continue
} else {
dict append test_case_fail test_openingtext "\n$ln"
dict incr test_case_fail banner_lines
dict append results out "<stdout><$pkg> $ln" \n
continue
}
}
if {$fail_stage eq ""} { if {$fail_stage eq ""} {
#not within a test case failure section, so we can parse summary lines and other output normally. #not within a test case failure section, so we can parse summary lines and other output normally.
@ -124,6 +180,17 @@ tcl::namespace::eval punk::tcltestrun {
continue continue
} }
# ---- <testname_words> start (-verbose start; the runner always enables it)
# Track the most recently started test: the anchor that lets a multi-line
# opening banner be recognised by name prefix (G-161). The name may itself
# contain spaces and even the word "start", so anchor on the LAST " start".
if {[string match "---- * start" $ln_trimright]} {
set start_pos [string last " start" $ln_trimright]
set current_start_name [string range $ln_trimright 5 [expr {$start_pos - 1}]]
dict append results out "<stdout><$pkg> $ln" \n
continue
}
if {[string match "Tests ended at*" $ln]} { if {[string match "Tests ended at*" $ln]} {
#review - what outputs this? #review - what outputs this?
#puts stdout "<stdout><$pkg> $ln" #puts stdout "<stdout><$pkg> $ln"
@ -153,6 +220,19 @@ tcl::namespace::eval punk::tcltestrun {
dict set test_case_fail stage open dict set test_case_fail stage open
set line_inner [string range $ln 5 end-7] ;#trim leading ====<space> and trailing <space>FAILED set line_inner [string range $ln 5 end-7] ;#trim leading ====<space> and trailing <space>FAILED
dict set test_case_fail test_openingtext $line_inner dict set test_case_fail test_openingtext $line_inner
} elseif {$current_start_name ne "" && [string match "==== *" $ln]
&& [string equal -length [string length "$current_start_name "] "$current_start_name " [string range $ln 5 end]]} {
#G-161: entry point to a MULTI-LINE opening banner - a line starting
#"==== <current test name> " that does not end " FAILED" is the first
#physical line of a banner whose description contains embedded newlines
#(single tcltest puts; see the banner stage above for the terminator).
#Anchoring on the current started test's name (exact prefix compare, no
#glob) means ordinary output cannot open banner capture, and streams
#without -verbose start events never take this branch - their parsing
#is unchanged.
dict set test_case_fail stage banner
dict set test_case_fail test_openingtext [string range $ln 5 end]
dict set test_case_fail banner_lines 1
} }
dict append results out "<stdout><$pkg> $ln" \n dict append results out "<stdout><$pkg> $ln" \n
@ -232,7 +312,20 @@ tcl::namespace::eval punk::tcltestrun {
# Result capture for FAILED (non-error) tests: grab "---- Result was:" and "---- Result should have been" blocks. # Result capture for FAILED (non-error) tests: grab "---- Result was:" and "---- Result should have been" blocks.
if {$fail_stage eq "contents"} { if {$fail_stage eq "contents"} {
if {[dict exists $test_case_fail result_stage] && [dict get $test_case_fail result_stage] ne ""} { if {[dict exists $test_case_fail result_stage] && [dict get $test_case_fail result_stage] ne ""} {
if {[string match "---- *" $ln] || [string match "==== * FAILED" $ln_trimright] || [string match "++++ *" $ln]} { if {[string match "---- Result should have been*" $ln]} {
# Direct transition result_was -> result_expected. The two blocks are
# adjacent single puts, so this opener always arrives while the
# result_was capture is still ACTIVE. Before G-161 the line merely
# reset the active capture and the fall-through skipped the opener
# elseif below: result_expected was never captured (absent from every
# report) and the expected-value lines leaked into test_body.
dict set test_case_fail result_stage "result_expected"
if {![dict exists $test_case_fail result_expected]} {
dict set test_case_fail result_expected ""
}
dict append results out "<stdout><$pkg> $ln" \n
continue
} elseif {[string match "---- *" $ln] || [string match "==== * FAILED" $ln_trimright] || [string match "++++ *" $ln]} {
dict set test_case_fail result_stage "" dict set test_case_fail result_stage ""
# fall through to normal handling below (do not continue) # fall through to normal handling below (do not continue)
} else { } else {
@ -304,6 +397,7 @@ tcl::namespace::eval punk::tcltestrun {
dict unset test_case_fail result_was dict unset test_case_fail result_was
dict unset test_case_fail result_expected dict unset test_case_fail result_expected
dict unset test_case_fail result_stage dict unset test_case_fail result_stage
dict unset test_case_fail banner_lines
dict append results out "<stdout><$pkg> $ln" \n dict append results out "<stdout><$pkg> $ln" \n
continue ;#skip to next line. continue ;#skip to next line.
@ -543,7 +637,7 @@ namespace eval ::punk::args::register {
package provide punk::tcltestrun [tcl::namespace::eval punk::tcltestrun { package provide punk::tcltestrun [tcl::namespace::eval punk::tcltestrun {
variable pkg punk::tcltestrun variable pkg punk::tcltestrun
variable version variable version
set version 0.3.1 set version 0.4.0
}] }]
return return

72
src/bootsupport/modules/shellfilter-0.2.4.tm → src/bootsupport/modules/shellfilter-0.2.5.tm

@ -419,7 +419,10 @@ namespace eval shellfilter::chan {
} }
method initialize {transform_handle mode} { method initialize {transform_handle mode} {
#return [list initialize read drain write flush clear finalize] #return [list initialize read drain write flush clear finalize]
return [list initialize write flush clear finalize] #'clear' deliberately NOT declared: when a transform declares it, the core
#delivers a 'clear' op before EVERY write (tclIORTrans.c ReflectOutput), not
#just on seek - see goals/archive/G-145-piped-usage-ansi-remnants.md.
return [list initialize write flush finalize]
} }
method finalize {transform_handle} { method finalize {transform_handle} {
#Note that an error in the finalize can stop 'chan pop' from running properly. #Note that an error in the finalize can stop 'chan pop' from running properly.
@ -430,8 +433,15 @@ namespace eval shellfilter::chan {
# must be present but we ignore it because we do not # must be present but we ignore it because we do not
# post any events # post any events
} }
#G-145 defect class: 'clear' must NOT discard o_encbuf (held partial multi-byte
#character). With clear declared, the core calls it before every write; dropping
#the carry leaves the next chunk starting with orphan continuation bytes, making
#whole chunks unconvertible - they accumulate in o_encbuf and successive clears
#discard them, eating contiguous ranges of multibyte-dense output (the 2026-08-03
#unix repl result-echo corruption). Kept as a state-preserving no-op in case
#'clear' is ever re-declared; this write-only transform has no read-side state
#(the documented scope of 'clear').
method clear {transform_handle} { method clear {transform_handle} {
set o_encbuf ""
return return
} }
#method drain {transform_handle} { #method drain {transform_handle} {
@ -471,16 +481,22 @@ namespace eval shellfilter::chan {
# return $clear # return $clear
#} #}
method flush {transform_handle} { method flush {transform_handle} {
set clear $o_buffered$o_encbuf #this class holds only o_encbuf (raw bytes of a trailing incomplete
if {[catch {tcl::encoding::convertfrom $o_enc $clear} stringdata]} { #multi-byte character) - there is no o_buffered ansi-carry here (that
#if we can't convert the buffer contents to a string - does it make sense to emit the raw bytes? #belongs to the ansiwrap/ansistrip style transforms).
# - probably not. #An incomplete char is not decodable on its own: hold it for the next write
#REVIEW? #rather than emitting garbage or discarding (G-145: dropping held stream
#state corrupts content split across write chunks). finalize dropping it at
#true end of stream is acceptable.
if {$o_encbuf eq ""} {
return ""
}
if {[catch {tcl::encoding::convertfrom $o_enc $o_encbuf} stringdata]} {
return "" return ""
} }
set o_buffered ""
set o_encbuf "" set o_encbuf ""
return $stringdata puts -nonewline $o_localchan $stringdata
return [tcl::encoding::convertto $o_enc $stringdata]
} }
method write {transform_handle bytes} { method write {transform_handle bytes} {
#set logdata [tcl::encoding::convertfrom $o_enc $bytes] #set logdata [tcl::encoding::convertfrom $o_enc $bytes]
@ -570,16 +586,19 @@ namespace eval shellfilter::chan {
# return $clear # return $clear
#} #}
method flush {transform_handle} { method flush {transform_handle} {
set clear $o_buffered$o_encbuf #only o_encbuf exists in this class (no o_buffered ansi-carry as in ansiwrap).
if {[catch {tcl::encoding::convertfrom $o_enc $clear} stringdata]} { #An incomplete multi-byte char is not decodable on its own: hold it for the
#if we can't convert the buffer contents to a string - does it make sense to emit the raw bytes? #next write rather than discarding (G-145: dropping held stream state
# - probably not. #corrupts content split across write chunks).
#REVIEW? if {$o_encbuf eq ""} {
return ""
}
if {[catch {tcl::encoding::convertfrom $o_enc $o_encbuf} stringdata]} {
return "" return ""
} }
set o_buffered ""
set o_encbuf "" set o_encbuf ""
return $stringdata ::shellfilter::log::write $o_logsource $stringdata
return [tcl::encoding::convertto $o_enc $stringdata]
} }
method write {ch bytes} { method write {ch bytes} {
#set logdata [tcl::encoding::convertfrom $o_enc $bytes] #set logdata [tcl::encoding::convertfrom $o_enc $bytes]
@ -668,16 +687,19 @@ namespace eval shellfilter::chan {
# return # return
#} #}
method flush {transform_handle} { method flush {transform_handle} {
set clear $o_buffered$o_encbuf #only o_encbuf exists in this class (no o_buffered ansi-carry as in ansiwrap).
if {[catch {tcl::encoding::convertfrom $o_enc $clear} stringdata]} { #logonly emits nothing downstream. An incomplete multi-byte char is not
#if we have data in the buffer that we haven't been able to convert to a string #decodable on its own: hold it for the next write rather than discarding
#- then we probably have some kind of encoding mismatch. Is it safer to discard it than to emit garbage chars to the log? #(G-145: dropping held stream state corrupts content split across write
#REVIEW. - we are writing the raw bytes to the log here because we can't convert them to a string. #chunks); if it decodes anyway (e.g after an encoding change), log it now.
#This may be useful for debugging issues, but it may also result in garbage data in the log. if {$o_encbuf eq ""} {
::shellfilter::log::write $o_logsource $o_encbuf return ""
}
if {![catch {tcl::encoding::convertfrom $o_enc $o_encbuf} stringdata]} {
::shellfilter::log::write $o_logsource $stringdata
set o_encbuf "" set o_encbuf ""
} }
return return ""
} }
method write {transform_handle bytes} { method write {transform_handle bytes} {
#set logdata [encoding convertfrom $o_enc $bytes] #set logdata [encoding convertfrom $o_enc $bytes]
@ -3838,5 +3860,5 @@ namespace eval shellfilter {
package provide shellfilter [namespace eval shellfilter { package provide shellfilter [namespace eval shellfilter {
variable version variable version
set version 0.2.4 set version 0.2.5
}] }]
Loading…
Cancel
Save