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