• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2011 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 ** GNU General Public License for more details.
11 */
12 #include <errno.h>
13 #include "android/utils/system.h"
14 #include "android/utils/assert.h"
15 #include "android/utils/lineinput.h"
16 
17 struct LineInput {
18     char*   line;
19     size_t  line_size;
20     int     line_num;
21     int     error;
22     int     eof;
23 
24     struct {
25         FILE*  file;
26     } std;
27 
28     char    line0[128];
29 };
30 
31 /* Error codes returned by the internal line reading function(s) */
32 enum {
33     LINEINPUT_ERROR = -1,
34     LINEINPUT_EOF = -2,
35 };
36 
37 
38 static LineInput*
_lineInput_new(void)39 _lineInput_new( void )
40 {
41     LineInput*  input;
42 
43     ANEW0(input);
44     input->line      = input->line0;
45     input->line_size = sizeof(input->line0);
46 
47     return input;
48 }
49 
50 /* Create a LineInput object that reads from a FILE* object */
51 LineInput*
lineInput_newFromStdFile(FILE * file)52 lineInput_newFromStdFile( FILE* file )
53 {
54     LineInput* input = _lineInput_new();
55 
56     input->std.file = file;
57     return input;
58 }
59 
60 /* Grow the line buffer a bit */
61 static void
_lineInput_grow(LineInput * input)62 _lineInput_grow( LineInput* input )
63 {
64     char*  line;
65 
66     input->line_size += input->line_size >> 1;
67     line = input->line;
68     if (line == input->line0)
69         line = NULL;
70 
71     AARRAY_RENEW(line, input->line_size);
72     input->line = line;
73 }
74 
75 /* Forward declaration */
76 static int _lineInput_getLineFromStdFile( LineInput* input, FILE* file );
77 
78 const char*
lineInput_getLine(LineInput * input)79 lineInput_getLine( LineInput* input )
80 {
81     return lineInput_getLineAndSize(input, NULL);
82 }
83 
84 const char*
lineInput_getLineAndSize(LineInput * input,size_t * pSize)85 lineInput_getLineAndSize( LineInput* input, size_t *pSize )
86 {
87     int ret;
88 
89     /* be safe */
90     if (pSize)
91         *pSize = 0;
92 
93     /* check parameters */
94     if (input == NULL) {
95         errno = EINVAL;
96         return NULL;
97     }
98 
99     /* check state */
100     if (input->error) {
101         return NULL;
102     }
103     if (input->eof) {
104         return NULL;
105     }
106 
107     ret = _lineInput_getLineFromStdFile(input, input->std.file);
108     if (ret >= 0) {
109         input->line_num += 1;
110         if (pSize != NULL) {
111             *pSize = ret;
112             return input->line;
113         }
114         return input->line;
115     }
116     if (ret == LINEINPUT_EOF) {
117         input->line_num += 1;
118         input->eof = 1;
119         return NULL;
120     }
121     if (ret == LINEINPUT_ERROR) {
122         input->error = errno;
123         return NULL;
124     }
125     AASSERT_UNREACHED();
126     return NULL;
127 }
128 
129 /* Returns the number of the last line read by lineInput_getLine */
130 int
lineInput_getLineNumber(LineInput * input)131 lineInput_getLineNumber( LineInput* input )
132 {
133     return input->line_num;
134 }
135 
136 /* Returns TRUE iff the end of file was reached */
137 int
lineInput_isEof(LineInput * input)138 lineInput_isEof( LineInput* input )
139 {
140     return (input->eof != 0);
141 }
142 
143 /* Return the error condition of a LineInput object.
144  * These are standard errno code for the last operation.
145  * Note: EOF corresponds to 0 here.
146  */
147 int
lineInput_getError(LineInput * input)148 lineInput_getError( LineInput* input )
149 {
150     return input->error;
151 }
152 
153 void
lineInput_free(LineInput * input)154 lineInput_free( LineInput* input )
155 {
156     if (input != NULL) {
157         if (input->line != NULL) {
158             if (input->line != input->line0)
159                 AFREE(input->line);
160             input->line = NULL;
161             input->line_size = 0;
162         }
163         AFREE(input);
164     }
165 }
166 
167 
168 /* Internal function used to read a new line from a FILE* using fgets().
169  * We assume that this is more efficient than calling fgetc() in a loop.
170  *
171  * Return length of line, or either LINEINPUT_EOF / LINEINPUT_ERROR
172  */
173 static int
_lineInput_getLineFromStdFile(LineInput * input,FILE * file)174 _lineInput_getLineFromStdFile( LineInput* input, FILE* file )
175 {
176     int   offset = 0;
177     char* p;
178 
179     input->line[0] = '\0';
180 
181     for (;;) {
182         char* buffer = input->line + offset;
183         int   avail  = input->line_size - offset;
184 
185         if (!fgets(buffer, avail, file)) {
186             /* We either reached the end of file or an i/o error occured.
187              * If we already read line data, just return it this time.
188              */
189             if (offset > 0) {
190                 return offset;
191             }
192             goto INPUT_ERROR;
193         }
194 
195         /* Find the terminating zero */
196         p = memchr(buffer, '\0', avail);
197         AASSERT(p != NULL);
198 
199         if (p == buffer) {
200             /* This happens when the file has an embedded '\0', treat it
201              * as an eof, or bad things usually happen after that. */
202             input->eof = 1;
203             if (offset > 0)
204                 return offset;
205             else
206                 return LINEINPUT_EOF;
207         }
208 
209         if (p[-1] != '\n' && p[-1] != '\r') {
210             /* This happens when the line is longer than our current buffer,
211             * so grow its size and try again. */
212             offset = p - input->line;
213             _lineInput_grow(input);
214             continue;
215         }
216 
217         break;
218     }
219 
220     /* Get rid of trailing newline(s). Consider: \n, \r, and \r\n */
221     if (p[-1] == '\n') {
222         p -= 1;
223         if (p > input->line && p[-1] == '\r') {
224             p -= 1;
225         }
226         p[0] = '\0';
227     }
228     else if (p[-1] == '\r') {
229         p -= 1;
230         p[0] = '\0';
231     }
232 
233     /* We did it */
234     return (p - input->line);
235 
236 INPUT_ERROR:
237     if (feof(file)) {
238         input->eof = 1;
239         return LINEINPUT_EOF;
240     }
241     input->error = errno;
242     return LINEINPUT_ERROR;
243 }
244 
245