summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--Doc/Zsh/builtins.yo58
-rw-r--r--Doc/Zsh/contrib.yo219
-rw-r--r--Functions/MIME/pick-web-browser59
-rw-r--r--Functions/Misc/.distfiles1
-rw-r--r--Functions/Misc/zcalc31
-rw-r--r--Src/builtin.c176
-rw-r--r--Src/exec.c11
-rw-r--r--Src/math.c64
-rw-r--r--Src/module.c10
-rw-r--r--Src/zsh.h5
-rw-r--r--Test/C04funcdef.ztst59
12 files changed, 568 insertions, 137 deletions
diff --git a/ChangeLog b/ChangeLog
index 3d3fe0c83..ddae2d0c5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2006-04-19 Peter Stephenson <p.w.stephenson@ntlworld.com>
+
+ * unposted: Doc/Zsh/contrib.yo, Functions/MIME/pick-web-browser:
+ add some styles for commands.
+
+ * 22416: Doc/Zsh/builtins.yo, Doc/Zsh/contrib.yo,
+ Functions/Misc/.distfiles, Functions/Misc/zcalc,
+ Functions/Misc/zmathfuncdef, Src/builtin.c, Src/exec.c,
+ Src/module,c, Src/math.c, Src/module.c, Src/zsh.h,
+ Test/C04funcdef.ztst: user-defined math functions via
+ shell functions.
+
2006-04-14 Doug Kearns <djkea2@gus.gscit.monash.edu.au>
* unposted: Completion/Unix/Command/_raggle: update for version
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 99b3238bd..bd81a7746 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -501,8 +501,52 @@ Equivalent to tt(typeset -E), except that options irrelevant to floating
point numbers are not permitted.
)
findex(functions)
-item(tt(functions) [ {tt(PLUS())|tt(-)}tt(UXkmtuz) ] [ var(name) ... ])(
-Equivalent to tt(typeset -f).
+xitem(tt(functions) [ {tt(PLUS())|tt(-)}tt(UXkmtuz) ] [ var(name) ... ])
+xitem(tt(functions -M) var(mathfn) [ var(min) [ var(max) [ var(shellfn) ] ] ])
+xitem(tt(functions -M) [ tt(-m) var(pattern) ... ])
+item(tt(functions +M) [ tt(-m) ] var(mathfn))(
+Equivalent to tt(typeset -f), with the exception of the tt(-M) option.
+Use of the tt(-M) option may not be combined with any of the options
+handled by tt(typeset -f).
+
+tt(functions -M) var(mathfn) defines var(mathfn) as the name of
+a mathematical function recognised in all forms of arithmetical expressions;
+see
+ifzman(the section `Arithmetic Evaluation' in zmanref(zshmisc))\
+ifnzman(noderef(Arithmetic Evaluation))\
+. By default var(mathfn) may take
+any number of comma-separated arguments. If var(min) is given,
+it must have exactly var(min) args; if var(min) and var(max) are
+both given, it must have at least var(min) and and at most var(max)
+args. var(max) may be -1 to indicate that there is no upper limit.
+
+By default the function is implemented by a shell function of the same
+name; if var(shellfn) is specified it gives the name of the corresponding
+shell function while var(mathfn) remains the name used in arithmetical
+expressions. The name of the function in tt($0) is var(mathfn) (not
+var(shellfn) as would usually be the case), provided the option
+tt(FUNCTION_ARGZERO) is in effect. The positional parameters in the shell
+function correspond to the arguments of the mathematical function call.
+The result of the last arithmetical expression evaluated
+inside the shell function (even if it is a form that normally only returns
+a status) gives the result of the mathematical function.
+
+tt(functions -M) with no arguments lists all such user-defined functions in
+the same form as a definition. With the additional option tt(-m) and
+a list of arguments, all functions whose var(mathfn) matches one of
+the pattern arguments are listed.
+
+tt(function +M) removes the list of mathematical functions; with the
+additional option tt(-m) the arguments are treated as patterns and
+all functions whose tt(mathfn) matches the pattern are removed. Note
+that the shell function implementing the behaviour is not removed
+(regardless of whether its name coincides with tt(mathfn)).
+
+For example, the following prints the cube of 3:
+
+example(zmath_cube+LPAR()RPAR() { (( $1 * $1 * $1 )) }
+functions -M cube 1 1 zmath_cube
+print $(( cube+LPAR()3+RPAR() )))
)
module(getcap)(zsh/cap)
findex(getln)
@@ -652,8 +696,10 @@ tt(kill -IO) and tt(kill -POLL) have the same effect.
findex(let)
item(tt(let) var(arg) ...)(
Evaluate each var(arg) as an arithmetic expression.
-See noderef(Arithmetic Evaluation) for a description
-of arithmetic expressions. The exit status is 0 if the
+See
+ifzman(the section `Arithmetic Evaluation' in zmanref(zshmisc))\
+ifnzman(noderef(Arithmetic Evaluation))
+for a description of arithmetic expressions. The exit status is 0 if the
value of the last expression is nonzero, and 1 otherwise.
)
findex(limit)
@@ -856,7 +902,9 @@ that allows it to be reused as shell input. With the numeric format
specifiers, if the corresponding argument starts with a quote character,
the numeric value of the following character is used as the number to
print otherwise the argument is evaluated as an arithmetic expression. See
-noderef(Arithmetic Evaluation) for a description of arithmetic
+ifzman(the section `Arithmetic Evaluation' in zmanref(zshmisc))\
+ifnzman(noderef(Arithmetic Evaluation))
+for a description of arithmetic
expressions. With `tt(%n)', the corresponding argument is taken as an
identifier which is created as an integer parameter.
diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index a0fc1ff56..e8555d89d 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -15,6 +15,7 @@ menu(Prompt Themes)
menu(ZLE Functions)
menu(Exception Handling)
menu(MIME Functions)
+menu(Mathematical Functions)
menu(Other Functions)
endmenu()
@@ -1339,7 +1340,7 @@ if a shell error subsequently occurs. Adding tt(unset EXCEPTION) at the
start of the outermost layer of any code that uses exception handling will
eliminate this problem.
-texinode(MIME Functions)(Other Functions)(Exception Handling)(User Contributions)
+texinode(MIME Functions)(Mathematical Functions)(Exception Handling)(User Contributions)
sect(MIME Functions)
Three functions are available to provide handling of files recognised by
@@ -1347,6 +1348,8 @@ extension, for example to dispatch a file tt(text.ps) when executed as a
command to an appropriate viewer.
startitem()
+findex(zsh-mime-setup)
+findex(zsh-mime-handler)
xitem(tt(zsh-mime-setup [-flv]))
item(tt(zsh-mime-handler))(
These two functions use the files tt(~/.mime.types) and tt(/etc/mime.types),
@@ -1520,6 +1523,7 @@ example(text/html; /usr/bin/lynx '%s'; needsterminal)
)
enditem()
)
+findex(pick-web-browser)
item(tt(pick-web-browser))(
This function is separate from the two MIME functions described above
and can be assigned directly to a suffix:
@@ -1528,19 +1532,19 @@ example(autoload -U pick-web-browser
alias -s html=pick-web-browser)
It is provided as an intelligent front end to dispatch a web browser.
-It will check if an X Windows display is available, and if so
-if there is already a browser running which can accept a remote
+It will check if an X Windows display is available, and if so if there
+is already a browser running on the display which can accept a remote
connection. In that case, the file will be displayed in that browser;
you should check explicitly if it has appeared in the running browser's
-window. Otherwise, it will start a new browser according to a builtin
+window. Otherwise, it will start a new browser according to a built-in
set of preferences.
Alternatively, tt(pick-web-browser) can be run as a zsh script.
Two styles are available to customize the choice of browsers:
-tt(x-browsers) when running under the X Windows System, and
+tt(x-browsers) when running under the X Window System, and
tt(tty-browsers) otherwise. These are arrays in decreasing order
-of preference consiting of the command name under which to start the
+of preference consisting of the command name under which to start the
browser. They are looked up in the context tt(:mime:) (which may
be extended in future, so appending `tt(*)' is recommended). For
example,
@@ -1550,10 +1554,140 @@ example(zstyle ':mime:*' x-browsers opera konqueror netscape)
specifies that tt(pick-web-browser) should first look for a runing
instance of Opera, Konqueror or Netscape, in that order, and if it
fails to find any should attempt to start Opera.
+
+In addition, the style tt(command), if set, is used to pick the command
+used to open a page for a browser. The context is
+tt(:mime:browser:new:$browser:) to start a new browser or
+tt(:mime:browser:running:$browser:) to open a URL in a browser already
+runing on the current X display. The escape sequence tt(%b) in the
+style's value will be replaced by the browser, while tt(%u) will be
+replaced by the URL. If the style is not set, the default for all new
+instances is equivalent to tt(%b %u) and the defaults for using running
+browsers are equivalent to the values tt(kfmclient openURL %u) for
+Konqueror, tt(firefox -new-tab %u) for Firefox and tt(%b -remote
+"openUrl+LPAR()%u+RPAR()") for all others.
)
enditem()
-texinode(Other Functions)()(MIME Functions)(User Contributions)
+texinode(Mathematical Functions)(Other Functions)(MIME Functions)(User Contributions)
+sect(Mathematical Functions)
+
+startitem()
+findex(zcalc)
+item(tt(zcalc) [ var(expression) ... ])(
+A reasonably powerful calculator based on zsh's arithmetic evaluation
+facility. The syntax is similar to that of formulae in most programming
+languages; see
+ifzman(the section `Arithmetic Evaluation' in zmanref(zshmisc))\
+ifnzman(noderef(Arithmetic Evaluation)) for details. The mathematical
+library tt(zsh/mathfunc) will be loaded if it is available; see
+ifzman(the section `The zsh/mathfunc Module' in zmanref(zshmodules))\
+ifnzman(noderef(The zsh/mathfunc Module)). The mathematical functions
+correspond to the raw system libraries, so trigonometric functions are
+evaluated using radians, and so on.
+
+Each line typed is evaluated as an expression. The prompt shows a number,
+which corresponds to a positional parameter where the result of that
+calculation is stored. For example, the result of the calculation on the
+line preceded by `tt(4> )' is available as tt($4). The last value
+calculated is available as tt(ans). Full command line editing, including
+the history of previous calculations, is available; the history is saved in
+the file tt(~/.zcalc_history). To exit, enter a blank line or type `tt(q)'
+on its own.
+
+If arguments are given to tt(zcalc) on start up, they are used to prime the
+first few positional parameters. A visual indication of this is given when
+the calculator starts.
+
+The constants tt(PI) (3.14159...) and tt(E) (2.71828...) are provided.
+Parameter assignment is possible, but note that all parameters will be put
+into the global namespace.
+
+The output base can be initialised by passing the option `tt(-#)var(base)',
+for example `tt(zcalc -#16)' (the `tt(#)' may have to be quoted, depending
+on the globbing options set).
+
+The prompt is configurable via the parameter tt(ZCALCPROMPT), which
+undergoes standard prompt expansion. The index of the current entry is
+stored locally in the first element of the array tt(psvar), which can be
+referred to in tt(ZCALCPROMPT) as `tt(%1v)'. The default prompt is
+`tt(%1v> )'.
+
+The output precision may be specified within zcalc by special commands
+familiar from many calculators:
+startitem()
+item(tt(norm))(
+The default output format. It corresponds to the printf tt(%g)
+specification. Typically this shows six decimal digits.
+)
+item(tt(sci) var(digits))(
+Scientific notation, corresponding to the printf tt(%g) output format with
+the precision given by var(digits). This produces either fixed point or
+exponential notation depending on the value output.
+)
+item(tt(fix) var(digits))(
+Fixed point notation, corresponding to the printf tt(%f) output format with
+the precision given by var(digits).
+)
+item(tt(eng) var(digits))(
+Exponential notation, corresponding to the printf tt(%E) output format with
+the precision given by var(digits).
+)
+enditem()
+
+Other special commands:
+startitem()
+item(tt(local) var(arg) ...)(
+Declare variables local to the function. Note that certain variables
+are used by the function for its own purposes. Other variables
+may be used, too, but they will be taken from or put into the global
+scope.
+)
+item(tt(function) var(name) [ var(body) ])(
+Define a mathematical function or (with no var(body)) delete it.
+The function is defined using tt(zmathfuncdef), see below.
+
+Note that tt(zcalc) takes care of all quoting. Hence for example:
+
+example(function cube $1 * $1 * $1)
+
+defines a function to cube the sole argument.
+)
+item(tt([#)var(base)tt(]))(
+When this syntax appears on a line by itself, the default output radix
+is set to var(base). Use, for example, `tt([#16])' to display hexadecimal
+output preceded by an indication of the base, or `tt([##16])' just to
+display the raw number in the given base. Bases themselves are always
+specified in decimal. `tt([#])' restores the normal output format. Note
+that setting an output base suppresses floating point output; use `tt([#])'
+to return to normal operation.
+
+)
+enditem()
+
+See the comments in the function for a few extra tips.
+)
+findex(zmathfuncdef)
+item(tt(zmathfuncdef) var(mathfunc) [ var(body) ])(
+A convenient front end to tt(functions -M).
+
+With two arguments, define a mathematical function named var(mathfunc)
+which can be used in any form of arithmetic evaluation. var(body)
+is a mathematical expression to implement the function. It may
+contain references to position parameters tt($1), tt($2), ...
+to refer to mandatory parameters and tt(${1:-)var(defvalue)tt(}) ...
+to refer to optional parameters. Note that the forms must be
+strictly adhered to for the function to calculate the correct number
+of arguments. The implementation is held in a shell function named
+tt(zsh_math_func_)var(mathfunc); usually the user will not need
+to refer to the shell function directly.
+
+With one argument, remove the mathematical function var(mathfunc)
+as well as the shell function implementation.
+)
+enditem()
+
+texinode(Other Functions)()(Mathematical Functions)(User Contributions)
sect(Other Functions)
There are a large number of helpful functions in the tt(Functions/Misc)
@@ -1720,77 +1854,6 @@ This is a good choice in that example because no plain file can be named
For details of the other tt(zargs) options, see zmanref(xargs) or run
tt(zargs) with the tt(-)tt(-help) option.
)
-findex(zcalc)
-item(tt(zcalc) [ var(expression) ... ])(
-A reasonably powerful calculator based on zsh's arithmetic evaluation
-facility. The syntax is similar to that of formulae in most programming
-languages; see
-ifzman(the section `Arithmetic Evaluation' in zmanref(zshmisc))\
-ifnzman(noderef(Arithmetic Evaluation)) for details. The mathematical
-library tt(zsh/mathfunc) will be loaded if it is available; see
-ifzman(the section `The zsh/mathfunc Module' in zmanref(zshmodules))\
-ifnzman(noderef(The zsh/mathfunc Module)). The mathematical functions
-correspond to the raw system libraries, so trigonometric functions are
-evaluated using radians, and so on.
-
-Each line typed is evaluated as an expression. The prompt shows a number,
-which corresponds to a positional parameter where the result of that
-calculation is stored. For example, the result of the calculation on the
-line preceded by `tt(4> )' is available as tt($4). Full command line
-editing, including the history of previous calculations, is available; the
-history is saved in the file tt(~/.zcalc_history). To exit, enter a blank
-line or type `tt(q)' on its own.
-
-If arguments are given to tt(zcalc) on start up, they are used to prime the
-first few positional parameters. A visual indication of this is given when
-the calculator starts.
-
-The constants tt(PI) (3.14159...) and tt(E) (2.71828...) are provided.
-Parameter assignment is possible, but note that all parameters will be put
-into the global namespace.
-
-An extra facility is provided for changing the default output base. Use,
-for example, `tt([#16])' to display hexadecimal output preceded by an
-indication of the base, or `tt([##16])' just to display the raw number in
-the given base. Bases themselves are always specified in decimal.
-`tt([#])' restores the normal output format. Note that setting an output
-base suppresses floating point output; use `tt([#])' to return to normal
-operation.
-
-The output base can be initialised by passing the option `tt(-#)var(base)',
-for example `tt(zcalc -#16)' (the `tt(#)' may have to be quoted, depending
-on the globbing options set).
-
-The prompt is configurable via the parameter tt(ZCALCPROMPT), which
-undergoes standard prompt expansion. The index of the current entry is
-stored locally in the first element of the array tt(psvar), which can be
-referred to in tt(ZCALCPROMPT) as `tt(%1v)'. The default prompt is
-`tt(%1v> )'.
-
-The output precision may be specified within zcalc by special commands
-familiar from many calculators:
-startitem()
-item(tt(norm))(
-The default output format. It corresponds to the printf tt(%g)
-specification. Typically this shows six decimal digits.
-)
-item(tt(sci) var(digits))(
-Scientific notation, corresponding to the printf tt(%g) output format with
-the precision given by var(digits). This produces either fixed point or
-exponential notation depending on the value output.
-)
-item(tt(fix) var(digits))(
-Fixed point notation, corresponding to the printf tt(%f) output format with
-the precision given by var(digits).
-)
-item(tt(eng) var(digits))(
-Exponential notation, corresponding to the printf tt(%E) output format with
-the precision given by var(digits).
-)
-enditem()
-
-See the comments in the function for a few extra tips.
-)
findex(zed)
xitem(tt(zed) [ tt(-f) ] var(name))
item(tt(zed -b))(
diff --git a/Functions/MIME/pick-web-browser b/Functions/MIME/pick-web-browser
index 665b0db54..ce35a0a79 100644
--- a/Functions/MIME/pick-web-browser
+++ b/Functions/MIME/pick-web-browser
@@ -22,6 +22,8 @@
emulate -L zsh
setopt extendedglob cbases nonomatch
+zmodload -i zsh/zutil
+
local -a xbrowsers ttybrowsers
# X Windows browsers which might be running and can accept
@@ -38,7 +40,7 @@ zstyle -a :mime: tty-browsers ttybrowsers ||
litc="-_./"
local -a windows remoteargs match mbegin mend
-local url browser
+local url browser command
url=$1
if [[ -f $url ]]; then
@@ -80,22 +82,31 @@ if [[ -n $DISPLAY ]]; then
# Is any browser we've heard of running?
for browser in $xbrowsers; do
- if [[ $windows[(I)(#i)$browser] -ne 0 ]]; then
- if [[ $browser = konqueror ]]; then
- # kfmclient is less hairy and better supported than direct
- # use of dcop. Run kfmclient --commands
- # for more information. Note that as konqueror is a fully
- # featured file manager, this will actually do complete
- # MIME handling, not just web pages.
- kfmclient openURL $url ||
- dcop $(dcop|grep konqueror) default openBrowserWindow $url
- elif [[ $browser = firefox ]]; then
- # open in new tab: should make this customizable
- $browser -new-tab $url
+ # Some browser executables call themselves <browser>-bin
+ if [[ $windows[(I)(#i)$browser(|[.-]bin)] -ne 0 ]]; then
+ if zstyle -s ":mime:browser:running:${browser}:" command command; then
+ # The (q)'s here and below are pure paranoia: no browser
+ # name is going to include metacharacters, and we already
+ # converted difficult characters in the URL to hex.
+ zformat -f command $command b:${(q)browser} u:${(q)url}
+ eval $command
else
- # Mozilla bells and whistles are described at:
- # http://www.mozilla.org/unix/remote.html
- $browser -remote "openURL($url)"
+ if [[ $browser = konqueror ]]; then
+ # kfmclient is less hairy and better supported than direct
+ # use of dcop. Run kfmclient --commands
+ # for more information. Note that as konqueror is a fully
+ # featured file manager, this will actually do complete
+ # MIME handling, not just web pages.
+ kfmclient openURL $url ||
+ dcop $(dcop|grep konqueror) default openBrowserWindow $url
+ elif [[ $browser = firefox ]]; then
+ # open in new tab
+ $browser -new-tab $url
+ else
+ # Mozilla bells and whistles are described at:
+ # http://www.mozilla.org/unix/remote.html
+ $browser -remote "openURL($url)"
+ fi
fi
return
fi
@@ -104,8 +115,13 @@ if [[ -n $DISPLAY ]]; then
# Start our preferred X Windows browser in the background.
for browser in $xbrowsers; do
if eval "[[ =$browser != \\=$browser ]]"; then
- # The following is to make the job text more readable.
- eval ${(q)browser} ${(q)url} "&"
+ if zstyle -s ":mime:browser:new:${browser}:" command command; then
+ zformat -f command $command b:${(q)browser} u:${(q)url}
+ eval $command "&"
+ else
+ # The following is to make the job text more readable.
+ eval ${(q)browser} ${(q)url} "&"
+ fi
break
fi
done
@@ -113,7 +129,12 @@ else
# Start up dumb terminal browser.
for browser in $ttybrowsers; do
if eval "[[ =$browser != \\=$browser ]]"; then
- $browser $url
+ if zstyle -s ":mime:browser:new:${browser}" command command; then
+ zformat -f command $command b:${(q)browser} u:${(q)url}
+ eval $command
+ else
+ $browser $url
+ fi
break
fi
done
diff --git a/Functions/Misc/.distfiles b/Functions/Misc/.distfiles
index 9b078cb65..08cd89554 100644
--- a/Functions/Misc/.distfiles
+++ b/Functions/Misc/.distfiles
@@ -3,4 +3,5 @@ DISTFILES_SRC='
allopt getjobs mere relative zcalc zmv zargs
checkmail harden nslookup run-help zed zrecompile
colors is-at-least promptnl tetris zkbd zstyle+
+zmathfuncdef
'
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
index b83a939c9..9ce02c02f 100644
--- a/Functions/Misc/zcalc
+++ b/Functions/Misc/zcalc
@@ -42,6 +42,13 @@
# use the variables listed in the `local' and `integer' lines below
# (translation: I can't be bothered to provide a sandbox).
#
+# You can declare or delete math functions (implemented via zmathfuncdef):
+# 1> function cube $1 * $1 * $1
+# This has a single compulsory argument. Note the function takes care of
+# the punctuation. To delete the function, put nothing (at all) after
+# the function name:
+# 1> function cube
+#
# Some constants are already available: (case sensitive as always):
# PI pi, i.e. 3.1415926545897931
# E e, i.e. 2.7182818284590455
@@ -86,6 +93,8 @@
emulate -L zsh
setopt extendedglob
+# TODO: make local variables that shouldn't be visible in expressions
+# begin with _.
local line ans base defbase forms match mbegin mend psvar optlist opt arg
local compcontext="-math-"
integer num outdigits outform=1
@@ -96,6 +105,7 @@ history -ap "${ZDOTDIR:-$HOME}/.zcalc_history"
forms=( '%2$g' '%.*g' '%.*f' '%.*E' )
zmodload -i zsh/mathfunc 2>/dev/null
+autoload zmathfuncdef
: ${ZCALCPROMPT="%1v> "}
@@ -167,34 +177,39 @@ while vared -cehp "${(%)ZCALCPROMPT}" line; do
print -s -- $line
case ${${line##[[:blank:]]#}%%[[:blank:]]#} in
- q) # Exit if `q' on its own.
+ (q) # Exit if `q' on its own.
return 0
;;
- norm) # restore output format to default
+ (norm) # restore output format to default
outform=1
;;
- sci[[:blank:]]#(#b)(<->)(#B))
+ (sci[[:blank:]]#(#b)(<->)(#B))
outdigits=$match[1]
outform=2
;;
- fix[[:blank:]]#(#b)(<->)(#B))
+ (fix[[:blank:]]#(#b)(<->)(#B))
outdigits=$match[1]
outform=3
;;
- eng[[:blank:]]#(#b)(<->)(#B))
+ (eng[[:blank:]]#(#b)(<->)(#B))
outdigits=$match[1]
outform=4
;;
- local([[:blank:]]##*|))
+ (local([[:blank:]]##*|))
eval $line
line=
continue
;;
- *)
+ (function[[:blank:]]##(#b)([^[:blank:]]##)(|[[:blank:]]##([^[:blank:]]*)))
+ zmathfuncdef $match[1] $match[3]
+ line=
+ continue
+ ;;
+ (*)
# Latest value is stored as a string, because it might be floating
# point or integer --- we don't know till after the evaluation, and
# arrays always store scalars anyway.
- #
+ #
# Since it's a string, we'd better make sure we know which
# base it's in, so don't change that until we actually print it.
eval "ans=\$(( $line ))"
diff --git a/Src/builtin.c b/Src/builtin.c
index 7dca28a58..05203d485 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -46,7 +46,7 @@ static struct builtin builtins[] =
BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL),
BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmrs", NULL),
- BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "tUXwkz", "u"),
+ BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "ktUwXz", "u"),
BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL),
BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL),
BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
@@ -72,7 +72,7 @@ static struct builtin builtins[] =
BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "nlre:IRWAdDfEimpPa", NULL),
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
- BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmtuUz", NULL),
+ BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtuUz", NULL),
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL),
@@ -2476,6 +2476,43 @@ eval_autoload(Shfunc shf, char *name, Options ops, int func)
(OPT_ISSET(ops,'z') ? 0 : 1)), 1);
}
+
+/* List a user-defined math function. */
+static void
+listusermathfunc(MathFunc p)
+{
+ int showargs;
+
+ if (p->module)
+ showargs = 3;
+ else if (p->maxargs != (p->minargs ? p->minargs : -1))
+ showargs = 2;
+ else if (p->minargs)
+ showargs = 1;
+ else
+ showargs = 0;
+
+ printf("functions -M %s", p->name);
+ if (showargs) {
+ printf(" %d", p->minargs);
+ showargs--;
+ }
+ if (showargs) {
+ printf(" %d", p->maxargs);
+ showargs--;
+ }
+ if (showargs) {
+ /*
+ * function names are not required to consist of ident characters
+ */
+ putchar(' ');
+ quotedzputs(p->module, stdout);
+ showargs--;
+ }
+ putchar('\n');
+}
+
+
/* Display or change the attributes of shell functions. *
* If called as autoload, it will define a new autoloaded *
* (undefined) shell function. */
@@ -2522,6 +2559,141 @@ bin_functions(char *name, char **argv, Options ops, int func)
if (OPT_PLUS(ops,'f') || OPT_ISSET(ops,'+'))
pflags |= PRINT_NAMEONLY;
+ if (OPT_MINUS(ops,'M') || OPT_PLUS(ops,'M')) {
+ MathFunc p, q;
+ /*
+ * Add/remove/list function as mathematical.
+ */
+ if (on || off || pflags || OPT_ISSET(ops,'X') || OPT_ISSET(ops,'u')
+ || OPT_ISSET(ops,'U') || OPT_ISSET(ops,'w')) {
+ zwarnnam(name, "invalid option(s)", NULL, 0);
+ return 1;
+ }
+ if (!*argv) {
+ /* List functions. */
+ queue_signals();
+ for (p = mathfuncs; p; p = p->next)
+ if (p->flags & MFF_USERFUNC)
+ listusermathfunc(p);
+ unqueue_signals();
+ } else if (OPT_ISSET(ops,'m')) {
+ /* List matching functions. */
+ for (; *argv; argv++) {
+ tokenize(*argv);
+ if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
+ queue_signals();
+ for (p = mathfuncs, q = NULL; p; q = p, p = p->next) {
+ MathFunc next;
+ do {
+ next = NULL;
+ if ((p->flags & MFF_USERFUNC) &&
+ pattry(pprog, p->name)) {
+ if (OPT_PLUS(ops,'M')) {
+ next = p->next;
+ removemathfunc(q, p);
+ p = next;
+ } else
+ listusermathfunc(p);
+ }
+ /* if we deleted one, retry with the new p */
+ } while (next);
+ }
+ unqueue_signals();
+ } else {
+ untokenize(*argv);
+ zwarnnam(name, "bad pattern : %s", *argv, 0);
+ returnval = 1;
+ }
+ }
+ } else if (OPT_PLUS(ops,'M')) {
+ /* Delete functions. -m is allowed but is handled above. */
+ for (; *argv; argv++) {
+ queue_signals();
+ for (p = mathfuncs, q = NULL; p; q = p, p = p->next) {
+ if (!strcmp(p->name, *argv)) {
+ if (!(p->flags & MFF_USERFUNC)) {
+ zwarnnam(name, "+M %s: is a library function",
+ *argv, 0);
+ returnval = 1;
+ break;
+ }
+ removemathfunc(q, p);
+ break;
+ }
+ }
+ unqueue_signals();
+ }
+ } else {
+ /* Add a function */
+ int minargs = 0, maxargs = -1;
+ char *funcname = *argv++;
+ char *modname = NULL;
+ char *ptr;
+
+ for (ptr = funcname; *ptr; ptr++)
+ if (!iident(*ptr))
+ break;
+ if (idigit(*funcname) || funcname == ptr || *ptr) {
+ zwarnnam(name, "-M %s: bad math function name", funcname, 0);
+ return 1;
+ }
+
+ if (*argv) {
+ minargs = (int)zstrtol(*argv, &ptr, 0);
+ if (minargs < 0 || *ptr) {
+ zwarnnam(name, "-M: invalid min number of arguments: %s",
+ *argv, 0);
+ return 1;
+ }
+ maxargs = minargs;
+ argv++;
+ }
+ if (*argv) {
+ maxargs = (int)zstrtol(*argv, &ptr, 0);
+ if (maxargs < -1 ||
+ (maxargs != -1 && maxargs < minargs) ||
+ *ptr) {
+ zwarnnam(name,
+ "-M: invalid max number of arguments: %s",
+ *argv, 0);
+ return 1;
+ }
+ argv++;
+ }
+ if (*argv)
+ modname = *argv++;
+ if (*argv) {
+ zwarnnam(name, "-M: too many arguments", NULL, 0);
+ return 1;
+ }
+
+ p = (MathFunc)zshcalloc(sizeof(struct mathfunc));
+ p->name = ztrdup(funcname);
+ p->flags = MFF_USERFUNC;
+ p->module = modname ? ztrdup(modname) : NULL;
+ p->minargs = minargs;
+ p->maxargs = maxargs;
+
+ queue_signals();
+ for (q = mathfuncs; q; q = q->next) {
+ if (!strcmp(q->name, funcname)) {
+ zwarnnam(name, "-M %s: function already exists",
+ funcname, 0);
+ zsfree(p->name);
+ zsfree(p->module);
+ zfree(p, sizeof(struct mathfunc));
+ return 1;
+ }
+ }
+
+ p->next = mathfuncs;
+ mathfuncs = p;
+ unqueue_signals();
+ }
+
+ return returnval;
+ }
+
/* If no arguments given, we will print functions. If flags *
* are given, we will print only functions containing these *
* flags, else we'll print them all. */
diff --git a/Src/exec.c b/Src/exec.c
index bb0e4161e..4612c9c1e 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -142,7 +142,6 @@ mod_export Funcstack funcstack;
#define execerr() if (!forked) { lastval = 1; goto done; } else _exit(1)
-static LinkList args;
static int doneps4;
static char *STTYval;
@@ -464,7 +463,7 @@ isgooderr(int e, char *dir)
/**/
void
-execute(UNUSED(Cmdnam cmdname), int dash, int defpath)
+execute(LinkList args, int dash, int defpath)
{
Cmdnam cn;
char buf[MAXCMDLEN], buf2[MAXCMDLEN];
@@ -482,15 +481,12 @@ execute(UNUSED(Cmdnam cmdname), int dash, int defpath)
* we first run the stty command with the value of this *
* parameter as it arguments. */
if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) {
- LinkList exargs = args;
char *t = tricat("stty", " ", s);
STTYval = 0; /* this prevents infinite recursion */
zsfree(s);
- args = NULL;
execstring(t, 1, 0);
zsfree(t);
- args = exargs;
} else if (s) {
STTYval = 0;
zsfree(s);
@@ -1827,6 +1823,7 @@ static void
execcmd(Estate state, int input, int output, int how, int last1)
{
HashNode hn = NULL;
+ LinkList args;
LinkNode node;
Redir fn;
struct multio *mfds[10];
@@ -2638,7 +2635,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
zsfree(STTYval);
STTYval = 0;
}
- execute((Cmdnam) hn, cflags & BINF_DASH, use_defpath);
+ execute(args, cflags & BINF_DASH, use_defpath);
} else { /* ( ... ) */
DPUTS(varspc,
"BUG: assignment before complex command");
@@ -4094,7 +4091,6 @@ execsave(void)
struct execstack *es;
es = (struct execstack *) malloc(sizeof(struct execstack));
- es->args = args;
es->list_pipe_pid = list_pipe_pid;
es->nowait = nowait;
es->pline_level = pline_level;
@@ -4122,7 +4118,6 @@ execrestore(void)
struct execstack *en;
DPUTS(!exstack, "BUG: execrestore() without execsave()");
- args = exstack->args;
list_pipe_pid = exstack->list_pipe_pid;
nowait = exstack->nowait;
pline_level = exstack->pline_level;
diff --git a/Src/math.c b/Src/math.c
index acf7bdd11..b14c1cd7a 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -42,6 +42,14 @@ int noeval;
/**/
mod_export mnumber zero_mnumber;
+/*
+ * The last value we computed: note this isn't cleared
+ * until the next computation, unlike unlike yyval.
+ * Everything else is saved and returned to allow recursive calls.
+ */
+/**/
+mnumber lastmathval;
+
/* last input base we used */
/**/
@@ -582,22 +590,42 @@ callmathfunc(char *o)
a[strlen(a) - 1] = '\0';
if ((f = getmathfunc(n, 1))) {
- if (f->flags & MFF_STR)
+ if (f->flags & MFF_STR) {
return f->sfunc(n, a, f->funcid);
- else {
+ } else {
int argc = 0;
- mnumber *argv = NULL, *q;
+ mnumber *argv = NULL, *q, marg;
LinkList l = newlinklist();
LinkNode node;
+ if (f->flags & MFF_USERFUNC) {
+ /* first argument is function name: always use mathfunc */
+ addlinknode(l, n);
+ }
+
while (iblank(*a))
a++;
while (*a) {
if (*a) {
argc++;
- q = (mnumber *) zhalloc(sizeof(mnumber));
- *q = mathevall(a, ARGPREC, &a);
- addlinknode(l, q);
+ if (f->flags & MFF_USERFUNC) {
+ /* need to pass strings */
+ char *str;
+ marg = mathevall(a, ARGPREC, &a);
+ if (marg.type & MN_FLOAT) {
+ /* convfloat is off the heap */
+ str = convfloat(marg.u.d, 0, 0, NULL);
+ } else {
+ char buf[BDIGBUFSIZE];
+ convbase(buf, marg.u.l, 10);
+ str = dupstring(buf);
+ }
+ addlinknode(l, str);
+ } else {
+ q = (mnumber *) zhalloc(sizeof(mnumber));
+ *q = mathevall(a, ARGPREC, &a);
+ addlinknode(l, q);
+ }
if (errflag || mtok != COMMA)
break;
}
@@ -608,12 +636,24 @@ callmathfunc(char *o)
if (!errflag) {
if (argc >= f->minargs && (f->maxargs < 0 ||
argc <= f->maxargs)) {
- if (argc) {
- q = argv = (mnumber *)zhalloc(argc * sizeof(mnumber));
- for (node = firstnode(l); node; incnode(node))
- *q++ = *(mnumber *)getdata(node);
+ if (f->flags & MFF_USERFUNC) {
+ char *shfnam = f->module ? f->module : n;
+ Eprog prog = getshfunc(shfnam);
+ if (prog == &dummy_eprog)
+ zerr("no such function: %s", shfnam, 0);
+ else {
+ doshfunc(n, prog, l, 0, 1);
+ return lastmathval;
+ }
+ } else {
+ if (argc) {
+ q = argv =
+ (mnumber *)zhalloc(argc * sizeof(mnumber));
+ for (node = firstnode(l); node; incnode(node))
+ *q++ = *(mnumber *)getdata(node);
+ }
+ return f->nfunc(n, argc, argv, f->funcid);
}
- return f->nfunc(n, argc, argv, f->funcid);
} else
zerr("wrong number of arguments: %s", o, 0);
}
@@ -1013,7 +1053,7 @@ mathevall(char *s, int prek, char **ep)
sp = xsp;
stack = xstack;
}
- return ret;
+ return lastmathval = ret;
}
diff --git a/Src/module.c b/Src/module.c
index 17daffc2d..de3fd9932 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -1384,7 +1384,7 @@ bin_zmodload_math(char *nam, char **args, Options ops)
MathFunc p;
for (p = mathfuncs; p; p = p->next) {
- if (p->module) {
+ if (!(p->flags & MFF_USERFUNC) && p->module) {
if (OPT_ISSET(ops,'L')) {
fputs("zmodload -af", stdout);
printf(" %s %s\n", p->module, p->name);
@@ -2085,7 +2085,8 @@ add_autoparam(char *nam, char *module)
MathFunc mathfuncs;
/**/
-static void removemathfunc(MathFunc previous, MathFunc current)
+void
+removemathfunc(MathFunc previous, MathFunc current)
{
if (previous)
previous->next = current->next;
@@ -2105,7 +2106,7 @@ getmathfunc(char *name, int autol)
for (p = mathfuncs; p; q = p, p = p->next)
if (!strcmp(name, p->name)) {
- if (autol && p->module) {
+ if (autol && p->module && !(p->flags & MFF_USERFUNC)) {
char *n = dupstring(p->module);
removemathfunc(q, p);
@@ -2131,7 +2132,7 @@ addmathfunc(MathFunc f)
for (p = mathfuncs; p; q = p, p = p->next)
if (!strcmp(f->name, p->name)) {
- if (p->module) {
+ if (p->module && !(p->flags & MFF_USERFUNC)) {
/*
* Autoloadable, replace.
*/
@@ -2206,6 +2207,7 @@ deletemathfunc(MathFunc f)
else
mathfuncs = f->next;
+ /* the following applies to both unloaded and user-defined functions */
if (f->module) {
zsfree(f->name);
zsfree(f->module);
diff --git a/Src/zsh.h b/Src/zsh.h
index 6a6ff2fe4..092e05c0c 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -86,8 +86,12 @@ struct mathfunc {
int funcid;
};
+/* Math function takes a string argument */
#define MFF_STR 1
+/* Math function has been loaded from library */
#define MFF_ADDED 2
+/* Math function is implemented by a shell function */
+#define MFF_USERFUNC 4
#define NUMMATHFUNC(name, func, min, max, id) \
{ NULL, name, 0, func, NULL, NULL, min, max, id }
@@ -815,7 +819,6 @@ struct process {
struct execstack {
struct execstack *next;
- LinkList args;
pid_t list_pipe_pid;
int nowait;
int pline_level;
diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst
index 07a20349d..04098d2ce 100644
--- a/Test/C04funcdef.ztst
+++ b/Test/C04funcdef.ztst
@@ -11,3 +11,62 @@
foo
0:Function definition without braces
>bar
+
+ functions -M m1
+ m1() { (( $# )) }
+ print $(( m1() ))
+ print $(( m1(1) ))
+ print $(( m1(1,2) ))
+0:User-defined math functions, argument handling
+>0
+>1
+>2
+
+ functions -M m2
+ m2() {
+ integer sum
+ local val
+ for val in $*; do
+ (( sum += $val ))
+ done
+ }
+ print $(( m2(1) ))
+ print $(( m2(1,3+3,4**2) ))
+0:User-defined math functions, complex argument handling
+>1
+>23
+
+ functions -M m3 1 2
+ m3() { (( 1 )) }
+ print zero
+ (print $(( m3() )))
+ print one
+ print $(( m3(1) ))
+ print two
+ print $(( m3(1,2) ))
+ print three
+ (print $(( m3(1,2,3) )))
+1:User-defined math functions, argument checking
+>zero
+>one
+>1
+>two
+>1
+>three
+?(eval):4: wrong number of arguments: m3()
+?(eval):10: wrong number of arguments: m3(1,2,3)
+
+ functions -M m4 0 0 testmathfunc
+ functions -M m5 0 0 testmathfunc
+ testmathfunc() {
+ if [[ $0 = m4 ]]; then
+ (( 4 ))
+ else
+ (( 5 ))
+ fi
+ }
+ print $(( m4() ))
+ print $(( m5() ))
+0:User-defined math functions, multiple interfaces
+>4
+>5