| 239 assert(cxBufferEof(&prop->input)); |
241 assert(cxBufferEof(&prop->input)); |
| 240 |
242 |
| 241 return CX_PROPERTIES_NO_DATA; |
243 return CX_PROPERTIES_NO_DATA; |
| 242 } |
244 } |
| 243 |
245 |
| 244 static int cx_properties_sink_map( |
246 #ifndef CX_PROPERTIES_LOAD_FILL_SIZE |
| 245 cx_attr_unused CxProperties *prop, |
247 #define CX_PROPERTIES_LOAD_FILL_SIZE 1024 |
| 246 CxPropertiesSink *sink, |
248 #endif |
| 247 cxstring key, |
249 const unsigned cx_properties_load_fill_size = CX_PROPERTIES_LOAD_FILL_SIZE; |
| 248 cxstring value |
250 #ifndef CX_PROPERTIES_LOAD_BUF_SIZE |
| 249 ) { |
251 #define CX_PROPERTIES_LOAD_BUF_SIZE 256 |
| 250 CxMap *map = sink->sink; |
252 #endif |
| 251 CxAllocator *alloc = sink->data; |
253 const unsigned cx_properties_load_buf_size = CX_PROPERTIES_LOAD_BUF_SIZE; |
| 252 cxmutstr v = cx_strdup_a(alloc, value); |
254 |
| 253 int r = cxMapPut(map, key, v.ptr); |
255 CxPropertiesStatus cx_properties_load(CxPropertiesConfig config, |
| 254 if (r != 0) cx_strfree_a(alloc, &v); |
256 cxstring filename, CxMap *target) { |
| 255 return r; |
257 // sanity check for the map |
| 256 } |
258 const bool use_cstring = cxCollectionStoresPointers(target); |
| 257 |
259 if (!use_cstring && cxCollectionElementSize(target) != sizeof(cxmutstr)) { |
| 258 CxPropertiesSink cxPropertiesMapSink(CxMap *map) { |
260 return CX_PROPERTIES_MAP_ERROR; |
| 259 CxPropertiesSink sink; |
261 } |
| 260 sink.sink = map; |
262 |
| 261 sink.data = (void*) cxDefaultAllocator; |
263 // create a duplicate to guarantee zero-termination |
| 262 sink.sink_func = cx_properties_sink_map; |
264 cxmutstr fname = cx_strdup(filename); |
| 263 return sink; |
265 if (fname.ptr == NULL) { |
| 264 } |
266 return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| 265 |
267 } |
| 266 static int cx_properties_read_string( |
268 |
| 267 CxProperties *prop, |
269 // open the file |
| 268 CxPropertiesSource *src, |
270 FILE *f = fopen(fname.ptr, "r"); |
| 269 cxstring *target |
271 if (f == NULL) { |
| 270 ) { |
272 cx_strfree(&fname); |
| 271 if (prop->input.space == src->src) { |
273 return CX_PROPERTIES_FILE_ERROR; |
| 272 // when the input buffer already contains the string |
274 } |
| 273 // we have nothing more to provide |
275 |
| 274 target->length = 0; |
276 // initialize the parser |
| 275 } else { |
277 char linebuf[cx_properties_load_buf_size]; |
| 276 target->ptr = src->src; |
278 char fillbuf[cx_properties_load_fill_size]; |
| 277 target->length = src->data_size; |
|
| 278 } |
|
| 279 return 0; |
|
| 280 } |
|
| 281 |
|
| 282 static int cx_properties_read_file( |
|
| 283 cx_attr_unused CxProperties *prop, |
|
| 284 CxPropertiesSource *src, |
|
| 285 cxstring *target |
|
| 286 ) { |
|
| 287 target->ptr = src->data_ptr; |
|
| 288 target->length = fread(src->data_ptr, 1, src->data_size, src->src); |
|
| 289 return ferror((FILE*)src->src); |
|
| 290 } |
|
| 291 |
|
| 292 static int cx_properties_read_init_file( |
|
| 293 cx_attr_unused CxProperties *prop, |
|
| 294 CxPropertiesSource *src |
|
| 295 ) { |
|
| 296 src->data_ptr = cxMallocDefault(src->data_size); |
|
| 297 if (src->data_ptr == NULL) return 1; |
|
| 298 return 0; |
|
| 299 } |
|
| 300 |
|
| 301 static void cx_properties_read_clean_file( |
|
| 302 cx_attr_unused CxProperties *prop, |
|
| 303 CxPropertiesSource *src |
|
| 304 ) { |
|
| 305 cxFreeDefault(src->data_ptr); |
|
| 306 } |
|
| 307 |
|
| 308 CxPropertiesSource cxPropertiesStringSource(cxstring str) { |
|
| 309 CxPropertiesSource src; |
|
| 310 src.src = (void*) str.ptr; |
|
| 311 src.data_size = str.length; |
|
| 312 src.data_ptr = NULL; |
|
| 313 src.read_func = cx_properties_read_string; |
|
| 314 src.read_init_func = NULL; |
|
| 315 src.read_clean_func = NULL; |
|
| 316 return src; |
|
| 317 } |
|
| 318 |
|
| 319 CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len) { |
|
| 320 CxPropertiesSource src; |
|
| 321 src.src = (void*) str; |
|
| 322 src.data_size = len; |
|
| 323 src.data_ptr = NULL; |
|
| 324 src.read_func = cx_properties_read_string; |
|
| 325 src.read_init_func = NULL; |
|
| 326 src.read_clean_func = NULL; |
|
| 327 return src; |
|
| 328 } |
|
| 329 |
|
| 330 CxPropertiesSource cxPropertiesCstrSource(const char *str) { |
|
| 331 CxPropertiesSource src; |
|
| 332 src.src = (void*) str; |
|
| 333 src.data_size = strlen(str); |
|
| 334 src.data_ptr = NULL; |
|
| 335 src.read_func = cx_properties_read_string; |
|
| 336 src.read_init_func = NULL; |
|
| 337 src.read_clean_func = NULL; |
|
| 338 return src; |
|
| 339 } |
|
| 340 |
|
| 341 CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size) { |
|
| 342 CxPropertiesSource src; |
|
| 343 src.src = file; |
|
| 344 src.data_size = chunk_size; |
|
| 345 src.data_ptr = NULL; |
|
| 346 src.read_func = cx_properties_read_file; |
|
| 347 src.read_init_func = cx_properties_read_init_file; |
|
| 348 src.read_clean_func = cx_properties_read_clean_file; |
|
| 349 return src; |
|
| 350 } |
|
| 351 |
|
| 352 CxPropertiesStatus cxPropertiesLoad( |
|
| 353 CxProperties *prop, |
|
| 354 CxPropertiesSink sink, |
|
| 355 CxPropertiesSource source |
|
| 356 ) { |
|
| 357 assert(source.read_func != NULL); |
|
| 358 assert(sink.sink_func != NULL); |
|
| 359 |
|
| 360 // initialize reader |
|
| 361 if (source.read_init_func != NULL) { |
|
| 362 if (source.read_init_func(prop, &source)) { |
|
| 363 return CX_PROPERTIES_READ_INIT_FAILED; // LCOV_EXCL_LINE |
|
| 364 } |
|
| 365 } |
|
| 366 |
|
| 367 // transfer the data from the source to the sink |
|
| 368 CxPropertiesStatus status; |
279 CxPropertiesStatus status; |
| 369 CxPropertiesStatus kv_status = CX_PROPERTIES_NO_DATA; |
280 CxProperties parser; |
| 370 bool found = false; |
281 cxPropertiesInit(&parser, config); |
| |
282 cxPropertiesUseStack(&parser, linebuf, cx_properties_load_buf_size); |
| |
283 |
| |
284 // read/fill/parse loop |
| |
285 status = CX_PROPERTIES_NO_DATA; |
| 371 while (true) { |
286 while (true) { |
| 372 // read input |
287 size_t r = fread(fillbuf, 1, cx_properties_load_fill_size, f); |
| 373 cxstring input; |
288 if (ferror(f)) { |
| 374 if (source.read_func(prop, &source, &input)) { // LCOV_EXCL_START |
289 status = CX_PROPERTIES_FILE_ERROR; |
| 375 status = CX_PROPERTIES_READ_FAILED; |
|
| 376 break; |
290 break; |
| 377 } // LCOV_EXCL_STOP |
291 } |
| 378 |
292 if (r == 0) { |
| 379 // no more data - break |
293 break; |
| 380 if (input.length == 0) { |
294 } |
| 381 if (found) { |
295 if (cxPropertiesFilln(&parser, fillbuf, r)) { |
| 382 // something was found, check the last kv_status |
296 status = CX_PROPERTIES_BUFFER_ALLOC_FAILED; |
| 383 if (kv_status == CX_PROPERTIES_INCOMPLETE_DATA) { |
297 break; |
| 384 status = CX_PROPERTIES_INCOMPLETE_DATA; |
298 } |
| 385 } else { |
299 cxstring key, value; |
| 386 status = CX_PROPERTIES_NO_ERROR; |
300 while (true) { |
| 387 } |
301 status = cxPropertiesNext(&parser, &key, &value); |
| |
302 if (status != CX_PROPERTIES_NO_ERROR) { |
| |
303 break; |
| 388 } else { |
304 } else { |
| 389 // nothing found |
305 cxmutstr v = cx_strdup(value); |
| 390 status = CX_PROPERTIES_NO_DATA; |
306 if (v.ptr == NULL) { |
| 391 } |
307 status = CX_PROPERTIES_MAP_ERROR; |
| |
308 break; |
| |
309 } |
| |
310 void *mv = use_cstring ? (void*)v.ptr : &v; |
| |
311 if (cxMapPut(target, key, mv)) { |
| |
312 cx_strfree(&v); |
| |
313 status = CX_PROPERTIES_MAP_ERROR; |
| |
314 break; |
| |
315 } |
| |
316 } |
| |
317 } |
| |
318 if (status > CX_PROPERTIES_OK) { |
| 392 break; |
319 break; |
| 393 } |
320 } else if (status == CX_PROPERTIES_NO_DATA) { |
| 394 |
321 // we want to report this case differently in this function |
| 395 // set the input buffer and read the k/v-pairs |
322 status = CX_PROPERTIES_NO_ERROR; |
| 396 cxPropertiesFill(prop, input); |
323 } |
| 397 |
324 } |
| 398 do { |
325 |
| 399 cxstring key, value; |
326 // cleanup and exit |
| 400 kv_status = cxPropertiesNext(prop, &key, &value); |
327 fclose(f); |
| 401 if (kv_status == CX_PROPERTIES_NO_ERROR) { |
328 cxPropertiesDestroy(&parser); |
| 402 found = true; |
329 cx_strfree(&fname); |
| 403 if (sink.sink_func(prop, &sink, key, value)) { |
|
| 404 kv_status = CX_PROPERTIES_SINK_FAILED; // LCOV_EXCL_LINE |
|
| 405 } |
|
| 406 } |
|
| 407 } while (kv_status == CX_PROPERTIES_NO_ERROR); |
|
| 408 |
|
| 409 if (kv_status > CX_PROPERTIES_OK) { |
|
| 410 status = kv_status; |
|
| 411 break; |
|
| 412 } |
|
| 413 } |
|
| 414 |
|
| 415 if (source.read_clean_func != NULL) { |
|
| 416 source.read_clean_func(prop, &source); |
|
| 417 } |
|
| 418 |
|
| 419 return status; |
330 return status; |
| 420 } |
331 } |