Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/tests/stdio/getdelim_test.c
39530 views
1
/*-
2
* Copyright (c) 2009 David Schultz <[email protected]>
3
* Copyright (c) 2021 Dell EMC
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/param.h>
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
#include <sys/wait.h>
32
33
#include <fcntl.h>
34
#include <errno.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
39
#include <atf-c.h>
40
41
#define CHUNK_MAX 10
42
43
/* The assertions depend on this string. */
44
char apothegm[] = "All work and no play\0 makes Jack a dull boy.\n";
45
46
/*
47
* This is a neurotic reader function designed to give getdelim() a
48
* hard time. It reads through the string `apothegm' and returns a
49
* random number of bytes up to the requested length.
50
*/
51
static int
52
_reader(void *cookie, char *buf, int len)
53
{
54
size_t *offp = cookie;
55
size_t r;
56
57
r = random() % CHUNK_MAX + 1;
58
if (len > r)
59
len = r;
60
if (len > sizeof(apothegm) - *offp)
61
len = sizeof(apothegm) - *offp;
62
memcpy(buf, apothegm + *offp, len);
63
*offp += len;
64
return (len);
65
}
66
67
static FILE *
68
mkfilebuf(void)
69
{
70
size_t *offp;
71
72
offp = malloc(sizeof(*offp)); /* XXX leak */
73
*offp = 0;
74
return (fropen(offp, _reader));
75
}
76
77
ATF_TC_WITHOUT_HEAD(getline_basic);
78
ATF_TC_BODY(getline_basic, tc)
79
{
80
FILE *fp;
81
char *line;
82
size_t linecap;
83
int i;
84
85
srandom(0);
86
87
/*
88
* Test multiple times with different buffer sizes
89
* and different _reader() return values.
90
*/
91
errno = 0;
92
for (i = 0; i < 8; i++) {
93
fp = mkfilebuf();
94
linecap = i;
95
line = malloc(i);
96
/* First line: the full apothegm */
97
ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
98
ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
99
ATF_REQUIRE(linecap >= sizeof(apothegm));
100
/* Second line: the NUL terminator following the newline */
101
ATF_REQUIRE(getline(&line, &linecap, fp) == 1);
102
ATF_REQUIRE(line[0] == '\0' && line[1] == '\0');
103
/* Third line: EOF */
104
line[0] = 'X';
105
ATF_REQUIRE(getline(&line, &linecap, fp) == -1);
106
ATF_REQUIRE(line[0] == '\0');
107
free(line);
108
line = NULL;
109
ATF_REQUIRE(feof(fp));
110
ATF_REQUIRE(!ferror(fp));
111
fclose(fp);
112
}
113
ATF_REQUIRE(errno == 0);
114
}
115
116
ATF_TC_WITHOUT_HEAD(stream_error);
117
ATF_TC_BODY(stream_error, tc)
118
{
119
char *line;
120
size_t linecap;
121
122
/* Make sure read errors are handled properly. */
123
line = NULL;
124
linecap = 0;
125
errno = 0;
126
ATF_REQUIRE(getline(&line, &linecap, stdout) == -1);
127
ATF_REQUIRE(errno == EBADF);
128
errno = 0;
129
ATF_REQUIRE(getdelim(&line, &linecap, 'X', stdout) == -1);
130
ATF_REQUIRE(errno == EBADF);
131
ATF_REQUIRE(ferror(stdout));
132
}
133
134
ATF_TC_WITHOUT_HEAD(invalid_params);
135
ATF_TC_BODY(invalid_params, tc)
136
{
137
FILE *fp;
138
char *line;
139
size_t linecap;
140
141
/* Make sure NULL linep or linecapp pointers are handled. */
142
fp = mkfilebuf();
143
ATF_REQUIRE(getline(NULL, &linecap, fp) == -1);
144
ATF_REQUIRE(errno == EINVAL);
145
ATF_REQUIRE(getline(&line, NULL, fp) == -1);
146
ATF_REQUIRE(errno == EINVAL);
147
ATF_REQUIRE(ferror(fp));
148
fclose(fp);
149
}
150
151
ATF_TC_WITHOUT_HEAD(eof);
152
ATF_TC_BODY(eof, tc)
153
{
154
FILE *fp;
155
char *line;
156
size_t linecap;
157
158
/* Make sure getline() allocates memory as needed if fp is at EOF. */
159
errno = 0;
160
fp = mkfilebuf();
161
while (!feof(fp)) /* advance to EOF; can't fseek this stream */
162
getc(fp);
163
line = NULL;
164
linecap = 0;
165
printf("getline\n");
166
ATF_REQUIRE(getline(&line, &linecap, fp) == -1);
167
ATF_REQUIRE(line[0] == '\0');
168
ATF_REQUIRE(linecap > 0);
169
ATF_REQUIRE(errno == 0);
170
printf("feof\n");
171
ATF_REQUIRE(feof(fp));
172
ATF_REQUIRE(!ferror(fp));
173
fclose(fp);
174
}
175
176
ATF_TC_WITHOUT_HEAD(nul);
177
ATF_TC_BODY(nul, tc)
178
{
179
FILE *fp;
180
char *line;
181
size_t linecap, n;
182
183
errno = 0;
184
line = NULL;
185
linecap = 0;
186
/* Make sure a NUL delimiter works. */
187
fp = mkfilebuf();
188
n = strlen(apothegm);
189
printf("getdelim\n");
190
ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);
191
ATF_REQUIRE(strcmp(line, apothegm) == 0);
192
ATF_REQUIRE(line[n + 1] == '\0');
193
ATF_REQUIRE(linecap > n + 1);
194
n = strlen(apothegm + n + 1);
195
printf("getdelim 2\n");
196
ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);
197
ATF_REQUIRE(line[n + 1] == '\0');
198
ATF_REQUIRE(linecap > n + 1);
199
ATF_REQUIRE(errno == 0);
200
ATF_REQUIRE(!ferror(fp));
201
fclose(fp);
202
}
203
204
ATF_TC_WITHOUT_HEAD(empty_NULL_buffer);
205
ATF_TC_BODY(empty_NULL_buffer, tc)
206
{
207
FILE *fp;
208
char *line;
209
size_t linecap;
210
211
/* Make sure NULL *linep and zero *linecapp are handled. */
212
fp = mkfilebuf();
213
line = NULL;
214
linecap = 42;
215
ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
216
ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
217
fp = mkfilebuf();
218
free(line);
219
line = malloc(100);
220
linecap = 0;
221
ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
222
ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
223
free(line);
224
ATF_REQUIRE(!ferror(fp));
225
fclose(fp);
226
}
227
228
static void
229
_ipc_read(int fd, char wait_c)
230
{
231
char c;
232
ssize_t len;
233
234
c = 0;
235
while (c != wait_c) {
236
len = read(fd, &c, 1);
237
ATF_CHECK_MSG(len != 0,
238
"EOF on IPC pipe while waiting. Did other side fail?");
239
ATF_CHECK_MSG(len == 1 || errno == EINTR,
240
"read %zu bytes errno %d\n", len, errno);
241
if (len != 1 || errno != EINTR)
242
break;
243
}
244
}
245
246
static void
247
_ipc_write(int fd, char c)
248
{
249
250
while ((write(fd, &c, 1) != 1))
251
ATF_REQUIRE(errno == EINTR);
252
}
253
254
static void
255
ipc_wait(int ipcfd[2])
256
{
257
258
_ipc_read(ipcfd[0], '+');
259
/* Send ACK. */
260
_ipc_write(ipcfd[1], '-');
261
}
262
263
static void
264
ipc_wakeup(int ipcfd[2])
265
{
266
267
_ipc_write(ipcfd[1], '+');
268
/* Wait for ACK. */
269
_ipc_read(ipcfd[0], '-');
270
}
271
272
static void
273
_nonblock_eagain(int buf_mode)
274
{
275
FILE *fp;
276
const char delim = '!';
277
const char *strs[] = {
278
"first line partial!",
279
"second line is sent in full!",
280
"third line is sent partially!",
281
"last line is sent in full!",
282
};
283
char *line;
284
size_t linecap, strslen[nitems(strs)];
285
ssize_t linelen;
286
int fd_fifo, flags, i, ipcfd[2], pipedes[2], pipedes2[2], status;
287
pid_t pid;
288
289
line = NULL;
290
linecap = 0;
291
for (i = 0; i < nitems(strslen); i++)
292
strslen[i] = strlen(strs[i]);
293
ATF_REQUIRE(pipe2(pipedes, O_CLOEXEC) == 0);
294
ATF_REQUIRE(pipe2(pipedes2, O_CLOEXEC) == 0);
295
296
(void)unlink("fifo");
297
ATF_REQUIRE(mkfifo("fifo", 0666) == 0);
298
ATF_REQUIRE((pid = fork()) >= 0);
299
if (pid == 0) {
300
close(pipedes[0]);
301
ipcfd[1] = pipedes[1];
302
ipcfd[0] = pipedes2[0];
303
close(pipedes2[1]);
304
305
ATF_REQUIRE((fd_fifo = open("fifo", O_WRONLY)) != -1);
306
307
/* Partial write. */
308
ATF_REQUIRE(write(fd_fifo, strs[0], strslen[0] - 3) ==
309
strslen[0] - 3);
310
ipc_wakeup(ipcfd);
311
312
ipc_wait(ipcfd);
313
/* Finish off the first line. */
314
ATF_REQUIRE(write(fd_fifo,
315
&(strs[0][strslen[0] - 3]), 3) == 3);
316
/* And include the second full line and a partial 3rd line. */
317
ATF_REQUIRE(write(fd_fifo, strs[1], strslen[1]) == strslen[1]);
318
ATF_REQUIRE(write(fd_fifo, strs[2], strslen[2] - 3) ==
319
strslen[2] - 3);
320
ipc_wakeup(ipcfd);
321
322
ipc_wait(ipcfd);
323
/* Finish the partial write and partially send the last. */
324
ATF_REQUIRE(write(fd_fifo,
325
&(strs[2][strslen[2] - 3]), 3) == 3);
326
ATF_REQUIRE(write(fd_fifo, strs[3], strslen[3] - 3) ==
327
strslen[3] - 3);
328
ipc_wakeup(ipcfd);
329
330
ipc_wait(ipcfd);
331
/* Finish the write */
332
ATF_REQUIRE(write(fd_fifo,
333
&(strs[3][strslen[3] - 3]), 3) == 3);
334
ipc_wakeup(ipcfd);
335
_exit(0);
336
}
337
ipcfd[0] = pipedes[0];
338
close(pipedes[1]);
339
close(pipedes2[0]);
340
ipcfd[1] = pipedes2[1];
341
342
ATF_REQUIRE((fp = fopen("fifo", "r")) != NULL);
343
setvbuf(fp, (char *)NULL, buf_mode, 0);
344
ATF_REQUIRE((flags = fcntl(fileno(fp), F_GETFL, 0)) != -1);
345
ATF_REQUIRE(fcntl(fileno(fp), F_SETFL, flags | O_NONBLOCK) >= 0);
346
347
/* Wait until the writer completes its partial write. */
348
ipc_wait(ipcfd);
349
ATF_REQUIRE_ERRNO(EAGAIN,
350
(linelen = getdelim(&line, &linecap, delim, fp)) == -1);
351
ATF_REQUIRE_STREQ("", line);
352
ATF_REQUIRE(ferror(fp));
353
ATF_REQUIRE(!feof(fp));
354
clearerr(fp);
355
ipc_wakeup(ipcfd);
356
357
ipc_wait(ipcfd);
358
/*
359
* Should now have the finished first line, a full second line,
360
* and a partial third line.
361
*/
362
ATF_CHECK(getdelim(&line, &linecap, delim, fp) == strslen[0]);
363
ATF_REQUIRE_STREQ(strs[0], line);
364
ATF_REQUIRE(getdelim(&line, &linecap, delim, fp) == strslen[1]);
365
ATF_REQUIRE_STREQ(strs[1], line);
366
367
ATF_REQUIRE_ERRNO(EAGAIN,
368
(linelen = getdelim(&line, &linecap, delim, fp)) == -1);
369
ATF_REQUIRE_STREQ("", line);
370
ATF_REQUIRE(ferror(fp));
371
ATF_REQUIRE(!feof(fp));
372
clearerr(fp);
373
ipc_wakeup(ipcfd);
374
375
/* Wait for the partial write to be completed and another to be done. */
376
ipc_wait(ipcfd);
377
ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);
378
ATF_REQUIRE(!ferror(fp));
379
ATF_REQUIRE(!feof(fp));
380
ATF_REQUIRE_STREQ(strs[2], line);
381
ATF_REQUIRE(linelen == strslen[2]);
382
383
ATF_REQUIRE_ERRNO(EAGAIN,
384
(linelen = getdelim(&line, &linecap, delim, fp)) == -1);
385
ATF_REQUIRE_STREQ("", line);
386
ATF_REQUIRE(ferror(fp));
387
ATF_REQUIRE(!feof(fp));
388
clearerr(fp);
389
ipc_wakeup(ipcfd);
390
391
ipc_wait(ipcfd);
392
ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);
393
ATF_REQUIRE(!ferror(fp));
394
ATF_REQUIRE(!feof(fp));
395
ATF_REQUIRE_STREQ(strs[3], line);
396
ATF_REQUIRE(linelen == strslen[3]);
397
398
ATF_REQUIRE(waitpid(pid, &status, WEXITED) != -1);
399
ATF_REQUIRE(WIFEXITED(status));
400
ATF_REQUIRE(WEXITSTATUS(status) == 0);
401
}
402
403
ATF_TC_WITHOUT_HEAD(nonblock_eagain_buffered);
404
ATF_TC_BODY(nonblock_eagain_buffered, tc)
405
{
406
407
_nonblock_eagain(_IOFBF);
408
}
409
410
ATF_TC_WITHOUT_HEAD(nonblock_eagain_unbuffered);
411
ATF_TC_BODY(nonblock_eagain_unbuffered, tc)
412
{
413
414
_nonblock_eagain(_IONBF);
415
}
416
417
418
ATF_TP_ADD_TCS(tp)
419
{
420
421
ATF_TP_ADD_TC(tp, getline_basic);
422
ATF_TP_ADD_TC(tp, stream_error);
423
ATF_TP_ADD_TC(tp, eof);
424
ATF_TP_ADD_TC(tp, invalid_params);
425
ATF_TP_ADD_TC(tp, nul);
426
ATF_TP_ADD_TC(tp, empty_NULL_buffer);
427
ATF_TP_ADD_TC(tp, nonblock_eagain_unbuffered);
428
ATF_TP_ADD_TC(tp, nonblock_eagain_buffered);
429
430
return (atf_no_error());
431
}
432
433