1 #ifndef _BABELTRACE_COMPAT_STDIO_H
2 #define _BABELTRACE_COMPAT_STDIO_H
3 
4 /*
5  * Copyright (C) 2015 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include <stdio.h>
27 #include <errno.h>
28 #include <assert.h>
29 
30 #define BT_GETLINE_MINBUFLEN	64
31 
32 static inline
_bt_getline_bufalloc(char ** lineptr,size_t * n,size_t linelen)33 char * _bt_getline_bufalloc(char **lineptr, size_t *n, size_t linelen)
34 {
35 	size_t buflen = *n;
36 	char *buf = *lineptr;
37 
38 	if (buflen >= linelen && buf != NULL) {
39 		return buf;
40 	}
41 	if (buf == NULL) {
42 		buflen = BT_GETLINE_MINBUFLEN;
43 	} else {
44 		buflen = buflen << 1;
45 		if (buflen < BT_GETLINE_MINBUFLEN) {
46 			buflen = BT_GETLINE_MINBUFLEN;
47 		}
48 	}
49 	/* Check below not strictly needed, extra safety. */
50 	if (buflen < linelen) {
51 		buflen = linelen;
52 	}
53 	buf = realloc(buf, buflen);
54 	if (!buf) {
55 		errno = ENOMEM;
56 		return NULL;
57 	}
58 	*n = buflen;
59 	*lineptr = buf;
60 	return buf;
61 }
62 
63 /*
64  * Returns line length (including possible final \n, excluding final
65  * \0). On end of file, returns -1 with nonzero feof(stream) and errno
66  * set to 0. On error, returns -1 with errno set.
67  *
68  * This interface is similar to the getline(3) man page part of the
69  * Linux man-pages project, release 3.74. One major difference from the
70  * Open Group POSIX specification is that this implementation does not
71  * necessarily set the ferror() flag on error (because it is internal to
72  * libc).
73  */
74 static inline
bt_getline(char ** lineptr,size_t * n,FILE * stream)75 ssize_t bt_getline(char **lineptr, size_t *n, FILE *stream)
76 {
77 	size_t linelen = 0;
78 	char *buf;
79 	int found_eof = 0;
80 
81 	if (lineptr == NULL || n == NULL) {
82 		errno = EINVAL;
83 		return -1;
84 	}
85 	for (;;) {
86 		char c;
87 		int ret;
88 
89 		ret = fgetc(stream);
90 		if (ret == EOF) {
91 			if (ferror(stream)) {
92 				/* ferror() is set, errno set by fgetc(). */
93 				return -1;
94 			}
95 			assert(feof(stream));
96 			found_eof = 1;
97 			break;
98 		}
99 		c = (char) ret;
100 		if (linelen == SSIZE_MAX) {
101 			errno = EOVERFLOW;
102 			return -1;
103 		}
104 		buf = _bt_getline_bufalloc(lineptr, n, ++linelen);
105 		if (!buf) {
106 			return -1;
107 		}
108 		buf[linelen - 1] = c;
109 		if (c == '\n') {
110 			break;
111 		}
112 	}
113 	if (!linelen && found_eof) {
114 		/* feof() is set. */
115 		errno = 0;
116 		return -1;
117 	}
118 	buf = _bt_getline_bufalloc(lineptr, n, ++linelen);
119 	if (!buf) {
120 		return -1;
121 	}
122 	buf[linelen - 1] = '\0';
123 	return linelen - 1;	/* Count don't include final \0. */
124 }
125 
126 #endif /* _BABELTRACE_COMPAT_STDIO_H */
127