/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2014 Richard Hughes * * 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 AS_BUILD_DEP11 #include #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_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 AS_BUILD_DEP11 /** * 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_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 AS_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 AS_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 AS_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; }