diff --git a/cf/roken-frag.m4 b/cf/roken-frag.m4 index 90e514c20..30b1fec24 100644 --- a/cf/roken-frag.m4 +++ b/cf/roken-frag.m4 @@ -338,11 +338,13 @@ AC_BROKEN([ \ fnmatch \ freehostent \ getcwd \ + getdelim \ getdtablesize \ getegid \ geteuid \ getgid \ gethostname \ + getline \ getifaddrs \ getipnodebyaddr \ getipnodebyname \ diff --git a/lib/roken/Makefile.am b/lib/roken/Makefile.am index 8350d7034..0a2d850d5 100644 --- a/lib/roken/Makefile.am +++ b/lib/roken/Makefile.am @@ -276,7 +276,9 @@ EXTRA_DIST = \ dirent-test.c \ dlfcn.hin \ dlfcn_w32.c \ + getdelim.c \ getifaddrs_w32.c \ + getline.c \ ndbm_wrap.c \ ndbm_wrap.h \ rename.c \ diff --git a/lib/roken/NTMakefile b/lib/roken/NTMakefile index 5749efd5c..55b7a5605 100644 --- a/lib/roken/NTMakefile +++ b/lib/roken/NTMakefile @@ -60,6 +60,8 @@ libroken_la_OBJS = \ $(OBJ)\fseeko.obj \ $(OBJ)\ftello.obj \ $(OBJ)\getauxval.obj \ + $(OBJ)\getdelim.obj \ + $(OBJ)\getline.obj \ $(OBJ)\getaddrinfo_hostspec.obj \ $(OBJ)\get_window_size.obj \ $(OBJ)\getarg.obj \ diff --git a/lib/roken/getdelim.c b/lib/roken/getdelim.c new file mode 100644 index 000000000..7a224ff8c --- /dev/null +++ b/lib/roken/getdelim.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 James E. Ingram + * Copyright (c) 2024 Heimdal Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Implementation of the getdelim() function from POSIX 2008. + * + * getdelim() reads from a stream until a specified delimiter is encountered. + * + * See: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html + * + * NOTE: It is always the caller's responsibility to free the line buffer, even + * when an error occurs. + */ + +#include + +#include +#include +#include + +#include "roken.h" + +#ifndef SSIZE_MAX +#define SSIZE_MAX ((ssize_t)(SIZE_MAX / 2)) +#endif + +#define GETDELIM_MINLEN 16 /* minimum line buffer size */ +#define GETDELIM_MAXLEN 65536 /* maximum line buffer size */ + +#ifndef HAVE_GETDELIM + +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +getdelim(char **lineptr, size_t *n, int delimiter, FILE *stream) +{ + char *buf, *pos; + int c; + ssize_t bytes; + size_t read; + + if (lineptr == NULL || n == NULL) { + errno = EINVAL; + return -1; + } + if (stream == NULL) { + errno = EBADF; + return -1; + } + + + /* read characters until delimiter is found, end of file is reached, or an + error occurs. */ + read = 0; + bytes = 0; + buf = *lineptr; + pos = buf; + while ((c = getc(stream)) != EOF) { + if (bytes + 1 >= SSIZE_MAX) { + errno = ERANGE; + return -1; + } + read++; + bytes++; + if (*n < GETDELIM_MINLEN || read >= *n - 2 /* 1 for the delimiter, one for a NUL */) { + size_t newsz = *n + (GETDELIM_MINLEN + ((*n) >> 1)); + + /* + * Better than an overflow check like (size_t)SSIZE_MAX - newsz < + * newsz. Obviously we assume larger than 16-bit architectures. + */ + if (newsz > GETDELIM_MAXLEN) { + errno = ERANGE; + return -1; + } + buf = realloc(*lineptr, newsz); + if (buf == NULL) { + /* ENOMEM */ + return -1; + } + *n = newsz; + pos = buf + bytes - 1; + *lineptr = buf; + } + + *pos++ = (char)c; + if (c == delimiter) { + *pos = '\0'; + return bytes; + } + } + + if (ferror(stream) || (feof(stream) && (bytes == 0))) { + /* EOF, or an error from getc(). */ + return -1; + } + + *pos = '\0'; + return bytes; +} + +#endif /* !HAVE_GETDELIM */ diff --git a/lib/roken/getline.c b/lib/roken/getline.c new file mode 100644 index 000000000..1fabb4df9 --- /dev/null +++ b/lib/roken/getline.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 James E. Ingram + * Copyright (c) 2024 Heimdal Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Implementation of the getline() function from POSIX 2008. + * + * getline() reads from a stream until a newline is encountered. + * + * See: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html + * + * NOTE: It is always the caller's responsibility to free the line buffer, even + * when an error occurs. + */ + +#include + +#include + +#include "roken.h" + +#ifndef HAVE_GETLINE + +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +getline(char **lineptr, size_t *n, FILE *stream) +{ + return getdelim(lineptr, n, '\n', stream); +} + +#endif /* !HAVE_GETLINE */ diff --git a/lib/roken/roken.h.in b/lib/roken/roken.h.in index 2487515de..5d8cac61c 100644 --- a/lib/roken/roken.h.in +++ b/lib/roken/roken.h.in @@ -663,6 +663,16 @@ ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL strsep(char**, const char*); ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL strsep_copy(const char**, const char*, char*, size_t); #endif +#ifndef HAVE_GETDELIM +#define getdelim rk_getdelim +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL getdelim(char**, size_t*, int, FILE*); +#endif + +#ifndef HAVE_GETLINE +#define getline rk_getline +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL getline(char**, size_t*, FILE*); +#endif + #ifndef HAVE_STRCASECMP #define strcasecmp rk_strcasecmp ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL strcasecmp(const char *, const char *); diff --git a/lib/roken/version-script.map b/lib/roken/version-script.map index 33adff512..61c0a4b05 100644 --- a/lib/roken/version-script.map +++ b/lib/roken/version-script.map @@ -73,9 +73,11 @@ HEIMDAL_ROKEN_2.0 { rk_getaddrinfo; rk_getauxv; rk_getauxval; + rk_getdelim; rk_getifaddrs; rk_getipnodebyaddr; rk_getipnodebyname; + rk_getline; rk_getnameinfo; rk_getprogname; rk_getpwnam_r;