rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

text_reader.c (6152B)


      1 /* Copyright (C) 2013-2023, 2025 Vincent Forest (vaplv@free.fr)
      2  *
      3  * The RSys library is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published
      5  * by the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * The RSys library is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "dynamic_array_char.h"
     17 #include "mem_allocator.h"
     18 #include "ref_count.h"
     19 #include "str.h"
     20 #include "text_reader.h"
     21 
     22 /* #chars to add to the line buffer when there is no sufficient space to store
     23  * the text */
     24 #define CHUNK 32
     25 
     26 struct txtrdr {
     27   FILE* stream; /* Stream of the text to read */
     28   struct str name; /* Stream name */
     29   size_t nlines; /* # read lines */
     30   struct darray_char line; /* Buffer storing the read line */
     31 
     32   /* String of chars from which the remaining line chars are skipped */
     33   char reject[4];
     34 
     35   /* Boolean defining if the stream is internally managed or not, i.e. if it
     36    * has to be closed on text_reader release or not */
     37   int manage_stream;
     38 
     39   struct mem_allocator* allocator;
     40   ref_T ref;
     41 };
     42 
     43 /*******************************************************************************
     44  * Helper functions
     45  ******************************************************************************/
     46 static void
     47 release_txtrdr(ref_T* ref)
     48 {
     49   struct txtrdr* txtrdr = NULL;
     50   ASSERT(ref);
     51   txtrdr = CONTAINER_OF(ref, struct txtrdr, ref);
     52   str_release(&txtrdr->name);
     53   darray_char_release(&txtrdr->line);
     54   if(txtrdr->stream && txtrdr->manage_stream) fclose(txtrdr->stream);
     55   MEM_RM(txtrdr->allocator, txtrdr);
     56 }
     57 
     58 /*******************************************************************************
     59  * Text reader API
     60  ******************************************************************************/
     61 res_T
     62 txtrdr_stream
     63   (struct mem_allocator* mem_allocator,
     64    FILE* stream,
     65    const char* name,
     66    const char comment,
     67    struct txtrdr** out_txtrdr)
     68 {
     69   struct mem_allocator* allocator = NULL;
     70   struct txtrdr* txtrdr = NULL;
     71   res_T res = RES_OK;
     72   ASSERT(stream && out_txtrdr);
     73 
     74   allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
     75   txtrdr = MEM_CALLOC(allocator, 1, sizeof(*txtrdr));
     76   if(!txtrdr) {
     77     res = RES_MEM_ERR;
     78     goto error;
     79   }
     80   ref_init(&txtrdr->ref);
     81   str_init(allocator, &txtrdr->name);
     82   darray_char_init(allocator, &txtrdr->line);
     83   txtrdr->allocator = allocator;
     84   txtrdr->stream = stream;
     85   txtrdr->nlines = 0;
     86   txtrdr->reject[0] = '\n';
     87   txtrdr->reject[1] = '\r';
     88   txtrdr->reject[2] = comment;
     89   txtrdr->reject[3] = '\0'; /* Finalize the string */
     90 
     91   res = str_set(&txtrdr->name, name ? name : "<null>");
     92   if(res != RES_OK) goto error;
     93 
     94   res = darray_char_resize(&txtrdr->line, CHUNK);
     95   if(res != RES_OK) goto error;
     96 
     97 exit:
     98   *out_txtrdr = txtrdr;
     99   return res;
    100 error:
    101   if(txtrdr) {
    102     txtrdr_ref_put(txtrdr);
    103     txtrdr = NULL;
    104   }
    105   goto exit;
    106 }
    107 
    108 res_T
    109 txtrdr_file
    110   (struct mem_allocator* mem_allocator,
    111    const char* filename,
    112    const char comment,
    113    struct txtrdr** out_txtrdr)
    114 {
    115   FILE* fp = NULL;
    116   res_T res = RES_OK;
    117   ASSERT(filename);
    118 
    119   fp = fopen(filename, "r");
    120   if(!fp) { res = RES_IO_ERR; goto error; }
    121 
    122   res = txtrdr_stream(mem_allocator, fp, filename, comment, out_txtrdr);
    123   if(res != RES_OK) goto error;
    124 
    125   (*out_txtrdr)->manage_stream = 1;
    126 
    127 exit:
    128   return res;
    129 error:
    130   if(fp) fclose(fp);
    131   goto exit;
    132 }
    133 
    134 void
    135 txtrdr_ref_get(struct txtrdr* txtrdr)
    136 {
    137   ASSERT(txtrdr);
    138   ref_get(&txtrdr->ref);
    139 }
    140 
    141 void
    142 txtrdr_ref_put(struct txtrdr* txtrdr)
    143 {
    144   ASSERT(txtrdr);
    145   ref_put(&txtrdr->ref, release_txtrdr);
    146 }
    147 
    148 res_T
    149 txtrdr_read_line(struct txtrdr* txtrdr)
    150 {
    151   char* str = NULL;
    152   res_T res = RES_OK;
    153 
    154   if(!txtrdr) {
    155     res = RES_BAD_ARG;
    156     goto error;
    157   }
    158 
    159   do {
    160     /* Read the line */
    161     str = fgets
    162       (darray_char_data_get(&txtrdr->line),
    163        (int)darray_char_size_get(&txtrdr->line),
    164        txtrdr->stream);
    165     if(!str) {
    166       if(ferror(txtrdr->stream)) {
    167         res = RES_IO_ERR;
    168         goto error;
    169       } else {
    170         darray_char_clear(&txtrdr->line);
    171         break;
    172       }
    173     }
    174 
    175     /* Ensure tht the whole line is read */
    176     while(!strrchr(DARRAY_BUF(&txtrdr->line), '\n') && !feof(txtrdr->stream)) {
    177       const size_t sz = darray_char_size_get(&txtrdr->line);
    178       char* more = NULL;
    179 
    180       /* Resize the line buffer */
    181       res = darray_char_resize(&txtrdr->line, sz+CHUNK);
    182       if(res != RES_OK) goto error;
    183 
    184       /* Read the remaing chars */
    185       more = darray_char_data_get(&txtrdr->line) + sz-1/*null char*/;
    186       str = fgets(more, CHUNK+1/*previous null char*/, txtrdr->stream);
    187       if(!str && ferror(txtrdr->stream)) {
    188         res = RES_IO_ERR;
    189         goto error;
    190       }
    191     }
    192 
    193     /* Remove new line & comments */
    194     str = darray_char_data_get(&txtrdr->line);
    195     str[strcspn(str, txtrdr->reject)] = '\0';
    196 
    197     ++txtrdr->nlines; /* Increment the number of read lines */
    198 
    199   } while(strspn(str, " \t") == strlen(str));/*Keep going if the line is Empty*/
    200 
    201 exit:
    202   return res;
    203 error:
    204   goto exit;
    205 }
    206 
    207 char*
    208 txtrdr_get_line(struct txtrdr* txtrdr)
    209 {
    210   ASSERT(txtrdr);
    211   return darray_char_size_get(&txtrdr->line) == 0
    212     ? NULL : darray_char_data_get(&txtrdr->line);
    213 }
    214 
    215 const char*
    216 txtrdr_get_cline(const struct txtrdr* txtrdr)
    217 {
    218   ASSERT(txtrdr);
    219   return darray_char_size_get(&txtrdr->line) == 0
    220     ? NULL : darray_char_cdata_get(&txtrdr->line);
    221 }
    222 
    223 size_t
    224 txtrdr_get_line_num(const struct txtrdr* txtrdr)
    225 {
    226   ASSERT(txtrdr);
    227   return txtrdr->nlines;
    228 }
    229 
    230 void
    231 txtrdr_set_line_num(struct txtrdr* txtrdr, const size_t nlines)
    232 {
    233   ASSERT(txtrdr);
    234   txtrdr->nlines = nlines;
    235 }
    236 
    237 const char*
    238 txtrdr_get_name(const struct txtrdr* txtrdr)
    239 {
    240   ASSERT(txtrdr);
    241   return str_cget(&txtrdr->name);
    242 }
    243 
    244 FILE*
    245 txtrdr_get_stream(const struct txtrdr* txtrdr)
    246 {
    247   ASSERT(txtrdr);
    248   return txtrdr->stream;
    249 }