~emersion/mrsh

ref: aa7d536d91b2e4bc6685ee979d40fcbf6d4dcbd2 mrsh/shell/task/case_clause.c -rw-r--r-- 2.9 KiB View raw
                                                                                
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
#include "shell/task.h"
#include <fnmatch.h>
#include <stdlib.h>

struct task_case_item {
	struct task *body;
	struct mrsh_array *patterns; // struct mrsh_word *
	size_t index;
	struct task *word;
};

struct task_case_clause {
	struct task task;

	struct {
		struct mrsh_word *word;
	} ast;
	struct {
		struct task *word;
	} tasks;

	char *word;
	struct task_case_item *selected;
	struct mrsh_array cases; // struct task_case_item *
	size_t index;
};

static void task_case_clause_destroy(struct task *task) {
	struct task_case_clause *tcc = (struct task_case_clause *)task;
	for (size_t i = 0; i < tcc->cases.len; ++i) {
		struct task_case_item *tci = tcc->cases.data[i];
		task_destroy(tci->body);
		free(tci);
	}
	mrsh_array_finish(&tcc->cases);
	task_destroy(tcc->tasks.word);
	mrsh_word_destroy(tcc->ast.word);
	free(tcc->word);
	free(tcc);
}

static int task_case_clause_poll(struct task *task, struct context *ctx) {
	struct task_case_clause *tcc = (struct task_case_clause *)task;
	if (tcc->tasks.word) {
		int word_status = task_poll(tcc->tasks.word, ctx);
		if (word_status < 0) {
			return word_status;
		}
		tcc->word = mrsh_word_str(tcc->ast.word);
		task_destroy(tcc->tasks.word);
		tcc->tasks.word = NULL;
	}

	while (!tcc->selected && tcc->index < tcc->cases.len) {
		struct task_case_item *tci =
			(struct task_case_item *)tcc->cases.data[tcc->index];
		if (tci->word) {
			int word_status = task_poll(tci->word, ctx);
			if (word_status < 0) {
				return word_status;
			}
			struct mrsh_word_string *word = (struct mrsh_word_string *)
				tci->patterns->data[tci->index - 1];
			task_destroy(tci->word);
			tci->word = NULL;
			if (fnmatch(word->str, tcc->word, 0) == 0) {
				tcc->selected = tci;
				break;
			}
			if (tci->index == tci->patterns->len) {
				++tcc->index;
			}
		} else {
			struct mrsh_word **word_ptr =
				(struct mrsh_word **)&tci->patterns->data[tci->index++];
			tci->word = task_word_create(word_ptr, TILDE_EXPANSION_NAME);
		}
	}

	if (tcc->selected) {
		return task_poll(tcc->selected->body, ctx);
	}

	return 0;
}

static const struct task_interface task_case_clause_impl = {
	.destroy = task_case_clause_destroy,
	.poll = task_case_clause_poll,
};

struct task *task_case_clause_create(
		const struct mrsh_word *word, const struct mrsh_array *cases) {
	struct task_case_clause *task = calloc(1, sizeof(struct task_case_clause));
	task_init(&task->task, &task_case_clause_impl);
	if (!mrsh_array_reserve(&task->cases, cases->len)) {
		free(task);
		return NULL;
	}
	for (size_t i = 0; i < cases->len; ++i) {
		struct mrsh_case_item *mci = cases->data[i];
		struct task_case_item *tci = calloc(1, sizeof(struct task_case_item));
		mrsh_array_add(&task->cases, tci);
		tci->patterns = &mci->patterns;
		tci->body = task_for_command_list_array(&mci->body);
	}
	task->ast.word = mrsh_word_copy(word);
	task->tasks.word = task_word_create(&task->ast.word, TILDE_EXPANSION_NAME);
	return (struct task *)task;
}