| 103 Otherwise, you should always call `cxPropertiesDestroy()` when you are done with the parser. |
109 Otherwise, you should always call `cxPropertiesDestroy()` when you are done with the parser. |
| 104 |
110 |
| 105 > It is strongly recommended to always call `cxPropertiesDestroy()` when you are done with the parser, |
111 > It is strongly recommended to always call `cxPropertiesDestroy()` when you are done with the parser, |
| 106 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`. |
112 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`. |
| 107 |
113 |
| |
114 All the above operations are combined in the function `cxPropertiesLoad()`, |
| |
115 which opens the file designated by the `filename` and loads all properties from that file into the specified `CxMap`. |
| |
116 The convenience macro `cxPropertiesLoadDefault()` uses the default parser configuration for this. |
| |
117 The target map must either store pointers of type `char*` or elements of type `cxmutstr`. |
| |
118 |
| |
119 > The stack buffers used by `cxPropertiesLoad()` can be changed when building UCX from sources |
| |
120 > by setting the `CX_PROPERTIES_LOAD_FILL_SIZE` and `CX_PROPERTIES_LOAD_BUF_SIZE` macros |
| |
121 > (see [](install.md#small-buffer-optimizations)). |
| |
122 |
| 108 ### List of Status Codes |
123 ### List of Status Codes |
| 109 |
124 |
| 110 Below is a full list of status codes for `cxPropertiesNext()`. |
125 Below is a full list of status codes for `cxPropertiesNext()` and `cxPropertiesLoad()`. |
| 111 |
126 |
| 112 | Status Code | Meaning | |
127 | Status Code | Meaning | |
| 113 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
128 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 114 | CX_PROPERTIES_NO_ERROR | A key/value pair was found and returned. | |
129 | CX_PROPERTIES_NO_ERROR | A key/value pair was found and returned. | |
| 115 | CX_PROPERTIES_NO_DATA | The input buffer does not contain more data. | |
130 | CX_PROPERTIES_NO_DATA | The input buffer does not contain data. | |
| 116 | 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. | |
131 | 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. | |
| 117 | CX_PROPERTIES_NULL_INPUT | The input buffer was never initialized. Probably you forgot to call `cxPropertiesFill()` at least once. | |
132 | CX_PROPERTIES_NULL_INPUT | The input buffer was never initialized. Probably you forgot to call `cxPropertiesFill()` at least once. | |
| 118 | CX_PROPERTIES_INVALID_EMPTY_KEY | Only white-spaces were found on the left hand-side of the delimiter. Keys must not be empty. | |
133 | CX_PROPERTIES_INVALID_EMPTY_KEY | Only white-spaces were found on the left hand-side of the delimiter. Keys must not be empty. | |
| 119 | CX_PROPERTIES_INVALID_MISSING_DELIMITER | A line contains data, but no delimiter. | |
134 | CX_PROPERTIES_INVALID_MISSING_DELIMITER | A line contains data, but no delimiter. | |
| 120 | CX_PROPERTIES_BUFFER_ALLOC_FAILED | More internal buffer was needed, but could not be allocated. | |
135 | CX_PROPERTIES_BUFFER_ALLOC_FAILED | More internal buffer was needed, but could not be allocated. | |
| |
136 | CX_PROPERTIES_FILE_ERROR | A file operation failed (only for `cxPropertiesLoad()`). | |
| 121 |
137 |
| 122 |
138 For `cxPropertiesLoad()` the status code `CX_PROPERTIES_NO_ERROR` means that at least one property was loaded into the map, |
| 123 ## Sources and Sinks |
139 while `CX_PROPERTIES_NO_DATA` means that the file is syntactically fine but does not contain any properties. |
| 124 |
|
| 125 ```C |
|
| 126 #include <cx/properties.h> |
|
| 127 |
|
| 128 CxPropertiesSource |
|
| 129 cxPropertiesStringSource(cxstring str); |
|
| 130 |
|
| 131 CxPropertiesSource |
|
| 132 cxPropertiesCstrSource(const char *str); |
|
| 133 |
|
| 134 CxPropertiesSource |
|
| 135 cxPropertiesCstrnSource(const char *str, size_t len); |
|
| 136 |
|
| 137 CxPropertiesSource |
|
| 138 cxPropertiesFileSource(FILE *file, size_t chunk_size); |
|
| 139 |
|
| 140 CxPropertiesSink |
|
| 141 cxPropertiesMapSink(CxMap *map); |
|
| 142 |
|
| 143 CxPropertiesStatus |
|
| 144 cxPropertiesLoad(CxProperties *prop, |
|
| 145 CxPropertiesSink sink, CxPropertiesSource source); |
|
| 146 ``` |
|
| 147 |
|
| 148 The basic idea of `cxPropertiesLoad()` is that key/value-pairs are extracted from a _source_ and ingested by a _sink_. |
|
| 149 For the most common scenarios where properties data is read from a string or a file and put into a map, several functions are available. |
|
| 150 But you can specify your [own sources and sinks](#creating-own-sources-and-sinks), as well. |
|
| 151 |
|
| 152 The following example shows a simple function which loads all properties data from a file. |
|
| 153 The `chunk_size` argument when creating the file source specifies |
|
| 154 how many bytes are read from the file and filled into the properties parser in one read/sink cycle. |
|
| 155 |
|
| 156 ```C |
|
| 157 #include <stdio.h> |
|
| 158 #include <cx/properties.h> |
|
| 159 |
|
| 160 int load_props_from_file(const char *filename, CxMap *map) { |
|
| 161 FILE *f = fopen(filename, "r"); |
|
| 162 if (!f) return -1; |
|
| 163 CxProperties prop; |
|
| 164 cxPropertiesInitDefault(&prop); |
|
| 165 CxPropertiesSink sink = cxPropertiesMapSink(map); |
|
| 166 CxPropertiesSource src = cxPropertiesFileSource(f, 512); |
|
| 167 CxPropertiesStatus status = cxPropertiesLoad(&prop, sink, src); |
|
| 168 fclose(f); |
|
| 169 return status; |
|
| 170 } |
|
| 171 |
|
| 172 // usage: |
|
| 173 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); |
|
| 174 if (load_props_from_file("my-props.properties", map)) { |
|
| 175 // error handling |
|
| 176 } else { |
|
| 177 // assuming my-props.properties contains the following line: |
|
| 178 // my-key = some value |
|
| 179 char *value = cxMapGet(map, "my-key"); |
|
| 180 } |
|
| 181 ``` |
|
| 182 |
|
| 183 > The function `cxPropertiesLoad()` should usually not return `CX_PROPERTIES_INCOMPLETE_DATA` because the parser is automatically refilled from the source. |
|
| 184 > If it does, it could mean that the source was unable to provide all the data, or the properties data ended unexpectedly. |
|
| 185 > The most expected status code is `CX_PROPERTIES_NO_ERROR` which means that at least one key/value-pair was found. |
|
| 186 > If `cxPropertiesLoad()` returns `CX_PROPERTIES_NO_DATA` it means that the source did not provide any key/value-pair. |
|
| 187 > There are several special status codes that are documented [below](#additional-status-codes). |
|
| 188 |
|
| 189 ### Creating own Sources and Sinks |
|
| 190 |
|
| 191 ```C |
|
| 192 #include <cx/properties.h> |
|
| 193 |
|
| 194 typedef int(*cx_properties_read_init_func)(CxProperties *prop, |
|
| 195 CxPropertiesSource *src); |
|
| 196 |
|
| 197 typedef int(*cx_properties_read_func)(CxProperties *prop, |
|
| 198 CxPropertiesSource *src, cxstring *target); |
|
| 199 |
|
| 200 typedef void(*cx_properties_read_clean_func)(CxProperties *prop, |
|
| 201 CxPropertiesSource *src); |
|
| 202 |
|
| 203 typedef int(*cx_properties_sink_func)(CxProperties *prop, |
|
| 204 CxPropertiesSink *sink, cxstring key, cxstring value); |
|
| 205 |
|
| 206 typedef struct cx_properties_source_s { |
|
| 207 void *src; |
|
| 208 void *data_ptr; |
|
| 209 size_t data_size; |
|
| 210 cx_properties_read_func read_func; |
|
| 211 cx_properties_read_init_func read_init_func; |
|
| 212 cx_properties_read_clean_func read_clean_func; |
|
| 213 } CxPropertiesSource; |
|
| 214 |
|
| 215 typedef struct cx_properties_sink_s { |
|
| 216 void *sink; |
|
| 217 void *data; |
|
| 218 cx_properties_sink_func sink_func; |
|
| 219 } CxPropertiesSink; |
|
| 220 ``` |
|
| 221 |
|
| 222 You can create your own sources and sinks by initializing the respective structures. |
|
| 223 |
|
| 224 For a source, only the `read_func` is mandatory, the other two functions are optional and used for initialization and cleanup, if required. |
|
| 225 The file source created by `cxPropertiesFileSource()`, for example, |
|
| 226 uses the `read_init_func` to allocate, and the `read_clean_func` to free the read buffer, respectively. |
|
| 227 |
|
| 228 Since the default map sink created by `cxPropertiesMapSink()` stores `char*` pointers into a map, |
|
| 229 the following example uses a different sink, which stores them as `cxmutstr` values, automatically freeing them |
|
| 230 when the map gets destroyed. |
|
| 231 And instead of reading the data from a file with `fread()`, it uses `mmap()` to map the file into memory for reading. |
|
| 232 |
|
| 233 ```C |
|
| 234 #include <stdio.h> |
|
| 235 #include <unistd.h> |
|
| 236 #include <fcntl.h> |
|
| 237 #include <sys/stat.h> |
|
| 238 #include <sys/mman.h> |
|
| 239 #include <cx/properties.h> |
|
| 240 #include <cx/hash_map.h> |
|
| 241 |
|
| 242 static int prop_mmap(CxProperties *prop, CxPropertiesSource *src) { |
|
| 243 struct stat s; |
|
| 244 int fd = open(src->src, O_RDONLY); |
|
| 245 if (fd < 0) return -1; |
|
| 246 // re-use the data field to store the fd |
|
| 247 // there are cleaner ways, but this is just for illustration |
|
| 248 src->src = (void*) fd; |
|
| 249 fstat(fd, &s); |
|
| 250 // memory map the entire file |
|
| 251 // and store the address and length in the properties source |
|
| 252 src->data_ptr = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
|
| 253 src->data_size = s.st_size; |
|
| 254 return src->data_ptr == NULL; |
|
| 255 } |
|
| 256 |
|
| 257 static int prop_read(CxProperties *prop, CxPropertiesSource *src, |
|
| 258 cxstring *target) { |
|
| 259 // copy the address and length of the mapped data to the target |
|
| 260 target->ptr = src->data_ptr; |
|
| 261 target->length = src->data_size; |
|
| 262 // set the new size to zero to indicate that there is no more data |
|
| 263 src->data_size = 0; |
|
| 264 return 0; |
|
| 265 } |
|
| 266 |
|
| 267 static void prop_unmap(CxProperties *prop, CxPropertiesSource *src) { |
|
| 268 // unmap the memory and close the file |
|
| 269 munmap(src->data_ptr, src->data_size); |
|
| 270 close((int)src->src); |
|
| 271 } |
|
| 272 |
|
| 273 static int prop_sink(CxProperties *prop, CxPropertiesSink *sink, |
|
| 274 cxstring key, cxstring value) { |
|
| 275 CxMap *map = sink->sink; |
|
| 276 // copy the string and store it into the map |
|
| 277 cxmutstr v = cx_strdup(value); |
|
| 278 int r = cxMapPut(map, key, &v); |
|
| 279 if (r != 0) cx_strfree(&v); |
|
| 280 return r; |
|
| 281 } |
|
| 282 |
|
| 283 int load_props_from_file(const char *filename, CxMap *map) { |
|
| 284 CxProperties prop; |
|
| 285 cxPropertiesInitDefault(&prop); |
|
| 286 CxPropertiesSource src; |
|
| 287 src.src = (void*) filename; |
|
| 288 src.read_init_func = prop_mmap; |
|
| 289 src.read_func = prop_read; |
|
| 290 src.read_clean_func = prop_unmap; |
|
| 291 CxPropertiesSink sink; |
|
| 292 sink.sink = map; |
|
| 293 sink.sink_func = prop_sink; |
|
| 294 return cxPropertiesLoad(&prop, sink, src); |
|
| 295 } |
|
| 296 |
|
| 297 int main() { |
|
| 298 // in contrast to the default map sink, |
|
| 299 // this one here stores the UCX strings by value |
|
| 300 CxMap *map = cxHashMapCreateSimple(sizeof(cxmutstr)); |
|
| 301 |
|
| 302 // automatically free the UCX string when removed from the map |
|
| 303 cxDefineDestructor(map, cx_strfree); |
|
| 304 |
|
| 305 // use our custom load function to load the data from the file |
|
| 306 if (load_props_from_file("my-props.properties", map)) { |
|
| 307 fputs("Error reading properties.\n", stderr); |
|
| 308 return 1; |
|
| 309 } |
|
| 310 |
|
| 311 // output the read key/value pairs for illustration |
|
| 312 CxMapIterator iter = cxMapIterator(map); |
|
| 313 cx_foreach(CxMapEntry *, entry, iter) { |
|
| 314 cxstring k = cx_strn(entry->key->data, entry->key->len); |
|
| 315 cxmutstr *v = entry->value; |
|
| 316 printf("%" CX_PRIstr " = %" CX_PRIstr "\n", |
|
| 317 CX_SFMT(k), CX_SFMT(*v)); |
|
| 318 } |
|
| 319 |
|
| 320 // freeing the map also frees the strings |
|
| 321 // because we have registered cx_strfree() as destructor function |
|
| 322 cxMapFree(map); |
|
| 323 |
|
| 324 return 0; |
|
| 325 } |
|
| 326 ``` |
|
| 327 |
|
| 328 > A cleaner implementation that does not produce a warning for bluntly casting an `int` to a `void*` |
|
| 329 > can be achieved by declaring a struct that contains the information, allocate memory for |
|
| 330 > that struct, and store the pointer in `data_ptr`. |
|
| 331 > For illustrating how properties sources and sinks can be implemented, this was not necessary. |
|
| 332 |
|
| 333 ### Additional Status Codes |
|
| 334 |
|
| 335 For sources and sinks there are three additional special status codes, |
|
| 336 which only appear as return values for `cxPropertiesLoad()`. |
|
| 337 |
|
| 338 | Status Code | Meaning | |
|
| 339 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
|
| 340 | CX_PROPERTIES_READ_INIT_FAILED | Initializing the properties source failed and the `cx_properties_read_init_func` returned non-zero. | |
|
| 341 | CX_PROPERTIES_READ_FAILED | Reading from a properties source failed and the `cx_properties_read_func` returned non-zero. | |
|
| 342 | CX_PROPERTIES_SINK_FAILED | Sinking a key/value-pair failed and the `cx_properties_sink_func` returned non-zero. | |
|
| 343 |
|
| 344 |
140 |
| 345 <seealso> |
141 <seealso> |
| 346 <category ref="apidoc"> |
142 <category ref="apidoc"> |
| 347 <a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a> |
143 <a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a> |
| 348 </category> |
144 </category> |