docs/Writerside/topics/properties.h.md

Sun, 07 Dec 2025 19:36:51 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 07 Dec 2025 19:36:51 +0100
changeset 1556
afdaa70034f8
parent 1555
8972247f54e8
child 1557
03fbf1c99e73
permissions
-rw-r--r--

add cxJsonFromString() - resolves #771

# Properties

The UCX properties parser can be used to parse line-based key/value strings. 

## Supported Syntax

Key/value pairs must be line-based and separated by a single character delimiter.
The parser supports up to three different characters which introduce comments.
All characters starting with a comment character up to the end of the line are ignored.
Blank lines are also ignored.

An example properties file looks like this:

```Ini
# Comment line at start of file
key1 = value1
key2 = value2
# next is a blank line and will be ignored

  keys_are_trimmed  =    and_so_are_values   # also a comment
```

> Delimiter and comment characters are configured with the  `CxPropertiesConfig` structure.
> There is also a field reserved for `continuation` which will be used as a line continuation character
> in a future version of UCX.
> In UCX 3.1 this is not implemented.

## Basic Parsing

The following listing shows the properties-parser API.

> To simplify documentation, we introduce the pseudo-type `AnyStr` with the meaning that
> any UCX string and any C string are supported.
> The implementation is actually hidden behind a macro which uses `cx_strcast()` to guarantee compatibility.
{style="note"}

```C
#include <cx/properties.h>

typedef struct cx_properties_config_s {
    char delimiter;
    char comment1;
    char comment2;
    char comment3;
    // reserved for future use - not implemented in UCX 3.1
    char continuation;
} CxPropertiesConfig;

void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);

void cxPropertiesInitDefault(CxProperties *prop);

void cxPropertiesDestroy(CxProperties *prop);

void cxPropertiesReset(CxProperties *prop);

int cxPropertiesFilln(CxProperties *prop,
        const char *buf, size_t len);

int cxPropertiesFill(CxProperties *prop, AnyStr string);

CxPropertiesStatus cxPropertiesNext(CxProperties *prop,
        cxstring *key, cxstring *value);

void cxPropertiesUseStack(CxProperties *prop,
        char *buf, size_t capacity);

CxPropertiesStatus cxPropertiesLoad(CxPropertiesConfig config,
        AnyStr filename, CxMap *target);

CxPropertiesStatus cxPropertiesLoadDefault(
        AnyStr filename, CxMap *target);
```

The first step is to initialize a `CxProperties` structure with a call to `cxPropertiesInit()` using the desired config.
The shorthand `cxPropertiesInitDefault()` creates a default configuration with the equals sign `'='` as delimiter
and the hash-symbol `'#'` as comment symbol (the other two comment symbols remain unused in the default config).

> In a future UCX version, the default `continuation` character will be a backslash `'\'`.
> In UCX 3.1 this feature is not implemented, yet.

The actual parsing is an interleaving invocation of the `cxPropertiesFill()` (or `cxPropertiesFilln()`) and `cxPropertiesNext()` functions.
The `cxPropertiesFill()` function is a convenience function, that accepts UCX strings and normal zero-terminated C strings and behaves otherwise like `cxPropertiesFilln()`.

Filling the input buffer is cost-free if there is no data already in the input buffer.
In that case, the input buffer only stores the pointer to the original data without creating a copy.
Calling `cxPropertiesNext()` will return with `CX_PROPERTIES_NO_ERROR` (= zero) for each key/value-pair that is successfully parsed,
and stores the pointers and lengths for both the key and the value into the structures pointed to by the `key` and `value` arguments.

When all the data from the input buffer was successfully consumed, `cxPropertiesNext()` returns `CX_PROPERTIES_NO_DATA`.

> This is all still free of any copies and allocations.
> That means, the pointers in `key` and `value` after `cxPropertiesNext()` returns will point into the input buffer.
> If you intend to store the key and/or the value somewhere else, it is strongly recommended to create a copy with `cx_strdup()`,
> because you will otherwise soon end up with a dangling pointer.
> {style="note"}

If `cxPropertiesNext()` returns `CX_PROPERTIES_INCOMPLETE_DATA` it means that the input buffer is exhausted,
but the last line did not contain a full key/value pair.
In that case, you can call `cxPropertiesFill()` again to add more data and continue with `cxPropertiesNext()`.

Note that adding more data to a non-empty input buffer will lead to an allocation,
unless you specified some stack memory with `cxPropertiesUseStack()`.
The stack capacity must be large enough to contain the longest line in your data.
If the internal buffer is not large enough to contain a single line, it is extended.
If that is not possible for some reason, `cxPropertiesNext()` fails and returns `CX_PROPERTIES_BUFFER_ALLOC_FAILED`. 

If you want to reuse a `CxProperties` structure with the same config, you can call `cxPropertiesReset()`, even if the last operation was a failure.
Otherwise, you should always call `cxPropertiesDestroy()` when you are done with the parser.

> It is strongly recommended to always call `cxPropertiesDestroy()` when you are done with the parser,
> even if you did not expect any allocations because you used `cxPropertiesUseStack()`.

All the above operations are combined in the function `cxPropertiesLoad()`,
which opens the file designated by the `filename` and loads all properties from that file into the specified `CxMap`.
The convenience macro `cxPropertiesLoadDefault()` uses the default parser configuration for this.
The target map must either store pointers of type `char*` or elements of type `cxmutstr`.

> The stack buffers used by `cxPropertiesLoad()` can be changed when building UCX from sources
> by setting the `CX_PROPERTIES_LOAD_FILL_SIZE` and `CX_PROPERTIES_LOAD_BUF_SIZE` macros
> (see [](install.md#small-buffer-optimizations)).

### List of Status Codes

Below is a full list of status codes for `cxPropertiesNext()` and `cxPropertiesLoad()`.

| Status Code                             | Meaning                                                                                                                                                                                                                 |
|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CX_PROPERTIES_NO_ERROR                  | A key/value pair was found and returned.                                                                                                                                                                                |
| CX_PROPERTIES_NO_DATA                   | The input buffer does not contain data.                                                                                                                                                                                 |
| 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. |
| CX_PROPERTIES_NULL_INPUT                | The input buffer was never initialized. Probably you forgot to call `cxPropertiesFill()` at least once.                                                                                                                 |
| CX_PROPERTIES_INVALID_EMPTY_KEY         | Only white-spaces were found on the left hand-side of the delimiter. Keys must not be empty.                                                                                                                            |
| CX_PROPERTIES_INVALID_MISSING_DELIMITER | A line contains data, but no delimiter.                                                                                                                                                                                 |
| CX_PROPERTIES_BUFFER_ALLOC_FAILED       | More internal buffer was needed, but could not be allocated.                                                                                                                                                            |
| CX_PROPERTIES_FILE_ERROR                | A file operation failed (only for `cxPropertiesLoad()`).                                                                                                                                                                |

For `cxPropertiesLoad()` the status code `CX_PROPERTIES_NO_ERROR` means that at least one property was loaded into the map,
while `CX_PROPERTIES_NO_DATA` means that the file is syntactically fine but does not contain any properties.

<seealso>
<category ref="apidoc">
<a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a>
</category>
</seealso>

mercurial