Line data Source code
1 : /*!
2 : * \file
3 : * \author Christopher Stender
4 : * \date 2018-02-15
5 : * \version 0.1
6 : *
7 : * \brief Read line from file descriptor
8 : *
9 : * \details
10 : * This readline implementation reads a single line from a file descriptor
11 : * (e.g. a socket). Two versions are provided.
12 : *
13 : *\copyright
14 : * Copyright (c) 2023 Fraunhofer IIS
15 : *
16 : * Permission is hereby granted, free of charge, to any person obtaining a copy
17 : * of this software and associated documentation files (the “Software”), to deal
18 : * in the Software without restriction, including without limitation the rights
19 : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 : * copies of the Software, and to permit persons to whom the Software is
21 : * furnished to do so, subject to the following conditions:
22 : *
23 : * The above copyright notice and this permission notice shall be included in
24 : * all copies or substantial portions of the Software.
25 : *
26 : * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 : * THE SOFTWARE.
33 : */
34 :
35 : #include <stdio.h>
36 : #include <string.h>
37 : #include <stdlib.h>
38 : #include <unistd.h>
39 : #include <errno.h>
40 :
41 : #include "creadline.h"
42 :
43 : /* Do not change the maximum line size here! Define
44 : * CREADLINE_MAX_LINE_SIZE in the header file to
45 : * overwrite the default value. */
46 : #ifndef CREADLINE_MAX_LINE_SIZE
47 : /*! Internal definition of the maximum allowed line size. Can be overwritten by
48 : * defining CREADLINE_MAX_LINE_SIZE. */
49 : #define __MAX_LINEBUF_SIZE__ 64*1024*1024
50 : #else
51 : #define __MAX_LINEBUF_SIZE__ CREADLINE_MAX_LINE_SIZE
52 : #endif
53 :
54 0 : static int strnpos(const char *s, int c, size_t maxlen)
55 : {
56 : int pos;
57 :
58 0 : for (pos = 0; pos < maxlen; pos++) {
59 :
60 0 : if (s[pos] == c)
61 0 : return pos;
62 :
63 0 : if (s[pos] == '\0')
64 0 : return -1;
65 : }
66 :
67 0 : return -1;
68 : }
69 :
70 0 : int creadline_r(int fd, void **dst, creadline_buf_t *buffer)
71 : {
72 : char *linebuf;
73 : size_t linebuf_size;
74 : int linebuf_nbytes;
75 :
76 : int readret;
77 : int nl_pos;
78 : int rem_nbytes;
79 :
80 : /* Initialize internal line buffer */
81 0 : linebuf_nbytes = 0;
82 0 : linebuf_size = CREADLINE_BLOCK_SIZE;
83 0 : linebuf = malloc(linebuf_size);
84 0 : if(linebuf == NULL) {
85 0 : perror("malloc() failed");
86 0 : goto error_ret;
87 : }
88 :
89 : /* Restore remaining characters from the last call */
90 0 : if (buffer->nbytes != 0) {
91 0 : strncpy(linebuf, buffer->buf, buffer->nbytes);
92 0 : linebuf_nbytes = buffer->nbytes;
93 0 : buffer->nbytes = 0;
94 : }
95 :
96 : /* Check if linebuf contains a newline character */
97 0 : nl_pos = strnpos(linebuf, '\n', linebuf_nbytes);
98 :
99 0 : while (nl_pos < 0) {
100 :
101 : /* No newline character found -> read more bytes and check again */
102 :
103 : /* Check if there is enough space left to call read again. If a new
104 : * read could write beyond the line buffer it's size is doubled as long
105 : * as __MAX_LINEBUF_SIZE__ is not reached. */
106 :
107 0 : if (linebuf_nbytes + CREADLINE_BLOCK_SIZE > linebuf_size) {
108 :
109 0 : char* new_linebuf = NULL;
110 0 : int new_linebuf_size = 2*linebuf_size;
111 :
112 0 : if (new_linebuf_size > __MAX_LINEBUF_SIZE__) {
113 0 : fprintf(stderr, "ERROR: Maximum line size of %i bytes exceeded!\n", __MAX_LINEBUF_SIZE__);
114 0 : goto error_free;
115 : }
116 :
117 0 : new_linebuf = realloc(linebuf, new_linebuf_size);
118 0 : if (new_linebuf == NULL) {
119 0 : perror("realloc() failed");
120 0 : goto error_free;
121 : }
122 0 : linebuf = new_linebuf;
123 0 : linebuf_size = new_linebuf_size;
124 : }
125 :
126 : /* Read more bytes... */
127 0 : readret = read(fd, linebuf+linebuf_nbytes, CREADLINE_BLOCK_SIZE);
128 0 : if (readret < 0) {
129 :
130 0 : if (errno == EINTR) { /* EINTR is not an error */
131 0 : continue;
132 : }
133 :
134 0 : perror("read() failed");
135 0 : goto error_free;
136 :
137 0 : } else if (readret == 0) { /* EOF */
138 :
139 0 : if (linebuf_nbytes == 0) {
140 :
141 : /* It is important to set dst to NULL because a return value
142 : * of 0 can also indicate an zero-length string ("\0") */
143 0 : *dst = NULL;
144 0 : free(linebuf);
145 0 : return 0;
146 :
147 : } else {
148 0 : fprintf(stderr, "ERROR: Received EOF while line buffer is not empty\n");
149 0 : goto error_free;
150 : }
151 : }
152 :
153 : /* Check if the read data contains a newline character */
154 0 : nl_pos = strnpos(linebuf+linebuf_nbytes, '\n', readret);
155 :
156 : /* If a newline was found, get its absolute position */
157 0 : if(nl_pos >= 0) {
158 0 : nl_pos += linebuf_nbytes;
159 : }
160 :
161 0 : linebuf_nbytes += readret;
162 : }
163 :
164 : /* Found newline character */
165 :
166 : /* Copy characters located after the newline to the (external) buffer */
167 0 : rem_nbytes = linebuf_nbytes - nl_pos - 1;
168 0 : if (rem_nbytes > 0) {
169 0 : strncpy(buffer->buf, linebuf+nl_pos+1, rem_nbytes);
170 0 : buffer->nbytes = rem_nbytes;
171 : }
172 :
173 : /* Ignore '\r' before '\n' to handle also "\r\n" sequences */
174 0 : if ( (nl_pos > 0) && (linebuf[nl_pos-1] == '\r') ) {
175 0 : nl_pos--;
176 : }
177 :
178 : /* Replace '\n' (or '\r' in front of a '\n') by
179 : * '\0' to terminate the string */
180 0 : linebuf[nl_pos] = '\0';
181 :
182 : /* Set dst pointer and return string length */
183 0 : *dst = linebuf;
184 0 : return nl_pos;
185 :
186 : error_free:
187 0 : free(linebuf);
188 :
189 : error_ret:
190 0 : *dst = NULL;
191 0 : return -1;
192 : }
193 :
194 0 : int creadline(int fd, void **dst)
195 : {
196 : static int fd_last = -1;
197 : static creadline_buf_t buffer;
198 :
199 : /* Reset buffer if a new fd is used */
200 0 : if(fd_last != fd) {
201 0 : buffer.nbytes = 0;
202 0 : fd_last = fd;
203 : }
204 :
205 0 : return creadline_r(fd, dst, &buffer);
206 : }
207 :
|