@@ -1603,7 +1603,7 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
pp_format (this->printer, &diagnostic->message, m_urlifier);
m_output_format->on_begin_diagnostic (*diagnostic);
- pp_output_formatted_text (this->printer);
+ pp_output_formatted_text (this->printer, m_urlifier);
if (m_show_cwe)
print_any_cwe (*diagnostic);
if (m_show_rules)
@@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
+#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
#include "intl.h"
@@ -1031,7 +1032,16 @@ obstack_append_string (obstack *ostack, const char *str)
obstack_grow (ostack, str, strlen (str));
}
-/* Given quoted text starting at QUOTED_TEXT_START_IDX within PP's buffer,
+/* Append STR to OSTACK, without a null-terminator. */
+
+static void
+obstack_append_string (obstack *ostack, const char *str, size_t len)
+{
+ obstack_grow (ostack, str, len);
+}
+
+/* Given quoted text within the buffer OBSTACK
+ at the half-open interval [QUOTED_TEXT_START_IDX, QUOTED_TEXT_END_IDX),
potentially use URLIFIER (if non-null) to see if there's a URL for the
quoted text.
@@ -1039,47 +1049,45 @@ obstack_append_string (obstack *ostack, const char *str)
version of the text, using PP's settings.
For example, given this is the buffer:
- "this is a test `hello world"
+ "this is a test `hello worldTRAILING-CONTENT"
.................^~~~~~~~~~~
with the quoted text starting at the 'h' of "hello world", the buffer
becomes:
- "this is a test `BEGIN_URL(URL)hello worldEND(URL)"
+ "this is a test `BEGIN_URL(URL)hello worldEND(URL)TRAILING-CONTENT"
.................^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.................-----------replacement-----------
-*/
-static void
+ Return the new offset into the buffer of the quoted text endpoint i.e.
+ the offset of "TRAILING-CONTENT" in the above. */
+
+static size_t
urlify_quoted_string (pretty_printer *pp,
+ obstack *obstack,
const urlifier *urlifier,
- size_t quoted_text_start_idx)
+ size_t quoted_text_start_idx,
+ size_t quoted_text_end_idx)
{
if (pp->url_format == URL_FORMAT_NONE)
- return;
+ return quoted_text_end_idx;
if (!urlifier)
- return;
+ return quoted_text_end_idx;
- output_buffer * const buffer = pp_buffer (pp);
-
- /* Get end of quoted string. */
- const size_t close_quote_idx
- = obstack_object_size (&buffer->chunk_obstack);
- gcc_assert (close_quote_idx >= quoted_text_start_idx);
- if (close_quote_idx == quoted_text_start_idx)
+ const size_t quoted_len = quoted_text_end_idx - quoted_text_start_idx;
+ if (quoted_len == 0)
/* Empty quoted string; do nothing. */
- return;
- const size_t len = close_quote_idx - quoted_text_start_idx;
- const char *start = (buffer->chunk_obstack.object_base
- + quoted_text_start_idx);
- char *url = urlifier->get_url_for_quoted_text (start, len);
+ return quoted_text_end_idx;
+ const char *start = (obstack->object_base + quoted_text_start_idx);
+ char *url = urlifier->get_url_for_quoted_text (start, quoted_len);
if (!url)
/* No URL for this quoted text; do nothing. */
- return;
+ return quoted_text_end_idx;
- /* Stash a copy of the quoted text. */
- char *text = xstrndup (start, len);
+ /* Stash a copy of the remainder of the chunk. */
+ char *text = xstrndup (start,
+ obstack_object_size (obstack) - quoted_text_start_idx);
/* Replace quoted text... */
- buffer->chunk_obstack.next_free -= len;
+ obstack->next_free = obstack->object_base + quoted_text_start_idx;
/* ...with URLified version of the text. */
/* Begin URL. */
@@ -1089,27 +1097,136 @@ urlify_quoted_string (pretty_printer *pp,
case URL_FORMAT_NONE:
gcc_unreachable ();
case URL_FORMAT_ST:
- obstack_append_string (&buffer->chunk_obstack,
- "\33]8;;");
- obstack_append_string (&buffer->chunk_obstack, url);
- obstack_append_string (&buffer->chunk_obstack,
- "\33\\");
+ obstack_append_string (obstack, "\33]8;;");
+ obstack_append_string (obstack, url);
+ obstack_append_string (obstack, "\33\\");
break;
case URL_FORMAT_BEL:
- obstack_append_string (&buffer->chunk_obstack,
- "\33]8;;");
- obstack_append_string (&buffer->chunk_obstack, url);
- obstack_append_string (&buffer->chunk_obstack,
- "\a");
+ obstack_append_string (obstack, "\33]8;;");
+ obstack_append_string (obstack, url);
+ obstack_append_string (obstack, "\a");
break;
}
- /* Add the text back. */
- obstack_append_string (&buffer->chunk_obstack, text);
+ /* Add back the quoted part of the text. */
+ obstack_append_string (obstack, text, quoted_len);
/* End URL. */
- obstack_append_string (&buffer->chunk_obstack,
+ obstack_append_string (obstack,
get_end_url_string (pp));
+
+ size_t new_end_idx = obstack_object_size (obstack);
+
+ /* Add back the remainder of the text after the quoted part. */
+ obstack_append_string (obstack, text + quoted_len);
free (text);
free (url);
+ return new_end_idx;
+}
+
+/* A class for tracking quoted text within a buffer for
+ use by a urlifier. */
+
+class quoting_info
+{
+public:
+ /* Called when quoted text is begun in phase 1 or 2. */
+ void on_begin_quote (const output_buffer &buf,
+ unsigned chunk_idx)
+ {
+ /* Stash location of start of quoted string. */
+ size_t byte_offset = obstack_object_size (&buf.chunk_obstack);
+ m_loc_last_open_quote = location (chunk_idx, byte_offset);
+ }
+
+ /* Called when quoted text is ended in phase 1 or 2. */
+ void on_end_quote (pretty_printer *pp,
+ output_buffer &buf,
+ unsigned chunk_idx,
+ const urlifier &urlifier)
+ {
+ /* If possible, do urlification now. */
+ if (chunk_idx == m_loc_last_open_quote.m_chunk_idx)
+ {
+ urlify_quoted_string (pp,
+ &buf.chunk_obstack,
+ &urlifier,
+ m_loc_last_open_quote.m_byte_offset,
+ obstack_object_size (&buf.chunk_obstack));
+ m_loc_last_open_quote = location ();
+ return;
+ }
+ /* Otherwise the quoted text straddles multiple chunks.
+ Stash the location of end of quoted string for use in phase 3. */
+ size_t byte_offset = obstack_object_size (&buf.chunk_obstack);
+ m_phase_3_quotes.push_back (run (m_loc_last_open_quote,
+ location (chunk_idx, byte_offset)));
+ m_loc_last_open_quote = location ();
+ }
+
+ bool has_phase_3_quotes_p () const
+ {
+ return m_phase_3_quotes.size () > 0;
+ }
+ void handle_phase_3 (pretty_printer *pp,
+ const urlifier &urlifier);
+
+private:
+ struct location
+ {
+ location ()
+ : m_chunk_idx (UINT_MAX),
+ m_byte_offset (SIZE_MAX)
+ {
+ }
+
+ location (unsigned chunk_idx,
+ size_t byte_offset)
+ : m_chunk_idx (chunk_idx),
+ m_byte_offset (byte_offset)
+ {
+ }
+
+ unsigned m_chunk_idx;
+ size_t m_byte_offset;
+ };
+
+ struct run
+ {
+ run (location start, location end)
+ : m_start (start), m_end (end)
+ {
+ }
+
+ location m_start;
+ location m_end;
+ };
+
+ location m_loc_last_open_quote;
+ std::vector<run> m_phase_3_quotes;
+};
+
+static void
+on_begin_quote (const output_buffer &buf,
+ unsigned chunk_idx,
+ const urlifier *urlifier)
+{
+ if (!urlifier)
+ return;
+ if (!buf.cur_chunk_array->m_quotes)
+ buf.cur_chunk_array->m_quotes = new quoting_info ();
+ buf.cur_chunk_array->m_quotes->on_begin_quote (buf, chunk_idx);
+}
+
+static void
+on_end_quote (pretty_printer *pp,
+ output_buffer &buf,
+ unsigned chunk_idx,
+ const urlifier *urlifier)
+{
+ if (!urlifier)
+ return;
+ if (!buf.cur_chunk_array->m_quotes)
+ buf.cur_chunk_array->m_quotes = new quoting_info ();
+ buf.cur_chunk_array->m_quotes->on_end_quote (pp, buf, chunk_idx, *urlifier);
}
/* The following format specifiers are recognized as being client independent:
@@ -1161,11 +1278,12 @@ urlify_quoted_string (pretty_printer *pp,
"before %<quoted%> after"
with a URLIFIER that has a URL for "quoted" might be emitted as:
"before `BEGIN_URL(http://example.com)quotedEND_URL' after"
- This only works for message fragments that are:
+ This is handled here for message fragments that are:
- quoted entirely in phase 1 (e.g. "%<this is quoted%>"), or
- quoted entirely in phase 2 (e.g. "%qs"),
- but *not* in strings that use a mixture of both phases
- (e.g. "%<this is a mixture: %s %>"). */
+ Quoted fragments that use a mixture of both phases
+ (e.g. "%<this is a mixture: %s %>")
+ are stashed into the output_buffer's m_quotes for use in phase 3. */
void
pp_format (pretty_printer *pp,
@@ -1182,12 +1300,11 @@ pp_format (pretty_printer *pp,
bool any_unnumbered = false, any_numbered = false;
const char **formatters[PP_NL_ARGMAX];
- /* Keep track of location of last "%", if any. */
- size_t quoted_text_start_idx = 0;
-
/* Allocate a new chunk structure. */
new_chunk_array = XOBNEW (&buffer->chunk_obstack, struct chunk_info);
+
new_chunk_array->prev = buffer->cur_chunk_array;
+ new_chunk_array->m_quotes = nullptr;
buffer->cur_chunk_array = new_chunk_array;
args = new_chunk_array->args;
@@ -1229,20 +1346,14 @@ pp_format (pretty_printer *pp,
obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr));
p++;
- /* Stash offset of start of quoted string. */
- quoted_text_start_idx
- = obstack_object_size (&buffer->chunk_obstack);
-
+ on_begin_quote (*buffer, chunk, urlifier);
continue;
}
case '>':
{
- if (quoted_text_start_idx)
- {
- urlify_quoted_string (pp, urlifier, quoted_text_start_idx);
- quoted_text_start_idx = 0;
- }
+ on_end_quote (pp, *buffer, chunk, urlifier);
+
const char *colorstr = colorize_stop (pp_show_color (pp));
obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr));
}
@@ -1282,14 +1393,7 @@ pp_format (pretty_printer *pp,
default:
/* Handled in phase 2. Terminate the plain chunk here. */
obstack_1grow (&buffer->chunk_obstack, '\0');
- gcc_assert (chunk < PP_NL_ARGMAX * 2);
args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *);
- /* We can't yet handle urlifying quoted strings that use
- a combination of phase 1 and phase 2 e.g.
- "did you mean %<-%s%>".
- Stop any phase 1 quoted text if there are going to be any
- phase 2 quoted chunks. */
- quoted_text_start_idx = 0;
break;
}
@@ -1392,7 +1496,6 @@ pp_format (pretty_printer *pp,
bool plus = false;
bool hash = false;
bool quote = false;
- quoted_text_start_idx = 0;
/* We do not attempt to enforce any ordering on the modifier
characters. */
@@ -1435,8 +1538,7 @@ pp_format (pretty_printer *pp,
if (quote)
{
pp_begin_quote (pp, pp_show_color (pp));
- quoted_text_start_idx
- = obstack_object_size (&buffer->chunk_obstack);
+ on_begin_quote (*buffer, chunk, urlifier);
}
switch (*p)
@@ -1608,11 +1710,7 @@ pp_format (pretty_printer *pp,
if (quote)
{
- if (quoted_text_start_idx)
- {
- urlify_quoted_string (pp, urlifier, quoted_text_start_idx);
- quoted_text_start_idx = 0;
- }
+ on_end_quote (pp, *buffer, chunk, urlifier);
pp_end_quote (pp, pp_show_color (pp));
}
@@ -1636,9 +1734,136 @@ pp_format (pretty_printer *pp,
pp_clear_state (pp);
}
-/* Format of a message pointed to by TEXT. */
+struct auto_obstack
+{
+ auto_obstack ()
+ {
+ obstack_init (&m_obstack);
+ }
+
+ ~auto_obstack ()
+ {
+ obstack_free (&m_obstack, NULL);
+ }
+
+ void grow (const void *src, size_t length)
+ {
+ obstack_grow (&m_obstack, src, length);
+ }
+
+ void *object_base () const
+ {
+ return m_obstack.object_base;
+ }
+
+ size_t object_size () const
+ {
+ return obstack_object_size (&m_obstack);
+ }
+
+ obstack m_obstack;
+};
+
+/* Subroutine of pp_output_formatted_text for the awkward case where
+ quoted text straddles multiple chunks.
+
+ Flush PP's buffer's chunks to PP's output buffer, whilst inserting
+ URLs for any quoted text that should be URLified.
+
+ For example, given:
+ | pp_format (pp,
+ | "unrecognized option %qs; did you mean %<-%s%>",
+ | "foo", "foption");
+ we would have these chunks:
+ | chunk 0: "unrecognized option "
+ | chunk 1: "`foo'" (already checked for urlification)
+ | chunk 2: "; did you mean `-"
+ | ^*
+ | chunk 3: "foption"
+ | *******
+ | chunk 4: "'"
+ | ^
+ and this quoting_info would have recorded the open quote near the end
+ of chunk 2 and close quote at the start of chunk 4; this function would
+ check the combination of the end of chunk 2 and all of chunk 3 ("-foption")
+ for urlification. */
+
+void
+quoting_info::handle_phase_3 (pretty_printer *pp,
+ const urlifier &urlifier)
+{
+ unsigned int chunk;
+ output_buffer * const buffer = pp_buffer (pp);
+ struct chunk_info *chunk_array = buffer->cur_chunk_array;
+ const char **args = chunk_array->args;
+
+ /* We need to construct the string into an intermediate buffer
+ for this case, since using pp_string can introduce prefixes
+ and line-wrapping, and omit whitespace at the start of lines. */
+ auto_obstack combined_buf;
+
+ /* Iterate simultaneously through both
+ - the chunks and
+ - the runs of quoted characters
+ Accumulate text from the chunks into combined_buf, and handle
+ runs of quoted characters when handling the chunks they
+ correspond to. */
+ size_t start_of_run_byte_offset = 0;
+ std::vector<quoting_info::run>::const_iterator iter_run
+ = buffer->cur_chunk_array->m_quotes->m_phase_3_quotes.begin ();
+ std::vector<quoting_info::run>::const_iterator end_runs
+ = buffer->cur_chunk_array->m_quotes->m_phase_3_quotes.end ();
+ for (chunk = 0; args[chunk]; chunk++)
+ {
+ size_t start_of_chunk_idx = combined_buf.object_size ();
+
+ combined_buf.grow (args[chunk], strlen (args[chunk]));
+
+ if (iter_run != end_runs
+ && chunk == iter_run->m_end.m_chunk_idx)
+ {
+ /* A run is ending; consider for it urlification. */
+ const size_t end_of_run_byte_offset
+ = start_of_chunk_idx + iter_run->m_end.m_byte_offset;
+ const size_t end_offset
+ = urlify_quoted_string (pp,
+ &combined_buf.m_obstack,
+ &urlifier,
+ start_of_run_byte_offset,
+ end_of_run_byte_offset);
+
+ /* If URLification occurred it will have grown the buffer.
+ We need to update start_of_chunk_idx so that offsets
+ relative to it are still correct, for the case where
+ we have a chunk that both ends a quoted run and starts
+ another quoted run. */
+ gcc_assert (end_offset >= end_of_run_byte_offset);
+ start_of_chunk_idx += end_offset - end_of_run_byte_offset;
+
+ iter_run++;
+ }
+ if (iter_run != end_runs
+ && chunk == iter_run->m_start.m_chunk_idx)
+ {
+ /* Note where the run starts w.r.t. the composed buffer. */
+ start_of_run_byte_offset
+ = start_of_chunk_idx + iter_run->m_start.m_byte_offset;
+ }
+ }
+
+ /* Now print to PP. */
+ const char *start
+ = static_cast <const char *> (combined_buf.object_base ());
+ pp_maybe_wrap_text (pp, start, start + combined_buf.object_size ());
+}
+
+/* Format of a message pointed to by TEXT.
+ If URLIFIER is non-null then use it on any quoted text that was not
+ handled in phases 1 or 2 to potentially add URLs. */
+
void
-pp_output_formatted_text (pretty_printer *pp)
+pp_output_formatted_text (pretty_printer *pp,
+ const urlifier *urlifier)
{
unsigned int chunk;
output_buffer * const buffer = pp_buffer (pp);
@@ -1649,11 +1874,20 @@ pp_output_formatted_text (pretty_printer *pp)
/* This is a third phase, first 2 phases done in pp_format_args.
Now we actually print it. */
- for (chunk = 0; args[chunk]; chunk++)
- pp_string (pp, args[chunk]);
+
+ /* If we have any deferred urlification, handle it now. */
+ if (urlifier
+ && pp->url_format != URL_FORMAT_NONE
+ && buffer->cur_chunk_array->m_quotes
+ && buffer->cur_chunk_array->m_quotes->has_phase_3_quotes_p ())
+ buffer->cur_chunk_array->m_quotes->handle_phase_3 (pp, *urlifier);
+ else
+ for (chunk = 0; args[chunk]; chunk++)
+ pp_string (pp, args[chunk]);
/* Deallocate the chunk structure and everything after it (i.e. the
associated series of formatted strings). */
+ delete buffer->cur_chunk_array->m_quotes;
buffer->cur_chunk_array = chunk_array->prev;
obstack_free (&buffer->chunk_obstack, chunk_array);
}
@@ -2800,7 +3034,7 @@ pp_printf_with_urlifier (pretty_printer *pp,
va_start (ap, msg);
text_info text (msg, &ap, errno);
pp_format (pp, &text, urlifier);
- pp_output_formatted_text (pp);
+ pp_output_formatted_text (pp, urlifier);
va_end (ap);
}
@@ -2876,9 +3110,71 @@ test_urlification ()
pp_printf_with_urlifier (&pp, &urlifier,
"foo %<-f%s%> bar",
"option");
- /* We don't support this, but make sure we don't crash. */
ASSERT_STREQ
- ("foo `-foption' bar",
+ ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
+ pp_formatted_text (&pp));
+ }
+
+ /* Likewise, where there is trailing phase 1 content within the
+ quoted region. */
+ {
+ pretty_printer pp;
+ pp.url_format = URL_FORMAT_ST;
+ pp_printf_with_urlifier (&pp, &urlifier,
+ "foo %<-f%sion%> bar %<-f%sion%> baz",
+ "opt", "opt");
+ ASSERT_STREQ
+ ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
+ pp_formatted_text (&pp));
+ }
+
+ /* Likewise. */
+ {
+ pretty_printer pp;
+ pp.url_format = URL_FORMAT_ST;
+ pp_printf_with_urlifier (&pp, &urlifier,
+ "foo %<%sption%> bar %<-f%sion%> baz",
+ "-fo", "opt");
+ ASSERT_STREQ
+ ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
+ pp_formatted_text (&pp));
+ }
+
+ /* Another mixed usage of %< and %s, where the quoted string is built
+ between a mixture of phase 1 and multiple phase 2. */
+ {
+ pretty_printer pp;
+ pp.url_format = URL_FORMAT_ST;
+ pp_printf_with_urlifier (&pp, &urlifier,
+ "foo %<-f%s%s%> bar",
+ "opt", "ion");
+ ASSERT_STREQ
+ ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
+ pp_formatted_text (&pp));
+ }
+
+ /* Mixed usage of %< and %s with a prefix. */
+ {
+ pretty_printer pp;
+ pp.url_format = URL_FORMAT_ST;
+ pp_set_prefix (&pp, xstrdup ("PREFIX"));
+ pp_printf_with_urlifier (&pp, &urlifier,
+ "foo %<-f%s%> bar",
+ "option");
+ ASSERT_STREQ
+ ("PREFIXfoo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
+ pp_formatted_text (&pp));
+ }
+
+ /* Example of mixed %< and %s with numbered args. */
+ {
+ pretty_printer pp;
+ pp.url_format = URL_FORMAT_ST;
+ pp_printf_with_urlifier (&pp, &urlifier,
+ "foo %<-f%2$st%1$sn%> bar",
+ "io", "op");
+ ASSERT_STREQ
+ ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
pp_formatted_text (&pp));
}
}
@@ -69,6 +69,8 @@ enum diagnostic_prefixing_rule_t
DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE = 0x2
};
+class quoting_info;
+
/* The chunk_info data structure forms a stack of the results from the
first phase of formatting (pp_format) which have not yet been
output (pp_output_formatted_text). A stack is necessary because
@@ -86,6 +88,10 @@ struct chunk_info
text, and the third phase simply emits all the chunks in sequence
with appropriate line-wrapping. */
const char *args[PP_NL_ARGMAX * 2];
+
+ /* If non-null, information on quoted text runs within the chunks
+ for use by a urlifier. */
+ quoting_info *m_quotes;
};
/* The output buffer datatype. This is best seen as an abstract datatype
@@ -409,7 +415,8 @@ extern void pp_flush (pretty_printer *);
extern void pp_really_flush (pretty_printer *);
extern void pp_format (pretty_printer *, text_info *,
const urlifier * = nullptr);
-extern void pp_output_formatted_text (pretty_printer *);
+extern void pp_output_formatted_text (pretty_printer *,
+ const urlifier * = nullptr);
extern void pp_format_verbatim (pretty_printer *, text_info *);
extern void pp_indent (pretty_printer *);