about summary refs log tree commit diff
path: root/Src/builtin.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/builtin.c')
-rw-r--r--Src/builtin.c176
1 files changed, 174 insertions, 2 deletions
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.                         */