about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Doc/Zsh/arith.yo8
-rw-r--r--Doc/Zsh/builtins.yo10
-rw-r--r--Etc/zsh-development-guide85
-rw-r--r--Src/Modules/example.c48
-rw-r--r--Src/Modules/example.mdd1
-rw-r--r--Src/builtin.c2
-rw-r--r--Src/math.c79
-rw-r--r--Src/mkbltnmlst.sh4
-rw-r--r--Src/mkmakemod.sh3
-rw-r--r--Src/module.c234
-rw-r--r--Src/zsh.h24
11 files changed, 479 insertions, 19 deletions
diff --git a/Doc/Zsh/arith.yo b/Doc/Zsh/arith.yo
index f40a84732..b18aed94c 100644
--- a/Doc/Zsh/arith.yo
+++ b/Doc/Zsh/arith.yo
@@ -78,6 +78,14 @@ short-circuiting, and only one of the latter two expressions in a ternary
 operator is evaluated.  Note the precedence of the bitwise AND, OR,
 and XOR operators.
 
+cindex(math functions)
+cindex(functions, math)
+Mathematical functions can be called with the syntax
+`var(func)tt(LPAR())var(args)tt(RPAR())', where the function decides
+if the var(args) is used as a string or a comma-separated list of
+arithmetic expressions. The shell currently defines no mathematical
+functions, but modules may define some.
+
 An expression of the form `tt(#\)var(x)' where var(x) is any character
 sequence such as `tt(a)', `tt(^A)', or `tt(\M-\C-x)' gives the ascii
 value of this character and an expression of the form `tt(#)var(foo)'
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 7b79cd924..328e7db80 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1259,8 +1259,8 @@ cindex(modules, loading)
 cindex(loading modules)
 xitem(tt(zmodload) [ tt(-dL) ] [ ... ])
 xitem(tt(zmodload -e) [ ... ])
-xitem(tt(zmodload) [ tt(-a) [ tt(-bcp) [ tt(-I) ] ] ] [ tt(-iL) ] ...)
-item(tt(zmodload) tt(-u) [ tt(-abcdp) [ tt(-I) ] ] [ tt(-iL) ] ...)(
+xitem(tt(zmodload) [ tt(-a) [ tt(-bcpf) [ tt(-I) ] ] ] [ tt(-iL) ] ...)
+item(tt(zmodload) tt(-u) [ tt(-abcdpf) [ tt(-I) ] ] [ tt(-iL) ] ...)(
 tt(zmodload) performs operations relating to zsh's loadable modules.
 This feature is not available on all operating systems,
 or on all installations on a particular operating system.
@@ -1349,6 +1349,12 @@ item(tt(zmodload) tt(-up) [ tt(-i) ] var(parameter) ...)(
 The tt(-p) option is like the tt(-b) and tt(-c) options, but makes
 tt(zmodload) work on autoloaded parameters instead.
 )
+xitem(tt(zmodload) tt(-af) [ tt(-L) ])
+xitem(tt(zmodload) tt(-af) [ tt(-i) ] var(name) [ var(function) ... ])
+item(tt(zmodload) tt(-uf) [ tt(-i) ] var(function) ...)(
+The tt(-p) option is like the tt(-b), tt(-p), and tt(-c) options, but
+makes tt(zmodload) work on autoloaded math functions instead. 
+)
 xitem(tt(zmodload) tt(-a) [ tt(-L) ])
 xitem(tt(zmodload) tt(-a) [ tt(-i) ] var(name) [ var(builtin) ... ])
 item(tt(zmodload) tt(-ua) [ tt(-i) ] var(builtin) ...)(
diff --git a/Etc/zsh-development-guide b/Etc/zsh-development-guide
index 4ec4ff079..7223af07b 100644
--- a/Etc/zsh-development-guide
+++ b/Etc/zsh-development-guide
@@ -129,6 +129,7 @@ variables:
                     autoloading (without the leading `-')
   - autoprefixconds like autoinfixconds, but for prefix condition codes
   - autoparams      parameters defined by the module, for autoloading
+  - automathfuncs   math functions defined by the module, for autoloading
   - objects         .o files making up this module (*must* be defined)
   - proto           .pro files for this module (default generated from $objects)
   - headers         extra headers for this module (default none)
@@ -398,6 +399,90 @@ builtins and condition codes:
     ...
   }
 
+Modules can also define math functions. Again, they are described
+using a table:
+
+  static struct mathfunc mftab[] = {
+    NUMMATHFUNC("sum", math_sum, 1, -1, 0),
+    STRMATHFUNC("length", math_length, 0),
+  };
+
+The `NUMMATHFUNC()' macro defines a math function that gets an array
+of mnumbers (the zsh type for representing values in arithmetic
+expressions) taken from the string in parentheses at the function
+call. Its arguments are the name of the function, the C-function
+implementing it, the minimum and maximum number of arguments (as
+usual, the later may be `-1' to specify that the function accepts any
+number of arguments), and finally an integer that is given unchanged
+to the C-function (to be able to implement multiple math functions in
+one C-function).
+
+The `STRMATHFUNC()' macro defines a math function that gets the string 
+in parentheses at the call as one string argument (without the
+parentheses). The arguments are the name of the function, the
+C-function, and an integer used like the last argument of
+`NUMMATHFUNC()'.
+
+The C-functions implementing the math functions look like this:
+
+  /**/
+  static mnumber
+  math_sum(char *name, int argc, mnumber *argv, int id)
+  {
+    ...
+  }
+
+  /**/
+  static mnumber
+  math_length(char *name, char *arg, int id)
+  {
+    ...
+  }
+
+Functions defined with `NUMMATHFUNC' get the name of the function, the 
+number of numeric arguments, an array with these arguments, and the
+last argument from the macro-call. Functions defined with
+`STRMATHFUNC' get the name of the function, the string between the
+parentheses at the call, and the last argument from the macro-call.
+
+Both types of functions return an mnumber which is a descriminated
+union looking like:
+
+  typedef struct {
+    union {
+      zlong l;
+      double d;
+    } u;
+    int type;
+  } mnumber;
+
+The `type' field should be set to `MN_INTEGER' or `MN_FLOAT' and
+depending on its value either `u.l' or `u.d' contains the value.
+
+To register and de-register math functions, the functions
+`addmathfuncs()' and `deletemathfuncs()' are used:
+
+  /**/
+  int
+  boot_example(Module m)
+  {
+    int ret;
+
+    ret = addmathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
+    ...
+  }
+
+  /**/
+  int
+  cleanup_example(Module m)
+  {
+    deletemathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
+    ...
+  }
+
+The arguments and return values are as for the functions used to
+register and de-register parameters, conditions, etc.
+
 Modules can also define function hooks. Other modules can then add
 functions to these hooks to make the first module call these functions
 instead of the default.
diff --git a/Src/Modules/example.c b/Src/Modules/example.c
index b0bbee967..fbe392e94 100644
--- a/Src/Modules/example.c
+++ b/Src/Modules/example.c
@@ -101,6 +101,47 @@ cond_i_ex(char **a, int id)
 }
 
 /**/
+static mnumber
+math_sum(char *name, int argc, mnumber *argv, int id)
+{
+    mnumber ret;
+    int f = 0;
+
+    ret.u.l = 0;
+    while (argc--) {
+	if (argv->type == MN_INTEGER) {
+	    if (f)
+		ret.u.d += (double) argv->u.l;
+	    else
+		ret.u.l += argv->u.l;
+	} else {
+	    if (f)
+		ret.u.d += argv->u.d;
+	    else {
+		ret.u.d = ((double) ret.u.l) + ((double) argv->u.d);
+		f = 1;
+	    }
+	}
+	argv++;
+    }
+    ret.type = (f ? MN_FLOAT : MN_INTEGER);
+
+    return ret;
+}
+
+/**/
+static mnumber
+math_length(char *name, char *arg, int id)
+{
+    mnumber ret;
+
+    ret.type = MN_INTEGER;
+    ret.u.l = strlen(arg);
+
+    return ret;
+}
+
+/**/
 static int
 ex_wrapper(List list, FuncWrap w, char *name)
 {
@@ -136,6 +177,11 @@ static struct paramdef patab[] = {
     ARRPARAMDEF("exarr", &arrparam),
 };
 
+static struct mathfunc mftab[] = {
+    NUMMATHFUNC("sum", math_sum, 1, -1, 0),
+    STRMATHFUNC("length", math_length, 0),
+};
+
 static struct funcwrap wrapper[] = {
     WRAPDEF(ex_wrapper),
 };
@@ -162,6 +208,7 @@ boot_example(Module m)
     return !(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
 	     addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
 	     addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
+	     addmathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab)) |
 	     !addwrapper(m, wrapper));
 }
 
@@ -174,6 +221,7 @@ cleanup_example(Module m)
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
     deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
+    deletemathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
     deletewrapper(m, wrapper);
     return 0;
 }
diff --git a/Src/Modules/example.mdd b/Src/Modules/example.mdd
index 5ed55e6f1..e32e8182e 100644
--- a/Src/Modules/example.mdd
+++ b/Src/Modules/example.mdd
@@ -3,5 +3,6 @@ autobins="example"
 autoinfixconds="ex"
 autoprefixconds="len"
 autoparams="exint exstr exarr"
+automathfuncs="sum length"
 
 objects="example.o"
diff --git a/Src/builtin.c b/Src/builtin.c
index ca765ccfc..ae608cc39 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -125,7 +125,7 @@ static struct builtin builtins[] =
     BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
 
 #ifdef DYNAMIC
-    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcdipue", NULL),
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcfdipue", NULL),
 #else
     BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "e", NULL),
 #endif
diff --git a/Src/math.c b/Src/math.c
index e044cbd9f..3fb741854 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -141,7 +141,8 @@ static int unary = 1;
 #define POWER 49
 #define CID 50
 #define POWEREQ 51
-#define TOKCOUNT 52
+#define FUNC 52
+#define TOKCOUNT 53
 
 /* precedences */
 
@@ -157,7 +158,7 @@ static int prec[TOKCOUNT] =
     15,  15, 15, 15,  15,
     15,  15, 15, 16, 200,
      2,   2,  0,  0,   7,
-     0,  15
+     0,  15, 0
 };
 
 #define TOPPREC 16
@@ -175,7 +176,7 @@ static int type[TOKCOUNT] =
 /* 35 */  RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
 /* 40 */  BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL|OP_OP,
 /* 45 */  RL, RL, LR|OP_OPF, LR|OP_OPF, RL|OP_A2,
-/* 50 */  LR|OP_OPF, RL|OP_E2
+/* 50 */  LR|OP_OPF, RL|OP_E2, LR|OP_OPF
 };
 
 #define LVCOUNT 32
@@ -392,6 +393,7 @@ zzlex(void)
 		cct = 1;
 	    }
 	    if (iident(*ptr)) {
+		int func = 0;
 		char *p, q;
 
 		p = ptr;
@@ -400,12 +402,14 @@ zzlex(void)
 		    return EOI;
 		}
 		while (iident(*++ptr));
-		if (*ptr == '[') {
+		if (*ptr == '[' || (!cct && *ptr == '(')) {
+		    char op = *ptr, cp = ((*ptr == '[') ? ']' : ')');
 		    int l;
+		    func = (op == '(');
 		    for (ptr++, l = 1; *ptr && l; ptr++) {
-			if (*ptr == '[')
+			if (*ptr == op)
 			    l++;
-			if (*ptr == ']')
+			if (*ptr == cp)
 			    l--;
 			if (*ptr == '\\' && ptr[1])
 			    ptr++;
@@ -415,7 +419,7 @@ zzlex(void)
 		*ptr = '\0';
 		lvals[yylval = lvc++] = ztrdup(p);
 		*ptr = q;
-		return cct ? CID : ID;
+		return (func ? FUNC : (cct ? CID : ID));
 	    }
 	    else if (cct) {
 		yyval.u.l = poundgetfn(NULL);
@@ -484,6 +488,64 @@ setvar(LV s, mnumber v)
 
 
 /**/
+static mnumber
+callmathfunc(char *o)
+{
+    MathFunc f;
+    char *a, *n;
+    static mnumber dummy;
+
+    n = a = dupstring(o);
+
+    while (*a != '(')
+	a++;
+    *a++ = '\0';
+    a[strlen(a) - 1] = '\0';
+
+    if ((f = getmathfunc(n, 1))) {
+	if (f->flags & MFF_STR)
+	    return f->sfunc(n, a, f->funcid);
+	else {
+	    int argc = 0;
+	    mnumber *argv, *q;
+	    LinkList l = newlinklist();
+	    LinkNode node;
+	    char *p;
+
+	    if (*a) {
+		for (p = a; *a; a++) {
+		    if (*a == '\\' && a[1])
+			a++;
+		    else if (*a == ',') {
+			*a = '\0';
+			addlinknode(l, p);
+			argc++;
+			p = a + 1;
+		    }
+		}
+		addlinknode(l, p);
+		argc++;
+	    }
+	    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++ = matheval((char *) getdata(node));
+		}
+		return f->nfunc(n, argc, argv, f->funcid);
+	    } else
+		zerr("wrong number of argument: %s", o, 0);
+	}
+    } else
+	zerr("unknown function: %s", n, 0);
+
+    dummy.type = MN_INTEGER;
+    dummy.u.l = 0;
+
+    return dummy;
+}
+
+/**/
 static int
 notzero(mnumber a)
 {
@@ -957,6 +1019,9 @@ mathparse(int pc)
 	case CID:
 	    push(getcvar(yylval), yylval);
 	    break;
+	case FUNC:
+	    push(callmathfunc(lvals[yylval]), yylval);
+	    break;
 	case M_INPAR:
 	    mathparse(TOPPREC);
 	    if (mtok != M_OUTPAR) {
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index c174cbac6..e11972794 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -25,6 +25,7 @@ for x_mod in $x_mods; do
 	*)  echo "/* non-linked-in known module \`$x_mod' */"
 	    eval "loc=\$loc_$x_mod"
 	    unset moddeps autobins autoinfixconds autoprefixconds autoparams
+	    unset automathfuncs
 	    . $srcdir/../$loc/${x_mod}.mdd
 	    for bin in $autobins; do
 		echo "    add_autobin(\"$bin\", \"$x_mod\");"
@@ -38,6 +39,9 @@ for x_mod in $x_mods; do
 	    for param in $autoparams; do
 		echo "    add_autoparam(\"$param\", \"$x_mod\");"
 	    done
+	    for mfunc in $automathfuncs; do
+		echo "    add_automath(\"$mfunc\", \"$x_mod\");"
+	    done
 	    for dep in $moddeps; do
 		case $bin_mods in
 		    *" $dep "*)
diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh
index cd92c7f07..9584474a9 100644
--- a/Src/mkmakemod.sh
+++ b/Src/mkmakemod.sh
@@ -25,6 +25,7 @@
 #                   autoloading (without the leading `-')
 #   autoprefixconds like autoinfixconds, but for prefix condition codes
 #   autoparams      parameters defined by the module, for autoloading
+#   automathfuncs   math functions defined by the module, for autoloading
 #   objects         .o files making up this module (*must* be defined)
 #   proto           .pro files for this module (default generated from $objects)
 #   headers         extra headers for this module (default none)
@@ -171,7 +172,7 @@ if $first_stage; then
     for module in $here_modules; do
 
 	unset moddeps nozshdep alwayslink hasexport
-	unset autobins autoinfixconds autoprefixconds autoparams
+	unset autobins autoinfixconds autoprefixconds autoparams automathfuncs
 	unset objects proto headers hdrdeps otherincs
 	. $top_srcdir/$the_subdir/${module}.mdd
 	test -n "${moddeps+set}" || moddeps=
diff --git a/Src/module.c b/Src/module.c
index dab2c0350..69a43cc3c 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -696,8 +696,9 @@ autoloadscan(HashNode hn, int printflags)
 int
 bin_zmodload(char *nam, char **args, char *ops, int func)
 {
-    if ((ops['b'] || ops['c'] || ops['p']) && !(ops['a'] || ops['u'])) {
-	zwarnnam(nam, "-b, -c, and -p must be combined with -a or -u",
+    if ((ops['b'] || ops['c'] || ops['p'] || ops['f']) &&
+	!(ops['a'] || ops['u'])) {
+	zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u",
 		 NULL, 0);
 	return 1;
     }
@@ -718,10 +719,12 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 	return bin_zmodload_exist(nam, args, ops);
     else if (ops['d'])
 	return bin_zmodload_dep(nam, args, ops);
-    else if ((ops['a'] || ops['b']) && !(ops['c'] || ops['p']))
+    else if ((ops['a'] || ops['b']) && !(ops['c'] || ops['p'] || ops['f']))
 	return bin_zmodload_auto(nam, args, ops);
     else if (ops['c'] && !(ops['b'] || ops['p']))
 	return bin_zmodload_cond(nam, args, ops);
+    else if (ops['f'] && !(ops['b'] || ops['p']))
+	return bin_zmodload_math(nam, args, ops);
     else if (ops['p'] && !(ops['b'] || ops['c']))
 	return bin_zmodload_param(nam, args, ops);
     else if (!(ops['a'] || ops['b'] || ops['c'] || ops['p']))
@@ -935,9 +938,10 @@ bin_zmodload_cond(char *nam, char **args, char *ops)
 			putchar('I');
 		    printf(" %s %s\n", p->module, p->name);
 		} else {
-		    fputs("post ", stdout);
 		    if (p->flags & CONDF_INFIX)
 			fputs("infix ", stdout);
+		    else
+			fputs("post ", stdout);
 		    printf("%s (%s)\n",p->name, p->module);
 		}
 	    }
@@ -958,7 +962,68 @@ bin_zmodload_cond(char *nam, char **args, char *ops)
 		zwarnnam(nam, "%s: `/' is illegal in a condition", cnam, 0);
 		ret = 1;
 	    } else if (add_autocond(cnam, ops['I'], modnam) && !ops['i']) {
-		zwarnnam(nam, "failed to add condition %s", cnam, 0);
+		zwarnnam(nam, "failed to add condition `%s'", cnam, 0);
+		ret = 1;
+	    }
+	} while(*args);
+	return ret;
+    }
+}
+
+/**/
+static int
+bin_zmodload_math(char *nam, char **args, char *ops)
+{
+    int ret = 0;
+
+    if (ops['u']) {
+	/* remove autoloaded conditions */
+	for (; *args; args++) {
+	    MathFunc f = getmathfunc(*args, 0);
+
+	    if (!f) {
+		if (!ops['i']) {
+		    zwarnnam(nam, "%s: no such math function", *args, 0);
+		    ret = 1;
+		}
+	    } else if (f->flags & CONDF_ADDED) {
+		zwarnnam(nam, "%s: math function is already defined", *args, 0);
+		ret = 1;
+	    } else
+		deletemathfunc(f);
+	}
+	return ret;
+    } else if (!*args) {
+	/* list autoloaded math functions */
+	MathFunc p;
+
+	for (p = mathfuncs; p; p = p->next) {
+	    if (p->module) {
+		if (ops['L']) {
+		    fputs("zmodload -af", stdout);
+		    printf(" %s %s\n", p->module, p->name);
+		} else
+		    printf("%s (%s)\n",p->name, p->module);
+	    }
+	}
+	return 0;
+    } else {
+	/* add autoloaded conditions */
+	char *modnam;
+
+	modnam = *args++;
+	if(isset(RESTRICTED) && strchr(modnam, '/')) {
+	    zwarnnam(nam, "%s: restricted", modnam, 0);
+	    return 1;
+	}
+	do {
+	    char *fnam = *args ? *args++ : modnam;
+	    if (strchr(fnam, '/')) {
+		zwarnnam(nam, "%s: `/' is illegal in a math function",
+			 fnam, 0);
+		ret = 1;
+	    } else if (add_automathfunc(fnam, modnam) && !ops['i']) {
+		zwarnnam(nam, "failed to add math function `%s'", fnam, 0);
 		ret = 1;
 	    }
 	} while(*args);
@@ -1272,8 +1337,10 @@ addconddefs(char const *nam, Conddef c, int size)
     int hads = 0, hadf = 0;
 
     while (size--) {
-	if (c->flags & CONDF_ADDED)
+	if (c->flags & CONDF_ADDED) {
+	    c++;
 	    continue;
+	}
 	if (addconddef(c)) {
 	    zwarnnam(nam, "name clash when adding condition `%s'", c->name, 0);
 	    hadf = 1;
@@ -1534,7 +1601,7 @@ deleteparamdefs(char const *nam, Paramdef d, int size)
 int
 add_autocond(char *nam, int inf, char *module)
 {
-    Conddef c = zalloc(sizeof(*c));
+    Conddef c = (Conddef) zalloc(sizeof(*c));
 
     c->name = ztrdup(nam);
     c->flags = (inf  ? CONDF_INFIX : 0);
@@ -1587,8 +1654,10 @@ deleteconddefs(char const *nam, Conddef c, int size)
     int hads = 0, hadf = 0;
 
     while (size--) {
-	if (!(c->flags & CONDF_ADDED))
+	if (!(c->flags & CONDF_ADDED)) {
+	    c++;
 	    continue;
+	}
 	if (deleteconddef(c)) {
 	    zwarnnam(nam, "condition `%s' already deleted", c->name, 0);
 	    hadf = 1;
@@ -1618,3 +1687,152 @@ add_autoparam(char *nam, char *module)
 
 /**/
 #endif
+
+/* List of math functions. */
+
+/**/
+MathFunc mathfuncs;
+
+/**/
+MathFunc
+getmathfunc(char *name, int autol)
+{
+    MathFunc p, q = NULL;
+
+    for (p = mathfuncs; p; q = p, p = p->next)
+	if (!strcmp(name, p->name)) {
+#ifdef DYNAMIC
+	    if (autol && p->module) {
+		char *n = dupstring(p->module);
+
+		if (q)
+		    q->next = p->next;
+		else
+		    mathfuncs = p->next;
+
+		zsfree(p->module);
+		zfree(p, sizeof(*p));
+
+		load_module(n);
+
+		return getmathfunc(name, 0);
+	    }
+#endif
+	    return p;
+	}
+
+    return NULL;
+}
+
+/**/
+int
+addmathfunc(MathFunc f)
+{
+    MathFunc p;
+
+    if (f->flags & MFF_ADDED)
+	return 1;
+
+    for (p = mathfuncs; p; p = p->next)
+	if (!strcmp(f->name, p->name))
+	    return 1;
+
+    f->flags |= MFF_ADDED;
+    f->next = mathfuncs;
+    mathfuncs = f;
+
+    return 0;
+}
+
+/**/
+int
+addmathfuncs(char const *nam, MathFunc f, int size)
+{
+    int hads = 0, hadf = 0;
+
+    while (size--) {
+	if (f->flags & MFF_ADDED) {
+	    f++;
+	    continue;
+	}
+	if (addmathfunc(f)) {
+	    zwarnnam(nam, "name clash when adding math function `%s'",
+		     f->name, 0);
+	    hadf = 1;
+	} else
+	    hads = 2;
+	f++;
+    }
+    return hadf ? hads : 1;
+}
+
+#ifdef DYNAMIC
+
+/**/
+int
+add_automathfunc(char *nam, char *module)
+{
+    MathFunc f = (MathFunc) zalloc(sizeof(*f));
+
+    f->name = ztrdup(nam);
+    f->module = ztrdup(module);
+
+    if (addmathfunc(f)) {
+	zsfree(f->name);
+	zsfree(f->module);
+	zfree(f, sizeof(*f));
+
+	return 1;
+    }
+    return 0;
+}
+
+/**/
+int
+deletemathfunc(MathFunc f)
+{
+    MathFunc p, q;
+
+    for (p = mathfuncs, q = NULL; p && p != f; q = p, p = p->next);
+
+    if (p) {
+	if (q)
+	    q->next = f->next;
+	else
+	    mathfuncs = f->next;
+
+	if (f->module) {
+	    zsfree(f->name);
+	    zsfree(f->module);
+	    zfree(f, sizeof(*f));
+	} else
+	    f->flags &= ~MFF_ADDED;
+
+	return 0;
+    }
+    return -1;
+}
+
+/**/
+int
+deletemathfuncs(char const *nam, MathFunc f, int size)
+{
+    int hads = 0, hadf = 0;
+
+    while (size--) {
+	if (!(f->flags & MFF_ADDED)) {
+	    f++;
+	    continue;
+	}
+	if (deletemathfunc(f)) {
+	    zwarnnam(nam, "math function `%s' already deleted",
+		     f->name, 0);
+	    hadf = 1;
+	} else
+	    hads = 2;
+	f++;
+    }
+    return hadf ? hads : 1;
+}
+
+#endif /* DYNAMIC */
diff --git a/Src/zsh.h b/Src/zsh.h
index 51df16313..651515fc5 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -79,6 +79,30 @@ typedef struct {
 
 typedef int LV;
 
+typedef struct mathfunc *MathFunc;
+typedef mnumber (*NumMathFunc)(char *, int, mnumber *, int);
+typedef mnumber (*StrMathFunc)(char *, char *, int);
+
+struct mathfunc {
+    MathFunc next;
+    char *name;
+    int flags;
+    NumMathFunc nfunc;
+    StrMathFunc sfunc;
+    char *module;
+    int minargs;
+    int maxargs;
+    int funcid;
+};
+
+#define MFF_STR      1
+#define MFF_ADDED    2
+
+#define NUMMATHFUNC(name, func, min, max, id) \
+    { NULL, name, 0, func, NULL, NULL, min, max, id }
+#define STRMATHFUNC(name, func, id) \
+    { NULL, name, MFF_STR, NULL, func, NULL, 0, 0, id }
+
 /* Character tokens are sometimes casted to (unsigned char)'s.         * 
  * Unfortunately, some compilers don't correctly cast signed to        * 
  * unsigned promotions; i.e. (int)(unsigned char)((char) -1) evaluates *