Path: blob/main/lib/libc/tests/stdio/getdelim_test.c
39530 views
/*-1* Copyright (c) 2009 David Schultz <[email protected]>2* Copyright (c) 2021 Dell EMC3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/types.h>29#include <sys/stat.h>30#include <sys/wait.h>3132#include <fcntl.h>33#include <errno.h>34#include <stdio.h>35#include <stdlib.h>36#include <string.h>3738#include <atf-c.h>3940#define CHUNK_MAX 104142/* The assertions depend on this string. */43char apothegm[] = "All work and no play\0 makes Jack a dull boy.\n";4445/*46* This is a neurotic reader function designed to give getdelim() a47* hard time. It reads through the string `apothegm' and returns a48* random number of bytes up to the requested length.49*/50static int51_reader(void *cookie, char *buf, int len)52{53size_t *offp = cookie;54size_t r;5556r = random() % CHUNK_MAX + 1;57if (len > r)58len = r;59if (len > sizeof(apothegm) - *offp)60len = sizeof(apothegm) - *offp;61memcpy(buf, apothegm + *offp, len);62*offp += len;63return (len);64}6566static FILE *67mkfilebuf(void)68{69size_t *offp;7071offp = malloc(sizeof(*offp)); /* XXX leak */72*offp = 0;73return (fropen(offp, _reader));74}7576ATF_TC_WITHOUT_HEAD(getline_basic);77ATF_TC_BODY(getline_basic, tc)78{79FILE *fp;80char *line;81size_t linecap;82int i;8384srandom(0);8586/*87* Test multiple times with different buffer sizes88* and different _reader() return values.89*/90errno = 0;91for (i = 0; i < 8; i++) {92fp = mkfilebuf();93linecap = i;94line = malloc(i);95/* First line: the full apothegm */96ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);97ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);98ATF_REQUIRE(linecap >= sizeof(apothegm));99/* Second line: the NUL terminator following the newline */100ATF_REQUIRE(getline(&line, &linecap, fp) == 1);101ATF_REQUIRE(line[0] == '\0' && line[1] == '\0');102/* Third line: EOF */103line[0] = 'X';104ATF_REQUIRE(getline(&line, &linecap, fp) == -1);105ATF_REQUIRE(line[0] == '\0');106free(line);107line = NULL;108ATF_REQUIRE(feof(fp));109ATF_REQUIRE(!ferror(fp));110fclose(fp);111}112ATF_REQUIRE(errno == 0);113}114115ATF_TC_WITHOUT_HEAD(stream_error);116ATF_TC_BODY(stream_error, tc)117{118char *line;119size_t linecap;120121/* Make sure read errors are handled properly. */122line = NULL;123linecap = 0;124errno = 0;125ATF_REQUIRE(getline(&line, &linecap, stdout) == -1);126ATF_REQUIRE(errno == EBADF);127errno = 0;128ATF_REQUIRE(getdelim(&line, &linecap, 'X', stdout) == -1);129ATF_REQUIRE(errno == EBADF);130ATF_REQUIRE(ferror(stdout));131}132133ATF_TC_WITHOUT_HEAD(invalid_params);134ATF_TC_BODY(invalid_params, tc)135{136FILE *fp;137char *line;138size_t linecap;139140/* Make sure NULL linep or linecapp pointers are handled. */141fp = mkfilebuf();142ATF_REQUIRE(getline(NULL, &linecap, fp) == -1);143ATF_REQUIRE(errno == EINVAL);144ATF_REQUIRE(getline(&line, NULL, fp) == -1);145ATF_REQUIRE(errno == EINVAL);146ATF_REQUIRE(ferror(fp));147fclose(fp);148}149150ATF_TC_WITHOUT_HEAD(eof);151ATF_TC_BODY(eof, tc)152{153FILE *fp;154char *line;155size_t linecap;156157/* Make sure getline() allocates memory as needed if fp is at EOF. */158errno = 0;159fp = mkfilebuf();160while (!feof(fp)) /* advance to EOF; can't fseek this stream */161getc(fp);162line = NULL;163linecap = 0;164printf("getline\n");165ATF_REQUIRE(getline(&line, &linecap, fp) == -1);166ATF_REQUIRE(line[0] == '\0');167ATF_REQUIRE(linecap > 0);168ATF_REQUIRE(errno == 0);169printf("feof\n");170ATF_REQUIRE(feof(fp));171ATF_REQUIRE(!ferror(fp));172fclose(fp);173}174175ATF_TC_WITHOUT_HEAD(nul);176ATF_TC_BODY(nul, tc)177{178FILE *fp;179char *line;180size_t linecap, n;181182errno = 0;183line = NULL;184linecap = 0;185/* Make sure a NUL delimiter works. */186fp = mkfilebuf();187n = strlen(apothegm);188printf("getdelim\n");189ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);190ATF_REQUIRE(strcmp(line, apothegm) == 0);191ATF_REQUIRE(line[n + 1] == '\0');192ATF_REQUIRE(linecap > n + 1);193n = strlen(apothegm + n + 1);194printf("getdelim 2\n");195ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);196ATF_REQUIRE(line[n + 1] == '\0');197ATF_REQUIRE(linecap > n + 1);198ATF_REQUIRE(errno == 0);199ATF_REQUIRE(!ferror(fp));200fclose(fp);201}202203ATF_TC_WITHOUT_HEAD(empty_NULL_buffer);204ATF_TC_BODY(empty_NULL_buffer, tc)205{206FILE *fp;207char *line;208size_t linecap;209210/* Make sure NULL *linep and zero *linecapp are handled. */211fp = mkfilebuf();212line = NULL;213linecap = 42;214ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);215ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);216fp = mkfilebuf();217free(line);218line = malloc(100);219linecap = 0;220ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);221ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);222free(line);223ATF_REQUIRE(!ferror(fp));224fclose(fp);225}226227static void228_ipc_read(int fd, char wait_c)229{230char c;231ssize_t len;232233c = 0;234while (c != wait_c) {235len = read(fd, &c, 1);236ATF_CHECK_MSG(len != 0,237"EOF on IPC pipe while waiting. Did other side fail?");238ATF_CHECK_MSG(len == 1 || errno == EINTR,239"read %zu bytes errno %d\n", len, errno);240if (len != 1 || errno != EINTR)241break;242}243}244245static void246_ipc_write(int fd, char c)247{248249while ((write(fd, &c, 1) != 1))250ATF_REQUIRE(errno == EINTR);251}252253static void254ipc_wait(int ipcfd[2])255{256257_ipc_read(ipcfd[0], '+');258/* Send ACK. */259_ipc_write(ipcfd[1], '-');260}261262static void263ipc_wakeup(int ipcfd[2])264{265266_ipc_write(ipcfd[1], '+');267/* Wait for ACK. */268_ipc_read(ipcfd[0], '-');269}270271static void272_nonblock_eagain(int buf_mode)273{274FILE *fp;275const char delim = '!';276const char *strs[] = {277"first line partial!",278"second line is sent in full!",279"third line is sent partially!",280"last line is sent in full!",281};282char *line;283size_t linecap, strslen[nitems(strs)];284ssize_t linelen;285int fd_fifo, flags, i, ipcfd[2], pipedes[2], pipedes2[2], status;286pid_t pid;287288line = NULL;289linecap = 0;290for (i = 0; i < nitems(strslen); i++)291strslen[i] = strlen(strs[i]);292ATF_REQUIRE(pipe2(pipedes, O_CLOEXEC) == 0);293ATF_REQUIRE(pipe2(pipedes2, O_CLOEXEC) == 0);294295(void)unlink("fifo");296ATF_REQUIRE(mkfifo("fifo", 0666) == 0);297ATF_REQUIRE((pid = fork()) >= 0);298if (pid == 0) {299close(pipedes[0]);300ipcfd[1] = pipedes[1];301ipcfd[0] = pipedes2[0];302close(pipedes2[1]);303304ATF_REQUIRE((fd_fifo = open("fifo", O_WRONLY)) != -1);305306/* Partial write. */307ATF_REQUIRE(write(fd_fifo, strs[0], strslen[0] - 3) ==308strslen[0] - 3);309ipc_wakeup(ipcfd);310311ipc_wait(ipcfd);312/* Finish off the first line. */313ATF_REQUIRE(write(fd_fifo,314&(strs[0][strslen[0] - 3]), 3) == 3);315/* And include the second full line and a partial 3rd line. */316ATF_REQUIRE(write(fd_fifo, strs[1], strslen[1]) == strslen[1]);317ATF_REQUIRE(write(fd_fifo, strs[2], strslen[2] - 3) ==318strslen[2] - 3);319ipc_wakeup(ipcfd);320321ipc_wait(ipcfd);322/* Finish the partial write and partially send the last. */323ATF_REQUIRE(write(fd_fifo,324&(strs[2][strslen[2] - 3]), 3) == 3);325ATF_REQUIRE(write(fd_fifo, strs[3], strslen[3] - 3) ==326strslen[3] - 3);327ipc_wakeup(ipcfd);328329ipc_wait(ipcfd);330/* Finish the write */331ATF_REQUIRE(write(fd_fifo,332&(strs[3][strslen[3] - 3]), 3) == 3);333ipc_wakeup(ipcfd);334_exit(0);335}336ipcfd[0] = pipedes[0];337close(pipedes[1]);338close(pipedes2[0]);339ipcfd[1] = pipedes2[1];340341ATF_REQUIRE((fp = fopen("fifo", "r")) != NULL);342setvbuf(fp, (char *)NULL, buf_mode, 0);343ATF_REQUIRE((flags = fcntl(fileno(fp), F_GETFL, 0)) != -1);344ATF_REQUIRE(fcntl(fileno(fp), F_SETFL, flags | O_NONBLOCK) >= 0);345346/* Wait until the writer completes its partial write. */347ipc_wait(ipcfd);348ATF_REQUIRE_ERRNO(EAGAIN,349(linelen = getdelim(&line, &linecap, delim, fp)) == -1);350ATF_REQUIRE_STREQ("", line);351ATF_REQUIRE(ferror(fp));352ATF_REQUIRE(!feof(fp));353clearerr(fp);354ipc_wakeup(ipcfd);355356ipc_wait(ipcfd);357/*358* Should now have the finished first line, a full second line,359* and a partial third line.360*/361ATF_CHECK(getdelim(&line, &linecap, delim, fp) == strslen[0]);362ATF_REQUIRE_STREQ(strs[0], line);363ATF_REQUIRE(getdelim(&line, &linecap, delim, fp) == strslen[1]);364ATF_REQUIRE_STREQ(strs[1], line);365366ATF_REQUIRE_ERRNO(EAGAIN,367(linelen = getdelim(&line, &linecap, delim, fp)) == -1);368ATF_REQUIRE_STREQ("", line);369ATF_REQUIRE(ferror(fp));370ATF_REQUIRE(!feof(fp));371clearerr(fp);372ipc_wakeup(ipcfd);373374/* Wait for the partial write to be completed and another to be done. */375ipc_wait(ipcfd);376ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);377ATF_REQUIRE(!ferror(fp));378ATF_REQUIRE(!feof(fp));379ATF_REQUIRE_STREQ(strs[2], line);380ATF_REQUIRE(linelen == strslen[2]);381382ATF_REQUIRE_ERRNO(EAGAIN,383(linelen = getdelim(&line, &linecap, delim, fp)) == -1);384ATF_REQUIRE_STREQ("", line);385ATF_REQUIRE(ferror(fp));386ATF_REQUIRE(!feof(fp));387clearerr(fp);388ipc_wakeup(ipcfd);389390ipc_wait(ipcfd);391ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);392ATF_REQUIRE(!ferror(fp));393ATF_REQUIRE(!feof(fp));394ATF_REQUIRE_STREQ(strs[3], line);395ATF_REQUIRE(linelen == strslen[3]);396397ATF_REQUIRE(waitpid(pid, &status, WEXITED) != -1);398ATF_REQUIRE(WIFEXITED(status));399ATF_REQUIRE(WEXITSTATUS(status) == 0);400}401402ATF_TC_WITHOUT_HEAD(nonblock_eagain_buffered);403ATF_TC_BODY(nonblock_eagain_buffered, tc)404{405406_nonblock_eagain(_IOFBF);407}408409ATF_TC_WITHOUT_HEAD(nonblock_eagain_unbuffered);410ATF_TC_BODY(nonblock_eagain_unbuffered, tc)411{412413_nonblock_eagain(_IONBF);414}415416417ATF_TP_ADD_TCS(tp)418{419420ATF_TP_ADD_TC(tp, getline_basic);421ATF_TP_ADD_TC(tp, stream_error);422ATF_TP_ADD_TC(tp, eof);423ATF_TP_ADD_TC(tp, invalid_params);424ATF_TP_ADD_TC(tp, nul);425ATF_TP_ADD_TC(tp, empty_NULL_buffer);426ATF_TP_ADD_TC(tp, nonblock_eagain_unbuffered);427ATF_TP_ADD_TC(tp, nonblock_eagain_buffered);428429return (atf_no_error());430}431432433