ucx/cx/properties.h

changeset 11
0aa8cbd7912e
child 16
04c9f8d8f03b
equal deleted inserted replaced
10:80f9d007cb52 11:0aa8cbd7912e
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 character, when appearing at the end of a line, continues that line.
63 * This is '\' by default.
64 */
65 // char continuation; // TODO: line continuation in properties
66
67 /**
68 * The first comment character.
69 * This is '#' by default.
70 */
71 char comment1;
72
73 /**
74 * The second comment character.
75 * This is not set by default.
76 */
77 char comment2;
78
79 /**
80 * The third comment character.
81 * This is not set by default.
82 */
83 char comment3;
84 };
85
86 /**
87 * Typedef for the properties config.
88 */
89 typedef struct cx_properties_config_s CxPropertiesConfig;
90
91 /**
92 * Default properties configuration.
93 */
94 extern const CxPropertiesConfig cx_properties_config_default;
95
96 /**
97 * Status codes for the properties interface.
98 */
99 enum cx_properties_status {
100 /**
101 * Everything is fine.
102 */
103 CX_PROPERTIES_NO_ERROR,
104 /**
105 * The input buffer does not contain more data.
106 */
107 CX_PROPERTIES_NO_DATA,
108 /**
109 * The input ends unexpectedly.
110 *
111 * This either happens when the last line does not terminate with a line
112 * break, or when the input ends with a parsed key but no value.
113 */
114 CX_PROPERTIES_INCOMPLETE_DATA,
115 /**
116 * Not used as a status and never returned by any function.
117 *
118 * You can use this enumerator to check for all "good" status results
119 * by checking if the status is less than \c CX_PROPERTIES_OK.
120 *
121 * A "good" status means, that you can refill data and continue parsing.
122 */
123 CX_PROPERTIES_OK,
124 /**
125 * Input buffer is \c NULL.
126 */
127 CX_PROPERTIES_NULL_INPUT,
128 /**
129 * The line contains a delimiter, but no key.
130 */
131 CX_PROPERTIES_INVALID_EMPTY_KEY,
132 /**
133 * The line contains data, but no delimiter.
134 */
135 CX_PROPERTIES_INVALID_MISSING_DELIMITER,
136 /**
137 * More internal buffer was needed, but could not be allocated.
138 */
139 CX_PROPERTIES_BUFFER_ALLOC_FAILED,
140 /**
141 * Initializing the properties source failed.
142 *
143 * @see cx_properties_read_init_func
144 */
145 CX_PROPERTIES_READ_INIT_FAILED,
146 /**
147 * Reading from a properties source failed.
148 *
149 * @see cx_properties_read_func
150 */
151 CX_PROPERTIES_READ_FAILED,
152 /**
153 * Sinking a k/v-pair failed.
154 *
155 * @see cx_properties_sink_func
156 */
157 CX_PROPERTIES_SINK_FAILED,
158 };
159
160 /**
161 * Typedef for the properties status enum.
162 */
163 typedef enum cx_properties_status CxPropertiesStatus;
164
165 /**
166 * Interface for working with properties data.
167 */
168 struct cx_properties_s {
169 /**
170 * The configuration.
171 */
172 CxPropertiesConfig config;
173
174 /**
175 * The text input buffer.
176 */
177 CxBuffer input;
178
179 /**
180 * Internal buffer.
181 */
182 CxBuffer buffer;
183 };
184
185 /**
186 * Typedef for the properties interface.
187 */
188 typedef struct cx_properties_s CxProperties;
189
190
191 /**
192 * Typedef for a properties sink.
193 */
194 typedef struct cx_properties_sink_s CxPropertiesSink;
195
196 /**
197 * A function that consumes a k/v-pair in a sink.
198 *
199 * The sink could be e.g. a map and the sink function would be calling
200 * a map function to store the k/v-pair.
201 *
202 * @param prop the properties interface that wants to sink a k/v-pair
203 * @param sink the sink
204 * @param key the key
205 * @param value the value
206 * @return zero on success, non-zero when sinking the k/v-pair failed
207 */
208 cx_attr_nonnull
209 typedef int(*cx_properties_sink_func)(
210 CxProperties *prop,
211 CxPropertiesSink *sink,
212 cxstring key,
213 cxstring value
214 );
215
216 /**
217 * Defines a sink for k/v-pairs.
218 */
219 struct cx_properties_sink_s {
220 /**
221 * The sink object.
222 */
223 void *sink;
224 /**
225 * Optional custom data.
226 */
227 void *data;
228 /**
229 * A function for consuming k/v-pairs into the sink.
230 */
231 cx_properties_sink_func sink_func;
232 };
233
234
235 /**
236 * Typedef for a properties source.
237 */
238 typedef struct cx_properties_source_s CxPropertiesSource;
239
240 /**
241 * A function that reads data from a source.
242 *
243 * When the source is depleted, implementations SHALL provide an empty
244 * string in the \p target and return zero.
245 * A non-zero return value is only permitted in case of an error.
246 *
247 * The meaning of the optional parameters is implementation-dependent.
248 *
249 * @param prop the properties interface that wants to read from the source
250 * @param src the source
251 * @param target a string buffer where the read data shall be stored
252 * @return zero on success, non-zero when reading data failed
253 */
254 cx_attr_nonnull
255 typedef int(*cx_properties_read_func)(
256 CxProperties *prop,
257 CxPropertiesSource *src,
258 cxstring *target
259 );
260
261 /**
262 * A function that may initialize additional memory for the source.
263 *
264 * @param prop the properties interface that wants to read from the source
265 * @param src the source
266 * @return zero when initialization was successful, non-zero otherwise
267 */
268 cx_attr_nonnull
269 typedef int(*cx_properties_read_init_func)(
270 CxProperties *prop,
271 CxPropertiesSource *src
272 );
273
274 /**
275 * A function that cleans memory initialized by the read_init_func.
276 *
277 * @param prop the properties interface that wants to read from the source
278 * @param src the source
279 */
280 cx_attr_nonnull
281 typedef void(*cx_properties_read_clean_func)(
282 CxProperties *prop,
283 CxPropertiesSource *src
284 );
285
286 /**
287 * Defines a properties source.
288 */
289 struct cx_properties_source_s {
290 /**
291 * The source object.
292 *
293 * For example a file stream or a string.
294 */
295 void *src;
296 /**
297 * Optional additional data pointer.
298 */
299 void *data_ptr;
300 /**
301 * Optional size information.
302 */
303 size_t data_size;
304 /**
305 * A function that reads data from the source.
306 */
307 cx_properties_read_func read_func;
308 /**
309 * Optional function that may prepare the source for reading data.
310 */
311 cx_properties_read_init_func read_init_func;
312 /**
313 * Optional function that cleans additional memory allocated by the
314 * read_init_func.
315 */
316 cx_properties_read_clean_func read_clean_func;
317 };
318
319 /**
320 * Initialize a properties interface.
321 *
322 * @param prop the properties interface
323 * @param config the properties configuration
324 * @see cxPropertiesInitDefault()
325 */
326 cx_attr_nonnull
327 void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
328
329 /**
330 * Destroys the properties interface.
331 *
332 * \note Even when you are certain that you did not use the interface in a
333 * way that caused a memory allocation, you should call this function anyway.
334 * Future versions of the library might add features that need additional memory
335 * and you really don't want to search the entire code where you might need
336 * add call to this function.
337 *
338 * @param prop the properties interface
339 */
340 cx_attr_nonnull
341 void cxPropertiesDestroy(CxProperties *prop);
342
343 /**
344 * Destroys and re-initializes the properties interface.
345 *
346 * You might want to use this, to reset the parser after
347 * encountering a syntax error.
348 *
349 * @param prop the properties interface
350 */
351 cx_attr_nonnull
352 static inline void cxPropertiesReset(CxProperties *prop) {
353 CxPropertiesConfig config = prop->config;
354 cxPropertiesDestroy(prop);
355 cxPropertiesInit(prop, config);
356 }
357
358 /**
359 * Initialize a properties parser with the default configuration.
360 *
361 * @param prop the properties interface
362 * @see cxPropertiesInit()
363 */
364 #define cxPropertiesInitDefault(prop) \
365 cxPropertiesInit(prop, cx_properties_config_default)
366
367 /**
368 * Fills the input buffer with data.
369 *
370 * After calling this function, you can parse the data by calling
371 * cxPropertiesNext().
372 *
373 * @remark The properties interface tries to avoid allocations.
374 * When you use this function and cxPropertiesNext() interleaving,
375 * no allocations are performed. However, you must not free the
376 * pointer to the data in that case. When you invoke the fill
377 * function more than once before calling cxPropertiesNext(),
378 * the additional data is appended - inevitably leading to
379 * an allocation of a new buffer and copying the previous contents.
380 *
381 * @param prop the properties interface
382 * @param buf a pointer to the data
383 * @param len the length of the data
384 * @return non-zero when a memory allocation was necessary but failed
385 * @see cxPropertiesFill()
386 */
387 cx_attr_nonnull
388 cx_attr_access_r(2, 3)
389 int cxPropertiesFilln(
390 CxProperties *prop,
391 const char *buf,
392 size_t len
393 );
394
395 #ifdef __cplusplus
396 } // extern "C"
397 cx_attr_nonnull
398 static inline int cxPropertiesFill(
399 CxProperties *prop,
400 cxstring str
401 ) {
402 return cxPropertiesFilln(prop, str.ptr, str.length);
403 }
404
405 cx_attr_nonnull
406 static inline int cxPropertiesFill(
407 CxProperties *prop,
408 cxmutstr str
409 ) {
410 return cxPropertiesFilln(prop, str.ptr, str.length);
411 }
412
413 cx_attr_nonnull
414 cx_attr_cstr_arg(2)
415 static inline int cxPropertiesFill(
416 CxProperties *prop,
417 const char *str
418 ) {
419 return cxPropertiesFilln(prop, str, strlen(str));
420 }
421
422 extern "C" {
423 #else // __cplusplus
424 /**
425 * Fills the input buffer with data.
426 *
427 * After calling this function, you can parse the data by calling
428 * cxPropertiesNext().
429 *
430 * @attention The properties interface tries to avoid allocations.
431 * When you use this function and cxPropertiesNext() interleaving,
432 * no allocations are performed. However, you must not free the
433 * pointer to the data in that case. When you invoke the fill
434 * function more than once before calling cxPropertiesNext(),
435 * the additional data is appended - inevitably leading to
436 * an allocation of a new buffer and copying the previous contents.
437 *
438 * @param prop the properties interface
439 * @param str the text to fill in
440 * @return non-zero when a memory allocation was necessary but failed
441 * @see cxPropertiesFilln()
442 */
443 #define cxPropertiesFill(prop, str) _Generic((str), \
444 cxstring: cx_properties_fill_cxstr, \
445 cxmutstr: cx_properties_fill_mutstr, \
446 char*: cx_properties_fill_str, \
447 const char*: cx_properties_fill_str) \
448 (prop, str)
449
450 /**
451 * @copydoc cxPropertiesFill()
452 */
453 cx_attr_nonnull
454 static inline int cx_properties_fill_cxstr(
455 CxProperties *prop,
456 cxstring str
457 ) {
458 return cxPropertiesFilln(prop, str.ptr, str.length);
459 }
460
461 /**
462 * @copydoc cxPropertiesFill()
463 */
464 cx_attr_nonnull
465 static inline int cx_properties_fill_mutstr(
466 CxProperties *prop,
467 cxmutstr str
468 ) {
469 return cxPropertiesFilln(prop, str.ptr, str.length);
470 }
471
472 /**
473 * @copydoc cxPropertiesFill()
474 */
475 cx_attr_nonnull
476 cx_attr_cstr_arg(2)
477 static inline int cx_properties_fill_str(
478 CxProperties *prop,
479 const char *str
480 ) {
481 return cxPropertiesFilln(prop, str, strlen(str));
482 }
483 #endif
484
485 /**
486 * Specifies stack memory that shall be used as internal buffer.
487 *
488 * @param prop the properties interface
489 * @param buf a pointer to stack memory
490 * @param capacity the capacity of the stack memory
491 */
492 cx_attr_nonnull
493 void cxPropertiesUseStack(
494 CxProperties *prop,
495 char *buf,
496 size_t capacity
497 );
498
499 /**
500 * Retrieves the next key/value-pair.
501 *
502 * This function returns zero as long as there are key/value-pairs found.
503 * If no more key/value-pairs are found, #CX_PROPERTIES_NO_DATA is returned.
504 *
505 * When an incomplete line is encountered, #CX_PROPERTIES_INCOMPLETE_DATA is
506 * returned, and you can add more data with #cxPropertiesFill().
507 *
508 * \remark The incomplete line will be stored in an internal buffer, which is
509 * allocated on the heap, by default. If you want to avoid allocations,
510 * you can specify sufficient space with cxPropertiesUseStack() after
511 * initialization with cxPropertiesInit().
512 *
513 * \attention The returned strings will point into a buffer that might not be
514 * available later. It is strongly recommended to copy the strings for further
515 * use.
516 *
517 * @param prop the properties interface
518 * @param key a pointer to the cxstring that shall contain the property name
519 * @param value a pointer to the cxstring that shall contain the property value
520 * @return the status code as defined above
521 * @see cxPropertiesFill()
522 */
523 cx_attr_nonnull
524 cx_attr_nodiscard
525 CxPropertiesStatus cxPropertiesNext(
526 CxProperties *prop,
527 cxstring *key,
528 cxstring *value
529 );
530
531 /**
532 * Creates a properties sink for an UCX map.
533 *
534 * The values stored in the map will be pointers to strings allocated
535 * by #cx_strdup_a().
536 * The default stdlib allocator will be used, unless you specify a custom
537 * allocator in the optional \c data of the sink.
538 *
539 * @param map the map that shall consume the k/v-pairs.
540 * @return the sink
541 * @see cxPropertiesLoad()
542 */
543 cx_attr_nonnull
544 cx_attr_nodiscard
545 CxPropertiesSink cxPropertiesMapSink(CxMap *map);
546
547 /**
548 * Creates a properties source based on an UCX string.
549 *
550 * @param str the string
551 * @return the properties source
552 * @see cxPropertiesLoad()
553 */
554 cx_attr_nodiscard
555 CxPropertiesSource cxPropertiesStringSource(cxstring str);
556
557 /**
558 * Creates a properties source based on C string with the specified length.
559 *
560 * @param str the string
561 * @param len the length
562 * @return the properties source
563 * @see cxPropertiesLoad()
564 */
565 cx_attr_nonnull
566 cx_attr_nodiscard
567 cx_attr_access_r(1, 2)
568 CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
569
570 /**
571 * Creates a properties source based on a C string.
572 *
573 * The length will be determined with strlen(), so the string MUST be
574 * zero-terminated.
575 *
576 * @param str the string
577 * @return the properties source
578 * @see cxPropertiesLoad()
579 */
580 cx_attr_nonnull
581 cx_attr_nodiscard
582 cx_attr_cstr_arg(1)
583 CxPropertiesSource cxPropertiesCstrSource(const char *str);
584
585 /**
586 * Creates a properties source based on an FILE.
587 *
588 * @param file the file
589 * @param chunk_size how many bytes may be read in one operation
590 *
591 * @return the properties source
592 * @see cxPropertiesLoad()
593 */
594 cx_attr_nonnull
595 cx_attr_nodiscard
596 cx_attr_access_r(1)
597 CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
598
599
600 /**
601 * Loads properties data from a source and transfers it to a sink.
602 *
603 * This function tries to read as much data from the source as possible.
604 * When the source was completely consumed and at least on k/v-pair was found,
605 * the return value will be #CX_PROPERTIES_NO_ERROR.
606 * When the source was consumed but no k/v-pairs were found, the return value
607 * will be #CX_PROPERTIES_NO_DATA.
608 * The other result codes apply, according to their description.
609 *
610 * @param prop the properties interface
611 * @param sink the sink
612 * @param source the source
613 * @return the status of the last operation
614 */
615 cx_attr_nonnull
616 CxPropertiesStatus cxPropertiesLoad(
617 CxProperties *prop,
618 CxPropertiesSink sink,
619 CxPropertiesSource source
620 );
621
622 #ifdef __cplusplus
623 } // extern "C"
624 #endif
625
626 #endif // UCX_PROPERTIES_H

mercurial