Commit 5ce957b3 authored by Matthias Klumpp's avatar Matthias Klumpp
Browse files

Implement support for the WITH operator in recent SPDX

This is a very basic implementation, but it will do the job for now. In
future, exceptions should probably get their own prefix and not share @
with license IDs.

Resolves: #261
parent e2b23d95
...@@ -75,8 +75,8 @@ as_spdx_license_tokenize_drop (AsSpdxHelper *helper) ...@@ -75,8 +75,8 @@ as_spdx_license_tokenize_drop (AsSpdxHelper *helper)
if (helper->collect->len == 0) if (helper->collect->len == 0)
return; return;
/* is license enum */ /* is license or exception enum */
if (as_is_spdx_license_id (tmp)) { if (as_is_spdx_license_id (tmp) || as_is_spdx_license_exception_id (tmp)) {
g_ptr_array_add (helper->array, g_strdup_printf ("@%s", tmp)); g_ptr_array_add (helper->array, g_strdup_printf ("@%s", tmp));
helper->last_token_literal = FALSE; helper->last_token_literal = FALSE;
g_string_truncate (helper->collect, 0); g_string_truncate (helper->collect, 0);
...@@ -122,6 +122,14 @@ as_spdx_license_tokenize_drop (AsSpdxHelper *helper) ...@@ -122,6 +122,14 @@ as_spdx_license_tokenize_drop (AsSpdxHelper *helper)
return; return;
} }
/* is extension to the license */
if (g_strcmp0 (tmp, "with") == 0 || g_strcmp0 (tmp, "WITH") == 0) {
g_ptr_array_add (helper->array, g_strdup ("^"));
helper->last_token_literal = FALSE;
g_string_truncate (helper->collect, 0);
return;
}
/* is literal */ /* is literal */
if (helper->last_token_literal) { if (helper->last_token_literal) {
last_literal = g_strdup (_g_ptr_array_last (helper->array)); last_literal = g_strdup (_g_ptr_array_last (helper->array));
...@@ -141,7 +149,7 @@ as_spdx_license_tokenize_drop (AsSpdxHelper *helper) ...@@ -141,7 +149,7 @@ as_spdx_license_tokenize_drop (AsSpdxHelper *helper)
* *
* Searches the known list of SPDX license IDs. * Searches the known list of SPDX license IDs.
* *
* Returns: %TRUE if the icon is a valid "SPDX license ID" * Returns: %TRUE if the string is a valid SPDX license ID
* *
* Since: 0.9.8 * Since: 0.9.8
**/ **/
...@@ -171,12 +179,44 @@ as_is_spdx_license_id (const gchar *license_id) ...@@ -171,12 +179,44 @@ as_is_spdx_license_id (const gchar *license_id)
return g_strstr_len (g_bytes_get_data (data, NULL), -1, key) != NULL; return g_strstr_len (g_bytes_get_data (data, NULL), -1, key) != NULL;
} }
/**
* as_is_spdx_license_exception_id:
* @exception_id: a single SPDX license ID, e.g. "GCC-exception-3.1"
*
* Searches the known list of SPDX license exception IDs.
*
* Returns: %TRUE if the string is a valid SPDX license exception ID
*
* Since: 0.12.10
**/
gboolean
as_is_spdx_license_exception_id (const gchar *exception_id)
{
g_autoptr(GBytes) data = NULL;
g_autofree gchar *key = NULL;
/* handle invalid */
if (exception_id == NULL || exception_id[0] == '\0')
return FALSE;
/* load the readonly data section and look for the icon name */
data = g_resource_lookup_data (as_get_resource (),
"/org/freedesktop/appstream/spdx-license-exception-ids.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
NULL);
if (data == NULL)
return FALSE;
key = g_strdup_printf ("\n%s\n", exception_id);
return g_strstr_len (g_bytes_get_data (data, NULL), -1, key) != NULL;
}
/** /**
* as_is_spdx_license_expression: * as_is_spdx_license_expression:
* @license: a SPDX license string, e.g. "CC-BY-3.0 and GFDL-1.3" * @license: a SPDX license string, e.g. "CC-BY-3.0 and GFDL-1.3"
* *
* Checks the licence string to check it being a valid licence. * Checks the licence string to check it being a valid licence.
* NOTE: SPDX licences can't typically contain brackets. * NOTE: SPDX licenses can't typically contain brackets.
* *
* Returns: %TRUE if the icon is a valid "SPDX license" * Returns: %TRUE if the icon is a valid "SPDX license"
* *
...@@ -187,6 +227,7 @@ as_is_spdx_license_expression (const gchar *license) ...@@ -187,6 +227,7 @@ as_is_spdx_license_expression (const gchar *license)
{ {
guint i; guint i;
g_auto(GStrv) tokens = NULL; g_auto(GStrv) tokens = NULL;
gboolean expect_exception = FALSE;
/* handle nothing set */ /* handle nothing set */
if (license == NULL || license[0] == '\0') if (license == NULL || license[0] == '\0')
...@@ -203,10 +244,17 @@ as_is_spdx_license_expression (const gchar *license) ...@@ -203,10 +244,17 @@ as_is_spdx_license_expression (const gchar *license)
tokens = as_spdx_license_tokenize (license); tokens = as_spdx_license_tokenize (license);
if (tokens == NULL) if (tokens == NULL)
return FALSE; return FALSE;
for (i = 0; tokens[i] != NULL; i++) { for (i = 0; tokens[i] != NULL; i++) {
if (tokens[i][0] == '@') { if (tokens[i][0] == '@') {
if (as_is_spdx_license_id (tokens[i] + 1)) if (expect_exception) {
continue; expect_exception = FALSE;
if (as_is_spdx_license_exception_id (tokens[i] + 1))
continue;
} else {
if (as_is_spdx_license_id (tokens[i] + 1))
continue;
}
} }
if (as_is_spdx_license_id (tokens[i])) if (as_is_spdx_license_id (tokens[i]))
continue; continue;
...@@ -216,6 +264,10 @@ as_is_spdx_license_expression (const gchar *license) ...@@ -216,6 +264,10 @@ as_is_spdx_license_expression (const gchar *license)
continue; continue;
if (g_strcmp0 (tokens[i], "+") == 0) if (g_strcmp0 (tokens[i], "+") == 0)
continue; continue;
if (g_strcmp0 (tokens[i], "^") == 0) {
expect_exception = TRUE;
continue;
}
return FALSE; return FALSE;
} }
...@@ -260,9 +312,10 @@ as_utils_spdx_license_2to3 (const gchar *license2) ...@@ -260,9 +312,10 @@ as_utils_spdx_license_2to3 (const gchar *license2)
* @license: a license string, e.g. "LGPLv2+ and (QPL or GPLv2) and MIT" * @license: a license string, e.g. "LGPLv2+ and (QPL or GPLv2) and MIT"
* *
* Tokenizes the SPDX license string (or any simarly formatted string) * Tokenizes the SPDX license string (or any simarly formatted string)
* into parts. Any licence parts of the string e.g. "LGPL-2.0+" are prefexed * into parts. Any license parts of the string e.g. "LGPL-2.0+" are prefexed
* with "@", the conjunctive replaced with "&" and the disjunctive replaced * with "@", the conjunctive replaced with "&", the disjunctive replaced
* with "|". Brackets are added as indervidual tokens and other strings are * with "|" and the WITH operator for license exceptions replaced with "^".
* Brackets are added as indervidual tokens and other strings are
* appended into single tokens where possible. * appended into single tokens where possible.
* *
* Returns: (transfer full) (nullable): array of strings, or %NULL for invalid * Returns: (transfer full) (nullable): array of strings, or %NULL for invalid
...@@ -344,6 +397,10 @@ as_spdx_license_detokenize (gchar **license_tokens) ...@@ -344,6 +397,10 @@ as_spdx_license_detokenize (gchar **license_tokens)
g_string_append (tmp, " OR "); g_string_append (tmp, " OR ");
continue; continue;
} }
if (g_strcmp0 (license_tokens[i], "^") == 0) {
g_string_append (tmp, " WITH ");
continue;
}
if (g_strcmp0 (license_tokens[i], "+") == 0) { if (g_strcmp0 (license_tokens[i], "+") == 0) {
g_string_append (tmp, "+"); g_string_append (tmp, "+");
continue; continue;
...@@ -497,12 +554,18 @@ as_validate_is_content_license_id (const gchar *license_id) ...@@ -497,12 +554,18 @@ as_validate_is_content_license_id (const gchar *license_id)
return TRUE; return TRUE;
if (g_strcmp0 (license_id, "@FSFUL") == 0) if (g_strcmp0 (license_id, "@FSFUL") == 0)
return TRUE; return TRUE;
/* any operators are fine */
if (g_strcmp0 (license_id, "&") == 0) if (g_strcmp0 (license_id, "&") == 0)
return TRUE; return TRUE;
if (g_strcmp0 (license_id, "|") == 0) if (g_strcmp0 (license_id, "|") == 0)
return TRUE; return TRUE;
if (g_strcmp0 (license_id, "+") == 0) if (g_strcmp0 (license_id, "+") == 0)
return TRUE; return TRUE;
/* if there is any license exception involved, we don't have a content license */
if (g_strcmp0 (license_id, "^") == 0)
return FALSE;
return FALSE; return FALSE;
} }
......
...@@ -29,8 +29,9 @@ ...@@ -29,8 +29,9 @@
G_BEGIN_DECLS G_BEGIN_DECLS
gboolean as_is_spdx_license_id (const gchar *license_id); gboolean as_is_spdx_license_id (const gchar *license_id);
gboolean as_is_spdx_license_expression (const gchar *license); gboolean as_is_spdx_license_exception_id (const gchar *exception_id);
gboolean as_is_spdx_license_expression (const gchar *license);
gchar **as_spdx_license_tokenize (const gchar *license); gchar **as_spdx_license_tokenize (const gchar *license);
gchar *as_spdx_license_detokenize (gchar **license_tokens); gchar *as_spdx_license_detokenize (gchar **license_tokens);
......
...@@ -274,20 +274,33 @@ test_spdx (void) ...@@ -274,20 +274,33 @@ test_spdx (void)
g_strfreev (tok); g_strfreev (tok);
g_free (tmp); g_free (tmp);
/* trailing brackets */ /* trailing brackets */
tok = as_spdx_license_tokenize ("MPLv1.1 and (LGPLv3 or GPLv3)"); tok = as_spdx_license_tokenize ("MPLv1.1 and (LGPLv3 or GPLv3)");
tmp = g_strjoinv (" ", tok); tmp = g_strjoinv (" ", tok);
g_assert_cmpstr (tmp, ==, "MPLv1.1 & ( LGPLv3 | GPLv3 )"); g_assert_cmpstr (tmp, ==, "MPLv1.1 & ( LGPLv3 | GPLv3 )");
g_strfreev (tok); g_strfreev (tok);
g_free (tmp); g_free (tmp);
/* deprecated names */ /* deprecated names */
tok = as_spdx_license_tokenize ("CC0 and (CC0 or CC0)"); tok = as_spdx_license_tokenize ("CC0 and (CC0 or CC0)");
tmp = g_strjoinv (" ", tok); tmp = g_strjoinv (" ", tok);
g_assert_cmpstr (tmp, ==, "@CC0-1.0 & ( @CC0-1.0 | @CC0-1.0 )"); g_assert_cmpstr (tmp, ==, "@CC0-1.0 & ( @CC0-1.0 | @CC0-1.0 )");
g_strfreev (tok); g_strfreev (tok);
g_free (tmp); g_free (tmp);
/* WITH operator */
tok = as_spdx_license_tokenize ("GPL-3.0-or-later WITH GCC-exception-3.1");
tmp = g_strjoinv (" ", tok);
g_assert_cmpstr (tmp, ==, "@GPL-3.0+ ^ @GCC-exception-3.1");
g_strfreev (tok);
g_free (tmp);
tok = as_spdx_license_tokenize ("OFL-1.1 OR (GPL-3.0-or-later WITH Font-exception-2.0)");
tmp = g_strjoinv (" ", tok);
g_assert_cmpstr (tmp, ==, "@OFL-1.1 | ( @GPL-3.0+ ^ @Font-exception-2.0 )");
g_strfreev (tok);
g_free (tmp);
/* SPDX strings */ /* SPDX strings */
g_assert (as_is_spdx_license_expression ("CC0-1.0")); g_assert (as_is_spdx_license_expression ("CC0-1.0"));
g_assert (as_is_spdx_license_expression ("CC0")); g_assert (as_is_spdx_license_expression ("CC0"));
...@@ -296,6 +309,8 @@ test_spdx (void) ...@@ -296,6 +309,8 @@ test_spdx (void)
g_assert (as_is_spdx_license_expression ("CC0-1.0 AND GFDL-1.3")); g_assert (as_is_spdx_license_expression ("CC0-1.0 AND GFDL-1.3"));
g_assert (as_is_spdx_license_expression ("CC-BY-SA-3.0+")); g_assert (as_is_spdx_license_expression ("CC-BY-SA-3.0+"));
g_assert (as_is_spdx_license_expression ("CC-BY-SA-3.0+ AND Zlib")); g_assert (as_is_spdx_license_expression ("CC-BY-SA-3.0+ AND Zlib"));
g_assert (as_is_spdx_license_expression ("GPL-3.0-or-later WITH GCC-exception-3.1"));
g_assert (as_is_spdx_license_expression ("GPL-3.0-or-later WITH Font-exception-2.0 AND OFL-1.1"));
g_assert (as_is_spdx_license_expression ("NOASSERTION")); g_assert (as_is_spdx_license_expression ("NOASSERTION"));
g_assert (!as_is_spdx_license_expression ("CC0 dave")); g_assert (!as_is_spdx_license_expression ("CC0 dave"));
g_assert (!as_is_spdx_license_expression ("")); g_assert (!as_is_spdx_license_expression (""));
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment