1 /*
2 * spd_getline.c - portable implementation of getline
3 *
4 * Copyright (C) 2010 Brailcom, o.p.s.
5 *
6 * This is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This software is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <glib.h>
30 #include <spd_utils.h>
31
32 #define INITIAL_BUF_SIZE 120
33 #define GETLINE_FAILURE -1
34
35 /*
36 * A portable implementation of getline.
37 * Originally provided by Willie Walker, and modified by Chris Brannon.
38 *
39 * Note: `*lineptr' must be a valid argument that could be passed to the
40 * `g_free()' glib function, i.e. it must be g_malloc'ed, not malloc'ed.
41 * In all other respects, this function behaves just like the POSIX version
42 * of getline.
43 */
44
spd_getline(char ** lineptr,size_t * n,FILE * f)45 ssize_t spd_getline(char **lineptr, size_t * n, FILE * f)
46 {
47 int ch;
48 ssize_t buf_pos = 0;
49 ssize_t needed = 2; /* Always buf_pos + 2 (see below). */
50 size_t new_length = 0;
51 char *temp;
52
53 if ((n == NULL) || (lineptr == NULL) || (f == NULL)) {
54 errno = EINVAL;
55 return GETLINE_FAILURE;
56 }
57
58 if (errno != 0)
59 errno = 0;
60
61 if ((*lineptr == NULL) || (*n == 0)) {
62 *n = INITIAL_BUF_SIZE;
63 *lineptr = g_malloc(*n * sizeof(char));
64
65 if (*lineptr == NULL) {
66 *n = 0;
67 return GETLINE_FAILURE; /* Out of memory. */
68 }
69 }
70
71 /*
72 * For each iteration of this loop, the following holds.
73 * We have read buf_pos bytes of the line. The buffer contains buf_pos
74 * bytes, not including the trailing null byte at the end of the string.
75 * When we read another character, we want to store it, and we also need
76 * enough room for a null byte. So we need to realloc as soon as our
77 * capacity becomes less than buf_pos + 2.
78 * Hence the variable "needed" which always equals buf_pos + 2.
79 */
80
81 while ((ch = getc(f)) != EOF) {
82 if (errno != 0)
83 return GETLINE_FAILURE;
84
85 if (needed > *n) {
86 new_length = *n * 2;
87 if (new_length <= *n) { /* Overflow. */
88 errno = ENOMEM;
89 /* We couldn't store the character, */
90 /* so put it back on the stream. */
91 ungetc(ch, f);
92 return GETLINE_FAILURE;
93 }
94 temp =
95 (char *)g_realloc(*lineptr,
96 new_length * sizeof(char));
97 if (temp == NULL) {
98 ungetc(ch, f);
99 return GETLINE_FAILURE;
100 }
101 *n = new_length;
102 *lineptr = temp;
103 }
104 (*lineptr)[buf_pos++] = ch;
105
106 if (ch == '\n')
107 break;
108
109 if (needed == SSIZE_MAX) {
110 /* We'll overflow ssize_t on the next round. */
111 errno = ENOMEM;
112 return GETLINE_FAILURE;
113 }
114 needed++;
115 }
116 (*lineptr)[buf_pos] = '\0';
117
118 if (buf_pos == 0) {
119 buf_pos = GETLINE_FAILURE;
120 }
121 return buf_pos;
122 }
123