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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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