+/*
+ * getline.c -- replacement getline() implementation
+ *
+ * This code is Copyright (c) 2016, by the authors of nmh. See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else /* ! HAVE_STDBOOL_H */
+# define bool int
+# define true 1
+# define false 0
+#endif /* ! HAVE_STDBOOL_H */
+
+/* Largest possible size of buffer that allows SSIZE_MAX to be returned
+ * to indicate SSIZE_MAX - 1 characters read before the '\n'. The
+ * additional 1 is for the terminating NUL. */
+#define MAX_AVAIL ((size_t)SSIZE_MAX + 1)
+
+/* Ideal increase in size of line buffer. */
+#define GROWTH 128
+
+ssize_t getline(char **lineptr, size_t *n, FILE *stream)
+{
+ char *l;
+ size_t avail;
+ size_t used;
+ int c;
+ bool last;
+
+ if (!lineptr || !n) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ l = *lineptr;
+ if (l)
+ avail = *n <= MAX_AVAIL ? *n : MAX_AVAIL;
+ else
+ avail = *n = 0; /* POSIX allows *lineptr = NULL, *n = 42. */
+ used = 0;
+ last = false;
+ for (;;) {
+ c = getc(stream);
+ if (c == EOF) {
+ if (ferror(stream) || /* errno set by stdlib. */
+ !used) /* EOF with nothing read. */
+ return -1;
+ /* Line will be returned without a \n terminator. */
+append_nul:
+ c = '\0';
+ last = true;
+ }
+
+ if (used == avail) {
+ size_t want;
+ char *new;
+
+ if (avail == MAX_AVAIL) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ want = avail + GROWTH;
+ if (want > MAX_AVAIL)
+ want = MAX_AVAIL;
+ new = realloc(l, want);
+ if (!new)
+ return -1; /* errno set by stdlib. */
+ l = *lineptr = new;
+ avail = *n = want;
+ }
+ l[used++] = c;
+
+ if (last)
+ return used - 1; /* Don't include NUL. */
+
+ if (c == '\n')
+ goto append_nul; /* Final half loop. */
+ }
+}