X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgvariant-parser.c;h=3a8c63d3caf2347eadec532c1fae499e1cf57705;hb=ecf1359191b2f796a7d63288652dd1a93525417d;hp=45d26958487f4403e1c493c60de4f8e68c89c783;hpb=5620f96305ee276ac758de989589a93de89ed40a;p=platform%2Fupstream%2Fglib.git
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
index 45d2695..3a8c63d 100644
--- a/glib/gvariant-parser.c
+++ b/glib/gvariant-parser.c
@@ -12,19 +12,26 @@
* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see .
*
* Author: Ryan Lortie
*/
+#include "config.h"
+
#include
#include
#include
-#include
-#include "galias.h"
+#include "gerror.h"
+#include "gquark.h"
+#include "gstring.h"
+#include "gstrfuncs.h"
+#include "gtestutils.h"
+#include "gvariant.h"
+#include "gvarianttype.h"
+#include "gslice.h"
+#include "gthread.h"
/*
* two-pass algorithm
@@ -39,15 +46,42 @@
* are not currently defined for this domain. See #GError for
* information on error domains.
**/
+/**
+ * GVariantParseError:
+ * @G_VARIANT_PARSE_ERROR_FAILED: generic error (unused)
+ * @G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED: a non-basic #GVariantType was given where a basic type was expected
+ * @G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE: cannot infer the #GVariantType
+ * @G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED: an indefinite #GVariantType was given where a definite type was expected
+ * @G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END: extra data after parsing finished
+ * @G_VARIANT_PARSE_ERROR_INVALID_CHARACTER: invalid character in number or unicode escape
+ * @G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING: not a valid #GVariant format string
+ * @G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH: not a valid object path
+ * @G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE: not a valid type signature
+ * @G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING: not a valid #GVariant type string
+ * @G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE: could not find a common type for array entries
+ * @G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE: the numerical value is out of range of the given type
+ * @G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG: the numerical value is out of range for any type
+ * @G_VARIANT_PARSE_ERROR_TYPE_ERROR: cannot parse as variant of the specified type
+ * @G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN: an unexpected token was encountered
+ * @G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD: an unknown keyword was encountered
+ * @G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT: unterminated string constant
+ * @G_VARIANT_PARSE_ERROR_VALUE_EXPECTED: no value given
+ *
+ * Error codes returned by parsing text-format GVariants.
+ **/
+G_DEFINE_QUARK (g-variant-parse-error-quark, g_variant_parse_error)
+
+/**
+ * g_variant_parser_get_error_quark:
+ *
+ * Same as g_variant_error_quark().
+ *
+ * Deprecated: Use g_variant_parse_error_quark() instead.
+ */
GQuark
g_variant_parser_get_error_quark (void)
{
- static GQuark the_quark;
-
- if (the_quark == 0)
- the_quark = g_quark_from_static_string ("g-variant-parse-error-quark");
-
- return the_quark;
+ return g_variant_parse_error_quark ();
}
typedef struct
@@ -55,10 +89,12 @@ typedef struct
gint start, end;
} SourceRef;
+G_GNUC_PRINTF(5, 0)
static void
parser_set_error_va (GError **error,
SourceRef *location,
SourceRef *other,
+ gint code,
const gchar *format,
va_list ap)
{
@@ -77,21 +113,23 @@ parser_set_error_va (GError **error,
g_string_append_c (msg, ':');
g_string_append_vprintf (msg, format, ap);
- g_set_error_literal (error, G_VARIANT_PARSE_ERROR, 0, msg->str);
+ g_set_error_literal (error, G_VARIANT_PARSE_ERROR, code, msg->str);
g_string_free (msg, TRUE);
}
+G_GNUC_PRINTF(5, 6)
static void
parser_set_error (GError **error,
SourceRef *location,
SourceRef *other,
+ gint code,
const gchar *format,
...)
{
va_list ap;
va_start (ap, format);
- parser_set_error_va (error, location, other, format, ap);
+ parser_set_error_va (error, location, other, code, format, ap);
va_end (ap);
}
@@ -105,10 +143,12 @@ typedef struct
} TokenStream;
+G_GNUC_PRINTF(5, 6)
static void
token_stream_set_error (TokenStream *stream,
GError **error,
gboolean this_token,
+ gint code,
const gchar *format,
...)
{
@@ -123,18 +163,18 @@ token_stream_set_error (TokenStream *stream,
ref.end = ref.start;
va_start (ap, format);
- parser_set_error_va (error, &ref, NULL, format, ap);
+ parser_set_error_va (error, &ref, NULL, code, format, ap);
va_end (ap);
}
-static void
+static gboolean
token_stream_prepare (TokenStream *stream)
{
gint brackets = 0;
const gchar *end;
if (stream->this != NULL)
- return;
+ return TRUE;
while (stream->stream != stream->end && g_ascii_isspace (*stream->stream))
stream->stream++;
@@ -142,7 +182,7 @@ token_stream_prepare (TokenStream *stream)
if (stream->stream == stream->end || *stream->stream == '\0')
{
stream->this = stream->stream;
- return;
+ return FALSE;
}
switch (stream->stream[0])
@@ -156,7 +196,22 @@ token_stream_prepare (TokenStream *stream)
break;
break;
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'b':
+ if (stream->stream[1] == '\'' || stream->stream[1] == '"')
+ {
+ for (end = stream->stream + 2; end != stream->end; end++)
+ if (*end == stream->stream[1] || *end == '\0' ||
+ (*end == '\\' && (++end == stream->end || *end == '\0')))
+ break;
+
+ if (end != stream->end && *end)
+ end++;
+ break;
+ }
+
+ else /* âââ */;
+
+ case 'a': /* 'b' */ case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
@@ -166,12 +221,24 @@ token_stream_prepare (TokenStream *stream)
break;
break;
+ case '\'': case '"':
+ for (end = stream->stream + 1; end != stream->end; end++)
+ if (*end == stream->stream[0] || *end == '\0' ||
+ (*end == '\\' && (++end == stream->end || *end == '\0')))
+ break;
+
+ if (end != stream->end && *end)
+ end++;
+ break;
+
case '@': case '%':
- /* stop at the first space, comma or unmatched bracket.
- * deals nicely with cases like (%i, %i).
+ /* stop at the first space, comma, colon or unmatched bracket.
+ * deals nicely with cases like (%i, %i) or {%i: %i}.
+ * Also: ] and > are never in format strings.
*/
for (end = stream->stream + 1;
- end != stream->end && *end != ',' && !g_ascii_isspace (*end);
+ end != stream->end && *end != ',' &&
+ *end != ':' && *end != '>' && *end != ']' && !g_ascii_isspace (*end);
end++)
if (*end == '(' || *end == '{')
@@ -182,16 +249,6 @@ token_stream_prepare (TokenStream *stream)
break;
- case '\'': case '"':
- for (end = stream->stream + 1; end != stream->end; end++)
- if (*end == stream->stream[0] || *end == '\0' ||
- (*end == '\\' && (++end == stream->end || *end == '\0')))
- break;
-
- if (end != stream->end && *end)
- end++;
- break;
-
default:
end = stream->stream + 1;
break;
@@ -199,6 +256,8 @@ token_stream_prepare (TokenStream *stream)
stream->this = stream->stream;
stream->stream = end;
+
+ return TRUE;
}
static void
@@ -211,23 +270,39 @@ static gboolean
token_stream_peek (TokenStream *stream,
gchar first_char)
{
- token_stream_prepare (stream);
+ if (!token_stream_prepare (stream))
+ return FALSE;
return stream->this[0] == first_char;
}
static gboolean
+token_stream_peek2 (TokenStream *stream,
+ gchar first_char,
+ gchar second_char)
+{
+ if (!token_stream_prepare (stream))
+ return FALSE;
+
+ return stream->this[0] == first_char &&
+ stream->this[1] == second_char;
+}
+
+static gboolean
token_stream_is_keyword (TokenStream *stream)
{
- token_stream_prepare (stream);
+ if (!token_stream_prepare (stream))
+ return FALSE;
- return g_ascii_isalpha (stream->this[0]);
+ return g_ascii_isalpha (stream->this[0]) &&
+ g_ascii_isalpha (stream->this[1]);
}
static gboolean
token_stream_is_numeric (TokenStream *stream)
{
- token_stream_prepare (stream);
+ if (!token_stream_prepare (stream))
+ return FALSE;
return (g_ascii_isdigit (stream->this[0]) ||
stream->this[0] == '-' ||
@@ -236,21 +311,25 @@ token_stream_is_numeric (TokenStream *stream)
}
static gboolean
-token_stream_consume (TokenStream *stream,
- const gchar *token)
+token_stream_peek_string (TokenStream *stream,
+ const gchar *token)
{
gint length = strlen (token);
- token_stream_prepare (stream);
+ return token_stream_prepare (stream) &&
+ stream->stream - stream->this == length &&
+ memcmp (stream->this, token, length) == 0;
+}
- if (stream->stream - stream->this == length &&
- memcmp (stream->this, token, length) == 0)
- {
- token_stream_next (stream);
- return TRUE;
- }
+static gboolean
+token_stream_consume (TokenStream *stream,
+ const gchar *token)
+{
+ if (!token_stream_peek_string (stream, token))
+ return FALSE;
- return FALSE;
+ token_stream_next (stream);
+ return TRUE;
}
static gboolean
@@ -263,7 +342,8 @@ token_stream_require (TokenStream *stream,
if (!token_stream_consume (stream, token))
{
token_stream_set_error (stream, error, FALSE,
- "expected `%s'%s", token, purpose);
+ G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN,
+ "expected '%s'%s", token, purpose);
return FALSE;
}
@@ -285,7 +365,8 @@ token_stream_get (TokenStream *stream)
{
gchar *result;
- token_stream_prepare (stream);
+ if (!token_stream_prepare (stream))
+ return NULL;
result = g_strndup (stream->this, stream->stream - stream->this);
@@ -307,7 +388,7 @@ token_stream_end_ref (TokenStream *stream,
ref->end = stream->stream - stream->start;
}
-void
+static void
pattern_copy (gchar **out,
const gchar **in)
{
@@ -459,10 +540,12 @@ ast_free (AST *ast)
ast->class->free (ast);
}
+G_GNUC_PRINTF(5, 6)
static void
ast_set_error (AST *ast,
GError **error,
AST *other_ast,
+ gint code,
const gchar *format,
...)
{
@@ -471,6 +554,7 @@ ast_set_error (AST *ast,
va_start (ap, format);
parser_set_error_va (error, &ast->source_ref,
other_ast ? & other_ast->source_ref : NULL,
+ code,
format, ap);
va_end (ap);
}
@@ -484,7 +568,8 @@ ast_type_error (AST *ast,
typestr = g_variant_type_dup_string (type);
ast_set_error (ast, error, NULL,
- "can not parse as value of type `%s'",
+ G_VARIANT_PARSE_ERROR_TYPE_ERROR,
+ "can not parse as value of type '%s'",
typestr);
g_free (typestr);
@@ -514,7 +599,9 @@ ast_resolve (AST *ast,
switch (pattern[i])
{
case '*':
- ast_set_error (ast, error, NULL, "unable to infer type");
+ ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE,
+ "unable to infer type");
g_free (pattern);
return NULL;
@@ -627,6 +714,7 @@ ast_array_get_pattern (AST **array,
* report the error. note: 'j' is first.
*/
ast_set_error (array[j], error, array[i],
+ G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE,
"unable to find a common type");
g_free (tmp);
return NULL;
@@ -634,7 +722,10 @@ ast_array_get_pattern (AST **array,
j++;
}
+
}
+
+ g_free (tmp);
}
return pattern;
@@ -731,7 +822,9 @@ maybe_parse (TokenStream *stream,
else if (!token_stream_consume (stream, "nothing"))
{
- token_stream_set_error (stream, error, TRUE, "unknown keyword");
+ token_stream_set_error (stream, error, TRUE,
+ G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
+ "unknown keyword");
return NULL;
}
@@ -862,7 +955,7 @@ array_parse (TokenStream *stream,
if (need_comma &&
!token_stream_require (stream, ",",
- " or `]' to follow array element",
+ " or ']' to follow array element",
error))
goto error;
@@ -941,6 +1034,12 @@ tuple_get_value (AST *ast,
{
GVariant *child;
+ if (childtype == NULL)
+ {
+ g_variant_builder_clear (&builder);
+ return ast_type_error (ast, type, error);
+ }
+
if (!(child = ast_get_value (tuple->children[i], childtype, error)))
{
g_variant_builder_clear (&builder);
@@ -951,6 +1050,12 @@ tuple_get_value (AST *ast,
childtype = g_variant_type_next (childtype);
}
+ if (childtype != NULL)
+ {
+ g_variant_builder_clear (&builder);
+ return ast_type_error (ast, type, error);
+ }
+
return g_variant_builder_end (&builder);
}
@@ -989,7 +1094,7 @@ tuple_parse (TokenStream *stream,
if (need_comma &&
!token_stream_require (stream, ",",
- " or `)' to follow tuple element",
+ " or ')' to follow tuple element",
error))
goto error;
@@ -1049,7 +1154,9 @@ variant_get_value (AST *ast,
Variant *variant = (Variant *) ast;
GVariant *child;
- g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT));
+ if (!g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT))
+ return ast_type_error (ast, type, error);
+
child = ast_resolve (variant->value, error);
if (child == NULL)
@@ -1142,6 +1249,7 @@ dictionary_get_pattern (AST *ast,
if (!strchr ("bynqiuxthdsogNS", key_char))
{
ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED,
"dictionary keys must have basic types");
return NULL;
}
@@ -1181,7 +1289,7 @@ dictionary_get_value (AST *ast,
if (!(subvalue = ast_get_value (dict->keys[0], subtype, error)))
{
g_variant_builder_clear (&builder);
- return FALSE;
+ return NULL;
}
g_variant_builder_add_value (&builder, subvalue);
@@ -1189,7 +1297,7 @@ dictionary_get_value (AST *ast,
if (!(subvalue = ast_get_value (dict->values[0], subtype, error)))
{
g_variant_builder_clear (&builder);
- return FALSE;
+ return NULL;
}
g_variant_builder_add_value (&builder, subvalue);
@@ -1219,14 +1327,14 @@ dictionary_get_value (AST *ast,
if (!(subvalue = ast_get_value (dict->keys[i], key, error)))
{
g_variant_builder_clear (&builder);
- return FALSE;
+ return NULL;
}
g_variant_builder_add_value (&builder, subvalue);
if (!(subvalue = ast_get_value (dict->values[i], val, error)))
{
g_variant_builder_clear (&builder);
- return FALSE;
+ return NULL;
}
g_variant_builder_add_value (&builder, subvalue);
g_variant_builder_close (&builder);
@@ -1289,7 +1397,7 @@ dictionary_parse (TokenStream *stream,
only_one = token_stream_consume (stream, ",");
if (!only_one &&
!token_stream_require (stream, ":",
- " or `,' to follow dictionary entry key",
+ " or ',' to follow dictionary entry key",
error))
goto error;
@@ -1315,7 +1423,7 @@ dictionary_parse (TokenStream *stream,
AST *child;
if (!token_stream_require (stream, ",",
- " or `}' to follow dictionary entry", error))
+ " or '}' to follow dictionary entry", error))
goto error;
child = parse (stream, app, error);
@@ -1377,7 +1485,9 @@ string_get_value (AST *ast,
{
if (!g_variant_is_object_path (string->string))
{
- ast_set_error (ast, error, NULL, "not a valid object path");
+ ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH,
+ "not a valid object path");
return NULL;
}
@@ -1388,7 +1498,9 @@ string_get_value (AST *ast,
{
if (!g_variant_is_signature (string->string))
{
- ast_set_error (ast, error, NULL, "not a valid signature");
+ ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE,
+ "not a valid signature");
return NULL;
}
@@ -1408,6 +1520,43 @@ string_free (AST *ast)
g_slice_free (String, string);
}
+static gboolean
+unicode_unescape (const gchar *src,
+ gint *src_ofs,
+ gchar *dest,
+ gint *dest_ofs,
+ gint length,
+ SourceRef *ref,
+ GError **error)
+{
+ gchar buffer[9];
+ guint64 value;
+ gchar *end;
+
+ (*src_ofs)++;
+
+ g_assert (length < sizeof (buffer));
+ strncpy (buffer, src + *src_ofs, length);
+ buffer[length] = '\0';
+
+ value = g_ascii_strtoull (buffer, &end, 0x10);
+
+ if (value == 0 || end != buffer + length)
+ {
+ parser_set_error (error, ref, NULL,
+ G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
+ "invalid %d-character unicode escape", length);
+ return FALSE;
+ }
+
+ g_assert (value <= G_MAXUINT32);
+
+ *dest_ofs += g_unichar_to_utf8 (value, dest + *dest_ofs);
+ *src_ofs += length;
+
+ return TRUE;
+}
+
static AST *
string_parse (TokenStream *stream,
va_list *app,
@@ -1441,6 +1590,136 @@ string_parse (TokenStream *stream,
{
case '\0':
parser_set_error (error, &ref, NULL,
+ G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
+ "unterminated string constant");
+ g_free (token);
+ g_free (str);
+ return NULL;
+
+ case '\\':
+ switch (token[++i])
+ {
+ case '\0':
+ parser_set_error (error, &ref, NULL,
+ G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
+ "unterminated string constant");
+ g_free (token);
+ g_free (str);
+ return NULL;
+
+ case 'u':
+ if (!unicode_unescape (token, &i, str, &j, 4, &ref, error))
+ {
+ g_free (token);
+ g_free (str);
+ return NULL;
+ }
+ continue;
+
+ case 'U':
+ if (!unicode_unescape (token, &i, str, &j, 8, &ref, error))
+ {
+ g_free (token);
+ g_free (str);
+ return NULL;
+ }
+ continue;
+
+ case 'a': str[j++] = '\a'; i++; continue;
+ case 'b': str[j++] = '\b'; i++; continue;
+ case 'f': str[j++] = '\f'; i++; continue;
+ case 'n': str[j++] = '\n'; i++; continue;
+ case 'r': str[j++] = '\r'; i++; continue;
+ case 't': str[j++] = '\t'; i++; continue;
+ case 'v': str[j++] = '\v'; i++; continue;
+ case '\n': i++; continue;
+ }
+
+ default:
+ str[j++] = token[i++];
+ }
+ str[j++] = '\0';
+ g_free (token);
+
+ string = g_slice_new (String);
+ string->ast.class = &string_class;
+ string->string = str;
+
+ token_stream_next (stream);
+
+ return (AST *) string;
+}
+
+typedef struct
+{
+ AST ast;
+ gchar *string;
+} ByteString;
+
+static gchar *
+bytestring_get_pattern (AST *ast,
+ GError **error)
+{
+ return g_strdup ("May");
+}
+
+static GVariant *
+bytestring_get_value (AST *ast,
+ const GVariantType *type,
+ GError **error)
+{
+ ByteString *string = (ByteString *) ast;
+
+ if (!g_variant_type_equal (type, G_VARIANT_TYPE_BYTESTRING))
+ return ast_type_error (ast, type, error);
+
+ return g_variant_new_bytestring (string->string);
+}
+
+static void
+bytestring_free (AST *ast)
+{
+ ByteString *string = (ByteString *) ast;
+
+ g_free (string->string);
+ g_slice_free (ByteString, string);
+}
+
+static AST *
+bytestring_parse (TokenStream *stream,
+ va_list *app,
+ GError **error)
+{
+ static const ASTClass bytestring_class = {
+ bytestring_get_pattern,
+ maybe_wrapper, bytestring_get_value,
+ bytestring_free
+ };
+ ByteString *string;
+ SourceRef ref;
+ gchar *token;
+ gsize length;
+ gchar quote;
+ gchar *str;
+ gint i, j;
+
+ token_stream_start_ref (stream, &ref);
+ token = token_stream_get (stream);
+ token_stream_end_ref (stream, &ref);
+ g_assert (token[0] == 'b');
+ length = strlen (token);
+ quote = token[1];
+
+ str = g_malloc (length);
+ g_assert (quote == '"' || quote == '\'');
+ j = 0;
+ i = 2;
+ while (token[i] != quote)
+ switch (token[i])
+ {
+ case '\0':
+ parser_set_error (error, &ref, NULL,
+ G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
"unterminated string constant");
g_free (token);
return NULL;
@@ -1450,6 +1729,7 @@ string_parse (TokenStream *stream,
{
case '\0':
parser_set_error (error, &ref, NULL,
+ G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
"unterminated string constant");
g_free (token);
return NULL;
@@ -1470,11 +1750,13 @@ string_parse (TokenStream *stream,
}
continue;
+ case 'a': str[j++] = '\a'; i++; continue;
case 'b': str[j++] = '\b'; i++; continue;
case 'f': str[j++] = '\f'; i++; continue;
case 'n': str[j++] = '\n'; i++; continue;
case 'r': str[j++] = '\r'; i++; continue;
case 't': str[j++] = '\t'; i++; continue;
+ case 'v': str[j++] = '\v'; i++; continue;
case '\n': i++; continue;
}
@@ -1484,8 +1766,8 @@ string_parse (TokenStream *stream,
str[j++] = '\0';
g_free (token);
- string = g_slice_new (String);
- string->ast.class = &string_class;
+ string = g_slice_new (ByteString);
+ string->ast.class = &bytestring_class;
string->string = str;
token_stream_next (stream);
@@ -1507,8 +1789,9 @@ number_get_pattern (AST *ast,
Number *number = (Number *) ast;
if (strchr (number->token, '.') ||
- (!g_str_has_prefix (number->token, "0x") &&
- strchr (number->token, 'e')))
+ (!g_str_has_prefix (number->token, "0x") && strchr (number->token, 'e')) ||
+ strstr (number->token, "inf") ||
+ strstr (number->token, "nan"))
return g_strdup ("Md");
return g_strdup ("MN");
@@ -1519,7 +1802,9 @@ number_overflow (AST *ast,
const GVariantType *type,
GError **error)
{
- ast_set_error (ast, error, NULL, "number out of range for type `%c'",
+ ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE,
+ "number out of range for type '%c'",
g_variant_type_peek_string (type)[0]);
return NULL;
}
@@ -1547,7 +1832,9 @@ number_get_value (AST *ast,
dbl_val = g_ascii_strtod (token, &end);
if (dbl_val != 0.0 && errno == ERANGE)
{
- ast_set_error (ast, error, NULL, "number too big for any type");
+ ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
+ "number too big for any type");
return NULL;
}
@@ -1566,7 +1853,9 @@ number_get_value (AST *ast,
abs_val = g_ascii_strtoull (token, &end, 0);
if (abs_val == G_MAXUINT64 && errno == ERANGE)
{
- ast_set_error (ast, error, NULL, "integer too big for any type");
+ ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
+ "integer too big for any type");
return NULL;
}
@@ -1586,6 +1875,7 @@ number_get_value (AST *ast,
ref.end = ref.start + 1;
parser_set_error (error, &ref, NULL,
+ G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
"invalid character in number");
return NULL;
}
@@ -1608,7 +1898,7 @@ number_get_value (AST *ast,
case 'q':
if (negative || abs_val > G_MAXUINT16)
return number_overflow (ast, type, error);
- return g_variant_new_uint16 (negative ? -abs_val : abs_val);
+ return g_variant_new_uint16 (abs_val);
case 'i':
if (abs_val - negative > G_MAXINT32)
@@ -1618,7 +1908,7 @@ number_get_value (AST *ast,
case 'u':
if (negative || abs_val > G_MAXUINT32)
return number_overflow (ast, type, error);
- return g_variant_new_uint32 (negative ? -abs_val : abs_val);
+ return g_variant_new_uint32 (abs_val);
case 'x':
if (abs_val - negative > G_MAXINT64)
@@ -1628,7 +1918,7 @@ number_get_value (AST *ast,
case 't':
if (negative)
return number_overflow (ast, type, error);
- return g_variant_new_uint64 (negative ? -abs_val : abs_val);
+ return g_variant_new_uint64 (abs_val);
case 'h':
if (abs_val - negative > G_MAXINT32)
@@ -1796,6 +2086,7 @@ positional_parse (TokenStream *stream,
if (*endptr || positional->value == NULL)
{
token_stream_set_error (stream, error, TRUE,
+ G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING,
"invalid GVariant format string");
/* memory management doesn't matter in case of programmer error. */
return NULL;
@@ -1867,6 +2158,7 @@ typedecl_parse (TokenStream *stream,
if (!g_variant_type_string_is_valid (token + 1))
{
token_stream_set_error (stream, error, TRUE,
+ G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING,
"invalid type declaration");
g_free (token);
@@ -1878,6 +2170,7 @@ typedecl_parse (TokenStream *stream,
if (!g_variant_type_is_definite (type))
{
token_stream_set_error (stream, error, TRUE,
+ G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED,
"type declarations must be definite");
g_variant_type_free (type);
g_free (token);
@@ -1931,7 +2224,9 @@ typedecl_parse (TokenStream *stream,
else
{
- token_stream_set_error (stream, error, TRUE, "unknown keyword");
+ token_stream_set_error (stream, error, TRUE,
+ G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
+ "unknown keyword");
return NULL;
}
}
@@ -1982,6 +2277,11 @@ parse (TokenStream *stream,
else if (token_stream_consume (stream, "false"))
result = boolean_new (FALSE);
+ else if (token_stream_is_numeric (stream) ||
+ token_stream_peek_string (stream, "inf") ||
+ token_stream_peek_string (stream, "nan"))
+ result = number_parse (stream, app, error);
+
else if (token_stream_peek (stream, 'n') ||
token_stream_peek (stream, 'j'))
result = maybe_parse (stream, app, error);
@@ -1990,16 +2290,19 @@ parse (TokenStream *stream,
token_stream_is_keyword (stream))
result = typedecl_parse (stream, app, error);
- else if (token_stream_is_numeric (stream))
- result = number_parse (stream, app, error);
-
else if (token_stream_peek (stream, '\'') ||
token_stream_peek (stream, '"'))
result = string_parse (stream, app, error);
+ else if (token_stream_peek2 (stream, 'b', '\'') ||
+ token_stream_peek2 (stream, 'b', '"'))
+ result = bytestring_parse (stream, app, error);
+
else
{
- token_stream_set_error (stream, error, FALSE, "expected value");
+ token_stream_set_error (stream, error, FALSE,
+ G_VARIANT_PARSE_ERROR_VALUE_EXPECTED,
+ "expected value");
return NULL;
}
@@ -2014,17 +2317,18 @@ parse (TokenStream *stream,
/**
* g_variant_parse:
- * @type: a #GVariantType, or %NULL
+ * @type: (allow-none): a #GVariantType, or %NULL
* @text: a string containing a GVariant in text form
- * @limit: a pointer to the end of @text, or %NULL
- * @endptr: a location to store the end pointer, or %NULL
- * @error: a pointer to a %NULL #GError pointer, or %NULL
- * @Returns: a reference to a #GVariant, or %NULL
+ * @limit: (allow-none): a pointer to the end of @text, or %NULL
+ * @endptr: (allow-none): a location to store the end pointer, or %NULL
+ * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
*
* Parses a #GVariant from a text representation.
*
* A single #GVariant is parsed from the content of @text.
*
+ * The format is described [here][gvariant-text].
+ *
* The memory at @limit will never be accessed and the parser behaves as
* if the character at @limit is the nul terminator. This has the
* effect of bounding @text.
@@ -2045,10 +2349,12 @@ parse (TokenStream *stream,
* is returned.
*
* In case of any error, %NULL will be returned. If @error is non-%NULL
- * then it will be set to reflect the error that occured.
+ * then it will be set to reflect the error that occurred.
*
* Officially, the language understood by the parser is "any string
* produced by g_variant_print()".
+ *
+ * Returns: a reference to a #GVariant, or %NULL
**/
GVariant *
g_variant_parse (const GVariantType *type,
@@ -2057,7 +2363,7 @@ g_variant_parse (const GVariantType *type,
const gchar **endptr,
GError **error)
{
- TokenStream stream = { };
+ TokenStream stream = { 0, };
GVariant *result = NULL;
AST *ast;
@@ -2091,6 +2397,7 @@ g_variant_parse (const GVariantType *type,
stream.stream - text };
parser_set_error (error, &ref, NULL,
+ G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END,
"expected end of input");
g_variant_unref (result);
@@ -2111,7 +2418,6 @@ g_variant_parse (const GVariantType *type,
* g_variant_new_parsed_va:
* @format: a text format #GVariant
* @app: a pointer to a #va_list
- * @returns: a new, usually floating, #GVariant
*
* Parses @format and returns the result.
*
@@ -2124,18 +2430,24 @@ g_variant_parse (const GVariantType *type,
* #GVariant pointer will be returned unmodified, without adding any
* additional references.
*
+ * Note that the arguments in @app must be of the correct width for their types
+ * specified in @format when collected into the #va_list. See
+ * the [GVariant varargs documentation][gvariant-varargs].
+ *
* In order to behave correctly in all cases it is necessary for the
* calling function to g_variant_ref_sink() the return result before
* returning control to the user that originally provided the pointer.
* At this point, the caller will have their own full reference to the
* result. This can also be done by adding the result to a container,
* or by passing it to another g_variant_new() call.
+ *
+ * Returns: a new, usually floating, #GVariant
**/
GVariant *
g_variant_new_parsed_va (const gchar *format,
va_list *app)
{
- TokenStream stream = { };
+ TokenStream stream = { 0, };
GVariant *result = NULL;
GError *error = NULL;
AST *ast;
@@ -2166,25 +2478,30 @@ g_variant_new_parsed_va (const gchar *format,
* g_variant_new_parsed:
* @format: a text format #GVariant
* @...: arguments as per @format
- * @returns: a new floating #GVariant instance
*
* Parses @format and returns the result.
*
- * @format must be a text format #GVariant with one extention: at any
+ * @format must be a text format #GVariant with one extension: at any
* point that a value may appear in the text, a '%' character followed
* by a GVariant format string (as per g_variant_new()) may appear. In
* that case, the same arguments are collected from the argument list as
* g_variant_new() would have collected.
*
- * Consider this simple example:
+ * Note that the arguments must be of the correct width for their types
+ * specified in @format. This can be achieved by casting them. See
+ * the [GVariant varargs documentation][gvariant-varargs].
*
- *
+ * Consider this simple example:
+ * |[
* g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
- *
+ * ]|
*
* In the example, the variable argument parameters are collected and
* filled in as if they were part of the original string to produce the
- * result of [('one', 1), ('two', 2), ('three', 3)]
.
+ * result of
+ * |[
+ * [('one', 1), ('two', 2), ('three', 3)]
+ * ]|
*
* This function is intended only to be used with @format as a string
* literal. Any parse error is fatal to the calling process. If you
@@ -2192,8 +2509,10 @@ g_variant_new_parsed_va (const gchar *format,
*
* You may not use this function to return, unmodified, a single
* #GVariant pointer from the argument list. ie: @format may not solely
- * be anything along the lines of "%*", "%?", "%r", or anything starting
+ * be anything along the lines of "%*", "%?", "\%r", or anything starting
* with "%@".
+ *
+ * Returns: a new floating #GVariant instance
**/
GVariant *
g_variant_new_parsed (const gchar *format,
@@ -2209,5 +2528,271 @@ g_variant_new_parsed (const gchar *format,
return result;
}
-#define __G_VARIANT_PARSER_C__
-#include "galiasdef.c"
+/**
+ * g_variant_builder_add_parsed:
+ * @builder: a #GVariantBuilder
+ * @format: a text format #GVariant
+ * @...: arguments as per @format
+ *
+ * Adds to a #GVariantBuilder.
+ *
+ * This call is a convenience wrapper that is exactly equivalent to
+ * calling g_variant_new_parsed() followed by
+ * g_variant_builder_add_value().
+ *
+ * Note that the arguments must be of the correct width for their types
+ * specified in @format_string. This can be achieved by casting them. See
+ * the [GVariant varargs documentation][gvariant-varargs].
+ *
+ * This function might be used as follows:
+ *
+ * |[
+ * GVariant *
+ * make_pointless_dictionary (void)
+ * {
+ * GVariantBuilder builder;
+ * int i;
+ *
+ * g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ * g_variant_builder_add_parsed (&builder, "{'width', <%i>}", 600);
+ * g_variant_builder_add_parsed (&builder, "{'title', <%s>}", "foo");
+ * g_variant_builder_add_parsed (&builder, "{'transparency', <0.5>}");
+ * return g_variant_builder_end (&builder);
+ * }
+ * ]|
+ *
+ * Since: 2.26
+ */
+void
+g_variant_builder_add_parsed (GVariantBuilder *builder,
+ const gchar *format,
+ ...)
+{
+ va_list ap;
+
+ va_start (ap, format);
+ g_variant_builder_add_value (builder, g_variant_new_parsed_va (format, &ap));
+ va_end (ap);
+}
+
+static gboolean
+parse_num (const gchar *num,
+ const gchar *limit,
+ gint *result)
+{
+ gchar *endptr;
+ gint64 bignum;
+
+ bignum = g_ascii_strtoll (num, &endptr, 10);
+
+ if (endptr != limit)
+ return FALSE;
+
+ if (bignum < 0 || bignum > G_MAXINT)
+ return FALSE;
+
+ *result = bignum;
+
+ return TRUE;
+}
+
+static void
+add_last_line (GString *err,
+ const gchar *str)
+{
+ const gchar *last_nl;
+ gchar *chomped;
+ gint i;
+
+ /* This is an error at the end of input. If we have a file
+ * with newlines, that's probably the empty string after the
+ * last newline, which is not the most useful thing to show.
+ *
+ * Instead, show the last line of non-whitespace that we have
+ * and put the pointer at the end of it.
+ */
+ chomped = g_strchomp (g_strdup (str));
+ last_nl = strrchr (chomped, '\n');
+ if (last_nl == NULL)
+ last_nl = chomped;
+ else
+ last_nl++;
+
+ /* Print the last line like so:
+ *
+ * [1, 2, 3,
+ * ^
+ */
+ g_string_append (err, " ");
+ if (last_nl[0])
+ g_string_append (err, last_nl);
+ else
+ g_string_append (err, "(empty input)");
+ g_string_append (err, "\n ");
+ for (i = 0; last_nl[i]; i++)
+ g_string_append_c (err, ' ');
+ g_string_append (err, "^\n");
+ g_free (chomped);
+}
+
+static void
+add_lines_from_range (GString *err,
+ const gchar *str,
+ const gchar *start1,
+ const gchar *end1,
+ const gchar *start2,
+ const gchar *end2)
+{
+ while (str < end1 || str < end2)
+ {
+ const gchar *nl;
+
+ nl = str + strcspn (str, "\n");
+
+ if ((start1 < nl && str < end1) || (start2 < nl && str < end2))
+ {
+ const gchar *s;
+
+ /* We're going to print this line */
+ g_string_append (err, " ");
+ g_string_append_len (err, str, nl - str);
+ g_string_append (err, "\n ");
+
+ /* And add underlines... */
+ for (s = str; s < nl; s++)
+ {
+ if ((start1 <= s && s < end1) || (start2 <= s && s < end2))
+ g_string_append_c (err, '^');
+ else
+ g_string_append_c (err, ' ');
+ }
+ g_string_append_c (err, '\n');
+ }
+
+ if (!*nl)
+ break;
+
+ str = nl + 1;
+ }
+}
+
+/**
+ * g_variant_parse_error_print_context:
+ * @error: a #GError from the #GVariantParseError domain
+ * @source_str: the string that was given to the parser
+ *
+ * Pretty-prints a message showing the context of a #GVariant parse
+ * error within the string for which parsing was attempted.
+ *
+ * The resulting string is suitable for output to the console or other
+ * monospace media where newlines are treated in the usual way.
+ *
+ * The message will typically look something like one of the following:
+ *
+ * |[
+ * unterminated string constant:
+ * (1, 2, 3, 'abc
+ * ^^^^
+ * ]|
+ *
+ * or
+ *
+ * |[
+ * unable to find a common type:
+ * [1, 2, 3, 'str']
+ * ^ ^^^^^
+ * ]|
+ *
+ * The format of the message may change in a future version.
+ *
+ * @error must have come from a failed attempt to g_variant_parse() and
+ * @source_str must be exactly the same string that caused the error.
+ * If @source_str was not nul-terminated when you passed it to
+ * g_variant_parse() then you must add nul termination before using this
+ * function.
+ *
+ * Returns: (transfer full): the printed message
+ *
+ * Since: 2.40
+ **/
+gchar *
+g_variant_parse_error_print_context (GError *error,
+ const gchar *source_str)
+{
+ const gchar *colon, *dash, *comma;
+ gboolean success = FALSE;
+ GString *err;
+
+ g_return_val_if_fail (error->domain == G_VARIANT_PARSE_ERROR, FALSE);
+
+ /* We can only have a limited number of possible types of ranges
+ * emitted from the parser:
+ *
+ * - a: -- usually errors from the tokeniser (eof, invalid char, etc.)
+ * - a-b: -- usually errors from handling one single token
+ * - a-b,c-d: -- errors involving two tokens (ie: type inferencing)
+ *
+ * We never see, for example "a,c".
+ */
+
+ colon = strchr (error->message, ':');
+ dash = strchr (error->message, '-');
+ comma = strchr (error->message, ',');
+
+ if (!colon)
+ return NULL;
+
+ err = g_string_new (colon + 1);
+ g_string_append (err, ":\n");
+
+ if (dash == NULL || colon < dash)
+ {
+ gint point;
+
+ /* we have a single point */
+ if (!parse_num (error->message, colon, &point))
+ goto out;
+
+ if (point >= strlen (source_str))
+ /* the error is at the end of the input */
+ add_last_line (err, source_str);
+ else
+ /* otherwise just treat it as a error at a thin range */
+ add_lines_from_range (err, source_str, source_str + point, source_str + point + 1, NULL, NULL);
+ }
+ else
+ {
+ /* We have one or two ranges... */
+ if (comma && comma < colon)
+ {
+ gint start1, end1, start2, end2;
+ const gchar *dash2;
+
+ /* Two ranges */
+ dash2 = strchr (comma, '-');
+
+ if (!parse_num (error->message, dash, &start1) || !parse_num (dash + 1, comma, &end1) ||
+ !parse_num (comma + 1, dash2, &start2) || !parse_num (dash2 + 1, colon, &end2))
+ goto out;
+
+ add_lines_from_range (err, source_str,
+ source_str + start1, source_str + end1,
+ source_str + start2, source_str + end2);
+ }
+ else
+ {
+ gint start, end;
+
+ /* One range */
+ if (!parse_num (error->message, dash, &start) || !parse_num (dash + 1, colon, &end))
+ goto out;
+
+ add_lines_from_range (err, source_str, source_str + start, source_str + end, NULL, NULL);
+ }
+ }
+
+ success = TRUE;
+
+out:
+ return g_string_free (err, !success);
+}