mirror of https://github.com/dyne/zuper.git
refactoring
removes optional switches moved some helpers. in string. completed more documentation
This commit is contained in:
parent
b7a0dca0ec
commit
5464a0f9d5
37
README.md
37
README.md
|
|
@ -11,7 +11,7 @@
|
|||
(_)
|
||||
```
|
||||
|
||||
**Z**sh **U**ltimate **P**rogrammer's **E**xtensions **R**efurbished - version 0.2
|
||||
**Z**sh **U**ltimate **P**rogrammer's **E**xtensions **R**efurbished
|
||||
|
||||
# Introduction
|
||||
|
||||
|
|
@ -29,6 +29,14 @@ used as a programming language.
|
|||
- improved temp file handling
|
||||
- more to come...
|
||||
|
||||
# Requirements
|
||||
|
||||
Zuper requires the following programs to be installed:
|
||||
|
||||
```
|
||||
zsh curl sed awk hexdump
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
Documentation is still lacking, however to use Zuper in Zsh scripts
|
||||
|
|
@ -42,23 +50,24 @@ program call `endgame` for a clean exit. Example test program:
|
|||
# switch on debugging output
|
||||
DEBUG=1
|
||||
|
||||
# switch on zuper's key/value load/save extension
|
||||
zkv=1
|
||||
# switch off zuper's consul kv get/set extension
|
||||
unset consul
|
||||
# switch on zuper's helper extensions
|
||||
helper=1
|
||||
|
||||
# switch logging into test.log
|
||||
LOG=test.log
|
||||
|
||||
##### INIT
|
||||
# load our zuper library
|
||||
source zuper
|
||||
|
||||
# declare a custom global variable
|
||||
vars+=(myvar)
|
||||
# assign a default value to our global variable
|
||||
myvar=${myvar:-ok}
|
||||
# declare a global associative map
|
||||
maps+=(mymap)
|
||||
# conclude the init phase
|
||||
source zuper.init
|
||||
#####
|
||||
|
||||
# register the zdump debug function to be executed on exit
|
||||
destruens+=(zdump)
|
||||
|
||||
|
||||
# declare a custom function to print it out
|
||||
|
|
@ -79,14 +88,6 @@ testfun() {
|
|||
# but can also be delete earlier here, optionally
|
||||
}
|
||||
|
||||
# declare a global associative map
|
||||
maps+=(mymap)
|
||||
|
||||
# conclude the init phase
|
||||
source zuper.init
|
||||
|
||||
# register the zdump debug function to be executed on exit
|
||||
destruens+=(zdump)
|
||||
|
||||
# call our custom function
|
||||
testfun
|
||||
|
|
@ -121,7 +122,7 @@ done
|
|||
|
||||
Here we reference applications where zuper is used succesfully:
|
||||
|
||||
- Devuan Simple Development Toolkit https://git.devuan.org/devuan/devuan-sdk#tab-readme
|
||||
- Devuan Simple Development Toolkit https://git.devuan.org/groups/sdk
|
||||
- Dowse IoT awareness OS http://dyne.org/software/dowse
|
||||
- Jaro Mail terminal email http://dyne.org/software/jaro-mail
|
||||
|
||||
|
|
|
|||
437
zuper
437
zuper
|
|
@ -31,10 +31,13 @@ vars=(DEBUG QUIET LOG)
|
|||
arrs=(req freq)
|
||||
|
||||
vars+=(zuper_version)
|
||||
zuper_version=0.2
|
||||
zuper_version=0.3
|
||||
|
||||
# load necessary zsh extensions
|
||||
zmodload zsh/regex
|
||||
zmodload zsh/system
|
||||
zmodload zsh/net/tcp
|
||||
zmodload zsh/mapfile
|
||||
|
||||
# {{{ Messaging
|
||||
|
||||
|
|
@ -158,7 +161,7 @@ zerr() {
|
|||
}
|
||||
|
||||
|
||||
ckreq reqck() {
|
||||
function ckreq reqck() {
|
||||
err=0
|
||||
for v in $req; do
|
||||
[[ "${(P)v}" = "" ]] && {
|
||||
|
|
@ -240,9 +243,9 @@ endgame() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# Register endgame() to be called at exit.
|
||||
# Use this to make sure endgame() is called at exit.
|
||||
# unlike TRAPEXIT, the zshexit() hook is not called when functions exit.
|
||||
zshexit() { endgame EXIT; return $? }
|
||||
function zuper.exit zshexit() { endgame EXIT; return $? }
|
||||
|
||||
# }}} Debugging
|
||||
|
||||
|
|
@ -281,7 +284,7 @@ destruens+=(_ztmp_destructor)
|
|||
# tokenizer, works only with one char length delimiters
|
||||
# saves everything in global array tok=()
|
||||
arrs+=(tok)
|
||||
strtok() {
|
||||
function string.strtok strtok() {
|
||||
fn "strtok $*"
|
||||
_string="$1"
|
||||
_delim="$2"
|
||||
|
|
@ -313,7 +316,149 @@ strtok() {
|
|||
fi
|
||||
}
|
||||
|
||||
# TODO: move in here some helpers
|
||||
# remote leading and trailing spaces in a string taken from stdin
|
||||
function string.trim trim() {
|
||||
sed -e 's/^[[:space:]]*//g ; s/[[:space:]]*\$//g'
|
||||
}
|
||||
|
||||
# extract all emails found in a text from stdin
|
||||
# outputs them one per line
|
||||
function string.extract_emails extract_emails() {
|
||||
awk '{ for (i=1;i<=NF;i++)
|
||||
if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) {
|
||||
gsub(/<|>|,/ , "" , $i); print $i } }'
|
||||
}
|
||||
|
||||
# takes a string as argument, returns success if is an email
|
||||
function string.isemail isemail() {
|
||||
[[ "$1" -regex-match "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" ]] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# takes a numeric argument and prints out a human readable size
|
||||
function string.human_size human_size() {
|
||||
[[ $1 -gt 0 ]] || {
|
||||
error "human_size() called with invalid argument"
|
||||
return 1
|
||||
}
|
||||
|
||||
# we use the binary operation for speed
|
||||
# shift right 10 is divide by 1024
|
||||
|
||||
# gigabytes
|
||||
[[ $1 -gt 1073741824 ]] && {
|
||||
print -n "$(( $1 >> 30 )) GB"
|
||||
return 0
|
||||
}
|
||||
|
||||
# megabytes
|
||||
[[ $1 -gt 1048576 ]] && {
|
||||
print -n "$(( $1 >> 20 )) MB"
|
||||
return 0
|
||||
}
|
||||
# kilobytes
|
||||
[[ $1 -gt 1024 ]] && {
|
||||
print -n "$(( $1 >> 10 )) KB"
|
||||
return 0
|
||||
}
|
||||
# bytes
|
||||
print -n "$1 Bytes"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# strips out all html/xml tags (everything between < >)
|
||||
function string.html_strip xml_strip html_strip() { sed 's/<[^>]\+>//g' }
|
||||
|
||||
# changes stdin string special chars to be shown in html
|
||||
function string.escape_html escape_html() {
|
||||
sed -e '
|
||||
s/\&/\&/g
|
||||
s/>/\>/g
|
||||
s/</\</g
|
||||
s/"/\"/g
|
||||
'
|
||||
}
|
||||
|
||||
# escapes special chars in urls
|
||||
function string.decode_url decode_url urldecode() {
|
||||
sed -e '
|
||||
s/%25/%/gi
|
||||
s/%20/ /gi
|
||||
s/%09/ /gi
|
||||
s/%21/!/gi
|
||||
s/%22/"/gi
|
||||
s/%23/#/gi
|
||||
s/%24/\$/gi
|
||||
s/%26/\&/gi
|
||||
s/%27/'\''/gi
|
||||
s/%28/(/gi
|
||||
s/%29/)/gi
|
||||
s/%2a/\*/gi
|
||||
s/%2b/+/gi
|
||||
s/%2c/,/gi
|
||||
s/%2d/-/gi
|
||||
s/%2e/\./gi
|
||||
s/%2f/\//gi
|
||||
s/%3a/:/gi
|
||||
s/%3b/;/gi
|
||||
s/%3d/=/gi
|
||||
s/%3e//gi
|
||||
s/%3f/?/gi
|
||||
s/%40/@/gi
|
||||
s/%5b/\[/gi
|
||||
s/%5c/\\/gi
|
||||
s/%5d/\]/gi
|
||||
s/%5e/\^/gi
|
||||
s/%5f/_/gi
|
||||
s/%60/`/gi
|
||||
s/%7b/{/gi
|
||||
s/%7c/|/gi
|
||||
s/%7d/}/gi
|
||||
s/%7e/~/gi
|
||||
s/%09/ /gi
|
||||
'
|
||||
}
|
||||
|
||||
function helper.encode-url encode_url urlencode() {
|
||||
sed -e '
|
||||
s/%/%25/g
|
||||
s/ /%20/g
|
||||
s/ /%09/g
|
||||
s/!/%21/g
|
||||
s/"/%22/g
|
||||
s/#/%23/g
|
||||
s/\$/%24/g
|
||||
s/\&/%26/g
|
||||
s/'\''/%27/g
|
||||
s/(/%28/g
|
||||
s/)/%29/g
|
||||
s/\*/%2a/g
|
||||
s/+/%2b/g
|
||||
s/,/%2c/g
|
||||
s/-/%2d/g
|
||||
s/\./%2e/g
|
||||
s/\//%2f/g
|
||||
s/:/%3a/g
|
||||
s/;/%3b/g
|
||||
s//%3e/g
|
||||
s/?/%3f/g
|
||||
s/@/%40/g
|
||||
s/\[/%5b/g
|
||||
s/\\/%5c/g
|
||||
s/\]/%5d/g
|
||||
s/\^/%5e/g
|
||||
s/_/%5f/g
|
||||
s/`/%60/g
|
||||
s/{/%7b/g
|
||||
s/|/%7c/g
|
||||
s/}/%7d/g
|
||||
s/~/%7e/g
|
||||
s/ /%09/g
|
||||
'
|
||||
}
|
||||
|
||||
|
||||
# }}} Strings
|
||||
|
||||
|
|
@ -425,69 +570,67 @@ net.scan_exits() {
|
|||
|
||||
# optional: define zkv=1 on source
|
||||
|
||||
[[ "$zkv" = "" ]] || {
|
||||
|
||||
##########################
|
||||
# Key/Value file storage using ZSh associative maps
|
||||
##########################
|
||||
# Key/Value file storage using ZSh associative maps
|
||||
|
||||
|
||||
# load a map from a file
|
||||
# map must be already instantiated with typeset -A by called
|
||||
# name of map is defined inside the file
|
||||
function zkv.load() {
|
||||
fn "zkv-load $*"
|
||||
# load a map from a file
|
||||
# map must be already instantiated with typeset -A by called
|
||||
# name of map is defined inside the file
|
||||
function zkv.load() {
|
||||
fn "zkv-load $*"
|
||||
|
||||
file=$1
|
||||
[[ "$file" = "" ]] && {
|
||||
error "zkv-open() missing argument: file-path"
|
||||
zerr
|
||||
return 1 }
|
||||
[[ -r "$file" ]] || {
|
||||
error "zkv-open() file not found $file"
|
||||
zerr
|
||||
return 1 }
|
||||
[[ -s "$file" ]] || {
|
||||
error "zkv-open() file is empty"
|
||||
zerr
|
||||
return 1 }
|
||||
file=$1
|
||||
[[ "$file" = "" ]] && {
|
||||
error "zkv-open() missing argument: file-path"
|
||||
zerr
|
||||
return 1 }
|
||||
[[ -r "$file" ]] || {
|
||||
error "zkv-open() file not found $file"
|
||||
zerr
|
||||
return 1 }
|
||||
[[ -s "$file" ]] || {
|
||||
error "zkv-open() file is empty"
|
||||
zerr
|
||||
return 1 }
|
||||
|
||||
source $file
|
||||
source $file
|
||||
}
|
||||
|
||||
# save a map in a file
|
||||
# $1 = name of the map associative array
|
||||
# $2 = full path to the file
|
||||
function zkv.save() {
|
||||
fn "zkv.save $*"
|
||||
|
||||
_map=$1
|
||||
_path=$2
|
||||
[[ "$_path" = "" ]] && {
|
||||
error "zkv.save() missing argument: map-name path-to-file"
|
||||
zerr
|
||||
return 1
|
||||
}
|
||||
[[ -r $_path ]] && {
|
||||
func "zkv.close() overwriting $_path"
|
||||
func "backup turd left behind: ${_path}~"
|
||||
mv $_path $_path~
|
||||
}
|
||||
touch $_path
|
||||
|
||||
# save a map in a file
|
||||
# $1 = name of the map associative array
|
||||
# $2 = full path to the file
|
||||
function zkv.save() {
|
||||
fn "zkv.save $*"
|
||||
|
||||
_map=$1
|
||||
_path=$2
|
||||
[[ "$_path" = "" ]] && {
|
||||
error "zkv.save() missing argument: map-name path-to-file"
|
||||
zerr
|
||||
return 1
|
||||
}
|
||||
[[ -r $_path ]] && {
|
||||
func "zkv.close() overwriting $_path"
|
||||
func "backup turd left behind: ${_path}~"
|
||||
mv $_path $_path~
|
||||
}
|
||||
touch $_path
|
||||
|
||||
# wondering about http://www.zsh.org/mla/users/2015/msg00286.html
|
||||
# meanwhile solved using a double array, wasting a full map memcpy
|
||||
_karr=(${(Pk)_map})
|
||||
_varr=(${(Pv)_map})
|
||||
_num="${#_karr}"
|
||||
for c in {1..$_num}; do
|
||||
# can also be cat here, however for speed we use builtins
|
||||
# switch to cat if compatibility is an issue
|
||||
sysread -o 1 <<EOF >> $_path
|
||||
# wondering about http://www.zsh.org/mla/users/2015/msg00286.html
|
||||
# meanwhile solved using a double array, wasting a full map memcpy
|
||||
_karr=(${(Pk)_map})
|
||||
_varr=(${(Pv)_map})
|
||||
_num="${#_karr}"
|
||||
for c in {1..$_num}; do
|
||||
# can also be cat here, however for speed we use builtins
|
||||
# switch to cat if compatibility is an issue
|
||||
sysread -o 1 <<EOF >> $_path
|
||||
$_map+=("${_karr[$c]}" "${(v)_varr[$c]}")
|
||||
EOF
|
||||
done
|
||||
func "$_num key/values stored in $_path"
|
||||
}
|
||||
done
|
||||
func "$_num key/values stored in $_path"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -496,7 +639,7 @@ EOF
|
|||
# {{{ Get/Set REST API
|
||||
|
||||
########
|
||||
# Restful API client
|
||||
# Restful API client (WIP, needs more testing)
|
||||
# there is a clear zsh optimization here in get/set kv
|
||||
# using zsh/tcp instead of spawning curl
|
||||
# and perhaps querying with one call using ?recursive
|
||||
|
|
@ -633,165 +776,17 @@ EOF
|
|||
# }}} Get/Set REST API
|
||||
|
||||
# {{{ Helpers
|
||||
[[ "$helpers" = "" ]] || {
|
||||
|
||||
function helper.isfound isfound() {
|
||||
command -v $1 1>/dev/null 2>/dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
# remote leading and trailing spaces in a string taken from stdin
|
||||
function helper.trim trim() {
|
||||
sed -e 's/^[[:space:]]*//g ; s/[[:space:]]*\$//g'
|
||||
}
|
||||
|
||||
zmodload zsh/mapfile
|
||||
# faster substitute for cat
|
||||
function helper.printfile printfile() {
|
||||
print ${mapfile[$1]}
|
||||
}
|
||||
|
||||
# extract all emails found in a text from stdin
|
||||
# outputs them one per line
|
||||
function helper.extract-emails extract_emails() {
|
||||
awk '{ for (i=1;i<=NF;i++)
|
||||
if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) {
|
||||
gsub(/<|>|,/ , "" , $i); print $i } }'
|
||||
}
|
||||
|
||||
|
||||
zmodload zsh/regex
|
||||
# takes a string as argument, returns success if is an email
|
||||
function helper.isemail isemail() {
|
||||
[[ "$1" -regex-match "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" ]] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# takes a numeric argument and prints out a human readable size
|
||||
function helper.human-size human_size() {
|
||||
[[ $1 -gt 0 ]] || {
|
||||
error "human_size() called with invalid argument"
|
||||
return 1
|
||||
}
|
||||
|
||||
# we use the binary operation for speed
|
||||
# shift right 10 is divide by 1024
|
||||
|
||||
# gigabytes
|
||||
[[ $1 -gt 1073741824 ]] && {
|
||||
print -n "$(( $1 >> 30 )) GB"
|
||||
return 0
|
||||
}
|
||||
|
||||
# megabytes
|
||||
[[ $1 -gt 1048576 ]] && {
|
||||
print -n "$(( $1 >> 20 )) MB"
|
||||
return 0
|
||||
}
|
||||
# kilobytes
|
||||
[[ $1 -gt 1024 ]] && {
|
||||
print -n "$(( $1 >> 10 )) KB"
|
||||
return 0
|
||||
}
|
||||
# bytes
|
||||
print -n "$1 Bytes"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# strips out all html/xml tags (everything between < >)
|
||||
function helper.html-strip xml_strip html_strip() { sed 's/<[^>]\+>//g' }
|
||||
|
||||
# changes stdin string special chars to be shown in html
|
||||
function helper.escape-html escape_html() {
|
||||
sed -e '
|
||||
s/\&/\&/g
|
||||
s/>/\>/g
|
||||
s/</\</g
|
||||
s/"/\"/g
|
||||
'
|
||||
}
|
||||
|
||||
# escapes special chars in urls
|
||||
function helper.decode-url decode_url urldecode() {
|
||||
sed -e '
|
||||
s/%25/%/gi
|
||||
s/%20/ /gi
|
||||
s/%09/ /gi
|
||||
s/%21/!/gi
|
||||
s/%22/"/gi
|
||||
s/%23/#/gi
|
||||
s/%24/\$/gi
|
||||
s/%26/\&/gi
|
||||
s/%27/'\''/gi
|
||||
s/%28/(/gi
|
||||
s/%29/)/gi
|
||||
s/%2a/\*/gi
|
||||
s/%2b/+/gi
|
||||
s/%2c/,/gi
|
||||
s/%2d/-/gi
|
||||
s/%2e/\./gi
|
||||
s/%2f/\//gi
|
||||
s/%3a/:/gi
|
||||
s/%3b/;/gi
|
||||
s/%3d/=/gi
|
||||
s/%3e//gi
|
||||
s/%3f/?/gi
|
||||
s/%40/@/gi
|
||||
s/%5b/\[/gi
|
||||
s/%5c/\\/gi
|
||||
s/%5d/\]/gi
|
||||
s/%5e/\^/gi
|
||||
s/%5f/_/gi
|
||||
s/%60/`/gi
|
||||
s/%7b/{/gi
|
||||
s/%7c/|/gi
|
||||
s/%7d/}/gi
|
||||
s/%7e/~/gi
|
||||
s/%09/ /gi
|
||||
'
|
||||
}
|
||||
|
||||
function helper.encode-url encode_url urlencode() {
|
||||
sed -e '
|
||||
s/%/%25/g
|
||||
s/ /%20/g
|
||||
s/ /%09/g
|
||||
s/!/%21/g
|
||||
s/"/%22/g
|
||||
s/#/%23/g
|
||||
s/\$/%24/g
|
||||
s/\&/%26/g
|
||||
s/'\''/%27/g
|
||||
s/(/%28/g
|
||||
s/)/%29/g
|
||||
s/\*/%2a/g
|
||||
s/+/%2b/g
|
||||
s/,/%2c/g
|
||||
s/-/%2d/g
|
||||
s/\./%2e/g
|
||||
s/\//%2f/g
|
||||
s/:/%3a/g
|
||||
s/;/%3b/g
|
||||
s//%3e/g
|
||||
s/?/%3f/g
|
||||
s/@/%40/g
|
||||
s/\[/%5b/g
|
||||
s/\\/%5c/g
|
||||
s/\]/%5d/g
|
||||
s/\^/%5e/g
|
||||
s/_/%5f/g
|
||||
s/`/%60/g
|
||||
s/{/%7b/g
|
||||
s/|/%7c/g
|
||||
s/}/%7d/g
|
||||
s/~/%7e/g
|
||||
s/ /%09/g
|
||||
'
|
||||
}
|
||||
|
||||
function helper.isfound isfound() {
|
||||
command -v $1 1>/dev/null 2>/dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
# faster substitute for cat
|
||||
function helper.printfile printfile() {
|
||||
print ${mapfile[$1]}
|
||||
}
|
||||
|
||||
# }}} Helpers
|
||||
|
||||
# {{{ Config
|
||||
|
|
@ -807,7 +802,7 @@ vars+=(config_section_type)
|
|||
arrs+=(config_section)
|
||||
config_section_type=org-mode
|
||||
|
||||
config.section.type() {
|
||||
config.section_type() {
|
||||
fn config.section.type
|
||||
_type=$1
|
||||
req=(_type)
|
||||
|
|
@ -829,7 +824,7 @@ config.section.type() {
|
|||
}
|
||||
|
||||
# fills in contents of section in array config_section
|
||||
config.section.read() {
|
||||
config.section_read() {
|
||||
fn config.section.read
|
||||
_file=$1
|
||||
_section=$2
|
||||
|
|
|
|||
Loading…
Reference in New Issue