1 2

[PATCH] Implement arg stack for functions

Details
Message ID
<20181104132843.23022-1-sir@cmpwn.com>
Sender timestamp
1541338123
DKIM signature
permerror
Download raw message
Patch: +76 -27
---
 builtin/getopts.c    |  4 ++--
 builtin/set.c        | 12 ++++++------
 builtin/shift.c      | 12 ++++++------
 include/mrsh/shell.h | 11 +++++++++--
 main.c               |  2 +-
 shell/shell.c        | 33 ++++++++++++++++++++++++++++++---
 shell/task/command.c | 10 ++++++++--
 shell/task/word.c    | 11 ++++++-----
 test/function.sh     |  8 ++++++++
 9 files changed, 76 insertions(+), 27 deletions(-)

diff --git a/builtin/getopts.c b/builtin/getopts.c
index 7ce0be9..6699eb8 100644
--- a/builtin/getopts.c
@@ -29,8 +29,8 @@ int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]) {
 		optc = argc - optind - 2;
 		optv = &argv[optind + 2];
 	} else {
-		optc = state->argc;
-		optv = state->argv;
+		optc = state->args->argc;
+		optv = state->args->argv;
 	}
 	char *optstring = argv[optind];
 	char *name = argv[optind + 1];
diff --git a/builtin/set.c b/builtin/set.c
index 86e858b..9465704 100644
--- a/builtin/set.c
@@ -178,15 +178,15 @@ static int set(struct mrsh_state *state, int argc, char *argv[], bool cmdline) {
 				return EXIT_FAILURE;
 			}
 		} else {
-			argv_0 = strdup(state->argv[0]);
+			argv_0 = strdup(state->args->argv[0]);
 		}
-		argv_free(state->argc, state->argv);
-		state->argc = argc - i + 1;
-		state->argv = argv_dup(argv_0, state->argc, &argv[i]);
+		argv_free(state->args->argc, state->args->argv);
+		state->args->argc = argc - i + 1;
+		state->args->argv = argv_dup(argv_0, state->args->argc, &argv[i]);
 	} else if (cmdline) {
 		// No args given, but we need to initialize state->argv
-		state->argc = 1;
-		state->argv = argv_dup(strdup(argv[0]), 1, argv);
+		state->args->argc = 1;
+		state->args->argv = argv_dup(strdup(argv[0]), 1, argv);
 	}
 
 	return EXIT_SUCCESS;
diff --git a/builtin/shift.c b/builtin/shift.c
index 1809238..6b5b28e 100644
--- a/builtin/shift.c
@@ -33,20 +33,20 @@ int builtin_shift(struct mrsh_state *state, int argc, char *argv[]) {
 			state->exit = EXIT_FAILURE;
 		}
 		return EXIT_FAILURE;
-	} else if (n > state->argc - 1) {
+	} else if (n > state->args->argc - 1) {
 		fprintf(stderr, "shift: [n] must be less than $#\n");
 		if (!state->interactive) {
 			state->exit = EXIT_FAILURE;
 		}
 		return EXIT_FAILURE;
 	}
-	for (int i = 1, j = n + 1; j < state->argc; ++i, ++j) {
-		if (j <= state->argc - n) {
-			state->argv[i] = state->argv[j];
+	for (int i = 1, j = n + 1; j < state->args->argc; ++i, ++j) {
+		if (j <= state->args->argc - n) {
+			state->args->argv[i] = state->args->argv[j];
 		} else {
-			free(state->argv[i]);
+			free(state->args->argv[i]);
 		}
 	}
-	state->argc -= n;
+	state->args->argc -= n;
 	return EXIT_SUCCESS;
 }
diff --git a/include/mrsh/shell.h b/include/mrsh/shell.h
index 5b73562..8b3980d 100644
--- a/include/mrsh/shell.h
+++ b/include/mrsh/shell.h
@@ -73,12 +73,17 @@ struct mrsh_function {
 	struct mrsh_command *body;
 };
 
+struct mrsh_args {
+	char **argv;
+	int argc;
+	struct mrsh_args *prev;
+};
+
 struct mrsh_state {
 	int exit;
 	uint32_t options; // enum mrsh_option
 	FILE *input;
-	char **argv;
-	int argc;
+	struct mrsh_args *args;
 	bool interactive;
 	struct mrsh_hashtable variables; // mrsh_variable *
 	struct mrsh_hashtable aliases; // char *
@@ -93,6 +98,8 @@ void mrsh_env_set(struct mrsh_state *state,
 void mrsh_env_unset(struct mrsh_state *state, const char *key);
 const char *mrsh_env_get(struct mrsh_state *state,
 		const char *key, uint32_t *attribs);
+void mrsh_push_args(struct mrsh_state *state, int argc, const char *argv[]);
+void mrsh_pop_args(struct mrsh_state *state);
 int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog);
 int mrsh_run_word(struct mrsh_state *state, struct mrsh_word **word);
 bool mrsh_run_arithm_expr(struct mrsh_arithm_expr *expr, long *result);
diff --git a/main.c b/main.c
index 08dce5f..e3b3027 100644
--- a/main.c
+++ b/main.c
@@ -149,7 +149,7 @@ int main(int argc, char *argv[]) {
 				// Nothing to see here
 			} else if (err_msg != NULL) {
 				fprintf(stderr, "%s:%d:%d: syntax error: %s\n",
-					state.argv[0], err_pos.line, err_pos.column, err_msg);
+					state.args->argv[0], err_pos.line, err_pos.column, err_msg);
 				if (state.interactive) {
 					continue;
 				} else {
diff --git a/shell/shell.c b/shell/shell.c
index 7872745..d814c91 100644
--- a/shell/shell.c
+++ b/shell/shell.c
@@ -13,6 +13,7 @@ void mrsh_state_init(struct mrsh_state *state) {
 	state->interactive = isatty(STDIN_FILENO);
 	state->options = state->interactive ? MRSH_OPT_INTERACTIVE : 0;
 	state->input = stdin;
+	state->args = calloc(1, sizeof(struct mrsh_args));
 }
 
 static void state_string_finish_iterator(const char *key, void *value,
@@ -52,10 +53,16 @@ void mrsh_state_finish(struct mrsh_state *state) {
 	mrsh_hashtable_for_each(&state->aliases,
 		state_string_finish_iterator, NULL);
 	mrsh_hashtable_finish(&state->aliases);
-	for (int i = 0; i < state->argc; ++i) {
-		free(state->argv[i]);
+	struct mrsh_args *args = state->args;
+	while (args) {
+		for (int i = 0; i < args->argc; ++i) {
+			free(args->argv[i]);
+		}
+		free(args->argv);
+		struct mrsh_args *prev = args->prev;
+		free(args);
+		args = prev;
 	}
-	free(state->argv);
 }
 
 void mrsh_env_set(struct mrsh_state *state,
@@ -84,6 +91,26 @@ const char *mrsh_env_get(struct mrsh_state *state,
 	return var ? var->value : NULL;
 }
 
+void mrsh_push_args(struct mrsh_state *state, int argc, const char *argv[]) {
+	struct mrsh_args *next = calloc(1, sizeof(struct mrsh_args));
+	next->argc = argc;
+	next->argv = malloc(sizeof(char *) * argc);
+	for (int i = 0; i < argc; ++i) {
+		next->argv[i] = strdup(argv[i]);
+	}
+	next->prev = state->args;
+	state->args = next;
+}
+
+void mrsh_pop_args(struct mrsh_state *state) {
+	struct mrsh_args *args = state->args;
+	state->args = args->prev;
+	for (int i = 0; i < args->argc; ++i) {
+		free(args->argv[i]);
+	}
+	free(args);
+}
+
 int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog) {
 	struct task *task = task_for_command_list_array(&prog->body);
 
diff --git a/shell/task/command.c b/shell/task/command.c
index 1275c0f..7e6398a 100644
--- a/shell/task/command.c
+++ b/shell/task/command.c
@@ -115,7 +115,9 @@ static void get_args(struct mrsh_array *args, struct mrsh_simple_command *sc,
 }
 
 static bool task_function_start(struct task_command *tc, struct context *ctx) {
-	// TODO: Push new $@/$#
+	int argc = tc->args.len - 1;
+	const char **argv = (const char **)tc->args.data;
+	mrsh_push_args(ctx->state, argc, argv);
 	tc->fn_task = task_for_command(tc->fn_def->body);
 	return tc->fn_task != NULL;
 }
@@ -130,7 +132,11 @@ static int task_function_poll(struct task *task, struct context *ctx) {
 		tc->started = true;
 	}
 
-	return task_poll(tc->fn_task, ctx);
+	int ret = task_poll(tc->fn_task, ctx);
+	if (ret >= 0) {
+		mrsh_pop_args(ctx->state);
+	}
+	return ret;
 }
 
 static int task_builtin_poll(struct task *task, struct context *ctx) {
diff --git a/shell/task/word.c b/shell/task/word.c
index 5bc5198..c1a6150 100644
--- a/shell/task/word.c
+++ b/shell/task/word.c
@@ -97,7 +97,7 @@ static const char *parameter_get_value(struct mrsh_state *state, char *name) {
 	} else if (strcmp(name, "*") == 0) {
 		// TODO
 	} else if (strcmp(name, "#") == 0) {
-		sprintf(value, "%d", state->argc - 1);
+		sprintf(value, "%d", state->args->argc - 1);
 		return value;
 	} else if (strcmp(name, "?") == 0) {
 		sprintf(value, "%d", state->last_status);
@@ -110,10 +110,10 @@ static const char *parameter_get_value(struct mrsh_state *state, char *name) {
 	} else if (strcmp(name, "!") == 0) {
 		// TODO
 	} else if (end[0] == '\0') {
-		if (lvalue >= state->argc) {
+		if (lvalue >= state->args->argc) {
 			return NULL;
 		}
-		return state->argv[lvalue];
+		return state->args->argv[lvalue];
 	}
 	// User-set cases
 	return mrsh_env_get(state, name, NULL);
@@ -147,7 +147,7 @@ static int task_word_poll(struct task *task, struct context *ctx) {
 		if (value == NULL) {
 			if ((ctx->state->options & MRSH_OPT_NOUNSET)) {
 				fprintf(stderr, "%s: %s: unbound variable\n",
-						ctx->state->argv[0], wp->name);
+						ctx->state->args->argv[0], wp->name);
 				return TASK_STATUS_ERROR;
 			}
 			value = "";
@@ -211,7 +211,8 @@ static int task_word_poll(struct task *task, struct context *ctx) {
 			if (err_msg != NULL) {
 				// TODO: improve error line/column
 				fprintf(stderr, "%s (arithmetic %d:%d): %s\n",
-					ctx->state->argv[0], err_pos.line, err_pos.column, err_msg);
+					ctx->state->args->argv[0], err_pos.line,
+					err_pos.column, err_msg);
 			} else {
 				fprintf(stderr, "expected an arithmetic expression\n");
 			}
diff --git a/test/function.sh b/test/function.sh
index 631074f..a316ee9 100644
--- a/test/function.sh
+++ b/test/function.sh
@@ -19,10 +19,18 @@ func_c() {
 	}
 }
 
+func_d() if true; then echo func_d; fi
+
+func_e() {
+	echo $1
+}
+
 func_a
 func_b
 func_c
 func_c
+func_d
+func_e hello
 
 output=$(func_a)
 
-- 
2.19.1
Details
Message ID
<f4qr8PI6xa40hSiZF5DDjQeCllY1vnVJ0A7g_xdUStAdUSx2rmRbnw9lFseqdMXmfmk_bvYq-LFjkasjo7_fSJ4J5UVZZ2DQNjH1b4CX_f8=@emersion.fr>
In-Reply-To
<20181104132843.23022-1-sir@cmpwn.com> (view parent)
Sender timestamp
1541532630
DKIM signature
pass
Download raw message
Pushed:

To git.sr.ht:~emersion/mrsh
   fd9074d..568940c  master -> master

Thanks!