/* * SPDX-License-Identifier: BSD-2-Clause * *​ Copyright (c) 2025 The FreeBSD Foundation *​ *​ Portions of this software were developed by * Tuukka Pasanen under sponsorship from * the FreeBSD Foundation */ #include #include #include #include #include #include #include "util.h" /* * !doc * * .. c:function:: void spdxtool_util_set_key(pkgconf_client_t *client, const char *key, const char *key_value, const char *key_default) * * Set key wit default value. Default value is used if key is NULL * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :param const char *key: Key to be preserved * :param const char *key_value: Value for key * :param const char *key_default: Default value if key_value is NULL * :return: nothing */ void spdxtool_util_set_key(pkgconf_client_t *client, const char *key, const char *key_value, const char *key_default) { PKGCONF_TRACE(client, "set uri_root to: %s", key_value != NULL ? key_value : key_default); pkgconf_tuple_add_global(client, key, key_value != NULL ? key_value : key_default); } /* * !doc * * .. c:function:: void spdxtool_util_set_uri_root(pkgconf_client_t *client, const char *uri_root) * * Set URI/URL root for spdxId. Type for this is 'xsd:anyURI' which means * it can they may be absolute or relative. * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :param const char *uri_root: URI root. * :return: nothing */ void spdxtool_util_set_uri_root(pkgconf_client_t *client, const char *uri_root) { spdxtool_util_set_key(client, "spdx_uri_root", uri_root, "https://github.com/pkgconf/pkgconf"); } /* * !doc * * .. c:function:: const char *spdxtool_util_get_uri_root(pkgconf_client_t *client) * * Get current URI * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :return: URI or NULL */ const char * spdxtool_util_get_uri_root(pkgconf_client_t *client) { return pkgconf_tuple_find_global(client, "spdx_uri_root"); } /* * !doc * * .. c:function:: void spdxtool_util_set_uri_separator_colon(pkgconf_client_t *client, bool is_colon) * * when using URI rather than URL change separator from slash '/' to colon ':'. * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :param bool is_colon If true use colon ':' is separator and otherwise slash '/' * :return: nothing */ void spdxtool_util_set_uri_separator_colon(pkgconf_client_t *client, bool is_colon) { if (is_colon == true) client->flags |= SPDXTOOL_SEPARATOR_COLON; else client->flags = client->flags & ~SPDXTOOL_SEPARATOR_COLON; } /* * !doc * * .. c:function:: char spdxtool_util_get_uri_separator(pkgconf_client_t *client) * * Get separator char * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :return: Colon ':' or slash '/' */ char spdxtool_util_get_uri_separator(pkgconf_client_t *client) { if (client->flags & SPDXTOOL_SEPARATOR_COLON) return ':'; return '/'; } /* * !doc * * .. c:function:: void spdxtool_util_set_spdx_version(pkgconf_client_t *client, const char *spdx_version) * * Set current SPDX SBOM Spec version. If not set it's 3.0.1 * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :param char* spdx_version: SPDX specification version. * :return: nothing */ void spdxtool_util_set_spdx_version(pkgconf_client_t *client, const char *spdx_version) { spdxtool_util_set_key(client, "spdx_version", spdx_version, "3.0.1"); } /* * !doc * * .. c:function:: const char *spdxtool_util_get_spdx_version(pkgconf_client_t *client) * * Get SPDX SBOM specification version in use * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :return: Spec version */ const char * spdxtool_util_get_spdx_version(pkgconf_client_t *client) { return pkgconf_tuple_find_global(client, "spdx_version"); } /* * !doc * * .. c:function:: void spdxtool_util_set_spdx_license(pkgconf_client_t *client, const char *spdx_license) * * Under which license SBOM is released. License string should be in * SPDX style. Something like: FreeBSD-DOC, CC0-1.0 or MIT * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :param const char *spdx_license: SPDX compatible license * :return: nothing */ void spdxtool_util_set_spdx_license(pkgconf_client_t *client, const char *spdx_license) { spdxtool_util_set_key(client, "spdx_license", spdx_license, "CC0-1.0"); } /* * !doc * * .. c:function:: char *spdxtool_util_get_spdx_id_int(pkgconf_client_t *client, char *part) * * Get spdxId with current URI. * URI is lookg like https://test/part/1 * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :param char *part: Addition subdir part before number * :return: URI */ char * spdxtool_util_get_spdx_id_int(pkgconf_client_t *client, const char *part) { static size_t last_id = 0; const char *global_xsd_any_uri = spdxtool_util_get_uri_root(client); char sep = spdxtool_util_get_uri_separator(client); pkgconf_buffer_t current_uri = PKGCONF_BUFFER_INITIALIZER; pkgconf_buffer_join(¤t_uri, sep, global_xsd_any_uri, part, NULL); pkgconf_buffer_append_fmt(¤t_uri, "%c" SIZE_FMT_SPECIFIER, sep, ++last_id); return pkgconf_buffer_freeze(¤t_uri); } /* * !doc * * .. c:function:: char *spdxtool_util_get_spdx_id_string(pkgconf_client_t *client, const char *part, const char *string_id) * * Get string id URI * looks something like: https://test/part/string_id * * :param pkgconf_client_t* client: The pkgconf client being accessed. * :param const char* part: subdir part of URI. * :param const char* string_id: String ID. * :return: URI */ char * spdxtool_util_get_spdx_id_string(pkgconf_client_t *client, const char *part, const char *string_id) { const char *global_xsd_any_uri = spdxtool_util_get_uri_root(client); char sep = spdxtool_util_get_uri_separator(client); pkgconf_buffer_t current_uri = PKGCONF_BUFFER_INITIALIZER; pkgconf_buffer_join(¤t_uri, sep, global_xsd_any_uri, part, string_id, NULL); return pkgconf_buffer_freeze(¤t_uri); } /* * !doc * * .. c:function:: char *spdxtool_util_get_iso8601_time(time_t *wanted_time) * * Get ISO8601 time string * * :param time_t *wanted_time: Time to be converted * :return: Time string in ISO8601 format */ char * spdxtool_util_get_iso8601_time(time_t *wanted_time) { char buf[PKGCONF_ITEM_SIZE]; if (!wanted_time) return NULL; struct tm *tm_info = gmtime(wanted_time); if (!tm_info) return NULL; /* ISO8601 time with Z at the end */ strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", tm_info); return strdup(buf); } /* * !doc * * .. c:function:: char *spdxtool_util_get_current_iso8601_time(void) * * Get ISO8601 current timestamp, honouring SOURCE_DATE_EPOCH when set * * :return: Time string in ISO8601 format */ char * spdxtool_util_get_current_iso8601_time(void) { time_t now; const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); if (source_date_epoch != NULL && *source_date_epoch != '\0') now = (time_t) strtoll(source_date_epoch, NULL, 10); else time(&now); return spdxtool_util_get_iso8601_time(&now); } /* * !doc * * .. c:function:: char *spdxtool_util_string_correction(char *str) * * Lowercase string and change spaces to '_' * * :param char *str: String to be converted * :return: Converted string */ char * spdxtool_util_string_correction(char *str) { char *ptr = str; /* Lowecase string and make spaces '_' */ for ( ; *ptr; ++ptr) { *ptr = (char) tolower(*ptr); if(isspace((unsigned char) *ptr)) { *ptr = '_'; } } return str; } /* * !doc * * .. c:function:: char *spdxtool_util_tuple_lookup(pkgconf_client_t *client, pkgconf_list_t *vars, const char *key) * * Lowercase string and change spaces to '_' * * :param pkgconf_client_t *client: client to use for the lookup * :param pkgconf_list_t *vars: locally-scoped vars to look through * :param char *key: key to lookup * :return: Converted string */ char * spdxtool_util_tuple_lookup(pkgconf_client_t *client, pkgconf_list_t *vars, const char *key) { pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER; pkgconf_bytecode_eval_ctx_t ctx = { .client = client, .vars = vars, }; pkgconf_variable_t *v = pkgconf_bytecode_eval_lookup_var(&ctx, key, strlen(key)); if (v == NULL) return NULL; if (!pkgconf_variable_eval(client, vars, v, &buf, NULL)) return NULL; return pkgconf_buffer_freeze(&buf); }