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 }