aboutsummaryrefslogtreecommitdiff
blob: 91128d39ace44b2afa385bf15b5307dcf55a2650 (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
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#include "tests.h"

const char *color_normal = "\033[0m";
const char *color_green  = "\033[32;01m";
const char *color_yellow = "\033[33;01m";
const char *color_red    = "\033[31;01m";

#ifndef CONFIG
# define CONFIG 1
#endif

#define V_STRMODE "<r|w|a>[+bcemx] (see `man 3 fopen`)"

static bool _strtoul(const char *sul, unsigned long *ul)
{
	char *e;
	*ul = strtoul(sul, &e, 0);
	return (*e == '\0');
}

static int _get_flags(const char *str_flags, const value_pair flags[])
{
	const char *delim = "|";
	char *tok = strtok(strdup(str_flags), delim);
	int ret = 0;
	while (tok) {
		bool found;
		ret |= _lookup_val(flags, tok, &found);
		if (!found) {
			int a;
			sscanf(tok, "%i", &a);
			ret |= a;
		}
		tok = strtok(NULL, delim);
	}
	return ret;
}

#define V_FFLAGS "O_XXX flags (see `man 2 open`)"
int f_get_flags(const char *str_flags)
{
	const value_pair flags[] = {
		PAIR(O_APPEND)
		PAIR(O_CREAT)
		PAIR(O_DIRECTORY)
		PAIR(O_EXCL)
		PAIR(O_NOCTTY)
		PAIR(O_NOFOLLOW)
		PAIR(O_RDONLY)
		PAIR(O_RDWR)
		PAIR(O_TRUNC)
		PAIR(O_WRONLY)
		{ }
	};
	if (!str_flags)
		return O_CREAT | O_RDWR;
	return _get_flags(str_flags, flags);
}

#define V_FILE "NULL | path"
const char *f_get_file(const char *str_file)
{
	if (!strcmp(str_file, "NULL"))
		return NULL;
	else
		return str_file;
}

#define V_ATFLAGS "0 | AT_SYMLINK_NOFOLLOW | AT_REMOVEDIR | AT_SYMLINK_FOLLOW | AT_EACCESS"
int at_get_flags(const char *str_flags)
{
	const value_pair flags[] = {
		PAIR(AT_SYMLINK_NOFOLLOW)
		PAIR(AT_REMOVEDIR)
		PAIR(AT_SYMLINK_FOLLOW)
		PAIR(AT_EACCESS)
		{ }
	};
	return _get_flags(str_flags, flags);
}

#define V_MODE_T "0x# (for hex) | 0# (for octal) | # (for decimal)"
mode_t sscanf_mode_t(const char *str_mode)
{
	/* some systems (like Linux) have a 32bit mode_t.  Others
	 * (like FreeBSD) have a 16bit mode_t.  We can't straight
	 * sscanf() into it otherwise we might smash the stack.
	 */
	int mode;
	/* Default to full access.  */
	if (!str_mode)
		return 0777;
	sscanf(str_mode, "%i", &mode);
	return (mode_t)mode;
}

#define V_DEV_T V_MODE_T
dev_t sscanf_dev_t(const char *str_dev)
{
	/* Similar issue with dev_t as mode_t.  Can't assume that
	 * sizeof(dev_t) == sizeof(int).  Often dev_t is 64bit.
	 */
	int dev;
	sscanf(str_dev, "%i", &dev);
	return (dev_t)dev;
}

#define V_DIRFD "AT_FDCWD | fd # | path[:<flags>[:<mode>]]"
int at_get_fd(const char *str_dirfd)
{
	/* work some magic ... expected format:
	 *	- AT_FDCWD
	 *	- number
	 *	- path[:<flags>[:<mode>]]
	 */
	if (!strcmp(str_dirfd, "AT_FDCWD"))
		return AT_FDCWD;

	char *str = strdup(str_dirfd);
	char *str_path, *str_flags, *str_mode;

	str_path = strtok(str, ":");
	str_flags = strtok(NULL, ":");
	if (str_flags == NULL) {
		unsigned long dirfd;
		if (_strtoul(str_dirfd, &dirfd))
			return dirfd;
	}
	str_mode = strtok(NULL, ":");

	return open64(str_path, f_get_flags(str_flags), sscanf_mode_t(str_mode));
}

#define V_TIMESPEC "NULL | NOW | #[,#]"
struct timespec *parse_timespec(const char *s)
{
	struct timespec *times;

	if (!strcmp(s, "NULL"))
		return NULL;

	times = xzalloc(sizeof(*times));

	if (!strcmp(s, "NOW")) {
		times->tv_sec = time(0);
	} else {
		int64_t sec = 0, nsec = 0;
		sscanf(s, "%" PRIi64 ",%" PRIi64, &sec, &nsec);
		times->tv_sec = sec;
		times->tv_nsec = nsec;
	}

	return times;
}

#define V_ACCESS_MODE "r | w | x | f"
int access_mode(const char *s)
{
	int ret = 0;
	if (strchr(s, 'r')) ret |= R_OK;
	if (strchr(s, 'w')) ret |= W_OK;
	if (strchr(s, 'x')) ret |= X_OK;
	if (strchr(s, 'f')) ret  = F_OK;
	return ret;
}

int main(int argc, char *argv[])
{
#if CONFIG
	int i, test_ret;

	if ((argc - 1) % (ARG_CNT + 1) || argc == 1) {
#define _ARG_USE "<ret> " ARG_USE
		printf(
			"usage: " SFUNC " <tests>\n"
			"test: < " _ARG_USE " >\n"
			"\n"
		);
		const char *vusage[] = {
			"<ret>", "#[,<errno>]; # is a decimal and errno can be symbolic",
			"<dirfd>", V_DIRFD,
			"<file>", V_FILE,
			"<times>", V_TIMESPEC,
			"<atflags>", V_ATFLAGS,
			"<fflags>", V_FFLAGS,
			"<mode>", V_MODE_T,
			"<strmode>", V_STRMODE,
			"<acc_mode>", V_ACCESS_MODE,
			"<dev>", V_DEV_T,
			"<uid>", "# (decimal)",
			"<gid>", "# (decimal)",
		};
		for (i = 0; i < ARRAY_SIZE(vusage); i += 2)
			if (strstr(_ARG_USE, vusage[i]))
				printf("%-10s := %s\n", vusage[i], vusage[i + 1]);
		exit(1);
	}

	if (getenv("NOCOLOR"))
		color_normal = color_green = color_yellow = color_red = "";

	test_ret = 0;
	i = 1;
	while (i < argc) {
		char *s;

		s = argv[i++];
		long want_ret = atoi(s);

		int want_errno = 0;
		s = strchr(s, ',');
		if (s++) {
			want_errno = lookup_errno(s);
			if (!want_errno)
				err("unable to lookup errno '%s'", s);
		}

		process_args();

		errno = 0;
		long actual_ret = (long)FUNC(FUNC_IMP);
		int actual_errno = errno;

		bool passed_ret = want_ret == actual_ret;
		bool passed_errno = !want_errno || want_errno == actual_errno;
		bool passed = passed_ret && passed_errno;
#define COLOR(b) (b ? color_green : color_red)

		printf(
			"%s%s%s: " SFUNC "(" FUNC_STR ") = "
			"%s%li (wanted %li)%s; "
			"%serrno = %s:%i [%s] (wanted %s:%i [%s])%s\n",
			COLOR(passed), passed ? "PASS" : "FAIL", color_normal,
#if ARG_CNT
			FUNC_IMP,
#endif
			(passed ? "" : COLOR(passed_ret)),
			actual_ret, want_ret, color_normal,
			(!passed && want_errno) ? COLOR(passed_errno) : "",
			rev_lookup_errno(actual_errno), actual_errno, strerror(actual_errno),
			rev_lookup_errno(want_errno), want_errno, strerror(want_errno),
			color_normal);

		if (!passed) ++test_ret;
	}

	return test_ret;
#else
	puts("not implemented");
	return 77;
#endif
}