From 8a49b1b8400b4f0fbc24611362dc066a2e3d6d5b Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 9 Jun 2026 23:38:00 -0400 Subject: [PATCH 1/2] lib.gis: Quote YAML description in parser --md-description output --- lib/gis/parser_md.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/gis/parser_md.c b/lib/gis/parser_md.c index 2cf51a1b122..f1f15465d7f 100644 --- a/lib/gis/parser_md.c +++ b/lib/gis/parser_md.c @@ -19,6 +19,24 @@ #include "parser_local_proto.h" +/*! + \brief Print a string as the content of a double-quoted YAML scalar. + + Escapes the characters that are special inside a double-quoted YAML + scalar (backslash and double quote). Quoting the value lets it contain + YAML-significant sequences such as ": " without breaking the parser. + + \param str string to print (escaped) to stdout + */ +static void print_yaml_escaped(const char *str) +{ + for (const char *p = str; *p; p++) { + if (*p == '\\' || *p == '"') + fprintf(stdout, "\\"); + fprintf(stdout, "%c", *p); + } +} + /*! \brief Print module usage description in Markdown format. */ @@ -32,14 +50,14 @@ void G__usage_markdown(void) /* print metadata used by man/build*.py */ fprintf(stdout, "---\n"); fprintf(stdout, "name: %s\n", st->pgm_name); - fprintf(stdout, "description: "); + fprintf(stdout, "description: \""); if (st->module_info.label) - fprintf(stdout, "%s", st->module_info.label); + print_yaml_escaped(st->module_info.label); if (st->module_info.label && st->module_info.description) fprintf(stdout, " "); if (st->module_info.description) - fprintf(stdout, "%s", st->module_info.description); - fprintf(stdout, "\n"); + print_yaml_escaped(st->module_info.description); + fprintf(stdout, "\"\n"); fprintf(stdout, "keywords: [ "); G__print_keywords(stdout, NULL, FALSE); fprintf(stdout, " ]"); From 79db4c942478ba774764dcc47552cfac1a0df717 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Wed, 10 Jun 2026 15:51:16 -0400 Subject: [PATCH 2/2] man: Decode quoted YAML description in index and topic pages --- man/build_md.py | 17 ++++++++++++++++- man/build_topics.py | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/man/build_md.py b/man/build_md.py index a252063e8d6..a6ba78cf5d5 100644 --- a/man/build_md.py +++ b/man/build_md.py @@ -1,4 +1,5 @@ import os +import re import string # File template pieces follow @@ -78,6 +79,20 @@ ############################################################################ +def unquote_yaml(text): + """Decode the description value from --md-description frontmatter. + + The value is emitted as a double-quoted YAML scalar so it can contain + YAML-significant sequences such as ": ". This strips the surrounding + quotes and undoes the backslash escaping of `"` and `\\`. A value that + is not quoted is returned unchanged. + """ + text = text.strip() + if len(text) >= 2 and text.startswith('"') and text.endswith('"'): + return re.sub(r"\\(.)", r"\1", text[1:-1]) + return text + + def get_desc(cmd): desc = "" with open(cmd) as f: @@ -86,7 +101,7 @@ def get_desc(cmd): if not line: return desc if "description:" in line: - desc = line.split(":", 1)[1].strip() + desc = unquote_yaml(line.split(":", 1)[1]) break return desc diff --git a/man/build_topics.py b/man/build_topics.py index 3b8e6c9ee09..1b7ccf2eac8 100644 --- a/man/build_topics.py +++ b/man/build_topics.py @@ -32,6 +32,7 @@ def build_topics(ext): headertopics_tmpl, man_dir, moduletopics_tmpl, + unquote_yaml, ) keywords = {} @@ -86,7 +87,7 @@ def build_topics(ext): key = keys[1] # Second keyword is topic. match = re.match(r"description:\s*(.*)\s*", line) if match: - text = match.group(1) + text = unquote_yaml(match.group(1)) if not text: print(f"Warning: Empty tile in {fname}", file=sys.stderr) break