summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2014-03-21 20:28:50 +0000
committerRichard Hughes <richard@hughsie.com>2014-08-20 10:01:26 +0100
commitc63ae20cbbfe7e241fca2775f4df05da85335d13 (patch)
treee8f3a00955a29eb6ad285b2d6bf93bdb06c73601
parent451a7489300e19221bb5bed6da182839e2e3ad69 (diff)
downloadappstream-glib-c63ae20cbbfe7e241fca2775f4df05da85335d13.tar.gz
Use libyaml to read DEP-11 metadata
This is optional, but enabled by default. Use --disable-dep11 to remove the libyaml dependancy.
-rw-r--r--Makefile.am4
-rw-r--r--README.md5
-rw-r--r--client/as-util.c9
-rw-r--r--configure.ac14
-rw-r--r--contrib/libappstream-glib.spec.in1
-rw-r--r--data/tests/Makefile.am2
-rw-r--r--data/tests/example-v06.yml.gzbin0 -> 55531 bytes
-rw-r--r--data/tests/example.yml31
-rw-r--r--libappstream-glib/Makefile.am9
-rw-r--r--libappstream-glib/as-app-private.h3
-rw-r--r--libappstream-glib/as-app.c139
-rw-r--r--libappstream-glib/as-cleanup.h3
-rw-r--r--libappstream-glib/as-image-private.h3
-rw-r--r--libappstream-glib/as-image.c31
-rw-r--r--libappstream-glib/as-node.h2
-rw-r--r--libappstream-glib/as-provide-private.h3
-rw-r--r--libappstream-glib/as-provide.c19
-rw-r--r--libappstream-glib/as-screenshot-private.h3
-rw-r--r--libappstream-glib/as-screenshot.c51
-rw-r--r--libappstream-glib/as-self-test.c209
-rw-r--r--libappstream-glib/as-store.c79
-rw-r--r--libappstream-glib/as-yaml.c381
-rw-r--r--libappstream-glib/as-yaml.h49
23 files changed, 1033 insertions, 17 deletions
diff --git a/Makefile.am b/Makefile.am
index 6664264..de6d6a6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,7 +65,9 @@ ChangeLog:
echo A git checkout and git-log is required to generate this file >> $@); \
fi
-DISTCHECK_CONFIGURE_FLAGS = --with-bashcompletiondir=/${prefix}/share/bash-completion/completions
+DISTCHECK_CONFIGURE_FLAGS = \
+ --enable-dep11 \
+ --with-bashcompletiondir=/${prefix}/share/bash-completion/completions
.PHONY: ChangeLog
diff --git a/README.md b/README.md
index 221652b..9a1f004 100644
--- a/README.md
+++ b/README.md
@@ -4,11 +4,12 @@ AppStream-Glib
This library provides GObjects and helper methods to make it easy to read and
write AppStream metadata. It also provides a simple DOM implementation that
makes it easy to edit nodes and convert to and from the standardized XML
-representation.
+representation. It also supports reading of Debian-style DEP-11 metadata.
What this library allows you to do:
* Read and write compressed AppStream XML files
+ * Read compressed Debian YAML files
* Add and search for applications in an application store
* Get screenshot image data and release announcements
* Easily retrieve the best application data for the current locale
@@ -27,7 +28,7 @@ copy. To do the latter just do:
dnf install automake autoconf libtool glib-devel docbook-utils \
gtk-doc gobject-introspection-devel rpm-devel \
gtk3-devel sqlite-devel libsoup-devel gettext-devel \
- intltool libarchive-devel
+ intltool libarchive-devel libyaml-devel
./autogen.sh
make
make install
diff --git a/client/as-util.c b/client/as-util.c
index 9533db8..ce9c32a 100644
--- a/client/as-util.c
+++ b/client/as-util.c
@@ -942,8 +942,13 @@ as_util_install_filename (const gchar *filename,
switch (as_app_guess_source_kind (filename)) {
case AS_APP_SOURCE_KIND_APPSTREAM:
- ret = as_util_install_xml (filename, origin,
- "/usr/share/app-info/xmls", error);
+ if (g_strstr_len (filename, -1, ".yml.gz") != NULL) {
+ ret = as_util_install_xml (filename, origin,
+ "/usr/share/app-info/yaml", error);
+ } else {
+ ret = as_util_install_xml (filename, origin,
+ "/usr/share/app-info/xmls", error);
+ }
break;
case AS_APP_SOURCE_KIND_APPDATA:
case AS_APP_SOURCE_KIND_METAINFO:
diff --git a/configure.ac b/configure.ac
index bbabd97..4dd97bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -167,6 +167,19 @@ AS_IF([test "x$enable_man" != "xno"],
[have_manutils=no])
AM_CONDITIONAL([ENABLE_MAN], [test "x$have_manutils" = "xyes"])
+dnl ---------------------------------------------------------------------------
+dnl - Use libyaml for DEP-11 support
+dnl ---------------------------------------------------------------------------
+AC_ARG_ENABLE(dep11, AS_HELP_STRING([--enable-dep11],[enable DEP-11]),
+ enable_dep11=$enableval,enable_dep11=yes)
+AM_CONDITIONAL(HAVE_DEP11, test x$enable_dep11 = xyes)
+if test x$enable_dep11 = xyes; then
+ AC_CHECK_HEADER(yaml.h, [], [AC_MSG_ERROR([No yaml.h])])
+ YAML_LIBS="-lyaml"
+ AC_SUBST(YAML_LIBS)
+ AC_DEFINE(PK_BUILD_DEP11,1,[Build DEP-11 code])
+fi
+
AC_CONFIG_FILES([
Makefile
client/Makefile
@@ -193,5 +206,6 @@ AC_MSG_RESULT([
datarootdir: ${datarootdir}
includedir: ${includedir}
lib dir: ${libdir}
+ DEP-11 support: ${enable_dep11}
Bash completion dir: ${with_bashcompletiondir}
])
diff --git a/contrib/libappstream-glib.spec.in b/contrib/libappstream-glib.spec.in
index 0c0c6bd..e5d2852 100644
--- a/contrib/libappstream-glib.spec.in
+++ b/contrib/libappstream-glib.spec.in
@@ -66,6 +66,7 @@ GLib headers and libraries for appstream-builder.
%build
%configure \
--enable-gtk-doc \
+ --disable-dep11 \
--disable-static \
--disable-silent-rules \
--disable-dependency-tracking
diff --git a/data/tests/Makefile.am b/data/tests/Makefile.am
index e1b8878..ff6a4db 100644
--- a/data/tests/Makefile.am
+++ b/data/tests/Makefile.am
@@ -14,6 +14,8 @@ test_files = \
example.desktop \
example.metainfo.xml \
example-v04.xml.gz \
+ example-v06.yml.gz \
+ example.yml \
intltool.appdata.xml.in \
origin.xml \
settings-panel.desktop \
diff --git a/data/tests/example-v06.yml.gz b/data/tests/example-v06.yml.gz
new file mode 100644
index 0000000..d8861e9
--- /dev/null
+++ b/data/tests/example-v06.yml.gz
Binary files differ
diff --git a/data/tests/example.yml b/data/tests/example.yml
new file mode 100644
index 0000000..3baa476
--- /dev/null
+++ b/data/tests/example.yml
@@ -0,0 +1,31 @@
+---
+File: DEP-11
+Origin: aequorea
+Version: '0.6'
+---
+Type: desktop-app
+ID: iceweasel.desktop
+Name:
+ C: Iceweasel
+Packages:
+ - iceweasel
+Icon:
+ cached: iceweasel.png
+Keywords:
+ C:
+ - browser
+Screenshots:
+ - default: true
+ source-image:
+ height: 770
+ url: http://localhost/source/screenshot.png
+ width: 1026
+ thumbnails:
+ - height: 423
+ url: http://localhost/752x423/screenshot.png
+ width: 752
+---
+Type: desktop-app
+ID: dave.desktop
+Name:
+ C: dave
diff --git a/libappstream-glib/Makefile.am b/libappstream-glib/Makefile.am
index eeb0ca0..28e6e8d 100644
--- a/libappstream-glib/Makefile.am
+++ b/libappstream-glib/Makefile.am
@@ -9,6 +9,7 @@ AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(GDKPIXBUF_CFLAGS) \
$(SOUP_CFLAGS) \
+ $(YAML_CFLAGS) \
-I$(top_srcdir)/libappstream-glib \
-I$(top_builddir)/libappstream-glib \
-I. \
@@ -102,7 +103,9 @@ libappstream_glib_la_SOURCES = \
as-tag.c \
as-utils.c \
as-utils-private.h \
- as-version.h
+ as-version.h \
+ as-yaml.c \
+ as-yaml.h
if HAVE_GPERF
libappstream_glib_la_SOURCES += as-tag-private.h
@@ -114,7 +117,8 @@ CLEANFILES = $(BUILT_SOURCES)
libappstream_glib_la_LIBADD = \
$(GLIB_LIBS) \
$(GDKPIXBUF_LIBS) \
- $(SOUP_LIBS)
+ $(SOUP_LIBS) \
+ $(YAML_LIBS)
libappstream_glib_la_LDFLAGS = \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
@@ -133,6 +137,7 @@ as_self_test_LDADD = \
$(GLIB_LIBS) \
$(GDKPIXBUF_LIBS) \
$(SOUP_LIBS) \
+ $(YAML_LIBS) \
$(lib_LTLIBRARIES)
as_self_test_CFLAGS = $(WARNINGFLAGS_C)
diff --git a/libappstream-glib/as-app-private.h b/libappstream-glib/as-app-private.h
index c973fe1..bf33c19 100644
--- a/libappstream-glib/as-app-private.h
+++ b/libappstream-glib/as-app-private.h
@@ -79,6 +79,9 @@ GNode *as_app_node_insert (AsApp *app,
gboolean as_app_node_parse (AsApp *app,
GNode *node,
GError **error);
+gboolean as_app_node_parse_dep11 (AsApp *app,
+ GNode *node,
+ GError **error);
G_END_DECLS
diff --git a/libappstream-glib/as-app.c b/libappstream-glib/as-app.c
index 40bd913..0da8e47 100644
--- a/libappstream-glib/as-app.c
+++ b/libappstream-glib/as-app.c
@@ -46,6 +46,7 @@
#include "as-screenshot-private.h"
#include "as-tag.h"
#include "as-utils-private.h"
+#include "as-yaml.h"
typedef struct _AsAppPrivate AsAppPrivate;
struct _AsAppPrivate
@@ -214,6 +215,8 @@ as_app_guess_source_kind (const gchar *filename)
{
if (g_str_has_suffix (filename, ".xml.gz"))
return AS_APP_SOURCE_KIND_APPSTREAM;
+ if (g_str_has_suffix (filename, ".yml.gz"))
+ return AS_APP_SOURCE_KIND_APPSTREAM;
if (g_str_has_suffix (filename, ".desktop"))
return AS_APP_SOURCE_KIND_DESKTOP;
if (g_str_has_suffix (filename, ".desktop.in"))
@@ -2646,6 +2649,8 @@ as_app_node_insert_keywords (AsApp *app, GNode *parent, gdouble api_version)
g_ptr_array_sort (keywords, as_app_ptr_array_sort_cb);
for (i = 0; i < keywords->len; i++) {
tmp = g_ptr_array_index (keywords, i);
+ if (tmp == NULL)
+ continue;
if (g_strcmp0 (lang, "C") != 0 &&
g_hash_table_lookup (already_in_c, tmp) != NULL)
continue;
@@ -3348,6 +3353,140 @@ as_app_node_parse (AsApp *app, GNode *node, GError **error)
return as_app_node_parse_full (app, node, AS_APP_PARSE_FLAG_NONE, error);
}
+/**
+ * as_app_node_parse_dep11:
+ * @app: a #AsApp instance.
+ * @node: a #GNode.
+ * @error: A #GError or %NULL.
+ *
+ * Populates the object from a DEP-11 node.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.3.0
+ **/
+gboolean
+as_app_node_parse_dep11 (AsApp *app, GNode *node, GError **error)
+{
+ GNode *c;
+ GNode *c2;
+ GNode *n;
+ const gchar *tmp;
+
+ for (n = node->children; n != NULL; n = n->next) {
+ tmp = as_yaml_node_get_key (n);
+ if (g_strcmp0 (tmp, "ID") == 0) {
+ as_app_set_id_full (app, as_yaml_node_get_value (n), -1);
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Type") == 0) {
+ if (g_strcmp0 (as_yaml_node_get_value (n), "desktop-app") == 0) {
+ as_app_set_id_kind (app, AS_ID_KIND_DESKTOP);
+ continue;
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Packages") == 0) {
+ for (c = n->children; c != NULL; c = c->next)
+ as_app_add_pkgname (app, as_yaml_node_get_key (c), -1);
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Name") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ as_app_set_name (app,
+ as_yaml_node_get_key (c),
+ as_yaml_node_get_value (c), -1);
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Summary") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ as_app_set_comment (app,
+ as_yaml_node_get_key (c),
+ as_yaml_node_get_value (c), -1);
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Description") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ as_app_set_description (app,
+ as_yaml_node_get_key (c),
+ as_yaml_node_get_value (c), -1);
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Keywords") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ for (c2 = c->children; c2 != NULL; c2 = c2->next) {
+ if (as_yaml_node_get_key (c2) == NULL)
+ continue;
+ as_app_add_keyword (app,
+ as_yaml_node_get_key (c),
+ as_yaml_node_get_key (c2), -1);
+ }
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Categories") == 0) {
+ for (c = n->children; c != NULL; c = c->next)
+ as_app_add_category (app, as_yaml_node_get_key (c), -1);
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Icon") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ if (g_strcmp0 (as_yaml_node_get_key (c), "cached") == 0) {
+ as_app_set_icon (app, as_yaml_node_get_value (c), -1);
+ as_app_set_icon_kind (app, AS_ICON_KIND_CACHED);
+ continue;
+ }
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Url") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ if (g_strcmp0 (as_yaml_node_get_key (c), "homepage") == 0) {
+ as_app_add_url (app,
+ AS_URL_KIND_HOMEPAGE,
+ as_yaml_node_get_value (c),
+ -1);
+ continue;
+ }
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Provides") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ if (g_strcmp0 (as_yaml_node_get_key (c), "mimetypes") == 0) {
+ for (c2 = c->children; c2 != NULL; c2 = c2->next) {
+ as_app_add_mimetype (app,
+ as_yaml_node_get_key (c2),
+ -1);
+ }
+ continue;
+ } else {
+ _cleanup_object_unref_ AsProvide *pr = NULL;
+ pr = as_provide_new ();
+ if (!as_provide_node_parse_dep11 (pr, c, error))
+ return FALSE;
+ as_app_add_provide (app, pr);
+ }
+ }
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Screenshots") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ _cleanup_object_unref_ AsScreenshot *ss = NULL;
+ ss = as_screenshot_new ();
+ if (!as_screenshot_node_parse_dep11 (ss, c, error))
+ return FALSE;
+ as_app_add_screenshot (app, ss);
+ }
+ continue;
+ }
+ }
+ return TRUE;
+}
+
#if !GLIB_CHECK_VERSION(2,39,1)
/**
* as_app_value_tokenize:
diff --git a/libappstream-glib/as-cleanup.h b/libappstream-glib/as-cleanup.h
index 6408cf1..277aa28 100644
--- a/libappstream-glib/as-cleanup.h
+++ b/libappstream-glib/as-cleanup.h
@@ -27,6 +27,7 @@
#include <libsoup/soup.h>
#include "as-node.h"
+#include "as-yaml.h"
G_BEGIN_DECLS
@@ -59,6 +60,7 @@ GS_DEFINE_CLEANUP_FUNCTION0(GHashTable*, gs_local_hashtable_unref, g_hash_table_
GS_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, gs_local_keyfile_unref, g_key_file_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GMarkupParseContext*, gs_local_markup_parse_context_unref, g_markup_parse_context_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GNode*, gs_local_node_unref, as_node_unref)
+GS_DEFINE_CLEANUP_FUNCTION0(GNode*, gs_local_yaml_unref, as_yaml_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GObject*, gs_local_obj_unref, g_object_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, gs_local_ptrarray_unref, g_ptr_array_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GTimer*, gs_local_destroy_timer, g_timer_destroy)
@@ -87,6 +89,7 @@ GS_DEFINE_CLEANUP_FUNCTION(void*, gs_local_free, g_free)
#define _cleanup_keyfile_unref_ __attribute__ ((cleanup(gs_local_keyfile_unref)))
#define _cleanup_markup_parse_context_unref_ __attribute__ ((cleanup(gs_local_markup_parse_context_unref)))
#define _cleanup_node_unref_ __attribute__ ((cleanup(gs_local_node_unref)))
+#define _cleanup_yaml_unref_ __attribute__ ((cleanup(gs_local_yaml_unref)))
#define _cleanup_object_unref_ __attribute__ ((cleanup(gs_local_obj_unref)))
#define _cleanup_ptrarray_unref_ __attribute__ ((cleanup(gs_local_ptrarray_unref)))
#define _cleanup_uri_unref_ __attribute__ ((cleanup(gs_local_uri_unref)))
diff --git a/libappstream-glib/as-image-private.h b/libappstream-glib/as-image-private.h
index 2381f1e..a24528f 100644
--- a/libappstream-glib/as-image-private.h
+++ b/libappstream-glib/as-image-private.h
@@ -36,6 +36,9 @@ GNode *as_image_node_insert (AsImage *image,
gboolean as_image_node_parse (AsImage *image,
GNode *node,
GError **error);
+gboolean as_image_node_parse_dep11 (AsImage *image,
+ GNode *node,
+ GError **error);
G_END_DECLS
diff --git a/libappstream-glib/as-image.c b/libappstream-glib/as-image.c
index 0c49e19..195e0d3 100644
--- a/libappstream-glib/as-image.c
+++ b/libappstream-glib/as-image.c
@@ -38,6 +38,7 @@
#include "as-image-private.h"
#include "as-node-private.h"
#include "as-utils-private.h"
+#include "as-yaml.h"
typedef struct _AsImagePrivate AsImagePrivate;
struct _AsImagePrivate
@@ -442,6 +443,36 @@ as_image_node_parse (AsImage *image, GNode *node, GError **error)
}
/**
+ * as_image_node_parse_dep11:
+ * @image: a #AsImage instance.
+ * @node: a #GNode.
+ * @error: A #GError or %NULL.
+ *
+ * Populates the object from a DEP-11 node.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.3.0
+ **/
+gboolean
+as_image_node_parse_dep11 (AsImage *im, GNode *node, GError **error)
+{
+ GNode *n;
+ const gchar *tmp;
+
+ for (n = node->children; n != NULL; n = n->next) {
+ tmp = as_yaml_node_get_key (n);
+ if (g_strcmp0 (tmp, "height") == 0)
+ as_image_set_height (im, as_yaml_node_get_value_as_int (n));
+ else if (g_strcmp0 (tmp, "width") == 0)
+ as_image_set_width (im, as_yaml_node_get_value_as_int (n));
+ else if (g_strcmp0 (tmp, "url") == 0)
+ as_image_set_url (im, as_yaml_node_get_value (n), -1);
+ }
+ return TRUE;
+}
+
+/**
* as_image_load_filename:
* @image: a #AsImage instance.
* @filename: filename to read from
diff --git a/libappstream-glib/as-node.h b/libappstream-glib/as-node.h
index 4569887..335cf2c 100644
--- a/libappstream-glib/as-node.h
+++ b/libappstream-glib/as-node.h
@@ -98,12 +98,14 @@ typedef enum {
* AsNodeError:
* @AS_NODE_ERROR_FAILED: Generic failure
* @AS_NODE_ERROR_INVALID_MARKUP: XML markup was invalid
+ * @AS_NODE_ERROR_NO_SUPPORT: No support for parsing
*
* The error type.
**/
typedef enum {
AS_NODE_ERROR_FAILED,
AS_NODE_ERROR_INVALID_MARKUP, /* Since: 0.2.4 */
+ AS_NODE_ERROR_NO_SUPPORT, /* Since: 0.3.0 */
/*< private >*/
AS_NODE_ERROR_LAST
} AsNodeError;
diff --git a/libappstream-glib/as-provide-private.h b/libappstream-glib/as-provide-private.h
index c2527e8..0a64fdc 100644
--- a/libappstream-glib/as-provide-private.h
+++ b/libappstream-glib/as-provide-private.h
@@ -36,6 +36,9 @@ GNode *as_provide_node_insert (AsProvide *provide,
gboolean as_provide_node_parse (AsProvide *provide,
GNode *node,
GError **error);
+gboolean as_provide_node_parse_dep11 (AsProvide *provide,
+ GNode *node,
+ GError **error);
G_END_DECLS
diff --git a/libappstream-glib/as-provide.c b/libappstream-glib/as-provide.c
index f9d980d..563a03a 100644
--- a/libappstream-glib/as-provide.c
+++ b/libappstream-glib/as-provide.c
@@ -36,6 +36,7 @@
#include "as-node-private.h"
#include "as-provide-private.h"
#include "as-utils-private.h"
+#include "as-yaml.h"
typedef struct _AsProvidePrivate AsProvidePrivate;
struct _AsProvidePrivate
@@ -261,6 +262,24 @@ as_provide_node_insert (AsProvide *provide, GNode *parent, gdouble api_version)
}
/**
+ * as_provide_node_parse_dep11:
+ * @provide: a #AsProvide instance.
+ * @node: a #GNode.
+ * @error: A #GError or %NULL.
+ *
+ * Populates the object from a DEP-11 node.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.3.0
+ **/
+gboolean
+as_provide_node_parse_dep11 (AsProvide *provide, GNode *node, GError **error)
+{
+ return TRUE;
+}
+
+/**
* as_provide_node_parse:
* @provide: a #AsProvide instance.
* @node: a #GNode.
diff --git a/libappstream-glib/as-screenshot-private.h b/libappstream-glib/as-screenshot-private.h
index 92ccaa9..fd868e1 100644
--- a/libappstream-glib/as-screenshot-private.h
+++ b/libappstream-glib/as-screenshot-private.h
@@ -38,6 +38,9 @@ GNode *as_screenshot_node_insert (AsScreenshot *screenshot,
gboolean as_screenshot_node_parse (AsScreenshot *screenshot,
GNode *node,
GError **error);
+gboolean as_screenshot_node_parse_dep11 (AsScreenshot *screenshot,
+ GNode *node,
+ GError **error);
G_END_DECLS
diff --git a/libappstream-glib/as-screenshot.c b/libappstream-glib/as-screenshot.c
index 59b217a..218d5fa 100644
--- a/libappstream-glib/as-screenshot.c
+++ b/libappstream-glib/as-screenshot.c
@@ -39,6 +39,7 @@
#include "as-screenshot-private.h"
#include "as-tag.h"
#include "as-utils-private.h"
+#include "as-yaml.h"
typedef struct _AsScreenshotPrivate AsScreenshotPrivate;
struct _AsScreenshotPrivate
@@ -418,6 +419,56 @@ as_screenshot_node_parse (AsScreenshot *screenshot, GNode *node, GError **error)
}
/**
+ * as_screenshot_node_parse_dep11:
+ * @screenshot: a #AsScreenshot instance.
+ * @node: a #GNode.
+ * @error: A #GError or %NULL.
+ *
+ * Populates the object from a DEP-11 node.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.3.0
+ **/
+gboolean
+as_screenshot_node_parse_dep11 (AsScreenshot *ss, GNode *node, GError **error)
+{
+ GNode *c;
+ GNode *n;
+ const gchar *tmp;
+
+ for (n = node->children; n != NULL; n = n->next) {
+ tmp = as_yaml_node_get_key (n);
+ if (g_strcmp0 (tmp, "default") == 0) {
+ if (g_strcmp0 (as_yaml_node_get_value (n), "true") == 0)
+ as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_DEFAULT);
+ else if (g_strcmp0 (as_yaml_node_get_value (n), "false") == 0)
+ as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_NORMAL);
+ continue;
+ }
+ if (g_strcmp0 (tmp, "source-image") == 0) {
+ _cleanup_object_unref_ AsImage *im = as_image_new ();
+ as_image_set_kind (im, AS_IMAGE_KIND_SOURCE);
+ if (!as_image_node_parse_dep11 (im, n, error))
+ return FALSE;
+ as_screenshot_add_image (ss, im);
+ continue;
+ }
+ if (g_strcmp0 (tmp, "thumbnails") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ _cleanup_object_unref_ AsImage *im = as_image_new ();
+ as_image_set_kind (im, AS_IMAGE_KIND_THUMBNAIL);
+ if (!as_image_node_parse_dep11 (im, c, error))
+ return FALSE;
+ as_screenshot_add_image (ss, im);
+ }
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+/**
* as_screenshot_new:
*
* Creates a new #AsScreenshot.
diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c
index 3519430..9b46c09 100644
--- a/libappstream-glib/as-self-test.c
+++ b/libappstream-glib/as-self-test.c
@@ -36,6 +36,7 @@
#include "as-store.h"
#include "as-tag.h"
#include "as-utils-private.h"
+#include "as-yaml.h"
/**
* cd_test_get_filename:
@@ -2400,6 +2401,211 @@ as_test_store_metadata_func (void)
g_ptr_array_unref (apps);
}
+static void
+as_test_yaml_func (void)
+{
+ GNode *node;
+ GError *error = NULL;
+ GString *str;
+ const gchar *expected;
+ _cleanup_free_ gchar *filename = NULL;
+ _cleanup_object_unref_ GFile *file = NULL;
+
+ /* simple header */
+ node = as_yaml_from_data (
+ "File: DEP-11\n"
+ "Origin: aequorea\n"
+ "Version: '0.6'\n",
+ -1, &error);
+ g_assert_no_error (error);
+ g_assert (node != NULL);
+ str = as_yaml_to_string (node);
+ expected =
+ "[MAP]{\n"
+ " [KVL]File=DEP-11\n"
+ " [KVL]Origin=aequorea\n"
+ " [KVL]Version=0.6\n";
+ if (g_strcmp0 (str->str, expected) != 0)
+ g_warning ("Expected:\n%s\nGot:\n%s", expected, str->str);
+ g_assert_cmpstr (str->str, ==, expected);
+ g_string_free (str, TRUE);
+ as_yaml_unref (node);
+
+ /* simple list */
+ node = as_yaml_from_data (
+ "Mimetypes:\n"
+ " - text/html\n"
+ " - text/xml\n"
+ " - application/xhtml+xml\n"
+ "Kudos:\n"
+ " - AppMenu\n"
+ " - SearchProvider\n"
+ " - Notifications\n",
+ -1, &error);
+ g_assert_no_error (error);
+ g_assert (node != NULL);
+ str = as_yaml_to_string (node);
+ expected =
+ "[MAP]{\n"
+ " [SEQ]Mimetypes\n"
+ " [KEY]text/html\n"
+ " [KEY]text/xml\n"
+ " [KEY]application/xhtml+xml\n"
+ " [SEQ]Kudos\n"
+ " [KEY]AppMenu\n"
+ " [KEY]SearchProvider\n"
+ " [KEY]Notifications\n";
+ if (g_strcmp0 (str->str, expected) != 0)
+ g_warning ("Expected:\n%s\nGot:\n%s", expected, str->str);
+ g_assert_cmpstr (str->str, ==, expected);
+ g_string_free (str, TRUE);
+ as_yaml_unref (node);
+
+ /* dummy application */
+ filename = as_test_get_filename ("example.yml");
+ g_assert (filename != NULL);
+ file = g_file_new_for_path (filename);
+ node = as_yaml_from_file (file, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (node != NULL);
+ str = as_yaml_to_string (node);
+ expected =
+ "[MAP]{\n"
+ " [KVL]File=DEP-11\n"
+ " [KVL]Origin=aequorea\n"
+ " [KVL]Version=0.6\n"
+ "[MAP]{\n"
+ " [KVL]Type=desktop-app\n"
+ " [KVL]ID=iceweasel.desktop\n"
+ " [MAP]Name\n"
+ " [KVL]C=Iceweasel\n"
+ " [SEQ]Packages\n"
+ " [KEY]iceweasel\n"
+ " [MAP]Icon\n"
+ " [KVL]cached=iceweasel.png\n"
+ " [MAP]Keywords\n"
+ " [SEQ]C\n"
+ " [KEY]browser\n"
+ " [SEQ]Screenshots\n"
+ " [MAP]{\n"
+ " [KVL]default=true\n"
+ " [MAP]source-image\n"
+ " [KVL]height=770\n"
+ " [KVL]url=http://localhost/source/screenshot.png\n"
+ " [KVL]width=1026\n"
+ " [SEQ]thumbnails\n"
+ " [MAP]{\n"
+ " [KVL]height=423\n"
+ " [KVL]url=http://localhost/752x423/screenshot.png\n"
+ " [KVL]width=752\n"
+ "[MAP]{\n"
+ " [KVL]Type=desktop-app\n"
+ " [KVL]ID=dave.desktop\n"
+ " [MAP]Name\n"
+ " [KVL]C=dave\n";
+ if (g_strcmp0 (str->str, expected) != 0)
+ g_warning ("Expected:\n%s\nGot:\n%s", expected, str->str);
+ g_assert_cmpstr (str->str, ==, expected);
+ g_string_free (str, TRUE);
+ as_yaml_unref (node);
+
+}
+
+static void
+as_test_store_yaml_func (void)
+{
+ AsApp *app;
+ GError *error = NULL;
+ gboolean ret;
+ _cleanup_free_ gchar *filename = NULL;
+ _cleanup_object_unref_ AsStore *store = NULL;
+ _cleanup_object_unref_ GFile *file = NULL;
+ _cleanup_string_free_ GString *str = NULL;
+ const gchar *xml =
+ "<components version=\"0.6\" origin=\"aequorea\">\n"
+ "<component type=\"desktop\">\n"
+ "<id>dave.desktop</id>\n"
+ "<name>dave</name>\n"
+ "</component>\n"
+ "<component type=\"desktop\">\n"
+ "<id>iceweasel.desktop</id>\n"
+ "<pkgname>iceweasel</pkgname>\n"
+ "<name>Iceweasel</name>\n"
+ "<icon type=\"cached\">iceweasel.png</icon>\n"
+ "<keywords>\n"
+ "<keyword>browser</keyword>\n"
+ "</keywords>\n"
+ "<screenshots>\n"
+ "<screenshot type=\"default\">\n"
+ "<image type=\"source\" height=\"770\" width=\"1026\">http://localhost/source/screenshot.png</image>\n"
+ "<image type=\"thumbnail\" height=\"423\" width=\"752\">http://localhost/752x423/screenshot.png</image>\n"
+ "</screenshot>\n"
+ "</screenshots>\n"
+ "</component>\n"
+ "</components>\n";
+
+ /* load store */
+ store = as_store_new ();
+ filename = as_test_get_filename ("example.yml");
+ file = g_file_new_for_path (filename);
+ ret = as_store_from_file (store, file, NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* test it matches expected XML */
+ str = as_store_to_xml (store, AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE);
+ if (g_strcmp0 (str->str, xml) != 0)
+ g_warning ("Expected:\n%s\nGot:\n%s", xml, str->str);
+ g_assert_cmpstr (str->str, ==, xml);
+
+ /* test store properties */
+ g_assert_cmpstr (as_store_get_origin (store), ==, "aequorea");
+ g_assert_cmpfloat (as_store_get_api_version (store), <, 0.6 + 0.01);
+ g_assert_cmpfloat (as_store_get_api_version (store), >, 0.6 - 0.01);
+ g_assert_cmpint (as_store_get_size (store), ==, 2);
+ g_assert (as_store_get_app_by_id (store, "iceweasel.desktop") != NULL);
+ g_assert (as_store_get_app_by_id (store, "dave.desktop") != NULL);
+
+ /* test application properties */
+ app = as_store_get_app_by_id (store, "iceweasel.desktop");
+ g_assert_cmpint (as_app_get_id_kind (app), ==, AS_ID_KIND_DESKTOP);
+ g_assert_cmpstr (as_app_get_pkgname_default (app), ==, "iceweasel");
+ g_assert_cmpstr (as_app_get_name (app, "C"), ==, "Iceweasel");
+}
+
+static void
+as_test_store_speed_yaml_func (void)
+{
+ GError *error = NULL;
+ gboolean ret;
+ guint i;
+ guint loops = 10;
+ _cleanup_free_ gchar *filename = NULL;
+ _cleanup_object_unref_ GFile *file = NULL;
+ _cleanup_timer_destroy_ GTimer *timer = NULL;
+
+ filename = as_test_get_filename ("example-v06.yml.gz");
+ g_assert (filename != NULL);
+ file = g_file_new_for_path (filename);
+ timer = g_timer_new ();
+ for (i = 0; i < loops; i++) {
+ _cleanup_object_unref_ AsStore *store;
+ store = as_store_new ();
+ ret = as_store_from_file (store, file, NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* test store properties */
+ g_assert_cmpstr (as_store_get_origin (store), ==, "bartholomea");
+ g_assert_cmpfloat (as_store_get_api_version (store), <, 0.6 + 0.01);
+ g_assert_cmpfloat (as_store_get_api_version (store), >, 0.6 - 0.01);
+ g_assert_cmpint (as_store_get_size (store), ==, 85);
+ g_assert (as_store_get_app_by_id (store, "blobwars.desktop") != NULL);
+ }
+ g_print ("%.0f ms: ", g_timer_elapsed (timer, NULL) * 1000 / loops);
+
+}
+
int
main (int argc, char **argv)
{
@@ -2442,6 +2648,7 @@ main (int argc, char **argv)
g_test_add_func ("/AppStream/utils", as_test_utils_func);
g_test_add_func ("/AppStream/utils{icons}", as_test_utils_icons_func);
g_test_add_func ("/AppStream/utils{spdx-token}", as_test_utils_spdx_token_func);
+ g_test_add_func ("/AppStream/yaml", as_test_yaml_func);
g_test_add_func ("/AppStream/store", as_test_store_func);
g_test_add_func ("/AppStream/store{demote}", as_test_store_demote_func);
g_test_add_func ("/AppStream/store{merges}", as_test_store_merges_func);
@@ -2450,6 +2657,7 @@ main (int argc, char **argv)
g_test_add_func ("/AppStream/store{versions}", as_test_store_versions_func);
g_test_add_func ("/AppStream/store{origin}", as_test_store_origin_func);
g_test_add_func ("/AppStream/store{app-install}", as_test_store_app_install_func);
+ g_test_add_func ("/AppStream/store{yaml}", as_test_store_yaml_func);
g_test_add_func ("/AppStream/store{metadata}", as_test_store_metadata_func);
g_test_add_func ("/AppStream/store{validate}", as_test_store_validate_func);
g_test_add_func ("/AppStream/store{local-app-install}", as_test_store_local_app_install_func);
@@ -2457,6 +2665,7 @@ main (int argc, char **argv)
g_test_add_func ("/AppStream/store{speed-appstream}", as_test_store_speed_appstream_func);
g_test_add_func ("/AppStream/store{speed-appdata}", as_test_store_speed_appdata_func);
g_test_add_func ("/AppStream/store{speed-desktop}", as_test_store_speed_desktop_func);
+ g_test_add_func ("/AppStream/store{speed-yaml}", as_test_store_speed_yaml_func);
return g_test_run ();
}
diff --git a/libappstream-glib/as-store.c b/libappstream-glib/as-store.c
index f0b59f4..ccac34e 100644
--- a/libappstream-glib/as-store.c
+++ b/libappstream-glib/as-store.c
@@ -42,6 +42,7 @@
#include "as-problem.h"
#include "as-store.h"
#include "as-utils-private.h"
+#include "as-yaml.h"
#define AS_API_VERSION_NEWEST 0.6
@@ -543,6 +544,53 @@ as_store_from_root (AsStore *store,
}
/**
+ * as_store_load_yaml_file:
+ **/
+static gboolean
+as_store_load_yaml_file (AsStore *store,
+ GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GNode *app_n;
+ GNode *n;
+ const gchar *tmp;
+ _cleanup_yaml_unref_ GNode *root = NULL;
+
+ /* load file */
+ root = as_yaml_from_file (file, cancellable, error);
+ if (root == NULL)
+ return FALSE;
+
+ /* get header information */
+ for (n = root->children->children; n != NULL; n = n->next) {
+ tmp = as_yaml_node_get_key (n);
+ if (g_strcmp0 (tmp, "Origin") == 0) {
+ as_store_set_origin (store, as_yaml_node_get_value (n));
+ continue;
+ }
+ if (g_strcmp0 (tmp, "Version") == 0) {
+ if (as_yaml_node_get_value (n) != NULL)
+ as_store_set_api_version (store, g_ascii_strtod (as_yaml_node_get_value (n), NULL));
+ continue;
+ }
+ }
+
+ /* parse applications */
+ for (app_n = root->children->next; app_n != NULL; app_n = app_n->next) {
+ _cleanup_object_unref_ AsApp *app = NULL;
+ if (app_n->children == NULL)
+ continue;
+ app = as_app_new ();
+ if (!as_app_node_parse_dep11 (app, app_n, error))
+ return FALSE;
+ if (as_app_get_id_full (app) != NULL)
+ as_store_add_app (store, app);
+ }
+ return TRUE;
+}
+
+/**
* as_store_from_file:
* @store: a #AsStore instance.
* @file: a #GFile.
@@ -550,7 +598,8 @@ as_store_from_root (AsStore *store,
* @cancellable: a #GCancellable.
* @error: A #GError or %NULL.
*
- * Parses an AppStream XML file and adds any valid applications to the store.
+ * Parses an AppStream XML or DEP-11 YAML file and adds any valid applications
+ * to the store.
*
* If the root node does not have a 'origin' attribute, then the method
* as_store_set_origin() should be called *before* this function if cached
@@ -567,11 +616,18 @@ as_store_from_file (AsStore *store,
GCancellable *cancellable,
GError **error)
{
+ _cleanup_free_ gchar *filename = NULL;
_cleanup_error_free_ GError *error_local = NULL;
_cleanup_node_unref_ GNode *root = NULL;
g_return_val_if_fail (AS_IS_STORE (store), FALSE);
+ /* a DEP-11 file */
+ filename = g_file_get_path (file);
+ if (g_strstr_len (filename, -1, ".yml") != NULL)
+ return as_store_load_yaml_file (store, file, cancellable, error);
+
+ /* an AppStream XML file */
root = as_node_from_file (file,
AS_NODE_FROM_XML_FLAG_LITERAL_TEXT,
cancellable,
@@ -1054,6 +1110,7 @@ as_store_monitor_directory (AsStore *store,
static gboolean
as_store_load_app_info (AsStore *store,
const gchar *path,
+ const gchar *format,
GCancellable *cancellable,
GError **error)
{
@@ -1061,31 +1118,31 @@ as_store_load_app_info (AsStore *store,
_cleanup_dir_close_ GDir *dir = NULL;
_cleanup_error_free_ GError *error_local = NULL;
_cleanup_free_ gchar *icon_root = NULL;
- _cleanup_free_ gchar *path_xml = NULL;
+ _cleanup_free_ gchar *path_md = NULL;
/* watch the directory for changes */
if (!as_store_monitor_directory (store, path, cancellable, error))
return FALSE;
/* search all files */
- path_xml = g_build_filename (path, "xmls", NULL);
- if (!g_file_test (path_xml, G_FILE_TEST_EXISTS))
+ path_md = g_build_filename (path, format, NULL);
+ if (!g_file_test (path_md, G_FILE_TEST_EXISTS))
return TRUE;
- dir = g_dir_open (path_xml, 0, &error_local);
+ dir = g_dir_open (path_md, 0, &error_local);
if (dir == NULL) {
g_set_error (error,
AS_STORE_ERROR,
AS_STORE_ERROR_FAILED,
"Failed to open %s: %s",
- path_xml, error_local->message);
+ path_md, error_local->message);
return FALSE;
}
icon_root = g_build_filename (path, "icons", NULL);
while ((tmp = g_dir_read_name (dir)) != NULL) {
- _cleanup_free_ gchar *filename_xml;
- filename_xml = g_build_filename (path_xml, tmp, NULL);
+ _cleanup_free_ gchar *filename_md;
+ filename_md = g_build_filename (path_md, tmp, NULL);
if (!as_store_load_app_info_file (store,
- filename_xml,
+ filename_md,
icon_root,
cancellable,
error))
@@ -1375,7 +1432,9 @@ as_store_load (AsStore *store,
dest = g_build_filename (priv->destdir ? priv->destdir : "/", tmp, NULL);
if (!g_file_test (dest, G_FILE_TEST_EXISTS))
continue;
- if (!as_store_load_app_info (store, dest, cancellable, error))
+ if (!as_store_load_app_info (store, dest, "xmls", cancellable, error))
+ return FALSE;
+ if (!as_store_load_app_info (store, dest, "yaml", cancellable, error))
return FALSE;
}
diff --git a/libappstream-glib/as-yaml.c b/libappstream-glib/as-yaml.c
new file mode 100644
index 0000000..9bc0cf1
--- /dev/null
+++ b/libappstream-glib/as-yaml.c
@@ -0,0 +1,381 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2014 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#ifdef PK_BUILD_DEP11
+#include <yaml.h>
+#endif
+
+#include "as-cleanup.h"
+#include "as-node.h"
+#include "as-yaml.h"
+
+typedef enum {
+ AS_YAML_NODE_KIND_UNKNOWN,
+ AS_YAML_NODE_KIND_MAP,
+ AS_YAML_NODE_KIND_SEQ,
+ AS_YAML_NODE_KIND_KEY,
+ AS_YAML_NODE_KIND_KEY_VALUE,
+ AS_YAML_NODE_KIND_LAST
+} AsYamlNodeKind;
+
+typedef struct {
+ gchar *key;
+ gchar *value;
+ AsYamlNodeKind kind;
+} AsYamlNode;
+
+/**
+ * as_yaml_node_get_kind:
+ **/
+static AsYamlNodeKind
+as_yaml_node_get_kind (GNode *node)
+{
+ AsYamlNode *ym;
+ if (node == NULL)
+ return AS_YAML_NODE_KIND_UNKNOWN;
+ ym = node->data;
+ if (ym == NULL)
+ return AS_YAML_NODE_KIND_UNKNOWN;
+ return ym->kind;
+}
+
+/**
+ * as_yaml_node_get_key:
+ **/
+const gchar *
+as_yaml_node_get_key (const GNode *node)
+{
+ AsYamlNode *ym;
+ if (node == NULL)
+ return NULL;
+ ym = node->data;
+ if (ym == NULL)
+ return NULL;
+ if (ym->key == NULL || ym->key[0] == '\0')
+ return NULL;
+ return ym->key;
+}
+
+/**
+ * as_yaml_node_get_value:
+ **/
+const gchar *
+as_yaml_node_get_value (const GNode *node)
+{
+ AsYamlNode *ym;
+ if (node == NULL)
+ return NULL;
+ ym = node->data;
+ if (ym == NULL)
+ return NULL;
+ if (ym->value == NULL || ym->value[0] == '\0')
+ return NULL;
+ return ym->value;
+}
+
+/**
+ * as_yaml_node_get_value_as_int:
+ **/
+gint
+as_yaml_node_get_value_as_int (const GNode *node)
+{
+ const gchar *tmp;
+ gchar *endptr = NULL;
+ gint64 value_tmp;
+
+ tmp = as_yaml_node_get_value (node);
+ if (tmp == NULL)
+ return G_MAXINT;
+ value_tmp = g_ascii_strtoll (tmp, &endptr, 10);
+ if (value_tmp == 0 && tmp == endptr)
+ return G_MAXINT;
+ if (value_tmp > G_MAXINT || value_tmp < G_MININT)
+ return G_MAXINT;
+ return value_tmp;
+}
+
+/**
+ * as_node_yaml_destroy_node_cb:
+ **/
+static gboolean
+as_node_yaml_destroy_node_cb (GNode *node, gpointer data)
+{
+ AsYamlNode *ym = node->data;
+ if (ym == NULL)
+ return FALSE;
+ g_free (ym->key);
+ g_free (ym->value);
+ g_slice_free (AsYamlNode, ym);
+ return FALSE;
+}
+
+/**
+ * as_yaml_unref:
+ **/
+void
+as_yaml_unref (GNode *node)
+{
+ g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ as_node_yaml_destroy_node_cb, NULL);
+ g_node_destroy (node);
+}
+
+/**
+ * as_yaml_to_string_cb:
+ **/
+static gboolean
+as_yaml_to_string_cb (GNode *node, gpointer data)
+{
+ AsYamlNode *ym = node->data;
+ GString *str = (GString *) data;
+ gint depth;
+ gint i;
+
+ depth = g_node_depth (node);
+ for (i = 0; i < depth - 2; i++)
+ g_string_append (str, " ");
+ if (ym == NULL)
+ return FALSE;
+ switch (ym->kind) {
+ case AS_YAML_NODE_KIND_MAP:
+ g_string_append (str, "[MAP]");
+ break;
+ case AS_YAML_NODE_KIND_SEQ:
+ g_string_append (str, "[SEQ]");
+ break;
+ case AS_YAML_NODE_KIND_KEY:
+ g_string_append (str, "[KEY]");
+ break;
+ case AS_YAML_NODE_KIND_KEY_VALUE:
+ g_string_append (str, "[KVL]");
+ break;
+ case AS_YAML_NODE_KIND_UNKNOWN:
+ default:
+ g_string_append (str, "???: ");
+ break;
+ }
+
+ if (ym->value != NULL) {
+ g_string_append_printf (str, "%s=%s\n", ym->key, ym->value);
+ return FALSE;
+ }
+ g_string_append_printf (str, "%s\n", ym->key);
+ return FALSE;
+}
+
+/**
+ * as_yaml_to_string:
+ **/
+GString *
+as_yaml_to_string (GNode *node)
+{
+ GString *str = g_string_new ("");
+ g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ as_yaml_to_string_cb, str);
+ return str;
+}
+
+#if PK_BUILD_DEP11
+/**
+ * as_yaml_node_new:
+ **/
+static AsYamlNode *
+as_yaml_node_new (AsYamlNodeKind kind, const gchar *id)
+{
+ AsYamlNode *ym;
+ ym = g_slice_new0 (AsYamlNode);
+ ym->kind = kind;
+ ym->key = g_strdup (id);
+ return ym;
+}
+
+/**
+ * as_node_yaml_process_layer:
+ **/
+static void
+as_node_yaml_process_layer (yaml_parser_t *parser, GNode *parent)
+{
+ AsYamlNode *ym;
+ GNode *last_scalar = NULL;
+ GNode *new;
+ const gchar *tmp;
+ gboolean valid = TRUE;
+ yaml_event_t event;
+
+ while (valid) {
+ yaml_parser_parse (parser, &event);
+ switch (event.type) {
+ case YAML_SCALAR_EVENT:
+ tmp = (const gchar *) event.data.scalar.value;
+ if (as_yaml_node_get_kind (parent) != AS_YAML_NODE_KIND_SEQ &&
+ last_scalar != NULL) {
+ ym = last_scalar->data;
+ ym->value = g_strdup (tmp);
+ ym->kind = AS_YAML_NODE_KIND_KEY_VALUE;
+ last_scalar = NULL;
+ } else {
+ ym = as_yaml_node_new (AS_YAML_NODE_KIND_KEY, tmp);
+ last_scalar = g_node_append_data (parent, ym);
+ }
+ break;
+ case YAML_MAPPING_START_EVENT:
+ if (last_scalar == NULL) {
+ ym = as_yaml_node_new (AS_YAML_NODE_KIND_MAP, "{");
+ new = g_node_append_data (parent, ym);
+ } else {
+ ym = last_scalar->data;
+ ym->kind = AS_YAML_NODE_KIND_MAP;
+ new = last_scalar;
+ }
+ as_node_yaml_process_layer (parser, new);
+ last_scalar = NULL;
+ break;
+ case YAML_SEQUENCE_START_EVENT:
+ if (last_scalar == NULL) {
+ ym = as_yaml_node_new (AS_YAML_NODE_KIND_SEQ, "[");
+ new = g_node_append_data (parent, ym);
+ } else {
+ ym = last_scalar->data;
+ ym->kind = AS_YAML_NODE_KIND_SEQ;
+ new = last_scalar;
+ }
+ as_node_yaml_process_layer (parser, new);
+ last_scalar = NULL;
+ break;
+ case YAML_STREAM_START_EVENT:
+ break;
+ case YAML_MAPPING_END_EVENT:
+ case YAML_SEQUENCE_END_EVENT:
+ case YAML_STREAM_END_EVENT:
+ valid = FALSE;
+ break;
+ default:
+ break;
+ }
+ yaml_event_delete (&event);
+ }
+}
+#endif
+
+/**
+ * as_yaml_from_data:
+ **/
+GNode *
+as_yaml_from_data (const gchar *data, gssize data_len, GError **error)
+{
+ GNode *node = NULL;
+#if PK_BUILD_DEP11
+ yaml_parser_t parser;
+
+ /* parse */
+ yaml_parser_initialize (&parser);
+ if (data_len < 0)
+ data_len = strlen (data);
+ yaml_parser_set_input_string (&parser, (guchar *) data, data_len);
+ node = g_node_new (NULL);
+ as_node_yaml_process_layer (&parser, node);
+ yaml_parser_delete (&parser);
+#else
+ g_set_error_literal (error,
+ AS_NODE_ERROR,
+ AS_NODE_ERROR_NO_SUPPORT,
+ "No DEP-11 support, needs libyaml");
+#endif
+ return node;
+}
+
+#if PK_BUILD_DEP11
+/**
+ * as_yaml_read_handler_cb:
+ **/
+static int
+as_yaml_read_handler_cb (void *data,
+ unsigned char *buffer,
+ size_t size,
+ size_t *size_read)
+{
+ GInputStream *stream = G_INPUT_STREAM (data);
+ *size_read = g_input_stream_read (stream, buffer, size, NULL, NULL);
+ return 1;
+}
+#endif
+
+/**
+ * as_yaml_from_file:
+ **/
+GNode *
+as_yaml_from_file (GFile *file, GCancellable *cancellable, GError **error)
+{
+ GNode *node = NULL;
+#if PK_BUILD_DEP11
+ const gchar *content_type = NULL;
+ yaml_parser_t parser;
+ _cleanup_free_ gchar *data = NULL;
+ _cleanup_object_unref_ GConverter *conv = NULL;
+ _cleanup_object_unref_ GFileInfo *info = NULL;
+ _cleanup_object_unref_ GInputStream *file_stream = NULL;
+ _cleanup_object_unref_ GInputStream *stream_data = NULL;
+
+ /* what kind of file is this */
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ cancellable,
+ error);
+ if (info == NULL)
+ return NULL;
+
+ /* decompress if required */
+ file_stream = G_INPUT_STREAM (g_file_read (file, cancellable, error));
+ if (file_stream == NULL)
+ return NULL;
+ content_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+ if (g_strcmp0 (content_type, "application/gzip") == 0 ||
+ g_strcmp0 (content_type, "application/x-gzip") == 0) {
+ conv = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
+ stream_data = g_converter_input_stream_new (file_stream, conv);
+ } else if (g_strcmp0 (content_type, "application/x-yaml") == 0) {
+ stream_data = g_object_ref (file_stream);
+ } else {
+ g_set_error (error,
+ AS_NODE_ERROR,
+ AS_NODE_ERROR_FAILED,
+ "cannot process file of type %s",
+ content_type);
+ return NULL;
+ }
+
+ /* parse */
+ yaml_parser_initialize (&parser);
+ yaml_parser_set_input (&parser, as_yaml_read_handler_cb, stream_data);
+ node = g_node_new (NULL);
+ as_node_yaml_process_layer (&parser, node);
+ yaml_parser_delete (&parser);
+#else
+ g_set_error_literal (error,
+ AS_NODE_ERROR,
+ AS_NODE_ERROR_NO_SUPPORT,
+ "No DEP-11 support, needs libyaml");
+#endif
+ return node;
+}
diff --git a/libappstream-glib/as-yaml.h b/libappstream-glib/as-yaml.h
new file mode 100644
index 0000000..3f479e8
--- /dev/null
+++ b/libappstream-glib/as-yaml.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2014 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__APPSTREAM_GLIB_H) && !defined (AS_COMPILATION)
+#error "Only <appstream-glib.h> can be included directly."
+#endif
+
+#ifndef __AS_YAML_H
+#define __AS_YAML_H
+
+#include <gio/gio.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void as_yaml_unref (GNode *node);
+GString *as_yaml_to_string (GNode *node);
+GNode *as_yaml_from_data (const gchar *data,
+ gssize data_len,
+ GError **error);
+GNode *as_yaml_from_file (GFile *file,
+ GCancellable *cancellable,
+ GError **error);
+const gchar *as_yaml_node_get_key (const GNode *node);
+const gchar *as_yaml_node_get_value (const GNode *node);
+gint as_yaml_node_get_value_as_int (const GNode *node);
+
+G_END_DECLS
+
+#endif /* __AS_YAML_H */
+