1 /* $NetBSD: getdelim.c,v 1.7 2009/10/25 20:44:13 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Roy Marples. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __RCSID("$NetBSD: getdelim.c,v 1.7 2009/10/25 20:44:13 christos Exp $"); 32 33 #include "namespace.h" 34 35 #include <sys/param.h> 36 37 #include <assert.h> 38 #include <errno.h> 39 #include <limits.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 44 #include "reentrant.h" 45 #include "local.h" 46 47 /* Minimum buffer size we create. 48 * This should allow config files to fit into our power of 2 buffer growth 49 * without the need for a realloc. */ 50 #define MINBUF 128 51 52 /* This private function allows strings of upto SIZE_MAX - 2 53 * and returns 0 on EOF, both of which are disallowed by POSIX. 54 * Maybe this should be named fgetdelim and proposed to the OpenGroup....*/ 55 ssize_t 56 __getdelim(char **__restrict buf, size_t *__restrict buflen, 57 int sep, FILE *__restrict fp) 58 { 59 unsigned char *p; 60 size_t len, newlen, off; 61 char *newb; 62 63 _DIAGASSERT(fp != NULL); 64 65 if (buf == NULL || buflen == NULL) { 66 errno = EINVAL; 67 return -1; 68 } 69 70 /* If buf is NULL, we have to assume a size of zero */ 71 if (*buf == NULL) 72 *buflen = 0; 73 74 FLOCKFILE(fp); 75 _SET_ORIENTATION(fp, -1); 76 off = 0; 77 for (;;) { 78 /* If the input buffer is empty, refill it */ 79 if (fp->_r <= 0 && __srefill(fp)) { 80 if (__sferror(fp)) 81 goto error; 82 break; 83 } 84 85 /* Scan through looking for the separator */ 86 p = memchr(fp->_p, sep, (size_t)fp->_r); 87 if (p == NULL) 88 len = fp->_r; 89 else 90 len = (p - fp->_p) + 1; 91 92 newlen = off + len + 1; 93 /* Ensure we can handle it */ 94 if (newlen < off || newlen > SIZE_MAX - 2) { 95 errno = EOVERFLOW; 96 goto error; 97 } 98 if (newlen > *buflen) { 99 if (newlen < MINBUF) 100 newlen = MINBUF; 101 if (!powerof2(newlen)) { 102 /* Grow the buffer to the next power of 2 */ 103 newlen--; 104 newlen |= newlen >> 1; 105 newlen |= newlen >> 2; 106 newlen |= newlen >> 4; 107 newlen |= newlen >> 8; 108 newlen |= newlen >> 16; 109 #if SIZE_T_MAX > 0xffffffffU 110 newlen |= newlen >> 32; 111 #endif 112 newlen++; 113 } 114 115 newb = realloc(*buf, newlen); 116 if (newb == NULL) 117 goto error; 118 *buf = newb; 119 *buflen = newlen; 120 } 121 122 (void)memcpy((*buf + off), fp->_p, len); 123 /* Safe, len is never greater than what fp->_r can fit. */ 124 fp->_r -= (int)len; 125 fp->_p += (int)len; 126 off += len; 127 if (p != NULL) 128 break; 129 } 130 FUNLOCKFILE(fp); 131 if (*buf != NULL) 132 *(*buf + off) = '\0'; 133 return off; 134 135 error: 136 FUNLOCKFILE(fp); 137 return -1; 138 } 139 140 ssize_t 141 getdelim(char **__restrict buf, size_t *__restrict buflen, 142 int sep, FILE *__restrict fp) 143 { 144 ssize_t len; 145 146 len = __getdelim(buf, buflen, sep, fp); 147 if (len == 0) { 148 /* POSIX requires that we return -1 on EOF */ 149 return -1; 150 } else if (len < -1) { 151 /* POSIX requires no string larger than SSIZE_MAX */ 152 errno = EOVERFLOW; 153 return -1; 154 } 155 return len; 156 } 157