Home | History | Annotate | Download | only in utils
      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