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
17struct 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) */
32enum {
33    LINEINPUT_ERROR = -1,
34    LINEINPUT_EOF = -2,
35};
36
37
38static LineInput*
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 */
51LineInput*
52lineInput_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 */
61static void
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 */
76static int _lineInput_getLineFromStdFile( LineInput* input, FILE* file );
77
78const char*
79lineInput_getLine( LineInput* input )
80{
81    return lineInput_getLineAndSize(input, NULL);
82}
83
84const char*
85lineInput_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 */
130int
131lineInput_getLineNumber( LineInput* input )
132{
133    return input->line_num;
134}
135
136/* Returns TRUE iff the end of file was reached */
137int
138lineInput_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 */
147int
148lineInput_getError( LineInput* input )
149{
150    return input->error;
151}
152
153void
154lineInput_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 */
173static int
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
236INPUT_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