2 * Copyright © 2009, 2010 Codethink Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Author: Ryan Lortie <desrt@desrt.ca>
31 #include "gstrfuncs.h"
32 #include "gtestutils.h"
34 #include "gvarianttype.h"
40 * designed by ryan lortie and william hua
41 * designed in itb-229 and at ghazi's, 2009.
45 * G_VARIANT_PARSE_ERROR:
47 * Error domain for GVariant text format parsing. Specific error codes
48 * are not currently defined for this domain. See #GError for
49 * information on error domains.
53 * @G_VARIANT_PARSE_ERROR_FAILED: generic error (unused)
54 * @G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED: a non-basic #GVariantType was given where a basic type was expected
55 * @G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE: cannot infer the #GVariantType
56 * @G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED: an indefinite #GVariantType was given where a definite type was expected
57 * @G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END: extra data after parsing finished
58 * @G_VARIANT_PARSE_ERROR_INVALID_CHARACTER: invalid character in number or unicode escape
59 * @G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING: not a valid #GVariant format string
60 * @G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH: not a valid object path
61 * @G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE: not a valid type signature
62 * @G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING: not a valid #GVariant type string
63 * @G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE: could not find a common type for array entries
64 * @G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE: the numerical value is out of range of the given type
65 * @G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG: the numerical value is out of range for any type
66 * @G_VARIANT_PARSE_ERROR_TYPE_ERROR: cannot parse as variant of the specified type
67 * @G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN: an unexpected token was encountered
68 * @G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD: an unknown keyword was encountered
69 * @G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT: unterminated string constant
70 * @G_VARIANT_PARSE_ERROR_VALUE_EXPECTED: no value given
72 * Error codes returned by parsing text-format GVariants.
74 G_DEFINE_QUARK (g-variant-parse-error-quark, g_variant_parser_get_error)
82 parser_set_error_va (GError **error,
89 GString *msg = g_string_new (NULL);
91 if (location->start == location->end)
92 g_string_append_printf (msg, "%d", location->start);
94 g_string_append_printf (msg, "%d-%d", location->start, location->end);
98 g_assert (other->start != other->end);
99 g_string_append_printf (msg, ",%d-%d", other->start, other->end);
101 g_string_append_c (msg, ':');
103 g_string_append_vprintf (msg, format, ap);
104 g_set_error_literal (error, G_VARIANT_PARSE_ERROR, code, msg->str);
105 g_string_free (msg, TRUE);
109 parser_set_error (GError **error,
118 va_start (ap, format);
119 parser_set_error_va (error, location, other, code, format, ap);
134 token_stream_set_error (TokenStream *stream,
144 ref.start = stream->this - stream->start;
147 ref.end = stream->stream - stream->start;
151 va_start (ap, format);
152 parser_set_error_va (error, &ref, NULL, code, format, ap);
157 token_stream_prepare (TokenStream *stream)
162 if (stream->this != NULL)
165 while (stream->stream != stream->end && g_ascii_isspace (*stream->stream))
168 if (stream->stream == stream->end || *stream->stream == '\0')
170 stream->this = stream->stream;
174 switch (stream->stream[0])
176 case '-': case '+': case '.': case '0': case '1': case '2':
177 case '3': case '4': case '5': case '6': case '7': case '8':
179 for (end = stream->stream; end != stream->end; end++)
180 if (!g_ascii_isalnum (*end) &&
181 *end != '-' && *end != '+' && *end != '.')
186 if (stream->stream[1] == '\'' || stream->stream[1] == '"')
188 for (end = stream->stream + 2; end != stream->end; end++)
189 if (*end == stream->stream[1] || *end == '\0' ||
190 (*end == '\\' && (++end == stream->end || *end == '\0')))
193 if (end != stream->end && *end)
200 case 'a': /* 'b' */ case 'c': case 'd': case 'e': case 'f':
201 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
202 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
203 case 's': case 't': case 'u': case 'v': case 'w': case 'x':
205 for (end = stream->stream; end != stream->end; end++)
206 if (!g_ascii_isalnum (*end))
211 for (end = stream->stream + 1; end != stream->end; end++)
212 if (*end == stream->stream[0] || *end == '\0' ||
213 (*end == '\\' && (++end == stream->end || *end == '\0')))
216 if (end != stream->end && *end)
221 /* stop at the first space, comma, colon or unmatched bracket.
222 * deals nicely with cases like (%i, %i) or {%i: %i}.
224 for (end = stream->stream + 1;
225 end != stream->end && *end != ',' &&
226 *end != ':' && *end != '>' && !g_ascii_isspace (*end);
229 if (*end == '(' || *end == '{')
232 else if ((*end == ')' || *end == '}') && !brackets--)
238 end = stream->stream + 1;
242 stream->this = stream->stream;
243 stream->stream = end;
249 token_stream_next (TokenStream *stream)
255 token_stream_peek (TokenStream *stream,
258 if (!token_stream_prepare (stream))
261 return stream->this[0] == first_char;
265 token_stream_peek2 (TokenStream *stream,
269 if (!token_stream_prepare (stream))
272 return stream->this[0] == first_char &&
273 stream->this[1] == second_char;
277 token_stream_is_keyword (TokenStream *stream)
279 if (!token_stream_prepare (stream))
282 return g_ascii_isalpha (stream->this[0]) &&
283 g_ascii_isalpha (stream->this[1]);
287 token_stream_is_numeric (TokenStream *stream)
289 if (!token_stream_prepare (stream))
292 return (g_ascii_isdigit (stream->this[0]) ||
293 stream->this[0] == '-' ||
294 stream->this[0] == '+' ||
295 stream->this[0] == '.');
299 token_stream_peek_string (TokenStream *stream,
302 gint length = strlen (token);
304 return token_stream_prepare (stream) &&
305 stream->stream - stream->this == length &&
306 memcmp (stream->this, token, length) == 0;
310 token_stream_consume (TokenStream *stream,
313 if (!token_stream_peek_string (stream, token))
316 token_stream_next (stream);
321 token_stream_require (TokenStream *stream,
323 const gchar *purpose,
327 if (!token_stream_consume (stream, token))
329 token_stream_set_error (stream, error, FALSE,
330 G_VARIANT_PARSE_ERROR_UNEXPECTED_TOKEN,
331 "expected `%s'%s", token, purpose);
339 token_stream_assert (TokenStream *stream,
342 gboolean correct_token;
344 correct_token = token_stream_consume (stream, token);
345 g_assert (correct_token);
349 token_stream_get (TokenStream *stream)
353 if (!token_stream_prepare (stream))
356 result = g_strndup (stream->this, stream->stream - stream->this);
362 token_stream_start_ref (TokenStream *stream,
365 token_stream_prepare (stream);
366 ref->start = stream->this - stream->start;
370 token_stream_end_ref (TokenStream *stream,
373 ref->end = stream->stream - stream->start;
377 pattern_copy (gchar **out,
382 while (**in == 'a' || **in == 'm' || **in == 'M')
383 *(*out)++ = *(*in)++;
387 if (**in == '(' || **in == '{')
390 else if (**in == ')' || **in == '}')
393 *(*out)++ = *(*in)++;
399 pattern_coalesce (const gchar *left,
405 /* the length of the output is loosely bound by the sum of the input
406 * lengths, not simply the greater of the two lengths.
408 * (*(iii)) + ((iii)*) ((iii)(iii))
412 out = result = g_malloc (strlen (left) + strlen (right));
414 while (*left && *right)
424 const gchar **one = &left, **the_other = &right;
427 if (**one == '*' && **the_other != ')')
429 pattern_copy (&out, the_other);
433 else if (**one == 'M' && **the_other == 'm')
435 *out++ = *(*the_other)++;
438 else if (**one == 'M' && **the_other != 'm')
443 else if (**one == 'N' && strchr ("ynqiuxthd", **the_other))
445 *out++ = *(*the_other)++;
449 else if (**one == 'S' && strchr ("sog", **the_other))
451 *out++ = *(*the_other)++;
455 else if (one == &left)
457 one = &right, the_other = &left;
477 typedef struct _AST AST;
478 typedef gchar * (*get_pattern_func) (AST *ast,
480 typedef GVariant * (*get_value_func) (AST *ast,
481 const GVariantType *type,
483 typedef GVariant * (*get_base_value_func) (AST *ast,
484 const GVariantType *type,
486 typedef void (*free_func) (AST *ast);
490 gchar * (* get_pattern) (AST *ast,
492 GVariant * (* get_value) (AST *ast,
493 const GVariantType *type,
495 GVariant * (* get_base_value) (AST *ast,
496 const GVariantType *type,
498 void (* free) (AST *ast);
503 const ASTClass *class;
504 SourceRef source_ref;
508 ast_get_pattern (AST *ast,
511 return ast->class->get_pattern (ast, error);
515 ast_get_value (AST *ast,
516 const GVariantType *type,
519 return ast->class->get_value (ast, type, error);
525 ast->class->free (ast);
529 ast_set_error (AST *ast,
538 va_start (ap, format);
539 parser_set_error_va (error, &ast->source_ref,
540 other_ast ? & other_ast->source_ref : NULL,
547 ast_type_error (AST *ast,
548 const GVariantType *type,
553 typestr = g_variant_type_dup_string (type);
554 ast_set_error (ast, error, NULL,
555 G_VARIANT_PARSE_ERROR_TYPE_ERROR,
556 "can not parse as value of type `%s'",
564 ast_resolve (AST *ast,
571 pattern = ast_get_pattern (ast, error);
576 /* choose reasonable defaults
578 * 1) favour non-maybe values where possible
579 * 2) default type for strings is 's'
580 * 3) default type for integers is 'i'
582 for (i = 0; pattern[i]; i++)
586 ast_set_error (ast, error, NULL,
587 G_VARIANT_PARSE_ERROR_CANNOT_INFER_TYPE,
588 "unable to infer type");
604 pattern[j++] = pattern[i];
609 value = ast_get_value (ast, G_VARIANT_TYPE (pattern), error);
616 static AST *parse (TokenStream *stream,
621 ast_array_append (AST ***array,
625 if ((*n_items & (*n_items - 1)) == 0)
626 *array = g_renew (AST *, *array, *n_items ? 2 ** n_items : 1);
628 (*array)[(*n_items)++] = ast;
632 ast_array_free (AST **array,
637 for (i = 0; i < n_items; i++)
643 ast_array_get_pattern (AST **array,
650 pattern = ast_get_pattern (array[0], error);
655 for (i = 1; i < n_items; i++)
659 tmp = ast_get_pattern (array[i], error);
667 merged = pattern_coalesce (pattern, tmp);
672 /* set coalescence implies pairwise coalescence (i think).
673 * we should therefore be able to trace the failure to a single
684 /* if 'j' reaches 'i' then we failed to find the pair */
687 tmp2 = ast_get_pattern (array[j], NULL);
688 g_assert (tmp2 != NULL);
690 m = pattern_coalesce (tmp, tmp2);
696 /* we found a conflict between 'i' and 'j'.
698 * report the error. note: 'j' is first.
700 ast_set_error (array[j], error, array[i],
701 G_VARIANT_PARSE_ERROR_NO_COMMON_TYPE,
702 "unable to find a common type");
726 maybe_get_pattern (AST *ast,
729 Maybe *maybe = (Maybe *) ast;
731 if (maybe->child != NULL)
733 gchar *child_pattern;
736 child_pattern = ast_get_pattern (maybe->child, error);
738 if (child_pattern == NULL)
741 pattern = g_strdup_printf ("m%s", child_pattern);
742 g_free (child_pattern);
747 return g_strdup ("m*");
751 maybe_get_value (AST *ast,
752 const GVariantType *type,
755 Maybe *maybe = (Maybe *) ast;
758 if (!g_variant_type_is_maybe (type))
759 return ast_type_error (ast, type, error);
761 type = g_variant_type_element (type);
765 value = ast_get_value (maybe->child, type, error);
773 return g_variant_new_maybe (type, value);
777 maybe_free (AST *ast)
779 Maybe *maybe = (Maybe *) ast;
781 if (maybe->child != NULL)
782 ast_free (maybe->child);
784 g_slice_free (Maybe, maybe);
788 maybe_parse (TokenStream *stream,
792 static const ASTClass maybe_class = {
794 maybe_get_value, NULL,
800 if (token_stream_consume (stream, "just"))
802 child = parse (stream, app, error);
807 else if (!token_stream_consume (stream, "nothing"))
809 token_stream_set_error (stream, error, TRUE,
810 G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
815 maybe = g_slice_new (Maybe);
816 maybe->ast.class = &maybe_class;
817 maybe->child = child;
819 return (AST *) maybe;
823 maybe_wrapper (AST *ast,
824 const GVariantType *type,
827 const GVariantType *t;
831 for (depth = 0, t = type;
832 g_variant_type_is_maybe (t);
833 depth++, t = g_variant_type_element (t));
835 value = ast->class->get_base_value (ast, t, error);
841 value = g_variant_new_maybe (NULL, value);
855 array_get_pattern (AST *ast,
858 Array *array = (Array *) ast;
862 if (array->n_children == 0)
863 return g_strdup ("Ma*");
865 pattern = ast_array_get_pattern (array->children, array->n_children, error);
870 result = g_strdup_printf ("Ma%s", pattern);
877 array_get_value (AST *ast,
878 const GVariantType *type,
881 Array *array = (Array *) ast;
882 const GVariantType *childtype;
883 GVariantBuilder builder;
886 if (!g_variant_type_is_array (type))
887 return ast_type_error (ast, type, error);
889 g_variant_builder_init (&builder, type);
890 childtype = g_variant_type_element (type);
892 for (i = 0; i < array->n_children; i++)
896 if (!(child = ast_get_value (array->children[i], childtype, error)))
898 g_variant_builder_clear (&builder);
902 g_variant_builder_add_value (&builder, child);
905 return g_variant_builder_end (&builder);
909 array_free (AST *ast)
911 Array *array = (Array *) ast;
913 ast_array_free (array->children, array->n_children);
914 g_slice_free (Array, array);
918 array_parse (TokenStream *stream,
922 static const ASTClass array_class = {
924 maybe_wrapper, array_get_value,
927 gboolean need_comma = FALSE;
930 array = g_slice_new (Array);
931 array->ast.class = &array_class;
932 array->children = NULL;
933 array->n_children = 0;
935 token_stream_assert (stream, "[");
936 while (!token_stream_consume (stream, "]"))
941 !token_stream_require (stream, ",",
942 " or `]' to follow array element",
946 child = parse (stream, app, error);
951 ast_array_append (&array->children, &array->n_children, child);
955 return (AST *) array;
958 ast_array_free (array->children, array->n_children);
959 g_slice_free (Array, array);
973 tuple_get_pattern (AST *ast,
976 Tuple *tuple = (Tuple *) ast;
977 gchar *result = NULL;
981 parts = g_new (gchar *, tuple->n_children + 4);
982 parts[tuple->n_children + 1] = (gchar *) ")";
983 parts[tuple->n_children + 2] = NULL;
984 parts[0] = (gchar *) "M(";
986 for (i = 0; i < tuple->n_children; i++)
987 if (!(parts[i + 1] = ast_get_pattern (tuple->children[i], error)))
990 if (i == tuple->n_children)
991 result = g_strjoinv ("", parts);
993 /* parts[0] should not be freed */
1002 tuple_get_value (AST *ast,
1003 const GVariantType *type,
1006 Tuple *tuple = (Tuple *) ast;
1007 const GVariantType *childtype;
1008 GVariantBuilder builder;
1011 if (!g_variant_type_is_tuple (type))
1012 return ast_type_error (ast, type, error);
1014 g_variant_builder_init (&builder, type);
1015 childtype = g_variant_type_first (type);
1017 for (i = 0; i < tuple->n_children; i++)
1021 if (childtype == NULL)
1023 g_variant_builder_clear (&builder);
1024 return ast_type_error (ast, type, error);
1027 if (!(child = ast_get_value (tuple->children[i], childtype, error)))
1029 g_variant_builder_clear (&builder);
1033 g_variant_builder_add_value (&builder, child);
1034 childtype = g_variant_type_next (childtype);
1037 if (childtype != NULL)
1039 g_variant_builder_clear (&builder);
1040 return ast_type_error (ast, type, error);
1043 return g_variant_builder_end (&builder);
1047 tuple_free (AST *ast)
1049 Tuple *tuple = (Tuple *) ast;
1051 ast_array_free (tuple->children, tuple->n_children);
1052 g_slice_free (Tuple, tuple);
1056 tuple_parse (TokenStream *stream,
1060 static const ASTClass tuple_class = {
1062 maybe_wrapper, tuple_get_value,
1065 gboolean need_comma = FALSE;
1066 gboolean first = TRUE;
1069 tuple = g_slice_new (Tuple);
1070 tuple->ast.class = &tuple_class;
1071 tuple->children = NULL;
1072 tuple->n_children = 0;
1074 token_stream_assert (stream, "(");
1075 while (!token_stream_consume (stream, ")"))
1080 !token_stream_require (stream, ",",
1081 " or `)' to follow tuple element",
1085 child = parse (stream, app, error);
1090 ast_array_append (&tuple->children, &tuple->n_children, child);
1092 /* the first time, we absolutely require a comma, so grab it here
1093 * and leave need_comma = FALSE so that the code above doesn't
1094 * require a second comma.
1096 * the second and remaining times, we set need_comma = TRUE.
1100 if (!token_stream_require (stream, ",",
1101 " after first tuple element", error))
1110 return (AST *) tuple;
1113 ast_array_free (tuple->children, tuple->n_children);
1114 g_slice_free (Tuple, tuple);
1127 variant_get_pattern (AST *ast,
1130 return g_strdup ("Mv");
1134 variant_get_value (AST *ast,
1135 const GVariantType *type,
1138 Variant *variant = (Variant *) ast;
1141 g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT));
1142 child = ast_resolve (variant->value, error);
1147 return g_variant_new_variant (child);
1151 variant_free (AST *ast)
1153 Variant *variant = (Variant *) ast;
1155 ast_free (variant->value);
1156 g_slice_free (Variant, variant);
1160 variant_parse (TokenStream *stream,
1164 static const ASTClass variant_class = {
1165 variant_get_pattern,
1166 maybe_wrapper, variant_get_value,
1172 token_stream_assert (stream, "<");
1173 value = parse (stream, app, error);
1178 if (!token_stream_require (stream, ">", " to follow variant value", error))
1184 variant = g_slice_new (Variant);
1185 variant->ast.class = &variant_class;
1186 variant->value = value;
1188 return (AST *) variant;
1201 dictionary_get_pattern (AST *ast,
1204 Dictionary *dict = (Dictionary *) ast;
1205 gchar *value_pattern;
1210 if (dict->n_children == 0)
1211 return g_strdup ("Ma{**}");
1213 key_pattern = ast_array_get_pattern (dict->keys,
1214 abs (dict->n_children),
1217 if (key_pattern == NULL)
1220 /* we can not have maybe keys */
1221 if (key_pattern[0] == 'M')
1222 key_char = key_pattern[1];
1224 key_char = key_pattern[0];
1226 g_free (key_pattern);
1229 * plus undetermined number type and undetermined string type.
1231 if (!strchr ("bynqiuxthdsogNS", key_char))
1233 ast_set_error (ast, error, NULL,
1234 G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED,
1235 "dictionary keys must have basic types");
1239 value_pattern = ast_get_pattern (dict->values[0], error);
1241 if (value_pattern == NULL)
1244 result = g_strdup_printf ("M%s{%c%s}",
1245 dict->n_children > 0 ? "a" : "",
1246 key_char, value_pattern);
1247 g_free (value_pattern);
1253 dictionary_get_value (AST *ast,
1254 const GVariantType *type,
1257 Dictionary *dict = (Dictionary *) ast;
1259 if (dict->n_children == -1)
1261 const GVariantType *subtype;
1262 GVariantBuilder builder;
1265 if (!g_variant_type_is_dict_entry (type))
1266 return ast_type_error (ast, type, error);
1268 g_variant_builder_init (&builder, type);
1270 subtype = g_variant_type_key (type);
1271 if (!(subvalue = ast_get_value (dict->keys[0], subtype, error)))
1273 g_variant_builder_clear (&builder);
1276 g_variant_builder_add_value (&builder, subvalue);
1278 subtype = g_variant_type_value (type);
1279 if (!(subvalue = ast_get_value (dict->values[0], subtype, error)))
1281 g_variant_builder_clear (&builder);
1284 g_variant_builder_add_value (&builder, subvalue);
1286 return g_variant_builder_end (&builder);
1290 const GVariantType *entry, *key, *val;
1291 GVariantBuilder builder;
1294 if (!g_variant_type_is_subtype_of (type, G_VARIANT_TYPE_DICTIONARY))
1295 return ast_type_error (ast, type, error);
1297 entry = g_variant_type_element (type);
1298 key = g_variant_type_key (entry);
1299 val = g_variant_type_value (entry);
1301 g_variant_builder_init (&builder, type);
1303 for (i = 0; i < dict->n_children; i++)
1307 g_variant_builder_open (&builder, entry);
1309 if (!(subvalue = ast_get_value (dict->keys[i], key, error)))
1311 g_variant_builder_clear (&builder);
1314 g_variant_builder_add_value (&builder, subvalue);
1316 if (!(subvalue = ast_get_value (dict->values[i], val, error)))
1318 g_variant_builder_clear (&builder);
1321 g_variant_builder_add_value (&builder, subvalue);
1322 g_variant_builder_close (&builder);
1325 return g_variant_builder_end (&builder);
1330 dictionary_free (AST *ast)
1332 Dictionary *dict = (Dictionary *) ast;
1335 if (dict->n_children > -1)
1336 n_children = dict->n_children;
1340 ast_array_free (dict->keys, n_children);
1341 ast_array_free (dict->values, n_children);
1342 g_slice_free (Dictionary, dict);
1346 dictionary_parse (TokenStream *stream,
1350 static const ASTClass dictionary_class = {
1351 dictionary_get_pattern,
1352 maybe_wrapper, dictionary_get_value,
1355 gint n_keys, n_values;
1360 dict = g_slice_new (Dictionary);
1361 dict->ast.class = &dictionary_class;
1363 dict->values = NULL;
1364 n_keys = n_values = 0;
1366 token_stream_assert (stream, "{");
1368 if (token_stream_consume (stream, "}"))
1370 dict->n_children = 0;
1371 return (AST *) dict;
1374 if ((first = parse (stream, app, error)) == NULL)
1377 ast_array_append (&dict->keys, &n_keys, first);
1379 only_one = token_stream_consume (stream, ",");
1381 !token_stream_require (stream, ":",
1382 " or `,' to follow dictionary entry key",
1386 if ((first = parse (stream, app, error)) == NULL)
1389 ast_array_append (&dict->values, &n_values, first);
1393 if (!token_stream_require (stream, "}", " at end of dictionary entry",
1397 g_assert (n_keys == 1 && n_values == 1);
1398 dict->n_children = -1;
1400 return (AST *) dict;
1403 while (!token_stream_consume (stream, "}"))
1407 if (!token_stream_require (stream, ",",
1408 " or `}' to follow dictionary entry", error))
1411 child = parse (stream, app, error);
1416 ast_array_append (&dict->keys, &n_keys, child);
1418 if (!token_stream_require (stream, ":",
1419 " to follow dictionary entry key", error))
1422 child = parse (stream, app, error);
1427 ast_array_append (&dict->values, &n_values, child);
1430 g_assert (n_keys == n_values);
1431 dict->n_children = n_keys;
1433 return (AST *) dict;
1436 ast_array_free (dict->keys, n_keys);
1437 ast_array_free (dict->values, n_values);
1438 g_slice_free (Dictionary, dict);
1450 string_get_pattern (AST *ast,
1453 return g_strdup ("MS");
1457 string_get_value (AST *ast,
1458 const GVariantType *type,
1461 String *string = (String *) ast;
1463 if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
1464 return g_variant_new_string (string->string);
1466 else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
1468 if (!g_variant_is_object_path (string->string))
1470 ast_set_error (ast, error, NULL,
1471 G_VARIANT_PARSE_ERROR_INVALID_OBJECT_PATH,
1472 "not a valid object path");
1476 return g_variant_new_object_path (string->string);
1479 else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
1481 if (!g_variant_is_signature (string->string))
1483 ast_set_error (ast, error, NULL,
1484 G_VARIANT_PARSE_ERROR_INVALID_SIGNATURE,
1485 "not a valid signature");
1489 return g_variant_new_signature (string->string);
1493 return ast_type_error (ast, type, error);
1497 string_free (AST *ast)
1499 String *string = (String *) ast;
1501 g_free (string->string);
1502 g_slice_free (String, string);
1506 unicode_unescape (const gchar *src,
1520 g_assert (length < sizeof (buffer));
1521 strncpy (buffer, src + *src_ofs, length);
1522 buffer[length] = '\0';
1524 value = g_ascii_strtoull (buffer, &end, 0x10);
1526 if (value == 0 || end != buffer + length)
1528 parser_set_error (error, ref, NULL,
1529 G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
1530 "invalid %d-character unicode escape", length);
1534 g_assert (value <= G_MAXUINT32);
1536 *dest_ofs += g_unichar_to_utf8 (value, dest + *dest_ofs);
1543 string_parse (TokenStream *stream,
1547 static const ASTClass string_class = {
1549 maybe_wrapper, string_get_value,
1560 token_stream_start_ref (stream, &ref);
1561 token = token_stream_get (stream);
1562 token_stream_end_ref (stream, &ref);
1563 length = strlen (token);
1566 str = g_malloc (length);
1567 g_assert (quote == '"' || quote == '\'');
1570 while (token[i] != quote)
1574 parser_set_error (error, &ref, NULL,
1575 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1576 "unterminated string constant");
1585 parser_set_error (error, &ref, NULL,
1586 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1587 "unterminated string constant");
1593 if (!unicode_unescape (token, &i, str, &j, 4, &ref, error))
1602 if (!unicode_unescape (token, &i, str, &j, 8, &ref, error))
1610 case 'a': str[j++] = '\a'; i++; continue;
1611 case 'b': str[j++] = '\b'; i++; continue;
1612 case 'f': str[j++] = '\f'; i++; continue;
1613 case 'n': str[j++] = '\n'; i++; continue;
1614 case 'r': str[j++] = '\r'; i++; continue;
1615 case 't': str[j++] = '\t'; i++; continue;
1616 case 'v': str[j++] = '\v'; i++; continue;
1617 case '\n': i++; continue;
1621 str[j++] = token[i++];
1626 string = g_slice_new (String);
1627 string->ast.class = &string_class;
1628 string->string = str;
1630 token_stream_next (stream);
1632 return (AST *) string;
1642 bytestring_get_pattern (AST *ast,
1645 return g_strdup ("May");
1649 bytestring_get_value (AST *ast,
1650 const GVariantType *type,
1653 ByteString *string = (ByteString *) ast;
1655 g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_BYTESTRING));
1657 return g_variant_new_bytestring (string->string);
1661 bytestring_free (AST *ast)
1663 ByteString *string = (ByteString *) ast;
1665 g_free (string->string);
1666 g_slice_free (ByteString, string);
1670 bytestring_parse (TokenStream *stream,
1674 static const ASTClass bytestring_class = {
1675 bytestring_get_pattern,
1676 maybe_wrapper, bytestring_get_value,
1687 token_stream_start_ref (stream, &ref);
1688 token = token_stream_get (stream);
1689 token_stream_end_ref (stream, &ref);
1690 g_assert (token[0] == 'b');
1691 length = strlen (token);
1694 str = g_malloc (length);
1695 g_assert (quote == '"' || quote == '\'');
1698 while (token[i] != quote)
1702 parser_set_error (error, &ref, NULL,
1703 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1704 "unterminated string constant");
1712 parser_set_error (error, &ref, NULL,
1713 G_VARIANT_PARSE_ERROR_UNTERMINATED_STRING_CONSTANT,
1714 "unterminated string constant");
1718 case '0': case '1': case '2': case '3':
1719 case '4': case '5': case '6': case '7':
1721 /* up to 3 characters */
1722 guchar val = token[i++] - '0';
1724 if ('0' <= token[i] && token[i] < '8')
1725 val = (val << 3) | (token[i++] - '0');
1727 if ('0' <= token[i] && token[i] < '8')
1728 val = (val << 3) | (token[i++] - '0');
1734 case 'a': str[j++] = '\a'; i++; continue;
1735 case 'b': str[j++] = '\b'; i++; continue;
1736 case 'f': str[j++] = '\f'; i++; continue;
1737 case 'n': str[j++] = '\n'; i++; continue;
1738 case 'r': str[j++] = '\r'; i++; continue;
1739 case 't': str[j++] = '\t'; i++; continue;
1740 case 'v': str[j++] = '\v'; i++; continue;
1741 case '\n': i++; continue;
1745 str[j++] = token[i++];
1750 string = g_slice_new (ByteString);
1751 string->ast.class = &bytestring_class;
1752 string->string = str;
1754 token_stream_next (stream);
1756 return (AST *) string;
1767 number_get_pattern (AST *ast,
1770 Number *number = (Number *) ast;
1772 if (strchr (number->token, '.') ||
1773 (!g_str_has_prefix (number->token, "0x") && strchr (number->token, 'e')) ||
1774 strstr (number->token, "inf") ||
1775 strstr (number->token, "nan"))
1776 return g_strdup ("Md");
1778 return g_strdup ("MN");
1782 number_overflow (AST *ast,
1783 const GVariantType *type,
1786 ast_set_error (ast, error, NULL,
1787 G_VARIANT_PARSE_ERROR_NUMBER_OUT_OF_RANGE,
1788 "number out of range for type `%c'",
1789 g_variant_type_peek_string (type)[0]);
1794 number_get_value (AST *ast,
1795 const GVariantType *type,
1798 Number *number = (Number *) ast;
1806 token = number->token;
1808 if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
1813 dbl_val = g_ascii_strtod (token, &end);
1814 if (dbl_val != 0.0 && errno == ERANGE)
1816 ast_set_error (ast, error, NULL,
1817 G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
1818 "number too big for any type");
1822 /* silence uninitialised warnings... */
1829 negative = token[0] == '-';
1830 if (token[0] == '-')
1834 abs_val = g_ascii_strtoull (token, &end, 0);
1835 if (abs_val == G_MAXUINT64 && errno == ERANGE)
1837 ast_set_error (ast, error, NULL,
1838 G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
1839 "integer too big for any type");
1846 /* silence uninitialised warning... */
1854 ref = ast->source_ref;
1855 ref.start += end - number->token;
1856 ref.end = ref.start + 1;
1858 parser_set_error (error, &ref, NULL,
1859 G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
1860 "invalid character in number");
1865 return g_variant_new_double (dbl_val);
1867 switch (*g_variant_type_peek_string (type))
1870 if (negative || abs_val > G_MAXUINT8)
1871 return number_overflow (ast, type, error);
1872 return g_variant_new_byte (abs_val);
1875 if (abs_val - negative > G_MAXINT16)
1876 return number_overflow (ast, type, error);
1877 return g_variant_new_int16 (negative ? -abs_val : abs_val);
1880 if (negative || abs_val > G_MAXUINT16)
1881 return number_overflow (ast, type, error);
1882 return g_variant_new_uint16 (abs_val);
1885 if (abs_val - negative > G_MAXINT32)
1886 return number_overflow (ast, type, error);
1887 return g_variant_new_int32 (negative ? -abs_val : abs_val);
1890 if (negative || abs_val > G_MAXUINT32)
1891 return number_overflow (ast, type, error);
1892 return g_variant_new_uint32 (abs_val);
1895 if (abs_val - negative > G_MAXINT64)
1896 return number_overflow (ast, type, error);
1897 return g_variant_new_int64 (negative ? -abs_val : abs_val);
1901 return number_overflow (ast, type, error);
1902 return g_variant_new_uint64 (abs_val);
1905 if (abs_val - negative > G_MAXINT32)
1906 return number_overflow (ast, type, error);
1907 return g_variant_new_handle (negative ? -abs_val : abs_val);
1910 return ast_type_error (ast, type, error);
1915 number_free (AST *ast)
1917 Number *number = (Number *) ast;
1919 g_free (number->token);
1920 g_slice_free (Number, number);
1924 number_parse (TokenStream *stream,
1928 static const ASTClass number_class = {
1930 maybe_wrapper, number_get_value,
1935 number = g_slice_new (Number);
1936 number->ast.class = &number_class;
1937 number->token = token_stream_get (stream);
1938 token_stream_next (stream);
1940 return (AST *) number;
1950 boolean_get_pattern (AST *ast,
1953 return g_strdup ("Mb");
1957 boolean_get_value (AST *ast,
1958 const GVariantType *type,
1961 Boolean *boolean = (Boolean *) ast;
1963 if (!g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
1964 return ast_type_error (ast, type, error);
1966 return g_variant_new_boolean (boolean->value);
1970 boolean_free (AST *ast)
1972 Boolean *boolean = (Boolean *) ast;
1974 g_slice_free (Boolean, boolean);
1978 boolean_new (gboolean value)
1980 static const ASTClass boolean_class = {
1981 boolean_get_pattern,
1982 maybe_wrapper, boolean_get_value,
1987 boolean = g_slice_new (Boolean);
1988 boolean->ast.class = &boolean_class;
1989 boolean->value = value;
1991 return (AST *) boolean;
2002 positional_get_pattern (AST *ast,
2005 Positional *positional = (Positional *) ast;
2007 return g_strdup (g_variant_get_type_string (positional->value));
2011 positional_get_value (AST *ast,
2012 const GVariantType *type,
2015 Positional *positional = (Positional *) ast;
2018 g_assert (positional->value != NULL);
2020 if G_UNLIKELY (!g_variant_is_of_type (positional->value, type))
2021 return ast_type_error (ast, type, error);
2023 /* NOTE: if _get is called more than once then
2024 * things get messed up with respect to floating refs.
2026 * fortunately, this function should only ever get called once.
2028 g_assert (positional->value != NULL);
2029 value = positional->value;
2030 positional->value = NULL;
2036 positional_free (AST *ast)
2038 Positional *positional = (Positional *) ast;
2040 /* if positional->value is set, just leave it.
2041 * memory management doesn't matter in case of programmer error.
2043 g_slice_free (Positional, positional);
2047 positional_parse (TokenStream *stream,
2051 static const ASTClass positional_class = {
2052 positional_get_pattern,
2053 positional_get_value, NULL,
2056 Positional *positional;
2057 const gchar *endptr;
2060 token = token_stream_get (stream);
2061 g_assert (token[0] == '%');
2063 positional = g_slice_new (Positional);
2064 positional->ast.class = &positional_class;
2065 positional->value = g_variant_new_va (token + 1, &endptr, app);
2067 if (*endptr || positional->value == NULL)
2069 token_stream_set_error (stream, error, TRUE,
2070 G_VARIANT_PARSE_ERROR_INVALID_FORMAT_STRING,
2071 "invalid GVariant format string");
2072 /* memory management doesn't matter in case of programmer error. */
2076 token_stream_next (stream);
2079 return (AST *) positional;
2091 typedecl_get_pattern (AST *ast,
2094 TypeDecl *decl = (TypeDecl *) ast;
2096 return g_variant_type_dup_string (decl->type);
2100 typedecl_get_value (AST *ast,
2101 const GVariantType *type,
2104 TypeDecl *decl = (TypeDecl *) ast;
2106 return ast_get_value (decl->child, type, error);
2110 typedecl_free (AST *ast)
2112 TypeDecl *decl = (TypeDecl *) ast;
2114 ast_free (decl->child);
2115 g_variant_type_free (decl->type);
2116 g_slice_free (TypeDecl, decl);
2120 typedecl_parse (TokenStream *stream,
2124 static const ASTClass typedecl_class = {
2125 typedecl_get_pattern,
2126 typedecl_get_value, NULL,
2133 if (token_stream_peek (stream, '@'))
2137 token = token_stream_get (stream);
2139 if (!g_variant_type_string_is_valid (token + 1))
2141 token_stream_set_error (stream, error, TRUE,
2142 G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING,
2143 "invalid type declaration");
2149 type = g_variant_type_new (token + 1);
2151 if (!g_variant_type_is_definite (type))
2153 token_stream_set_error (stream, error, TRUE,
2154 G_VARIANT_PARSE_ERROR_DEFINITE_TYPE_EXPECTED,
2155 "type declarations must be definite");
2156 g_variant_type_free (type);
2162 token_stream_next (stream);
2167 if (token_stream_consume (stream, "boolean"))
2168 type = g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
2170 else if (token_stream_consume (stream, "byte"))
2171 type = g_variant_type_copy (G_VARIANT_TYPE_BYTE);
2173 else if (token_stream_consume (stream, "int16"))
2174 type = g_variant_type_copy (G_VARIANT_TYPE_INT16);
2176 else if (token_stream_consume (stream, "uint16"))
2177 type = g_variant_type_copy (G_VARIANT_TYPE_UINT16);
2179 else if (token_stream_consume (stream, "int32"))
2180 type = g_variant_type_copy (G_VARIANT_TYPE_INT32);
2182 else if (token_stream_consume (stream, "handle"))
2183 type = g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
2185 else if (token_stream_consume (stream, "uint32"))
2186 type = g_variant_type_copy (G_VARIANT_TYPE_UINT32);
2188 else if (token_stream_consume (stream, "int64"))
2189 type = g_variant_type_copy (G_VARIANT_TYPE_INT64);
2191 else if (token_stream_consume (stream, "uint64"))
2192 type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
2194 else if (token_stream_consume (stream, "double"))
2195 type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
2197 else if (token_stream_consume (stream, "string"))
2198 type = g_variant_type_copy (G_VARIANT_TYPE_STRING);
2200 else if (token_stream_consume (stream, "objectpath"))
2201 type = g_variant_type_copy (G_VARIANT_TYPE_OBJECT_PATH);
2203 else if (token_stream_consume (stream, "signature"))
2204 type = g_variant_type_copy (G_VARIANT_TYPE_SIGNATURE);
2208 token_stream_set_error (stream, error, TRUE,
2209 G_VARIANT_PARSE_ERROR_UNKNOWN_KEYWORD,
2215 if ((child = parse (stream, app, error)) == NULL)
2217 g_variant_type_free (type);
2221 decl = g_slice_new (TypeDecl);
2222 decl->ast.class = &typedecl_class;
2224 decl->child = child;
2226 return (AST *) decl;
2230 parse (TokenStream *stream,
2234 SourceRef source_ref;
2237 token_stream_prepare (stream);
2238 token_stream_start_ref (stream, &source_ref);
2240 if (token_stream_peek (stream, '['))
2241 result = array_parse (stream, app, error);
2243 else if (token_stream_peek (stream, '('))
2244 result = tuple_parse (stream, app, error);
2246 else if (token_stream_peek (stream, '<'))
2247 result = variant_parse (stream, app, error);
2249 else if (token_stream_peek (stream, '{'))
2250 result = dictionary_parse (stream, app, error);
2252 else if (app && token_stream_peek (stream, '%'))
2253 result = positional_parse (stream, app, error);
2255 else if (token_stream_consume (stream, "true"))
2256 result = boolean_new (TRUE);
2258 else if (token_stream_consume (stream, "false"))
2259 result = boolean_new (FALSE);
2261 else if (token_stream_is_numeric (stream) ||
2262 token_stream_peek_string (stream, "inf") ||
2263 token_stream_peek_string (stream, "nan"))
2264 result = number_parse (stream, app, error);
2266 else if (token_stream_peek (stream, 'n') ||
2267 token_stream_peek (stream, 'j'))
2268 result = maybe_parse (stream, app, error);
2270 else if (token_stream_peek (stream, '@') ||
2271 token_stream_is_keyword (stream))
2272 result = typedecl_parse (stream, app, error);
2274 else if (token_stream_peek (stream, '\'') ||
2275 token_stream_peek (stream, '"'))
2276 result = string_parse (stream, app, error);
2278 else if (token_stream_peek2 (stream, 'b', '\'') ||
2279 token_stream_peek2 (stream, 'b', '"'))
2280 result = bytestring_parse (stream, app, error);
2284 token_stream_set_error (stream, error, FALSE,
2285 G_VARIANT_PARSE_ERROR_VALUE_EXPECTED,
2292 token_stream_end_ref (stream, &source_ref);
2293 result->source_ref = source_ref;
2301 * @type: (allow-none): a #GVariantType, or %NULL
2302 * @text: a string containing a GVariant in text form
2303 * @limit: (allow-none): a pointer to the end of @text, or %NULL
2304 * @endptr: (allow-none): a location to store the end pointer, or %NULL
2305 * @error: (allow-none): a pointer to a %NULL #GError pointer, or %NULL
2307 * Parses a #GVariant from a text representation.
2309 * A single #GVariant is parsed from the content of @text.
2311 * The format is described <link linkend='gvariant-text'>here</link>.
2313 * The memory at @limit will never be accessed and the parser behaves as
2314 * if the character at @limit is the nul terminator. This has the
2315 * effect of bounding @text.
2317 * If @endptr is non-%NULL then @text is permitted to contain data
2318 * following the value that this function parses and @endptr will be
2319 * updated to point to the first character past the end of the text
2320 * parsed by this function. If @endptr is %NULL and there is extra data
2321 * then an error is returned.
2323 * If @type is non-%NULL then the value will be parsed to have that
2324 * type. This may result in additional parse errors (in the case that
2325 * the parsed value doesn't fit the type) but may also result in fewer
2326 * errors (in the case that the type would have been ambiguous, such as
2327 * with empty arrays).
2329 * In the event that the parsing is successful, the resulting #GVariant
2332 * In case of any error, %NULL will be returned. If @error is non-%NULL
2333 * then it will be set to reflect the error that occurred.
2335 * Officially, the language understood by the parser is "any string
2336 * produced by g_variant_print()".
2338 * Returns: a reference to a #GVariant, or %NULL
2341 g_variant_parse (const GVariantType *type,
2344 const gchar **endptr,
2347 TokenStream stream = { 0, };
2348 GVariant *result = NULL;
2351 g_return_val_if_fail (text != NULL, NULL);
2352 g_return_val_if_fail (text == limit || text != NULL, NULL);
2354 stream.start = text;
2355 stream.stream = text;
2358 if ((ast = parse (&stream, NULL, error)))
2361 result = ast_resolve (ast, error);
2363 result = ast_get_value (ast, type, error);
2367 g_variant_ref_sink (result);
2371 while (stream.stream != limit &&
2372 g_ascii_isspace (*stream.stream))
2375 if (stream.stream != limit && *stream.stream != '\0')
2377 SourceRef ref = { stream.stream - text,
2378 stream.stream - text };
2380 parser_set_error (error, &ref, NULL,
2381 G_VARIANT_PARSE_ERROR_INPUT_NOT_AT_END,
2382 "expected end of input");
2383 g_variant_unref (result);
2389 *endptr = stream.stream;
2399 * g_variant_new_parsed_va:
2400 * @format: a text format #GVariant
2401 * @app: a pointer to a #va_list
2403 * Parses @format and returns the result.
2405 * This is the version of g_variant_new_parsed() intended to be used
2408 * The return value will be floating if it was a newly created GVariant
2409 * instance. In the case that @format simply specified the collection
2410 * of a #GVariant pointer (eg: @format was "%*") then the collected
2411 * #GVariant pointer will be returned unmodified, without adding any
2412 * additional references.
2414 * In order to behave correctly in all cases it is necessary for the
2415 * calling function to g_variant_ref_sink() the return result before
2416 * returning control to the user that originally provided the pointer.
2417 * At this point, the caller will have their own full reference to the
2418 * result. This can also be done by adding the result to a container,
2419 * or by passing it to another g_variant_new() call.
2421 * Returns: a new, usually floating, #GVariant
2424 g_variant_new_parsed_va (const gchar *format,
2427 TokenStream stream = { 0, };
2428 GVariant *result = NULL;
2429 GError *error = NULL;
2432 g_return_val_if_fail (format != NULL, NULL);
2433 g_return_val_if_fail (app != NULL, NULL);
2435 stream.start = format;
2436 stream.stream = format;
2439 if ((ast = parse (&stream, app, &error)))
2441 result = ast_resolve (ast, &error);
2446 g_error ("g_variant_new_parsed: %s", error->message);
2449 g_error ("g_variant_new_parsed: trailing text after value");
2455 * g_variant_new_parsed:
2456 * @format: a text format #GVariant
2457 * @...: arguments as per @format
2459 * Parses @format and returns the result.
2461 * @format must be a text format #GVariant with one extension: at any
2462 * point that a value may appear in the text, a '%' character followed
2463 * by a GVariant format string (as per g_variant_new()) may appear. In
2464 * that case, the same arguments are collected from the argument list as
2465 * g_variant_new() would have collected.
2467 * Consider this simple example:
2469 * <informalexample><programlisting>
2470 * g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
2471 * </programlisting></informalexample>
2473 * In the example, the variable argument parameters are collected and
2474 * filled in as if they were part of the original string to produce the
2475 * result of <code>[('one', 1), ('two', 2), ('three', 3)]</code>.
2477 * This function is intended only to be used with @format as a string
2478 * literal. Any parse error is fatal to the calling process. If you
2479 * want to parse data from untrusted sources, use g_variant_parse().
2481 * You may not use this function to return, unmodified, a single
2482 * #GVariant pointer from the argument list. ie: @format may not solely
2483 * be anything along the lines of "%*", "%?", "\%r", or anything starting
2486 * Returns: a new floating #GVariant instance
2489 g_variant_new_parsed (const gchar *format,
2495 va_start (ap, format);
2496 result = g_variant_new_parsed_va (format, &ap);
2503 * g_variant_builder_add_parsed:
2504 * @builder: a #GVariantBuilder
2505 * @format: a text format #GVariant
2506 * @...: arguments as per @format
2508 * Adds to a #GVariantBuilder.
2510 * This call is a convenience wrapper that is exactly equivalent to
2511 * calling g_variant_new_parsed() followed by
2512 * g_variant_builder_add_value().
2514 * This function might be used as follows:
2518 * make_pointless_dictionary (void)
2520 * GVariantBuilder *builder;
2523 * builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
2524 * g_variant_builder_add_parsed (builder, "{'width', <%i>}", 600);
2525 * g_variant_builder_add_parsed (builder, "{'title', <%s>}", "foo");
2526 * g_variant_builder_add_parsed (builder, "{'transparency', <0.5>}");
2527 * return g_variant_builder_end (builder);
2534 g_variant_builder_add_parsed (GVariantBuilder *builder,
2535 const gchar *format,
2540 va_start (ap, format);
2541 g_variant_builder_add_value (builder, g_variant_new_parsed_va (format, &ap));