~emersion/mrsh

ref: fb1a6e2ce995f61ca8e41e8ac3f591bc50922592 mrsh/shell/shell.c -rw-r--r-- 4.9 KiB
fb1a6e2cemersion readme: add link to overall project status 3 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include "shell.h"

void mrsh_state_init(struct mrsh_state *state) {
	state->exit = -1;
	state->interactive = isatty(STDIN_FILENO);
	state->options = state->interactive ? MRSH_OPT_INTERACTIVE : 0;
	state->input = stdin;
}

static void state_string_finish_iterator(const char *key, void *value,
		void *user_data) {
	free(value);
}

void mrsh_state_finish(struct mrsh_state *state) {
	mrsh_hashtable_for_each(&state->variables,
		state_string_finish_iterator, NULL);
	mrsh_hashtable_finish(&state->variables);
	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]);
	}
	free(state->argv);
}

static struct task *handle_simple_command(struct mrsh_simple_command *sc) {
	struct task *task_list = task_list_create();

	for (size_t i = 0; i < sc->assignments.len; ++i) {
		struct mrsh_assignment *assign = sc->assignments.data[i];
		task_list_add(task_list,
			task_word_create(&assign->value, TILDE_EXPANSION_ASSIGNMENT));
	}

	if (sc->name == NULL) {
		task_list_add(task_list, task_assignment_create(&sc->assignments));
		return task_list;
	}

	task_list_add(task_list,
		task_word_create(&sc->name, TILDE_EXPANSION_NAME));

	for (size_t i = 0; i < sc->arguments.len; ++i) {
		struct mrsh_word **arg_ptr =
			(struct mrsh_word **)&sc->arguments.data[i];
		task_list_add(task_list,
			task_word_create(arg_ptr, TILDE_EXPANSION_NAME));
	}

	for (size_t i = 0; i < sc->io_redirects.len; ++i) {
		struct mrsh_io_redirect *redir = sc->io_redirects.data[i];
		task_list_add(task_list,
			task_word_create(&redir->name, TILDE_EXPANSION_NAME));
		for (size_t j = 0; j < redir->here_document.len; ++j) {
			struct mrsh_word **line_word_ptr =
				(struct mrsh_word **)&redir->here_document.data[j];
			task_list_add(task_list,
				task_word_create(line_word_ptr, TILDE_EXPANSION_NAME));
		}
	}

	task_list_add(task_list, task_command_create(sc));

	return task_list;
}

static struct task *handle_node(struct mrsh_node *node);

static struct task *handle_command_list_array(struct mrsh_array *array) {
	struct task *task_list = task_list_create();

	for (size_t i = 0; i < array->len; ++i) {
		struct mrsh_command_list *list = array->data[i];
		struct task *child = handle_node(list->node);
		if (list->ampersand) {
			child = task_async_create(child);
		}
		task_list_add(task_list, child);
	}

	return task_list;
}

static struct task *handle_command(struct mrsh_command *cmd);

static struct task *handle_if_clause(struct mrsh_if_clause *ic) {
	struct task *condition = handle_command_list_array(&ic->condition);
	struct task *body = handle_command_list_array(&ic->body);
	struct task *else_part = NULL;
	if (ic->else_part != NULL) {
		else_part = handle_command(ic->else_part);
	}
	return task_if_clause_create(condition, body, else_part);
}

static struct task *handle_command(struct mrsh_command *cmd) {
	switch (cmd->type) {
	case MRSH_SIMPLE_COMMAND:;
		struct mrsh_simple_command *sc = mrsh_command_get_simple_command(cmd);
		assert(sc != NULL);
		return handle_simple_command(sc);
	case MRSH_BRACE_GROUP:;
		struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
		assert(bg != NULL);
		return handle_command_list_array(&bg->body);
	case MRSH_IF_CLAUSE:;
		struct mrsh_if_clause *ic = mrsh_command_get_if_clause(cmd);
		assert(ic != NULL);
		return handle_if_clause(ic);
	case MRSH_FUNCTION_DEFINITION:
		assert(false); // TODO: implement this
	}
	assert(false);
}

static struct task *handle_pipeline(struct mrsh_pipeline *pl) {
	struct task *task_pipeline = task_pipeline_create();

	for (size_t i = 0; i < pl->commands.len; ++i) {
		struct mrsh_command *cmd = pl->commands.data[i];
		task_pipeline_add(task_pipeline, handle_command(cmd));
	}

	return task_pipeline;
}

static struct task *handle_binop(struct mrsh_binop *binop) {
	struct task *left = handle_node(binop->left);
	struct task *right = handle_node(binop->right);
	return task_binop_create(binop->type, left, right);
}

static struct task *handle_node(struct mrsh_node *node) {
	switch (node->type) {
	case MRSH_NODE_PIPELINE:;
		struct mrsh_pipeline *pl = mrsh_node_get_pipeline(node);
		assert(pl != NULL);
		return handle_pipeline(pl);
	case MRSH_NODE_BINOP:;
		struct mrsh_binop *binop = mrsh_node_get_binop(node);
		assert(binop != NULL);
		return handle_binop(binop);
	}
	assert(false);
}

int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog) {
	struct task *task = handle_command_list_array(&prog->body);

	struct context ctx = {
		.state = state,
		.stdin_fileno = -1,
		.stdout_fileno = -1,
	};
	int ret = task_run(task, &ctx);
	task_destroy(task);
	return ret;
}

int mrsh_run_word(struct mrsh_state *state, struct mrsh_word **word) {
	struct task *task = task_word_create(word, TILDE_EXPANSION_NAME);

	struct context ctx = {
		.state = state,
		.stdin_fileno = -1,
		.stdout_fileno = -1,
	};
	int ret = task_run(task, &ctx);
	task_destroy(task);
	return ret;
}