Blame src/xb-silo-export.c

Packit Service a3c5fa
/*
Packit Service a3c5fa
 * Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
Packit Service a3c5fa
 *
Packit Service a3c5fa
 * SPDX-License-Identifier: LGPL-2.1+
Packit Service a3c5fa
 */
Packit Service a3c5fa
Packit Service a3c5fa
#define G_LOG_DOMAIN				"XbSilo"
Packit Service a3c5fa
Packit Service a3c5fa
#include "config.h"
Packit Service a3c5fa
Packit Service a3c5fa
#include <gio/gio.h>
Packit Service a3c5fa
Packit Service a3c5fa
#include "xb-node-private.h"
Packit Service a3c5fa
#include "xb-silo-export-private.h"
Packit Service a3c5fa
#include "xb-silo-private.h"
Packit Service a3c5fa
#include "xb-string-private.h"
Packit Service a3c5fa
Packit Service a3c5fa
typedef struct {
Packit Service a3c5fa
	GString			*xml;
Packit Service a3c5fa
	XbNodeExportFlags	 flags;
Packit Service a3c5fa
	guint32			 off;
Packit Service a3c5fa
	guint			 level;
Packit Service a3c5fa
} XbSiloExportHelper;
Packit Service a3c5fa
Packit Service a3c5fa
static gboolean
Packit Service a3c5fa
xb_silo_export_node (XbSilo *self, XbSiloExportHelper *helper, XbSiloNode *sn, GError **error)
Packit Service a3c5fa
{
Packit Service a3c5fa
	XbSiloNode *sn2;
Packit Service a3c5fa
Packit Service a3c5fa
	helper->off = xb_silo_get_offset_for_node (self, sn);
Packit Service a3c5fa
Packit Service a3c5fa
	/* add start of opening tag */
Packit Service a3c5fa
	if (helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_INDENT) {
Packit Service a3c5fa
		for (guint i = 0; i < helper->level; i++)
Packit Service a3c5fa
			g_string_append (helper->xml, "  ");
Packit Service a3c5fa
	}
Packit Service a3c5fa
	g_string_append_printf (helper->xml, "<%s",
Packit Service a3c5fa
				xb_silo_from_strtab (self, sn->element_name));
Packit Service a3c5fa
Packit Service a3c5fa
	/* add any attributes */
Packit Service a3c5fa
	for (guint8 i = 0; i < sn->nr_attrs; i++) {
Packit Service a3c5fa
		XbSiloAttr *a = xb_silo_get_attr (self, helper->off, i);
Packit Service a3c5fa
		g_autofree gchar *key = xb_string_xml_escape (xb_silo_from_strtab (self, a->attr_name));
Packit Service a3c5fa
		g_autofree gchar *val = xb_string_xml_escape (xb_silo_from_strtab (self, a->attr_value));
Packit Service a3c5fa
		g_string_append_printf (helper->xml, " %s=\"%s\"", key, val);
Packit Service a3c5fa
	}
Packit Service a3c5fa
Packit Service a3c5fa
	/* finish the opening tag and add any text if it exists */
Packit Service a3c5fa
	if (sn->text != XB_SILO_UNSET) {
Packit Service a3c5fa
		g_autofree gchar *text = xb_string_xml_escape (xb_silo_from_strtab (self, sn->text));
Packit Service a3c5fa
		g_string_append (helper->xml, ">");
Packit Service a3c5fa
		g_string_append (helper->xml, text);
Packit Service a3c5fa
	} else {
Packit Service a3c5fa
		g_string_append (helper->xml, ">");
Packit Service a3c5fa
		if (helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE)
Packit Service a3c5fa
			g_string_append (helper->xml, "\n");
Packit Service a3c5fa
	}
Packit Service a3c5fa
	helper->off += xb_silo_node_get_size (sn);
Packit Service a3c5fa
Packit Service a3c5fa
	/* recurse deeper */
Packit Service a3c5fa
	while (xb_silo_get_node(self, helper->off)->is_node) {
Packit Service a3c5fa
		XbSiloNode *child = xb_silo_get_node (self, helper->off);
Packit Service a3c5fa
		helper->level++;
Packit Service a3c5fa
		if (!xb_silo_export_node (self, helper, child, error))
Packit Service a3c5fa
			return FALSE;
Packit Service a3c5fa
		helper->level--;
Packit Service a3c5fa
	}
Packit Service a3c5fa
Packit Service a3c5fa
	/* check for the single byte sentinel */
Packit Service a3c5fa
	sn2 = xb_silo_get_node (self, helper->off);
Packit Service a3c5fa
	if (sn2->is_node) {
Packit Service a3c5fa
		g_set_error (error,
Packit Service a3c5fa
			     G_IO_ERROR,
Packit Service a3c5fa
			     G_IO_ERROR_INVALID_DATA,
Packit Service a3c5fa
			     "no seninel at %" G_GUINT32_FORMAT,
Packit Service a3c5fa
			     helper->off);
Packit Service a3c5fa
		return FALSE;
Packit Service a3c5fa
	}
Packit Service a3c5fa
	helper->off += xb_silo_node_get_size (sn2);
Packit Service a3c5fa
Packit Service a3c5fa
	/* add closing tag */
Packit Service a3c5fa
	if ((helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_INDENT) > 0 &&
Packit Service a3c5fa
	    sn->text == XB_SILO_UNSET) {
Packit Service a3c5fa
		for (guint i = 0; i < helper->level; i++)
Packit Service a3c5fa
			g_string_append (helper->xml, "  ");
Packit Service a3c5fa
	}
Packit Service a3c5fa
	g_string_append_printf (helper->xml, "</%s>",
Packit Service a3c5fa
				xb_silo_from_strtab (self, sn->element_name));
Packit Service a3c5fa
Packit Service a3c5fa
	/* add any optional tail */
Packit Service a3c5fa
	if (sn->tail != XB_SILO_UNSET) {
Packit Service a3c5fa
		g_autofree gchar *tail = xb_string_xml_escape (xb_silo_from_strtab (self, sn->tail));
Packit Service a3c5fa
		g_string_append (helper->xml, tail);
Packit Service a3c5fa
	}
Packit Service a3c5fa
Packit Service a3c5fa
	if (helper->flags & XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE)
Packit Service a3c5fa
		g_string_append (helper->xml, "\n");
Packit Service a3c5fa
Packit Service a3c5fa
	return TRUE;
Packit Service a3c5fa
}
Packit Service a3c5fa
Packit Service a3c5fa
/* private */
Packit Service a3c5fa
GString *
Packit Service a3c5fa
xb_silo_export_with_root (XbSilo *self, XbNode *root, XbNodeExportFlags flags, GError **error)
Packit Service a3c5fa
{
Packit Service a3c5fa
	XbSiloNode *sn;
Packit Service a3c5fa
	XbSiloExportHelper helper = {
Packit Service a3c5fa
		.flags		= flags,
Packit Service a3c5fa
		.level		= 0,
Packit Service a3c5fa
		.off		= sizeof(XbSiloHeader),
Packit Service a3c5fa
	};
Packit Service a3c5fa
Packit Service a3c5fa
	g_return_val_if_fail (XB_IS_SILO (self), NULL);
Packit Service a3c5fa
Packit Service a3c5fa
	/* this implies the other */
Packit Service a3c5fa
	if (flags & XB_NODE_EXPORT_FLAG_ONLY_CHILDREN)
Packit Service a3c5fa
		flags |= XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS;
Packit Service a3c5fa
Packit Service a3c5fa
	/* optional subtree export */
Packit Service a3c5fa
	if (root != NULL) {
Packit Service a3c5fa
		sn = xb_node_get_sn (root);
Packit Service a3c5fa
		if (sn != NULL && flags & XB_NODE_EXPORT_FLAG_ONLY_CHILDREN)
Packit Service a3c5fa
			sn = xb_silo_node_get_child (self, sn);
Packit Service a3c5fa
	} else {
Packit Service a3c5fa
		sn = xb_silo_get_sroot (self);
Packit Service a3c5fa
	}
Packit Service a3c5fa
Packit Service a3c5fa
	/* no root */
Packit Service a3c5fa
	if (sn == NULL) {
Packit Service a3c5fa
		g_set_error_literal (error,
Packit Service a3c5fa
				     G_IO_ERROR,
Packit Service a3c5fa
				     G_IO_ERROR_NOT_FOUND,
Packit Service a3c5fa
				     "no data to export");
Packit Service a3c5fa
		return NULL;
Packit Service a3c5fa
	}
Packit Service a3c5fa
Packit Service a3c5fa
	/* root node */
Packit Service a3c5fa
	helper.xml = g_string_new (NULL);
Packit Service a3c5fa
	if ((flags & XB_NODE_EXPORT_FLAG_ADD_HEADER) > 0)
Packit Service a3c5fa
		g_string_append (helper.xml, "\n");
Packit Service a3c5fa
	do {
Packit Service a3c5fa
		if (!xb_silo_export_node (self, &helper, sn, error)) {
Packit Service a3c5fa
			g_string_free (helper.xml, TRUE);
Packit Service a3c5fa
			return NULL;
Packit Service a3c5fa
		}
Packit Service a3c5fa
		if ((flags & XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS) == 0)
Packit Service a3c5fa
			break;
Packit Service a3c5fa
		sn = xb_silo_node_get_next (self, sn);
Packit Service a3c5fa
	} while (sn != NULL);
Packit Service a3c5fa
Packit Service a3c5fa
	/* success */
Packit Service a3c5fa
	return helper.xml;
Packit Service a3c5fa
}
Packit Service a3c5fa
Packit Service a3c5fa
/**
Packit Service a3c5fa
 * xb_silo_export:
Packit Service a3c5fa
 * @self: a #XbSilo
Packit Service a3c5fa
 * @flags: some #XbNodeExportFlags, e.g. #XB_NODE_EXPORT_FLAG_NONE
Packit Service a3c5fa
 * @error: the #GError, or %NULL
Packit Service a3c5fa
 *
Packit Service a3c5fa
 * Exports the silo back to XML.
Packit Service a3c5fa
 *
Packit Service a3c5fa
 * Returns: XML data, or %NULL for an error
Packit Service a3c5fa
 *
Packit Service a3c5fa
 * Since: 0.1.0
Packit Service a3c5fa
 **/
Packit Service a3c5fa
gchar *
Packit Service a3c5fa
xb_silo_export (XbSilo *self, XbNodeExportFlags flags, GError **error)
Packit Service a3c5fa
{
Packit Service a3c5fa
	GString *xml;
Packit Service a3c5fa
	g_return_val_if_fail (XB_IS_SILO (self), NULL);
Packit Service a3c5fa
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Packit Service a3c5fa
	xml = xb_silo_export_with_root (self, NULL, flags, error);
Packit Service a3c5fa
	if (xml == NULL)
Packit Service a3c5fa
		return NULL;
Packit Service a3c5fa
	return g_string_free (xml, FALSE);
Packit Service a3c5fa
}
Packit Service a3c5fa
Packit Service a3c5fa
/**
Packit Service a3c5fa
 * xb_silo_export_file:
Packit Service a3c5fa
 * @self: a #XbSilo
Packit Service a3c5fa
 * @file: a #GFile
Packit Service a3c5fa
 * @flags: some #XbNodeExportFlags, e.g. #XB_NODE_EXPORT_FLAG_NONE
Packit Service a3c5fa
 * @cancellable: a #GCancellable, or %NULL
Packit Service a3c5fa
 * @error: the #GError, or %NULL
Packit Service a3c5fa
 *
Packit Service a3c5fa
 * Exports the silo back to an XML file.
Packit Service a3c5fa
 *
Packit Service a3c5fa
 * Returns: %TRUE on success
Packit Service a3c5fa
 *
Packit Service a3c5fa
 * Since: 0.1.2
Packit Service a3c5fa
 **/
Packit Service a3c5fa
gboolean
Packit Service a3c5fa
xb_silo_export_file (XbSilo *self,
Packit Service a3c5fa
		     GFile *file,
Packit Service a3c5fa
		     XbNodeExportFlags flags,
Packit Service a3c5fa
		     GCancellable *cancellable,
Packit Service a3c5fa
		     GError **error)
Packit Service a3c5fa
{
Packit Service a3c5fa
	g_autoptr(GString) xml = NULL;
Packit Service a3c5fa
Packit Service a3c5fa
	g_return_val_if_fail (XB_IS_SILO (self), FALSE);
Packit Service a3c5fa
	g_return_val_if_fail (G_IS_FILE (file), FALSE);
Packit Service a3c5fa
	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
Packit Service a3c5fa
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
Packit Service a3c5fa
Packit Service a3c5fa
	xml = xb_silo_export_with_root (self, NULL, flags, error);
Packit Service a3c5fa
	if (xml == NULL)
Packit Service a3c5fa
		return FALSE;
Packit Service a3c5fa
	return g_file_replace_contents (file,
Packit Service a3c5fa
					xml->str,
Packit Service a3c5fa
					xml->len,
Packit Service a3c5fa
					NULL, /* etag */
Packit Service a3c5fa
					FALSE, /* make-backup */
Packit Service a3c5fa
					G_FILE_CREATE_NONE,
Packit Service a3c5fa
					NULL, /* new etag */
Packit Service a3c5fa
					cancellable,
Packit Service a3c5fa
					error);
Packit Service a3c5fa
}