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