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
spdxtool_util_set_key(pkgconf_client_t * client,const char * key,const char * key_value,const char * key_default)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
spdxtool_util_set_uri_root(pkgconf_client_t * client,const char * uri_root)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 *
spdxtool_util_get_uri_root(pkgconf_client_t * client)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
spdxtool_util_set_uri_separator_colon(pkgconf_client_t * client,bool is_colon)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
spdxtool_util_get_uri_separator(pkgconf_client_t * client)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
spdxtool_util_set_spdx_version(pkgconf_client_t * client,const char * spdx_version)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 *
spdxtool_util_get_spdx_version(pkgconf_client_t * client)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
spdxtool_util_set_spdx_license(pkgconf_client_t * client,const char * spdx_license)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 *
spdxtool_util_get_spdx_id_int(pkgconf_client_t * client,const char * part)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 *
spdxtool_util_get_spdx_id_string(pkgconf_client_t * client,const char * part,const char * string_id)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 *
spdxtool_util_get_iso8601_time(time_t * wanted_time)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 *
spdxtool_util_get_current_iso8601_time(void)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 *
spdxtool_util_string_correction(char * str)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 *
spdxtool_util_tuple_lookup(pkgconf_client_t * client,pkgconf_list_t * vars,const char * key)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