diff options
author | 2012-03-21 23:44:35 +0000 | |
---|---|---|
committer | 2012-03-21 23:44:35 +0000 | |
commit | dc89375155d9f94f665cd70800cb655b8fc502b7 (patch) | |
tree | 744c2f0f81b2a6580b4f6c8f9c6405c825cda475 /lib/libc/stdio/getdelim.c | |
parent | Implement execvpe(3) and posix_spawn(3) and family. Based on (diff) | |
download | wireguard-openbsd-dc89375155d9f94f665cd70800cb655b8fc502b7.tar.xz wireguard-openbsd-dc89375155d9f94f665cd70800cb655b8fc502b7.zip |
Implement getdelim(3) and getline(3).
Prompted in a mail to tech@ by Jan Klemkow (j-dot-klemkow-at-wemelug-dot-de)
but this is based on NetBSD's implementation instead with some tweaks by me.
Further improvements would happen in tree.
ok millert@; discussed with many others
ports cleanup by naddy@, sthen@. Antti Harri, Gonzalo L. R. and myself.
Diffstat (limited to 'lib/libc/stdio/getdelim.c')
-rw-r--r-- | lib/libc/stdio/getdelim.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/lib/libc/stdio/getdelim.c b/lib/libc/stdio/getdelim.c new file mode 100644 index 00000000000..dcde0c34e61 --- /dev/null +++ b/lib/libc/stdio/getdelim.c @@ -0,0 +1,134 @@ +/* $OpenBSD: getdelim.c,v 1.1 2012/03/21 23:44:35 fgsch Exp $ */ +/* $NetBSD: getdelim.c,v 1.13 2011/07/22 23:12:30 joerg Exp $ */ + +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "local.h" + +/* Minimum buffer size we create. + * This should allow config files to fit into our power of 2 buffer growth + * without the need for a realloc. */ +#define MINBUF 128 + +ssize_t +getdelim(char **__restrict buf, size_t *__restrict buflen, + int sep, FILE *__restrict fp) +{ + unsigned char *p; + size_t len, newlen, off; + char *newb; + + FLOCKFILE(fp); + + if (buf == NULL || buflen == NULL) { + errno = EINVAL; + goto error; + } + + /* If buf is NULL, we have to assume a size of zero */ + if (*buf == NULL) + *buflen = 0; + + _SET_ORIENTATION(fp, -1); + off = 0; + do { + /* If the input buffer is empty, refill it */ + if (fp->_r <= 0 && __srefill(fp)) { + if (__sferror(fp)) + goto error; + /* No error, so EOF. */ + break; + } + + /* Scan through looking for the separator */ + p = memchr(fp->_p, sep, (size_t)fp->_r); + if (p == NULL) + len = fp->_r; + else + len = (p - fp->_p) + 1; + + newlen = off + len; + /* Ensure we can handle it */ + if (newlen < off || newlen > SSIZE_MAX) { + errno = EOVERFLOW; + goto error; + } + newlen++; /* reserve space for the NULL terminator */ + if (newlen > *buflen) { + if (newlen < MINBUF) + newlen = MINBUF; +#define powerof2(x) ((((x)-1)&(x))==0) + if (!powerof2(newlen)) { + /* Grow the buffer to the next power of 2 */ + newlen--; + newlen |= newlen >> 1; + newlen |= newlen >> 2; + newlen |= newlen >> 4; + newlen |= newlen >> 8; + newlen |= newlen >> 16; +#if SIZE_T_MAX > 0xffffffffU + newlen |= newlen >> 32; +#endif + newlen++; + } + + newb = realloc(*buf, newlen); + if (newb == NULL) + goto error; + *buf = newb; + *buflen = newlen; + } + + (void)memcpy((*buf + off), fp->_p, len); + /* Safe, len is never greater than what fp->_r can fit. */ + fp->_r -= (int)len; + fp->_p += (int)len; + off += len; + } while (p == NULL); + + FUNLOCKFILE(fp); + + /* POSIX demands we return -1 on EOF. */ + if (off == 0) + return -1; + + if (*buf != NULL) + *(*buf + off) = '\0'; + return off; + +error: + fp->_flags |= __SERR; + FUNLOCKFILE(fp); + return -1; +} |