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* 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* 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 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* 79 lineInput_getLine( LineInput* input ) 80 { 81 return lineInput_getLineAndSize(input, NULL); 82 } 83 84 const char* 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 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 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 148 lineInput_getError( LineInput* input ) 149 { 150 return input->error; 151 } 152 153 void 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 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