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