get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2229942/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2229942,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2229942/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260428232709.1953743-1-dmalcolm@redhat.com/",
    "project": {
        "id": 17,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/17/?format=api",
        "name": "GNU Compiler Collection",
        "link_name": "gcc",
        "list_id": "gcc-patches.gcc.gnu.org",
        "list_email": "gcc-patches@gcc.gnu.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null
    },
    "msgid": "<20260428232709.1953743-1-dmalcolm@redhat.com>",
    "date": "2026-04-28T23:27:09",
    "name": "[pushed:,r17-169] Introduce pretty-print-token-buffer.{cc,h}",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "9b9a41b10d49702d352ca514ad60abfcffd95bde",
    "submitter": {
        "id": 24465,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/24465/?format=api",
        "name": "David Malcolm",
        "email": "dmalcolm@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260428232709.1953743-1-dmalcolm@redhat.com/mbox/",
    "series": [
        {
            "id": 501951,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/501951/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=501951",
            "date": "2026-04-28T23:27:09",
            "name": "[pushed:,r17-169] Introduce pretty-print-token-buffer.{cc,h}",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/501951/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2229942/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2229942/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=WdXX6MvK;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)",
            "sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=WdXX6MvK",
            "sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com",
            "sourceware.org; spf=pass smtp.mailfrom=redhat.com",
            "server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124"
        ],
        "Received": [
            "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g4xSN2wrmz1yHv\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 29 Apr 2026 09:28:04 +1000 (AEST)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 4F7504BBC0D1\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 28 Apr 2026 23:28:02 +0000 (GMT)",
            "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id 687934BBC0D6\n for <gcc-patches@gcc.gnu.org>; Tue, 28 Apr 2026 23:27:20 +0000 (GMT)",
            "from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-43-mruw_Jw9NmqAgLxi7f3ekQ-1; Tue,\n 28 Apr 2026 19:27:18 -0400",
            "from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 6547E1956095\n for <gcc-patches@gcc.gnu.org>; Tue, 28 Apr 2026 23:27:17 +0000 (UTC)",
            "from t14s.localdomain.com (unknown [10.22.65.168])\n by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id BAF1D195608E; Tue, 28 Apr 2026 23:27:16 +0000 (UTC)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org 4F7504BBC0D1",
            "OpenDKIM Filter v2.11.0 sourceware.org 687934BBC0D6"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 687934BBC0D6",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 687934BBC0D6",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777418840; cv=none;\n b=R94a/HsGAF11JiavAwLfHugseYV7s/Gz0hmm1nB0GQtu6qv+fZAs6GKSmwaYlHSL8/uiiu+eiWV5f06iJUqxhdu+AiR03rMtmUHQMGaeXGO9DUpTySyNpz5C6CZKYmp4Gq03byqHfqzNcXxSksZb6qod5p99bOeQ2emmDwNDsO8=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777418840; c=relaxed/simple;\n bh=OZ6fmSh1HiU4wgGskN7G1RKxfjkOJTU9uJsPFOmdSmA=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=uR5EoryzU5XCeFUI8nIztPUpRVdxStN3p8GMwNv1nbCVFCmI3oE2Zd6om3tbPQJJBy866XbBL3enNIqnPZe/ZvsuMWa2IxNXy+Z8xrnPGLkl67DRGufNTdpR7IHVNfPkYSmKOIEv43XprgYl4digmyam9Vpdu0KoJlppZaz2t78=",
        "ARC-Authentication-Results": "i=1; server2.sourceware.org",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1777418840;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding;\n bh=QVbkrhJGE/kmFaUcCa3LJjdgGXsbTLyWnVSPjq3szEo=;\n b=WdXX6MvKiqYOVTiXlz8fz6+W1NEfYMDzUWpfnrpdWnvLdljwpjTLQED5Jg4gkVAr4Y0Fmw\n 6qq7XXuPiQzZ5HCry/l3V8dIAVQ+Kf7FoyZG70ERSrcdbw7DunRxWhY9JNRGO5FG2Fr37D\n vY1SoTaRv6tfLOsDCpuHZA9QdwHplyo=",
        "X-MC-Unique": "mruw_Jw9NmqAgLxi7f3ekQ-1",
        "X-Mimecast-MFC-AGG-ID": "mruw_Jw9NmqAgLxi7f3ekQ_1777418837",
        "From": "David Malcolm <dmalcolm@redhat.com>",
        "To": "gcc-patches@gcc.gnu.org",
        "Cc": "David Malcolm <dmalcolm@redhat.com>",
        "Subject": "[pushed: r17-169] Introduce pretty-print-token-buffer.{cc,h}",
        "Date": "Tue, 28 Apr 2026 19:27:09 -0400",
        "Message-ID": "<20260428232709.1953743-1-dmalcolm@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.0 on 10.30.177.17",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-MFC-PROC-ID": "sCxxELGCNz5FKXXxCWSEQp3C_14bplyQDFWhK-LdTfg_1777418837",
        "X-Mimecast-Originator": "redhat.com",
        "Content-Transfer-Encoding": "8bit",
        "content-type": "text/plain; charset=\"US-ASCII\"; x-default=true",
        "X-BeenThere": "gcc-patches@gcc.gnu.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>",
        "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>",
        "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>",
        "List-Post": "<mailto:gcc-patches@gcc.gnu.org>",
        "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>",
        "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>",
        "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"
    },
    "content": "Move the implementation of diagnostic_message_buffer from libdiagnostics\nto a new pretty-print-token-buffer.{cc,h}, for capturing the tokens from\na pretty-print.\n\nImplement a new class pp_token_buffer_element for replaying the tokens\nin a pretty_print_token_buffer into another pretty-print, using \"%e\".\n\nAdd selftests.\n\nSuccessfully bootstrapped & regrtested on powerpc64le-unknown-linux-gnu\n(albeit without ada,cobol,d, and possibly missing some optional test dependencies)\nPushed to trunk as r17-169-g9c529989e0fd9c.\n\ngcc/ChangeLog:\n\t* Makefile.in (OBJS-libcommon): Add pretty-print-token-buffer.o.\n\t* libgdiagnostics.cc: Drop include of \"auto-obstack.h\".\n\tInclude \"pretty-print-token-buffer.h\".\n\t(class copying_token_printer): Move to\n\tpretty-print-token-buffer.cc.\n\t(struct diagnostic_message_buffer): Reimplement as a subclass of\n\tpretty_print_token_buffer.\n\t(diagnostic_message_buffer::to_string): Rename to\n\tpretty_print_token_buffer::to_string and move to\n\tpretty-print-token-buffer.cc.\n\t* pretty-print-token-buffer.cc: New file, based on material from\n\tlibgdiagnostics.cc.\n\t* pretty-print-token-buffer.h: New file, based on material from\n\tlibgdiagnostics.h.\n\t* selftest-run-tests.cc (selftest::run_tests): Call\n\tselftest::pretty_print_token_buffer_cc_tests.\n\t* selftest.h (selftest::pretty_print_token_buffer_cc_tests): New\n\tdecl.\n\nSigned-off-by: David Malcolm <dmalcolm@redhat.com>\n---\n gcc/Makefile.in                  |   2 +-\n gcc/libgdiagnostics.cc           | 177 +--------------\n gcc/pretty-print-token-buffer.cc | 362 +++++++++++++++++++++++++++++++\n gcc/pretty-print-token-buffer.h  |  70 ++++++\n gcc/selftest-run-tests.cc        |   1 +\n gcc/selftest.h                   |   1 +\n 6 files changed, 440 insertions(+), 173 deletions(-)\n create mode 100644 gcc/pretty-print-token-buffer.cc\n create mode 100644 gcc/pretty-print-token-buffer.h",
    "diff": "diff --git a/gcc/Makefile.in b/gcc/Makefile.in\nindex 8ecef4ccdc7..bcbcffa4118 100644\n--- a/gcc/Makefile.in\n+++ b/gcc/Makefile.in\n@@ -1904,7 +1904,7 @@ OBJS-libcommon = \\\n \tdiagnostics/diagnostics-selftests.o \\\n \tgcc-diagnostic-spec.o \\\n \tgraphviz.o pex.o \\\n-\tpretty-print.o intl.o \\\n+\tpretty-print.o pretty-print-token-buffer.o intl.o \\\n \tjson.o json-parsing.o json-diagnostic.o \\\n \tpub-sub.o \\\n \txml.o \\\ndiff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc\nindex dd5109229b3..1cf451273d0 100644\n--- a/gcc/libgdiagnostics.cc\n+++ b/gcc/libgdiagnostics.cc\n@@ -44,7 +44,7 @@ along with GCC; see the file COPYING3.  If not see\n #include \"libgdiagnostics-private.h\"\n #include \"pretty-print-format-impl.h\"\n #include \"pretty-print-markup.h\"\n-#include \"auto-obstack.h\"\n+#include \"pretty-print-token-buffer.h\"\n \n class owned_nullable_string\n {\n@@ -252,97 +252,6 @@ private:\n   diagnostics::source_printing_options m_source_printing;\n };\n \n-/* A token_printer that makes a deep copy of the pp_token_list\n-   into another obstack.  */\n-\n-class copying_token_printer : public token_printer\n-{\n-public:\n-  copying_token_printer (obstack &dst_obstack,\n-\t\t\t pp_token_list &dst_token_list)\n-  : m_dst_obstack (dst_obstack),\n-    m_dst_token_list (dst_token_list)\n-  {\n-  }\n-\n-  void\n-  print_tokens (pretty_printer *,\n-\t\tconst pp_token_list &tokens) final override\n-  {\n-    for (auto iter = tokens.m_first; iter; iter = iter->m_next)\n-      switch (iter->m_kind)\n-\t{\n-\tdefault:\n-\t  gcc_unreachable ();\n-\n-\tcase pp_token::kind::text:\n-\t  {\n-\t    const pp_token_text *sub = as_a <const pp_token_text *> (iter);\n-\t    /* Copy the text, with null terminator.  */\n-\t    obstack_grow (&m_dst_obstack, sub->m_value.get (),\n-\t\t\t  strlen (sub->m_value.get ()) + 1);\n-\t    m_dst_token_list.push_back_text\n-\t      (label_text::borrow (XOBFINISH (&m_dst_obstack,\n-\t\t\t\t\t      const char *)));\n-\t  }\n-\t  break;\n-\n-\tcase pp_token::kind::begin_color:\n-\t  {\n-\t    pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);\n-\t    /* Copy the color, with null terminator.  */\n-\t    obstack_grow (&m_dst_obstack, sub->m_value.get (),\n-\t\t\t  strlen (sub->m_value.get ()) + 1);\n-\t    m_dst_token_list.push_back<pp_token_begin_color>\n-\t      (label_text::borrow (XOBFINISH (&m_dst_obstack,\n-\t\t\t\t\t      const char *)));\n-\t  }\n-\t  break;\n-\tcase pp_token::kind::end_color:\n-\t  m_dst_token_list.push_back<pp_token_end_color> ();\n-\t  break;\n-\n-\tcase pp_token::kind::begin_quote:\n-\t  m_dst_token_list.push_back<pp_token_begin_quote> ();\n-\t  break;\n-\tcase pp_token::kind::end_quote:\n-\t  m_dst_token_list.push_back<pp_token_end_quote> ();\n-\t  break;\n-\n-\tcase pp_token::kind::begin_url:\n-\t  {\n-\t    pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);\n-\t    /* Copy the URL, with null terminator.  */\n-\t    obstack_grow (&m_dst_obstack, sub->m_value.get (),\n-\t\t\t  strlen (sub->m_value.get ()) + 1);\n-\t    m_dst_token_list.push_back<pp_token_begin_url>\n-\t      (label_text::borrow (XOBFINISH (&m_dst_obstack,\n-\t\t\t\t\t      const char *)));\n-\t  }\n-\t  break;\n-\tcase pp_token::kind::end_url:\n-\t  m_dst_token_list.push_back<pp_token_end_url> ();\n-\t  break;\n-\n-\tcase pp_token::kind::event_id:\n-\t  {\n-\t    pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);\n-\t    m_dst_token_list.push_back<pp_token_event_id> (sub->m_event_id);\n-\t  }\n-\t  break;\n-\n-\tcase pp_token::kind::custom_data:\n-\t  /* These should have been eliminated by replace_custom_tokens.  */\n-\t  gcc_unreachable ();\n-\t  break;\n-\t}\n-  }\n-\n-private:\n-  obstack &m_dst_obstack;\n-  pp_token_list &m_dst_token_list;\n-};\n-\n class sarif_sink : public sink\n {\n public:\n@@ -352,31 +261,13 @@ public:\n \t      const diagnostics::sarif_generation_options &sarif_gen_opts);\n };\n \n-struct diagnostic_message_buffer\n+struct diagnostic_message_buffer : public pretty_print_token_buffer\n {\n-  diagnostic_message_buffer ()\n-  : m_tokens (m_obstack)\n-  {\n-  }\n-\n-  diagnostic_message_buffer (const char *gmsgid,\n-\t\t\t     va_list *args)\n-  : m_tokens (m_obstack)\n+  diagnostic_message_buffer () {}\n+  diagnostic_message_buffer (const char *gmsgid, va_list *args)\n+  : pretty_print_token_buffer (gmsgid, args)\n   {\n-    text_info text (gmsgid, args, errno);\n-    pretty_printer pp;\n-    pp.set_output_stream (nullptr);\n-    copying_token_printer tok_printer (m_obstack, m_tokens);\n-    pp.set_token_printer (&tok_printer);\n-    pp_format (&pp, &text);\n-    pp_output_formatted_text (&pp, nullptr);\n   }\n-\n-\n-  std::string to_string () const;\n-\n-  auto_obstack m_obstack;\n-  pp_token_list m_tokens;\n };\n \n /* A pp_element subclass that replays the saved tokens in a\n@@ -1566,64 +1457,6 @@ sarif_sink (diagnostic_manager &mgr,\n   mgr.get_dc ().add_sink (std::move (inner_sink));\n }\n \n-// struct diagnostic_message_buffer\n-\n-std::string\n-diagnostic_message_buffer::to_string () const\n-{\n-  std::string result;\n-\n-  /* Convert to text, dropping colorization, URLs, etc.  */\n-  for (auto iter = m_tokens.m_first; iter; iter = iter->m_next)\n-    switch (iter->m_kind)\n-      {\n-      default:\n-\tgcc_unreachable ();\n-\n-      case pp_token::kind::text:\n-\t{\n-\t  pp_token_text *sub = as_a <pp_token_text *> (iter);\n-\t  result += sub->m_value.get ();\n-\t}\n-\tbreak;\n-\n-      case pp_token::kind::begin_color:\n-      case pp_token::kind::end_color:\n-\t// Skip\n-\tbreak;\n-\n-      case pp_token::kind::begin_quote:\n-\tresult += open_quote;\n-\tbreak;\n-\n-      case pp_token::kind::end_quote:\n-\tresult += close_quote;\n-\tbreak;\n-\n-      case pp_token::kind::begin_url:\n-      case pp_token::kind::end_url:\n-\t// Skip\n-\tbreak;\n-\n-      case pp_token::kind::event_id:\n-\t{\n-\t  pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);\n-\t  gcc_assert (sub->m_event_id.known_p ());\n-\t  result += '(';\n-\t  result += std::to_string (sub->m_event_id.one_based ());\n-\t  result += ')';\n-\t}\n-\tbreak;\n-\n-      case pp_token::kind::custom_data:\n-\t/* We don't have a way of handling custom_data tokens here.  */\n-\tgcc_unreachable ();\n-\tbreak;\n-      }\n-\n-  return result;\n-}\n-\n /* struct diagnostic_manager.  */\n \n void\ndiff --git a/gcc/pretty-print-token-buffer.cc b/gcc/pretty-print-token-buffer.cc\nnew file mode 100644\nindex 00000000000..61c4a7608b3\n--- /dev/null\n+++ b/gcc/pretty-print-token-buffer.cc\n@@ -0,0 +1,362 @@\n+/* Capturing the results of pretty_print for later playback.\n+   Copyright (C) 2023-2026 Free Software Foundation, Inc.\n+   Contributed by David Malcolm <dmalcolm@redhat.com>.\n+\n+This file is part of GCC.\n+\n+GCC is free software; you can redistribute it and/or modify it under\n+the terms of the GNU General Public License as published by the Free\n+Software Foundation; either version 3, or (at your option) any later\n+version.\n+\n+GCC is distributed in the hope that it will be useful, but WITHOUT ANY\n+WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n+for more details.\n+\n+You should have received a copy of the GNU General Public License\n+along with GCC; see the file COPYING3.  If not see\n+<http://www.gnu.org/licenses/>.  */\n+\n+#include \"config.h\"\n+#define INCLUDE_STRING\n+#include \"system.h\"\n+#include \"coretypes.h\"\n+#include \"intl.h\"\n+#include \"pretty-print.h\"\n+#include \"pretty-print-token-buffer.h\"\n+#include \"selftest.h\"\n+\n+/* A token_printer that makes a deep copy of the pp_token_list\n+   into another obstack.  */\n+\n+class copying_token_printer : public token_printer\n+{\n+public:\n+  copying_token_printer (obstack &dst_obstack,\n+\t\t\t pp_token_list &dst_token_list)\n+  : m_dst_obstack (dst_obstack),\n+    m_dst_token_list (dst_token_list)\n+  {\n+  }\n+\n+  void\n+  print_tokens (pretty_printer *,\n+\t\tconst pp_token_list &tokens) final override\n+  {\n+    for (auto iter = tokens.m_first; iter; iter = iter->m_next)\n+      switch (iter->m_kind)\n+\t{\n+\tdefault:\n+\t  gcc_unreachable ();\n+\n+\tcase pp_token::kind::text:\n+\t  {\n+\t    const pp_token_text *sub = as_a <const pp_token_text *> (iter);\n+\t    /* Copy the text, with null terminator.  */\n+\t    obstack_grow (&m_dst_obstack, sub->m_value.get (),\n+\t\t\t  strlen (sub->m_value.get ()) + 1);\n+\t    m_dst_token_list.push_back_text\n+\t      (label_text::borrow (XOBFINISH (&m_dst_obstack,\n+\t\t\t\t\t      const char *)));\n+\t  }\n+\t  break;\n+\n+\tcase pp_token::kind::begin_color:\n+\t  {\n+\t    pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);\n+\t    /* Copy the color, with null terminator.  */\n+\t    obstack_grow (&m_dst_obstack, sub->m_value.get (),\n+\t\t\t  strlen (sub->m_value.get ()) + 1);\n+\t    m_dst_token_list.push_back<pp_token_begin_color>\n+\t      (label_text::borrow (XOBFINISH (&m_dst_obstack,\n+\t\t\t\t\t      const char *)));\n+\t  }\n+\t  break;\n+\tcase pp_token::kind::end_color:\n+\t  m_dst_token_list.push_back<pp_token_end_color> ();\n+\t  break;\n+\n+\tcase pp_token::kind::begin_quote:\n+\t  m_dst_token_list.push_back<pp_token_begin_quote> ();\n+\t  break;\n+\tcase pp_token::kind::end_quote:\n+\t  m_dst_token_list.push_back<pp_token_end_quote> ();\n+\t  break;\n+\n+\tcase pp_token::kind::begin_url:\n+\t  {\n+\t    pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);\n+\t    /* Copy the URL, with null terminator.  */\n+\t    obstack_grow (&m_dst_obstack, sub->m_value.get (),\n+\t\t\t  strlen (sub->m_value.get ()) + 1);\n+\t    m_dst_token_list.push_back<pp_token_begin_url>\n+\t      (label_text::borrow (XOBFINISH (&m_dst_obstack,\n+\t\t\t\t\t      const char *)));\n+\t  }\n+\t  break;\n+\tcase pp_token::kind::end_url:\n+\t  m_dst_token_list.push_back<pp_token_end_url> ();\n+\t  break;\n+\n+\tcase pp_token::kind::event_id:\n+\t  {\n+\t    pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);\n+\t    m_dst_token_list.push_back<pp_token_event_id> (sub->m_event_id);\n+\t  }\n+\t  break;\n+\n+\tcase pp_token::kind::custom_data:\n+\t  /* These should have been eliminated by replace_custom_tokens.  */\n+\t  gcc_unreachable ();\n+\t  break;\n+\t}\n+  }\n+\n+private:\n+  obstack &m_dst_obstack;\n+  pp_token_list &m_dst_token_list;\n+};\n+\n+pretty_print_token_buffer::pretty_print_token_buffer ()\n+: m_obstack (std::make_unique<auto_obstack> ()),\n+  m_tokens (*m_obstack.get ())\n+{\n+}\n+\n+/* Capture GMSGID and ARGS as a sequence of pretty_print tokens.  */\n+\n+pretty_print_token_buffer::pretty_print_token_buffer (const char *gmsgid,\n+\t\t\t\t\t\t      va_list *args)\n+: m_obstack (std::make_unique<auto_obstack> ()),\n+  m_tokens (*m_obstack.get ())\n+{\n+  text_info text (gmsgid, args, errno);\n+  pretty_printer pp;\n+  pp.set_output_stream (nullptr);\n+  copying_token_printer tok_printer (*m_obstack.get (), m_tokens);\n+  pp.set_token_printer (&tok_printer);\n+  pp_format (&pp, &text);\n+  pp_output_formatted_text (&pp, nullptr);\n+}\n+\n+pretty_print_token_buffer::\n+pretty_print_token_buffer (pretty_print_token_buffer &&other)\n+: m_obstack (std::move (other.m_obstack)),\n+  m_tokens (std::move (other.m_tokens))\n+{\n+}\n+\n+/* Convert to text, dropping colorization, URLs, etc.  */\n+\n+std::string\n+pretty_print_token_buffer::to_string () const\n+{\n+  std::string result;\n+\n+  for (auto iter = m_tokens.m_first; iter; iter = iter->m_next)\n+    switch (iter->m_kind)\n+      {\n+      default:\n+\tgcc_unreachable ();\n+\n+      case pp_token::kind::text:\n+\t{\n+\t  pp_token_text *sub = as_a <pp_token_text *> (iter);\n+\t  result += sub->m_value.get ();\n+\t}\n+\tbreak;\n+\n+      case pp_token::kind::begin_color:\n+      case pp_token::kind::end_color:\n+\t// Skip\n+\tbreak;\n+\n+      case pp_token::kind::begin_quote:\n+\tresult += open_quote;\n+\tbreak;\n+\n+      case pp_token::kind::end_quote:\n+\tresult += close_quote;\n+\tbreak;\n+\n+      case pp_token::kind::begin_url:\n+      case pp_token::kind::end_url:\n+\t// Skip\n+\tbreak;\n+\n+      case pp_token::kind::event_id:\n+\t{\n+\t  pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);\n+\t  gcc_assert (sub->m_event_id.known_p ());\n+\t  result += '(';\n+\t  result += std::to_string (sub->m_event_id.one_based ());\n+\t  result += ')';\n+\t}\n+\tbreak;\n+\n+      case pp_token::kind::custom_data:\n+\t/* We don't have a way of handling custom_data tokens here.  */\n+\tgcc_unreachable ();\n+\tbreak;\n+      }\n+\n+  return result;\n+}\n+\n+// class pp_token_buffer_element : public pp_element\n+\n+void\n+pp_token_buffer_element::add_to_phase_2 (pp_markup::context &ctxt)\n+{\n+  for (auto iter = m_token_buf.m_tokens.m_first; iter; iter = iter->m_next)\n+    switch (iter->m_kind)\n+      {\n+      default:\n+\tgcc_unreachable ();\n+\n+      case pp_token::kind::text:\n+\t{\n+\t  const pp_token_text *sub = as_a <const pp_token_text *> (iter);\n+\t  pp_string (&ctxt.m_pp, sub->m_value.get ());\n+\t  ctxt.push_back_any_text ();\n+\t}\n+\tbreak;\n+\n+      case pp_token::kind::begin_color:\n+\t{\n+\t  const pp_token_begin_color *sub\n+\t    = as_a <const pp_token_begin_color *> (iter);\n+\t  gcc_assert (sub->m_value.get ());\n+\t  ctxt.begin_highlight_color (sub->m_value.get ());\n+\t}\n+\tbreak;\n+\n+      case pp_token::kind::end_color:\n+\tctxt.end_highlight_color ();\n+\tbreak;\n+\n+      case pp_token::kind::begin_quote:\n+\tctxt.begin_quote ();\n+\tbreak;\n+\n+      case pp_token::kind::end_quote:\n+\tctxt.end_quote ();\n+\tbreak;\n+\n+      case pp_token::kind::begin_url:\n+\t{\n+\t  const pp_token_begin_url *sub\n+\t    = as_a <const pp_token_begin_url *> (iter);\n+\t  gcc_assert (sub->m_value.get ());\n+\t  ctxt.begin_url (sub->m_value.get ());\n+\t}\n+\tbreak;\n+\n+      case pp_token::kind::end_url:\n+\tctxt.end_url ();\n+\tbreak;\n+\n+      case pp_token::kind::event_id:\n+\t{\n+\t  const pp_token_event_id *sub\n+\t    = as_a <const pp_token_event_id *> (iter);\n+\t  gcc_assert (sub->m_event_id.known_p ());\n+\t  ctxt.add_event_id (sub->m_event_id);\n+\t}\n+\tbreak;\n+\n+      case pp_token::kind::custom_data:\n+\t/* We don't have a way of handling custom_data tokens here.  */\n+\tgcc_unreachable ();\n+\tbreak;\n+      }\n+}\n+\n+#if CHECKING_P\n+\n+namespace selftest {\n+\n+static pretty_print_token_buffer\n+pp_printf_to_buf (const char *fmt, ...)\n+{\n+  va_list args;\n+  va_start (args, fmt);\n+\n+  pretty_print_token_buffer buf (fmt, &args);\n+\n+  va_end (args);\n+\n+  return buf;\n+}\n+\n+static void\n+test_empty ()\n+{\n+  pretty_print_token_buffer buf;\n+  pp_token_buffer_element e (buf);\n+  pretty_printer pp;\n+  pp_printf (&pp, \"before %e after\", &e);\n+  ASSERT_STREQ (pp_formatted_text (&pp), \"before  after\");\n+}\n+\n+static void\n+test_print ()\n+{\n+  pretty_print_token_buffer buf\n+    = pp_printf_to_buf (\"x: %qs y: %qs\", \"foo\", \"bar\");\n+\n+  // Check that the individual tokens are captured in \"buf\".\n+  pp_token *tok0 = buf.m_tokens.m_first;\n+  ASSERT_EQ (tok0->m_kind, pp_token::kind::text);\n+  ASSERT_STREQ (((pp_token_text *)tok0)->m_value.get (),\n+\t\t\"x: \");\n+\n+  pp_token *tok1 = tok0->m_next;\n+  ASSERT_EQ (tok1->m_kind, pp_token::kind::begin_quote);\n+\n+  pp_token *tok2 = tok1->m_next;\n+  ASSERT_EQ (tok2->m_kind, pp_token::kind::text);\n+  ASSERT_STREQ (((pp_token_text *)tok2)->m_value.get (),\n+\t\t\"foo\");\n+\n+  pp_token *tok3 = tok2->m_next;\n+  ASSERT_EQ (tok3->m_kind, pp_token::kind::end_quote);\n+\n+  pp_token *tok4 = tok3->m_next;\n+  ASSERT_EQ (tok4->m_kind, pp_token::kind::text);\n+  ASSERT_STREQ (((pp_token_text *)tok4)->m_value.get (),\n+\t\t\" y: \");\n+\n+  pp_token *tok5 = tok4->m_next;\n+  ASSERT_EQ (tok5->m_kind, pp_token::kind::begin_quote);\n+\n+  pp_token *tok6 = tok5->m_next;\n+  ASSERT_EQ (tok6->m_kind, pp_token::kind::text);\n+  ASSERT_STREQ (((pp_token_text *)tok6)->m_value.get (),\n+\t\t\"bar\");\n+\n+  pp_token *tok7 = tok6->m_next;\n+  ASSERT_EQ (tok7->m_kind, pp_token::kind::end_quote);\n+  ASSERT_EQ (tok7->m_next, nullptr);\n+\n+\n+  // Check that we can replay buf via pp_token_buffer_element\n+  pp_token_buffer_element e (buf);\n+  pretty_printer pp;\n+  pp_printf (&pp, \"before %e after\", &e);\n+  ASSERT_STREQ (pp_formatted_text (&pp), \"before x: 'foo' y: 'bar' after\");\n+}\n+\n+/* Run all of the selftests within this file.  */\n+\n+void\n+pretty_print_token_buffer_cc_tests ()\n+{\n+  test_empty ();\n+  test_print ();\n+}\n+\n+} // namespace selftest\n+\n+#endif /* CHECKING_P */\ndiff --git a/gcc/pretty-print-token-buffer.h b/gcc/pretty-print-token-buffer.h\nnew file mode 100644\nindex 00000000000..cab691d2c3f\n--- /dev/null\n+++ b/gcc/pretty-print-token-buffer.h\n@@ -0,0 +1,70 @@\n+/* Capturing the results of pretty_print for later playback.\n+   Copyright (C) 2023-2026 Free Software Foundation, Inc.\n+   Contributed by David Malcolm <dmalcolm@redhat.com>.\n+\n+This file is part of GCC.\n+\n+GCC is free software; you can redistribute it and/or modify it under\n+the terms of the GNU General Public License as published by the Free\n+Software Foundation; either version 3, or (at your option) any later\n+version.\n+\n+GCC is distributed in the hope that it will be useful, but WITHOUT ANY\n+WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n+for more details.\n+\n+You should have received a copy of the GNU General Public License\n+along with GCC; see the file COPYING3.  If not see\n+<http://www.gnu.org/licenses/>.  */\n+\n+#ifndef GCC_PRETTY_PRINT_TOKEN_BUFFER_H\n+#define GCC_PRETTY_PRINT_TOKEN_BUFFER_H\n+\n+#include \"pretty-print-format-impl.h\"\n+#include \"auto-obstack.h\"\n+#include \"pretty-print-markup.h\"\n+\n+/* A class for capturing the results of pretty-printing as tokens,\n+   potentially for playback into a different pretty-printer.  */\n+\n+class pretty_print_token_buffer\n+{\n+public:\n+  pretty_print_token_buffer ();\n+  pretty_print_token_buffer (const char *gmsgid,\n+\t\t\t     va_list *args);\n+\n+  pretty_print_token_buffer (const pretty_print_token_buffer &) = delete;\n+  pretty_print_token_buffer (pretty_print_token_buffer &&);\n+\n+  ~pretty_print_token_buffer () = default;\n+\n+  std::string to_string () const;\n+\n+  void dump (FILE *out) const { m_tokens.dump (out); }\n+  void DEBUG_FUNCTION dump () const { dump (stderr); }\n+\n+  std::unique_ptr<auto_obstack> m_obstack;\n+  pp_token_list m_tokens;\n+};\n+\n+/* A pp_element subclass for use with \"%e\" that replays the buffered tokens\n+   from TOKEN_BUF in another formatting call.  */\n+\n+class pp_token_buffer_element : public pp_element\n+{\n+public:\n+  pp_token_buffer_element (const pretty_print_token_buffer &token_buf)\n+  : m_token_buf (token_buf)\n+  {\n+  }\n+\n+  void\n+  add_to_phase_2 (pp_markup::context &ctxt) final override;\n+\n+private:\n+  const pretty_print_token_buffer &m_token_buf;\n+};\n+\n+#endif /* GCC_PRETTY_PRINT_TOKEN_BUFFER_H */\ndiff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc\nindex 7ead010360f..86b2340e9b9 100644\n--- a/gcc/selftest-run-tests.cc\n+++ b/gcc/selftest-run-tests.cc\n@@ -68,6 +68,7 @@ selftest::run_tests ()\n   hash_set_tests_cc_tests ();\n   vec_cc_tests ();\n   pretty_print_cc_tests ();\n+  pretty_print_token_buffer_cc_tests ();\n   wide_int_cc_tests ();\n   ggc_tests_cc_tests ();\n   sreal_cc_tests ();\ndiff --git a/gcc/selftest.h b/gcc/selftest.h\nindex 462786dab2f..e3e86ffaf0f 100644\n--- a/gcc/selftest.h\n+++ b/gcc/selftest.h\n@@ -246,6 +246,7 @@ extern void ordered_hash_map_tests_cc_tests ();\n extern void path_coverage_cc_tests ();\n extern void predict_cc_tests ();\n extern void pretty_print_cc_tests ();\n+extern void pretty_print_token_buffer_cc_tests ();\n extern void pub_sub_cc_tests ();\n extern void range_op_tests ();\n extern void range_tests ();\n",
    "prefixes": [
        "pushed:",
        "r17-169"
    ]
}