UNIXworkcode

1 # Properties 2 3 The UCX properties parser can be used to parse line-based key/value strings. 4 5 ## Supported Syntax 6 7 Key/value pairs must be line-based and separated by a single character delimiter. 8 The parser supports up to three different characters which introduce comments. 9 All characters starting with a comment character up to the end of the line are ignored. 10 Blank lines are also ignored. 11 12 An example properties file looks like this: 13 14 ```Ini 15 # Comment line at start of file 16 key1 = value1 17 key2 = value2 18 # next is a blank line and will be ignored 19 20 keys_are_trimmed = and_so_are_values # also a comment 21 22 key3 = value that \ 23 is continued on the next line 24 ``` 25 26 > Delimiter, line continuation character, and up to three comment characters 27 > can be configured with the `CxPropertiesConfig` structure (see below). 28 29 ## Basic Parsing 30 31 The following listing shows the properties-parser API. 32 33 > To simplify documentation, we introduce the pseudo-type `AnyStr` with the meaning that 34 > any UCX string and any C string are supported. 35 > The implementation is actually hidden behind a macro which uses `cx_strcast()` to guarantee compatibility. 36 {style="note"} 37 38 ```C 39 #include <cx/properties.h> 40 41 typedef struct cx_properties_config_s { 42 char delimiter; 43 char comment1; 44 char comment2; 45 char comment3; 46 char continuation; 47 } CxPropertiesConfig; 48 49 void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config); 50 51 void cxPropertiesInitDefault(CxProperties *prop); 52 53 void cxPropertiesDestroy(CxProperties *prop); 54 55 void cxPropertiesReset(CxProperties *prop); 56 57 int cxPropertiesFilln(CxProperties *prop, 58 const char *buf, size_t len); 59 60 int cxPropertiesFill(CxProperties *prop, AnyStr string); 61 62 CxPropertiesStatus cxPropertiesNext(CxProperties *prop, 63 cxstring *key, cxstring *value); 64 65 void cxPropertiesUseStack(CxProperties *prop, 66 char *buf, size_t capacity); 67 68 CxPropertiesStatus cxPropertiesLoad(CxPropertiesConfig config, 69 const CxAllocator *allocator, 70 AnyStr filename, CxMap *target); 71 72 CxPropertiesStatus cxPropertiesLoadDefault( 73 const CxAllocator *allocator, 74 AnyStr filename, CxMap *target); 75 ``` 76 77 The first step is to initialize a `CxProperties` structure with a call to `cxPropertiesInit()` using the desired config. 78 The shorthand `cxPropertiesInitDefault()` creates a default configuration with the equals sign `'='` as delimiter 79 and the hash-symbol `'#'` as comment symbol (the other two comment symbols remain unused in the default config). 80 The line continuation symbol is set to `'\'`. 81 Note that leading spaces of a continuing line are ignored. 82 83 The actual parsing is an interleaving invocation of the `cxPropertiesFill()` (or `cxPropertiesFilln()`) and `cxPropertiesNext()` functions. 84 The `cxPropertiesFill()` function is a convenience function, that accepts UCX strings and normal zero-terminated C strings and behaves otherwise like `cxPropertiesFilln()`. 85 86 Filling the input buffer is cost-free if there is no data already in the input buffer. 87 In that case, the input buffer only stores the pointer to the original data without creating a copy. 88 Calling `cxPropertiesNext()` will return with `CX_PROPERTIES_NO_ERROR` (= zero) for each key/value-pair that is successfully parsed, 89 and stores the pointers and lengths for both the key and the value into the structures pointed to by the `key` and `value` arguments. 90 91 When all the data from the input buffer was successfully consumed, `cxPropertiesNext()` returns `CX_PROPERTIES_NO_DATA`. 92 93 > This is all still free of any copies and allocations. 94 > That means, the pointers in `key` and `value` after `cxPropertiesNext()` returns will point into the input buffer. 95 > If you intend to store the key and/or the value somewhere else, it is strongly recommended to create a copy with `cx_strdup()`, 96 > because you will otherwise soon end up with a dangling pointer. 97 > {style="note"} 98 99 If `cxPropertiesNext()` returns `CX_PROPERTIES_INCOMPLETE_DATA` it means that the input buffer is exhausted, 100 but the last line did not contain a full key/value pair. 101 In that case, you can call `cxPropertiesFill()` again to add more data and continue with `cxPropertiesNext()`. 102 103 Note that adding more data to a non-empty input buffer will lead to an allocation, 104 unless you specified some stack memory with `cxPropertiesUseStack()`. 105 The stack capacity must be large enough to contain the longest line in your data. 106 If the internal buffer is not large enough to contain a single line, it is extended. 107 If that is not possible for some reason, `cxPropertiesNext()` fails and returns `CX_PROPERTIES_BUFFER_ALLOC_FAILED`. 108 109 If you want to reuse a `CxProperties` structure with the same config, you can call `cxPropertiesReset()`, even if the last operation was a failure. 110 Otherwise, you should always call `cxPropertiesDestroy()` when you are done with the parser. 111 112 > It is strongly recommended to always call `cxPropertiesDestroy()` when you are done with the parser, 113 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`. 114 115 All the above operations are combined in the function `cxPropertiesLoad()`, 116 which opens the file designated by the `filename` and loads all properties from that file into the specified `CxMap`. 117 The convenience macro `cxPropertiesLoadDefault()` uses the default parser configuration for this. 118 The target map must either store pointers of type `char*` or elements of type `cxmutstr`. 119 In either case, the specified `allocator` is used to allocate the memory for the value. 120 If the function encounters an error, all properties that have been added to the map so far stay in the map; there is no rollback. 121 122 > The stack buffers used by `cxPropertiesLoad()` can be changed when building UCX from sources 123 > by setting the `CX_PROPERTIES_LOAD_FILL_SIZE` and `CX_PROPERTIES_LOAD_BUF_SIZE` macros 124 > (see [](install.md#small-buffer-optimizations)). 125 126 ### List of Status Codes 127 128 Below is a full list of status codes for `cxPropertiesNext()` and `cxPropertiesLoad()`. 129 130 | Status Code | Meaning | 131 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 132 | CX_PROPERTIES_NO_ERROR | A key/value pair was found and returned. | 133 | CX_PROPERTIES_NO_DATA | The input buffer does not contain data. | 134 | CX_PROPERTIES_INCOMPLETE_DATA | The input ends unexpectedly. This can happen when the last line does not terminate with a line break, or when the input ends with a parsed key but no value. Use `cxPropertiesFill()` to add more data before retrying. | 135 | CX_PROPERTIES_NULL_INPUT | The input buffer was never initialized. Probably you forgot to call `cxPropertiesFill()` at least once. | 136 | CX_PROPERTIES_INVALID_EMPTY_KEY | Only white-spaces were found on the left hand-side of the delimiter. Keys must not be empty. | 137 | CX_PROPERTIES_INVALID_MISSING_DELIMITER | A line contains data, but no delimiter. | 138 | CX_PROPERTIES_BUFFER_ALLOC_FAILED | More internal buffer was needed, but could not be allocated. | 139 | CX_PROPERTIES_FILE_ERROR | A file operation failed (only for `cxPropertiesLoad()`). | 140 141 For `cxPropertiesLoad()` the status code `CX_PROPERTIES_NO_ERROR` means that at least one property was loaded into the map, 142 while `CX_PROPERTIES_NO_DATA` means that the file is syntactically fine but does not contain any properties. 143 144 <seealso> 145 <category ref="apidoc"> 146 <a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a> 147 </category> 148 </seealso> 149