UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 /** 29 * @file properties.h 30 * @brief Interface for parsing data from properties files. 31 * @author Mike Becker 32 * @author Olaf Wintermann 33 * @copyright 2-Clause BSD License 34 */ 35 36 #ifndef UCX_PROPERTIES_H 37 #define UCX_PROPERTIES_H 38 39 #include "common.h" 40 #include "string.h" 41 #include "map.h" 42 #include "buffer.h" 43 44 #include <stdio.h> 45 #include <string.h> 46 47 #ifdef __cplusplus 48 extern "C" { 49 #endif 50 51 /** 52 * Configures the expected characters for the properties parser. 53 */ 54 struct cx_properties_config_s { 55 /** 56 * The key/value delimiter that shall be used. 57 * This is '=' by default. 58 */ 59 char delimiter; 60 61 /** 62 * The first comment character. 63 * This is '#' by default. 64 */ 65 char comment1; 66 67 /** 68 * The second comment character. 69 * This is not set by default. 70 */ 71 char comment2; 72 73 /** 74 * The third comment character. 75 * This is not set by default. 76 */ 77 char comment3; 78 79 /* 80 * The character, when appearing at the end of a line, continues that line. 81 * This is '\' by default. 82 */ 83 /** 84 * Reserved for future use. 85 */ 86 char continuation; 87 }; 88 89 /** 90 * Typedef for the properties config. 91 */ 92 typedef struct cx_properties_config_s CxPropertiesConfig; 93 94 /** 95 * Default properties configuration. 96 */ 97 cx_attr_export 98 extern const CxPropertiesConfig cx_properties_config_default; 99 100 /** 101 * Status codes for the properties interface. 102 */ 103 enum cx_properties_status { 104 /** 105 * Everything is fine. 106 */ 107 CX_PROPERTIES_NO_ERROR, 108 /** 109 * The input buffer does not contain more data. 110 */ 111 CX_PROPERTIES_NO_DATA, 112 /** 113 * The input ends unexpectedly. 114 * 115 * This either happens when the last line does not terminate with a line 116 * break, or when the input ends with a parsed key but no value. 117 */ 118 CX_PROPERTIES_INCOMPLETE_DATA, 119 /** 120 * Not used as a status and never returned by any function. 121 * 122 * You can use this enumerator to check for all "good" status results 123 * by checking if the status is less than @c CX_PROPERTIES_OK. 124 * 125 * A "good" status means, that you can refill data and continue parsing. 126 */ 127 CX_PROPERTIES_OK, 128 /** 129 * Input buffer is @c NULL. 130 */ 131 CX_PROPERTIES_NULL_INPUT, 132 /** 133 * The line contains a delimiter, but no key. 134 */ 135 CX_PROPERTIES_INVALID_EMPTY_KEY, 136 /** 137 * The line contains data, but no delimiter. 138 */ 139 CX_PROPERTIES_INVALID_MISSING_DELIMITER, 140 /** 141 * More internal buffer was needed, but could not be allocated. 142 */ 143 CX_PROPERTIES_BUFFER_ALLOC_FAILED, 144 /** 145 * Initializing the properties source failed. 146 * 147 * @see cx_properties_read_init_func 148 */ 149 CX_PROPERTIES_READ_INIT_FAILED, 150 /** 151 * Reading from a properties source failed. 152 * 153 * @see cx_properties_read_func 154 */ 155 CX_PROPERTIES_READ_FAILED, 156 /** 157 * Sinking a k/v-pair failed. 158 * 159 * @see cx_properties_sink_func 160 */ 161 CX_PROPERTIES_SINK_FAILED, 162 }; 163 164 /** 165 * Typedef for the properties status enum. 166 */ 167 typedef enum cx_properties_status CxPropertiesStatus; 168 169 /** 170 * Interface for working with properties data. 171 */ 172 struct cx_properties_s { 173 /** 174 * The configuration. 175 */ 176 CxPropertiesConfig config; 177 178 /** 179 * The text input buffer. 180 */ 181 CxBuffer input; 182 183 /** 184 * Internal buffer. 185 */ 186 CxBuffer buffer; 187 }; 188 189 /** 190 * Typedef for the properties interface. 191 */ 192 typedef struct cx_properties_s CxProperties; 193 194 195 /** 196 * Typedef for a properties sink. 197 */ 198 typedef struct cx_properties_sink_s CxPropertiesSink; 199 200 /** 201 * A function that consumes a k/v-pair in a sink. 202 * 203 * The sink could be e.g. a map and the sink function would be calling 204 * a map function to store the k/v-pair. 205 * 206 * @param prop the properties interface that wants to sink a k/v-pair 207 * @param sink the sink 208 * @param key the key 209 * @param value the value 210 * @retval zero success 211 * @retval non-zero sinking the k/v-pair failed 212 */ 213 cx_attr_nonnull 214 typedef int(*cx_properties_sink_func)( 215 CxProperties *prop, 216 CxPropertiesSink *sink, 217 cxstring key, 218 cxstring value 219 ); 220 221 /** 222 * Defines a sink for k/v-pairs. 223 */ 224 struct cx_properties_sink_s { 225 /** 226 * The sink object. 227 */ 228 void *sink; 229 /** 230 * Optional custom data. 231 */ 232 void *data; 233 /** 234 * A function for consuming k/v-pairs into the sink. 235 */ 236 cx_properties_sink_func sink_func; 237 }; 238 239 240 /** 241 * Typedef for a properties source. 242 */ 243 typedef struct cx_properties_source_s CxPropertiesSource; 244 245 /** 246 * A function that reads data from a source. 247 * 248 * When the source is depleted, implementations SHALL provide an empty 249 * string in the @p target and return zero. 250 * A non-zero return value is only permitted in case of an error. 251 * 252 * The meaning of the optional parameters is implementation-dependent. 253 * 254 * @param prop the properties interface that wants to read from the source 255 * @param src the source 256 * @param target a string buffer where the read data shall be stored 257 * @retval zero success 258 * @retval non-zero reading the data failed 259 */ 260 cx_attr_nonnull 261 typedef int(*cx_properties_read_func)( 262 CxProperties *prop, 263 CxPropertiesSource *src, 264 cxstring *target 265 ); 266 267 /** 268 * A function that may initialize additional memory for the source. 269 * 270 * @param prop the properties interface that wants to read from the source 271 * @param src the source 272 * @retval zero initialization was successful 273 * @retval non-zero otherwise 274 */ 275 cx_attr_nonnull 276 typedef int(*cx_properties_read_init_func)( 277 CxProperties *prop, 278 CxPropertiesSource *src 279 ); 280 281 /** 282 * A function that cleans memory initialized by the read_init_func. 283 * 284 * @param prop the properties interface that wants to read from the source 285 * @param src the source 286 */ 287 cx_attr_nonnull 288 typedef void(*cx_properties_read_clean_func)( 289 CxProperties *prop, 290 CxPropertiesSource *src 291 ); 292 293 /** 294 * Defines a properties source. 295 */ 296 struct cx_properties_source_s { 297 /** 298 * The source object. 299 * 300 * For example a file stream or a string. 301 */ 302 void *src; 303 /** 304 * Optional additional data pointer. 305 */ 306 void *data_ptr; 307 /** 308 * Optional size information. 309 */ 310 size_t data_size; 311 /** 312 * A function that reads data from the source. 313 */ 314 cx_properties_read_func read_func; 315 /** 316 * Optional function that may prepare the source for reading data. 317 */ 318 cx_properties_read_init_func read_init_func; 319 /** 320 * Optional function that cleans additional memory allocated by the 321 * read_init_func. 322 */ 323 cx_properties_read_clean_func read_clean_func; 324 }; 325 326 /** 327 * Initialize a properties interface. 328 * 329 * @param prop the properties interface 330 * @param config the properties configuration 331 * @see cxPropertiesInitDefault() 332 */ 333 cx_attr_nonnull 334 cx_attr_export 335 void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config); 336 337 /** 338 * Destroys the properties interface. 339 * 340 * @note Even when you are certain that you did not use the interface in a 341 * way that caused a memory allocation, you should call this function anyway. 342 * Future versions of the library might add features that need additional memory 343 * and you really don't want to search the entire code where you might need 344 * add call to this function. 345 * 346 * @param prop the properties interface 347 */ 348 cx_attr_nonnull 349 cx_attr_export 350 void cxPropertiesDestroy(CxProperties *prop); 351 352 /** 353 * Destroys and re-initializes the properties interface. 354 * 355 * You might want to use this, to reset the parser after 356 * encountering a syntax error. 357 * 358 * @param prop the properties interface 359 */ 360 cx_attr_nonnull 361 static inline void cxPropertiesReset(CxProperties *prop) { 362 CxPropertiesConfig config = prop->config; 363 cxPropertiesDestroy(prop); 364 cxPropertiesInit(prop, config); 365 } 366 367 /** 368 * Initialize a properties parser with the default configuration. 369 * 370 * @param prop (@c CxProperties*) the properties interface 371 * @see cxPropertiesInit() 372 */ 373 #define cxPropertiesInitDefault(prop) \ 374 cxPropertiesInit(prop, cx_properties_config_default) 375 376 /** 377 * Fills the input buffer with data. 378 * 379 * After calling this function, you can parse the data by calling 380 * cxPropertiesNext(). 381 * 382 * @remark The properties interface tries to avoid allocations. 383 * When you use this function and cxPropertiesNext() interleaving, 384 * no allocations are performed. However, you must not free the 385 * pointer to the data in that case. When you invoke the fill 386 * function more than once before calling cxPropertiesNext(), 387 * the additional data is appended - inevitably leading to 388 * an allocation of a new buffer and copying the previous contents. 389 * 390 * @param prop the properties interface 391 * @param buf a pointer to the data 392 * @param len the length of the data 393 * @retval zero success 394 * @retval non-zero a memory allocation was necessary but failed 395 * @see cxPropertiesFill() 396 */ 397 cx_attr_nonnull 398 cx_attr_access_r(2, 3) 399 cx_attr_export 400 int cxPropertiesFilln( 401 CxProperties *prop, 402 const char *buf, 403 size_t len 404 ); 405 406 #ifdef __cplusplus 407 } // extern "C" 408 cx_attr_nonnull 409 static inline int cxPropertiesFill( 410 CxProperties *prop, 411 cxstring str 412 ) { 413 return cxPropertiesFilln(prop, str.ptr, str.length); 414 } 415 416 cx_attr_nonnull 417 static inline int cxPropertiesFill( 418 CxProperties *prop, 419 cxmutstr str 420 ) { 421 return cxPropertiesFilln(prop, str.ptr, str.length); 422 } 423 424 cx_attr_nonnull 425 cx_attr_cstr_arg(2) 426 static inline int cxPropertiesFill( 427 CxProperties *prop, 428 const char *str 429 ) { 430 return cxPropertiesFilln(prop, str, strlen(str)); 431 } 432 433 extern "C" { 434 #else // __cplusplus 435 /** 436 * Fills the input buffer with data. 437 * 438 * After calling this function, you can parse the data by calling 439 * cxPropertiesNext(). 440 * 441 * @attention The properties interface tries to avoid allocations. 442 * When you use this function and cxPropertiesNext() interleaving, 443 * no allocations are performed. However, you must not free the 444 * pointer to the data in that case. When you invoke the fill 445 * function more than once before calling cxPropertiesNext(), 446 * the additional data is appended - inevitably leading to 447 * an allocation of a new buffer and copying the previous contents. 448 * 449 * @param prop the properties interface 450 * @param str the text to fill in 451 * @retval zero success 452 * @retval non-zero a memory allocation was necessary but failed 453 * @see cxPropertiesFilln() 454 */ 455 #define cxPropertiesFill(prop, str) _Generic((str), \ 456 cxstring: cx_properties_fill_cxstr, \ 457 cxmutstr: cx_properties_fill_mutstr, \ 458 char*: cx_properties_fill_str, \ 459 const char*: cx_properties_fill_str) \ 460 (prop, str) 461 462 /** 463 * @copydoc cxPropertiesFill() 464 */ 465 cx_attr_nonnull 466 static inline int cx_properties_fill_cxstr( 467 CxProperties *prop, 468 cxstring str 469 ) { 470 return cxPropertiesFilln(prop, str.ptr, str.length); 471 } 472 473 /** 474 * @copydoc cxPropertiesFill() 475 */ 476 cx_attr_nonnull 477 static inline int cx_properties_fill_mutstr( 478 CxProperties *prop, 479 cxmutstr str 480 ) { 481 return cxPropertiesFilln(prop, str.ptr, str.length); 482 } 483 484 /** 485 * @copydoc cxPropertiesFill() 486 */ 487 cx_attr_nonnull 488 cx_attr_cstr_arg(2) 489 static inline int cx_properties_fill_str( 490 CxProperties *prop, 491 const char *str 492 ) { 493 return cxPropertiesFilln(prop, str, strlen(str)); 494 } 495 #endif 496 497 /** 498 * Specifies stack memory that shall be used as internal buffer. 499 * 500 * @param prop the properties interface 501 * @param buf a pointer to stack memory 502 * @param capacity the capacity of the stack memory 503 */ 504 cx_attr_nonnull 505 cx_attr_export 506 void cxPropertiesUseStack( 507 CxProperties *prop, 508 char *buf, 509 size_t capacity 510 ); 511 512 /** 513 * Retrieves the next key/value-pair. 514 * 515 * This function returns zero as long as there are key/value-pairs found. 516 * If no more key/value-pairs are found, #CX_PROPERTIES_NO_DATA is returned. 517 * 518 * When an incomplete line is encountered, #CX_PROPERTIES_INCOMPLETE_DATA is 519 * returned, and you can add more data with #cxPropertiesFill(). 520 * 521 * @remark The incomplete line will be stored in an internal buffer, which is 522 * allocated on the heap, by default. If you want to avoid allocations, 523 * you can specify sufficient space with cxPropertiesUseStack() after 524 * initialization with cxPropertiesInit(). 525 * 526 * @attention The returned strings will point into a buffer that might not be 527 * available later. It is strongly recommended to copy the strings for further 528 * use. 529 * 530 * @param prop the properties interface 531 * @param key a pointer to the cxstring that shall contain the property name 532 * @param value a pointer to the cxstring that shall contain the property value 533 * @retval CX_PROPERTIES_NO_ERROR (zero) a key/value pair was found 534 * @retval CX_PROPERTIES_NO_DATA there is no (more) data in the input buffer 535 * @retval CX_PROPERTIES_INCOMPLETE_DATA the data in the input buffer is incomplete 536 * (fill more data and try again) 537 * @retval CX_PROPERTIES_NULL_INPUT the input buffer was never filled 538 * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key 539 * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter 540 * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed 541 */ 542 cx_attr_nonnull 543 cx_attr_nodiscard 544 cx_attr_export 545 CxPropertiesStatus cxPropertiesNext( 546 CxProperties *prop, 547 cxstring *key, 548 cxstring *value 549 ); 550 551 /** 552 * Creates a properties sink for an UCX map. 553 * 554 * The values stored in the map will be pointers to strings allocated 555 * by #cx_strdup_a(). 556 * The default stdlib allocator will be used, unless you specify a custom 557 * allocator in the optional @c data of the sink. 558 * 559 * @param map the map that shall consume the k/v-pairs. 560 * @return the sink 561 * @see cxPropertiesLoad() 562 */ 563 cx_attr_nonnull 564 cx_attr_nodiscard 565 cx_attr_export 566 CxPropertiesSink cxPropertiesMapSink(CxMap *map); 567 568 /** 569 * Creates a properties source based on an UCX string. 570 * 571 * @param str the string 572 * @return the properties source 573 * @see cxPropertiesLoad() 574 */ 575 cx_attr_nodiscard 576 cx_attr_export 577 CxPropertiesSource cxPropertiesStringSource(cxstring str); 578 579 /** 580 * Creates a properties source based on C string with the specified length. 581 * 582 * @param str the string 583 * @param len the length 584 * @return the properties source 585 * @see cxPropertiesLoad() 586 */ 587 cx_attr_nonnull 588 cx_attr_nodiscard 589 cx_attr_access_r(1, 2) 590 cx_attr_export 591 CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len); 592 593 /** 594 * Creates a properties source based on a C string. 595 * 596 * The length will be determined with strlen(), so the string MUST be 597 * zero-terminated. 598 * 599 * @param str the string 600 * @return the properties source 601 * @see cxPropertiesLoad() 602 */ 603 cx_attr_nonnull 604 cx_attr_nodiscard 605 cx_attr_cstr_arg(1) 606 cx_attr_export 607 CxPropertiesSource cxPropertiesCstrSource(const char *str); 608 609 /** 610 * Creates a properties source based on an FILE. 611 * 612 * @param file the file 613 * @param chunk_size how many bytes may be read in one operation 614 * 615 * @return the properties source 616 * @see cxPropertiesLoad() 617 */ 618 cx_attr_nonnull 619 cx_attr_nodiscard 620 cx_attr_access_r(1) 621 cx_attr_export 622 CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size); 623 624 625 /** 626 * Loads properties data from a source and transfers it to a sink. 627 * 628 * This function tries to read as much data from the source as possible. 629 * When the source was completely consumed and at least on k/v-pair was found, 630 * the return value will be #CX_PROPERTIES_NO_ERROR. 631 * When the source was consumed but no k/v-pairs were found, the return value 632 * will be #CX_PROPERTIES_NO_DATA. 633 * In case the source data ends unexpectedly, the #CX_PROPERTIES_INCOMPLETE_DATA 634 * is returned. In that case you should call this function again with the same 635 * sink and either an updated source or the same source if the source is able to 636 * yield the missing data. 637 * 638 * The other result codes apply, according to their description. 639 * 640 * @param prop the properties interface 641 * @param sink the sink 642 * @param source the source 643 * @retval CX_PROPERTIES_NO_ERROR (zero) a key/value pair was found 644 * @retval CX_PROPERTIES_READ_INIT_FAILED initializing the source failed 645 * @retval CX_PROPERTIES_READ_FAILED reading from the source failed 646 * @retval CX_PROPERTIES_SINK_FAILED sinking the properties into the sink failed 647 * @retval CX_PROPERTIES_NO_DATA the source did not provide any key/value pairs 648 * @retval CX_PROPERTIES_INCOMPLETE_DATA the source did not provide enough data 649 * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key 650 * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter 651 * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed 652 */ 653 cx_attr_nonnull 654 cx_attr_export 655 CxPropertiesStatus cxPropertiesLoad( 656 CxProperties *prop, 657 CxPropertiesSink sink, 658 CxPropertiesSource source 659 ); 660 661 #ifdef __cplusplus 662 } // extern "C" 663 #endif 664 665 #endif // UCX_PROPERTIES_H 666