summaryrefslogtreecommitdiff
path: root/src/user/app/shell/parser.c
blob: 072c7201494ce540c763dbc89e1501734243c03c (plain)
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
#include "shell.h"
#include <stdbool.h>
#include <string.h>

static char skipspace(char **sp) {
	char *s = *sp;
	while (*s && isspace(*s)) s++;
	*sp = s;
	return *s;
}

static bool isspecial(char c) {
	return c == '>' || c == '#';
}

static char *parg(char **sp) {
	char *s = *sp;
	char *res = NULL;

	if (skipspace(&s)) {
		// TODO incorrectly handles strings like a"b"
		switch (*s) {
			case '"':
				s++;
				res = s;
				while (*s && *s != '"')
					s++;
				break;
			default:
				res = s;
				while (*s && !isspace(*s) && !isspecial(*s))
					s++;
				if (*s == '#') {
					*s = '\0'; /* end parsing early */
					if (res == s) /* don't output an empty arg */
						res = NULL;
				}
				break;
		}
		if (*s) *s++ = '\0';
	}

	*sp = s;
	return res;
}

int parse(char *s, char **argv, size_t argvlen, struct redir *redir) {
	if (argvlen == 0) return -1;
	size_t argc = 0;
	char *arg;

	*argv = NULL;
	redir->stdout = NULL;
	redir->append = false;

	while (skipspace(&s)) {
		switch (*s) {
			case '>':
				s++;
				if (*s == '>') {
					s++;
					redir->append = true;
				}
				redir->stdout = parg(&s);
				break;
			default:
				arg = parg(&s);
				argv[argc++] = arg;
				if (argc >= argvlen)
					return -1;
		}
	}
	argv[argc] = NULL;
	return argc;
}