aboutsummaryrefslogtreecommitdiff
path: root/src/strsplit.c
blob: c2e1eaca00b4a8faacfae0823dcb1d03df77e539 (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
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
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "defs.h"

void
argcv_free(int ac, char **av)
{
	int i;
	for (i = 0; i < ac; i++)
		free(av[i]);
	free(av);
}

int
strsplit(char const *str, int max, int *ret_ac, char ***ret_av, char **endp)
{
	char **av = NULL;
	int ac;
	char const *p;
	enum { Stop, Error, Inspace, Inquote, Inword } state;
	
	av = calloc(max+1, sizeof(*av));
	if (!av)
		return STRSPLIT_NOMEM;
	ac = 0;

#define ISWS(c) ((c)==' '||(c)=='\t')
#define UNESCAPE(p) ((p)[0] == '\\' && ((p)[1] == '\\' || (p)[1] == '"'))
	
	if (ISWS(*str))
		state = Inspace;
	else if (*str == '"') {
		state = Inquote;
		str++;
	} else
		state = Inword;
	
	p = str;
	while (state != Stop && state != Error) {
		switch (state) {
		case Inquote:
			if (*p == 0) {
				p = str;
				state = Error;
			} else if (UNESCAPE(p))
				p += 2;
			else if (*p == '"') {
				char *q;
				size_t len = p - str;
				av[ac] = malloc(len + 1);
				if (!av[ac])
					goto err;
				q = av[ac];
				while (str < p) {
					if (UNESCAPE(str))
						str++;
					*q++ = *str++;
				}
				*q = 0;
				ac++;
				str++;
				p = str;
				if (*p == 0)
					state = Stop;
				else if (ISWS(*str))
					state = Inspace;
				else
					state = Error;
			} else
				p++;
			break;

		case Inword:
			if (*p == 0 || ISWS(*p)) {
				size_t len = p - str;
				av[ac] = malloc(len + 1);
				if (!av[ac])
					goto err;
				memcpy(av[ac], str, len);
				av[ac][len] = 0;
				ac++;
				state = *p ? Inspace : Stop;
			} else
				p++;
			break;

		case Inspace:
			if (*p == 0) {
				state = Stop;
				break;
			} else if (*p == '"') {
				state = Inquote;
				p++;
				str = p;
			} else if (!ISWS(*p)) {
				state = Inword;
				str = p;
			} else
				p++;
			if (ac == max && state != Inspace) {
				state = Stop;
			}
			break;
			
		default:
			abort();
		}
	}
	av[ac] = NULL;
	*ret_av = av;
	*ret_ac = ac;
	*endp = (char*) p;
	return state == Stop ? STRSPLIT_OK : STRSPLIT_ERR;
err:
	argcv_free(ac, av);
	return STRSPLIT_NOMEM;
}

Return to:

Send suggestions and report system problems to the System administrator.