diff options
author | Richard Hughes <richard@hughsie.com> | 2014-03-21 20:28:50 +0000 |
---|---|---|
committer | Richard Hughes <richard@hughsie.com> | 2014-08-20 10:01:26 +0100 |
commit | c63ae20cbbfe7e241fca2775f4df05da85335d13 (patch) | |
tree | e8f3a00955a29eb6ad285b2d6bf93bdb06c73601 | |
parent | 451a7489300e19221bb5bed6da182839e2e3ad69 (diff) | |
download | appstream-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.am | 4 | ||||
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | client/as-util.c | 9 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | contrib/libappstream-glib.spec.in | 1 | ||||
-rw-r--r-- | data/tests/Makefile.am | 2 | ||||
-rw-r--r-- | data/tests/example-v06.yml.gz | bin | 0 -> 55531 bytes | |||
-rw-r--r-- | data/tests/example.yml | 31 | ||||
-rw-r--r-- | libappstream-glib/Makefile.am | 9 | ||||
-rw-r--r-- | libappstream-glib/as-app-private.h | 3 | ||||
-rw-r--r-- | libappstream-glib/as-app.c | 139 | ||||
-rw-r--r-- | libappstream-glib/as-cleanup.h | 3 | ||||
-rw-r--r-- | libappstream-glib/as-image-private.h | 3 | ||||
-rw-r--r-- | libappstream-glib/as-image.c | 31 | ||||
-rw-r--r-- | libappstream-glib/as-node.h | 2 | ||||
-rw-r--r-- | libappstream-glib/as-provide-private.h | 3 | ||||
-rw-r--r-- | libappstream-glib/as-provide.c | 19 | ||||
-rw-r--r-- | libappstream-glib/as-screenshot-private.h | 3 | ||||
-rw-r--r-- | libappstream-glib/as-screenshot.c | 51 | ||||
-rw-r--r-- | libappstream-glib/as-self-test.c | 209 | ||||
-rw-r--r-- | libappstream-glib/as-store.c | 79 | ||||
-rw-r--r-- | libappstream-glib/as-yaml.c | 381 | ||||
-rw-r--r-- | libappstream-glib/as-yaml.h | 49 |
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 @@ -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 Binary files differnew file mode 100644 index 0000000..d8861e9 --- /dev/null +++ b/data/tests/example-v06.yml.gz 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 */ + |