Branch data Line data Source code
1 : : /* Various declarations for language-independent pretty-print subroutines.
2 : : Copyright (C) 2003-2025 Free Software Foundation, Inc.
3 : : Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
4 : :
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 : 1208418 : text_info::set_location (unsigned int idx, location_t loc,
854 : : enum range_display_kind range_display_kind)
855 : : {
856 : 1208418 : gcc_checking_assert (m_richloc);
857 : 1208418 : m_richloc->set_range (idx, loc, range_display_kind);
858 : 1208418 : }
859 : :
860 : : location_t
861 : 95573788 : text_info::get_location (unsigned int index_of_location) const
862 : : {
863 : 95573788 : gcc_checking_assert (m_richloc);
864 : :
865 : 95573788 : if (index_of_location == 0)
866 : 95573788 : return m_richloc->get_loc ();
867 : : else
868 : : return UNKNOWN_LOCATION;
869 : : }
870 : :
871 : : // Default construct an output buffer.
872 : :
873 : 675109304 : output_buffer::output_buffer ()
874 : 675109304 : : m_formatted_obstack (),
875 : 675109304 : m_chunk_obstack (),
876 : 675109304 : m_obstack (&m_formatted_obstack),
877 : 675109304 : m_cur_formatted_chunks (nullptr),
878 : 675109304 : m_stream (stderr),
879 : 675109304 : m_line_length (),
880 : 675109304 : m_digit_buffer (),
881 : 675109304 : m_flush_p (true)
882 : : {
883 : 675109304 : obstack_init (&m_formatted_obstack);
884 : 675109304 : obstack_init (&m_chunk_obstack);
885 : 675109304 : }
886 : :
887 : : // Release resources owned by an output buffer at the end of lifetime.
888 : :
889 : 674272014 : output_buffer::~output_buffer ()
890 : : {
891 : 674272014 : obstack_free (&m_chunk_obstack, NULL);
892 : 674272014 : obstack_free (&m_formatted_obstack, NULL);
893 : 674272014 : }
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 : 13146023 : output_buffer::push_formatted_chunks ()
901 : : {
902 : : /* Allocate a new chunk structure. */
903 : 13146023 : pp_formatted_chunks *new_chunk_array
904 : 13146023 : = XOBNEW (&m_chunk_obstack, pp_formatted_chunks);
905 : 13146023 : new_chunk_array->m_prev = m_cur_formatted_chunks;
906 : 13146023 : m_cur_formatted_chunks = new_chunk_array;
907 : 13146023 : 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 : 13146023 : output_buffer::pop_formatted_chunks ()
916 : : {
917 : 13146023 : pp_formatted_chunks *old_top = m_cur_formatted_chunks;
918 : 13146023 : gcc_assert (old_top);
919 : 13146023 : m_cur_formatted_chunks = old_top->m_prev;
920 : 13146023 : obstack_free (&m_chunk_obstack, old_top);
921 : 13146023 : }
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 : 677680587 : 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 : 677680587 : if (!pp_is_wrapping_line (this)
1065 : 16 : || pp_prefixing_rule (this) == DIAGNOSTICS_SHOW_PREFIX_ONCE
1066 : 677680595 : || pp_prefixing_rule (this) == DIAGNOSTICS_SHOW_PREFIX_NEVER)
1067 : 677680583 : 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 : 677680587 : }
1079 : :
1080 : : /* Clear this pretty_printer's output state. */
1081 : : inline void
1082 : 22714441 : pretty_printer::clear_state ()
1083 : : {
1084 : 22714441 : m_emitted_prefix = false;
1085 : 22714441 : pp_indentation (this) = 0;
1086 : : }
1087 : :
1088 : : /* Print X to PP in decimal. */
1089 : : template<unsigned int N, typename T>
1090 : : void
1091 : 970740 : pp_wide_integer (pretty_printer *pp, const poly_int<N, T> &x)
1092 : : {
1093 : : if (x.is_constant ())
1094 : 970740 : 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 : 970740 : }
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 : 8649550 : pp_write_text_to_stream (pretty_printer *pp)
1115 : : {
1116 : 8649550 : const char *text = pp_formatted_text (pp);
1117 : : #ifdef __MINGW32__
1118 : : mingw_ansi_fputs (text, pp_buffer (pp)->m_stream);
1119 : : #else
1120 : 8649550 : fputs (text, pp_buffer (pp)->m_stream);
1121 : : #endif
1122 : 8649550 : pp_clear_output_area (pp);
1123 : 8649550 : }
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 : 2929 : pp_write_text_as_dot_label_to_stream (pretty_printer *pp, bool for_record)
1136 : : {
1137 : 2929 : const char *text = pp_formatted_text (pp);
1138 : 2929 : const char *p = text;
1139 : 2929 : FILE *fp = pp_buffer (pp)->m_stream;
1140 : :
1141 : 722362 : for (;*p; p++)
1142 : : {
1143 : 719433 : bool escape_char;
1144 : 719433 : switch (*p)
1145 : : {
1146 : : /* Print newlines as a left-aligned newline. */
1147 : 24516 : case '\n':
1148 : 24516 : fputs ("\\l", fp);
1149 : 24516 : escape_char = true;
1150 : 24516 : 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 : 1 : 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 : 1 : 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 : 138527 : if (escape_char)
1180 : 138742 : fputc ('\\', fp);
1181 : :
1182 : 719433 : fputc (*p, fp);
1183 : : }
1184 : :
1185 : 2929 : pp_clear_output_area (pp);
1186 : 2929 : }
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 : 681 : pp_write_text_as_html_like_dot_to_stream (pretty_printer *pp)
1205 : : {
1206 : 681 : const char *text = pp_formatted_text (pp);
1207 : 681 : const char *p = text;
1208 : 681 : FILE *fp = pp_buffer (pp)->m_stream;
1209 : :
1210 : 26840 : for (;*p; p++)
1211 : : {
1212 : 26159 : switch (*p)
1213 : : {
1214 : 36 : case '"':
1215 : 36 : fputs (""", fp);
1216 : 36 : break;
1217 : 18 : case '&':
1218 : 18 : fputs ("&", fp);
1219 : 18 : break;
1220 : 84 : case '<':
1221 : 84 : fputs ("<", fp);
1222 : 84 : break;
1223 : 72 : case '>':
1224 : 72 : fputs (">",fp);
1225 : 72 : break;
1226 : :
1227 : 25949 : default:
1228 : 25949 : fputc (*p, fp);
1229 : 25949 : break;
1230 : : }
1231 : : }
1232 : :
1233 : 681 : pp_clear_output_area (pp);
1234 : 681 : }
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 : 745894515 : pp_maybe_wrap_text (pretty_printer *pp, const char *start, const char *end)
1272 : : {
1273 : 745894515 : if (pp_is_wrapping_line (pp))
1274 : 24 : pp_wrap_text (pp, start, end);
1275 : : else
1276 : 745894491 : pp_append_text (pp, start, end);
1277 : 745894515 : }
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 : 774832292 : pp_append_r (pretty_printer *pp, const char *start, int length)
1283 : : {
1284 : 774832292 : 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 : 22446 : pp_indent (pretty_printer *pp)
1292 : : {
1293 : 22446 : int n = pp_indentation (pp);
1294 : 22446 : int i;
1295 : :
1296 : 1045226 : for (i = 0; i < n; ++i)
1297 : 1022780 : pp_space (pp);
1298 : 22446 : }
1299 : :
1300 : : static const char *get_end_url_string (pretty_printer *);
1301 : :
1302 : : /* struct pp_token. */
1303 : :
1304 : 47988112 : pp_token::pp_token (enum kind k)
1305 : 47988112 : : m_kind (k),
1306 : 47988112 : m_prev (nullptr),
1307 : 47988112 : m_next (nullptr)
1308 : : {
1309 : 47988112 : }
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 : 101947454 : allocate_object (size_t sz, obstack &s)
1381 : : {
1382 : : /* We must not be half-way through an object. */
1383 : 101947454 : gcc_assert (obstack_base (&s) == obstack_next_free (&s));
1384 : :
1385 : 101947454 : obstack_blank (&s, sz);
1386 : 101947454 : void *buf = obstack_finish (&s);
1387 : 101947454 : return buf;
1388 : : }
1389 : :
1390 : : /* Make room for a pp_token instance within obstack S. */
1391 : :
1392 : : void *
1393 : 47988112 : pp_token::operator new (size_t sz, obstack &s)
1394 : : {
1395 : 47988112 : return allocate_object (sz, s);
1396 : : }
1397 : :
1398 : : void
1399 : 47988112 : 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 : 47988112 : }
1404 : :
1405 : : /* class pp_token_list. */
1406 : :
1407 : : /* Make room for a pp_token_list instance within obstack S. */
1408 : :
1409 : : void *
1410 : 49167054 : pp_token_list::operator new (size_t sz, obstack &s)
1411 : : {
1412 : 49167054 : return allocate_object (sz, s);
1413 : : }
1414 : :
1415 : : void
1416 : 49167054 : 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 : 49167054 : }
1422 : :
1423 : 65961946 : pp_token_list::pp_token_list (obstack &s)
1424 : 65961946 : : m_obstack (s),
1425 : 65961946 : m_first (nullptr),
1426 : 65961946 : m_end (nullptr)
1427 : : {
1428 : 65961946 : }
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 : 65961946 : pp_token_list::~pp_token_list ()
1440 : : {
1441 : 100970734 : for (auto iter = m_first; iter; )
1442 : : {
1443 : 35008788 : pp_token *next = iter->m_next;
1444 : 35008788 : delete iter;
1445 : 35008788 : iter = next;
1446 : : }
1447 : 65961946 : }
1448 : :
1449 : : void
1450 : 57027587 : pp_token_list::push_back_text (label_text &&text)
1451 : : {
1452 : 57027587 : if (text.get ()[0] == '\0')
1453 : : return; // pushing empty string is a no-op
1454 : 42059888 : 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 : 81971953 : pp_token_list::push_back (std::unique_ptr<pp_token> tok)
1468 : : {
1469 : 81971953 : if (!m_first)
1470 : : {
1471 : 58304109 : gcc_assert (m_end == nullptr);
1472 : 58304109 : m_first = tok.get ();
1473 : 58304109 : m_end = tok.get ();
1474 : : }
1475 : : else
1476 : : {
1477 : 23667844 : gcc_assert (m_end != nullptr);
1478 : 23667844 : m_end->m_next = tok.get ();
1479 : 23667844 : tok->m_prev = m_end;
1480 : 23667844 : m_end = tok.get ();
1481 : : }
1482 : 81971953 : tok.release ();
1483 : 81971953 : }
1484 : :
1485 : : void
1486 : 35280095 : pp_token_list::push_back_list (pp_token_list &&list)
1487 : : {
1488 : 69301332 : while (auto tok = list.pop_front ())
1489 : 69301332 : push_back (std::move (tok));
1490 : 35280095 : }
1491 : :
1492 : : std::unique_ptr<pp_token>
1493 : 69343844 : pp_token_list::pop_front ()
1494 : : {
1495 : 69343844 : pp_token *result = m_first;
1496 : 69343844 : if (result == nullptr)
1497 : 35280103 : return nullptr;
1498 : :
1499 : 34063741 : gcc_assert (result->m_prev == nullptr);
1500 : 34063741 : m_first = result->m_next;
1501 : 34063741 : if (result->m_next)
1502 : : {
1503 : 2331552 : gcc_assert (result != m_end);
1504 : 2331552 : m_first->m_prev = nullptr;
1505 : : }
1506 : : else
1507 : : {
1508 : 31732189 : gcc_assert (result == m_end);
1509 : 31732189 : m_end = nullptr;
1510 : : }
1511 : 34063741 : result->m_next = nullptr;
1512 : 34063741 : return std::unique_ptr<pp_token> (result);
1513 : : }
1514 : :
1515 : : std::unique_ptr<pp_token>
1516 : 12936828 : pp_token_list::remove_token (pp_token *tok)
1517 : : {
1518 : 12936828 : gcc_assert (tok);
1519 : 12936828 : if (tok->m_prev)
1520 : : {
1521 : 12936828 : gcc_assert (tok != m_first);
1522 : 12936828 : 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 : 12936828 : if (tok->m_next)
1530 : : {
1531 : 9079539 : gcc_assert (tok != m_end);
1532 : 9079539 : tok->m_next->m_prev = tok->m_prev;
1533 : : }
1534 : : else
1535 : : {
1536 : 3857289 : gcc_assert (tok == m_end);
1537 : 3857289 : m_end = tok->m_prev;
1538 : : }
1539 : 12936828 : tok->m_prev = nullptr;
1540 : 12936828 : tok->m_next = nullptr;
1541 : 12936828 : gcc_assert (m_first != tok);
1542 : 12936828 : gcc_assert (m_end != tok);
1543 : 12936828 : return std::unique_ptr<pp_token> (tok);
1544 : : }
1545 : :
1546 : : /* Insert NEW_TOK after RELATIVE_TOK. */
1547 : :
1548 : : void
1549 : 37404 : pp_token_list::insert_after (std::unique_ptr<pp_token> new_tok_up,
1550 : : pp_token *relative_tok)
1551 : : {
1552 : 37404 : pp_token *new_tok = new_tok_up.release ();
1553 : :
1554 : 37404 : gcc_assert (new_tok);
1555 : 37404 : gcc_assert (new_tok->m_prev == nullptr);
1556 : 37404 : gcc_assert (new_tok->m_next == nullptr);
1557 : 37404 : gcc_assert (relative_tok);
1558 : :
1559 : 37404 : if (relative_tok->m_next)
1560 : : {
1561 : 37404 : gcc_assert (relative_tok != m_end);
1562 : 37404 : 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 : 37404 : new_tok->m_prev = relative_tok;
1570 : 37404 : new_tok->m_next = relative_tok->m_next;
1571 : 37404 : relative_tok->m_next = new_tok;
1572 : 37404 : }
1573 : :
1574 : : void
1575 : 13146023 : pp_token_list::replace_custom_tokens ()
1576 : : {
1577 : 13146023 : pp_token *iter = m_first;
1578 : 47167260 : while (iter)
1579 : : {
1580 : 34021237 : pp_token *next = iter->m_next;
1581 : 34021237 : if (iter->m_kind == pp_token::kind::custom_data)
1582 : : {
1583 : 3648865 : pp_token_list tok_list (m_obstack);
1584 : 3648865 : pp_token_custom_data *sub = as_a <pp_token_custom_data *> (iter);
1585 : 3648865 : 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 : 3648865 : }
1597 : : iter = next;
1598 : : }
1599 : 13146023 : }
1600 : :
1601 : : /* Merge any runs of consecutive text tokens within this list
1602 : : into individual text tokens. */
1603 : :
1604 : : void
1605 : 13146027 : pp_token_list::merge_consecutive_text_tokens ()
1606 : : {
1607 : 13146027 : pp_token *start_of_run = m_first;
1608 : 34230460 : while (start_of_run)
1609 : : {
1610 : 21084433 : if (start_of_run->m_kind != pp_token::kind::text)
1611 : : {
1612 : 5848316 : start_of_run = start_of_run->m_next;
1613 : 5848316 : continue;
1614 : : }
1615 : : pp_token *end_of_run = start_of_run;
1616 : 28172937 : while (end_of_run->m_next
1617 : 28172937 : && end_of_run->m_next->m_kind == pp_token::kind::text)
1618 : : end_of_run = end_of_run->m_next;
1619 : 15236117 : 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 : 22521396 : for (auto iter = start_of_run; iter != end_of_run->m_next;
1627 : 17729108 : iter = iter->m_next)
1628 : : {
1629 : 17729108 : pp_token_text *iter_text = static_cast<pp_token_text *> (iter);
1630 : 17729108 : sz += strlen (iter_text->m_value.get ());
1631 : : }
1632 : :
1633 : : /* Allocate and populate buffer for merged text
1634 : : (within m_obstack). */
1635 : 4792288 : char * const buf = (char *)allocate_object (sz + 1, m_obstack);
1636 : 4792288 : char *p = buf;
1637 : 22521396 : for (auto iter = start_of_run; iter != end_of_run->m_next;
1638 : 17729108 : iter = iter->m_next)
1639 : : {
1640 : 17729108 : pp_token_text *iter_text = static_cast<pp_token_text *> (iter);
1641 : 17729108 : size_t iter_sz = strlen (iter_text->m_value.get ());
1642 : 17729108 : memcpy (p, iter_text->m_value.get (), iter_sz);
1643 : 17729108 : p += iter_sz;
1644 : : }
1645 : 4792288 : *p = '\0';
1646 : :
1647 : : /* Replace start_of_run's buffer pointer with the new buffer. */
1648 : 4792288 : static_cast<pp_token_text *> (start_of_run)->m_value
1649 : 4792288 : = label_text::borrow (buf);
1650 : :
1651 : : /* Remove all the other text tokens in the run. */
1652 : 4792288 : pp_token * const next = end_of_run->m_next;
1653 : 17729108 : while (start_of_run->m_next != next)
1654 : 12936820 : 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 : 13146027 : }
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 : 1538017 : pp_token_list::apply_urlifier (const urlifier &urlifier)
1668 : : {
1669 : 4802808 : for (pp_token *iter = m_first; iter; )
1670 : : {
1671 : 3264791 : if (iter->m_kind == pp_token::kind::begin_quote
1672 : 917983 : && iter->m_next
1673 : 917983 : && iter->m_next->m_kind == pp_token::kind::text
1674 : 909905 : && iter->m_next->m_next
1675 : 885993 : && iter->m_next->m_next->m_kind == pp_token::kind::end_quote)
1676 : : {
1677 : 871232 : pp_token *begin_quote = iter;
1678 : 871232 : pp_token_text *text = as_a <pp_token_text *> (begin_quote->m_next);
1679 : 871232 : pp_token *end_quote = text->m_next;
1680 : 1742464 : if (char *url = urlifier.get_url_for_quoted_text
1681 : 871232 : (text->m_value.get (),
1682 : : strlen (text->m_value.get ())))
1683 : : {
1684 : 18698 : auto begin_url
1685 : 18698 : = make_token<pp_token_begin_url> (label_text::take (url));
1686 : 18698 : auto end_url = make_token<pp_token_end_url> ();
1687 : 18698 : insert_after (std::move (begin_url), begin_quote);
1688 : 18698 : insert_after (std::move (end_url), text);
1689 : 18698 : }
1690 : 871232 : iter = end_quote->m_next;
1691 : 871232 : }
1692 : : else
1693 : 2393559 : iter = iter->m_next;
1694 : : }
1695 : 1538017 : }
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 : 56973996 : push_back_any_text (pp_token_list *tok_list,
1743 : : obstack *cur_obstack)
1744 : : {
1745 : 56973996 : obstack_1grow (cur_obstack, '\0');
1746 : 56973996 : tok_list->push_back_text
1747 : 56973996 : (label_text::borrow (XOBFINISH (cur_obstack,
1748 : : const char *)));
1749 : 56973996 : }
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 : 13146023 : pretty_printer::format (text_info &text)
1817 : : {
1818 : 13146023 : pp_formatted_chunks *new_chunk_array = m_buffer->push_formatted_chunks ();
1819 : 13146023 : pp_token_list **args = new_chunk_array->m_args;
1820 : :
1821 : 13146023 : pp_token_list **formatters[PP_NL_ARGMAX];
1822 : 13146023 : 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 : 13146023 : 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 : 13146023 : m_buffer->m_obstack = &m_buffer->m_chunk_obstack;
1843 : 13146023 : const int old_line_length = m_buffer->m_line_length;
1844 : 13146023 : const pp_wrapping_mode_t old_wrapping_mode = pp_set_verbatim_wrapping (this);
1845 : :
1846 : 13146023 : 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 : 13146023 : if (m_format_postprocessor)
1851 : 336331 : m_format_postprocessor->handle (this);
1852 : :
1853 : : /* Revert to normal obstack and wrapping mode. */
1854 : 13146023 : m_buffer->m_obstack = &m_buffer->m_formatted_obstack;
1855 : 13146023 : m_buffer->m_line_length = old_line_length;
1856 : 13146023 : pp_wrapping_mode (this) = old_wrapping_mode;
1857 : 13146023 : clear_state ();
1858 : 13146023 : }
1859 : :
1860 : : static void
1861 : 13146023 : format_phase_1 (const text_info &text,
1862 : : obstack &chunk_obstack,
1863 : : pp_token_list **args,
1864 : : pp_token_list ***formatters)
1865 : : {
1866 : 13146023 : unsigned chunk = 0;
1867 : 13146023 : unsigned int curarg = 0;
1868 : 13146023 : bool any_unnumbered = false, any_numbered = false;
1869 : 13146023 : pp_token_list *cur_token_list;
1870 : 13146023 : args[chunk++] = cur_token_list = pp_token_list::make (chunk_obstack);
1871 : 21688401 : for (const char *p = text.m_format_spec; *p; )
1872 : : {
1873 : 248666248 : while (*p != '\0' && *p != '%')
1874 : : {
1875 : 227028493 : obstack_1grow (&chunk_obstack, *p);
1876 : 227028493 : p++;
1877 : : }
1878 : :
1879 : 21637755 : if (*p == '\0')
1880 : : break;
1881 : :
1882 : 14182298 : switch (*++p)
1883 : : {
1884 : 0 : case '\0':
1885 : 0 : gcc_unreachable ();
1886 : :
1887 : 13226 : case '%':
1888 : 13226 : obstack_1grow (&chunk_obstack, '%');
1889 : 13226 : p++;
1890 : 13226 : continue;
1891 : :
1892 : 122800 : case '<':
1893 : 122800 : {
1894 : 122800 : push_back_any_text (cur_token_list, &chunk_obstack);
1895 : 122800 : cur_token_list->push_back<pp_token_begin_quote> ();
1896 : 122800 : p++;
1897 : 122800 : continue;
1898 : : }
1899 : :
1900 : 122800 : case '>':
1901 : 122800 : {
1902 : 122800 : push_back_any_text (cur_token_list, &chunk_obstack);
1903 : 122800 : cur_token_list->push_back<pp_token_end_quote> ();
1904 : 122800 : p++;
1905 : 122800 : continue;
1906 : : }
1907 : 18373 : case '\'':
1908 : 18373 : {
1909 : 18373 : push_back_any_text (cur_token_list, &chunk_obstack);
1910 : 18373 : cur_token_list->push_back<pp_token_end_quote> ();
1911 : 18373 : p++;
1912 : : }
1913 : 18373 : 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 : 18089 : case 'R':
1924 : 18089 : {
1925 : 18089 : push_back_any_text (cur_token_list, &chunk_obstack);
1926 : 18089 : cur_token_list->push_back<pp_token_end_color> ();
1927 : 18089 : p++;
1928 : 18089 : continue;
1929 : : }
1930 : :
1931 : 15 : case 'm':
1932 : 15 : {
1933 : 15 : const char *errstr = xstrerror (text.m_err_no);
1934 : 15 : obstack_grow (&chunk_obstack, errstr, strlen (errstr));
1935 : : }
1936 : 15 : p++;
1937 : 15 : continue;
1938 : :
1939 : 13886959 : default:
1940 : : /* Handled in phase 2. Terminate the plain chunk here. */
1941 : 13886959 : push_back_any_text (cur_token_list, &chunk_obstack);
1942 : 13886959 : break;
1943 : 295339 : }
1944 : :
1945 : : /* Start a new token list for the formatting args. */
1946 : 13886959 : args[chunk] = cur_token_list = pp_token_list::make (chunk_obstack);
1947 : :
1948 : 13886959 : unsigned argno;
1949 : 13886959 : 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 : 13886911 : argno = curarg++;
1963 : 13886911 : any_unnumbered = true;
1964 : 13886911 : gcc_assert (!any_numbered);
1965 : : }
1966 : 13886959 : gcc_assert (argno < PP_NL_ARGMAX);
1967 : 13886959 : gcc_assert (!formatters[argno]);
1968 : 13886959 : formatters[argno] = &args[chunk++];
1969 : 15405054 : do
1970 : : {
1971 : 15405054 : obstack_1grow (&chunk_obstack, *p);
1972 : 15405054 : p++;
1973 : : }
1974 : 15405054 : while (strchr ("qwlzt+#", p[-1]));
1975 : :
1976 : 13886959 : if (p[-1] == '.')
1977 : : {
1978 : : /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1979 : : (where M == N + 1). */
1980 : 6267 : 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 : 6259 : gcc_assert (*p == '*');
1993 : 6259 : obstack_1grow (&chunk_obstack, '*');
1994 : 6259 : p++;
1995 : :
1996 : 6259 : 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 : 6255 : gcc_assert (!any_numbered);
2011 : 6255 : formatters[argno+1] = formatters[argno];
2012 : 6255 : curarg++;
2013 : : }
2014 : 6259 : gcc_assert (*p == 's');
2015 : 6259 : obstack_1grow (&chunk_obstack, 's');
2016 : 6259 : p++;
2017 : : }
2018 : : }
2019 : 13886959 : if (*p == '\0')
2020 : : {
2021 : 5639920 : push_back_any_text (cur_token_list, &chunk_obstack);
2022 : 5639920 : break;
2023 : : }
2024 : :
2025 : 8247039 : obstack_1grow (&chunk_obstack, '\0');
2026 : 8247039 : push_back_any_text (cur_token_list, &chunk_obstack);
2027 : :
2028 : : /* Start a new token list for the next (non-formatted) text. */
2029 : 8247039 : gcc_assert (chunk < PP_NL_ARGMAX * 2);
2030 : 8247039 : args[chunk++] = cur_token_list = pp_token_list::make (chunk_obstack);
2031 : : }
2032 : :
2033 : 13146023 : obstack_1grow (&chunk_obstack, '\0');
2034 : 13146023 : push_back_any_text (cur_token_list, &chunk_obstack);
2035 : 13146023 : gcc_assert (chunk < PP_NL_ARGMAX * 2);
2036 : 13146023 : args[chunk] = nullptr;
2037 : 13146023 : }
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 : 13146023 : format_phase_2 (pretty_printer *pp,
2044 : : text_info &text,
2045 : : obstack &chunk_obstack,
2046 : : pp_token_list ***formatters)
2047 : : {
2048 : 13146023 : unsigned argno;
2049 : 27032982 : for (argno = 0; formatters[argno]; argno++)
2050 : : {
2051 : 13886959 : int precision = 0;
2052 : 13886959 : bool wide = false;
2053 : 13886959 : bool plus = false;
2054 : 13886959 : bool hash = false;
2055 : 13886959 : bool quote = false;
2056 : :
2057 : : /* We expect a single text token containing the formatter. */
2058 : 13886959 : pp_token_list *tok_list = *(formatters[argno]);
2059 : 13886959 : gcc_assert (tok_list);
2060 : 13886959 : gcc_assert (tok_list->m_first == tok_list->m_end);
2061 : 13886959 : gcc_assert (tok_list->m_first->m_kind == pp_token::kind::text);
2062 : :
2063 : : /* Accumulate the value of the formatted text into here. */
2064 : 13886959 : pp_token_list *formatted_tok_list
2065 : 13886959 : = pp_token_list::make (chunk_obstack);
2066 : :
2067 : : /* We do not attempt to enforce any ordering on the modifier
2068 : : characters. */
2069 : :
2070 : 13886959 : const char *p;
2071 : 15405054 : for (p = as_a <pp_token_text *> (tok_list->m_first)->m_value.get ();; p++)
2072 : : {
2073 : 15405054 : switch (*p)
2074 : : {
2075 : 964326 : case 'q':
2076 : 964326 : gcc_assert (!quote);
2077 : 964326 : quote = true;
2078 : 964326 : continue;
2079 : :
2080 : 3368 : case '+':
2081 : 3368 : gcc_assert (!plus);
2082 : 3368 : plus = true;
2083 : 3368 : continue;
2084 : :
2085 : 40985 : case '#':
2086 : 40985 : gcc_assert (!hash);
2087 : 40985 : hash = true;
2088 : 40985 : continue;
2089 : :
2090 : 59368 : case 'w':
2091 : 59368 : gcc_assert (!wide);
2092 : 59368 : wide = true;
2093 : 59368 : 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 : 449989 : case 'l':
2106 : : /* We don't support precision beyond that of "long long". */
2107 : 449989 : gcc_assert (precision < 2);
2108 : 449989 : precision++;
2109 : 449989 : continue;
2110 : : }
2111 : 13886959 : break;
2112 : : }
2113 : :
2114 : 13886959 : gcc_assert (!wide || precision == 0);
2115 : :
2116 : 13886959 : if (quote)
2117 : : {
2118 : 964326 : push_back_any_text (formatted_tok_list, &chunk_obstack);
2119 : 964326 : formatted_tok_list->push_back<pp_token_begin_quote> ();
2120 : : }
2121 : :
2122 : 13886959 : switch (*p)
2123 : : {
2124 : 18089 : case 'r':
2125 : 18089 : {
2126 : 18089 : const char *color = va_arg (*text.m_args_ptr, const char *);
2127 : 18089 : formatted_tok_list->push_back<pp_token_begin_color>
2128 : 18089 : (label_text::borrow (color));
2129 : : }
2130 : 18089 : break;
2131 : :
2132 : 3500 : case 'c':
2133 : 3500 : {
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 : 3500 : char chr = (char) va_arg (*text.m_args_ptr, int);
2138 : 3500 : if (ISPRINT (chr) || !quote)
2139 : 3460 : pp_character (pp, chr);
2140 : : else
2141 : : {
2142 : 40 : const char str [2] = { chr, '\0' };
2143 : 40 : pp_quoted_string (pp, str, 1);
2144 : : }
2145 : : break;
2146 : : }
2147 : :
2148 : 1042809 : case 'd':
2149 : 1042809 : case 'i':
2150 : 1042809 : if (wide)
2151 : 19795 : pp_wide_integer (pp, va_arg (*text.m_args_ptr, HOST_WIDE_INT));
2152 : : else
2153 : 1023014 : 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 : 4269971 : case 's':
2167 : 4269971 : if (quote)
2168 : 690163 : pp_quoted_string (pp, va_arg (*text.m_args_ptr, const char *));
2169 : : else
2170 : 3579808 : 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 : 1331890 : case 'p':
2184 : 1331890 : pp_pointer (pp, va_arg (*text.m_args_ptr, void *));
2185 : 1331890 : break;
2186 : :
2187 : 1919420 : case 'u':
2188 : 1919420 : if (wide)
2189 : 39503 : pp_scalar (pp, HOST_WIDE_INT_PRINT_UNSIGNED,
2190 : : va_arg (*text.m_args_ptr, unsigned HOST_WIDE_INT));
2191 : : else
2192 : 1879917 : pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2193 : : unsigned, "u");
2194 : : break;
2195 : :
2196 : 66675 : case 'f':
2197 : 66675 : pp_double (pp, va_arg (*text.m_args_ptr, double));
2198 : 66675 : 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 : 2453 : case 'x':
2218 : 2453 : 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 : 2387 : pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2223 : : unsigned, "x");
2224 : : break;
2225 : :
2226 : 6267 : case '.':
2227 : 6267 : {
2228 : 6267 : int n;
2229 : 6267 : 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 : 6267 : p++;
2235 : 6267 : 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 : 6259 : gcc_assert (*p == '*');
2245 : 6259 : p++;
2246 : 6259 : gcc_assert (*p == 's');
2247 : 6259 : n = va_arg (*text.m_args_ptr, int);
2248 : :
2249 : : /* This consumes a second entry in the formatters array. */
2250 : 6259 : gcc_assert (formatters[argno] == formatters[argno+1]);
2251 : : argno++;
2252 : : }
2253 : :
2254 : 6267 : 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 : 6267 : size_t len = n < 0 ? strlen (s) : strnlen (s, n);
2260 : :
2261 : 6267 : pp_append_text (pp, s, s + len);
2262 : : }
2263 : 6267 : break;
2264 : :
2265 : 23801 : case '@':
2266 : 23801 : {
2267 : : /* diagnostic_event_id_t *. */
2268 : 23801 : diagnostic_event_id_ptr event_id
2269 : 23801 : = va_arg (*text.m_args_ptr, diagnostic_event_id_ptr);
2270 : 23801 : gcc_assert (event_id->known_p ());
2271 : 23801 : formatted_tok_list->push_back<pp_token_event_id> (*event_id);
2272 : : }
2273 : 23801 : 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 : 23759 : case 'e':
2284 : 23759 : {
2285 : 23759 : pp_element *element = va_arg (*text.m_args_ptr, pp_element *);
2286 : 23759 : pp_markup::context ctxt (*pp,
2287 : : quote, /* by reference */
2288 : 23759 : formatted_tok_list);
2289 : 23759 : element->add_to_phase_2 (ctxt);
2290 : : }
2291 : 23759 : break;
2292 : :
2293 : 5177890 : default:
2294 : 5177890 : {
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 : 5177890 : printer_fn format_decoder = pp_format_decoder (pp);
2301 : 5177890 : gcc_assert (format_decoder);
2302 : 5177890 : gcc_assert (formatted_tok_list);
2303 : 5177890 : bool ok = format_decoder (pp, &text, p,
2304 : : precision, wide, plus, hash, "e,
2305 : : *formatted_tok_list);
2306 : 5177890 : gcc_assert (ok);
2307 : : }
2308 : : }
2309 : :
2310 : 13886959 : if (quote)
2311 : : {
2312 : 920672 : push_back_any_text (formatted_tok_list, &chunk_obstack);
2313 : 920672 : formatted_tok_list->push_back<pp_token_end_quote> ();
2314 : : }
2315 : :
2316 : 13886959 : push_back_any_text (formatted_tok_list, &chunk_obstack);
2317 : 13886959 : delete *formatters[argno];
2318 : 13886959 : *formatters[argno] = formatted_tok_list;
2319 : : }
2320 : :
2321 : : if (CHECKING_P)
2322 : 393633495 : for (; argno < PP_NL_ARGMAX; argno++)
2323 : 380487472 : gcc_assert (!formatters[argno]);
2324 : 13146023 : }
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 : 13146023 : pp_output_formatted_text (pretty_printer *pp,
2336 : : const urlifier *urlifier)
2337 : : {
2338 : 13146023 : output_buffer * const buffer = pp_buffer (pp);
2339 : 13146023 : gcc_assert (buffer->m_obstack == &buffer->m_formatted_obstack);
2340 : :
2341 : 13146023 : pp_formatted_chunks *chunk_array = buffer->m_cur_formatted_chunks;
2342 : 13146023 : pp_token_list * const *token_lists = chunk_array->get_token_lists ();
2343 : :
2344 : 13146023 : {
2345 : : /* Consolidate into one token list. */
2346 : 13146023 : pp_token_list tokens (buffer->m_chunk_obstack);
2347 : 48426118 : for (unsigned chunk = 0; token_lists[chunk]; chunk++)
2348 : : {
2349 : 35280095 : tokens.push_back_list (std::move (*token_lists[chunk]));
2350 : 35280095 : delete token_lists[chunk];
2351 : : }
2352 : :
2353 : 13146023 : tokens.replace_custom_tokens ();
2354 : :
2355 : 13146023 : tokens.merge_consecutive_text_tokens ();
2356 : :
2357 : 13146023 : if (urlifier)
2358 : 1538017 : 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 : 13146023 : if (pp->m_token_printer)
2363 : 10845597 : pp->m_token_printer->print_tokens (pp, tokens);
2364 : : else
2365 : 2300426 : 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 : 13146023 : }
2372 : :
2373 : 13146023 : buffer->pop_formatted_chunks ();
2374 : 13146023 : }
2375 : :
2376 : : /* Default implementation of token printing. */
2377 : :
2378 : : void
2379 : 2300426 : default_token_printer (pretty_printer *pp,
2380 : : const pp_token_list &tokens)
2381 : : {
2382 : : /* Convert to text, possibly with colorization, URLs, etc. */
2383 : 8567778 : for (auto iter = tokens.m_first; iter; iter = iter->m_next)
2384 : 6267352 : switch (iter->m_kind)
2385 : : {
2386 : 0 : default:
2387 : 0 : gcc_unreachable ();
2388 : :
2389 : 4102760 : case pp_token::kind::text:
2390 : 4102760 : {
2391 : 4102760 : pp_token_text *sub = as_a <pp_token_text *> (iter);
2392 : 4102760 : pp_string (pp, sub->m_value.get ());
2393 : : }
2394 : 4102760 : break;
2395 : :
2396 : 26001 : case pp_token::kind::begin_color:
2397 : 26001 : {
2398 : 26001 : pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);
2399 : 26001 : pp_string (pp, colorize_start (pp_show_color (pp),
2400 : : sub->m_value.get ()));
2401 : : }
2402 : 26001 : break;
2403 : 26001 : case pp_token::kind::end_color:
2404 : 26001 : pp_string (pp, colorize_stop (pp_show_color (pp)));
2405 : 26001 : break;
2406 : :
2407 : 1047676 : case pp_token::kind::begin_quote:
2408 : 1047676 : pp_begin_quote (pp, pp_show_color (pp));
2409 : 1047676 : break;
2410 : 1003740 : case pp_token::kind::end_quote:
2411 : 1003740 : pp_end_quote (pp, pp_show_color (pp));
2412 : 1003740 : break;
2413 : :
2414 : 18708 : case pp_token::kind::begin_url:
2415 : 18708 : {
2416 : 18708 : pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
2417 : 18708 : pp_begin_url (pp, sub->m_value.get ());
2418 : : }
2419 : 18708 : break;
2420 : 18708 : case pp_token::kind::end_url:
2421 : 18708 : pp_end_url (pp);
2422 : 18708 : break;
2423 : :
2424 : 23758 : case pp_token::kind::event_id:
2425 : 23758 : {
2426 : 23758 : pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
2427 : 23758 : gcc_assert (sub->m_event_id.known_p ());
2428 : 23758 : pp_string (pp, colorize_start (pp_show_color (pp), "path"));
2429 : 23758 : pp_character (pp, '(');
2430 : 23758 : pp_decimal_int (pp, sub->m_event_id.one_based ());
2431 : 23758 : pp_character (pp, ')');
2432 : 23758 : pp_string (pp, colorize_stop (pp_show_color (pp)));
2433 : : }
2434 : 23758 : 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 : 6267352 : break;
2440 : : }
2441 : 2300426 : }
2442 : :
2443 : : /* Helper subroutine of output_verbatim and verbatim. Do the appropriate
2444 : : settings needed by BUFFER for a verbatim formatting. */
2445 : : void
2446 : 65790 : pp_format_verbatim (pretty_printer *pp, text_info *text)
2447 : : {
2448 : : /* Set verbatim mode. */
2449 : 65790 : pp_wrapping_mode_t oldmode = pp_set_verbatim_wrapping (pp);
2450 : :
2451 : : /* Do the actual formatting. */
2452 : 65790 : pp_format (pp, text);
2453 : 65790 : pp_output_formatted_text (pp);
2454 : :
2455 : : /* Restore previous settings. */
2456 : 65790 : pp_wrapping_mode (pp) = oldmode;
2457 : 65790 : }
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 : 9561196 : pp_flush (pretty_printer *pp)
2463 : : {
2464 : 9561196 : pp->clear_state ();
2465 : 9561196 : if (!pp_buffer (pp)->m_flush_p)
2466 : : return;
2467 : 8367116 : pp_write_text_to_stream (pp);
2468 : 8367116 : 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 : 7222 : pp_really_flush (pretty_printer *pp)
2475 : : {
2476 : 7222 : pp->clear_state ();
2477 : 7222 : pp_write_text_to_stream (pp);
2478 : 7222 : fflush (pp_buffer (pp)->m_stream);
2479 : 7222 : }
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 : 103143 : pp_set_line_maximum_length (pretty_printer *pp, int length)
2486 : : {
2487 : 103143 : pp_line_cutoff (pp) = length;
2488 : 103143 : pp->set_real_maximum_length ();
2489 : 103143 : }
2490 : :
2491 : : /* Clear PRETTY-PRINTER output area text info. */
2492 : : void
2493 : 204397534 : pp_clear_output_area (pretty_printer *pp)
2494 : : {
2495 : 204397534 : obstack_free (pp_buffer (pp)->m_obstack,
2496 : : obstack_base (pp_buffer (pp)->m_obstack));
2497 : 204397534 : pp_buffer (pp)->m_line_length = 0;
2498 : 204397534 : }
2499 : :
2500 : : /* Set PREFIX for PRETTY-PRINTER, taking ownership of PREFIX, which
2501 : : will eventually be free-ed. */
2502 : :
2503 : : void
2504 : 677577444 : pretty_printer::set_prefix (char *prefix)
2505 : : {
2506 : 677577444 : free (m_prefix);
2507 : 677577444 : m_prefix = prefix;
2508 : 677577444 : set_real_maximum_length ();
2509 : 677577444 : m_emitted_prefix = false;
2510 : 677577444 : pp_indentation (this) = 0;
2511 : 677577444 : }
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 : 394995 : pp_take_prefix (pretty_printer *pp)
2519 : : {
2520 : 394995 : char *result = pp->m_prefix;
2521 : 394995 : pp->m_prefix = nullptr;
2522 : 394995 : return result;
2523 : : }
2524 : :
2525 : : /* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */
2526 : : void
2527 : 1220297 : pp_destroy_prefix (pretty_printer *pp)
2528 : : {
2529 : 1220297 : if (pp->m_prefix)
2530 : : {
2531 : 1220297 : free (pp->m_prefix);
2532 : 1220297 : pp->m_prefix = nullptr;
2533 : : }
2534 : 1220297 : }
2535 : :
2536 : : /* Write out this pretty_printer's prefix. */
2537 : : void
2538 : 296178950 : pretty_printer::emit_prefix ()
2539 : : {
2540 : 296178950 : if (m_prefix)
2541 : : {
2542 : 1939080 : switch (pp_prefixing_rule (this))
2543 : : {
2544 : : default:
2545 : : case DIAGNOSTICS_SHOW_PREFIX_NEVER:
2546 : : break;
2547 : :
2548 : 1590605 : case DIAGNOSTICS_SHOW_PREFIX_ONCE:
2549 : 1590605 : if (m_emitted_prefix)
2550 : : {
2551 : 24 : pp_indent (this);
2552 : 24 : break;
2553 : : }
2554 : 1590581 : pp_indentation (this) += 3;
2555 : : /* Fall through. */
2556 : :
2557 : 1610582 : case DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE:
2558 : 1610582 : {
2559 : 1610582 : int prefix_length = strlen (m_prefix);
2560 : 1610582 : pp_append_r (this, m_prefix, prefix_length);
2561 : 1610582 : m_emitted_prefix = true;
2562 : : }
2563 : 1610582 : break;
2564 : : }
2565 : : }
2566 : 296178950 : }
2567 : :
2568 : : /* Construct a PRETTY-PRINTER of MAXIMUM_LENGTH characters per line. */
2569 : :
2570 : 673726658 : pretty_printer::pretty_printer (int maximum_length)
2571 : 673726658 : : m_buffer (new (XCNEW (output_buffer)) output_buffer ()),
2572 : 673726658 : m_prefix (nullptr),
2573 : 673726658 : m_padding (pp_none),
2574 : 673726658 : m_maximum_length (0),
2575 : 673726658 : m_indent_skip (0),
2576 : 673726658 : m_wrapping (),
2577 : 673726658 : m_format_decoder (nullptr),
2578 : 673726658 : m_format_postprocessor (nullptr),
2579 : 673726658 : m_token_printer (nullptr),
2580 : 673726658 : m_emitted_prefix (false),
2581 : 673726658 : m_need_newline (false),
2582 : 673726658 : m_translate_identifiers (true),
2583 : 673726658 : m_show_color (false),
2584 : 673726658 : m_show_highlight_colors (false),
2585 : 673726658 : m_url_format (URL_FORMAT_NONE),
2586 : 673726658 : m_skipping_null_url (false)
2587 : : {
2588 : 673726658 : pp_line_cutoff (this) = maximum_length;
2589 : : /* By default, we emit prefixes once per message. */
2590 : 673726658 : pp_prefixing_rule (this) = DIAGNOSTICS_SHOW_PREFIX_ONCE;
2591 : 673726658 : pp_set_prefix (this, NULL);
2592 : 673726658 : }
2593 : :
2594 : : /* Copy constructor for pretty_printer. */
2595 : :
2596 : 1286280 : pretty_printer::pretty_printer (const pretty_printer &other)
2597 : 1286280 : : m_buffer (new (XCNEW (output_buffer)) output_buffer ()),
2598 : 1286280 : m_prefix (nullptr),
2599 : 1286280 : m_padding (other.m_padding),
2600 : 1286280 : m_maximum_length (other.m_maximum_length),
2601 : 1286280 : m_indent_skip (other.m_indent_skip),
2602 : 1286280 : m_wrapping (other.m_wrapping),
2603 : 1286280 : m_format_decoder (other.m_format_decoder),
2604 : 1286280 : m_format_postprocessor (nullptr),
2605 : 1286280 : m_token_printer (other.m_token_printer),
2606 : 1286280 : m_emitted_prefix (other.m_emitted_prefix),
2607 : 1286280 : m_need_newline (other.m_need_newline),
2608 : 1286280 : m_translate_identifiers (other.m_translate_identifiers),
2609 : 1286280 : m_show_color (other.m_show_color),
2610 : 1286280 : m_show_highlight_colors (other.m_show_highlight_colors),
2611 : 1286280 : m_url_format (other.m_url_format),
2612 : 1286280 : m_skipping_null_url (false)
2613 : : {
2614 : 1286280 : pp_line_cutoff (this) = m_maximum_length;
2615 : : /* By default, we emit prefixes once per message. */
2616 : 1286280 : pp_prefixing_rule (this) = pp_prefixing_rule (&other);
2617 : 1286280 : pp_set_prefix (this, NULL);
2618 : :
2619 : 1286280 : if (other.m_format_postprocessor)
2620 : 220071 : m_format_postprocessor = other.m_format_postprocessor->clone ();
2621 : 1286280 : }
2622 : :
2623 : 674879888 : pretty_printer::~pretty_printer ()
2624 : : {
2625 : 674177617 : m_buffer->~output_buffer ();
2626 : 674177617 : XDELETE (m_buffer);
2627 : 674177617 : free (m_prefix);
2628 : 674879888 : }
2629 : :
2630 : : /* Base class implementation of pretty_printer::clone vfunc. */
2631 : :
2632 : : std::unique_ptr<pretty_printer>
2633 : 802883 : pretty_printer::clone () const
2634 : : {
2635 : 802883 : 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 : 760043163 : 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 : 760043163 : if (pp_buffer (pp)->m_line_length == 0)
2648 : : {
2649 : 296072053 : pp->emit_prefix ();
2650 : 296072053 : if (pp_is_wrapping_line (pp))
2651 : 52 : while (start != end && *start == ' ')
2652 : 0 : ++start;
2653 : : }
2654 : 760043163 : pp_append_r (pp, start, end - start);
2655 : 760043163 : }
2656 : :
2657 : : /* Finishes constructing a NULL-terminated character string representing
2658 : : the PRETTY-PRINTED text. */
2659 : : const char *
2660 : 216451987 : pp_formatted_text (pretty_printer *pp)
2661 : : {
2662 : 216451987 : 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 : 336308373 : pp_last_position_in_text (const pretty_printer *pp)
2669 : : {
2670 : 336308373 : 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 : 658675 : pp_printf (pretty_printer *pp, const char *msg, ...)
2684 : : {
2685 : 658675 : va_list ap;
2686 : :
2687 : 658675 : va_start (ap, msg);
2688 : 658675 : text_info text (msg, &ap, errno);
2689 : 658675 : pp_format (pp, &text);
2690 : 658675 : pp_output_formatted_text (pp);
2691 : 658675 : va_end (ap);
2692 : 658675 : }
2693 : :
2694 : : /* Format a message into PP using ngettext to handle
2695 : : singular vs plural. */
2696 : :
2697 : : void
2698 : 1593 : pp_printf_n (pretty_printer *pp,
2699 : : unsigned HOST_WIDE_INT n,
2700 : : const char *singular_gmsgid, const char *plural_gmsgid, ...)
2701 : : {
2702 : 1593 : va_list ap;
2703 : :
2704 : 1593 : va_start (ap, plural_gmsgid);
2705 : :
2706 : 1593 : unsigned long gtn;
2707 : 1593 : if (sizeof n <= sizeof gtn)
2708 : 1593 : 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 : 1593 : const char *msg = ngettext (singular_gmsgid, plural_gmsgid, gtn);
2715 : 1593 : text_info text (msg, &ap, errno);
2716 : 1593 : pp_format (pp, &text);
2717 : 1593 : pp_output_formatted_text (pp);
2718 : 1593 : va_end (ap);
2719 : 1593 : }
2720 : :
2721 : : /* Output MESSAGE verbatim into BUFFER. */
2722 : : void
2723 : 65790 : pp_verbatim (pretty_printer *pp, const char *msg, ...)
2724 : : {
2725 : 65790 : va_list ap;
2726 : :
2727 : 65790 : va_start (ap, msg);
2728 : 65790 : text_info text (msg, &ap, errno);
2729 : 65790 : pp_format_verbatim (pp, &text);
2730 : 65790 : va_end (ap);
2731 : 65790 : }
2732 : :
2733 : :
2734 : :
2735 : : /* Have PRETTY-PRINTER start a new line. */
2736 : : void
2737 : 8122552 : pp_newline (pretty_printer *pp)
2738 : : {
2739 : 8122552 : obstack_1grow (pp_buffer (pp)->m_obstack, '\n');
2740 : 8122552 : pp_needs_newline (pp) = false;
2741 : 8122552 : pp_buffer (pp)->m_line_length = 0;
2742 : 8122552 : }
2743 : :
2744 : : /* Have PRETTY-PRINTER add a CHARACTER. */
2745 : : void
2746 : 421980455 : pp_character (pretty_printer *pp, int c)
2747 : : {
2748 : 421980455 : 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 : 421980715 : && pp->remaining_character_count_for_line () <= 0)
2752 : : {
2753 : 8 : pp_newline (pp);
2754 : 8 : if (ISSPACE (c))
2755 : : return;
2756 : : }
2757 : 421980455 : obstack_1grow (pp_buffer (pp)->m_obstack, c);
2758 : 421980455 : ++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 : 745203012 : pp_string (pretty_printer *pp, const char *str)
2765 : : {
2766 : 745203012 : gcc_checking_assert (str);
2767 : 745203012 : pp_maybe_wrap_text (pp, str, str + strlen (str));
2768 : 745203012 : }
2769 : :
2770 : : /* As per pp_string, but only append the first LEN of STR. */
2771 : :
2772 : : void
2773 : 1224 : pp_string_n (pretty_printer *pp, const char *str, size_t len)
2774 : : {
2775 : 1224 : gcc_checking_assert (str);
2776 : 1224 : pp_maybe_wrap_text (pp, str, str + len);
2777 : 1224 : }
2778 : :
2779 : : /* Append code point C to the output area of PRETTY-PRINTER, encoding it
2780 : : as UTF-8. */
2781 : :
2782 : : void
2783 : 13178547 : pp_unicode_character (pretty_printer *pp, unsigned c)
2784 : : {
2785 : 13178547 : static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
2786 : 13178547 : static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
2787 : 13178547 : size_t nbytes;
2788 : 13178547 : uchar buf[6], *p = &buf[6];
2789 : :
2790 : 13178547 : nbytes = 1;
2791 : 13178547 : if (c < 0x80)
2792 : 13126420 : *--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 : 13178547 : pp_append_r (pp, (const char *)p, nbytes);
2806 : 13178547 : }
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 : 690211 : pp_quoted_string (pretty_printer *pp, const char *str, size_t n /* = -1 */)
2814 : : {
2815 : 690211 : gcc_checking_assert (str);
2816 : :
2817 : 690211 : const char *last = str;
2818 : 690211 : const char *ps;
2819 : :
2820 : : /* Compute the length if not specified. */
2821 : 690211 : if (n == (size_t) -1)
2822 : 690171 : n = strlen (str);
2823 : :
2824 : 5504973 : for (ps = str; n; ++ps, --n)
2825 : : {
2826 : 4814762 : if (ISPRINT (*ps))
2827 : 4814702 : continue;
2828 : :
2829 : : /* Don't escape a valid UTF-8 extended char. */
2830 : 72 : const unsigned char *ups = (const unsigned char *) ps;
2831 : 72 : if (*ups & 0x80)
2832 : : {
2833 : 33 : unsigned int extended_char;
2834 : 33 : const int valid_utf8_len = decode_utf8_char (ups, n, &extended_char);
2835 : 33 : if (valid_utf8_len > 0)
2836 : : {
2837 : 12 : ps += valid_utf8_len - 1;
2838 : 12 : n -= valid_utf8_len - 1;
2839 : 12 : continue;
2840 : : }
2841 : : }
2842 : :
2843 : 60 : 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 : 60 : char buf [11];
2849 : 60 : int n = sprintf (buf, "\\x%02x", (unsigned char)*ps);
2850 : 60 : pp_maybe_wrap_text (pp, buf, buf + n);
2851 : 60 : last = ps + 1;
2852 : : }
2853 : :
2854 : 690211 : pp_maybe_wrap_text (pp, last, ps);
2855 : 690211 : }
2856 : :
2857 : : /* Maybe print out a whitespace if needed. */
2858 : :
2859 : : void
2860 : 553356 : pretty_printer::maybe_space ()
2861 : : {
2862 : 553356 : if (m_padding != pp_none)
2863 : : {
2864 : 436736 : pp_space (this);
2865 : 436736 : m_padding = pp_none;
2866 : : }
2867 : 553356 : }
2868 : :
2869 : : // Add a newline to the pretty printer PP and flush formatted text.
2870 : :
2871 : : void
2872 : 3354361 : pp_newline_and_flush (pretty_printer *pp)
2873 : : {
2874 : 3354361 : pp_newline (pp);
2875 : 3354361 : pp_flush (pp);
2876 : 3354361 : pp_needs_newline (pp) = false;
2877 : 3354361 : }
2878 : :
2879 : : // Add a newline to the pretty printer PP, followed by indentation.
2880 : :
2881 : : void
2882 : 6804 : pp_newline_and_indent (pretty_printer *pp, int n)
2883 : : {
2884 : 6804 : pp_indentation (pp) += n;
2885 : 6804 : pp_newline (pp);
2886 : 6804 : pp_indent (pp);
2887 : 6804 : pp_needs_newline (pp) = false;
2888 : 6804 : }
2889 : :
2890 : : // Add separator C, followed by a single whitespace.
2891 : :
2892 : : void
2893 : 44968115 : pp_separate_with (pretty_printer *pp, char c)
2894 : : {
2895 : 44968115 : pp_character (pp, c);
2896 : 44968115 : pp_space (pp);
2897 : 44968115 : }
2898 : :
2899 : : /* Add a localized open quote, and if SHOW_COLOR is true, begin colorizing
2900 : : using the "quote" color. */
2901 : :
2902 : : void
2903 : 1111427 : pp_begin_quote (pretty_printer *pp, bool show_color)
2904 : : {
2905 : 1111427 : pp_string (pp, open_quote);
2906 : 1111427 : pp_string (pp, colorize_start (show_color, "quote"));
2907 : 1111427 : }
2908 : :
2909 : : /* If SHOW_COLOR is true, stop colorizing.
2910 : : Add a localized close quote. */
2911 : :
2912 : : void
2913 : 1130554 : pp_end_quote (pretty_printer *pp, bool show_color)
2914 : : {
2915 : 1130554 : pp_string (pp, colorize_stop (show_color));
2916 : 1130554 : pp_string (pp, close_quote);
2917 : 1130554 : }
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 : 4038620947 : decode_utf8_char (const unsigned char *p, size_t len, unsigned int *value)
2927 : : {
2928 : 4038620947 : unsigned int t = *p;
2929 : :
2930 : 4038620947 : if (len == 0)
2931 : 0 : abort ();
2932 : 4038620947 : if (t & 0x80)
2933 : : {
2934 : : size_t utf8_len = 0;
2935 : : unsigned int ch;
2936 : : size_t i;
2937 : 10884 : for (t = *p; t & 0x80; t <<= 1)
2938 : 8014 : utf8_len++;
2939 : :
2940 : 2870 : if (utf8_len > len || utf8_len < 2 || utf8_len > 6)
2941 : : {
2942 : 18 : *value = (unsigned int) -1;
2943 : 18 : return 0;
2944 : : }
2945 : 2852 : ch = *p & ((1 << (7 - utf8_len)) - 1);
2946 : 7958 : for (i = 1; i < utf8_len; i++)
2947 : : {
2948 : 5110 : unsigned int u = p[i];
2949 : 5110 : if ((u & 0xC0) != 0x80)
2950 : : {
2951 : 4 : *value = (unsigned int) -1;
2952 : 4 : return 0;
2953 : : }
2954 : 5106 : ch = (ch << 6) | (u & 0x3F);
2955 : : }
2956 : 2848 : if ( (ch <= 0x7F && utf8_len > 1)
2957 : 2848 : || (ch <= 0x7FF && utf8_len > 2)
2958 : 2848 : || (ch <= 0xFFFF && utf8_len > 3)
2959 : 2848 : || (ch <= 0x1FFFFF && utf8_len > 4)
2960 : 2848 : || (ch <= 0x3FFFFFF && utf8_len > 5)
2961 : 2848 : || (ch >= 0xD800 && ch <= 0xDFFF))
2962 : : {
2963 : 0 : *value = (unsigned int) -1;
2964 : 0 : return 0;
2965 : : }
2966 : 2848 : *value = ch;
2967 : 2848 : return utf8_len;
2968 : : }
2969 : : else
2970 : : {
2971 : 4038618077 : *value = t;
2972 : 4038618077 : 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 : 550986271 : identifier_to_locale (const char *ident)
2994 : : {
2995 : 550986271 : const unsigned char *uid = (const unsigned char *) ident;
2996 : 550986271 : size_t idlen = strlen (ident);
2997 : 550986271 : bool valid_printable_utf8 = true;
2998 : 550986271 : bool all_ascii = true;
2999 : 550986271 : size_t i;
3000 : :
3001 : 4589601850 : for (i = 0; i < idlen;)
3002 : : {
3003 : 4038615582 : unsigned int c;
3004 : 4038615582 : size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
3005 : 4038615582 : if (utf8_len == 0 || c <= 0x1F || (c >= 0x7F && c <= 0x9F))
3006 : : {
3007 : 3 : valid_printable_utf8 = false;
3008 : 3 : break;
3009 : : }
3010 : 4038615579 : if (utf8_len > 1)
3011 : 1418 : all_ascii = false;
3012 : 4038615579 : 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 : 550986271 : 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 : 550986268 : 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 : 641 : if (locale_encoding != NULL)
3046 : : {
3047 : 641 : iconv_t cd = iconv_open (locale_encoding, "UTF-8");
3048 : 641 : bool conversion_ok = true;
3049 : 641 : char *ret = NULL;
3050 : 641 : if (cd != (iconv_t) -1)
3051 : : {
3052 : 641 : size_t ret_alloc = 4 * idlen + 1;
3053 : 641 : for (;;)
3054 : : {
3055 : : /* Repeat the whole conversion process as needed with
3056 : : larger buffers so non-reversible transformations can
3057 : : always be detected. */
3058 : 641 : ICONV_CONST char *inbuf = CONST_CAST (char *, ident);
3059 : 641 : char *outbuf;
3060 : 641 : size_t inbytesleft = idlen;
3061 : 641 : size_t outbytesleft = ret_alloc - 1;
3062 : 641 : size_t iconv_ret;
3063 : :
3064 : 641 : ret = (char *) identifier_to_locale_alloc (ret_alloc);
3065 : 641 : outbuf = ret;
3066 : :
3067 : 641 : if (iconv (cd, 0, 0, 0, 0) == (size_t) -1)
3068 : : {
3069 : : conversion_ok = false;
3070 : 641 : break;
3071 : : }
3072 : :
3073 : 641 : iconv_ret = iconv (cd, &inbuf, &inbytesleft,
3074 : : &outbuf, &outbytesleft);
3075 : 641 : if (iconv_ret == (size_t) -1 || inbytesleft != 0)
3076 : : {
3077 : 641 : 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 : 641 : iconv_close (cd);
3115 : 641 : if (conversion_ok)
3116 : : return ret;
3117 : : }
3118 : : }
3119 : : #endif
3120 : :
3121 : : /* Otherwise, convert non-ASCII characters in IDENT to UCNs. */
3122 : 641 : {
3123 : 641 : char *ret = (char *) identifier_to_locale_alloc (10 * idlen + 1);
3124 : 641 : char *p = ret;
3125 : 5973 : for (i = 0; i < idlen;)
3126 : : {
3127 : 5332 : unsigned int c;
3128 : 5332 : size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
3129 : 5332 : if (utf8_len == 1)
3130 : 3914 : *p++ = uid[i];
3131 : : else
3132 : : {
3133 : 1418 : sprintf (p, "\\U%08x", c);
3134 : 1418 : p += 10;
3135 : : }
3136 : 5332 : i += utf8_len;
3137 : : }
3138 : 641 : *p = 0;
3139 : 641 : 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 : 18732 : pretty_printer::begin_url (const char *url)
3167 : : {
3168 : 18732 : 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 : 18720 : 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 : 18756 : pretty_printer::end_url ()
3217 : : {
3218 : 18756 : 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 : 18744 : 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 : 8491 : pp_markup::context::begin_quote ()
3268 : : {
3269 : 8491 : gcc_assert (!m_quoted);
3270 : 8491 : gcc_assert (m_formatted_token_list);
3271 : 8491 : push_back_any_text ();
3272 : 8491 : m_formatted_token_list->push_back<pp_token_begin_quote> ();
3273 : 8491 : m_quoted = true;
3274 : 8491 : }
3275 : :
3276 : : void
3277 : 8491 : 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 : 8491 : if (!m_quoted)
3282 : : return;
3283 : 7674 : gcc_assert (m_formatted_token_list);
3284 : 7674 : push_back_any_text ();
3285 : 7674 : m_formatted_token_list->push_back<pp_token_end_quote> ();
3286 : 7674 : m_quoted = false;
3287 : : }
3288 : :
3289 : : void
3290 : 8052 : pp_markup::context::begin_highlight_color (const char *color_name)
3291 : : {
3292 : 8052 : if (!pp_show_highlight_colors (&m_pp))
3293 : : return;
3294 : :
3295 : 8044 : push_back_any_text ();
3296 : 8044 : m_formatted_token_list->push_back <pp_token_begin_color>
3297 : 8044 : (label_text::borrow (color_name));
3298 : : }
3299 : :
3300 : : void
3301 : 8052 : pp_markup::context::end_highlight_color ()
3302 : : {
3303 : 8052 : if (!pp_show_highlight_colors (&m_pp))
3304 : : return;
3305 : :
3306 : 8044 : push_back_any_text ();
3307 : 8044 : 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 : 32253 : pp_markup::context::push_back_any_text ()
3335 : : {
3336 : 32253 : obstack *cur_obstack = m_buf.m_obstack;
3337 : 32253 : obstack_1grow (cur_obstack, '\0');
3338 : 32253 : m_formatted_token_list->push_back_text
3339 : 32253 : (label_text::borrow (XOBFINISH (cur_obstack,
3340 : : const char *)));
3341 : 32253 : }
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 */
|