Skip to content
This repository was archived by the owner on Jul 26, 2026. It is now read-only.

Commit 18db0b9

Browse files
phil-baseclaude
andcommitted
Add email example; fix const correctness across all API signatures
- Propagate const char * through all public API parameters (context, value, syntax) and matching internal signatures in attribute.c, element.c, context.c, syntax.c, cfi_json.c — allows callers to pass string literals and const strings without casts - Add examples/email.c: models a multi-part email with indexed recipient nodes, demonstrates BaseCfiLook enumeration, BaseCfiWalk, JSON round-trip - Wire examples/email into Makefile (pattern rule, `make examples`, `make all`) - Update README: lead with the email use case and a motivating snippet; point to examples/email.c for the full runnable version Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent dc27730 commit 18db0b9

16 files changed

Lines changed: 280 additions & 117 deletions

‎Makefile‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,30 @@ DEPS = $(OBJS:.o=.d)
1818
$(LIB): $(OBJS)
1919
ar -rs $@ $^
2020

21-
examples/test1.o: examples/test1.c
21+
examples/%.o: examples/%.c
2222
$(CC) $(CFLAGS) -I. -c -o $@ $<
2323

2424
test1: $(LIB) examples/test1.o
2525
$(CC) $(CFLAGS) -o $@ examples/test1.o $(LIB)
2626

27+
examples/email: $(LIB) examples/email.o
28+
$(CC) $(CFLAGS) -o $@ examples/email.o $(LIB)
29+
2730
check: test1
2831
./test1
2932

3033
memcheck: test1
3134
valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./test1
3235

33-
all: $(LIB) test1
36+
examples: examples/email
37+
38+
all: $(LIB) test1 examples
3439

3540
clean:
36-
$(RM) $(OBJS) $(DEPS) examples/test1.o examples/test1.d
41+
$(RM) $(OBJS) $(DEPS) examples/test1.o examples/test1.d examples/email.o examples/email.d
3742

3843
clobber: clean
39-
$(RM) $(LIB) test1 $(MODULE).pc log.log
44+
$(RM) $(LIB) test1 examples/email $(MODULE).pc log.log
4045

4146
$(MODULE).pc: basecfi.pc.in
4247
sed -e 's|@PREFIX@|$(PREFIX)|g' -e 's|@VERSION@|$(VERSION)|g' $< > $@
@@ -47,4 +52,4 @@ install: $(LIB) $(MODULE).pc
4752
install -m 444 $(HEADERS) $(PREFIX)/include
4853
install -m 444 $(MODULE).pc $(PCDIR)/$(MODULE).pc
4954

50-
.PHONY: all check memcheck clean clobber install
55+
.PHONY: all check memcheck examples clean clobber install

‎README.md‎

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,56 @@
11
# CFI — Common Format Interface
22

3-
A self-contained C99 library for building and querying hierarchical key-value trees in memory. Nodes are addressed by slash-separated paths; each node holds named string attributes. No external dependencies.
3+
A self-contained C library for building and querying hierarchical key-value trees in memory. Nodes are addressed by slash-separated paths; each node holds named string attributes. No external dependencies.
4+
5+
CFI is well-suited to structured message or protocol data: anything where you have
6+
repeated, named sub-elements (headers, parts, recipients) that you want to address
7+
by path and enumerate without knowing the shape upfront.
48

59
## Building
610

711
```sh
8-
make # builds basecfi.a + test binaries
12+
make # builds basecfi.a + example binaries
913
make check # run the test suite
1014
make install # install basecfi.a and headers to /usr/local (override PREFIX=)
1115
```
1216

1317
CI runs on every push against Ubuntu and macOS.
1418

15-
## Quick start
19+
## Example — modelling a multi-part email
1620

1721
```c
18-
#include "basecfi.h"
19-
#include "cfi_json.h" /* for ToJson / FromJson / Copy */
20-
21-
/* create a handle */
22-
void *h = NULL;
23-
BaseCfiCreate(&h, NULL);
24-
BaseCfiGoto(h, "/");
25-
26-
/* build a tree */
27-
BaseCfiAdd(h, "user", 0); /* add child node */
28-
BaseCfiAdd(h, "prefs", 0);
29-
30-
BaseCfiGoto(h, "/user");
31-
BaseCfiSet(h, "name", "Alice"); /* set attribute on current node */
32-
BaseCfiSet(h, "email", "a@example.com");
33-
34-
BaseCfiGoto(h, "/prefs");
35-
BaseCfiSet(h, "theme", "dark");
22+
/* add three recipients as indexed child nodes under /headers */
23+
char *ctx;
24+
25+
ctx = BaseCfiIndexer("to", 0);
26+
BaseCfiAdd(h, ctx, 1); /* create /headers/to[0] and go there */
27+
BaseCfiSet(h, "name", "Bob Smith");
28+
BaseCfiSet(h, "address", "bob@example.com");
29+
BaseCfiGoto(h, "/headers");
30+
free(ctx);
31+
32+
/* ... repeat for to[1], to[2] ... */
33+
34+
/* enumerate recipients without knowing the count upfront */
35+
char **children;
36+
BaseCfiLook(h, "/headers", BASE_CFI_LOOK_CONTEXT, &children);
37+
for (int i = 0; children[i]; i++) {
38+
if (strncmp(children[i], "to[", 3) != 0) continue;
39+
char path[128];
40+
snprintf(path, sizeof(path), "/headers/%s/name", children[i]);
41+
printf("%s\n", BaseCfiValue(h, path, 1));
42+
}
43+
for (int i = 0; children[i]; i++) free(children[i]);
44+
free(children);
3645

37-
/* read back */
38-
char *v;
39-
BaseCfiGet(h, "/user/name", &v); /* v → "Alice"; do NOT free */
46+
/* serialize the whole message to JSON */
47+
char *json = BaseCfiToJson(h, "/");
48+
```
4049
41-
/* list children of root */
42-
char **list;
43-
BaseCfiLook(h, "/", BASE_CFI_LOOK_CONTEXT, &list);
44-
/* list = {"user", "prefs", NULL} — free each element, then the array */
45-
for (int i = 0; list[i]; i++) free(list[i]);
46-
free(list);
50+
A fully runnable version of this example is in [`examples/email.c`](examples/email.c).
51+
Build it with `make examples` (or `make all`) and run `./examples/email`.
4752
48-
BaseCfiDestroy(h);
49-
```
53+
## Quick start
5054
5155
## Paths
5256

‎attribute.c‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ int BaseCfiAttributeDestroy(BaseCfiElement *element)
125125
* Returns BASE_CFI_OK on success. Otherwise returns error status.
126126
*/
127127

128-
int BaseCfiAttributeFindByName(BaseCfiElement *element, char *name, int *offset)
128+
int BaseCfiAttributeFindByName(BaseCfiElement *element, const char *name, int *offset)
129129
{
130130
static char *thisfunc = "BaseCfiAttributeFindByName()";
131131
int i;
@@ -213,7 +213,7 @@ int BaseCfiAttributeDestroyByPosition(BaseCfiElement *element, int position)
213213
* Returns BASE_CFI_OK on success. Otherwise returns error status.
214214
*/
215215

216-
int BaseCfiAttributeDestroyByName(BaseCfiElement *element, char *name)
216+
int BaseCfiAttributeDestroyByName(BaseCfiElement *element, const char *name)
217217
{
218218
static char *thisfunc = "BaseCfiAttributeDestroyByName()";
219219
int status;
@@ -264,7 +264,7 @@ int BaseCfiAttributeDestroyByName(BaseCfiElement *element, char *name)
264264
* Returns BASE_CFI_OK on success. Otherwise returns error status.
265265
*/
266266

267-
int BaseCfiAttributeCreate(BaseCfiElement *element, char *name, char *value)
267+
int BaseCfiAttributeCreate(BaseCfiElement *element, const char *name, const char *value)
268268
{
269269
static char *thisfunc = "BaseCfiAttributeCreate()";
270270
int i;
@@ -340,7 +340,7 @@ int BaseCfiAttributeCreate(BaseCfiElement *element, char *name, char *value)
340340
* Returns BASE_CFI_OK on success. Otherwise returns an error status.
341341
*/
342342

343-
int BaseCfiAttributeSet(BaseCfiElement *element, char *name, char *value, int set)
343+
int BaseCfiAttributeSet(BaseCfiElement *element, const char *name, const char *value, int set)
344344
{
345345
static char *thisfunc = "BaseCfiAttributeSet()";
346346
int status;
@@ -438,7 +438,7 @@ int BaseCfiAttributeSet(BaseCfiElement *element, char *name, char *value, int se
438438
* Returns BASE_CFI_OK on success. Otherwise returns error status.
439439
*/
440440

441-
int BaseCfiAttributeGetByName(BaseCfiElement *element, char *name, char **value)
441+
int BaseCfiAttributeGetByName(BaseCfiElement *element, const char *name, char **value)
442442
{
443443
static char *thisfunc = "BaseCfiAttributeGetByName()";
444444
int i;

‎attribute.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@
2424

2525
// prototypes
2626

27-
int BaseCfiAttributeSet(BaseCfiElement *, char *, char *, int);
28-
int BaseCfiAttributeGetByName(BaseCfiElement *, char *, char **);
27+
int BaseCfiAttributeSet(BaseCfiElement *, const char *, const char *, int);
28+
int BaseCfiAttributeGetByName(BaseCfiElement *, const char *, char **);

‎basecfi.h‎

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
* and not yet implemented; non-NULL values are accepted but ignored.
8383
* *cfihandle: receives an opaque handle on success.
8484
*/
85-
int BaseCfiCreate(void **cfihandle, char *syntax);
85+
int BaseCfiCreate(void **cfihandle, const char *syntax);
8686

8787
/*
8888
* BaseCfiDestroy — free the tree and all its contents.
@@ -99,28 +99,28 @@ int BaseCfiDestroy(void *cfihandle);
9999
* Returns BASE_CFI_CONTEXT_FOUND if the node already exists.
100100
* If gothere is non-zero, the default context is moved to the new node.
101101
*/
102-
int BaseCfiAdd(void *cfihandle, char *context, int gothere);
102+
int BaseCfiAdd(void *cfihandle, const char *context, int gothere);
103103

104104
/*
105105
* BaseCfiDelete — remove the node at context and all of its descendants.
106106
*/
107-
int BaseCfiDelete(void *cfihandle, char *context);
107+
int BaseCfiDelete(void *cfihandle, const char *context);
108108

109109
/*
110110
* BaseCfiSet — set (or replace) an attribute on the node at context.
111111
*
112112
* The library copies value; the caller retains ownership.
113113
* Pass NULL as value to delete the attribute.
114114
*/
115-
int BaseCfiSet(void *cfihandle, char *context, char *value);
115+
int BaseCfiSet(void *cfihandle, const char *context, const char *value);
116116

117117
/*
118118
* BaseCfiExtend — append value to an existing attribute.
119119
*
120120
* Creates the attribute if it does not yet exist.
121121
* The library copies value; the caller retains ownership.
122122
*/
123-
int BaseCfiExtend(void *cfihandle, char *context, char *value);
123+
int BaseCfiExtend(void *cfihandle, const char *context, const char *value);
124124

125125
/*
126126
* BaseCfiGet — retrieve an attribute value.
@@ -130,7 +130,7 @@ int BaseCfiExtend(void *cfihandle, char *context, char *value);
130130
* *value is set to NULL (and BASE_CFI_OK is returned) when the attribute
131131
* or its parent node does not exist.
132132
*/
133-
int BaseCfiGet(void *cfihandle, char *context, char **value);
133+
int BaseCfiGet(void *cfihandle, const char *context, char **value);
134134

135135
/*
136136
* BaseCfiLook — list the direct children of a node.
@@ -144,15 +144,15 @@ int BaseCfiGet(void *cfihandle, char *context, char **value);
144144
* for (int i = 0; (*list)[i]; i++) free((*list)[i]);
145145
* free(*list);
146146
*/
147-
int BaseCfiLook(void *cfihandle, char *context, int type, char ***list);
147+
int BaseCfiLook(void *cfihandle, const char *context, int type, char ***list);
148148

149149
/*
150150
* BaseCfiGoto — set the default context for relative path resolution.
151151
*
152152
* Pass "/" to reset to the root. Subsequent calls that use a relative
153153
* context path resolve from this point.
154154
*/
155-
int BaseCfiGoto(void *cfihandle, char *context);
155+
int BaseCfiGoto(void *cfihandle, const char *context);
156156

157157
/*
158158
* BaseCfiValue — convenience wrapper around BaseCfiGet.
@@ -161,7 +161,7 @@ int BaseCfiGoto(void *cfihandle, char *context);
161161
* If safe is non-zero, returns "" (never NULL) when the attribute is
162162
* missing or an error occurs. If safe is zero, returns NULL on error.
163163
*/
164-
char *BaseCfiValue(void *cfihandle, char *context, int safe);
164+
char *BaseCfiValue(void *cfihandle, const char *context, int safe);
165165

166166
/*
167167
* BaseCfiIndexer — format a context name with an array index.
@@ -170,19 +170,19 @@ char *BaseCfiValue(void *cfihandle, char *context, int safe);
170170
* The caller must free the returned pointer.
171171
* Returns NULL on allocation failure.
172172
*/
173-
char *BaseCfiIndexer(char *context, int index);
173+
char *BaseCfiIndexer(const char *context, int index);
174174

175175
/*
176176
* BaseCfiSetLong — store a long integer as a string attribute.
177177
*/
178-
int BaseCfiSetLong(void *cfihandle, char *context, long value);
178+
int BaseCfiSetLong(void *cfihandle, const char *context, long value);
179179

180180
/*
181181
* BaseCfiValueLong — retrieve an attribute and return it as a long.
182182
*
183183
* Returns 0 when the attribute is absent or cannot be converted.
184184
*/
185-
long BaseCfiValueLong(void *cfihandle, char *context);
185+
long BaseCfiValueLong(void *cfihandle, const char *context);
186186

187187
/*
188188
* Walk API
@@ -217,7 +217,7 @@ typedef int (*BaseCfiWalkFn)(void *cfihandle,
217217
int flags,
218218
void *userdata);
219219

220-
int BaseCfiWalk(void *cfihandle, char *context,
220+
int BaseCfiWalk(void *cfihandle, const char *context,
221221
BaseCfiWalkFn callback, void *userdata);
222222

223223
/*
@@ -227,7 +227,7 @@ int BaseCfiWalk(void *cfihandle, char *context,
227227
* any argument is invalid. Does not write to the error output and does
228228
* not alter the last-error state.
229229
*/
230-
int BaseCfiExists(void *cfihandle, char *context);
230+
int BaseCfiExists(void *cfihandle, const char *context);
231231

232232
/*
233233
* BaseCfiCount — count the direct children and/or attributes of a node.
@@ -236,7 +236,7 @@ int BaseCfiExists(void *cfihandle, char *context);
236236
* BASE_CFI_LOOK_ATTRIBUTE, or both OR'd together.
237237
* Returns the count on success, -1 on error.
238238
*/
239-
int BaseCfiCount(void *cfihandle, char *context, int type);
239+
int BaseCfiCount(void *cfihandle, const char *context, int type);
240240

241241
/*
242242
* BaseCfiCopy — deep-copy the subtree at src_ctx into dst at dst_ctx.
@@ -246,4 +246,4 @@ int BaseCfiCount(void *cfihandle, char *context, int type);
246246
* a name with attributes on src_ctx.
247247
* Returns BASE_CFI_OK on success, an error code otherwise.
248248
*/
249-
int BaseCfiCopy(void *src, char *src_ctx, void *dst, char *dst_ctx);
249+
int BaseCfiCopy(void *src, const char *src_ctx, void *dst, const char *dst_ctx);

‎basecfi.pc‎

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)