X-Git-Url: http://git.draconx.ca/gitweb/dxcommon.git/blobdiff_plain/9efff8c7a48509fabab57631f5c64c669183dee0..27f6a9c3d4663286f1bec753fbd035ab1a6eb5de:/src/getline.h?ds=sidebyside diff --git a/src/getline.h b/src/getline.h new file mode 100644 index 0000000..b0f4999 --- /dev/null +++ b/src/getline.h @@ -0,0 +1,121 @@ +/* + * Copyright © 2024 Nick Bowler + * + * getline-like function which removes trailing newline (if any). + * + * If HAVE_GETLINE is not defined (or defined to 0) then a standard C + * implementation is used. Othewrise, the POSIX getline function + * is called. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef DX_GETLINE_H_ +#define DX_GETLINE_H_ + +#include +#include +#include +#include + +/* + * Size of the initial buffer allocated internally by the fallback + * implementation when *linebuf is NULL. + */ +#ifndef DX_GETLINE_INITIAL_ALLOC +# define DX_GETLINE_INITIAL_ALLOC 75 +#endif + +enum { + DX_GETLINE_OK = 1, + DX_GETLINE_EOF = 0, + DX_GETLINE_ERROR = -1, + DX_GETLINE_ENOMEM = -2 +}; + +/* + * Wrapper around getline with standard C fallback. + * + * Note that for portability to some getline implementations (e.g., FreeBSD) + * both *linebuf and *n should be set to zero on the initial call. + * + * If pre-allocating a buffer, ensure that its size is more than 1 byte, + * otherwise AIX 7.2 getline fails to work correctly. + * + * Returns 1 (DX_GETLINE_OK) if a line was read or 0 (DX_GETLINE_EOF) if + * no line could be read because the end of file was reached. + * + * On failure, returns a negative value. If the C library input call failed + * then the return value is DX_GETLINE_ERROR and the reason for the failure + * should be available in errno. + * + * For the standard C fallback only, a return value of DX_GETLINE_ENOMEM + * indicates that the buffer allocation could not be expanded to fit the + * input line. + */ +static inline int dx_getline(char **linebuf, size_t *n, FILE *f) +{ +#if HAVE_GETLINE + ssize_t rc; + + if ((rc = getline(linebuf, n, f)) < 0) { + if (ferror(f)) + return DX_GETLINE_ERROR; + return DX_GETLINE_EOF; + } + + if (rc-- && (*linebuf)[rc] == '\n') + (*linebuf)[rc] = '\0'; + + return DX_GETLINE_OK; +#else + char *work = *linebuf; + size_t pos = 0; + size_t sz; + + if (!work) { + sz = DX_GETLINE_INITIAL_ALLOC; + goto initial_alloc; + } + + for (sz = *n;;) { + if (!fgets(&work[pos], sz - pos, f)) { + if (ferror(f)) + return DX_GETLINE_ERROR; + + return pos ? DX_GETLINE_OK : DX_GETLINE_ERROR; + } + + pos += strlen(&work[pos]); + if (work[pos-1] == '\n') { + work[pos-1] = '\0'; + return DX_GETLINE_OK; + } + + if (sz > INT_MAX/2 || sz > ((size_t)-1)/4) + break; + + sz = ((sz*4) + 2) / 3; +initial_alloc: + work = realloc(work, sz); + if (!work) + break; + *linebuf = work; + *n = sz; + } + + return DX_GETLINE_ENOMEM; +#endif +} + +#endif