1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 The FreeBSD Foundation
5 *
6 * Portions of this software were developed by
7 * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
8 * the FreeBSD Foundation
9 *
10 * Copyright (C) 2026 Elizabeth Ashford.
11 */
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include "util.h"
16
17 #ifndef CLI__SPDXTOOL__SERIALIZE_H
18 #define CLI__SPDXTOOL__SERIALIZE_H
19
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24 typedef enum spdxtool_serialize_type_
25 {
26 SPDXTOOL_SERIALIZE_TYPE_STRING, // JSON string type
27 SPDXTOOL_SERIALIZE_TYPE_INT, // JSON number type (int)
28 SPDXTOOL_SERIALIZE_TYPE_BOOL, // JSON bool type
29 SPDXTOOL_SERIALIZE_TYPE_NULL, // JSON null type
30 SPDXTOOL_SERIALIZE_TYPE_OBJECT, // JSON object type
31 SPDXTOOL_SERIALIZE_TYPE_ARRAY // JSON array type
32 } spdxtool_serialize_type_t;
33
34 typedef struct spdxtool_serialize_value_ {
35 spdxtool_serialize_type_t type;
36 union {
37 char *s;
38 int i;
39 bool b;
40 struct spdxtool_serialize_object_list_ *o;
41 struct spdxtool_serialize_array_ *a;
42 } value;
43 } spdxtool_serialize_value_t;
44
45 typedef struct spdxtool_serialize_object_ {
46 char *key;
47 spdxtool_serialize_value_t *value;
48 } spdxtool_serialize_object_t;
49
50 typedef struct spdxtool_serialize_object_list_ {
51 pkgconf_list_t entries;
52 } spdxtool_serialize_object_list_t;
53
54 typedef struct spdxtool_serialize_array_ {
55 pkgconf_list_t items;
56 } spdxtool_serialize_array_t;
57
58 bool
59 spdxtool_serialize_value_to_buf(pkgconf_buffer_t *buffer, spdxtool_serialize_value_t *value, unsigned int indent);
60
61 spdxtool_serialize_value_t *
62 spdxtool_serialize_value_dup(const spdxtool_serialize_value_t *value);
63
64 spdxtool_serialize_value_t *
65 spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_value_t* value);
66
67 spdxtool_serialize_object_list_t *
68 spdxtool_serialize_object_list_new(void);
69
70 spdxtool_serialize_array_t *
71 spdxtool_serialize_array_new(void);
72
73 spdxtool_serialize_value_t *
74 spdxtool_serialize_array_add_take(spdxtool_serialize_array_t *array, spdxtool_serialize_value_t* value);
75
76 void
77 spdxtool_serialize_value_free(spdxtool_serialize_value_t *value);
78
79 void
80 spdxtool_serialize_object_list_free(spdxtool_serialize_object_list_t *object_list);
81
82 void
83 spdxtool_serialize_object_free(spdxtool_serialize_object_t *object);
84
85 void
86 spdxtool_serialize_array_free(spdxtool_serialize_array_t *array);
87
88 /*
89 * !doc
90 *
91 * .. c:function:: spdxtool_serialize_value_t * spdxtool_serialize_value_string(const char *s)
92 *
93 * Construct a JSON string value. The string is copied internally.
94 * If this return value is not stolen, it must be freed with spdxtool_serialize_value_free().
95 *
96 * :param const char *s: String to copy. May be NULL, in which case the value holds NULL.
97 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_STRING.
98 */
99 static inline spdxtool_serialize_value_t *
spdxtool_serialize_value_string(const char * s)100 spdxtool_serialize_value_string(const char *s)
101 {
102 if (!s)
103 return NULL;
104
105 char *sv = strdup(s);
106 if (!sv)
107 return NULL;
108
109 spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
110 if (!value)
111 {
112 free(sv);
113 return NULL;
114 }
115
116 value->type = SPDXTOOL_SERIALIZE_TYPE_STRING;
117 value->value.s = sv;
118 return value;
119 }
120
121 /*
122 * !doc
123 *
124 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_value_int(int d)
125 *
126 * Construct a JSON integer value.
127 * If this return value is not stolen, it must be freed with spdxtool_serialize_value_free().
128 *
129 * :param int d: int value.
130 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_INT.
131 */
132 static inline spdxtool_serialize_value_t *
spdxtool_serialize_value_int(int i)133 spdxtool_serialize_value_int(int i)
134 {
135 spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
136 if (!value)
137 return NULL;
138
139 value->type = SPDXTOOL_SERIALIZE_TYPE_INT;
140 value->value.i = i;
141 return value;
142 }
143
144 /*
145 * !doc
146 *
147 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_value_bool(bool b)
148 *
149 * Construct a JSON boolean value.
150 * If this return value is not stolen, it must be freed with spdxtool_serialize_value_free().
151 *
152 * :param bool b: Boolean value.
153 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_BOOL.
154 */
155 static inline spdxtool_serialize_value_t *
spdxtool_serialize_value_bool(bool b)156 spdxtool_serialize_value_bool(bool b)
157 {
158 spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
159 if (!value)
160 return NULL;
161
162 value->type = SPDXTOOL_SERIALIZE_TYPE_BOOL;
163 value->value.b = b;
164 return value;
165 }
166
167 /*
168 * !doc
169 *
170 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_value_null(void)
171 *
172 * Construct a JSON null value.
173 * If this return value is not stolen, it must be freed with spdxtool_serialize_value_free().
174 *
175 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_NULL.
176 */
177 static inline spdxtool_serialize_value_t *
spdxtool_serialize_value_null(void)178 spdxtool_serialize_value_null(void)
179 {
180 spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
181 if (!value)
182 return NULL;
183
184 value->type = SPDXTOOL_SERIALIZE_TYPE_NULL;
185 return value;
186 }
187
188
189 /*
190 * !doc
191 *
192 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_value_object(spdxtool_serialize_object_list_t *object_list)
193 *
194 * Construct a JSON object value wrapping an existing object list.
195 * The returned value takes ownership of the object list.
196 * If this return value is not stolen, it must be freed with spdxtool_serialize_value_free().
197 *
198 * :param spdxtool_serialize_object_list_t *object_list: Object list to wrap. Ownership transfers to the returned value.
199 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_OBJECT.
200 */
201 static inline spdxtool_serialize_value_t *
spdxtool_serialize_value_object(spdxtool_serialize_object_list_t * object_list)202 spdxtool_serialize_value_object(spdxtool_serialize_object_list_t *object_list)
203 {
204 spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
205 if (!value)
206 {
207 spdxtool_serialize_object_list_free(object_list);
208 return NULL;
209 }
210
211 value->type = SPDXTOOL_SERIALIZE_TYPE_OBJECT;
212 value->value.o = object_list;
213 return value;
214 }
215
216 /*
217 * !doc
218 *
219 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_value_array(spdxtool_serialize_array_t *array)
220 *
221 * Construct a JSON array value wrapping an existing array.
222 * The returned value takes ownership of the array.
223 * If this return value is not stolen, it must be freed with spdxtool_serialize_value_free().
224 *
225 * :param spdxtool_serialize_array_t *array: Array to wrap. Ownership transfers to the returned value.
226 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_ARRAY.
227 */
228 static inline spdxtool_serialize_value_t *
spdxtool_serialize_value_array(spdxtool_serialize_array_t * array)229 spdxtool_serialize_value_array(spdxtool_serialize_array_t *array)
230 {
231 spdxtool_serialize_value_t *value = calloc(1, sizeof(spdxtool_serialize_value_t));
232 if (!value)
233 {
234 spdxtool_serialize_array_free(array);
235 return NULL;
236 }
237
238 value->type = SPDXTOOL_SERIALIZE_TYPE_ARRAY;
239 value->value.a = array;
240 return value;
241 }
242
243 /*
244 * !doc
245 *
246 * .. c:function:: void spdxtool_serialize_object_add_string(spdxtool_serialize_object_list_t *object_list, const char *key, const char *value)
247 *
248 * Add a string key-value pair to a JSON object. The string is copied internally.
249 * Unconditionally adds the key even if value is NULL.
250 *
251 * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
252 * :param const char *key: Key string.
253 * :param const char *value: String value to copy.
254 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_STRING, located in the object.
255 * This object is not owned by the caller.
256 */
257 static inline spdxtool_serialize_value_t *
spdxtool_serialize_object_add_string(spdxtool_serialize_object_list_t * object_list,const char * key,const char * value)258 spdxtool_serialize_object_add_string(spdxtool_serialize_object_list_t *object_list, const char *key, const char *value)
259 {
260 return spdxtool_serialize_object_add_take(object_list, key, spdxtool_serialize_value_string(value));
261 }
262
263 /*
264 * !doc
265 *
266 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_string_opt(spdxtool_serialize_object_list_t *object_list, const char *key, const char *value)
267 *
268 * Add a string key-value pair to a JSON object only if value is non-NULL.
269 * Use this for optional fields that should be omitted entirely when absent.
270 *
271 * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
272 * :param const char *key: Key string.
273 * :param const char *value: String value to copy, or NULL to skip.
274 * :return: If value is set: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_STRING, located in the object.
275 * This object is not owned by the caller.
276 * If value is not set: NULL.
277 */
278 static inline spdxtool_serialize_value_t *
spdxtool_serialize_object_add_string_opt(spdxtool_serialize_object_list_t * object_list,const char * key,const char * value)279 spdxtool_serialize_object_add_string_opt(spdxtool_serialize_object_list_t *object_list, const char *key, const char *value)
280 {
281 if (value)
282 return spdxtool_serialize_object_add_take(object_list, key, spdxtool_serialize_value_string(value));
283
284 return NULL;
285 }
286
287 /*
288 * !doc
289 *
290 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_int(spdxtool_serialize_object_list_t *object_list, const char *key, int value)
291 *
292 * Add a int key-value pair to a JSON object.
293 *
294 * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
295 * :param const char *key: Key string.
296 * :param int value: Integer value.
297 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_INT, located in the object.
298 * This object is not owned by the caller.
299 */
300 static inline spdxtool_serialize_value_t *
spdxtool_serialize_object_add_int(spdxtool_serialize_object_list_t * object_list,const char * key,int value)301 spdxtool_serialize_object_add_int(spdxtool_serialize_object_list_t *object_list, const char *key, int value)
302 {
303 return spdxtool_serialize_object_add_take(object_list, key, spdxtool_serialize_value_int(value));
304 }
305
306 /*
307 * !doc
308 *
309 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_bool(spdxtool_serialize_object_list_t *object_list, const char *key, bool value)
310 *
311 * Add a boolean key-value pair to a JSON object.
312 *
313 * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
314 * :param const char *key: Key string.
315 * :param bool value: Boolean value.
316 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_BOOL, located in the object.
317 * This object is not owned by the caller.
318 */
319 static inline spdxtool_serialize_value_t *
spdxtool_serialize_object_add_bool(spdxtool_serialize_object_list_t * object_list,const char * key,bool value)320 spdxtool_serialize_object_add_bool(spdxtool_serialize_object_list_t *object_list, const char *key, bool value)
321 {
322 return spdxtool_serialize_object_add_take(object_list, key, spdxtool_serialize_value_bool(value));
323 }
324
325 /*
326 * !doc
327 *
328 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_null(spdxtool_serialize_object_list_t *object_list, const char *key)
329 *
330 * Add a null key-value pair to a JSON object.
331 *
332 * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
333 * :param const char *key: Key string.
334 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_NULL, located in the object.
335 * This object is not owned by the caller.
336 */
337 static inline spdxtool_serialize_value_t *
spdxtool_serialize_object_add_null(spdxtool_serialize_object_list_t * object_list,const char * key)338 spdxtool_serialize_object_add_null(spdxtool_serialize_object_list_t *object_list, const char *key)
339 {
340 return spdxtool_serialize_object_add_take(object_list, key, spdxtool_serialize_value_null());
341 }
342
343 /*
344 * !doc
345 *
346 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_object(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_object_list_t *value)
347 *
348 * Add an object key-value pair to a JSON object.
349 * This takes ownership of the object in value unconditionally, freeing on failure.
350 *
351 * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
352 * :param const char *key: Key string.
353 * :param spdxtool_serialize_object_list_t *value: Object value to add.
354 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_OBJECT, located in the object.
355 * This object is not owned by the caller.
356 */
357 static inline spdxtool_serialize_value_t *
spdxtool_serialize_object_add_object(spdxtool_serialize_object_list_t * object_list,const char * key,spdxtool_serialize_object_list_t * value)358 spdxtool_serialize_object_add_object(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_object_list_t *value)
359 {
360 return spdxtool_serialize_object_add_take(object_list, key, spdxtool_serialize_value_object(value));
361 }
362
363 /*
364 * !doc
365 *
366 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_array(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_array_t *value)
367 *
368 * Add an array key-value pair to a JSON object.
369 * This takes ownership of the array in value unconditionally, freeing on failure.
370 *
371 * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
372 * :param const char *key: Key string.
373 * :param spdxtool_serialize_array_t *value: Array value to add.
374 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_ARRAY, located in the object.
375 * This object is not owned by the caller.
376 */
377 static inline spdxtool_serialize_value_t *
spdxtool_serialize_object_add_array(spdxtool_serialize_object_list_t * object_list,const char * key,spdxtool_serialize_array_t * value)378 spdxtool_serialize_object_add_array(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_array_t *value)
379 {
380 return spdxtool_serialize_object_add_take(object_list, key, spdxtool_serialize_value_array(value));
381 }
382
383 /*
384 * !doc
385 *
386 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_string(spdxtool_serialize_array_t *array, const char *value)
387 *
388 * Append a string value to a JSON array. The string is copied internally.
389 * Unconditionally appends even if value is NULL.
390 *
391 * :param spdxtool_serialize_array_t *array: Array to append to.
392 * :param const char *value: String value to copy.
393 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_STRING, located in the array.
394 * This object is not owned by the caller.
395 */
396 static inline spdxtool_serialize_value_t *
spdxtool_serialize_array_add_string(spdxtool_serialize_array_t * array,const char * value)397 spdxtool_serialize_array_add_string(spdxtool_serialize_array_t *array, const char *value)
398 {
399 return spdxtool_serialize_array_add_take(array, spdxtool_serialize_value_string(value));
400 }
401
402 /*
403 * !doc
404 *
405 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_string_opt(spdxtool_serialize_array_t *a, const char *value)
406 *
407 * Append a string value to a JSON array only if value is non-NULL.
408 * Use this for optional array entries that should be omitted when absent.
409 *
410 * :param spdxtool_serialize_array_t *a: Array to append to.
411 * :param const char *value: String value to copy, or NULL to skip.
412 * :return: If value is set: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_STRING, located in the array.
413 * This object is not owned by the caller.
414 * If value is not set: NULL.
415 */
416 static inline spdxtool_serialize_value_t *
spdxtool_serialize_array_add_string_opt(spdxtool_serialize_array_t * array,const char * value)417 spdxtool_serialize_array_add_string_opt(spdxtool_serialize_array_t *array, const char *value)
418 {
419 if (value)
420 return spdxtool_serialize_array_add_take(array, spdxtool_serialize_value_string(value));
421
422 return NULL;
423 }
424
425 /*
426 * !doc
427 *
428 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_int(spdxtool_serialize_array_t *array, int value)
429 *
430 * Append a int value to a JSON array.
431 *
432 * :param spdxtool_serialize_array_t *array: Array to append to.
433 * :param int value: integer value.
434 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_INT, located in the array.
435 * This object is not owned by the caller.
436 */
437 static inline spdxtool_serialize_value_t *
spdxtool_serialize_array_add_int(spdxtool_serialize_array_t * array,int value)438 spdxtool_serialize_array_add_int(spdxtool_serialize_array_t *array, int value)
439 {
440 return spdxtool_serialize_array_add_take(array, spdxtool_serialize_value_int(value));
441 }
442
443 /*
444 * !doc
445 *
446 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_bool(spdxtool_serialize_array_t *array, bool value)
447 *
448 * Append a boolean value to a JSON array.
449 *
450 * :param spdxtool_serialize_array_t *array: Array to append to.
451 * :param bool value: Boolean value.
452 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_BOOL, located in the array.
453 * This object is not owned by the caller.
454 */
455 static inline spdxtool_serialize_value_t *
spdxtool_serialize_array_add_bool(spdxtool_serialize_array_t * array,bool value)456 spdxtool_serialize_array_add_bool(spdxtool_serialize_array_t *array, bool value)
457 {
458 return spdxtool_serialize_array_add_take(array, spdxtool_serialize_value_bool(value));
459 }
460
461 /*
462 * !doc
463 *
464 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_null(spdxtool_serialize_array_t *array)
465 *
466 * Append a null value to a JSON array.
467 *
468 * :param spdxtool_serialize_array_t *array: Array to append to.
469 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_NULL, located in the array.
470 * This object is not owned by the caller.
471 */
472 static inline spdxtool_serialize_value_t *
spdxtool_serialize_array_add_null(spdxtool_serialize_array_t * array)473 spdxtool_serialize_array_add_null(spdxtool_serialize_array_t *array)
474 {
475 return spdxtool_serialize_array_add_take(array, spdxtool_serialize_value_null());
476 }
477
478 /*
479 * !doc
480 *
481 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_object(spdxtool_serialize_array_t *array, spdxtool_serialize_object_list_t *value)
482 *
483 * Append an object value to a JSON array.
484 * This takes ownership of the object in value unconditionally, freeing on failure.
485 *
486 * :param spdxtool_serialize_array_t *array: Array to append to.
487 * :param spdxtool_serialize_object_list_t *value: Object value.
488 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_OBJECT, located in the array.
489 * This object is not owned by the caller.
490 */
491 static inline spdxtool_serialize_value_t *
spdxtool_serialize_array_add_object(spdxtool_serialize_array_t * array,spdxtool_serialize_object_list_t * value)492 spdxtool_serialize_array_add_object(spdxtool_serialize_array_t *array, spdxtool_serialize_object_list_t *value)
493 {
494 if (!value)
495 return NULL;
496
497 spdxtool_serialize_value_t *ret = spdxtool_serialize_value_object(value);
498 if (!ret)
499 {
500 // Since we take possession of the pointer unconditionally, clean up.
501 spdxtool_serialize_object_list_free(value);
502 return NULL;
503 }
504
505 return spdxtool_serialize_array_add_take(array, ret);
506 }
507
508 /*
509 * !doc
510 *
511 * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_array(spdxtool_serialize_array_t *array, spdxtool_serialize_array_t *value)
512 *
513 * Append an array value to a JSON array.
514 * This takes ownership of the array in value unconditionally, freeing on failure.
515 *
516 * :param spdxtool_serialize_array_t *array: Array to append to.
517 * :param spdxtool_serialize_array_t *value: Array value.
518 * :return: spdxtool_serialize_value_t * of type SPDXTOOL_SERIALIZE_TYPE_ARRAY, located in the array.
519 * This object is not owned by the caller.
520 */
521 static inline spdxtool_serialize_value_t *
spdxtool_serialize_array_add_array(spdxtool_serialize_array_t * array,spdxtool_serialize_array_t * value)522 spdxtool_serialize_array_add_array(spdxtool_serialize_array_t *array, spdxtool_serialize_array_t *value)
523 {
524 if (!value)
525 return NULL;
526
527 spdxtool_serialize_value_t *ret = spdxtool_serialize_value_array(value);
528 if (!ret)
529 {
530 // Since we take possession of the pointer unconditionally, clean up.
531 spdxtool_serialize_array_free(value);
532 return NULL;
533 }
534
535 return spdxtool_serialize_array_add_take(array, ret);
536 }
537
538 spdxtool_serialize_value_t *
539 spdxtool_serialize_sbom(pkgconf_client_t *client, spdxtool_core_agent_t *agent, spdxtool_core_creation_info_t *creation, spdxtool_core_spdx_document_t *spdx);
540
541 #ifdef __cplusplus
542 }
543 #endif
544
545 #endif
546