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