Commit Diff


commit - 6019438afeeb073278dec3b2a194b3529d9efe33
commit + 6eb34747737c7874d9de86686c201138de0391c6
blob - 66bbecb8e5aeeb790093e8dc7cb7cb3099cefd2c
blob + b63dac1d8755d3325c9ac524e93162f91b617940
--- script.c
+++ script.c
@@ -214,6 +214,20 @@ setvar(char *sym, struct op *op)
 	return EVAL_OK;
 }
 
+static inline void
+setvar_raw(char *sym, struct op *op)
+{
+	struct binding	*b;
+	struct env	*e;
+
+	b = xcalloc(1, sizeof(*b));
+	b->name = sym;
+	b->raw = op;
+
+	e = TAILQ_FIRST(&envs);
+	TAILQ_INSERT_HEAD(&e->bindings, b, entry);
+}
+
 static inline int
 getvar(const char *sym, struct value *v)
 {
@@ -233,6 +247,25 @@ getvar(const char *sym, struct value *v)
 	return EVAL_ERR;
 }
 
+static inline int
+getvar_raw(const char *sym, struct op **raw)
+{
+	struct env	*e;
+	struct binding	*b;
+
+	TAILQ_FOREACH(e, &envs, entry) {
+		TAILQ_FOREACH(b, &e->bindings, entry) {
+			if (!strcmp(sym, b->name)) {
+				*raw = b->raw;
+				return EVAL_OK;
+			}
+		}
+	}
+
+	fprintf(stderr, "no rest argument `...'\n");
+	return EVAL_ERR;
+}
+
 int
 global_set(char *sym, struct op *op)
 {
@@ -586,6 +619,13 @@ eval(struct op *op)
 #endif
 
 	switch (op->type) {
+	case OP_REST:
+		if ((ret = getvar_raw("...", &t)) != EVAL_OK)
+			return ret;
+		if ((ret = eval(t)) != EVAL_OK)
+			return ret;
+		break;
+
 	case OP_ASSIGN:
 		ret = setvar(op->v.assign.name, op->v.assign.expr);
 		if (ret != EVAL_OK)
@@ -609,11 +649,25 @@ eval(struct op *op)
 
 		proc = op->v.funcall.proc;
 		if (proc->nativefn != NULL) {
-			/* push arguments on the stack */
+			/*
+			 * Push arguments on the stack for builtin
+			 * functions.  Counting the height of the
+			 * stack is done to compute the correct number
+			 * in the vararg case.  argc only counts the
+			 * "syntactical" arguments, i.e. foo(x, ...)
+			 * has argc == 2, but at runtime argc may be
+			 * 1, 2 or a greater number!
+			 */
+
+			i = stackh;
 			t = op->v.funcall.argv;
 			if (t != NULL && (ret = eval(t)) != EVAL_OK)
 				return ret;
-			if ((ret = proc->nativefn(op->v.funcall.argc))
+			i = stackh - i;
+
+			assert(i >= 0);
+
+			if ((ret = proc->nativefn(i))
 			    != EVAL_OK)
 				return ret;
 		} else {
@@ -622,6 +676,18 @@ eval(struct op *op)
 			for (t = op->v.funcall.argv, i = 0;
 			     t != NULL;
 			     t = t->next, i++) {
+				/*
+				 * Push a pseudo variable `...' (and
+				 * don't evaluate it) in the vararg
+				 * case.  A special case is when the
+				 * variable is itself `...'.
+				 */
+				if (proc->vararg && i == proc->minargs) {
+					if (t->type != OP_REST)
+						setvar_raw(xstrdup("..."), t);
+					break;
+				}
+
 				if ((ret = setvar(proc->args[i], t))
 				    != EVAL_OK)
 					return ret;