1 /* $NetBSD: getdelim.c,v 1.3 2009/07/14 18:29:41 roy 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.3 2009/07/14 18:29:41 roy Exp $"); 32 33 #include <sys/param.h> 34 35 #include <assert.h> 36 #include <errno.h> 37 #include <limits.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include "reentrant.h" 43 #include "local.h" 44 45 /* Minimum buffer size we create. 46 * This should allow config files to fit into our power of 2 buffer growth 47 * without the need for a realloc. */ 48 #define MINBUF 128 49 50 ssize_t 51 getdelim(char **__restrict buf, size_t *__restrict buflen, 52 int sep, FILE *__restrict fp) 53 { 54 unsigned char *p; 55 size_t len, off, newlen; 56 char *newb; 57 58 _DIAGASSERT(fp != NULL); 59 60 if (buf == NULL || buflen == NULL) { 61 errno = EINVAL; 62 return -1; 63 } 64 65 /* If buf is NULL, we have to assume a size of zero */ 66 if (*buf == NULL) 67 *buflen = 0; 68 69 FLOCKFILE(fp); 70 _SET_ORIENTATION(fp, -1); 71 off = 0; 72 for (;;) { 73 /* If the input buffer is empty, refill it */ 74 if (fp->_r <= 0 && __srefill(fp)) { 75 /* POSIX requires we return -1 on EOF */ 76 if (off == 0 || __sferror(fp)) 77 goto error; 78 break; 79 } 80 81 /* Scan through looking for the separator */ 82 p = memchr(fp->_p, sep, (size_t)fp->_r); 83 if (p == NULL) 84 len = fp->_r; 85 else 86 len = (p - fp->_p) + 1; 87 88 newlen = off + len; 89 /* Ensure that the resultant buffer length fits in ssize_t */ 90 if (newlen > (size_t)SSIZE_MAX) { 91 errno = EOVERFLOW; 92 goto error; 93 } 94 newlen++; /* reserve space for the NULL terminator */ 95 if (newlen > *buflen) { 96 if (newlen < MINBUF) 97 newlen = MINBUF; 98 if (!powerof2(newlen)) { 99 /* Grow the buffer to the next power of 2 */ 100 newlen--; 101 newlen |= newlen >> 1; 102 newlen |= newlen >> 2; 103 newlen |= newlen >> 4; 104 newlen |= newlen >> 8; 105 newlen |= newlen >> 16; 106 #if SIZE_T_MAX > 0xffffffffU 107 newlen |= newlen >> 32; 108 #endif 109 newlen++; 110 } 111 112 newb = realloc(*buf, newlen); 113 if (newb == NULL) 114 goto error; 115 *buf = newb; 116 *buflen = newlen; 117 } 118 119 (void)memcpy((void *)(*buf + off), (void *)fp->_p, len); 120 /* Safe, len is never greater than what fp->_r can fit. */ 121 fp->_r -= (int)len; 122 fp->_p += (int)len; 123 off += len; 124 if (p != NULL) 125 break; 126 } 127 FUNLOCKFILE(fp); 128 if (*buf != NULL) 129 *(*buf + off) = '\0'; 130 return off; 131 132 error: 133 FUNLOCKFILE(fp); 134 return -1; 135 } 136