xref: /freebsd/contrib/pkgconf/cli/spdxtool/serialize.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1*592efe25SPierre Pronchery /*
2*592efe25SPierre Pronchery  * SPDX-License-Identifier: BSD-2-Clause
3*592efe25SPierre Pronchery  *
4*592efe25SPierre Pronchery  *​ Copyright (c) 2025 The FreeBSD Foundation
5*592efe25SPierre Pronchery  *​
6*592efe25SPierre Pronchery  *​ Portions of this software were developed by
7*592efe25SPierre Pronchery  * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
8*592efe25SPierre Pronchery  * the FreeBSD Foundation
9*592efe25SPierre Pronchery  *​
10*592efe25SPierre Pronchery  *​ Copyright (C) 2026 Elizabeth Ashford.
11*592efe25SPierre Pronchery  */
12*592efe25SPierre Pronchery 
13*592efe25SPierre Pronchery #include <stdlib.h>
14*592efe25SPierre Pronchery #include <string.h>
15*592efe25SPierre Pronchery #include "util.h"
16*592efe25SPierre Pronchery #include "core.h"
17*592efe25SPierre Pronchery #include "software.h"
18*592efe25SPierre Pronchery #include "simplelicensing.h"
19*592efe25SPierre Pronchery #include "serialize.h"
20*592efe25SPierre Pronchery 
21*592efe25SPierre Pronchery static void
serialize_escape_string(pkgconf_buffer_t * buffer,const char * s)22*592efe25SPierre Pronchery serialize_escape_string(pkgconf_buffer_t *buffer, const char *s)
23*592efe25SPierre Pronchery {
24*592efe25SPierre Pronchery 	for (const char *p = s; *p; p++)
25*592efe25SPierre Pronchery 	{
26*592efe25SPierre Pronchery 		switch (*p)
27*592efe25SPierre Pronchery 		{
28*592efe25SPierre Pronchery 		case '\"':
29*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "\\\"");
30*592efe25SPierre Pronchery 			break;
31*592efe25SPierre Pronchery 		case '\\':
32*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "\\\\");
33*592efe25SPierre Pronchery 			break;
34*592efe25SPierre Pronchery 		case '\b':
35*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "\\b");
36*592efe25SPierre Pronchery 			break;
37*592efe25SPierre Pronchery 		case '\f':
38*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "\\f");
39*592efe25SPierre Pronchery 			break;
40*592efe25SPierre Pronchery 		case '\n':
41*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "\\n");
42*592efe25SPierre Pronchery 			break;
43*592efe25SPierre Pronchery 		case '\r':
44*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "\\r");
45*592efe25SPierre Pronchery 			break;
46*592efe25SPierre Pronchery 		case '\t':
47*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "\\t");
48*592efe25SPierre Pronchery 			break;
49*592efe25SPierre Pronchery 		default:
50*592efe25SPierre Pronchery 			if ((unsigned char) *p < 0x20)
51*592efe25SPierre Pronchery 				pkgconf_buffer_append_fmt(buffer, "\\u%04x", (unsigned int)(unsigned char) *p);
52*592efe25SPierre Pronchery 			else
53*592efe25SPierre Pronchery 				pkgconf_buffer_push_byte(buffer, *p);
54*592efe25SPierre Pronchery 		}
55*592efe25SPierre Pronchery 	}
56*592efe25SPierre Pronchery }
57*592efe25SPierre Pronchery 
58*592efe25SPierre Pronchery static inline void
serialize_add_indent(pkgconf_buffer_t * buffer,unsigned int level)59*592efe25SPierre Pronchery serialize_add_indent(pkgconf_buffer_t *buffer, unsigned int level)
60*592efe25SPierre Pronchery {
61*592efe25SPierre Pronchery 	for (; level; level--)
62*592efe25SPierre Pronchery 		pkgconf_buffer_append(buffer, "    ");
63*592efe25SPierre Pronchery }
64*592efe25SPierre Pronchery 
65*592efe25SPierre Pronchery /*
66*592efe25SPierre Pronchery  * !doc
67*592efe25SPierre Pronchery  *
68*592efe25SPierre Pronchery  * .. c:function:: void spdxtool_serialize_value_to_buf(pkgconf_buffer_t *buffer, spdxtool_serialize_value_t *value, unsigned int indent)
69*592efe25SPierre Pronchery  *
70*592efe25SPierre Pronchery  *    Serialize the given JSON to the buffer
71*592efe25SPierre Pronchery  *
72*592efe25SPierre Pronchery  *    :param pkgconf_buffer_t *buffer: Buffer to add to.
73*592efe25SPierre Pronchery  *    :param spdxtool_serialize_value *value: Value to serialize.
74*592efe25SPierre Pronchery  *    :param unsigned int indent: Indent level
75*592efe25SPierre Pronchery  *    :return: true on success, false on failure
76*592efe25SPierre Pronchery  */
77*592efe25SPierre Pronchery bool
spdxtool_serialize_value_to_buf(pkgconf_buffer_t * buffer,spdxtool_serialize_value_t * value,unsigned int indent)78*592efe25SPierre Pronchery spdxtool_serialize_value_to_buf(pkgconf_buffer_t *buffer, spdxtool_serialize_value_t *value, unsigned int indent)
79*592efe25SPierre Pronchery {
80*592efe25SPierre Pronchery 	if (!buffer || !value)
81*592efe25SPierre Pronchery 		return false;
82*592efe25SPierre Pronchery 
83*592efe25SPierre Pronchery 	switch(value->type) {
84*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_STRING:
85*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, '"');
86*592efe25SPierre Pronchery 			serialize_escape_string(buffer, value->value.s ? value->value.s : "");
87*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, '"');
88*592efe25SPierre Pronchery 			break;
89*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_INT:
90*592efe25SPierre Pronchery 			pkgconf_buffer_append_fmt(buffer, "%d", value->value.i);
91*592efe25SPierre Pronchery 			break;
92*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_BOOL:
93*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, value->value.b ? "true" : "false");
94*592efe25SPierre Pronchery 			break;
95*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_NULL:
96*592efe25SPierre Pronchery 			pkgconf_buffer_append(buffer, "null");
97*592efe25SPierre Pronchery 			break;
98*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_OBJECT:
99*592efe25SPierre Pronchery 		{
100*592efe25SPierre Pronchery 			pkgconf_node_t *iter;
101*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, '{');
102*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, '\n');
103*592efe25SPierre Pronchery 
104*592efe25SPierre Pronchery 			PKGCONF_FOREACH_LIST_ENTRY(value->value.o->entries.head, iter)
105*592efe25SPierre Pronchery 			{
106*592efe25SPierre Pronchery 				spdxtool_serialize_object_t *entry = iter->data;
107*592efe25SPierre Pronchery 				serialize_add_indent(buffer, indent + 1);
108*592efe25SPierre Pronchery 				pkgconf_buffer_append_fmt(buffer, "\"%s\": ", entry->key);
109*592efe25SPierre Pronchery 				spdxtool_serialize_value_to_buf(buffer, entry->value, indent + 1);
110*592efe25SPierre Pronchery 				if (iter->next)
111*592efe25SPierre Pronchery 					pkgconf_buffer_push_byte(buffer, ',');
112*592efe25SPierre Pronchery 				pkgconf_buffer_push_byte(buffer, '\n');
113*592efe25SPierre Pronchery 			}
114*592efe25SPierre Pronchery 
115*592efe25SPierre Pronchery 			serialize_add_indent(buffer, indent);
116*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, '}');
117*592efe25SPierre Pronchery 			break;
118*592efe25SPierre Pronchery 		}
119*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_ARRAY:
120*592efe25SPierre Pronchery 		{
121*592efe25SPierre Pronchery 			pkgconf_node_t *iter;
122*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, '[');
123*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, '\n');
124*592efe25SPierre Pronchery 
125*592efe25SPierre Pronchery 			PKGCONF_FOREACH_LIST_ENTRY(value->value.a->items.head, iter)
126*592efe25SPierre Pronchery 			{
127*592efe25SPierre Pronchery 				spdxtool_serialize_value_t *entry = iter->data;
128*592efe25SPierre Pronchery 				serialize_add_indent(buffer, indent + 1);
129*592efe25SPierre Pronchery 				spdxtool_serialize_value_to_buf(buffer, entry, indent + 1);
130*592efe25SPierre Pronchery 				if (iter->next)
131*592efe25SPierre Pronchery 					pkgconf_buffer_push_byte(buffer, ',');
132*592efe25SPierre Pronchery 				pkgconf_buffer_push_byte(buffer, '\n');
133*592efe25SPierre Pronchery 			}
134*592efe25SPierre Pronchery 			serialize_add_indent(buffer, indent);
135*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(buffer, ']');
136*592efe25SPierre Pronchery 			break;
137*592efe25SPierre Pronchery 		}
138*592efe25SPierre Pronchery 	}
139*592efe25SPierre Pronchery 
140*592efe25SPierre Pronchery 	return true;
141*592efe25SPierre Pronchery }
142*592efe25SPierre Pronchery 
143*592efe25SPierre Pronchery /*
144*592efe25SPierre Pronchery  * !doc
145*592efe25SPierre Pronchery  *
146*592efe25SPierre Pronchery  * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_value_t *value)
147*592efe25SPierre Pronchery  *
148*592efe25SPierre Pronchery  *    Add a key-value pair to a JSON object list. The key is copied internally.
149*592efe25SPierre Pronchery  *    The object list takes ownership of the value.
150*592efe25SPierre Pronchery  *
151*592efe25SPierre Pronchery  *    :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
152*592efe25SPierre Pronchery  *    :param const char *key: Key string, copied internally.
153*592efe25SPierre Pronchery  *    :param spdxtool_serialize_value_t *value: Value to associate with the key. Ownership transfers to the object list.
154*592efe25SPierre Pronchery  *    :return: The value added, not owned by the caller.
155*592efe25SPierre Pronchery  */
156*592efe25SPierre Pronchery spdxtool_serialize_value_t *
spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t * object_list,const char * key,spdxtool_serialize_value_t * value)157*592efe25SPierre Pronchery spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_value_t *value)
158*592efe25SPierre Pronchery {
159*592efe25SPierre Pronchery 	if (!object_list || !value)
160*592efe25SPierre Pronchery 	{
161*592efe25SPierre Pronchery 		spdxtool_serialize_value_free(value);
162*592efe25SPierre Pronchery 		return NULL;
163*592efe25SPierre Pronchery 	}
164*592efe25SPierre Pronchery 
165*592efe25SPierre Pronchery 	pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
166*592efe25SPierre Pronchery 	spdxtool_serialize_object_t *object = calloc(1, sizeof(spdxtool_serialize_object_t));
167*592efe25SPierre Pronchery 	char *keycopy = key ? strdup(key) : strdup("");
168*592efe25SPierre Pronchery 	if (!node || !object || !keycopy)
169*592efe25SPierre Pronchery 	{
170*592efe25SPierre Pronchery 		free(node);
171*592efe25SPierre Pronchery 		free(keycopy);
172*592efe25SPierre Pronchery 		/* object->key/value are not assigned yet; free the struct itself */
173*592efe25SPierre Pronchery 		free(object);
174*592efe25SPierre Pronchery 		spdxtool_serialize_value_free(value);
175*592efe25SPierre Pronchery 		return NULL;
176*592efe25SPierre Pronchery 	}
177*592efe25SPierre Pronchery 
178*592efe25SPierre Pronchery 	object->key = keycopy;
179*592efe25SPierre Pronchery 	object->value = value;
180*592efe25SPierre Pronchery 	pkgconf_node_insert_tail(node, object, &object_list->entries);
181*592efe25SPierre Pronchery 	return value;
182*592efe25SPierre Pronchery }
183*592efe25SPierre Pronchery 
184*592efe25SPierre Pronchery /*
185*592efe25SPierre Pronchery  * !doc
186*592efe25SPierre Pronchery  *
187*592efe25SPierre Pronchery  * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_take(spdxtool_serialize_array_t *array, spdxtool_serialize_value_t value)
188*592efe25SPierre Pronchery  *
189*592efe25SPierre Pronchery  *    Add a value to a JSON array. The array takes ownership of the value.
190*592efe25SPierre Pronchery  *
191*592efe25SPierre Pronchery  *    :param spdxtool_serialize_array_t *array: Array to add to.
192*592efe25SPierre Pronchery  *    :param spdxtool_serialize_value_t value: Value to append. Ownership transfers to the array.
193*592efe25SPierre Pronchery  *    :return: The value added, not owned by the caller.
194*592efe25SPierre Pronchery  */
195*592efe25SPierre Pronchery spdxtool_serialize_value_t *
spdxtool_serialize_array_add_take(spdxtool_serialize_array_t * array,spdxtool_serialize_value_t * value)196*592efe25SPierre Pronchery spdxtool_serialize_array_add_take(spdxtool_serialize_array_t *array, spdxtool_serialize_value_t *value)
197*592efe25SPierre Pronchery {
198*592efe25SPierre Pronchery 	if (!array)
199*592efe25SPierre Pronchery 	{
200*592efe25SPierre Pronchery 		// Taking value, so free
201*592efe25SPierre Pronchery 		spdxtool_serialize_value_free(value);
202*592efe25SPierre Pronchery 		return NULL;
203*592efe25SPierre Pronchery 	}
204*592efe25SPierre Pronchery 
205*592efe25SPierre Pronchery 	pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
206*592efe25SPierre Pronchery 	if (!node)
207*592efe25SPierre Pronchery 	{
208*592efe25SPierre Pronchery 		spdxtool_serialize_value_free(value);
209*592efe25SPierre Pronchery 		return NULL;
210*592efe25SPierre Pronchery 	}
211*592efe25SPierre Pronchery 
212*592efe25SPierre Pronchery 	pkgconf_node_insert_tail(node, value, &array->items);
213*592efe25SPierre Pronchery 	return value;
214*592efe25SPierre Pronchery }
215*592efe25SPierre Pronchery 
216*592efe25SPierre Pronchery /*
217*592efe25SPierre Pronchery  * !doc
218*592efe25SPierre Pronchery  *
219*592efe25SPierre Pronchery  * .. c:function:: spdxtool_serialize_object_list_t *spdxtool_serialize_object_list_new(void)
220*592efe25SPierre Pronchery  *
221*592efe25SPierre Pronchery  *    Allocate and initialize a new empty JSON object list.
222*592efe25SPierre Pronchery  *
223*592efe25SPierre Pronchery  *    :return: Pointer to a new spdxtool_serialize_object_list_t, or NULL on allocation failure.
224*592efe25SPierre Pronchery  */
225*592efe25SPierre Pronchery spdxtool_serialize_object_list_t *
spdxtool_serialize_object_list_new(void)226*592efe25SPierre Pronchery spdxtool_serialize_object_list_new(void)
227*592efe25SPierre Pronchery {
228*592efe25SPierre Pronchery 	return calloc(1, sizeof(spdxtool_serialize_object_list_t));
229*592efe25SPierre Pronchery }
230*592efe25SPierre Pronchery 
231*592efe25SPierre Pronchery /*
232*592efe25SPierre Pronchery  * !doc
233*592efe25SPierre Pronchery  *
234*592efe25SPierre Pronchery  * .. c:function:: spdxtool_serialize_array_t *spdxtool_serialize_array_new(void)
235*592efe25SPierre Pronchery  *
236*592efe25SPierre Pronchery  *    Allocate and initialize a new empty JSON array.
237*592efe25SPierre Pronchery  *
238*592efe25SPierre Pronchery  *    :return: Pointer to a new spdxtool_serialize_array_t, or NULL on allocation failure.
239*592efe25SPierre Pronchery  */
240*592efe25SPierre Pronchery spdxtool_serialize_array_t *
spdxtool_serialize_array_new(void)241*592efe25SPierre Pronchery spdxtool_serialize_array_new(void)
242*592efe25SPierre Pronchery {
243*592efe25SPierre Pronchery 	return calloc(1, sizeof(spdxtool_serialize_array_t));
244*592efe25SPierre Pronchery }
245*592efe25SPierre Pronchery 
246*592efe25SPierre Pronchery /*
247*592efe25SPierre Pronchery  * !doc
248*592efe25SPierre Pronchery  *
249*592efe25SPierre Pronchery  * .. c:function:: void spdxtool_serialize_value_free(spdxtool_serialize_value_t *value)
250*592efe25SPierre Pronchery  *
251*592efe25SPierre Pronchery  *    Free all resources owned by a JSON value. For strings, frees the string.
252*592efe25SPierre Pronchery  *    For objects and arrays, recursively frees all children. The value pointer
253*592efe25SPierre Pronchery  *    itself is not freed as it is assumed to be stack-allocated.
254*592efe25SPierre Pronchery  *
255*592efe25SPierre Pronchery  *    :param spdxtool_serialize_value_t *value: Value to free. May be NULL.
256*592efe25SPierre Pronchery  *    :return: nothing
257*592efe25SPierre Pronchery  */
258*592efe25SPierre Pronchery void
spdxtool_serialize_value_free(spdxtool_serialize_value_t * value)259*592efe25SPierre Pronchery spdxtool_serialize_value_free(spdxtool_serialize_value_t *value)
260*592efe25SPierre Pronchery {
261*592efe25SPierre Pronchery 	if (!value)
262*592efe25SPierre Pronchery 		return;
263*592efe25SPierre Pronchery 
264*592efe25SPierre Pronchery 	switch (value->type)
265*592efe25SPierre Pronchery 	{
266*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_STRING:
267*592efe25SPierre Pronchery 			free(value->value.s);
268*592efe25SPierre Pronchery 			break;
269*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_ARRAY:
270*592efe25SPierre Pronchery 			spdxtool_serialize_array_free(value->value.a);
271*592efe25SPierre Pronchery 			break;
272*592efe25SPierre Pronchery 		case SPDXTOOL_SERIALIZE_TYPE_OBJECT:
273*592efe25SPierre Pronchery 			spdxtool_serialize_object_list_free(value->value.o);
274*592efe25SPierre Pronchery 			break;
275*592efe25SPierre Pronchery 		default:
276*592efe25SPierre Pronchery 			// Nothing to do
277*592efe25SPierre Pronchery 			break;
278*592efe25SPierre Pronchery 	}
279*592efe25SPierre Pronchery 
280*592efe25SPierre Pronchery 	free(value);
281*592efe25SPierre Pronchery }
282*592efe25SPierre Pronchery 
283*592efe25SPierre Pronchery /*
284*592efe25SPierre Pronchery  * !doc
285*592efe25SPierre Pronchery  *
286*592efe25SPierre Pronchery  * .. c:function:: void spdxtool_serialize_object_free(spdxtool_serialize_object_t *object)
287*592efe25SPierre Pronchery  *
288*592efe25SPierre Pronchery  *    Free a JSON object entry, including its key string and owned value.
289*592efe25SPierre Pronchery  *    The object pointer itself is not freed by this function.
290*592efe25SPierre Pronchery  *
291*592efe25SPierre Pronchery  *    :param spdxtool_serialize_object_t *object: Object entry to free. May be NULL.
292*592efe25SPierre Pronchery  *    :return: nothing
293*592efe25SPierre Pronchery  */
294*592efe25SPierre Pronchery void
spdxtool_serialize_object_free(spdxtool_serialize_object_t * object)295*592efe25SPierre Pronchery spdxtool_serialize_object_free(spdxtool_serialize_object_t *object)
296*592efe25SPierre Pronchery {
297*592efe25SPierre Pronchery 	if (!object)
298*592efe25SPierre Pronchery 		return;
299*592efe25SPierre Pronchery 
300*592efe25SPierre Pronchery 	free(object->key);
301*592efe25SPierre Pronchery 	spdxtool_serialize_value_free(object->value);
302*592efe25SPierre Pronchery }
303*592efe25SPierre Pronchery 
304*592efe25SPierre Pronchery /*
305*592efe25SPierre Pronchery  * !doc
306*592efe25SPierre Pronchery  *
307*592efe25SPierre Pronchery  * .. c:function:: void spdxtool_serialize_object_list_free(spdxtool_serialize_object_list_t *object_list)
308*592efe25SPierre Pronchery  *
309*592efe25SPierre Pronchery  *    Free a JSON object list and all of its entries, including their keys and values.
310*592efe25SPierre Pronchery  *
311*592efe25SPierre Pronchery  *    :param spdxtool_serialize_object_list_t *object_list: Object list to free. May be NULL.
312*592efe25SPierre Pronchery  *    :return: nothing
313*592efe25SPierre Pronchery  */
314*592efe25SPierre Pronchery void
spdxtool_serialize_object_list_free(spdxtool_serialize_object_list_t * object_list)315*592efe25SPierre Pronchery spdxtool_serialize_object_list_free(spdxtool_serialize_object_list_t *object_list)
316*592efe25SPierre Pronchery {
317*592efe25SPierre Pronchery 	if (!object_list)
318*592efe25SPierre Pronchery 		return;
319*592efe25SPierre Pronchery 
320*592efe25SPierre Pronchery 	pkgconf_node_t *iter_next = NULL, *iter = NULL;
321*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(object_list->entries.head, iter_next, iter)
322*592efe25SPierre Pronchery 	{
323*592efe25SPierre Pronchery 		spdxtool_serialize_object_t *object = iter->data;
324*592efe25SPierre Pronchery 		spdxtool_serialize_object_free(object);
325*592efe25SPierre Pronchery 		free(object);
326*592efe25SPierre Pronchery 		free(iter);
327*592efe25SPierre Pronchery 	}
328*592efe25SPierre Pronchery 
329*592efe25SPierre Pronchery 	free(object_list);
330*592efe25SPierre Pronchery }
331*592efe25SPierre Pronchery 
332*592efe25SPierre Pronchery /*
333*592efe25SPierre Pronchery  * !doc
334*592efe25SPierre Pronchery  *
335*592efe25SPierre Pronchery  * .. c:function:: void spdxtool_serialize_array_free(spdxtool_serialize_array_t *array)
336*592efe25SPierre Pronchery  *
337*592efe25SPierre Pronchery  *    Free a JSON array and all of its elements.
338*592efe25SPierre Pronchery  *
339*592efe25SPierre Pronchery  *    :param spdxtool_serialize_array_t *array: Array to free. May be NULL.
340*592efe25SPierre Pronchery  *    :return: nothing
341*592efe25SPierre Pronchery  */
342*592efe25SPierre Pronchery void
spdxtool_serialize_array_free(spdxtool_serialize_array_t * array)343*592efe25SPierre Pronchery spdxtool_serialize_array_free(spdxtool_serialize_array_t *array)
344*592efe25SPierre Pronchery {
345*592efe25SPierre Pronchery 	if (!array)
346*592efe25SPierre Pronchery 		return;
347*592efe25SPierre Pronchery 
348*592efe25SPierre Pronchery 	pkgconf_node_t *iter_next = NULL, *iter = NULL;
349*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(array->items.head, iter_next, iter)
350*592efe25SPierre Pronchery 	{
351*592efe25SPierre Pronchery 		spdxtool_serialize_value_t *value = iter->data;
352*592efe25SPierre Pronchery 		spdxtool_serialize_value_free(value);
353*592efe25SPierre Pronchery 		free(iter);
354*592efe25SPierre Pronchery 	}
355*592efe25SPierre Pronchery 
356*592efe25SPierre Pronchery 	free(array);
357*592efe25SPierre Pronchery }
358*592efe25SPierre Pronchery 
359*592efe25SPierre Pronchery /*
360*592efe25SPierre Pronchery  * !doc
361*592efe25SPierre Pronchery  *
362*592efe25SPierre Pronchery  * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_sbom(pkgconf_client_t *client, spdxtool_core_agent_t *agent, spdxtool_core_creation_info_t *creation, spdxtool_core_spdx_document_t *spdx)
363*592efe25SPierre Pronchery  *
364*592efe25SPierre Pronchery  *    Serialize a complete SPDX SBOM document to a JSON-LD value tree. Iterates
365*592efe25SPierre Pronchery  *    all SBOMs, packages, relationships, and license expressions registered on
366*592efe25SPierre Pronchery  *    the document. The SpdxDocument object is emitted last to ensure all element
367*592efe25SPierre Pronchery  *    IDs have been populated by prior iteration. This function must be called
368*592efe25SPierre Pronchery  *    after pkgconf_pkg_traverse has completed so that all packages and their
369*592efe25SPierre Pronchery  *    dependencies are registered on spdx.
370*592efe25SPierre Pronchery  *
371*592efe25SPierre Pronchery  *    :param pkgconf_client_t *client: The pkgconf client being accessed.
372*592efe25SPierre Pronchery  *    :param spdxtool_core_agent_t *agent: Agent struct to include in the document.
373*592efe25SPierre Pronchery  *    :param spdxtool_core_creation_info_t *creation: CreationInfo struct to include in the document.
374*592efe25SPierre Pronchery  *    :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct containing all registered SBOMs, packages, relationships, and licenses.
375*592efe25SPierre Pronchery  *    :return: spdxtool_serialize_value_t * representing the complete JSON-LD document, or a null string value on allocation failure.
376*592efe25SPierre Pronchery  */
377*592efe25SPierre Pronchery spdxtool_serialize_value_t *
spdxtool_serialize_sbom(pkgconf_client_t * client,spdxtool_core_agent_t * agent,spdxtool_core_creation_info_t * creation,spdxtool_core_spdx_document_t * spdx)378*592efe25SPierre Pronchery spdxtool_serialize_sbom(pkgconf_client_t *client, spdxtool_core_agent_t *agent, spdxtool_core_creation_info_t *creation, spdxtool_core_spdx_document_t *spdx)
379*592efe25SPierre Pronchery {
380*592efe25SPierre Pronchery 	const char *errstr = "out of memory";
381*592efe25SPierre Pronchery 	spdxtool_serialize_value_t *ret = NULL;
382*592efe25SPierre Pronchery 	spdxtool_serialize_array_t *graph = NULL;
383*592efe25SPierre Pronchery 	spdxtool_serialize_object_list_t *root = spdxtool_serialize_object_list_new();
384*592efe25SPierre Pronchery 	if (!root)
385*592efe25SPierre Pronchery 		goto err;
386*592efe25SPierre Pronchery 
387*592efe25SPierre Pronchery 	if (!spdxtool_serialize_object_add_string(root, "@context", "https://spdx.org/rdf/3.0.1/spdx-context.jsonld"))
388*592efe25SPierre Pronchery 		goto err;
389*592efe25SPierre Pronchery 
390*592efe25SPierre Pronchery 	graph = spdxtool_serialize_array_new();
391*592efe25SPierre Pronchery 	if (!graph)
392*592efe25SPierre Pronchery 		goto err;
393*592efe25SPierre Pronchery 
394*592efe25SPierre Pronchery 	if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_agent_to_object(client, agent)))
395*592efe25SPierre Pronchery 		goto err;
396*592efe25SPierre Pronchery 
397*592efe25SPierre Pronchery 	if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_creation_info_to_object(client, creation)))
398*592efe25SPierre Pronchery 		goto err;
399*592efe25SPierre Pronchery 
400*592efe25SPierre Pronchery 	pkgconf_node_t *iter = NULL;
401*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(spdx->maintainers.head, iter)
402*592efe25SPierre Pronchery 	{
403*592efe25SPierre Pronchery 		spdxtool_core_agent_t *maintainer = iter->data;
404*592efe25SPierre Pronchery 		if (!maintainer)
405*592efe25SPierre Pronchery 		{
406*592efe25SPierre Pronchery 			errstr = "maintainers list corrupted";
407*592efe25SPierre Pronchery 			goto err;
408*592efe25SPierre Pronchery 		}
409*592efe25SPierre Pronchery 		if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_agent_to_object(client, maintainer)))
410*592efe25SPierre Pronchery 			goto err;
411*592efe25SPierre Pronchery 	}
412*592efe25SPierre Pronchery 
413*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(spdx->licenses.head, iter)
414*592efe25SPierre Pronchery 	{
415*592efe25SPierre Pronchery 		spdxtool_simplelicensing_license_expression_t *expression = iter->data;
416*592efe25SPierre Pronchery 		if (!expression)
417*592efe25SPierre Pronchery 		{
418*592efe25SPierre Pronchery 			errstr = "licenses list corrupted";
419*592efe25SPierre Pronchery 			goto err;
420*592efe25SPierre Pronchery 		}
421*592efe25SPierre Pronchery 		if (!spdxtool_serialize_array_add_take(graph, spdxtool_simplelicensing_licenseExpression_to_object(client, spdx->creation_info, expression)))
422*592efe25SPierre Pronchery 			goto err;
423*592efe25SPierre Pronchery 	}
424*592efe25SPierre Pronchery 
425*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(spdx->rootElement.head, iter)
426*592efe25SPierre Pronchery 	{
427*592efe25SPierre Pronchery 		spdxtool_software_sbom_t *current_sbom = iter->data;
428*592efe25SPierre Pronchery 		if (!current_sbom)
429*592efe25SPierre Pronchery 		{
430*592efe25SPierre Pronchery 			errstr = "sbom list corrupted";
431*592efe25SPierre Pronchery 			goto err;
432*592efe25SPierre Pronchery 		}
433*592efe25SPierre Pronchery 		if (!spdxtool_serialize_array_add_take(graph, spdxtool_software_sbom_to_object(client, current_sbom)))
434*592efe25SPierre Pronchery 			goto err;
435*592efe25SPierre Pronchery 	}
436*592efe25SPierre Pronchery 
437*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(spdx->packages.head, iter)
438*592efe25SPierre Pronchery 	{
439*592efe25SPierre Pronchery 		pkgconf_pkg_t *pkg = iter->data;
440*592efe25SPierre Pronchery 		if (!pkg)
441*592efe25SPierre Pronchery 		{
442*592efe25SPierre Pronchery 			errstr = "pkg list corrupted";
443*592efe25SPierre Pronchery 			goto err;
444*592efe25SPierre Pronchery 		}
445*592efe25SPierre Pronchery 		if (!spdxtool_serialize_array_add_take(graph, spdxtool_software_package_to_object(client, pkg, spdx)))
446*592efe25SPierre Pronchery 			goto err;
447*592efe25SPierre Pronchery 	}
448*592efe25SPierre Pronchery 
449*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(spdx->relationships.head, iter)
450*592efe25SPierre Pronchery 	{
451*592efe25SPierre Pronchery 		spdxtool_core_relationship_t *relationship = iter->data;
452*592efe25SPierre Pronchery 		if (!relationship)
453*592efe25SPierre Pronchery 		{
454*592efe25SPierre Pronchery 			errstr = "relationship list corrupted";
455*592efe25SPierre Pronchery 			goto err;
456*592efe25SPierre Pronchery 		}
457*592efe25SPierre Pronchery 		if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_relationship_to_object(client, relationship)))
458*592efe25SPierre Pronchery 			goto err;
459*592efe25SPierre Pronchery 	}
460*592efe25SPierre Pronchery 
461*592efe25SPierre Pronchery 	// SpdxDocument last — spdx->element must be fully populated first
462*592efe25SPierre Pronchery 	if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_spdx_document_to_object(client, spdx)))
463*592efe25SPierre Pronchery 		goto err;
464*592efe25SPierre Pronchery 
465*592efe25SPierre Pronchery 	bool ok = spdxtool_serialize_object_add_array(root, "@graph", graph);
466*592efe25SPierre Pronchery 	graph = NULL;
467*592efe25SPierre Pronchery 	if (!ok)
468*592efe25SPierre Pronchery 		goto err;
469*592efe25SPierre Pronchery 
470*592efe25SPierre Pronchery 	ret = spdxtool_serialize_value_object(root);
471*592efe25SPierre Pronchery 	root = NULL;
472*592efe25SPierre Pronchery 
473*592efe25SPierre Pronchery err:
474*592efe25SPierre Pronchery 	if (!ret)
475*592efe25SPierre Pronchery 		pkgconf_error(client, "spdxtool_serialize_sbom: %s", errstr);
476*592efe25SPierre Pronchery 
477*592efe25SPierre Pronchery 	spdxtool_serialize_object_list_free(root);
478*592efe25SPierre Pronchery 	spdxtool_serialize_array_free(graph);
479*592efe25SPierre Pronchery 	return ret;
480*592efe25SPierre Pronchery }
481