testsuite: Fix up gcc.dg/pr113693.c for ia32
[official-gcc.git] / gcc / pretty-print.cc
blob825175787a845385dfad73e54017c6c42b29eed7
1 /* Various declarations for language-independent pretty-print subroutines.
2 Copyright (C) 2003-2024 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
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-urlifier.h"
28 #include "diagnostic-color.h"
29 #include "diagnostic-event-id.h"
30 #include "selftest.h"
32 #if HAVE_ICONV
33 #include <iconv.h>
34 #endif
36 #ifdef __MINGW32__
38 /* Replacement for fputs() that handles ANSI escape codes on Windows NT.
39 Contributed by: Liu Hao (lh_mouse at 126 dot com)
41 XXX: This file is compiled into libcommon.a that will be self-contained.
42 It looks like that these functions can be put nowhere else. */
44 #include <io.h>
45 #define WIN32_LEAN_AND_MEAN 1
46 #include <windows.h>
48 /* Write all bytes in [s,s+n) into the specified stream.
49 Errors are ignored. */
50 static void
51 write_all (HANDLE h, const char *s, size_t n)
53 size_t rem = n;
54 DWORD step;
56 while (rem != 0)
58 if (rem <= UINT_MAX)
59 step = rem;
60 else
61 step = UINT_MAX;
62 if (!WriteFile (h, s + n - rem, step, &step, NULL))
63 break;
64 rem -= step;
68 /* Find the beginning of an escape sequence.
69 There are two cases:
70 1. If the sequence begins with an ESC character (0x1B) and a second
71 character X in [0x40,0x5F], returns X and stores a pointer to
72 the third character into *head.
73 2. If the sequence begins with a character X in [0x80,0x9F], returns
74 (X-0x40) and stores a pointer to the second character into *head.
75 Stores the number of ESC character(s) in *prefix_len.
76 Returns 0 if no such sequence can be found. */
77 static int
78 find_esc_head (int *prefix_len, const char **head, const char *str)
80 int c;
81 const char *r = str;
82 int escaped = 0;
84 for (;;)
86 c = (unsigned char) *r;
87 if (c == 0)
89 /* Not found. */
90 return 0;
92 if (escaped && 0x40 <= c && c <= 0x5F)
94 /* Found (case 1). */
95 *prefix_len = 2;
96 *head = r + 1;
97 return c;
99 if (0x80 <= c && c <= 0x9F)
101 /* Found (case 2). */
102 *prefix_len = 1;
103 *head = r + 1;
104 return c - 0x40;
106 ++r;
107 escaped = c == 0x1B;
111 /* Find the terminator of an escape sequence.
112 str should be the value stored in *head by a previous successful
113 call to find_esc_head().
114 Returns 0 if no such sequence can be found. */
115 static int
116 find_esc_terminator (const char **term, const char *str)
118 int c;
119 const char *r = str;
121 for (;;)
123 c = (unsigned char) *r;
124 if (c == 0)
126 /* Not found. */
127 return 0;
129 if (0x40 <= c && c <= 0x7E)
131 /* Found. */
132 *term = r;
133 return c;
135 ++r;
139 /* Handle a sequence of codes. Sequences that are invalid, reserved,
140 unrecognized or unimplemented are ignored silently.
141 There isn't much we can do because of lameness of Windows consoles. */
142 static void
143 eat_esc_sequence (HANDLE h, int esc_code,
144 const char *esc_head, const char *esc_term)
146 /* Numbers in an escape sequence cannot be negative, because
147 a minus sign in the middle of it would have terminated it. */
148 long n1, n2;
149 char *eptr, *delim;
150 CONSOLE_SCREEN_BUFFER_INFO sb;
151 COORD cr;
152 /* ED and EL parameters. */
153 DWORD cnt, step;
154 long rows;
155 /* SGR parameters. */
156 WORD attrib_add, attrib_rm;
157 const char *param;
159 switch (MAKEWORD (esc_code, *esc_term))
161 /* ESC [ n1 'A'
162 Move the cursor up by n1 characters. */
163 case MAKEWORD ('[', 'A'):
164 if (esc_head == esc_term)
165 n1 = 1;
166 else
168 n1 = strtol (esc_head, &eptr, 10);
169 if (eptr != esc_term)
170 break;
173 if (GetConsoleScreenBufferInfo (h, &sb))
175 cr = sb.dwCursorPosition;
176 /* Stop at the topmost boundary. */
177 if (cr.Y > n1)
178 cr.Y -= n1;
179 else
180 cr.Y = 0;
181 SetConsoleCursorPosition (h, cr);
183 break;
185 /* ESC [ n1 'B'
186 Move the cursor down by n1 characters. */
187 case MAKEWORD ('[', 'B'):
188 if (esc_head == esc_term)
189 n1 = 1;
190 else
192 n1 = strtol (esc_head, &eptr, 10);
193 if (eptr != esc_term)
194 break;
197 if (GetConsoleScreenBufferInfo (h, &sb))
199 cr = sb.dwCursorPosition;
200 /* Stop at the bottommost boundary. */
201 if (sb.dwSize.Y - cr.Y > n1)
202 cr.Y += n1;
203 else
204 cr.Y = sb.dwSize.Y;
205 SetConsoleCursorPosition (h, cr);
207 break;
209 /* ESC [ n1 'C'
210 Move the cursor right by n1 characters. */
211 case MAKEWORD ('[', 'C'):
212 if (esc_head == esc_term)
213 n1 = 1;
214 else
216 n1 = strtol (esc_head, &eptr, 10);
217 if (eptr != esc_term)
218 break;
221 if (GetConsoleScreenBufferInfo (h, &sb))
223 cr = sb.dwCursorPosition;
224 /* Stop at the rightmost boundary. */
225 if (sb.dwSize.X - cr.X > n1)
226 cr.X += n1;
227 else
228 cr.X = sb.dwSize.X;
229 SetConsoleCursorPosition (h, cr);
231 break;
233 /* ESC [ n1 'D'
234 Move the cursor left by n1 characters. */
235 case MAKEWORD ('[', 'D'):
236 if (esc_head == esc_term)
237 n1 = 1;
238 else
240 n1 = strtol (esc_head, &eptr, 10);
241 if (eptr != esc_term)
242 break;
245 if (GetConsoleScreenBufferInfo (h, &sb))
247 cr = sb.dwCursorPosition;
248 /* Stop at the leftmost boundary. */
249 if (cr.X > n1)
250 cr.X -= n1;
251 else
252 cr.X = 0;
253 SetConsoleCursorPosition (h, cr);
255 break;
257 /* ESC [ n1 'E'
258 Move the cursor to the beginning of the n1-th line downwards. */
259 case MAKEWORD ('[', 'E'):
260 if (esc_head == esc_term)
261 n1 = 1;
262 else
264 n1 = strtol (esc_head, &eptr, 10);
265 if (eptr != esc_term)
266 break;
269 if (GetConsoleScreenBufferInfo (h, &sb))
271 cr = sb.dwCursorPosition;
272 cr.X = 0;
273 /* Stop at the bottommost boundary. */
274 if (sb.dwSize.Y - cr.Y > n1)
275 cr.Y += n1;
276 else
277 cr.Y = sb.dwSize.Y;
278 SetConsoleCursorPosition (h, cr);
280 break;
282 /* ESC [ n1 'F'
283 Move the cursor to the beginning of the n1-th line upwards. */
284 case MAKEWORD ('[', 'F'):
285 if (esc_head == esc_term)
286 n1 = 1;
287 else
289 n1 = strtol (esc_head, &eptr, 10);
290 if (eptr != esc_term)
291 break;
294 if (GetConsoleScreenBufferInfo (h, &sb))
296 cr = sb.dwCursorPosition;
297 cr.X = 0;
298 /* Stop at the topmost boundary. */
299 if (cr.Y > n1)
300 cr.Y -= n1;
301 else
302 cr.Y = 0;
303 SetConsoleCursorPosition (h, cr);
305 break;
307 /* ESC [ n1 'G'
308 Move the cursor to the (1-based) n1-th column. */
309 case MAKEWORD ('[', 'G'):
310 if (esc_head == esc_term)
311 n1 = 1;
312 else
314 n1 = strtol (esc_head, &eptr, 10);
315 if (eptr != esc_term)
316 break;
319 if (GetConsoleScreenBufferInfo (h, &sb))
321 cr = sb.dwCursorPosition;
322 n1 -= 1;
323 /* Stop at the leftmost or rightmost boundary. */
324 if (n1 < 0)
325 cr.X = 0;
326 else if (n1 > sb.dwSize.X)
327 cr.X = sb.dwSize.X;
328 else
329 cr.X = n1;
330 SetConsoleCursorPosition (h, cr);
332 break;
334 /* ESC [ n1 ';' n2 'H'
335 ESC [ n1 ';' n2 'f'
336 Move the cursor to the (1-based) n1-th row and
337 (also 1-based) n2-th column. */
338 case MAKEWORD ('[', 'H'):
339 case MAKEWORD ('[', 'f'):
340 if (esc_head == esc_term)
342 /* Both parameters are omitted and set to 1 by default. */
343 n1 = 1;
344 n2 = 1;
346 else if (!(delim = (char *) memchr (esc_head, ';',
347 esc_term - esc_head)))
349 /* Only the first parameter is given. The second one is
350 set to 1 by default. */
351 n1 = strtol (esc_head, &eptr, 10);
352 if (eptr != esc_term)
353 break;
354 n2 = 1;
356 else
358 /* Both parameters are given. The first one shall be
359 terminated by the semicolon. */
360 n1 = strtol (esc_head, &eptr, 10);
361 if (eptr != delim)
362 break;
363 n2 = strtol (delim + 1, &eptr, 10);
364 if (eptr != esc_term)
365 break;
368 if (GetConsoleScreenBufferInfo (h, &sb))
370 cr = sb.dwCursorPosition;
371 n1 -= 1;
372 n2 -= 1;
373 /* The cursor position shall be relative to the view coord of
374 the console window, which is usually smaller than the actual
375 buffer. FWIW, the 'appropriate' solution will be shrinking
376 the buffer to match the size of the console window,
377 destroying scrollback in the process. */
378 n1 += sb.srWindow.Top;
379 n2 += sb.srWindow.Left;
380 /* Stop at the topmost or bottommost boundary. */
381 if (n1 < 0)
382 cr.Y = 0;
383 else if (n1 > sb.dwSize.Y)
384 cr.Y = sb.dwSize.Y;
385 else
386 cr.Y = n1;
387 /* Stop at the leftmost or rightmost boundary. */
388 if (n2 < 0)
389 cr.X = 0;
390 else if (n2 > sb.dwSize.X)
391 cr.X = sb.dwSize.X;
392 else
393 cr.X = n2;
394 SetConsoleCursorPosition (h, cr);
396 break;
398 /* ESC [ n1 'J'
399 Erase display. */
400 case MAKEWORD ('[', 'J'):
401 if (esc_head == esc_term)
402 /* This is one of the very few codes whose parameters have
403 a default value of zero. */
404 n1 = 0;
405 else
407 n1 = strtol (esc_head, &eptr, 10);
408 if (eptr != esc_term)
409 break;
412 if (GetConsoleScreenBufferInfo (h, &sb))
414 /* The cursor is not necessarily in the console window, which
415 makes the behavior of this code harder to define. */
416 switch (n1)
418 case 0:
419 /* If the cursor is in or above the window, erase from
420 it to the bottom of the window; otherwise, do nothing. */
421 cr = sb.dwCursorPosition;
422 cnt = sb.dwSize.X - sb.dwCursorPosition.X;
423 rows = sb.srWindow.Bottom - sb.dwCursorPosition.Y;
424 break;
425 case 1:
426 /* If the cursor is in or under the window, erase from
427 it to the top of the window; otherwise, do nothing. */
428 cr.X = 0;
429 cr.Y = sb.srWindow.Top;
430 cnt = sb.dwCursorPosition.X + 1;
431 rows = sb.dwCursorPosition.Y - sb.srWindow.Top;
432 break;
433 case 2:
434 /* Erase the entire window. */
435 cr.X = sb.srWindow.Left;
436 cr.Y = sb.srWindow.Top;
437 cnt = 0;
438 rows = sb.srWindow.Bottom - sb.srWindow.Top + 1;
439 break;
440 default:
441 /* Erase the entire buffer. */
442 cr.X = 0;
443 cr.Y = 0;
444 cnt = 0;
445 rows = sb.dwSize.Y;
446 break;
448 if (rows < 0)
449 break;
450 cnt += rows * sb.dwSize.X;
451 FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
452 FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
454 break;
456 /* ESC [ n1 'K'
457 Erase line. */
458 case MAKEWORD ('[', 'K'):
459 if (esc_head == esc_term)
460 /* This is one of the very few codes whose parameters have
461 a default value of zero. */
462 n1 = 0;
463 else
465 n1 = strtol (esc_head, &eptr, 10);
466 if (eptr != esc_term)
467 break;
470 if (GetConsoleScreenBufferInfo (h, &sb))
472 switch (n1)
474 case 0:
475 /* Erase from the cursor to the end. */
476 cr = sb.dwCursorPosition;
477 cnt = sb.dwSize.X - sb.dwCursorPosition.X;
478 break;
479 case 1:
480 /* Erase from the cursor to the beginning. */
481 cr = sb.dwCursorPosition;
482 cr.X = 0;
483 cnt = sb.dwCursorPosition.X + 1;
484 break;
485 default:
486 /* Erase the entire line. */
487 cr = sb.dwCursorPosition;
488 cr.X = 0;
489 cnt = sb.dwSize.X;
490 break;
492 FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
493 FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
495 break;
497 /* ESC [ n1 ';' n2 'm'
498 Set SGR parameters. Zero or more parameters will follow. */
499 case MAKEWORD ('[', 'm'):
500 attrib_add = 0;
501 attrib_rm = 0;
502 if (esc_head == esc_term)
504 /* When no parameter is given, reset the console. */
505 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
506 | FOREGROUND_BLUE);
507 attrib_rm = -1; /* Removes everything. */
508 goto sgr_set_it;
510 param = esc_head;
513 /* Parse a parameter. */
514 n1 = strtol (param, &eptr, 10);
515 if (*eptr != ';' && eptr != esc_term)
516 goto sgr_set_it;
518 switch (n1)
520 case 0:
521 /* Reset. */
522 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
523 | FOREGROUND_BLUE);
524 attrib_rm = -1; /* Removes everything. */
525 break;
526 case 1:
527 /* Bold. */
528 attrib_add |= FOREGROUND_INTENSITY;
529 break;
530 case 4:
531 /* Underline. */
532 attrib_add |= COMMON_LVB_UNDERSCORE;
533 break;
534 case 5:
535 /* Blink. */
536 /* XXX: It is not BLINKING at all! */
537 attrib_add |= BACKGROUND_INTENSITY;
538 break;
539 case 7:
540 /* Reverse. */
541 attrib_add |= COMMON_LVB_REVERSE_VIDEO;
542 break;
543 case 22:
544 /* No bold. */
545 attrib_add &= ~FOREGROUND_INTENSITY;
546 attrib_rm |= FOREGROUND_INTENSITY;
547 break;
548 case 24:
549 /* No underline. */
550 attrib_add &= ~COMMON_LVB_UNDERSCORE;
551 attrib_rm |= COMMON_LVB_UNDERSCORE;
552 break;
553 case 25:
554 /* No blink. */
555 /* XXX: It is not BLINKING at all! */
556 attrib_add &= ~BACKGROUND_INTENSITY;
557 attrib_rm |= BACKGROUND_INTENSITY;
558 break;
559 case 27:
560 /* No reverse. */
561 attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
562 attrib_rm |= COMMON_LVB_REVERSE_VIDEO;
563 break;
564 case 30:
565 case 31:
566 case 32:
567 case 33:
568 case 34:
569 case 35:
570 case 36:
571 case 37:
572 /* Foreground color. */
573 attrib_add &= ~(FOREGROUND_RED | FOREGROUND_GREEN
574 | FOREGROUND_BLUE);
575 n1 -= 30;
576 if (n1 & 1)
577 attrib_add |= FOREGROUND_RED;
578 if (n1 & 2)
579 attrib_add |= FOREGROUND_GREEN;
580 if (n1 & 4)
581 attrib_add |= FOREGROUND_BLUE;
582 attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
583 | FOREGROUND_BLUE);
584 break;
585 case 38:
586 /* Reserved for extended foreground color.
587 Don't know how to handle parameters remaining.
588 Bail out. */
589 goto sgr_set_it;
590 case 39:
591 /* Reset foreground color. */
592 /* Set to grey. */
593 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
594 | FOREGROUND_BLUE);
595 attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
596 | FOREGROUND_BLUE);
597 break;
598 case 40:
599 case 41:
600 case 42:
601 case 43:
602 case 44:
603 case 45:
604 case 46:
605 case 47:
606 /* Background color. */
607 attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
608 | BACKGROUND_BLUE);
609 n1 -= 40;
610 if (n1 & 1)
611 attrib_add |= BACKGROUND_RED;
612 if (n1 & 2)
613 attrib_add |= BACKGROUND_GREEN;
614 if (n1 & 4)
615 attrib_add |= BACKGROUND_BLUE;
616 attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
617 | BACKGROUND_BLUE);
618 break;
619 case 48:
620 /* Reserved for extended background color.
621 Don't know how to handle parameters remaining.
622 Bail out. */
623 goto sgr_set_it;
624 case 49:
625 /* Reset background color. */
626 /* Set to black. */
627 attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
628 | BACKGROUND_BLUE);
629 attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
630 | BACKGROUND_BLUE);
631 break;
634 /* Prepare the next parameter. */
635 param = eptr + 1;
637 while (param != esc_term);
639 sgr_set_it:
640 /* 0xFFFF removes everything. If it is not the case,
641 care must be taken to preserve old attributes. */
642 if (attrib_rm != 0xFFFF && GetConsoleScreenBufferInfo (h, &sb))
644 attrib_add |= sb.wAttributes & ~attrib_rm;
646 if (attrib_add & COMMON_LVB_REVERSE_VIDEO)
648 /* COMMON_LVB_REVERSE_VIDEO is only effective for DBCS.
649 * Swap foreground and background colors by hand.
651 attrib_add = (attrib_add & 0xFF00)
652 | ((attrib_add & 0x00F0) >> 4)
653 | ((attrib_add & 0x000F) << 4);
654 attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
656 SetConsoleTextAttribute (h, attrib_add);
657 break;
662 mingw_ansi_fputs (const char *str, FILE *fp)
664 const char *read = str;
665 HANDLE h;
666 DWORD mode;
667 int esc_code, prefix_len;
668 const char *esc_head, *esc_term;
670 h = (HANDLE) _get_osfhandle (_fileno (fp));
671 if (h == INVALID_HANDLE_VALUE)
672 return EOF;
674 /* Don't mess up stdio functions with Windows APIs. */
675 fflush (fp);
677 if (GetConsoleMode (h, &mode))
678 /* If it is a console, translate ANSI escape codes as needed. */
679 for (;;)
681 if ((esc_code = find_esc_head (&prefix_len, &esc_head, read)) == 0)
683 /* Write all remaining characters, then exit. */
684 write_all (h, read, strlen (read));
685 break;
687 if (find_esc_terminator (&esc_term, esc_head) == 0)
688 /* Ignore incomplete escape sequences at the moment.
689 FIXME: The escape state shall be cached for further calls
690 to this function. */
691 break;
692 write_all (h, read, esc_head - prefix_len - read);
693 eat_esc_sequence (h, esc_code, esc_head, esc_term);
694 read = esc_term + 1;
696 else
697 /* If it is not a console, write everything as-is. */
698 write_all (h, read, strlen (read));
700 return 1;
703 #endif /* __MINGW32__ */
705 static int
706 decode_utf8_char (const unsigned char *, size_t len, unsigned int *);
707 static void pp_quoted_string (pretty_printer *, const char *, size_t = -1);
709 /* Overwrite the given location/range within this text_info's rich_location.
710 For use e.g. when implementing "+" in client format decoders. */
712 void
713 text_info::set_location (unsigned int idx, location_t loc,
714 enum range_display_kind range_display_kind)
716 gcc_checking_assert (m_richloc);
717 m_richloc->set_range (idx, loc, range_display_kind);
720 location_t
721 text_info::get_location (unsigned int index_of_location) const
723 gcc_checking_assert (m_richloc);
725 if (index_of_location == 0)
726 return m_richloc->get_loc ();
727 else
728 return UNKNOWN_LOCATION;
731 // Default construct an output buffer.
733 output_buffer::output_buffer ()
734 : formatted_obstack (),
735 chunk_obstack (),
736 obstack (&formatted_obstack),
737 cur_chunk_array (),
738 stream (stderr),
739 line_length (),
740 digit_buffer (),
741 flush_p (true)
743 obstack_init (&formatted_obstack);
744 obstack_init (&chunk_obstack);
747 // Release resources owned by an output buffer at the end of lifetime.
749 output_buffer::~output_buffer ()
751 obstack_free (&chunk_obstack, NULL);
752 obstack_free (&formatted_obstack, NULL);
756 /* Format an integer given by va_arg (ARG, type-specifier T) where
757 type-specifier is a precision modifier as indicated by PREC. F is
758 a string used to construct the appropriate format-specifier. */
759 #define pp_integer_with_precision(PP, ARG, PREC, T, F) \
760 do \
761 switch (PREC) \
763 case 0: \
764 pp_scalar (PP, "%" F, va_arg (ARG, T)); \
765 break; \
767 case 1: \
768 pp_scalar (PP, "%l" F, va_arg (ARG, long T)); \
769 break; \
771 case 2: \
772 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
773 va_arg (ARG, long long T)); \
774 break; \
776 case 3: \
777 if (T (-1) < T (0)) \
778 pp_scalar (PP, "%" GCC_PRISZ F, \
779 (fmt_size_t) va_arg (ARG, ssize_t)); \
780 else \
781 pp_scalar (PP, "%" GCC_PRISZ F, \
782 (fmt_size_t) va_arg (ARG, size_t)); \
783 break; \
785 case 4: \
786 if (sizeof (ptrdiff_t) <= sizeof (int)) \
787 pp_scalar (PP, "%" F, \
788 (int) va_arg (ARG, ptrdiff_t)); \
789 else if (sizeof (ptrdiff_t) <= sizeof (long)) \
790 pp_scalar (PP, "%l" F, \
791 (long int) va_arg (ARG, ptrdiff_t)); \
792 else \
793 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
794 (long long int) \
795 va_arg (ARG, ptrdiff_t)); \
796 break; \
798 default: \
799 break; \
801 while (0)
804 /* Subroutine of pp_set_maximum_length. Set up PRETTY-PRINTER's
805 internal maximum characters per line. */
806 static void
807 pp_set_real_maximum_length (pretty_printer *pp)
809 /* If we're told not to wrap lines then do the obvious thing. In case
810 we'll emit prefix only once per message, it is appropriate
811 not to increase unnecessarily the line-length cut-off. */
812 if (!pp_is_wrapping_line (pp)
813 || pp_prefixing_rule (pp) == DIAGNOSTICS_SHOW_PREFIX_ONCE
814 || pp_prefixing_rule (pp) == DIAGNOSTICS_SHOW_PREFIX_NEVER)
815 pp->maximum_length = pp_line_cutoff (pp);
816 else
818 int prefix_length = pp->prefix ? strlen (pp->prefix) : 0;
819 /* If the prefix is ridiculously too long, output at least
820 32 characters. */
821 if (pp_line_cutoff (pp) - prefix_length < 32)
822 pp->maximum_length = pp_line_cutoff (pp) + 32;
823 else
824 pp->maximum_length = pp_line_cutoff (pp);
828 /* Clear PRETTY-PRINTER's output state. */
829 static inline void
830 pp_clear_state (pretty_printer *pp)
832 pp->emitted_prefix = false;
833 pp_indentation (pp) = 0;
836 /* Print X to PP in decimal. */
837 template<unsigned int N, typename T>
838 void
839 pp_wide_integer (pretty_printer *pp, const poly_int<N, T> &x)
841 if (x.is_constant ())
842 pp_wide_integer (pp, x.coeffs[0]);
843 else
845 pp_left_bracket (pp);
846 for (unsigned int i = 0; i < N; ++i)
848 if (i != 0)
849 pp_comma (pp);
850 pp_wide_integer (pp, x.coeffs[i]);
852 pp_right_bracket (pp);
856 template void pp_wide_integer (pretty_printer *, const poly_uint16 &);
857 template void pp_wide_integer (pretty_printer *, const poly_int64 &);
858 template void pp_wide_integer (pretty_printer *, const poly_uint64 &);
860 /* Flush the formatted text of PRETTY-PRINTER onto the attached stream. */
861 void
862 pp_write_text_to_stream (pretty_printer *pp)
864 const char *text = pp_formatted_text (pp);
865 #ifdef __MINGW32__
866 mingw_ansi_fputs (text, pp_buffer (pp)->stream);
867 #else
868 fputs (text, pp_buffer (pp)->stream);
869 #endif
870 pp_clear_output_area (pp);
873 /* As pp_write_text_to_stream, but for GraphViz label output.
875 Flush the formatted text of pretty-printer PP onto the attached stream.
876 Replace characters in PPF that have special meaning in a GraphViz .dot
877 file.
879 This routine is not very fast, but it doesn't have to be as this is only
880 be used by routines dumping intermediate representations in graph form. */
882 void
883 pp_write_text_as_dot_label_to_stream (pretty_printer *pp, bool for_record)
885 const char *text = pp_formatted_text (pp);
886 const char *p = text;
887 FILE *fp = pp_buffer (pp)->stream;
889 for (;*p; p++)
891 bool escape_char;
892 switch (*p)
894 /* Print newlines as a left-aligned newline. */
895 case '\n':
896 fputs ("\\l", fp);
897 escape_char = true;
898 break;
900 /* The following characters are only special for record-shape nodes. */
901 case '|':
902 case '{':
903 case '}':
904 case '<':
905 case '>':
906 case ' ':
907 escape_char = for_record;
908 break;
910 /* The following characters always have to be escaped
911 for use in labels. */
912 case '\\':
913 /* There is a bug in some (f.i. 2.36.0) versions of graphiz
914 ( http://www.graphviz.org/mantisbt/view.php?id=2524 ) related to
915 backslash as last char in label. Let's avoid triggering it. */
916 gcc_assert (*(p + 1) != '\0');
917 /* Fall through. */
918 case '"':
919 escape_char = true;
920 break;
922 default:
923 escape_char = false;
924 break;
927 if (escape_char)
928 fputc ('\\', fp);
930 fputc (*p, fp);
933 pp_clear_output_area (pp);
936 /* As pp_write_text_to_stream, but for GraphViz HTML-like strings.
938 Flush the formatted text of pretty-printer PP onto the attached stream,
939 escaping these characters
940 " & < >
941 using XML escape sequences.
943 http://www.graphviz.org/doc/info/lang.html#html states:
944 special XML escape sequences for ", &, <, and > may be necessary in
945 order to embed these characters in attribute values or raw text
946 This doesn't list "'" (which would normally be escaped in XML
947 as "&apos;" or in HTML as "&#39;");.
949 Experiments show that escaping "'" doesn't seem to be necessary. */
951 void
952 pp_write_text_as_html_like_dot_to_stream (pretty_printer *pp)
954 const char *text = pp_formatted_text (pp);
955 const char *p = text;
956 FILE *fp = pp_buffer (pp)->stream;
958 for (;*p; p++)
960 switch (*p)
962 case '"':
963 fputs ("&quot;", fp);
964 break;
965 case '&':
966 fputs ("&amp;", fp);
967 break;
968 case '<':
969 fputs ("&lt;", fp);
970 break;
971 case '>':
972 fputs ("&gt;",fp);
973 break;
975 default:
976 fputc (*p, fp);
977 break;
981 pp_clear_output_area (pp);
984 /* Wrap a text delimited by START and END into PRETTY-PRINTER. */
985 static void
986 pp_wrap_text (pretty_printer *pp, const char *start, const char *end)
988 bool wrapping_line = pp_is_wrapping_line (pp);
990 while (start != end)
992 /* Dump anything bordered by whitespaces. */
994 const char *p = start;
995 while (p != end && !ISBLANK (*p) && *p != '\n')
996 ++p;
997 if (wrapping_line
998 && p - start >= pp_remaining_character_count_for_line (pp))
999 pp_newline (pp);
1000 pp_append_text (pp, start, p);
1001 start = p;
1004 if (start != end && ISBLANK (*start))
1006 pp_space (pp);
1007 ++start;
1009 if (start != end && *start == '\n')
1011 pp_newline (pp);
1012 ++start;
1017 /* Same as pp_wrap_text but wrap text only when in line-wrapping mode. */
1018 static inline void
1019 pp_maybe_wrap_text (pretty_printer *pp, const char *start, const char *end)
1021 if (pp_is_wrapping_line (pp))
1022 pp_wrap_text (pp, start, end);
1023 else
1024 pp_append_text (pp, start, end);
1027 /* Append to the output area of PRETTY-PRINTER a string specified by its
1028 STARTing character and LENGTH. */
1029 static inline void
1030 pp_append_r (pretty_printer *pp, const char *start, int length)
1032 output_buffer_append_r (pp_buffer (pp), start, length);
1035 /* Insert enough spaces into the output area of PRETTY-PRINTER to bring
1036 the column position to the current indentation level, assuming that a
1037 newline has just been written to the buffer. */
1038 void
1039 pp_indent (pretty_printer *pp)
1041 int n = pp_indentation (pp);
1042 int i;
1044 for (i = 0; i < n; ++i)
1045 pp_space (pp);
1048 static const char *get_end_url_string (pretty_printer *);
1050 /* Append STR to OSTACK, without a null-terminator. */
1052 static void
1053 obstack_append_string (obstack *ostack, const char *str)
1055 obstack_grow (ostack, str, strlen (str));
1058 /* Append STR to OSTACK, without a null-terminator. */
1060 static void
1061 obstack_append_string (obstack *ostack, const char *str, size_t len)
1063 obstack_grow (ostack, str, len);
1066 /* Given quoted text within the buffer OBSTACK
1067 at the half-open interval [QUOTED_TEXT_START_IDX, QUOTED_TEXT_END_IDX),
1068 potentially use URLIFIER (if non-null) to see if there's a URL for the
1069 quoted text.
1071 If so, replace the quoted part of the text in the buffer with a URLified
1072 version of the text, using PP's settings.
1074 For example, given this is the buffer:
1075 "this is a test `hello worldTRAILING-CONTENT"
1076 .................^~~~~~~~~~~
1077 with the quoted text starting at the 'h' of "hello world", the buffer
1078 becomes:
1079 "this is a test `BEGIN_URL(URL)hello worldEND(URL)TRAILING-CONTENT"
1080 .................^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1081 .................-----------replacement-----------
1083 Return the new offset into the buffer of the quoted text endpoint i.e.
1084 the offset of "TRAILING-CONTENT" in the above. */
1086 static size_t
1087 urlify_quoted_string (pretty_printer *pp,
1088 obstack *obstack,
1089 const urlifier *urlifier,
1090 size_t quoted_text_start_idx,
1091 size_t quoted_text_end_idx)
1093 if (pp->url_format == URL_FORMAT_NONE)
1094 return quoted_text_end_idx;
1095 if (!urlifier)
1096 return quoted_text_end_idx;
1098 const size_t quoted_len = quoted_text_end_idx - quoted_text_start_idx;
1099 if (quoted_len == 0)
1100 /* Empty quoted string; do nothing. */
1101 return quoted_text_end_idx;
1102 const char *start = (obstack->object_base + quoted_text_start_idx);
1103 char *url = urlifier->get_url_for_quoted_text (start, quoted_len);
1104 if (!url)
1105 /* No URL for this quoted text; do nothing. */
1106 return quoted_text_end_idx;
1108 /* Stash a copy of the remainder of the chunk. */
1109 char *text = xstrndup (start,
1110 obstack_object_size (obstack) - quoted_text_start_idx);
1112 /* Replace quoted text... */
1113 obstack->next_free = obstack->object_base + quoted_text_start_idx;
1115 /* ...with URLified version of the text. */
1116 /* Begin URL. */
1117 switch (pp->url_format)
1119 default:
1120 case URL_FORMAT_NONE:
1121 gcc_unreachable ();
1122 case URL_FORMAT_ST:
1123 obstack_append_string (obstack, "\33]8;;");
1124 obstack_append_string (obstack, url);
1125 obstack_append_string (obstack, "\33\\");
1126 break;
1127 case URL_FORMAT_BEL:
1128 obstack_append_string (obstack, "\33]8;;");
1129 obstack_append_string (obstack, url);
1130 obstack_append_string (obstack, "\a");
1131 break;
1133 /* Add back the quoted part of the text. */
1134 obstack_append_string (obstack, text, quoted_len);
1135 /* End URL. */
1136 obstack_append_string (obstack,
1137 get_end_url_string (pp));
1139 size_t new_end_idx = obstack_object_size (obstack);
1141 /* Add back the remainder of the text after the quoted part. */
1142 obstack_append_string (obstack, text + quoted_len);
1143 free (text);
1144 free (url);
1145 return new_end_idx;
1148 /* A class for tracking quoted text within a buffer for
1149 use by a urlifier. */
1151 class quoting_info
1153 public:
1154 /* Called when quoted text is begun in phase 1 or 2. */
1155 void on_begin_quote (const output_buffer &buf,
1156 unsigned chunk_idx)
1158 /* Stash location of start of quoted string. */
1159 size_t byte_offset = obstack_object_size (&buf.chunk_obstack);
1160 m_loc_last_open_quote = location (chunk_idx, byte_offset);
1163 /* Called when quoted text is ended in phase 1 or 2. */
1164 void on_end_quote (pretty_printer *pp,
1165 output_buffer &buf,
1166 unsigned chunk_idx,
1167 const urlifier &urlifier)
1169 /* If possible, do urlification now. */
1170 if (chunk_idx == m_loc_last_open_quote.m_chunk_idx)
1172 urlify_quoted_string (pp,
1173 &buf.chunk_obstack,
1174 &urlifier,
1175 m_loc_last_open_quote.m_byte_offset,
1176 obstack_object_size (&buf.chunk_obstack));
1177 m_loc_last_open_quote = location ();
1178 return;
1180 /* Otherwise the quoted text straddles multiple chunks.
1181 Stash the location of end of quoted string for use in phase 3. */
1182 size_t byte_offset = obstack_object_size (&buf.chunk_obstack);
1183 m_phase_3_quotes.push_back (run (m_loc_last_open_quote,
1184 location (chunk_idx, byte_offset)));
1185 m_loc_last_open_quote = location ();
1188 bool has_phase_3_quotes_p () const
1190 return m_phase_3_quotes.size () > 0;
1192 void handle_phase_3 (pretty_printer *pp,
1193 const urlifier &urlifier);
1195 private:
1196 struct location
1198 location ()
1199 : m_chunk_idx (UINT_MAX),
1200 m_byte_offset (SIZE_MAX)
1204 location (unsigned chunk_idx,
1205 size_t byte_offset)
1206 : m_chunk_idx (chunk_idx),
1207 m_byte_offset (byte_offset)
1211 unsigned m_chunk_idx;
1212 size_t m_byte_offset;
1215 struct run
1217 run (location start, location end)
1218 : m_start (start), m_end (end)
1222 location m_start;
1223 location m_end;
1226 location m_loc_last_open_quote;
1227 std::vector<run> m_phase_3_quotes;
1230 static void
1231 on_begin_quote (const output_buffer &buf,
1232 unsigned chunk_idx,
1233 const urlifier *urlifier)
1235 if (!urlifier)
1236 return;
1237 if (!buf.cur_chunk_array->m_quotes)
1238 buf.cur_chunk_array->m_quotes = new quoting_info ();
1239 buf.cur_chunk_array->m_quotes->on_begin_quote (buf, chunk_idx);
1242 static void
1243 on_end_quote (pretty_printer *pp,
1244 output_buffer &buf,
1245 unsigned chunk_idx,
1246 const urlifier *urlifier)
1248 if (!urlifier)
1249 return;
1250 if (!buf.cur_chunk_array->m_quotes)
1251 buf.cur_chunk_array->m_quotes = new quoting_info ();
1252 buf.cur_chunk_array->m_quotes->on_end_quote (pp, buf, chunk_idx, *urlifier);
1255 /* The following format specifiers are recognized as being client independent:
1256 %d, %i: (signed) integer in base ten.
1257 %u: unsigned integer in base ten.
1258 %o: unsigned integer in base eight.
1259 %x: unsigned integer in base sixteen.
1260 %ld, %li, %lo, %lu, %lx: long versions of the above.
1261 %lld, %lli, %llo, %llu, %llx: long long versions.
1262 %wd, %wi, %wo, %wu, %wx: HOST_WIDE_INT versions.
1263 %zd, %zi, %zo, %zu, %zx: size_t versions.
1264 %td, %ti, %to, %tu, %tx: ptrdiff_t versions.
1265 %f: double
1266 %c: character.
1267 %s: string.
1268 %p: pointer (printed in a host-dependent manner).
1269 %r: if pp_show_color(pp), switch to color identified by const char *.
1270 %R: if pp_show_color(pp), reset color.
1271 %m: strerror(text->err_no) - does not consume a value from args_ptr.
1272 %%: '%'.
1273 %<: opening quote.
1274 %>: closing quote.
1275 %{: URL start. Consumes a const char * argument for the URL.
1276 %}: URL end. Does not consume any arguments.
1277 %': apostrophe (should only be used in untranslated messages;
1278 translations should use appropriate punctuation directly).
1279 %@: diagnostic_event_id_ptr, for which event_id->known_p () must be true.
1280 %.*s: a substring the length of which is specified by an argument
1281 integer.
1282 %Ns: likewise, but length specified as constant in the format string.
1283 Flag 'q': quote formatted text (must come immediately after '%').
1284 %Z: Requires two arguments - array of int, and len. Prints elements
1285 of the array.
1287 Arguments can be used sequentially, or through %N$ resp. *N$
1288 notation Nth argument after the format string. If %N$ / *N$
1289 notation is used, it must be used for all arguments, except %m, %%,
1290 %<, %>, %} and %', which may not have a number, as they do not consume
1291 an argument. When %M$.*N$s is used, M must be N + 1. (This may
1292 also be written %M$.*s, provided N is not otherwise used.) The
1293 format string must have conversion specifiers with argument numbers
1294 1 up to highest argument; each argument may only be used once.
1295 A format string can have at most 30 arguments. */
1297 /* Formatting phases 1 and 2: render TEXT->format_spec plus
1298 text->m_args_ptr into a series of chunks in pp_buffer (PP)->args[].
1299 Phase 3 is in pp_output_formatted_text.
1301 If URLIFIER is non-NULL, then use it to add URLs for quoted
1302 strings, so that e.g.
1303 "before %<quoted%> after"
1304 with a URLIFIER that has a URL for "quoted" might be emitted as:
1305 "before `BEGIN_URL(http://example.com)quotedEND_URL' after"
1306 This is handled here for message fragments that are:
1307 - quoted entirely in phase 1 (e.g. "%<this is quoted%>"), or
1308 - quoted entirely in phase 2 (e.g. "%qs"),
1309 Quoted fragments that use a mixture of both phases
1310 (e.g. "%<this is a mixture: %s %>")
1311 are stashed into the output_buffer's m_quotes for use in phase 3. */
1313 void
1314 pp_format (pretty_printer *pp,
1315 text_info *text,
1316 const urlifier *urlifier)
1318 output_buffer * const buffer = pp_buffer (pp);
1319 const char *p;
1320 const char **args;
1321 struct chunk_info *new_chunk_array;
1323 unsigned int curarg = 0, chunk = 0, argno;
1324 pp_wrapping_mode_t old_wrapping_mode;
1325 bool any_unnumbered = false, any_numbered = false;
1326 const char **formatters[PP_NL_ARGMAX];
1328 /* Allocate a new chunk structure. */
1329 new_chunk_array = XOBNEW (&buffer->chunk_obstack, struct chunk_info);
1331 new_chunk_array->prev = buffer->cur_chunk_array;
1332 new_chunk_array->m_quotes = nullptr;
1333 buffer->cur_chunk_array = new_chunk_array;
1334 args = new_chunk_array->args;
1336 /* Formatting phase 1: split up TEXT->format_spec into chunks in
1337 pp_buffer (PP)->args[]. Even-numbered chunks are to be output
1338 verbatim, odd-numbered chunks are format specifiers.
1339 %m, %%, %<, %>, %} and %' are replaced with the appropriate text at
1340 this point. */
1342 memset (formatters, 0, sizeof formatters);
1344 for (p = text->m_format_spec; *p; )
1346 while (*p != '\0' && *p != '%')
1348 obstack_1grow (&buffer->chunk_obstack, *p);
1349 p++;
1352 if (*p == '\0')
1353 break;
1355 switch (*++p)
1357 case '\0':
1358 gcc_unreachable ();
1360 case '%':
1361 obstack_1grow (&buffer->chunk_obstack, '%');
1362 p++;
1363 continue;
1365 case '<':
1367 obstack_grow (&buffer->chunk_obstack,
1368 open_quote, strlen (open_quote));
1369 const char *colorstr
1370 = colorize_start (pp_show_color (pp), "quote");
1371 obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr));
1372 p++;
1374 on_begin_quote (*buffer, chunk, urlifier);
1375 continue;
1378 case '>':
1380 on_end_quote (pp, *buffer, chunk, urlifier);
1382 const char *colorstr = colorize_stop (pp_show_color (pp));
1383 obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr));
1385 /* FALLTHRU */
1386 case '\'':
1387 obstack_grow (&buffer->chunk_obstack,
1388 close_quote, strlen (close_quote));
1389 p++;
1390 continue;
1392 case '}':
1394 const char *endurlstr = get_end_url_string (pp);
1395 obstack_grow (&buffer->chunk_obstack, endurlstr,
1396 strlen (endurlstr));
1398 p++;
1399 continue;
1401 case 'R':
1403 const char *colorstr = colorize_stop (pp_show_color (pp));
1404 obstack_grow (&buffer->chunk_obstack, colorstr,
1405 strlen (colorstr));
1406 p++;
1407 continue;
1410 case 'm':
1412 const char *errstr = xstrerror (text->m_err_no);
1413 obstack_grow (&buffer->chunk_obstack, errstr, strlen (errstr));
1415 p++;
1416 continue;
1418 default:
1419 /* Handled in phase 2. Terminate the plain chunk here. */
1420 obstack_1grow (&buffer->chunk_obstack, '\0');
1421 args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *);
1422 break;
1425 if (ISDIGIT (*p))
1427 char *end;
1428 argno = strtoul (p, &end, 10) - 1;
1429 p = end;
1430 gcc_assert (*p == '$');
1431 p++;
1433 any_numbered = true;
1434 gcc_assert (!any_unnumbered);
1436 else
1438 argno = curarg++;
1439 any_unnumbered = true;
1440 gcc_assert (!any_numbered);
1442 gcc_assert (argno < PP_NL_ARGMAX);
1443 gcc_assert (!formatters[argno]);
1444 formatters[argno] = &args[chunk];
1447 obstack_1grow (&buffer->chunk_obstack, *p);
1448 p++;
1450 while (strchr ("qwlzt+#", p[-1]));
1452 if (p[-1] == '.')
1454 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1455 (where M == N + 1). */
1456 if (ISDIGIT (*p))
1460 obstack_1grow (&buffer->chunk_obstack, *p);
1461 p++;
1463 while (ISDIGIT (p[-1]));
1464 gcc_assert (p[-1] == 's');
1466 else
1468 gcc_assert (*p == '*');
1469 obstack_1grow (&buffer->chunk_obstack, '*');
1470 p++;
1472 if (ISDIGIT (*p))
1474 char *end;
1475 unsigned int argno2 = strtoul (p, &end, 10) - 1;
1476 p = end;
1477 gcc_assert (argno2 == argno - 1);
1478 gcc_assert (!any_unnumbered);
1479 gcc_assert (*p == '$');
1481 p++;
1482 formatters[argno2] = formatters[argno];
1484 else
1486 gcc_assert (!any_numbered);
1487 formatters[argno+1] = formatters[argno];
1488 curarg++;
1490 gcc_assert (*p == 's');
1491 obstack_1grow (&buffer->chunk_obstack, 's');
1492 p++;
1495 if (*p == '\0')
1496 break;
1498 obstack_1grow (&buffer->chunk_obstack, '\0');
1499 gcc_assert (chunk < PP_NL_ARGMAX * 2);
1500 args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *);
1503 obstack_1grow (&buffer->chunk_obstack, '\0');
1504 gcc_assert (chunk < PP_NL_ARGMAX * 2);
1505 args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *);
1506 args[chunk] = 0;
1508 /* Set output to the argument obstack, and switch line-wrapping and
1509 prefixing off. */
1510 buffer->obstack = &buffer->chunk_obstack;
1511 const int old_line_length = buffer->line_length;
1512 old_wrapping_mode = pp_set_verbatim_wrapping (pp);
1514 /* Second phase. Replace each formatter with the formatted text it
1515 corresponds to. */
1517 for (argno = 0; formatters[argno]; argno++)
1519 int precision = 0;
1520 bool wide = false;
1521 bool plus = false;
1522 bool hash = false;
1523 bool quote = false;
1525 /* We do not attempt to enforce any ordering on the modifier
1526 characters. */
1528 for (p = *formatters[argno];; p++)
1530 switch (*p)
1532 case 'q':
1533 gcc_assert (!quote);
1534 quote = true;
1535 continue;
1537 case '+':
1538 gcc_assert (!plus);
1539 plus = true;
1540 continue;
1542 case '#':
1543 gcc_assert (!hash);
1544 hash = true;
1545 continue;
1547 case 'w':
1548 gcc_assert (!wide);
1549 wide = true;
1550 continue;
1552 case 'z':
1553 gcc_assert (!precision);
1554 precision = 3;
1555 continue;
1557 case 't':
1558 gcc_assert (!precision);
1559 precision = 4;
1560 continue;
1562 case 'l':
1563 /* We don't support precision beyond that of "long long". */
1564 gcc_assert (precision < 2);
1565 precision++;
1566 continue;
1568 break;
1571 gcc_assert (!wide || precision == 0);
1573 if (quote)
1575 pp_begin_quote (pp, pp_show_color (pp));
1576 on_begin_quote (*buffer, chunk, urlifier);
1579 switch (*p)
1581 case 'r':
1582 pp_string (pp, colorize_start (pp_show_color (pp),
1583 va_arg (*text->m_args_ptr,
1584 const char *)));
1585 break;
1587 case 'c':
1589 /* When quoting, print alphanumeric, punctuation, and the space
1590 character unchanged, and all others in hexadecimal with the
1591 "\x" prefix. Otherwise print them all unchanged. */
1592 int chr = va_arg (*text->m_args_ptr, int);
1593 if (ISPRINT (chr) || !quote)
1594 pp_character (pp, chr);
1595 else
1597 const char str [2] = { chr, '\0' };
1598 pp_quoted_string (pp, str, 1);
1600 break;
1603 case 'd':
1604 case 'i':
1605 if (wide)
1606 pp_wide_integer (pp, va_arg (*text->m_args_ptr, HOST_WIDE_INT));
1607 else
1608 pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1609 int, "d");
1610 break;
1612 case 'o':
1613 if (wide)
1614 pp_scalar (pp, "%" HOST_WIDE_INT_PRINT "o",
1615 va_arg (*text->m_args_ptr, unsigned HOST_WIDE_INT));
1616 else
1617 pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1618 unsigned, "o");
1619 break;
1621 case 's':
1622 if (quote)
1623 pp_quoted_string (pp, va_arg (*text->m_args_ptr, const char *));
1624 else
1625 pp_string (pp, va_arg (*text->m_args_ptr, const char *));
1626 break;
1628 case 'p':
1629 pp_pointer (pp, va_arg (*text->m_args_ptr, void *));
1630 break;
1632 case 'u':
1633 if (wide)
1634 pp_scalar (pp, HOST_WIDE_INT_PRINT_UNSIGNED,
1635 va_arg (*text->m_args_ptr, unsigned HOST_WIDE_INT));
1636 else
1637 pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1638 unsigned, "u");
1639 break;
1641 case 'f':
1642 pp_double (pp, va_arg (*text->m_args_ptr, double));
1643 break;
1645 case 'Z':
1647 int *v = va_arg (*text->m_args_ptr, int *);
1648 unsigned len = va_arg (*text->m_args_ptr, unsigned);
1650 for (unsigned i = 0; i < len; ++i)
1652 pp_scalar (pp, "%i", v[i]);
1653 if (i < len - 1)
1655 pp_comma (pp);
1656 pp_space (pp);
1659 break;
1662 case 'x':
1663 if (wide)
1664 pp_scalar (pp, HOST_WIDE_INT_PRINT_HEX,
1665 va_arg (*text->m_args_ptr, unsigned HOST_WIDE_INT));
1666 else
1667 pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1668 unsigned, "x");
1669 break;
1671 case '.':
1673 int n;
1674 const char *s;
1676 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1677 (where M == N + 1). The format string should be verified
1678 already from the first phase. */
1679 p++;
1680 if (ISDIGIT (*p))
1682 char *end;
1683 n = strtoul (p, &end, 10);
1684 p = end;
1685 gcc_assert (*p == 's');
1687 else
1689 gcc_assert (*p == '*');
1690 p++;
1691 gcc_assert (*p == 's');
1692 n = va_arg (*text->m_args_ptr, int);
1694 /* This consumes a second entry in the formatters array. */
1695 gcc_assert (formatters[argno] == formatters[argno+1]);
1696 argno++;
1699 s = va_arg (*text->m_args_ptr, const char *);
1701 /* Append the lesser of precision and strlen (s) characters
1702 from the array (which need not be a nul-terminated string).
1703 Negative precision is treated as if it were omitted. */
1704 size_t len = n < 0 ? strlen (s) : strnlen (s, n);
1706 pp_append_text (pp, s, s + len);
1708 break;
1710 case '@':
1712 /* diagnostic_event_id_t *. */
1713 diagnostic_event_id_ptr event_id
1714 = va_arg (*text->m_args_ptr, diagnostic_event_id_ptr);
1715 gcc_assert (event_id->known_p ());
1717 pp_string (pp, colorize_start (pp_show_color (pp), "path"));
1718 pp_character (pp, '(');
1719 pp_decimal_int (pp, event_id->one_based ());
1720 pp_character (pp, ')');
1721 pp_string (pp, colorize_stop (pp_show_color (pp)));
1723 break;
1725 case '{':
1726 pp_begin_url (pp, va_arg (*text->m_args_ptr, const char *));
1727 break;
1729 default:
1731 bool ok;
1733 /* Call the format decoder.
1734 Pass the address of "quote" so that format decoders can
1735 potentially disable printing of the closing quote
1736 (e.g. when printing "'TYPEDEF' aka 'TYPE'" in the C family
1737 of frontends). */
1738 gcc_assert (pp_format_decoder (pp));
1739 ok = pp_format_decoder (pp) (pp, text, p,
1740 precision, wide, plus, hash, &quote,
1741 formatters[argno]);
1742 gcc_assert (ok);
1746 if (quote)
1748 on_end_quote (pp, *buffer, chunk, urlifier);
1749 pp_end_quote (pp, pp_show_color (pp));
1752 obstack_1grow (&buffer->chunk_obstack, '\0');
1753 *formatters[argno] = XOBFINISH (&buffer->chunk_obstack, const char *);
1756 if (CHECKING_P)
1757 for (; argno < PP_NL_ARGMAX; argno++)
1758 gcc_assert (!formatters[argno]);
1760 /* If the client supplied a postprocessing object, call its "handle"
1761 hook here. */
1762 if (pp->m_format_postprocessor)
1763 pp->m_format_postprocessor->handle (pp);
1765 /* Revert to normal obstack and wrapping mode. */
1766 buffer->obstack = &buffer->formatted_obstack;
1767 buffer->line_length = old_line_length;
1768 pp_wrapping_mode (pp) = old_wrapping_mode;
1769 pp_clear_state (pp);
1772 struct auto_obstack
1774 auto_obstack ()
1776 obstack_init (&m_obstack);
1779 ~auto_obstack ()
1781 obstack_free (&m_obstack, NULL);
1784 void grow (const void *src, size_t length)
1786 obstack_grow (&m_obstack, src, length);
1789 void *object_base () const
1791 return m_obstack.object_base;
1794 size_t object_size () const
1796 return obstack_object_size (&m_obstack);
1799 obstack m_obstack;
1802 /* Subroutine of pp_output_formatted_text for the awkward case where
1803 quoted text straddles multiple chunks.
1805 Flush PP's buffer's chunks to PP's output buffer, whilst inserting
1806 URLs for any quoted text that should be URLified.
1808 For example, given:
1809 | pp_format (pp,
1810 | "unrecognized option %qs; did you mean %<-%s%>",
1811 | "foo", "foption");
1812 we would have these chunks:
1813 | chunk 0: "unrecognized option "
1814 | chunk 1: "`foo'" (already checked for urlification)
1815 | chunk 2: "; did you mean `-"
1816 | ^*
1817 | chunk 3: "foption"
1818 | *******
1819 | chunk 4: "'"
1821 and this quoting_info would have recorded the open quote near the end
1822 of chunk 2 and close quote at the start of chunk 4; this function would
1823 check the combination of the end of chunk 2 and all of chunk 3 ("-foption")
1824 for urlification. */
1826 void
1827 quoting_info::handle_phase_3 (pretty_printer *pp,
1828 const urlifier &urlifier)
1830 unsigned int chunk;
1831 output_buffer * const buffer = pp_buffer (pp);
1832 struct chunk_info *chunk_array = buffer->cur_chunk_array;
1833 const char **args = chunk_array->args;
1835 /* We need to construct the string into an intermediate buffer
1836 for this case, since using pp_string can introduce prefixes
1837 and line-wrapping, and omit whitespace at the start of lines. */
1838 auto_obstack combined_buf;
1840 /* Iterate simultaneously through both
1841 - the chunks and
1842 - the runs of quoted characters
1843 Accumulate text from the chunks into combined_buf, and handle
1844 runs of quoted characters when handling the chunks they
1845 correspond to. */
1846 size_t start_of_run_byte_offset = 0;
1847 std::vector<quoting_info::run>::const_iterator iter_run
1848 = buffer->cur_chunk_array->m_quotes->m_phase_3_quotes.begin ();
1849 std::vector<quoting_info::run>::const_iterator end_runs
1850 = buffer->cur_chunk_array->m_quotes->m_phase_3_quotes.end ();
1851 for (chunk = 0; args[chunk]; chunk++)
1853 size_t start_of_chunk_idx = combined_buf.object_size ();
1855 combined_buf.grow (args[chunk], strlen (args[chunk]));
1857 if (iter_run != end_runs
1858 && chunk == iter_run->m_end.m_chunk_idx)
1860 /* A run is ending; consider for it urlification. */
1861 const size_t end_of_run_byte_offset
1862 = start_of_chunk_idx + iter_run->m_end.m_byte_offset;
1863 const size_t end_offset
1864 = urlify_quoted_string (pp,
1865 &combined_buf.m_obstack,
1866 &urlifier,
1867 start_of_run_byte_offset,
1868 end_of_run_byte_offset);
1870 /* If URLification occurred it will have grown the buffer.
1871 We need to update start_of_chunk_idx so that offsets
1872 relative to it are still correct, for the case where
1873 we have a chunk that both ends a quoted run and starts
1874 another quoted run. */
1875 gcc_assert (end_offset >= end_of_run_byte_offset);
1876 start_of_chunk_idx += end_offset - end_of_run_byte_offset;
1878 iter_run++;
1880 if (iter_run != end_runs
1881 && chunk == iter_run->m_start.m_chunk_idx)
1883 /* Note where the run starts w.r.t. the composed buffer. */
1884 start_of_run_byte_offset
1885 = start_of_chunk_idx + iter_run->m_start.m_byte_offset;
1889 /* Now print to PP. */
1890 const char *start
1891 = static_cast <const char *> (combined_buf.object_base ());
1892 pp_maybe_wrap_text (pp, start, start + combined_buf.object_size ());
1895 /* Format of a message pointed to by TEXT.
1896 If URLIFIER is non-null then use it on any quoted text that was not
1897 handled in phases 1 or 2 to potentially add URLs. */
1899 void
1900 pp_output_formatted_text (pretty_printer *pp,
1901 const urlifier *urlifier)
1903 unsigned int chunk;
1904 output_buffer * const buffer = pp_buffer (pp);
1905 struct chunk_info *chunk_array = buffer->cur_chunk_array;
1906 const char **args = chunk_array->args;
1908 gcc_assert (buffer->obstack == &buffer->formatted_obstack);
1910 /* This is a third phase, first 2 phases done in pp_format_args.
1911 Now we actually print it. */
1913 /* If we have any deferred urlification, handle it now. */
1914 if (urlifier
1915 && pp->url_format != URL_FORMAT_NONE
1916 && buffer->cur_chunk_array->m_quotes
1917 && buffer->cur_chunk_array->m_quotes->has_phase_3_quotes_p ())
1918 buffer->cur_chunk_array->m_quotes->handle_phase_3 (pp, *urlifier);
1919 else
1920 for (chunk = 0; args[chunk]; chunk++)
1921 pp_string (pp, args[chunk]);
1923 /* Deallocate the chunk structure and everything after it (i.e. the
1924 associated series of formatted strings). */
1925 delete buffer->cur_chunk_array->m_quotes;
1926 buffer->cur_chunk_array = chunk_array->prev;
1927 obstack_free (&buffer->chunk_obstack, chunk_array);
1930 /* Helper subroutine of output_verbatim and verbatim. Do the appropriate
1931 settings needed by BUFFER for a verbatim formatting. */
1932 void
1933 pp_format_verbatim (pretty_printer *pp, text_info *text)
1935 /* Set verbatim mode. */
1936 pp_wrapping_mode_t oldmode = pp_set_verbatim_wrapping (pp);
1938 /* Do the actual formatting. */
1939 pp_format (pp, text);
1940 pp_output_formatted_text (pp);
1942 /* Restore previous settings. */
1943 pp_wrapping_mode (pp) = oldmode;
1946 /* Flush the content of BUFFER onto the attached stream. This
1947 function does nothing unless pp->output_buffer->flush_p. */
1948 void
1949 pp_flush (pretty_printer *pp)
1951 pp_clear_state (pp);
1952 if (!pp->buffer->flush_p)
1953 return;
1954 pp_write_text_to_stream (pp);
1955 fflush (pp_buffer (pp)->stream);
1958 /* Flush the content of BUFFER onto the attached stream independently
1959 of the value of pp->output_buffer->flush_p. */
1960 void
1961 pp_really_flush (pretty_printer *pp)
1963 pp_clear_state (pp);
1964 pp_write_text_to_stream (pp);
1965 fflush (pp_buffer (pp)->stream);
1968 /* Sets the number of maximum characters per line PRETTY-PRINTER can
1969 output in line-wrapping mode. A LENGTH value 0 suppresses
1970 line-wrapping. */
1971 void
1972 pp_set_line_maximum_length (pretty_printer *pp, int length)
1974 pp_line_cutoff (pp) = length;
1975 pp_set_real_maximum_length (pp);
1978 /* Clear PRETTY-PRINTER output area text info. */
1979 void
1980 pp_clear_output_area (pretty_printer *pp)
1982 obstack_free (pp_buffer (pp)->obstack,
1983 obstack_base (pp_buffer (pp)->obstack));
1984 pp_buffer (pp)->line_length = 0;
1987 /* Set PREFIX for PRETTY-PRINTER, taking ownership of PREFIX, which
1988 will eventually be free-ed. */
1990 void
1991 pp_set_prefix (pretty_printer *pp, char *prefix)
1993 free (pp->prefix);
1994 pp->prefix = prefix;
1995 pp_set_real_maximum_length (pp);
1996 pp->emitted_prefix = false;
1997 pp_indentation (pp) = 0;
2000 /* Take ownership of PP's prefix, setting it to NULL.
2001 This allows clients to save, override, and then restore an existing
2002 prefix, without it being free-ed. */
2004 char *
2005 pp_take_prefix (pretty_printer *pp)
2007 char *result = pp->prefix;
2008 pp->prefix = NULL;
2009 return result;
2012 /* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */
2013 void
2014 pp_destroy_prefix (pretty_printer *pp)
2016 if (pp->prefix != NULL)
2018 free (pp->prefix);
2019 pp->prefix = NULL;
2023 /* Write out PRETTY-PRINTER's prefix. */
2024 void
2025 pp_emit_prefix (pretty_printer *pp)
2027 if (pp->prefix != NULL)
2029 switch (pp_prefixing_rule (pp))
2031 default:
2032 case DIAGNOSTICS_SHOW_PREFIX_NEVER:
2033 break;
2035 case DIAGNOSTICS_SHOW_PREFIX_ONCE:
2036 if (pp->emitted_prefix)
2038 pp_indent (pp);
2039 break;
2041 pp_indentation (pp) += 3;
2042 /* Fall through. */
2044 case DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE:
2046 int prefix_length = strlen (pp->prefix);
2047 pp_append_r (pp, pp->prefix, prefix_length);
2048 pp->emitted_prefix = true;
2050 break;
2055 /* Construct a PRETTY-PRINTER of MAXIMUM_LENGTH characters per line. */
2057 pretty_printer::pretty_printer (int maximum_length)
2058 : buffer (new (XCNEW (output_buffer)) output_buffer ()),
2059 prefix (),
2060 padding (pp_none),
2061 maximum_length (),
2062 indent_skip (),
2063 wrapping (),
2064 format_decoder (),
2065 m_format_postprocessor (NULL),
2066 emitted_prefix (),
2067 need_newline (),
2068 translate_identifiers (true),
2069 show_color (),
2070 url_format (URL_FORMAT_NONE),
2071 m_skipping_null_url (false)
2073 pp_line_cutoff (this) = maximum_length;
2074 /* By default, we emit prefixes once per message. */
2075 pp_prefixing_rule (this) = DIAGNOSTICS_SHOW_PREFIX_ONCE;
2076 pp_set_prefix (this, NULL);
2079 /* Copy constructor for pretty_printer. */
2081 pretty_printer::pretty_printer (const pretty_printer &other)
2082 : buffer (new (XCNEW (output_buffer)) output_buffer ()),
2083 prefix (),
2084 padding (other.padding),
2085 maximum_length (other.maximum_length),
2086 indent_skip (other.indent_skip),
2087 wrapping (other.wrapping),
2088 format_decoder (other.format_decoder),
2089 m_format_postprocessor (NULL),
2090 emitted_prefix (other.emitted_prefix),
2091 need_newline (other.need_newline),
2092 translate_identifiers (other.translate_identifiers),
2093 show_color (other.show_color),
2094 url_format (other.url_format),
2095 m_skipping_null_url (false)
2097 pp_line_cutoff (this) = maximum_length;
2098 /* By default, we emit prefixes once per message. */
2099 pp_prefixing_rule (this) = pp_prefixing_rule (&other);
2100 pp_set_prefix (this, NULL);
2102 if (other.m_format_postprocessor)
2103 m_format_postprocessor = other.m_format_postprocessor->clone ();
2106 pretty_printer::~pretty_printer ()
2108 if (m_format_postprocessor)
2109 delete m_format_postprocessor;
2110 buffer->~output_buffer ();
2111 XDELETE (buffer);
2112 free (prefix);
2115 /* Base class implementation of pretty_printer::clone vfunc. */
2117 pretty_printer *
2118 pretty_printer::clone () const
2120 return new pretty_printer (*this);
2123 /* Append a string delimited by START and END to the output area of
2124 PRETTY-PRINTER. No line wrapping is done. However, if beginning a
2125 new line then emit PRETTY-PRINTER's prefix and skip any leading
2126 whitespace if appropriate. The caller must ensure that it is
2127 safe to do so. */
2128 void
2129 pp_append_text (pretty_printer *pp, const char *start, const char *end)
2131 /* Emit prefix and skip whitespace if we're starting a new line. */
2132 if (pp_buffer (pp)->line_length == 0)
2134 pp_emit_prefix (pp);
2135 if (pp_is_wrapping_line (pp))
2136 while (start != end && *start == ' ')
2137 ++start;
2139 pp_append_r (pp, start, end - start);
2142 /* Finishes constructing a NULL-terminated character string representing
2143 the PRETTY-PRINTED text. */
2144 const char *
2145 pp_formatted_text (pretty_printer *pp)
2147 return output_buffer_formatted_text (pp_buffer (pp));
2150 /* Return a pointer to the last character emitted in PRETTY-PRINTER's
2151 output area. A NULL pointer means no character available. */
2152 const char *
2153 pp_last_position_in_text (const pretty_printer *pp)
2155 return output_buffer_last_position_in_text (pp_buffer (pp));
2158 /* Return the amount of characters PRETTY-PRINTER can accept to
2159 make a full line. Meaningful only in line-wrapping mode. */
2161 pp_remaining_character_count_for_line (pretty_printer *pp)
2163 return pp->maximum_length - pp_buffer (pp)->line_length;
2167 /* Format a message into BUFFER a la printf. */
2168 void
2169 pp_printf (pretty_printer *pp, const char *msg, ...)
2171 va_list ap;
2173 va_start (ap, msg);
2174 text_info text (msg, &ap, errno);
2175 pp_format (pp, &text);
2176 pp_output_formatted_text (pp);
2177 va_end (ap);
2181 /* Output MESSAGE verbatim into BUFFER. */
2182 void
2183 pp_verbatim (pretty_printer *pp, const char *msg, ...)
2185 va_list ap;
2187 va_start (ap, msg);
2188 text_info text (msg, &ap, errno);
2189 pp_format_verbatim (pp, &text);
2190 va_end (ap);
2195 /* Have PRETTY-PRINTER start a new line. */
2196 void
2197 pp_newline (pretty_printer *pp)
2199 obstack_1grow (pp_buffer (pp)->obstack, '\n');
2200 pp_needs_newline (pp) = false;
2201 pp_buffer (pp)->line_length = 0;
2204 /* Have PRETTY-PRINTER add a CHARACTER. */
2205 void
2206 pp_character (pretty_printer *pp, int c)
2208 if (pp_is_wrapping_line (pp)
2209 /* If printing UTF-8, don't wrap in the middle of a sequence. */
2210 && (((unsigned int) c) & 0xC0) != 0x80
2211 && pp_remaining_character_count_for_line (pp) <= 0)
2213 pp_newline (pp);
2214 if (ISSPACE (c))
2215 return;
2217 obstack_1grow (pp_buffer (pp)->obstack, c);
2218 ++pp_buffer (pp)->line_length;
2221 /* Append a STRING to the output area of PRETTY-PRINTER; the STRING may
2222 be line-wrapped if in appropriate mode. */
2223 void
2224 pp_string (pretty_printer *pp, const char *str)
2226 gcc_checking_assert (str);
2227 pp_maybe_wrap_text (pp, str, str + strlen (str));
2230 /* Append code point C to the output area of PRETTY-PRINTER, encoding it
2231 as UTF-8. */
2233 void
2234 pp_unicode_character (pretty_printer *pp, unsigned c)
2236 static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
2237 static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
2238 size_t nbytes;
2239 uchar buf[6], *p = &buf[6];
2241 nbytes = 1;
2242 if (c < 0x80)
2243 *--p = c;
2244 else
2248 *--p = ((c & 0x3F) | 0x80);
2249 c >>= 6;
2250 nbytes++;
2252 while (c >= 0x3F || (c & limits[nbytes-1]));
2253 *--p = (c | masks[nbytes-1]);
2256 pp_append_r (pp, (const char *)p, nbytes);
2259 /* Append the leading N characters of STRING to the output area of
2260 PRETTY-PRINTER, quoting in hexadecimal non-printable characters.
2261 Setting N = -1 is as if N were set to strlen (STRING). The STRING
2262 may be line-wrapped if in appropriate mode. */
2263 static void
2264 pp_quoted_string (pretty_printer *pp, const char *str, size_t n /* = -1 */)
2266 gcc_checking_assert (str);
2268 const char *last = str;
2269 const char *ps;
2271 /* Compute the length if not specified. */
2272 if (n == (size_t) -1)
2273 n = strlen (str);
2275 for (ps = str; n; ++ps, --n)
2277 if (ISPRINT (*ps))
2278 continue;
2280 /* Don't escape a valid UTF-8 extended char. */
2281 const unsigned char *ups = (const unsigned char *) ps;
2282 if (*ups & 0x80)
2284 unsigned int extended_char;
2285 const int valid_utf8_len = decode_utf8_char (ups, n, &extended_char);
2286 if (valid_utf8_len > 0)
2288 ps += valid_utf8_len - 1;
2289 n -= valid_utf8_len - 1;
2290 continue;
2294 if (last < ps)
2295 pp_maybe_wrap_text (pp, last, ps);
2297 /* Append the hexadecimal value of the character. Allocate a buffer
2298 that's large enough for a 32-bit char plus the hex prefix. */
2299 char buf [11];
2300 int n = sprintf (buf, "\\x%02x", (unsigned char)*ps);
2301 pp_maybe_wrap_text (pp, buf, buf + n);
2302 last = ps + 1;
2305 pp_maybe_wrap_text (pp, last, ps);
2308 /* Maybe print out a whitespace if needed. */
2310 void
2311 pp_maybe_space (pretty_printer *pp)
2313 if (pp->padding != pp_none)
2315 pp_space (pp);
2316 pp->padding = pp_none;
2320 // Add a newline to the pretty printer PP and flush formatted text.
2322 void
2323 pp_newline_and_flush (pretty_printer *pp)
2325 pp_newline (pp);
2326 pp_flush (pp);
2327 pp_needs_newline (pp) = false;
2330 // Add a newline to the pretty printer PP, followed by indentation.
2332 void
2333 pp_newline_and_indent (pretty_printer *pp, int n)
2335 pp_indentation (pp) += n;
2336 pp_newline (pp);
2337 pp_indent (pp);
2338 pp_needs_newline (pp) = false;
2341 // Add separator C, followed by a single whitespace.
2343 void
2344 pp_separate_with (pretty_printer *pp, char c)
2346 pp_character (pp, c);
2347 pp_space (pp);
2350 /* Add a localized open quote, and if SHOW_COLOR is true, begin colorizing
2351 using the "quote" color. */
2353 void
2354 pp_begin_quote (pretty_printer *pp, bool show_color)
2356 pp_string (pp, open_quote);
2357 pp_string (pp, colorize_start (show_color, "quote"));
2360 /* If SHOW_COLOR is true, stop colorizing.
2361 Add a localized close quote. */
2363 void
2364 pp_end_quote (pretty_printer *pp, bool show_color)
2366 pp_string (pp, colorize_stop (show_color));
2367 pp_string (pp, close_quote);
2371 /* The string starting at P has LEN (at least 1) bytes left; if they
2372 start with a valid UTF-8 sequence, return the length of that
2373 sequence and set *VALUE to the value of that sequence, and
2374 otherwise return 0 and set *VALUE to (unsigned int) -1. */
2376 static int
2377 decode_utf8_char (const unsigned char *p, size_t len, unsigned int *value)
2379 unsigned int t = *p;
2381 if (len == 0)
2382 abort ();
2383 if (t & 0x80)
2385 size_t utf8_len = 0;
2386 unsigned int ch;
2387 size_t i;
2388 for (t = *p; t & 0x80; t <<= 1)
2389 utf8_len++;
2391 if (utf8_len > len || utf8_len < 2 || utf8_len > 6)
2393 *value = (unsigned int) -1;
2394 return 0;
2396 ch = *p & ((1 << (7 - utf8_len)) - 1);
2397 for (i = 1; i < utf8_len; i++)
2399 unsigned int u = p[i];
2400 if ((u & 0xC0) != 0x80)
2402 *value = (unsigned int) -1;
2403 return 0;
2405 ch = (ch << 6) | (u & 0x3F);
2407 if ( (ch <= 0x7F && utf8_len > 1)
2408 || (ch <= 0x7FF && utf8_len > 2)
2409 || (ch <= 0xFFFF && utf8_len > 3)
2410 || (ch <= 0x1FFFFF && utf8_len > 4)
2411 || (ch <= 0x3FFFFFF && utf8_len > 5)
2412 || (ch >= 0xD800 && ch <= 0xDFFF))
2414 *value = (unsigned int) -1;
2415 return 0;
2417 *value = ch;
2418 return utf8_len;
2420 else
2422 *value = t;
2423 return 1;
2427 /* Allocator for identifier_to_locale and corresponding function to
2428 free memory. */
2430 void *(*identifier_to_locale_alloc) (size_t) = xmalloc;
2431 void (*identifier_to_locale_free) (void *) = free;
2433 /* Given IDENT, an identifier in the internal encoding, return a
2434 version of IDENT suitable for diagnostics in the locale character
2435 set: either IDENT itself, or a string, allocated using
2436 identifier_to_locale_alloc, converted to the locale character set
2437 and using escape sequences if not representable in the locale
2438 character set or containing control characters or invalid byte
2439 sequences. Existing backslashes in IDENT are not doubled, so the
2440 result may not uniquely specify the contents of an arbitrary byte
2441 sequence identifier. */
2443 const char *
2444 identifier_to_locale (const char *ident)
2446 const unsigned char *uid = (const unsigned char *) ident;
2447 size_t idlen = strlen (ident);
2448 bool valid_printable_utf8 = true;
2449 bool all_ascii = true;
2450 size_t i;
2452 for (i = 0; i < idlen;)
2454 unsigned int c;
2455 size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
2456 if (utf8_len == 0 || c <= 0x1F || (c >= 0x7F && c <= 0x9F))
2458 valid_printable_utf8 = false;
2459 break;
2461 if (utf8_len > 1)
2462 all_ascii = false;
2463 i += utf8_len;
2466 /* If IDENT contains invalid UTF-8 sequences (which may occur with
2467 attributes putting arbitrary byte sequences in identifiers), or
2468 control characters, we use octal escape sequences for all bytes
2469 outside printable ASCII. */
2470 if (!valid_printable_utf8)
2472 char *ret = (char *) identifier_to_locale_alloc (4 * idlen + 1);
2473 char *p = ret;
2474 for (i = 0; i < idlen; i++)
2476 if (uid[i] > 0x1F && uid[i] < 0x7F)
2477 *p++ = uid[i];
2478 else
2480 sprintf (p, "\\%03o", uid[i]);
2481 p += 4;
2484 *p = 0;
2485 return ret;
2488 /* Otherwise, if it is valid printable ASCII, or printable UTF-8
2489 with the locale character set being UTF-8, IDENT is used. */
2490 if (all_ascii || locale_utf8)
2491 return ident;
2493 /* Otherwise IDENT is converted to the locale character set if
2494 possible. */
2495 #if defined ENABLE_NLS && defined HAVE_LANGINFO_CODESET && HAVE_ICONV
2496 if (locale_encoding != NULL)
2498 iconv_t cd = iconv_open (locale_encoding, "UTF-8");
2499 bool conversion_ok = true;
2500 char *ret = NULL;
2501 if (cd != (iconv_t) -1)
2503 size_t ret_alloc = 4 * idlen + 1;
2504 for (;;)
2506 /* Repeat the whole conversion process as needed with
2507 larger buffers so non-reversible transformations can
2508 always be detected. */
2509 ICONV_CONST char *inbuf = CONST_CAST (char *, ident);
2510 char *outbuf;
2511 size_t inbytesleft = idlen;
2512 size_t outbytesleft = ret_alloc - 1;
2513 size_t iconv_ret;
2515 ret = (char *) identifier_to_locale_alloc (ret_alloc);
2516 outbuf = ret;
2518 if (iconv (cd, 0, 0, 0, 0) == (size_t) -1)
2520 conversion_ok = false;
2521 break;
2524 iconv_ret = iconv (cd, &inbuf, &inbytesleft,
2525 &outbuf, &outbytesleft);
2526 if (iconv_ret == (size_t) -1 || inbytesleft != 0)
2528 if (errno == E2BIG)
2530 ret_alloc *= 2;
2531 identifier_to_locale_free (ret);
2532 ret = NULL;
2533 continue;
2535 else
2537 conversion_ok = false;
2538 break;
2541 else if (iconv_ret != 0)
2543 conversion_ok = false;
2544 break;
2546 /* Return to initial shift state. */
2547 if (iconv (cd, 0, 0, &outbuf, &outbytesleft) == (size_t) -1)
2549 if (errno == E2BIG)
2551 ret_alloc *= 2;
2552 identifier_to_locale_free (ret);
2553 ret = NULL;
2554 continue;
2556 else
2558 conversion_ok = false;
2559 break;
2562 *outbuf = 0;
2563 break;
2565 iconv_close (cd);
2566 if (conversion_ok)
2567 return ret;
2570 #endif
2572 /* Otherwise, convert non-ASCII characters in IDENT to UCNs. */
2574 char *ret = (char *) identifier_to_locale_alloc (10 * idlen + 1);
2575 char *p = ret;
2576 for (i = 0; i < idlen;)
2578 unsigned int c;
2579 size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
2580 if (utf8_len == 1)
2581 *p++ = uid[i];
2582 else
2584 sprintf (p, "\\U%08x", c);
2585 p += 10;
2587 i += utf8_len;
2589 *p = 0;
2590 return ret;
2594 /* Support for encoding URLs.
2595 See egmontkob/Hyperlinks_in_Terminal_Emulators.md
2596 ( https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda ).
2598 > A hyperlink is opened upon encountering an OSC 8 escape sequence with
2599 > the target URI. The syntax is
2601 > OSC 8 ; params ; URI ST
2603 > A hyperlink is closed with the same escape sequence, omitting the
2604 > parameters and the URI but keeping the separators:
2606 > OSC 8 ; ; ST
2608 > OSC (operating system command) is typically ESC ].
2610 Use BEL instead of ST, as that is currently rendered better in some
2611 terminal emulators that don't support OSC 8, like konsole. */
2613 /* If URL-printing is enabled, write an "open URL" escape sequence to PP
2614 for the given URL. */
2616 void
2617 pp_begin_url (pretty_printer *pp, const char *url)
2619 if (!url)
2621 /* Handle null URL by skipping all output here,
2622 and in the next pp_end_url. */
2623 pp->m_skipping_null_url = true;
2624 return;
2626 switch (pp->url_format)
2628 case URL_FORMAT_NONE:
2629 break;
2630 case URL_FORMAT_ST:
2631 pp_string (pp, "\33]8;;");
2632 pp_string (pp, url);
2633 pp_string (pp, "\33\\");
2634 break;
2635 case URL_FORMAT_BEL:
2636 pp_string (pp, "\33]8;;");
2637 pp_string (pp, url);
2638 pp_string (pp, "\a");
2639 break;
2640 default:
2641 gcc_unreachable ();
2645 /* Helper function for pp_end_url and pp_format, return the "close URL" escape
2646 sequence string. */
2648 static const char *
2649 get_end_url_string (pretty_printer *pp)
2651 switch (pp->url_format)
2653 case URL_FORMAT_NONE:
2654 return "";
2655 case URL_FORMAT_ST:
2656 return "\33]8;;\33\\";
2657 case URL_FORMAT_BEL:
2658 return "\33]8;;\a";
2659 default:
2660 gcc_unreachable ();
2664 /* If URL-printing is enabled, write a "close URL" escape sequence to PP. */
2666 void
2667 pp_end_url (pretty_printer *pp)
2669 if (pp->m_skipping_null_url)
2671 /* We gracefully handle pp_begin_url (NULL) by omitting output for
2672 both begin and end. Here we handle the latter. */
2673 pp->m_skipping_null_url = false;
2674 return;
2676 if (pp->url_format != URL_FORMAT_NONE)
2677 pp_string (pp, get_end_url_string (pp));
2680 #if CHECKING_P
2682 namespace selftest {
2684 /* Smoketest for pretty_printer. */
2686 static void
2687 test_basic_printing ()
2689 pretty_printer pp;
2690 pp_string (&pp, "hello");
2691 pp_space (&pp);
2692 pp_string (&pp, "world");
2694 ASSERT_STREQ ("hello world", pp_formatted_text (&pp));
2697 /* Helper function for testing pp_format.
2698 Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
2699 prints EXPECTED, assuming that pp_show_color is SHOW_COLOR. */
2701 static void
2702 assert_pp_format_va (const location &loc, const char *expected,
2703 bool show_color, const char *fmt, va_list *ap)
2705 pretty_printer pp;
2706 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
2708 text_info ti (fmt, ap, 0, nullptr, &rich_loc);
2710 pp_show_color (&pp) = show_color;
2711 pp_format (&pp, &ti);
2712 pp_output_formatted_text (&pp);
2713 ASSERT_STREQ_AT (loc, expected, pp_formatted_text (&pp));
2716 /* Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
2717 prints EXPECTED, with show_color disabled. */
2719 static void
2720 assert_pp_format (const location &loc, const char *expected,
2721 const char *fmt, ...)
2723 va_list ap;
2725 va_start (ap, fmt);
2726 assert_pp_format_va (loc, expected, false, fmt, &ap);
2727 va_end (ap);
2730 /* As above, but with colorization enabled. */
2732 static void
2733 assert_pp_format_colored (const location &loc, const char *expected,
2734 const char *fmt, ...)
2736 /* The tests of colorization assume the default color scheme.
2737 If GCC_COLORS is set, then the colors have potentially been
2738 overridden; skip the test. */
2739 if (getenv ("GCC_COLORS"))
2740 return;
2742 va_list ap;
2744 va_start (ap, fmt);
2745 assert_pp_format_va (loc, expected, true, fmt, &ap);
2746 va_end (ap);
2749 /* Helper function for calling testing pp_format,
2750 by calling assert_pp_format with various numbers of arguments.
2751 These exist mostly to avoid having to write SELFTEST_LOCATION
2752 throughout test_pp_format. */
2754 #define ASSERT_PP_FORMAT_1(EXPECTED, FMT, ARG1) \
2755 SELFTEST_BEGIN_STMT \
2756 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
2757 (ARG1)); \
2758 SELFTEST_END_STMT
2760 #define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \
2761 SELFTEST_BEGIN_STMT \
2762 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
2763 (ARG1), (ARG2)); \
2764 SELFTEST_END_STMT
2766 #define ASSERT_PP_FORMAT_3(EXPECTED, FMT, ARG1, ARG2, ARG3) \
2767 SELFTEST_BEGIN_STMT \
2768 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
2769 (ARG1), (ARG2), (ARG3)); \
2770 SELFTEST_END_STMT
2772 /* Verify that pp_format works, for various format codes. */
2774 static void
2775 test_pp_format ()
2777 /* Avoid introducing locale-specific differences in the results
2778 by hardcoding open_quote and close_quote. */
2779 auto_fix_quotes fix_quotes;
2781 /* Verify that plain text is passed through unchanged. */
2782 assert_pp_format (SELFTEST_LOCATION, "unformatted", "unformatted");
2784 /* Verify various individual format codes, in the order listed in the
2785 comment for pp_format above. For each code, we append a second
2786 argument with a known bit pattern (0x12345678), to ensure that we
2787 are consuming arguments correctly. */
2788 ASSERT_PP_FORMAT_2 ("-27 12345678", "%d %x", -27, 0x12345678);
2789 ASSERT_PP_FORMAT_2 ("-5 12345678", "%i %x", -5, 0x12345678);
2790 ASSERT_PP_FORMAT_2 ("10 12345678", "%u %x", 10, 0x12345678);
2791 ASSERT_PP_FORMAT_2 ("17 12345678", "%o %x", 15, 0x12345678);
2792 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678);
2793 ASSERT_PP_FORMAT_2 ("-27 12345678", "%ld %x", (long)-27, 0x12345678);
2794 ASSERT_PP_FORMAT_2 ("-5 12345678", "%li %x", (long)-5, 0x12345678);
2795 ASSERT_PP_FORMAT_2 ("10 12345678", "%lu %x", (long)10, 0x12345678);
2796 ASSERT_PP_FORMAT_2 ("17 12345678", "%lo %x", (long)15, 0x12345678);
2797 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%lx %x", (long)0xcafebabe,
2798 0x12345678);
2799 ASSERT_PP_FORMAT_2 ("-27 12345678", "%lld %x", (long long)-27, 0x12345678);
2800 ASSERT_PP_FORMAT_2 ("-5 12345678", "%lli %x", (long long)-5, 0x12345678);
2801 ASSERT_PP_FORMAT_2 ("10 12345678", "%llu %x", (long long)10, 0x12345678);
2802 ASSERT_PP_FORMAT_2 ("17 12345678", "%llo %x", (long long)15, 0x12345678);
2803 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe,
2804 0x12345678);
2805 ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", (HOST_WIDE_INT)-27, 0x12345678);
2806 ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", (HOST_WIDE_INT)-5, 0x12345678);
2807 ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", (unsigned HOST_WIDE_INT)10,
2808 0x12345678);
2809 ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", (HOST_WIDE_INT)15, 0x12345678);
2810 ASSERT_PP_FORMAT_2 ("0xcafebabe 12345678", "%wx %x", (HOST_WIDE_INT)0xcafebabe,
2811 0x12345678);
2812 ASSERT_PP_FORMAT_2 ("-27 12345678", "%zd %x", (ssize_t)-27, 0x12345678);
2813 ASSERT_PP_FORMAT_2 ("-5 12345678", "%zi %x", (ssize_t)-5, 0x12345678);
2814 ASSERT_PP_FORMAT_2 ("10 12345678", "%zu %x", (size_t)10, 0x12345678);
2815 ASSERT_PP_FORMAT_2 ("17 12345678", "%zo %x", (size_t)15, 0x12345678);
2816 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%zx %x", (size_t)0xcafebabe,
2817 0x12345678);
2818 ASSERT_PP_FORMAT_2 ("-27 12345678", "%td %x", (ptrdiff_t)-27, 0x12345678);
2819 ASSERT_PP_FORMAT_2 ("-5 12345678", "%ti %x", (ptrdiff_t)-5, 0x12345678);
2820 ASSERT_PP_FORMAT_2 ("10 12345678", "%tu %x", (ptrdiff_t)10, 0x12345678);
2821 ASSERT_PP_FORMAT_2 ("17 12345678", "%to %x", (ptrdiff_t)15, 0x12345678);
2822 ASSERT_PP_FORMAT_2 ("1afebabe 12345678", "%tx %x", (ptrdiff_t)0x1afebabe,
2823 0x12345678);
2824 ASSERT_PP_FORMAT_2 ("1.000000 12345678", "%f %x", 1.0, 0x12345678);
2825 ASSERT_PP_FORMAT_2 ("A 12345678", "%c %x", 'A', 0x12345678);
2826 ASSERT_PP_FORMAT_2 ("hello world 12345678", "%s %x", "hello world",
2827 0x12345678);
2829 /* Not nul-terminated. */
2830 char arr[5] = { '1', '2', '3', '4', '5' };
2831 ASSERT_PP_FORMAT_3 ("123 12345678", "%.*s %x", 3, arr, 0x12345678);
2832 ASSERT_PP_FORMAT_3 ("1234 12345678", "%.*s %x", -1, "1234", 0x12345678);
2833 ASSERT_PP_FORMAT_3 ("12345 12345678", "%.*s %x", 7, "12345", 0x12345678);
2835 /* We can't test for %p; the pointer is printed in an implementation-defined
2836 manner. */
2837 ASSERT_PP_FORMAT_2 ("normal colored normal 12345678",
2838 "normal %rcolored%R normal %x",
2839 "error", 0x12345678);
2840 assert_pp_format_colored
2841 (SELFTEST_LOCATION,
2842 "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678",
2843 "normal %rcolored%R normal %x", "error", 0x12345678);
2844 /* TODO:
2845 %m: strerror(text->err_no) - does not consume a value from args_ptr. */
2846 ASSERT_PP_FORMAT_1 ("% 12345678", "%% %x", 0x12345678);
2847 ASSERT_PP_FORMAT_1 ("` 12345678", "%< %x", 0x12345678);
2848 ASSERT_PP_FORMAT_1 ("' 12345678", "%> %x", 0x12345678);
2849 ASSERT_PP_FORMAT_1 ("' 12345678", "%' %x", 0x12345678);
2850 ASSERT_PP_FORMAT_3 ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678);
2851 ASSERT_PP_FORMAT_2 ("abc 12345678", "%.3s %x", "abcdef", 0x12345678);
2853 /* Verify flag 'q'. */
2854 ASSERT_PP_FORMAT_2 ("`foo' 12345678", "%qs %x", "foo", 0x12345678);
2855 assert_pp_format_colored (SELFTEST_LOCATION,
2856 "`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x",
2857 "foo", 0x12345678);
2858 /* Verify "%@". */
2860 diagnostic_event_id_t first (2);
2861 diagnostic_event_id_t second (7);
2863 ASSERT_PP_FORMAT_2 ("first `free' at (3); second `free' at (8)",
2864 "first %<free%> at %@; second %<free%> at %@",
2865 &first, &second);
2866 assert_pp_format_colored
2867 (SELFTEST_LOCATION,
2868 "first `\e[01m\e[Kfree\e[m\e[K' at \e[01;36m\e[K(3)\e[m\e[K;"
2869 " second `\e[01m\e[Kfree\e[m\e[K' at \e[01;36m\e[K(8)\e[m\e[K",
2870 "first %<free%> at %@; second %<free%> at %@",
2871 &first, &second);
2874 /* Verify %Z. */
2875 int v[] = { 1, 2, 3 };
2876 ASSERT_PP_FORMAT_3 ("1, 2, 3 12345678", "%Z %x", v, 3, 0x12345678);
2878 int v2[] = { 0 };
2879 ASSERT_PP_FORMAT_3 ("0 12345678", "%Z %x", v2, 1, 0x12345678);
2881 /* Verify that combinations work, along with unformatted text. */
2882 assert_pp_format (SELFTEST_LOCATION,
2883 "the quick brown fox jumps over the lazy dog",
2884 "the %s %s %s jumps over the %s %s",
2885 "quick", "brown", "fox", "lazy", "dog");
2886 assert_pp_format (SELFTEST_LOCATION, "item 3 of 7", "item %i of %i", 3, 7);
2887 assert_pp_format (SELFTEST_LOCATION, "problem with `bar' at line 10",
2888 "problem with %qs at line %i", "bar", 10);
2890 /* Verified numbered args. */
2891 assert_pp_format (SELFTEST_LOCATION,
2892 "foo: second bar: first",
2893 "foo: %2$s bar: %1$s",
2894 "first", "second");
2895 assert_pp_format (SELFTEST_LOCATION,
2896 "foo: 1066 bar: 1776",
2897 "foo: %2$i bar: %1$i",
2898 1776, 1066);
2899 assert_pp_format (SELFTEST_LOCATION,
2900 "foo: second bar: 1776",
2901 "foo: %2$s bar: %1$i",
2902 1776, "second");
2905 /* A subclass of pretty_printer for use by test_prefixes_and_wrapping. */
2907 class test_pretty_printer : public pretty_printer
2909 public:
2910 test_pretty_printer (enum diagnostic_prefixing_rule_t rule,
2911 int max_line_length)
2913 pp_set_prefix (this, xstrdup ("PREFIX: "));
2914 wrapping.rule = rule;
2915 pp_set_line_maximum_length (this, max_line_length);
2919 /* Verify that the various values of enum diagnostic_prefixing_rule_t work
2920 as expected, with and without line wrapping. */
2922 static void
2923 test_prefixes_and_wrapping ()
2925 /* Tests of the various prefixing rules, without wrapping.
2926 Newlines embedded in pp_string don't affect it; we have to
2927 explicitly call pp_newline. */
2929 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 0);
2930 pp_string (&pp, "the quick brown fox");
2931 pp_newline (&pp);
2932 pp_string (&pp, "jumps over the lazy dog");
2933 pp_newline (&pp);
2934 ASSERT_STREQ (pp_formatted_text (&pp),
2935 "PREFIX: the quick brown fox\n"
2936 " jumps over the lazy dog\n");
2939 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 0);
2940 pp_string (&pp, "the quick brown fox");
2941 pp_newline (&pp);
2942 pp_string (&pp, "jumps over the lazy dog");
2943 pp_newline (&pp);
2944 ASSERT_STREQ (pp_formatted_text (&pp),
2945 "the quick brown fox\n"
2946 "jumps over the lazy dog\n");
2949 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 0);
2950 pp_string (&pp, "the quick brown fox");
2951 pp_newline (&pp);
2952 pp_string (&pp, "jumps over the lazy dog");
2953 pp_newline (&pp);
2954 ASSERT_STREQ (pp_formatted_text (&pp),
2955 "PREFIX: the quick brown fox\n"
2956 "PREFIX: jumps over the lazy dog\n");
2959 /* Tests of the various prefixing rules, with wrapping. */
2961 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 20);
2962 pp_string (&pp, "the quick brown fox jumps over the lazy dog");
2963 pp_newline (&pp);
2964 pp_string (&pp, "able was I ere I saw elba");
2965 pp_newline (&pp);
2966 ASSERT_STREQ (pp_formatted_text (&pp),
2967 "PREFIX: the quick \n"
2968 " brown fox jumps \n"
2969 " over the lazy \n"
2970 " dog\n"
2971 " able was I ere I \n"
2972 " saw elba\n");
2975 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 20);
2976 pp_string (&pp, "the quick brown fox jumps over the lazy dog");
2977 pp_newline (&pp);
2978 pp_string (&pp, "able was I ere I saw elba");
2979 pp_newline (&pp);
2980 ASSERT_STREQ (pp_formatted_text (&pp),
2981 "the quick brown fox \n"
2982 "jumps over the lazy \n"
2983 "dog\n"
2984 "able was I ere I \n"
2985 "saw elba\n");
2988 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 20);
2989 pp_string (&pp, "the quick brown fox jumps over the lazy dog");
2990 pp_newline (&pp);
2991 pp_string (&pp, "able was I ere I saw elba");
2992 pp_newline (&pp);
2993 ASSERT_STREQ (pp_formatted_text (&pp),
2994 "PREFIX: the quick brown fox jumps over the lazy dog\n"
2995 "PREFIX: able was I ere I saw elba\n");
3000 /* Verify that URL-printing works as expected. */
3002 void
3003 test_urls ()
3006 pretty_printer pp;
3007 pp.url_format = URL_FORMAT_NONE;
3008 pp_begin_url (&pp, "http://example.com");
3009 pp_string (&pp, "This is a link");
3010 pp_end_url (&pp);
3011 ASSERT_STREQ ("This is a link",
3012 pp_formatted_text (&pp));
3016 pretty_printer pp;
3017 pp.url_format = URL_FORMAT_ST;
3018 pp_begin_url (&pp, "http://example.com");
3019 pp_string (&pp, "This is a link");
3020 pp_end_url (&pp);
3021 ASSERT_STREQ ("\33]8;;http://example.com\33\\This is a link\33]8;;\33\\",
3022 pp_formatted_text (&pp));
3026 pretty_printer pp;
3027 pp.url_format = URL_FORMAT_BEL;
3028 pp_begin_url (&pp, "http://example.com");
3029 pp_string (&pp, "This is a link");
3030 pp_end_url (&pp);
3031 ASSERT_STREQ ("\33]8;;http://example.com\aThis is a link\33]8;;\a",
3032 pp_formatted_text (&pp));
3036 /* Verify that we gracefully reject null URLs. */
3038 void
3039 test_null_urls ()
3042 pretty_printer pp;
3043 pp.url_format = URL_FORMAT_NONE;
3044 pp_begin_url (&pp, nullptr);
3045 pp_string (&pp, "This isn't a link");
3046 pp_end_url (&pp);
3047 ASSERT_STREQ ("This isn't a link",
3048 pp_formatted_text (&pp));
3052 pretty_printer pp;
3053 pp.url_format = URL_FORMAT_ST;
3054 pp_begin_url (&pp, nullptr);
3055 pp_string (&pp, "This isn't a link");
3056 pp_end_url (&pp);
3057 ASSERT_STREQ ("This isn't a link",
3058 pp_formatted_text (&pp));
3062 pretty_printer pp;
3063 pp.url_format = URL_FORMAT_BEL;
3064 pp_begin_url (&pp, nullptr);
3065 pp_string (&pp, "This isn't a link");
3066 pp_end_url (&pp);
3067 ASSERT_STREQ ("This isn't a link",
3068 pp_formatted_text (&pp));
3072 /* Verify that URLification works as expected. */
3074 static void
3075 pp_printf_with_urlifier (pretty_printer *pp,
3076 const urlifier *urlifier,
3077 const char *msg, ...)
3079 va_list ap;
3081 va_start (ap, msg);
3082 text_info text (msg, &ap, errno);
3083 pp_format (pp, &text, urlifier);
3084 pp_output_formatted_text (pp, urlifier);
3085 va_end (ap);
3089 void
3090 test_urlification ()
3092 class test_urlifier : public urlifier
3094 public:
3095 char *
3096 get_url_for_quoted_text (const char *p, size_t sz) const final override
3098 if (!strncmp (p, "-foption", sz))
3099 return xstrdup ("http://example.com");
3100 return nullptr;
3104 auto_fix_quotes fix_quotes;
3105 const test_urlifier urlifier;
3107 /* Uses of "%<" and "%>". */
3110 pretty_printer pp;
3111 pp.url_format = URL_FORMAT_NONE;
3112 pp_printf_with_urlifier (&pp, &urlifier,
3113 "foo %<-foption%> %<unrecognized%> bar");
3114 ASSERT_STREQ ("foo `-foption' `unrecognized' bar",
3115 pp_formatted_text (&pp));
3118 pretty_printer pp;
3119 pp.url_format = URL_FORMAT_ST;
3120 pp_printf_with_urlifier (&pp, &urlifier,
3121 "foo %<-foption%> %<unrecognized%> bar");
3122 ASSERT_STREQ
3123 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
3124 " `unrecognized' bar",
3125 pp_formatted_text (&pp));
3128 pretty_printer pp;
3129 pp.url_format = URL_FORMAT_BEL;
3130 pp_printf_with_urlifier (&pp, &urlifier,
3131 "foo %<-foption%> %<unrecognized%> bar");
3132 ASSERT_STREQ
3133 ("foo `\33]8;;http://example.com\a-foption\33]8;;\a'"
3134 " `unrecognized' bar",
3135 pp_formatted_text (&pp));
3139 /* Use of "%qs". */
3141 pretty_printer pp;
3142 pp.url_format = URL_FORMAT_ST;
3143 pp_printf_with_urlifier (&pp, &urlifier,
3144 "foo %qs %qs bar",
3145 "-foption", "unrecognized");
3146 ASSERT_STREQ
3147 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
3148 " `unrecognized' bar",
3149 pp_formatted_text (&pp));
3152 /* Mixed usage of %< and %s, where the quoted string is built between
3153 a mixture of phase 1 and phase 2. */
3155 pretty_printer pp;
3156 pp.url_format = URL_FORMAT_ST;
3157 pp_printf_with_urlifier (&pp, &urlifier,
3158 "foo %<-f%s%> bar",
3159 "option");
3160 ASSERT_STREQ
3161 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3162 pp_formatted_text (&pp));
3165 /* Likewise, where there is trailing phase 1 content within the
3166 quoted region. */
3168 pretty_printer pp;
3169 pp.url_format = URL_FORMAT_ST;
3170 pp_printf_with_urlifier (&pp, &urlifier,
3171 "foo %<-f%sion%> bar %<-f%sion%> baz",
3172 "opt", "opt");
3173 ASSERT_STREQ
3174 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
3175 pp_formatted_text (&pp));
3178 /* Likewise. */
3180 pretty_printer pp;
3181 pp.url_format = URL_FORMAT_ST;
3182 pp_printf_with_urlifier (&pp, &urlifier,
3183 "foo %<%sption%> bar %<-f%sion%> baz",
3184 "-fo", "opt");
3185 ASSERT_STREQ
3186 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
3187 pp_formatted_text (&pp));
3190 /* Another mixed usage of %< and %s, where the quoted string is built
3191 between a mixture of phase 1 and multiple phase 2. */
3193 pretty_printer pp;
3194 pp.url_format = URL_FORMAT_ST;
3195 pp_printf_with_urlifier (&pp, &urlifier,
3196 "foo %<-f%s%s%> bar",
3197 "opt", "ion");
3198 ASSERT_STREQ
3199 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3200 pp_formatted_text (&pp));
3203 /* Mixed usage of %< and %s with a prefix. */
3205 pretty_printer pp;
3206 pp.url_format = URL_FORMAT_ST;
3207 pp_set_prefix (&pp, xstrdup ("PREFIX"));
3208 pp_printf_with_urlifier (&pp, &urlifier,
3209 "foo %<-f%s%> bar",
3210 "option");
3211 ASSERT_STREQ
3212 ("PREFIXfoo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3213 pp_formatted_text (&pp));
3216 /* Example of mixed %< and %s with numbered args. */
3218 pretty_printer pp;
3219 pp.url_format = URL_FORMAT_ST;
3220 pp_printf_with_urlifier (&pp, &urlifier,
3221 "foo %<-f%2$st%1$sn%> bar",
3222 "io", "op");
3223 ASSERT_STREQ
3224 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3225 pp_formatted_text (&pp));
3229 /* Test multibyte awareness. */
3230 static void test_utf8 ()
3233 /* Check that pp_quoted_string leaves valid UTF-8 alone. */
3235 pretty_printer pp;
3236 const char *s = "\xf0\x9f\x98\x82";
3237 pp_quoted_string (&pp, s);
3238 ASSERT_STREQ (pp_formatted_text (&pp), s);
3241 /* Check that pp_quoted_string escapes non-UTF-8 nonprintable bytes. */
3243 pretty_printer pp;
3244 pp_quoted_string (&pp, "\xf0!\x9f\x98\x82");
3245 ASSERT_STREQ (pp_formatted_text (&pp),
3246 "\\xf0!\\x9f\\x98\\x82");
3249 /* Check that pp_character will line-wrap at the beginning of a UTF-8
3250 sequence, but not in the middle. */
3252 pretty_printer pp (3);
3253 const char s[] = "---\xf0\x9f\x98\x82";
3254 for (int i = 0; i != sizeof (s) - 1; ++i)
3255 pp_character (&pp, s[i]);
3256 pp_newline (&pp);
3257 for (int i = 1; i != sizeof (s) - 1; ++i)
3258 pp_character (&pp, s[i]);
3259 pp_character (&pp, '-');
3260 ASSERT_STREQ (pp_formatted_text (&pp),
3261 "---\n"
3262 "\xf0\x9f\x98\x82\n"
3263 "--\xf0\x9f\x98\x82\n"
3264 "-");
3269 /* Run all of the selftests within this file. */
3271 void
3272 pretty_print_cc_tests ()
3274 test_basic_printing ();
3275 test_pp_format ();
3276 test_prefixes_and_wrapping ();
3277 test_urls ();
3278 test_null_urls ();
3279 test_urlification ();
3280 test_utf8 ();
3283 } // namespace selftest
3285 #endif /* CHECKING_P */