~emersion/mrsh

568940c42ad1d30cb67d55f7a6799e43eb15bc6f — Drew DeVault 2 years ago fd9074d
Implement arg stack for functions
M builtin/getopts.c => builtin/getopts.c +2 -2
@@ 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];

M builtin/set.c => builtin/set.c +6 -6
@@ 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;

M builtin/shift.c => builtin/shift.c +6 -6
@@ 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;
}

M include/mrsh/shell.h => include/mrsh/shell.h +9 -2
@@ 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);

M main.c => main.c +1 -1
@@ 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 {

M shell/shell.c => shell/shell.c +30 -3
@@ 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);


M shell/task/command.c => shell/task/command.c +8 -2
@@ 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) {

M shell/task/word.c => shell/task/word.c +6 -5
@@ 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");
			}

M test/function.sh => test/function.sh +8 -0
@@ 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)