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