src/properties.c

changeset 1555
8972247f54e8
parent 1503
48993e0e0dba
equal deleted inserted replaced
1554:91fb10c89611 1555:8972247f54e8
27 */ 27 */
28 28
29 #include "cx/properties.h" 29 #include "cx/properties.h"
30 30
31 #include <assert.h> 31 #include <assert.h>
32 #include <stdio.h>
33 #include <string.h>
32 34
33 const CxPropertiesConfig cx_properties_config_default = { 35 const CxPropertiesConfig cx_properties_config_default = {
34 '=', 36 '=',
35 '#', 37 '#',
36 '\0', 38 '\0',
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 }

mercurial