]> diplodocus.org Git - nmh/blob - sbr/getline.c
Reverted commit 9a4b4a3d3b27fe4a7ff6d0b8724ce1c06b5917eb.
[nmh] / sbr / getline.c
1 /*
2 * getline.c -- replacement getline() implementation
3 *
4 * This code is Copyright (c) 2016, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <limits.h>
12 #include <errno.h>
13
14 #include <h/mh.h>
15
16 /* Largest possible size of buffer that allows SSIZE_MAX to be returned
17 * to indicate SSIZE_MAX - 1 characters read before the '\n'. The
18 * additional 1 is for the terminating NUL. */
19 #define MAX_AVAIL ((size_t)SSIZE_MAX + 1)
20
21 /* Ideal increase in size of line buffer. */
22 #define GROWTH 128
23
24 ssize_t getline(char **lineptr, size_t *n, FILE *stream)
25 {
26 char *l;
27 size_t avail;
28 size_t used;
29 int c;
30 bool last;
31
32 if (!lineptr || !n) {
33 errno = EINVAL;
34 return -1;
35 }
36
37 l = *lineptr;
38 if (l)
39 avail = *n <= MAX_AVAIL ? *n : MAX_AVAIL;
40 else
41 avail = *n = 0; /* POSIX allows *lineptr = NULL, *n = 42. */
42 used = 0;
43 last = false;
44 for (;;) {
45 c = getc(stream);
46 if (c == EOF) {
47 if (ferror(stream) || /* errno set by stdlib. */
48 !used) /* EOF with nothing read. */
49 return -1;
50 /* Line will be returned without a \n terminator. */
51 append_nul:
52 c = '\0';
53 last = true;
54 }
55
56 if (used == avail) {
57 size_t want;
58 char *new;
59
60 if (avail == MAX_AVAIL) {
61 errno = EOVERFLOW;
62 return -1;
63 }
64 want = avail + GROWTH;
65 if (want > MAX_AVAIL)
66 want = MAX_AVAIL;
67 new = realloc(l, want);
68 if (!new)
69 return -1; /* errno set by stdlib. */
70 l = *lineptr = new;
71 avail = *n = want;
72 }
73 l[used++] = c;
74
75 if (last)
76 return used - 1; /* Don't include NUL. */
77
78 if (c == '\n')
79 goto append_nul; /* Final half loop. */
80 }
81 }