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 char *line; 117 size_t linecap; 118 119 /* Make sure read errors are handled properly. */ 120 line = NULL; 121 linecap = 0; 122 errno = 0; 123 ATF_REQUIRE(getline(&line, &linecap, stdout) == -1); 124 ATF_REQUIRE(errno == EBADF); 125 errno = 0; 126 ATF_REQUIRE(getdelim(&line, &linecap, 'X', stdout) == -1); 127 ATF_REQUIRE(errno == EBADF); 128 ATF_REQUIRE(ferror(stdout)); 129 } 130 131 ATF_TC_WITHOUT_HEAD(invalid_params); 132 ATF_TC_BODY(invalid_params, tc) 133 { 134 FILE *fp; 135 char *line; 136 size_t linecap; 137 138 /* Make sure NULL linep or linecapp pointers are handled. */ 139 fp = mkfilebuf(); 140 ATF_REQUIRE(getline(NULL, &linecap, fp) == -1); 141 ATF_REQUIRE(errno == EINVAL); 142 ATF_REQUIRE(getline(&line, NULL, fp) == -1); 143 ATF_REQUIRE(errno == EINVAL); 144 ATF_REQUIRE(ferror(fp)); 145 fclose(fp); 146 } 147 148 ATF_TC_WITHOUT_HEAD(eof); 149 ATF_TC_BODY(eof, tc) 150 { 151 FILE *fp; 152 char *line; 153 size_t linecap; 154 155 /* Make sure getline() allocates memory as needed if fp is at EOF. */ 156 errno = 0; 157 fp = mkfilebuf(); 158 while (!feof(fp)) /* advance to EOF; can't fseek this stream */ 159 getc(fp); 160 line = NULL; 161 linecap = 0; 162 printf("getline\n"); 163 ATF_REQUIRE(getline(&line, &linecap, fp) == -1); 164 ATF_REQUIRE(line[0] == '\0'); 165 ATF_REQUIRE(linecap > 0); 166 ATF_REQUIRE(errno == 0); 167 printf("feof\n"); 168 ATF_REQUIRE(feof(fp)); 169 ATF_REQUIRE(!ferror(fp)); 170 fclose(fp); 171 } 172 173 ATF_TC_WITHOUT_HEAD(nul); 174 ATF_TC_BODY(nul, tc) 175 { 176 FILE *fp; 177 char *line; 178 size_t linecap, n; 179 180 errno = 0; 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 line = NULL; 211 linecap = 42; 212 ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); 213 ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); 214 fp = mkfilebuf(); 215 free(line); 216 line = malloc(100); 217 linecap = 0; 218 ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1); 219 ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0); 220 free(line); 221 ATF_REQUIRE(!ferror(fp)); 222 fclose(fp); 223 } 224 225 ATF_TP_ADD_TCS(tp) 226 { 227 228 ATF_TP_ADD_TC(tp, getline_basic); 229 ATF_TP_ADD_TC(tp, stream_error); 230 ATF_TP_ADD_TC(tp, eof); 231 ATF_TP_ADD_TC(tp, invalid_params); 232 ATF_TP_ADD_TC(tp, nul); 233 ATF_TP_ADD_TC(tp, empty_NULL_buffer); 234 235 return (atf_no_error()); 236 } 237