summaryrefslogtreecommitdiffstats
path: root/Functions
diff options
context:
space:
mode:
Diffstat (limited to 'Functions')
-rw-r--r--Functions/.cvsignore2
-rw-r--r--Functions/Misc/regexp-replace125
-rwxr-xr-xFunctions/Misc/zgetopt181
-rw-r--r--Functions/Misc/zkbd10
-rw-r--r--Functions/Misc/zmathfuncdef2
-rw-r--r--Functions/Misc/zmv2
6 files changed, 257 insertions, 65 deletions
diff --git a/Functions/.cvsignore b/Functions/.cvsignore
deleted file mode 100644
index 89ff7b089..000000000
--- a/Functions/.cvsignore
+++ /dev/null
@@ -1,2 +0,0 @@
-Makefile
-*.swp
diff --git a/Functions/Misc/regexp-replace b/Functions/Misc/regexp-replace
index d4408f0f7..86b28c5aa 100644
--- a/Functions/Misc/regexp-replace
+++ b/Functions/Misc/regexp-replace
@@ -1,91 +1,98 @@
-# Replace all occurrences of a regular expression in a variable. The
-# variable is modified directly. Respects the setting of the
-# option RE_MATCH_PCRE.
+# Replace all occurrences of a regular expression in a scalar variable.
+# The variable is modified directly. Respects the setting of the option
+# RE_MATCH_PCRE, but otherwise sets the zsh emulation mode.
#
-# First argument: *name* (not contents) of variable.
-# Second argument: regular expression
-# Third argument: replacement string. This can contain all forms of
-# $ and backtick substitutions; in particular, $MATCH will be replaced
-# by the portion of the string matched by the regular expression.
+# Arguments:
+#
+# 1. *name* (not contents) of variable or more generally any lvalue;
+# expected to be scalar.
+#
+# 2. regular expression
+#
+# 3. replacement string. This can contain all forms of
+# $ and backtick substitutions; in particular, $MATCH will be
+# replaced by the portion of the string matched by the regular
+# expression. Parsing errors are fatal to the shell process.
-# we use positional parameters instead of variables to avoid
-# clashing with the user's variable. Make sure we start with 3 and only
-# 3 elements:
-argv=("$1" "$2" "$3")
+if (( $# < 2 || $# > 3 )); then
+ print -ru2 "Usage: $0 <varname> <regexp> [<replacement>]"
+ return 2
+fi
-# $4 records whether pcre is enabled as that information would otherwise
-# be lost after emulate -L zsh
-4=0
-[[ -o re_match_pcre ]] && 4=1
+local _regexp_replace_use_pcre=0
+[[ -o re_match_pcre ]] && _regexp_replace_use_pcre=1
emulate -L zsh
+local _regexp_replace_subject=${(P)1} \
+ _regexp_replace_regexp=$2 \
+ _regexp_replace_replacement=$3 \
+ _regexp_replace_result \
+ MATCH MBEGIN MEND
-local MATCH MBEGIN MEND
local -a match mbegin mend
-if (( $4 )); then
+if (( _regexp_replace_use_pcre )); then
# if using pcre, we're using pcre_match and a running offset
# That's needed for ^, \A, \b, and look-behind operators to work
# properly.
zmodload zsh/pcre || return 2
- pcre_compile -- "$2" && pcre_study || return 2
+ pcre_compile -- "$_regexp_replace_regexp" && pcre_study || return 2
- # $4 is the current *byte* offset, $5, $6 reserved for later use
- 4=0 6=
+ local _regexp_replace_offset=0 _regexp_replace_start _regexp_replace_stop _regexp_replace_new ZPCRE_OP
+ local -a _regexp_replace_finds
- local ZPCRE_OP
- while pcre_match -b -n $4 -- "${(P)1}"; do
- # append offsets and computed replacement to the array
- # we need to perform the evaluation in a scalar assignment so that if
- # it generates an array, the elements are converted to string (by
+ while pcre_match -b -n $_regexp_replace_offset -- "$_regexp_replace_subject"; do
+ # we need to perform the evaluation in a scalar assignment so that
+ # if it generates an array, the elements are converted to string (by
# joining with the first character of $IFS as usual)
- 5=${(e)3}
- argv+=(${(s: :)ZPCRE_OP} "$5")
+ _regexp_replace_new=${(Xe)_regexp_replace_replacement}
+
+ _regexp_replace_finds+=( ${(s[ ])ZPCRE_OP} "$_regexp_replace_new" )
# for 0-width matches, increase offset by 1 to avoid
# infinite loop
- 4=$((argv[-2] + (argv[-3] == argv[-2])))
+ (( _regexp_replace_offset = _regexp_replace_finds[-2] + (_regexp_replace_finds[-3] == _regexp_replace_finds[-2]) ))
done
- (($# > 6)) || return # no match
+ (( $#_regexp_replace_finds )) || return # no match
- set +o multibyte
+ unsetopt multibyte
- # $5 contains the result, $6 the current offset
- 5= 6=1
- for 2 3 4 in "$@[7,-1]"; do
- 5+=${(P)1[$6,$2]}$4
- 6=$(($3 + 1))
+ _regexp_replace_offset=1
+ for _regexp_replace_start _regexp_replace_stop _regexp_replace_new in "$_regexp_replace_finds[@]"; do
+ _regexp_replace_result+=${_regexp_replace_subject[_regexp_replace_offset,_regexp_replace_start]}$_regexp_replace_new
+ (( _regexp_replace_offset = _regexp_replace_stop + 1 ))
done
- 5+=${(P)1[$6,-1]}
-else
+ _regexp_replace_result+=${_regexp_replace_subject[_regexp_replace_offset,-1]}
+
+else # no PCRE
# in ERE, we can't use an offset so ^, (and \<, \b, \B, [[:<:]] where
# available) won't work properly.
- # $4 is the string to be matched
- 4=${(P)1}
-
- while [[ -n $4 ]]; do
- if [[ $4 =~ $2 ]]; then
- # append initial part and substituted match
- 5+=${4[1,MBEGIN-1]}${(e)3}
- # truncate remaining string
- if ((MEND < MBEGIN)); then
- # zero-width match, skip one character for the next match
- ((MEND++))
- 5+=${4[1]}
- fi
- 4=${4[MEND+1,-1]}
- # indicate we did something
- 6=1
- else
- break
+ local _regexp_replace_ok=0
+ while [[ $_regexp_replace_subject =~ $_regexp_replace_regexp ]]; do
+ # append initial part and substituted match
+ _regexp_replace_result+=$_regexp_replace_subject[1,MBEGIN-1]${(Xe)_regexp_replace_replacement}
+ # truncate remaining string
+ if (( MEND < MBEGIN )); then
+ # zero-width match, skip one character for the next match
+ (( MEND++ ))
+ _regexp_replace_result+=$_regexp_replace_subject[MBEGIN]
fi
+ _regexp_replace_subject=$_regexp_replace_subject[MEND+1,-1]
+ _regexp_replace_ok=1
+ [[ -z $_regexp_replace_subject ]] && break
done
- [[ -n $6 ]] || return # no match
- 5+=$4
+ (( _regexp_replace_ok )) || return
+ _regexp_replace_result+=$_regexp_replace_subject
fi
-eval $1=\$5
+# assign result to target variable if at least one substitution was
+# made. At this point, if the variable was originally array or assoc, it
+# is converted to scalar. If $1 doesn't contain a valid lvalue
+# specification, an exception is raised (exits the shell process if
+# non-interactive).
+: ${(P)1::="$_regexp_replace_result"}
+
diff --git a/Functions/Misc/zgetopt b/Functions/Misc/zgetopt
new file mode 100755
index 000000000..4d5481abd
--- /dev/null
+++ b/Functions/Misc/zgetopt
@@ -0,0 +1,181 @@
+#!/bin/zsh -f
+
+# wrapper around zparseopts which gives it an interface similar to util-linux's
+# getopt(1). see zshcontrib(1) for documentation
+
+emulate -L zsh -o extended_glob
+zmodload -i zsh/zutil || return 3
+
+local func=${funcstack[1]:-${0:t}}
+local caller=${funcstack[2]:-${ZSH_ARGZERO:t}}
+local errname=$caller:$func
+local optspec pat i posix=0
+local -a match mbegin mend optvv argvv
+local -a array lopts sopts name
+local -a specs no_arg_opts req_arg_opts opt_arg_opts tmp
+
+[[ $zsh_eval_context[-1] == toplevel ]] && errname=$func
+
+# same as leading + in short-opts spec
+(( $+POSIXLY_CORRECT )) && posix=1
+
+zparseopts -n $errname -D -F -G - \
+ {A,-array}:-=array \
+ {l,-longoptions,-long-options}+:-=lopts \
+ {n,-name}:-=name \
+ {o,-options}:-=sopts \
+|| {
+ print -ru2 "usage: $func [-A <array>] [-l <spec>] [-n <name>] [-o <spec>] -- <arg> ..."
+ return 2
+}
+
+name=( ${(@)name/#(-n|--name=)/} )
+[[ -n $name ]] || name=( $caller )
+
+(( $#array )) && array=( "${(@)array/#(-A|--array=)/}" )
+
+if [[ $zsh_eval_context[-1] != toplevel ]]; then
+ [[ $array == *[^A-Za-z0-9_.]* ]] && {
+ print -ru2 - "$errname: invalid array name: $array"
+ return 2
+ }
+ (( $#array )) || array=( argv )
+
+elif [[ -n $array ]]; then
+ print -ru2 - "$errname: -A option not meaningful unless called as function"
+ return 2
+fi
+
+# getopt requires a short option spec; we'll require either short or long
+(( $#sopts || $#lopts )) || {
+ print -ru2 - "$errname: missing option spec"
+ return 2
+}
+
+optspec=${(@)sopts/#(-o|--options=)/}
+sopts=( )
+
+for (( i = 1; i <= $#optspec; i++ )); do
+ # leading '+': act POSIXLY_CORRECT
+ if [[ $i == 1 && $optspec[i] == + ]]; then
+ posix=1
+ # leading '-': should leave operands interspersed with options, but this is
+ # not really possible with zparseopts
+ elif [[ $i == 1 && $optspec[i] == - ]]; then
+ print -ru2 - "$errname: optspec with leading - (disable operand collection) not supported"
+ return 2
+ # special characters: [+=\\] because they're special to zparseopts, ':'
+ # because it's special to getopt, '-' because it's the parsing terminator
+ elif [[ $optspec[i] == [+:=\\-] ]]; then
+ print -ru2 - "$errname: invalid short-option name: $optspec[i]"
+ return 2
+ # 'a'
+ elif [[ $optspec[i+1] != : ]]; then
+ sopts+=( $optspec[i] )
+ # 'a:'
+ elif [[ $optspec[i+2] != : ]]; then
+ sopts+=( $optspec[i]: )
+ (( i += 1 ))
+ # 'a::'
+ elif [[ $optspec[i+3] != : ]]; then
+ sopts+=( $optspec[i]:: )
+ (( i += 2 ))
+ fi
+done
+
+lopts=( ${(@)lopts/#(-l|--long(|-)options=)/} )
+lopts=( ${(@s<,>)lopts} )
+
+# don't allow characters that are special to zparseopts in long-option specs.
+# see above
+pat='(*[+=\\]*|:*|*:::##|*:[^:]*)'
+[[ -n ${(@M)lopts:#$~pat} ]] && {
+ print -ru2 - "$errname: invalid long-option spec: ${${(@M)lopts:#$~pat}[1]}"
+ return 2
+}
+
+lopts=( ${(@)lopts/#/-} )
+specs=( $sopts $lopts )
+
+# used below to identify options with optional optargs
+no_arg_opts=( ${(@)${(@M)specs:#*[^:]}/#/-} )
+req_arg_opts=( ${(@)${(@)${(@M)specs:#*[^:]:}/#/-}/%:#} )
+opt_arg_opts=( ${(@)${(@)${(@M)specs:#*::}/#/-}/%:#} )
+
+# getopt returns all instances of each option given, so add +
+specs=( ${(@)specs/%(#b)(:#)/+$match[1]} )
+
+# if we've got nothing now, the user probably gave -o '' or sth. pass an
+# explicit '' to zparseopts
+(( $#specs )) || specs=( '' )
+
+# POSIXLY_CORRECT: stop parsing options after first non-option argument
+if (( posix )); then
+ argvv=( "$@" )
+ zparseopts -n $name -a optvv -v argvv -D -F -G - "${(@)specs}" || return 1
+
+# default: permute options following non-option arguments
+else
+ zparseopts -n $name -a optvv -D -E -F -G - "${(@)specs}" || return 1
+ # -D + -E leaves an explicit -- in argv where-ever it might appear
+ local seen
+ while (( $# )); do
+ [[ -z $seen && $1 == -- ]] && seen=1 && shift && continue
+ argvv+=( "$1" )
+ shift
+ done
+fi
+
+# getopt outputs all optargs as separate parameters, even missing optional ones,
+# so we scan through and add/separate those if needed
+(( $#opt_arg_opts )) && {
+ local cur next
+ local -a old_optvv=( "${(@)optvv}" )
+ optvv=( )
+
+ for (( i = 1; i <= $#old_optvv; i++ )); do
+ cur=$old_optvv[i]
+ next=$old_optvv[i+1]
+ # option with no optarg
+ if [[ -n ${no_arg_opts[(r)$cur]} ]]; then
+ optvv+=( $cur )
+ # option with required optarg -- will appear in next element
+ elif [[ -n ${req_arg_opts[(r)$cur]} ]]; then
+ optvv+=( $cur "$next" )
+ (( i++ ))
+ # long option with optional optarg -- will appear in same element delimited
+ # by = (even if missing)
+ elif [[ $cur == *=* && -n ${opt_arg_opts[(r)${cur%%=*}]} ]]; then
+ optvv+=( ${cur%%=*} "${cur#*=}" )
+ # short option with optional optarg -- will appear in same element with no
+ # delimiter (thus the option appears alone if the optarg is missing)
+ elif [[ -n ${opt_arg_opts[(r)${(M)cur#-?}]} ]]; then
+ optvv+=( ${(M)cur#-?} "${cur#-?}" )
+ # ???
+ else
+ print -ru2 - "$errname: parse error, please report!"
+ print -ru2 - "$errname: specs: ${(j< >)${(@q+)specs}}"
+ print -ru2 - "$errname: old_optvv: ${(j< >)${(@q+)old_optvv}}"
+ print -ru2 - "$errname: cur: $cur"
+ optvv+=( $cur ) # i guess?
+ fi
+ done
+}
+
+# called as function, assign to array. use EXIT trap to assign in caller's
+# context
+if [[ -n $array ]]; then
+ trap "$array=( ${(j< >)${(@q+)optvv}} -- ${(j< >)${(@q+)argvv}} )" EXIT
+
+# called as function, print
+elif [[ $zsh_eval_context[-1] != toplevel ]]; then
+ print -r - "${(@q+)optvv}" -- "${(@q+)argvv}"
+
+# called as a script, print. use unconditional single-quoting. this is ugly but
+# it's the closest to what getopt does and it offers compatibility with legacy
+# shells
+else
+ print -r - "${(@qq)optvv}" -- "${(@qq)argvv}"
+fi
+
+return 0
diff --git a/Functions/Misc/zkbd b/Functions/Misc/zkbd
index 1065a84f1..493cc65ea 100644
--- a/Functions/Misc/zkbd
+++ b/Functions/Misc/zkbd
@@ -9,9 +9,15 @@
}
emulate -RL zsh
-local zkbd term key seq
+local zkbd override term key seq
-zkbd=${ZDOTDIR:-$HOME}/.zkbd
+zparseopts -D -F -- d:=override || return 1
+
+if (( $#override )); then
+ zkbd=$override[2]
+else
+ zkbd=${ZDOTDIR:-$HOME}/.zkbd
+fi
[[ -d $zkbd ]] || mkdir $zkbd || return 1
trap 'unfunction getmbkey getseq; command rm -f $zkbd/$TERM.tmp' 0
diff --git a/Functions/Misc/zmathfuncdef b/Functions/Misc/zmathfuncdef
index 5ed991f68..6b8dfe1dd 100644
--- a/Functions/Misc/zmathfuncdef
+++ b/Functions/Misc/zmathfuncdef
@@ -61,7 +61,7 @@ if ! zmodload -e zsh/mathfunc; then
mathfuncs=(abs acos acosh asin asinh atan atanh cbrt ceil cos cosh erf erfc
exp expm1 fabs float floor gamma int j0 j1 lgamma log log10 log1p logb
sin sinh sqrt tan tanh y0 y1 signgam copysign fmod hypot nextafter jn yn
- ldexp scalb rand48)
+ ldexp scalb rand48 ilogb isinf isnan log2 rint)
mathfuncpat="(${(j.|.)mathfuncs})"
bodysearch=$body
while [[ $bodysearch = (#b)(*[^[:alnum]]|)([[:alnum:]]##)\((*) ]]; do
diff --git a/Functions/Misc/zmv b/Functions/Misc/zmv
index 2002af5a6..ed4868346 100644
--- a/Functions/Misc/zmv
+++ b/Functions/Misc/zmv
@@ -183,7 +183,7 @@ repl=$2
shift 2
if [[ -n $opt_s && $action != ln ]]; then
- print -r -- "$myname: invalid option: -s" >&2
+ print -r -- "$myname: bad option: -s" >&2
return 1
fi