You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

677 lines
21 KiB

'\"
'\" Generated from file '_module_lib-0\&.1\&.1\&.tm\&.man' by tcllib/doctools with format 'nroff'
'\" Copyright (c) 2024
'\"
.TH "punkshell_module_punk::lib" 0 0\&.1\&.1 doc "punk 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::lib \- punk general utility functions
.SH SYNOPSIS
package require \fBpunk::lib \fR
.sp
\fBlremove\fR \fIlist\fR ?index \&.\&.\&.?
.sp
\fBlpop\fR \fIlistvar\fR ?index?
.sp
\fBinvoke\fR \fIcommand\fR
.sp
\fBlindex_resolve\fR \fIlist\fR \fIindex\fR
.sp
\fBlindex_resolve_basic\fR \fIlist\fR \fIindex\fR
.sp
\fBK\fR \fIx\fR \fIy\fR
.sp
\fBis_utf8_multibyteprefix\fR \fIstr\fR
.sp
\fBis_utf8_single\fR \fI1234bytes\fR
.sp
\fBget_utf8_leading\fR \fIrawbytes\fR
.sp
\fBhex2dec\fR ?option value\&.\&.\&.? \fIlist_largeHex\fR
.sp
\fBdex2hex\fR ?option value\&.\&.\&.? \fIlist_decimals\fR
.sp
\fBlog2\fR \fIx\fR
.sp
\fBlogbase\fR \fIb\fR \fIx\fR
.sp
\fBfactors\fR \fIx\fR
.sp
\fBoddFactors\fR \fIx\fR
.sp
\fBgreatestFactorBelow\fR \fIx\fR
.sp
\fBgreatestOddFactorBelow\fR \fIx\fR
.sp
\fBgreatestOddFactor\fR \fIx\fR
.sp
\fBgcd\fR \fIn\fR \fIm\fR
.sp
\fBgcd\fR \fIn\fR \fIm\fR
.sp
\fBcommonDivisors\fR \fIx\fR \fIy\fR
.sp
\fBhasglobs\fR \fIstr\fR
.sp
\fBtrimzero\fR \fInumber\fR
.sp
\fBsubstring_count\fR \fIstr\fR \fIsubstring\fR
.sp
\fBdict_merge_ordered\fR \fIdefaults\fR \fImain\fR
.sp
\fBaskuser\fR \fIquestion\fR
.sp
\fBlinesort\fR ?sortoption ?val?\&.\&.\&.? \fItextblock\fR
.sp
\fBlist_as_lines\fR ?-joinchar char? \fIlinelist\fR
.sp
\fBlines_as_list\fR ?option value \&.\&.\&.? \fItext\fR
.sp
.BE
.SH DESCRIPTION
.PP
This is a set of utility functions that are commonly used across punk modules or are just considered to be general-purpose functions\&.
.PP
The base set includes string and math functions but has no specific theme
.SH OVERVIEW
.PP
overview of punk::lib
.SS CONCEPTS
.PP
The punk::lib modules should have no strong dependencies other than Tcl
.PP
Dependendencies that only affect display or additional functionality may be included - but should fail gracefully if not present, and only when a function is called that uses one of these soft dependencies\&.
.PP
This requirement for no strong dependencies, means that many utility functions that might otherwise seem worthy of inclusion here are not present\&.
.SS DEPENDENCIES
.PP
packages used by punk::lib
.IP \(bu
\fBTcl 8\&.6-\fR
.PP
.SH API
.SS "NAMESPACE PUNK::LIB::COMPAT"
.PP
compatibility functions for features that may not be available in earlier Tcl versions
.PP
These are generally 'forward compatibility' functions ie allowing earlier versions to use later features/idioms by using a Tcl-only version of a missing builtin\&.
.PP
Such Tcl-only versions will inevitably be less performant - perhaps significantly so\&.
.TP
\fBlremove\fR \fIlist\fR ?index \&.\&.\&.?
.sp
Forwards compatible lremove for versions 8\&.6 or less to support equivalent 8\&.7 lremove
.TP
\fBlpop\fR \fIlistvar\fR ?index?
.sp
Forwards compatible lpop for versions 8\&.6 or less to support equivalent 8\&.7 lpop
.PP
.SS "NAMESPACE PUNK::LIB"
.PP
Core API functions for punk::lib
.TP
\fBinvoke\fR \fIcommand\fR
.sp
Invoke an external command (using tcl open command) capturing stdout,stderr and the exitcode
.CS
set script {
puts stdout {hello on stdout}
puts stderr {hello on stderr}
exit 42
}
invoke [list tclsh <<$script]
.CE
.TP
\fBlindex_resolve\fR \fIlist\fR \fIindex\fR
.sp
Resolve an index which may be of the forms accepted by Tcl list commands such as end-2 or 2+2 to the actual integer index for the supplied list
.sp
Users may define procs which accept a list index and wish to accept the forms understood by Tcl\&.
.sp
This means the proc may be called with something like $x+2 end-$y etc
.sp
Sometimes the actual integer index is desired\&.
.sp
We want to resolve the index used, without passing arbitrary expressions into the 'expr' function - which could have security risks\&.
.sp
lindex_resolve will parse the index expression and return:
.sp
a) -3 if the supplied index expression is below the lower bound for the supplied list\&. (< 0)
.sp
b) -2 if the supplied index expression is above the upper bound for the supplied list\&. (> end)
.sp
We don't return -1 - as the similar function lindex_resolve_basic uses this to denote out of range at either end of the list
.sp
Otherwise it will return an integer corresponding to the position in the list\&.
.sp
This is in stark contrast to Tcl list function indices which will return empty strings for out or bounds indices, or in the case of lrange, return results anyway\&.
.sp
Like Tcl list commands - it will produce an error if the form of the index is not acceptable
.sp
For empty lists, end and end+x indices are considered to be out of bounds on the upper side - thus returning -2
.TP
\fBlindex_resolve_basic\fR \fIlist\fR \fIindex\fR
.sp
Accepts index of the forms accepted by Tcl's list commands\&. (e\&.g compound indices such as 3+1 end-2)
.sp
returns -1 for out of range at either end, or a valid integer index
.sp
Unlike lindex_resolve; lindex_resolve_basic can't determine if an out of range index was out of range at the lower or upper bound
.sp
This is only likely to be faster than average over lindex_resolve for Tcl which has the builtin lseq command
.sp
The performance advantage is more likely to be present when using compound indexes such as $x+1 or end-1
.sp
For pure integer indices the performance should be equivalent
.TP
\fBK\fR \fIx\fR \fIy\fR
.sp
The K-combinator function - returns the first argument, x and discards y
.sp
see \fIhttps://wiki\&.tcl-lang\&.org/page/K\fR
.sp
It is used in cases where command-substitution at the calling-point performs some desired effect\&.
.TP
\fBis_utf8_multibyteprefix\fR \fIstr\fR
.sp
Returns a boolean if str is potentially a prefix for a multibyte utf-8 character
.sp
ie - tests if it is possible that appending more data will result in a utf-8 codepoint
.sp
Will return false for an already complete utf-8 codepoint
.sp
It is assumed the incomplete sequence is at the beginning of the bytes argument
.sp
Suitable input for this might be from the unreturned tail portion of get_utf8_leading $testbytes
.sp
e\&.g using: set head [get_utf8_leading $testbytes] ; set tail [string range $testbytes [string length $head] end]
.TP
\fBis_utf8_single\fR \fI1234bytes\fR
.sp
Tests input of 1,2,3 or 4 bytes and responds with a boolean indicating if it is a valid utf-8 character (codepoint)
.TP
\fBget_utf8_leading\fR \fIrawbytes\fR
.sp
return the leading portion of rawbytes that is a valid utf8 sequence\&.
.sp
This will stop at the point at which the bytes can't be interpreted as a complete utf-8 codepoint
.sp
e\&.g It will not return the first byte or 2 of a 3-byte utf-8 character if the last byte is missing, and will return only the valid utf-8 string from before the first byte of the incomplete character\&.
.sp
It will also only return the prefix before any bytes that cannot be part of a utf-8 sequence at all\&.
.sp
Note that while this will return valid utf8 - it has no knowledge of grapheme clusters or diacritics
.sp
This means if it is being used to process bytes split at some arbitrary point - the trailing data that isn't returned could be part of a grapheme cluster that belongs with the last character of the leading string already returned
.sp
The utf-8 BOM \\xEF\\xBB\\xBF is a valid UTF8 3-byte sequence and so can also be returned as part of the leading utf8 bytes
.TP
\fBhex2dec\fR ?option value\&.\&.\&.? \fIlist_largeHex\fR
.sp
Convert a list of (possibly large) unprefixed hex strings to their decimal values
.sp
hex2dec accepts and ignores internal underscores in the same manner as Tcl 8\&.7+ numbers e\&.g hex2dec FF_FF returns 65535
.sp
Leading and trailing underscores are ignored as a matter of implementation convenience - but this shouldn't be relied upon\&.
.sp
Leading or trailing whitespace in each list member is allowed e\&.g hex2dec " F" returns 15
.sp
Internal whitespace e\&.g "F F" is not permitted - but a completely empty element "" is allowed and will return 0
.TP
\fBdex2hex\fR ?option value\&.\&.\&.? \fIlist_decimals\fR
.sp
Convert a list of decimal integers to a list of hex values
.sp
-width <int> can be used to make each hex value at least int characters wide, with leading zeroes\&.
.sp
-case upper|lower determines the case of the hex letters in the output
.TP
\fBlog2\fR \fIx\fR
.sp
log base2 of x
.sp
This uses a 'live' proc body - the divisor for the change of base is computed once at definition time
.sp
(courtesy of RS \fIhttps://wiki\&.tcl-lang\&.org/page/Additional+math+functions\fR)
.TP
\fBlogbase\fR \fIb\fR \fIx\fR
.sp
log base b of x
.sp
This function uses expr's natural log and the change of base division\&.
.sp
This means for example that we can get results like: logbase 10 1000 = 2\&.9999999999999996
.sp
Use expr's log10() function or tcl::mathfunc::log10 for base 10
.TP
\fBfactors\fR \fIx\fR
.sp
Return a sorted list of the positive factors of x where x > 0
.sp
For x = 0 we return only 0 and 1 as technically any number divides zero and there are an infinite number of factors\&. (including zero itself in this context)*
.sp
This is a simple brute-force implementation that iterates all numbers below the square root of x to check the factors
.sp
Because the implementation is so simple - the performance is very reasonable for numbers below at least a few 10's of millions
.sp
See tcllib math::numtheory::factors for a more complex implementation - which seems to be slower for 'small' numbers
.sp
Comparisons were done with some numbers below 17 digits long
.sp
For seriously big numbers - this simple algorithm would no doubt be outperformed by more complex algorithms\&.
.sp
The numtheory library stores some data about primes etc with each call - so may become faster when being used on more numbers
but has the disadvantage of being slower for 'small' numbers and using more memory\&.
.sp
If the largest factor below x is needed - the greatestOddFactorBelow and GreatestFactorBelow functions are a faster way to get there than computing the whole list, even for small values of x
.sp
* Taking x=0; Notion of x being divisible by integer y being: There exists an integer p such that x = py
.sp
In other mathematical contexts zero may be considered not to divide anything\&.
.TP
\fBoddFactors\fR \fIx\fR
.sp
Return a list of odd integer factors of x, sorted in ascending order
.TP
\fBgreatestFactorBelow\fR \fIx\fR
.sp
Return the largest factor of x excluding itself
.sp
factor functions can be useful for console layout calculations
.sp
See Tcllib math::numtheory for more extensive implementations
.TP
\fBgreatestOddFactorBelow\fR \fIx\fR
.sp
Return the largest odd integer factor of x excluding x itself
.TP
\fBgreatestOddFactor\fR \fIx\fR
.sp
Return the largest odd integer factor of x
.sp
For an odd value of x - this will always return x
.TP
\fBgcd\fR \fIn\fR \fIm\fR
.sp
Return the greatest common divisor of m and n
.sp
Straight from Lars Hellström's math::numtheory library in Tcllib
.sp
Graphical use:
.sp
An a by b rectangle can be covered with square tiles of side-length c,
.sp
only if c is a common divisor of a and b
.TP
\fBgcd\fR \fIn\fR \fIm\fR
.sp
Return the lowest common multiple of m and n
.sp
Straight from Lars Hellström's math::numtheory library in Tcllib
.sp
.TP
\fBcommonDivisors\fR \fIx\fR \fIy\fR
.sp
Return a list of all the common factors of x and y
.sp
(equivalent to factors of their gcd)
.TP
\fBhasglobs\fR \fIstr\fR
.sp
Return a boolean indicating whether str contains any of the glob characters: * ? [ ]
.sp
hasglobs uses append to preserve Tcls internal representation for str - so it should help avoid shimmering in the few cases where this may matter\&.
.TP
\fBtrimzero\fR \fInumber\fR
.sp
Return number with left-hand-side zeros trimmed off - unless all zero
.sp
If number is all zero - a single 0 is returned
.TP
\fBsubstring_count\fR \fIstr\fR \fIsubstring\fR
.sp
Search str and return number of occurrences of substring
.TP
\fBdict_merge_ordered\fR \fIdefaults\fR \fImain\fR
.sp
The standard dict merge accepts multiple dicts with values from dicts to the right (2nd argument) taking precedence\&.
.sp
When merging with a dict of default values - this means that any default key/vals that weren't in the main dict appear in the output before the main data\&.
.sp
This function merges the two dicts whilst maintaining the key order of main followed by defaults\&.
.TP
\fBaskuser\fR \fIquestion\fR
.sp
A basic utility to read an answer from stdin
.sp
The prompt is written to the terminal and then it waits for a user to type something
.sp
stdin is temporarily configured to blocking and then put back in its original state in case it wasn't already so\&.
.sp
If the terminal is using punk::console and is in raw mode - the terminal will temporarily be put in line mode\&.
.sp
(Generic terminal raw vs linemode detection not yet present)
.sp
The user must hit enter to submit the response
.sp
The return value is the string if any that was typed prior to hitting enter\&.
.sp
The question argument can be manually colourised using the various punk::ansi funcitons
.CS
set answer [punk::lib::askuser "[a+ green bold]Do you want to proceed? (Y|N)[a]"]
if {[string match y* [string tolower $answer]]} {
puts "Proceeding"
} else {
puts "Cancelled by user"
}
.CE
.TP
\fBlinesort\fR ?sortoption ?val?\&.\&.\&.? \fItextblock\fR
.sp
Sort lines in textblock
.sp
Returns another textblock with lines sorted
.sp
options are flags as accepted by lsort ie -ascii -command -decreasing -dictionary -index -indices -integer -nocase -real -stride -unique
.TP
\fBlist_as_lines\fR ?-joinchar char? \fIlinelist\fR
.sp
This simply joines the elements of the list with -joinchar
.sp
It is mainly intended for use in pipelines where the primary argument comes at the end - but it can also be used as a general replacement for join $lines <le>
.sp
The sister function lines_as_list takes a block of text and splits it into lines - but with more options related to trimming the block and/or each line\&.
.TP
\fBlines_as_list\fR ?option value \&.\&.\&.? \fItext\fR
.sp
Returns a list of possibly trimmed lines depeding on options
.sp
The concept of lines is raw lines from splitting on newline after crlf is mapped to lf
.sp
- not console lines which may be entirely different due to control characters such as vertical tabs or ANSI movements
.PP
.SH INTERNAL
.SS "NAMESPACE PUNK::LIB::SYSTEM"
.PP
Internal functions that are not part of the API
.PP
.SH KEYWORDS
lib, module, utility
.SH COPYRIGHT
.nf
Copyright (c) 2024
.fi