Blame src/xb-string.c

Packit caecb6
/*
Packit caecb6
 * Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
Packit caecb6
 *
Packit caecb6
 * SPDX-License-Identifier: LGPL-2.1+
Packit caecb6
 */
Packit caecb6
Packit caecb6
#define G_LOG_DOMAIN				"XbSilo"
Packit caecb6
Packit caecb6
#include "config.h"
Packit caecb6
Packit caecb6
#include <string.h>
Packit caecb6
#include <gio/gio.h>
Packit caecb6
Packit caecb6
#include "xb-string-private.h"
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_string_replace: (skip)
Packit caecb6
 * @str: The #GString to operate on
Packit caecb6
 * @search: The text to search for
Packit caecb6
 * @replace: The text to use for substitutions
Packit caecb6
 *
Packit caecb6
 * Performs multiple search and replace operations on the given string.
Packit caecb6
 *
Packit caecb6
 * Returns: the number of replacements done, or 0 if @search is not found.
Packit caecb6
 **/
Packit caecb6
guint
Packit caecb6
xb_string_replace (GString *str, const gchar *search, const gchar *replace)
Packit caecb6
{
Packit caecb6
	gchar *tmp;
Packit caecb6
	guint count = 0;
Packit caecb6
	gsize search_idx = 0;
Packit caecb6
	gsize replace_len;
Packit caecb6
	gsize search_len;
Packit caecb6
Packit caecb6
	g_return_val_if_fail (str != NULL, 0);
Packit caecb6
	g_return_val_if_fail (search != NULL, 0);
Packit caecb6
	g_return_val_if_fail (replace != NULL, 0);
Packit caecb6
Packit caecb6
	/* nothing to do */
Packit caecb6
	if (str->len == 0)
Packit caecb6
		return 0;
Packit caecb6
Packit caecb6
	search_len = strlen (search);
Packit caecb6
	replace_len = strlen (replace);
Packit caecb6
Packit caecb6
	do {
Packit caecb6
		tmp = g_strstr_len (str->str + search_idx, -1, search);
Packit caecb6
		if (tmp == NULL)
Packit caecb6
			break;
Packit caecb6
Packit caecb6
		/* advance the counter in case @replace contains @search */
Packit caecb6
		search_idx = (gsize) (tmp - str->str);
Packit caecb6
Packit caecb6
		/* reallocate the string if required */
Packit caecb6
		if (search_len > replace_len) {
Packit caecb6
			g_string_erase (str,
Packit caecb6
					(gssize) search_idx,
Packit caecb6
					(gssize) (search_len - replace_len));
Packit caecb6
			memcpy (tmp, replace, replace_len);
Packit caecb6
		} else if (search_len < replace_len) {
Packit caecb6
			g_string_insert_len (str,
Packit caecb6
					     (gssize) search_idx,
Packit caecb6
					     replace,
Packit caecb6
					     (gssize) (replace_len - search_len));
Packit caecb6
			/* we have to treat this specially as it could have
Packit caecb6
			 * been reallocated when the insertion happened */
Packit caecb6
			memcpy (str->str + search_idx, replace, replace_len);
Packit caecb6
		} else {
Packit caecb6
			/* just memcmp in the new string */
Packit caecb6
			memcpy (tmp, replace, replace_len);
Packit caecb6
		}
Packit caecb6
		search_idx += replace_len;
Packit caecb6
		count++;
Packit caecb6
	} while (TRUE);
Packit caecb6
Packit caecb6
	return count;
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_string_append_union:
Packit caecb6
 * @xpath: The #GString to operate on
Packit caecb6
 * @fmt: The format string
Packit caecb6
 * @...: varargs for @fmt
Packit caecb6
 *
Packit caecb6
 * Appends an XPath query into the string, automatically adding the union
Packit caecb6
 * operator (`|`) if required.
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.2
Packit caecb6
 **/
Packit caecb6
void
Packit caecb6
xb_string_append_union (GString *xpath, const gchar *fmt, ...)
Packit caecb6
{
Packit caecb6
	va_list args;
Packit caecb6
Packit caecb6
	g_return_if_fail (xpath != NULL);
Packit caecb6
	g_return_if_fail (fmt != NULL);
Packit caecb6
Packit caecb6
	if (xpath->len > 0)
Packit caecb6
		g_string_append (xpath, "|");
Packit caecb6
	va_start (args, fmt);
Packit caecb6
#pragma clang diagnostic ignored "-Wformat-nonliteral"
Packit caecb6
	g_string_append_vprintf (xpath, fmt, args);
Packit caecb6
#pragma clang diagnostic pop
Packit caecb6
	va_end (args);
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_string_contains: (skip)
Packit caecb6
 * @text: The source string
Packit caecb6
 * @search: The text to search for
Packit caecb6
 *
Packit caecb6
 * Searches for a substring match.
Packit caecb6
 *
Packit caecb6
 * Returns: %TRUE if the string @search is contained in @text.
Packit caecb6
 **/
Packit caecb6
gboolean
Packit caecb6
xb_string_contains (const gchar *text, const gchar *search)
Packit caecb6
{
Packit caecb6
	guint search_sz;
Packit caecb6
	guint text_sz;
Packit caecb6
Packit caecb6
	/* can't possibly match */
Packit caecb6
	if (text == NULL || search == NULL)
Packit caecb6
		return FALSE;
Packit caecb6
Packit caecb6
	/* sanity check */
Packit caecb6
	text_sz = strlen (text);
Packit caecb6
	search_sz = strlen (search);
Packit caecb6
	if (search_sz > text_sz)
Packit caecb6
		return FALSE;
Packit caecb6
	for (guint i = 0; i < text_sz - search_sz + 1; i++) {
Packit caecb6
		if (strncmp (text + i, search, search_sz) == 0)
Packit caecb6
			return TRUE;
Packit caecb6
	}
Packit caecb6
	return FALSE;
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_string_search: (skip)
Packit caecb6
 * @text: The source string
Packit caecb6
 * @search: The text to search for
Packit caecb6
 *
Packit caecb6
 * Searches for a fuzzy search match, ignoring search matches that are not at
Packit caecb6
 * the start of the token.
Packit caecb6
 *
Packit caecb6
 * Returns: %TRUE if the string @search is contained in @text.
Packit caecb6
 **/
Packit caecb6
gboolean
Packit caecb6
xb_string_search (const gchar *text, const gchar *search)
Packit caecb6
{
Packit caecb6
	guint search_sz;
Packit caecb6
	guint text_sz;
Packit caecb6
	gboolean is_sow = TRUE;
Packit caecb6
Packit caecb6
	/* can't possibly match */
Packit caecb6
	if (text == NULL || text[0] == '\0')
Packit caecb6
		return FALSE;
Packit caecb6
	if (search == NULL || search[0] == '\0')
Packit caecb6
		return FALSE;
Packit caecb6
Packit caecb6
	/* sanity check */
Packit caecb6
	text_sz = strlen (text);
Packit caecb6
	search_sz = strlen (search);
Packit caecb6
	if (search_sz > text_sz)
Packit caecb6
		return FALSE;
Packit caecb6
	for (guint i = 0; i < text_sz - search_sz + 1; i++) {
Packit caecb6
		if (!g_ascii_isalnum (text[i])) {
Packit caecb6
			is_sow = TRUE;
Packit caecb6
			continue;
Packit caecb6
		}
Packit caecb6
		if (!is_sow)
Packit caecb6
			continue;
Packit caecb6
		if (g_ascii_strncasecmp (text + i, search, search_sz) == 0)
Packit caecb6
			return TRUE;
Packit caecb6
		/* no longer the start of the word */
Packit caecb6
		is_sow = FALSE;
Packit caecb6
	}
Packit caecb6
	return FALSE;
Packit caecb6
}
Packit caecb6
Packit caecb6
/**
Packit caecb6
 * xb_string_escape:
Packit caecb6
 * @str: string, e.g. `app/org.gnome.ghex/x86_64/stable`
Packit caecb6
 *
Packit caecb6
 * Escapes XPath control sequences such as newlines, tabs, and forward slashes.
Packit caecb6
 *
Packit caecb6
 * Returns: (transfer full): new string that is safe to use for queries
Packit caecb6
 *
Packit caecb6
 * Since: 0.1.2
Packit caecb6
 **/
Packit caecb6
gchar *
Packit caecb6
xb_string_escape (const gchar *str)
Packit caecb6
{
Packit caecb6
	GString *tmp = g_string_new (str);
Packit caecb6
	xb_string_replace (tmp, "/", "\\/");
Packit caecb6
	xb_string_replace (tmp, "\t", "\\t");
Packit caecb6
	xb_string_replace (tmp, "\n", "\\n");
Packit caecb6
	return g_string_free (tmp, FALSE);
Packit caecb6
}
Packit caecb6
Packit caecb6
gchar *
Packit caecb6
xb_string_xml_escape (const gchar *str)
Packit caecb6
{
Packit caecb6
	GString *tmp = g_string_new (str);
Packit caecb6
	xb_string_replace (tmp, "&", "&");
Packit caecb6
	xb_string_replace (tmp, "<", "<");
Packit caecb6
	xb_string_replace (tmp, ">", ">");
Packit caecb6
	xb_string_replace (tmp, "\"", """);
Packit caecb6
	return g_string_free (tmp, FALSE);
Packit caecb6
}
Packit caecb6
Packit caecb6
/* private */
Packit caecb6
gboolean
Packit caecb6
xb_string_isspace (const gchar *str, gssize strsz)
Packit caecb6
{
Packit caecb6
	gsize strsz_safe;
Packit caecb6
	if (str == NULL)
Packit caecb6
		return TRUE;
Packit caecb6
	strsz_safe = strsz >= 0 ? (gsize) strsz : strlen (str);
Packit caecb6
	for (gsize i = 0; i < strsz_safe; i++) {
Packit caecb6
		if (!g_ascii_isspace (str[i]))
Packit caecb6
			return FALSE;
Packit caecb6
	}
Packit caecb6
	return TRUE;
Packit caecb6
}
Packit caecb6
Packit caecb6
void
Packit caecb6
xb_guid_compute_for_data (XbGuid *out, const guint8 *buf, gsize bufsz)
Packit caecb6
{
Packit caecb6
	guint8 buf_tmp[20] = { 0x0 };
Packit caecb6
	gsize buf_tmpsz = sizeof(buf_tmp);
Packit caecb6
	g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA1);
Packit caecb6
	if (buf != NULL && bufsz != 0)
Packit caecb6
		g_checksum_update (checksum, (const guchar *) buf, bufsz);
Packit caecb6
	g_checksum_get_digest (checksum, buf_tmp, &buf_tmpsz);
Packit caecb6
	memcpy (out, buf_tmp, sizeof(XbGuid));
Packit caecb6
}
Packit caecb6
Packit caecb6
gchar *
Packit caecb6
xb_guid_to_string (XbGuid *guid)
Packit caecb6
{
Packit caecb6
	return g_strdup_printf ("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
Packit caecb6
				(guint) GUINT32_TO_BE (guid->tlo),
Packit caecb6
				(guint) GUINT16_TO_BE (guid->tmi),
Packit caecb6
				(guint) GUINT16_TO_BE (guid->thi),
Packit caecb6
				(guint) GUINT16_TO_BE (guid->clo),
Packit caecb6
				guid->nde[0], guid->nde[1],
Packit caecb6
				guid->nde[2], guid->nde[3],
Packit caecb6
				guid->nde[4], guid->nde[5]);
Packit caecb6
}