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 11 #include <libpkgconf/libpkgconf.h> 12 #include <libpkgconf/stdinc.h> 13 #include <string.h> 14 #include <stdlib.h> 15 #include <time.h> 16 #include <ctype.h> 17 #include "util.h" 18 19 /* 20 * !doc 21 * 22 * .. c:function:: void spdxtool_util_set_key(pkgconf_client_t *client, const char *key, const char *key_value, const char *key_default) 23 * 24 * Set key wit default value. Default value is used if key is NULL 25 * 26 * :param pkgconf_client_t* client: The pkgconf client being accessed. 27 * :param const char *key: Key to be preserved 28 * :param const char *key_value: Value for key 29 * :param const char *key_default: Default value if key_value is NULL 30 * :return: nothing 31 */ 32 void 33 spdxtool_util_set_key(pkgconf_client_t *client, const char *key, const char *key_value, const char *key_default) 34 { 35 PKGCONF_TRACE(client, "set uri_root to: %s", key_value != NULL ? key_value : key_default); 36 pkgconf_tuple_add_global(client, key, key_value != NULL ? key_value : key_default); 37 } 38 39 /* 40 * !doc 41 * 42 * .. c:function:: void spdxtool_util_set_uri_root(pkgconf_client_t *client, const char *uri_root) 43 * 44 * Set URI/URL root for spdxId. Type for this is 'xsd:anyURI' which means 45 * it can they may be absolute or relative. 46 * 47 * :param pkgconf_client_t* client: The pkgconf client being accessed. 48 * :param const char *uri_root: URI root. 49 * :return: nothing 50 */ 51 void 52 spdxtool_util_set_uri_root(pkgconf_client_t *client, const char *uri_root) 53 { 54 spdxtool_util_set_key(client, "spdx_uri_root", uri_root, "https://github.com/pkgconf/pkgconf"); 55 } 56 57 /* 58 * !doc 59 * 60 * .. c:function:: const char *spdxtool_util_get_uri_root(pkgconf_client_t *client) 61 * 62 * Get current URI 63 * 64 * :param pkgconf_client_t* client: The pkgconf client being accessed. 65 * :return: URI or NULL 66 */ 67 const char * 68 spdxtool_util_get_uri_root(pkgconf_client_t *client) 69 { 70 return pkgconf_tuple_find_global(client, "spdx_uri_root"); 71 } 72 73 /* 74 * !doc 75 * 76 * .. c:function:: void spdxtool_util_set_uri_separator_colon(pkgconf_client_t *client, bool is_colon) 77 * 78 * when using URI rather than URL change separator from slash '/' to colon ':'. 79 * 80 * :param pkgconf_client_t* client: The pkgconf client being accessed. 81 * :param bool is_colon If true use colon ':' is separator and otherwise slash '/' 82 * :return: nothing 83 */ 84 void 85 spdxtool_util_set_uri_separator_colon(pkgconf_client_t *client, bool is_colon) 86 { 87 if (is_colon == true) 88 client->flags |= SPDXTOOL_SEPARATOR_COLON; 89 else 90 client->flags = client->flags & ~SPDXTOOL_SEPARATOR_COLON; 91 } 92 93 /* 94 * !doc 95 * 96 * .. c:function:: char spdxtool_util_get_uri_separator(pkgconf_client_t *client) 97 * 98 * Get separator char 99 * 100 * :param pkgconf_client_t* client: The pkgconf client being accessed. 101 * :return: Colon ':' or slash '/' 102 */ 103 char 104 spdxtool_util_get_uri_separator(pkgconf_client_t *client) 105 { 106 if (client->flags & SPDXTOOL_SEPARATOR_COLON) 107 return ':'; 108 109 return '/'; 110 } 111 112 /* 113 * !doc 114 * 115 * .. c:function:: void spdxtool_util_set_spdx_version(pkgconf_client_t *client, const char *spdx_version) 116 * 117 * Set current SPDX SBOM Spec version. If not set it's 3.0.1 118 * 119 * :param pkgconf_client_t* client: The pkgconf client being accessed. 120 * :param char* spdx_version: SPDX specification version. 121 * :return: nothing 122 */ 123 void 124 spdxtool_util_set_spdx_version(pkgconf_client_t *client, const char *spdx_version) 125 { 126 spdxtool_util_set_key(client, "spdx_version", spdx_version, "3.0.1"); 127 } 128 129 /* 130 * !doc 131 * 132 * .. c:function:: const char *spdxtool_util_get_spdx_version(pkgconf_client_t *client) 133 * 134 * Get SPDX SBOM specification version in use 135 * 136 * :param pkgconf_client_t* client: The pkgconf client being accessed. 137 * :return: Spec version 138 */ 139 const char * 140 spdxtool_util_get_spdx_version(pkgconf_client_t *client) 141 { 142 return pkgconf_tuple_find_global(client, "spdx_version"); 143 } 144 145 /* 146 * !doc 147 * 148 * .. c:function:: void spdxtool_util_set_spdx_license(pkgconf_client_t *client, const char *spdx_license) 149 * 150 * Under which license SBOM is released. License string should be in 151 * SPDX style. Something like: FreeBSD-DOC, CC0-1.0 or MIT 152 * 153 * :param pkgconf_client_t* client: The pkgconf client being accessed. 154 * :param const char *spdx_license: SPDX compatible license 155 * :return: nothing 156 */ 157 void 158 spdxtool_util_set_spdx_license(pkgconf_client_t *client, const char *spdx_license) 159 { 160 spdxtool_util_set_key(client, "spdx_license", spdx_license, "CC0-1.0"); 161 } 162 163 /* 164 * !doc 165 * 166 * .. c:function:: char *spdxtool_util_get_spdx_id_int(pkgconf_client_t *client, char *part) 167 * 168 * Get spdxId with current URI. 169 * URI is lookg like https://test/part/1 170 * 171 * :param pkgconf_client_t* client: The pkgconf client being accessed. 172 * :param char *part: Addition subdir part before number 173 * :return: URI 174 */ 175 char * 176 spdxtool_util_get_spdx_id_int(pkgconf_client_t *client, const char *part) 177 { 178 static size_t last_id = 0; 179 const char *global_xsd_any_uri = spdxtool_util_get_uri_root(client); 180 char sep = spdxtool_util_get_uri_separator(client); 181 pkgconf_buffer_t current_uri = PKGCONF_BUFFER_INITIALIZER; 182 183 pkgconf_buffer_join(¤t_uri, sep, global_xsd_any_uri, part, NULL); 184 pkgconf_buffer_append_fmt(¤t_uri, "%c" SIZE_FMT_SPECIFIER, sep, ++last_id); 185 186 return pkgconf_buffer_freeze(¤t_uri); 187 } 188 189 /* 190 * !doc 191 * 192 * .. c:function:: char *spdxtool_util_get_spdx_id_string(pkgconf_client_t *client, const char *part, const char *string_id) 193 * 194 * Get string id URI 195 * looks something like: https://test/part/string_id 196 * 197 * :param pkgconf_client_t* client: The pkgconf client being accessed. 198 * :param const char* part: subdir part of URI. 199 * :param const char* string_id: String ID. 200 * :return: URI 201 */ 202 char * 203 spdxtool_util_get_spdx_id_string(pkgconf_client_t *client, const char *part, const char *string_id) 204 { 205 const char *global_xsd_any_uri = spdxtool_util_get_uri_root(client); 206 char sep = spdxtool_util_get_uri_separator(client); 207 pkgconf_buffer_t current_uri = PKGCONF_BUFFER_INITIALIZER; 208 209 pkgconf_buffer_join(¤t_uri, sep, global_xsd_any_uri, part, string_id, NULL); 210 211 return pkgconf_buffer_freeze(¤t_uri); 212 } 213 214 /* 215 * !doc 216 * 217 * .. c:function:: char *spdxtool_util_get_iso8601_time(time_t *wanted_time) 218 * 219 * Get ISO8601 time string 220 * 221 * :param time_t *wanted_time: Time to be converted 222 * :return: Time string in ISO8601 format 223 */ 224 char * 225 spdxtool_util_get_iso8601_time(time_t *wanted_time) 226 { 227 char buf[PKGCONF_ITEM_SIZE]; 228 229 if (!wanted_time) 230 return NULL; 231 232 struct tm *tm_info = gmtime(wanted_time); 233 if (!tm_info) 234 return NULL; 235 236 /* ISO8601 time with Z at the end */ 237 strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", tm_info); 238 return strdup(buf); 239 } 240 241 /* 242 * !doc 243 * 244 * .. c:function:: char *spdxtool_util_get_current_iso8601_time(void) 245 * 246 * Get ISO8601 current timestamp, honouring SOURCE_DATE_EPOCH when set 247 * 248 * :return: Time string in ISO8601 format 249 */ 250 char * 251 spdxtool_util_get_current_iso8601_time(void) 252 { 253 time_t now; 254 const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); 255 256 if (source_date_epoch != NULL && *source_date_epoch != '\0') 257 now = (time_t) strtoll(source_date_epoch, NULL, 10); 258 else 259 time(&now); 260 261 return spdxtool_util_get_iso8601_time(&now); 262 } 263 264 /* 265 * !doc 266 * 267 * .. c:function:: char *spdxtool_util_string_correction(char *str) 268 * 269 * Lowercase string and change spaces to '_' 270 * 271 * :param char *str: String to be converted 272 * :return: Converted string 273 */ 274 char * 275 spdxtool_util_string_correction(char *str) 276 { 277 char *ptr = str; 278 /* Lowecase string and make spaces '_' */ 279 for ( ; *ptr; ++ptr) 280 { 281 *ptr = (char) tolower(*ptr); 282 if(isspace((unsigned char) *ptr)) 283 { 284 *ptr = '_'; 285 } 286 } 287 288 return str; 289 } 290 291 /* 292 * !doc 293 * 294 * .. c:function:: char *spdxtool_util_tuple_lookup(pkgconf_client_t *client, pkgconf_list_t *vars, const char *key) 295 * 296 * Lowercase string and change spaces to '_' 297 * 298 * :param pkgconf_client_t *client: client to use for the lookup 299 * :param pkgconf_list_t *vars: locally-scoped vars to look through 300 * :param char *key: key to lookup 301 * :return: Converted string 302 */ 303 char * 304 spdxtool_util_tuple_lookup(pkgconf_client_t *client, pkgconf_list_t *vars, const char *key) 305 { 306 pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER; 307 pkgconf_bytecode_eval_ctx_t ctx = { 308 .client = client, 309 .vars = vars, 310 }; 311 312 pkgconf_variable_t *v = pkgconf_bytecode_eval_lookup_var(&ctx, key, strlen(key)); 313 if (v == NULL) 314 return NULL; 315 316 if (!pkgconf_variable_eval(client, vars, v, &buf, NULL)) 317 return NULL; 318 319 return pkgconf_buffer_freeze(&buf); 320 } 321