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