1/* Various declarations for language-independent pretty-print subroutines.
2 Copyright (C) 2003-2025 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#define INCLUDE_VECTOR
23#include "system.h"
24#include "coretypes.h"
25#include "intl.h"
26#include "pretty-print.h"
27#include "pretty-print-format-impl.h"
28#include "pretty-print-markup.h"
29#include "pretty-print-urlifier.h"
30#include "diagnostic-color.h"
31#include "diagnostic-event-id.h"
32#include "diagnostic-highlight-colors.h"
33#include "selftest.h"
34
35#if HAVE_ICONV
36#include <iconv.h>
37#endif
38
39#ifdef __MINGW32__
40
41/* Replacement for fputs() that handles ANSI escape codes on Windows NT.
42 Contributed by: Liu Hao (lh_mouse at 126 dot com)
43
44 XXX: This file is compiled into libcommon.a that will be self-contained.
45 It looks like that these functions can be put nowhere else. */
46
47#include <io.h>
48#define WIN32_LEAN_AND_MEAN 1
49#include <windows.h>
50
51/* Write all bytes in [s,s+n) into the specified stream.
52 Errors are ignored. */
53static void
54write_all (HANDLE h, const char *s, size_t n)
55{
56 size_t rem = n;
57 DWORD step;
58
59 while (rem != 0)
60 {
61 if (rem <= UINT_MAX)
62 step = rem;
63 else
64 step = UINT_MAX;
65 if (!WriteFile (h, s + n - rem, step, &step, NULL))
66 break;
67 rem -= step;
68 }
69}
70
71/* Find the beginning of an escape sequence.
72 There are two cases:
73 1. If the sequence begins with an ESC character (0x1B) and a second
74 character X in [0x40,0x5F], returns X and stores a pointer to
75 the third character into *head.
76 2. If the sequence begins with a character X in [0x80,0x9F], returns
77 (X-0x40) and stores a pointer to the second character into *head.
78 Stores the number of ESC character(s) in *prefix_len.
79 Returns 0 if no such sequence can be found. */
80static int
81find_esc_head (int *prefix_len, const char **head, const char *str)
82{
83 int c;
84 const char *r = str;
85 int escaped = 0;
86
87 for (;;)
88 {
89 c = (unsigned char) *r;
90 if (c == 0)
91 {
92 /* Not found. */
93 return 0;
94 }
95 if (escaped && 0x40 <= c && c <= 0x5F)
96 {
97 /* Found (case 1). */
98 *prefix_len = 2;
99 *head = r + 1;
100 return c;
101 }
102 if (0x80 <= c && c <= 0x9F)
103 {
104 /* Found (case 2). */
105 *prefix_len = 1;
106 *head = r + 1;
107 return c - 0x40;
108 }
109 ++r;
110 escaped = c == 0x1B;
111 }
112}
113
114/* Find the terminator of an escape sequence.
115 str should be the value stored in *head by a previous successful
116 call to find_esc_head().
117 Returns 0 if no such sequence can be found. */
118static int
119find_esc_terminator (const char **term, const char *str)
120{
121 int c;
122 const char *r = str;
123
124 for (;;)
125 {
126 c = (unsigned char) *r;
127 if (c == 0)
128 {
129 /* Not found. */
130 return 0;
131 }
132 if (0x40 <= c && c <= 0x7E)
133 {
134 /* Found. */
135 *term = r;
136 return c;
137 }
138 ++r;
139 }
140}
141
142/* Handle a sequence of codes. Sequences that are invalid, reserved,
143 unrecognized or unimplemented are ignored silently.
144 There isn't much we can do because of lameness of Windows consoles. */
145static void
146eat_esc_sequence (HANDLE h, int esc_code,
147 const char *esc_head, const char *esc_term)
148{
149 /* Numbers in an escape sequence cannot be negative, because
150 a minus sign in the middle of it would have terminated it. */
151 long n1, n2;
152 char *eptr, *delim;
153 CONSOLE_SCREEN_BUFFER_INFO sb;
154 COORD cr;
155 /* ED and EL parameters. */
156 DWORD cnt, step;
157 long rows;
158 /* SGR parameters. */
159 WORD attrib_add, attrib_rm;
160 const char *param;
161
162 switch (MAKEWORD (esc_code, *esc_term))
163 {
164 /* ESC [ n1 'A'
165 Move the cursor up by n1 characters. */
166 case MAKEWORD ('[', 'A'):
167 if (esc_head == esc_term)
168 n1 = 1;
169 else
170 {
171 n1 = strtol (esc_head, &eptr, 10);
172 if (eptr != esc_term)
173 break;
174 }
175
176 if (GetConsoleScreenBufferInfo (h, &sb))
177 {
178 cr = sb.dwCursorPosition;
179 /* Stop at the topmost boundary. */
180 if (cr.Y > n1)
181 cr.Y -= n1;
182 else
183 cr.Y = 0;
184 SetConsoleCursorPosition (h, cr);
185 }
186 break;
187
188 /* ESC [ n1 'B'
189 Move the cursor down by n1 characters. */
190 case MAKEWORD ('[', 'B'):
191 if (esc_head == esc_term)
192 n1 = 1;
193 else
194 {
195 n1 = strtol (esc_head, &eptr, 10);
196 if (eptr != esc_term)
197 break;
198 }
199
200 if (GetConsoleScreenBufferInfo (h, &sb))
201 {
202 cr = sb.dwCursorPosition;
203 /* Stop at the bottommost boundary. */
204 if (sb.dwSize.Y - cr.Y > n1)
205 cr.Y += n1;
206 else
207 cr.Y = sb.dwSize.Y;
208 SetConsoleCursorPosition (h, cr);
209 }
210 break;
211
212 /* ESC [ n1 'C'
213 Move the cursor right by n1 characters. */
214 case MAKEWORD ('[', 'C'):
215 if (esc_head == esc_term)
216 n1 = 1;
217 else
218 {
219 n1 = strtol (esc_head, &eptr, 10);
220 if (eptr != esc_term)
221 break;
222 }
223
224 if (GetConsoleScreenBufferInfo (h, &sb))
225 {
226 cr = sb.dwCursorPosition;
227 /* Stop at the rightmost boundary. */
228 if (sb.dwSize.X - cr.X > n1)
229 cr.X += n1;
230 else
231 cr.X = sb.dwSize.X;
232 SetConsoleCursorPosition (h, cr);
233 }
234 break;
235
236 /* ESC [ n1 'D'
237 Move the cursor left by n1 characters. */
238 case MAKEWORD ('[', 'D'):
239 if (esc_head == esc_term)
240 n1 = 1;
241 else
242 {
243 n1 = strtol (esc_head, &eptr, 10);
244 if (eptr != esc_term)
245 break;
246 }
247
248 if (GetConsoleScreenBufferInfo (h, &sb))
249 {
250 cr = sb.dwCursorPosition;
251 /* Stop at the leftmost boundary. */
252 if (cr.X > n1)
253 cr.X -= n1;
254 else
255 cr.X = 0;
256 SetConsoleCursorPosition (h, cr);
257 }
258 break;
259
260 /* ESC [ n1 'E'
261 Move the cursor to the beginning of the n1-th line downwards. */
262 case MAKEWORD ('[', 'E'):
263 if (esc_head == esc_term)
264 n1 = 1;
265 else
266 {
267 n1 = strtol (esc_head, &eptr, 10);
268 if (eptr != esc_term)
269 break;
270 }
271
272 if (GetConsoleScreenBufferInfo (h, &sb))
273 {
274 cr = sb.dwCursorPosition;
275 cr.X = 0;
276 /* Stop at the bottommost boundary. */
277 if (sb.dwSize.Y - cr.Y > n1)
278 cr.Y += n1;
279 else
280 cr.Y = sb.dwSize.Y;
281 SetConsoleCursorPosition (h, cr);
282 }
283 break;
284
285 /* ESC [ n1 'F'
286 Move the cursor to the beginning of the n1-th line upwards. */
287 case MAKEWORD ('[', 'F'):
288 if (esc_head == esc_term)
289 n1 = 1;
290 else
291 {
292 n1 = strtol (esc_head, &eptr, 10);
293 if (eptr != esc_term)
294 break;
295 }
296
297 if (GetConsoleScreenBufferInfo (h, &sb))
298 {
299 cr = sb.dwCursorPosition;
300 cr.X = 0;
301 /* Stop at the topmost boundary. */
302 if (cr.Y > n1)
303 cr.Y -= n1;
304 else
305 cr.Y = 0;
306 SetConsoleCursorPosition (h, cr);
307 }
308 break;
309
310 /* ESC [ n1 'G'
311 Move the cursor to the (1-based) n1-th column. */
312 case MAKEWORD ('[', 'G'):
313 if (esc_head == esc_term)
314 n1 = 1;
315 else
316 {
317 n1 = strtol (esc_head, &eptr, 10);
318 if (eptr != esc_term)
319 break;
320 }
321
322 if (GetConsoleScreenBufferInfo (h, &sb))
323 {
324 cr = sb.dwCursorPosition;
325 n1 -= 1;
326 /* Stop at the leftmost or rightmost boundary. */
327 if (n1 < 0)
328 cr.X = 0;
329 else if (n1 > sb.dwSize.X)
330 cr.X = sb.dwSize.X;
331 else
332 cr.X = n1;
333 SetConsoleCursorPosition (h, cr);
334 }
335 break;
336
337 /* ESC [ n1 ';' n2 'H'
338 ESC [ n1 ';' n2 'f'
339 Move the cursor to the (1-based) n1-th row and
340 (also 1-based) n2-th column. */
341 case MAKEWORD ('[', 'H'):
342 case MAKEWORD ('[', 'f'):
343 if (esc_head == esc_term)
344 {
345 /* Both parameters are omitted and set to 1 by default. */
346 n1 = 1;
347 n2 = 1;
348 }
349 else if (!(delim = (char *) memchr (esc_head, ';',
350 esc_term - esc_head)))
351 {
352 /* Only the first parameter is given. The second one is
353 set to 1 by default. */
354 n1 = strtol (esc_head, &eptr, 10);
355 if (eptr != esc_term)
356 break;
357 n2 = 1;
358 }
359 else
360 {
361 /* Both parameters are given. The first one shall be
362 terminated by the semicolon. */
363 n1 = strtol (esc_head, &eptr, 10);
364 if (eptr != delim)
365 break;
366 n2 = strtol (delim + 1, &eptr, 10);
367 if (eptr != esc_term)
368 break;
369 }
370
371 if (GetConsoleScreenBufferInfo (h, &sb))
372 {
373 cr = sb.dwCursorPosition;
374 n1 -= 1;
375 n2 -= 1;
376 /* The cursor position shall be relative to the view coord of
377 the console window, which is usually smaller than the actual
378 buffer. FWIW, the 'appropriate' solution will be shrinking
379 the buffer to match the size of the console window,
380 destroying scrollback in the process. */
381 n1 += sb.srWindow.Top;
382 n2 += sb.srWindow.Left;
383 /* Stop at the topmost or bottommost boundary. */
384 if (n1 < 0)
385 cr.Y = 0;
386 else if (n1 > sb.dwSize.Y)
387 cr.Y = sb.dwSize.Y;
388 else
389 cr.Y = n1;
390 /* Stop at the leftmost or rightmost boundary. */
391 if (n2 < 0)
392 cr.X = 0;
393 else if (n2 > sb.dwSize.X)
394 cr.X = sb.dwSize.X;
395 else
396 cr.X = n2;
397 SetConsoleCursorPosition (h, cr);
398 }
399 break;
400
401 /* ESC [ n1 'J'
402 Erase display. */
403 case MAKEWORD ('[', 'J'):
404 if (esc_head == esc_term)
405 /* This is one of the very few codes whose parameters have
406 a default value of zero. */
407 n1 = 0;
408 else
409 {
410 n1 = strtol (esc_head, &eptr, 10);
411 if (eptr != esc_term)
412 break;
413 }
414
415 if (GetConsoleScreenBufferInfo (h, &sb))
416 {
417 /* The cursor is not necessarily in the console window, which
418 makes the behavior of this code harder to define. */
419 switch (n1)
420 {
421 case 0:
422 /* If the cursor is in or above the window, erase from
423 it to the bottom of the window; otherwise, do nothing. */
424 cr = sb.dwCursorPosition;
425 cnt = sb.dwSize.X - sb.dwCursorPosition.X;
426 rows = sb.srWindow.Bottom - sb.dwCursorPosition.Y;
427 break;
428 case 1:
429 /* If the cursor is in or under the window, erase from
430 it to the top of the window; otherwise, do nothing. */
431 cr.X = 0;
432 cr.Y = sb.srWindow.Top;
433 cnt = sb.dwCursorPosition.X + 1;
434 rows = sb.dwCursorPosition.Y - sb.srWindow.Top;
435 break;
436 case 2:
437 /* Erase the entire window. */
438 cr.X = sb.srWindow.Left;
439 cr.Y = sb.srWindow.Top;
440 cnt = 0;
441 rows = sb.srWindow.Bottom - sb.srWindow.Top + 1;
442 break;
443 default:
444 /* Erase the entire buffer. */
445 cr.X = 0;
446 cr.Y = 0;
447 cnt = 0;
448 rows = sb.dwSize.Y;
449 break;
450 }
451 if (rows < 0)
452 break;
453 cnt += rows * sb.dwSize.X;
454 FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
455 FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
456 }
457 break;
458
459 /* ESC [ n1 'K'
460 Erase line. */
461 case MAKEWORD ('[', 'K'):
462 if (esc_head == esc_term)
463 /* This is one of the very few codes whose parameters have
464 a default value of zero. */
465 n1 = 0;
466 else
467 {
468 n1 = strtol (esc_head, &eptr, 10);
469 if (eptr != esc_term)
470 break;
471 }
472
473 if (GetConsoleScreenBufferInfo (h, &sb))
474 {
475 switch (n1)
476 {
477 case 0:
478 /* Erase from the cursor to the end. */
479 cr = sb.dwCursorPosition;
480 cnt = sb.dwSize.X - sb.dwCursorPosition.X;
481 break;
482 case 1:
483 /* Erase from the cursor to the beginning. */
484 cr = sb.dwCursorPosition;
485 cr.X = 0;
486 cnt = sb.dwCursorPosition.X + 1;
487 break;
488 default:
489 /* Erase the entire line. */
490 cr = sb.dwCursorPosition;
491 cr.X = 0;
492 cnt = sb.dwSize.X;
493 break;
494 }
495 FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
496 FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
497 }
498 break;
499
500 /* ESC [ n1 ';' n2 'm'
501 Set SGR parameters. Zero or more parameters will follow. */
502 case MAKEWORD ('[', 'm'):
503 attrib_add = 0;
504 attrib_rm = 0;
505 if (esc_head == esc_term)
506 {
507 /* When no parameter is given, reset the console. */
508 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
509 | FOREGROUND_BLUE);
510 attrib_rm = -1; /* Removes everything. */
511 goto sgr_set_it;
512 }
513 param = esc_head;
514 do
515 {
516 /* Parse a parameter. */
517 n1 = strtol (param, &eptr, 10);
518 if (*eptr != ';' && eptr != esc_term)
519 goto sgr_set_it;
520
521 switch (n1)
522 {
523 case 0:
524 /* Reset. */
525 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
526 | FOREGROUND_BLUE);
527 attrib_rm = -1; /* Removes everything. */
528 break;
529 case 1:
530 /* Bold. */
531 attrib_add |= FOREGROUND_INTENSITY;
532 break;
533 case 4:
534 /* Underline. */
535 attrib_add |= COMMON_LVB_UNDERSCORE;
536 break;
537 case 5:
538 /* Blink. */
539 /* XXX: It is not BLINKING at all! */
540 attrib_add |= BACKGROUND_INTENSITY;
541 break;
542 case 7:
543 /* Reverse. */
544 attrib_add |= COMMON_LVB_REVERSE_VIDEO;
545 break;
546 case 22:
547 /* No bold. */
548 attrib_add &= ~FOREGROUND_INTENSITY;
549 attrib_rm |= FOREGROUND_INTENSITY;
550 break;
551 case 24:
552 /* No underline. */
553 attrib_add &= ~COMMON_LVB_UNDERSCORE;
554 attrib_rm |= COMMON_LVB_UNDERSCORE;
555 break;
556 case 25:
557 /* No blink. */
558 /* XXX: It is not BLINKING at all! */
559 attrib_add &= ~BACKGROUND_INTENSITY;
560 attrib_rm |= BACKGROUND_INTENSITY;
561 break;
562 case 27:
563 /* No reverse. */
564 attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
565 attrib_rm |= COMMON_LVB_REVERSE_VIDEO;
566 break;
567 case 30:
568 case 31:
569 case 32:
570 case 33:
571 case 34:
572 case 35:
573 case 36:
574 case 37:
575 /* Foreground color. */
576 attrib_add &= ~(FOREGROUND_RED | FOREGROUND_GREEN
577 | FOREGROUND_BLUE);
578 n1 -= 30;
579 if (n1 & 1)
580 attrib_add |= FOREGROUND_RED;
581 if (n1 & 2)
582 attrib_add |= FOREGROUND_GREEN;
583 if (n1 & 4)
584 attrib_add |= FOREGROUND_BLUE;
585 attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
586 | FOREGROUND_BLUE);
587 break;
588 case 38:
589 /* Reserved for extended foreground color.
590 Don't know how to handle parameters remaining.
591 Bail out. */
592 goto sgr_set_it;
593 case 39:
594 /* Reset foreground color. */
595 /* Set to grey. */
596 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
597 | FOREGROUND_BLUE);
598 attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
599 | FOREGROUND_BLUE);
600 break;
601 case 40:
602 case 41:
603 case 42:
604 case 43:
605 case 44:
606 case 45:
607 case 46:
608 case 47:
609 /* Background color. */
610 attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
611 | BACKGROUND_BLUE);
612 n1 -= 40;
613 if (n1 & 1)
614 attrib_add |= BACKGROUND_RED;
615 if (n1 & 2)
616 attrib_add |= BACKGROUND_GREEN;
617 if (n1 & 4)
618 attrib_add |= BACKGROUND_BLUE;
619 attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
620 | BACKGROUND_BLUE);
621 break;
622 case 48:
623 /* Reserved for extended background color.
624 Don't know how to handle parameters remaining.
625 Bail out. */
626 goto sgr_set_it;
627 case 49:
628 /* Reset background color. */
629 /* Set to black. */
630 attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
631 | BACKGROUND_BLUE);
632 attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
633 | BACKGROUND_BLUE);
634 break;
635 }
636
637 /* Prepare the next parameter. */
638 param = eptr + 1;
639 }
640 while (param != esc_term);
641
642sgr_set_it:
643 /* 0xFFFF removes everything. If it is not the case,
644 care must be taken to preserve old attributes. */
645 if (attrib_rm != 0xFFFF && GetConsoleScreenBufferInfo (h, &sb))
646 {
647 attrib_add |= sb.wAttributes & ~attrib_rm;
648 }
649 if (attrib_add & COMMON_LVB_REVERSE_VIDEO)
650 {
651 /* COMMON_LVB_REVERSE_VIDEO is only effective for DBCS.
652 * Swap foreground and background colors by hand.
653 */
654 attrib_add = (attrib_add & 0xFF00)
655 | ((attrib_add & 0x00F0) >> 4)
656 | ((attrib_add & 0x000F) << 4);
657 attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
658 }
659 SetConsoleTextAttribute (h, attrib_add);
660 break;
661 }
662}
663
664int
665mingw_ansi_fputs (const char *str, FILE *fp)
666{
667 const char *read = str;
668 HANDLE h;
669 DWORD mode;
670 int esc_code, prefix_len;
671 const char *esc_head, *esc_term;
672
673 h = (HANDLE) _get_osfhandle (_fileno (fp));
674 if (h == INVALID_HANDLE_VALUE)
675 return EOF;
676
677 /* Don't mess up stdio functions with Windows APIs. */
678 fflush (fp);
679
680 if (GetConsoleMode (h, &mode)
681#ifdef ENABLE_VIRTUAL_TERMINAL_PROCESSING
682 && !(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
683#endif
684 )
685 /* If it is a console, and doesn't support ANSI escape codes, translate
686 them as needed. */
687 for (;;)
688 {
689 if ((esc_code = find_esc_head (&prefix_len, &esc_head, read)) == 0)
690 {
691 /* Write all remaining characters, then exit. */
692 write_all (h, read, strlen (read));
693 break;
694 }
695 if (find_esc_terminator (&esc_term, esc_head) == 0)
696 /* Ignore incomplete escape sequences at the moment.
697 FIXME: The escape state shall be cached for further calls
698 to this function. */
699 break;
700 write_all (h, read, esc_head - prefix_len - read);
701 eat_esc_sequence (h, esc_code, esc_head, esc_term);
702 read = esc_term + 1;
703 }
704 else
705 /* If it is not a console, write everything as-is. */
706 write_all (h, read, strlen (read));
707
708 return 1;
709}
710
711#endif /* __MINGW32__ */
712
713static int
714decode_utf8_char (const unsigned char *, size_t len, unsigned int *);
715static void pp_quoted_string (pretty_printer *, const char *, size_t = -1);
716
717static void
718default_token_printer (pretty_printer *pp,
719 const pp_token_list &tokens);
720
721/* Overwrite the given location/range within this text_info's rich_location.
722 For use e.g. when implementing "+" in client format decoders. */
723
724void
725text_info::set_location (unsigned int idx, location_t loc,
726 enum range_display_kind range_display_kind)
727{
728 gcc_checking_assert (m_richloc);
729 m_richloc->set_range (idx, loc, range_display_kind);
730}
731
732location_t
733text_info::get_location (unsigned int index_of_location) const
734{
735 gcc_checking_assert (m_richloc);
736
737 if (index_of_location == 0)
738 return m_richloc->get_loc ();
739 else
740 return UNKNOWN_LOCATION;
741}
742
743// Default construct an output buffer.
744
745output_buffer::output_buffer ()
746 : m_formatted_obstack (),
747 m_chunk_obstack (),
748 m_obstack (&m_formatted_obstack),
749 m_cur_formatted_chunks (nullptr),
750 m_stream (stderr),
751 m_line_length (),
752 m_digit_buffer (),
753 m_flush_p (true)
754{
755 obstack_init (&m_formatted_obstack);
756 obstack_init (&m_chunk_obstack);
757}
758
759// Release resources owned by an output buffer at the end of lifetime.
760
761output_buffer::~output_buffer ()
762{
763 obstack_free (&m_chunk_obstack, NULL);
764 obstack_free (&m_formatted_obstack, NULL);
765}
766
767/* Allocate a new pp_formatted_chunks from chunk_obstack and push
768 it onto this buffer's stack.
769 This represents the result of phases 1 and 2 of formatting. */
770
771pp_formatted_chunks *
772output_buffer::push_formatted_chunks ()
773{
774 /* Allocate a new chunk structure. */
775 pp_formatted_chunks *new_chunk_array
776 = XOBNEW (&m_chunk_obstack, pp_formatted_chunks);
777 new_chunk_array->m_prev = m_cur_formatted_chunks;
778 m_cur_formatted_chunks = new_chunk_array;
779 return new_chunk_array;
780}
781
782/* Deallocate the current pp_formatted_chunks structure and everything after it
783 (i.e. the associated series of formatted strings, pp_token_lists, and
784 pp_tokens). */
785
786void
787output_buffer::pop_formatted_chunks ()
788{
789 pp_formatted_chunks *old_top = m_cur_formatted_chunks;
790 gcc_assert (old_top);
791 m_cur_formatted_chunks = old_top->m_prev;
792 obstack_free (&m_chunk_obstack, old_top);
793}
794
795static const int bytes_per_hexdump_line = 16;
796
797static void
798print_hexdump_line (FILE *out, int indent,
799 const void *buf, size_t size, size_t line_start_idx)
800{
801 fprintf (stream: out, format: "%*s%08lx: ", indent, "", (unsigned long)line_start_idx);
802 for (size_t offset = 0; offset < bytes_per_hexdump_line; ++offset)
803 {
804 const size_t idx = line_start_idx + offset;
805 if (idx < size)
806 fprintf (stream: out, format: "%02x ", ((const unsigned char *)buf)[idx]);
807 else
808 fprintf (stream: out, format: " ");
809 }
810 fprintf (stream: out, format: "| ");
811 for (size_t offset = 0; offset < bytes_per_hexdump_line; ++offset)
812 {
813 const size_t idx = line_start_idx + offset;
814 if (idx < size)
815 {
816 unsigned char ch = ((const unsigned char *)buf)[idx];
817 if (!ISPRINT (ch))
818 ch = '.';
819 fputc (c: ch, stream: out);
820 }
821 else
822 break;
823 }
824 fprintf (stream: out, format: "\n");
825
826}
827
828static void
829print_hexdump (FILE *out, int indent, const void *buf, size_t size)
830{
831 for (size_t idx = 0; idx < size; idx += bytes_per_hexdump_line)
832 print_hexdump_line (out, indent, buf, size, line_start_idx: idx);
833}
834
835/* Dump state of this output_buffer to OUT, for debugging. */
836
837void
838output_buffer::dump (FILE *out, int indent) const
839{
840 {
841 size_t obj_size = obstack_object_size (&m_formatted_obstack);
842 fprintf (stream: out, format: "%*sm_formatted_obstack current object: length %li:\n",
843 indent, "", (long)obj_size);
844 print_hexdump (out, indent: indent + 2,
845 buf: m_formatted_obstack.object_base, size: obj_size);
846 }
847 {
848 size_t obj_size = obstack_object_size (&m_chunk_obstack);
849 fprintf (stream: out, format: "%*sm_chunk_obstack current object: length %li:\n",
850 indent, "", (long)obj_size);
851 print_hexdump (out, indent: indent + 2,
852 buf: m_chunk_obstack.object_base, size: obj_size);
853 }
854
855 int depth = 0;
856 for (pp_formatted_chunks *iter = m_cur_formatted_chunks;
857 iter;
858 iter = iter->m_prev, depth++)
859 {
860 fprintf (stream: out, format: "%*spp_formatted_chunks: depth %i\n",
861 indent, "",
862 depth);
863 iter->dump (out, indent: indent + 2);
864 }
865}
866
867#ifndef PTRDIFF_MAX
868#define PTRDIFF_MAX INTTYPE_MAXIMUM (ptrdiff_t)
869#endif
870
871/* Format an integer given by va_arg (ARG, type-specifier T) where
872 type-specifier is a precision modifier as indicated by PREC. F is
873 a string used to construct the appropriate format-specifier. */
874#define pp_integer_with_precision(PP, ARG, PREC, T, F) \
875 do \
876 switch (PREC) \
877 { \
878 case 0: \
879 pp_scalar (PP, "%" F, va_arg (ARG, T)); \
880 break; \
881 \
882 case 1: \
883 pp_scalar (PP, "%l" F, va_arg (ARG, long T)); \
884 break; \
885 \
886 case 2: \
887 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
888 va_arg (ARG, long long T)); \
889 break; \
890 \
891 case 3: \
892 if (T (-1) < T (0)) \
893 pp_scalar (PP, "%" GCC_PRISZ F, \
894 (fmt_size_t) va_arg (ARG, ssize_t)); \
895 else \
896 pp_scalar (PP, "%" GCC_PRISZ F, \
897 (fmt_size_t) va_arg (ARG, size_t)); \
898 break; \
899 \
900 case 4: \
901 if (T (-1) >= T (0)) \
902 { \
903 unsigned long long a = va_arg (ARG, ptrdiff_t); \
904 unsigned long long m = PTRDIFF_MAX; \
905 m = 2 * m + 1; \
906 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
907 a & m); \
908 } \
909 else if (sizeof (ptrdiff_t) <= sizeof (int)) \
910 pp_scalar (PP, "%" F, \
911 (int) va_arg (ARG, ptrdiff_t)); \
912 else if (sizeof (ptrdiff_t) <= sizeof (long)) \
913 pp_scalar (PP, "%l" F, \
914 (long int) va_arg (ARG, ptrdiff_t)); \
915 else \
916 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
917 (long long int) \
918 va_arg (ARG, ptrdiff_t)); \
919 break; \
920 \
921 default: \
922 break; \
923 } \
924 while (0)
925
926
927/* Subroutine of pp_set_maximum_length. Set up PRETTY-PRINTER's
928 internal maximum characters per line. */
929
930void
931pretty_printer::set_real_maximum_length ()
932{
933 /* If we're told not to wrap lines then do the obvious thing. In case
934 we'll emit prefix only once per message, it is appropriate
935 not to increase unnecessarily the line-length cut-off. */
936 if (!pp_is_wrapping_line (this)
937 || pp_prefixing_rule (pp: this) == DIAGNOSTICS_SHOW_PREFIX_ONCE
938 || pp_prefixing_rule (pp: this) == DIAGNOSTICS_SHOW_PREFIX_NEVER)
939 m_maximum_length = pp_line_cutoff (pp: this);
940 else
941 {
942 int prefix_length = m_prefix ? strlen (s: m_prefix) : 0;
943 /* If the prefix is ridiculously too long, output at least
944 32 characters. */
945 if (pp_line_cutoff (pp: this) - prefix_length < 32)
946 m_maximum_length = pp_line_cutoff (pp: this) + 32;
947 else
948 m_maximum_length = pp_line_cutoff (pp: this);
949 }
950}
951
952/* Clear this pretty_printer's output state. */
953inline void
954pretty_printer::clear_state ()
955{
956 m_emitted_prefix = false;
957 pp_indentation (pp: this) = 0;
958}
959
960/* Print X to PP in decimal. */
961template<unsigned int N, typename T>
962void
963pp_wide_integer (pretty_printer *pp, const poly_int<N, T> &x)
964{
965 if (x.is_constant ())
966 pp_wide_integer (pp, x.coeffs[0]);
967 else
968 {
969 pp_left_bracket (pp);
970 for (unsigned int i = 0; i < N; ++i)
971 {
972 if (i != 0)
973 pp_comma (pp);
974 pp_wide_integer (pp, x.coeffs[i]);
975 }
976 pp_right_bracket (pp);
977 }
978}
979
980template void pp_wide_integer (pretty_printer *, const poly_uint16 &);
981template void pp_wide_integer (pretty_printer *, const poly_int64 &);
982template void pp_wide_integer (pretty_printer *, const poly_uint64 &);
983
984/* Flush the formatted text of PRETTY-PRINTER onto the attached stream. */
985void
986pp_write_text_to_stream (pretty_printer *pp)
987{
988 const char *text = pp_formatted_text (pp);
989#ifdef __MINGW32__
990 mingw_ansi_fputs (text, pp_buffer (pp)->m_stream);
991#else
992 fputs (s: text, stream: pp_buffer (pp)->m_stream);
993#endif
994 pp_clear_output_area (pp);
995}
996
997/* As pp_write_text_to_stream, but for GraphViz label output.
998
999 Flush the formatted text of pretty-printer PP onto the attached stream.
1000 Replace characters in PPF that have special meaning in a GraphViz .dot
1001 file.
1002
1003 This routine is not very fast, but it doesn't have to be as this is only
1004 be used by routines dumping intermediate representations in graph form. */
1005
1006void
1007pp_write_text_as_dot_label_to_stream (pretty_printer *pp, bool for_record)
1008{
1009 const char *text = pp_formatted_text (pp);
1010 const char *p = text;
1011 FILE *fp = pp_buffer (pp)->m_stream;
1012
1013 for (;*p; p++)
1014 {
1015 bool escape_char;
1016 switch (*p)
1017 {
1018 /* Print newlines as a left-aligned newline. */
1019 case '\n':
1020 fputs (s: "\\l", stream: fp);
1021 escape_char = true;
1022 break;
1023
1024 /* The following characters are only special for record-shape nodes. */
1025 case '|':
1026 case '{':
1027 case '}':
1028 case '<':
1029 case '>':
1030 case ' ':
1031 escape_char = for_record;
1032 break;
1033
1034 /* The following characters always have to be escaped
1035 for use in labels. */
1036 case '\\':
1037 /* There is a bug in some (f.i. 2.36.0) versions of graphiz
1038 ( http://www.graphviz.org/mantisbt/view.php?id=2524 ) related to
1039 backslash as last char in label. Let's avoid triggering it. */
1040 gcc_assert (*(p + 1) != '\0');
1041 /* Fall through. */
1042 case '"':
1043 escape_char = true;
1044 break;
1045
1046 default:
1047 escape_char = false;
1048 break;
1049 }
1050
1051 if (escape_char)
1052 fputc (c: '\\', stream: fp);
1053
1054 fputc (c: *p, stream: fp);
1055 }
1056
1057 pp_clear_output_area (pp);
1058}
1059
1060/* As pp_write_text_to_stream, but for GraphViz HTML-like strings.
1061
1062 Flush the formatted text of pretty-printer PP onto the attached stream,
1063 escaping these characters
1064 " & < >
1065 using XML escape sequences.
1066
1067 http://www.graphviz.org/doc/info/lang.html#html states:
1068 special XML escape sequences for ", &, <, and > may be necessary in
1069 order to embed these characters in attribute values or raw text
1070 This doesn't list "'" (which would normally be escaped in XML
1071 as "&apos;" or in HTML as "&#39;");.
1072
1073 Experiments show that escaping "'" doesn't seem to be necessary. */
1074
1075void
1076pp_write_text_as_html_like_dot_to_stream (pretty_printer *pp)
1077{
1078 const char *text = pp_formatted_text (pp);
1079 const char *p = text;
1080 FILE *fp = pp_buffer (pp)->m_stream;
1081
1082 for (;*p; p++)
1083 {
1084 switch (*p)
1085 {
1086 case '"':
1087 fputs (s: "&quot;", stream: fp);
1088 break;
1089 case '&':
1090 fputs (s: "&amp;", stream: fp);
1091 break;
1092 case '<':
1093 fputs (s: "&lt;", stream: fp);
1094 break;
1095 case '>':
1096 fputs (s: "&gt;",stream: fp);
1097 break;
1098
1099 default:
1100 fputc (c: *p, stream: fp);
1101 break;
1102 }
1103 }
1104
1105 pp_clear_output_area (pp);
1106}
1107
1108/* Wrap a text delimited by START and END into PRETTY-PRINTER. */
1109static void
1110pp_wrap_text (pretty_printer *pp, const char *start, const char *end)
1111{
1112 bool wrapping_line = pp_is_wrapping_line (pp);
1113
1114 while (start != end)
1115 {
1116 /* Dump anything bordered by whitespaces. */
1117 {
1118 const char *p = start;
1119 while (p != end && !ISBLANK (*p) && *p != '\n')
1120 ++p;
1121 if (wrapping_line
1122 && p - start >= pp->remaining_character_count_for_line ())
1123 pp_newline (pp);
1124 pp_append_text (pp, start, p);
1125 start = p;
1126 }
1127
1128 if (start != end && ISBLANK (*start))
1129 {
1130 pp_space (pp);
1131 ++start;
1132 }
1133 if (start != end && *start == '\n')
1134 {
1135 pp_newline (pp);
1136 ++start;
1137 }
1138 }
1139}
1140
1141/* Same as pp_wrap_text but wrap text only when in line-wrapping mode. */
1142static inline void
1143pp_maybe_wrap_text (pretty_printer *pp, const char *start, const char *end)
1144{
1145 if (pp_is_wrapping_line (pp))
1146 pp_wrap_text (pp, start, end);
1147 else
1148 pp_append_text (pp, start, end);
1149}
1150
1151/* Append to the output area of PRETTY-PRINTER a string specified by its
1152 STARTing character and LENGTH. */
1153static inline void
1154pp_append_r (pretty_printer *pp, const char *start, int length)
1155{
1156 output_buffer_append_r (buff: pp_buffer (pp), start, length);
1157}
1158
1159/* Insert enough spaces into the output area of PRETTY-PRINTER to bring
1160 the column position to the current indentation level, assuming that a
1161 newline has just been written to the buffer. */
1162void
1163pp_indent (pretty_printer *pp)
1164{
1165 int n = pp_indentation (pp);
1166 int i;
1167
1168 for (i = 0; i < n; ++i)
1169 pp_space (pp);
1170}
1171
1172static const char *get_end_url_string (pretty_printer *);
1173
1174/* struct pp_token. */
1175
1176pp_token::pp_token (enum kind k)
1177: m_kind (k),
1178 m_prev (nullptr),
1179 m_next (nullptr)
1180{
1181}
1182
1183void
1184pp_token::dump (FILE *out) const
1185{
1186 switch (m_kind)
1187 {
1188 default:
1189 gcc_unreachable ();
1190 case kind::text:
1191 {
1192 const pp_token_text *sub = as_a <const pp_token_text *> (p: this);
1193 gcc_assert (sub->m_value.get ());
1194 fprintf (stream: out, format: "TEXT(\"%s\")", sub->m_value.get ());
1195 }
1196 break;
1197 case kind::begin_color:
1198 {
1199 const pp_token_begin_color *sub
1200 = as_a <const pp_token_begin_color *> (p: this);
1201 gcc_assert (sub->m_value.get ());
1202 fprintf (stream: out, format: "BEGIN_COLOR(\"%s\")", sub->m_value.get ());
1203 break;
1204 }
1205 case kind::end_color:
1206 fprintf (stream: out, format: "END_COLOR");
1207 break;
1208 case kind::begin_quote:
1209 fprintf (stream: out, format: "BEGIN_QUOTE");
1210 break;
1211 case kind::end_quote:
1212 fprintf (stream: out, format: "END_QUOTE");
1213 break;
1214 case kind::begin_url:
1215 {
1216 const pp_token_begin_url *sub
1217 = as_a <const pp_token_begin_url *> (p: this);
1218 gcc_assert (sub->m_value.get ());
1219 fprintf (stream: out, format: "BEGIN_URL(\"%s\")", sub->m_value.get ());
1220 }
1221 break;
1222 case kind::end_url:
1223 fprintf (stream: out, format: "END_URL");
1224 break;
1225
1226 case kind::event_id:
1227 {
1228 const pp_token_event_id *sub
1229 = as_a <const pp_token_event_id *> (p: this);
1230 gcc_assert (sub->m_event_id.known_p ());
1231 fprintf (stream: out, format: "EVENT((%i))", sub->m_event_id.one_based ());
1232 }
1233 break;
1234
1235 case kind::custom_data:
1236 {
1237 const pp_token_custom_data *sub
1238 = as_a <const pp_token_custom_data *> (p: this);
1239 gcc_assert (sub->m_value.get ());
1240 fprintf (stream: out, format: "CUSTOM(");
1241 sub->m_value->dump (out);
1242 fprintf (stream: out, format: ")");
1243 }
1244 break;
1245 }
1246}
1247
1248/* Allocate SZ bytes within S, which must not be half-way through
1249 building another object. */
1250
1251static void *
1252allocate_object (size_t sz, obstack &s)
1253{
1254 /* We must not be half-way through an object. */
1255 gcc_assert (obstack_base (&s) == obstack_next_free (&s));
1256
1257 obstack_blank (&s, sz);
1258 void *buf = obstack_finish (&s);
1259 return buf;
1260}
1261
1262/* Make room for a pp_token instance within obstack S. */
1263
1264void *
1265pp_token::operator new (size_t sz, obstack &s)
1266{
1267 return allocate_object (sz, s);
1268}
1269
1270void
1271pp_token::operator delete (void *)
1272{
1273 /* No-op: pp_tokens are allocated within obstacks, so
1274 the memory will be reclaimed when the obstack is freed. */
1275}
1276
1277/* class pp_token_list. */
1278
1279/* Make room for a pp_token_list instance within obstack S. */
1280
1281void *
1282pp_token_list::operator new (size_t sz, obstack &s)
1283{
1284 return allocate_object (sz, s);
1285}
1286
1287void
1288pp_token_list::operator delete (void *)
1289{
1290 /* No-op: pp_token_list allocated within obstacks don't
1291 need their own reclaim the memory will be reclaimed when
1292 the obstack is freed. */
1293}
1294
1295pp_token_list::pp_token_list (obstack &s)
1296: m_obstack (s),
1297 m_first (nullptr),
1298 m_end (nullptr)
1299{
1300}
1301
1302pp_token_list::pp_token_list (pp_token_list &&other)
1303: m_obstack (other.m_obstack),
1304 m_first (other.m_first),
1305 m_end (other.m_end)
1306{
1307 other.m_first = nullptr;
1308 other.m_end = nullptr;
1309}
1310
1311pp_token_list::~pp_token_list ()
1312{
1313 for (auto iter = m_first; iter; )
1314 {
1315 pp_token *next = iter->m_next;
1316 delete iter;
1317 iter = next;
1318 }
1319}
1320
1321void
1322pp_token_list::push_back_text (label_text &&text)
1323{
1324 if (text.get ()[0] == '\0')
1325 return; // pushing empty string is a no-op
1326 push_back<pp_token_text> (args: std::move (text));
1327}
1328
1329void
1330pp_token_list::push_back (std::unique_ptr<pp_token> tok)
1331{
1332 if (!m_first)
1333 {
1334 gcc_assert (m_end == nullptr);
1335 m_first = tok.get ();
1336 m_end = tok.get ();
1337 }
1338 else
1339 {
1340 gcc_assert (m_end != nullptr);
1341 m_end->m_next = tok.get ();
1342 tok->m_prev = m_end;
1343 m_end = tok.get ();
1344 }
1345 tok.release ();
1346}
1347
1348void
1349pp_token_list::push_back_list (pp_token_list &&list)
1350{
1351 while (auto tok = list.pop_front ())
1352 push_back (tok: std::move (tok));
1353}
1354
1355std::unique_ptr<pp_token>
1356pp_token_list::pop_front ()
1357{
1358 pp_token *result = m_first;
1359 if (result == nullptr)
1360 return nullptr;
1361
1362 gcc_assert (result->m_prev == nullptr);
1363 m_first = result->m_next;
1364 if (result->m_next)
1365 {
1366 gcc_assert (result != m_end);
1367 m_first->m_prev = nullptr;
1368 }
1369 else
1370 {
1371 gcc_assert (result == m_end);
1372 m_end = nullptr;
1373 }
1374 result->m_next = nullptr;
1375 return std::unique_ptr<pp_token> (result);
1376}
1377
1378std::unique_ptr<pp_token>
1379pp_token_list::remove_token (pp_token *tok)
1380{
1381 gcc_assert (tok);
1382 if (tok->m_prev)
1383 {
1384 gcc_assert (tok != m_first);
1385 tok->m_prev->m_next = tok->m_next;
1386 }
1387 else
1388 {
1389 gcc_assert (tok == m_first);
1390 m_first = tok->m_next;
1391 }
1392 if (tok->m_next)
1393 {
1394 gcc_assert (tok != m_end);
1395 tok->m_next->m_prev = tok->m_prev;
1396 }
1397 else
1398 {
1399 gcc_assert (tok == m_end);
1400 m_end = tok->m_prev;
1401 }
1402 tok->m_prev = nullptr;
1403 tok->m_next = nullptr;
1404 gcc_assert (m_first != tok);
1405 gcc_assert (m_end != tok);
1406 return std::unique_ptr<pp_token> (tok);
1407}
1408
1409/* Insert NEW_TOK after RELATIVE_TOK. */
1410
1411void
1412pp_token_list::insert_after (std::unique_ptr<pp_token> new_tok_up,
1413 pp_token *relative_tok)
1414{
1415 pp_token *new_tok = new_tok_up.release ();
1416
1417 gcc_assert (new_tok);
1418 gcc_assert (new_tok->m_prev == nullptr);
1419 gcc_assert (new_tok->m_next == nullptr);
1420 gcc_assert (relative_tok);
1421
1422 if (relative_tok->m_next)
1423 {
1424 gcc_assert (relative_tok != m_end);
1425 relative_tok->m_next->m_prev = new_tok;
1426 }
1427 else
1428 {
1429 gcc_assert (relative_tok == m_end);
1430 m_end = new_tok;
1431 }
1432 new_tok->m_prev = relative_tok;
1433 new_tok->m_next = relative_tok->m_next;
1434 relative_tok->m_next = new_tok;
1435}
1436
1437void
1438pp_token_list::replace_custom_tokens ()
1439{
1440 pp_token *iter = m_first;
1441 while (iter)
1442 {
1443 pp_token *next = iter->m_next;
1444 if (iter->m_kind == pp_token::kind::custom_data)
1445 {
1446 pp_token_list tok_list (m_obstack);
1447 pp_token_custom_data *sub = as_a <pp_token_custom_data *> (p: iter);
1448 if (sub->m_value->as_standard_tokens (out&: tok_list))
1449 {
1450 while (auto tok = tok_list.pop_front ())
1451 {
1452 /* The resulting token list must not contain any
1453 custom data. */
1454 gcc_assert (tok->m_kind != pp_token::kind::custom_data);
1455 insert_after (new_tok_up: std::move (tok), relative_tok: iter);
1456 }
1457 remove_token (tok: iter);
1458 }
1459 }
1460 iter = next;
1461 }
1462}
1463
1464/* Merge any runs of consecutive text tokens within this list
1465 into individual text tokens. */
1466
1467void
1468pp_token_list::merge_consecutive_text_tokens ()
1469{
1470 pp_token *start_of_run = m_first;
1471 while (start_of_run)
1472 {
1473 if (start_of_run->m_kind != pp_token::kind::text)
1474 {
1475 start_of_run = start_of_run->m_next;
1476 continue;
1477 }
1478 pp_token *end_of_run = start_of_run;
1479 while (end_of_run->m_next
1480 && end_of_run->m_next->m_kind == pp_token::kind::text)
1481 end_of_run = end_of_run->m_next;
1482 if (end_of_run != start_of_run)
1483 {
1484 /* start_of_run through end_of_run are a run of consecutive
1485 text tokens. */
1486
1487 /* Calculate size of buffer for merged text. */
1488 size_t sz = 0;
1489 for (auto iter = start_of_run; iter != end_of_run->m_next;
1490 iter = iter->m_next)
1491 {
1492 pp_token_text *iter_text = static_cast<pp_token_text *> (iter);
1493 sz += strlen (s: iter_text->m_value.get ());
1494 }
1495
1496 /* Allocate and populate buffer for merged text
1497 (within m_obstack). */
1498 char * const buf = (char *)allocate_object (sz: sz + 1, s&: m_obstack);
1499 char *p = buf;
1500 for (auto iter = start_of_run; iter != end_of_run->m_next;
1501 iter = iter->m_next)
1502 {
1503 pp_token_text *iter_text = static_cast<pp_token_text *> (iter);
1504 size_t iter_sz = strlen (s: iter_text->m_value.get ());
1505 memcpy (dest: p, src: iter_text->m_value.get (), n: iter_sz);
1506 p += iter_sz;
1507 }
1508 *p = '\0';
1509
1510 /* Replace start_of_run's buffer pointer with the new buffer. */
1511 static_cast<pp_token_text *> (start_of_run)->m_value
1512 = label_text::borrow (buffer: buf);
1513
1514 /* Remove all the other text tokens in the run. */
1515 pp_token * const next = end_of_run->m_next;
1516 while (start_of_run->m_next != next)
1517 remove_token (tok: start_of_run->m_next);
1518 start_of_run = next;
1519 }
1520 else
1521 start_of_run = end_of_run->m_next;
1522 }
1523}
1524
1525/* Apply URLIFIER to this token list.
1526 Find BEGIN_QUOTE, TEXT, END_QUOTE triples, and if URLIFIER has a url
1527 for the value of TEXT, then wrap TEXT in a {BEGIN,END}_URL pair. */
1528
1529void
1530pp_token_list::apply_urlifier (const urlifier &urlifier)
1531{
1532 for (pp_token *iter = m_first; iter; )
1533 {
1534 if (iter->m_kind == pp_token::kind::begin_quote
1535 && iter->m_next
1536 && iter->m_next->m_kind == pp_token::kind::text
1537 && iter->m_next->m_next
1538 && iter->m_next->m_next->m_kind == pp_token::kind::end_quote)
1539 {
1540 pp_token *begin_quote = iter;
1541 pp_token_text *text = as_a <pp_token_text *> (p: begin_quote->m_next);
1542 pp_token *end_quote = text->m_next;
1543 if (char *url = urlifier.get_url_for_quoted_text
1544 (p: text->m_value.get (),
1545 sz: strlen (s: text->m_value.get ())))
1546 {
1547 auto begin_url
1548 = make_token<pp_token_begin_url> (args: label_text::take (buffer: url));
1549 auto end_url = make_token<pp_token_end_url> ();
1550 insert_after (new_tok_up: std::move (begin_url), relative_tok: begin_quote);
1551 insert_after (new_tok_up: std::move (end_url), relative_tok: text);
1552 }
1553 iter = end_quote->m_next;
1554 }
1555 else
1556 iter = iter->m_next;
1557 }
1558}
1559
1560void
1561pp_token_list::dump (FILE *out) const
1562{
1563 for (auto iter = m_first; iter; iter = iter->m_next)
1564 {
1565 iter->dump (out);
1566 if (iter->m_next)
1567 fprintf (stream: out, format: ", ");
1568 }
1569 fprintf (stream: out, format: "]\n");
1570}
1571
1572
1573/* Adds a chunk to the end of formatted output, so that it
1574 will be printed by pp_output_formatted_text. */
1575
1576void
1577pp_formatted_chunks::append_formatted_chunk (obstack &s, const char *content)
1578{
1579 unsigned int chunk_idx;
1580 for (chunk_idx = 0; m_args[chunk_idx]; chunk_idx++)
1581 ;
1582 pp_token_list *tokens = pp_token_list::make (s);
1583 tokens->push_back_text (text: label_text::borrow (buffer: content));
1584 m_args[chunk_idx++] = tokens;
1585 m_args[chunk_idx] = nullptr;
1586}
1587
1588void
1589pp_formatted_chunks::dump (FILE *out, int indent) const
1590{
1591 for (size_t idx = 0; m_args[idx]; ++idx)
1592 {
1593 fprintf (stream: out, format: "%*s%i: ",
1594 indent, "",
1595 (int)idx);
1596 m_args[idx]->dump (out);
1597 }
1598}
1599
1600/* Finish any text accumulating within CUR_OBSTACK,
1601 terminating it.
1602 Push a text pp_token to the end of TOK_LIST containing
1603 a borrowed copy of the text in CUR_OBSTACK. */
1604
1605static void
1606push_back_any_text (pp_token_list *tok_list,
1607 obstack *cur_obstack)
1608{
1609 obstack_1grow (cur_obstack, '\0');
1610 tok_list->push_back_text
1611 (text: label_text::borrow (XOBFINISH (cur_obstack,
1612 const char *)));
1613}
1614
1615/* The following format specifiers are recognized as being client independent:
1616 %d, %i: (signed) integer in base ten.
1617 %u: unsigned integer in base ten.
1618 %o: unsigned integer in base eight.
1619 %x: unsigned integer in base sixteen.
1620 %ld, %li, %lo, %lu, %lx: long versions of the above.
1621 %lld, %lli, %llo, %llu, %llx: long long versions.
1622 %wd, %wi, %wo, %wu, %wx: HOST_WIDE_INT versions.
1623 %zd, %zi, %zo, %zu, %zx: size_t versions.
1624 %td, %ti, %to, %tu, %tx: ptrdiff_t versions.
1625 %f: double
1626 %c: character.
1627 %s: string.
1628 %p: pointer (printed in a host-dependent manner).
1629 %r: if pp_show_color(pp), switch to color identified by const char *.
1630 %R: if pp_show_color(pp), reset color.
1631 %m: strerror(text->err_no) - does not consume a value from args_ptr.
1632 %%: '%'.
1633 %<: opening quote.
1634 %>: closing quote.
1635 %{: URL start. Consumes a const char * argument for the URL.
1636 %}: URL end. Does not consume any arguments.
1637 %': apostrophe (should only be used in untranslated messages;
1638 translations should use appropriate punctuation directly).
1639 %@: diagnostic_event_id_ptr, for which event_id->known_p () must be true.
1640 %.*s: a substring the length of which is specified by an argument
1641 integer.
1642 %.Ns: likewise, but length specified as constant in the format string.
1643 Flag 'q': quote formatted text (must come immediately after '%').
1644 %Z: Requires two arguments - array of int, and len. Prints elements
1645 of the array.
1646
1647 %e: Consumes a pp_element * argument.
1648
1649 Arguments can be used sequentially, or through %N$ resp. *N$
1650 notation Nth argument after the format string. If %N$ / *N$
1651 notation is used, it must be used for all arguments, except %m, %%,
1652 %<, %>, %} and %', which may not have a number, as they do not consume
1653 an argument. When %M$.*N$s is used, M must be N + 1. (This may
1654 also be written %M$.*s, provided N is not otherwise used.) The
1655 format string must have conversion specifiers with argument numbers
1656 1 up to highest argument; each argument may only be used once.
1657 A format string can have at most 30 arguments. */
1658
1659/* Implementation of pp_format.
1660 Formatting phases 1 and 2:
1661 - push a pp_formatted_chunks instance.
1662 - render TEXT->format_spec plus text->m_args_ptr into the pp_formatted_chunks
1663 instance as pp_token_lists.
1664 Phase 3 is in pp_output_formatted_text, which pops the pp_formatted_chunks
1665 instance. */
1666
1667static void
1668format_phase_1 (const text_info &text,
1669 obstack &chunk_obstack,
1670 pp_token_list **args,
1671 pp_token_list ***formatters);
1672
1673static void
1674format_phase_2 (pretty_printer *pp,
1675 text_info &text,
1676 obstack &chunk_obstack,
1677 pp_token_list ***formatters);
1678
1679void
1680pretty_printer::format (text_info &text)
1681{
1682 pp_formatted_chunks *new_chunk_array = m_buffer->push_formatted_chunks ();
1683 pp_token_list **args = new_chunk_array->m_args;
1684
1685 pp_token_list **formatters[PP_NL_ARGMAX];
1686 memset (s: formatters, c: 0, n: sizeof formatters);
1687
1688 /* Formatting phase 1: split up TEXT->format_spec into chunks in
1689 pp_buffer (PP)->args[]. Even-numbered chunks are to be output
1690 verbatim, odd-numbered chunks are format specifiers.
1691 %m, %%, %<, %>, %} and %' are replaced with the appropriate text at
1692 this point. */
1693 format_phase_1 (text, chunk_obstack&: m_buffer->m_chunk_obstack, args, formatters);
1694
1695 /* Note that you can debug the state of the chunk arrays here using
1696 (gdb) call m_buffer->cur_chunk_array->dump()
1697 which, given e.g. "foo: %s bar: %s" might print:
1698 0: [TEXT("foo: ")]
1699 1: [TEXT("s")]
1700 2: [TEXT(" bar: ")]
1701 3: [TEXT("s")]
1702 */
1703
1704 /* Set output to the argument obstack, and switch line-wrapping and
1705 prefixing off. */
1706 m_buffer->m_obstack = &m_buffer->m_chunk_obstack;
1707 const int old_line_length = m_buffer->m_line_length;
1708 const pp_wrapping_mode_t old_wrapping_mode = pp_set_verbatim_wrapping (pp: this);
1709
1710 format_phase_2 (pp: this, text, chunk_obstack&: m_buffer->m_chunk_obstack, formatters);
1711
1712 /* If the client supplied a postprocessing object, call its "handle"
1713 hook here. */
1714 if (m_format_postprocessor)
1715 m_format_postprocessor->handle (this);
1716
1717 /* Revert to normal obstack and wrapping mode. */
1718 m_buffer->m_obstack = &m_buffer->m_formatted_obstack;
1719 m_buffer->m_line_length = old_line_length;
1720 pp_wrapping_mode (pp: this) = old_wrapping_mode;
1721 clear_state ();
1722}
1723
1724static void
1725format_phase_1 (const text_info &text,
1726 obstack &chunk_obstack,
1727 pp_token_list **args,
1728 pp_token_list ***formatters)
1729{
1730 unsigned chunk = 0;
1731 unsigned int curarg = 0;
1732 bool any_unnumbered = false, any_numbered = false;
1733 pp_token_list *cur_token_list;
1734 args[chunk++] = cur_token_list = pp_token_list::make (s&: chunk_obstack);
1735 for (const char *p = text.m_format_spec; *p; )
1736 {
1737 while (*p != '\0' && *p != '%')
1738 {
1739 obstack_1grow (&chunk_obstack, *p);
1740 p++;
1741 }
1742
1743 if (*p == '\0')
1744 break;
1745
1746 switch (*++p)
1747 {
1748 case '\0':
1749 gcc_unreachable ();
1750
1751 case '%':
1752 obstack_1grow (&chunk_obstack, '%');
1753 p++;
1754 continue;
1755
1756 case '<':
1757 {
1758 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1759 cur_token_list->push_back<pp_token_begin_quote> ();
1760 p++;
1761 continue;
1762 }
1763
1764 case '>':
1765 {
1766 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1767 cur_token_list->push_back<pp_token_end_quote> ();
1768 p++;
1769 continue;
1770 }
1771 case '\'':
1772 {
1773 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1774 cur_token_list->push_back<pp_token_end_quote> ();
1775 p++;
1776 }
1777 continue;
1778
1779 case '}':
1780 {
1781 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1782 cur_token_list->push_back<pp_token_end_url> ();
1783 p++;
1784 }
1785 continue;
1786
1787 case 'R':
1788 {
1789 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1790 cur_token_list->push_back<pp_token_end_color> ();
1791 p++;
1792 continue;
1793 }
1794
1795 case 'm':
1796 {
1797 const char *errstr = xstrerror (text.m_err_no);
1798 obstack_grow (&chunk_obstack, errstr, strlen (errstr));
1799 }
1800 p++;
1801 continue;
1802
1803 default:
1804 /* Handled in phase 2. Terminate the plain chunk here. */
1805 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1806 break;
1807 }
1808
1809 /* Start a new token list for the formatting args. */
1810 args[chunk] = cur_token_list = pp_token_list::make (s&: chunk_obstack);
1811
1812 unsigned argno;
1813 if (ISDIGIT (*p))
1814 {
1815 char *end;
1816 argno = strtoul (nptr: p, endptr: &end, base: 10) - 1;
1817 p = end;
1818 gcc_assert (*p == '$');
1819 p++;
1820
1821 any_numbered = true;
1822 gcc_assert (!any_unnumbered);
1823 }
1824 else
1825 {
1826 argno = curarg++;
1827 any_unnumbered = true;
1828 gcc_assert (!any_numbered);
1829 }
1830 gcc_assert (argno < PP_NL_ARGMAX);
1831 gcc_assert (!formatters[argno]);
1832 formatters[argno] = &args[chunk++];
1833 do
1834 {
1835 obstack_1grow (&chunk_obstack, *p);
1836 p++;
1837 }
1838 while (strchr (s: "qwlzt+#", c: p[-1]));
1839
1840 if (p[-1] == '.')
1841 {
1842 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1843 (where M == N + 1). */
1844 if (ISDIGIT (*p))
1845 {
1846 do
1847 {
1848 obstack_1grow (&chunk_obstack, *p);
1849 p++;
1850 }
1851 while (ISDIGIT (p[-1]));
1852 gcc_assert (p[-1] == 's');
1853 }
1854 else
1855 {
1856 gcc_assert (*p == '*');
1857 obstack_1grow (&chunk_obstack, '*');
1858 p++;
1859
1860 if (ISDIGIT (*p))
1861 {
1862 char *end;
1863 unsigned int argno2 = strtoul (nptr: p, endptr: &end, base: 10) - 1;
1864 p = end;
1865 gcc_assert (argno2 == argno - 1);
1866 gcc_assert (!any_unnumbered);
1867 gcc_assert (*p == '$');
1868
1869 p++;
1870 formatters[argno2] = formatters[argno];
1871 }
1872 else
1873 {
1874 gcc_assert (!any_numbered);
1875 formatters[argno+1] = formatters[argno];
1876 curarg++;
1877 }
1878 gcc_assert (*p == 's');
1879 obstack_1grow (&chunk_obstack, 's');
1880 p++;
1881 }
1882 }
1883 if (*p == '\0')
1884 {
1885 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1886 break;
1887 }
1888
1889 obstack_1grow (&chunk_obstack, '\0');
1890 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1891
1892 /* Start a new token list for the next (non-formatted) text. */
1893 gcc_assert (chunk < PP_NL_ARGMAX * 2);
1894 args[chunk++] = cur_token_list = pp_token_list::make (s&: chunk_obstack);
1895 }
1896
1897 obstack_1grow (&chunk_obstack, '\0');
1898 push_back_any_text (tok_list: cur_token_list, cur_obstack: &chunk_obstack);
1899 gcc_assert (chunk < PP_NL_ARGMAX * 2);
1900 args[chunk] = nullptr;
1901}
1902
1903/* Second phase. Replace each formatter with pp_tokens for the formatted
1904 text it corresponds to, consuming va_args from TEXT->m_args_ptr. */
1905
1906static void
1907format_phase_2 (pretty_printer *pp,
1908 text_info &text,
1909 obstack &chunk_obstack,
1910 pp_token_list ***formatters)
1911{
1912 unsigned argno;
1913 for (argno = 0; formatters[argno]; argno++)
1914 {
1915 int precision = 0;
1916 bool wide = false;
1917 bool plus = false;
1918 bool hash = false;
1919 bool quote = false;
1920
1921 /* We expect a single text token containing the formatter. */
1922 pp_token_list *tok_list = *(formatters[argno]);
1923 gcc_assert (tok_list);
1924 gcc_assert (tok_list->m_first == tok_list->m_end);
1925 gcc_assert (tok_list->m_first->m_kind == pp_token::kind::text);
1926
1927 /* Accumulate the value of the formatted text into here. */
1928 pp_token_list *formatted_tok_list
1929 = pp_token_list::make (s&: chunk_obstack);
1930
1931 /* We do not attempt to enforce any ordering on the modifier
1932 characters. */
1933
1934 const char *p;
1935 for (p = as_a <pp_token_text *> (p: tok_list->m_first)->m_value.get ();; p++)
1936 {
1937 switch (*p)
1938 {
1939 case 'q':
1940 gcc_assert (!quote);
1941 quote = true;
1942 continue;
1943
1944 case '+':
1945 gcc_assert (!plus);
1946 plus = true;
1947 continue;
1948
1949 case '#':
1950 gcc_assert (!hash);
1951 hash = true;
1952 continue;
1953
1954 case 'w':
1955 gcc_assert (!wide);
1956 wide = true;
1957 continue;
1958
1959 case 'z':
1960 gcc_assert (!precision);
1961 precision = 3;
1962 continue;
1963
1964 case 't':
1965 gcc_assert (!precision);
1966 precision = 4;
1967 continue;
1968
1969 case 'l':
1970 /* We don't support precision beyond that of "long long". */
1971 gcc_assert (precision < 2);
1972 precision++;
1973 continue;
1974 }
1975 break;
1976 }
1977
1978 gcc_assert (!wide || precision == 0);
1979
1980 if (quote)
1981 {
1982 push_back_any_text (tok_list: formatted_tok_list, cur_obstack: &chunk_obstack);
1983 formatted_tok_list->push_back<pp_token_begin_quote> ();
1984 }
1985
1986 switch (*p)
1987 {
1988 case 'r':
1989 {
1990 const char *color = va_arg (*text.m_args_ptr, const char *);
1991 formatted_tok_list->push_back<pp_token_begin_color>
1992 (args: label_text::borrow (buffer: color));
1993 }
1994 break;
1995
1996 case 'c':
1997 {
1998 /* When quoting, print alphanumeric, punctuation, and the space
1999 character unchanged, and all others in hexadecimal with the
2000 "\x" prefix. Otherwise print them all unchanged. */
2001 char chr = (char) va_arg (*text.m_args_ptr, int);
2002 if (ISPRINT (chr) || !quote)
2003 pp_character (pp, chr);
2004 else
2005 {
2006 const char str [2] = { chr, '\0' };
2007 pp_quoted_string (pp, str, 1);
2008 }
2009 break;
2010 }
2011
2012 case 'd':
2013 case 'i':
2014 if (wide)
2015 pp_wide_integer (pp, va_arg (*text.m_args_ptr, HOST_WIDE_INT));
2016 else
2017 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2018 int, "d");
2019 break;
2020
2021 case 'o':
2022 if (wide)
2023 pp_scalar (pp, "%" HOST_WIDE_INT_PRINT "o",
2024 va_arg (*text.m_args_ptr, unsigned HOST_WIDE_INT));
2025 else
2026 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2027 unsigned, "o");
2028 break;
2029
2030 case 's':
2031 if (quote)
2032 pp_quoted_string (pp, va_arg (*text.m_args_ptr, const char *));
2033 else
2034 pp_string (pp, va_arg (*text.m_args_ptr, const char *));
2035 break;
2036
2037 case 'p':
2038 pp_pointer (pp, va_arg (*text.m_args_ptr, void *));
2039 break;
2040
2041 case 'u':
2042 if (wide)
2043 pp_scalar (pp, HOST_WIDE_INT_PRINT_UNSIGNED,
2044 va_arg (*text.m_args_ptr, unsigned HOST_WIDE_INT));
2045 else
2046 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2047 unsigned, "u");
2048 break;
2049
2050 case 'f':
2051 pp_double (pp, va_arg (*text.m_args_ptr, double));
2052 break;
2053
2054 case 'Z':
2055 {
2056 int *v = va_arg (*text.m_args_ptr, int *);
2057 unsigned len = va_arg (*text.m_args_ptr, unsigned);
2058
2059 for (unsigned i = 0; i < len; ++i)
2060 {
2061 pp_scalar (pp, "%i", v[i]);
2062 if (i < len - 1)
2063 {
2064 pp_comma (pp);
2065 pp_space (pp);
2066 }
2067 }
2068 break;
2069 }
2070
2071 case 'x':
2072 if (wide)
2073 pp_scalar (pp, HOST_WIDE_INT_PRINT_HEX,
2074 va_arg (*text.m_args_ptr, unsigned HOST_WIDE_INT));
2075 else
2076 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2077 unsigned, "x");
2078 break;
2079
2080 case '.':
2081 {
2082 int n;
2083 const char *s;
2084
2085 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
2086 (where M == N + 1). The format string should be verified
2087 already from the first phase. */
2088 p++;
2089 if (ISDIGIT (*p))
2090 {
2091 char *end;
2092 n = strtoul (nptr: p, endptr: &end, base: 10);
2093 p = end;
2094 gcc_assert (*p == 's');
2095 }
2096 else
2097 {
2098 gcc_assert (*p == '*');
2099 p++;
2100 gcc_assert (*p == 's');
2101 n = va_arg (*text.m_args_ptr, int);
2102
2103 /* This consumes a second entry in the formatters array. */
2104 gcc_assert (formatters[argno] == formatters[argno+1]);
2105 argno++;
2106 }
2107
2108 s = va_arg (*text.m_args_ptr, const char *);
2109
2110 /* Append the lesser of precision and strlen (s) characters
2111 from the array (which need not be a nul-terminated string).
2112 Negative precision is treated as if it were omitted. */
2113 size_t len = n < 0 ? strlen (s: s) : strnlen (string: s, maxlen: n);
2114
2115 pp_append_text (pp, s, s + len);
2116 }
2117 break;
2118
2119 case '@':
2120 {
2121 /* diagnostic_event_id_t *. */
2122 diagnostic_event_id_ptr event_id
2123 = va_arg (*text.m_args_ptr, diagnostic_event_id_ptr);
2124 gcc_assert (event_id->known_p ());
2125 formatted_tok_list->push_back<pp_token_event_id> (args&: *event_id);
2126 }
2127 break;
2128
2129 case '{':
2130 {
2131 const char *url = va_arg (*text.m_args_ptr, const char *);
2132 formatted_tok_list->push_back<pp_token_begin_url>
2133 (args: label_text::borrow (buffer: url));
2134 }
2135 break;
2136
2137 case 'e':
2138 {
2139 pp_element *element = va_arg (*text.m_args_ptr, pp_element *);
2140 pp_markup::context ctxt (*pp,
2141 quote, /* by reference */
2142 formatted_tok_list);
2143 element->add_to_phase_2 (ctxt);
2144 }
2145 break;
2146
2147 default:
2148 {
2149 /* Call the format decoder.
2150 Pass the address of "quote" so that format decoders can
2151 potentially disable printing of the closing quote
2152 (e.g. when printing "'TYPEDEF' aka 'TYPE'" in the C family
2153 of frontends). */
2154 printer_fn format_decoder = pp_format_decoder (pp);
2155 gcc_assert (format_decoder);
2156 gcc_assert (formatted_tok_list);
2157 bool ok = format_decoder (pp, &text, p,
2158 precision, wide, plus, hash, &quote,
2159 *formatted_tok_list);
2160 gcc_assert (ok);
2161 }
2162 }
2163
2164 if (quote)
2165 {
2166 push_back_any_text (tok_list: formatted_tok_list, cur_obstack: &chunk_obstack);
2167 formatted_tok_list->push_back<pp_token_end_quote> ();
2168 }
2169
2170 push_back_any_text (tok_list: formatted_tok_list, cur_obstack: &chunk_obstack);
2171 delete *formatters[argno];
2172 *formatters[argno] = formatted_tok_list;
2173 }
2174
2175 if (CHECKING_P)
2176 for (; argno < PP_NL_ARGMAX; argno++)
2177 gcc_assert (!formatters[argno]);
2178}
2179
2180struct auto_obstack
2181{
2182 auto_obstack ()
2183 {
2184 obstack_init (&m_obstack);
2185 }
2186
2187 ~auto_obstack ()
2188 {
2189 obstack_free (&m_obstack, NULL);
2190 }
2191
2192 operator obstack & () { return m_obstack; }
2193
2194 void grow (const void *src, size_t length)
2195 {
2196 obstack_grow (&m_obstack, src, length);
2197 }
2198
2199 void *object_base () const
2200 {
2201 return m_obstack.object_base;
2202 }
2203
2204 size_t object_size () const
2205 {
2206 return obstack_object_size (&m_obstack);
2207 }
2208
2209 obstack m_obstack;
2210};
2211
2212/* Phase 3 of formatting a message (phases 1 and 2 done by pp_format).
2213
2214 Pop a pp_formatted_chunks from chunk_obstack, collecting all the tokens from
2215 phases 1 and 2 of formatting, and writing into text in formatted_obstack.
2216
2217 If URLIFIER is non-null then use it on any quoted text that was not
2218 handled in phases 1 or 2 to potentially add URLs. */
2219
2220void
2221pp_output_formatted_text (pretty_printer *pp,
2222 const urlifier *urlifier)
2223{
2224 output_buffer * const buffer = pp_buffer (pp);
2225 gcc_assert (buffer->m_obstack == &buffer->m_formatted_obstack);
2226
2227 pp_formatted_chunks *chunk_array = buffer->m_cur_formatted_chunks;
2228 pp_token_list * const *token_lists = chunk_array->get_token_lists ();
2229
2230 {
2231 /* Consolidate into one token list. */
2232 pp_token_list tokens (buffer->m_chunk_obstack);
2233 for (unsigned chunk = 0; token_lists[chunk]; chunk++)
2234 {
2235 tokens.push_back_list (list: std::move (*token_lists[chunk]));
2236 delete token_lists[chunk];
2237 }
2238
2239 tokens.replace_custom_tokens ();
2240
2241 tokens.merge_consecutive_text_tokens ();
2242
2243 if (urlifier)
2244 tokens.apply_urlifier (urlifier: *urlifier);
2245
2246 /* This is a third phase, first 2 phases done in pp_format_args.
2247 Now we actually print it. */
2248 if (pp->m_token_printer)
2249 pp->m_token_printer->print_tokens (pp, tokens);
2250 else
2251 default_token_printer (pp, tokens);
2252
2253 /* Close the scope here to ensure that "tokens" above is fully cleared up
2254 before popping the current pp_formatted_chunks, since that latter will pop
2255 the chunk_obstack, and "tokens" may be using blocks within
2256 the current pp_formatted_chunks's chunk_obstack level. */
2257 }
2258
2259 buffer->pop_formatted_chunks ();
2260}
2261
2262/* Default implementation of token printing. */
2263
2264static void
2265default_token_printer (pretty_printer *pp,
2266 const pp_token_list &tokens)
2267{
2268 /* Convert to text, possibly with colorization, URLs, etc. */
2269 for (auto iter = tokens.m_first; iter; iter = iter->m_next)
2270 switch (iter->m_kind)
2271 {
2272 default:
2273 gcc_unreachable ();
2274
2275 case pp_token::kind::text:
2276 {
2277 pp_token_text *sub = as_a <pp_token_text *> (p: iter);
2278 pp_string (pp, sub->m_value.get ());
2279 }
2280 break;
2281
2282 case pp_token::kind::begin_color:
2283 {
2284 pp_token_begin_color *sub = as_a <pp_token_begin_color *> (p: iter);
2285 pp_string (pp, colorize_start (show_color: pp_show_color (pp),
2286 name: sub->m_value.get ()));
2287 }
2288 break;
2289 case pp_token::kind::end_color:
2290 pp_string (pp, colorize_stop (pp_show_color (pp)));
2291 break;
2292
2293 case pp_token::kind::begin_quote:
2294 pp_begin_quote (pp, pp_show_color (pp));
2295 break;
2296 case pp_token::kind::end_quote:
2297 pp_end_quote (pp, pp_show_color (pp));
2298 break;
2299
2300 case pp_token::kind::begin_url:
2301 {
2302 pp_token_begin_url *sub = as_a <pp_token_begin_url *> (p: iter);
2303 pp_begin_url (pp, url: sub->m_value.get ());
2304 }
2305 break;
2306 case pp_token::kind::end_url:
2307 pp_end_url (pp);
2308 break;
2309
2310 case pp_token::kind::event_id:
2311 {
2312 pp_token_event_id *sub = as_a <pp_token_event_id *> (p: iter);
2313 gcc_assert (sub->m_event_id.known_p ());
2314 pp_string (pp, colorize_start (show_color: pp_show_color (pp), name: "path"));
2315 pp_character (pp, '(');
2316 pp_decimal_int (pp, sub->m_event_id.one_based ());
2317 pp_character (pp, ')');
2318 pp_string (pp, colorize_stop (pp_show_color (pp)));
2319 }
2320 break;
2321
2322 case pp_token::kind::custom_data:
2323 /* These should have been eliminated by replace_custom_tokens. */
2324 gcc_unreachable ();
2325 break;
2326 }
2327}
2328
2329/* Helper subroutine of output_verbatim and verbatim. Do the appropriate
2330 settings needed by BUFFER for a verbatim formatting. */
2331void
2332pp_format_verbatim (pretty_printer *pp, text_info *text)
2333{
2334 /* Set verbatim mode. */
2335 pp_wrapping_mode_t oldmode = pp_set_verbatim_wrapping (pp);
2336
2337 /* Do the actual formatting. */
2338 pp_format (pp, text);
2339 pp_output_formatted_text (pp);
2340
2341 /* Restore previous settings. */
2342 pp_wrapping_mode (pp) = oldmode;
2343}
2344
2345/* Flush the content of BUFFER onto the attached stream. This
2346 function does nothing unless pp->output_buffer->flush_p. */
2347void
2348pp_flush (pretty_printer *pp)
2349{
2350 pp->clear_state ();
2351 if (!pp_buffer (pp)->m_flush_p)
2352 return;
2353 pp_write_text_to_stream (pp);
2354 fflush (stream: pp_buffer (pp)->m_stream);
2355}
2356
2357/* Flush the content of BUFFER onto the attached stream independently
2358 of the value of pp->output_buffer->flush_p. */
2359void
2360pp_really_flush (pretty_printer *pp)
2361{
2362 pp->clear_state ();
2363 pp_write_text_to_stream (pp);
2364 fflush (stream: pp_buffer (pp)->m_stream);
2365}
2366
2367/* Sets the number of maximum characters per line PRETTY-PRINTER can
2368 output in line-wrapping mode. A LENGTH value 0 suppresses
2369 line-wrapping. */
2370void
2371pp_set_line_maximum_length (pretty_printer *pp, int length)
2372{
2373 pp_line_cutoff (pp) = length;
2374 pp->set_real_maximum_length ();
2375}
2376
2377/* Clear PRETTY-PRINTER output area text info. */
2378void
2379pp_clear_output_area (pretty_printer *pp)
2380{
2381 obstack_free (pp_buffer (pp)->m_obstack,
2382 obstack_base (pp_buffer (pp)->m_obstack));
2383 pp_buffer (pp)->m_line_length = 0;
2384}
2385
2386/* Set PREFIX for PRETTY-PRINTER, taking ownership of PREFIX, which
2387 will eventually be free-ed. */
2388
2389void
2390pretty_printer::set_prefix (char *prefix)
2391{
2392 free (ptr: m_prefix);
2393 m_prefix = prefix;
2394 set_real_maximum_length ();
2395 m_emitted_prefix = false;
2396 pp_indentation (pp: this) = 0;
2397}
2398
2399/* Take ownership of PP's prefix, setting it to NULL.
2400 This allows clients to save, override, and then restore an existing
2401 prefix, without it being free-ed. */
2402
2403char *
2404pp_take_prefix (pretty_printer *pp)
2405{
2406 char *result = pp->m_prefix;
2407 pp->m_prefix = nullptr;
2408 return result;
2409}
2410
2411/* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */
2412void
2413pp_destroy_prefix (pretty_printer *pp)
2414{
2415 if (pp->m_prefix)
2416 {
2417 free (ptr: pp->m_prefix);
2418 pp->m_prefix = nullptr;
2419 }
2420}
2421
2422/* Write out this pretty_printer's prefix. */
2423void
2424pretty_printer::emit_prefix ()
2425{
2426 if (m_prefix)
2427 {
2428 switch (pp_prefixing_rule (pp: this))
2429 {
2430 default:
2431 case DIAGNOSTICS_SHOW_PREFIX_NEVER:
2432 break;
2433
2434 case DIAGNOSTICS_SHOW_PREFIX_ONCE:
2435 if (m_emitted_prefix)
2436 {
2437 pp_indent (pp: this);
2438 break;
2439 }
2440 pp_indentation (pp: this) += 3;
2441 /* Fall through. */
2442
2443 case DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE:
2444 {
2445 int prefix_length = strlen (s: m_prefix);
2446 pp_append_r (pp: this, start: m_prefix, length: prefix_length);
2447 m_emitted_prefix = true;
2448 }
2449 break;
2450 }
2451 }
2452}
2453
2454/* Construct a PRETTY-PRINTER of MAXIMUM_LENGTH characters per line. */
2455
2456pretty_printer::pretty_printer (int maximum_length)
2457 : m_buffer (new (XCNEW (output_buffer)) output_buffer ()),
2458 m_prefix (nullptr),
2459 m_padding (pp_none),
2460 m_maximum_length (0),
2461 m_indent_skip (0),
2462 m_wrapping (),
2463 m_format_decoder (nullptr),
2464 m_format_postprocessor (nullptr),
2465 m_token_printer (nullptr),
2466 m_emitted_prefix (false),
2467 m_need_newline (false),
2468 m_translate_identifiers (true),
2469 m_show_color (false),
2470 m_show_highlight_colors (false),
2471 m_url_format (URL_FORMAT_NONE),
2472 m_skipping_null_url (false)
2473{
2474 pp_line_cutoff (pp: this) = maximum_length;
2475 /* By default, we emit prefixes once per message. */
2476 pp_prefixing_rule (pp: this) = DIAGNOSTICS_SHOW_PREFIX_ONCE;
2477 pp_set_prefix (pp: this, NULL);
2478}
2479
2480/* Copy constructor for pretty_printer. */
2481
2482pretty_printer::pretty_printer (const pretty_printer &other)
2483: m_buffer (new (XCNEW (output_buffer)) output_buffer ()),
2484 m_prefix (nullptr),
2485 m_padding (other.m_padding),
2486 m_maximum_length (other.m_maximum_length),
2487 m_indent_skip (other.m_indent_skip),
2488 m_wrapping (other.m_wrapping),
2489 m_format_decoder (other.m_format_decoder),
2490 m_format_postprocessor (nullptr),
2491 m_token_printer (other.m_token_printer),
2492 m_emitted_prefix (other.m_emitted_prefix),
2493 m_need_newline (other.m_need_newline),
2494 m_translate_identifiers (other.m_translate_identifiers),
2495 m_show_color (other.m_show_color),
2496 m_show_highlight_colors (other.m_show_highlight_colors),
2497 m_url_format (other.m_url_format),
2498 m_skipping_null_url (false)
2499{
2500 pp_line_cutoff (pp: this) = m_maximum_length;
2501 /* By default, we emit prefixes once per message. */
2502 pp_prefixing_rule (pp: this) = pp_prefixing_rule (pp: &other);
2503 pp_set_prefix (pp: this, NULL);
2504
2505 if (other.m_format_postprocessor)
2506 m_format_postprocessor = other.m_format_postprocessor->clone ();
2507}
2508
2509pretty_printer::~pretty_printer ()
2510{
2511 m_buffer->~output_buffer ();
2512 XDELETE (m_buffer);
2513 free (ptr: m_prefix);
2514}
2515
2516/* Base class implementation of pretty_printer::clone vfunc. */
2517
2518std::unique_ptr<pretty_printer>
2519pretty_printer::clone () const
2520{
2521 return std::make_unique<pretty_printer> (args: *this);
2522}
2523
2524/* Append a string delimited by START and END to the output area of
2525 PRETTY-PRINTER. No line wrapping is done. However, if beginning a
2526 new line then emit PRETTY-PRINTER's prefix and skip any leading
2527 whitespace if appropriate. The caller must ensure that it is
2528 safe to do so. */
2529void
2530pp_append_text (pretty_printer *pp, const char *start, const char *end)
2531{
2532 /* Emit prefix and skip whitespace if we're starting a new line. */
2533 if (pp_buffer (pp)->m_line_length == 0)
2534 {
2535 pp->emit_prefix ();
2536 if (pp_is_wrapping_line (pp))
2537 while (start != end && *start == ' ')
2538 ++start;
2539 }
2540 pp_append_r (pp, start, length: end - start);
2541}
2542
2543/* Finishes constructing a NULL-terminated character string representing
2544 the PRETTY-PRINTED text. */
2545const char *
2546pp_formatted_text (pretty_printer *pp)
2547{
2548 return output_buffer_formatted_text (buff: pp_buffer (pp));
2549}
2550
2551/* Return a pointer to the last character emitted in PRETTY-PRINTER's
2552 output area. A NULL pointer means no character available. */
2553const char *
2554pp_last_position_in_text (const pretty_printer *pp)
2555{
2556 return output_buffer_last_position_in_text (buff: pp_buffer (pp));
2557}
2558
2559/* Return the amount of characters PRETTY-PRINTER can accept to
2560 make a full line. Meaningful only in line-wrapping mode. */
2561int
2562pretty_printer::remaining_character_count_for_line ()
2563{
2564 return m_maximum_length - pp_buffer (pp: this)->m_line_length;
2565}
2566
2567/* Format a message into BUFFER a la printf. */
2568void
2569pp_printf (pretty_printer *pp, const char *msg, ...)
2570{
2571 va_list ap;
2572
2573 va_start (ap, msg);
2574 text_info text (msg, &ap, errno);
2575 pp_format (pp, text: &text);
2576 pp_output_formatted_text (pp);
2577 va_end (ap);
2578}
2579
2580/* Format a message into PP using ngettext to handle
2581 singular vs plural. */
2582
2583void
2584pp_printf_n (pretty_printer *pp,
2585 unsigned HOST_WIDE_INT n,
2586 const char *singular_gmsgid, const char *plural_gmsgid, ...)
2587{
2588 va_list ap;
2589
2590 va_start (ap, plural_gmsgid);
2591
2592 unsigned long gtn;
2593 if (sizeof n <= sizeof gtn)
2594 gtn = n;
2595 else
2596 /* Use the largest number ngettext can handle, otherwise
2597 preserve the six least significant decimal digits for
2598 languages where the plural form depends on them. */
2599 gtn = n <= ULONG_MAX ? n : n % 1000000LU + 1000000LU;
2600 const char *msg = ngettext (msgid1: singular_gmsgid, msgid2: plural_gmsgid, n: gtn);
2601 text_info text (msg, &ap, errno);
2602 pp_format (pp, text: &text);
2603 pp_output_formatted_text (pp);
2604 va_end (ap);
2605}
2606
2607/* Output MESSAGE verbatim into BUFFER. */
2608void
2609pp_verbatim (pretty_printer *pp, const char *msg, ...)
2610{
2611 va_list ap;
2612
2613 va_start (ap, msg);
2614 text_info text (msg, &ap, errno);
2615 pp_format_verbatim (pp, text: &text);
2616 va_end (ap);
2617}
2618
2619
2620
2621/* Have PRETTY-PRINTER start a new line. */
2622void
2623pp_newline (pretty_printer *pp)
2624{
2625 obstack_1grow (pp_buffer (pp)->m_obstack, '\n');
2626 pp_needs_newline (pp) = false;
2627 pp_buffer (pp)->m_line_length = 0;
2628}
2629
2630/* Have PRETTY-PRINTER add a CHARACTER. */
2631void
2632pp_character (pretty_printer *pp, int c)
2633{
2634 if (pp_is_wrapping_line (pp)
2635 /* If printing UTF-8, don't wrap in the middle of a sequence. */
2636 && (((unsigned int) c) & 0xC0) != 0x80
2637 && pp->remaining_character_count_for_line () <= 0)
2638 {
2639 pp_newline (pp);
2640 if (ISSPACE (c))
2641 return;
2642 }
2643 obstack_1grow (pp_buffer (pp)->m_obstack, c);
2644 ++pp_buffer (pp)->m_line_length;
2645}
2646
2647/* Append a STRING to the output area of PRETTY-PRINTER; the STRING may
2648 be line-wrapped if in appropriate mode. */
2649void
2650pp_string (pretty_printer *pp, const char *str)
2651{
2652 gcc_checking_assert (str);
2653 pp_maybe_wrap_text (pp, start: str, end: str + strlen (s: str));
2654}
2655
2656/* As per pp_string, but only append the first LEN of STR. */
2657
2658void
2659pp_string_n (pretty_printer *pp, const char *str, size_t len)
2660{
2661 gcc_checking_assert (str);
2662 pp_maybe_wrap_text (pp, start: str, end: str + len);
2663}
2664
2665/* Append code point C to the output area of PRETTY-PRINTER, encoding it
2666 as UTF-8. */
2667
2668void
2669pp_unicode_character (pretty_printer *pp, unsigned c)
2670{
2671 static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
2672 static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
2673 size_t nbytes;
2674 uchar buf[6], *p = &buf[6];
2675
2676 nbytes = 1;
2677 if (c < 0x80)
2678 *--p = c;
2679 else
2680 {
2681 do
2682 {
2683 *--p = ((c & 0x3F) | 0x80);
2684 c >>= 6;
2685 nbytes++;
2686 }
2687 while (c >= 0x3F || (c & limits[nbytes-1]));
2688 *--p = (c | masks[nbytes-1]);
2689 }
2690
2691 pp_append_r (pp, start: (const char *)p, length: nbytes);
2692}
2693
2694/* Append the leading N characters of STRING to the output area of
2695 PRETTY-PRINTER, quoting in hexadecimal non-printable characters.
2696 Setting N = -1 is as if N were set to strlen (STRING). The STRING
2697 may be line-wrapped if in appropriate mode. */
2698static void
2699pp_quoted_string (pretty_printer *pp, const char *str, size_t n /* = -1 */)
2700{
2701 gcc_checking_assert (str);
2702
2703 const char *last = str;
2704 const char *ps;
2705
2706 /* Compute the length if not specified. */
2707 if (n == (size_t) -1)
2708 n = strlen (s: str);
2709
2710 for (ps = str; n; ++ps, --n)
2711 {
2712 if (ISPRINT (*ps))
2713 continue;
2714
2715 /* Don't escape a valid UTF-8 extended char. */
2716 const unsigned char *ups = (const unsigned char *) ps;
2717 if (*ups & 0x80)
2718 {
2719 unsigned int extended_char;
2720 const int valid_utf8_len = decode_utf8_char (ups, len: n, &extended_char);
2721 if (valid_utf8_len > 0)
2722 {
2723 ps += valid_utf8_len - 1;
2724 n -= valid_utf8_len - 1;
2725 continue;
2726 }
2727 }
2728
2729 if (last < ps)
2730 pp_maybe_wrap_text (pp, start: last, end: ps);
2731
2732 /* Append the hexadecimal value of the character. Allocate a buffer
2733 that's large enough for a 32-bit char plus the hex prefix. */
2734 char buf [11];
2735 int n = sprintf (s: buf, format: "\\x%02x", (unsigned char)*ps);
2736 pp_maybe_wrap_text (pp, start: buf, end: buf + n);
2737 last = ps + 1;
2738 }
2739
2740 pp_maybe_wrap_text (pp, start: last, end: ps);
2741}
2742
2743/* Maybe print out a whitespace if needed. */
2744
2745void
2746pretty_printer::maybe_space ()
2747{
2748 if (m_padding != pp_none)
2749 {
2750 pp_space (this);
2751 m_padding = pp_none;
2752 }
2753}
2754
2755// Add a newline to the pretty printer PP and flush formatted text.
2756
2757void
2758pp_newline_and_flush (pretty_printer *pp)
2759{
2760 pp_newline (pp);
2761 pp_flush (pp);
2762 pp_needs_newline (pp) = false;
2763}
2764
2765// Add a newline to the pretty printer PP, followed by indentation.
2766
2767void
2768pp_newline_and_indent (pretty_printer *pp, int n)
2769{
2770 pp_indentation (pp) += n;
2771 pp_newline (pp);
2772 pp_indent (pp);
2773 pp_needs_newline (pp) = false;
2774}
2775
2776// Add separator C, followed by a single whitespace.
2777
2778void
2779pp_separate_with (pretty_printer *pp, char c)
2780{
2781 pp_character (pp, c);
2782 pp_space (pp);
2783}
2784
2785/* Add a localized open quote, and if SHOW_COLOR is true, begin colorizing
2786 using the "quote" color. */
2787
2788void
2789pp_begin_quote (pretty_printer *pp, bool show_color)
2790{
2791 pp_string (pp, str: open_quote);
2792 pp_string (pp, str: colorize_start (show_color, name: "quote"));
2793}
2794
2795/* If SHOW_COLOR is true, stop colorizing.
2796 Add a localized close quote. */
2797
2798void
2799pp_end_quote (pretty_printer *pp, bool show_color)
2800{
2801 pp_string (pp, str: colorize_stop (show_color));
2802 pp_string (pp, str: close_quote);
2803}
2804
2805
2806/* The string starting at P has LEN (at least 1) bytes left; if they
2807 start with a valid UTF-8 sequence, return the length of that
2808 sequence and set *VALUE to the value of that sequence, and
2809 otherwise return 0 and set *VALUE to (unsigned int) -1. */
2810
2811static int
2812decode_utf8_char (const unsigned char *p, size_t len, unsigned int *value)
2813{
2814 unsigned int t = *p;
2815
2816 if (len == 0)
2817 abort ();
2818 if (t & 0x80)
2819 {
2820 size_t utf8_len = 0;
2821 unsigned int ch;
2822 size_t i;
2823 for (t = *p; t & 0x80; t <<= 1)
2824 utf8_len++;
2825
2826 if (utf8_len > len || utf8_len < 2 || utf8_len > 6)
2827 {
2828 *value = (unsigned int) -1;
2829 return 0;
2830 }
2831 ch = *p & ((1 << (7 - utf8_len)) - 1);
2832 for (i = 1; i < utf8_len; i++)
2833 {
2834 unsigned int u = p[i];
2835 if ((u & 0xC0) != 0x80)
2836 {
2837 *value = (unsigned int) -1;
2838 return 0;
2839 }
2840 ch = (ch << 6) | (u & 0x3F);
2841 }
2842 if ( (ch <= 0x7F && utf8_len > 1)
2843 || (ch <= 0x7FF && utf8_len > 2)
2844 || (ch <= 0xFFFF && utf8_len > 3)
2845 || (ch <= 0x1FFFFF && utf8_len > 4)
2846 || (ch <= 0x3FFFFFF && utf8_len > 5)
2847 || (ch >= 0xD800 && ch <= 0xDFFF))
2848 {
2849 *value = (unsigned int) -1;
2850 return 0;
2851 }
2852 *value = ch;
2853 return utf8_len;
2854 }
2855 else
2856 {
2857 *value = t;
2858 return 1;
2859 }
2860}
2861
2862/* Allocator for identifier_to_locale and corresponding function to
2863 free memory. */
2864
2865void *(*identifier_to_locale_alloc) (size_t) = xmalloc;
2866void (*identifier_to_locale_free) (void *) = free;
2867
2868/* Given IDENT, an identifier in the internal encoding, return a
2869 version of IDENT suitable for diagnostics in the locale character
2870 set: either IDENT itself, or a string, allocated using
2871 identifier_to_locale_alloc, converted to the locale character set
2872 and using escape sequences if not representable in the locale
2873 character set or containing control characters or invalid byte
2874 sequences. Existing backslashes in IDENT are not doubled, so the
2875 result may not uniquely specify the contents of an arbitrary byte
2876 sequence identifier. */
2877
2878const char *
2879identifier_to_locale (const char *ident)
2880{
2881 const unsigned char *uid = (const unsigned char *) ident;
2882 size_t idlen = strlen (s: ident);
2883 bool valid_printable_utf8 = true;
2884 bool all_ascii = true;
2885 size_t i;
2886
2887 for (i = 0; i < idlen;)
2888 {
2889 unsigned int c;
2890 size_t utf8_len = decode_utf8_char (p: &uid[i], len: idlen - i, value: &c);
2891 if (utf8_len == 0 || c <= 0x1F || (c >= 0x7F && c <= 0x9F))
2892 {
2893 valid_printable_utf8 = false;
2894 break;
2895 }
2896 if (utf8_len > 1)
2897 all_ascii = false;
2898 i += utf8_len;
2899 }
2900
2901 /* If IDENT contains invalid UTF-8 sequences (which may occur with
2902 attributes putting arbitrary byte sequences in identifiers), or
2903 control characters, we use octal escape sequences for all bytes
2904 outside printable ASCII. */
2905 if (!valid_printable_utf8)
2906 {
2907 char *ret = (char *) identifier_to_locale_alloc (4 * idlen + 1);
2908 char *p = ret;
2909 for (i = 0; i < idlen; i++)
2910 {
2911 if (uid[i] > 0x1F && uid[i] < 0x7F)
2912 *p++ = uid[i];
2913 else
2914 {
2915 sprintf (s: p, format: "\\%03o", uid[i]);
2916 p += 4;
2917 }
2918 }
2919 *p = 0;
2920 return ret;
2921 }
2922
2923 /* Otherwise, if it is valid printable ASCII, or printable UTF-8
2924 with the locale character set being UTF-8, IDENT is used. */
2925 if (all_ascii || locale_utf8)
2926 return ident;
2927
2928 /* Otherwise IDENT is converted to the locale character set if
2929 possible. */
2930#if defined ENABLE_NLS && defined HAVE_LANGINFO_CODESET && HAVE_ICONV
2931 if (locale_encoding != NULL)
2932 {
2933 iconv_t cd = iconv_open (tocode: locale_encoding, fromcode: "UTF-8");
2934 bool conversion_ok = true;
2935 char *ret = NULL;
2936 if (cd != (iconv_t) -1)
2937 {
2938 size_t ret_alloc = 4 * idlen + 1;
2939 for (;;)
2940 {
2941 /* Repeat the whole conversion process as needed with
2942 larger buffers so non-reversible transformations can
2943 always be detected. */
2944 ICONV_CONST char *inbuf = CONST_CAST (char *, ident);
2945 char *outbuf;
2946 size_t inbytesleft = idlen;
2947 size_t outbytesleft = ret_alloc - 1;
2948 size_t iconv_ret;
2949
2950 ret = (char *) identifier_to_locale_alloc (ret_alloc);
2951 outbuf = ret;
2952
2953 if (iconv (cd: cd, inbuf: 0, inbytesleft: 0, outbuf: 0, outbytesleft: 0) == (size_t) -1)
2954 {
2955 conversion_ok = false;
2956 break;
2957 }
2958
2959 iconv_ret = iconv (cd: cd, inbuf: &inbuf, inbytesleft: &inbytesleft,
2960 outbuf: &outbuf, outbytesleft: &outbytesleft);
2961 if (iconv_ret == (size_t) -1 || inbytesleft != 0)
2962 {
2963 if (errno == E2BIG)
2964 {
2965 ret_alloc *= 2;
2966 identifier_to_locale_free (ret);
2967 ret = NULL;
2968 continue;
2969 }
2970 else
2971 {
2972 conversion_ok = false;
2973 break;
2974 }
2975 }
2976 else if (iconv_ret != 0)
2977 {
2978 conversion_ok = false;
2979 break;
2980 }
2981 /* Return to initial shift state. */
2982 if (iconv (cd: cd, inbuf: 0, inbytesleft: 0, outbuf: &outbuf, outbytesleft: &outbytesleft) == (size_t) -1)
2983 {
2984 if (errno == E2BIG)
2985 {
2986 ret_alloc *= 2;
2987 identifier_to_locale_free (ret);
2988 ret = NULL;
2989 continue;
2990 }
2991 else
2992 {
2993 conversion_ok = false;
2994 break;
2995 }
2996 }
2997 *outbuf = 0;
2998 break;
2999 }
3000 iconv_close (cd: cd);
3001 if (conversion_ok)
3002 return ret;
3003 }
3004 }
3005#endif
3006
3007 /* Otherwise, convert non-ASCII characters in IDENT to UCNs. */
3008 {
3009 char *ret = (char *) identifier_to_locale_alloc (10 * idlen + 1);
3010 char *p = ret;
3011 for (i = 0; i < idlen;)
3012 {
3013 unsigned int c;
3014 size_t utf8_len = decode_utf8_char (p: &uid[i], len: idlen - i, value: &c);
3015 if (utf8_len == 1)
3016 *p++ = uid[i];
3017 else
3018 {
3019 sprintf (s: p, format: "\\U%08x", c);
3020 p += 10;
3021 }
3022 i += utf8_len;
3023 }
3024 *p = 0;
3025 return ret;
3026 }
3027}
3028
3029/* Support for encoding URLs.
3030 See egmontkob/Hyperlinks_in_Terminal_Emulators.md
3031 ( https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda ).
3032
3033 > A hyperlink is opened upon encountering an OSC 8 escape sequence with
3034 > the target URI. The syntax is
3035 >
3036 > OSC 8 ; params ; URI ST
3037 >
3038 > A hyperlink is closed with the same escape sequence, omitting the
3039 > parameters and the URI but keeping the separators:
3040 >
3041 > OSC 8 ; ; ST
3042 >
3043 > OSC (operating system command) is typically ESC ].
3044
3045 Use BEL instead of ST, as that is currently rendered better in some
3046 terminal emulators that don't support OSC 8, like konsole. */
3047
3048/* If URL-printing is enabled, write an "open URL" escape sequence to PP
3049 for the given URL. */
3050
3051void
3052pretty_printer::begin_url (const char *url)
3053{
3054 if (!url)
3055 {
3056 /* Handle null URL by skipping all output here,
3057 and in the next pp_end_url. */
3058 m_skipping_null_url = true;
3059 return;
3060 }
3061 switch (m_url_format)
3062 {
3063 case URL_FORMAT_NONE:
3064 break;
3065 case URL_FORMAT_ST:
3066 pp_string (pp: this, str: "\33]8;;");
3067 pp_string (pp: this, str: url);
3068 pp_string (pp: this, str: "\33\\");
3069 break;
3070 case URL_FORMAT_BEL:
3071 pp_string (pp: this, str: "\33]8;;");
3072 pp_string (pp: this, str: url);
3073 pp_string (pp: this, str: "\a");
3074 break;
3075 default:
3076 gcc_unreachable ();
3077 }
3078}
3079
3080/* Helper function for pp_end_url and pp_format, return the "close URL" escape
3081 sequence string. */
3082
3083static const char *
3084get_end_url_string (pretty_printer *pp)
3085{
3086 switch (pp->get_url_format ())
3087 {
3088 case URL_FORMAT_NONE:
3089 return "";
3090 case URL_FORMAT_ST:
3091 return "\33]8;;\33\\";
3092 case URL_FORMAT_BEL:
3093 return "\33]8;;\a";
3094 default:
3095 gcc_unreachable ();
3096 }
3097}
3098
3099/* If URL-printing is enabled, write a "close URL" escape sequence to PP. */
3100
3101void
3102pretty_printer::end_url ()
3103{
3104 if (m_skipping_null_url)
3105 {
3106 /* We gracefully handle pp_begin_url (NULL) by omitting output for
3107 both begin and end. Here we handle the latter. */
3108 m_skipping_null_url = false;
3109 return;
3110 }
3111 if (m_url_format != URL_FORMAT_NONE)
3112 pp_string (pp: this, str: get_end_url_string (pp: this));
3113}
3114
3115/* Dump state of this pretty_printer to OUT, for debugging. */
3116
3117void
3118pretty_printer::dump (FILE *out, int indent) const
3119{
3120 fprintf (stream: out, format: "%*sm_show_color: %s\n",
3121 indent, "",
3122 m_show_color ? "true" : "false");
3123
3124 fprintf (stream: out, format: "%*sm_url_format: ", indent, "");
3125 switch (m_url_format)
3126 {
3127 case URL_FORMAT_NONE:
3128 fprintf (stream: out, format: "none");
3129 break;
3130 case URL_FORMAT_ST:
3131 fprintf (stream: out, format: "st");
3132 break;
3133 case URL_FORMAT_BEL:
3134 fprintf (stream: out, format: "bel");
3135 break;
3136 default:
3137 gcc_unreachable ();
3138 }
3139 fprintf (stream: out, format: "\n");
3140
3141 fprintf (stream: out, format: "%*sm_buffer:\n", indent, "");
3142 m_buffer->dump (out, indent: indent + 2);
3143}
3144
3145/* class pp_markup::context. */
3146
3147void
3148pp_markup::context::begin_quote ()
3149{
3150 gcc_assert (!m_quoted);
3151 gcc_assert (m_formatted_token_list);
3152 push_back_any_text ();
3153 m_formatted_token_list->push_back<pp_token_begin_quote> ();
3154 m_quoted = true;
3155}
3156
3157void
3158pp_markup::context::end_quote ()
3159{
3160 /* Bail out if the quotes have already been ended, such as by
3161 printing a type emitting "TYPEDEF' {aka `TYPE'}". */
3162 if (!m_quoted)
3163 return;
3164 gcc_assert (m_formatted_token_list);
3165 push_back_any_text ();
3166 m_formatted_token_list->push_back<pp_token_end_quote> ();
3167 m_quoted = false;
3168}
3169
3170void
3171pp_markup::context::begin_highlight_color (const char *color_name)
3172{
3173 if (!pp_show_highlight_colors (pp: &m_pp))
3174 return;
3175
3176 push_back_any_text ();
3177 m_formatted_token_list->push_back <pp_token_begin_color>
3178 (args: label_text::borrow (buffer: color_name));
3179}
3180
3181void
3182pp_markup::context::end_highlight_color ()
3183{
3184 if (!pp_show_highlight_colors (pp: &m_pp))
3185 return;
3186
3187 push_back_any_text ();
3188 m_formatted_token_list->push_back<pp_token_end_color> ();
3189}
3190
3191void
3192pp_markup::context::push_back_any_text ()
3193{
3194 obstack *cur_obstack = m_buf.m_obstack;
3195 obstack_1grow (cur_obstack, '\0');
3196 m_formatted_token_list->push_back_text
3197 (text: label_text::borrow (XOBFINISH (cur_obstack,
3198 const char *)));
3199}
3200
3201void
3202pp_markup::comma_separated_quoted_strings::add_to_phase_2 (context &ctxt)
3203{
3204 for (unsigned i = 0; i < m_strings.length (); i++)
3205 {
3206 if (i > 0)
3207 pp_string (pp: &ctxt.m_pp, str: ", ");
3208 ctxt.begin_quote ();
3209 pp_string (pp: &ctxt.m_pp, str: m_strings[i]);
3210 ctxt.end_quote ();
3211 }
3212}
3213
3214/* Color names for expressing "expected" vs "actual" values. */
3215const char *const highlight_colors::expected = "highlight-a";
3216const char *const highlight_colors::actual = "highlight-b";
3217
3218/* Color names for expressing "LHS" vs "RHS" values in a binary operation. */
3219const char *const highlight_colors::lhs = "highlight-a";
3220const char *const highlight_colors::rhs = "highlight-b";
3221
3222#if CHECKING_P
3223
3224namespace selftest {
3225
3226/* Smoketest for pretty_printer. */
3227
3228static void
3229test_basic_printing ()
3230{
3231 pretty_printer pp;
3232 pp_string (pp: &pp, str: "hello");
3233 pp_space (&pp);
3234 pp_string (pp: &pp, str: "world");
3235
3236 ASSERT_STREQ ("hello world", pp_formatted_text (&pp));
3237}
3238
3239/* Helper function for testing pp_format.
3240 Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
3241 prints EXPECTED, assuming that pp_show_color is SHOW_COLOR. */
3242
3243static void
3244assert_pp_format_va (const location &loc, const char *expected,
3245 bool show_color, const char *fmt, va_list *ap)
3246{
3247 pretty_printer pp;
3248 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
3249
3250 text_info ti (fmt, ap, 0, nullptr, &rich_loc);
3251
3252 pp_show_color (pp: &pp) = show_color;
3253 pp_format (pp: &pp, text: &ti);
3254 pp_output_formatted_text (pp: &pp);
3255 ASSERT_STREQ_AT (loc, expected, pp_formatted_text (&pp));
3256}
3257
3258/* Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
3259 prints EXPECTED, with show_color disabled. */
3260
3261static void
3262assert_pp_format (const location &loc, const char *expected,
3263 const char *fmt, ...)
3264{
3265 va_list ap;
3266
3267 va_start (ap, fmt);
3268 assert_pp_format_va (loc, expected, show_color: false, fmt, ap: &ap);
3269 va_end (ap);
3270}
3271
3272/* As above, but with colorization enabled. */
3273
3274static void
3275assert_pp_format_colored (const location &loc, const char *expected,
3276 const char *fmt, ...)
3277{
3278 /* The tests of colorization assume the default color scheme.
3279 If GCC_COLORS is set, then the colors have potentially been
3280 overridden; skip the test. */
3281 if (getenv (name: "GCC_COLORS"))
3282 return;
3283
3284 va_list ap;
3285
3286 va_start (ap, fmt);
3287 assert_pp_format_va (loc, expected, show_color: true, fmt, ap: &ap);
3288 va_end (ap);
3289}
3290
3291/* Helper function for calling testing pp_format,
3292 by calling assert_pp_format with various numbers of arguments.
3293 These exist mostly to avoid having to write SELFTEST_LOCATION
3294 throughout test_pp_format. */
3295
3296#define ASSERT_PP_FORMAT_1(EXPECTED, FMT, ARG1) \
3297 SELFTEST_BEGIN_STMT \
3298 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3299 (ARG1)); \
3300 SELFTEST_END_STMT
3301
3302#define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \
3303 SELFTEST_BEGIN_STMT \
3304 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3305 (ARG1), (ARG2)); \
3306 SELFTEST_END_STMT
3307
3308#define ASSERT_PP_FORMAT_3(EXPECTED, FMT, ARG1, ARG2, ARG3) \
3309 SELFTEST_BEGIN_STMT \
3310 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3311 (ARG1), (ARG2), (ARG3)); \
3312 SELFTEST_END_STMT
3313
3314/* Verify that pp_format works, for various format codes. */
3315
3316static void
3317test_pp_format ()
3318{
3319 /* Avoid introducing locale-specific differences in the results
3320 by hardcoding open_quote and close_quote. */
3321 auto_fix_quotes fix_quotes;
3322
3323 /* Verify that plain text is passed through unchanged. */
3324 assert_pp_format (SELFTEST_LOCATION, expected: "unformatted", fmt: "unformatted");
3325
3326 /* Verify various individual format codes, in the order listed in the
3327 comment for pp_format above. For each code, we append a second
3328 argument with a known bit pattern (0x12345678), to ensure that we
3329 are consuming arguments correctly. */
3330 ASSERT_PP_FORMAT_2 ("-27 12345678", "%d %x", -27, 0x12345678);
3331 ASSERT_PP_FORMAT_2 ("-5 12345678", "%i %x", -5, 0x12345678);
3332 ASSERT_PP_FORMAT_2 ("10 12345678", "%u %x", 10, 0x12345678);
3333 ASSERT_PP_FORMAT_2 ("17 12345678", "%o %x", 15, 0x12345678);
3334 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678);
3335 ASSERT_PP_FORMAT_2 ("-27 12345678", "%ld %x", (long)-27, 0x12345678);
3336 ASSERT_PP_FORMAT_2 ("-5 12345678", "%li %x", (long)-5, 0x12345678);
3337 ASSERT_PP_FORMAT_2 ("10 12345678", "%lu %x", (long)10, 0x12345678);
3338 ASSERT_PP_FORMAT_2 ("17 12345678", "%lo %x", (long)15, 0x12345678);
3339 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%lx %x", (long)0xcafebabe,
3340 0x12345678);
3341 ASSERT_PP_FORMAT_2 ("-27 12345678", "%lld %x", (long long)-27, 0x12345678);
3342 ASSERT_PP_FORMAT_2 ("-5 12345678", "%lli %x", (long long)-5, 0x12345678);
3343 ASSERT_PP_FORMAT_2 ("10 12345678", "%llu %x", (long long)10, 0x12345678);
3344 ASSERT_PP_FORMAT_2 ("17 12345678", "%llo %x", (long long)15, 0x12345678);
3345 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe,
3346 0x12345678);
3347 ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", HOST_WIDE_INT_C (-27),
3348 0x12345678);
3349 ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", HOST_WIDE_INT_C (-5),
3350 0x12345678);
3351 ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", HOST_WIDE_INT_UC (10),
3352 0x12345678);
3353 ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", HOST_WIDE_INT_C (15),
3354 0x12345678);
3355 ASSERT_PP_FORMAT_2 ("0xcafebabe 12345678", "%wx %x",
3356 HOST_WIDE_INT_C (0xcafebabe), 0x12345678);
3357 ASSERT_PP_FORMAT_2 ("-27 12345678", "%zd %x", (ssize_t)-27, 0x12345678);
3358 ASSERT_PP_FORMAT_2 ("-5 12345678", "%zi %x", (ssize_t)-5, 0x12345678);
3359 ASSERT_PP_FORMAT_2 ("10 12345678", "%zu %x", (size_t)10, 0x12345678);
3360 ASSERT_PP_FORMAT_2 ("17 12345678", "%zo %x", (size_t)15, 0x12345678);
3361 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%zx %x", (size_t)0xcafebabe,
3362 0x12345678);
3363 ASSERT_PP_FORMAT_2 ("-27 12345678", "%td %x", (ptrdiff_t)-27, 0x12345678);
3364 ASSERT_PP_FORMAT_2 ("-5 12345678", "%ti %x", (ptrdiff_t)-5, 0x12345678);
3365 ASSERT_PP_FORMAT_2 ("10 12345678", "%tu %x", (ptrdiff_t)10, 0x12345678);
3366 ASSERT_PP_FORMAT_2 ("17 12345678", "%to %x", (ptrdiff_t)15, 0x12345678);
3367 ASSERT_PP_FORMAT_2 ("1afebabe 12345678", "%tx %x", (ptrdiff_t)0x1afebabe,
3368 0x12345678);
3369 ASSERT_PP_FORMAT_2 ("1.000000 12345678", "%f %x", 1.0, 0x12345678);
3370 ASSERT_PP_FORMAT_2 ("A 12345678", "%c %x", 'A', 0x12345678);
3371 ASSERT_PP_FORMAT_2 ("hello world 12345678", "%s %x", "hello world",
3372 0x12345678);
3373
3374 /* Not nul-terminated. */
3375 char arr[5] = { '1', '2', '3', '4', '5' };
3376 ASSERT_PP_FORMAT_3 ("123 12345678", "%.*s %x", 3, arr, 0x12345678);
3377 ASSERT_PP_FORMAT_3 ("1234 12345678", "%.*s %x", -1, "1234", 0x12345678);
3378 ASSERT_PP_FORMAT_3 ("12345 12345678", "%.*s %x", 7, "12345", 0x12345678);
3379
3380 /* We can't test for %p; the pointer is printed in an implementation-defined
3381 manner. */
3382 ASSERT_PP_FORMAT_2 ("normal colored normal 12345678",
3383 "normal %rcolored%R normal %x",
3384 "error", 0x12345678);
3385 assert_pp_format_colored
3386 (SELFTEST_LOCATION,
3387 expected: "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678",
3388 fmt: "normal %rcolored%R normal %x", "error", 0x12345678);
3389 /* TODO:
3390 %m: strerror(text->err_no) - does not consume a value from args_ptr. */
3391 ASSERT_PP_FORMAT_1 ("% 12345678", "%% %x", 0x12345678);
3392 ASSERT_PP_FORMAT_1 ("` 12345678", "%< %x", 0x12345678);
3393 ASSERT_PP_FORMAT_1 ("' 12345678", "%> %x", 0x12345678);
3394 ASSERT_PP_FORMAT_1 ("' 12345678", "%' %x", 0x12345678);
3395 ASSERT_PP_FORMAT_3 ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678);
3396 ASSERT_PP_FORMAT_2 ("abc 12345678", "%.3s %x", "abcdef", 0x12345678);
3397
3398 /* Verify flag 'q'. */
3399 ASSERT_PP_FORMAT_2 ("`foo' 12345678", "%qs %x", "foo", 0x12345678);
3400 assert_pp_format_colored (SELFTEST_LOCATION,
3401 expected: "`\33[01m\33[Kfoo\33[m\33[K' 12345678", fmt: "%qs %x",
3402 "foo", 0x12345678);
3403 /* Verify "%@". */
3404 {
3405 diagnostic_event_id_t first (2);
3406 diagnostic_event_id_t second (7);
3407
3408 ASSERT_PP_FORMAT_2 ("first `free' at (3); second `free' at (8)",
3409 "first %<free%> at %@; second %<free%> at %@",
3410 &first, &second);
3411 assert_pp_format_colored
3412 (SELFTEST_LOCATION,
3413 expected: "first `free' at (3);"
3414 " second `free' at (8)",
3415 fmt: "first %<free%> at %@; second %<free%> at %@",
3416 &first, &second);
3417 }
3418
3419 /* Verify %Z. */
3420 int v[] = { 1, 2, 3 };
3421 ASSERT_PP_FORMAT_3 ("1, 2, 3 12345678", "%Z %x", v, 3, 0x12345678);
3422
3423 int v2[] = { 0 };
3424 ASSERT_PP_FORMAT_3 ("0 12345678", "%Z %x", v2, 1, 0x12345678);
3425
3426 /* Verify %e. */
3427 {
3428 pp_element_quoted_string foo ("foo");
3429 pp_element_quoted_string bar ("bar");
3430 ASSERT_PP_FORMAT_2 ("before `foo' `bar' after",
3431 "before %e %e after",
3432 &foo, &bar);
3433 }
3434
3435 /* Verify that combinations work, along with unformatted text. */
3436 assert_pp_format (SELFTEST_LOCATION,
3437 expected: "the quick brown fox jumps over the lazy dog",
3438 fmt: "the %s %s %s jumps over the %s %s",
3439 "quick", "brown", "fox", "lazy", "dog");
3440 assert_pp_format (SELFTEST_LOCATION, expected: "item 3 of 7", fmt: "item %i of %i", 3, 7);
3441 assert_pp_format (SELFTEST_LOCATION, expected: "problem with `bar' at line 10",
3442 fmt: "problem with %qs at line %i", "bar", 10);
3443
3444 /* Verified numbered args. */
3445 assert_pp_format (SELFTEST_LOCATION,
3446 expected: "foo: second bar: first",
3447 fmt: "foo: %2$s bar: %1$s",
3448 "first", "second");
3449 assert_pp_format (SELFTEST_LOCATION,
3450 expected: "foo: 1066 bar: 1776",
3451 fmt: "foo: %2$i bar: %1$i",
3452 1776, 1066);
3453 assert_pp_format (SELFTEST_LOCATION,
3454 expected: "foo: second bar: 1776",
3455 fmt: "foo: %2$s bar: %1$i",
3456 1776, "second");
3457 assert_pp_format (SELFTEST_LOCATION,
3458 expected: "foo: sec bar: 3360",
3459 fmt: "foo: %3$.*2$s bar: %1$o",
3460 1776, 3, "second");
3461 assert_pp_format (SELFTEST_LOCATION,
3462 expected: "foo: seco bar: 3360",
3463 fmt: "foo: %2$.4s bar: %1$o",
3464 1776, "second");
3465}
3466
3467static void
3468test_merge_consecutive_text_tokens ()
3469{
3470 auto_obstack s;
3471 pp_token_list list (s);
3472 list.push_back_text (text: label_text::borrow (buffer: "hello"));
3473 list.push_back_text (text: label_text::borrow (buffer: " "));
3474 list.push_back_text (text: label_text::take (buffer: xstrdup ("world")));
3475 list.push_back_text (text: label_text::borrow (buffer: "!"));
3476
3477 list.merge_consecutive_text_tokens ();
3478 // We expect a single text token, with concatenated text
3479 ASSERT_EQ (list.m_first, list.m_end);
3480 pp_token *tok = list.m_first;
3481 ASSERT_NE (tok, nullptr);
3482 ASSERT_EQ (tok->m_kind, pp_token::kind::text);
3483 ASSERT_STREQ (as_a <pp_token_text *> (tok)->m_value.get (), "hello world!");
3484}
3485
3486/* Verify that we can create custom tokens that can be lowered
3487 in phase 3. */
3488
3489static void
3490test_custom_tokens_1 ()
3491{
3492 struct custom_token_adder : public pp_element
3493 {
3494 public:
3495 struct value : public pp_token_custom_data::value
3496 {
3497 value (custom_token_adder &adder)
3498 : m_adder (adder)
3499 {
3500 m_adder.m_num_living_values++;
3501 }
3502 value (const value &other)
3503 : m_adder (other.m_adder)
3504 {
3505 m_adder.m_num_living_values++;
3506 }
3507 value (value &&other)
3508 : m_adder (other.m_adder)
3509 {
3510 m_adder.m_num_living_values++;
3511 }
3512 value &operator= (const value &other) = delete;
3513 value &operator= (value &&other) = delete;
3514 ~value ()
3515 {
3516 m_adder.m_num_living_values--;
3517 }
3518
3519 void dump (FILE *out) const final override
3520 {
3521 fprintf (stream: out, format: "\"%s\"", m_adder.m_name);
3522 }
3523
3524 bool as_standard_tokens (pp_token_list &out) final override
3525 {
3526 ASSERT_TRUE (m_adder.m_num_living_values > 0);
3527 out.push_back<pp_token_text> (args: label_text::borrow (buffer: m_adder.m_name));
3528 return true;
3529 }
3530
3531 custom_token_adder &m_adder;
3532 };
3533
3534 custom_token_adder (const char *name)
3535 : m_name (name),
3536 m_num_living_values (0)
3537 {
3538 }
3539
3540 void add_to_phase_2 (pp_markup::context &ctxt) final override
3541 {
3542 auto val_ptr = std::make_unique<value> (args&: *this);
3543 ctxt.m_formatted_token_list->push_back<pp_token_custom_data>
3544 (args: std::move (val_ptr));
3545 }
3546
3547 const char *m_name;
3548 int m_num_living_values;
3549 };
3550
3551 custom_token_adder e1 ("foo");
3552 custom_token_adder e2 ("bar");
3553 ASSERT_EQ (e1.m_num_living_values, 0);
3554 ASSERT_EQ (e2.m_num_living_values, 0);
3555
3556 pretty_printer pp;
3557 pp_printf (pp: &pp, msg: "before %e middle %e after", &e1, &e2);
3558
3559 /* Verify that instances were cleaned up. */
3560 ASSERT_EQ (e1.m_num_living_values, 0);
3561 ASSERT_EQ (e2.m_num_living_values, 0);
3562
3563 ASSERT_STREQ (pp_formatted_text (&pp),
3564 "before foo middle bar after");
3565}
3566
3567/* Verify that we can create custom tokens that aren't lowered
3568 in phase 3, but instead are handled by a custom token_printer.
3569 Use this to verify the inputs seen by such token_printers. */
3570
3571static void
3572test_custom_tokens_2 ()
3573{
3574 struct custom_token_adder : public pp_element
3575 {
3576 struct value : public pp_token_custom_data::value
3577 {
3578 public:
3579 value (custom_token_adder &adder)
3580 : m_adder (adder)
3581 {
3582 m_adder.m_num_living_values++;
3583 }
3584 value (const value &other)
3585 : m_adder (other.m_adder)
3586 {
3587 m_adder.m_num_living_values++;
3588 }
3589 value (value &&other)
3590 : m_adder (other.m_adder)
3591 {
3592 m_adder.m_num_living_values++;
3593 }
3594 value &operator= (const value &other) = delete;
3595 value &operator= (value &&other) = delete;
3596 ~value ()
3597 {
3598 m_adder.m_num_living_values--;
3599 }
3600
3601 void dump (FILE *out) const final override
3602 {
3603 fprintf (stream: out, format: "\"%s\"", m_adder.m_name);
3604 }
3605
3606 bool as_standard_tokens (pp_token_list &) final override
3607 {
3608 return false;
3609 }
3610
3611 custom_token_adder &m_adder;
3612 };
3613
3614 custom_token_adder (const char *name)
3615 : m_name (name),
3616 m_num_living_values (0)
3617 {
3618 }
3619
3620 void add_to_phase_2 (pp_markup::context &ctxt) final override
3621 {
3622 auto val_ptr = std::make_unique<value> (args&: *this);
3623 ctxt.m_formatted_token_list->push_back<pp_token_custom_data>
3624 (args: std::move (val_ptr));
3625 }
3626
3627 const char *m_name;
3628 int m_num_living_values;
3629 };
3630
3631 class custom_token_printer : public token_printer
3632 {
3633 void print_tokens (pretty_printer *pp,
3634 const pp_token_list &tokens) final override
3635 {
3636 /* Verify that TOKENS has:
3637 [TEXT("before "), CUSTOM("foo"), TEXT(" middle "), CUSTOM("bar"),
3638 TEXT(" after")] */
3639 pp_token *tok_0 = tokens.m_first;
3640 ASSERT_NE (tok_0, nullptr);
3641 ASSERT_EQ (tok_0->m_kind, pp_token::kind::text);
3642 ASSERT_STREQ (as_a<pp_token_text *> (tok_0)->m_value.get (),
3643 "before ");
3644
3645 pp_token *tok_1 = tok_0->m_next;
3646 ASSERT_NE (tok_1, nullptr);
3647 ASSERT_EQ (tok_1->m_prev, tok_0);
3648 ASSERT_EQ (tok_1->m_kind, pp_token::kind::custom_data);
3649
3650 custom_token_adder::value *v1
3651 = static_cast <custom_token_adder::value *>
3652 (as_a<pp_token_custom_data *> (p: tok_1)->m_value.get ());
3653 ASSERT_STREQ (v1->m_adder.m_name, "foo");
3654 ASSERT_TRUE (v1->m_adder.m_num_living_values > 0);
3655
3656 pp_token *tok_2 = tok_1->m_next;
3657 ASSERT_NE (tok_2, nullptr);
3658 ASSERT_EQ (tok_2->m_prev, tok_1);
3659 ASSERT_EQ (tok_2->m_kind, pp_token::kind::text);
3660 ASSERT_STREQ (as_a<pp_token_text *> (tok_2)->m_value.get (),
3661 " middle ");
3662
3663 pp_token *tok_3 = tok_2->m_next;
3664 ASSERT_NE (tok_3, nullptr);
3665 ASSERT_EQ (tok_3->m_prev, tok_2);
3666 ASSERT_EQ (tok_3->m_kind, pp_token::kind::custom_data);
3667 custom_token_adder::value *v3
3668 = static_cast <custom_token_adder::value *>
3669 (as_a<pp_token_custom_data *> (p: tok_3)->m_value.get ());
3670 ASSERT_STREQ (v3->m_adder.m_name, "bar");
3671 ASSERT_TRUE (v3->m_adder.m_num_living_values > 0);
3672
3673 pp_token *tok_4 = tok_3->m_next;
3674 ASSERT_NE (tok_4, nullptr);
3675 ASSERT_EQ (tok_4->m_prev, tok_3);
3676 ASSERT_EQ (tok_4->m_kind, pp_token::kind::text);
3677 ASSERT_STREQ (as_a<pp_token_text *> (tok_4)->m_value.get (),
3678 " after");
3679 ASSERT_EQ (tok_4->m_next, nullptr);
3680
3681 /* Normally we'd loop over the tokens, printing them to PP
3682 and handling the custom tokens.
3683 Instead, print a message to PP to verify that we were called. */
3684 pp_string (pp, str: "print_tokens was called");
3685 }
3686 };
3687
3688 custom_token_adder e1 ("foo");
3689 custom_token_adder e2 ("bar");
3690 ASSERT_EQ (e1.m_num_living_values, 0);
3691 ASSERT_EQ (e2.m_num_living_values, 0);
3692
3693 custom_token_printer tp;
3694 pretty_printer pp;
3695 pp.set_token_printer (&tp);
3696 pp_printf (pp: &pp, msg: "before %e middle %e after", &e1, &e2);
3697
3698 /* Verify that instances were cleaned up. */
3699 ASSERT_EQ (e1.m_num_living_values, 0);
3700 ASSERT_EQ (e2.m_num_living_values, 0);
3701
3702 ASSERT_STREQ (pp_formatted_text (&pp),
3703 "print_tokens was called");
3704}
3705
3706/* Helper subroutine for test_pp_format_stack.
3707 Call pp_format (phases 1 and 2), without calling phase 3. */
3708
3709static void
3710push_pp_format (pretty_printer *pp, const char *msg, ...)
3711{
3712 va_list ap;
3713
3714 va_start (ap, msg);
3715 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
3716 text_info ti (msg, &ap, 0, nullptr, &rich_loc);
3717 pp_format (pp, text: &ti);
3718 va_end (ap);
3719}
3720
3721#define ASSERT_TEXT_TOKEN(TOKEN, EXPECTED_TEXT) \
3722 SELFTEST_BEGIN_STMT \
3723 ASSERT_NE ((TOKEN), nullptr); \
3724 ASSERT_EQ ((TOKEN)->m_kind, pp_token::kind::text); \
3725 ASSERT_STREQ \
3726 (as_a <const pp_token_text *> (TOKEN)->m_value.get (), \
3727 (EXPECTED_TEXT)); \
3728 SELFTEST_END_STMT
3729
3730
3731/* Verify that the stack of pp_formatted_chunks works as expected. */
3732
3733static void
3734test_pp_format_stack ()
3735{
3736 auto_fix_quotes fix_quotes;
3737
3738 pretty_printer pp;
3739 push_pp_format (pp: &pp, msg: "unexpected foo: %i bar: %qs", 42, "test");
3740 push_pp_format (pp: &pp, msg: "In function: %qs", "test_fn");
3741
3742 /* Expect the top of the stack to have:
3743 (gdb) call top->dump()
3744 0: [TEXT("In function: ")]
3745 1: [BEGIN_QUOTE, TEXT("test_fn"), END_QUOTE]. */
3746
3747 pp_formatted_chunks *top = pp_buffer (pp: &pp)->m_cur_formatted_chunks;
3748 ASSERT_NE (top, nullptr);
3749 ASSERT_TEXT_TOKEN (top->get_token_lists ()[0]->m_first, "In function: ");
3750 ASSERT_EQ (top->get_token_lists ()[1]->m_first->m_kind,
3751 pp_token::kind::begin_quote);
3752 ASSERT_EQ (top->get_token_lists ()[2], nullptr);
3753
3754 /* Expect an entry in the stack below it with:
3755 0: [TEXT("unexpected foo: ")]
3756 1: [TEXT("42")]
3757 2: [TEXT(" bar: ")]
3758 3: [BEGIN_QUOTE, TEXT("test"), END_QUOTE]. */
3759 pp_formatted_chunks *prev = top->get_prev ();
3760 ASSERT_NE (prev, nullptr);
3761 ASSERT_TEXT_TOKEN (prev->get_token_lists ()[0]->m_first, "unexpected foo: ");
3762 ASSERT_TEXT_TOKEN (prev->get_token_lists ()[1]->m_first, "42");
3763 ASSERT_TEXT_TOKEN (prev->get_token_lists ()[2]->m_first, " bar: ");
3764 ASSERT_EQ (prev->get_token_lists ()[3]->m_first->m_kind,
3765 pp_token::kind::begin_quote);
3766 ASSERT_EQ (prev->get_token_lists ()[4], nullptr);
3767
3768 ASSERT_EQ (prev->get_prev (), nullptr);
3769
3770 /* Pop the top of the stack. */
3771 pp_output_formatted_text (pp: &pp);
3772 ASSERT_EQ (pp_buffer (&pp)->m_cur_formatted_chunks, prev);
3773 pp_newline (pp: &pp);
3774
3775 /* Pop the remaining entry from the stack. */
3776 pp_output_formatted_text (pp: &pp);
3777 ASSERT_EQ (pp_buffer (&pp)->m_cur_formatted_chunks, nullptr);
3778
3779 ASSERT_STREQ (pp_formatted_text (&pp),
3780 "In function: `test_fn'\nunexpected foo: 42 bar: `test'");
3781}
3782
3783/* Verify usage of pp_printf from within a pp_element's
3784 add_to_phase_2 vfunc. */
3785static void
3786test_pp_printf_within_pp_element ()
3787{
3788 class kv_element : public pp_element
3789 {
3790 public:
3791 kv_element (const char *key, int value)
3792 : m_key (key), m_value (value)
3793 {
3794 }
3795
3796 void add_to_phase_2 (pp_markup::context &ctxt) final override
3797 {
3798 /* We can't call pp_printf directly on ctxt.m_pp from within
3799 formatting. As a workaround, work with a clone of the pp. */
3800 std::unique_ptr<pretty_printer> pp (ctxt.m_pp.clone ());
3801 pp_printf (pp: pp.get (), msg: "(%qs: %qi)", m_key, m_value);
3802 pp_string (pp: &ctxt.m_pp, str: pp_formatted_text (pp: pp.get ()));
3803 }
3804
3805 private:
3806 const char *m_key;
3807 int m_value;
3808 };
3809
3810 auto_fix_quotes fix_quotes;
3811
3812 kv_element e1 ("foo", 42);
3813 kv_element e2 ("bar", 1066);
3814 ASSERT_PP_FORMAT_2 ("before (`foo': `42') (`bar': `1066') after",
3815 "before %e %e after",
3816 &e1, &e2);
3817 assert_pp_format_colored (SELFTEST_LOCATION,
3818 expected: ("before "
3819 "(`\33[01m\33[Kfoo\33[m\33[K'"
3820 ": "
3821 "`\33[01m\33[K42\33[m\33[K')"
3822 " "
3823 "(`\33[01m\33[Kbar\33[m\33[K'"
3824 ": "
3825 "`\33[01m\33[K1066\33[m\33[K')"
3826 " after"),
3827 fmt: "before %e %e after",
3828 &e1, &e2);
3829}
3830
3831/* A subclass of pretty_printer for use by test_prefixes_and_wrapping. */
3832
3833class test_pretty_printer : public pretty_printer
3834{
3835 public:
3836 test_pretty_printer (enum diagnostic_prefixing_rule_t rule,
3837 int max_line_length)
3838 {
3839 pp_set_prefix (pp: this, prefix: xstrdup ("PREFIX: "));
3840 pp_prefixing_rule (pp: this) = rule;
3841 pp_set_line_maximum_length (pp: this, length: max_line_length);
3842 }
3843};
3844
3845/* Verify that the various values of enum diagnostic_prefixing_rule_t work
3846 as expected, with and without line wrapping. */
3847
3848static void
3849test_prefixes_and_wrapping ()
3850{
3851 /* Tests of the various prefixing rules, without wrapping.
3852 Newlines embedded in pp_string don't affect it; we have to
3853 explicitly call pp_newline. */
3854 {
3855 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 0);
3856 pp_string (pp: &pp, str: "the quick brown fox");
3857 pp_newline (pp: &pp);
3858 pp_string (pp: &pp, str: "jumps over the lazy dog");
3859 pp_newline (pp: &pp);
3860 ASSERT_STREQ (pp_formatted_text (&pp),
3861 "PREFIX: the quick brown fox\n"
3862 " jumps over the lazy dog\n");
3863 }
3864 {
3865 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 0);
3866 pp_string (pp: &pp, str: "the quick brown fox");
3867 pp_newline (pp: &pp);
3868 pp_string (pp: &pp, str: "jumps over the lazy dog");
3869 pp_newline (pp: &pp);
3870 ASSERT_STREQ (pp_formatted_text (&pp),
3871 "the quick brown fox\n"
3872 "jumps over the lazy dog\n");
3873 }
3874 {
3875 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 0);
3876 pp_string (pp: &pp, str: "the quick brown fox");
3877 pp_newline (pp: &pp);
3878 pp_string (pp: &pp, str: "jumps over the lazy dog");
3879 pp_newline (pp: &pp);
3880 ASSERT_STREQ (pp_formatted_text (&pp),
3881 "PREFIX: the quick brown fox\n"
3882 "PREFIX: jumps over the lazy dog\n");
3883 }
3884
3885 /* Tests of the various prefixing rules, with wrapping. */
3886 {
3887 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 20);
3888 pp_string (pp: &pp, str: "the quick brown fox jumps over the lazy dog");
3889 pp_newline (pp: &pp);
3890 pp_string (pp: &pp, str: "able was I ere I saw elba");
3891 pp_newline (pp: &pp);
3892 ASSERT_STREQ (pp_formatted_text (&pp),
3893 "PREFIX: the quick \n"
3894 " brown fox jumps \n"
3895 " over the lazy \n"
3896 " dog\n"
3897 " able was I ere I \n"
3898 " saw elba\n");
3899 }
3900 {
3901 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 20);
3902 pp_string (pp: &pp, str: "the quick brown fox jumps over the lazy dog");
3903 pp_newline (pp: &pp);
3904 pp_string (pp: &pp, str: "able was I ere I saw elba");
3905 pp_newline (pp: &pp);
3906 ASSERT_STREQ (pp_formatted_text (&pp),
3907 "the quick brown fox \n"
3908 "jumps over the lazy \n"
3909 "dog\n"
3910 "able was I ere I \n"
3911 "saw elba\n");
3912 }
3913 {
3914 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 20);
3915 pp_string (pp: &pp, str: "the quick brown fox jumps over the lazy dog");
3916 pp_newline (pp: &pp);
3917 pp_string (pp: &pp, str: "able was I ere I saw elba");
3918 pp_newline (pp: &pp);
3919 ASSERT_STREQ (pp_formatted_text (&pp),
3920 "PREFIX: the quick brown fox jumps over the lazy dog\n"
3921 "PREFIX: able was I ere I saw elba\n");
3922 }
3923
3924}
3925
3926/* Verify that URL-printing works as expected. */
3927
3928static void
3929test_urls ()
3930{
3931 {
3932 pretty_printer pp;
3933 pp.set_url_format (URL_FORMAT_NONE);
3934 pp_begin_url (pp: &pp, url: "http://example.com");
3935 pp_string (pp: &pp, str: "This is a link");
3936 pp_end_url (pp: &pp);
3937 ASSERT_STREQ ("This is a link",
3938 pp_formatted_text (&pp));
3939 }
3940
3941 {
3942 pretty_printer pp;
3943 pp.set_url_format (URL_FORMAT_ST);
3944 pp_begin_url (pp: &pp, url: "http://example.com");
3945 pp_string (pp: &pp, str: "This is a link");
3946 pp_end_url (pp: &pp);
3947 ASSERT_STREQ ("\33]8;;http://example.com\33\\This is a link\33]8;;\33\\",
3948 pp_formatted_text (&pp));
3949 }
3950
3951 {
3952 pretty_printer pp;
3953 pp.set_url_format (URL_FORMAT_BEL);
3954 pp_begin_url (pp: &pp, url: "http://example.com");
3955 pp_string (pp: &pp, str: "This is a link");
3956 pp_end_url (pp: &pp);
3957 ASSERT_STREQ ("\33]8;;http://example.com\aThis is a link\33]8;;\a",
3958 pp_formatted_text (&pp));
3959 }
3960}
3961
3962static void
3963test_urls_from_braces ()
3964{
3965 {
3966 pretty_printer pp;
3967 pp.set_url_format (URL_FORMAT_NONE);
3968 pp_printf (pp: &pp, msg: "before %{text%} after",
3969 "http://example.com");
3970 ASSERT_STREQ ("before text after",
3971 pp_formatted_text (&pp));
3972 }
3973
3974 {
3975 pretty_printer pp;
3976 pp.set_url_format (URL_FORMAT_ST);
3977 pp_printf (pp: &pp, msg: "before %{text%} after",
3978 "http://example.com");
3979 ASSERT_STREQ ("before \33]8;;http://example.com\33\\text\33]8;;\33\\ after",
3980 pp_formatted_text (&pp));
3981 }
3982
3983 {
3984 pretty_printer pp;
3985 pp.set_url_format (URL_FORMAT_BEL);
3986 pp_printf (pp: &pp, msg: "before %{text%} after",
3987 "http://example.com");
3988 ASSERT_STREQ ("before \33]8;;http://example.com\atext\33]8;;\a after",
3989 pp_formatted_text (&pp));
3990 }
3991}
3992
3993/* Verify that we gracefully reject null URLs. */
3994
3995static void
3996test_null_urls ()
3997{
3998 {
3999 pretty_printer pp;
4000 pp.set_url_format (URL_FORMAT_NONE);
4001 pp_begin_url (pp: &pp, url: nullptr);
4002 pp_string (pp: &pp, str: "This isn't a link");
4003 pp_end_url (pp: &pp);
4004 ASSERT_STREQ ("This isn't a link",
4005 pp_formatted_text (&pp));
4006 }
4007
4008 {
4009 pretty_printer pp;
4010 pp.set_url_format (URL_FORMAT_ST);
4011 pp_begin_url (pp: &pp, url: nullptr);
4012 pp_string (pp: &pp, str: "This isn't a link");
4013 pp_end_url (pp: &pp);
4014 ASSERT_STREQ ("This isn't a link",
4015 pp_formatted_text (&pp));
4016 }
4017
4018 {
4019 pretty_printer pp;
4020 pp.set_url_format (URL_FORMAT_BEL);
4021 pp_begin_url (pp: &pp, url: nullptr);
4022 pp_string (pp: &pp, str: "This isn't a link");
4023 pp_end_url (pp: &pp);
4024 ASSERT_STREQ ("This isn't a link",
4025 pp_formatted_text (&pp));
4026 }
4027}
4028
4029/* Verify that URLification works as expected. */
4030
4031static void
4032pp_printf_with_urlifier (pretty_printer *pp,
4033 const urlifier *urlifier,
4034 const char *msg, ...)
4035{
4036 va_list ap;
4037
4038 va_start (ap, msg);
4039 text_info text (msg, &ap, errno);
4040 pp_format (pp, text: &text);
4041 pp_output_formatted_text (pp, urlifier);
4042 va_end (ap);
4043}
4044
4045static void
4046test_urlification ()
4047{
4048 class test_urlifier : public urlifier
4049 {
4050 public:
4051 char *
4052 get_url_for_quoted_text (const char *p, size_t sz) const final override
4053 {
4054 if (!strncmp (s1: p, s2: "-foption", n: sz))
4055 return xstrdup ("http://example.com");
4056 return nullptr;
4057 }
4058 };
4059
4060 auto_fix_quotes fix_quotes;
4061 const test_urlifier urlifier;
4062
4063 /* Uses of "%<" and "%>". */
4064 {
4065 {
4066 pretty_printer pp;
4067 pp.set_url_format (URL_FORMAT_NONE);
4068 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4069 msg: "foo %<-foption%> %<unrecognized%> bar");
4070 ASSERT_STREQ ("foo `-foption' `unrecognized' bar",
4071 pp_formatted_text (&pp));
4072 }
4073 {
4074 pretty_printer pp;
4075 pp.set_url_format (URL_FORMAT_ST);
4076 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4077 msg: "foo %<-foption%> %<unrecognized%> bar");
4078 ASSERT_STREQ
4079 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
4080 " `unrecognized' bar",
4081 pp_formatted_text (&pp));
4082 }
4083 {
4084 pretty_printer pp;
4085 pp.set_url_format (URL_FORMAT_BEL);
4086 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4087 msg: "foo %<-foption%> %<unrecognized%> bar");
4088 ASSERT_STREQ
4089 ("foo `\33]8;;http://example.com\a-foption\33]8;;\a'"
4090 " `unrecognized' bar",
4091 pp_formatted_text (&pp));
4092 }
4093 }
4094
4095 /* Use of "%qs". */
4096 {
4097 pretty_printer pp;
4098 pp.set_url_format (URL_FORMAT_ST);
4099 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4100 msg: "foo %qs %qs bar",
4101 "-foption", "unrecognized");
4102 ASSERT_STREQ
4103 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
4104 " `unrecognized' bar",
4105 pp_formatted_text (&pp));
4106 }
4107
4108 /* Mixed usage of %< and %s, where the quoted string is built between
4109 a mixture of phase 1 and phase 2. */
4110 {
4111 pretty_printer pp;
4112 pp.set_url_format (URL_FORMAT_ST);
4113 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4114 msg: "foo %<-f%s%> bar",
4115 "option");
4116 ASSERT_STREQ
4117 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4118 pp_formatted_text (&pp));
4119 }
4120
4121 /* Likewise, where there is trailing phase 1 content within the
4122 quoted region. */
4123 {
4124 pretty_printer pp;
4125 pp.set_url_format (URL_FORMAT_ST);
4126 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4127 msg: "foo %<-f%sion%> bar %<-f%sion%> baz",
4128 "opt", "opt");
4129 ASSERT_STREQ
4130 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
4131 pp_formatted_text (&pp));
4132 }
4133
4134 /* Likewise. */
4135 {
4136 pretty_printer pp;
4137 pp.set_url_format (URL_FORMAT_ST);
4138 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4139 msg: "foo %<%sption%> bar %<-f%sion%> baz",
4140 "-fo", "opt");
4141 ASSERT_STREQ
4142 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
4143 pp_formatted_text (&pp));
4144 }
4145
4146 /* Another mixed usage of %< and %s, where the quoted string is built
4147 between a mixture of phase 1 and multiple phase 2. */
4148 {
4149 pretty_printer pp;
4150 pp.set_url_format (URL_FORMAT_ST);
4151 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4152 msg: "foo %<-f%s%s%> bar",
4153 "opt", "ion");
4154 ASSERT_STREQ
4155 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4156 pp_formatted_text (&pp));
4157 }
4158
4159 /* Mixed usage of %< and %s with a prefix. */
4160 {
4161 pretty_printer pp;
4162 pp.set_url_format (URL_FORMAT_ST);
4163 pp_set_prefix (pp: &pp, prefix: xstrdup ("PREFIX"));
4164 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4165 msg: "foo %<-f%s%> bar",
4166 "option");
4167 ASSERT_STREQ
4168 ("PREFIXfoo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4169 pp_formatted_text (&pp));
4170 }
4171
4172 /* Example of mixed %< and %s with numbered args. */
4173 {
4174 pretty_printer pp;
4175 pp.set_url_format (URL_FORMAT_ST);
4176 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4177 msg: "foo %<-f%2$st%1$sn%> bar",
4178 "io", "op");
4179 ASSERT_STREQ
4180 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4181 pp_formatted_text (&pp));
4182 }
4183
4184 /* Example of %e. */
4185 {
4186 pretty_printer pp;
4187 pp.set_url_format (URL_FORMAT_ST);
4188 pp_element_quoted_string elem ("-foption");
4189 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4190 msg: "foo %e bar",
4191 &elem);
4192 ASSERT_STREQ
4193 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4194 pp_formatted_text (&pp));
4195 }
4196
4197 /* Test the example from pretty-print-format-impl.h. */
4198 {
4199 pretty_printer pp;
4200 pp.set_url_format (URL_FORMAT_ST);
4201 pp_printf_with_urlifier (pp: &pp, urlifier: &urlifier,
4202 msg: "foo: %i, bar: %s, option: %qs",
4203 42, "baz", "-foption");
4204 ASSERT_STREQ (pp_formatted_text (&pp),
4205 "foo: 42, bar: baz, option:"
4206 " `]8;;http://example.com\\-foption]8;;\\'");
4207 }
4208}
4209
4210/* Test multibyte awareness. */
4211static void test_utf8 ()
4212{
4213
4214 /* Check that pp_quoted_string leaves valid UTF-8 alone. */
4215 {
4216 pretty_printer pp;
4217 const char *s = "\xf0\x9f\x98\x82";
4218 pp_quoted_string (pp: &pp, str: s);
4219 ASSERT_STREQ (pp_formatted_text (&pp), s);
4220 }
4221
4222 /* Check that pp_quoted_string escapes non-UTF-8 nonprintable bytes. */
4223 {
4224 pretty_printer pp;
4225 pp_quoted_string (pp: &pp, str: "\xf0!\x9f\x98\x82");
4226 ASSERT_STREQ (pp_formatted_text (&pp),
4227 "\\xf0!\\x9f\\x98\\x82");
4228 }
4229
4230 /* Check that pp_character will line-wrap at the beginning of a UTF-8
4231 sequence, but not in the middle. */
4232 {
4233 pretty_printer pp (3);
4234 const char s[] = "---\xf0\x9f\x98\x82";
4235 for (int i = 0; i != sizeof (s) - 1; ++i)
4236 pp_character (pp: &pp, c: s[i]);
4237 pp_newline (pp: &pp);
4238 for (int i = 1; i != sizeof (s) - 1; ++i)
4239 pp_character (pp: &pp, c: s[i]);
4240 pp_character (pp: &pp, c: '-');
4241 ASSERT_STREQ (pp_formatted_text (&pp),
4242 "---\n"
4243 "\xf0\x9f\x98\x82\n"
4244 "--\xf0\x9f\x98\x82\n"
4245 "-");
4246 }
4247
4248}
4249
4250/* Verify that class comma_separated_quoted_strings works as expected. */
4251
4252static void
4253test_comma_separated_quoted_strings ()
4254{
4255 auto_fix_quotes fix_quotes;
4256
4257 auto_vec<const char *> none;
4258 pp_markup::comma_separated_quoted_strings e_none (none);
4259
4260 auto_vec<const char *> one;
4261 one.safe_push (obj: "one");
4262 pp_markup::comma_separated_quoted_strings e_one (one);
4263
4264 auto_vec<const char *> many;
4265 many.safe_push (obj: "0");
4266 many.safe_push (obj: "1");
4267 many.safe_push (obj: "2");
4268 pp_markup::comma_separated_quoted_strings e_many (many);
4269
4270 ASSERT_PP_FORMAT_3 ("none: () one: (`one') many: (`0', `1', `2')",
4271 "none: (%e) one: (%e) many: (%e)",
4272 &e_none, &e_one, &e_many);
4273 assert_pp_format_colored (SELFTEST_LOCATION,
4274 expected: "one: (`one')",
4275 fmt: "one: (%e)",
4276 &e_one);
4277}
4278
4279/* Run all of the selftests within this file. */
4280
4281void
4282pretty_print_cc_tests ()
4283{
4284 test_basic_printing ();
4285 test_pp_format ();
4286 test_merge_consecutive_text_tokens ();
4287 test_custom_tokens_1 ();
4288 test_custom_tokens_2 ();
4289 test_pp_format_stack ();
4290 test_pp_printf_within_pp_element ();
4291 test_prefixes_and_wrapping ();
4292 test_urls ();
4293 test_urls_from_braces ();
4294 test_null_urls ();
4295 test_urlification ();
4296 test_utf8 ();
4297 test_comma_separated_quoted_strings ();
4298}
4299
4300} // namespace selftest
4301
4302#endif /* CHECKING_P */
4303

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of gcc/pretty-print.cc