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