From b7474e065b82d930f8da472440282ea7654d491d Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 19 Apr 2006 16:09:06 +0000 Subject: 22416, tweaked: math functions via shell functions unposted: add styles to pick-web-browser --- ChangeLog | 12 +++ Doc/Zsh/builtins.yo | 58 ++++++++++- Doc/Zsh/contrib.yo | 219 ++++++++++++++++++++++++++-------------- Functions/MIME/pick-web-browser | 59 +++++++---- Functions/Misc/.distfiles | 1 + Functions/Misc/zcalc | 31 ++++-- Src/builtin.c | 176 +++++++++++++++++++++++++++++++- Src/exec.c | 11 +- Src/math.c | 64 +++++++++--- Src/module.c | 10 +- Src/zsh.h | 5 +- Test/C04funcdef.ztst | 59 +++++++++++ 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 + + * 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 * 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 -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 -- cgit 1.4.1