1 /*- 2 * Copyright (c) 2009 David Schultz <das@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #define _WITH_GETLINE 31 #include <errno.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 36 #include <atf-c.h> 37 38 #define CHUNK_MAX 10 39 40 /* The assertions depend on this string. */ 41 char apothegm[] = "All work and no play\0 makes Jack a dull boy.\n"; 42 43 /* 44 * This is a neurotic reader function designed to give getdelim() a 45 * hard time. It reads through the string `apothegm' and returns a 46 * random number of bytes up to the requested length. 47 */ 48 static int 49 _reader(void *cookie, char *buf, int len) 50 { 51 size_t *offp = cookie; 52 size_t r; 53 54 r = random() % CHUNK_MAX + 1; 55 if (len > r) 56 len = r; 57 if (len > sizeof(apothegm) - *offp) 58 len = sizeof(apothegm) - *offp; 59 memcpy(buf, apothegm + *offp, len); 60 *offp += len; 61 return (len); 62 } 63 64 static FILE * 65 mkfilebuf(void) 66 { 67 size_t *offp; 68 69 offp = malloc(sizeof(*offp)); /* XXX leak */ 70 *offp = 0; 71 return (fropen(offp, _reader)); 72 } 73 74 ATF_TC_WITHOUT_HEAD(getline_basic); 75 ATF_TC_BODY(getline_basic, tc) 76 { 77 FILE *fp; 78 char *line; 79 size_t linecap; 80 int i; 81 82 srandom(0); 83 84 /* 85 * Test multiple times with different buffer sizes 86 * and different _reader() return values. 87 */ 88 errno = 0; 89 for (i = 0; i < 8; i++) { 90 fp = mkfilebuf(); 91 linecap = i; 92 line = malloc(i); 93 /* First line: the full apothegm */ 94 ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); 95 ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); 96 ATF_REQUIRE(linecap >= sizeof(apothegm)); 97 /* Second line: the NUL terminator following the newline */ 98 ATF_REQUIRE(getline(&line, &linecap, fp) == 1); 99 ATF_REQUIRE(line[0] == '\0' && line[1] == '\0'); 100 /* Third line: EOF */ 101 line[0] = 'X'; 102 ATF_REQUIRE(getline(&line, &linecap, fp) == -1); 103 ATF_REQUIRE(line[0] == '\0'); 104 free(line); 105 line = NULL; 106 ATF_REQUIRE(feof(fp)); 107 ATF_REQUIRE(!ferror(fp)); 108 fclose(fp); 109 } 110 ATF_REQUIRE(errno == 0); 111 } 112 113 ATF_TC_WITHOUT_HEAD(stream_error); 114 ATF_TC_BODY(stream_error, tc) 115 { 116 FILE *fp; 117 char *line; 118 size_t linecap; 119 120 /* Make sure read errors are handled properly. */ 121 line = NULL; 122 linecap = 0; 123 errno = 0; 124 ATF_REQUIRE(getline(&line, &linecap, stdout) == -1); 125 ATF_REQUIRE(errno == EBADF); 126 errno = 0; 127 ATF_REQUIRE(getdelim(&line, &linecap, 'X', stdout) == -1); 128 ATF_REQUIRE(errno == EBADF); 129 ATF_REQUIRE(ferror(stdout)); 130 } 131 132 ATF_TC_WITHOUT_HEAD(invalid_params); 133 ATF_TC_BODY(invalid_params, tc) 134 { 135 FILE *fp; 136 char *line; 137 size_t linecap; 138 139 /* Make sure NULL linep or linecapp pointers are handled. */ 140 fp = mkfilebuf(); 141 ATF_REQUIRE(getline(NULL, &linecap, fp) == -1); 142 ATF_REQUIRE(errno == EINVAL); 143 ATF_REQUIRE(getline(&line, NULL, fp) == -1); 144 ATF_REQUIRE(errno == EINVAL); 145 ATF_REQUIRE(ferror(fp)); 146 fclose(fp); 147 } 148 149 ATF_TC_WITHOUT_HEAD(eof); 150 ATF_TC_BODY(eof, tc) 151 { 152 FILE *fp; 153 char *line; 154 size_t linecap; 155 156 /* Make sure getline() allocates memory as needed if fp is at EOF. */ 157 errno = 0; 158 fp = mkfilebuf(); 159 while (!feof(fp)) /* advance to EOF; can't fseek this stream */ 160 getc(fp); 161 line = NULL; 162 linecap = 0; 163 printf("getline\n"); 164 ATF_REQUIRE(getline(&line, &linecap, fp) == -1); 165 ATF_REQUIRE(line[0] == '\0'); 166 ATF_REQUIRE(linecap > 0); 167 ATF_REQUIRE(errno == 0); 168 printf("feof\n"); 169 ATF_REQUIRE(feof(fp)); 170 ATF_REQUIRE(!ferror(fp)); 171 fclose(fp); 172 } 173 174 ATF_TC_WITHOUT_HEAD(nul); 175 ATF_TC_BODY(nul, tc) 176 { 177 FILE *fp; 178 char *line; 179 size_t linecap, n; 180 181 line = NULL; 182 linecap = 0; 183 /* Make sure a NUL delimiter works. */ 184 fp = mkfilebuf(); 185 n = strlen(apothegm); 186 printf("getdelim\n"); 187 ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1); 188 ATF_REQUIRE(strcmp(line, apothegm) == 0); 189 ATF_REQUIRE(line[n + 1] == '\0'); 190 ATF_REQUIRE(linecap > n + 1); 191 n = strlen(apothegm + n + 1); 192 printf("getdelim 2\n"); 193 ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1); 194 ATF_REQUIRE(line[n + 1] == '\0'); 195 ATF_REQUIRE(linecap > n + 1); 196 ATF_REQUIRE(errno == 0); 197 ATF_REQUIRE(!ferror(fp)); 198 fclose(fp); 199 } 200 201 ATF_TC_WITHOUT_HEAD(empty_NULL_buffer); 202 ATF_TC_BODY(empty_NULL_buffer, tc) 203 { 204 FILE *fp; 205 char *line; 206 size_t linecap; 207 208 /* Make sure NULL *linep and zero *linecapp are handled. */ 209 fp = mkfilebuf(); 210 free(line); 211 line = NULL; 212 linecap = 42; 213 ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); 214 ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); 215 fp = mkfilebuf(); 216 free(line); 217 line = malloc(100); 218 linecap = 0; 219 ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); 220 ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); 221 free(line); 222 ATF_REQUIRE(!ferror(fp)); 223 fclose(fp); 224 } 225 226 ATF_TP_ADD_TCS(tp) 227 { 228 229 ATF_TP_ADD_TC(tp, getline_basic); 230 ATF_TP_ADD_TC(tp, stream_error); 231 ATF_TP_ADD_TC(tp, eof); 232 ATF_TP_ADD_TC(tp, invalid_params); 233 ATF_TP_ADD_TC(tp, nul); 234 ATF_TP_ADD_TC(tp, empty_NULL_buffer); 235 236 return (atf_no_error()); 237 } 238