]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
varlink: parse comments too
authorLennart Poettering <lennart@poettering.net>
Mon, 29 Apr 2024 13:33:02 +0000 (15:33 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 17 Jun 2024 07:20:21 +0000 (09:20 +0200)
src/shared/varlink-idl.c
src/test/test-varlink-idl.c

index f249ff5955cb1af4239ba00f5f5a314345e32eb2..e100137a7578e291a510363e701a93cbf8fb5965 100644 (file)
@@ -657,8 +657,10 @@ static int varlink_idl_subparse_token(
 static int varlink_idl_subparse_comment(
                 const char **p,
                 unsigned *line,
-                unsigned *column) {
+                unsigned *column,
+                char **ret) {
 
+        _cleanup_free_ char *comment = NULL;
         size_t l;
 
         assert(p);
@@ -667,9 +669,30 @@ static int varlink_idl_subparse_comment(
         assert(column);
 
         l = strcspn(*p, NEWLINE);
+
+        if (!utf8_is_valid_n(*p, l))
+                return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Comment is not valid UTF-8.", *line, *column);
+
+        if (ret) {
+                /* Remove a single space as prefix of a comment, if one is specified. This is because we
+                 * generally expect comments to be formatted as "# foobar" rather than "#foobar", and will
+                 * ourselves format them that way. We accept the comments without the space too however. We
+                 * will not strip more than one space, to allow indented comment blocks. */
+
+                if (**p == ' ')
+                        comment = strndup(*p + 1, l - 1);
+                else
+                        comment = strndup(*p, l);
+                if (!comment)
+                        return -ENOMEM;
+        }
+
         advance_line_column(*p, l + 1, line, column);
         *p += l;
 
+        if (ret)
+                *ret = TAKE_PTR(comment);
+
         return 1;
 }
 
@@ -856,7 +879,7 @@ static int varlink_idl_subparse_struct_or_enum(
                                 return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
 
                         state = STATE_NAME;
-                        allowed_delimiters = ")";
+                        allowed_delimiters = ")#";
                         allowed_chars = VALID_CHARS_IDENTIFIER;
                         break;
 
@@ -865,7 +888,23 @@ static int varlink_idl_subparse_struct_or_enum(
 
                         if (!token)
                                 return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
-                        if (streq(token, ")"))
+                        else if (streq(token, "#")) {
+                                _cleanup_free_ char *comment = NULL;
+
+                                r = varlink_idl_subparse_comment(p, line, column, &comment);
+                                if (r < 0)
+                                        return r;
+
+                                r = varlink_symbol_realloc(symbol, *n_fields + 1);
+                                if (r < 0)
+                                        return r;
+
+                                VarlinkField *field = (*symbol)->fields + (*n_fields)++;
+                                *field = (VarlinkField) {
+                                        .name = TAKE_PTR(comment),
+                                        .field_type = _VARLINK_FIELD_COMMENT,
+                                };
+                        } else if (streq(token, ")"))
                                 state = STATE_DONE;
                         else {
                                 field_name = TAKE_PTR(token);
@@ -929,7 +968,7 @@ static int varlink_idl_subparse_struct_or_enum(
 
                                 if (streq(token, ",")) {
                                         state = STATE_NAME;
-                                        allowed_delimiters = NULL;
+                                        allowed_delimiters = "#";
                                         allowed_chars = VALID_CHARS_IDENTIFIER;
                                 } else {
                                         assert(streq(token, ")"));
@@ -947,7 +986,7 @@ static int varlink_idl_subparse_struct_or_enum(
                                 return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
                         if (streq(token, ",")) {
                                 state = STATE_NAME;
-                                allowed_delimiters = NULL;
+                                allowed_delimiters = "#";
                                 allowed_chars = VALID_CHARS_IDENTIFIER;
                         } else if (streq(token, ")"))
                                 state = STATE_DONE;
@@ -1062,9 +1101,24 @@ int varlink_idl_parse(
                         if (!token)
                                 return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
                         if (streq(token, "#")) {
-                                r = varlink_idl_subparse_comment(&text, line, column);
+                                _cleanup_free_ char *comment = NULL;
+
+                                r = varlink_idl_subparse_comment(&text, line, column, &comment);
                                 if (r < 0)
                                         return r;
+
+                                r = varlink_interface_realloc(&interface, n_symbols + 1);
+                                if (r < 0)
+                                        return r;
+
+                                r = varlink_symbol_realloc(&symbol, 0);
+                                if (r < 0)
+                                        return r;
+
+                                symbol->symbol_type = _VARLINK_INTERFACE_COMMENT;
+                                symbol->name = TAKE_PTR(comment);
+
+                                interface->symbols[n_symbols++] = TAKE_PTR(symbol);
                         } else if (streq(token, "interface")) {
                                 state = STATE_INTERFACE;
                                 allowed_delimiters = NULL;
@@ -1074,9 +1128,6 @@ int varlink_idl_parse(
                         break;
 
                 case STATE_INTERFACE:
-                        assert(!interface);
-                        assert(n_symbols == 0);
-
                         if (!token)
                                 return varlink_idl_log(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Premature EOF.", *line, *column);
 
@@ -1084,7 +1135,9 @@ int varlink_idl_parse(
                         if (r < 0)
                                 return r;
 
+                        assert(!interface->name);
                         interface->name = TAKE_PTR(token);
+
                         state = STATE_PRE_SYMBOL;
                         allowed_delimiters = "#";
                         allowed_chars = VALID_CHARS_RESERVED;
@@ -1097,9 +1150,25 @@ int varlink_idl_parse(
                         }
 
                         if (streq(token, "#")) {
-                                r = varlink_idl_subparse_comment(&text, line, column);
+                                _cleanup_free_ char *comment = NULL;
+
+                                r = varlink_idl_subparse_comment(&text, line, column, &comment);
                                 if (r < 0)
                                         return r;
+
+                                r = varlink_interface_realloc(&interface, n_symbols + 1);
+                                if (r < 0)
+                                        return r;
+
+                                assert(!symbol);
+                                r = varlink_symbol_realloc(&symbol, 0);
+                                if (r < 0)
+                                        return r;
+
+                                symbol->symbol_type = _VARLINK_SYMBOL_COMMENT;
+                                symbol->name = TAKE_PTR(comment);
+
+                                interface->symbols[n_symbols++] = TAKE_PTR(symbol);
                         } else if (streq(token, "method")) {
                                 state = STATE_METHOD;
                                 allowed_chars = VALID_CHARS_IDENTIFIER;
index 6a28edf4671e5d6513c18770d6bbe220006031a5..f8c8080ee55ef5f61e834e8da23e5506fcef9dee 100644 (file)
@@ -123,6 +123,21 @@ static void test_parse_format_one(const VarlinkInterface *iface) {
         assert_se(varlink_idl_parse(text, NULL, NULL, &parsed) >= 0);
         assert_se(varlink_idl_consistent(parsed, LOG_ERR) >= 0);
         assert_se(varlink_idl_format(parsed, &text2) >= 0);
+
+        ASSERT_STREQ(text, text2);
+
+        text = mfree(text);
+        text2 = mfree(text2);
+        parsed = varlink_interface_free(parsed);
+
+        /* Do the same thing, but aggressively line break, and make sure this is roundtrippable as well */
+        assert_se(varlink_idl_dump(stdout, /* use_colors=*/ true, 23, iface) >= 0);
+        assert_se(varlink_idl_consistent(iface, LOG_ERR) >= 0);
+        assert_se(varlink_idl_format_full(iface, 23, &text) >= 0);
+        assert_se(varlink_idl_parse(text, NULL, NULL, &parsed) >= 0);
+        assert_se(varlink_idl_consistent(parsed, LOG_ERR) >= 0);
+        assert_se(varlink_idl_format_full(parsed, 23, &text2) >= 0);
+
         ASSERT_STREQ(text, text2);
 }