Browse Source

bugfixes, template work

master
Julian Noble 11 months ago
parent
commit
ea590b0220
  1. 51
      src/decktemplates/custom/_project/modules/template_anyname-0.0.2.tm
  2. 107
      src/decktemplates/custom/punk/modules/template_cli-0.0.1.tm
  3. 51
      src/decktemplates/vendor/_project/modules/template_anyname-0.0.2.tm
  4. 3
      src/decktemplates/vendor/punk/modules/modulename_buildversion.txt
  5. 10
      src/decktemplates/vendor/punk/modules/modulename_description.txt
  6. 51
      src/decktemplates/vendor/punk/modules/nodoc/template_anyname-0.0.2.tm
  7. 51
      src/decktemplates/vendor/punk/modules/template_anyname-0.0.2.tm
  8. 106
      src/decktemplates/vendor/punk/modules/template_cli-0.0.1.tm
  9. 175
      src/decktemplates/vendor/punk/modules/template_module-0.0.1.tm
  10. 176
      src/decktemplates/vendor/punk/modules/template_module-0.0.2.tm
  11. 0
      src/decktemplates/vendor/punk/modules/template_module_unversioned.tm
  12. 95
      src/decktemplates/vendor/punk/modules/template_moduleexactversion-0.0.1.tm
  13. 0
      src/decktemplates/vendor/punk/utility/scriptappwrappers/basic/shellbat.bat
  14. 560
      src/embedded/man/files/punk/_module_ansi-0.1.0.tm.n
  15. 400
      src/embedded/man/files/punk/_module_args-0.1.0.tm.n
  16. 2
      src/embedded/man/files/punk/_module_fileline-0.1.0.tm.n
  17. 4
      src/embedded/man/files/punk/_module_flib-0.1.0.tm.n
  18. 16
      src/embedded/man/files/punk/_module_path-0.1.0.tm.n
  19. 57
      src/embedded/man/index.n
  20. 12
      src/embedded/man/toc.n
  21. 4
      src/embedded/md/.doc/tocdoc
  22. 2
      src/embedded/md/.idx
  23. 2
      src/embedded/md/.toc
  24. 2
      src/embedded/md/.xrf
  25. 333
      src/embedded/md/doc/files/punk/_module_ansi-0.1.0.tm.md
  26. 177
      src/embedded/md/doc/files/punk/_module_args-0.1.0.tm.md
  27. 2
      src/embedded/md/doc/files/punk/_module_fileline-0.1.0.tm.md
  28. 6
      src/embedded/md/doc/files/punk/_module_flib-0.1.0.tm.md
  29. 18
      src/embedded/md/doc/files/punk/_module_path-0.1.0.tm.md
  30. 8
      src/embedded/md/doc/toc.md
  31. 19
      src/embedded/md/index.md
  32. 8
      src/embedded/md/toc.md
  33. 4
      src/embedded/www/.doc/tocdoc
  34. 2
      src/embedded/www/.idx
  35. 2
      src/embedded/www/.toc
  36. 2
      src/embedded/www/.xrf
  37. 312
      src/embedded/www/doc/files/punk/_module_ansi-0.1.0.tm.html
  38. 238
      src/embedded/www/doc/files/punk/_module_args-0.1.0.tm.html
  39. 1
      src/embedded/www/doc/files/punk/_module_fileline-0.1.0.tm.html
  40. 8
      src/embedded/www/doc/files/punk/_module_flib-0.1.0.tm.html
  41. 11
      src/embedded/www/doc/files/punk/_module_path-0.1.0.tm.html
  42. 18
      src/embedded/www/doc/toc.html
  43. 52
      src/embedded/www/index.html
  44. 18
      src/embedded/www/toc.html
  45. 2
      src/mixtemplates/layouts/basic/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm
  46. 324
      src/modules/punk-0.1.tm
  47. 591
      src/modules/punk/ansi-999999.0a1.0.tm
  48. 596
      src/modules/punk/args-999999.0a1.0.tm
  49. 3
      src/modules/punk/args-buildversion.txt
  50. 39
      src/modules/punk/cap-999999.0a1.0.tm
  51. 308
      src/modules/punk/cap/handlers/templates-999999.0a1.0.tm
  52. 7
      src/modules/punk/char-999999.0a1.0.tm
  53. 2
      src/modules/punk/du-999999.0a1.0.tm
  54. 44
      src/modules/punk/fileline-999999.0a1.0.tm
  55. 541
      src/modules/punk/flib-999999.0a1.0.tm
  56. 3
      src/modules/punk/flib-buildversion.txt
  57. 368
      src/modules/punk/lib-0.1.tm
  58. 74
      src/modules/punk/mix/base-0.1.tm
  59. 28
      src/modules/punk/mix/cli-0.3.tm
  60. 4
      src/modules/punk/mix/commandset/doc-999999.0a1.0.tm
  61. 178
      src/modules/punk/mix/commandset/module-999999.0a1.0.tm
  62. 2
      src/modules/punk/mix/commandset/project-999999.0a1.0.tm
  63. 185
      src/modules/punk/mix/commandset/scriptwrap-999999.0a1.0.tm
  64. 22
      src/modules/punk/mix/templates-999999.0a1.0.tm
  65. 6
      src/modules/punk/mix/templates/layouts/project/src/buildsuites/samplesuite1/download_and_build.config
  66. 52
      src/modules/punk/mix/templates/layouts/project/src/mixtemplates/modules/template_module-0.0.1.tm
  67. 0
      src/modules/punk/mix/templates/utility/scriptappwrappers/basic/multishell-old.cmd
  68. 112
      src/modules/punk/mix/templates/utility/scriptappwrappers/basic/shellbat.bat
  69. 0
      src/modules/punk/mix/templates/utility/scriptappwrappers/multishell.cmd
  70. 524
      src/modules/punk/mix/templates/utility/scriptappwrappers/multishell1.cmd
  71. 4
      src/modules/punk/ns-999999.0a1.0.tm
  72. 46
      src/modules/punk/path-999999.0a1.0.tm
  73. 74
      src/modules/textblock-999999.0a1.0.tm
  74. 48
      src/punk86.vfs/lib/app-punk/repl.tcl
  75. 1
      src/punk86.vfs/lib/app-shellspy/shellspy.tcl
  76. 49
      src/scriptapps/create-localmachine-selfsigned-cert.ps1
  77. 117
      src/vendorlib/pluginmgr/ChangeLog
  78. 2
      src/vendorlib/pluginmgr/pkgIndex.tcl
  79. 427
      src/vendorlib/pluginmgr/pluginmgr.man
  80. 429
      src/vendorlib/pluginmgr/pluginmgr.tcl

51
src/decktemplates/custom/_project/modules/template_anyname-0.0.2.tm

@ -0,0 +1,51 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2023
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
apply {code { #auto determine package name and version from name and placement of .tm file
foreach base [tcl::tm::list] {
set nsprefix "";#in case sourced directly and not in any of the .tm paths
if {[string match -nocase ${base}* [info script]]} {
set nsprefix [string trimleft [join [lrange [file split [string range [info script] [string length $base]+1 end]] 0 end-1] ::]:: ::]
break
}
}
set ver [join [lassign [split [file rootname [file tail [info script] ]] -] pkgtail] -]
set pkgns ${nsprefix}${pkgtail}
namespace eval $pkgns [string map [list <pkg> $pkgns <ver> $ver] $code]
package provide $pkgns $ver;# only provide package if code evaluated without error
} ::} {
#--------------------------------------
variable pkg "<pkg>"
variable version "<ver>"
#--------------------------------------
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
#proc test {args} {puts "[namespace current]::test got args: $args"}
namespace eval [namespace current]::lib {
#proc test {args} {puts "[namespace current]::test got args: $args"}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
}
return

107
src/decktemplates/custom/punk/modules/template_cli-0.0.1.tm

@ -0,0 +1,107 @@
# -*- tcl -*-
# punk template_cli overridden - custom
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin %project%_cli-module_%pkg% 0 999999.0a1.0]
#[copyright "%year%"]
#[titledesc {Command Line Interface Module}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require %pkg%]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of %pkg%
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by %pkg%
#[list_begin itemized]
package require Tcl 8.6
package require punk::overlay
package require punk::mix::base
package require punk::mix::util
#*** !doctools
#[item] [package {Tcl 8.6}]
#[item] [package {punk::overlay}]
#[item] [package {punk::mix::base}]
#[item] [package {punk::mix::util}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
namespace eval %pkg% {
namespace ensemble create
#package require punk::overlay
#punk::overlay::import_commandset debug . ::punk:mix::commandset::debug
proc help {args} {
set basehelp [punk::mix::base help {*}$args]
return $basehelp
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg%::lib {
namespace path ::punk::mix::util ;#askuser, do_in_path, foreach-file etc
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
proc _cli {args} {
#don't use tailcall - base uses info level to determine caller
::punk::mix::base::_cli {*}$args
}
variable default_command help
punk::overlay::custom_from_base [namespace current] ::punk::mix::base
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable version
set version 999999.0a1.0
}]
return

51
src/decktemplates/vendor/_project/modules/template_anyname-0.0.2.tm vendored

@ -0,0 +1,51 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2023
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
apply {code { #auto determine package name and version from name and placement of .tm file
foreach base [tcl::tm::list] {
set nsprefix "";#in case sourced directly and not in any of the .tm paths
if {[string match -nocase ${base}* [info script]]} {
set nsprefix [string trimleft [join [lrange [file split [string range [info script] [string length $base]+1 end]] 0 end-1] ::]:: ::]
break
}
}
set ver [join [lassign [split [file rootname [file tail [info script] ]] -] pkgtail] -]
set pkgns ${nsprefix}${pkgtail}
namespace eval $pkgns [string map [list <pkg> $pkgns <ver> $ver] $code]
package provide $pkgns $ver;# only provide package if code evaluated without error
} ::} {
#--------------------------------------
variable pkg "<pkg>"
variable version "<ver>"
#--------------------------------------
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
#proc test {args} {puts "[namespace current]::test got args: $args"}
namespace eval [namespace current]::lib {
#proc test {args} {puts "[namespace current]::test got args: $args"}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
}
return

3
src/decktemplates/vendor/punk/modules/modulename_buildversion.txt vendored

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

10
src/decktemplates/vendor/punk/modules/modulename_description.txt vendored

@ -0,0 +1,10 @@
Identifier: %package%
Version: %version%
Title: %title%
Creator: %name% <%email%>
Description: %description%
Rights: BSD
URL: %url%
Available:
Architecture: tcl
Subject:

51
src/decktemplates/vendor/punk/modules/nodoc/template_anyname-0.0.2.tm vendored

@ -0,0 +1,51 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2023
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
apply {code { #auto determine package name and version from name and placement of .tm file
foreach base [tcl::tm::list] {
set nsprefix "";#in case sourced directly and not in any of the .tm paths
if {[string match -nocase ${base}* [info script]]} {
set nsprefix [string trimleft [join [lrange [file split [string range [info script] [string length $base]+1 end]] 0 end-1] ::]:: ::]
break
}
}
set ver [join [lassign [split [file rootname [file tail [info script] ]] -] pkgtail] -]
set pkgns ${nsprefix}${pkgtail}
namespace eval $pkgns [string map [list <pkg> $pkgns <ver> $ver] $code]
package provide $pkgns $ver;# only provide package if code evaluated without error
} ::} {
#--------------------------------------
variable pkg "<pkg>"
variable version "<ver>"
#--------------------------------------
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
#proc test {args} {puts "[namespace current]::test got args: $args"}
namespace eval [namespace current]::lib {
#proc test {args} {puts "[namespace current]::test got args: $args"}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
}
return

51
src/decktemplates/vendor/punk/modules/template_anyname-0.0.2.tm vendored

@ -0,0 +1,51 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2023
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
apply {code { #auto determine package name and version from name and placement of .tm file
foreach base [tcl::tm::list] {
set nsprefix "";#in case sourced directly and not in any of the .tm paths
if {[string match -nocase ${base}* [info script]]} {
set nsprefix [string trimleft [join [lrange [file split [string range [info script] [string length $base]+1 end]] 0 end-1] ::]:: ::]
break
}
}
set ver [join [lassign [split [file rootname [file tail [info script] ]] -] pkgtail] -]
set pkgns ${nsprefix}${pkgtail}
namespace eval $pkgns [string map [list <pkg> $pkgns <ver> $ver] $code]
package provide $pkgns $ver;# only provide package if code evaluated without error
} ::} {
#--------------------------------------
variable pkg "<pkg>"
variable version "<ver>"
#--------------------------------------
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
#proc test {args} {puts "[namespace current]::test got args: $args"}
namespace eval [namespace current]::lib {
#proc test {args} {puts "[namespace current]::test got args: $args"}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
}
return

106
src/decktemplates/vendor/punk/modules/template_cli-0.0.1.tm vendored

@ -0,0 +1,106 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin %project%_cli-module_%pkg% 0 999999.0a1.0]
#[copyright "%year%"]
#[titledesc {Command Line Interface Module}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require %pkg%]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of %pkg%
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by %pkg%
#[list_begin itemized]
package require Tcl 8.6
package require punk::overlay
package require punk::mix::base
package require punk::mix::util
#*** !doctools
#[item] [package {Tcl 8.6}]
#[item] [package {punk::overlay}]
#[item] [package {punk::mix::base}]
#[item] [package {punk::mix::util}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
namespace eval %pkg% {
namespace ensemble create
#package require punk::overlay
#punk::overlay::import_commandset debug . ::punk:mix::commandset::debug
proc help {args} {
set basehelp [punk::mix::base help {*}$args]
return $basehelp
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg%::lib {
namespace path ::punk::mix::util ;#askuser, do_in_path, foreach-file etc
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
proc _cli {args} {
#don't use tailcall - base uses info level to determine caller
::punk::mix::base::_cli {*}$args
}
variable default_command help
punk::overlay::custom_from_base [namespace current] ::punk::mix::base
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable version
set version 999999.0a1.0
}]
return

175
src/decktemplates/vendor/punk/modules/template_module-0.0.1.tm vendored

@ -0,0 +1,175 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin %project%_module_%pkg% 0 999999.0a1.0]
#[copyright "%year%"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require %pkg%]
#[keywords module]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of %pkg%
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by %pkg%
#[list_begin itemized]
package require Tcl 8.6
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg%::class {
#*** !doctools
#[subsection {Namespace %pkg%::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
namespace export *
#variable xyz
#*** !doctools
#[subsection {Namespace %pkg%}]
#[para] Core API functions for %pkg%
#[list_begin definitions]
#proc sample1 {p1 args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [opt {?option value...?}]]
# #[para]Description of sample1
# return "ok"
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace %pkg% ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg%::lib {
namespace export *
namespace path [namespace parent]
#*** !doctools
#[subsection {Namespace %pkg%::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]]
# #[para]Description of utility1
# return 1
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace %pkg%::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval %pkg%::system {
#*** !doctools
#[subsection {Namespace %pkg%::system}]
#[para] Internal functions that are not part of the API
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable pkg %pkg%
variable version
set version 999999.0a1.0
}]
return
#*** !doctools
#[manpage_end]

176
src/decktemplates/vendor/punk/modules/template_module-0.0.2.tm vendored

@ -0,0 +1,176 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'pmix make' or bin/punkmake to update from <pkg>-buildversion.txt
# module template: %moduletemplate%
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin %project%_module_%pkg% 0 999999.0a1.0]
#[copyright "%year%"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require %pkg%]
#[keywords module]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of %pkg%
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by %pkg%
#[list_begin itemized]
package require Tcl 8.6
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg%::class {
#*** !doctools
#[subsection {Namespace %pkg%::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
namespace export *
#variable xyz
#*** !doctools
#[subsection {Namespace %pkg%}]
#[para] Core API functions for %pkg%
#[list_begin definitions]
#proc sample1 {p1 args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [opt {option value...}]]
# #[para]Description of sample1
# return "ok"
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace %pkg% ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg%::lib {
namespace export *
namespace path [namespace parent]
#*** !doctools
#[subsection {Namespace %pkg%::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]]
# #[para]Description of utility1
# return 1
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace %pkg%::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval %pkg%::system {
#*** !doctools
#[subsection {Namespace %pkg%::system}]
#[para] Internal functions that are not part of the API
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable pkg %pkg%
variable version
set version 999999.0a1.0
}]
return
#*** !doctools
#[manpage_end]

0
src/mixtemplates/modules/template_unversioned.tm → src/decktemplates/vendor/punk/modules/template_module_unversioned.tm vendored

95
src/mixtemplates/modules/template_module-0.0.1.tm → src/decktemplates/vendor/punk/modules/template_moduleexactversion-0.0.1.tm vendored

@ -1,52 +1,45 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable pkg %pkg%
variable version
set version 999999.0a1.0
}]
# -*- tcl -*-
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% %version%
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable version
set version %version%
}]
return

0
src/modules/punk/mix/templates/utility/scriptappwrappers/punk-shellbat.bat → src/decktemplates/vendor/punk/utility/scriptappwrappers/basic/shellbat.bat vendored

560
src/embedded/man/files/punk/_module_ansi-0.1.0.tm.n

@ -0,0 +1,560 @@
'\"
'\" Generated from file '_module_ansi-0\&.1\&.0\&.tm\&.man' by tcllib/doctools with format 'nroff'
'\" Copyright (c) 2023
'\"
.TH "punkshell_module_punk::ansi" 0 0\&.1\&.0 doc "punk Ansi library"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\" Start paragraph describing an argument to a library procedure.
.\" type is type of argument (int, etc.), in/out is either "in", "out",
.\" or "in/out" to describe whether procedure reads or modifies arg,
.\" and indent is equivalent to second arg of .IP (shouldn't ever be
.\" needed; use .AS below instead)
.\"
.\" .AS ?type? ?name?
.\" Give maximum sizes of arguments for setting tab stops. Type and
.\" name are examples of largest possible arguments that will be passed
.\" to .AP later. If args are omitted, default tab stops are used.
.\"
.\" .BS
.\" Start box enclosure. From here until next .BE, everything will be
.\" enclosed in one large box.
.\"
.\" .BE
.\" End of box enclosure.
.\"
.\" .CS
.\" Begin code excerpt.
.\"
.\" .CE
.\" End code excerpt.
.\"
.\" .VS ?version? ?br?
.\" Begin vertical sidebar, for use in marking newly-changed parts
.\" of man pages. The first argument is ignored and used for recording
.\" the version when the .VS was added, so that the sidebars can be
.\" found and removed when they reach a certain age. If another argument
.\" is present, then a line break is forced before starting the sidebar.
.\"
.\" .VE
.\" End of vertical sidebar.
.\"
.\" .DS
.\" Begin an indented unfilled display.
.\"
.\" .DE
.\" End of indented unfilled display.
.\"
.\" .SO ?manpage?
.\" Start of list of standard options for a Tk widget. The manpage
.\" argument defines where to look up the standard options; if
.\" omitted, defaults to "options". The options follow on successive
.\" lines, in three columns separated by tabs.
.\"
.\" .SE
.\" End of list of standard options for a Tk widget.
.\"
.\" .OP cmdName dbName dbClass
.\" Start of description of a specific option. cmdName gives the
.\" option's name as specified in the class command, dbName gives
.\" the option's name in the option database, and dbClass gives
.\" the option's class in the option database.
.\"
.\" .UL arg1 arg2
.\" Print arg1 underlined, then print arg2 normally.
.\"
.\" .QW arg1 ?arg2?
.\" Print arg1 in quotes, then arg2 normally (for trailing punctuation).
.\"
.\" .PQ arg1 ?arg2?
.\" Print an open parenthesis, arg1 in quotes, then arg2 normally
.\" (for trailing punctuation) and then a closing parenthesis.
.\"
.\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
.\" # Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
. ie !"\\$2"" .TP \\n()Cu
. el .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1 \\fI\\$2\\fP (\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1 \\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
.\" # define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
.\" # BS - start boxed text
.\" # ^y = starting y location
.\" # ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
.\" # BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\" Draw four-sided box normally, but don't draw top of
.\" box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
.\" # VS - start vertical sidebar
.\" # ^Y = starting y location
.\" # ^v = 1 (for troff; for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
.\" # VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
.\" # Special macro to handle page bottom: finish off current
.\" # box/sidebar if in box/sidebar mode, then invoked standard
.\" # page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\" Draw three-sided box if this is the box's first page,
.\" draw two sides but no top otherwise.
.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
.\}
.if \\n(^v \{\
.nr ^x \\n(^tu+1v-\\n(^Yu
\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
.\}
.bp
'fi
.ev
.if \\n(^b \{\
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
.\" # DS - begin display
.de DS
.RS
.nf
.sp
..
.\" # DE - end display
.de DE
.fi
.RE
.sp
..
.\" # SO - start of list of standard options
.de SO
'ie '\\$1'' .ds So \\fBoptions\\fR
'el .ds So \\fB\\$1\\fR
.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
.\" # SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\*(So manual entry for details on the standard options.
..
.\" # OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name: \\fB\\$1\\fR
Database Name: \\fB\\$2\\fR
Database Class: \\fB\\$3\\fR
.fi
.IP
..
.\" # CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i
..
.\" # CE - end code excerpt
.de CE
.fi
.RE
..
.\" # UL - underline word
.de UL
\\$1\l'|0\(ul'\\$2
..
.\" # QW - apply quotation marks to word
.de QW
.ie '\\*(lq'"' ``\\$1''\\$2
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\$2
..
.\" # PQ - apply parens and quotation marks to word
.de PQ
.ie '\\*(lq'"' (``\\$1''\\$2)\\$3
.\"" fix emacs highlighting
.el (\\*(lq\\$1\\*(rq\\$2)\\$3
..
.\" # QR - quoted range
.de QR
.ie '\\*(lq'"' ``\\$1''\\-``\\$2''\\$3
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\-\\*(lq\\$2\\*(rq\\$3
..
.\" # MT - "empty" string
.de MT
.QW ""
..
.BS
.SH NAME
punkshell_module_punk::ansi \- Ansi string functions
.SH SYNOPSIS
package require \fBpunk::ansi \fR
.sp
\fBstripansi\fR \fItext\fR
.sp
\fBa?\fR ?ansicode\&.\&.\&.?
.sp
\fBa+\fR ?ansicode\&.\&.\&.?
.sp
\fBa\fR ?ansicode\&.\&.\&.?
.sp
\fBget_code_name\fR \fIcode\fR
.sp
\fBreset\fR
.sp
\fBreset_soft\fR
.sp
\fBreset_colour\fR
.sp
\fBclear\fR
.sp
\fBclear_above\fR
.sp
\fBclear_below\fR
.sp
\fBcursor_on\fR
.sp
\fBcursor_off\fR
.sp
\fBmove\fR \fIrow\fR \fIcol\fR
.sp
\fBmove_emit\fR \fIrow\fR \fIcol\fR \fIdata\fR ?row col data\&.\&.\&.?
.sp
\fBmove_forward\fR \fIn\fR
.sp
\fBmove_back\fR \fIn\fR
.sp
\fBmove_up\fR \fIn\fR
.sp
\fBmove_down\fR \fIn\fR
.sp
\fBerase_line\fR
.sp
\fBerase_sol\fR
.sp
\fBerase_eol\fR
.sp
\fBcursor_pos\fR
.sp
\fBtitleset\fR \fIwindowtitles\fR
.sp
\fBdetect\fR \fItext\fR
.sp
\fBdetect_csi\fR \fItext\fR
.sp
\fBdetect_sgr\fR \fItext\fR
.sp
\fBstrip\fR \fItext\fR
.sp
\fBlength\fR \fItext\fR
.sp
.BE
.SH DESCRIPTION
.PP
Ansi based terminal control string functions
.PP
See \fBpunk::ansi::console\fR for related functions for controlling a console
.SH OVERVIEW
.PP
overview of punk::ansi
.PP
punk::ansi functions return their values - no implicit emission to console/stdout
.SS CONCEPTS
.PP
Ansi codes can be used to control most terminals on most platforms in an 'almost' standard manner
.PP
There are many differences in terminal implementations - but most should support a core set of features
.PP
punk::ansi does not contain any code for direct terminal manipulation via the local system APIs\&.
.PP
Sticking to ansi codes where possible may be better for cross-platform and remote operation where such APIs are unlikely to be useable\&.
.SS DEPENDENCIES
.PP
packages used by punk::ansi
.IP \(bu
\fBTcl 8\&.6\fR
.PP
.SH API
.SS "NAMESPACE PUNK::ANSI"
.PP
Core API functions for punk::ansi
.TP
\fBstripansi\fR \fItext\fR
.sp
Return a string with ansi codes stripped out
.TP
\fBa?\fR ?ansicode\&.\&.\&.?
.sp
Return an ansi string representing a table of codes and a panel showing the colours
.TP
\fBa+\fR ?ansicode\&.\&.\&.?
.sp
Returns the ansi code to apply those from the supplied list - without any reset being performed first
.sp
e\&.g to set foreground red and bold
.sp
punk::ansi::a red bold
.sp
to set background red
.sp
punk::ansi::a Red
.sp
see \fBpunk::ansi::a?\fR to display a list of codes
.TP
\fBa\fR ?ansicode\&.\&.\&.?
.sp
Returns the ansi code to reset any current settings and apply those from the supplied list
.sp
by calling punk::ansi::a with no arguments - the result is a reset to plain text
.sp
e\&.g to set foreground red and bold
.sp
punk::ansi::a red bold
.sp
to set background red
.sp
punk::ansi::a Red
.sp
see \fBpunk::ansi::a?\fR to display a list of codes
.TP
\fBget_code_name\fR \fIcode\fR
.sp
for example
.sp
get_code_name red will return 31
.sp
get_code_name 31 will return red
.TP
\fBreset\fR
.sp
reset console
.TP
\fBreset_soft\fR
.TP
\fBreset_colour\fR
.sp
reset colour only
.TP
\fBclear\fR
.TP
\fBclear_above\fR
.TP
\fBclear_below\fR
.TP
\fBcursor_on\fR
.TP
\fBcursor_off\fR
.TP
\fBmove\fR \fIrow\fR \fIcol\fR
.sp
Return an ansi sequence to move to row,col
.sp
aka cursor home
.TP
\fBmove_emit\fR \fIrow\fR \fIcol\fR \fIdata\fR ?row col data\&.\&.\&.?
.sp
Return an ansi string representing a move to row col with data appended
.sp
row col data can be repeated any number of times to return a string representing the output of the data elements at all those points
.sp
Compare to punk::console::move_emit which calls this function - but writes it to stdout
.sp
punk::console::move_emit_return will also return the cursor to the original position
.sp
There is no punk::ansi::move_emit_return because in a standard console there is no ansi string which can represent a jump back to starting position\&.
.sp
There is an ansi code to write the current cursor position to stdin (which will generally display on the console) - this is not quite the same thing\&.
.sp
punk::console::move_emit_return does it by emitting that code and starting a loop to read stdin
.sp
punk::ansi could implement a move_emit_return using the punk::console mechanism - but the resulting string would capture the cursor position at the time the string is built - which is not necessarily when the string is used\&.
.sp
The following example shows how to do this manually, emitting the string blah at screen position 10,10 and emitting DONE back at the line we started:
.sp
.CS
punk::ansi::move_emit 10 10 blah {*}[punk::console::get_cursor_pos_list] DONE
.CE
.sp
A string created by any move_emit_return for punk::ansi would not behave in an intuitive manner compared to other punk::ansi move functions - so is deliberately omitted\&.
.TP
\fBmove_forward\fR \fIn\fR
.TP
\fBmove_back\fR \fIn\fR
.TP
\fBmove_up\fR \fIn\fR
.TP
\fBmove_down\fR \fIn\fR
.TP
\fBerase_line\fR
.TP
\fBerase_sol\fR
.sp
Erase to start of line, leaving cursor position alone\&.
.TP
\fBerase_eol\fR
.TP
\fBcursor_pos\fR
.sp
cursor_pos unlikely to be useful on it's own like this as when written to the terminal, this sequence causes the terminal to emit the row;col sequence to stdin
.sp
The output on screen will look something like ^[[47;3R
.sp
Use punk::console::get_cursor_pos or punk::console::get_cursor_pos_list instead\&.
.sp
These functions will emit the code - but read it in from stdin so that it doesn't display, and then return the row and column as a colon-delimited string or list respectively\&.
.sp
The punk::ansi::cursor_pos function is used by punk::console::get_cursor_pos and punk::console::get_cursor_pos_list
.TP
\fBtitleset\fR \fIwindowtitles\fR
.sp
Returns the code to set the title of the terminal window to windowtitle
.sp
This may not work on terminals which have multiple panes/windows
.PP
.SS "NAMESPACE PUNK::ANSI::TA"
.PP
text ansi functions
.PP
based on but not identical to the Perl Text Ansi module:
.PP
https://github\&.com/perlancar/perl-Text-ANSI-Util/blob/master/lib/Text/ANSI/BaseUtil\&.pm
.TP
\fBdetect\fR \fItext\fR
.sp
Return a boolean indicating whether Ansi codes were detected in text
.sp
.TP
\fBdetect_csi\fR \fItext\fR
.sp
Return a boolean indicating whether an Ansi Control Sequence Introducer (CSI) was detected in text
.sp
The csi is often represented in code as \\x1b or \\033 followed by a left bracket [
.sp
The initial byte or escape is commonly referenced as ESC in Ansi documentation
.sp
There is also a multi-byte escape sequence \\u009b
.sp
This is less commonly used but is also detected here
.sp
(This function is not in perl ta)
.TP
\fBdetect_sgr\fR \fItext\fR
.sp
Return a boolean indicating whether an ansi Select Graphics Rendition code was detected\&.
.sp
This is the set of CSI sequences ending in 'm'
.sp
This is most commonly an Ansi colour code - but also things such as underline and italics
.sp
An SGR with empty or a single zero argument is a reset of the SGR features - this is also detected\&.
.sp
(This function is not in perl ta)
.TP
\fBstrip\fR \fItext\fR
.sp
Return text stripped of Ansi codes
.sp
This is a tailcall to punk::ansi::stripansi
.TP
\fBlength\fR \fItext\fR
.sp
Return the character length after stripping ansi codes - not the printing length
.PP
.SS "NAMESPACE PUNK::ANSI::ANSISTRING"
.PP
punk::ansi::string ensemble
.PP
.SH KEYWORDS
ansi, console, module, string, terminal
.SH COPYRIGHT
.nf
Copyright (c) 2023
.fi

400
src/embedded/man/files/punk/_module_args-0.1.0.tm.n

@ -0,0 +1,400 @@
'\"
'\" Generated from file '_module_args-0\&.1\&.0\&.tm\&.man' by tcllib/doctools with format 'nroff'
'\" Copyright (c) 2024
'\"
.TH "punkshell_module_punk::args" 0 0\&.1\&.0 doc "args to option-value dict and values dict"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\" Start paragraph describing an argument to a library procedure.
.\" type is type of argument (int, etc.), in/out is either "in", "out",
.\" or "in/out" to describe whether procedure reads or modifies arg,
.\" and indent is equivalent to second arg of .IP (shouldn't ever be
.\" needed; use .AS below instead)
.\"
.\" .AS ?type? ?name?
.\" Give maximum sizes of arguments for setting tab stops. Type and
.\" name are examples of largest possible arguments that will be passed
.\" to .AP later. If args are omitted, default tab stops are used.
.\"
.\" .BS
.\" Start box enclosure. From here until next .BE, everything will be
.\" enclosed in one large box.
.\"
.\" .BE
.\" End of box enclosure.
.\"
.\" .CS
.\" Begin code excerpt.
.\"
.\" .CE
.\" End code excerpt.
.\"
.\" .VS ?version? ?br?
.\" Begin vertical sidebar, for use in marking newly-changed parts
.\" of man pages. The first argument is ignored and used for recording
.\" the version when the .VS was added, so that the sidebars can be
.\" found and removed when they reach a certain age. If another argument
.\" is present, then a line break is forced before starting the sidebar.
.\"
.\" .VE
.\" End of vertical sidebar.
.\"
.\" .DS
.\" Begin an indented unfilled display.
.\"
.\" .DE
.\" End of indented unfilled display.
.\"
.\" .SO ?manpage?
.\" Start of list of standard options for a Tk widget. The manpage
.\" argument defines where to look up the standard options; if
.\" omitted, defaults to "options". The options follow on successive
.\" lines, in three columns separated by tabs.
.\"
.\" .SE
.\" End of list of standard options for a Tk widget.
.\"
.\" .OP cmdName dbName dbClass
.\" Start of description of a specific option. cmdName gives the
.\" option's name as specified in the class command, dbName gives
.\" the option's name in the option database, and dbClass gives
.\" the option's class in the option database.
.\"
.\" .UL arg1 arg2
.\" Print arg1 underlined, then print arg2 normally.
.\"
.\" .QW arg1 ?arg2?
.\" Print arg1 in quotes, then arg2 normally (for trailing punctuation).
.\"
.\" .PQ arg1 ?arg2?
.\" Print an open parenthesis, arg1 in quotes, then arg2 normally
.\" (for trailing punctuation) and then a closing parenthesis.
.\"
.\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
.\" # Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
. ie !"\\$2"" .TP \\n()Cu
. el .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1 \\fI\\$2\\fP (\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1 \\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
.\" # define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
.\" # BS - start boxed text
.\" # ^y = starting y location
.\" # ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
.\" # BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\" Draw four-sided box normally, but don't draw top of
.\" box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
.\" # VS - start vertical sidebar
.\" # ^Y = starting y location
.\" # ^v = 1 (for troff; for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
.\" # VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
.\" # Special macro to handle page bottom: finish off current
.\" # box/sidebar if in box/sidebar mode, then invoked standard
.\" # page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\" Draw three-sided box if this is the box's first page,
.\" draw two sides but no top otherwise.
.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
.\}
.if \\n(^v \{\
.nr ^x \\n(^tu+1v-\\n(^Yu
\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
.\}
.bp
'fi
.ev
.if \\n(^b \{\
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
.\" # DS - begin display
.de DS
.RS
.nf
.sp
..
.\" # DE - end display
.de DE
.fi
.RE
.sp
..
.\" # SO - start of list of standard options
.de SO
'ie '\\$1'' .ds So \\fBoptions\\fR
'el .ds So \\fB\\$1\\fR
.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
.\" # SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\*(So manual entry for details on the standard options.
..
.\" # OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name: \\fB\\$1\\fR
Database Name: \\fB\\$2\\fR
Database Class: \\fB\\$3\\fR
.fi
.IP
..
.\" # CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i
..
.\" # CE - end code excerpt
.de CE
.fi
.RE
..
.\" # UL - underline word
.de UL
\\$1\l'|0\(ul'\\$2
..
.\" # QW - apply quotation marks to word
.de QW
.ie '\\*(lq'"' ``\\$1''\\$2
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\$2
..
.\" # PQ - apply parens and quotation marks to word
.de PQ
.ie '\\*(lq'"' (``\\$1''\\$2)\\$3
.\"" fix emacs highlighting
.el (\\*(lq\\$1\\*(rq\\$2)\\$3
..
.\" # QR - quoted range
.de QR
.ie '\\*(lq'"' ``\\$1''\\-``\\$2''\\$3
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\-\\*(lq\\$2\\*(rq\\$3
..
.\" # MT - "empty" string
.de MT
.QW ""
..
.BS
.SH NAME
punkshell_module_punk::args \- args parsing
.SH SYNOPSIS
package require \fBpunk::args \fR
.sp
\fBopts_values\fR \fIoptionspecs\fR \fIrawargs\fR ?option value\&.\&.\&.?
.sp
.BE
.SH DESCRIPTION
.PP
Utilities for parsing proc args
.SH OVERVIEW
.PP
overview of punk::args
.SS CONCEPTS
.PP
There are 2 main conventions for parsing a proc args list
.IP [1]
.sp
leading option-value pairs followed by a list of values (Tk style)
.IP [2]
.sp
leading list of values followed by option-value pairs (Tcl style)
.PP
.PP
punk::args is focused on the 1st convention (Tk style): parsing of args in leading option-value pair style - even for non-Tk usage\&.
.PP
The proc can still contain some leading required values e\&.g
.CS
proc dostuff {arg1 arg2 args} {\&.\&.\&.}}
.CE
.PP
but having the core values elements at the end of args is more generally useful - especially in cases where the number of trailing values is unknown and/or the proc is to be called in a functional 'pipeline' style\&.
.PP
The basic principle is that a call to punk::args::opts_vals is made near the beginning of the proc e\&.g
.CS
proc dofilestuff {args} {
lassign [dict values [punk::args {
-directory -default ""
-translation -default binary
} $args]] opts values
puts "translation is [dict get $opts -translation]"
foreach f [dict values $values] {
puts "doing stuff with file: $f"
}
}
.CE
.SS NOTES
.PP
There are alternative args parsing packages such as:
.IP [1]
argp
.IP [2]
The tcllib set of TEPAM modules
.sp
TEPAM requires an alternative procedure declaration syntax instead of proc - but has support for Tk and documentation generation\&.
.PP
.PP
punk::args was designed initially without specific reference to TEPAM - and to handle some edge cases in specific projects where TEPAM wasn't suitable\&.
.PP
In subsequent revisions of punk::args - some features were made to operate in a way that is similar to TEPAM - to avoid gratuitous differences where possible, but of course there are differences
.PP
and those used TEPAM or mixing TEPAM and punk::args should take care to assess the differences\&.
.PP
TEPAM is a mature solution and is widely available as it is included in tcllib\&.
.PP
Serious consideration should be given to using TEPAM if suitable for your project\&.
.SS DEPENDENCIES
.PP
packages used by punk::args
.IP \(bu
\fBTcl 8\&.6\fR
.PP
.SH API
.SS "NAMESPACE PUNK::ARGS::CLASS"
.PP
class definitions
.PP
.SS "NAMESPACE PUNK::ARGS"
.PP
Core API functions for punk::args
.TP
\fBopts_values\fR \fIoptionspecs\fR \fIrawargs\fR ?option value\&.\&.\&.?
.sp
Parse rawargs as a sequence of zero or more option-value pairs followed by zero or more values
.sp
Returns a dict of the form: opts <options_dict> values <values_dict>
.sp
ARGUMENTS:
.RS
.TP
multiline-string \fIoptionspecs\fR
.sp
This a block of text with records delimited by newlines (lf or crlf)
.sp
Each optionspec line must be of the form:
.sp
-optionname -key val -key2 val2\&.\&.\&.
.sp
where the valid keys for each option specification are: -default -type -range -choices -optional
.TP
list \fIrawargs\fR
.sp
This is a list of the arguments to parse\&. Usually it will be the \\$args value from the containing proc
.RE
.sp
.PP
.SS "NAMESPACE PUNK::ARGS::LIB"
.PP
Secondary functions that are part of the API
.PP
.SH INTERNAL
.SS "NAMESPACE PUNK::ARGS::SYSTEM"
.PP
Internal functions that are not part of the API
.SH KEYWORDS
args, arguments, module, parse, proc
.SH COPYRIGHT
.nf
Copyright (c) 2024
.fi

2
src/embedded/man/files/punk/_module_fileline-0.1.0.tm.n

@ -368,6 +368,8 @@ CR line-endings that are intended to be interpreted as such should be mapped to
packages needed by punk::fileline
.IP \(bu
\fBTcl 8\&.6\fR
.IP \(bu
\fBpunk::args\fR
.PP
.SS "OPTIONAL DEPENDENCIES"
.PP

4
src/embedded/man/files/punk/_module_flib-0.1.0.tm.n

@ -2,7 +2,7 @@
'\" Generated from file '_module_flib-0\&.1\&.0\&.tm\&.man' by tcllib/doctools with format 'nroff'
'\" Copyright (c) 2024
'\"
.TH "shellspy_module_punk::flib" 0 0\&.1\&.0 doc "-"
.TH "punkshell_module_punk::flib" 0 0\&.1\&.0 doc "-"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
@ -272,7 +272,7 @@ Database Class: \\fB\\$3\\fR
..
.BS
.SH NAME
shellspy_module_punk::flib \- Module API
punkshell_module_punk::flib \- Module API
.SH SYNOPSIS
package require \fBpunk::flib \fR
.sp

16
src/embedded/man/files/punk/_module_path-0.1.0.tm.n

@ -280,7 +280,7 @@ package require \fBpunk::path \fR
.sp
\fBglobmatchpath\fR \fIpathglob\fR \fIpath\fR ?option value\&.\&.\&.?
.sp
\fBtreefilenames\fR \fIbasepath\fR \fItailglob\fR ?option value\&.\&.\&.?
\fBtreefilenames\fR ?option value\&.\&.\&.? ?globpattern\&.\&.\&.?
.sp
\fBrelative\fR \fIreference\fR \fIlocation\fR
.sp
@ -346,9 +346,19 @@ ie - the driveletter alone in paths such as c:/etc will still be case insensitiv
.sp
Explicitly specifying -nocase 0 will require the entire case to match including the driveletter\&.
.TP
\fBtreefilenames\fR \fIbasepath\fR \fItailglob\fR ?option value\&.\&.\&.?
\fBtreefilenames\fR ?option value\&.\&.\&.? ?globpattern\&.\&.\&.?
.sp
basic (glob based) list of filenames matching tailglob - recursive
basic (glob based) list of filenames matching each pattern in tailglobs - recursive
.sp
options:
.sp
?-dir? <path>
.sp
defaults to [pwd] - base path for tree to search
.sp
?-antiglob_paths? <list>
.sp
list of path patterns to exclude - may include * and ** path segments e\&.g /usr/**
.sp
no natsorting - so order is dependent on filesystem
.TP

57
src/embedded/man/index.n

@ -272,6 +272,24 @@ Database Class: \\fB\\$3\\fR
.SH INDEX
doc
.RS
ansi
.RS
.TP
\fBfiles/punk/_module_ansi-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::ansi
.RE
args
.RS
.TP
\fBfiles/punk/_module_args-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::args
.RE
arguments
.RS
.TP
\fBfiles/punk/_module_args-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::args
.RE
capability
.RS
.TP
@ -284,6 +302,12 @@ changelog
\fBfiles/project_changes\&.n\fR
punkshell__project_changes
.RE
console
.RS
.TP
\fBfiles/punk/_module_ansi-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::ansi
.RE
file
.RS
.TP
@ -299,21 +323,30 @@ punkshell_module_punk::path
module
.RS
.TP
\fBfiles/punk/_module_ansi-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::ansi
.TP
\fBfiles/punk/_module_args-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::args
.TP
\fBfiles/punk/_module_cap-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::cap
.TP
\fBfiles/punk/_module_fileline-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::fileline
.TP
\fBfiles/punk/_module_flib-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::flib
.TP
\fBfiles/punk/_module_path-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::path
.TP
\fBfiles/punk/_module_flib-0\&.1\&.0\&.tm\&.n\fR
shellspy_module_punk::flib
.RE
parse
.RS
.TP
\fBfiles/punk/_module_args-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::args
.TP
\fBfiles/punk/_module_fileline-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::fileline
.RE
@ -329,6 +362,12 @@ plugin
\fBfiles/punk/_module_cap-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::cap
.RE
proc
.RS
.TP
\fBfiles/punk/_module_args-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::args
.RE
punk
.RS
.TP
@ -365,6 +404,18 @@ punkshell__project_changes
\fBfiles/project_intro\&.n\fR
punkshell__project_intro
.RE
string
.RS
.TP
\fBfiles/punk/_module_ansi-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::ansi
.RE
terminal
.RS
.TP
\fBfiles/punk/_module_ansi-0\&.1\&.0\&.tm\&.n\fR
punkshell_module_punk::ansi
.RE
text
.RS
.TP

12
src/embedded/man/toc.n

@ -282,17 +282,23 @@ doc
\fBpunkshell__project_intro\fR
\fIfiles/project_intro\&.n\fR: Introduction to punkshell
.TP
\fBpunkshell_module_punk::ansi\fR
\fIfiles/punk/_module_ansi-0\&.1\&.0\&.tm\&.n\fR: Ansi string functions
.TP
\fBpunkshell_module_punk::args\fR
\fIfiles/punk/_module_args-0\&.1\&.0\&.tm\&.n\fR: args parsing
.TP
\fBpunkshell_module_punk::cap\fR
\fIfiles/punk/_module_cap-0\&.1\&.0\&.tm\&.n\fR: capability provider and handler plugin system
.TP
\fBpunkshell_module_punk::fileline\fR
\fIfiles/punk/_module_fileline-0\&.1\&.0\&.tm\&.n\fR: file line-handling utilities
.TP
\fBpunkshell_module_punk::flib\fR
\fIfiles/punk/_module_flib-0\&.1\&.0\&.tm\&.n\fR: Module API
.TP
\fBpunkshell_module_punk::mix::commandset::project\fR
\fIfiles/punk/mix/commandset/_module_project-0\&.1\&.0\&.tm\&.n\fR: pmix commandset - project
.TP
\fBpunkshell_module_punk::path\fR
\fIfiles/punk/_module_path-0\&.1\&.0\&.tm\&.n\fR: Filesystem path utilities
.TP
\fBshellspy_module_punk::flib\fR
\fIfiles/punk/_module_flib-0\&.1\&.0\&.tm\&.n\fR: Module API

4
src/embedded/md/.doc/tocdoc

@ -2,9 +2,11 @@
[item doc/files/main.md punkshell {punkshell - Core}]
[item doc/files/project_changes.md punkshell__project_changes {punkshell Changes}]
[item doc/files/project_intro.md punkshell__project_intro {Introduction to punkshell}]
[item doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi {Ansi string functions}]
[item doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args {args parsing}]
[item doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap {capability provider and handler plugin system}]
[item doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline {file line-handling utilities}]
[item doc/files/punk/_module_flib-0.1.0.tm.md punkshell_module_punk::flib {Module API}]
[item doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md punkshell_module_punk::mix::commandset::project {pmix commandset - project}]
[item doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path {Filesystem path utilities}]
[item doc/files/punk/_module_flib-0.1.0.tm.md shellspy_module_punk::flib {Module API}]
[toc_end]

2
src/embedded/md/.idx

@ -1 +1 @@
{file {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline}} repl {{doc/files/project_intro.md punkshell__project_intro} {doc/files/project_changes.md punkshell__project_changes} {doc/files/main.md punkshell}} text {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline}} shell {{doc/files/project_intro.md punkshell__project_intro} {doc/files/project_changes.md punkshell__project_changes} {doc/files/main.md punkshell}} changelog {{doc/files/project_changes.md punkshell__project_changes}} capability {{doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap}} parse {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline}} filesystem {{doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path}} path {{doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path}} module {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} {doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} {doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} {doc/files/punk/_module_flib-0.1.0.tm.md shellspy_module_punk::flib}} punk {{doc/files/project_intro.md punkshell__project_intro} {doc/files/project_changes.md punkshell__project_changes} {doc/files/main.md punkshell}} plugin {{doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap}}} {{shell doc/files/project_changes.md punkshell__project_changes} . {changelog doc/files/project_changes.md punkshell__project_changes} . {shell doc/files/main.md punkshell} . {text doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {repl doc/files/project_intro.md punkshell__project_intro} . {module doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} . {plugin doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} . {filesystem doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} . {path doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} . {module doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} . {punk doc/files/project_changes.md punkshell__project_changes} . {shell doc/files/project_intro.md punkshell__project_intro} . {parse doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {punk doc/files/main.md punkshell} . {module doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {module doc/files/punk/_module_flib-0.1.0.tm.md shellspy_module_punk::flib} . {repl doc/files/project_changes.md punkshell__project_changes} . {punk doc/files/project_intro.md punkshell__project_intro} . {file doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {repl doc/files/main.md punkshell} . {capability doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} .} 12 {file file repl repl text text shell shell changelog changelog capability capability parse parse filesystem filesystem path path module module punk punk plugin plugin}
{file {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline}} console {{doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi}} repl {{doc/files/project_intro.md punkshell__project_intro} {doc/files/project_changes.md punkshell__project_changes} {doc/files/main.md punkshell}} text {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline}} arguments {{doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args}} changelog {{doc/files/project_changes.md punkshell__project_changes}} shell {{doc/files/project_intro.md punkshell__project_intro} {doc/files/project_changes.md punkshell__project_changes} {doc/files/main.md punkshell}} capability {{doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap}} ansi {{doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi}} parse {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} {doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args}} terminal {{doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi}} proc {{doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args}} path {{doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path}} filesystem {{doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path}} args {{doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args}} punk {{doc/files/project_intro.md punkshell__project_intro} {doc/files/project_changes.md punkshell__project_changes} {doc/files/main.md punkshell}} module {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} {doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} {doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} {doc/files/punk/_module_flib-0.1.0.tm.md punkshell_module_punk::flib} {doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args} {doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi}} plugin {{doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap}} string {{doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi}}} {{module doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi} . {shell doc/files/project_changes.md punkshell__project_changes} . {changelog doc/files/project_changes.md punkshell__project_changes} . {shell doc/files/main.md punkshell} . {string doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi} . {parse doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args} . {text doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {repl doc/files/project_intro.md punkshell__project_intro} . {proc doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args} . {module doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} . {path doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} . {filesystem doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} . {args doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args} . {plugin doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} . {module doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path} . {module doc/files/punk/_module_flib-0.1.0.tm.md punkshell_module_punk::flib} . {module doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args} . {console doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi} . {shell doc/files/project_intro.md punkshell__project_intro} . {punk doc/files/project_changes.md punkshell__project_changes} . {parse doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {punk doc/files/main.md punkshell} . {module doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {repl doc/files/project_changes.md punkshell__project_changes} . {punk doc/files/project_intro.md punkshell__project_intro} . {file doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline} . {arguments doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args} . {repl doc/files/main.md punkshell} . {ansi doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi} . {capability doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap} . {terminal doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi} .} 19 {file file repl repl console console text text arguments arguments shell shell changelog changelog capability capability parse parse ansi ansi proc proc terminal terminal filesystem filesystem path path args args module module punk punk plugin plugin string string}

2
src/embedded/md/.toc

@ -1 +1 @@
doc {doc/toc {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline {file line-handling utilities}} {doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap {capability provider and handler plugin system}} {doc/files/project_intro.md punkshell__project_intro {Introduction to punkshell}} {doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path {Filesystem path utilities}} {doc/files/punk/_module_flib-0.1.0.tm.md shellspy_module_punk::flib {Module API}} {doc/files/project_changes.md punkshell__project_changes {punkshell Changes}} {doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md punkshell_module_punk::mix::commandset::project {pmix commandset - project}} {doc/files/main.md punkshell {punkshell - Core}}}}
doc {doc/toc {{doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::fileline {file line-handling utilities}} {doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::cap {capability provider and handler plugin system}} {doc/files/project_intro.md punkshell__project_intro {Introduction to punkshell}} {doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::path {Filesystem path utilities}} {doc/files/punk/_module_flib-0.1.0.tm.md punkshell_module_punk::flib {Module API}} {doc/files/punk/_module_args-0.1.0.tm.md punkshell_module_punk::args {args parsing}} {doc/files/project_changes.md punkshell__project_changes {punkshell Changes}} {doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md punkshell_module_punk::mix::commandset::project {pmix commandset - project}} {doc/files/punk/_module_ansi-0.1.0.tm.md punkshell_module_punk::ansi {Ansi string functions}} {doc/files/main.md punkshell {punkshell - Core}}}}

2
src/embedded/md/.xrf

@ -1 +1 @@
kw,capability {index.md capability} punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.md sa,punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md {punkshell Changes} doc/files/project_changes.md {Introduction to punkshell} doc/files/project_intro.md sa,punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md sa,punkshell(n) doc/files/main.md filesystem {index.md filesystem} sa,punkshell doc/files/main.md kw,shell {index.md shell} sa,punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.md sa,punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.md kw,parse {index.md parse} sa,punkshell__project_changes(n) doc/files/project_changes.md kw,path {index.md path} kw,module {index.md module} punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.md punkshell(n) doc/files/main.md kw,plugin {index.md plugin} punkshell doc/files/main.md kw,file {index.md file} punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.md changelog {index.md changelog} punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.md punkshell__project_changes(n) doc/files/project_changes.md sa,punkshell__project_changes doc/files/project_changes.md path {index.md path} sa,shellspy_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.md file {index.md file} sa,punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.md punkshell__project_changes doc/files/project_changes.md kw,filesystem {index.md filesystem} sa,punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md shellspy_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.md {Module API} doc/files/punk/_module_flib-0.1.0.tm.md shell {index.md shell} punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.md kw,repl {index.md repl} capability {index.md capability} kw,text {index.md text} parse {index.md parse} sa,punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md {punkshell - Core} doc/files/main.md {pmix commandset - project} doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md {capability provider and handler plugin system} doc/files/punk/_module_cap-0.1.0.tm.md repl {index.md repl} punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.md kw,punk {index.md punk} sa,punkshell__project_intro(n) doc/files/project_intro.md text {index.md text} sa,punkshell__project_intro doc/files/project_intro.md {Filesystem path utilities} doc/files/punk/_module_path-0.1.0.tm.md sa,shellspy_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.md sa,punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.md punkshell__project_intro(n) doc/files/project_intro.md {file line-handling utilities} doc/files/punk/_module_fileline-0.1.0.tm.md punkshell__project_intro doc/files/project_intro.md kw,changelog {index.md changelog} module {index.md module} punk {index.md punk} shellspy_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.md plugin {index.md plugin}
kw,capability {index.md capability} punkshell_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.md punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::args(0) doc/files/punk/_module_args-0.1.0.tm.md kw,proc {index.md proc} sa,punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md {punkshell Changes} doc/files/project_changes.md punkshell_module_punk::ansi doc/files/punk/_module_ansi-0.1.0.tm.md {Introduction to punkshell} doc/files/project_intro.md proc {index.md proc} sa,punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md sa,punkshell(n) doc/files/main.md filesystem {index.md filesystem} sa,punkshell doc/files/main.md kw,shell {index.md shell} sa,punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.md sa,punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.md kw,parse {index.md parse} sa,punkshell_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.md sa,punkshell__project_changes(n) doc/files/project_changes.md kw,terminal {index.md terminal} kw,args {index.md args} kw,path {index.md path} kw,module {index.md module} punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.md punkshell(n) doc/files/main.md kw,string {index.md string} kw,plugin {index.md plugin} punkshell doc/files/main.md kw,file {index.md file} punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.md changelog {index.md changelog} punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.md punkshell_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.md punkshell__project_changes(n) doc/files/project_changes.md sa,punkshell__project_changes doc/files/project_changes.md kw,arguments {index.md arguments} terminal {index.md terminal} args {index.md args} path {index.md path} file {index.md file} sa,punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.md sa,punkshell_module_punk::args doc/files/punk/_module_args-0.1.0.tm.md {args parsing} doc/files/punk/_module_args-0.1.0.tm.md punkshell__project_changes doc/files/project_changes.md {Ansi string functions} doc/files/punk/_module_ansi-0.1.0.tm.md kw,filesystem {index.md filesystem} sa,punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md {Module API} doc/files/punk/_module_flib-0.1.0.tm.md sa,punkshell_module_punk::ansi(0) doc/files/punk/_module_ansi-0.1.0.tm.md shell {index.md shell} punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.md punkshell_module_punk::args doc/files/punk/_module_args-0.1.0.tm.md kw,repl {index.md repl} capability {index.md capability} kw,text {index.md text} parse {index.md parse} sa,punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.md punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md {punkshell - Core} doc/files/main.md {pmix commandset - project} doc/files/punk/mix/commandset/_module_project-0.1.0.tm.md punkshell_module_punk::ansi(0) doc/files/punk/_module_ansi-0.1.0.tm.md kw,ansi {index.md ansi} {capability provider and handler plugin system} doc/files/punk/_module_cap-0.1.0.tm.md console {index.md console} repl {index.md repl} punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.md kw,punk {index.md punk} sa,punkshell__project_intro(n) doc/files/project_intro.md text {index.md text} sa,punkshell__project_intro doc/files/project_intro.md {Filesystem path utilities} doc/files/punk/_module_path-0.1.0.tm.md arguments {index.md arguments} sa,punkshell_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.md kw,console {index.md console} sa,punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.md sa,punkshell_module_punk::args(0) doc/files/punk/_module_args-0.1.0.tm.md ansi {index.md ansi} punkshell__project_intro(n) doc/files/project_intro.md {file line-handling utilities} doc/files/punk/_module_fileline-0.1.0.tm.md punkshell__project_intro doc/files/project_intro.md kw,changelog {index.md changelog} module {index.md module} punk {index.md punk} sa,punkshell_module_punk::ansi doc/files/punk/_module_ansi-0.1.0.tm.md string {index.md string} plugin {index.md plugin}

333
src/embedded/md/doc/files/punk/_module_ansi-0.1.0.tm.md

@ -0,0 +1,333 @@
[//000000001]: # (punkshell\_module\_punk::ansi \- punk Ansi library)
[//000000002]: # (Generated from file '\_module\_ansi\-0\.1\.0\.tm\.man' by tcllib/doctools with format 'markdown')
[//000000003]: # (Copyright &copy; 2023)
[//000000004]: # (punkshell\_module\_punk::ansi\(0\) 0\.1\.0 doc "punk Ansi library")
<hr> [ <a href="../../../toc.md">Main Table Of Contents</a> &#124; <a
href="../../toc.md">Table Of Contents</a> &#124; <a
href="../../../index.md">Keyword Index</a> ] <hr>
# NAME
punkshell\_module\_punk::ansi \- Ansi string functions
# <a name='toc'></a>Table Of Contents
- [Table Of Contents](#toc)
- [Synopsis](#synopsis)
- [Description](#section1)
- [Overview](#section2)
- [Concepts](#subsection1)
- [dependencies](#subsection2)
- [API](#section3)
- [Namespace punk::ansi](#subsection3)
- [Namespace punk::ansi::ta](#subsection4)
- [Namespace punk::ansi::ansistring](#subsection5)
- [Keywords](#keywords)
- [Copyright](#copyright)
# <a name='synopsis'></a>SYNOPSIS
package require punk::ansi
[__stripansi__ *text*](#1)
[__a?__ ?ansicode\.\.\.?](#2)
[__a\+__ ?ansicode\.\.\.?](#3)
[__a__ ?ansicode\.\.\.?](#4)
[__get\_code\_name__ *code*](#5)
[__reset__](#6)
[__reset\_soft__](#7)
[__reset\_colour__](#8)
[__clear__](#9)
[__clear\_above__](#10)
[__clear\_below__](#11)
[__cursor\_on__](#12)
[__cursor\_off__](#13)
[__move__ *row* *col*](#14)
[__move\_emit__ *row* *col* *data* ?row col data\.\.\.?](#15)
[__move\_forward__ *n*](#16)
[__move\_back__ *n*](#17)
[__move\_up__ *n*](#18)
[__move\_down__ *n*](#19)
[__erase\_line__](#20)
[__erase\_sol__](#21)
[__erase\_eol__](#22)
[__cursor\_pos__](#23)
[__titleset__ *windowtitles*](#24)
[__detect__ *text*](#25)
[__detect\_csi__ *text*](#26)
[__detect\_sgr__ *text*](#27)
[__strip__ *text*](#28)
[__length__ *text*](#29)
# <a name='description'></a>DESCRIPTION
Ansi based terminal control string functions
See __punk::ansi::console__ for related functions for controlling a console
# <a name='section2'></a>Overview
overview of punk::ansi
punk::ansi functions return their values \- no implicit emission to
console/stdout
## <a name='subsection1'></a>Concepts
Ansi codes can be used to control most terminals on most platforms in an
'almost' standard manner
There are many differences in terminal implementations \- but most should support
a core set of features
punk::ansi does not contain any code for direct terminal manipulation via the
local system APIs\.
Sticking to ansi codes where possible may be better for cross\-platform and
remote operation where such APIs are unlikely to be useable\.
## <a name='subsection2'></a>dependencies
packages used by punk::ansi
- __Tcl 8\.6__
# <a name='section3'></a>API
## <a name='subsection3'></a>Namespace punk::ansi
Core API functions for punk::ansi
- <a name='1'></a>__stripansi__ *text*
Return a string with ansi codes stripped out
- <a name='2'></a>__a?__ ?ansicode\.\.\.?
Return an ansi string representing a table of codes and a panel showing the
colours
- <a name='3'></a>__a\+__ ?ansicode\.\.\.?
Returns the ansi code to apply those from the supplied list \- without any
reset being performed first
e\.g to set foreground red and bold
punk::ansi::a red bold
to set background red
punk::ansi::a Red
see __punk::ansi::a?__ to display a list of codes
- <a name='4'></a>__a__ ?ansicode\.\.\.?
Returns the ansi code to reset any current settings and apply those from the
supplied list
by calling punk::ansi::a with no arguments \- the result is a reset to plain
text
e\.g to set foreground red and bold
punk::ansi::a red bold
to set background red
punk::ansi::a Red
see __punk::ansi::a?__ to display a list of codes
- <a name='5'></a>__get\_code\_name__ *code*
for example
get\_code\_name red will return 31
get\_code\_name 31 will return red
- <a name='6'></a>__reset__
reset console
- <a name='7'></a>__reset\_soft__
- <a name='8'></a>__reset\_colour__
reset colour only
- <a name='9'></a>__clear__
- <a name='10'></a>__clear\_above__
- <a name='11'></a>__clear\_below__
- <a name='12'></a>__cursor\_on__
- <a name='13'></a>__cursor\_off__
- <a name='14'></a>__move__ *row* *col*
Return an ansi sequence to move to row,col
aka cursor home
- <a name='15'></a>__move\_emit__ *row* *col* *data* ?row col data\.\.\.?
Return an ansi string representing a move to row col with data appended
row col data can be repeated any number of times to return a string
representing the output of the data elements at all those points
Compare to punk::console::move\_emit which calls this function \- but writes
it to stdout
punk::console::move\_emit\_return will also return the cursor to the original
position
There is no punk::ansi::move\_emit\_return because in a standard console there
is no ansi string which can represent a jump back to starting position\.
There is an ansi code to write the current cursor position to stdin \(which
will generally display on the console\) \- this is not quite the same thing\.
punk::console::move\_emit\_return does it by emitting that code and starting a
loop to read stdin
punk::ansi could implement a move\_emit\_return using the punk::console
mechanism \- but the resulting string would capture the cursor position at
the time the string is built \- which is not necessarily when the string is
used\.
The following example shows how to do this manually, emitting the string
blah at screen position 10,10 and emitting DONE back at the line we started:
punk::ansi::move_emit 10 10 blah {*}[punk::console::get_cursor_pos_list] DONE
A string created by any move\_emit\_return for punk::ansi would not behave in
an intuitive manner compared to other punk::ansi move functions \- so is
deliberately omitted\.
- <a name='16'></a>__move\_forward__ *n*
- <a name='17'></a>__move\_back__ *n*
- <a name='18'></a>__move\_up__ *n*
- <a name='19'></a>__move\_down__ *n*
- <a name='20'></a>__erase\_line__
- <a name='21'></a>__erase\_sol__
Erase to start of line, leaving cursor position alone\.
- <a name='22'></a>__erase\_eol__
- <a name='23'></a>__cursor\_pos__
cursor\_pos unlikely to be useful on it's own like this as when written to
the terminal, this sequence causes the terminal to emit the row;col sequence
to stdin
The output on screen will look something like ^\[\[47;3R
Use punk::console::get\_cursor\_pos or punk::console::get\_cursor\_pos\_list
instead\.
These functions will emit the code \- but read it in from stdin so that it
doesn't display, and then return the row and column as a colon\-delimited
string or list respectively\.
The punk::ansi::cursor\_pos function is used by punk::console::get\_cursor\_pos
and punk::console::get\_cursor\_pos\_list
- <a name='24'></a>__titleset__ *windowtitles*
Returns the code to set the title of the terminal window to windowtitle
This may not work on terminals which have multiple panes/windows
## <a name='subsection4'></a>Namespace punk::ansi::ta
text ansi functions
based on but not identical to the Perl Text Ansi module:
https://github\.com/perlancar/perl\-Text\-ANSI\-Util/blob/master/lib/Text/ANSI/BaseUtil\.pm
- <a name='25'></a>__detect__ *text*
Return a boolean indicating whether Ansi codes were detected in text
- <a name='26'></a>__detect\_csi__ *text*
Return a boolean indicating whether an Ansi Control Sequence Introducer
\(CSI\) was detected in text
The csi is often represented in code as \\x1b or \\033 followed by a left
bracket \[
The initial byte or escape is commonly referenced as ESC in Ansi
documentation
There is also a multi\-byte escape sequence \\u009b
This is less commonly used but is also detected here
\(This function is not in perl ta\)
- <a name='27'></a>__detect\_sgr__ *text*
Return a boolean indicating whether an ansi Select Graphics Rendition code
was detected\.
This is the set of CSI sequences ending in 'm'
This is most commonly an Ansi colour code \- but also things such as
underline and italics
An SGR with empty or a single zero argument is a reset of the SGR features \-
this is also detected\.
\(This function is not in perl ta\)
- <a name='28'></a>__strip__ *text*
Return text stripped of Ansi codes
This is a tailcall to punk::ansi::stripansi
- <a name='29'></a>__length__ *text*
Return the character length after stripping ansi codes \- not the printing
length
## <a name='subsection5'></a>Namespace punk::ansi::ansistring
punk::ansi::string ensemble
# <a name='keywords'></a>KEYWORDS
[ansi](\.\./\.\./\.\./index\.md\#ansi), [console](\.\./\.\./\.\./index\.md\#console),
[module](\.\./\.\./\.\./index\.md\#module), [string](\.\./\.\./\.\./index\.md\#string),
[terminal](\.\./\.\./\.\./index\.md\#terminal)
# <a name='copyright'></a>COPYRIGHT
Copyright &copy; 2023

177
src/embedded/md/doc/files/punk/_module_args-0.1.0.tm.md

@ -0,0 +1,177 @@
[//000000001]: # (punkshell\_module\_punk::args \- args to option\-value dict and values dict)
[//000000002]: # (Generated from file '\_module\_args\-0\.1\.0\.tm\.man' by tcllib/doctools with format 'markdown')
[//000000003]: # (Copyright &copy; 2024)
[//000000004]: # (punkshell\_module\_punk::args\(0\) 0\.1\.0 doc "args to option\-value dict and values dict")
<hr> [ <a href="../../../toc.md">Main Table Of Contents</a> &#124; <a
href="../../toc.md">Table Of Contents</a> &#124; <a
href="../../../index.md">Keyword Index</a> ] <hr>
# NAME
punkshell\_module\_punk::args \- args parsing
# <a name='toc'></a>Table Of Contents
- [Table Of Contents](#toc)
- [Synopsis](#synopsis)
- [Description](#section1)
- [Overview](#section2)
- [Concepts](#subsection1)
- [Notes](#subsection2)
- [dependencies](#subsection3)
- [API](#section3)
- [Namespace punk::args::class](#subsection4)
- [Namespace punk::args](#subsection5)
- [Namespace punk::args::lib](#subsection6)
- [Internal](#section4)
- [Namespace punk::args::system](#subsection7)
- [Keywords](#keywords)
- [Copyright](#copyright)
# <a name='synopsis'></a>SYNOPSIS
package require punk::args
[__opts\_values__ *optionspecs* *rawargs* ?option value\.\.\.?](#1)
# <a name='description'></a>DESCRIPTION
Utilities for parsing proc args
# <a name='section2'></a>Overview
overview of punk::args
## <a name='subsection1'></a>Concepts
There are 2 main conventions for parsing a proc args list
1. leading option\-value pairs followed by a list of values \(Tk style\)
1. leading list of values followed by option\-value pairs \(Tcl style\)
punk::args is focused on the 1st convention \(Tk style\): parsing of args in
leading option\-value pair style \- even for non\-Tk usage\.
The proc can still contain some leading required values e\.g
proc dostuff {arg1 arg2 args} {...}}
but having the core values elements at the end of args is more generally useful
\- especially in cases where the number of trailing values is unknown and/or the
proc is to be called in a functional 'pipeline' style\.
The basic principle is that a call to punk::args::opts\_vals is made near the
beginning of the proc e\.g
proc dofilestuff {args} {
lassign [dict values [punk::args {
-directory -default ""
-translation -default binary
} $args]] opts values
puts "translation is [dict get $opts -translation]"
foreach f [dict values $values] {
puts "doing stuff with file: $f"
}
}
## <a name='subsection2'></a>Notes
There are alternative args parsing packages such as:
1. argp
1. The tcllib set of TEPAM modules
TEPAM requires an alternative procedure declaration syntax instead of proc
\- but has support for Tk and documentation generation\.
punk::args was designed initially without specific reference to TEPAM \- and to
handle some edge cases in specific projects where TEPAM wasn't suitable\.
In subsequent revisions of punk::args \- some features were made to operate in a
way that is similar to TEPAM \- to avoid gratuitous differences where possible,
but of course there are differences
and those used TEPAM or mixing TEPAM and punk::args should take care to assess
the differences\.
TEPAM is a mature solution and is widely available as it is included in tcllib\.
Serious consideration should be given to using TEPAM if suitable for your
project\.
## <a name='subsection3'></a>dependencies
packages used by punk::args
- __Tcl 8\.6__
# <a name='section3'></a>API
## <a name='subsection4'></a>Namespace punk::args::class
class definitions
## <a name='subsection5'></a>Namespace punk::args
- <a name='1'></a>__opts\_values__ *optionspecs* *rawargs* ?option value\.\.\.?
Parse rawargs as a sequence of zero or more option\-value pairs followed by
zero or more values
Returns a dict of the form: opts <options\_dict> values <values\_dict>
ARGUMENTS:
* multiline\-string *optionspecs*
This a block of text with records delimited by newlines \(lf or crlf\)
Each optionspec line must be of the form:
\-optionname \-key val \-key2 val2\.\.\.
where the valid keys for each option specification are: \-default \-type
\-range \-choices \-optional
* list *rawargs*
This is a list of the arguments to parse\. Usually it will be the \\$args
value from the containing proc
## <a name='subsection6'></a>Namespace punk::args::lib
Secondary functions that are part of the API
# <a name='section4'></a>Internal
## <a name='subsection7'></a>Namespace punk::args::system
# <a name='keywords'></a>KEYWORDS
[args](\.\./\.\./\.\./index\.md\#args),
[arguments](\.\./\.\./\.\./index\.md\#arguments),
[module](\.\./\.\./\.\./index\.md\#module), [parse](\.\./\.\./\.\./index\.md\#parse),
[proc](\.\./\.\./\.\./index\.md\#proc)
# <a name='copyright'></a>COPYRIGHT
Copyright &copy; 2024

2
src/embedded/md/doc/files/punk/_module_fileline-0.1.0.tm.md

@ -129,6 +129,8 @@ packages needed by punk::fileline
- __Tcl 8\.6__
- __punk::args__
## <a name='subsection4'></a>optional dependencies
packages that add functionality but aren't strictly required

6
src/embedded/md/doc/files/punk/_module_flib-0.1.0.tm.md

@ -1,8 +1,8 @@
[//000000001]: # (shellspy\_module\_punk::flib \- \-)
[//000000001]: # (punkshell\_module\_punk::flib \- \-)
[//000000002]: # (Generated from file '\_module\_flib\-0\.1\.0\.tm\.man' by tcllib/doctools with format 'markdown')
[//000000003]: # (Copyright &copy; 2024)
[//000000004]: # (shellspy\_module\_punk::flib\(0\) 0\.1\.0 doc "\-")
[//000000004]: # (punkshell\_module\_punk::flib\(0\) 0\.1\.0 doc "\-")
<hr> [ <a href="../../../toc.md">Main Table Of Contents</a> &#124; <a
href="../../toc.md">Table Of Contents</a> &#124; <a
@ -10,7 +10,7 @@ href="../../../index.md">Keyword Index</a> ] <hr>
# NAME
shellspy\_module\_punk::flib \- Module API
punkshell\_module\_punk::flib \- Module API
# <a name='toc'></a>Table Of Contents

18
src/embedded/md/doc/files/punk/_module_path-0.1.0.tm.md

@ -48,7 +48,7 @@ package require punk::path
[__pathglob\_as\_re__ *pathglob*](#1)
[__globmatchpath__ *pathglob* *path* ?option value\.\.\.?](#2)
[__treefilenames__ *basepath* *tailglob* ?option value\.\.\.?](#3)
[__treefilenames__ ?option value\.\.\.? ?globpattern\.\.\.?](#3)
[__relative__ *reference* *location*](#4)
# <a name='description'></a>DESCRIPTION
@ -123,9 +123,21 @@ class definitions
Explicitly specifying \-nocase 0 will require the entire case to match
including the driveletter\.
- <a name='3'></a>__treefilenames__ *basepath* *tailglob* ?option value\.\.\.?
- <a name='3'></a>__treefilenames__ ?option value\.\.\.? ?globpattern\.\.\.?
basic \(glob based\) list of filenames matching tailglob \- recursive
basic \(glob based\) list of filenames matching each pattern in tailglobs \-
recursive
options:
?\-dir? <path>
defaults to \[pwd\] \- base path for tree to search
?\-antiglob\_paths? <list>
list of path patterns to exclude \- may include \* and \*\* path segments e\.g
/usr/\*\*
no natsorting \- so order is dependent on filesystem

8
src/embedded/md/doc/toc.md

@ -9,12 +9,16 @@
- [punkshell\_\_project\_intro](doc/files/project\_intro\.md) Introduction to punkshell
- [punkshell\_module\_punk::ansi](doc/files/punk/\_module\_ansi\-0\.1\.0\.tm\.md) Ansi string functions
- [punkshell\_module\_punk::args](doc/files/punk/\_module\_args\-0\.1\.0\.tm\.md) args parsing
- [punkshell\_module\_punk::cap](doc/files/punk/\_module\_cap\-0\.1\.0\.tm\.md) capability provider and handler plugin system
- [punkshell\_module\_punk::fileline](doc/files/punk/\_module\_fileline\-0\.1\.0\.tm\.md) file line\-handling utilities
- [punkshell\_module\_punk::flib](doc/files/punk/\_module\_flib\-0\.1\.0\.tm\.md) Module API
- [punkshell\_module\_punk::mix::commandset::project](doc/files/punk/mix/commandset/\_module\_project\-0\.1\.0\.tm\.md) pmix commandset \- project
- [punkshell\_module\_punk::path](doc/files/punk/\_module\_path\-0\.1\.0\.tm\.md) Filesystem path utilities
- [shellspy\_module\_punk::flib](doc/files/punk/\_module\_flib\-0\.1\.0\.tm\.md) Module API

19
src/embedded/md/index.md

@ -5,16 +5,26 @@
----
[C](#cC) &#183; [F](#cF) &#183; [M](#cM) &#183; [P](#cP) &#183; [R](#cR) &#183; [S](#cS) &#183; [T](#cT)
[A](#cA) &#183; [C](#cC) &#183; [F](#cF) &#183; [M](#cM) &#183; [P](#cP) &#183; [R](#cR) &#183; [S](#cS) &#183; [T](#cT)
----
#### <a name='cA'></a>Keywords: A
|||
|---|---|
|<a name='ansi'></a>ansi|[punkshell\_module\_punk::ansi](doc/files/punk/\_module\_ansi\-0\.1\.0\.tm\.md)|
|<a name='args'></a>args|[punkshell\_module\_punk::args](doc/files/punk/\_module\_args\-0\.1\.0\.tm\.md)|
|<a name='arguments'></a>arguments|[punkshell\_module\_punk::args](doc/files/punk/\_module\_args\-0\.1\.0\.tm\.md)|
#### <a name='cC'></a>Keywords: C
|||
|---|---|
|<a name='capability'></a>capability|[punkshell\_module\_punk::cap](doc/files/punk/\_module\_cap\-0\.1\.0\.tm\.md)|
|<a name='changelog'></a>changelog|[punkshell\_\_project\_changes](doc/files/project\_changes\.md)|
|<a name='console'></a>console|[punkshell\_module\_punk::ansi](doc/files/punk/\_module\_ansi\-0\.1\.0\.tm\.md)|
#### <a name='cF'></a>Keywords: F
@ -29,16 +39,17 @@
|||
|---|---|
|<a name='module'></a>module|[punkshell\_module\_punk::cap](doc/files/punk/\_module\_cap\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::fileline](doc/files/punk/\_module\_fileline\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::path](doc/files/punk/\_module\_path\-0\.1\.0\.tm\.md) &#183; [shellspy\_module\_punk::flib](doc/files/punk/\_module\_flib\-0\.1\.0\.tm\.md)|
|<a name='module'></a>module|[punkshell\_module\_punk::ansi](doc/files/punk/\_module\_ansi\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::args](doc/files/punk/\_module\_args\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::cap](doc/files/punk/\_module\_cap\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::fileline](doc/files/punk/\_module\_fileline\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::flib](doc/files/punk/\_module\_flib\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::path](doc/files/punk/\_module\_path\-0\.1\.0\.tm\.md)|
#### <a name='cP'></a>Keywords: P
|||
|---|---|
|<a name='parse'></a>parse|[punkshell\_module\_punk::fileline](doc/files/punk/\_module\_fileline\-0\.1\.0\.tm\.md)|
|<a name='parse'></a>parse|[punkshell\_module\_punk::args](doc/files/punk/\_module\_args\-0\.1\.0\.tm\.md) &#183; [punkshell\_module\_punk::fileline](doc/files/punk/\_module\_fileline\-0\.1\.0\.tm\.md)|
|<a name='path'></a>path|[punkshell\_module\_punk::path](doc/files/punk/\_module\_path\-0\.1\.0\.tm\.md)|
|<a name='plugin'></a>plugin|[punkshell\_module\_punk::cap](doc/files/punk/\_module\_cap\-0\.1\.0\.tm\.md)|
|<a name='proc'></a>proc|[punkshell\_module\_punk::args](doc/files/punk/\_module\_args\-0\.1\.0\.tm\.md)|
|<a name='punk'></a>punk|[punkshell](doc/files/main\.md) &#183; [punkshell\_\_project\_changes](doc/files/project\_changes\.md) &#183; [punkshell\_\_project\_intro](doc/files/project\_intro\.md)|
@ -54,10 +65,12 @@
|||
|---|---|
|<a name='shell'></a>shell|[punkshell](doc/files/main\.md) &#183; [punkshell\_\_project\_changes](doc/files/project\_changes\.md) &#183; [punkshell\_\_project\_intro](doc/files/project\_intro\.md)|
|<a name='string'></a>string|[punkshell\_module\_punk::ansi](doc/files/punk/\_module\_ansi\-0\.1\.0\.tm\.md)|
#### <a name='cT'></a>Keywords: T
|||
|---|---|
|<a name='terminal'></a>terminal|[punkshell\_module\_punk::ansi](doc/files/punk/\_module\_ansi\-0\.1\.0\.tm\.md)|
|<a name='text'></a>text|[punkshell\_module\_punk::fileline](doc/files/punk/\_module\_fileline\-0\.1\.0\.tm\.md)|

8
src/embedded/md/toc.md

@ -9,12 +9,16 @@
- [punkshell\_\_project\_intro](doc/files/project\_intro\.md) Introduction to punkshell
- [punkshell\_module\_punk::ansi](doc/files/punk/\_module\_ansi\-0\.1\.0\.tm\.md) Ansi string functions
- [punkshell\_module\_punk::args](doc/files/punk/\_module\_args\-0\.1\.0\.tm\.md) args parsing
- [punkshell\_module\_punk::cap](doc/files/punk/\_module\_cap\-0\.1\.0\.tm\.md) capability provider and handler plugin system
- [punkshell\_module\_punk::fileline](doc/files/punk/\_module\_fileline\-0\.1\.0\.tm\.md) file line\-handling utilities
- [punkshell\_module\_punk::flib](doc/files/punk/\_module\_flib\-0\.1\.0\.tm\.md) Module API
- [punkshell\_module\_punk::mix::commandset::project](doc/files/punk/mix/commandset/\_module\_project\-0\.1\.0\.tm\.md) pmix commandset \- project
- [punkshell\_module\_punk::path](doc/files/punk/\_module\_path\-0\.1\.0\.tm\.md) Filesystem path utilities
- [shellspy\_module\_punk::flib](doc/files/punk/\_module\_flib\-0\.1\.0\.tm\.md) Module API

4
src/embedded/www/.doc/tocdoc

@ -2,9 +2,11 @@
[item doc/files/main.html punkshell {punkshell - Core}]
[item doc/files/project_changes.html punkshell__project_changes {punkshell Changes}]
[item doc/files/project_intro.html punkshell__project_intro {Introduction to punkshell}]
[item doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi {Ansi string functions}]
[item doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args {args parsing}]
[item doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap {capability provider and handler plugin system}]
[item doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline {file line-handling utilities}]
[item doc/files/punk/_module_flib-0.1.0.tm.html punkshell_module_punk::flib {Module API}]
[item doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html punkshell_module_punk::mix::commandset::project {pmix commandset - project}]
[item doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path {Filesystem path utilities}]
[item doc/files/punk/_module_flib-0.1.0.tm.html shellspy_module_punk::flib {Module API}]
[toc_end]

2
src/embedded/www/.idx

@ -1 +1 @@
{file {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline}} repl {{doc/files/project_intro.html punkshell__project_intro} {doc/files/project_changes.html punkshell__project_changes} {doc/files/main.html punkshell}} text {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline}} shell {{doc/files/project_intro.html punkshell__project_intro} {doc/files/project_changes.html punkshell__project_changes} {doc/files/main.html punkshell}} changelog {{doc/files/project_changes.html punkshell__project_changes}} capability {{doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap}} parse {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline}} filesystem {{doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path}} path {{doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path}} module {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} {doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} {doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} {doc/files/punk/_module_flib-0.1.0.tm.html shellspy_module_punk::flib}} punk {{doc/files/project_intro.html punkshell__project_intro} {doc/files/project_changes.html punkshell__project_changes} {doc/files/main.html punkshell}} plugin {{doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap}}} {{repl doc/files/main.html punkshell} . {file doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {punk doc/files/project_intro.html punkshell__project_intro} . {capability doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} . {shell doc/files/project_changes.html punkshell__project_changes} . {changelog doc/files/project_changes.html punkshell__project_changes} . {shell doc/files/main.html punkshell} . {text doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {repl doc/files/project_intro.html punkshell__project_intro} . {module doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} . {path doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} . {plugin doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} . {filesystem doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} . {module doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} . {shell doc/files/project_intro.html punkshell__project_intro} . {punk doc/files/project_changes.html punkshell__project_changes} . {parse doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {punk doc/files/main.html punkshell} . {module doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {module doc/files/punk/_module_flib-0.1.0.tm.html shellspy_module_punk::flib} . {repl doc/files/project_changes.html punkshell__project_changes} .} 12 {file file repl repl text text shell shell changelog changelog capability capability parse parse filesystem filesystem path path module module punk punk plugin plugin}
{file {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline}} console {{doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi}} repl {{doc/files/project_intro.html punkshell__project_intro} {doc/files/project_changes.html punkshell__project_changes} {doc/files/main.html punkshell}} text {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline}} arguments {{doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args}} changelog {{doc/files/project_changes.html punkshell__project_changes}} shell {{doc/files/project_intro.html punkshell__project_intro} {doc/files/project_changes.html punkshell__project_changes} {doc/files/main.html punkshell}} capability {{doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap}} ansi {{doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi}} parse {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} {doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args}} terminal {{doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi}} proc {{doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args}} path {{doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path}} filesystem {{doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path}} args {{doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args}} punk {{doc/files/project_intro.html punkshell__project_intro} {doc/files/project_changes.html punkshell__project_changes} {doc/files/main.html punkshell}} module {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} {doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} {doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} {doc/files/punk/_module_flib-0.1.0.tm.html punkshell_module_punk::flib} {doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args} {doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi}} plugin {{doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap}} string {{doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi}}} {{repl doc/files/main.html punkshell} . {arguments doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args} . {file doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {punk doc/files/project_intro.html punkshell__project_intro} . {ansi doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi} . {terminal doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi} . {capability doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} . {module doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi} . {shell doc/files/project_changes.html punkshell__project_changes} . {changelog doc/files/project_changes.html punkshell__project_changes} . {repl doc/files/project_intro.html punkshell__project_intro} . {text doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {parse doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args} . {shell doc/files/main.html punkshell} . {string doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi} . {module doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} . {proc doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args} . {path doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} . {filesystem doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} . {plugin doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap} . {args doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args} . {module doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path} . {module doc/files/punk/_module_flib-0.1.0.tm.html punkshell_module_punk::flib} . {module doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args} . {console doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi} . {shell doc/files/project_intro.html punkshell__project_intro} . {punk doc/files/project_changes.html punkshell__project_changes} . {parse doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {punk doc/files/main.html punkshell} . {module doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline} . {repl doc/files/project_changes.html punkshell__project_changes} .} 19 {file file repl repl console console text text arguments arguments shell shell changelog changelog capability capability parse parse ansi ansi proc proc terminal terminal filesystem filesystem path path args args module module punk punk plugin plugin string string}

2
src/embedded/www/.toc

@ -1 +1 @@
doc {doc/toc {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline {file line-handling utilities}} {doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap {capability provider and handler plugin system}} {doc/files/project_intro.html punkshell__project_intro {Introduction to punkshell}} {doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path {Filesystem path utilities}} {doc/files/punk/_module_flib-0.1.0.tm.html shellspy_module_punk::flib {Module API}} {doc/files/project_changes.html punkshell__project_changes {punkshell Changes}} {doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html punkshell_module_punk::mix::commandset::project {pmix commandset - project}} {doc/files/main.html punkshell {punkshell - Core}}}}
doc {doc/toc {{doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::fileline {file line-handling utilities}} {doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::cap {capability provider and handler plugin system}} {doc/files/project_intro.html punkshell__project_intro {Introduction to punkshell}} {doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::path {Filesystem path utilities}} {doc/files/punk/_module_flib-0.1.0.tm.html punkshell_module_punk::flib {Module API}} {doc/files/punk/_module_args-0.1.0.tm.html punkshell_module_punk::args {args parsing}} {doc/files/project_changes.html punkshell__project_changes {punkshell Changes}} {doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html punkshell_module_punk::mix::commandset::project {pmix commandset - project}} {doc/files/punk/_module_ansi-0.1.0.tm.html punkshell_module_punk::ansi {Ansi string functions}} {doc/files/main.html punkshell {punkshell - Core}}}}

2
src/embedded/www/.xrf

@ -1 +1 @@
kw,capability {index.html capability} punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.html sa,punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html {punkshell Changes} doc/files/project_changes.html {Introduction to punkshell} doc/files/project_intro.html sa,punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html sa,punkshell(n) doc/files/main.html filesystem {index.html filesystem} sa,punkshell doc/files/main.html kw,shell {index.html shell} sa,punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.html sa,punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.html kw,parse {index.html parse} sa,punkshell__project_changes(n) doc/files/project_changes.html kw,path {index.html path} kw,module {index.html module} punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.html punkshell(n) doc/files/main.html kw,plugin {index.html plugin} punkshell doc/files/main.html kw,file {index.html file} punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.html changelog {index.html changelog} punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.html punkshell__project_changes(n) doc/files/project_changes.html sa,punkshell__project_changes doc/files/project_changes.html path {index.html path} sa,shellspy_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.html file {index.html file} sa,punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.html punkshell__project_changes doc/files/project_changes.html kw,filesystem {index.html filesystem} sa,punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html shellspy_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.html {Module API} doc/files/punk/_module_flib-0.1.0.tm.html shell {index.html shell} punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.html kw,repl {index.html repl} capability {index.html capability} kw,text {index.html text} parse {index.html parse} sa,punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html {punkshell - Core} doc/files/main.html {pmix commandset - project} doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html {capability provider and handler plugin system} doc/files/punk/_module_cap-0.1.0.tm.html repl {index.html repl} punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.html kw,punk {index.html punk} sa,punkshell__project_intro(n) doc/files/project_intro.html text {index.html text} sa,punkshell__project_intro doc/files/project_intro.html {Filesystem path utilities} doc/files/punk/_module_path-0.1.0.tm.html sa,shellspy_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.html sa,punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.html punkshell__project_intro(n) doc/files/project_intro.html {file line-handling utilities} doc/files/punk/_module_fileline-0.1.0.tm.html punkshell__project_intro doc/files/project_intro.html kw,changelog {index.html changelog} module {index.html module} punk {index.html punk} shellspy_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.html plugin {index.html plugin}
kw,capability {index.html capability} punkshell_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.html punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::args(0) doc/files/punk/_module_args-0.1.0.tm.html kw,proc {index.html proc} sa,punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html {punkshell Changes} doc/files/project_changes.html punkshell_module_punk::ansi doc/files/punk/_module_ansi-0.1.0.tm.html {Introduction to punkshell} doc/files/project_intro.html proc {index.html proc} sa,punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::mix::commandset::project(0) doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html sa,punkshell(n) doc/files/main.html filesystem {index.html filesystem} sa,punkshell doc/files/main.html kw,shell {index.html shell} sa,punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.html sa,punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.html kw,parse {index.html parse} sa,punkshell_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.html sa,punkshell__project_changes(n) doc/files/project_changes.html kw,terminal {index.html terminal} kw,args {index.html args} kw,path {index.html path} kw,module {index.html module} punkshell_module_punk::fileline(0) doc/files/punk/_module_fileline-0.1.0.tm.html punkshell(n) doc/files/main.html kw,string {index.html string} kw,plugin {index.html plugin} punkshell doc/files/main.html kw,file {index.html file} punkshell_module_punk::cap doc/files/punk/_module_cap-0.1.0.tm.html changelog {index.html changelog} punkshell_module_punk::cap(0) doc/files/punk/_module_cap-0.1.0.tm.html punkshell_module_punk::flib(0) doc/files/punk/_module_flib-0.1.0.tm.html punkshell__project_changes(n) doc/files/project_changes.html sa,punkshell__project_changes doc/files/project_changes.html kw,arguments {index.html arguments} terminal {index.html terminal} args {index.html args} path {index.html path} file {index.html file} sa,punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.html sa,punkshell_module_punk::args doc/files/punk/_module_args-0.1.0.tm.html {args parsing} doc/files/punk/_module_args-0.1.0.tm.html punkshell__project_changes doc/files/project_changes.html {Ansi string functions} doc/files/punk/_module_ansi-0.1.0.tm.html kw,filesystem {index.html filesystem} sa,punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html {Module API} doc/files/punk/_module_flib-0.1.0.tm.html sa,punkshell_module_punk::ansi(0) doc/files/punk/_module_ansi-0.1.0.tm.html shell {index.html shell} punkshell_module_punk::path doc/files/punk/_module_path-0.1.0.tm.html punkshell_module_punk::args doc/files/punk/_module_args-0.1.0.tm.html kw,repl {index.html repl} capability {index.html capability} kw,text {index.html text} parse {index.html parse} sa,punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.html punkshell_module_punk::mix::commandset::project doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html {punkshell - Core} doc/files/main.html {pmix commandset - project} doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html punkshell_module_punk::ansi(0) doc/files/punk/_module_ansi-0.1.0.tm.html kw,ansi {index.html ansi} {capability provider and handler plugin system} doc/files/punk/_module_cap-0.1.0.tm.html console {index.html console} repl {index.html repl} punkshell_module_punk::fileline doc/files/punk/_module_fileline-0.1.0.tm.html kw,punk {index.html punk} sa,punkshell__project_intro(n) doc/files/project_intro.html text {index.html text} sa,punkshell__project_intro doc/files/project_intro.html {Filesystem path utilities} doc/files/punk/_module_path-0.1.0.tm.html arguments {index.html arguments} sa,punkshell_module_punk::flib doc/files/punk/_module_flib-0.1.0.tm.html kw,console {index.html console} sa,punkshell_module_punk::path(0) doc/files/punk/_module_path-0.1.0.tm.html sa,punkshell_module_punk::args(0) doc/files/punk/_module_args-0.1.0.tm.html ansi {index.html ansi} punkshell__project_intro(n) doc/files/project_intro.html {file line-handling utilities} doc/files/punk/_module_fileline-0.1.0.tm.html punkshell__project_intro doc/files/project_intro.html kw,changelog {index.html changelog} module {index.html module} punk {index.html punk} sa,punkshell_module_punk::ansi doc/files/punk/_module_ansi-0.1.0.tm.html string {index.html string} plugin {index.html plugin}

312
src/embedded/www/doc/files/punk/_module_ansi-0.1.0.tm.html

@ -0,0 +1,312 @@
<!DOCTYPE html><html><head>
<title>punkshell_module_punk::ansi - punk Ansi library</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 '_module_ansi-0.1.0.tm.man' by tcllib/doctools with format 'html'
-->
<!-- Copyright &amp;copy; 2023
-->
<!-- punkshell_module_punk::ansi.0
-->
<body><hr> [
<a href="../../../toc.html">Main Table Of Contents</a>
&#124; <a href="../../toc.html">Table Of Contents</a>
&#124; <a href="../../../index.html">Keyword Index</a>
] <hr>
<div class="doctools">
<h1 class="doctools_title">punkshell_module_punk::ansi(0) 0.1.0 doc &quot;punk Ansi library&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>punkshell_module_punk::ansi - Ansi string functions</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">Overview</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Concepts</a></li>
<li class="doctools_subsection"><a href="#subsection2">dependencies</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section3">API</a>
<ul>
<li class="doctools_subsection"><a href="#subsection3">Namespace punk::ansi</a></li>
<li class="doctools_subsection"><a href="#subsection4">Namespace punk::ansi::ta</a></li>
<li class="doctools_subsection"><a href="#subsection5">Namespace punk::ansi::ansistring</a></li>
</ul>
</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">punk::ansi</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="function">stripansi</b> <i class="arg">text</i></a></li>
<li><a href="#2"><b class="function">a?</b> <span class="opt">?ansicode...?</span></a></li>
<li><a href="#3"><b class="function">a+</b> <span class="opt">?ansicode...?</span></a></li>
<li><a href="#4"><b class="function">a</b> <span class="opt">?ansicode...?</span></a></li>
<li><a href="#5"><b class="function">get_code_name</b> <i class="arg">code</i></a></li>
<li><a href="#6"><b class="function">reset</b></a></li>
<li><a href="#7"><b class="function">reset_soft</b></a></li>
<li><a href="#8"><b class="function">reset_colour</b></a></li>
<li><a href="#9"><b class="function">clear</b></a></li>
<li><a href="#10"><b class="function">clear_above</b></a></li>
<li><a href="#11"><b class="function">clear_below</b></a></li>
<li><a href="#12"><b class="function">cursor_on</b></a></li>
<li><a href="#13"><b class="function">cursor_off</b></a></li>
<li><a href="#14"><b class="function">move</b> <i class="arg">row</i> <i class="arg">col</i></a></li>
<li><a href="#15"><b class="function">move_emit</b> <i class="arg">row</i> <i class="arg">col</i> <i class="arg">data</i> <span class="opt">?row col data...?</span></a></li>
<li><a href="#16"><b class="function">move_forward</b> <i class="arg">n</i></a></li>
<li><a href="#17"><b class="function">move_back</b> <i class="arg">n</i></a></li>
<li><a href="#18"><b class="function">move_up</b> <i class="arg">n</i></a></li>
<li><a href="#19"><b class="function">move_down</b> <i class="arg">n</i></a></li>
<li><a href="#20"><b class="function">erase_line</b></a></li>
<li><a href="#21"><b class="function">erase_sol</b></a></li>
<li><a href="#22"><b class="function">erase_eol</b></a></li>
<li><a href="#23"><b class="function">cursor_pos</b></a></li>
<li><a href="#24"><b class="function">titleset</b> <i class="arg">windowtitles</i></a></li>
<li><a href="#25"><b class="function">detect</b> <i class="arg">text</i></a></li>
<li><a href="#26"><b class="function">detect_csi</b> <i class="arg">text</i></a></li>
<li><a href="#27"><b class="function">detect_sgr</b> <i class="arg">text</i></a></li>
<li><a href="#28"><b class="function">strip</b> <i class="arg">text</i></a></li>
<li><a href="#29"><b class="function">length</b> <i class="arg">text</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>Ansi based terminal control string functions</p>
<p>See <b class="package">punk::ansi::console</b> for related functions for controlling a console</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">Overview</a></h2>
<p>overview of punk::ansi</p>
<p>punk::ansi functions return their values - no implicit emission to console/stdout</p>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Concepts</a></h3>
<p>Ansi codes can be used to control most terminals on most platforms in an 'almost' standard manner</p>
<p>There are many differences in terminal implementations - but most should support a core set of features</p>
<p>punk::ansi does not contain any code for direct terminal manipulation via the local system APIs.</p>
<p>Sticking to ansi codes where possible may be better for cross-platform and remote operation where such APIs are unlikely to be useable.</p>
</div>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">dependencies</a></h3>
<p>packages used by punk::ansi</p>
<ul class="doctools_itemized">
<li><p><b class="package">Tcl 8.6</b></p></li>
</ul>
</div>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">API</a></h2>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">Namespace punk::ansi</a></h3>
<p>Core API functions for punk::ansi</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="function">stripansi</b> <i class="arg">text</i></a></dt>
<dd><p>Return a string with ansi codes stripped out</p></dd>
<dt><a name="2"><b class="function">a?</b> <span class="opt">?ansicode...?</span></a></dt>
<dd><p>Return an ansi string representing a table of codes and a panel showing the colours</p></dd>
<dt><a name="3"><b class="function">a+</b> <span class="opt">?ansicode...?</span></a></dt>
<dd><p>Returns the ansi code to apply those from the supplied list - without any reset being performed first</p>
<p>e.g to set foreground red and bold</p>
<p>punk::ansi::a red bold</p>
<p>to set background red</p>
<p>punk::ansi::a Red</p>
<p>see <b class="cmd">punk::ansi::a?</b> to display a list of codes</p></dd>
<dt><a name="4"><b class="function">a</b> <span class="opt">?ansicode...?</span></a></dt>
<dd><p>Returns the ansi code to reset any current settings and apply those from the supplied list</p>
<p>by calling punk::ansi::a with no arguments - the result is a reset to plain text</p>
<p>e.g to set foreground red and bold</p>
<p>punk::ansi::a red bold</p>
<p>to set background red</p>
<p>punk::ansi::a Red</p>
<p>see <b class="cmd">punk::ansi::a?</b> to display a list of codes</p></dd>
<dt><a name="5"><b class="function">get_code_name</b> <i class="arg">code</i></a></dt>
<dd><p>for example</p>
<p>get_code_name red will return 31</p>
<p>get_code_name 31 will return red</p></dd>
<dt><a name="6"><b class="function">reset</b></a></dt>
<dd><p>reset console</p></dd>
<dt><a name="7"><b class="function">reset_soft</b></a></dt>
<dd></dd>
<dt><a name="8"><b class="function">reset_colour</b></a></dt>
<dd><p>reset colour only</p></dd>
<dt><a name="9"><b class="function">clear</b></a></dt>
<dd></dd>
<dt><a name="10"><b class="function">clear_above</b></a></dt>
<dd></dd>
<dt><a name="11"><b class="function">clear_below</b></a></dt>
<dd></dd>
<dt><a name="12"><b class="function">cursor_on</b></a></dt>
<dd></dd>
<dt><a name="13"><b class="function">cursor_off</b></a></dt>
<dd></dd>
<dt><a name="14"><b class="function">move</b> <i class="arg">row</i> <i class="arg">col</i></a></dt>
<dd><p>Return an ansi sequence to move to row,col</p>
<p>aka cursor home</p></dd>
<dt><a name="15"><b class="function">move_emit</b> <i class="arg">row</i> <i class="arg">col</i> <i class="arg">data</i> <span class="opt">?row col data...?</span></a></dt>
<dd><p>Return an ansi string representing a move to row col with data appended</p>
<p>row col data can be repeated any number of times to return a string representing the output of the data elements at all those points</p>
<p>Compare to punk::console::move_emit which calls this function - but writes it to stdout</p>
<p>punk::console::move_emit_return will also return the cursor to the original position</p>
<p>There is no punk::ansi::move_emit_return because in a standard console there is no ansi string which can represent a jump back to starting position.</p>
<p>There is an ansi code to write the current cursor position to stdin (which will generally display on the console) - this is not quite the same thing.</p>
<p>punk::console::move_emit_return does it by emitting that code and starting a loop to read stdin</p>
<p>punk::ansi could implement a move_emit_return using the punk::console mechanism - but the resulting string would capture the cursor position at the time the string is built - which is not necessarily when the string is used.</p>
<p>The following example shows how to do this manually, emitting the string blah at screen position 10,10 and emitting DONE back at the line we started:</p>
<pre class="doctools_example">punk::ansi::move_emit 10 10 blah {*}[punk::console::get_cursor_pos_list] DONE</pre>
<p>A string created by any move_emit_return for punk::ansi would not behave in an intuitive manner compared to other punk::ansi move functions - so is deliberately omitted.</p></dd>
<dt><a name="16"><b class="function">move_forward</b> <i class="arg">n</i></a></dt>
<dd></dd>
<dt><a name="17"><b class="function">move_back</b> <i class="arg">n</i></a></dt>
<dd></dd>
<dt><a name="18"><b class="function">move_up</b> <i class="arg">n</i></a></dt>
<dd></dd>
<dt><a name="19"><b class="function">move_down</b> <i class="arg">n</i></a></dt>
<dd></dd>
<dt><a name="20"><b class="function">erase_line</b></a></dt>
<dd></dd>
<dt><a name="21"><b class="function">erase_sol</b></a></dt>
<dd><p>Erase to start of line, leaving cursor position alone.</p></dd>
<dt><a name="22"><b class="function">erase_eol</b></a></dt>
<dd></dd>
<dt><a name="23"><b class="function">cursor_pos</b></a></dt>
<dd><p>cursor_pos unlikely to be useful on it's own like this as when written to the terminal, this sequence causes the terminal to emit the row;col sequence to stdin</p>
<p>The output on screen will look something like ^[[47;3R</p>
<p>Use punk::console::get_cursor_pos or punk::console::get_cursor_pos_list instead.</p>
<p>These functions will emit the code - but read it in from stdin so that it doesn't display, and then return the row and column as a colon-delimited string or list respectively.</p>
<p>The punk::ansi::cursor_pos function is used by punk::console::get_cursor_pos and punk::console::get_cursor_pos_list</p></dd>
<dt><a name="24"><b class="function">titleset</b> <i class="arg">windowtitles</i></a></dt>
<dd><p>Returns the code to set the title of the terminal window to windowtitle</p>
<p>This may not work on terminals which have multiple panes/windows</p></dd>
</dl>
</div>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Namespace punk::ansi::ta</a></h3>
<p>text ansi functions</p>
<p>based on but not identical to the Perl Text Ansi module:</p>
<p>https://github.com/perlancar/perl-Text-ANSI-Util/blob/master/lib/Text/ANSI/BaseUtil.pm</p>
<dl class="doctools_definitions">
<dt><a name="25"><b class="function">detect</b> <i class="arg">text</i></a></dt>
<dd><p>Return a boolean indicating whether Ansi codes were detected in text</p></dd>
<dt><a name="26"><b class="function">detect_csi</b> <i class="arg">text</i></a></dt>
<dd><p>Return a boolean indicating whether an Ansi Control Sequence Introducer (CSI) was detected in text</p>
<p>The csi is often represented in code as \x1b or \033 followed by a left bracket [</p>
<p>The initial byte or escape is commonly referenced as ESC in Ansi documentation</p>
<p>There is also a multi-byte escape sequence \u009b</p>
<p>This is less commonly used but is also detected here</p>
<p>(This function is not in perl ta)</p></dd>
<dt><a name="27"><b class="function">detect_sgr</b> <i class="arg">text</i></a></dt>
<dd><p>Return a boolean indicating whether an ansi Select Graphics Rendition code was detected.</p>
<p>This is the set of CSI sequences ending in 'm'</p>
<p>This is most commonly an Ansi colour code - but also things such as underline and italics</p>
<p>An SGR with empty or a single zero argument is a reset of the SGR features - this is also detected.</p>
<p>(This function is not in perl ta)</p></dd>
<dt><a name="28"><b class="function">strip</b> <i class="arg">text</i></a></dt>
<dd><p>Return text stripped of Ansi codes</p>
<p>This is a tailcall to punk::ansi::stripansi</p></dd>
<dt><a name="29"><b class="function">length</b> <i class="arg">text</i></a></dt>
<dd><p>Return the character length after stripping ansi codes - not the printing length</p></dd>
</dl>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Namespace punk::ansi::ansistring</a></h3>
<p>punk::ansi::string ensemble</p>
<dl class="doctools_definitions">
</dl>
</div>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../index.html#ansi">ansi</a>, <a href="../../../index.html#console">console</a>, <a href="../../../index.html#module">module</a>, <a href="../../../index.html#string">string</a>, <a href="../../../index.html#terminal">terminal</a></p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2023</p>
</div>
</div></body></html>

238
src/embedded/www/doc/files/punk/_module_args-0.1.0.tm.html

@ -0,0 +1,238 @@
<!DOCTYPE html><html><head>
<title>punkshell_module_punk::args - args to option-value dict and values dict</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 '_module_args-0.1.0.tm.man' by tcllib/doctools with format 'html'
-->
<!-- Copyright &amp;copy; 2024
-->
<!-- punkshell_module_punk::args.0
-->
<body><hr> [
<a href="../../../toc.html">Main Table Of Contents</a>
&#124; <a href="../../toc.html">Table Of Contents</a>
&#124; <a href="../../../index.html">Keyword Index</a>
] <hr>
<div class="doctools">
<h1 class="doctools_title">punkshell_module_punk::args(0) 0.1.0 doc &quot;args to option-value dict and values dict&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>punkshell_module_punk::args - args parsing</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">Overview</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Concepts</a></li>
<li class="doctools_subsection"><a href="#subsection2">Notes</a></li>
<li class="doctools_subsection"><a href="#subsection3">dependencies</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section3">API</a>
<ul>
<li class="doctools_subsection"><a href="#subsection4">Namespace punk::args::class</a></li>
<li class="doctools_subsection"><a href="#subsection5">Namespace punk::args</a></li>
<li class="doctools_subsection"><a href="#subsection6">Namespace punk::args::lib</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section4">Internal</a>
<ul>
<li class="doctools_subsection"><a href="#subsection7">Namespace punk::args::system</a></li>
</ul>
</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">punk::args</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="function">opts_values</b> <i class="arg">optionspecs</i> <i class="arg">rawargs</i> <span class="opt">?option value...?</span></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>Utilities for parsing proc args</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">Overview</a></h2>
<p>overview of punk::args</p>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Concepts</a></h3>
<p>There are 2 main conventions for parsing a proc args list</p>
<ol class="doctools_enumerated">
<li><p>leading option-value pairs followed by a list of values (Tk style)</p></li>
<li><p>leading list of values followed by option-value pairs (Tcl style)</p></li>
</ol>
<p>punk::args is focused on the 1st convention (Tk style): parsing of args in leading option-value pair style - even for non-Tk usage.</p>
<p>The proc can still contain some leading required values e.g</p>
<pre class="doctools_example">proc dostuff {arg1 arg2 args} {...}}</pre>
<p>but having the core values elements at the end of args is more generally useful - especially in cases where the number of trailing values is unknown and/or the proc is to be called in a functional 'pipeline' style.</p>
<p>The basic principle is that a call to punk::args::opts_vals is made near the beginning of the proc e.g</p>
<pre class="doctools_example">
proc dofilestuff {args} {
lassign [dict values [punk::args {
-directory -default &quot;&quot;
-translation -default binary
} $args]] opts values
puts &quot;translation is [dict get $opts -translation]&quot;
foreach f [dict values $values] {
puts &quot;doing stuff with file: $f&quot;
}
}
</pre>
</div>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">Notes</a></h3>
<p>There are alternative args parsing packages such as:</p>
<ol class="doctools_enumerated">
<li><p>argp</p></li>
<li><p>The tcllib set of TEPAM modules</p>
<p>TEPAM requires an alternative procedure declaration syntax instead of proc - but has support for Tk and documentation generation.</p></li>
</ol>
<p>punk::args was designed initially without specific reference to TEPAM - and to handle some edge cases in specific projects where TEPAM wasn't suitable.</p>
<p>In subsequent revisions of punk::args - some features were made to operate in a way that is similar to TEPAM - to avoid gratuitous differences where possible, but of course there are differences</p>
<p>and those used TEPAM or mixing TEPAM and punk::args should take care to assess the differences.</p>
<p>TEPAM is a mature solution and is widely available as it is included in tcllib.</p>
<p>Serious consideration should be given to using TEPAM if suitable for your project.</p>
</div>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">dependencies</a></h3>
<p>packages used by punk::args</p>
<ul class="doctools_itemized">
<li><p><b class="package">Tcl 8.6</b></p></li>
</ul>
</div>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">API</a></h2>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Namespace punk::args::class</a></h3>
<p>class definitions</p>
<ol class="doctools_enumerated">
</ol>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Namespace punk::args</a></h3>
<p>Core API functions for punk::args</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="function">opts_values</b> <i class="arg">optionspecs</i> <i class="arg">rawargs</i> <span class="opt">?option value...?</span></a></dt>
<dd><p>Parse rawargs as a sequence of zero or more option-value pairs followed by zero or more values</p>
<p>Returns a dict of the form: opts &lt;options_dict&gt; values &lt;values_dict&gt;</p>
<p>ARGUMENTS:</p>
<dl class="doctools_arguments">
<dt>multiline-string <i class="arg">optionspecs</i></dt>
<dd><p>This a block of text with records delimited by newlines (lf or crlf)</p>
<p>Each optionspec line must be of the form:</p>
<p>-optionname -key val -key2 val2...</p>
<p>where the valid keys for each option specification are: -default -type -range -choices -optional</p></dd>
<dt>list <i class="arg">rawargs</i></dt>
<dd><p>This is a list of the arguments to parse. Usually it will be the \$args value from the containing proc</p></dd>
</dl></dd>
</dl>
</div>
<div id="subsection6" class="doctools_subsection"><h3><a name="subsection6">Namespace punk::args::lib</a></h3>
<p>Secondary functions that are part of the API</p>
<dl class="doctools_definitions">
</dl>
</div>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">Internal</a></h2>
<div id="subsection7" class="doctools_subsection"><h3><a name="subsection7">Namespace punk::args::system</a></h3>
<p>Internal functions that are not part of the API</p>
</div>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../index.html#args">args</a>, <a href="../../../index.html#arguments">arguments</a>, <a href="../../../index.html#module">module</a>, <a href="../../../index.html#parse">parse</a>, <a href="../../../index.html#proc">proc</a></p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2024</p>
</div>
</div></body></html>

1
src/embedded/www/doc/files/punk/_module_fileline-0.1.0.tm.html

@ -200,6 +200,7 @@
<p>packages needed by punk::fileline</p>
<ul class="doctools_itemized">
<li><p><b class="package">Tcl 8.6</b></p></li>
<li><p><b class="package">punk::args</b></p></li>
</ul>
</div>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">optional dependencies</a></h3>

8
src/embedded/www/doc/files/punk/_module_flib-0.1.0.tm.html

@ -1,5 +1,5 @@
<!DOCTYPE html><html><head>
<title>shellspy_module_punk::flib - -</title>
<title>punkshell_module_punk::flib - -</title>
<style type="text/css"><!--
HTML {
background: #FFFFFF;
@ -95,7 +95,7 @@
-->
<!-- Copyright &amp;copy; 2024
-->
<!-- shellspy_module_punk::flib.0
<!-- punkshell_module_punk::flib.0
-->
<body><hr> [
<a href="../../../toc.html">Main Table Of Contents</a>
@ -103,9 +103,9 @@
&#124; <a href="../../../index.html">Keyword Index</a>
] <hr>
<div class="doctools">
<h1 class="doctools_title">shellspy_module_punk::flib(0) 0.1.0 doc &quot;-&quot;</h1>
<h1 class="doctools_title">punkshell_module_punk::flib(0) 0.1.0 doc &quot;-&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>shellspy_module_punk::flib - Module API</p>
<p>punkshell_module_punk::flib - Module API</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">

11
src/embedded/www/doc/files/punk/_module_path-0.1.0.tm.html

@ -142,7 +142,7 @@
<ul class="doctools_syntax">
<li><a href="#1"><b class="function">pathglob_as_re</b> <i class="arg">pathglob</i></a></li>
<li><a href="#2"><b class="function">globmatchpath</b> <i class="arg">pathglob</i> <i class="arg">path</i> <span class="opt">?option value...?</span></a></li>
<li><a href="#3"><b class="function">treefilenames</b> <i class="arg">basepath</i> <i class="arg">tailglob</i> <span class="opt">?option value...?</span></a></li>
<li><a href="#3"><b class="function">treefilenames</b> <span class="opt">?option value...?</span> <span class="opt">?globpattern...?</span></a></li>
<li><a href="#4"><b class="function">relative</b> <i class="arg">reference</i> <i class="arg">location</i></a></li>
</ul>
</div>
@ -189,8 +189,13 @@
<p>If -nocase is not supplied - default to case sensitive *except for driveletter*</p>
<p>ie - the driveletter alone in paths such as c:/etc will still be case insensitive. (ie c:/ETC/* will match C:/ETC/blah but not C:/etc/blah)</p>
<p>Explicitly specifying -nocase 0 will require the entire case to match including the driveletter.</p></dd>
<dt><a name="3"><b class="function">treefilenames</b> <i class="arg">basepath</i> <i class="arg">tailglob</i> <span class="opt">?option value...?</span></a></dt>
<dd><p>basic (glob based) list of filenames matching tailglob - recursive</p>
<dt><a name="3"><b class="function">treefilenames</b> <span class="opt">?option value...?</span> <span class="opt">?globpattern...?</span></a></dt>
<dd><p>basic (glob based) list of filenames matching each pattern in tailglobs - recursive</p>
<p>options:</p>
<p><span class="opt">?-dir?</span> &lt;path&gt;</p>
<p>defaults to [pwd] - base path for tree to search</p>
<p><span class="opt">?-antiglob_paths?</span> &lt;list&gt;</p>
<p>list of path patterns to exclude - may include * and ** path segments e.g /usr/**</p>
<p>no natsorting - so order is dependent on filesystem</p></dd>
<dt><a name="4"><b class="function">relative</b> <i class="arg">reference</i> <i class="arg">location</i></a></dt>
<dd><p>Taking two directory paths, a reference and a location, computes the path

18
src/embedded/www/doc/toc.html

@ -25,6 +25,14 @@
<td class="#doctools_tocright">Introduction to punkshell</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_ansi'><a href="files/punk/_module_ansi-0.1.0.tm.html">punkshell_module_punk::ansi</a></td>
<td class="#doctools_tocright">Ansi string functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_args'><a href="files/punk/_module_args-0.1.0.tm.html">punkshell_module_punk::args</a></td>
<td class="#doctools_tocright">args parsing</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_cap'><a href="files/punk/_module_cap-0.1.0.tm.html">punkshell_module_punk::cap</a></td>
<td class="#doctools_tocright">capability provider and handler plugin system</td>
</tr>
@ -33,16 +41,16 @@
<td class="#doctools_tocright">file line-handling utilities</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_flib'><a href="files/punk/_module_flib-0.1.0.tm.html">punkshell_module_punk::flib</a></td>
<td class="#doctools_tocright">Module API</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_mix_commandset_project'><a href="files/punk/mix/commandset/_module_project-0.1.0.tm.html">punkshell_module_punk::mix::commandset::project</a></td>
<td class="#doctools_tocright">pmix commandset - project</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_path'><a href="files/punk/_module_path-0.1.0.tm.html">punkshell_module_punk::path</a></td>
<td class="#doctools_tocright">Filesystem path utilities</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='shellspy_module_punk_flib'><a href="files/punk/_module_flib-0.1.0.tm.html">shellspy_module_punk::flib</a></td>
<td class="#doctools_tocright">Module API</td>
</tr>
</table>
</dd></dl><hr></body></html>

52
src/embedded/www/index.html

@ -13,22 +13,45 @@
] <hr>
<h3> Keyword Index </h3>
<hr><div class="#doctools_idxnav">
<a href="#cC"> C </a> &#183; <a href="#cF"> F </a> &#183; <a href="#cM"> M </a> &#183; <a href="#cP"> P </a> &#183; <a href="#cR"> R </a> &#183; <a href="#cS"> S </a> &#183; <a href="#cT"> T </a>
<a href="#cA"> A </a> &#183; <a href="#cC"> C </a> &#183; <a href="#cF"> F </a> &#183; <a href="#cM"> M </a> &#183; <a href="#cP"> P </a> &#183; <a href="#cR"> R </a> &#183; <a href="#cS"> S </a> &#183; <a href="#cT"> T </a>
</div>
<hr><table class="#doctools_idx" width="100%">
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cC">Keywords: C</a>
<a name="cA">Keywords: A</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="ansi"> ansi </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_ansi-0.1.0.tm.html"> punkshell_module_punk::ansi </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="args"> args </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_args-0.1.0.tm.html"> punkshell_module_punk::args </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="arguments"> arguments </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_args-0.1.0.tm.html"> punkshell_module_punk::args </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cC">Keywords: C</a>
</th></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="capability"> capability </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_cap-0.1.0.tm.html"> punkshell_module_punk::cap </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="changelog"> changelog </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/project_changes.html"> punkshell__project_changes </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="console"> console </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_ansi-0.1.0.tm.html"> punkshell_module_punk::ansi </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cF">Keywords: F</a>
</th></tr>
@ -48,7 +71,7 @@
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="module"> module </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_cap-0.1.0.tm.html"> punkshell_module_punk::cap </a> &#183; <a href="doc/files/punk/_module_fileline-0.1.0.tm.html"> punkshell_module_punk::fileline </a> &#183; <a href="doc/files/punk/_module_path-0.1.0.tm.html"> punkshell_module_punk::path </a> &#183; <a href="doc/files/punk/_module_flib-0.1.0.tm.html"> shellspy_module_punk::flib </a>
<a href="doc/files/punk/_module_ansi-0.1.0.tm.html"> punkshell_module_punk::ansi </a> &#183; <a href="doc/files/punk/_module_args-0.1.0.tm.html"> punkshell_module_punk::args </a> &#183; <a href="doc/files/punk/_module_cap-0.1.0.tm.html"> punkshell_module_punk::cap </a> &#183; <a href="doc/files/punk/_module_fileline-0.1.0.tm.html"> punkshell_module_punk::fileline </a> &#183; <a href="doc/files/punk/_module_flib-0.1.0.tm.html"> punkshell_module_punk::flib </a> &#183; <a href="doc/files/punk/_module_path-0.1.0.tm.html"> punkshell_module_punk::path </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cP">Keywords: P</a>
@ -56,7 +79,7 @@
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="parse"> parse </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_fileline-0.1.0.tm.html"> punkshell_module_punk::fileline </a>
<a href="doc/files/punk/_module_args-0.1.0.tm.html"> punkshell_module_punk::args </a> &#183; <a href="doc/files/punk/_module_fileline-0.1.0.tm.html"> punkshell_module_punk::fileline </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="path"> path </a></td>
@ -69,6 +92,11 @@
<a href="doc/files/punk/_module_cap-0.1.0.tm.html"> punkshell_module_punk::cap </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="proc"> proc </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_args-0.1.0.tm.html"> punkshell_module_punk::args </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="punk"> punk </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/main.html"> punkshell </a> &#183; <a href="doc/files/project_changes.html"> punkshell__project_changes </a> &#183; <a href="doc/files/project_intro.html"> punkshell__project_intro </a>
@ -76,7 +104,7 @@
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cR">Keywords: R</a>
</th></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="repl"> repl </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/main.html"> punkshell </a> &#183; <a href="doc/files/project_changes.html"> punkshell__project_changes </a> &#183; <a href="doc/files/project_intro.html"> punkshell__project_intro </a>
@ -84,15 +112,25 @@
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cS">Keywords: S</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="shell"> shell </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/main.html"> punkshell </a> &#183; <a href="doc/files/project_changes.html"> punkshell__project_changes </a> &#183; <a href="doc/files/project_intro.html"> punkshell__project_intro </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="string"> string </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_ansi-0.1.0.tm.html"> punkshell_module_punk::ansi </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cT">Keywords: T</a>
</th></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="terminal"> terminal </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_ansi-0.1.0.tm.html"> punkshell_module_punk::ansi </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="text"> text </a></td>
<td class="#doctools_idxright" width="65%">
<a href="doc/files/punk/_module_fileline-0.1.0.tm.html"> punkshell_module_punk::fileline </a>

18
src/embedded/www/toc.html

@ -25,6 +25,14 @@
<td class="#doctools_tocright">Introduction to punkshell</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_ansi'><a href="doc/files/punk/_module_ansi-0.1.0.tm.html">punkshell_module_punk::ansi</a></td>
<td class="#doctools_tocright">Ansi string functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_args'><a href="doc/files/punk/_module_args-0.1.0.tm.html">punkshell_module_punk::args</a></td>
<td class="#doctools_tocright">args parsing</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_cap'><a href="doc/files/punk/_module_cap-0.1.0.tm.html">punkshell_module_punk::cap</a></td>
<td class="#doctools_tocright">capability provider and handler plugin system</td>
</tr>
@ -33,16 +41,16 @@
<td class="#doctools_tocright">file line-handling utilities</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_flib'><a href="doc/files/punk/_module_flib-0.1.0.tm.html">punkshell_module_punk::flib</a></td>
<td class="#doctools_tocright">Module API</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_mix_commandset_project'><a href="doc/files/punk/mix/commandset/_module_project-0.1.0.tm.html">punkshell_module_punk::mix::commandset::project</a></td>
<td class="#doctools_tocright">pmix commandset - project</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='punkshell_module_punk_path'><a href="doc/files/punk/_module_path-0.1.0.tm.html">punkshell_module_punk::path</a></td>
<td class="#doctools_tocright">Filesystem path utilities</td>
</tr>
<tr class="#doctools_tocodd" >
<td class="#doctools_tocleft" ><a name='shellspy_module_punk_flib'><a href="doc/files/punk/_module_flib-0.1.0.tm.html">shellspy_module_punk::flib</a></td>
<td class="#doctools_tocright">Module API</td>
</tr>
</table>
</dd></dl><hr></body></html>

2
src/mixtemplates/layouts/basic/src/bootsupport/modules/punk/mix/commandset/loadedlib-0.1.0.tm

@ -419,7 +419,7 @@ namespace eval punk::mix::commandset::loadedlib {
if {![file exists $source_file]} {
error "Unable to verify source file existence at: $source_file"
}
set source_data [fcat $source_file -translation binary]
set source_data [fcat -translation binary $source_file]
if {![string match "*package provide*" $source_data]} {
puts stderr "Sorry - unable to verify source file contains 'package provide' statement of some sort - copy manually"
return false

324
src/modules/punk-0.1.tm

@ -5129,11 +5129,52 @@ namespace eval punk {
}
interp alias {} ~ {} punk::~
#maint - punk::args has similar
#JMN
#generally we expect values to contain leading dashes only if -- specified. Otherwise no reliable way determine difference between bad flags and values
#If no eopts (--) specified we stop looking for opts at the first nondash encountered in a position we'd expect a dash - so without eopt, values could contain dashes - but not in first position after flags.
#only supports -flag val pairs, not solo options
proc get_leading_opts_and_values {defaults rawargs} {
#If an option is supplied multiple times - only the last value is used.
proc get_leading_opts_and_values {defaults rawargs args} {
if {[llength $defaults] %2 != 0} {
error "get_leading_opts_and_values expected first argument 'defaults' to be a dictionary"
}
dict for {k v} $defaults {
if {![string match -* $k]} {
error "get_leading_opts_and_values problem with supplied defaults. Expect each key to begin with a dash. Got key '$k'"
}
}
#puts "--> [info frame -2] <--"
set cmdinfo [dict get [info frame -2] cmd]
#we can't treat cmdinfo as a list - it may be something like {command {*}$args} in which case lindex $cmdinfo 0 won't work
#hopefully first word is a plain proc name if this function was called in the normal manner - directly from a proc
#we will break at first space and assume the lhs of that will give enough info to be reasonable - (alternatively we could use entire cmdinfo - but it might be big and ugly)
set caller [regexp -inline {\S+} $cmdinfo]
#if called from commandline or some other contexts such as outside of a proc in a namespace - caller may just be "namespace"
if {$caller eq "namespace"} {
set caller "get_leading_opts_and_values called from namespace"
}
# ------------------------------
if {$caller ne "get_leading_opts_and_values"} {
#check our own args
lassign [get_leading_opts_and_values {-anyopts 0 -minvalues 0 -maxvalues -1} $args] _o ownopts _v ownvalues
if {[llength $ownvalues] > 0} {
error "get_leading_opts_and_values expected: a dictionary of defaults, a list of args and at most two option pairs -minvalues <int> and -maxvalues <int> - got extra arguments: '$ownvalues'"
}
set opt_minvalues [dict get $ownopts -minvalues]
set opt_maxvalues [dict get $ownopts -maxvalues]
set opt_anyopts [dict get $ownopts -anyopts]
} else {
#don't check our own args if we called ourself
set opt_minvalues 0
set opt_maxvalues 0
set opt_anyopts 0
}
# ------------------------------
if {[set eopts [lsearch $rawargs "--"]] >= 0} {
set values [lrange $rawargs $eopts+1 end]
set arglist [lrange $rawargs 0 $eopts-1]
@ -5145,6 +5186,10 @@ namespace eval punk {
if {![string match -* $k]} {
break
}
if {$i+1 >= [llength $rawargs]} {
#no value for last flag
error "bad options for $caller. No value supplied for last option $k"
}
incr i 2
}
set arglist [lrange $rawargs 0 $i-1]
@ -5154,12 +5199,30 @@ namespace eval punk {
set arglist [list]
}
}
set checked_args [dict create]
set caller [lindex [dict get [info frame -2] cmd] 0] ;#hopefully first word is a plain proc name if this function was called in the normal manner - directly from a proc
for {set i 0} {$i < [llength $arglist]} {incr i} {
#allow this to error out with message indicating expected flags
dict set checked_args [tcl::prefix match -message "$caller option" [dict keys $defaults] [lindex $arglist $i]] [lindex $arglist $i+1]
incr i ;#skip val
if {$opt_maxvalues == -1} {
#only check min
if {[llength $values] < $opt_minvalues} {
error "bad number of trailing values for $caller. Got [llength $values] values. Expected at least $opt_minvalues"
}
} else {
if {[llength $values] < $opt_minvalues || [llength $values] > $opt_maxvalues} {
if {$opt_minvalues == $opt_maxvalues} {
error "bad number of trailing values for $caller. Got [llength $values] values. Expected exactly $opt_minvalues"
} else {
error "bad number of trailing values for $caller. Got [llength $values] values. Expected between $opt_minvalues and $opt_maxvalues inclusive"
}
}
}
if {!$opt_anyopts} {
set checked_args [dict create]
for {set i 0} {$i < [llength $arglist]} {incr i} {
#allow this to error out with message indicating expected flags
dict set checked_args [tcl::prefix match -message "options for $caller. Unexpected option" [dict keys $defaults] [lindex $arglist $i]] [lindex $arglist $i+1]
incr i ;#skip val
}
} else {
set checked_args $arglist
}
set opts [dict merge $defaults $checked_args]
@ -6095,8 +6158,14 @@ namespace eval punk {
}
return $result
}
proc list_as_lines {list {joinchar \n}} {
join $list $joinchar
proc list_as_lines {args} {
set defaults [dict create\
-joinchar "\n"\
]
lassign [dict values [get_leading_opts_and_values $defaults $args -minvalues 1 -maxvalues 1]] opts values
set opt_joinchar [dict get $opts -joinchar]
set list [lindex $values 0]
join $list $opt_joinchar
}
#--------------------------------------------------
@ -6414,6 +6483,240 @@ namespace eval punk {
# -line trimline|trimleft|trimright -block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty -commandprefix {string length} ?
# -block trimming only trims completely empty lines. use -line trimming to remove whitespace e.g -line trimright will clear empty lines without affecting leading whitespace on other lines that aren't pure whitespace
proc linelist {args} {
set usage "linelist ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
error "linelist missing textchunk argument usage:$usage"
}
set text [lindex $args end]
set arglist [lrange $args 0 end-1]
set defaults [dict create\
-block {trimhead1 trimtail1}\
-line {}\
-commandprefix ""\
-ansiresets 1\
]
foreach {o v} $arglist {
if {$o ni [dict keys $defaults]} {
error "linelist: Unrecognized option '$o' usage:$usage"
}
}
set opts [dict merge $defaults $arglist]
# -- --- --- --- --- ---
set opt_block [dict get $opts -block]
set known_blockopts [list trimhead trimtail triminner trimall trimhead1 trimtail1 collateempty]
foreach bo $opt_block {
if {$bo ni $known_blockopts} {
error "linelist: unknown -block option value: $bo known values: $known_blockopts"
}
}
#normalize certain combos
if {[set posn [lsearch $opt_block trimhead1]] >=0 && "trimhead" in $opt_block} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {[set posn [lsearch $opt_block trimtail1]] >=0 && "trimtail" in $opt_block} {
set opt_block [lreplace $opt_block $posn $posn]
}
if {"trimall" in $opt_block} {
#no other block options make sense in combination with this
set opt_block [list "trimall"]
}
#TODO
if {"triminner" in $opt_block } {
error "linelist -block triminner not implemented - sorry"
}
# -- --- --- --- --- ---
set opt_line [dict get $opts -line]
set known_lineopts [list trimline trimleft trimright]
foreach lo $opt_line {
if {$lo ni $known_lineopts} {
error "linelist: unknown -line option value: $lo known values: $known_lineopts"
}
}
#normalize trimleft trimright combo
if {"trimleft" in $opt_line && "trimright" in $opt_line} {
set opt_line [list "trimline"]
}
# -- --- --- --- --- ---
set opt_commandprefix [dict get $opts -commandprefix]
# -- --- --- --- --- ---
set linelist [list]
set nlsplit [split $text \n]
if {![llength $opt_line]} {
set linelist $nlsplit
#lappend linelist {*}$nlsplit
} else {
foreach ln $nlsplit {
#already normalized trimleft+trimright to trimline
if {"trimline" in $opt_line} {
lappend linelist [string trim $ln]
} elseif {"trimleft" in $opt_line} {
lappend linelist [string trimleft $ln]
} elseif {"trimright" in $opt_line} {
lappend linelist [string trimright $ln]
}
}
}
if {"collateempty" in $opt_block} {
set inputlist $linelist[set linelist [list]]
set last "-"
foreach input $inputlist {
if {$input ne ""} {
lappend linelist $input
set last "-"
} else {
if {$last ne ""} {
lappend linelist ""
}
set last ""
}
}
}
if {"trimall" in $opt_block} {
set linelist [lsearch -all -inline -not -exact $linelist[set linelist {}] ""]
} else {
set start 0
if {"trimhead" in $opt_block} {
set idx 0
set lastempty -1
foreach ln $linelist {
if {[lindex $linelist $idx] ne ""} {
break
} else {
set lastempty $idx
}
incr idx
}
if {$lastempty >=0} {
set start [expr {$lastempty +1}]
}
}
set linelist [lrange $linelist $start end]
if {"trimtail" in $opt_block} {
set revlinelist [lreverse $linelist][set linelist {}]
set i 0
foreach ln $revlinelist {
if {$ln ne ""} {
set linelist [lreverse [lrange $revlinelist $i end]]
break
}
incr i
}
}
# --- ---
set start 0
set end "end"
if {"trimhead1" in $opt_block} {
if {[lindex $linelist 0] eq ""} {
set start 1
}
}
if {"trimtail1" in $opt_block} {
if {[lindex $linelist end] eq ""} {
set end "end-1"
}
}
set linelist [lrange $linelist $start $end]
}
if {[llength $opt_commandprefix]} {
set transformed [list]
foreach ln $linelist {
lappend transformed [{*}$opt_commandprefix $ln]
}
set linelist $transformed
}
return $linelist
}
proc lines_as_list {args} {
set defaults [dict create\
-block {}\
]
#pass -anyopts 1 so we can let the next function decide what arguments are valid - but still pass our defaults
lassign [dict values [get_leading_opts_and_values $defaults $args -anyopts 1]] opts values ;#implicit merge of opts over defaults
tailcall linelist {*}$opts {*}$values
}
#An implementation of a notoriously controversial metric.
proc LOC {args} {
set defaults [dict create\
-dir "\uFFFF"\
-exclude_dupfiles 1\
-exclude_punctlines 1\
-punctchars [list \{ \} \" \\ - _ + = . > , < ' : \; ` ~ ! @ # \$ % ^ & * \[ \] ( ) | / ?]\
]
lassign [dict values [get_leading_opts_and_values $defaults $args]] opts searchspecs ;#implicit merge of opts over defaults
# -- --- --- --- --- ---
set opt_dir [dict get $opts -dir]
if {$opt_dir eq "\uFFFF"} {
set opt_dir [pwd] ;#pwd can take over a ms on windows in a not terribly deep path even with SSDs - so as a general rule we don't use it in the original defaults list
}
# -- --- --- --- --- ---
set opt_exclude_dupfiles [dict get $opts -exclude_dupfiles]
set opt_exclude_punctlines [dict get $opts -exclude_punctlines] ;#exclude lines that consist purely of whitespace and the chars in -punctchars
set opt_punctchars [dict get $opts -punctchars]
# -- --- --- --- --- ---
set filepaths [punk::path::treefilenames -dir $opt_dir {*}$searchspecs]
set loc 0
set dupfileloc 0
set seentails [list]
set dupfilecount 0
set extensions [list]
set purepunctlines 0
foreach fpath $filepaths {
set isdupfile 0
set floc 0
set fpurepunctlines 0
set ext [file extension $fpath]
if {$ext ni $extensions} {
lappend extensions $ext
}
if {!$opt_exclude_punctlines} {
set floc [llength [linelist -line {trimright} -block {trimall} [fcat $fpath]]]
} else {
set lines [linelist -line {trimright} -block {trimall} [fcat $fpath]]
set mapawaypunctuation [list]
foreach p $opt_punctchars empty {} {
lappend mapawaypunctuation $p $empty
}
foreach ln $lines {
if {[string length [string trim [string map $mapawaypunctuation $ln]]] > 0} {
incr floc
} else {
incr fpurepunctlines
}
}
}
if {[file tail $fpath] in $seentails} {
set isdupfile 1
incr dupfilecount
incr dupfileloc $floc
}
if {!$isdupfile || ($isdupfile && !$opt_exclude_dupfiles)} {
incr loc $floc
incr purepunctlines $fpurepunctlines
}
lappend seentails [file tail $fpath]
}
if {$opt_exclude_punctlines} {
return [list loc $loc filecount [llength $filepaths] dupfiles $dupfilecount dupfileloc $dupfileloc extensions $extensions purepunctuationlines $purepunctlines]
}
return [list loc $loc filecount [llength $filepaths] dupfiles $dupfilecount dupfileloc $dupfileloc extensions $extensions]
}
# important for pipeline & match_assign
# -line trimline|trimleft|trimright -block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty -commandprefix {string length} ?
# -block trimming only trims completely empty lines. use -line trimming to remove whitespace e.g -line trimright will clear empty lines without affecting leading whitespace on other lines that aren't pure whitespace
proc linelistXXX {args} {
set usage "linelist ?-line trimline|trimleft|trimright? ?-block trimhead|trimtail|triminner|trimall|trimhead1|trimtail1|collateempty? -commandprefix <cmdlist> text"
if {[llength $args] == 0} {
error "linelist missing textchunk argument usage:$usage"
@ -7068,6 +7371,7 @@ namespace eval punk {
interp alias {} path_list {} punk::path_list
interp alias {} list_as_lines {} punk::list_as_lines
interp alias {} lines_as_list {} punk::lines_as_list
interp alias {} list_filter_cond {} punk::list_filter_cond
interp alias {} is_list_all_in_list {} punk::is_list_all_in_list
interp alias {} is_list_all_ni_list {} punk::is_list_all_ni_list
@ -7187,8 +7491,10 @@ package require punk::mod
#punk::mod::cli set_alias pmod
punk::mod::cli set_alias app
#todo - change to punk::deck
package require punk::mix
punk::mix::cli set_alias pmix
punk::mix::cli set_alias deck
package require punkcheck::cli
punkcheck::cli set_alias pcheck

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

@ -12,21 +12,74 @@
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::ansi 0 999999.0a1.0]
#[copyright "2023"]
#[titledesc {Ansi string functions}] [comment {-- Name section and table of contents description --}]
#[moddesc {punk Ansi library}] [comment {-- Description at end of page heading --}]
#[require punk::ansi]
#[keywords module ansi terminal console string]
#[description]
#[para]Ansi based terminal control string functions
#[para]See [package punk::ansi::console] for related functions for controlling a console
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of punk::ansi
#[para]punk::ansi functions return their values - no implicit emission to console/stdout
#[subsection Concepts]
#[para]Ansi codes can be used to control most terminals on most platforms in an 'almost' standard manner
#[para]There are many differences in terminal implementations - but most should support a core set of features
#[para]punk::ansi does not contain any code for direct terminal manipulation via the local system APIs.
#[para]Sticking to ansi codes where possible may be better for cross-platform and remote operation where such APIs are unlikely to be useable.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by punk::ansi
#[list_begin itemized]
package require Tcl 8.6
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
#punk::ansi functions return their values - no implicit emission to console/stdout
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::ansi {
#*** !doctools
#[subsection {Namespace punk::ansi}]
#[para] Core API functions for punk::ansi
#[list_begin definitions]
#see also ansicolor page on wiki https://wiki.tcl-lang.org/page/ANSI+color+control
variable test "blah\033\[1;33mETC\033\[0;mOK"
#Note that a? is actually a pattern. We can't explicitly match it without also matcing a+ ab etc. Presumably this won't matter here.
namespace export\
{a?} {a+} a \
@ -46,7 +99,11 @@ namespace eval punk::ansi {
#single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~).
dict set escape_terminals CSI [list @ \\ ^ _ ` | ~ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "\{" "\}"]
#dict set escape_terminals CSI [list J K m n A B C D E F G s u] ;#basic
dict set escape_terminals OSC [list \007 \033\\] ;#note mix of 1 and 2-byte terminals
dict set escape_terminals OSC [list \007 \033\\ \u009c] ;#note mix of 1 and 2-byte terminals
dict set escape_terminals DCS [list \007 \033\\ \u009c]
dict set escape_terminals MISC [list \007 \033\\ \u009c]
#NOTE - we are assuming an OSC or DCS started with one type of sequence (7 or 8bit) can be terminated by either 7 or 8 bit ST (or BEL e.g wezterm )
#This using a different type of ST to that of the opening sequence is presumably unlikely in the wild - but who knows?
#self-contained 2 byte ansi escape sequences - review more?
variable ansi_2byte_codes_dict
@ -58,26 +115,46 @@ namespace eval punk::ansi {
"NEL - Next Line" "\u001bE"\
"IND - Down one line" "\u001bD"\
"HTS - Set Tab Stop" "\u001bH"\
"DECPAM app keypad" "\x1b="\
"DECPNM norm keypad" "\x1b>"\
]
#debatable whether strip should reveal the somethinghidden - some terminals don't hide it anyway.
# "PM - Privacy Message" "\u001b^somethinghidden\033\\"\
#The intent is that it's not rendered to the terminal - so on balance it seems best to strip it out.
#todo - review - printing_length calculations affected by whether terminal honours PMs or not. detect and accomodate.
proc stripansi {text} {
#*** !doctools
#[call [fun stripansi] [arg text] ]
#[para]Return a string with ansi codes stripped out
#todo - character set selection - SS2 SS3 - how are they terminated? REVIEW
variable escape_terminals ;#dict
variable ansi_2byte_codes_dict
set text [convert_g0 $text]
#we should just map away the 2-byte sequences too
#standalone 3 byte VT100 sequences - some of these work in wezterm
#\x1b#3 double-height letters top half
#\x1b#4 double-height letters bottom half
#\x1b#5 single-width line
#\x1b#6 double-width line
#\x1b#8 dec test fill screen
set clean_map_2b [list \x1bc "" \x1b7 "" \x1b8 "" \x1bM "" \x1bE "" \x1bD "" \x1bH "" \x1b= "" \x1b> ""]
set clean_map_3b [list \x1b#3 "" \x1b#4 "" \x1b#5 "" \x1b#6 "" \x1b#8 ""]
set text [string map [concat $clean_map_2b $clean_map_3b] $text]
#we process char by char - line-endings whether \r\n or \n should be processed as per any other character.
#line endings can theoretically occur within an ansi escape sequence (review e.g title?)
#line endings can theoretically occur within an ansi escape sequence payload (review e.g title?)
set inputlist [split $text ""]
set outputlist [list]
set 2bytecodes [dict values $ansi_2byte_codes_dict]
set in_escapesequence 0
#assumption - undertext already 'rendered' - ie no backspaces or carriagereturns or other cursor movement controls
set i 0
@ -88,21 +165,23 @@ namespace eval punk::ansi {
#2nd byte - done.
set in_escapesequence 0
} elseif {$in_escapesequence != 0} {
set escseq [dict get $escape_terminals $in_escapesequence]
if {$u in $escseq} {
set endseq [dict get $escape_terminals $in_escapesequence]
if {$u in $endseq} {
set in_escapesequence 0
} elseif {$uv in $escseq} {
} elseif {$uv in $endseq} {
set in_escapseequence 2b ;#flag next byte as last in sequence
}
} else {
#handle both 7-bit and 8-bit CSI and OSC
if {[regexp {^(?:\033\[|\u009b)} $uv]} {
set in_escapesequence CSI
} elseif {[regexp {^(?:\033\]|\u009c)} $uv]} {
} elseif {[regexp {^(?:\033\]|\u009d)} $uv]} {
set in_escapesequence OSC
} elseif {$uv in $2bytecodes} {
#self-contained e.g terminal reset - don't pass through.
set in_escapesequence 2b
} elseif {[regexp {^(?:\033P|\u0090)} $uv]} {
set in_escapesequence DCS
} elseif {[regexp {^(?:\033X|\u0098|\033^|\u009E|\033_|\u009F)} $uv]} {
#SOS,PM,APC - all terminated with ST
set in_escapesequence MISC
} else {
lappend outputlist $u
}
@ -188,6 +267,9 @@ namespace eval punk::ansi {
return $map2
}
proc a? {args} {
#*** !doctools
#[call [fun a?] [opt {ansicode...}]]
#[para]Return an ansi string representing a table of codes and a panel showing the colours
variable SGR_setting_map
variable SGR_colour_map
@ -233,6 +315,15 @@ namespace eval punk::ansi {
}
}
proc a+ {args} {
#*** !doctools
#[call [fun a+] [opt {ansicode...}]]
#[para]Returns the ansi code to apply those from the supplied list - without any reset being performed first
#[para] e.g to set foreground red and bold
#[para]punk::ansi::a red bold
#[para]to set background red
#[para]punk::ansi::a Red
#[para]see [cmd punk::ansi::a?] to display a list of codes
#don't disable ansi here.
#we want this to be available to call even if ansi is off
variable SGR_map
@ -275,6 +366,17 @@ namespace eval punk::ansi {
return "\x1b\[[join $t {;}]m"
}
proc a {args} {
#*** !doctools
#[call [fun a] [opt {ansicode...}]]
#[para]Returns the ansi code to reset any current settings and apply those from the supplied list
#[para] by calling punk::ansi::a with no arguments - the result is a reset to plain text
#[para] e.g to set foreground red and bold
#[para]punk::ansi::a red bold
#[para]to set background red
#[para]punk::ansi::a Red
#[para]see [cmd punk::ansi::a?] to display a list of codes
#don't disable ansi here.
#we want this to be available to call even if ansi is off
variable SGR_map
@ -321,6 +423,11 @@ namespace eval punk::ansi {
proc get_code_name {code} {
#*** !doctools
#[call [fun get_code_name] [arg code]]
#[para]for example
#[para] get_code_name red will return 31
#[para] get_code_name 31 will return red
variable SGR_map
set res [list]
foreach i [split $code ";"] {
@ -335,44 +442,81 @@ namespace eval punk::ansi {
set res
}
proc reset {} {
return "\x1bc" ;#reset console
#*** !doctools
#[call [fun reset]]
#[para]reset console
return "\x1bc"
}
proc reset_soft {} {
#*** !doctools
#[call [fun reset_soft]]
return \x1b\[!p
}
proc reset_colour {} {
return "\x1b\[0m" ;#reset color only
#*** !doctools
#[call [fun reset_colour]]
#[para]reset colour only
return "\x1b\[0m"
}
# -- --- --- --- ---
proc clear {} {
#*** !doctools
#[call [fun clear]]
return "\033\[2J"
}
proc clear_above {} {
#*** !doctools
#[call [fun clear_above]]
return \033\[1J
}
proc clear_below {} {
#*** !doctools
#[call [fun clear_below]]
return \033\[0J
}
proc clear_all {} {
# - doesn't work??
return \033\[3J
}
#see also erase_ functions
# -- --- --- --- ---
proc cursor_on {} {
#*** !doctools
#[call [fun cursor_on]]
return "\033\[?25h"
}
proc cursor_off {} {
#*** !doctools
#[call [fun cursor_off]]
return "\033\[?25l"
}
# -- --- --- --- ---
proc move {row col} {
#aka cursor home
#*** !doctools
#[call [fun move] [arg row] [arg col]]
#[para]Return an ansi sequence to move to row,col
#[para]aka cursor home
return \033\[${row}\;${col}H
}
proc move_emit {row col data args} {
#*** !doctools
#[call [fun move_emit] [arg row] [arg col] [arg data] [opt {row col data...}]]
#[para]Return an ansi string representing a move to row col with data appended
#[para]row col data can be repeated any number of times to return a string representing the output of the data elements at all those points
#[para]Compare to punk::console::move_emit which calls this function - but writes it to stdout
#[para]punk::console::move_emit_return will also return the cursor to the original position
#[para]There is no punk::ansi::move_emit_return because in a standard console there is no ansi string which can represent a jump back to starting position.
#[para]There is an ansi code to write the current cursor position to stdin (which will generally display on the console) - this is not quite the same thing.
#[para]punk::console::move_emit_return does it by emitting that code and starting a loop to read stdin
#[para]punk::ansi could implement a move_emit_return using the punk::console mechanism - but the resulting string would capture the cursor position at the time the string is built - which is not necessarily when the string is used.
#[para]The following example shows how to do this manually, emitting the string blah at screen position 10,10 and emitting DONE back at the line we started:
#[para][example {punk::ansi::move_emit 10 10 blah {*}[punk::console::get_cursor_pos_list] DONE}]
#[para]A string created by any move_emit_return for punk::ansi would not behave in an intuitive manner compared to other punk::ansi move functions - so is deliberately omitted.
set out ""
append out \033\[${row}\;${col}H$data
foreach {row col data} $args {
@ -381,15 +525,23 @@ namespace eval punk::ansi {
return $out
}
proc move_forward {{n 1}} {
#*** !doctools
#[call [fun move_forward] [arg n]]
return \033\[${n}C
}
proc move_back {{n 1}} {
#*** !doctools
#[call [fun move_back] [arg n]]
return \033\[${n}D
}
proc move_up {{n 1}} {
#*** !doctools
#[call [fun move_up] [arg n]]
return \033\[${n}A
}
proc move_down {{n 1}} {
#*** !doctools
#[call [fun move_down] [arg n]]
return \033\[${n}B
}
# -- --- --- --- ---
@ -397,28 +549,42 @@ namespace eval punk::ansi {
# -- --- --- --- ---
proc erase_line {} {
#*** !doctools
#[call [fun erase_line]]
return \033\[2K
}
proc erase_sol {} {
#*** !doctools
#[call [fun erase_sol]]
#[para]Erase to start of line, leaving cursor position alone.
return \033\[1K
}
proc erase_eol {} {
#*** !doctools
#[call [fun erase_eol]]
return \033\[K
}
#see also clear_above clear_below
# -- --- --- --- ---
#cursor_pos unlikely to be useful on it's own like this
#Use punk::console::get_cursor_pos to emit and read result
proc cursor_pos {} {
#this function used by punk::console::get_cursor_pos
#*** !doctools
#[call [fun cursor_pos]]
#[para]cursor_pos unlikely to be useful on it's own like this as when written to the terminal, this sequence causes the terminal to emit the row;col sequence to stdin
#[para]The output on screen will look something like ^[lb][lb]47;3R
#[para]Use punk::console::get_cursor_pos or punk::console::get_cursor_pos_list instead.
#[para]These functions will emit the code - but read it in from stdin so that it doesn't display, and then return the row and column as a colon-delimited string or list respectively.
#[para]The punk::ansi::cursor_pos function is used by punk::console::get_cursor_pos and punk::console::get_cursor_pos_list
return \033\[6n
}
#alternative to string terminator is \007 -
proc titleset {windowtitle} {
#alternative to string terminator is \007 -
#*** !doctools
#[call [fun titleset] [arg windowtitles]]
#[para]Returns the code to set the title of the terminal window to windowtitle
#[para]This may not work on terminals which have multiple panes/windows
return "\033\]2;$windowtitle\033\\" ;#works for xterm and most derivatives
}
#titleget - https://invisible-island.net/xterm/xterm.faq.html#how2_title
@ -431,50 +597,12 @@ namespace eval punk::ansi {
return \x1b#8
}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::ansi ---}]
}
namespace eval internal {
proc splitn {str {len 1}} {
#from textutil::split::splitn
if {$len <= 0} {
return -code error "len must be > 0"
}
if {$len == 1} {
return [split $str {}]
}
set result [list]
set max [string length $str]
set i 0
set j [expr {$len -1}]
while {$i < $max} {
lappend result [string range $str $i $j]
incr i $len
incr j $len
}
return $result
}
#string to 2digit hex - e.g used by XTGETTCAP
proc str2hex {input} {
set 2hex ""
foreach ch [split $input ""] {
append 2hex [format %02X [scan $ch %c]]
}
return $2hex
}
proc hex2str {2digithexchars} {
set 2digithexchars [string map [list _ ""] $2digithexchars] ;#compatibility with tcl tip 551 (compatibility in the sense that users might expect to be able to use underscores and it's nice to support the syntax here too - not that it's required)
if {$2digithexchars eq ""} {
return ""
}
if {[string length $2digithexchars] % 2 != 0} {
error "hex2str requires an even number of hex digits (2 per character)"
}
set 2str ""
foreach pair [splitn $2digithexchars 2] {
append 2str [format %c 0x$pair]
}
return $2str
}
}
namespace eval punk::ansi {
# -- --- --- --- --- ---
@ -555,134 +683,238 @@ namespace eval punk::ansi {
return 0
}
proc is_Fs {code} {
puts stderr "is_Fs unimplemented"
}
}
# -- --- --- --- --- --- --- --- --- --- ---
#todo - implement colour resets like the perl module:
#https://metacpan.org/pod/Text::ANSI::Util
#(saves up all ansi color codes since previous color reset and replays the saved codes after our highlighting is done)
}
namespace eval ta {
namespace path ::punk::ansi
#*based* on but not identical to:
#https://github.com/perlancar/perl-Text-ANSI-Util/blob/master/lib/Text/ANSI/BaseUtil.pm
#handle both 7-bit and 8-bit csi
#review - does codepage affect this? e.g ebcdic has 8bit csi in different position
#CSI
#variable re_csi_open {(?:\033\[|\u009b)[0-9;]+} ;#too specific - doesn't detect \033\[m
variable re_csi_open {(?:\033\[|\u009b])}
#colour and style
variable re_csi_colour {(?:\033\[|\u009b)[0-9;]*m} ;#e.g \033\[31m \033\[m \033\[0m \033\[m0000m
#single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~).
variable re_csi_code {(?:\033\[|\u009b])[0-9;]*[a-zA-Z\\@^_|~`]}
#OSC - termnate with BEL (\a \007) or ST (string terminator \033\\)
#variable re_esc_osc1 {(?:\033\]|\u009c).*\007}
#variable re_esc_osc2 {(?:\033\]|\u009c).*\033\\}
#test - non-greedy
variable re_esc_osc1 {(?:\033\]|\u009c).*?\007}
variable re_esc_osc2 {(?:\033\]|\u009c).*?\033\\}
variable re_ansi_detect "${re_csi_open}|${re_esc_osc1}|${re_esc_osc2}"
#detect any ansi escapes
#review - only detect 'complete' codes - or just use the opening escapes for performance?
proc detect {text} {
variable re_ansi_detect
#variable re_csi_open
#variable re_esc_osc1
#variable re_esc_osc2
#todo - other escape sequences
#expr {[regexp $re_csi_open $text] || [regexp $re_esc_osc1 $text] || [regexp $re_esc_osc2 $text]}
expr {[regexp $re_ansi_detect $text]}
}
#not in perl ta
proc detect_csi {text} {
variable re_csi_colour
expr {[regexp $re_csi_colour $text]}
}
proc strip {text} {
tailcall stripansi $text
}
#note this is character length after stripping ansi codes - not the printing length
proc length {text} {
string length [stripansi $text]
}
#todo - handle newlines
#not in perl ta
proc printing_length {text} {
}
namespace eval punk::ansi::ta {
#*** !doctools
#[subsection {Namespace punk::ansi::ta}]
#[para] text ansi functions
#[para] based on but not identical to the Perl Text Ansi module:
#[para] https://github.com/perlancar/perl-Text-ANSI-Util/blob/master/lib/Text/ANSI/BaseUtil.pm
#[list_begin definitions]
namespace path ::punk::ansi
proc trunc {text width args} {
#handle both 7-bit and 8-bit csi
#review - does codepage affect this? e.g ebcdic has 8bit csi in different position
}
#CSI
#variable re_csi_open {(?:\033\[|\u009b)[0-9;]+} ;#too specific - doesn't detect \033\[m
variable re_csi_open {(?:\033\[|\u009b)}
#colour and style
variable re_csi_colour {(?:\033\[|\u009b)[0-9;]*m} ;#e.g \033\[31m \033\[m \033\[0m \033\[m0000m
#single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~).
variable re_csi_code {(?:\033\[|\u009b)[0-9;]*[a-zA-Z\\@^_|~`]}
#OSC - termnate with BEL (\a \007) or ST (string terminator \033\\)
# 8-byte string terminator is \x9c (\u009c)
#test - non-greedy
variable re_esc_osc1 {(?:\033\]).*?\007}
variable re_esc_osc2 {(?:\033\]).*?\033\\}
variable re_esc_osc3 {(?:\u009d).*?\u009c}
variable re_osc_open {(?:\033\]|\u009d).*}
variable re_ansi_detect "${re_csi_open}|${re_esc_osc1}|${re_esc_osc2}"
#detect any ansi escapes
#review - only detect 'complete' codes - or just use the opening escapes for performance?
proc detect {text} {
#*** !doctools
#[call [fun detect] [arg text]]
#[para]Return a boolean indicating whether Ansi codes were detected in text
#[para]
variable re_ansi_detect
#variable re_csi_open
#variable re_esc_osc1
#variable re_esc_osc2
#todo - other escape sequences
#expr {[regexp $re_csi_open $text] || [regexp $re_esc_osc1 $text] || [regexp $re_esc_osc2 $text]}
expr {[regexp $re_ansi_detect $text]}
}
#not in perl ta
proc detect_csi {text} {
#*** !doctools
#[call [fun detect_csi] [arg text]]
#[para]Return a boolean indicating whether an Ansi Control Sequence Introducer (CSI) was detected in text
#[para]The csi is often represented in code as \x1b or \033 followed by a left bracket [lb]
#[para]The initial byte or escape is commonly referenced as ESC in Ansi documentation
#[para]There is also a multi-byte escape sequence \u009b
#[para]This is less commonly used but is also detected here
#[para](This function is not in perl ta)
variable re_csi_open
expr {[regexp $re_csi_open $text]}
}
proc detect_sgr {text} {
#*** !doctools
#[call [fun detect_sgr] [arg text]]
#[para]Return a boolean indicating whether an ansi Select Graphics Rendition code was detected.
#[para]This is the set of CSI sequences ending in 'm'
#[para]This is most commonly an Ansi colour code - but also things such as underline and italics
#[para]An SGR with empty or a single zero argument is a reset of the SGR features - this is also detected.
#[para](This function is not in perl ta)
variable re_csi_colour
expr {[regexp $re_csi_colour $text]}
}
proc strip {text} {
#*** !doctools
#[call [fun strip] [arg text]]
#[para]Return text stripped of Ansi codes
#[para]This is a tailcall to punk::ansi::stripansi
tailcall stripansi $text
}
proc length {text} {
#*** !doctools
#[call [fun length] [arg text]]
#[para]Return the character length after stripping ansi codes - not the printing length
string length [stripansi $text]
}
#todo - handle newlines
#not in perl ta
proc printing_length {text} {
}
proc trunc {text width args} {
}
#not in perl ta
#returns just the plaintext portions in a list
proc split_at_codes {text} {
variable re_esc_osc1
variable re_esc_osc2
variable re_csi_code
textutil::splitx $text "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}"
}
# -- --- --- --- --- ---
#Split $text to a list containing alternating ANSI color codes and text.
#ANSI color codes are always on the second element, fourth, and so on.
#(ie plaintext on odd list-indices ansi on even indices)
# Example:
#ta_split_codes "" # => ""
#ta_split_codes "a" # => "a"
#ta_split_codes "a\e[31m" # => {"a" "\e[31m"}
#ta_split_codes "\e[31ma" # => {"" "\e[31m" "a"}
#ta_split_codes "\e[31ma\e[0m" # => {"" "\e[31m" "a" "\e[0m"}
#ta_split_codes "\e[31ma\e[0mb" # => {"" "\e[31m" "a" "\e[0m", "b"}
#ta_split_codes "\e[31m\e[0mb" # => {"" "\e[31m\e[0m" "b"}
#
proc split_codes {text} {
variable re_esc_osc1
variable re_esc_osc2
variable re_csi_code
set re "(?:${re_csi_code}|${re_esc_osc1}|${re_esc_osc2})+"
return [_perlish_split $re $text]
}
#like split_codes - but each ansi-escape is split out separately (with empty string of plaintext between codes so odd/even plain ansi still holds)
proc split_codes_single {text} {
variable re_esc_osc1
variable re_esc_osc2
variable re_csi_code
set re "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}"
return [_perlish_split $re $text]
}
#review - tcl greedy expressions may match multiple in one element
proc _perlish_split {re text} {
if {[string length $text] == 0} {
return {}
}
set list [list]
set start 0
while {[regexp -start $start -indices -- $re $text match]} {
lassign $match matchStart matchEnd
lappend list [string range $text $start $matchStart-1] [string range $text $matchStart $matchEnd]
set start [expr {$matchEnd+1}]
}
lappend list [string range $text $start end]
return $list
}
proc _ws_split {text} {
regexp -all -inline {(?:\S+)|(?:\s+)} $text
}
# -- --- --- --- --- ---
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::ansi::ta ---}]
}
# -- --- --- --- --- --- --- --- --- --- ---
namespace eval punk::ansi::ansistring {
#*** !doctools
#[subsection {Namespace punk::ansi::ansistring}]
#[para]punk::ansi::string ensemble
#[list_begin definitions]
namespace path [list ::punk::ansi ::punk::ansi::ta]
namespace ensemble create
namespace export length
proc length {string} {
string length [ansistrip $string]
}
proc trimleft {string args} {
#not in perl ta
#returns just the plaintext portions in a list
proc split_at_codes {text} {
variable re_esc_osc1
variable re_esc_osc2
variable re_csi_code
textutil::splitx $text "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}"
}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::ansi::ta ---}]
}
namespace eval punk::ansi::internal {
proc splitn {str {len 1}} {
#from textutil::split::splitn
if {$len <= 0} {
return -code error "len must be > 0"
}
# -- --- --- --- --- ---
#Split $text to a list containing alternating ANSI color codes and text.
#ANSI color codes are always on the second element, fourth, and so on.
#(ie plaintext on odd list-indices ansi on even indices)
# Example:
#ta_split_codes "" # => ""
#ta_split_codes "a" # => "a"
#ta_split_codes "a\e[31m" # => {"a" "\e[31m"}
#ta_split_codes "\e[31ma" # => {"" "\e[31m" "a"}
#ta_split_codes "\e[31ma\e[0m" # => {"" "\e[31m" "a" "\e[0m"}
#ta_split_codes "\e[31ma\e[0mb" # => {"" "\e[31m" "a" "\e[0m", "b"}
#ta_split_codes "\e[31m\e[0mb" # => {"" "\e[31m\e[0m" "b"}
#
proc split_codes {text} {
variable re_esc_osc1
variable re_esc_osc2
variable re_csi_code
set re "(?:${re_csi_code}|${re_esc_osc1}|${re_esc_osc2})+"
return [_perlish_split $re $text]
if {$len == 1} {
return [split $str {}]
}
#like split_codes - but each ansi-escape is split out separately (with empty string of plaintext between codes so odd/even plain ansi still holds)
proc split_codes_single {text} {
variable re_esc_osc1
variable re_esc_osc2
variable re_csi_code
set re "${re_csi_code}|${re_esc_osc1}|${re_esc_osc2}"
return [_perlish_split $re $text]
set result [list]
set max [string length $str]
set i 0
set j [expr {$len -1}]
while {$i < $max} {
lappend result [string range $str $i $j]
incr i $len
incr j $len
}
#review - tcl greedy expressions may match multiple in one element
proc _perlish_split {re text} {
if {[string length $text] == 0} {
return {}
}
set list [list]
set start 0
while {[regexp -start $start -indices -- $re $text match]} {
lassign $match matchStart matchEnd
lappend list [string range $text $start $matchStart-1] [string range $text $matchStart $matchEnd]
set start [expr {$matchEnd+1}]
}
lappend list [string range $text $start end]
return $list
return $result
}
#string to 2digit hex - e.g used by XTGETTCAP
proc str2hex {input} {
set 2hex ""
foreach ch [split $input ""] {
append 2hex [format %02X [scan $ch %c]]
}
proc _ws_split {text} {
regexp -all -inline {(?:\S+)|(?:\s+)} $text
return $2hex
}
proc hex2str {2digithexchars} {
set 2digithexchars [string map [list _ ""] $2digithexchars] ;#compatibility with tcl tip 551 (compatibility in the sense that users might expect to be able to use underscores and it's nice to support the syntax here too - not that it's required)
if {$2digithexchars eq ""} {
return ""
}
# -- --- --- --- --- ---
if {[string length $2digithexchars] % 2 != 0} {
error "hex2str requires an even number of hex digits (2 per character)"
}
set 2str ""
foreach pair [splitn $2digithexchars 2] {
append 2str [format %c 0x$pair]
}
return $2str
}
# -- --- --- --- --- --- --- --- --- --- ---
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
@ -691,4 +923,9 @@ package provide punk::ansi [namespace eval punk::ansi {
variable version
set version 999999.0a1.0
}]
return
return
#*** !doctools
#[manpage_end]

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

@ -0,0 +1,596 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'pmix make' or bin/punkmake to update from <pkg>-buildversion.txt
# module template: shellspy/src/decktemplates/vendor/punk/modules/template_module-0.0.2.tm
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2024
#
# @@ Meta Begin
# Application punk::args 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::args 0 999999.0a1.0]
#[copyright "2024"]
#[titledesc {args parsing}] [comment {-- Name section and table of contents description --}]
#[moddesc {args to option-value dict and values dict}] [comment {-- Description at end of page heading --}]
#[require punk::args]
#[keywords module proc args arguments parse]
#[description]
#[para]Utilities for parsing proc args
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of punk::args
#[subsection Concepts]
#[para]There are 2 main conventions for parsing a proc args list
#[list_begin enumerated]
#[enum]
#[para]leading option-value pairs followed by a list of values (Tk style)
#[enum]
#[para]leading list of values followed by option-value pairs (Tcl style)
#[list_end]
#[para]punk::args is focused on the 1st convention (Tk style): parsing of args in leading option-value pair style - even for non-Tk usage.
#[para]The proc can still contain some leading required values e.g [example "proc dostuff {arg1 arg2 args} {...}}"]
#[para]but having the core values elements at the end of args is more generally useful - especially in cases where the number of trailing values is unknown and/or the proc is to be called in a functional 'pipeline' style.
#[para]The basic principle is that a call to punk::args::opts_vals is made near the beginning of the proc e.g
#[example_begin]
# proc dofilestuff {args} {
# lassign [lb]dict values [lb]punk::args {
# -directory -default ""
# -translation -default binary
# } $args[rb][rb] opts values
#
# puts "translation is [lb]dict get $opts -translation[rb]"
# foreach f [lb]dict values $values[rb] {
# puts "doing stuff with file: $f"
# }
# }
#[example_end]
#*** !doctools
#[subsection Notes]
#[para]There are alternative args parsing packages such as:
#[list_begin enumerated]
#[enum]argp
#[enum]The tcllib set of TEPAM modules
#[para]TEPAM requires an alternative procedure declaration syntax instead of proc - but has support for Tk and documentation generation.
#[list_end]
#[para]punk::args was designed initially without specific reference to TEPAM - and to handle some edge cases in specific projects where TEPAM wasn't suitable.
#[para]In subsequent revisions of punk::args - some features were made to operate in a way that is similar to TEPAM - to avoid gratuitous differences where possible, but of course there are differences
#[para]and those used TEPAM or mixing TEPAM and punk::args should take care to assess the differences.
#[para]TEPAM is a mature solution and is widely available as it is included in tcllib.
#[para]Serious consideration should be given to using TEPAM if suitable for your project.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by punk::args
#[list_begin itemized]
package require Tcl 8.6
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args::class {
#*** !doctools
#[subsection {Namespace punk::args::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args {
namespace export *
#variable xyz
#*** !doctools
#[subsection {Namespace punk::args}]
#[para] Core API functions for punk::args
#[list_begin definitions]
#generally we expect values to contain leading dashes only if -- specified. Otherwise no reliable way determine difference between bad flags and values
#If no eopts (--) specified we stop looking for opts at the first nondash encountered in a position we'd expect a dash - so without eopt, values could contain dashes - but not in first position after flags.
#only supports -flag val pairs, not solo options
#If an option is supplied multiple times - only the last value is used.
proc opts_values {optionspecs rawargs args} {
#*** !doctools
#[call [fun opts_values] [arg optionspecs] [arg rawargs] [opt {option value...}]]
#[para]Parse rawargs as a sequence of zero or more option-value pairs followed by zero or more values
#[para]Returns a dict of the form: opts <options_dict> values <values_dict>
#[para]ARGUMENTS:
#[list_begin arguments]
#[arg_def multiline-string optionspecs]
#[para] This a block of text with records delimited by newlines (lf or crlf)
#[para]Each optionspec line must be of the form:
#[para]-optionname -key val -key2 val2...
#[para]where the valid keys for each option specification are: -default -type -range -choices -optional
#[arg_def list rawargs]
#[para] This is a list of the arguments to parse. Usually it will be the \$args value from the containing proc
#[list_end]
#[para]
set optionspecs [string map [list \r\n \n] $optionspecs]
set known_argspecs [list -default -type -range -choices -nocase -optional -multiple -validate_without_ansi -allow_ansi -strip_ansi]
set optspec_defaults [dict create\
-optional 1\
-allow_ansi 1\
-validate_without_ansi 0\
-strip_ansi 0\
-nocase 0\
]
set required_opts [list]
set required_vals [list]
set arg_info [dict create]
set defaults_dict [dict create]
#first process dashed and non-dashed argspecs without regard to whether non-dashed are at the beginning or end
set value_names [list]
foreach ln [split $optionspecs \n] {
set trimln [string trim $ln]
if {$trimln eq ""} {
continue
}
if {[string index $trimln 0] eq "#"} {
continue
}
set argname [lindex $trimln 0]
set argspecs [lrange $trimln 1 end]
if {[string match -* $argname]} {
dict set argspecs -ARGTYPE option
set is_opt 1
} else {
dict set argspecs -ARGTYPE value
lappend value_names $argname
set is_opt 0
}
if {[llength $argspecs] %2 != 0} {
error "punk::args::opts_values - bad optionspecs line for argument '$argname' Remaining items on line must be in paired option-value format - received '$argspecs'"
}
dict for {spec specval} $argspecs {
if {$spec ni [concat $known_argspecs -ARGTYPE]} {
error "punk::args::opts_values - unrecognised key '$spec' in specifications for argument '$argname' Known option specification keys: $known_argspecs"
}
}
set argspecs [dict merge $optspec_defaults $argspecs]
dict set arg_info $argname $argspecs
if {![dict get $argspecs -optional]} {
if {$is_opt} {
lappend required_opts $argname
} else {
lappend required_vals $argname
}
}
if {[dict exists $arg_info $argname -default]} {
dict set defaults_dict $argname [dict get $arg_info $argname -default]
}
}
#puts "--> [info frame -2] <--"
set cmdinfo [dict get [info frame -2] cmd]
#we can't treat cmdinfo as a list - it may be something like {command {*}$args} in which case lindex $cmdinfo 0 won't work
#hopefully first word is a plain proc name if this function was called in the normal manner - directly from a proc
#we will break at first space and assume the lhs of that will give enough info to be reasonable - (alternatively we could use entire cmdinfo - but it might be big and ugly)
set caller [regexp -inline {\S+} $cmdinfo]
#if called from commandline or some other contexts such as outside of a proc in a namespace - caller may just be "namespace"
if {$caller eq "namespace"} {
set caller "punk::args::opts_values called from namespace"
}
# ------------------------------
if {$caller ne "punk::args::opts_values"} {
#check our own args
lassign [punk::args::opts_values "-anyopts -default 0\n -minvalues -default 0\n -maxvalues -default -1" $args] _o ownopts _v ownvalues
if {[llength $ownvalues] > 0} {
error "punk::args::opts_values expected: a multiline text block of option-specifications, a list of args and at most three option pairs -minvalues <int>, -maxvalues <int>, -anyopts true|false - got extra arguments: '$ownvalues'"
}
set opt_minvalues [dict get $ownopts -minvalues]
set opt_maxvalues [dict get $ownopts -maxvalues]
set opt_anyopts [dict get $ownopts -anyopts]
} else {
#don't check our own args if we called ourself
set opt_minvalues 0
set opt_maxvalues 0
set opt_anyopts 0
}
# ------------------------------
if {[set eopts [lsearch $rawargs "--"]] >= 0} {
set values [lrange $rawargs $eopts+1 end]
set arglist [lrange $rawargs 0 $eopts-1]
} else {
if {[lsearch $rawargs -*] >= 0} {
#to support option values with leading dash e.g -offset -1 , we can't just take the last flagindex
set i 0
foreach {k v} $rawargs {
if {![string match -* $k]} {
break
}
if {$i+1 >= [llength $rawargs]} {
#no value for last flag
error "bad options for $caller. No value supplied for last option $k"
}
incr i 2
}
set arglist [lrange $rawargs 0 $i-1]
set values [lrange $rawargs $i end]
} else {
set values $rawargs ;#no -flags detected
set arglist [list]
}
}
#confirm any valnames before last don't have -multiple key
foreach valname [lrange $value_names 0 end-1] {
if {[dict exists $arg_info $valname -multiple ]} {
error "bad key -multiple on argument spec for '$valname'. Only the last value argument specification can be marked -multiple"
}
}
set values_dict [dict create]
set validx 0
set in_multiple ""
foreach valname $value_names val $values {
if {$validx+1 > [llength $values]} {
break
}
if {$valname ne ""} {
if {[dict exists $arg_info $valname -multiple] && [dict get $arg_info $valname -multiple]} {
dict lappend values_dict $valname $val
set in_multiple $valname
} else {
dict set values_dict $valname $val
}
} else {
if {$in_multiple ne ""} {
dict lappend values_dict $in_multiple $val
} else {
dict set values_dict $validx $val
}
}
incr validx
}
if {$opt_maxvalues == -1} {
#only check min
if {[llength $values] < $opt_minvalues} {
error "bad number of trailing values for $caller. Got [llength $values] values. Expected at least $opt_minvalues"
}
} else {
if {[llength $values] < $opt_minvalues || [llength $values] > $opt_maxvalues} {
if {$opt_minvalues == $opt_maxvalues} {
error "bad number of trailing values for $caller. Got [llength $values] values. Expected exactly $opt_minvalues"
} else {
error "bad number of trailing values for $caller. Got [llength $values] values. Expected between $opt_minvalues and $opt_maxvalues inclusive"
}
}
}
#opts explicitly marked as -optional 0 must be present - regardless of -anyopts (which allows us to ignore additional opts to pass on to next call)
#however - if -anyopts is true, there is a risk we will treat a shortened option name as matching our default - when it was intended for the next call
#We will always require exact matches for all required opts to avoid this risk, even though an ultimately-called function may not require the full-length option-name REVIEW
#The aim is to allow a wrapper function to specify a default value for an option (and/or other changes/restrictions to a particular option or two) - without having to re-specify all the options for the underlying function.
#without full respecification in the wrapper - we can't know that a supplied prefix is unambiguous at the next level
#For this reason we need to pass on full-length opts for any defined options in the wrapper even if anyopts is true
foreach r $required_opts {
if {$r ni [dict keys $arglist]} {
error "Required option missing for $caller. '$r' is marked with -optional false - so must be present in its full-length form"
}
}
foreach r $required_vals {
if {$r ni [dict keys $values_dict]} {
error "Required value missing for $caller. '$r' is marked with -optional false - so must be present"
}
}
if {!$opt_anyopts} {
set checked_args [dict create]
for {set i 0} {$i < [llength $arglist]} {incr i} {
#allow this to error out with message indicating expected flags
set val [lindex $arglist $i+1]
set fullopt [tcl::prefix match -message "options for $caller. Unexpected option" [dict keys $arg_info] [lindex $arglist $i]]
if {[dict exists $arg_info $fullopt -multiple] && [dict get $arg_info $fullopt -multiple]} {
dict lappend checked_args $fullopt $val
} else {
dict set checked_args $fullopt $val
}
incr i ;#skip val
}
} else {
#still need to use tcl::prefix match to normalize - but don't raise an error
set checked_args [dict create]
dict for {k v} $arglist {
if {![catch {tcl::prefix::match [dict keys $arg_info] $k} fullopt]} {
if {[dict exists $arg_info $fullopt -multiple] && [dict get $arg_info $fullopt -multiple]} {
dict lappend checked_args $fullopt $v
} else {
dict set checked_args $fullopt $v
}
} else {
#opt was unspecified
dict set checked_args $k $v
}
}
}
set opts [dict merge $defaults_dict $checked_args]
#assert - checked_args keys are full-length option names if -anyopts was false or if the supplied option as a shortname matched one of our defined options
#todo - allow defaults outside of choices/ranges
#check types,ranges,choices
set opts_and_values [concat $opts $values_dict]
dict for {o v} $opts_and_values {
if {[dict exists $arg_info $o -multiple] && [dict get $arg_info $o -multiple]} {
set vlist $v
} else {
set vlist [list $v]
}
if {[dict exists $arg_info $o -validate_without_ansi] && [dict get $arg_info $o -validate_without_ansi]} {
set validate_without_ansi 1
package require punk::ansi
} else {
set validate_without_ansi 0
}
if {[dict exists $arg_info $o -allow_ansi] && [dict get $arg_info $o -allow_ansi]} {
set allow_ansi 1
} else {
package require punk::ansi
set allow_ansi 0
}
foreach e $vlist {
if {!$allow_ansi} {
if {[punk::ansi::ta::detect $e]} {
error "Option $o for $caller contains ansi - but -allow_ansi is false. Received: '$e'"
}
}
}
set vlist_check [list]
foreach e $vlist {
if {$validate_without_ansi} {
lappend vlist_check [punk::ansi::stripansi $e]
} else {
lappend vlist_check $e
}
}
set is_default 0
foreach e $vlist e_check $vlist_check {
if {[dict exists $defaults_dict $o] && ($e_check eq [dict get $defaults_dict $o])} {
incr is_default
}
}
if {$is_default eq [llength $vlist]} {
set is_default true
}
#we want defaults to pass through - even if they don't pass the checks that would be required for a specified value
#If the caller manually specified a value that happens to match the default - we don't detect that as any different from an unspecified value - Review.
if {!$is_default} {
if {[dict exists $arg_info $o -type]} {
set type [dict get $arg_info $o -type]
if {[string tolower $type] in {int integer double}} {
if {[string tolower $type] in {int integer}} {
foreach e $vlist e_check $vlist_check {
if {![string is integer -strict $e_check]} {
error "Option $o for $caller requires type 'integer'. Received: '$e'"
}
}
} elseif {[string tolower $type] in {double}} {
foreach e $vlist e_check $vlist_check {
if {![string is double -strict $e_check]} {
error "Option $o for $caller requires type 'double'. Received: '$e'"
}
}
}
#todo - small-value double comparisons with error-margin? review
if {[dict exists $arg_info $o -range]} {
lassign [dict get $arg_info $o -range] low high
foreach e $vlist e_check $vlist_check {
if {$e_check < $low || $e_check > $high} {
error "Option $o for $caller must be between $low and $high. Received: '$e'"
}
}
}
} elseif {[string tolower $type] in {bool boolean}} {
foreach e $vlist e_check $vlist_check {
if {![string is boolean -strict $e_check]} {
error "Option $o for $caller requires type 'boolean'. Received: '$e'"
}
}
} elseif {[string tolower $type] in {alnum alpha ascii control digit graph lower print punct space upper wordchar xdigit}} {
foreach e $vlist e_check $vlist_check {
if {![string is [string tolower $type] $e_check]} {
error "Option $o for $caller requires type '[string tolower $type]'. Received: '$e'"
}
}
} elseif {[string tolower $type] in {file directory existingfile existingdirectory}} {
foreach e $vlist e_check $vlist_check {
if {!([string length $e_check]>0 && ![regexp {[\"*?<>\;]} $e_check])} {
error "Option $o for $caller requires type '[string tolower $type]'. Received: '$e' which doesn't look like it could be a file or directory"
}
}
if {[string tolower $type] in {existingfile}} {
foreach e $vlist e_check $vlist_check {
if {![file exists $e_check]} {
error "Option $o for $caller requires type '[string tolower $type]'. Received: '$e' which is not an existing file"
}
}
} elseif {[string tolower $type] in {existingdirectory}} {
foreach e $vlist e_check $vlist_check {
if {![file isdirectory $e_check]} {
error "Option $o for $caller requires type '[string tolower $type]'. Received: '$e' which is not an existing directory"
}
}
}
} elseif {[string tolower $type] in {char character}} {
foreach e $vlist e_check $vlist_check {
if {[string length != 1]} {
error "Option $o for $caller requires type '[string tolower $type]'. Received: '$e' which is not a single character"
}
}
}
}
if {[dict exists $arg_info $o -choices]} {
set choices [dict get $arg_info $o -choices]
set nocase [dict get $arg_info $o -nocase]
foreach e $vlist e_check $vlist_check {
if {$nocase} {
set casemsg "(case insensitive)"
set choices_test [string tolower $choices]
set v_test [string tolower $e_check]
} else {
set casemsg "(case sensitive)"
set v_test $e_check
set choices_test $choices
}
if {$v_test ni $choices_test} {
error "Option $o for $caller must be one of the listed values $choices $casemsg. Received: '$e'"
}
}
}
}
if {[dict exists $arg_info $o -strip_ansi] && [dict get $arg_info $o -strip_ansi]} {
set stripped_list [list]
foreach e $vlist {
lappend stripped_list [punk::ansi::stripansi $e]
}
if {[dict exists $arg_info $o -multiple] && [dict get $arg_info $o -multiple]} {
if {[dict get $arg_info $o -ARGTYPE] eq "option"} {
dict set opts $o $stripped_list
} else {
dict set values_dict $o $stripped_list
}
} else {
if {[dict get $arg_info $o -ARGTYPE] eq "option"} {
dict set opts $o [lindex $stripped_list 0]
} else {
dict set values_dict [lindex $stripped_list 0]
}
}
}
}
#maintain order of opts $opts values $values as caller may use lassign.
return [dict create opts $opts values $values_dict]
}
#proc sample1 {p1 args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [opt {?option value...?}]]
# #[para]Description of sample1
# return "ok"
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::args ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::args::lib {
namespace export *
namespace path [namespace parent]
#*** !doctools
#[subsection {Namespace punk::args::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {option value...}]]
# #[para]Description of utility1
# return 1
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::args::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval punk::args::system {
#*** !doctools
#[subsection {Namespace punk::args::system}]
#[para] Internal functions that are not part of the API
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::args [namespace eval punk::args {
variable pkg punk::args
variable version
set version 999999.0a1.0
}]
return
#*** !doctools
#[manpage_end]

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

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

39
src/modules/punk/cap-999999.0a1.0.tm

@ -264,7 +264,13 @@ namespace eval punk::cap {
if {$cn ne $capname} {
continue
}
set do_register [$capreg pkg_register $pkg $capdict $fullcapabilitylist]
if {[catch {$capreg pkg_register $pkg $capdict $fullcapabilitylist} do_register]} {
puts stderr "punk::cap::register_capabilityname '$capname' '$capnamespace' failed to register provider package '$pkg' - possible error in handler or provider"
puts stderr "error message:"
puts stderr $do_register
set do_register 0
}
set list_accepted [dict get $pkgcapsaccepted $pkg]
if {$do_register} {
if {$capspec ni $list_accepted} {
@ -339,7 +345,7 @@ namespace eval punk::cap {
}
#register package with arbitrary capnames from capabilitylist
#The registered pkg is a module that provides some service to that capname. Possibly just data members, that the capability will use.
#The registered pkg is a module that provides some service to that capname. Possibly just data members or possibly an implementation of an API, that the capability will use.
proc register_package {pkg capabilitylist args} {
variable pkgcapsdeclared
variable pkgcapsaccepted
@ -372,6 +378,10 @@ namespace eval punk::cap {
#for each capability
# - ensure 1st element is a single word
# - ensure that if 2nd element (capdict) is present - it is dict shaped
set capabilitylist_count [llength $capabilitylist]
set accepted_count 0
set errorlist [list];# list of dicts
set warninglist [list]
foreach capspec $capabilitylist {
lassign $capspec capname capdict
@ -381,16 +391,23 @@ namespace eval punk::cap {
}
}
if {[llength $capname] !=1} {
error "register_package error. pkg: '$pkg' An entry in the capability list doesn't appear to have a single-word name. Problematic entry:'$capspec'"
puts stderr "register_package error. pkg: '$pkg' An entry in the capability list doesn't appear to have a single-word name. Problematic entry:'$capspec'"
set reason "First element of capspec not a single-word name"
lappend errorlist [dict create msg $reason capspec $capspec]
continue
}
if {[expr {[llength $capdict] %2 != 0}]} {
error "register_package error. pkg:'$pkg' The second element for capname:'$capname' doesn't appear to be a valid dict. Problematic entry: '$capspec'"
puts stderr "register_package error. pkg:'$pkg' The second element for capname:'$capname' doesn't appear to be a valid dict. Problematic entry: '$capspec'"
set reason "The second element of the capspec isn't a valid dict"
lappend errorlist [dict create msg $reason capspec $capspec]
continue
}
if {$capspec in $pkg_already_accepted} {
#review - multiple handlers? if so - will need to record which handler(s) accepted the capspec
if {$warnings} {
puts stderr "WARNING: register_package pkg $pkg already has capspec marked as accepted: $capspec"
}
lappend warninglist [dict create msg "pkg $pkg already has this capspec marked as accepted" capspec $capspec]
continue
}
if {[dict exists $caps $capname]} {
@ -426,6 +443,14 @@ namespace eval punk::cap {
} else {
dict set pkgcapsdeclared $pkg $capabilitylist
}
set resultdict [list num_capabilities $capabilitylist_count num_accepted $accepted_count]
if {[llength $errorlist]} {
dict set resultdict errors $errorlist
}
if {[llength $warninglist]} {
dict set resultdict warnings $warninglist
}
return $resultdict
}
#todo!
@ -462,7 +487,7 @@ namespace eval punk::cap {
}
}
proc pkgcap {pkg} {
proc pkgcap {pkg {capsearch}} {
variable pkgcapsdeclared
variable pkgcapsaccepted
if {[string match ::* $pkg]} {
@ -471,9 +496,9 @@ namespace eval punk::cap {
if {[dict exists $pkgcapsdeclared $pkg]} {
set accepted ""
if {[dict exists $pkgcapsaccepted $pkg]} {
set accepted [dict get $pkgcapsaccepted $pkg]
set accepted [lsearch -all -inline -glob -index 0 [dict get $pkgcapsaccepted $pkg] $capsearch]
}
return [dict create declared [dict get $pkgcapsdeclared $pkg] accepted $accepted]
return [dict create declared [lsearch -all -inline -glob -index 0 [dict get $pkgcapsdeclared $pkg] $capsearch] accepted $accepted]
} else {
return
}

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

@ -18,7 +18,7 @@
## Requirements
##e.g package require frobz
package require punk::repo
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
@ -43,39 +43,137 @@ namespace eval punk::cap::handlers::templates {
# -- --- --- --- --- --- --- ---- ---
# validation of capdict
# -- --- --- --- --- --- --- ---- ---
if {![dict exists $capdict relpath]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability, but is missing 'relpath' key"
if {![dict exists $capdict vendor]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability, but is missing the 'vendor' key"
return 0
}
set provide_statement [package ifneeded $pkg [package require $pkg]]
set tmfile [lindex $provide_statement end]
if {![file exists $tmfile]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
if {![dict exists $capdict path] || ![dict exists $capdict pathtype]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability, but is missing the 'path' or 'pathtype' key"
return 0
}
set tpath [file normalize [file join $tmfile [dict get $capdict relpath]]] ;#relpath is relative to the tm *file* - not it's containing folder
if {![file isdirectory $tpath]} {
puts stderr "punk::cap::handlers::templates::capsystem pkg_register WARNING - unable to validate relpath location [dict get $capdict relpath] ($tpath) for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
set pathtype [dict get $capdict pathtype]
set vendor [dict get $capdict vendor]
set known_pathtypes [list currentproject_multivendor currentproject shellproject_multivendor shellproject module absolute]
if {$pathtype ni $known_pathtypes} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability, but 'pathtype' value '$pathtype' is not recognised. Known type: $known_pathtypes"
return 0
}
set path [dict get $capdict path]
set cname [string map [list . _] $capname]
set final_paths [list]
set multivendor_package_whitelist [list punk::mix::templates]
if {$pathtype eq "module"} {
set provide_statement [package ifneeded $pkg [package require $pkg]]
set tmfile [lindex $provide_statement end]
if {![file exists $tmfile]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - unable to determine base folder for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
return 0
}
if {[file pathtype $path] ne "relative"} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but provided a path '$path' of type $pathtype which doesn't seem to be a relative path"
}
set tmfolder [file dirname $tmfile]
#todo - handle wrapped or unwrapped tarjar files - in which case we have to adjust tmfolder appropriately
#set tpath [file normalize [file join $tmfile [dict get $capdict relpath]]] ;#relpath is relative to the tm *file* - not it's containing folder
set extended_capdict $capdict
set resolved_path [file join $tmfolder $path]
dict set extended_capdict resolved_path $resolved_path
} elseif {$pathtype eq "currentproject_multivendor"} {
#currently only intended for punk::mix::templates - review if 3rd party _multivendor trees even make sense
if {$pkg ni $multivendor_package_whitelist} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but package is not in whitelist $multivendor_package_whitelist - 3rd party _multivendor tree not supported"
return 0
}
if {[file pathtype $path] ne "relative"} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but provided a path '$path' of type $pathtype which doesn't seem to be a relative path"
return 0
}
set extended_capdict $capdict
dict set extended_capdict vendor $vendor ;#vendor key still required.. controlling vendor?
} elseif {$pathtype eq "currentproject"} {
if {[file pathtype $path] ne "relative"} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but provided a path '$path' of type $pathtype which doesn't seem to be a relative path"
return 0
}
#verify that the relative path is within the relative path of a currentproject_multivendor tree
#todo - api for the _multivendor tree controlling package to validate
set extended_capdict $capdict
dict set extended_capdict vendor $vendor
} elseif {$pathtype eq "shellproject"} {
if {[file pathtype $path] ne "relative"} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but provided a path '$path' of type $pathtype which doesn't seem to be a relative path"
return 0
}
set extended_capdict $capdict
dict set extended_capdict vendor $vendor
} elseif {$pathtype eq "shellproject_multivendor"} {
#currently only intended for punk::templates - review if 3rd party _multivendor trees even make sense
if {$pkg ni $multivendor_package_whitelist} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but package is not in whitelist $multivendor_package_whitelist - 3rd party _multivendor tree not supported"
return 0
}
if {[file pathtype $path] ne "relative"} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but provided a path '$path' of type $pathtype which doesn't seem to be a relative path"
return 0
}
set extended_capdict $capdict
dict set extended_capdict vendor $vendor
} elseif {$pathtype eq "absolute"} {
if {[file pathtype $path] ne "absolute"} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but provided a path '$path' of type $pathtype which doesn't seem to be absolute"
return 0
}
if {!file exists [file normalize $path]} {
puts stderr "punk::cap::handlers::templates::capsystem::pkg_register WARNING - package '$pkg' is attempting to register with punk::cap as a provider of '$capname' capability but provided a path '$path' which doesn't seem to exist"
return 0
}
#todo - verify no other provider has registered same absolute path - if sharing a project-external location is needed - they need their own subfolder
set extended_capdict $capdict
dict set extended_capdict resolved_path [file normalize $path]
dict set extended_capdict vendor $vendor
}
# -- --- --- --- --- --- --- ---- ---
# update package internal data
# -- --- --- --- --- --- --- ---- ---
upvar ::punk::cap::handlers::templates::provider_info_$cname provider_info
if {$capname ni $::punk::cap::handlers::templates::handled_caps} {
lappend ::punk::cap::handlers::templates::handled_caps $capname
}
set cname [string map [list . _] $capname]
upvar ::punk::cap::handlers::templates::pkg_folders_$cname pfolders
dict lappend pfolders $pkg $tpath
if {![info exists provider_info] || $extended_capdict ni [dict get $provider_info $pkg]} {
#this checks for duplicates from the same provider - but not if other providers already added the path
#review -
dict lappend provider_info $pkg $extended_capdict
}
#we don't check all final_paths - as currentproject* and shellproject* paths are dynamic based on pwd and script respectively
#temporary debug
foreach fp $final_paths {
set norm_path [file normalize $fp]
if {![file isdirectory $norm_path]} {
puts stderr "punk::cap::handlers::templates::capsystem pkg_register WARNING - unable to validate pathtype:$pathtype path:$path ($norm_path) for package '$pkg' which is attempting to register with punk::cap as a provider of '$capname' capability"
}
}
# -- --- --- --- --- --- --- ---- ---
# instantiation of api at punk::cap::handlers::templates::api_$capname
# -- --- --- --- --- --- --- ---- ---
if {[info commands ::punk::cap::handlers::templates::$capname] eq ""} {
punk::cap::handlers::templates::class::api create ::punk::cap::handlers::templates::api_$capname $capname
set apicmd "::punk::cap::handlers::templates::api_$capname"
if {[info commands $apicmd] eq ""} {
punk::cap::handlers::templates::class::api create $apicmd $capname
}
return 1
@ -84,8 +182,8 @@ namespace eval punk::cap::handlers::templates {
upvar ::punk::cap::handlers::templates::handled_caps hcaps
foreach capname $hcaps {
set cname [string map [list . _] $capname]
upvar ::punk::cap::handlers::templates::pkg_folders_$cname pfolders
dict unset pfolders $pkg
upvar ::punk::cap::handlers::templates::provider_info_$cname my_provider_info
dict unset my_provider_info $pkg
#destroy api objects?
}
}
@ -112,17 +210,183 @@ namespace eval punk::cap::handlers::templates {
method folders {} {
variable capabilityname
variable cname
upvar punk::cap::handlers::templates::pkg_folders_$cname pkg_folders
upvar ::punk::cap::handlers::templates::provider_info_$cname my_provider_info
package require punk::cap
set capinfo [punk::cap::capability $capabilityname]
# e.g {punk.templates {handler punk::mix::templates providers ::somepkg}}
#use the order of pkgs as registered with punk::cap - may have been modified with punk::cap::promote_package/demote_package
set pkgs [dict get $capinfo providers]
set providerpkg [dict get $capinfo providers]
set folderdict [dict create]
foreach pkg $pkgs {
foreach pfolder [dict get $pkg_folders $pkg] {
dict set folderdict $pfolder [list source $pkg sourcetype package]
#maintain separate paths for different override levels - all keyed on vendor (or pseudo-vendor '_project')
set found_paths_module [dict create]
set found_paths_currentproject_multivendor [dict create]
set found_paths_currentproject [dict create]
set found_paths_shellproject_multivendor [dict create]
set found_paths_shellproject [dict create]
set found_paths_absolute [list]
foreach pkg $providerpkg {
set found_paths [list]
#set acceptedlist [dict get [punk::cap::pkgcap $pkg $capabilityname] accepted]
foreach capdecl_extended [dict get $my_provider_info $pkg] {
#basic validation and extension was done when accepted - so we can trust the capdecl_extended dictionary has the right entries
set path [dict get $capdecl_extended path]
set pathtype [dict get $capdecl_extended pathtype]
set vendor [dict get $capdecl_extended vendor]
if {$pathtype eq "module"} {
#lappend found_paths [dict get $capdecl_extended resolved_path]
dict lappend found_paths_module $vendor [list pkg $pkg path [dict get $capdecl_extended resolved_path] pathtype $pathtype]
} elseif {$pathtype eq "currentproject_multivendor"} {
set searchbase [pwd]
set pathinfo [punk::repo::find_repos $searchbase]
set pwd_projectroot [dict get $pathinfo closest]
if {$pwd_projectroot ne ""} {
set deckbase [file join $pwd_projectroot $path]
if {![file exists $deckbase]} {
continue
}
#add vendor/x folders first - earlier in list is lower priority
set vendorbase [file join $deckbase vendor]
if {[file isdirectory $vendorbase]} {
set vendorfolders [glob -nocomplain -dir $vendorbase -type d -tails *]
foreach vf $vendorfolders {
if {$vf ne "_project"} {
#lappend found_paths [file join $vendorbase $vf]
dict lappend found_paths_currentproject_multivendor $vf [list pkg $pkg path [file join $vendorbase $vf] pathtype $pathtype]
}
}
if {[file isdirectory [file join $vendorbase _project]]} {
#lappend found_paths [file join $vendorbase _project]
dict lappend found_paths_currentproject_multivendor _project [list pkg $pkg path [file join $vendorbase _project] pathtype $pathtype]
}
}
set custombase [file join $deckbase custom]
if {[file isdirectory $custombase]} {
set customfolders [glob -nocomplain -dir $custombase -type d -tails *]
foreach cf $customfolders {
if {$cf ne "_project"} {
#lappend found_paths [file join $custombase $cf]
dict lappend found_paths_currentproject_multivendor $cf [list pkg $pkg path [file join $custombase $cf] pathtype $pathtype]
}
}
if {[file isdirectory [file join $custombase _project]]} {
#lappend found_paths [file join $custombase _project]
dict lappend found_paths_currentproject_multivendor _project [list pkg $pkg path [file join $custombase _project] pathtype $pathtype]
}
}
}
} elseif {$pathtype eq "currentproject"} {
set searchbase [pwd]
set pathinfo [punk::repo::find_repos $searchbase]
set pwd_projectroot [dict get $pathinfo closest]
if {$pwd_projectroot ne ""} {
#path relative to projectroot already validated by handler as being within a currentproject_multivendor tree
set targetfolder [file join $pwd_projectroot $path]
if {[file isdirectory $targetfolder]} {
#lappend found_paths $targetfolder
dict lappend found_paths_currentproject $vendor [list pkg $pkg path $targetfolder pathtype $pathtype]
}
}
} elseif {$pathtype eq "shellproject_multivendor"} {
#review - consider also [info script] - but it can be empty if we just start a tclsh, load packages and start a repl
set shellbase [file dirname [file dirname [file normalize [set ::argv0]/__]]] ;#review
set pathinfo [punk::repo::find_repos $shellbase]
set pwd_projectroot [dict get $pathinfo closest]
if {$pwd_projectroot ne ""} {
set deckbase [file join $pwd_projectroot $path]
if {![file exists $deckbase]} {
continue
}
#add vendor/x folders first - earlier in list is lower priority
set vendorbase [file join $deckbase vendor]
if {[file isdirectory $vendorbase]} {
set vendorfolders [glob -nocomplain -dir $vendorbase -type d -tails *]
foreach vf $vendorfolders {
if {$vf ne "_project"} {
dict lappend found_paths_shellproject_multivendor $vf [list pkg $pkg path [file join $vendorbase $vf] pathtype $pathtype]
}
}
if {[file isdirectory [file join $vendorbase _project]]} {
dict lappend found_paths_shellproject_multivendor _project [list pkg $pkg path [file join $vendorbase _project] pathtype $pathtype]
}
}
set custombase [file join $deckbase custom]
if {[file isdirectory $custombase]} {
set customfolders [glob -nocomplain -dir $custombase -type d -tails *]
foreach cf $customfolders {
if {$cf ne "_project"} {
dict lappend found_paths_shellproject_multivendor $cf [list pkg $pkg path [file join $custombase $cf] pathtype $pathtype]
}
}
if {[file isdirectory [file join $custombase _project]]} {
dict lappend found_paths_shellproject_multivendor _project [list pkg $pkg path [file join $custombase _project] pathtype $pathtype]
}
}
}
} elseif {$pathtype eq "shellproject"} {
#review - consider also [info script] - but it can be empty if we just start a tclsh, load packages and start a repl
set shellbase [file dirname [file dirname [file normalize [set ::argv0]/__]]] ;#review
set pathinfo [punk::repo::find_repos $shellbase]
set pwd_projectroot [dict get $pathinfo closest]
if {$pwd_projectroot ne ""} {
set targetfolder [file join $pwd_projectroot $path]
if {[file isdirectory $targetfolder]} {
dict lappend found_paths_shellproject $vendor [list pkg $pkg path $targetfolder pathtype $pathtype]
}
}
} elseif {$pathtype eq "absolute"} {
#lappend found_paths [dict get $capdecl_extended resolved_path]
dict lappend found_paths_absolute $vendor [list pkg $pkg path [dict get $capdecl_extended resolved_path] pathtype $pathtype]
}
}
#todo - ensure vendor pkg capdict elements such source and allowupdates override any existing entry from a _multivendor pkg?
#currently relying on order in which loaded? review
#foreach pfolder $found_paths {
# dict set folderdict $pfolder [list source $pkg sourcetype package]
#}
}
#add in order of preference low priority to high
dict for {vendor pathinfolist} $found_paths_module {
foreach pathinfo $pathinfolist {
dict set folderdict [dict get $pathinfo path] [list source [dict get $pathinfo pkg] sourcetype package pathtype [dict get $pathinfo pathtype] vendor $vendor]
}
}
#Templates within project of shell we launched with has lower priority than 'currentproject' (which depends on our CWD)
dict for {vendor pathinfolist} $found_paths_shellproject_multivendor {
foreach pathinfo $pathinfolist {
dict set folderdict [dict get $pathinfo path] [list source [dict get $pathinfo pkg] sourcetype package pathtype [dict get $pathinfo pathtype] vendor $vendor]
}
}
dict for {vendor pathinfolist} $found_paths_shellproject {
foreach pathinfo $pathinfolist {
dict set folderdict [dict get $pathinfo path] [list source [dict get $pathinfo pkg] sourcetype package pathtype [dict get $pathinfo pathtype] vendor $vendor]
}
}
dict for {vendor pathinfolist} $found_paths_currentproject_multivendor {
foreach pathinfo $pathinfolist {
dict set folderdict [dict get $pathinfo path] [list source [dict get $pathinfo pkg] sourcetype package pathtype [dict get $pathinfo pathtype] vendor $vendor]
}
}
dict for {vendor pathinfolist} $found_paths_currentproject {
foreach pathinfo $pathinfolist {
dict set folderdict [dict get $pathinfo path] [list source [dict get $pathinfo pkg] sourcetype package pathtype [dict get $pathinfo pathtype] vendor $vendor]
}
}
dict for {vendor pathinfolist} $found_paths_absolute {
foreach pathinfo $pathinfolist {
dict set folderdict [dict get $pathinfo path] [list source [dict get $pathinfo pkg] sourcetype package pathtype [dict get $pathinfo pathtype] vendor $vendor]
}
}
return $folderdict

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

@ -1739,8 +1739,11 @@ namespace eval punk::char {
#review - what about \r \t \b ?
proc string_width {text} {
#review is detecting \033 enough? what about 8-bit escapes?
if {[string first \n $text] > 0 || [string first \033 $text] > 0} {
error "string_width accepts only a single line and no ansi escape sequences"
if {[string first \n $text] >= 0} {
error "string_width accepts only a single line"
}
if {[string first \033 $text] >= 0} {
error "string_width doesn't accept ansi escape sequences. Use punk::ansi::stripansi first"
}
#todo - check double-width chars in unicode blocks.. try to do reasonably quicky
#short-circuit basic cases

2
src/modules/punk/du-999999.0a1.0.tm

@ -1250,7 +1250,7 @@ namespace eval punk::du {
#experiment with csv as easy way to get column like format..
#The /r is somewhat cheating however.. as it messes up redirected output .. e.g if redirected to text file
interp alias {} du {} .=args>* punk::du::du |> .=>1 natsort::sort -cols 1 -outputformat csv -outputformatoptions {\r\t\t\t} |> list_as_lines <args|
interp alias {} du {} .=args>* punk::du::du |> .=>1 natsort::sort -cols 1 -outputformat csv -outputformatoptions {\r\t\t\t} |> list_as_lines -- <args|
}

44
src/modules/punk/fileline-999999.0a1.0.tm

@ -59,8 +59,10 @@
#[list_begin itemized]
package require Tcl 8.6
package require punk::args
#*** !doctools
#[item] [package {Tcl 8.6}]
#[item] [package {punk::args}]
# #package require frobz
@ -1197,18 +1199,38 @@ namespace eval punk::fileline {
#[para] Core API functions for punk::fileline
#[list_begin definitions]
proc file_textinfo {filename} {
set fd [open $filename r] ;#open gives a good enough error message if file not readable
fconfigure $fd -translation binary
set rawfiledata [read $fd]
close $fd
set textobj [class::textinfo new $rawfiledata]
puts stdout "Bytes loaded : [$textobj chunklen]"
puts stdout "Lines recognised : [$textobj linecount]"
proc get_textinfo {args} {
set defaults {
-file -default {} -type existingfile
-translation -default binary
}
lassign [dict values [punk::args::opts_values $defaults $args -minvalues 0 -maxvalues 1]] opts values
# -- --- --- ---
set opt_file [dict get $opts -file]
set opt_translation [dict get $opts -translation]
# -- --- --- ---
if {$opt_file ne ""} {
set filename $opt_file
set fd [open $filename r]
fconfigure $fd -translation $opt_translation
set datachunk [read $fd]
close $fd
if {[llength $values]} {
puts stderr "Ignoring trailing argument [string length [lindex $values 0]] bytes. Not used when -file is specified"
}
} else {
set datachunk [lindex $values 0]
}
set textobj [class::textinfo new $datachunk]
set summary ""
append summary "Bytes loaded : [$textobj chunklen]" \n
append summary "Lines recognised : [$textobj linecount]" \n
set leinfo [$textobj chunk_le_counts 0 end]
puts stdout "crlf endings (windows) : [dict get $leinfo crlf]"
puts stdout "lf endings (unix) : [dict get $leinfo crlf]"
puts stdout "unterminated lines : [dict get $leinfo unterminated]"
append summary "crlf endings (windows) : [dict get $leinfo crlf]" \n
append summary "lf endings (unix) : [dict get $leinfo lf]" \n
append summary "unterminated lines : [dict get $leinfo unterminated]" \n
puts stdout $summary
return $textobj
}

541
src/modules/punk/flib-999999.0a1.0.tm

@ -0,0 +1,541 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2024
#
# @@ Meta Begin
# Application punk::flib 999999.0a1.0
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::flib 0 999999.0a1.0]
#[copyright "2024"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
#[require punk::flib]
#[keywords module]
#[description]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Overview]
#[para] overview of punk::flib
#[subsection Concepts]
#[para] -
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[subsection dependencies]
#[para] packages used by punk::flib
#[list_begin itemized]
package require Tcl 8.6
#*** !doctools
#[item] [package {Tcl 8.6}]
# #package require frobz
# #*** !doctools
# #[item] [package {frobz}]
#*** !doctools
#[list_end]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section API]
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# oo::class namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::flib::class {
#*** !doctools
#[subsection {Namespace punk::flib::class}]
#[para] class definitions
if {[info commands [namespace current]::interface_sample1] eq ""} {
#*** !doctools
#[list_begin enumerated]
# oo::class create interface_sample1 {
# #*** !doctools
# #[enum] CLASS [class interface_sample1]
# #[list_begin definitions]
# method test {arg1} {
# #*** !doctools
# #[call class::interface_sample1 [method test] [arg arg1]]
# #[para] test method
# puts "test: $arg1"
# }
# #*** !doctools
# #[list_end] [comment {-- end definitions interface_sample1}]
# }
#*** !doctools
#[list_end] [comment {--- end class enumeration ---}]
}
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Base namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::flib {
namespace export *
#variable xyz
#*** !doctools
#[subsection {Namespace punk::flib}]
#[para] Core API functions for punk::flib
#[list_begin definitions]
#proc sample1 {p1 args} {
# #*** !doctools
# #[call [fun sample1] [arg p1] [opt {?option value...?}]]
# #[para]Description of sample1
# return "ok"
#}
>pattern .. Create >libpattern ;#clone to a library factory
>libpattern .. Construct {args} {
var o_this
set o_this @this@
var o_last_child
set o_last_child ""
}
>libpattern .. Method version {} {
return 1.0.0
}
>libpattern .. Method aliasprefix {pfx} {
var o_this
var o_last_child
if {![string length $o_last_child]} {
error " . aliasprefix - Create library object with . new >somename first."
}
set patternmethods [$o_this .. PM]
set aliases [list]
foreach m $patternmethods {
set a ${pfx}${m}
if {[llength [info commands $a]]} {
puts stderr "WARNING - a command was already present at: $a"
}
interp alias "" $a "" [$o_last_child . $m .]
lappend aliases $a
}
return $aliases
}
>libpattern .. Method new {objcmdname} {
var o_this
set o_this @this@
var o_last_child
set nscaller [uplevel 1 [list namespace current]]
if {![string match ::* $objcmdname]} {
if {$nscaller eq "::"} {set nscaller ""}
set objcmdname ${nscaller}::$objcmdname
}
uplevel 1 [list $o_this .. Create $objcmdname]
set o_last_child $objcmdname
}
>libpattern .. Constructor {args} {
var o_this
set o_this @this@
}
>libpattern .. Clone >ls_lib
>ls_lib .. PatternMethod tail {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve tail on an empty input list" ">ls_lib . tail $args" [list argumenterror tail empty_list]
}
lrange $args 1 end
}
>ls_lib .. PatternMethod init {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve init on an empty input list" ">ls_lib . init $args" [list argumenterror init empty_list]
}
lrange $args 0 end-1
}
>ls_lib .. PatternMethod head {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve head on an empty input list" ">ls_lib . head $args" [list argumenterror head empty_list]
}
lindex $args 0
}
>ls_lib .. PatternMethod last {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve last on an empty input list. Use li.index end to avoid list length check" ">ls_lib . last $args" [list argumenterror last empty_list]
}
lindex $args end
}
>ls_lib .. PatternMethod elem {val args} {
expr {$val in $args}
}
>ls_lib .. PatternMethod index {idx args} {
lindex $args $idx
}
>ls_lib .. PatternMethod range {s e args} {
lrange $args $s $e
}
#take/drop - haskell-like - but no lazy support REVIEW
#see also https://www.haskellforall.com/2022/05/why-does-haskells-take-function-accept.html
>ls_lib .. PatternMethod take {n args} {
#keep basic behaviour like Haskell ie we allow returning less than n (without error) if insufficient elements
lrange $args 0 $n-1
}
>ls_lib .. PatternMethod drop {n args} {
lrange $args $n end
}
>ls_lib . new >ls
>ls_lib . aliasprefix "ls."
#list item lib
>libpattern .. Clone >li_lib
>li_lib .. PatternMethod tail {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve tail on an empty input list" ">li_lib . tail $listdata" [list argumenterror tail empty_list]
}
lrange $listdata 1 end
}
>li_lib .. PatternMethod init {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve init on an empty input list" ">li_lib . init $listdata" [list argumenterror init empty_list]
}
lrange $listdata 0 end-1
}
>li_lib .. PatternMethod head {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve head on an empty input list" ">li_lib . head $listdata" [list argumenterror head empty_list]
}
lindex $listdata 0
}
>li_lib .. PatternMethod last {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve last on an empty input list. Use li.index end to avoid list length check" ">li_lib . last $listdata" [list argumenterror last empty_list]
}
lindex $listdata end
}
>li_lib .. PatternMethod elem {val listdata} {
expr {$val in $listdata}
}
>li_lib .. PatternMethod index {idx listdata} {
lindex $listdata $idx
}
>li_lib .. PatternMethod range {s e listdata} {
lrange $listdata $s $e
}
#take/drop - haskell-like - but no lazy support REVIEW
#see also https://www.haskellforall.com/2022/05/why-does-haskells-take-function-accept.html
>li_lib .. PatternMethod take {n listdata} {
#keep basic behaviour like Haskell ie we allow returning less than n (without error) if insufficient elements
lrange $listdata 0 $n-1
}
>li_lib .. PatternMethod drop {n listdata} {
lrange $listdata $n end
}
#todo - takeWhile, dropWhile, takeWhileEnd, dropWhileEnd
>li_lib .. PatternMethod is_list_all_in_list {a b} {
package require struct::list
package require struct::set
set a_in_b [lsort [struct::set intersect [lsort -unique $a] $b ]]
return [struct::list equal [lsort $a] $a_in_b]
}
>li_lib .. PatternMethod is_list_all_ni_list {a b} {
package require struct::set
set i [struct::set intersect $a $b]
return [expr {[llength $i] == 0}]
}
>li_lib . new >li
>li_lib . aliasprefix "li."
>pattern .. Create >f_lib
>f_lib .. Construct {args} {
var o_this
set o_this @this@
var o_last_child
set o_last_child ""
}
>f_lib .. Method version {} {
return 1.0.0
}
>f_lib .. Method aliasprefix {pfx} {
var o_this
var o_last_child
if {![string length $o_last_child]} {
error ">f_lib . aliasprefix - Create library object with >f_lib . new >somename first."
}
set patternmethods [$o_this .. PM]
set aliases [list]
foreach m $patternmethods {
set a ${pfx}${m}
if {[llength [info commands $a]]} {
puts stderr "WARNING - a command was already present at: $a"
}
interp alias "" $a "" [$o_last_child . $m .]
lappend aliases $a
}
return $aliases
}
>f_lib .. Method new {objcmdname} {
var o_this
var o_last_child
set nscaller [uplevel 1 [list namespace current]]
if {![string match ::* $objcmdname]} {
if {$nscaller eq "::"} {set nscaller ""}
set objcmdname ${nscaller}::$objcmdname
}
uplevel 1 [list $o_this .. Create $objcmdname]
set o_last_child $objcmdname
}
>f_lib .. Constructor {args} {
var o_this
set o_this @this@
}
>f_lib .. PatternMethod foldl {total func sequence} {
struct::list::Lfold $sequence $total $func
}
#note: foldr is not equivalent to just doing a foldl on the reversed list
#todo - review/test/fix
>f_lib .. PatternMethod foldr {total func sequence} {
set this @this@
if {![llength $sequence]} {
return $total
}
v,h@head,t@tail.=val $sequence |h@head,t@tail> {
puts "-->$h"
$func [$this . foldr $total $func $t] $h
} <this@,func@,total@| $this $func $total
return 0
return $v
}
# reduce: simplest case - list of numbers - reduce with +
# more complex case: list of pipelines (e.g parsers) - reduce with 'andThen' operator of some sort.
>f_lib .. PatternMethod reduce {func sequence} {
struct::list::Lfold [lrange $sequence 1 end] [lindex $sequence 0] $func
}
>f_lib .. PatternMethod list_map {commandlist list} {
tailcall lmap item $list $commandlist
}
>f_lib .. PatternMethod list_unique {args} {
set list [concat {*}$args]
set d [dict create]
foreach item $list {
dict set d $item ""
}
dict keys $d
}
>f_lib .. PatternMethod list_as_lines {args} {
set list [concat {*}$args]
join $list \n
}
>f_lib .. PatternMethod list_filter_cond {itemcond listval} {
#maintenance - proc list_filter_cond
set filtered_list [list]
set binding {}
if {[info level] == 1} {
#up 1 is global
set get_vars [list info vars]
} else {
set get_vars [list info locals]
}
set vars [uplevel 1 {*}$get_vars]
set posn [lsearch $vars item]
set vars [lreplace $vars $posn $posn]
foreach v $vars {
upvar 1 $v var
if {(![array exists var]) && [info exists var]} {
lappend binding [list $v $var] ;#values captured as defaults for apply args.
}
}
#lappend binding [list item $args]
#puts stderr "binding: [join $binding \n]"
#apply [list $binding $pipescript [uplevel 1 namespace current]]
foreach item $listval {
set bindlist [list {*}$binding [list item $item]]
if {[apply [list $bindlist $itemcond [uplevel 1 namespace current]] ]} {
lappend filtered_list $item
}
}
return $filtered_list
}
>f_lib .. PatternMethod sum_llength {total listval} {
expr {$total + [llength $listval]}
}
>f_lib .. PatternMethod sum_length {total stringval} {
expr {$total + [string length $stringval]}
}
>f_lib .. PatternMethod debug {total item} {
puts stderr "incr tally: $total item: $item"
expr {$total + 1}
}
>f_lib .. PatternMethod dict_walk {d key} {
dict get $d $key
}
>f_lib .. PatternMethod sum {total num} {
expr {$total + $num}
}
>f_lib .. PatternMethod lcomp {expression args} {
#from https://wiki.tcl-lang.org/page/lcomp
set __0__ "lappend __1__ \[expr [list $expression]\]"
while {[llength $args] && [lindex $args 0] ni {for if with}} {
append __0__ " \[expr [list [lindex $args 0]]\]"
set args [lrange $args 1 end]
}
set tmpvar 2
set structure {}
set upvars {}
while {[llength $args]} {
set prefix ""
switch [lindex $args 0] {
for {
set nest [list foreach]
while {[llength $nest] == 1 || [lindex $args 0] eq "and"} {
if {[llength $args] < 4 || [lindex $args 2] ni {in inside}} {
error "wrong # operands: must be \"for\" vars \"in?side?\"\
vals ?\"and\" vars \"in?side?\" vals? ?...?"
}
switch [lindex $args 2] {
in {
lappend nest [lindex $args 1] [lindex $args 3]
} inside {
lappend nest __${tmpvar}__ [lindex $args 3]
append prefix "lassign \$__${tmpvar}__ [lindex $args 1]\n"
incr tmpvar
}}
set args [lrange $args 4 end]
}
lappend structure $nest $prefix
} if {
if {[llength $args] < 2} {
error "wrong # operands: must be \"if\" condition"
}
lappend structure [list if [lindex $args 1]] $prefix
set args [lrange $args 2 end]
} with {
if {[llength $args] < 2} {
error "wrong # operands: must be \"with\" varlist"
}
foreach var [lindex $args 1] {
lappend upvars $var $var
}
set args [lrange $args 2 end]
} default {
error "bad opcode \"[lindex $args 0]\": must be for, if, or with"
}}
}
foreach {prefix nest} [lreverse $structure] {
set __0__ [concat $nest [list \n$prefix$__0__]]
}
if {[llength $upvars]} {
set __0__ "upvar 1 $upvars; $__0__"
}
unset -nocomplain expression args tmpvar prefix nest structure var upvars
set __1__ ""
eval $__0__
return $__1__
}
>f_lib . new ::punk::lib::>f
>f_lib . aliasprefix "f."
interp alias {} >f {} ::punk::lib::>f
#Pattern-matching based functional operations
>pattern .. Create >P
>P .. Method map {pattern commandlist sequence} {
#set segment [string map [list <cmd> $commandlist] {<cmd>}]
set pipeline [list % {val $item} "|,item,$pattern>" $commandlist <item/0|]
tailcall % list $pipeline $sequence |p/0,l/1> {lmap val $l {{*}$p $val }}
}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::flib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# Secondary API namespace
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::flib::lib {
namespace export *
namespace path [namespace parent]
#*** !doctools
#[subsection {Namespace punk::flib::lib}]
#[para] Secondary functions that are part of the API
#[list_begin definitions]
#proc utility1 {p1 args} {
# #*** !doctools
# #[call lib::[fun utility1] [arg p1] [opt {?option value...?}]]
# #[para]Description of utility1
# return 1
#}
#*** !doctools
#[list_end] [comment {--- end definitions namespace punk::flib::lib ---}]
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[section Internal]
namespace eval punk::flib::system {
#*** !doctools
#[subsection {Namespace punk::flib::system}]
#[para] Internal functions that are not part of the API
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::flib [namespace eval punk::flib {
variable pkg punk::flib
variable version
set version 999999.0a1.0
}]
return
#*** !doctools
#[manpage_end]

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

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

368
src/modules/punk/lib-0.1.tm

@ -4,373 +4,7 @@ package provide punk::lib [namespace eval punk::lib {
}]
namespace eval punk::lib {
>pattern .. Create >libpattern ;#clone to a library factory
>libpattern .. Construct {args} {
var o_this
set o_this @this@
var o_last_child
set o_last_child ""
}
>libpattern .. Method version {} {
return 1.0.0
}
>libpattern .. Method aliasprefix {pfx} {
var o_this
var o_last_child
if {![string length $o_last_child]} {
error " . aliasprefix - Create library object with . new >somename first."
}
set patternmethods [$o_this .. PM]
set aliases [list]
foreach m $patternmethods {
set a ${pfx}${m}
if {[llength [info commands $a]]} {
puts stderr "WARNING - a command was already present at: $a"
}
interp alias "" $a "" [$o_last_child . $m .]
lappend aliases $a
}
return $aliases
}
>libpattern .. Method new {objcmdname} {
var o_this
set o_this @this@
var o_last_child
set nscaller [uplevel 1 [list namespace current]]
if {![string match ::* $objcmdname]} {
if {$nscaller eq "::"} {set nscaller ""}
set objcmdname ${nscaller}::$objcmdname
}
uplevel 1 [list $o_this .. Create $objcmdname]
set o_last_child $objcmdname
}
>libpattern .. Constructor {args} {
var o_this
set o_this @this@
}
>libpattern .. Clone >ls_lib
>ls_lib .. PatternMethod tail {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve tail on an empty input list" ">ls_lib . tail $args" [list argumenterror tail empty_list]
}
lrange $args 1 end
}
>ls_lib .. PatternMethod init {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve init on an empty input list" ">ls_lib . init $args" [list argumenterror init empty_list]
}
lrange $args 0 end-1
}
>ls_lib .. PatternMethod head {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve head on an empty input list" ">ls_lib . head $args" [list argumenterror head empty_list]
}
lindex $args 0
}
>ls_lib .. PatternMethod last {args} {
if {![llength $args]} {
error "argumenterror cannot retrieve last on an empty input list. Use li.index end to avoid list length check" ">ls_lib . last $args" [list argumenterror last empty_list]
}
lindex $args end
}
>ls_lib .. PatternMethod elem {val args} {
expr {$val in $args}
}
>ls_lib .. PatternMethod index {idx args} {
lindex $args $idx
}
>ls_lib .. PatternMethod range {s e args} {
lrange $args $s $e
}
#take/drop - haskell-like - but no lazy support REVIEW
#see also https://www.haskellforall.com/2022/05/why-does-haskells-take-function-accept.html
>ls_lib .. PatternMethod take {n args} {
#keep basic behaviour like Haskell ie we allow returning less than n (without error) if insufficient elements
lrange $args 0 $n-1
}
>ls_lib .. PatternMethod drop {n args} {
lrange $args $n end
}
>ls_lib . new >ls
>ls_lib . aliasprefix "ls."
#list item lib
>libpattern .. Clone >li_lib
>li_lib .. PatternMethod tail {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve tail on an empty input list" ">li_lib . tail $listdata" [list argumenterror tail empty_list]
}
lrange $listdata 1 end
}
>li_lib .. PatternMethod init {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve init on an empty input list" ">li_lib . init $listdata" [list argumenterror init empty_list]
}
lrange $listdata 0 end-1
}
>li_lib .. PatternMethod head {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve head on an empty input list" ">li_lib . head $listdata" [list argumenterror head empty_list]
}
lindex $listdata 0
}
>li_lib .. PatternMethod last {listdata} {
if {![llength $listdata]} {
error "argumenterror cannot retrieve last on an empty input list. Use li.index end to avoid list length check" ">li_lib . last $listdata" [list argumenterror last empty_list]
}
lindex $listdata end
}
>li_lib .. PatternMethod elem {val listdata} {
expr {$val in $listdata}
}
>li_lib .. PatternMethod index {idx listdata} {
lindex $listdata $idx
}
>li_lib .. PatternMethod range {s e listdata} {
lrange $listdata $s $e
}
#take/drop - haskell-like - but no lazy support REVIEW
#see also https://www.haskellforall.com/2022/05/why-does-haskells-take-function-accept.html
>li_lib .. PatternMethod take {n listdata} {
#keep basic behaviour like Haskell ie we allow returning less than n (without error) if insufficient elements
lrange $listdata 0 $n-1
}
>li_lib .. PatternMethod drop {n listdata} {
lrange $listdata $n end
}
#todo - takeWhile, dropWhile, takeWhileEnd, dropWhileEnd
>li_lib .. PatternMethod is_list_all_in_list {a b} {
package require struct::list
package require struct::set
set a_in_b [lsort [struct::set intersect [lsort -unique $a] $b ]]
return [struct::list equal [lsort $a] $a_in_b]
}
>li_lib .. PatternMethod is_list_all_ni_list {a b} {
package require struct::set
set i [struct::set intersect $a $b]
return [expr {[llength $i] == 0}]
}
>li_lib . new >li
>li_lib . aliasprefix "li."
>pattern .. Create >f_lib
>f_lib .. Construct {args} {
var o_this
set o_this @this@
var o_last_child
set o_last_child ""
}
>f_lib .. Method version {} {
return 1.0.0
}
>f_lib .. Method aliasprefix {pfx} {
var o_this
var o_last_child
if {![string length $o_last_child]} {
error ">f_lib . aliasprefix - Create library object with >f_lib . new >somename first."
}
set patternmethods [$o_this .. PM]
set aliases [list]
foreach m $patternmethods {
set a ${pfx}${m}
if {[llength [info commands $a]]} {
puts stderr "WARNING - a command was already present at: $a"
}
interp alias "" $a "" [$o_last_child . $m .]
lappend aliases $a
}
return $aliases
}
>f_lib .. Method new {objcmdname} {
var o_this
var o_last_child
set nscaller [uplevel 1 [list namespace current]]
if {![string match ::* $objcmdname]} {
if {$nscaller eq "::"} {set nscaller ""}
set objcmdname ${nscaller}::$objcmdname
}
uplevel 1 [list $o_this .. Create $objcmdname]
set o_last_child $objcmdname
}
>f_lib .. Constructor {args} {
var o_this
set o_this @this@
}
>f_lib .. PatternMethod foldl {total func sequence} {
struct::list::Lfold $sequence $total $func
}
#note: foldr is not equivalent to just doing a foldl on the reversed list
#todo - review/test/fix
>f_lib .. PatternMethod foldr {total func sequence} {
set this @this@
if {![llength $sequence]} {
return $total
}
v,h@head,t@tail.=val $sequence |h@head,t@tail> {
puts "-->$h"
$func [$this . foldr $total $func $t] $h
} <this@,func@,total@| $this $func $total
return 0
return $v
}
# reduce: simplest case - list of numbers - reduce with +
# more complex case: list of pipelines (e.g parsers) - reduce with 'andThen' operator of some sort.
>f_lib .. PatternMethod reduce {func sequence} {
struct::list::Lfold [lrange $sequence 1 end] [lindex $sequence 0] $func
}
>f_lib .. PatternMethod list_map {commandlist list} {
tailcall lmap item $list $commandlist
}
>f_lib .. PatternMethod list_unique {args} {
set list [concat {*}$args]
set d [dict create]
foreach item $list {
dict set d $item ""
}
dict keys $d
}
>f_lib .. PatternMethod list_as_lines {args} {
set list [concat {*}$args]
join $list \n
}
>f_lib .. PatternMethod list_filter_cond {itemcond listval} {
#maintenance - proc list_filter_cond
set filtered_list [list]
set binding {}
if {[info level] == 1} {
#up 1 is global
set get_vars [list info vars]
} else {
set get_vars [list info locals]
}
set vars [uplevel 1 {*}$get_vars]
set posn [lsearch $vars item]
set vars [lreplace $vars $posn $posn]
foreach v $vars {
upvar 1 $v var
if {(![array exists var]) && [info exists var]} {
lappend binding [list $v $var] ;#values captured as defaults for apply args.
}
}
#lappend binding [list item $args]
#puts stderr "binding: [join $binding \n]"
#apply [list $binding $pipescript [uplevel 1 namespace current]]
foreach item $listval {
set bindlist [list {*}$binding [list item $item]]
if {[apply [list $bindlist $itemcond [uplevel 1 namespace current]] ]} {
lappend filtered_list $item
}
}
return $filtered_list
}
>f_lib .. PatternMethod sum_llength {total listval} {
expr {$total + [llength $listval]}
}
>f_lib .. PatternMethod sum_length {total stringval} {
expr {$total + [string length $stringval]}
}
>f_lib .. PatternMethod debug {total item} {
puts stderr "incr tally: $total item: $item"
expr {$total + 1}
}
>f_lib .. PatternMethod dict_walk {d key} {
dict get $d $key
}
>f_lib .. PatternMethod sum {total num} {
expr {$total + $num}
}
>f_lib .. PatternMethod lcomp {expression args} {
#from https://wiki.tcl-lang.org/page/lcomp
set __0__ "lappend __1__ \[expr [list $expression]\]"
while {[llength $args] && [lindex $args 0] ni {for if with}} {
append __0__ " \[expr [list [lindex $args 0]]\]"
set args [lrange $args 1 end]
}
set tmpvar 2
set structure {}
set upvars {}
while {[llength $args]} {
set prefix ""
switch [lindex $args 0] {
for {
set nest [list foreach]
while {[llength $nest] == 1 || [lindex $args 0] eq "and"} {
if {[llength $args] < 4 || [lindex $args 2] ni {in inside}} {
error "wrong # operands: must be \"for\" vars \"in?side?\"\
vals ?\"and\" vars \"in?side?\" vals? ?...?"
}
switch [lindex $args 2] {
in {
lappend nest [lindex $args 1] [lindex $args 3]
} inside {
lappend nest __${tmpvar}__ [lindex $args 3]
append prefix "lassign \$__${tmpvar}__ [lindex $args 1]\n"
incr tmpvar
}}
set args [lrange $args 4 end]
}
lappend structure $nest $prefix
} if {
if {[llength $args] < 2} {
error "wrong # operands: must be \"if\" condition"
}
lappend structure [list if [lindex $args 1]] $prefix
set args [lrange $args 2 end]
} with {
if {[llength $args] < 2} {
error "wrong # operands: must be \"with\" varlist"
}
foreach var [lindex $args 1] {
lappend upvars $var $var
}
set args [lrange $args 2 end]
} default {
error "bad opcode \"[lindex $args 0]\": must be for, if, or with"
}}
}
foreach {prefix nest} [lreverse $structure] {
set __0__ [concat $nest [list \n$prefix$__0__]]
}
if {[llength $upvars]} {
set __0__ "upvar 1 $upvars; $__0__"
}
unset -nocomplain expression args tmpvar prefix nest structure var upvars
set __1__ ""
eval $__0__
return $__1__
}
>f_lib . new ::punk::lib::>f
>f_lib . aliasprefix "f."
interp alias {} >f {} ::punk::lib::>f
#Pattern-matching based functional operations
>pattern .. Create >P
>P .. Method map {pattern commandlist sequence} {
#set segment [string map [list <cmd> $commandlist] {<cmd>}]
set pipeline [list % {val $item} "|,item,$pattern>" $commandlist <item/0|]
tailcall % list $pipeline $sequence |p/0,l/1> {lmap val $l {{*}$p $val }}
}
}

74
src/modules/punk/mix/base-0.1.tm

@ -69,6 +69,8 @@ namespace eval punk::mix::base {
set d_commands [get_commands -extension $extension]
set all_commands [list {*}[dict get $d_commands main] {*}[dict get $d_commands base]]
error "Unknown subcommand \"[lindex $args 0]\": must be one of: $all_commands" "punk::mix::base _unknown $ns $args" [list unknown_ensemble_subcommand ensemble punk::mix::base]
}
proc _redirected {from_ns subcommand args} {
@ -93,6 +95,16 @@ namespace eval punk::mix::base {
}
tailcall [namespace current] $subcommand {*}$argvals {*}$args -extension $from_ns
} else {
if {[regexp {.*[*?].*} $subcommand]} {
set d_commands [get_commands -extension $from_ns]
set all_commands [list {*}[dict get $d_commands main] {*}[dict get $d_commands base]]
set matched_commands [lsearch -all -inline $all_commands $subcommand]
set commands ""
foreach m $matched_commands {
append commands $m \n
}
return $commands
}
tailcall [namespace current] $subcommand {*}$args -extension $from_ns
}
}
@ -269,7 +281,9 @@ namespace eval punk::mix::base {
}
#result for just 'pmix help'
puts stderr "-->$args"
set helpstr ""
append helpstr "limit commandlist with a glob search such as *word*"
append helpstr "commands:\n"
foreach {source cmdlist} $command_info {
@ -368,46 +382,54 @@ namespace eval punk::mix::base {
if {[punk::cap::capability_has_handler punk.templates]} {
set template_folder_dict [punk::cap::call_handler punk.templates folders]
dict for {dir folderinfo} $template_folder_dict {
#
dict set folderdict $dir $folderinfo
}
} else {
put stderr "get_template_basefolders WARNING - no handler available for the 'punk.templates' capability - template providers will be unable to provide template locations"
}
#2 middle precedence - mixtemplates folder relative to cwd
#2 ADHOC templates folder relative to cwd. higher precedence
set searchbase [pwd]
set fld [file join $searchbase mixtemplates]
set fld [file join $searchbase templates]
if {[file isdirectory $fld]} {
if {![dict exists $folderdict $fld]} {
dict set folderdict $fld [list source $searchbase sourcetype cwd]
}
}
set pathinfo [punk::repo::find_repos $searchbase]
set pwd_projectroot [dict get $pathinfo closest]
if {$pwd_projectroot ne ""} {
set fld [file join $pwd_projectroot src/mixtemplates]
if {![dict exists $folderdict $fld]} {
dict set folderdict $fld [list source $pwd_projectroot sourcetype project]
dict set folderdict $fld [list source $searchbase sourcetype cwd pathtype adhoc vendor _project]
}
}
#3 highest precedence - mixtemplates relative to scriptpath argument
if {$scriptpath ne ""} {
if {[file type $scriptpath] eq "file"} {
set searchbase [file dirname $scriptpath]
} else {
set searchbase $scriptpath
}
if {[file isdirectory [file join $searchbase mixtemplates]]} {
dict set folderdict [file join $searchbase mixtemplates] [list source $searchbase sourcetype pathsearch]
}
if 0 {
set pathinfo [punk::repo::find_repos $searchbase]
set scriptpath_projectroot [dict get $pathinfo closest]
if {$scriptpath_projectroot ne ""} {
set fld [file join $scriptpath_projectroot src/mixtemplates]
if {[file isdirectory $fld]} {
dict set folderdict $fld [list source $scriptpath_projectroot sourcetype project]
set pwd_projectroot [dict get $pathinfo closest]
if {$pwd_projectroot ne ""} {
set fld [file join $pwd_projectroot src/mixtemplates]
if {![dict exists $folderdict $fld]} {
dict set folderdict $fld [list source $pwd_projectroot sourcetype project]
}
}
#3 highest precedence - mixtemplates relative to scriptpath argument
if {$scriptpath ne ""} {
if {[file type $scriptpath] eq "file"} {
set searchbase [file dirname $scriptpath]
} else {
set searchbase $scriptpath
}
if {[file isdirectory [file join $searchbase mixtemplates]]} {
dict set folderdict [file join $searchbase mixtemplates] [list source $searchbase sourcetype pathsearch]
}
set pathinfo [punk::repo::find_repos $searchbase]
set scriptpath_projectroot [dict get $pathinfo closest]
if {$scriptpath_projectroot ne ""} {
set fld [file join $scriptpath_projectroot src/mixtemplates]
if {[file isdirectory $fld]} {
dict set folderdict $fld [list source $scriptpath_projectroot sourcetype project]
}
}
}
}
#don't sort - order in which encountered defines the precedence - with later overriding earlier
return $folderdict
}

28
src/modules/punk/mix/cli-0.3.tm

@ -210,7 +210,7 @@ namespace eval punk::mix::cli {
proc validate_modulename {modulename args} {
set defaults [list\
-name_description modulename\
-errorprefix validate_modulename\
]
if {[llength $args] %2 != 0} {error "validate_modulename args must be name-value pairs: received '$args'"}
set known_opts [dict keys $defaults]
@ -221,18 +221,18 @@ namespace eval punk::mix::cli {
}
set opts [dict merge $defaults $args]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_name_description [dict get $opts -name_description]
set opt_errorprefix [dict get $opts -errorprefix]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
validate_name_not_empty_or_spaced $modulename -name_description $opt_name_description
validate_name_not_empty_or_spaced $modulename -errorprefix $opt_errorprefix
set testname [string map [list :: ""] $modulename]
if {[string first : $testname] >=0} {
error "$opt_name_description '$modulename' can only contain paired colons"
error "$opt_errorprefix '$modulename' can only contain paired colons"
}
set badchars [list - "$" "?" "*"]
foreach bc $badchars {
if {[string first $bc $modulename] >= 0} {
error "$opt_name_description '$modulename' can not contain character '$bc'"
error "$opt_errorprefix '$modulename' can not contain character '$bc'"
}
}
return $modulename
@ -240,7 +240,7 @@ namespace eval punk::mix::cli {
proc validate_projectname {projectname args} {
set defaults [list\
-name_description projectname\
-errorprefix projectname\
]
if {[llength $args] %2 != 0} {error "validate_modulename args must be name-value pairs: received '$args'"}
set known_opts [dict keys $defaults]
@ -251,21 +251,21 @@ namespace eval punk::mix::cli {
}
set opts [dict merge $defaults $args]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_name_description [dict get $opts -name_description]
set opt_errorprefix [dict get $opts -errorprefix]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
validate_name_not_empty_or_spaced $projectname -name_description $opt_name_description
validate_name_not_empty_or_spaced $projectname -errorprefix $opt_errorprefix
set reserved_words [list etc lib bin modules src doc vendorlib vendormodules embedded runtime _aside _build]
if {$projectname in $reserved_words } {
error "$opt_name_description '$projectname' cannot be one of reserved_words: $reserved_words"
error "$opt_errorprefix '$projectname' cannot be one of reserved_words: $reserved_words"
}
if {[string first "::" $projectname] >= 0} {
error "$opt_name_description '$projectname' cannot contain namespace separator '::'"
error "$opt_errorprefix '$projectname' cannot contain namespace separator '::'"
}
return $projectname
}
proc validate_name_not_empty_or_spaced {name args} {
set defaults [list\
-name_description projectname\
-errorprefix projectname\
]
if {[llength $args] %2 != 0} {error "validate_modulename args must be name-value pairs: received '$args'"}
set known_opts [dict keys $defaults]
@ -276,13 +276,13 @@ namespace eval punk::mix::cli {
}
set opts [dict merge $defaults $args]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_name_description [dict get $opts -name_description]
set opt_errorprefix [dict get $opts -errorprefix]
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
if {![string length $name]} {
error "$opt_name_description cannot be empty"
error "$opt_errorprefix cannot be empty"
}
if {[string length [string map [list " " "" \n "" \r "" \t ""] $name]] != [string length $name]} {
error "$opt_name_description cannot contain whitespace"
error "$opt_errorprefix cannot contain whitespace"
}
return $name
}

4
src/modules/punk/mix/commandset/doc-999999.0a1.0.tm

@ -44,7 +44,7 @@ namespace eval punk::mix::commandset::doc {
}
#user may delete the comment containing "--- punk::docgen::overwrites" and then manually edit, and we won't overwrite
#we still generate output in src/docgen so user can diff and manually update if thats what they prefer
set oldfiles [punk::path::treefilenames $projectdir/src/doc _module_*.man]
set oldfiles [punk::path::treefilenames -dir $projectdir/src/doc _module_*.man]
foreach maybedoomed $oldfiles {
set fd [open $maybedoomed r]
set data [read $fd]
@ -216,7 +216,7 @@ namespace eval punk::mix::commandset::doc {
}
file mkdir $output_base
set matched_paths [punk::path::treefilenames $codesource_path *.tm -antiglob_paths {**/mix/templates/** **/mixtemplates/**}]
set matched_paths [punk::path::treefilenames -dir $codesource_path -antiglob_paths {**/mix/templates/** **/mixtemplates/**} *.tm]
set count 0
set newdocs [list]
set docgen_header_comments ""

178
src/modules/punk/mix/commandset/module-999999.0a1.0.tm

@ -17,8 +17,8 @@
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
package require punk::repo
# depends on punk,punk::mix::base,punk::mix::cli
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
@ -76,10 +76,16 @@ namespace eval punk::mix::commandset::module {
}
#require current dir when calling to be the projectdir, or
proc templates {args} {
set tdict [templates_dict {*}$args]
set tdict_low_to_high [templates_dict {*}$args]
#convert to screen order - with higher priority at the top
set tdict [dict create]
foreach k [lreverse [dict keys $tdict_low_to_high]] {
dict set tdict $k [dict get $tdict_low_to_high $k]
}
package require overtype
set paths [dict values $tdict]
set pathinfolist [dict values $tdict]
set paths [lsearch -all -inline -index 1 -subindices $pathinfolist *] ;#relies on first key of templates_dict being path
set names [dict keys $tdict]
set title1 "Path"
@ -112,9 +118,10 @@ namespace eval punk::mix::commandset::module {
-project \uFFFF\
-version \uFFFF\
-license <unspecified>\
-template module-0.0.1.tm\
-template punk.module\
-type \uFFFF\
-force 0\
-quiet 0\
]
set opts [dict merge $defaults $args]
@ -163,7 +170,7 @@ namespace eval punk::mix::commandset::module {
} else {
set modulename $module
}
punk::mix::cli::lib::validate_modulename $modulename -name_description "mix module.new name"
punk::mix::cli::lib::validate_modulename $modulename -errorprefix "punk::mix::commandset::module::new"
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
#options
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
@ -187,13 +194,65 @@ namespace eval punk::mix::commandset::module {
set opt_license [dict get $opts -license]
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_template [dict get $opts -template]
if {[regexp {.*[?*].*} $opt_template]} {
error "module.new -template does not support glob chars. Use an exact full name including version (and optionally .tm) - or use just the name without version or .tm, and the latest version will be selected"
}
set templates_dict [templates_dict] ;#possibly suffixed with .2 .3 etc
set templates_dict [templates_dict] ;#keys are possibly prefixed with <vendor>. and/or suffixed with .2 .3 etc if there are collisions - the remaining unsuffixed .tm being the one with highest preference
#todo - allow versionless name - pick latest which isn't suffixed with .2 etc
if {![dict exists $templates_dict $opt_template]} {
error "module.new unable to find template '$opt_template'. Known templates: [dict keys $templates_dict]"
#if the user wants to exactly match an unversioned template, in the presence of versioned ones - they may need to include the trailing .tm
if {[dict exists $templates_dict $opt_template]} {
#exact long name including version and .tm found
set templatefile [dict get $templates_dict $opt_template path]
set templatefile_info [dict get $templates_dict $opt_template sourceinfo]
} elseif {[dict exists $templates_dict $opt_template.tm]} {
#Note - an unversioned .tm template will be matched here - even though versioned templates of the same name may exist.
#It's reasonable to consider an unversioned file to be lower than any versioned one
# so this may be very surprising given that if all templates are versioned - the highest one is normally selected - REVIEW
set templatefile [dict get $templates_dict $opt_template.tm path]
set templatefile_info [dict get $templates_dict $opt_template.tm sourceinfo]
} else {
#if it wasn't an exact match for opt_template or opt_template.tm - then opt_template now shouldn't contain a version or end in .tm (we have also ruled out glob chars * & ? above)
#(if it does - then we just won't find anything - which is fine)
#module file name could contain dots - but only one dash - if it is versioned
set matches [lsearch -all -inline [dict keys $templates_dict] $opt_template-*] ;#the key is of form vendor.modulename-version.tm(.suffix) (version optional, suffix if lower precedence with same name was found)
#only .tm (or .TM .Tm .tM) files make it into the templates_dict - they are allowed to be unversioned though.
set key_version_list [list]
foreach m $matches {
#vendorname could contain dashes or dots - so easiest way to split out is to examine the stored vendor value in sourceinfo
set vendor [dict get $templates_dict $m sourceinfo vendor]
if {$vendor ne "_project"} {
#_project special case - not included in module names
set module $m
} else {
set module [string range [string length $vendor.] end]
}
lassign [punk::mix::cli::lib::split_modulename_version $m] _tailmname mversion
lappend key_version_list [list $m $mversion]
}
if {[llength $matches]} {
set highest_m ""
set highest_v ""
foreach kv $key_version_list {
if {$highest_v eq ""} {
set highest_m [lindex $kv 0]
set highest_v [lindex $kv 1]
} else {
if {[package vcompare $highest_v [lindex $kv 1]] == -1} {
set highest_m [lindex $kv 0]
set highest_v [lindex $kv 1]
}
}
}
set templatefile [dict get $templates_dict $highest_m path]
set templatefile_info [dict get $templates_dict $highest_m sourceinfo]
} else {
error "module.new unable to find template '$opt_template'. [dict size $templates_dict] Known templates. Use deck module.templates to display"
}
}
set templatefile [dict get $templates_dict $opt_template]
set tpldir [file dirname $templatefile] ;#use same folder for modulename_buildversion.txt, modulename_description.txt if they exist
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
@ -205,6 +264,8 @@ namespace eval punk::mix::commandset::module {
error "module.new - error - unknown -type '$opt_type' known-types: [punk::mix::cli::lib::module_types]"
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_quiet [dict get $opts -quiet]
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
@ -253,6 +314,8 @@ namespace eval punk::mix::commandset::module {
if {$opt_version_supplied ne "\uFFFF"} {
set build_version $opt_version
} else {
#
if {[util::is_valid_tm_version $t_version]} {
if {$mversion_supplied eq ""} {
set build_version $t_version
@ -273,7 +336,12 @@ namespace eval punk::mix::commandset::module {
set infile_version $build_version
}
set template_filedata [string map [list %project% $projectname %pkg% $modulename %year% $year %license% $opt_license %version% $infile_version] $template_filedata]
set moduletemplate [file join $projectname [punk::path::relative $projectdir $templatefile]] ;#if templatfile is on another volume - just $templatefile will be returned.
#moduletemplate should usually be a relative path - but could be absolute, or contain info about the relative locations of projectdir vs templatefile if template comes from another project or a module outside the project
#This path info may be undesired in the template output (%moduletemplate%)
#it is nevertheless useful information - and not the only way developer-machine/build-machine paths can leak
#for now the user has the option to override any templates and remove %moduletemplate% if it is a security/privacy concern
set template_filedata [string map [list %moduletemplate% $moduletemplate %project% $projectname %pkg% $modulename %year% $year %license% $opt_license %version% $infile_version] $template_filedata]
set modulefile $modulefolder/${moduletail}-$infile_version.tm
if {[file exists $modulefile]} {
@ -330,7 +398,9 @@ namespace eval punk::mix::commandset::module {
}
}
if {!$opt_quiet} {
puts stdout "Creating $modulefile from template $moduletemplate"
}
set fd [open $modulefile w]
fconfigure $fd -translation binary
puts -nonewline $fd $template_filedata
@ -347,49 +417,107 @@ namespace eval punk::mix::commandset::module {
}
namespace eval lib {
proc get_moduletemplate_folders {args} {
}
proc templates_dict {args} {
set defaults [list -scriptpath ""]
set opts [dict merge $defaults $args]
set globsearch [lindex $args 0]; #note that in this case our globsearch won't reduce the machine's effort in scannning the filesystem - as we need to search on the renamed results
if {$globsearch eq ""} {
set globsearch *
}
set args [lrange $args 1 end]
set opts [dict get [punk::get_leading_opts_and_values {
-scriptpath ""
} $args -maxvalues 0] opts]
# -- --- --- --- --- --- --- --- ---
set opt_scriptpath [dict get $opts -scriptpath]
# -- --- --- --- --- --- --- --- ---
set module_template_bases [list]
set tbasedict [punk::mix::base::lib::get_template_basefolders $opt_scriptpath]
dict for {tbase folderinfo} $tbasedict {
lappend module_template_bases [file join $tbase modules]
lappend module_template_bases [list modulefolder [file join $tbase modules] sourceinfo $folderinfo]
}
set template_files [list]
foreach basefld $module_template_bases {
set matched_files [glob -nocomplain -dir $basefld -type f template_*]
#don't apply our globsearch until after we have assigned vendor prefix and dup-suffixes
#Flip the priority order of the module bases to high-priority earliest in the list
#we want a sensible display order for files within the module bases - and for the files to be encountered in the order high prio to low when setting the trailing .<int> dup/overridden indicator
foreach baseinfo [lreverse $module_template_bases] {
set basefld [dict get $baseinfo modulefolder]
#set matched_files [glob -nocomplain -dir $basefld -type f template_*.tm] ;#glob is case insensitive on windows
set matched_files [punk::path::treefilenames -dir $basefld template_*.tm]
foreach tf $matched_files {
if {[string match ignore* $tf]} {
continue
}
set ext [file extension $tf]
if {$ext in [list ".tm"]} {
lappend template_files $tf
if {[string tolower $ext] in [list ".tm"]} {
#we will ignore any .tm files that don't have versions that tcl understands - but warn
#this reduces the cases we have to test later
set fname [file tail $tf]
lassign [split [punk::mix::cli::lib::split_modulename_version $fname]] mname ver
if {[catch {punk::mix::cli::lib::validate_modulename $mname} errM]} {
puts stderr "Invalid module name/version $tf - please rename with standard Tcl .tm module name and version (or leave out version)"
if {[string match *-* $mname]} {
puts stderr "Tcl module name cannot contain dash character - except between name and version"
}
} else {
lappend template_files [list file $tf baseinfo $baseinfo]
}
}
}
}
set tdict [dict create]
set seen_dict [dict create]
foreach fullpath $template_files {
foreach fileinfo $template_files {
set fullpath [dict get $fileinfo file]
set vendor [dict get $fileinfo baseinfo sourceinfo vendor]
set modulefolder [dict get $fileinfo baseinfo modulefolder]
#if template is in a hierarchy below modulefolder - we join the segments with :: - same as the way Tcl does for determining module names
set relativepath [punk::path::relative $modulefolder $fullpath]
set dirs [file dirname $relativepath]
if {$dirs eq "."} {
set dirs ""
}
set moduleprefix [join $dirs ::]
set ftail [file tail $fullpath]
set tname [string range $ftail [string length template_] end]
if {$moduleprefix ne ""} {
set tname ${moduleprefix}::$tname
}
if {$vendor ne "_project"} {
set tname ${vendor}.$tname
}
if {![dict exists $seen_dict $tname]} {
dict set seen_dict $tname 1
dict set tdict $tname $fullpath ; #first seen of filename gets no number
dict set tdict $tname [list path $fullpath {*}[dict get $fileinfo baseinfo]] ; #first seen of filename gets no number
} else {
set n [dict get $seen_dict $tname]
incr n
dict incr seen_dict $tname
dict set tdict ${tname}.$n $fullpath
dict set tdict ${tname}.$n [list path $fullpath {*}[dict get $fileinfo baseinfo]]
}
}
#assert path is first key of tdict (callers of templates_dict are allowed to rely on it being first)
#assert tdict has keys path,modulefolder,sourceinfo
set result [dict create]
set keys [lreverse [dict keys $tdict]] ;#return in priority order low to high (which is generally reverse of how we'd like to see it on screen)
#set keys [dict keys $tdict]
foreach k $keys {
if {[string match $globsearch $k]} {
dict set result $k [dict get $tdict $k]
}
}
return $tdict
return $result
}
}

2
src/modules/punk/mix/commandset/project-999999.0a1.0.tm

@ -129,7 +129,7 @@ namespace eval punk::mix::commandset::project {
error "punk::mix::cli::new error: unable to determine containing folder for '$newprojectpath_or_name'"
}
punk::mix::cli::lib::validate_projectname $projectname -name_description "punk mix project.new"
punk::mix::cli::lib::validate_projectname $projectname -errorprefix "punk mix project.new"
set defaults [list\

185
src/modules/punk/mix/commandset/scriptwrap-999999.0a1.0.tm

@ -33,75 +33,59 @@ namespace eval punk::mix::commandset::scriptwrap {
namespace import ::punk::fileline::class::*
}
#scriptpath allows templates command to use same custom template set as when multishell pointed to a filepath
#it may or may not be within a project
#by using the same folder or path, the same project root will be discovered. REVIEW.
proc templates_dict {args} {
set defaults [list -scriptpath ""]
set opts [dict merge $defaults $args]
set opt_scriptpath [dict get $opts -scriptpath]
set wrapper_folders [lib::get_wrapper_folders $opt_scriptpath]
set wrapper_templates [list]
foreach fld $wrapper_folders {
set templates [glob -nocomplain -dir $fld -type f *]
foreach tf $templates {
if {[string match ignore* $tf]} {
continue
}
set ext [file extension $tf]
if {$ext in [list "" ".bat" ".cmd" ".sh"]} {
lappend wrapper_templates $tf
}
}
proc templates {args} {
package require overtype
package require textblock
set tdict_low_to_high [templates_dict {*}$args]
#convert to screen order - with higher priority at the top
set tdict [dict create]
foreach k [lreverse [dict keys $tdict_low_to_high]] {
dict set tdict $k [dict get $tdict_low_to_high $k]
}
#set pathinfolist [dict values $tdict]
set names [dict keys $tdict]
set tdict [dict create]
set seen_dict [dict create]
foreach fullpath $wrapper_templates {
set ftail [file tail $fullpath]
if {![dict exists $seen_dict $ftail]} {
dict set seen_dict $ftail 1
dict set tdict $ftail $fullpath ; #first seen of filename gets no number
} else {
set n [dict get $seen_dict $ftail]
incr n
dict incr seen_dict $ftail
dict set tdict ${ftail}.$n $fullpath
}
#set paths [lsearch -all -inline -index 1 -subindices $pathinfolist *]; #first key of templates_dict is path
set paths [list]
set pathtypes [list]
dict for {nm tinfo} $tdict {
lappend paths [dict get $tinfo path]
lappend pathtypes [dict get $tinfo sourceinfo pathtype]
}
return $tdict
}
proc templates {args} {
package require overtype
set tdict [templates_dict {*}$args]
set paths [dict values $tdict]
set names [dict keys $tdict]
package require textblock
set title(name) "Template Name"
set widest(name) [tcl::mathfunc::max {*}[lmap v [concat [list $title(name)] $names] {string length $v}]]
set col(name) [string repeat " " $widest(name)]
set title(pathtype) "[a+ green]Path\n Type[a]"
set widest(pathtype) [tcl::mathfunc::max {*}[lmap v [concat [list $title(pathtype)] $pathtypes] {textblock::width $v}]]
set col(pathtype) [string repeat " " $widest(pathtype)]
set title1 "Path"
set widest1 [tcl::mathfunc::max {*}[lmap v [concat [list $title1] $paths] {punk::strlen $v}]]
set col1 [string repeat " " $widest1]
set title(path) "Path"
set widest(path) [tcl::mathfunc::max {*}[lmap v [concat [list $title(path)] $paths] {string length $v}]]
set col(path) [string repeat " " $widest(path)]
set title2 "Template Name"
set widest2 [tcl::mathfunc::max {*}[lmap v [concat [list $title2] $names] {punk::strlen $v}]]
set col2 [string repeat " " $widest2]
set tablewidth [expr {$widest1 + 1 + $widest2}]
set tablewidth [expr {$widest(name) + 1 + $widest(pathtype) + $widest(path)}]
set table ""
append table [string repeat - $tablewidth] \n
append table "[overtype::left $col1 $title1] [overtype::left $col2 $title2]" \n
append table "[overtype::left $col(name) $title(name)] [overtype::left $col(pathtype) $title(pathtype)] [overtype::left $col(path) $title(path)]" \n
append table [string repeat - $tablewidth] \n
foreach p $paths n $names {
append table "[overtype::left $col1 $p] [overtype::left $col2 $n]" \n
foreach n $names pt $pathtypes p $paths {
append table "[overtype::left $col(name) $n] [overtype::left $col(pathtype) $pt] [overtype::left $col(path) $p]" \n
}
return $table
}
proc templates_dict {args} {
tailcall lib::templates_dict {*}$args
}
#A batch file with unix line-endings is sensitive to label positioning.
@ -1163,11 +1147,32 @@ namespace eval punk::mix::commandset::scriptwrap {
namespace eval lib {
proc get_wrapper_folders {args} {
set opts [dict get [punk::get_leading_opts_and_values {
-scriptpath ""
} $args -maxvalues 0] opts]
# -- --- --- --- --- --- --- --- ---
set opt_scriptpath [dict get $opts -scriptpath]
# -- --- --- --- --- --- --- --- ---
set wrapper_template_bases [list]
set tbasedict [punk::mix::base::lib::get_template_basefolders $opt_scriptpath]
dict for {tbase folderinfo} $tbasedict {
set wrapf [file join $tbase utility/scriptappwrappers]
if {[file isdirectory $wrapf]} {
lappend wrapper_template_bases [list basefolder $wrapf sourceinfo $folderinfo]
}
}
return $wrapper_template_bases
}
#get_wrapper_folders
# scriptpath - file or folder
# It represents the base point from which to search for /wrapper folders either directly above the scriptpath or in the containing project if any
# The cwd will also be searched for /wrapper folder and project - but with lower precedence in the resultset (later in list)
proc get_wrapper_folders {{scriptpath ""}} {
proc get_wrapper_folders1 {{scriptpath ""}} {
set wrapper_folders [list]
if {$scriptpath ne ""} {
if {[file type $scriptpath] eq "file"} {
@ -1224,6 +1229,78 @@ namespace eval punk::mix::commandset::scriptwrap {
}
return $wrapper_folders
}
#scriptpath allows templates command to use same custom template set as when multishell pointed to a filepath
#it may or may not be within a project
#by using the same folder or path, the same project root will be discovered. REVIEW.
proc templates_dict {args} {
set globsearch [lindex $args 0]
if {$globsearch eq ""} {
set globsearch *
}
set args [lrange $args 1 end]
set opts [dict get [punk::get_leading_opts_and_values {
-scriptpath ""
} $args -maxvalues 0] opts]
# -- --- --- --- --- --- --- --- ---
set opt_scriptpath [dict get $opts -scriptpath]
# -- --- --- --- --- --- --- --- ---
set wrapper_folders [lib::get_wrapper_folders -scriptpath $opt_scriptpath]
set wrapper_files [list]
#flip the priority order to high-priority earliest in the list (display order - and processing order of found files)
foreach baseinfo [lreverse $wrapper_folders] {
set basefld [dict get $baseinfo basefolder]
#set matched_files [glob -nocomplain -dir $basefld -type f *]
set matched_files [punk::path::treefilenames -dir $basefld *]
foreach tf $matched_files {
if {[string match ignore* $tf]} {
continue
}
set ext [file extension $tf]
if {[string tolower $ext] in [list "" ".bat" ".cmd" ".sh" ".bash" ".pl" ".ps1" ".tcl"]} {
lappend wrapper_files [list file $tf baseinfo $baseinfo]
}
}
}
set tdict [dict create]
set seen_dict [dict create]
foreach fileinfo $wrapper_files {
set fullpath [dict get $fileinfo file]
set vendor [dict get $fileinfo baseinfo sourceinfo vendor]
set basefolder [dict get $fileinfo baseinfo basefolder]
set relativepath [punk::path::relative $basefolder $fullpath]
set ftail [file tail $fullpath]
set tname $relativepath
if {$vendor ne "_project"} {
set tname ${vendor}.$tname
}
if {![dict exists $seen_dict $tname]} {
dict set seen_dict $tname 1
dict set tdict $tname [list path $fullpath {*}[dict get $fileinfo baseinfo]] ; #first seen of filename gets no number
} else {
set n [dict get $seen_dict $tname]
incr n
dict incr seen_dict $tname
dict set tdict ${tname}.$n [list path $fullpath {*}[dict get $fileinfo baseinfo]]
}
}
#return in priority order low to high
set result [dict create]
set keys [lreverse [dict keys $tdict]]
foreach k $keys {
if {[string match $globsearch $k]} {
dict set result $k [dict get $tdict $k]
}
}
return $result
}
proc _scriptapp_tag_from_line {line} {
set result [list istag 0 raw ""] ;#default assumption. All
#----

22
src/modules/punk/mix/templates-999999.0a1.0.tm

@ -26,19 +26,26 @@ namespace eval punk::mix::templates {
variable pkg punk::mix::templates
variable cap_provider
#punk::cap::register_package punk::mix::templates [list\
# {punk.templates {relpath ../templates}}\
#]
namespace eval capsystem {
if {[info commands capprovider.registration] eq ""} {
punk::cap::class::interface_capprovider.registration create capprovider.registration
oo::objdefine capprovider.registration {
method get_declarations {} {
set decls [list]
lappend decls [list punk.templates {relpath ../templates}]
lappend decls [list punk.templates {relpath ../templates2}]
lappend decls [list punk.test {something blah}]
lappend decls [list punk.templates {path templates pathtype module vendor punk}]
#only punk::templates is allowed to register a _multivendor path - review
#other punk.template providers should use module, absolute, currentproject and shellproject pathtypes only
lappend decls [list punk.templates {path src/decktemplates pathtype currentproject_multivendor vendor punk}]
lappend decls [list punk.templates {path decktemplates pathtype shellproject_multivendor vendor punk}]
#we need a way to ensure we don't pull updates from a remote repo into a local project that is actually the same project ? review!
#need flags as to whether/how provider allows template updates that are out of sync with the provider pkg version
#perhaps a separate .txt file (alongside buildversion and description txt files) that has some package require statements (we can't put them in the template itself as the filled template may have nothing to do with the punk.templates provider)
lappend decls [list punk.templates {path src/decktemplates/vendor/punk pathtype currentproject vendor punk allowupdates 0 repo "https://www.gitea1.intx.com.au/jn/punkshell" reposubdir "src/decktemplates/vendor/punk"}]
lappend decls [list punk.isbogus {provider punk::mix::templates something blah}] ;#some capability for which there is no handler to validate - therefore no warning will result.
#review - we should report unhandled caps somewhere, or provide a mechanism to detect/report.
#we don't want to warn at the time this provider is loaded - as handler may legitimately be loaded later.
return $decls
}
}
@ -62,6 +69,7 @@ namespace eval punk::mix::templates {
#provider api
# -- ---
#none - declarations only
#todo - template folder install/update/status methods?
}

6
src/modules/punk/mix/templates/layouts/project/src/buildsuites/samplesuite1/download_and_build.config

@ -46,6 +46,12 @@ SOURCEDOWNLOAD -project tcl\
#EXTRACONFIG thread --with-tcl=$buildprefix/lib --with-tclinclude=$buildprefix/include
#SOURCEDOWNLOAD -project zint\
-mechanism git\
-branch master\
-remote https://git.code.sf.net/p/zint/code

52
src/modules/punk/mix/templates/layouts/project/src/mixtemplates/modules/template_module-0.0.1.tm

@ -1,52 +0,0 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) %year%
#
# @@ Meta Begin
# Application %pkg% 999999.0a1.0
# Meta platform tcl
# Meta license %license%
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
##e.g package require frobz
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval %pkg% {
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide %pkg% [namespace eval %pkg% {
variable pkg %pkg%
variable version
set version 999999.0a1.0
}]
return

0
src/modules/punk/mix/templates/utility/scriptappwrappers/punk-multishell-old.cmd → src/modules/punk/mix/templates/utility/scriptappwrappers/basic/multishell-old.cmd

112
src/modules/punk/mix/templates/utility/scriptappwrappers/basic/shellbat.bat

@ -0,0 +1,112 @@
: "[proc : args {}]" ;# *tcl shellbat - call with sh,bash,tclsh on any platform, or with cmd on windows.
: <<'HIDE_FROM_BASH_AND_SH'
: ;# leading colon hides from .bat, trailing slash hides next line from tcl \
@call tclsh "%~dp0%~n0.bat" %*
: ;#\
@set taskexitcode=%errorlevel% & goto :exit
# -*- tcl -*-
# #################################################################################################
# This is a tcl shellbat file
# It is tuned to run when called as a batch file, a tcl script, an sh script or a bash script,
# so the specific layout and characters used are quite sensitive to change.
# It can be called on unix or windows platforms with or without the interpreter being specified on the commandline.
# e.g ./filename.sh.bat in sh or bash or powershell
# e.g filename.sh or filename.sh.bat at windows command prompt
# e.g tclsh filename.sh.bat | sh filename.sh.bat | bash filename.sh.bat
# In all cases an arbitrary number of arguments are accepted
# To avoid the initial commandline on stdout when calling as a batch file on windows, use:
# cmd /Q /c filename.sh.bat
# (because we cannot use @if to silence it, as this isn't understood by tcl,sh or bash)
# #################################################################################################
#fconfigure stdout -translation crlf
# --- --- --- --- --- --- --- --- --- --- --- --- ---begin Tcl Payload
#puts "script : [info script]"
#puts "argcount : $::argc"
#puts "argvalues: $::argv"
#<tcl-payload>
#<tcl-payload/>
# --- --- --- --- --- --- --- --- --- --- --- --- ---
# only exit if needed. see exitcode notes at bottom of file and exit there for consistency across invocation methods
# --- --- --- --- --- --- --- --- --- --- --- --- ---end Tcl Payload
#--
#-- bash/sh code follows.
#-- protect from tcl using line continuation char on the previous comment for each line, like so: \
printf "etc"
#-- or alternatively place sh/bash script within the false==false block
#-- whilst being careful to balance braces {}
#-- For more complex needs you should call out to external scripts
#--
#-- END marker for hide_from_bash_and_sh\
HIDE_FROM_BASH_AND_SH
#---------------------------------------------------------
#-- This if statement hides(mostly) a sh/bash code block from Tcl
if false==false # else {
then
:
#---------------------------------------------------------
#-- leave as is if all that's required is launching the Tcl payload"
#--
#-- Note that sh/bash script isn't called when running a .bat from cmd.exe on windows by default
#-- adjust line 4: @call tclsh ... to something like @call sh ... @call bash .. or @call env sh ... etc as appropriate
#-- if sh/bash scripting needs to run on windows too.
#--
#printf "start of bash or sh code"
#<shell-payload-pre-tcl>
#</shell-payload-pre-tcl>
#-- sh/bash launches Tcl here instead of shebang line at top
#<shell-launch-tcl>
#-- use exec to use exitcode (if any) directly from the tcl script
exec /usr/bin/env tclsh "$0" "$@"
#</shell-launch-tcl>
#-- alternative - if sh/bash script required to run after the tcl call.
#/usr/bin/env tclsh "$0" "$@"
#tcl_exitcode=$?
#echo "tcl_exitcode: ${tcl_exitcode}"
#<shell-payload-post-tcl>
#</shell-payload-post-tcl>
#-- override exitcode example
#exit 66
#printf "No need for trailing slashes for sh/bash code here\n"
#---------------------------------------------------------
fi
# closing brace for Tcl }
#---------------------------------------------------------
#-- tcl and shell script now both active
#-- comment for line sample 1 with trailing continuation slash \
#printf "tcl-invisible sh/bash line sample 1 \n"
#-- comment for line sample 2 with trailing continuation slash \
#printf "tcl-invisible sh/bash line sample 2 \n"
#-- Consistent exitcode from sh,bash,tclsh or cmd
#-- Call exit in tcl (or sh/bash) code only if explicitly required, otherwise leave this commented out.
#-- (script might be more widely useable without explicit exit. e.g in tcl: set ::argc 1; set ::argv "val"; source filename.sh.bat )
#-- exit line unprotected by trailing slash will work for tcl and/or sh/bash
#exit 0
#exit 42
#-- make sure sh/bash/tcl all skip over .bat style exit \
: <<'shell_end'
#-- .bat exit with exitcode from tcl process \
:exit
: ;# \
@exit /B %taskexitcode%
# .bat has exited \
shell_end

0
src/modules/punk/mix/templates/utility/scriptappwrappers/punk-multishell.cmd → src/modules/punk/mix/templates/utility/scriptappwrappers/multishell.cmd

524
src/modules/punk/mix/templates/utility/scriptappwrappers/multishell1.cmd

@ -0,0 +1,524 @@
: "[rename set s;proc Hide x {proc $x args {}};Hide :]" "\$(function : {<#pwsh#>})" ^
set -- "$@" "a=[list shebangless punk MULTISHELL tclsh sh bash cmd pwsh powershell;proc Hide x {proc $x args {}}; Hide <#;Hide set;s 1 list]"; set -- : "$@";$1 = @'
: heredoc1 - hide from powershell using @ and squote above. (close sqote for unix shells) ' \
: .bat/.cmd launch section, leading colon hides from cmd, trailing slash hides next line from tcl \
: "[Hide @ECHO; Hide ); Hide (;Hide echo; Hide @REM]#not necessary but can help avoid errs in testing"
: << 'HEREDOC1B_HIDE_FROM_BASH_AND_SH'
: Continuation char at end of this line and rem with curly-braces used to exlude Tcl from the whole cmd block \
: {
: STRONG SUGGESTION: DO NOT MODIFY FIRST LINE OF THIS SCRIPT. shebang #! line is not required on unix or windows and will reduce functionality and/or portability.
: Even comment lines can be part of the functionality of this script (both on unix and windows) - modify with care.
@REM ############################################################################################################################
@REM THIS IS A POLYGLOT SCRIPT - supporting payloads in Tcl, bash, sh and/or powershelll (powershell.exe or pwsh.exe)
@REM It should remain portable between unix-like OSes & windows if the proper structure is maintained.
@REM ############################################################################################################################
@REM On windows, change the value of nextshell to one of the listed 2 digit values if desired, and add code within payload sections for tcl,sh,bash,powershell as appropriate.
@REM This wrapper can be edited manually (carefully!) - or sh,bash,tcl,powershell scripts can be wrapped using the Tcl-based punkshell system
@REM e.g from within a running punkshell: pmix scriptwrap.multishell <inputfilepath> -outputfolder <folderpath>
@REM On unix-like systems, call with sh, bash or tclsh. (powershell untested on unix - and requires wscript if security elevation is used)
@REM Due to lack of shebang (#! line) Unix-like systems will probably (hopefully) default to sh if the script is called without an interpreter - but it may depend on the shell in use when called.
@REM If you find yourself really wanting/needing to add a shebang line - do so on the basis that the script will exist on unix-like systems only.
@SETLOCAL EnableExtensions EnableDelayedExpansion
@SET "validshells= ^(10^) 'pwsh' ^(11^) 'sh' (^12^) 'bash' (^13^) 'tclsh'"
@SET "shells[10]=pwsh"
@SET "shells[11]=sh"
@set "shells[12]=bash"
@SET "shells[13]=tclsh"
: <nextshell>
@SET "nextshell=13"
: </nextshell>
@rem asadmin is for automatic elevation to administrator. Separate window will be created (seems unavoidable with current elevation mechanism) and user will still get security prompt (probably reasonable).
: <asadmin>
@SET "asadmin=0"
: </asadmin>
@REM nextshell set to index for validshells .eg 10 for pwsh
@REM @ECHO nextshell is %nextshell%
@SET "selected=!shells[%nextshell%]!"
@REM @ECHO selected %selected%
@CALL SET "keyRemoved=%%validshells:'!selected!'=%%"
@REM @ECHO keyremoved %keyRemoved%
@REM Note that 'powershell' e.g v5 is just a fallback for when pwsh is not available
@REM ## ### ### ### ### ### ### ### ### ### ### ### ### ###
@REM -- cmd/batch file section (ignored on unix but should be left in place)
@REM -- This section intended mainly to launch the next shell (and to escalate privileges if necessary)
@REM -- Avoid customising this if you are not familiar with batch scripting. cmd/batch script can be useful, but is probably the least expressive language and most error prone.
@REM -- For example - as this file needs to use unix-style lf line-endings - the label scanner is susceptible to the 512Byte boundary issue: https://www.dostips.com/forum/viewtopic.php?t=8988#p58888
@REM -- This label issue can be triggered/abused in files with crlf line endings too - but it is less likely to happen accidentaly.
@REm -- See also: https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/4095133#4095133
@REM ############################################################################################################################
@REM -- Due to this issue -seemingly trivial edits of the batch file section can break the script! (for Windows anyway)
@REM -- Even something as simple as adding or removing an @REM
@REM -- From within punkshell - use:
@REM -- pmix scriptwrap.checkfile <filepath>
@REM -- to check your templates or final wrapped scripts for byte boundary issues
@REM -- It will report any labels that are on boundaries
@REM -- This is why the nextshell value above is a 2 digit key instead of a string - so that editing the value doesn't change the byte offsets.
@REM -- Editing your sh,bash,tcl,pwsh payloads is much less likely to cause an issue. There is the possibility of the final batch :exit_multishell label spanning a boundary - so testing using pmix scriptwrap.checkfile is still recommended.
@REM -- Alternatively, as you should do anyway - test the final script on windows
@REM -- Aside from adding comments/whitespace to tweak the location of labels - you can try duplicating the label (e.g just add the label on a line above) but this is not guaranteed to work in all situations.
@REM -- '@REM' is a safer comment mechanism than a leading colon - which is used sparingly here.
@REM -- A colon anywhere in the script that happens to land on a 512 Byte boundary (from file start or from a callsite) could be misinterpreted as a label
@REM -- It is unknown what versions of cmd interpreters behave this way - and pmix scriptwrap.checkfile doesn't check all such boundaries.
@REm -- For this reason, batch labels should be chosen to be relatively unlikely to collide with other strings in the file, and simple names such as :exit or :end should probably be avoided
@REM ############################################################################################################################
@REM -- custom windows payloads should be in powershell,tclsh (or sh/bash if available) code sections
@REM ## ### ### ### ### ### ### ### ### ### ### ### ### ###
@SET "winpath=%~dp0"
@SET "fname=%~nx0"
@REM @ECHO fname %fname%
@REM @ECHO winpath %winpath%
@REM @ECHO commandlineascalled %0
@REM @ECHO commandlineresolved %~f0
@CALL :getNormalizedScriptTail nftail
@REM @ECHO normalizedscripttail %nftail%
@CALL :getFileTail %0 clinetail
@REM @ECHO clinetail %clinetail%
@CALL :stringToUpper %~nx0 capscripttail
@REM @ECHO capscriptname: %capscripttail%
@IF "%nftail%"=="%capscripttail%" (
@ECHO forcing asadmin=1 due to file name on filesystem being uppercase
@SET "asadmin=1"
) else (
@CALL :stringToUpper %clinetail% capcmdlinetail
@REM @ECHO capcmdlinetail !capcmdlinetail!
IF "%clinetail%"=="!capcmdlinetail!" (
@ECHO forcing asadmin=1 due to cmdline scriptname in uppercase
@set "asadmin=1"
)
)
@SET "vbsGetPrivileges=%temp%\punk_bat_elevate_%fname%.vbs"
@SET arglist=%*
@IF "%1"=="PUNK-ELEVATED" (
GOTO :gotPrivileges
)
@IF !asadmin!==1 (
net file 1>NUL 2>NUL
@IF '!errorlevel!'=='0' ( GOTO :gotPrivileges ) else ( GOTO :getPrivileges )
)
@GOTO skip_privileges
:getPrivileges
@IF '%1'=='PUNK-ELEVATED' (echo PUNK-ELEVATED & shift /1 & goto :gotPrivileges )
@ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
@ECHO args = "PUNK-ELEVATED " >> "%vbsGetPrivileges%"
@ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
@ECHO args = args ^& strArg ^& " " >> "%vbsGetPrivileges%"
@ECHO Next >> "%vbsGetPrivileges%"
@ECHO UAC.ShellExecute "%~dp0%~n0.cmd", args, "", "runas", 1 >> "%vbsGetPrivileges%"
@ECHO Launching script in new windows due to administrator elevation
@"%SystemRoot%\System32\WScript.exe" "%vbsGetPrivileges%" %*
@EXIT /B
:gotPrivileges
@REM setlocal & pushd .
@PUSHD .
@cd /d %~dp0
@IF "%1"=="PUNK-ELEVATED" (
@DEL "%vbsGetPrivileges%" 1>nul 2>nul
@SET arglist=%arglist:~14%
)
:skip_privileges
@SET need_ps1=0
@REM we want the ps1 to exist even if the nextshell isn't powershell
@if not exist "%~dp0%~n0.ps1" (
@SET need_ps1=1
) ELSE (
fc "%~dp0%~n0.cmd" "%~dp0%~n0.ps1" >nul || goto different
@REM @ECHO "files same"
@SET need_ps1=0
)
@GOTO :pscontinue
:different
@REM @ECHO "files differ"
@SET need_ps1=1
:pscontinue
@IF !need_ps1!==1 (
COPY "%~dp0%~n0.cmd" "%~dp0%~n0.ps1" >NUL
)
@REM avoid using CALL to launch pwsh,tclsh etc - it will intercept some args such as /?
@IF "!shells[%nextshell%]!"=="pwsh" (
REM pws vs powershell hasn't been tested because we didn't need to copy cmd to ps1 this time
REM test availability of preferred option of powershell7+ pwsh
pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted; write-host "statusmessage: pwsh-found" >NUL
SET pwshtest_exitcode=!errorlevel!
REM ECHO pwshtest_exitcode !pwshtest_exitcode!
REM fallback to powershell if pwsh failed
IF !pwshtest_exitcode!==0 (
pwsh -nop -nol -c set-executionpolicy -Scope Process Unrestricted; "%~dp0%~n0.ps1" %arglist% & SET task_exitcode=!errorlevel!
) ELSE (
REM CALL powershell -nop -nol -c write-host powershell-found
REM powershell -nop -nol -file "%~dp0%~n0.ps1" %*
powershell -nop -nol -c set-executionpolicy -Scope Process Unrestricted; %~dp0%~n0.ps1" %arglist%
SET task_exitcode=!errorlevel!
)
) ELSE (
IF "!shells[%nextshell%]!"=="bash" (
CALL :getWslPath %winpath% wslpath
REM ECHO wslfullpath "!wslpath!%fname%"
!shells[%nextshell%]! "!wslpath!%fname%" %arglist% & SET task_exitcode=!errorlevel!
) ELSE (
REM probably tclsh or sh
IF NOT "x%keyRemoved%"=="x%validshells%" (
REM sh on windows uses /c/ instead of /mnt/c - at least if using msys. Todo, review what is the norm on windows with and without msys2,cygwin,wsl
REM and what logic if any may be needed. For now sh with /c/xxx seems to work the same as sh with c:/xxx
!shells[%nextshell%]! "%~dp0%fname%" %arglist% & SET task_exitcode=!errorlevel!
) ELSE (
ECHO %fname% has invalid nextshell value ^(%nextshell%^) !shells[%nextshell%]! valid options are %validshells%
SET task_exitcode=66
GOTO :exit_multishell
)
)
)
@REM batch file library functions
@GOTO :endlib
:getWslPath
@SETLOCAL
@SET "_path=%~p1"
@SET "name=%~nx1"
@SET "drive=%~d1"
@SET "rtrn=%~2"
@SET "result=/mnt/%drive:~0,1%%_path:\=/%%name%"
@ENDLOCAL & (
@if "%~2" neq "" (
SET "%rtrn%=%result%"
) ELSE (
ECHO %result%
)
)
@EXIT /B
:getFileTail
@REM return tail of file without any normalization e.g c:/punkshell/bin/Punk.cmd returns Punk.cmd even if file is punk.cmd
@REM we can't use things such as %~nx1 as it can change capitalisation
@REM This function is designed explicitly to preserve capitalisation
@REM accepts full paths with either / or \ as delimiters - or
@SETLOCAL
@SET "rtrn=%~2"
@SET "arg=%~1"
@REM @SET "result=%_arg:*/=%"
@REM @SET "result=%~1"
@SET LF=^
: The above 2 empty lines are important. Don't remove
@CALL :stringContains "!arg!" "\" hasBackSlash
@IF "!hasBackslash!"=="true" (
@for %%A in ("!LF!") do @(
@FOR /F %%B in ("!arg:\=%%~A!") do @set "result=%%B"
)
) ELSE (
@CALL :stringContains "!arg!" "/" hasForwardSlash
@IF "!hasForwardSlash!"=="true" (
@FOR %%A in ("!LF!") do @(
@FOR /F %%B in ("!arg:/=%%~A!") do @set "result=%%B"
)
) ELSE (
@set "result=%arg%"
)
)
@ENDLOCAL & (
@if "%~2" neq "" (
@SET "%rtrn%=%result%"
) ELSE (
@ECHO %result%
)
)
@EXIT /B
@REM boundary padding
:getNormalizedScriptTail
@SETLOCAL
@SET "result=%~nx0"
@SET "rtrn=%~1"
@ENDLOCAL & (
@IF "%~1" neq "" (
@SET "%rtrn%=%result%"
) ELSE (
@ECHO %result%
)
)
@EXIT /B
:getNormalizedFileTailFromPath
@REM warn via echo, and do not set return variable if path not found
@REM note that %~nx1 does not preserve case of provided path - hence the name 'normalized'
@REM boundary padding
@REM boundary padding
@SETLOCAL
@CALL :stringContains %~1 "\" hasBackSlash
@CALL :stringContains %~1 "/" hasForwardSlash
@IF "%hasBackslash%-%hasForwardslash%"=="false-false" (
@SET "P=%cd%%~1"
@CALL :getNormalizedFileTailFromPath "!P!" ftail2
@SET "result=!ftail2!"
) else (
@IF EXIST "%~1" (
@SET "result=%~nx1"
) else (
@ECHO error getNormalizedFileTailFromPath file not found: %~1
@EXIT /B 1
)
)
@SET "rtrn=%~2"
@ENDLOCAL & (
@IF "%~2" neq "" (
SET "%rtrn%=%result%"
) ELSE (
@ECHO getNormalizedFileTailFromPath %1 result: %result%
)
)
@EXIT /B
:stringContains
@REM usage: @CALL:stringContains string needle returnvarname
@SETLOCAL
@SET "rtrn=%~3"
@SET "string=%~1"
@SET "needle=%~2"
@IF "!string:%needle%=!"=="!string!" @(
@SET "result=false"
) ELSE (
@SET "result=true"
)
@ENDLOCAL & (
@IF "%~3" neq "" (
@SET "%rtrn%=%result%"
) ELSE (
@ECHO stringContains %string% %needle% result: %result%
)
)
@EXIT /B
:stringToUpper
@SETLOCAL
@SET "rtrn=%~2"
@SET "string=%~1"
@SET "capstring=%~1"
@FOR %%A in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO @(
@SET "capstring=!capstring:%%A=%%A!"
)
@SET "result=!capstring!"
@ENDLOCAL & (
@IF "%~2" neq "" (
@SET "%rtrn%=%result%"
) ELSE (
@ECHO stringToUpper %string% result: %result%
)
)
@EXIT /B
:isNumeric
@SETLOCAL
@SET "notnumeric="&FOR /F "delims=0123456789" %%i in ("%1") do set "notnumeric=%%i"
@IF defined notnumeric (
@SET "result=false"
) else (
@SET "result=true"
)
@SET "rtrn=%~2"
@ENDLOCAL & (
@IF "%~2" neq "" (
@SET "%rtrn%=%result%"
) ELSE (
@ECHO %result%
)
)
@EXIT /B
:endlib
: \
@REM @SET taskexit_code=!errorlevel! & goto :exit_multishell
@GOTO :exit_multishell
# }
# -*- tcl -*-
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
# -- tcl script section
# -- This is a punk multishell file
# -- Primary payload target is Tcl, with sh,bash,powershell as helpers
# -- but it may equally be used with any of these being the primary script.
# -- It is tuned to run when called as a batch file, a tcl script a sh/bash script or a pwsh/powershell script
# -- i.e it is a polyglot file.
# -- The specific layout including some lines that appear just as comments is quite sensitive to change.
# -- It can be called on unix or windows platforms with or without the interpreter being specified on the commandline.
# -- e.g ./filename.polypunk.cmd in sh or bash
# -- e.g tclsh filename.cmd
# --
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
rename set ""; rename s set; set k {-- "$@" "a}; if {[info exists ::env($k)]} {unset ::env($k)} ;# tidyup and restore
Hide :exit_multishell;Hide {<#};Hide '@
namespace eval ::punk::multishell {
set last_script_root [file dirname [file normalize ${argv0}/__]]
set last_script [file dirname [file normalize [info script]/__]]
if {[info exists argv0] &&
$last_script eq $last_script_root
} {
set ::punk::multishell::is_main($last_script) 1 ;#run as executable/script - likely desirable to launch application and return an exitcode
} else {
set ::punk::multishell::is_main($last_script) 0 ;#sourced - likely to be being used as a library - no launch, no exit. Can use return.
}
if {"::punk::multishell::is_main" ni [info commands ::punk::multishell::is_main]} {
proc ::punk::multishell::is_main {{script_name {}}} {
if {$script_name eq ""} {
set script_name [file dirname [file normalize [info script]/--]]
}
if {![info exists ::punk::multishell::is_main($script_name)]} {
#e.g a .dll or something else unanticipated
puts stderr "Warning punk::multishell didn't recognize info script result: $script_name - will treat as if sourced and return instead of exiting"
puts stderr "Info: script_root: [file dirname [file normalize ${argv0}/__]]"
return 0
}
return [set ::punk::multishell::is_main($script_name)]
}
}
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin Tcl Payload
#puts "script : [info script]"
#puts "argcount : $::argc"
#puts "argvalues: $::argv"
#puts "argv0 : $::argv0"
# -- --- --- --- --- --- --- --- --- --- --- ---
#<tcl-payload>
#</tcl-payload>
# -- --- --- --- --- --- --- --- --- --- --- ---
# -- Best practice is to always return or exit above, or just by leaving the below defaults in place.
# -- If the multishell script is modified to have Tcl below the Tcl Payload section,
# -- then Tcl bracket balancing needs to be carefully managed in the shell and powershell sections below.
# -- Only the # in front of the two relevant if statements below needs to be removed to enable Tcl below
# -- but the sh/bash 'then' and 'fi' would also need to be uncommented.
# -- This facility left in place for experiments on whether configuration payloads etc can be appended
# -- to tail of file - possibly binary with ctrl-z char - but utility is dependent on which other interpreters/shells
# -- can be made to ignore/cope with such data.
if {[::punk::multishell::is_main]} {
exit 0
} else {
return
}
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end Tcl Payload
# end hide from unix shells \
HEREDOC1B_HIDE_FROM_BASH_AND_SH
# sh/bash \
shift && set -- "${@:1:$#-1}"
#------------------------------------------------------
# -- This if block only needed if Tcl didn't exit or return above.
if false==false # else {
then
: #
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
# -- sh/bash script section
# -- leave as is if all that is required is launching the Tcl payload"
# --
# -- Note that sh/bash script isn't called when running a .bat/.cmd from cmd.exe on windows by default
# -- adjust @call line above ... to something like @call sh ... @call bash .. or @call env sh ... etc as appropriate
# -- if sh/bash scripting needs to run on windows too.
# --
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin sh Payload
#printf "start of bash or sh code"
#<shell-payload-pre-tcl>
#</shell-payload-pre-tcl>
# -- --- --- --- --- --- --- ---
#<shell-launch-tcl>
exitcode=0 ;#default assumption
#-- sh/bash launches Tcl here instead of shebang line at top
#-- use exec to use exitcode (if any) directly from the tcl script
#exec /usr/bin/env tclsh "$0" "$@"
#-- alternative - can run sh/bash script after the tcl call.
/usr/bin/env tclsh "$0" "$@"
exitcode=$?
#echo "tcl exitcode: ${exitcode}"
#-- override exitcode example
#exit 66
#</shell-launch-tcl>
# -- --- --- --- --- --- --- ---
#<shell-payload-post-tcl>
#</shell-payload-post-tcl>
#printf "sh/bash done \n"
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end sh Payload
#------------------------------------------------------
fi
exit ${exitcode}
# end hide sh/bash block from Tcl
# This comment with closing brace should stay in place whether if commented or not }
#------------------------------------------------------
# begin hide powershell-block from Tcl - only needed if Tcl didn't exit or return above
if 0 {
: end heredoc1 - end hide from powershell \
'@
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
# -- powershell/pwsh section
# -- Do not edit if current file is the .ps1
# -- Edit the corresponding .cmd and it will autocopy
# -- unbalanced braces { } here *even in comments* will cause problems if there was no Tcl exit or return above
# ## ### ### ### ### ### ### ### ### ### ### ### ### ###
function GetScriptName { $myInvocation.ScriptName }
$scriptname = getScriptName
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin powershell Payload
#"Timestamp : {0,10:yyyy-MM-dd HH:mm:ss}" -f $(Get-Date) | write-host
#"Script Name : {0}" -f $scriptname | write-host
#"Powershell Version: {0}" -f $PSVersionTable.PSVersion.Major | write-host
#"powershell args : {0}" -f ($args -join ", ") | write-host
# -- --- --- ---
#<powershell-payload-pre-tcl>
#</powershell-payload-pre-tcl>
# -- --- --- --- --- --- --- ---
#<powershell-launch-tcl>
tclsh $scriptname $args
#</powershell-launch-tcl>
# -- --- --- --- --- --- --- ---
#<powershell-payload-post-tcl>
#</powershell-payload-post-tcl>
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---end powershell Payload
#"powershell reporting exitcode: {0}" -f $LASTEXITCODE | write-host
Exit $LASTEXITCODE
# heredoc2 for powershell to ignore block below
$1 = @'
'
: comment end hide powershell-block from Tcl \
# This comment with closing brace should stay in place whether 'if' commented or not }
: multishell doubled-up cmd exit label - return exitcode
:exit_multishell
:exit_multishell
: \
@REM @ECHO exitcode: !task_exitcode!
: \
@IF "%1"=="PUNK-ELEVATED" (echo. & @cmd /k echo elevated prompt: type exit to quit)
: \
@EXIT /B !task_exitcode!
# cmd has exited
: comment end heredoc2 \
'@
<#
# id:tailblock0
# -- powershell multiline comment
#>
<#
# id:tailblock1
# <ctrl-z>

# </ctrl-z>
# -- unreachable by tcl directly if ctrl-z character is in the <ctrl-z> section above. (but file can be read and split on \x1A)
# -- Potential for zip and/or base64 contents, but we can't stop pwsh parser from slurping in the data
# -- so for example a plain text tar archive could cause problems depending on the content.
# -- final line in file must be the powershell multiline comment terminator or other data it can handle.
# -- e.g plain # comment lines will work too
# -- (for example a powershell digital signature is a # commented block of data at the end of the file)
#>

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

@ -356,8 +356,8 @@ namespace eval punk::ns {
}
return [list_as_lines [lsort $result]]
#.= lsort $result |> list_as_lines
return [list_as_lines -- [lsort $result]]
#.= lsort $result |> list_as_lines --
}
proc nsglob_as_re {glob} {

46
src/modules/punk/path-999999.0a1.0.tm

@ -198,29 +198,47 @@ namespace eval punk::path {
#todo - implement treefiles which acts like dirfiles but allows path globbing in the same way as punk::ns::ns/
#then review if treefiles can replace dirfiles or if both should exist (dirfiles can have literal glob chars in path segments - but that is a rare usecase)
proc treefilenames {basepath tailglob args} {
proc treefilenames {args} {
#*** !doctools
#[call [fun treefilenames] [arg basepath] [arg tailglob] [opt {option value...}]]
#[para]basic (glob based) list of filenames matching tailglob - recursive
#[call [fun treefilenames] [opt {option value...}] [opt {globpattern...}]]
#[para]basic (glob based) list of filenames matching each pattern in tailglobs - recursive
#[para] options:
#[para] [opt -dir] <path>
#[para] defaults to [lb]pwd[rb] - base path for tree to search
#[para] [opt -antiglob_paths] <list>
#[para] list of path patterns to exclude - may include * and ** path segments e.g /usr/**
#[para]no natsorting - so order is dependent on filesystem
set defaults [dict create\
-call-depth-internal 0\
-antiglob_paths {}\
]
set opts [dict merge $defaults $args]
lassign [punk::get_leading_opts_and_values {
-directory "\uFFFF"
-call-depth-internal 0
-antiglob_paths {}
} $args] _o opts _v tailglobs
# -- --- --- --- --- --- ---
set opt_antiglob_paths [dict get $opts -antiglob_paths]
set CALLDEPTH [dict get $opts -call-depth-internal]
# -- --- --- --- --- --- ---
set opt_dir [dict get $opts -directory]
if {$opt_dir eq "\uFFFF"} {
set opt_dir [pwd]
}
# -- --- --- --- --- --- ---
set files [list]
if {$CALLDEPTH == 0} {
if {![file isdirectory $basepath]} {
if {![file isdirectory $opt_dir]} {
return [list]
}
set opts [dict merge $opts [list -directory $opt_dir]]
if {![llength $tailglobs]} {
lappend tailglobs *
}
}
set skip 0
foreach anti $opt_antiglob_paths {
if {[globmatchpath $anti $basepath]} {
if {[globmatchpath $anti $opt_dir]} {
set skip 1
break
}
@ -230,9 +248,9 @@ namespace eval punk::path {
}
#todo - account for vfs where matched path could appear to be a directory but is mounted so could be a desired match?
set dirfiles [glob -nocomplain -dir $basepath -type f $tailglob]
set dirfiles [lsort [glob -nocomplain -dir $opt_dir -type f {*}$tailglobs]]
lappend files {*}$dirfiles
set dirdirs [glob -nocomplain -dir $basepath -type d *]
set dirdirs [glob -nocomplain -dir $opt_dir -type d *]
foreach dir $dirdirs {
set skip 0
foreach anti $opt_antiglob_paths {
@ -244,8 +262,8 @@ namespace eval punk::path {
if {$skip} {
continue
}
set nextargs [dict merge $args [list -call-depth-internal [incr CALLDEPTH]]]
lappend files {*}[treefilenames $dir $tailglob {*}$nextargs]
set nextopts [dict merge $opts [list -directory $dir -call-depth-internal [incr CALLDEPTH]]]
lappend files {*}[treefilenames {*}$nextopts {*}$tailglobs]
}
return $files
}

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

@ -64,16 +64,28 @@ namespace eval textblock {
}
}
proc width {block} {
if {$block eq ""} {
return 0
}
set block [textutil::tabify::untabify2 $block]
if {[string first \n $block] >= 0} {
return [tcl::mathfunc::max {*}[lmap v [lines_as_list -- $block] {::punk::char::string_width [stripansi $v]}]]
}
return [punk::char::string_width [stripansi $block]]
}
#must be able to handle block as string with or without newlines
#if no newlines - attempt to treat as a list
#must handle whitespace-only string,list elements, and/or lines.
proc width {block} {
#reviewing 2024 - this seems like too much magic!
proc width1 {block} {
if {$block eq ""} {
return 0
}
set block [textutil::tabify::untabify2 $block]
if {[string first \n $block] >= 0} {
return [tcl::mathfunc::max {*}[lmap v [linelist $block] {::punk::char::string_width [stripansi $v]}]]
return [tcl::mathfunc::max {*}[lmap v [lines_as_list -- $block] {::punk::char::string_width [stripansi $v]}]]
}
if {[catch {llength $block}]} {
return [::punk::char::string_width [stripansi $block]]
@ -84,8 +96,8 @@ namespace eval textblock {
}
return [tcl::mathfunc::max {*}[lmap v $block {::punk::char::string_width [stripansi $v]}]]
}
pipealias ::textblock::padleft .= {list $input [string repeat " " $indent]} |/0,padding/1> linelist |> .= {lmap v $data {overtype::right $padding $v}} |> list_as_lines <input/0,indent/1|
pipealias ::textblock::padright .= {list $input [string repeat " " $colsize]} |/0,padding/1> linelist |> .= {lmap v $data {overtype::left $padding $v}} |> list_as_lines <input/0,colsize/1|
pipealias ::textblock::padleft .= {list $input [string repeat " " $indent]} |/0,padding/1> lines_as_list -- |> .= {lmap v $data {overtype::right $padding $v}} |> list_as_lines -- <input/0,indent/1|
pipealias ::textblock::padright .= {list $input [string repeat " " $colsize]} |/0,padding/1> lines_as_list -- |> .= {lmap v $data {overtype::left $padding $v}} |> list_as_lines -- <input/0,colsize/1|
proc ::textblock::pad {block args} {
set defaults [dict set\
-padchar " "\
@ -125,21 +137,43 @@ namespace eval textblock {
pipealias ::textblock::join_width .= {list $lhs [string repeat " " $w1] $rhs [string repeat " " $w2]} {|
/2,col1/1,col2/3
>} linelist -block {} {|
>} lines_as_list -- {|
data2
>} .=lhs> linelist -block {} {|
>} .=lhs> lines_as_list -- {|
>} .= {lmap v $data w $data2 {val "[overtype::left $col1 $v][overtype::left $col2 $w]"}} {|
>} list_as_lines <lhs/0,w1/1,rhs/2,w2/3|
>} list_as_lines -- <lhs/0,w1/1,rhs/2,w2/3|
pipealias ::textblock::join .= {list $lhs [string repeat " " [width $lhs]] $rhs [string repeat " " [width $rhs]]} {|
pipealias ::textblock::joinpair .= {list $lhs [string repeat " " [width $lhs]] $rhs [string repeat " " [width $rhs]]} {|
/2,col1/1,col2/3
>} .=> linelist -block {} {|
>} .=> lines_as_list -- {|
data2
>} .=lhs> linelist -block {} {|
>} .=lhs> lines_as_list -- {|
>} .= {lmap v $data w $data2 {val "[overtype::left $col1 $v][overtype::left $col2 $w]"}} {|
>} list_as_lines <lhs/0,rhs/1|
proc ::textblock::join2 {lhs rhs} {
>} list_as_lines -- <lhs/0,rhs/1|
proc ::textblock::join {args} {
lassign [punk::args::opts_values {
blocks -type string -multiple 1
} $args] _o opts _v values
set blocks [dict get $values blocks]
set idx 0
set fordata [list]
foreach b $blocks {
set c($idx) [string repeat " " [width $b]]
lappend fordata "v($idx)" [lines_as_list -- $b]
incr idx
}
set outlines [list]
foreach {*}$fordata {
set row ""
foreach colidx [lsort -integer -increasing [array names c]] {
append row [overtype::left $c($colidx) $v($colidx)]
}
lappend outlines $row
}
return [list_as_lines -- $outlines]
}
namespace eval ::textblock::piper {
@ -151,15 +185,25 @@ namespace eval textblock {
pipealias ::textblock::join_right .= {list $lhs [string repeat " " [width $lhs]] $rhs [string repeat " " [width $rhs]]} {|
/2,col1/1,col2/3
>} .=> linelist -block {} {|
>} .=> lines_as_list -- {|
data2
>} .=lhs> linelist -block {} {|
>} .=lhs> lines_as_list -- {|
>} .= {lmap v $data w $data2 {val "[overtype::right $col1 $v][overtype::right $col2 $w]"}} {|
>} list_as_lines <lhs/0,rhs/1|
proc example {{text "test\netc\nmore text"}} {
package require patternpunk
.= textblock::join [list 1 2 3 4 5 6 7] [>punk . lhs] |> .=>1 textblock::join " " |> .=>1 textblock::join $text |> .=>1 textblock::join [>punk . rhs] |> .=>1 textblock::join [lrepeat 7 " | "]
.= textblock::join [punk::list_as_lines -- [list 1 2 3 4 5 6 7]] [>punk . lhs] |> .=>1 textblock::join " " |> .=>1 textblock::join $text |> .=>1 textblock::join [>punk . rhs] |> .=>1 textblock::join [punk::list_as_lines -- [lrepeat 7 " | "]]
}
proc example2 {{text "test\netc\nmore text"}} {
package require patternpunk
.= textblock::join\
[punk::list_as_lines -- [list 1 2 3 4 5 6 7 8]]\
[>punk . lhs]\
" "\
$text\
[>punk . rhs]\
[punk::list_as_lines -- [lrepeat 8 " | "]]
}
proc frame {args} {

48
src/punk86.vfs/lib/app-punk/repl.tcl

@ -15,8 +15,9 @@ set original_tm_list [tcl::tm::list]
tcl::tm::remove {*}$original_tm_list
#tm list first added end up later in the list - and then override earlier ones if version the same - so add pwd-relative 1st to give higher priority
#(only if Tcl has scanned all paths - see below bogus package load)
#1
if {[file exists [pwd]/modules]} {
if {[file isdirectory [pwd]/modules]} {
catch {tcl::tm::add [pwd]/modules}
}
@ -30,7 +31,7 @@ if {[string match "*.vfs/*" [file normalize [info script]]]} {
set modulefolder [file dirname [file dirname [info nameofexecutable]]]/modules
}
if {[file exists $modulefolder]} {
if {[file isdirectory $modulefolder]} {
tcl::tm::add $modulefolder
} else {
puts stderr "Warning unable to find module folder at: $modulefolder"
@ -38,12 +39,34 @@ if {[file exists $modulefolder]} {
#libs are appended to end - so add higher prioriy libraries last (opposite to modules)
#auto_path - add exe-relative after exe-relative path
set libfolder [file dirname [file dirname [info nameofexecutable]]]/lib
if {[file exists $libfolder]} {
lappend ::auto_path $libfolder
}
if {[file exists [pwd]/lib]} {
lappend ::auto_path [pwd]/lib
if {"windows" eq $::tcl_platform(platform)} {
#case differences dont matter - but can stop us finding path in auto_path
set libfolder [file dirname [file dirname [info nameofexecutable]]]/lib
if {[string tolower $libfolder] ni [string tolower $::auto_path]} {
if {[file isdirectory $libfolder]} {
lappend ::auto_path $libfolder
}
}
set libfolder [pwd]/lib
if {[string tolower $libfolder] ni [string tolower $::auto_path]} {
if {[file isdirectory $libfolder]} {
lappend ::auto_path $libfolder
}
}
} else {
#case differences could represent different paths
set libfolder [file dirname [file dirname [info nameofexecutable]]]/lib
if {$libfolder ni $::auto_path} {
if {[file isdirectory $libfolder]} {
lappend ::auto_path $libfolder
}
}
set libfolder [pwd]/lib
if {$libfolder ni $::auto_path} {
if {[file isdirectory $libfolder]} {
lappend ::auto_path $libfolder
}
}
}
@ -68,13 +91,12 @@ if {[llength $currentdir_modules]} {
catch {tcl::tm::add [pwd]}
}
#puts stdout "$::auto_path"
package require Thread
catch {package require tcllibc}
#These are strong dependencies
# - the repl requires Threading and punk,shellfilter,shellrun to call and display properly.
# tm list already indexed - need 'package forget' to find modules based on current tcl::tm::list
# tm list already indexed - 'package forget' to find modules based on current tcl::tm::list
set required [list\
shellfilter\
shellrun\
@ -94,11 +116,15 @@ set tm_list_now [tcl::tm::list]
foreach p $original_tm_list {
if {$p ni $tm_list_now} {
#the prior tm paths go to the head of the list.
#They are processed first.. but an item of same version later in the list will override one at the head.
#They are processed first.. but an item of same version later in the list will override one at the head. (depending on when list was scanned)
tcl::tm::add $p
}
}
#------------------------------------------------------------------------------
#now we need to package require a bogus package to ensure Tcl scans the whole auto_path/tm list - otherwise a lower-versioned module earlier in the path may be loaded
#This seems to take not insignificant time (e.g 2023 i9 100ms) - but seems unavoidable for now
catch {package require flobrudder666_nonexistant}
#------------------------------------------------------------------------------
foreach pkg $required {
package require $pkg

1
src/punk86.vfs/lib/app-shellspy/shellspy.tcl

@ -46,7 +46,6 @@ if 0 {
#fconfigure stdin -encoding utf-16le
#fconfigure stdout -encoding utf-16le
#tcl::tm::remove {*}[tcl::tm::list]
set original_tm_list [tcl::tm::list]
tcl::tm::remove {*}$original_tm_list
set minimal_tm_list [list] ;#used initially to ensure core modules are loaded from a reduced set of paths to preference current project

49
src/scriptapps/create-localmachine-selfsigned-cert.ps1

@ -0,0 +1,49 @@
function ParameterDefinitions {
param(
[Parameter()][string] $subject = 'punkshell local',
[Parameter(Mandatory)][string] $friendlyname = 'test code signing',
[Parameter(ValueFromRemainingArguments)] $opts
)
}
function psmain {
[CmdletBinding()]
param()
dynamicparam { GetDynamicParamDictionary ParameterDefinitions }
process {
#called once with $PSBoundParameters dictionary
#can be used to validate arguments, or set a simpler variable name for access
switch ($PSBoundParameters.keys) {
'subject' {
Set-Variable -Name $_ -Value $PSBoundParameters."$_"
}
#...
}
}
end {
Write-Host "Certificate subject is: $subject"
$params = @{
Subject = $subject
Type = 'CodeSigningCert'
KeySpec = 'Signature'
KeyUsage = 'DigitalSignature'
FriendlyName = $friendlyname
CertStoreLocation = 'Cert:\LocalMachine\My'
NotAfter = (Get-Date).AddMonths(60)
}
Write-Host "please wait..."
#$cert = New-SelfSignedCertificate @params
#Write-Host "cert: $cert"
#echo "Exporting to file for import to certstore.."
#Export-Certificate -FilePath exported_localmachine_new-cert.cer -Cert $cert
#Import-Certificate -Filepath exported_localmachine_new-cert.cer -CertstoreLocation Cert:\LocalMachine\My
#echo "Finished importing.."
}
}
psmain @args

117
src/vendorlib/pluginmgr/ChangeLog

@ -0,0 +1,117 @@
2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.15 ========================
*
2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.14 ========================
*
2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.13 ========================
*
2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.12 ========================
*
2009-03-30 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* pluginmgr.tcl: Hook into the log system of the SafeBase to get
* pkgIndex.tcl: more detailed error information when a plugin
* pluginmgr.man: could not be loaded. Bumped to version 0.3
2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.11.1 ========================
*
2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.11 ========================
*
2007-09-19 Andreas Kupries <akupries@shaw.ca>
* pluginmgr.tcl (AddPaths): Fixed typo in the code converting the
* pluginmgr.man: package pattern into a regular expression. A
* pkgIndex.tcl: bogus space char separated * from its quoting
backslash, end result was a RE pattern without capturing
parentheses, so the plugin name was never properly extracted
from the package name. This fixes [SF Tcllib Bug 1798210].
Thanks to Jeremy Cowgar for the report. Additionally added
support for directory 'plugins' in the dot-directories
(pluginmgr::path). Package version bumped to 0.2.
2007-09-19 Andreas Kupries <andreask@activestate.com>
* pluginmgr.man: Fixed typo in the example, used the wrong
dot-path (.../plugins, should be .../plugin). Thanks to Jeremy
Cowgar for finding this.
2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.10 ========================
*
2007-06-22 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* pluginmgr.tcl: Replaced deprecated {expand} syntax in comments
with {*}.
2007-03-21 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* pluginmgr.man: Fixed all warnings due to use of now deprecated
commands. Added a section about how to give feedback.
2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.9 ========================
*
2005-10-06 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
* Released and tagged Tcllib 1.8 ========================
*
2005-07-07 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* pluginmgr.tcl: Added and documented option -setup,
* pluginmgr.man: and clone method.
2005-06-17 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* pluginmgr.tcl: Validation now reports missing API commands. Set
plugin name before external check, so that the callback can use
'do' when inspecting the plugin. Command setup has no need for
alias deletion, interpreters are always new and clean.
* plugin.tcl: Changed completely to a package based approach,
* plugin.man: using the safe base for proper package
handling. Plugins are packages. Framework has to perform only
validation and initialization, not search.
2005-04-13 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* plugin.tcl: Updated to changed method names in path, added
* plugin.man: method 'interpreter', and fixed bug in alias
creation.
2005-04-11 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* plugin.man: New module for the management of plugins.
* plugin.tcl:
* pkgIndex.tcl:

2
src/vendorlib/pluginmgr/pkgIndex.tcl

@ -0,0 +1,2 @@
if {![package vsatisfies [package provide Tcl] 8.5 9]} {return}
package ifneeded pluginmgr 0.4 [list source [file join $dir pluginmgr.tcl]]

427
src/vendorlib/pluginmgr/pluginmgr.man

@ -0,0 +1,427 @@
[comment {-*- tcl -*- paths manpage}]
[manpage_begin pluginmgr n 0.4]
[keywords {plugin management}]
[keywords {plugin search}]
[copyright {2005 Andreas Kupries <andreas_kupries@users.sourceforge.net>}]
[moddesc {Plugin management}]
[titledesc {Manage a plugin}]
[category {Programming tools}]
[require Tcl "8.5 9"]
[require pluginmgr [opt 0.4]]
[description]
This package provides commands and objects for the generic management
of plugins which can be loaded into an application.
[para]
To avoid the implementation of yet another system to locate Tcl code
the system provides by this package is built on top of the regular
package management system. Each plugin is considered as a package and
a simple invokation of [cmd {package require}] is enough to locate and
load it, if it exists. The only time we will need additional paths is
when a plugin manager is part of a wrapped application and has to be
able to search for plugins existing outside of that application. For
this situation the package provides a command to create a general set
of such paths based on names for the plugin manager and/or application
in question.
[para]
The main contribution of this package is a generic framework which
allows the easy declaration of
[list_begin enumerated]
[enum]
How to translate a plugin name to the name of the package implementing
it, and vice versa.
[enum]
The list of commands a plugin has to provide as API, and also of more
complex checks as code.
[enum]
The list of commands expected by the plugin from the environment.
[list_end]
This then allows the easy generation of plugin managers customized to
particular types of plugins for an application.
[para]
It should be noted that all plugin code is considered untrusted and
will always be executed within a safe interpreter. The interpreter is
enabled enough to allow plugins the loading of all additional packages
they may need.
[section {PUBLIC API}]
[subsection {PACKAGE COMMANDS}]
[list_begin definitions]
[call [cmd ::pluginmgr] [arg objectName] [opt [arg "option value"]...]]
This command creates a new plugin manager object with an associated
Tcl command whose name is [arg objectName]. This [term object] command
is explained in full detail in the sections [sectref {OBJECT COMMAND}]
and [sectref {OBJECT METHODS}]. The object command will be created
under the current namespace if the [arg objectName] is not fully
qualified, and in the specified namespace otherwise.
[para]
The options and their values coming after the name of the object are
used to set the initial configuration of the mamager object,
specifying the applicable plugins and their API.
[call [cmd ::pluginmgr::paths] [arg objectName] [arg name]...]
This utility command adds a set of paths to the specified object,
based on the given [arg name]s.
It will search for:
[list_begin enumerated]
[enum]
The environment variable [var [arg name]_PLUGINS]. Its contents will
be interpreted as a list of package paths. The entries have to be
separated by either [const :] (unix) or [const \;] (windows).
[para]
The name will be converted to upper-case letters.
[enum]
The registry entry "HKEY_LOCAL_MACHINE\SOFTWARE\[arg name]\PLUGINS".
Its contents will be interpreted as a list of package paths. The
entries have to be separated by [const \;]. This item is considered
only when on Windows (tm).
[para]
The casing of letters is not changed.
[enum]
The registry entry "HKEY_CURRENT_USER\SOFTWARE\[arg name]\PLUGINS".
Its contents will be interpreted as a list of package paths. The
entries have to be separated by [const \;]. This item is considered
only when on Windows (tm).
[para]
The casing of letters is not changed.
[enum]
The directory [file "~/.[arg name]/plugin"].
[enum]
The directory [file "~/.[arg name]/plugins"].
[para]
The casing of letters is not changed.
[list_end]
[para]
and add all the paths found that way to the list of package paths
maintained by the object.
[para]
If [arg name] is namespaced each item in the list will be repeated per
prefix of [arg name], with conversion of :-sequences into the proper
separator (underscore for environment variables, backslash for
registry entries, and / for directories).
[para]
Examples:
[para]
[example {
::pluginmgr::paths ::obj docidx
=> env DOCIDX_PLUGINS
reg HKEY_LOCAL_MACHINE\SOFTWARE\docidx\PLUGINS
reg HKEY_CURRENT_USER\SOFTWARE\docidx\PLUGINS
path ~/.docidx/plugins
::pluginmgr::paths ::obj doctools::idx
=> env DOCTOOLS_PLUGINS
env DOCTOOLS_IDX_PLUGINS
reg HKEY_LOCAL_MACHINE\SOFTWARE\doctools\PLUGINS
reg HKEY_LOCAL_MACHINE\SOFTWARE\doctools\idx\PLUGINS
reg HKEY_CURRENT_USER\SOFTWARE\doctools\PLUGINS
reg HKEY_CURRENT_USER\SOFTWARE\doctools\idx\PLUGINS
path ~/.doctools/plugin
path ~/.doctools/idx/plugin
}]
[list_end]
[subsection {OBJECT COMMAND}]
All commands created by the command [cmd ::pluginmgr] (See section
[sectref {PACKAGE COMMANDS}]) have the following general form and may
be used to invoke various operations on their plugin manager object.
[list_begin definitions]
[call [cmd objectName] [method method] [opt [arg "arg arg ..."]]]
The method [method method] and its [arg arg]'uments determine the exact
behavior of the command. See section [sectref {OBJECT METHODS}] for
the detailed specifications.
[list_end]
[subsection {OBJECT METHODS}]
[list_begin definitions]
[call [arg objectName] [method clone]]
This method creates a new plugin management object and returns the
associated object command. The generated object is a clone of the
object the method was invoked on. I.e. the new object will have the
same configuration as the current object. With regard to state, if the
current object has a plugin loaded then this plugin and all associated
state is moved to the generated clone and the current object is reset
into the base state (no plugin loaded). In this manner a configured
plugin manager is also a factory for loaded plugins.
[call [arg objectName] [method configure]]
The method returns a list of all known options and their current
values when called without any arguments.
[call [arg objectName] [method configure] [arg option]]
The method behaves like the method [method cget] when called with a
single argument and returns the value of the option specified by said
argument.
[call [arg objectName] [method configure] [option -option] [arg value]...]
The method reconfigures the specified [option option]s of the object,
setting them to the associated [arg value]s, when called with an even
number of arguments, at least two.
[para]
The legal options are described in the section
[sectref {OBJECT CONFIGURATION}].
[call [arg objectName] [method cget] [option -option]]
This method expects a legal configuration option as argument and will
return the current value of that option for the object the method was
invoked for.
[para]
The legal configuration options are described in section
[sectref {OBJECT CONFIGURATION}].
[call [arg objectName] [method destroy]]
This method destroys the object it is invoked for.
[call [arg objectName] [method do] [arg arg]...]
This method interprets its list of arguments as the words of a command
and invokes this command in the execution context of the plugin.
The result of the invoked command is made the result of the method.
The call will fail with an error if no valid plugin has been loaded
into the manager object.
[call [arg objectName] [method interpreter]]
This method returns the handle of the safe interpreter the current
plugin is loaded into. An empty string as return value signals that
the manager currently has no valid plugin loaded.
[call [arg objectName] [method plugin]]
This method returns the name of the plugin currently loaded. An empty
string as return value signals that the manager currently has no valid
plugin loaded.
[call [arg objectName] [method load] [arg string]]
This method loads, validates, and initializes a named plugin into the
manager object.
[para]
The algorithm to locate and load the plugin employed is:
[list_begin enumerated]
[enum]
If the [arg string] contains the path to an existing file then this
file is taken as the implementation of the plugin.
[enum]
Otherwise the plugin name is translated into a package name via the value
of the option [option -pattern] and then loaded through the
regular package management.
[enum]
The load fails.
[list_end]
[para]
The algorithm to validate and initialize the loaded code is:
[list_begin enumerated]
[enum]
If the option [option -api] is non-empty introspection commands are
used to ascertain that the plugin provides the listed commands.
[enum]
If the option [option -check] is non-empty the specified command
prefix is called.
[enum]
If either of the above fails the candidate plugin is unloaded again
[enum]
Otherwise all the commands specified via the option
[option -cmds] are installed in the plugin.
[list_end]
[para]
A previously loaded plugin is discarded, but only if the new plugin
was found and sucessfully validated and initialized. Note that there
will be no intereference between old and new plugin as both will be
put into separate safe interpreters.
[call [arg objectName] [method unload]]
This method unloads the currently loaded plugin. It returns the empty
string. The call will be silently ignored if no plugin is loaded at
all.
[call [arg objectName] [method list]]
This method uses the contents of the option [option -pattern] to find
all packages which can be plugins under the purview of this manager
object. It translates their names into plugin names and returns a list
containing them.
[call [arg objectName] [method path] [arg path]]
This methods adds the specified [arg path] to the list of additional
package paths to look at when searching for a plugin. It returns the
empty string. Duplicate paths are ignored, i.e. each path is added
only once. Paths are made absolute, but are not normalized.
[call [arg objectName] [method paths]]
This method returns a list containing all additional paths which have
been added to the plugin manager object since its creation.
[list_end]
[subsection {OBJECT CONFIGURATION}]
All plugin manager objects understand the following configuration options:
[list_begin options]
[opt_def -pattern [arg string]]
The value of this option is a glob pattern which has to contain
exactly one '*'-operator. All packages whose names match this pattern
are the plugins recognized by the manager object. And vice versa, the
replacement of the '*'-operator with a plugin name will yield the name
of the package implementing that plugin.
[para]
This option has no default, except if option [option -name] was set.
It has to be set before attempting to load a plugin, either directly,
or through option [option -name].
[opt_def -api [arg list]]
The value of this option is a list of command names, and any plugin
loaded has to provide these commands. Names which are not fully
qualified are considered to be rooted in the global namespace.
If empty no expectations are made on the plugin. The default value is
the empty list.
[opt_def -check [arg cmdprefix]]
The value of this option is interpreted as a command prefix.
Its purpose is to perform complex checks on a loaded plugin package to
validate it, which go beyond a simple list of provided commands.
[para]
It is called with the manager object command as the only argument and
has to return a boolean value. A value of [const true] will be
interpreted to mean that the candidate plugin passed the test.
The call will happen if and only if the candidate plugin already
passed the basic API check specified through the option [option -api].
[para]
The default value is the empty list, which causes the manager object
to suppress the call and to assume the candidate plugin passes.
[opt_def -cmds [arg dict]]
The value of this option is a dictionary. It specifies the commands
which will be made available to the plugin (as keys), and the trusted
commands in the environment which implement them (as values).
The trusted commands will be executed in the interpreter specified by
the option [option -cmdip].
The default value is the empty dictionary.
[opt_def -cmdip [arg ipspec]]
The value of this option is the path of the interpreter where the
trusted commands given to the plugin will be executed in.
The default is the empty string, referring to the current interpreter.
[opt_def -setup [arg cmdprefix]]
The value of this option is interpreted as a command prefix.
[para]
It is called whenever a new safe interpreter for a plugin has been
created, but before a plugin is loaded. It is provided with the
manager object command and the interpreter handle as its only
arguments. Any return value will be ignored.
[para]
Its purpose is give a user of the plugin management the ability to
define commands, packages, etc. a chosen plugin may need while being
loaded.
[list_end]
[vset CATEGORY pluginmgr]
[include ../common-text/feedback.inc]
[manpage_end]

429
src/vendorlib/pluginmgr/pluginmgr.tcl

@ -0,0 +1,429 @@
# plugin.tcl --
#
# Generic plugin management.
#
# Copyright (c) 2005 Andreas Kupries <andreas_kupries@sourceforge.net>
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id: pluginmgr.tcl,v 1.8 2009/03/31 02:14:40 andreas_kupries Exp $
# ### ### ### ######### ######### #########
## Description
# Each instance of the plugin manager can be configured with data
# which specifies where to find plugins, and how to validate
# them. With that it can then be configured to load and provide access
# to a specific plugin, doing all required checks and
# initialization. Users for specific plugin types simply have to
# encapsulate the generic class, providing all the specifics, leaving
# their users only the task of naming the requested actual plugin.
# ### ### ### ######### ######### #########
## Requisites
package require Tcl 8.5 9
package require snit
# ### ### ### ######### ######### #########
## Implementation
snit::type ::pluginmgr {
# ### ### ### ######### ######### #########
## Public API - Options
# - Pattern to match package name. Exactly one '*'. No default.
# - List of commands the plugin has to provide. Empty list default.
# - Callback for additional checking after the API presence has
# been verified. Empty list default.
# - Dictionary of commands to put into the plugin interpreter.
# Key: cmds for plugin, value is cmds to invoke for them.
# - Interpreter to use for the -cmds (invoked commands). Default
# is current interp.
# - Callback for additional setup actions on the plugin
# interpreter after its creation, but before plugin is loaded into
# it. Empty list default.
option -pattern {}
option -api {}
option -check {}
option -cmds {}
option -cmdip {}
option -setup {}
# ### ### ### ######### ######### #########
## Public API - Methods
method do {args} {
if {$plugin eq ""} {
return -code error "No plugin defined"
}
return [$sip eval $args]
}
method interpreter {} {
return $sip
}
method plugin {} {
return $plugin
}
method load {name} {
if {$name eq $plugin} return
if {$options(-pattern) eq ""} {
return -code error "Translation pattern is not configured"
}
set save $sip
$self SetupIp
if {![$self LoadPlugin $name]} {
set sip $save
return -code error "Unable to locate or load plugin \"$name\" ($myloaderror)"
}
if {![$self CheckAPI missing]} {
set sip $save
return -code error \
"Cannot use plugin \"$name\", API incomplete: \"$missing\" missing"
}
set savedname $plugin
set plugin $name
if {![$self CheckExternal]} {
set sip $save
set plugin $savedname
return -code error \
"Cannot use plugin \"$name\", API bad"
}
$self SetupExternalCmds
if {$save ne ""} {interp delete $save}
return
}
method unload {} {
if {$sip eq ""} return
interp delete $sip
set sip ""
set plugin ""
return
}
method list {} {
if {$options(-pattern) eq ""} {
return -code error "Translation pattern is not configured"
}
set save $sip
$self SetupIp
set result {}
set pattern [string map [list \
+ \\+ ? \\? \
\[ \\\[ \] \\\] \
( \\( ) \\) \
. \\. \* {(.*)} \
] $options(-pattern)]
# @mdgen NODEP: bogus-package
$sip eval {catch {package require bogus-package}}
foreach p [$sip eval {package names}] {
if {![regexp $pattern $p -> plugin]} continue
lappend result $plugin
}
interp delete $sip
set sip $save
return $result
}
method path {path} {
set path [file join [pwd] $path]
if {[lsearch -exact $paths $path] < 0} {
lappend paths $path
}
return
}
method paths {} {
return $paths
}
method clone {} {
set o [$type create %AUTO% \
-pattern $options(-pattern) \
-api $options(-api) \
-check $options(-check) \
-cmds $options(-cmds) \
-cmdip $options(-cmdip) \
-setup $options(-setup)]
$o __clone__ $paths $sip $plugin
# Clone has become owner of the interp.
set sip {}
set plugin {}
return $o
}
method __clone__ {_paths _sip _plugin} {
set paths $_paths
set sip $_sip
set plugin $_plugin
return
}
# ### ### ### ######### ######### #########
## Internal - Configuration and state
variable paths {} ; # List of paths to provide the sip with.
variable sip {} ; # Safe interp used for plugin execution.
variable plugin {} ; # Name of currently loaded plugin.
variable myloaderror {} ; # Last error reported by the Safe base
# ### ### ### ######### ######### #########
## Internal - Object construction and descruction.
constructor {args} {
$self configurelist $args
return
}
destructor {
if {$sip ne ""} {interp delete $sip}
return
}
# ### ### ### ######### ######### #########
## Internal - Option management
onconfigure -pattern {newvalue} {
set current $options(-pattern)
if {$newvalue eq $current} return
set n [regexp -all "\\*" $newvalue]
if {$n < 1} {
return -code error "Invalid pattern, * missing"
} elseif {$n > 1} {
return -code error "Invalid pattern, too many *'s"
}
set options(-pattern) $newvalue
return
}
onconfigure -api {newvalue} {
set current $options(-api)
if {$newvalue eq $current} return
set options(-api) $newvalue
return
}
onconfigure -cmds {newvalue} {
set current $options(-cmds)
if {$newvalue eq $current} return
set options(-cmds) $newvalue
return
}
onconfigure -cmdip {newvalue} {
set current $options(-cmdip)
if {$newvalue eq $current} return
set options(-cmdip) $newvalue
return
}
# ### ### ### ######### ######### #########
## Internal - Helper commands
method SetupIp {} {
set sip [::safe::interpCreate]
foreach p $paths {
::safe::interpAddToAccessPath $sip $p
}
if {![llength $options(-setup)]} return
uplevel \#0 [linsert $options(-setup) end $self $sip]
return
}
method LoadPlugin {name} {
if {[file exists $name]} {
# Plugin files are loaded directly.
$sip invokehidden source $name
return 1
}
# Otherwise the name is transformed into a package name
# and loaded thorugh the package management.
set pluginpackage [string map \
[list * $name] $options(-pattern)]
::safe::setLogCmd [mymethod PluginError]
if {[catch {
$sip eval [list package require $pluginpackage]
} res]} {
::safe::setLogCmd {}
return 0
}
::safe::setLogCmd {}
return 1
}
method CheckAPI {mv} {
upvar 1 $mv missing
if {![llength $options(-api)]} {return 1}
# Check the plugin for useability.
foreach p $options(-api) {
if {[llength [$sip eval [list info commands $p]]] == 1} continue
interp delete $sip
set missing $p
return 0
}
return 1
}
method CheckExternal {} {
if {![llength $options(-check)]} {return 1}
return [uplevel \#0 [linsert $options(-check) end $self]]
}
method SetupExternalCmds {} {
if {![llength $options(-cmds)]} return
set cip $options(-cmdip)
foreach {pcmd ecmd} $options(-cmds) {
eval [linsert $ecmd 0 interp alias $sip $pcmd $cip]
#interp alias $sip $pcmd $cip {*}$ecmd
}
return
}
method PluginError {message} {
if {[string match {*script error*} $message]} return
set myloaderror $message
return
}
# ### ### ### ######### ######### #########
proc paths {pmgr args} {
if {[llength $args] == 0} {
return -code error "wrong#args: Expect \"[info level 0] object name...\""
}
foreach name $args {
AddPaths $pmgr $name
}
return
}
proc AddPaths {pmgr name} {
global env tcl_platform
if {$tcl_platform(platform) eq "windows"} {
set sep \;
} else {
set sep :
}
#puts "$pmgr += ($name) $sep"
regsub -all {::+} [string trim $name :] \000 name
set name [split $name \000]
# Environment variables
set prefix {}
foreach part $name {
lappend prefix $part
set ev [string toupper [join $prefix _]]_PLUGINS
#puts "+? env($ev)"
if {[info exists env($ev)]} {
foreach path [split $env($ev) $sep] {
$pmgr path $path
}
}
}
# Windows registry
if {
($tcl_platform(platform) eq "windows") &&
![catch {package require registry}]
} {
foreach root {
HKEY_LOCAL_MACHINE
HKEY_CURRENT_USER
} {
set prefix {}
foreach part $name {
lappend prefix $part
set rk $root\\SOFTWARE\\[join $prefix \\]PLUGINS
#puts "+? registry($rk)"
if {![catch {set data [registry get $rk {}]}]} {
foreach path [split $data $sep] {
$pmgr path $path
}
}
}
}
}
# Home directory dot path
set prefix {}
foreach part $name {
lappend prefix $part
if {[package vsatisfies [package present Tcl] 9]} {
set pd [file join [file home] .[join $prefix /] plugin]
} else {
set pd [file join ~ .[join $prefix /] plugin]
}
#puts "+? path($pd)"
if {[file exists $pd]} {
$pmgr path $pd
}
# Cover for the goof in the example found in the docs.
# Note that supporting the directory name 'plugins' is
# also more consistent with the environment variables
# above, where we also use plugins, plural.
if {[package vsatisfies [package present Tcl] 9]} {
set pd [file join [file home] .[join $prefix /] plugins]
} else {
set pd [file join ~ .[join $prefix /] plugins]
}
#puts "+? path($pd)"
if {[file exists $pd]} {
$pmgr path $pd
}
}
return
}
}
# ### ### ### ######### ######### #########
## Ready
package provide pluginmgr 0.4
Loading…
Cancel
Save