xref: /freebsd/contrib/pkgconf/cli/spdxtool/util.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
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(&current_uri, sep, global_xsd_any_uri, part, NULL);
184 	pkgconf_buffer_append_fmt(&current_uri, "%c" SIZE_FMT_SPECIFIER, sep, ++last_id);
185 
186 	return pkgconf_buffer_freeze(&current_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(&current_uri, sep, global_xsd_any_uri, part, string_id, NULL);
210 
211 	return pkgconf_buffer_freeze(&current_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