Browse Source

G-037 achieved: make.tcl vfslibs propagates vendorlib packages into kit vfs lib_tcl trees; 8.6 kits to tcludp 1.0.13

New 'make.tcl vfslibs' step (also run inside 'make.tcl project' before kit vfs
assembly): propagates declared vendored platform-library packages into kit vfs
lib_tcl<N> trees, ending the hand-copying that let kits ship a known-buggy
tcludp through three make invocations (the G-036/G-037 motivating case).

- declaration: src/runtime/vendorlib_vfs.toml, parsed with bootsupport tomlish
  (no ad-hoc parsing; G-024's mapvfs toml conversion NOT required first - this
  file is recorded as a candidate for absorption into it). [install.<name>]
  entries: source (relative to src/), targets (kit vfs lib folders relative to
  src/vfs/), optional supersedes (folder names removed from each target before
  install - explicit, no silent mixed-version provision per G-035 concerns).
- participation is per-package per-kit; authority is the declaration - nothing
  syncs undeclared, so the known vendorlib/kit twapi drift is deliberately
  untouched (resolves the direction/authority design question).
- punkcheck provenance at src/vfs/.punkcheck (gitignored, outside kit
  payloads); skip-if-unchanged proven (rerun: 0 copied, 9 skipped per target);
  malformed/unresolvable entries fail the build naming the entry (exit 3).
- 8.6 kits upgraded (the G-036 loose-end decision, recorded): tcludp 1.0.13 is
  dual-generation (pkgIndex selects tcl9udp1013.dll on 9+, udp1013t.dll on
  8.5+) so the single vendorlib_tcl9 source serves punk8win.vfs/lib_tcl8;
  superseded udp1.0.12 removed.
- verified on rebuilt kits: punksys/punkbi (8.6.13) and punk902z (9.0.2) all
  report package require udp == 1.0.13 loading the generation-appropriate dll
  from their vfs; no udp1.0.12 remains under src/vfs; punksys piped-shell exit
  smoke clean.
- G-037 flipped proposed -> achieved 2026-07-08 under user direction (no
  intermediate active stop).
- includes project-build byproducts via established channels: bootsupport
  snapshots refreshed to punk-0.2.1 / punk::lib-0.3.0 (from today's version
  bumps) and make.tcl synced into layout copies.
- project 0.4.8 (CHANGELOG entry)

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 7 days ago
parent
commit
0505397590
  1. 5
      CHANGELOG.md
  2. 2
      GOALS.md
  3. 12
      goals/G-036-tcl9-udp-console-worker-wedge.md
  4. 47
      goals/G-037-vendorlib-vfs-propagation.md
  5. 2
      punkproject.toml
  6. 19
      src/bootsupport/modules/punk-0.2.1.tm
  7. 56
      src/bootsupport/modules/punk/lib-0.3.0.tm
  8. 99
      src/make.tcl
  9. 99
      src/project_layouts/custom/_project/punk.basic/src/make.tcl
  10. 19
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk-0.2.1.tm
  11. 56
      src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/lib-0.3.0.tm
  12. 99
      src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl
  13. 19
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk-0.2.1.tm
  14. 56
      src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/lib-0.3.0.tm
  15. 99
      src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl
  16. 32
      src/runtime/vendorlib_vfs.toml
  17. 39
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/broadcast.tcl
  18. 56
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/bug1158628.tcl
  19. 81
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/chat.tcl
  20. 45
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/multicast.tcl
  21. 65
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/udpcat.tcl
  22. 33
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/pkgIndex.tcl
  23. BIN
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/tcl9udp1013.dll
  24. 326
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/udp.html
  25. BIN
      src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/udp1013t.dll
  26. 2
      src/vfs/punk8win.vfs/lib_tcl8/udp1.0.12/pkgIndex.tcl
  27. BIN
      src/vfs/punk8win.vfs/lib_tcl8/udp1.0.12/udp1012.dll

5
CHANGELOG.md

@ -5,6 +5,11 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy.
## [0.4.8] - 2026-07-08
- G-037 completed (flipped to achieved): new `make.tcl vfslibs` step (also run inside `make.tcl project`) propagates declared vendored platform-library packages into kit vfs lib_tcl<N> trees — declarations in `src/runtime/vendorlib_vfs.toml` (tomlish-parsed, per-package per-kit with explicit `supersedes` removal so no silent mixed-version provision; punkcheck provenance at src/vfs/.punkcheck, outside kit payloads; malformed entries fail the build naming the entry). G-024's mapvfs toml conversion was not required; the file is recorded as a candidate for absorption into it. Authority is the declaration — undeclared drift (e.g. the vendorlib/kit twapi divergence) is deliberately untouched.
- 8.6 kits upgraded to tcludp 1.0.13 via the new step (the G-036 loose-end decision): the 1.0.13 folder is dual-generation (pkgIndex selects tcl9udp1013.dll or udp1013t.dll), so the single vendorlib_tcl9 source serves punk8win.vfs/lib_tcl8, replacing udp 1.0.12. Verified on rebuilt kits: punksys/punkbi (8.6.13) and punk902z (9.0.2) all load udp 1.0.13 from their vfs; no udp1.0.12 remains under src/vfs.
## [0.4.7] - 2026-07-08
- G-036 completed (flipped to achieved): `punk::lib` 0.3.0 adds `punk::lib::check::has_libbug_udp_threadexit` — version-based detection of the tcludp < 1.0.13 thread-exit bug on Tcl 9 Windows (the per-thread exit handler closes process-global event handles, silently freezing every other udp-using thread's event loop after any udp-loaded thread exits; root-caused and fixed-by-upgrade earlier in 0.4.3). `has_libbug_*` is the new check family for bundled/vendored library bugs; `help tcl` (punk 0.2.1) now scans it alongside `has_tclbug_*`, supports a full `url` reference key for non tcl-core trackers, and no longer errors on a triggered check carrying a reference without a description. Detection is loaded-version based, falling back to the best available registered version without loading the binary. New checkbugs.test covers the classifier matrix, the live check's dict shape/consistency, and a contract test over all existing check procs. Open decisions recorded in the G-036 detail: punk8win (8.6) kit's udp 1.0.12 swap; optional upstream tickets for residual tcludp trunk weaknesses.

2
GOALS.md

@ -267,7 +267,7 @@ Detail: goals/G-036-tcl9-udp-console-worker-wedge.md
Goal: the Tcl 9-only wedge - a worker thread that has used a tcludp syslog socket stops servicing its event queue (timers, thread::send) when the punkshell process is console-attached, the proximate trigger of the piped-stdin exit/quit hang - is root-caused to a named component (Tcl 9 Windows console driver, tcludp, Thread extension, or a specific interaction) with the smallest demonstrating repro, so the user can decide on and manually file an upstream report.
Acceptance: (reworked 2026-07-08 after the root cause was found) the wedge mechanism is identified and written up in the detail file - DONE: bundled tcludp 1.0.12's Windows per-thread UDP_ExitProc closes the process-global synchronization events, proven by dump handle-table analysis plus a live CloseHandle breakpoint and confirmed by the upstream 1.0.12->1.0.13 diff, which already fixes it (so no upstream report is required; the standalone minimal repro originally required here is waived as moot by the user); the tcl9 kits bundle tcludp >= 1.0.13 with the in-context batch harness baseline resolved - DONE 2026-07-08 (run-2 syslog workers alive vs the 4/4-wedged 1.0.12 baseline); DONE 2026-07-08 (punk::lib 0.3.0 has_libbug_udp_threadexit, surfaced via 'help tcl' in punk 0.2.1): a has_bug-style detection in the punkshell check machinery (in the vein of punk::lib::check::has_tclbug_* / punk::console::check::has_bug_*, surfaced through the same reporting as 'help tcl'/'help console') reports the vulnerable combination - simple version-based detection (loaded/bundled tcludp < 1.0.13 on Tcl 9 Windows) is sufficient, no behavioural probe needed; loose-end decisions (punk8win 8.6 kit's udp 1.0.12 swap; optional upstream tickets for residual tcludp trunk weaknesses) recorded in the detail file when made (open as of 2026-07-08 - non-gating).
### G-037 [proposed] Propagate platform vendor libraries into kit vfs lib_tcl trees via make.tcl
### G-037 [achieved 2026-07-08] Propagate platform vendor libraries into kit vfs lib_tcl trees via make.tcl
Scope: src/make.tcl (new or extended step), src/vendorlib_tcl8 + src/vendorlib_tcl9 (sources), src/vfs/<kit>.vfs/lib_tcl8 + lib_tcl9 (targets), punkcheck tracking
Detail: goals/G-037-vendorlib-vfs-propagation.md
Goal: platform-specific vendored binary packages under src/vendorlib_tcl<N>/<platform> reach the kit vfs lib_tcl<N> trees through a make.tcl step instead of hand-copying - updating a vendored package becomes a vendorlib drop plus standard build invocations (motivating case 2026-07-08: tcludp 1.0.12 -> 1.0.13 for the G-036 wedge fix - `libs`, `vfscommonupdate` and `project` all completed while every kit vfs still bundled udp 1.0.12, requiring a manual copy into each vfs lib_tcl9 folder).

12
goals/G-036-tcl9-udp-console-worker-wedge.md

@ -301,9 +301,15 @@ shorthand). Verified: current punk902z kit (udp 1.0.13) reports bug=0 with no wa
a simulated triggered has_libbug_* check renders the warning block with description and
url; checkbugs.test also pins the buginfo dict contract across all existing check procs.
Loose ends / optional follow-ups (open as of 2026-07-08 - non-gating, record decisions here when made):
- Check which tcludp the punksys (8.6) kit bundles and why 8.6 appeared immune (different udp
version, different exit-handler ordering, or fast-fail luck).
Loose ends / optional follow-ups (record decisions here when made):
- DECIDED + DONE 2026-07-08: punk8win.vfs (8.6 kits punksys/punkbi) swapped from udp 1.0.12
to tcludp 1.0.13 - the 1.0.13 folder is dual-generation (udp1013t.dll for 8.x selected by
its pkgIndex) so the same vendorlib_tcl9 source serves lib_tcl8. Applied via the new
G-037 vfslibs propagation step (declaration in src/runtime/vendorlib_vfs.toml removes the
superseded udp1.0.12). The 8.6 apparent immunity remains unexplained but is now moot for
punkshell kits (same fixed code everywhere).
- Check why 8.6 appeared immune (different exit-handler ordering, or fast-fail luck) -
academic now that all kits carry 1.0.13.
- Residual weaknesses still present in tcludp trunk, candidates for polite upstream tickets:
auto-reset-event-used-as-lock (lost-SetEvent risk, no ownership); packetNum not decremented
on the recvfrom error path (ICMP port-unreachable leaves packetNum>0 -> permanent

47
goals/G-037-vendorlib-vfs-propagation.md

@ -1,6 +1,6 @@
# G-037 Propagate platform vendor libraries into kit vfs lib_tcl trees via make.tcl
Status: proposed
Status: achieved 2026-07-08
Scope: src/make.tcl (new or extended step), src/vendorlib_tcl8 + src/vendorlib_tcl9 (sources), src/vfs/<kit>.vfs/lib_tcl8 + lib_tcl9 (targets), punkcheck tracking
Acceptance: with a newer package version placed under src/vendorlib_tcl9/<platform>, one documented make.tcl invocation updates the participating src/vfs/*/lib_tcl9 trees - installing the new package and removing or explicitly retiring the superseded version (no silent mixed-version provision, per the G-035 concerns) - with punkcheck-tracked provenance; which vfs folders participate is explicitly declared per kit rather than blanket-copied (kit vfs package sets may intentionally differ), with the declaration mechanism recorded (candidate home: the G-024 mapvfs toml); a subsequent `make.tcl project` yields kits loading the new version (provable via the tcludp case: built punk902z reports `package require udp` == 1.0.13 with no udp1.0.12 folder remaining in its vfs); the lib_tcl8 tree gets the same treatment or an explicit exclusion rationale in the goal record.
@ -47,6 +47,51 @@ What the existing targets actually cover (verified in make.tcl at the time of wr
- **lib_tcl8**: same mechanism should serve the tcl8 kits (punk8win.vfs still bundles udp
1.0.12 as of 0.4.3 - see G-036 loose ends), or the exclusion is recorded here.
## Implementation (2026-07-08)
Mechanism as landed (design points above settled as follows):
- **Declaration**: `src/runtime/vendorlib_vfs.toml`, parsed with the bootsupport tomlish
package (1.1.10) - no ad-hoc parsing. Named tables `[install.<name>]` with keys:
`source` (package folder relative to src/), `targets` (list of kit vfs lib folders
relative to src/vfs/), optional `supersedes` (folder names removed from each target
before install). G-024's mapvfs toml conversion was NOT required first - this file is
recorded as a candidate for absorption into it. (make.tcl's own help already carried a
matching intent: "update the src/vendorlib based on src/vendorlib/config.toml (todo)".)
- **Step**: `make.tcl vfslibs` (new subcommand), also run automatically inside
`make.tcl project` before kit vfs assembly. Malformed/unresolvable entries fail loudly
naming the entry (exit 3).
- **Supersession**: explicit `supersedes` folder-name list per entry (the simpler option;
pkgIndex-declared package identity detection remains a possible refinement). The step
never removes the folder it is about to install, and removal happens before install.
- **Direction/authority**: declaration-driven only - nothing syncs unless declared, so the
observed twapi drift (vendorlib 5.0b1 older than kit copies) is untouched until someone
declares a twapi entry. This resolves the "explicit direction/authority semantics"
question: authority is the declaration, not file timestamps/versions.
- **Cross-generation sources**: allowed when the package supports it - the motivating
tcludp 1.0.13 folder is dual-generation (tcl9udp1013.dll + udp1013t.dll, pkgIndex
selects by [package vsatisfies]), so the single vendorlib_tcl9 source serves the
punk8win.vfs/lib_tcl8 target (no duplicate binary committed under vendorlib_tcl8).
- **punkcheck provenance**: records at `src/vfs/.punkcheck` via -punkcheck-folder (kept
outside the .vfs payload folders so no records are baked into kits); skip-if-unchanged
verified (second run: 0 copied, 9 skipped per target).
- **lib_tcl8**: same mechanism, exercised immediately - punk8win.vfs/lib_tcl8 had
udp1.0.12 removed and tcludp1.0.13 installed via the declaration (the G-036 loose-end
decision, made 2026-07-08).
Verification (2026-07-08, acceptance met - goal flipped to achieved):
- `make.tcl vfslibs`: installed tcludp1.0.13 into punk8win.vfs/lib_tcl8 (9 files),
removed superseded udp1.0.12; re-recorded the two tcl9 kit targets (previously
hand-copied). Second run: 0 copied / 9 skipped per target (punkcheck skip-unchanged).
- `make.tcl project` (with the vfslibs step now inside it): INSTALLED KITS punkbi.exe,
punksys.exe, punk902z.exe. punk9win_for_tkruntime.vfs correctly detected as unchanged
(its 1.0.13 payload predated the step) - cached kit redeployed.
- Built kits: punksys 8.6.13 `package require udp` = 1.0.13 loading
lib_tcl8/tcludp1.0.13/udp1013t.dll; punkbi likewise; punk902z 9.0.2 = 1.0.13 loading
zipfs lib_tcl9/tcludp1.0.13/tcl9udp1013.dll. No udp1.0.12 folder remains anywhere
under src/vfs. Piped-shell exit smoke on punksys clean (PUNK_PIPE_EOF=exit).
## Alternatives considered
- Extending `vfscommonupdate` to also sync platform libs into `_vfscommon.vfs` - rejected

2
punkproject.toml

@ -1,3 +1,3 @@
[project]
name = "punkshell"
version = "0.4.7"
version = "0.4.8"

19
src/bootsupport/modules/punk-0.2.0.tm → src/bootsupport/modules/punk-0.2.1.tm

@ -8570,7 +8570,7 @@ namespace eval punk {
punk::args::define {
@id -id ::punk::helptopic::tcl
@cmd -name "help tcl" -summary "Tcl version warnings." -help "Show the running Tcl patchlevel and build-info, with warnings for known Tcl bugs affecting this interpreter (as detected by the punk::lib::check::has_tclbug_* checks)."
@cmd -name "help tcl" -summary "Tcl version warnings." -help "Show the running Tcl patchlevel and build-info, with warnings for known Tcl bugs affecting this interpreter and known bugs in bundled library packages (as detected by the punk::lib::check::has_tclbug_* and has_libbug_* checks)."
@values -min 0 -max 0
}
proc tcl {context args} {
@ -8580,8 +8580,12 @@ namespace eval punk {
catch {
append text \n "Tcl build-info: [::tcl::build-info]"
}
#generate warningblocks for each triggered Tcl bug in namespace ::punk::lib::check
set bugcheck_procs [info procs ::punk::lib::check::has_tclbug*]
#generate warningblocks for each triggered bug check in namespace ::punk::lib::check
#has_tclbug_* = Tcl core bugs; has_libbug_* = bundled/vendored library bugs (e.g. tcludp - G-036)
set bugcheck_procs [concat\
[info procs ::punk::lib::check::has_tclbug*]\
[info procs ::punk::lib::check::has_libbug*]\
]
foreach bp $bugcheck_procs {
set buginfo [$bp]
if {[dict get $buginfo bug]} {
@ -8595,12 +8599,15 @@ namespace eval punk {
major {set highlight [punk::ansi::a+ red bold]}
default {set highlight ""}
}
set indent " "
append warningblock \n $highlight "warning level: $level $bp triggered."
if {[dict exists $buginfo description]} {
set indent " "
append warningblock \n "[punk::lib::indent [dict get $buginfo description] $indent]"
}
if {[dict exists $buginfo bugref] && [dict get $buginfo bugref] ne ""} {
if {[dict exists $buginfo url] && [dict get $buginfo url] ne ""} {
#full reference url (e.g. non tcl-core trackers such as tcludp)
append warningblock \n "${indent}see [punk::ansi::hyperlink [dict get $buginfo url]]"
} elseif {[dict exists $buginfo bugref] && [dict get $buginfo bugref] ne ""} {
set bugref [dict get $buginfo bugref]
append warningblock \n "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/$bugref]"
}
@ -9350,7 +9357,7 @@ punkcheck::cli set_alias punkcheck
package provide punk [namespace eval punk {
#FUNCTL
variable version
set version 0.2.0
set version 0.2.1
}]

56
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/lib-0.2.1.tm → src/bootsupport/modules/punk/lib-0.3.0.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::lib 0.2.1
# Application punk::lib 0.3.0
# Meta platform tcl
# Meta license BSD
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::lib 0 0.2.1]
#[manpage_begin punkshell_module_punk::lib 0 0.3.0]
#[copyright "2024"]
#[titledesc {punk general utility functions}] [comment {-- Name section and table of contents description --}]
#[moddesc {punk library}] [comment {-- Description at end of page heading --}]
@ -250,6 +250,56 @@ tcl::namespace::eval punk::lib::check {
return [dict create bug $bug bugref e38dc74e2 description $description level medium]
}
#has_libbug_* procs report bugs in bundled/vendored library packages rather than the Tcl core.
#They are surfaced through the same 'help tcl' warning report as the has_tclbug_* checks.
#pure classifier, separated for testability - facts in, verdict out
proc libbug_udp_threadexit_applies {udpversion platform tclversion} {
if {$udpversion eq ""} {
return 0 ;#no udp package known - nothing vulnerable to load
}
if {$platform ne "windows"} {
return 0
}
if {![package vsatisfies $tclversion 9]} {
#tcl 8.6 exhibited apparent immunity in the G-036 investigation (unexplained - see goal detail)
#detection deliberately scoped to Tcl 9 per G-036 acceptance
return 0
}
return [expr {[package vcompare $udpversion 1.0.13] < 0}]
}
proc has_libbug_udp_threadexit {} {
#tcludp <= 1.0.12 on Windows: the per-thread exit handler UDP_ExitProc closes the
#PROCESS-GLOBAL sockListLock/waitForSock event handles. The first udp-loaded thread to
#exit corrupts the lock for every other udp-loaded thread - their event loops block
#forever in WaitForSingleObject on a dead or recycled handle (timers and thread::send
#unserviced). In punkshell the visible casualty is syslog log workers silently freezing
#(the original exit/quit hang this caused is separately mitigated). Root-caused
#2026-07-08 - see goal G-036 detail. Fixed upstream in tcludp 1.0.13 (per-thread
#UdpThreadExitProc deletes only the event source; ExitSockets closes the events at
#process exit).
#Version-based detection only (per G-036 acceptance) - no behavioural probe.
set udpversion [package provide udp]
if {$udpversion eq ""} {
#not loaded - determine what version would be provided, without loading the binary:
#an unsatisfiable require triggers the package unknown scan (registering ifneeded
#scripts) then fails before any dll load.
catch {package require udp 999999}
set available [package versions udp]
if {[llength $available]} {
set udpversion [lindex [lsort -command {package vcompare} $available] end]
}
}
set bug [libbug_udp_threadexit_applies $udpversion $::tcl_platform(platform) [package provide Tcl]]
if {$udpversion eq ""} {
set verinfo "no udp package found"
} else {
set verinfo "udp $udpversion"
}
set description "tcludp < 1.0.13 on Tcl 9 Windows ($verinfo): the udp package's per-thread exit\nhandler closes process-global event handles - after any udp-using thread exits, every\nother udp-using thread's event loop can freeze silently (e.g. syslog log workers stop).\nUpgrade the bundled/available udp package to 1.0.13+ (see goal G-036)."
return [dict create bug $bug bugref "" url https://core.tcl-lang.org/tcludp description $description level medium libversion $udpversion]
}
proc has_tclbug_safeinterp_compile {{show 0}} {
#ensemble calls within safe interp not compiled
#https://core.tcl-lang.org/tcl/tktview/1095bf7f756f9aed6bde
@ -9069,7 +9119,7 @@ namespace eval ::punk::args::register {
package provide punk::lib [tcl::namespace::eval punk::lib {
variable pkg punk::lib
variable version
set version 0.2.1
set version 0.3.0
}]
return

99
src/make.tcl

@ -22,7 +22,7 @@ namespace eval ::punkboot {
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k]
variable help_flags [list -help --help /? -h]
variable known_commands [list project modules libs packages vfs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
variable known_commands [list project modules libs packages vfs vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
}
@ -1348,6 +1348,10 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " $scriptname vendorupdate" \n
append h " - update the src/vendormodules based on src/vendormodules/include_modules.config" \n \n
append h " - update the src/vendorlib based on src/vendorlib/config.toml (todo)" \n \n
append h " $scriptname vfslibs" \n
append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n
append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n
append h " - also runs automatically as part of '$scriptname project'" \n \n
append h " $scriptname vfscommonupdate" \n
append h " - update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n
append h " - before calling this (followed by make project) - you can test using '<builtexe>(.exe) dev'" \n
@ -2353,7 +2357,7 @@ if {$::punkboot::command eq "bootsupport"} {
if {$::punkboot::command ni {project modules libs packages vfs bin}} {
if {$::punkboot::command ni {project modules libs packages vfs vfslibs bin}} {
puts stderr "Command $::punkboot::command not implemented - aborting."
flush stderr
after 100
@ -2475,6 +2479,97 @@ if {$::punkboot::command in {project packages libs}} {
}
}
if {$::punkboot::command in {project vfslibs}} {
#G-037: propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees.
#Participation is per-package per-kit (kit vfs package sets intentionally differ - e.g. twapi
#versions vary per kit) - declared in src/runtime/vendorlib_vfs.toml, never blanket-copied.
#punkcheck records live at src/vfs/.punkcheck so they are not baked into kit payloads.
set vfslibs_config $sourcefolder/runtime/vendorlib_vfs.toml
if {![file exists $vfslibs_config]} {
puts stdout "VFSLIBS: no config at $vfslibs_config - skipping vendorlib -> kit vfs propagation"
} else {
if {[catch {package require tomlish} tomlish_err]} {
puts stderr "$A(BAD)VFSLIBS: cannot process $vfslibs_config - tomlish package unavailable ($tomlish_err)$A(RST)"
exit 3
}
set fd [open $vfslibs_config r]
fconfigure $fd -encoding utf-8
set vfslibs_tomldata [read $fd]
close $fd
if {[catch {tomlish::to_dict [tomlish::from_toml $vfslibs_tomldata]} vfslibs_dict]} {
puts stderr "$A(BAD)VFSLIBS: failed to parse $vfslibs_config\n$vfslibs_dict$A(RST)"
exit 3
}
#tomlish::to_dict values are type-tagged (e.g {type STRING value x}, {type ARRAY value {...}})
proc ::punkboot::lib::toml_untag {tagged} {
if {[dict get $tagged type] eq "ARRAY"} {
set plain [list]
foreach el [dict get $tagged value] {
lappend plain [::punkboot::lib::toml_untag $el]
}
return $plain
}
return [dict get $tagged value]
}
set vfslibs_installs [dict create]
if {[dict exists $vfslibs_dict install]} {
set vfslibs_installs [dict get $vfslibs_dict install]
}
if {![dict size $vfslibs_installs]} {
puts stdout "VFSLIBS: no \[install.<name>\] entries found in $vfslibs_config"
}
dict for {iname ientry} $vfslibs_installs {
foreach reqkey {source targets} {
if {![dict exists $ientry $reqkey]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname in $vfslibs_config is missing required key '$reqkey'$A(RST)"
exit 3
}
}
set isource [punkboot::lib::toml_untag [dict get $ientry source]]
set itargets [punkboot::lib::toml_untag [dict get $ientry targets]]
set isupersedes [list]
if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
}
set source_pkg_folder $sourcefolder/$isource
if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)"
exit 3
}
set pkgtail [file tail $source_pkg_folder]
foreach t $itargets {
set target_vfs_dir [lindex [file split $t] 0]
if {![file isdirectory $sourcefolder/vfs/$target_vfs_dir]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname target '$t' - no vfs folder at $sourcefolder/vfs/$target_vfs_dir$A(RST)"
exit 3
}
set target_lib_folder $sourcefolder/vfs/$t
file mkdir $target_lib_folder
#supersession first - no silent mixed-version provision (G-035 concerns)
foreach sup $isupersedes {
if {$sup eq $pkgtail} {
continue ;#never remove what we are about to install
}
set suppath $target_lib_folder/$sup
if {[file exists $suppath]} {
puts stdout "VFSLIBS install.$iname: removing superseded '$sup' from vfs/$t"
file delete -force $suppath
}
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\
-createdir 1\
-overwrite installedsourcechanged-targets\
-punkcheck-folder $sourcefolder/vfs\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
}
if {$::punkboot::command in {project packages libs}} {
########################################################
lappend projectlibfolders lib

99
src/project_layouts/custom/_project/punk.basic/src/make.tcl

@ -22,7 +22,7 @@ namespace eval ::punkboot {
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k]
variable help_flags [list -help --help /? -h]
variable known_commands [list project modules libs packages vfs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
variable known_commands [list project modules libs packages vfs vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
}
@ -1348,6 +1348,10 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " $scriptname vendorupdate" \n
append h " - update the src/vendormodules based on src/vendormodules/include_modules.config" \n \n
append h " - update the src/vendorlib based on src/vendorlib/config.toml (todo)" \n \n
append h " $scriptname vfslibs" \n
append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n
append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n
append h " - also runs automatically as part of '$scriptname project'" \n \n
append h " $scriptname vfscommonupdate" \n
append h " - update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n
append h " - before calling this (followed by make project) - you can test using '<builtexe>(.exe) dev'" \n
@ -2353,7 +2357,7 @@ if {$::punkboot::command eq "bootsupport"} {
if {$::punkboot::command ni {project modules libs packages vfs bin}} {
if {$::punkboot::command ni {project modules libs packages vfs vfslibs bin}} {
puts stderr "Command $::punkboot::command not implemented - aborting."
flush stderr
after 100
@ -2475,6 +2479,97 @@ if {$::punkboot::command in {project packages libs}} {
}
}
if {$::punkboot::command in {project vfslibs}} {
#G-037: propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees.
#Participation is per-package per-kit (kit vfs package sets intentionally differ - e.g. twapi
#versions vary per kit) - declared in src/runtime/vendorlib_vfs.toml, never blanket-copied.
#punkcheck records live at src/vfs/.punkcheck so they are not baked into kit payloads.
set vfslibs_config $sourcefolder/runtime/vendorlib_vfs.toml
if {![file exists $vfslibs_config]} {
puts stdout "VFSLIBS: no config at $vfslibs_config - skipping vendorlib -> kit vfs propagation"
} else {
if {[catch {package require tomlish} tomlish_err]} {
puts stderr "$A(BAD)VFSLIBS: cannot process $vfslibs_config - tomlish package unavailable ($tomlish_err)$A(RST)"
exit 3
}
set fd [open $vfslibs_config r]
fconfigure $fd -encoding utf-8
set vfslibs_tomldata [read $fd]
close $fd
if {[catch {tomlish::to_dict [tomlish::from_toml $vfslibs_tomldata]} vfslibs_dict]} {
puts stderr "$A(BAD)VFSLIBS: failed to parse $vfslibs_config\n$vfslibs_dict$A(RST)"
exit 3
}
#tomlish::to_dict values are type-tagged (e.g {type STRING value x}, {type ARRAY value {...}})
proc ::punkboot::lib::toml_untag {tagged} {
if {[dict get $tagged type] eq "ARRAY"} {
set plain [list]
foreach el [dict get $tagged value] {
lappend plain [::punkboot::lib::toml_untag $el]
}
return $plain
}
return [dict get $tagged value]
}
set vfslibs_installs [dict create]
if {[dict exists $vfslibs_dict install]} {
set vfslibs_installs [dict get $vfslibs_dict install]
}
if {![dict size $vfslibs_installs]} {
puts stdout "VFSLIBS: no \[install.<name>\] entries found in $vfslibs_config"
}
dict for {iname ientry} $vfslibs_installs {
foreach reqkey {source targets} {
if {![dict exists $ientry $reqkey]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname in $vfslibs_config is missing required key '$reqkey'$A(RST)"
exit 3
}
}
set isource [punkboot::lib::toml_untag [dict get $ientry source]]
set itargets [punkboot::lib::toml_untag [dict get $ientry targets]]
set isupersedes [list]
if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
}
set source_pkg_folder $sourcefolder/$isource
if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)"
exit 3
}
set pkgtail [file tail $source_pkg_folder]
foreach t $itargets {
set target_vfs_dir [lindex [file split $t] 0]
if {![file isdirectory $sourcefolder/vfs/$target_vfs_dir]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname target '$t' - no vfs folder at $sourcefolder/vfs/$target_vfs_dir$A(RST)"
exit 3
}
set target_lib_folder $sourcefolder/vfs/$t
file mkdir $target_lib_folder
#supersession first - no silent mixed-version provision (G-035 concerns)
foreach sup $isupersedes {
if {$sup eq $pkgtail} {
continue ;#never remove what we are about to install
}
set suppath $target_lib_folder/$sup
if {[file exists $suppath]} {
puts stdout "VFSLIBS install.$iname: removing superseded '$sup' from vfs/$t"
file delete -force $suppath
}
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\
-createdir 1\
-overwrite installedsourcechanged-targets\
-punkcheck-folder $sourcefolder/vfs\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
}
if {$::punkboot::command in {project packages libs}} {
########################################################
lappend projectlibfolders lib

19
src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk-0.2.0.tm → src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk-0.2.1.tm

@ -8570,7 +8570,7 @@ namespace eval punk {
punk::args::define {
@id -id ::punk::helptopic::tcl
@cmd -name "help tcl" -summary "Tcl version warnings." -help "Show the running Tcl patchlevel and build-info, with warnings for known Tcl bugs affecting this interpreter (as detected by the punk::lib::check::has_tclbug_* checks)."
@cmd -name "help tcl" -summary "Tcl version warnings." -help "Show the running Tcl patchlevel and build-info, with warnings for known Tcl bugs affecting this interpreter and known bugs in bundled library packages (as detected by the punk::lib::check::has_tclbug_* and has_libbug_* checks)."
@values -min 0 -max 0
}
proc tcl {context args} {
@ -8580,8 +8580,12 @@ namespace eval punk {
catch {
append text \n "Tcl build-info: [::tcl::build-info]"
}
#generate warningblocks for each triggered Tcl bug in namespace ::punk::lib::check
set bugcheck_procs [info procs ::punk::lib::check::has_tclbug*]
#generate warningblocks for each triggered bug check in namespace ::punk::lib::check
#has_tclbug_* = Tcl core bugs; has_libbug_* = bundled/vendored library bugs (e.g. tcludp - G-036)
set bugcheck_procs [concat\
[info procs ::punk::lib::check::has_tclbug*]\
[info procs ::punk::lib::check::has_libbug*]\
]
foreach bp $bugcheck_procs {
set buginfo [$bp]
if {[dict get $buginfo bug]} {
@ -8595,12 +8599,15 @@ namespace eval punk {
major {set highlight [punk::ansi::a+ red bold]}
default {set highlight ""}
}
set indent " "
append warningblock \n $highlight "warning level: $level $bp triggered."
if {[dict exists $buginfo description]} {
set indent " "
append warningblock \n "[punk::lib::indent [dict get $buginfo description] $indent]"
}
if {[dict exists $buginfo bugref] && [dict get $buginfo bugref] ne ""} {
if {[dict exists $buginfo url] && [dict get $buginfo url] ne ""} {
#full reference url (e.g. non tcl-core trackers such as tcludp)
append warningblock \n "${indent}see [punk::ansi::hyperlink [dict get $buginfo url]]"
} elseif {[dict exists $buginfo bugref] && [dict get $buginfo bugref] ne ""} {
set bugref [dict get $buginfo bugref]
append warningblock \n "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/$bugref]"
}
@ -9350,7 +9357,7 @@ punkcheck::cli set_alias punkcheck
package provide punk [namespace eval punk {
#FUNCTL
variable version
set version 0.2.0
set version 0.2.1
}]

56
src/bootsupport/modules/punk/lib-0.2.1.tm → src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/lib-0.3.0.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::lib 0.2.1
# Application punk::lib 0.3.0
# Meta platform tcl
# Meta license BSD
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::lib 0 0.2.1]
#[manpage_begin punkshell_module_punk::lib 0 0.3.0]
#[copyright "2024"]
#[titledesc {punk general utility functions}] [comment {-- Name section and table of contents description --}]
#[moddesc {punk library}] [comment {-- Description at end of page heading --}]
@ -250,6 +250,56 @@ tcl::namespace::eval punk::lib::check {
return [dict create bug $bug bugref e38dc74e2 description $description level medium]
}
#has_libbug_* procs report bugs in bundled/vendored library packages rather than the Tcl core.
#They are surfaced through the same 'help tcl' warning report as the has_tclbug_* checks.
#pure classifier, separated for testability - facts in, verdict out
proc libbug_udp_threadexit_applies {udpversion platform tclversion} {
if {$udpversion eq ""} {
return 0 ;#no udp package known - nothing vulnerable to load
}
if {$platform ne "windows"} {
return 0
}
if {![package vsatisfies $tclversion 9]} {
#tcl 8.6 exhibited apparent immunity in the G-036 investigation (unexplained - see goal detail)
#detection deliberately scoped to Tcl 9 per G-036 acceptance
return 0
}
return [expr {[package vcompare $udpversion 1.0.13] < 0}]
}
proc has_libbug_udp_threadexit {} {
#tcludp <= 1.0.12 on Windows: the per-thread exit handler UDP_ExitProc closes the
#PROCESS-GLOBAL sockListLock/waitForSock event handles. The first udp-loaded thread to
#exit corrupts the lock for every other udp-loaded thread - their event loops block
#forever in WaitForSingleObject on a dead or recycled handle (timers and thread::send
#unserviced). In punkshell the visible casualty is syslog log workers silently freezing
#(the original exit/quit hang this caused is separately mitigated). Root-caused
#2026-07-08 - see goal G-036 detail. Fixed upstream in tcludp 1.0.13 (per-thread
#UdpThreadExitProc deletes only the event source; ExitSockets closes the events at
#process exit).
#Version-based detection only (per G-036 acceptance) - no behavioural probe.
set udpversion [package provide udp]
if {$udpversion eq ""} {
#not loaded - determine what version would be provided, without loading the binary:
#an unsatisfiable require triggers the package unknown scan (registering ifneeded
#scripts) then fails before any dll load.
catch {package require udp 999999}
set available [package versions udp]
if {[llength $available]} {
set udpversion [lindex [lsort -command {package vcompare} $available] end]
}
}
set bug [libbug_udp_threadexit_applies $udpversion $::tcl_platform(platform) [package provide Tcl]]
if {$udpversion eq ""} {
set verinfo "no udp package found"
} else {
set verinfo "udp $udpversion"
}
set description "tcludp < 1.0.13 on Tcl 9 Windows ($verinfo): the udp package's per-thread exit\nhandler closes process-global event handles - after any udp-using thread exits, every\nother udp-using thread's event loop can freeze silently (e.g. syslog log workers stop).\nUpgrade the bundled/available udp package to 1.0.13+ (see goal G-036)."
return [dict create bug $bug bugref "" url https://core.tcl-lang.org/tcludp description $description level medium libversion $udpversion]
}
proc has_tclbug_safeinterp_compile {{show 0}} {
#ensemble calls within safe interp not compiled
#https://core.tcl-lang.org/tcl/tktview/1095bf7f756f9aed6bde
@ -9069,7 +9119,7 @@ namespace eval ::punk::args::register {
package provide punk::lib [tcl::namespace::eval punk::lib {
variable pkg punk::lib
variable version
set version 0.2.1
set version 0.3.0
}]
return

99
src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl

@ -22,7 +22,7 @@ namespace eval ::punkboot {
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k]
variable help_flags [list -help --help /? -h]
variable known_commands [list project modules libs packages vfs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
variable known_commands [list project modules libs packages vfs vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
}
@ -1348,6 +1348,10 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " $scriptname vendorupdate" \n
append h " - update the src/vendormodules based on src/vendormodules/include_modules.config" \n \n
append h " - update the src/vendorlib based on src/vendorlib/config.toml (todo)" \n \n
append h " $scriptname vfslibs" \n
append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n
append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n
append h " - also runs automatically as part of '$scriptname project'" \n \n
append h " $scriptname vfscommonupdate" \n
append h " - update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n
append h " - before calling this (followed by make project) - you can test using '<builtexe>(.exe) dev'" \n
@ -2353,7 +2357,7 @@ if {$::punkboot::command eq "bootsupport"} {
if {$::punkboot::command ni {project modules libs packages vfs bin}} {
if {$::punkboot::command ni {project modules libs packages vfs vfslibs bin}} {
puts stderr "Command $::punkboot::command not implemented - aborting."
flush stderr
after 100
@ -2475,6 +2479,97 @@ if {$::punkboot::command in {project packages libs}} {
}
}
if {$::punkboot::command in {project vfslibs}} {
#G-037: propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees.
#Participation is per-package per-kit (kit vfs package sets intentionally differ - e.g. twapi
#versions vary per kit) - declared in src/runtime/vendorlib_vfs.toml, never blanket-copied.
#punkcheck records live at src/vfs/.punkcheck so they are not baked into kit payloads.
set vfslibs_config $sourcefolder/runtime/vendorlib_vfs.toml
if {![file exists $vfslibs_config]} {
puts stdout "VFSLIBS: no config at $vfslibs_config - skipping vendorlib -> kit vfs propagation"
} else {
if {[catch {package require tomlish} tomlish_err]} {
puts stderr "$A(BAD)VFSLIBS: cannot process $vfslibs_config - tomlish package unavailable ($tomlish_err)$A(RST)"
exit 3
}
set fd [open $vfslibs_config r]
fconfigure $fd -encoding utf-8
set vfslibs_tomldata [read $fd]
close $fd
if {[catch {tomlish::to_dict [tomlish::from_toml $vfslibs_tomldata]} vfslibs_dict]} {
puts stderr "$A(BAD)VFSLIBS: failed to parse $vfslibs_config\n$vfslibs_dict$A(RST)"
exit 3
}
#tomlish::to_dict values are type-tagged (e.g {type STRING value x}, {type ARRAY value {...}})
proc ::punkboot::lib::toml_untag {tagged} {
if {[dict get $tagged type] eq "ARRAY"} {
set plain [list]
foreach el [dict get $tagged value] {
lappend plain [::punkboot::lib::toml_untag $el]
}
return $plain
}
return [dict get $tagged value]
}
set vfslibs_installs [dict create]
if {[dict exists $vfslibs_dict install]} {
set vfslibs_installs [dict get $vfslibs_dict install]
}
if {![dict size $vfslibs_installs]} {
puts stdout "VFSLIBS: no \[install.<name>\] entries found in $vfslibs_config"
}
dict for {iname ientry} $vfslibs_installs {
foreach reqkey {source targets} {
if {![dict exists $ientry $reqkey]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname in $vfslibs_config is missing required key '$reqkey'$A(RST)"
exit 3
}
}
set isource [punkboot::lib::toml_untag [dict get $ientry source]]
set itargets [punkboot::lib::toml_untag [dict get $ientry targets]]
set isupersedes [list]
if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
}
set source_pkg_folder $sourcefolder/$isource
if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)"
exit 3
}
set pkgtail [file tail $source_pkg_folder]
foreach t $itargets {
set target_vfs_dir [lindex [file split $t] 0]
if {![file isdirectory $sourcefolder/vfs/$target_vfs_dir]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname target '$t' - no vfs folder at $sourcefolder/vfs/$target_vfs_dir$A(RST)"
exit 3
}
set target_lib_folder $sourcefolder/vfs/$t
file mkdir $target_lib_folder
#supersession first - no silent mixed-version provision (G-035 concerns)
foreach sup $isupersedes {
if {$sup eq $pkgtail} {
continue ;#never remove what we are about to install
}
set suppath $target_lib_folder/$sup
if {[file exists $suppath]} {
puts stdout "VFSLIBS install.$iname: removing superseded '$sup' from vfs/$t"
file delete -force $suppath
}
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\
-createdir 1\
-overwrite installedsourcechanged-targets\
-punkcheck-folder $sourcefolder/vfs\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
}
if {$::punkboot::command in {project packages libs}} {
########################################################
lappend projectlibfolders lib

19
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk-0.2.0.tm → src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk-0.2.1.tm

@ -8570,7 +8570,7 @@ namespace eval punk {
punk::args::define {
@id -id ::punk::helptopic::tcl
@cmd -name "help tcl" -summary "Tcl version warnings." -help "Show the running Tcl patchlevel and build-info, with warnings for known Tcl bugs affecting this interpreter (as detected by the punk::lib::check::has_tclbug_* checks)."
@cmd -name "help tcl" -summary "Tcl version warnings." -help "Show the running Tcl patchlevel and build-info, with warnings for known Tcl bugs affecting this interpreter and known bugs in bundled library packages (as detected by the punk::lib::check::has_tclbug_* and has_libbug_* checks)."
@values -min 0 -max 0
}
proc tcl {context args} {
@ -8580,8 +8580,12 @@ namespace eval punk {
catch {
append text \n "Tcl build-info: [::tcl::build-info]"
}
#generate warningblocks for each triggered Tcl bug in namespace ::punk::lib::check
set bugcheck_procs [info procs ::punk::lib::check::has_tclbug*]
#generate warningblocks for each triggered bug check in namespace ::punk::lib::check
#has_tclbug_* = Tcl core bugs; has_libbug_* = bundled/vendored library bugs (e.g. tcludp - G-036)
set bugcheck_procs [concat\
[info procs ::punk::lib::check::has_tclbug*]\
[info procs ::punk::lib::check::has_libbug*]\
]
foreach bp $bugcheck_procs {
set buginfo [$bp]
if {[dict get $buginfo bug]} {
@ -8595,12 +8599,15 @@ namespace eval punk {
major {set highlight [punk::ansi::a+ red bold]}
default {set highlight ""}
}
set indent " "
append warningblock \n $highlight "warning level: $level $bp triggered."
if {[dict exists $buginfo description]} {
set indent " "
append warningblock \n "[punk::lib::indent [dict get $buginfo description] $indent]"
}
if {[dict exists $buginfo bugref] && [dict get $buginfo bugref] ne ""} {
if {[dict exists $buginfo url] && [dict get $buginfo url] ne ""} {
#full reference url (e.g. non tcl-core trackers such as tcludp)
append warningblock \n "${indent}see [punk::ansi::hyperlink [dict get $buginfo url]]"
} elseif {[dict exists $buginfo bugref] && [dict get $buginfo bugref] ne ""} {
set bugref [dict get $buginfo bugref]
append warningblock \n "${indent}see [punk::ansi::hyperlink https://core.tcl-lang.org/tcl/tktview/$bugref]"
}
@ -9350,7 +9357,7 @@ punkcheck::cli set_alias punkcheck
package provide punk [namespace eval punk {
#FUNCTL
variable version
set version 0.2.0
set version 0.2.1
}]

56
src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/lib-0.2.1.tm → src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/lib-0.3.0.tm

@ -8,7 +8,7 @@
# (C) 2024
#
# @@ Meta Begin
# Application punk::lib 0.2.1
# Application punk::lib 0.3.0
# Meta platform tcl
# Meta license BSD
# @@ Meta End
@ -18,7 +18,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::lib 0 0.2.1]
#[manpage_begin punkshell_module_punk::lib 0 0.3.0]
#[copyright "2024"]
#[titledesc {punk general utility functions}] [comment {-- Name section and table of contents description --}]
#[moddesc {punk library}] [comment {-- Description at end of page heading --}]
@ -250,6 +250,56 @@ tcl::namespace::eval punk::lib::check {
return [dict create bug $bug bugref e38dc74e2 description $description level medium]
}
#has_libbug_* procs report bugs in bundled/vendored library packages rather than the Tcl core.
#They are surfaced through the same 'help tcl' warning report as the has_tclbug_* checks.
#pure classifier, separated for testability - facts in, verdict out
proc libbug_udp_threadexit_applies {udpversion platform tclversion} {
if {$udpversion eq ""} {
return 0 ;#no udp package known - nothing vulnerable to load
}
if {$platform ne "windows"} {
return 0
}
if {![package vsatisfies $tclversion 9]} {
#tcl 8.6 exhibited apparent immunity in the G-036 investigation (unexplained - see goal detail)
#detection deliberately scoped to Tcl 9 per G-036 acceptance
return 0
}
return [expr {[package vcompare $udpversion 1.0.13] < 0}]
}
proc has_libbug_udp_threadexit {} {
#tcludp <= 1.0.12 on Windows: the per-thread exit handler UDP_ExitProc closes the
#PROCESS-GLOBAL sockListLock/waitForSock event handles. The first udp-loaded thread to
#exit corrupts the lock for every other udp-loaded thread - their event loops block
#forever in WaitForSingleObject on a dead or recycled handle (timers and thread::send
#unserviced). In punkshell the visible casualty is syslog log workers silently freezing
#(the original exit/quit hang this caused is separately mitigated). Root-caused
#2026-07-08 - see goal G-036 detail. Fixed upstream in tcludp 1.0.13 (per-thread
#UdpThreadExitProc deletes only the event source; ExitSockets closes the events at
#process exit).
#Version-based detection only (per G-036 acceptance) - no behavioural probe.
set udpversion [package provide udp]
if {$udpversion eq ""} {
#not loaded - determine what version would be provided, without loading the binary:
#an unsatisfiable require triggers the package unknown scan (registering ifneeded
#scripts) then fails before any dll load.
catch {package require udp 999999}
set available [package versions udp]
if {[llength $available]} {
set udpversion [lindex [lsort -command {package vcompare} $available] end]
}
}
set bug [libbug_udp_threadexit_applies $udpversion $::tcl_platform(platform) [package provide Tcl]]
if {$udpversion eq ""} {
set verinfo "no udp package found"
} else {
set verinfo "udp $udpversion"
}
set description "tcludp < 1.0.13 on Tcl 9 Windows ($verinfo): the udp package's per-thread exit\nhandler closes process-global event handles - after any udp-using thread exits, every\nother udp-using thread's event loop can freeze silently (e.g. syslog log workers stop).\nUpgrade the bundled/available udp package to 1.0.13+ (see goal G-036)."
return [dict create bug $bug bugref "" url https://core.tcl-lang.org/tcludp description $description level medium libversion $udpversion]
}
proc has_tclbug_safeinterp_compile {{show 0}} {
#ensemble calls within safe interp not compiled
#https://core.tcl-lang.org/tcl/tktview/1095bf7f756f9aed6bde
@ -9069,7 +9119,7 @@ namespace eval ::punk::args::register {
package provide punk::lib [tcl::namespace::eval punk::lib {
variable pkg punk::lib
variable version
set version 0.2.1
set version 0.3.0
}]
return

99
src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl

@ -22,7 +22,7 @@ namespace eval ::punkboot {
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k]
variable help_flags [list -help --help /? -h]
variable known_commands [list project modules libs packages vfs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
variable known_commands [list project modules libs packages vfs vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
}
@ -1348,6 +1348,10 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " $scriptname vendorupdate" \n
append h " - update the src/vendormodules based on src/vendormodules/include_modules.config" \n \n
append h " - update the src/vendorlib based on src/vendorlib/config.toml (todo)" \n \n
append h " $scriptname vfslibs" \n
append h " - propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees" \n
append h " - declarations (per-package per-kit, with superseded-version removal) live in src/runtime/vendorlib_vfs.toml" \n
append h " - also runs automatically as part of '$scriptname project'" \n \n
append h " $scriptname vfscommonupdate" \n
append h " - update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc" \n
append h " - before calling this (followed by make project) - you can test using '<builtexe>(.exe) dev'" \n
@ -2353,7 +2357,7 @@ if {$::punkboot::command eq "bootsupport"} {
if {$::punkboot::command ni {project modules libs packages vfs bin}} {
if {$::punkboot::command ni {project modules libs packages vfs vfslibs bin}} {
puts stderr "Command $::punkboot::command not implemented - aborting."
flush stderr
after 100
@ -2475,6 +2479,97 @@ if {$::punkboot::command in {project packages libs}} {
}
}
if {$::punkboot::command in {project vfslibs}} {
#G-037: propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees.
#Participation is per-package per-kit (kit vfs package sets intentionally differ - e.g. twapi
#versions vary per kit) - declared in src/runtime/vendorlib_vfs.toml, never blanket-copied.
#punkcheck records live at src/vfs/.punkcheck so they are not baked into kit payloads.
set vfslibs_config $sourcefolder/runtime/vendorlib_vfs.toml
if {![file exists $vfslibs_config]} {
puts stdout "VFSLIBS: no config at $vfslibs_config - skipping vendorlib -> kit vfs propagation"
} else {
if {[catch {package require tomlish} tomlish_err]} {
puts stderr "$A(BAD)VFSLIBS: cannot process $vfslibs_config - tomlish package unavailable ($tomlish_err)$A(RST)"
exit 3
}
set fd [open $vfslibs_config r]
fconfigure $fd -encoding utf-8
set vfslibs_tomldata [read $fd]
close $fd
if {[catch {tomlish::to_dict [tomlish::from_toml $vfslibs_tomldata]} vfslibs_dict]} {
puts stderr "$A(BAD)VFSLIBS: failed to parse $vfslibs_config\n$vfslibs_dict$A(RST)"
exit 3
}
#tomlish::to_dict values are type-tagged (e.g {type STRING value x}, {type ARRAY value {...}})
proc ::punkboot::lib::toml_untag {tagged} {
if {[dict get $tagged type] eq "ARRAY"} {
set plain [list]
foreach el [dict get $tagged value] {
lappend plain [::punkboot::lib::toml_untag $el]
}
return $plain
}
return [dict get $tagged value]
}
set vfslibs_installs [dict create]
if {[dict exists $vfslibs_dict install]} {
set vfslibs_installs [dict get $vfslibs_dict install]
}
if {![dict size $vfslibs_installs]} {
puts stdout "VFSLIBS: no \[install.<name>\] entries found in $vfslibs_config"
}
dict for {iname ientry} $vfslibs_installs {
foreach reqkey {source targets} {
if {![dict exists $ientry $reqkey]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname in $vfslibs_config is missing required key '$reqkey'$A(RST)"
exit 3
}
}
set isource [punkboot::lib::toml_untag [dict get $ientry source]]
set itargets [punkboot::lib::toml_untag [dict get $ientry targets]]
set isupersedes [list]
if {[dict exists $ientry supersedes]} {
set isupersedes [punkboot::lib::toml_untag [dict get $ientry supersedes]]
}
set source_pkg_folder $sourcefolder/$isource
if {![file isdirectory $source_pkg_folder]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname source '$isource' not found at $source_pkg_folder$A(RST)"
exit 3
}
set pkgtail [file tail $source_pkg_folder]
foreach t $itargets {
set target_vfs_dir [lindex [file split $t] 0]
if {![file isdirectory $sourcefolder/vfs/$target_vfs_dir]} {
puts stderr "$A(BAD)VFSLIBS: entry install.$iname target '$t' - no vfs folder at $sourcefolder/vfs/$target_vfs_dir$A(RST)"
exit 3
}
set target_lib_folder $sourcefolder/vfs/$t
file mkdir $target_lib_folder
#supersession first - no silent mixed-version provision (G-035 concerns)
foreach sup $isupersedes {
if {$sup eq $pkgtail} {
continue ;#never remove what we are about to install
}
set suppath $target_lib_folder/$sup
if {[file exists $suppath]} {
puts stdout "VFSLIBS install.$iname: removing superseded '$sup' from vfs/$t"
file delete -force $suppath
}
}
puts stdout "VFSLIBS install.$iname: $isource -> vfs/$t/$pkgtail (if source file changed)"
set resultdict [punkcheck::install $source_pkg_folder $target_lib_folder/$pkgtail\
-installer make.tcl\
-createdir 1\
-overwrite installedsourcechanged-targets\
-punkcheck-folder $sourcefolder/vfs\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
}
}
}
}
if {$::punkboot::command in {project packages libs}} {
########################################################
lappend projectlibfolders lib

32
src/runtime/vendorlib_vfs.toml

@ -0,0 +1,32 @@
# vendorlib -> kit vfs platform-library propagation (goal G-037)
#
# Declares which vendored platform-library package folders (paths relative to src/) are
# installed into which kit vfs lib trees (paths relative to src/vfs/). Processed by
# 'tclsh src/make.tcl vfslibs' - and automatically as part of 'tclsh src/make.tcl project' -
# with punkcheck-tracked provenance (records in src/vfs/.punkcheck, outside kit payloads).
#
# Participation is per-package per-kit by design: kit vfs package sets intentionally differ
# (e.g. twapi versions vary per kit), so nothing is blanket-copied and a vendorlib drop does
# not force a package into every kit.
#
# Cross-generation installs are allowed when the package supports both Tcl major versions
# (e.g. tcludp 1.0.13 ships tcl9udp1013.dll + udp1013t.dll with a pkgIndex.tcl that selects
# by generation - so the same source folder serves lib_tcl9 and lib_tcl8 targets).
#
# Keys per [install.<name>] entry:
# source - package folder path relative to src/
# targets - list of kit vfs lib folders relative to src/vfs/
# supersedes - optional list of legacy/older package folder names to remove from each
# target before install (explicit - no silent mixed-version provision, per
# the G-035 concerns)
#
# This file is a candidate for absorption into the mapvfs toml conversion (goal G-024).
[install.tcludp]
source = "vendorlib_tcl9/win32-x86_64/tcludp1.0.13"
targets = [
"punk9win.vfs/lib_tcl9",
"punk9win_for_tkruntime.vfs/lib_tcl9",
"punk8win.vfs/lib_tcl8",
]
supersedes = ["udp1.0.12", "tcludp1.0.12"]

39
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/broadcast.tcl

@ -0,0 +1,39 @@
#!/usr/bin/env tclsh
# multicast.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
#
# Demonstrate the use of broadcast UDP sockets.
#
# You can send to ths using netcat:
# echo HELLO | nc -u 192.168.255.255 7772
#
# $Id: broadcast.tcl,v 1.1 2004/11/22 23:48:47 patthoyts Exp $
package require udp 1.0.6
proc udpEvent {chan} {
set data [read $chan]
set peer [fconfigure $chan -peer]
puts "$peer [string length $data] '$data'"
if {[string match "QUIT*" $data]} {
close $chan
set ::forever 1
}
return
}
# Select a subnet and the port number.
set subnet 192.168.255.255
set port 7772
# Create a listening socket and configure for sending too.
set s [udp_open $port]
fconfigure $s -buffering none -blocking 0
fconfigure $s -broadcast 1 -remote [list $subnet $port]
fileevent $s readable [list udpEvent $s]
# Announce our presence and run
puts -nonewline $s "hello, world"
set forever 0
vwait ::forever
exit

56
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/bug1158628.tcl

@ -0,0 +1,56 @@
#!/usr/bin/env tclsh
# bug1158628.tcl - Copyright (C) 2005 Pat Thoyts <patthoyts@users.sf.net>
#
# "On windows XP, I have a GUI that has an exit buttons which when
# pressed does: {set done 1; destroy .;exit} If there is an open UDP
# channel with a fileevent on it, the program will not exit --
# i.e. task manager still shows it. Also if I have the console up, the
# console goes away when the exit button is invoked, but the program
# does not exit. NOTE -- all windows are correctly destroyed (or at
# least withdrawn)"
#
# The fault is calling Tcl_UnregisterChannel in the udpClose function.
# We must let tcl handle this itself. Solved by Reinhard Max.
#
# This script demonstrates the problem. Using udp 1.0.6 the program hangs
# after printing "Exiting...". With the fix applied it properly exits.
#
# $Id: bug1158628.tcl,v 1.2 2005/05/19 20:46:23 patthoyts Exp $
#load [file join [file dirname [info script]] .. win Release udp107.dll]
#load [file join [file dirname [info script]] .. i386-unknown-openbsd3.6 libudp107.so]
package require udp
variable forever 0
proc Event {sock} {
variable forever
set pkt [read $sock]
set peer [fconfigure $sock -peer]
puts "Recieved [string length $pkt] from $peer\n$pkt"
#set forever 1
return
}
proc Listen {port} {
set s [udp_open $port]
fconfigure $s -blocking 0 -buffering none -translation binary
fileevent $s readable [list Event $s]
return $s
}
proc Exit {sock} {
puts "Exiting"
exit 0
}
if {!$tcl_interactive} {
puts "Bug #1158628 - hangs in exit if open udp channels"
puts " Using a buggy version, this will hang after printing Exiting..."
puts ""
set sock [Listen 10245]
puts "Wait 1 sec..."
after 1000 [list Exit $sock]
vwait forever
close $sock
}

81
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/chat.tcl

@ -0,0 +1,81 @@
#!/usr/bin/env tclsh
# chat.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>
#
# This is a sample application from TclUDP.
#
# This illustrates the use of multicast UDP messages to implement a
# primitive chat application.
#
# $Id: chat.tcl,v 1.2 2007/04/10 23:36:14 patthoyts Exp $
package require Tk 8.4-
package require udp 1.0.6
variable Address 224.5.1.21
variable Port 7771
proc Receive {sock} {
set pkt [read $sock]
set peer [fconfigure $sock -peer]
AddMessage $peer $pkt
return
}
proc Start {addr port} {
set s [udp_open $port]
fconfigure $s -blocking 0 -buffering none -translation binary \
-mcastadd $addr -remote [list $addr $port]
fileevent $s readable [list ::Receive $s]
return $s
}
proc CreateGui {socket} {
text .t -yscrollcommand {.s set}
scrollbar .s -command {.t yview}
frame .f -border 0
entry .f.e -textvariable ::_msg
button .f.ok -text Send -underline 0 \
-command "SendMessage $socket \$::_msg"
button .f.ex -text Exit -underline 1 -command {destroy .}
pack .f.ex .f.ok -side right
pack .f.e -side left -expand 1 -fill x
grid .t .s -sticky news
grid .f - -sticky ew
grid columnconfigure . 0 -weight 1
grid rowconfigure . 0 -weight 1
bind .f.e <Return> {.f.ok invoke}
.t tag configure CLNT -foreground red
.t configure -tabs {90}
}
proc SendMessage {sock msg} {
puts -nonewline $sock $msg
}
proc AddMessage {client msg} {
set msg [string map [list "\r\n" "" "\r" "" "\n" ""] $msg]
set client [lindex $client 0]
if {[string length $msg] > 0} {
.t insert end "$client\t" CLNT "$msg\n" MSG
.t see end
}
}
proc Main {} {
variable Address
variable Port
variable sock
set sock [Start $Address $Port]
CreateGui $sock
after idle [list SendMessage $sock \
"$::tcl_platform(user)@[info hostname] connected"]
tkwait window .
close $sock
}
if {!$tcl_interactive} {
set r [catch [linsert $argv 0 Main] err]
if {$r} {puts $::errorInfo} else {puts $err}
exit 0
}

45
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/multicast.tcl

@ -0,0 +1,45 @@
#!/usr/bin/env tclsh
# multicast.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
#
# Demonstrate the use of IPv4 multicast UDP sockets.
#
# You can send to ths using netcat:
# echo HELLO | nc -u 224.5.1.21 7771
#
# $Id: multicast.tcl,v 1.3 2007/04/10 23:49:38 patthoyts Exp $
package require udp 1.0.6
proc udpEvent {chan} {
set data [read $chan]
set peer [fconfigure $chan -peer]
set group [lindex [fconfigure $chan -remote] 0]
puts "$peer ($group) [string length $data] '$data' {[fconfigure $chan]}"
if {[string match "QUIT*" $data]} {
close $chan
set ::forever 1
}
return
}
# Select a multicast group and the port number.
#
# We have two groups here to show that it's possible.
#
set group1 224.5.1.21
set group2 224.5.2.21
set port 7771
# Create a listening socket and configure for sending too.
set s [udp_open $port]
fconfigure $s -buffering none -blocking 0
fconfigure $s -mcastadd $group2 -remote [list $group2 $port]
fconfigure $s -mcastadd $group1 -remote [list $group1 $port]
fileevent $s readable [list udpEvent $s]
# Announce our presence and run
puts -nonewline $s "hello, world"
set forever 0
vwait ::forever
exit

65
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/demos/udpcat.tcl

@ -0,0 +1,65 @@
#!/usr/bin/env tclsh
# udpsend.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
#
# Demo application - cat data from stdin via a UDP socket.
#
# $Id: udpcat.tcl,v 1.1 2004/11/22 23:48:47 patthoyts Exp $
package require udp 1.0.6
proc Event {sock} {
global forever
set pkt [read $sock]
set peer [fconfigure $sock -peer]
puts "Received [string length $pkt] from $peer\n$pkt"
set forever 1
return
}
proc Send {host port {msg {}}} {
set s [udp_open]
fconfigure $s -blocking 0 -buffering none -translation binary \
-remote [list $host $port]
fileevent $s readable [list Event $s]
if {$msg eq {}} {
fcopy stdin $s
} else {
puts -nonewline $s $msg
}
after 2000
close $s
}
proc Listen {port} {
set s [udp_open $port]
fconfigure $s -blocking 0 -buffering none -translation binary
fileevent $s readable [list Event $s]
return $s
}
# -------------------------------------------------------------------------
# Runtime
# udpsend listen -port N -blocking 0
# udpsend send host port message
# -------------------------------------------------------------------------
set forever 0
if {! $tcl_interactive} {
switch -exact -- [set cmd [lindex $argv 0]] {
send {
eval [list Send] [lrange $argv 1 end]
}
listen {
set s [Listen [lindex $argv 1]]
vwait ::forever
close $s
}
default {
puts "usage: udpcat send host port ?message?\
\n udpcat listen port"
}
}
}

33
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/pkgIndex.tcl

@ -0,0 +1,33 @@
# -*- tcl -*-
# Tcl package index file, version 1.1
#
if {[package vsatisfies [package provide Tcl] 9.0-]} {
package ifneeded udp 1.0.13 [list apply {{dir} {
# Load library
load [file join $dir tcl9udp1013.dll] [string totitle udp]
# Source init file
set initScript [file join $dir udp.tcl]
if {[file exists $initScript]} {
source -encoding utf-8 $initScript
}
}} $dir]
} else {
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded udp 1.0.13 [list apply {{dir} {
# Load library
if {[string tolower [file extension udp1013t.dll]] in [list .dll .dylib .so]} {
# Load dynamic library
load [file join $dir udp1013t.dll] [string totitle udp]
} else {
# Static library
load {} [string totitle udp]
}
# Source init file
set initScript [file join $dir udp.tcl]
if {[file exists $initScript]} {
source -encoding utf-8 $initScript
}
}} $dir]
}

BIN
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/tcl9udp1013.dll

Binary file not shown.

326
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/udp.html

@ -0,0 +1,326 @@
<!DOCTYPE html><html><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>udp - Tcl UDP extension</title>
<style type="text/css"><!--
HTML {
background: #FFFFFF;
color: black;
}
BODY {
background: #FFFFFF;
color: black;
}
DIV.doctools {
margin-left: 10%;
margin-right: 10%;
}
DIV.doctools H1,DIV.doctools H2 {
margin-left: -5%;
}
H1, H2, H3, H4 {
margin-top: 1em;
font-family: sans-serif;
font-size: large;
color: #005A9C;
background: transparent;
text-align: left;
}
H1.doctools_title {
text-align: center;
}
UL,OL {
margin-right: 0em;
margin-top: 3pt;
margin-bottom: 3pt;
}
UL LI {
list-style: disc;
}
OL LI {
list-style: decimal;
}
DT {
padding-top: 1ex;
}
UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
font: normal 12pt/14pt sans-serif;
list-style: none;
}
LI.doctools_section, LI.doctools_subsection {
list-style: none;
margin-left: 0em;
text-indent: 0em;
padding: 0em;
}
PRE {
display: block;
font-family: monospace;
white-space: pre;
margin: 0%;
padding-top: 0.5ex;
padding-bottom: 0.5ex;
padding-left: 1ex;
padding-right: 1ex;
width: 100%;
}
PRE.doctools_example {
color: black;
background: #f5dcb3;
border: 1px solid black;
}
UL.doctools_requirements LI, UL.doctools_syntax LI {
list-style: none;
margin-left: 0em;
text-indent: 0em;
padding: 0em;
}
DIV.doctools_synopsis {
color: black;
background: #80ffff;
border: 1px solid black;
font-family: serif;
margin-top: 1em;
margin-bottom: 1em;
}
UL.doctools_syntax {
margin-top: 1em;
border-top: 1px solid black;
}
UL.doctools_requirements {
margin-bottom: 1em;
border-bottom: 1px solid black;
}
--></style>
</head>
<!-- Generated from file 'udp.man' by tcllib/doctools with format 'html'
-->
<!-- Copyright &amp;copy; 1999-2000 Columbia University; all rights reserved
-->
<!-- udp.n
-->
<body><div class="doctools">
<h1 class="doctools_title">udp(n) 1.0.13 udp &quot;Tcl UDP extension&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>udp - Create UDP sockets in Tcl</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">COMMANDS</a></li>
<li class="doctools_section"><a href="#section3">EXAMPLES</a></li>
<li class="doctools_section"><a href="#section4">HISTORY</a></li>
<li class="doctools_section"><a href="#see-also">See Also</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.5-</b></li>
<li>package require <b class="pkgname">udp 1.0.13</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">udp_open</b> <span class="opt">?<i class="arg">localport</i>?</span> <span class="opt">?<b class="option">reuse</b>?</span> <span class="opt">?<b class="option">ipv6</b>?</span></a></li>
<li><a href="#2"><b class="cmd">udp_conf</b> <i class="arg">channel</i> <i class="arg">host</i> <i class="arg">port</i></a></li>
<li><a href="#3"><b class="cmd">udp_conf</b> <i class="arg">channel</i> <i class="arg"><span class="opt">?optionName?</span></i> <i class="arg"><span class="opt">?value?</span></i> <i class="arg"><span class="opt">?optionName value ...?</span></i></a></li>
<li><a href="#4"><b class="cmd">udp_peek</b> <i class="arg">channel</i> <span class="opt">?<i class="arg">buffersize</i>?</span></a></li>
<li><a href="#5"><b class="cmd">::udp::build-info</b></a></li>
<li><a href="#6"><b class="cmd">::udp::getaddrinfo</b> <i class="arg"><b class="option">-hostname</b> name</i> <i class="arg"><span class="opt">?optionName value ...?</span></i></a></li>
<li><a href="#7"><b class="cmd">::udp::getnameinfo</b> <i class="arg">address</i> <b class="option"><span class="opt">?ipv6?</span></b></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package provides support for using UDP through Tcl. The package provides
a new channel type and attempts to permit the use of packet oriented UDP
over stream oriented Tcl channels. The package defined three commands but
<b class="cmd">udp_conf</b> should be considered depreciated in favor of the standard
Tcl command <b class="cmd">fconfigure</b>.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">udp_open</b> <span class="opt">?<i class="arg">localport</i>?</span> <span class="opt">?<b class="option">reuse</b>?</span> <span class="opt">?<b class="option">ipv6</b>?</span></a></dt>
<dd><p><b class="cmd">udp_open</b> will open a UDP socket. If a <i class="arg">localport</i> is specified the UDP
socket will be opened on that port. Otherwise the system will choose a port
and the user can use the <b class="cmd">udp_conf</b> command to obtain the port number
if required.</p>
<p>The following keywords can be used to specify options on the opened socket.</p>
<dl class="doctools_definitions">
<dt><b class="option">reuse</b></dt>
<dd><p>Using this keyword sets the SO_REUSEADDR socket option which permits multiple
sockets to be bound to the same address/port combination.</p></dd>
<dt><b class="option">ipv6</b></dt>
<dd><p>By default a IPv4 socket is created. When keyword <b class="option">ipv6</b> is specified, an IPv6
socket is opened.</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">udp_conf</b> <i class="arg">channel</i> <i class="arg">host</i> <i class="arg">port</i></a></dt>
<dd><p><em>Deprecated</em> in favor of the standard Tcl <b class="cmd">fconfigure</b> or
<b class="cmd">chan configure</b> commands.</p>
<p><b class="cmd">udp_conf</b> in this configuration is used to specify the remote destination
for packets written to this <i class="arg">channel</i>. You must call this command before
writing data to the UDP socket.</p></dd>
<dt><a name="3"><b class="cmd">udp_conf</b> <i class="arg">channel</i> <i class="arg"><span class="opt">?optionName?</span></i> <i class="arg"><span class="opt">?value?</span></i> <i class="arg"><span class="opt">?optionName value ...?</span></i></a></dt>
<dd><p><em>Deprecated</em> in favor of the standard Tcl <b class="cmd">fconfigure</b> or
<b class="cmd">chan configure</b> commands.</p>
<p>In addition to being used to configure the remote host, the <b class="cmd">udp_conf</b>
command is used to obtain information about the UDP socket. NOTE all these
options are now available using the standard Tcl <b class="cmd">fconfigure</b> or
<b class="cmd">chan configure</b> command.</p>
<dl class="doctools_definitions">
<dt><b class="option">-myport</b></dt>
<dd><p>Returns the local port number of the socket. Read-only option.</p></dd>
<dt><b class="option">-remote</b> <i class="arg"><span class="opt">?address port?</span></i></dt>
<dd><p>Specifies or returns the remote hostname and port number. Can also be set using
<b class="cmd">udp_conf</b> <i class="arg">channel</i> <i class="arg">host</i> <i class="arg">port</i>.</p></dd>
<dt><b class="option">-peer</b></dt>
<dd><p>Returns the remote hostname and port number for the packet most recently
received by this socket. Read-only option.</p></dd>
<dt><b class="option">-family</b></dt>
<dd><p>Returns whether socket is configured for IPv4 or IPv6. Read-only option.</p></dd>
<dt><b class="option">-broadcast</b> <i class="arg"><span class="opt">?boolean?</span></i></dt>
<dd><p>Specifies or returns whether can listen and send on the broadcast address. For some systems
a flag must be set on the socket to use broadcast. This option is not permitted when
using IPv6, instead use multicast.</p></dd>
<dt><b class="option">-ttl</b> <i class="arg"><span class="opt">?count?</span></i></dt>
<dd><p>The time-to-live is given as the number of router hops the packet may do. For
multicast packets this is important in specifying the distribution of the
packet. The system default for multicast is 1 which restricts the packet
to the local subnet. To permit packets to pass routers, you must increase the
ttl. A value of 31 should keep it within a site, while 255 is global.</p></dd>
<dt><b class="option">-mcastadd</b> groupaddr</dt>
<dd></dd>
<dt><b class="option">-mcastadd</b> &quot;groupaddr netwif&quot;</dt>
<dd></dd>
<dt><b class="option">-mcastdrop</b> groupaddr</dt>
<dd></dd>
<dt><b class="option">-mcastdrop</b> &quot;groupaddr netwif&quot;</dt>
<dd></dd>
<dt><b class="option">-mcastgroups</b></dt>
<dd><p><b class="package">tcludp</b> sockets can support IPv4 and IPv6 multicast operations. To receive
multicast packets the application has to notify the operating system that
it should join a particular multicast group. For IPv4 these are specified as addresses
in the range 224.0.0.0 to 239.255.255.255.</p>
<p>When specifying only the <i class="arg">groupaddr</i> the system will determine the network interface to use.
Specifying the <i class="arg">netwif</i> will join a multicast group on a specific network interface.
This is useful on a multihomed system with multiple network interfaces.
On windows you must specify the network interface index. For other platforms the network
interface (e.g. 'eth0') name can be specified.</p>
<p>To view the current set of multicast groups for a channel use <i class="arg">-mcastgroups</i></p></dd>
<dt><b class="option">-mcastif</b></dt>
<dd><p>Returns which interface is used for outgoing multicast packets. UNIX only.</p></dd>
<dt><b class="option">-mcastloop</b> <i class="arg"><span class="opt">?boolean?</span></i></dt>
<dd><p>With multicast udp the system can choose to receive packets that it has sent
or it can drop them. This is known as multicast loopback and can be controlled
using this option. By default the value is true and your application will receive
its own transmissions.</p></dd>
</dl></dd>
<dt><a name="4"><b class="cmd">udp_peek</b> <i class="arg">channel</i> <span class="opt">?<i class="arg">buffersize</i>?</span></a></dt>
<dd><p>Examine a packet without removing it from the buffer. Option <i class="arg">buffersize</i> specifies the
maximum buffer size. Value must be between 0 and 16.</p>
<p>This function is not available on windows.</p></dd>
<dt><a name="5"><b class="cmd">::udp::build-info</b></a></dt>
<dd><p>Return information on the build environment.</p></dd>
<dt><a name="6"><b class="cmd">::udp::getaddrinfo</b> <i class="arg"><b class="option">-hostname</b> name</i> <i class="arg"><span class="opt">?optionName value ...?</span></i></a></dt>
<dd><p>Returns a list with info on the IP address matching the specified parameters.
Valid options are:</p>
<dl class="doctools_definitions">
<dt><b class="option">-hostname</b> <i class="arg">name</i></dt>
<dd><p>Specifies which IP address to use for lookup. Can use IP address or name. Required option.</p></dd>
<dt><b class="option">-port</b> <i class="arg">number</i></dt>
<dd><p>Specifies which port number to use for lookup.</p></dd>
<dt><b class="option">-service</b> <i class="arg">name</i></dt>
<dd><p>Specifies which well-known service (i.e. http, https, etc.) to use for port in lookup.</p></dd>
<dt><b class="option">-ipv4</b></dt>
<dd><p>Only return IP v4 addresses.</p></dd>
<dt><b class="option">-ipv6</b></dt>
<dd><p>Only return IP v6 addresses.</p></dd>
<dt><b class="option">-server</b></dt>
<dd><p>Only return services for which hostname is a server.</p></dd>
<dt><b class="option">-tcp</b></dt>
<dd><p>Only return TCP services.</p></dd>
<dt><b class="option">-udp</b></dt>
<dd><p>Only return UDP services.</p></dd>
</dl></dd>
<dt><a name="7"><b class="cmd">::udp::getnameinfo</b> <i class="arg">address</i> <b class="option"><span class="opt">?ipv6?</span></b></a></dt>
<dd><p><b class="cmd">::udp::getnameinfo</b> will return the name(s) corresponding to IP
address <i class="arg">address</i>. With <i class="arg">ipv6</i>, address is an IPv6 address.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">EXAMPLES</a></h2>
<pre class="doctools_example">
# Send data to a remote UDP socket
proc udp_puts {host port} {
set s [udp_open]
fconfigure $s -remote [list $host $port]
puts $s &quot;Hello, World&quot;
close $f
}
</pre>
<pre class="doctools_example">
# A simple UDP server
package require udp
proc udpEventHandler {sock} {
set pkt [read $sock]
set peer [fconfigure $sock -peer]
puts &quot;$peer: [string length $pkt] {$pkt}&quot;
return
}
proc udp_listen {port} {
set srv [udp_open $port]
fconfigure $srv -buffering none -translation binary
fileevent $srv readable [list ::udpEventHandler $srv]
puts &quot;Listening on udp port: [fconfigure $srv -myport]&quot;
return $srv
}
set sock [udp_listen 53530]
vwait forever
close $sock
</pre>
<pre class="doctools_example">
# A multicast demo.
proc udpEvent {chan} {
set data [read $chan]
set peer [fconfigure $chan -peer]
puts &quot;$peer [string length $data] '$data'&quot;
if {[string match &quot;QUIT*&quot; $data]} {
close $chan
set ::forever 1
}
return
}
set group 224.5.1.21
set port 7771
set s [udp_open $port]
fconfigure $s -buffering none -blocking 0
fconfigure $s -mcastadd $group -remote [list $group $port]
fileevent $s readable [list udpEvent $s]
puts -nonewline $s &quot;hello, world&quot;
set ::forever 0
vwait ::forever
exit
</pre>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">HISTORY</a></h2>
<p>Some of the code in this extension is copied from Michael Miller's tcludp
package. (http://www.neosoft.com/tcl/ftparchive/sorted/comm/tcludp-1.0/)
Compared with Michael's UDP extension, this extension provides Windows
support and provides the ability of using 'gets/puts' to read/write
the socket. In addition, it provides more configuration ability.</p>
<p>Enhancements to support binary data and to setup the package for the Tcl
Extension Architecture by Pat Thoyts.</p>
<p>Support for IPv6 and allowing a multicast join on a specific network interface is added by Huub Eikens.</p>
</div>
<div id="see-also" class="doctools_section"><h2><a name="see-also">See Also</a></h2>
<p>socket</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p>I/O, IP Address, TclUDP, UDP, asynchronous I/O, bind, channel, connection, domain, host, name, network, network address, networking, socket, udp</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 1999-2000 Columbia University; all rights reserved</p>
</div>
</div></body></html>

BIN
src/vfs/punk8win.vfs/lib_tcl8/tcludp1.0.13/udp1013t.dll

Binary file not shown.

2
src/vfs/punk8win.vfs/lib_tcl8/udp1.0.12/pkgIndex.tcl

@ -1,2 +0,0 @@
package ifneeded udp 1.0.12 \
[list load [file join $dir udp1012.dll]]

BIN
src/vfs/punk8win.vfs/lib_tcl8/udp1.0.12/udp1012.dll

Binary file not shown.
Loading…
Cancel
Save