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-urlifier.h"
28 : : #include "diagnostic-color.h"
29 : : #include "diagnostic-event-id.h"
30 : : #include "selftest.h"
31 : :
32 : : #if HAVE_ICONV
33 : : #include <iconv.h>
34 : : #endif
35 : :
36 : : #ifdef __MINGW32__
37 : :
38 : : /* Replacement for fputs() that handles ANSI escape codes on Windows NT.
39 : : Contributed by: Liu Hao (lh_mouse at 126 dot com)
40 : :
41 : : XXX: This file is compiled into libcommon.a that will be self-contained.
42 : : It looks like that these functions can be put nowhere else. */
43 : :
44 : : #include <io.h>
45 : : #define WIN32_LEAN_AND_MEAN 1
46 : : #include <windows.h>
47 : :
48 : : /* Write all bytes in [s,s+n) into the specified stream.
49 : : Errors are ignored. */
50 : : static void
51 : : write_all (HANDLE h, const char *s, size_t n)
52 : : {
53 : : size_t rem = n;
54 : : DWORD step;
55 : :
56 : : while (rem != 0)
57 : : {
58 : : if (rem <= UINT_MAX)
59 : : step = rem;
60 : : else
61 : : step = UINT_MAX;
62 : : if (!WriteFile (h, s + n - rem, step, &step, NULL))
63 : : break;
64 : : rem -= step;
65 : : }
66 : : }
67 : :
68 : : /* Find the beginning of an escape sequence.
69 : : There are two cases:
70 : : 1. If the sequence begins with an ESC character (0x1B) and a second
71 : : character X in [0x40,0x5F], returns X and stores a pointer to
72 : : the third character into *head.
73 : : 2. If the sequence begins with a character X in [0x80,0x9F], returns
74 : : (X-0x40) and stores a pointer to the second character into *head.
75 : : Stores the number of ESC character(s) in *prefix_len.
76 : : Returns 0 if no such sequence can be found. */
77 : : static int
78 : : find_esc_head (int *prefix_len, const char **head, const char *str)
79 : : {
80 : : int c;
81 : : const char *r = str;
82 : : int escaped = 0;
83 : :
84 : : for (;;)
85 : : {
86 : : c = (unsigned char) *r;
87 : : if (c == 0)
88 : : {
89 : : /* Not found. */
90 : : return 0;
91 : : }
92 : : if (escaped && 0x40 <= c && c <= 0x5F)
93 : : {
94 : : /* Found (case 1). */
95 : : *prefix_len = 2;
96 : : *head = r + 1;
97 : : return c;
98 : : }
99 : : if (0x80 <= c && c <= 0x9F)
100 : : {
101 : : /* Found (case 2). */
102 : : *prefix_len = 1;
103 : : *head = r + 1;
104 : : return c - 0x40;
105 : : }
106 : : ++r;
107 : : escaped = c == 0x1B;
108 : : }
109 : : }
110 : :
111 : : /* Find the terminator of an escape sequence.
112 : : str should be the value stored in *head by a previous successful
113 : : call to find_esc_head().
114 : : Returns 0 if no such sequence can be found. */
115 : : static int
116 : : find_esc_terminator (const char **term, const char *str)
117 : : {
118 : : int c;
119 : : const char *r = str;
120 : :
121 : : for (;;)
122 : : {
123 : : c = (unsigned char) *r;
124 : : if (c == 0)
125 : : {
126 : : /* Not found. */
127 : : return 0;
128 : : }
129 : : if (0x40 <= c && c <= 0x7E)
130 : : {
131 : : /* Found. */
132 : : *term = r;
133 : : return c;
134 : : }
135 : : ++r;
136 : : }
137 : : }
138 : :
139 : : /* Handle a sequence of codes. Sequences that are invalid, reserved,
140 : : unrecognized or unimplemented are ignored silently.
141 : : There isn't much we can do because of lameness of Windows consoles. */
142 : : static void
143 : : eat_esc_sequence (HANDLE h, int esc_code,
144 : : const char *esc_head, const char *esc_term)
145 : : {
146 : : /* Numbers in an escape sequence cannot be negative, because
147 : : a minus sign in the middle of it would have terminated it. */
148 : : long n1, n2;
149 : : char *eptr, *delim;
150 : : CONSOLE_SCREEN_BUFFER_INFO sb;
151 : : COORD cr;
152 : : /* ED and EL parameters. */
153 : : DWORD cnt, step;
154 : : long rows;
155 : : /* SGR parameters. */
156 : : WORD attrib_add, attrib_rm;
157 : : const char *param;
158 : :
159 : : switch (MAKEWORD (esc_code, *esc_term))
160 : : {
161 : : /* ESC [ n1 'A'
162 : : Move the cursor up by n1 characters. */
163 : : case MAKEWORD ('[', 'A'):
164 : : if (esc_head == esc_term)
165 : : n1 = 1;
166 : : else
167 : : {
168 : : n1 = strtol (esc_head, &eptr, 10);
169 : : if (eptr != esc_term)
170 : : break;
171 : : }
172 : :
173 : : if (GetConsoleScreenBufferInfo (h, &sb))
174 : : {
175 : : cr = sb.dwCursorPosition;
176 : : /* Stop at the topmost boundary. */
177 : : if (cr.Y > n1)
178 : : cr.Y -= n1;
179 : : else
180 : : cr.Y = 0;
181 : : SetConsoleCursorPosition (h, cr);
182 : : }
183 : : break;
184 : :
185 : : /* ESC [ n1 'B'
186 : : Move the cursor down by n1 characters. */
187 : : case MAKEWORD ('[', 'B'):
188 : : if (esc_head == esc_term)
189 : : n1 = 1;
190 : : else
191 : : {
192 : : n1 = strtol (esc_head, &eptr, 10);
193 : : if (eptr != esc_term)
194 : : break;
195 : : }
196 : :
197 : : if (GetConsoleScreenBufferInfo (h, &sb))
198 : : {
199 : : cr = sb.dwCursorPosition;
200 : : /* Stop at the bottommost boundary. */
201 : : if (sb.dwSize.Y - cr.Y > n1)
202 : : cr.Y += n1;
203 : : else
204 : : cr.Y = sb.dwSize.Y;
205 : : SetConsoleCursorPosition (h, cr);
206 : : }
207 : : break;
208 : :
209 : : /* ESC [ n1 'C'
210 : : Move the cursor right by n1 characters. */
211 : : case MAKEWORD ('[', 'C'):
212 : : if (esc_head == esc_term)
213 : : n1 = 1;
214 : : else
215 : : {
216 : : n1 = strtol (esc_head, &eptr, 10);
217 : : if (eptr != esc_term)
218 : : break;
219 : : }
220 : :
221 : : if (GetConsoleScreenBufferInfo (h, &sb))
222 : : {
223 : : cr = sb.dwCursorPosition;
224 : : /* Stop at the rightmost boundary. */
225 : : if (sb.dwSize.X - cr.X > n1)
226 : : cr.X += n1;
227 : : else
228 : : cr.X = sb.dwSize.X;
229 : : SetConsoleCursorPosition (h, cr);
230 : : }
231 : : break;
232 : :
233 : : /* ESC [ n1 'D'
234 : : Move the cursor left by n1 characters. */
235 : : case MAKEWORD ('[', 'D'):
236 : : if (esc_head == esc_term)
237 : : n1 = 1;
238 : : else
239 : : {
240 : : n1 = strtol (esc_head, &eptr, 10);
241 : : if (eptr != esc_term)
242 : : break;
243 : : }
244 : :
245 : : if (GetConsoleScreenBufferInfo (h, &sb))
246 : : {
247 : : cr = sb.dwCursorPosition;
248 : : /* Stop at the leftmost boundary. */
249 : : if (cr.X > n1)
250 : : cr.X -= n1;
251 : : else
252 : : cr.X = 0;
253 : : SetConsoleCursorPosition (h, cr);
254 : : }
255 : : break;
256 : :
257 : : /* ESC [ n1 'E'
258 : : Move the cursor to the beginning of the n1-th line downwards. */
259 : : case MAKEWORD ('[', 'E'):
260 : : if (esc_head == esc_term)
261 : : n1 = 1;
262 : : else
263 : : {
264 : : n1 = strtol (esc_head, &eptr, 10);
265 : : if (eptr != esc_term)
266 : : break;
267 : : }
268 : :
269 : : if (GetConsoleScreenBufferInfo (h, &sb))
270 : : {
271 : : cr = sb.dwCursorPosition;
272 : : cr.X = 0;
273 : : /* Stop at the bottommost boundary. */
274 : : if (sb.dwSize.Y - cr.Y > n1)
275 : : cr.Y += n1;
276 : : else
277 : : cr.Y = sb.dwSize.Y;
278 : : SetConsoleCursorPosition (h, cr);
279 : : }
280 : : break;
281 : :
282 : : /* ESC [ n1 'F'
283 : : Move the cursor to the beginning of the n1-th line upwards. */
284 : : case MAKEWORD ('[', 'F'):
285 : : if (esc_head == esc_term)
286 : : n1 = 1;
287 : : else
288 : : {
289 : : n1 = strtol (esc_head, &eptr, 10);
290 : : if (eptr != esc_term)
291 : : break;
292 : : }
293 : :
294 : : if (GetConsoleScreenBufferInfo (h, &sb))
295 : : {
296 : : cr = sb.dwCursorPosition;
297 : : cr.X = 0;
298 : : /* Stop at the topmost boundary. */
299 : : if (cr.Y > n1)
300 : : cr.Y -= n1;
301 : : else
302 : : cr.Y = 0;
303 : : SetConsoleCursorPosition (h, cr);
304 : : }
305 : : break;
306 : :
307 : : /* ESC [ n1 'G'
308 : : Move the cursor to the (1-based) n1-th column. */
309 : : case MAKEWORD ('[', 'G'):
310 : : if (esc_head == esc_term)
311 : : n1 = 1;
312 : : else
313 : : {
314 : : n1 = strtol (esc_head, &eptr, 10);
315 : : if (eptr != esc_term)
316 : : break;
317 : : }
318 : :
319 : : if (GetConsoleScreenBufferInfo (h, &sb))
320 : : {
321 : : cr = sb.dwCursorPosition;
322 : : n1 -= 1;
323 : : /* Stop at the leftmost or rightmost boundary. */
324 : : if (n1 < 0)
325 : : cr.X = 0;
326 : : else if (n1 > sb.dwSize.X)
327 : : cr.X = sb.dwSize.X;
328 : : else
329 : : cr.X = n1;
330 : : SetConsoleCursorPosition (h, cr);
331 : : }
332 : : break;
333 : :
334 : : /* ESC [ n1 ';' n2 'H'
335 : : ESC [ n1 ';' n2 'f'
336 : : Move the cursor to the (1-based) n1-th row and
337 : : (also 1-based) n2-th column. */
338 : : case MAKEWORD ('[', 'H'):
339 : : case MAKEWORD ('[', 'f'):
340 : : if (esc_head == esc_term)
341 : : {
342 : : /* Both parameters are omitted and set to 1 by default. */
343 : : n1 = 1;
344 : : n2 = 1;
345 : : }
346 : : else if (!(delim = (char *) memchr (esc_head, ';',
347 : : esc_term - esc_head)))
348 : : {
349 : : /* Only the first parameter is given. The second one is
350 : : set to 1 by default. */
351 : : n1 = strtol (esc_head, &eptr, 10);
352 : : if (eptr != esc_term)
353 : : break;
354 : : n2 = 1;
355 : : }
356 : : else
357 : : {
358 : : /* Both parameters are given. The first one shall be
359 : : terminated by the semicolon. */
360 : : n1 = strtol (esc_head, &eptr, 10);
361 : : if (eptr != delim)
362 : : break;
363 : : n2 = strtol (delim + 1, &eptr, 10);
364 : : if (eptr != esc_term)
365 : : break;
366 : : }
367 : :
368 : : if (GetConsoleScreenBufferInfo (h, &sb))
369 : : {
370 : : cr = sb.dwCursorPosition;
371 : : n1 -= 1;
372 : : n2 -= 1;
373 : : /* The cursor position shall be relative to the view coord of
374 : : the console window, which is usually smaller than the actual
375 : : buffer. FWIW, the 'appropriate' solution will be shrinking
376 : : the buffer to match the size of the console window,
377 : : destroying scrollback in the process. */
378 : : n1 += sb.srWindow.Top;
379 : : n2 += sb.srWindow.Left;
380 : : /* Stop at the topmost or bottommost boundary. */
381 : : if (n1 < 0)
382 : : cr.Y = 0;
383 : : else if (n1 > sb.dwSize.Y)
384 : : cr.Y = sb.dwSize.Y;
385 : : else
386 : : cr.Y = n1;
387 : : /* Stop at the leftmost or rightmost boundary. */
388 : : if (n2 < 0)
389 : : cr.X = 0;
390 : : else if (n2 > sb.dwSize.X)
391 : : cr.X = sb.dwSize.X;
392 : : else
393 : : cr.X = n2;
394 : : SetConsoleCursorPosition (h, cr);
395 : : }
396 : : break;
397 : :
398 : : /* ESC [ n1 'J'
399 : : Erase display. */
400 : : case MAKEWORD ('[', 'J'):
401 : : if (esc_head == esc_term)
402 : : /* This is one of the very few codes whose parameters have
403 : : a default value of zero. */
404 : : n1 = 0;
405 : : else
406 : : {
407 : : n1 = strtol (esc_head, &eptr, 10);
408 : : if (eptr != esc_term)
409 : : break;
410 : : }
411 : :
412 : : if (GetConsoleScreenBufferInfo (h, &sb))
413 : : {
414 : : /* The cursor is not necessarily in the console window, which
415 : : makes the behavior of this code harder to define. */
416 : : switch (n1)
417 : : {
418 : : case 0:
419 : : /* If the cursor is in or above the window, erase from
420 : : it to the bottom of the window; otherwise, do nothing. */
421 : : cr = sb.dwCursorPosition;
422 : : cnt = sb.dwSize.X - sb.dwCursorPosition.X;
423 : : rows = sb.srWindow.Bottom - sb.dwCursorPosition.Y;
424 : : break;
425 : : case 1:
426 : : /* If the cursor is in or under the window, erase from
427 : : it to the top of the window; otherwise, do nothing. */
428 : : cr.X = 0;
429 : : cr.Y = sb.srWindow.Top;
430 : : cnt = sb.dwCursorPosition.X + 1;
431 : : rows = sb.dwCursorPosition.Y - sb.srWindow.Top;
432 : : break;
433 : : case 2:
434 : : /* Erase the entire window. */
435 : : cr.X = sb.srWindow.Left;
436 : : cr.Y = sb.srWindow.Top;
437 : : cnt = 0;
438 : : rows = sb.srWindow.Bottom - sb.srWindow.Top + 1;
439 : : break;
440 : : default:
441 : : /* Erase the entire buffer. */
442 : : cr.X = 0;
443 : : cr.Y = 0;
444 : : cnt = 0;
445 : : rows = sb.dwSize.Y;
446 : : break;
447 : : }
448 : : if (rows < 0)
449 : : break;
450 : : cnt += rows * sb.dwSize.X;
451 : : FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
452 : : FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
453 : : }
454 : : break;
455 : :
456 : : /* ESC [ n1 'K'
457 : : Erase line. */
458 : : case MAKEWORD ('[', 'K'):
459 : : if (esc_head == esc_term)
460 : : /* This is one of the very few codes whose parameters have
461 : : a default value of zero. */
462 : : n1 = 0;
463 : : else
464 : : {
465 : : n1 = strtol (esc_head, &eptr, 10);
466 : : if (eptr != esc_term)
467 : : break;
468 : : }
469 : :
470 : : if (GetConsoleScreenBufferInfo (h, &sb))
471 : : {
472 : : switch (n1)
473 : : {
474 : : case 0:
475 : : /* Erase from the cursor to the end. */
476 : : cr = sb.dwCursorPosition;
477 : : cnt = sb.dwSize.X - sb.dwCursorPosition.X;
478 : : break;
479 : : case 1:
480 : : /* Erase from the cursor to the beginning. */
481 : : cr = sb.dwCursorPosition;
482 : : cr.X = 0;
483 : : cnt = sb.dwCursorPosition.X + 1;
484 : : break;
485 : : default:
486 : : /* Erase the entire line. */
487 : : cr = sb.dwCursorPosition;
488 : : cr.X = 0;
489 : : cnt = sb.dwSize.X;
490 : : break;
491 : : }
492 : : FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
493 : : FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
494 : : }
495 : : break;
496 : :
497 : : /* ESC [ n1 ';' n2 'm'
498 : : Set SGR parameters. Zero or more parameters will follow. */
499 : : case MAKEWORD ('[', 'm'):
500 : : attrib_add = 0;
501 : : attrib_rm = 0;
502 : : if (esc_head == esc_term)
503 : : {
504 : : /* When no parameter is given, reset the console. */
505 : : attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
506 : : | FOREGROUND_BLUE);
507 : : attrib_rm = -1; /* Removes everything. */
508 : : goto sgr_set_it;
509 : : }
510 : : param = esc_head;
511 : : do
512 : : {
513 : : /* Parse a parameter. */
514 : : n1 = strtol (param, &eptr, 10);
515 : : if (*eptr != ';' && eptr != esc_term)
516 : : goto sgr_set_it;
517 : :
518 : : switch (n1)
519 : : {
520 : : case 0:
521 : : /* Reset. */
522 : : attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
523 : : | FOREGROUND_BLUE);
524 : : attrib_rm = -1; /* Removes everything. */
525 : : break;
526 : : case 1:
527 : : /* Bold. */
528 : : attrib_add |= FOREGROUND_INTENSITY;
529 : : break;
530 : : case 4:
531 : : /* Underline. */
532 : : attrib_add |= COMMON_LVB_UNDERSCORE;
533 : : break;
534 : : case 5:
535 : : /* Blink. */
536 : : /* XXX: It is not BLINKING at all! */
537 : : attrib_add |= BACKGROUND_INTENSITY;
538 : : break;
539 : : case 7:
540 : : /* Reverse. */
541 : : attrib_add |= COMMON_LVB_REVERSE_VIDEO;
542 : : break;
543 : : case 22:
544 : : /* No bold. */
545 : : attrib_add &= ~FOREGROUND_INTENSITY;
546 : : attrib_rm |= FOREGROUND_INTENSITY;
547 : : break;
548 : : case 24:
549 : : /* No underline. */
550 : : attrib_add &= ~COMMON_LVB_UNDERSCORE;
551 : : attrib_rm |= COMMON_LVB_UNDERSCORE;
552 : : break;
553 : : case 25:
554 : : /* No blink. */
555 : : /* XXX: It is not BLINKING at all! */
556 : : attrib_add &= ~BACKGROUND_INTENSITY;
557 : : attrib_rm |= BACKGROUND_INTENSITY;
558 : : break;
559 : : case 27:
560 : : /* No reverse. */
561 : : attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
562 : : attrib_rm |= COMMON_LVB_REVERSE_VIDEO;
563 : : break;
564 : : case 30:
565 : : case 31:
566 : : case 32:
567 : : case 33:
568 : : case 34:
569 : : case 35:
570 : : case 36:
571 : : case 37:
572 : : /* Foreground color. */
573 : : attrib_add &= ~(FOREGROUND_RED | FOREGROUND_GREEN
574 : : | FOREGROUND_BLUE);
575 : : n1 -= 30;
576 : : if (n1 & 1)
577 : : attrib_add |= FOREGROUND_RED;
578 : : if (n1 & 2)
579 : : attrib_add |= FOREGROUND_GREEN;
580 : : if (n1 & 4)
581 : : attrib_add |= FOREGROUND_BLUE;
582 : : attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
583 : : | FOREGROUND_BLUE);
584 : : break;
585 : : case 38:
586 : : /* Reserved for extended foreground color.
587 : : Don't know how to handle parameters remaining.
588 : : Bail out. */
589 : : goto sgr_set_it;
590 : : case 39:
591 : : /* Reset foreground color. */
592 : : /* Set to grey. */
593 : : attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
594 : : | FOREGROUND_BLUE);
595 : : attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
596 : : | FOREGROUND_BLUE);
597 : : break;
598 : : case 40:
599 : : case 41:
600 : : case 42:
601 : : case 43:
602 : : case 44:
603 : : case 45:
604 : : case 46:
605 : : case 47:
606 : : /* Background color. */
607 : : attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
608 : : | BACKGROUND_BLUE);
609 : : n1 -= 40;
610 : : if (n1 & 1)
611 : : attrib_add |= BACKGROUND_RED;
612 : : if (n1 & 2)
613 : : attrib_add |= BACKGROUND_GREEN;
614 : : if (n1 & 4)
615 : : attrib_add |= BACKGROUND_BLUE;
616 : : attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
617 : : | BACKGROUND_BLUE);
618 : : break;
619 : : case 48:
620 : : /* Reserved for extended background color.
621 : : Don't know how to handle parameters remaining.
622 : : Bail out. */
623 : : goto sgr_set_it;
624 : : case 49:
625 : : /* Reset background color. */
626 : : /* Set to black. */
627 : : attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
628 : : | BACKGROUND_BLUE);
629 : : attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
630 : : | BACKGROUND_BLUE);
631 : : break;
632 : : }
633 : :
634 : : /* Prepare the next parameter. */
635 : : param = eptr + 1;
636 : : }
637 : : while (param != esc_term);
638 : :
639 : : sgr_set_it:
640 : : /* 0xFFFF removes everything. If it is not the case,
641 : : care must be taken to preserve old attributes. */
642 : : if (attrib_rm != 0xFFFF && GetConsoleScreenBufferInfo (h, &sb))
643 : : {
644 : : attrib_add |= sb.wAttributes & ~attrib_rm;
645 : : }
646 : : if (attrib_add & COMMON_LVB_REVERSE_VIDEO)
647 : : {
648 : : /* COMMON_LVB_REVERSE_VIDEO is only effective for DBCS.
649 : : * Swap foreground and background colors by hand.
650 : : */
651 : : attrib_add = (attrib_add & 0xFF00)
652 : : | ((attrib_add & 0x00F0) >> 4)
653 : : | ((attrib_add & 0x000F) << 4);
654 : : attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
655 : : }
656 : : SetConsoleTextAttribute (h, attrib_add);
657 : : break;
658 : : }
659 : : }
660 : :
661 : : int
662 : : mingw_ansi_fputs (const char *str, FILE *fp)
663 : : {
664 : : const char *read = str;
665 : : HANDLE h;
666 : : DWORD mode;
667 : : int esc_code, prefix_len;
668 : : const char *esc_head, *esc_term;
669 : :
670 : : h = (HANDLE) _get_osfhandle (_fileno (fp));
671 : : if (h == INVALID_HANDLE_VALUE)
672 : : return EOF;
673 : :
674 : : /* Don't mess up stdio functions with Windows APIs. */
675 : : fflush (fp);
676 : :
677 : : if (GetConsoleMode (h, &mode))
678 : : /* If it is a console, translate ANSI escape codes as needed. */
679 : : for (;;)
680 : : {
681 : : if ((esc_code = find_esc_head (&prefix_len, &esc_head, read)) == 0)
682 : : {
683 : : /* Write all remaining characters, then exit. */
684 : : write_all (h, read, strlen (read));
685 : : break;
686 : : }
687 : : if (find_esc_terminator (&esc_term, esc_head) == 0)
688 : : /* Ignore incomplete escape sequences at the moment.
689 : : FIXME: The escape state shall be cached for further calls
690 : : to this function. */
691 : : break;
692 : : write_all (h, read, esc_head - prefix_len - read);
693 : : eat_esc_sequence (h, esc_code, esc_head, esc_term);
694 : : read = esc_term + 1;
695 : : }
696 : : else
697 : : /* If it is not a console, write everything as-is. */
698 : : write_all (h, read, strlen (read));
699 : :
700 : : return 1;
701 : : }
702 : :
703 : : #endif /* __MINGW32__ */
704 : :
705 : : static int
706 : : decode_utf8_char (const unsigned char *, size_t len, unsigned int *);
707 : : static void pp_quoted_string (pretty_printer *, const char *, size_t = -1);
708 : :
709 : : /* Overwrite the given location/range within this text_info's rich_location.
710 : : For use e.g. when implementing "+" in client format decoders. */
711 : :
712 : : void
713 : 1083347 : text_info::set_location (unsigned int idx, location_t loc,
714 : : enum range_display_kind range_display_kind)
715 : : {
716 : 1083347 : gcc_checking_assert (m_richloc);
717 : 1083347 : m_richloc->set_range (idx, loc, range_display_kind);
718 : 1083347 : }
719 : :
720 : : location_t
721 : 88900918 : text_info::get_location (unsigned int index_of_location) const
722 : : {
723 : 88900918 : gcc_checking_assert (m_richloc);
724 : :
725 : 88900918 : if (index_of_location == 0)
726 : 88900918 : return m_richloc->get_loc ();
727 : : else
728 : : return UNKNOWN_LOCATION;
729 : : }
730 : :
731 : : // Default construct an output buffer.
732 : :
733 : 23327131 : output_buffer::output_buffer ()
734 : 23327131 : : formatted_obstack (),
735 : 23327131 : chunk_obstack (),
736 : 23327131 : obstack (&formatted_obstack),
737 : 23327131 : cur_chunk_array (),
738 : 23327131 : stream (stderr),
739 : 23327131 : line_length (),
740 : 3009199899 : digit_buffer (),
741 : 23327131 : flush_p (true)
742 : : {
743 : 23327131 : obstack_init (&formatted_obstack);
744 : 23327131 : obstack_init (&chunk_obstack);
745 : 23327131 : }
746 : :
747 : : // Release resources owned by an output buffer at the end of lifetime.
748 : :
749 : 22876595 : output_buffer::~output_buffer ()
750 : : {
751 : 22876595 : obstack_free (&chunk_obstack, NULL);
752 : 22876595 : obstack_free (&formatted_obstack, NULL);
753 : 22876595 : }
754 : :
755 : : #ifndef PTRDIFF_MAX
756 : : #define PTRDIFF_MAX INTTYPE_MAXIMUM (ptrdiff_t)
757 : : #endif
758 : :
759 : : /* Format an integer given by va_arg (ARG, type-specifier T) where
760 : : type-specifier is a precision modifier as indicated by PREC. F is
761 : : a string used to construct the appropriate format-specifier. */
762 : : #define pp_integer_with_precision(PP, ARG, PREC, T, F) \
763 : : do \
764 : : switch (PREC) \
765 : : { \
766 : : case 0: \
767 : : pp_scalar (PP, "%" F, va_arg (ARG, T)); \
768 : : break; \
769 : : \
770 : : case 1: \
771 : : pp_scalar (PP, "%l" F, va_arg (ARG, long T)); \
772 : : break; \
773 : : \
774 : : case 2: \
775 : : pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
776 : : va_arg (ARG, long long T)); \
777 : : break; \
778 : : \
779 : : case 3: \
780 : : if (T (-1) < T (0)) \
781 : : pp_scalar (PP, "%" GCC_PRISZ F, \
782 : : (fmt_size_t) va_arg (ARG, ssize_t)); \
783 : : else \
784 : : pp_scalar (PP, "%" GCC_PRISZ F, \
785 : : (fmt_size_t) va_arg (ARG, size_t)); \
786 : : break; \
787 : : \
788 : : case 4: \
789 : : if (T (-1) >= T (0)) \
790 : : { \
791 : : unsigned long long a = va_arg (ARG, ptrdiff_t); \
792 : : unsigned long long m = PTRDIFF_MAX; \
793 : : m = 2 * m + 1; \
794 : : pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
795 : : a & m); \
796 : : } \
797 : : else if (sizeof (ptrdiff_t) <= sizeof (int)) \
798 : : pp_scalar (PP, "%" F, \
799 : : (int) va_arg (ARG, ptrdiff_t)); \
800 : : else if (sizeof (ptrdiff_t) <= sizeof (long)) \
801 : : pp_scalar (PP, "%l" F, \
802 : : (long int) va_arg (ARG, ptrdiff_t)); \
803 : : else \
804 : : pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
805 : : (long long int) \
806 : : va_arg (ARG, ptrdiff_t)); \
807 : : break; \
808 : : \
809 : : default: \
810 : : break; \
811 : : } \
812 : : while (0)
813 : :
814 : :
815 : : /* Subroutine of pp_set_maximum_length. Set up PRETTY-PRINTER's
816 : : internal maximum characters per line. */
817 : : static void
818 : 18413624 : pp_set_real_maximum_length (pretty_printer *pp)
819 : : {
820 : : /* If we're told not to wrap lines then do the obvious thing. In case
821 : : we'll emit prefix only once per message, it is appropriate
822 : : not to increase unnecessarily the line-length cut-off. */
823 : 18413624 : if (!pp_is_wrapping_line (pp)
824 : 16 : || pp_prefixing_rule (pp) == DIAGNOSTICS_SHOW_PREFIX_ONCE
825 : 8 : || pp_prefixing_rule (pp) == DIAGNOSTICS_SHOW_PREFIX_NEVER)
826 : 18413620 : pp->maximum_length = pp_line_cutoff (pp);
827 : : else
828 : : {
829 : 4 : int prefix_length = pp->prefix ? strlen (pp->prefix) : 0;
830 : : /* If the prefix is ridiculously too long, output at least
831 : : 32 characters. */
832 : 4 : if (pp_line_cutoff (pp) - prefix_length < 32)
833 : 4 : pp->maximum_length = pp_line_cutoff (pp) + 32;
834 : : else
835 : 0 : pp->maximum_length = pp_line_cutoff (pp);
836 : : }
837 : 18413624 : }
838 : :
839 : : /* Clear PRETTY-PRINTER's output state. */
840 : : static inline void
841 : 17753026 : pp_clear_state (pretty_printer *pp)
842 : : {
843 : 17753026 : pp->emitted_prefix = false;
844 : 17753026 : pp_indentation (pp) = 0;
845 : : }
846 : :
847 : : /* Print X to PP in decimal. */
848 : : template<unsigned int N, typename T>
849 : : void
850 : 795233 : pp_wide_integer (pretty_printer *pp, const poly_int<N, T> &x)
851 : : {
852 : : if (x.is_constant ())
853 : 795233 : pp_wide_integer (pp, x.coeffs[0]);
854 : : else
855 : : {
856 : : pp_left_bracket (pp);
857 : : for (unsigned int i = 0; i < N; ++i)
858 : : {
859 : : if (i != 0)
860 : : pp_comma (pp);
861 : : pp_wide_integer (pp, x.coeffs[i]);
862 : : }
863 : : pp_right_bracket (pp);
864 : : }
865 : 795233 : }
866 : :
867 : : template void pp_wide_integer (pretty_printer *, const poly_uint16 &);
868 : : template void pp_wide_integer (pretty_printer *, const poly_int64 &);
869 : : template void pp_wide_integer (pretty_printer *, const poly_uint64 &);
870 : :
871 : : /* Flush the formatted text of PRETTY-PRINTER onto the attached stream. */
872 : : void
873 : 7422998 : pp_write_text_to_stream (pretty_printer *pp)
874 : : {
875 : 7422998 : const char *text = pp_formatted_text (pp);
876 : : #ifdef __MINGW32__
877 : : mingw_ansi_fputs (text, pp_buffer (pp)->stream);
878 : : #else
879 : 7422998 : fputs (text, pp_buffer (pp)->stream);
880 : : #endif
881 : 7422998 : pp_clear_output_area (pp);
882 : 7422998 : }
883 : :
884 : : /* As pp_write_text_to_stream, but for GraphViz label output.
885 : :
886 : : Flush the formatted text of pretty-printer PP onto the attached stream.
887 : : Replace characters in PPF that have special meaning in a GraphViz .dot
888 : : file.
889 : :
890 : : This routine is not very fast, but it doesn't have to be as this is only
891 : : be used by routines dumping intermediate representations in graph form. */
892 : :
893 : : void
894 : 2946 : pp_write_text_as_dot_label_to_stream (pretty_printer *pp, bool for_record)
895 : : {
896 : 2946 : const char *text = pp_formatted_text (pp);
897 : 2946 : const char *p = text;
898 : 2946 : FILE *fp = pp_buffer (pp)->stream;
899 : :
900 : 859734 : for (;*p; p++)
901 : : {
902 : 856788 : bool escape_char;
903 : 856788 : switch (*p)
904 : : {
905 : : /* Print newlines as a left-aligned newline. */
906 : 28941 : case '\n':
907 : 28941 : fputs ("\\l", fp);
908 : 28941 : escape_char = true;
909 : 28941 : break;
910 : :
911 : : /* The following characters are only special for record-shape nodes. */
912 : : case '|':
913 : : case '{':
914 : : case '}':
915 : : case '<':
916 : : case '>':
917 : : case ' ':
918 : : escape_char = for_record;
919 : : break;
920 : :
921 : : /* The following characters always have to be escaped
922 : : for use in labels. */
923 : 1 : case '\\':
924 : : /* There is a bug in some (f.i. 2.36.0) versions of graphiz
925 : : ( http://www.graphviz.org/mantisbt/view.php?id=2524 ) related to
926 : : backslash as last char in label. Let's avoid triggering it. */
927 : 1 : gcc_assert (*(p + 1) != '\0');
928 : : /* Fall through. */
929 : : case '"':
930 : : escape_char = true;
931 : : break;
932 : :
933 : : default:
934 : : escape_char = false;
935 : : break;
936 : : }
937 : :
938 : 164754 : if (escape_char)
939 : 164967 : fputc ('\\', fp);
940 : :
941 : 856788 : fputc (*p, fp);
942 : : }
943 : :
944 : 2946 : pp_clear_output_area (pp);
945 : 2946 : }
946 : :
947 : : /* As pp_write_text_to_stream, but for GraphViz HTML-like strings.
948 : :
949 : : Flush the formatted text of pretty-printer PP onto the attached stream,
950 : : escaping these characters
951 : : " & < >
952 : : using XML escape sequences.
953 : :
954 : : http://www.graphviz.org/doc/info/lang.html#html states:
955 : : special XML escape sequences for ", &, <, and > may be necessary in
956 : : order to embed these characters in attribute values or raw text
957 : : This doesn't list "'" (which would normally be escaped in XML
958 : : as "'" or in HTML as "'");.
959 : :
960 : : Experiments show that escaping "'" doesn't seem to be necessary. */
961 : :
962 : : void
963 : 815 : pp_write_text_as_html_like_dot_to_stream (pretty_printer *pp)
964 : : {
965 : 815 : const char *text = pp_formatted_text (pp);
966 : 815 : const char *p = text;
967 : 815 : FILE *fp = pp_buffer (pp)->stream;
968 : :
969 : 30832 : for (;*p; p++)
970 : : {
971 : 30017 : switch (*p)
972 : : {
973 : 0 : case '"':
974 : 0 : fputs (""", fp);
975 : 0 : break;
976 : 0 : case '&':
977 : 0 : fputs ("&", fp);
978 : 0 : break;
979 : 105 : case '<':
980 : 105 : fputs ("<", fp);
981 : 105 : break;
982 : 90 : case '>':
983 : 90 : fputs (">",fp);
984 : 90 : break;
985 : :
986 : 29822 : default:
987 : 29822 : fputc (*p, fp);
988 : 29822 : break;
989 : : }
990 : : }
991 : :
992 : 815 : pp_clear_output_area (pp);
993 : 815 : }
994 : :
995 : : /* Wrap a text delimited by START and END into PRETTY-PRINTER. */
996 : : static void
997 : 24 : pp_wrap_text (pretty_printer *pp, const char *start, const char *end)
998 : : {
999 : 24 : bool wrapping_line = pp_is_wrapping_line (pp);
1000 : :
1001 : 216 : while (start != end)
1002 : : {
1003 : : /* Dump anything bordered by whitespaces. */
1004 : : {
1005 : : const char *p = start;
1006 : 840 : while (p != end && !ISBLANK (*p) && *p != '\n')
1007 : 648 : ++p;
1008 : 192 : if (wrapping_line
1009 : 192 : && p - start >= pp_remaining_character_count_for_line (pp))
1010 : 28 : pp_newline (pp);
1011 : 192 : pp_append_text (pp, start, p);
1012 : 192 : start = p;
1013 : : }
1014 : :
1015 : 192 : if (start != end && ISBLANK (*start))
1016 : : {
1017 : 168 : pp_space (pp);
1018 : 168 : ++start;
1019 : : }
1020 : 192 : if (start != end && *start == '\n')
1021 : : {
1022 : 0 : pp_newline (pp);
1023 : 0 : ++start;
1024 : : }
1025 : : }
1026 : 24 : }
1027 : :
1028 : : /* Same as pp_wrap_text but wrap text only when in line-wrapping mode. */
1029 : : static inline void
1030 : 700252399 : pp_maybe_wrap_text (pretty_printer *pp, const char *start, const char *end)
1031 : : {
1032 : 700252399 : if (pp_is_wrapping_line (pp))
1033 : 24 : pp_wrap_text (pp, start, end);
1034 : : else
1035 : 700252375 : pp_append_text (pp, start, end);
1036 : 700252399 : }
1037 : :
1038 : : /* Append to the output area of PRETTY-PRINTER a string specified by its
1039 : : STARTing character and LENGTH. */
1040 : : static inline void
1041 : 714088380 : pp_append_r (pretty_printer *pp, const char *start, int length)
1042 : : {
1043 : 714088380 : output_buffer_append_r (pp_buffer (pp), start, length);
1044 : : }
1045 : :
1046 : : /* Insert enough spaces into the output area of PRETTY-PRINTER to bring
1047 : : the column position to the current indentation level, assuming that a
1048 : : newline has just been written to the buffer. */
1049 : : void
1050 : 7526 : pp_indent (pretty_printer *pp)
1051 : : {
1052 : 7526 : int n = pp_indentation (pp);
1053 : 7526 : int i;
1054 : :
1055 : 263094 : for (i = 0; i < n; ++i)
1056 : 255568 : pp_space (pp);
1057 : 7526 : }
1058 : :
1059 : : static const char *get_end_url_string (pretty_printer *);
1060 : :
1061 : : /* Append STR to OSTACK, without a null-terminator. */
1062 : :
1063 : : static void
1064 : 220 : obstack_append_string (obstack *ostack, const char *str)
1065 : : {
1066 : 220 : obstack_grow (ostack, str, strlen (str));
1067 : 220 : }
1068 : :
1069 : : /* Append STR to OSTACK, without a null-terminator. */
1070 : :
1071 : : static void
1072 : 44 : obstack_append_string (obstack *ostack, const char *str, size_t len)
1073 : : {
1074 : 44 : obstack_grow (ostack, str, len);
1075 : 44 : }
1076 : :
1077 : : /* Given quoted text within the buffer OBSTACK
1078 : : at the half-open interval [QUOTED_TEXT_START_IDX, QUOTED_TEXT_END_IDX),
1079 : : potentially use URLIFIER (if non-null) to see if there's a URL for the
1080 : : quoted text.
1081 : :
1082 : : If so, replace the quoted part of the text in the buffer with a URLified
1083 : : version of the text, using PP's settings.
1084 : :
1085 : : For example, given this is the buffer:
1086 : : "this is a test `hello worldTRAILING-CONTENT"
1087 : : .................^~~~~~~~~~~
1088 : : with the quoted text starting at the 'h' of "hello world", the buffer
1089 : : becomes:
1090 : : "this is a test `BEGIN_URL(URL)hello worldEND(URL)TRAILING-CONTENT"
1091 : : .................^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1092 : : .................-----------replacement-----------
1093 : :
1094 : : Return the new offset into the buffer of the quoted text endpoint i.e.
1095 : : the offset of "TRAILING-CONTENT" in the above. */
1096 : :
1097 : : static size_t
1098 : 791662 : urlify_quoted_string (pretty_printer *pp,
1099 : : obstack *obstack,
1100 : : const urlifier *urlifier,
1101 : : size_t quoted_text_start_idx,
1102 : : size_t quoted_text_end_idx)
1103 : : {
1104 : 791662 : if (pp->url_format == URL_FORMAT_NONE)
1105 : : return quoted_text_end_idx;
1106 : 56 : if (!urlifier)
1107 : : return quoted_text_end_idx;
1108 : :
1109 : 56 : const size_t quoted_len = quoted_text_end_idx - quoted_text_start_idx;
1110 : 56 : if (quoted_len == 0)
1111 : : /* Empty quoted string; do nothing. */
1112 : : return quoted_text_end_idx;
1113 : 56 : const char *start = (obstack->object_base + quoted_text_start_idx);
1114 : 56 : char *url = urlifier->get_url_for_quoted_text (start, quoted_len);
1115 : 56 : if (!url)
1116 : : /* No URL for this quoted text; do nothing. */
1117 : : return quoted_text_end_idx;
1118 : :
1119 : : /* Stash a copy of the remainder of the chunk. */
1120 : 88 : char *text = xstrndup (start,
1121 : 44 : obstack_object_size (obstack) - quoted_text_start_idx);
1122 : :
1123 : : /* Replace quoted text... */
1124 : 44 : obstack->next_free = obstack->object_base + quoted_text_start_idx;
1125 : :
1126 : : /* ...with URLified version of the text. */
1127 : : /* Begin URL. */
1128 : 44 : switch (pp->url_format)
1129 : : {
1130 : 0 : default:
1131 : 0 : case URL_FORMAT_NONE:
1132 : 0 : gcc_unreachable ();
1133 : 40 : case URL_FORMAT_ST:
1134 : 40 : obstack_append_string (obstack, "\33]8;;");
1135 : 40 : obstack_append_string (obstack, url);
1136 : 40 : obstack_append_string (obstack, "\33\\");
1137 : 40 : break;
1138 : 4 : case URL_FORMAT_BEL:
1139 : 4 : obstack_append_string (obstack, "\33]8;;");
1140 : 4 : obstack_append_string (obstack, url);
1141 : 4 : obstack_append_string (obstack, "\a");
1142 : 4 : break;
1143 : : }
1144 : : /* Add back the quoted part of the text. */
1145 : 44 : obstack_append_string (obstack, text, quoted_len);
1146 : : /* End URL. */
1147 : 44 : obstack_append_string (obstack,
1148 : : get_end_url_string (pp));
1149 : :
1150 : 44 : size_t new_end_idx = obstack_object_size (obstack);
1151 : :
1152 : : /* Add back the remainder of the text after the quoted part. */
1153 : 44 : obstack_append_string (obstack, text + quoted_len);
1154 : 44 : free (text);
1155 : 44 : free (url);
1156 : 44 : return new_end_idx;
1157 : : }
1158 : :
1159 : : /* A class for tracking quoted text within a buffer for
1160 : : use by a urlifier. */
1161 : :
1162 : 750562 : class quoting_info
1163 : : {
1164 : : public:
1165 : : /* Called when quoted text is begun in phase 1 or 2. */
1166 : 854081 : void on_begin_quote (const output_buffer &buf,
1167 : : unsigned chunk_idx)
1168 : : {
1169 : : /* Stash location of start of quoted string. */
1170 : 854081 : size_t byte_offset = obstack_object_size (&buf.chunk_obstack);
1171 : 854081 : m_loc_last_open_quote = location (chunk_idx, byte_offset);
1172 : 854081 : }
1173 : :
1174 : : /* Called when quoted text is ended in phase 1 or 2. */
1175 : 813164 : void on_end_quote (pretty_printer *pp,
1176 : : output_buffer &buf,
1177 : : unsigned chunk_idx,
1178 : : const urlifier &urlifier)
1179 : : {
1180 : : /* If possible, do urlification now. */
1181 : 813164 : if (chunk_idx == m_loc_last_open_quote.m_chunk_idx)
1182 : : {
1183 : 791630 : urlify_quoted_string (pp,
1184 : : &buf.chunk_obstack,
1185 : : &urlifier,
1186 : : m_loc_last_open_quote.m_byte_offset,
1187 : 791630 : obstack_object_size (&buf.chunk_obstack));
1188 : 791630 : m_loc_last_open_quote = location ();
1189 : 791630 : return;
1190 : : }
1191 : : /* Otherwise the quoted text straddles multiple chunks.
1192 : : Stash the location of end of quoted string for use in phase 3. */
1193 : 21534 : size_t byte_offset = obstack_object_size (&buf.chunk_obstack);
1194 : 43068 : m_phase_3_quotes.push_back (run (m_loc_last_open_quote,
1195 : : location (chunk_idx, byte_offset)));
1196 : 21534 : m_loc_last_open_quote = location ();
1197 : : }
1198 : :
1199 : 36 : bool has_phase_3_quotes_p () const
1200 : : {
1201 : 36 : return m_phase_3_quotes.size () > 0;
1202 : : }
1203 : : void handle_phase_3 (pretty_printer *pp,
1204 : : const urlifier &urlifier);
1205 : :
1206 : : private:
1207 : : struct location
1208 : : {
1209 : 813164 : location ()
1210 : 750562 : : m_chunk_idx (UINT_MAX),
1211 : 750562 : m_byte_offset (SIZE_MAX)
1212 : : {
1213 : : }
1214 : :
1215 : 875615 : location (unsigned chunk_idx,
1216 : : size_t byte_offset)
1217 : : : m_chunk_idx (chunk_idx),
1218 : : m_byte_offset (byte_offset)
1219 : : {
1220 : : }
1221 : :
1222 : : unsigned m_chunk_idx;
1223 : : size_t m_byte_offset;
1224 : : };
1225 : :
1226 : : struct run
1227 : : {
1228 : 21534 : run (location start, location end)
1229 : 21534 : : m_start (start), m_end (end)
1230 : : {
1231 : : }
1232 : :
1233 : : location m_start;
1234 : : location m_end;
1235 : : };
1236 : :
1237 : : location m_loc_last_open_quote;
1238 : : std::vector<run> m_phase_3_quotes;
1239 : : };
1240 : :
1241 : : static void
1242 : 1001926 : on_begin_quote (const output_buffer &buf,
1243 : : unsigned chunk_idx,
1244 : : const urlifier *urlifier)
1245 : : {
1246 : 1001926 : if (!urlifier)
1247 : : return;
1248 : 854081 : if (!buf.cur_chunk_array->m_quotes)
1249 : 750562 : buf.cur_chunk_array->m_quotes = new quoting_info ();
1250 : 854081 : buf.cur_chunk_array->m_quotes->on_begin_quote (buf, chunk_idx);
1251 : : }
1252 : :
1253 : : static void
1254 : 956190 : on_end_quote (pretty_printer *pp,
1255 : : output_buffer &buf,
1256 : : unsigned chunk_idx,
1257 : : const urlifier *urlifier)
1258 : : {
1259 : 956190 : if (!urlifier)
1260 : : return;
1261 : 813164 : if (!buf.cur_chunk_array->m_quotes)
1262 : 0 : buf.cur_chunk_array->m_quotes = new quoting_info ();
1263 : 813164 : buf.cur_chunk_array->m_quotes->on_end_quote (pp, buf, chunk_idx, *urlifier);
1264 : : }
1265 : :
1266 : : /* The following format specifiers are recognized as being client independent:
1267 : : %d, %i: (signed) integer in base ten.
1268 : : %u: unsigned integer in base ten.
1269 : : %o: unsigned integer in base eight.
1270 : : %x: unsigned integer in base sixteen.
1271 : : %ld, %li, %lo, %lu, %lx: long versions of the above.
1272 : : %lld, %lli, %llo, %llu, %llx: long long versions.
1273 : : %wd, %wi, %wo, %wu, %wx: HOST_WIDE_INT versions.
1274 : : %zd, %zi, %zo, %zu, %zx: size_t versions.
1275 : : %td, %ti, %to, %tu, %tx: ptrdiff_t versions.
1276 : : %f: double
1277 : : %c: character.
1278 : : %s: string.
1279 : : %p: pointer (printed in a host-dependent manner).
1280 : : %r: if pp_show_color(pp), switch to color identified by const char *.
1281 : : %R: if pp_show_color(pp), reset color.
1282 : : %m: strerror(text->err_no) - does not consume a value from args_ptr.
1283 : : %%: '%'.
1284 : : %<: opening quote.
1285 : : %>: closing quote.
1286 : : %{: URL start. Consumes a const char * argument for the URL.
1287 : : %}: URL end. Does not consume any arguments.
1288 : : %': apostrophe (should only be used in untranslated messages;
1289 : : translations should use appropriate punctuation directly).
1290 : : %@: diagnostic_event_id_ptr, for which event_id->known_p () must be true.
1291 : : %.*s: a substring the length of which is specified by an argument
1292 : : integer.
1293 : : %Ns: likewise, but length specified as constant in the format string.
1294 : : Flag 'q': quote formatted text (must come immediately after '%').
1295 : : %Z: Requires two arguments - array of int, and len. Prints elements
1296 : : of the array.
1297 : :
1298 : : Arguments can be used sequentially, or through %N$ resp. *N$
1299 : : notation Nth argument after the format string. If %N$ / *N$
1300 : : notation is used, it must be used for all arguments, except %m, %%,
1301 : : %<, %>, %} and %', which may not have a number, as they do not consume
1302 : : an argument. When %M$.*N$s is used, M must be N + 1. (This may
1303 : : also be written %M$.*s, provided N is not otherwise used.) The
1304 : : format string must have conversion specifiers with argument numbers
1305 : : 1 up to highest argument; each argument may only be used once.
1306 : : A format string can have at most 30 arguments. */
1307 : :
1308 : : /* Formatting phases 1 and 2: render TEXT->format_spec plus
1309 : : text->m_args_ptr into a series of chunks in pp_buffer (PP)->args[].
1310 : : Phase 3 is in pp_output_formatted_text.
1311 : :
1312 : : If URLIFIER is non-NULL, then use it to add URLs for quoted
1313 : : strings, so that e.g.
1314 : : "before %<quoted%> after"
1315 : : with a URLIFIER that has a URL for "quoted" might be emitted as:
1316 : : "before `BEGIN_URL(http://example.com)quotedEND_URL' after"
1317 : : This is handled here for message fragments that are:
1318 : : - quoted entirely in phase 1 (e.g. "%<this is quoted%>"), or
1319 : : - quoted entirely in phase 2 (e.g. "%qs"),
1320 : : Quoted fragments that use a mixture of both phases
1321 : : (e.g. "%<this is a mixture: %s %>")
1322 : : are stashed into the output_buffer's m_quotes for use in phase 3. */
1323 : :
1324 : : void
1325 : 9512673 : pp_format (pretty_printer *pp,
1326 : : text_info *text,
1327 : : const urlifier *urlifier)
1328 : : {
1329 : 9512673 : output_buffer * const buffer = pp_buffer (pp);
1330 : 9512673 : const char *p;
1331 : 9512673 : const char **args;
1332 : 9512673 : struct chunk_info *new_chunk_array;
1333 : :
1334 : 9512673 : unsigned int curarg = 0, chunk = 0, argno;
1335 : 9512673 : pp_wrapping_mode_t old_wrapping_mode;
1336 : 9512673 : bool any_unnumbered = false, any_numbered = false;
1337 : 9512673 : const char **formatters[PP_NL_ARGMAX];
1338 : :
1339 : : /* Allocate a new chunk structure. */
1340 : 9512673 : new_chunk_array = XOBNEW (&buffer->chunk_obstack, struct chunk_info);
1341 : :
1342 : 9512673 : new_chunk_array->prev = buffer->cur_chunk_array;
1343 : 9512673 : new_chunk_array->m_quotes = nullptr;
1344 : 9512673 : buffer->cur_chunk_array = new_chunk_array;
1345 : 9512673 : args = new_chunk_array->args;
1346 : :
1347 : : /* Formatting phase 1: split up TEXT->format_spec into chunks in
1348 : : pp_buffer (PP)->args[]. Even-numbered chunks are to be output
1349 : : verbatim, odd-numbered chunks are format specifiers.
1350 : : %m, %%, %<, %>, %} and %' are replaced with the appropriate text at
1351 : : this point. */
1352 : :
1353 : 9512673 : memset (formatters, 0, sizeof formatters);
1354 : :
1355 : 14588667 : for (p = text->m_format_spec; *p; )
1356 : : {
1357 : 217053853 : while (*p != '\0' && *p != '%')
1358 : : {
1359 : 202489497 : obstack_1grow (&buffer->chunk_obstack, *p);
1360 : 202489497 : p++;
1361 : : }
1362 : :
1363 : 14564356 : if (*p == '\0')
1364 : : break;
1365 : :
1366 : 9062826 : switch (*++p)
1367 : : {
1368 : 0 : case '\0':
1369 : 0 : gcc_unreachable ();
1370 : :
1371 : 13222 : case '%':
1372 : 13222 : obstack_1grow (&buffer->chunk_obstack, '%');
1373 : 13222 : p++;
1374 : 13222 : continue;
1375 : :
1376 : 102836 : case '<':
1377 : 102836 : {
1378 : 102836 : obstack_grow (&buffer->chunk_obstack,
1379 : : open_quote, strlen (open_quote));
1380 : 102836 : const char *colorstr
1381 : 102836 : = colorize_start (pp_show_color (pp), "quote");
1382 : 102836 : obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr));
1383 : 102836 : p++;
1384 : :
1385 : 102836 : on_begin_quote (*buffer, chunk, urlifier);
1386 : 102836 : continue;
1387 : 102836 : }
1388 : :
1389 : 102836 : case '>':
1390 : 102836 : {
1391 : 102836 : on_end_quote (pp, *buffer, chunk, urlifier);
1392 : :
1393 : 102836 : const char *colorstr = colorize_stop (pp_show_color (pp));
1394 : 102836 : obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr));
1395 : : }
1396 : : /* FALLTHRU */
1397 : 121196 : case '\'':
1398 : 121196 : obstack_grow (&buffer->chunk_obstack,
1399 : : close_quote, strlen (close_quote));
1400 : 121196 : p++;
1401 : 121196 : continue;
1402 : :
1403 : 8 : case '}':
1404 : 8 : {
1405 : 8 : const char *endurlstr = get_end_url_string (pp);
1406 : 8 : obstack_grow (&buffer->chunk_obstack, endurlstr,
1407 : : strlen (endurlstr));
1408 : : }
1409 : 8 : p++;
1410 : 8 : continue;
1411 : :
1412 : 14505 : case 'R':
1413 : 14505 : {
1414 : 14505 : const char *colorstr = colorize_stop (pp_show_color (pp));
1415 : 14505 : obstack_grow (&buffer->chunk_obstack, colorstr,
1416 : : strlen (colorstr));
1417 : 14505 : p++;
1418 : 14505 : continue;
1419 : 14505 : }
1420 : :
1421 : 13 : case 'm':
1422 : 13 : {
1423 : 13 : const char *errstr = xstrerror (text->m_err_no);
1424 : 13 : obstack_grow (&buffer->chunk_obstack, errstr, strlen (errstr));
1425 : : }
1426 : 13 : p++;
1427 : 13 : continue;
1428 : :
1429 : 8811046 : default:
1430 : : /* Handled in phase 2. Terminate the plain chunk here. */
1431 : 8811046 : obstack_1grow (&buffer->chunk_obstack, '\0');
1432 : 8811046 : args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *);
1433 : 8811046 : break;
1434 : 134439 : }
1435 : :
1436 : 8811046 : if (ISDIGIT (*p))
1437 : : {
1438 : 32 : char *end;
1439 : 32 : argno = strtoul (p, &end, 10) - 1;
1440 : 32 : p = end;
1441 : 32 : gcc_assert (*p == '$');
1442 : 32 : p++;
1443 : :
1444 : 32 : any_numbered = true;
1445 : 32 : gcc_assert (!any_unnumbered);
1446 : : }
1447 : : else
1448 : : {
1449 : 8811014 : argno = curarg++;
1450 : 8811014 : any_unnumbered = true;
1451 : 8811014 : gcc_assert (!any_numbered);
1452 : : }
1453 : 8811046 : gcc_assert (argno < PP_NL_ARGMAX);
1454 : 8811046 : gcc_assert (!formatters[argno]);
1455 : 8811046 : formatters[argno] = &args[chunk];
1456 : 9849597 : do
1457 : : {
1458 : 9849597 : obstack_1grow (&buffer->chunk_obstack, *p);
1459 : 9849597 : p++;
1460 : : }
1461 : 9849597 : while (strchr ("qwlzt+#", p[-1]));
1462 : :
1463 : 8811046 : if (p[-1] == '.')
1464 : : {
1465 : : /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1466 : : (where M == N + 1). */
1467 : 5208 : if (ISDIGIT (*p))
1468 : : {
1469 : 8 : do
1470 : : {
1471 : 8 : obstack_1grow (&buffer->chunk_obstack, *p);
1472 : 8 : p++;
1473 : : }
1474 : 8 : while (ISDIGIT (p[-1]));
1475 : 4 : gcc_assert (p[-1] == 's');
1476 : : }
1477 : : else
1478 : : {
1479 : 5204 : gcc_assert (*p == '*');
1480 : 5204 : obstack_1grow (&buffer->chunk_obstack, '*');
1481 : 5204 : p++;
1482 : :
1483 : 5204 : if (ISDIGIT (*p))
1484 : : {
1485 : 0 : char *end;
1486 : 0 : unsigned int argno2 = strtoul (p, &end, 10) - 1;
1487 : 0 : p = end;
1488 : 0 : gcc_assert (argno2 == argno - 1);
1489 : 0 : gcc_assert (!any_unnumbered);
1490 : 0 : gcc_assert (*p == '$');
1491 : :
1492 : 0 : p++;
1493 : 0 : formatters[argno2] = formatters[argno];
1494 : : }
1495 : : else
1496 : : {
1497 : 5204 : gcc_assert (!any_numbered);
1498 : 5204 : formatters[argno+1] = formatters[argno];
1499 : 5204 : curarg++;
1500 : : }
1501 : 5204 : gcc_assert (*p == 's');
1502 : 5204 : obstack_1grow (&buffer->chunk_obstack, 's');
1503 : 5204 : p++;
1504 : : }
1505 : : }
1506 : 8811046 : if (*p == '\0')
1507 : : break;
1508 : :
1509 : 4824214 : obstack_1grow (&buffer->chunk_obstack, '\0');
1510 : 4824214 : gcc_assert (chunk < PP_NL_ARGMAX * 2);
1511 : 4824214 : args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *);
1512 : : }
1513 : :
1514 : 9512673 : obstack_1grow (&buffer->chunk_obstack, '\0');
1515 : 9512673 : gcc_assert (chunk < PP_NL_ARGMAX * 2);
1516 : 9512673 : args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *);
1517 : 9512673 : args[chunk] = 0;
1518 : :
1519 : : /* Set output to the argument obstack, and switch line-wrapping and
1520 : : prefixing off. */
1521 : 9512673 : buffer->obstack = &buffer->chunk_obstack;
1522 : 9512673 : const int old_line_length = buffer->line_length;
1523 : 9512673 : old_wrapping_mode = pp_set_verbatim_wrapping (pp);
1524 : :
1525 : : /* Second phase. Replace each formatter with the formatted text it
1526 : : corresponds to. */
1527 : :
1528 : 18323719 : for (argno = 0; formatters[argno]; argno++)
1529 : : {
1530 : 8811046 : int precision = 0;
1531 : 8811046 : bool wide = false;
1532 : 8811046 : bool plus = false;
1533 : 8811046 : bool hash = false;
1534 : 8811046 : bool quote = false;
1535 : :
1536 : : /* We do not attempt to enforce any ordering on the modifier
1537 : : characters. */
1538 : :
1539 : 9849597 : for (p = *formatters[argno];; p++)
1540 : : {
1541 : 9849597 : switch (*p)
1542 : : {
1543 : 899090 : case 'q':
1544 : 899090 : gcc_assert (!quote);
1545 : 899090 : quote = true;
1546 : 899090 : continue;
1547 : :
1548 : 3254 : case '+':
1549 : 3254 : gcc_assert (!plus);
1550 : 3254 : plus = true;
1551 : 3254 : continue;
1552 : :
1553 : 41086 : case '#':
1554 : 41086 : gcc_assert (!hash);
1555 : 41086 : hash = true;
1556 : 41086 : continue;
1557 : :
1558 : 61435 : case 'w':
1559 : 61435 : gcc_assert (!wide);
1560 : 61435 : wide = true;
1561 : 61435 : continue;
1562 : :
1563 : 32 : case 'z':
1564 : 32 : gcc_assert (!precision);
1565 : 32 : precision = 3;
1566 : 32 : continue;
1567 : :
1568 : 27 : case 't':
1569 : 27 : gcc_assert (!precision);
1570 : 27 : precision = 4;
1571 : 27 : continue;
1572 : :
1573 : 33627 : case 'l':
1574 : : /* We don't support precision beyond that of "long long". */
1575 : 33627 : gcc_assert (precision < 2);
1576 : 33627 : precision++;
1577 : 33627 : continue;
1578 : : }
1579 : 8811046 : break;
1580 : : }
1581 : :
1582 : 8811046 : gcc_assert (!wide || precision == 0);
1583 : :
1584 : 8811046 : if (quote)
1585 : : {
1586 : 899090 : pp_begin_quote (pp, pp_show_color (pp));
1587 : 899090 : on_begin_quote (*buffer, chunk, urlifier);
1588 : : }
1589 : :
1590 : 8811046 : switch (*p)
1591 : : {
1592 : 14505 : case 'r':
1593 : 14505 : pp_string (pp, colorize_start (pp_show_color (pp),
1594 : 14505 : va_arg (*text->m_args_ptr,
1595 : : const char *)));
1596 : 14505 : break;
1597 : :
1598 : 3020 : case 'c':
1599 : 3020 : {
1600 : : /* When quoting, print alphanumeric, punctuation, and the space
1601 : : character unchanged, and all others in hexadecimal with the
1602 : : "\x" prefix. Otherwise print them all unchanged. */
1603 : 3020 : int chr = va_arg (*text->m_args_ptr, int);
1604 : 3020 : if (ISPRINT (chr) || !quote)
1605 : 2979 : pp_character (pp, chr);
1606 : : else
1607 : : {
1608 : 41 : const char str [2] = { chr, '\0' };
1609 : 41 : pp_quoted_string (pp, str, 1);
1610 : : }
1611 : : break;
1612 : : }
1613 : :
1614 : 1051107 : case 'd':
1615 : 1051107 : case 'i':
1616 : 1051107 : if (wide)
1617 : 19359 : pp_wide_integer (pp, va_arg (*text->m_args_ptr, HOST_WIDE_INT));
1618 : : else
1619 : 1031748 : pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1620 : : int, "d");
1621 : : break;
1622 : :
1623 : 52 : case 'o':
1624 : 52 : if (wide)
1625 : 4 : pp_scalar (pp, "%" HOST_WIDE_INT_PRINT "o",
1626 : : va_arg (*text->m_args_ptr, unsigned HOST_WIDE_INT));
1627 : : else
1628 : 48 : pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1629 : : unsigned, "o");
1630 : : break;
1631 : :
1632 : 2423756 : case 's':
1633 : 2423756 : if (quote)
1634 : 615660 : pp_quoted_string (pp, va_arg (*text->m_args_ptr, const char *));
1635 : : else
1636 : 1808096 : pp_string (pp, va_arg (*text->m_args_ptr, const char *));
1637 : : break;
1638 : :
1639 : 148165 : case 'p':
1640 : 148165 : pp_pointer (pp, va_arg (*text->m_args_ptr, void *));
1641 : 148165 : break;
1642 : :
1643 : 381043 : case 'u':
1644 : 381043 : if (wide)
1645 : 41990 : pp_scalar (pp, HOST_WIDE_INT_PRINT_UNSIGNED,
1646 : : va_arg (*text->m_args_ptr, unsigned HOST_WIDE_INT));
1647 : : else
1648 : 339053 : pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1649 : : unsigned, "u");
1650 : : break;
1651 : :
1652 : 66755 : case 'f':
1653 : 66755 : pp_double (pp, va_arg (*text->m_args_ptr, double));
1654 : 66755 : break;
1655 : :
1656 : 349 : case 'Z':
1657 : 349 : {
1658 : 349 : int *v = va_arg (*text->m_args_ptr, int *);
1659 : 349 : unsigned len = va_arg (*text->m_args_ptr, unsigned);
1660 : :
1661 : 717 : for (unsigned i = 0; i < len; ++i)
1662 : : {
1663 : 368 : pp_scalar (pp, "%i", v[i]);
1664 : 368 : if (i < len - 1)
1665 : : {
1666 : 19 : pp_comma (pp);
1667 : 19 : pp_space (pp);
1668 : : }
1669 : : }
1670 : : break;
1671 : : }
1672 : :
1673 : 1875 : case 'x':
1674 : 1875 : if (wide)
1675 : 82 : pp_scalar (pp, HOST_WIDE_INT_PRINT_HEX,
1676 : : va_arg (*text->m_args_ptr, unsigned HOST_WIDE_INT));
1677 : : else
1678 : 1793 : pp_integer_with_precision (pp, *text->m_args_ptr, precision,
1679 : : unsigned, "x");
1680 : : break;
1681 : :
1682 : 5208 : case '.':
1683 : 5208 : {
1684 : 5208 : int n;
1685 : 5208 : const char *s;
1686 : :
1687 : : /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1688 : : (where M == N + 1). The format string should be verified
1689 : : already from the first phase. */
1690 : 5208 : p++;
1691 : 5208 : if (ISDIGIT (*p))
1692 : : {
1693 : 4 : char *end;
1694 : 4 : n = strtoul (p, &end, 10);
1695 : 4 : p = end;
1696 : 4 : gcc_assert (*p == 's');
1697 : : }
1698 : : else
1699 : : {
1700 : 5204 : gcc_assert (*p == '*');
1701 : 5204 : p++;
1702 : 5204 : gcc_assert (*p == 's');
1703 : 5204 : n = va_arg (*text->m_args_ptr, int);
1704 : :
1705 : : /* This consumes a second entry in the formatters array. */
1706 : 5204 : gcc_assert (formatters[argno] == formatters[argno+1]);
1707 : : argno++;
1708 : : }
1709 : :
1710 : 5208 : s = va_arg (*text->m_args_ptr, const char *);
1711 : :
1712 : : /* Append the lesser of precision and strlen (s) characters
1713 : : from the array (which need not be a nul-terminated string).
1714 : : Negative precision is treated as if it were omitted. */
1715 : 5208 : size_t len = n < 0 ? strlen (s) : strnlen (s, n);
1716 : :
1717 : 5208 : pp_append_text (pp, s, s + len);
1718 : : }
1719 : 5208 : break;
1720 : :
1721 : 22383 : case '@':
1722 : 22383 : {
1723 : : /* diagnostic_event_id_t *. */
1724 : 22383 : diagnostic_event_id_ptr event_id
1725 : 22383 : = va_arg (*text->m_args_ptr, diagnostic_event_id_ptr);
1726 : 22383 : gcc_assert (event_id->known_p ());
1727 : :
1728 : 22383 : pp_string (pp, colorize_start (pp_show_color (pp), "path"));
1729 : 22383 : pp_character (pp, '(');
1730 : 22383 : pp_decimal_int (pp, event_id->one_based ());
1731 : 22383 : pp_character (pp, ')');
1732 : 22383 : pp_string (pp, colorize_stop (pp_show_color (pp)));
1733 : : }
1734 : 22383 : break;
1735 : :
1736 : 8 : case '{':
1737 : 8 : pp_begin_url (pp, va_arg (*text->m_args_ptr, const char *));
1738 : 8 : break;
1739 : :
1740 : 4692820 : default:
1741 : 4692820 : {
1742 : 4692820 : bool ok;
1743 : :
1744 : : /* Call the format decoder.
1745 : : Pass the address of "quote" so that format decoders can
1746 : : potentially disable printing of the closing quote
1747 : : (e.g. when printing "'TYPEDEF' aka 'TYPE'" in the C family
1748 : : of frontends). */
1749 : 4692820 : gcc_assert (pp_format_decoder (pp));
1750 : 4692820 : ok = pp_format_decoder (pp) (pp, text, p,
1751 : : precision, wide, plus, hash, "e,
1752 : : formatters[argno]);
1753 : 4692820 : gcc_assert (ok);
1754 : : }
1755 : : }
1756 : :
1757 : 8811046 : if (quote)
1758 : : {
1759 : 853354 : on_end_quote (pp, *buffer, chunk, urlifier);
1760 : 853354 : pp_end_quote (pp, pp_show_color (pp));
1761 : : }
1762 : :
1763 : 8811046 : obstack_1grow (&buffer->chunk_obstack, '\0');
1764 : 8811046 : *formatters[argno] = XOBFINISH (&buffer->chunk_obstack, const char *);
1765 : : }
1766 : :
1767 : : if (CHECKING_P)
1768 : 286076613 : for (; argno < PP_NL_ARGMAX; argno++)
1769 : 276563940 : gcc_assert (!formatters[argno]);
1770 : :
1771 : : /* If the client supplied a postprocessing object, call its "handle"
1772 : : hook here. */
1773 : 9512673 : if (pp->m_format_postprocessor)
1774 : 303633 : pp->m_format_postprocessor->handle (pp);
1775 : :
1776 : : /* Revert to normal obstack and wrapping mode. */
1777 : 9512673 : buffer->obstack = &buffer->formatted_obstack;
1778 : 9512673 : buffer->line_length = old_line_length;
1779 : 9512673 : pp_wrapping_mode (pp) = old_wrapping_mode;
1780 : 9512673 : pp_clear_state (pp);
1781 : 9512673 : }
1782 : :
1783 : : struct auto_obstack
1784 : : {
1785 : 24 : auto_obstack ()
1786 : 24 : {
1787 : 24 : obstack_init (&m_obstack);
1788 : 24 : }
1789 : :
1790 : 24 : ~auto_obstack ()
1791 : : {
1792 : 24 : obstack_free (&m_obstack, NULL);
1793 : 24 : }
1794 : :
1795 : 104 : void grow (const void *src, size_t length)
1796 : : {
1797 : 104 : obstack_grow (&m_obstack, src, length);
1798 : 104 : }
1799 : :
1800 : 24 : void *object_base () const
1801 : : {
1802 : 24 : return m_obstack.object_base;
1803 : : }
1804 : :
1805 : 128 : size_t object_size () const
1806 : : {
1807 : 128 : return obstack_object_size (&m_obstack);
1808 : : }
1809 : :
1810 : : obstack m_obstack;
1811 : : };
1812 : :
1813 : : /* Subroutine of pp_output_formatted_text for the awkward case where
1814 : : quoted text straddles multiple chunks.
1815 : :
1816 : : Flush PP's buffer's chunks to PP's output buffer, whilst inserting
1817 : : URLs for any quoted text that should be URLified.
1818 : :
1819 : : For example, given:
1820 : : | pp_format (pp,
1821 : : | "unrecognized option %qs; did you mean %<-%s%>",
1822 : : | "foo", "foption");
1823 : : we would have these chunks:
1824 : : | chunk 0: "unrecognized option "
1825 : : | chunk 1: "`foo'" (already checked for urlification)
1826 : : | chunk 2: "; did you mean `-"
1827 : : | ^*
1828 : : | chunk 3: "foption"
1829 : : | *******
1830 : : | chunk 4: "'"
1831 : : | ^
1832 : : and this quoting_info would have recorded the open quote near the end
1833 : : of chunk 2 and close quote at the start of chunk 4; this function would
1834 : : check the combination of the end of chunk 2 and all of chunk 3 ("-foption")
1835 : : for urlification. */
1836 : :
1837 : : void
1838 : 24 : quoting_info::handle_phase_3 (pretty_printer *pp,
1839 : : const urlifier &urlifier)
1840 : : {
1841 : 24 : unsigned int chunk;
1842 : 24 : output_buffer * const buffer = pp_buffer (pp);
1843 : 24 : struct chunk_info *chunk_array = buffer->cur_chunk_array;
1844 : 24 : const char **args = chunk_array->args;
1845 : :
1846 : : /* We need to construct the string into an intermediate buffer
1847 : : for this case, since using pp_string can introduce prefixes
1848 : : and line-wrapping, and omit whitespace at the start of lines. */
1849 : 24 : auto_obstack combined_buf;
1850 : :
1851 : : /* Iterate simultaneously through both
1852 : : - the chunks and
1853 : : - the runs of quoted characters
1854 : : Accumulate text from the chunks into combined_buf, and handle
1855 : : runs of quoted characters when handling the chunks they
1856 : : correspond to. */
1857 : 24 : size_t start_of_run_byte_offset = 0;
1858 : 24 : std::vector<quoting_info::run>::const_iterator iter_run
1859 : 24 : = buffer->cur_chunk_array->m_quotes->m_phase_3_quotes.begin ();
1860 : 24 : std::vector<quoting_info::run>::const_iterator end_runs
1861 : 24 : = buffer->cur_chunk_array->m_quotes->m_phase_3_quotes.end ();
1862 : 128 : for (chunk = 0; args[chunk]; chunk++)
1863 : : {
1864 : 104 : size_t start_of_chunk_idx = combined_buf.object_size ();
1865 : :
1866 : 104 : combined_buf.grow (args[chunk], strlen (args[chunk]));
1867 : :
1868 : 104 : if (iter_run != end_runs
1869 : 104 : && chunk == iter_run->m_end.m_chunk_idx)
1870 : : {
1871 : : /* A run is ending; consider for it urlification. */
1872 : 32 : const size_t end_of_run_byte_offset
1873 : 32 : = start_of_chunk_idx + iter_run->m_end.m_byte_offset;
1874 : 32 : const size_t end_offset
1875 : 32 : = urlify_quoted_string (pp,
1876 : : &combined_buf.m_obstack,
1877 : : &urlifier,
1878 : : start_of_run_byte_offset,
1879 : : end_of_run_byte_offset);
1880 : :
1881 : : /* If URLification occurred it will have grown the buffer.
1882 : : We need to update start_of_chunk_idx so that offsets
1883 : : relative to it are still correct, for the case where
1884 : : we have a chunk that both ends a quoted run and starts
1885 : : another quoted run. */
1886 : 32 : gcc_assert (end_offset >= end_of_run_byte_offset);
1887 : 32 : start_of_chunk_idx += end_offset - end_of_run_byte_offset;
1888 : :
1889 : 32 : iter_run++;
1890 : : }
1891 : 104 : if (iter_run != end_runs
1892 : 104 : && chunk == iter_run->m_start.m_chunk_idx)
1893 : : {
1894 : : /* Note where the run starts w.r.t. the composed buffer. */
1895 : 32 : start_of_run_byte_offset
1896 : 32 : = start_of_chunk_idx + iter_run->m_start.m_byte_offset;
1897 : : }
1898 : : }
1899 : :
1900 : : /* Now print to PP. */
1901 : 24 : const char *start
1902 : 24 : = static_cast <const char *> (combined_buf.object_base ());
1903 : 24 : pp_maybe_wrap_text (pp, start, start + combined_buf.object_size ());
1904 : 24 : }
1905 : :
1906 : : /* Format of a message pointed to by TEXT.
1907 : : If URLIFIER is non-null then use it on any quoted text that was not
1908 : : handled in phases 1 or 2 to potentially add URLs. */
1909 : :
1910 : : void
1911 : 2118540 : pp_output_formatted_text (pretty_printer *pp,
1912 : : const urlifier *urlifier)
1913 : : {
1914 : 2118540 : unsigned int chunk;
1915 : 2118540 : output_buffer * const buffer = pp_buffer (pp);
1916 : 2118540 : struct chunk_info *chunk_array = buffer->cur_chunk_array;
1917 : 2118540 : const char **args = chunk_array->args;
1918 : :
1919 : 2118540 : gcc_assert (buffer->obstack == &buffer->formatted_obstack);
1920 : :
1921 : : /* This is a third phase, first 2 phases done in pp_format_args.
1922 : : Now we actually print it. */
1923 : :
1924 : : /* If we have any deferred urlification, handle it now. */
1925 : 2118540 : if (urlifier
1926 : 1390150 : && pp->url_format != URL_FORMAT_NONE
1927 : 36 : && buffer->cur_chunk_array->m_quotes
1928 : 2118576 : && buffer->cur_chunk_array->m_quotes->has_phase_3_quotes_p ())
1929 : 24 : buffer->cur_chunk_array->m_quotes->handle_phase_3 (pp, *urlifier);
1930 : : else
1931 : 9914133 : for (chunk = 0; args[chunk]; chunk++)
1932 : 7795617 : pp_string (pp, args[chunk]);
1933 : :
1934 : : /* Deallocate the chunk structure and everything after it (i.e. the
1935 : : associated series of formatted strings). */
1936 : 2869102 : delete buffer->cur_chunk_array->m_quotes;
1937 : 2118540 : buffer->cur_chunk_array = chunk_array->prev;
1938 : 2118540 : obstack_free (&buffer->chunk_obstack, chunk_array);
1939 : 2118540 : }
1940 : :
1941 : : /* Helper subroutine of output_verbatim and verbatim. Do the appropriate
1942 : : settings needed by BUFFER for a verbatim formatting. */
1943 : : void
1944 : 42757 : pp_format_verbatim (pretty_printer *pp, text_info *text)
1945 : : {
1946 : : /* Set verbatim mode. */
1947 : 42757 : pp_wrapping_mode_t oldmode = pp_set_verbatim_wrapping (pp);
1948 : :
1949 : : /* Do the actual formatting. */
1950 : 42757 : pp_format (pp, text);
1951 : 42757 : pp_output_formatted_text (pp);
1952 : :
1953 : : /* Restore previous settings. */
1954 : 42757 : pp_wrapping_mode (pp) = oldmode;
1955 : 42757 : }
1956 : :
1957 : : /* Flush the content of BUFFER onto the attached stream. This
1958 : : function does nothing unless pp->output_buffer->flush_p. */
1959 : : void
1960 : 8233482 : pp_flush (pretty_printer *pp)
1961 : : {
1962 : 8233482 : pp_clear_state (pp);
1963 : 8233482 : if (!pp->buffer->flush_p)
1964 : : return;
1965 : 7163543 : pp_write_text_to_stream (pp);
1966 : 7163543 : fflush (pp_buffer (pp)->stream);
1967 : : }
1968 : :
1969 : : /* Flush the content of BUFFER onto the attached stream independently
1970 : : of the value of pp->output_buffer->flush_p. */
1971 : : void
1972 : 6871 : pp_really_flush (pretty_printer *pp)
1973 : : {
1974 : 6871 : pp_clear_state (pp);
1975 : 6871 : pp_write_text_to_stream (pp);
1976 : 6871 : fflush (pp_buffer (pp)->stream);
1977 : 6871 : }
1978 : :
1979 : : /* Sets the number of maximum characters per line PRETTY-PRINTER can
1980 : : output in line-wrapping mode. A LENGTH value 0 suppresses
1981 : : line-wrapping. */
1982 : : void
1983 : 106857 : pp_set_line_maximum_length (pretty_printer *pp, int length)
1984 : : {
1985 : 106857 : pp_line_cutoff (pp) = length;
1986 : 106857 : pp_set_real_maximum_length (pp);
1987 : 106857 : }
1988 : :
1989 : : /* Clear PRETTY-PRINTER output area text info. */
1990 : : void
1991 : 207006196 : pp_clear_output_area (pretty_printer *pp)
1992 : : {
1993 : 207006196 : obstack_free (pp_buffer (pp)->obstack,
1994 : : obstack_base (pp_buffer (pp)->obstack));
1995 : 207006196 : pp_buffer (pp)->line_length = 0;
1996 : 207006196 : }
1997 : :
1998 : : /* Set PREFIX for PRETTY-PRINTER, taking ownership of PREFIX, which
1999 : : will eventually be free-ed. */
2000 : :
2001 : : void
2002 : 18306767 : pp_set_prefix (pretty_printer *pp, char *prefix)
2003 : : {
2004 : 18306767 : free (pp->prefix);
2005 : 18306767 : pp->prefix = prefix;
2006 : 18306767 : pp_set_real_maximum_length (pp);
2007 : 18306767 : pp->emitted_prefix = false;
2008 : 18306767 : pp_indentation (pp) = 0;
2009 : 18306767 : }
2010 : :
2011 : : /* Take ownership of PP's prefix, setting it to NULL.
2012 : : This allows clients to save, override, and then restore an existing
2013 : : prefix, without it being free-ed. */
2014 : :
2015 : : char *
2016 : 358179 : pp_take_prefix (pretty_printer *pp)
2017 : : {
2018 : 358179 : char *result = pp->prefix;
2019 : 358179 : pp->prefix = NULL;
2020 : 358179 : return result;
2021 : : }
2022 : :
2023 : : /* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */
2024 : : void
2025 : 1112826 : pp_destroy_prefix (pretty_printer *pp)
2026 : : {
2027 : 1112826 : if (pp->prefix != NULL)
2028 : : {
2029 : 1112308 : free (pp->prefix);
2030 : 1112308 : pp->prefix = NULL;
2031 : : }
2032 : 1112826 : }
2033 : :
2034 : : /* Write out PRETTY-PRINTER's prefix. */
2035 : : void
2036 : 272974472 : pp_emit_prefix (pretty_printer *pp)
2037 : : {
2038 : 272974472 : if (pp->prefix != NULL)
2039 : : {
2040 : 1746988 : switch (pp_prefixing_rule (pp))
2041 : : {
2042 : : default:
2043 : : case DIAGNOSTICS_SHOW_PREFIX_NEVER:
2044 : : break;
2045 : :
2046 : 1434684 : case DIAGNOSTICS_SHOW_PREFIX_ONCE:
2047 : 1434684 : if (pp->emitted_prefix)
2048 : : {
2049 : 127 : pp_indent (pp);
2050 : 127 : break;
2051 : : }
2052 : 1434557 : pp_indentation (pp) += 3;
2053 : : /* Fall through. */
2054 : :
2055 : 1441879 : case DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE:
2056 : 1441879 : {
2057 : 1441879 : int prefix_length = strlen (pp->prefix);
2058 : 1441879 : pp_append_r (pp, pp->prefix, prefix_length);
2059 : 1441879 : pp->emitted_prefix = true;
2060 : : }
2061 : 1441879 : break;
2062 : : }
2063 : : }
2064 : 272974472 : }
2065 : :
2066 : : /* Construct a PRETTY-PRINTER of MAXIMUM_LENGTH characters per line. */
2067 : :
2068 : 16181284 : pretty_printer::pretty_printer (int maximum_length)
2069 : 16181284 : : buffer (new (XCNEW (output_buffer)) output_buffer ()),
2070 : 16181284 : prefix (),
2071 : 16181284 : padding (pp_none),
2072 : 16181284 : maximum_length (),
2073 : 16181284 : indent_skip (),
2074 : 16181284 : wrapping (),
2075 : 16181284 : format_decoder (),
2076 : 16181284 : m_format_postprocessor (NULL),
2077 : 16181284 : emitted_prefix (),
2078 : 16181284 : need_newline (),
2079 : 16181284 : translate_identifiers (true),
2080 : 16181284 : show_color (),
2081 : 16181284 : url_format (URL_FORMAT_NONE),
2082 : 16181284 : m_skipping_null_url (false)
2083 : : {
2084 : 16181284 : pp_line_cutoff (this) = maximum_length;
2085 : : /* By default, we emit prefixes once per message. */
2086 : 16181284 : pp_prefixing_rule (this) = DIAGNOSTICS_SHOW_PREFIX_ONCE;
2087 : 16181284 : pp_set_prefix (this, NULL);
2088 : 16181284 : }
2089 : :
2090 : : /* Copy constructor for pretty_printer. */
2091 : :
2092 : 48859 : pretty_printer::pretty_printer (const pretty_printer &other)
2093 : 48859 : : buffer (new (XCNEW (output_buffer)) output_buffer ()),
2094 : 48859 : prefix (),
2095 : 48859 : padding (other.padding),
2096 : 48859 : maximum_length (other.maximum_length),
2097 : 48859 : indent_skip (other.indent_skip),
2098 : 48859 : wrapping (other.wrapping),
2099 : 48859 : format_decoder (other.format_decoder),
2100 : 48859 : m_format_postprocessor (NULL),
2101 : 48859 : emitted_prefix (other.emitted_prefix),
2102 : 48859 : need_newline (other.need_newline),
2103 : 48859 : translate_identifiers (other.translate_identifiers),
2104 : 48859 : show_color (other.show_color),
2105 : 48859 : url_format (other.url_format),
2106 : 48859 : m_skipping_null_url (false)
2107 : : {
2108 : 48859 : pp_line_cutoff (this) = maximum_length;
2109 : : /* By default, we emit prefixes once per message. */
2110 : 48859 : pp_prefixing_rule (this) = pp_prefixing_rule (&other);
2111 : 48859 : pp_set_prefix (this, NULL);
2112 : :
2113 : 48859 : if (other.m_format_postprocessor)
2114 : 24796 : m_format_postprocessor = other.m_format_postprocessor->clone ();
2115 : 48859 : }
2116 : :
2117 : 15809305 : pretty_printer::~pretty_printer ()
2118 : : {
2119 : 15809163 : if (m_format_postprocessor)
2120 : 125494 : delete m_format_postprocessor;
2121 : 15809163 : buffer->~output_buffer ();
2122 : 15809163 : XDELETE (buffer);
2123 : 15809163 : free (prefix);
2124 : 15809305 : }
2125 : :
2126 : : /* Base class implementation of pretty_printer::clone vfunc. */
2127 : :
2128 : : pretty_printer *
2129 : 142 : pretty_printer::clone () const
2130 : : {
2131 : 142 : return new pretty_printer (*this);
2132 : : }
2133 : :
2134 : : /* Append a string delimited by START and END to the output area of
2135 : : PRETTY-PRINTER. No line wrapping is done. However, if beginning a
2136 : : new line then emit PRETTY-PRINTER's prefix and skip any leading
2137 : : whitespace if appropriate. The caller must ensure that it is
2138 : : safe to do so. */
2139 : : void
2140 : 712508693 : pp_append_text (pretty_printer *pp, const char *start, const char *end)
2141 : : {
2142 : : /* Emit prefix and skip whitespace if we're starting a new line. */
2143 : 712508693 : if (pp_buffer (pp)->line_length == 0)
2144 : : {
2145 : 272801653 : pp_emit_prefix (pp);
2146 : 272801653 : if (pp_is_wrapping_line (pp))
2147 : 52 : while (start != end && *start == ' ')
2148 : 0 : ++start;
2149 : : }
2150 : 712508693 : pp_append_r (pp, start, end - start);
2151 : 712508693 : }
2152 : :
2153 : : /* Finishes constructing a NULL-terminated character string representing
2154 : : the PRETTY-PRINTED text. */
2155 : : const char *
2156 : 200451948 : pp_formatted_text (pretty_printer *pp)
2157 : : {
2158 : 200451948 : return output_buffer_formatted_text (pp_buffer (pp));
2159 : : }
2160 : :
2161 : : /* Return a pointer to the last character emitted in PRETTY-PRINTER's
2162 : : output area. A NULL pointer means no character available. */
2163 : : const char *
2164 : 311214588 : pp_last_position_in_text (const pretty_printer *pp)
2165 : : {
2166 : 311214588 : return output_buffer_last_position_in_text (pp_buffer (pp));
2167 : : }
2168 : :
2169 : : /* Return the amount of characters PRETTY-PRINTER can accept to
2170 : : make a full line. Meaningful only in line-wrapping mode. */
2171 : : int
2172 : 452 : pp_remaining_character_count_for_line (pretty_printer *pp)
2173 : : {
2174 : 452 : return pp->maximum_length - pp_buffer (pp)->line_length;
2175 : : }
2176 : :
2177 : :
2178 : : /* Format a message into BUFFER a la printf. */
2179 : : void
2180 : 630822 : pp_printf (pretty_printer *pp, const char *msg, ...)
2181 : : {
2182 : 630822 : va_list ap;
2183 : :
2184 : 630822 : va_start (ap, msg);
2185 : 630822 : text_info text (msg, &ap, errno);
2186 : 630822 : pp_format (pp, &text);
2187 : 630822 : pp_output_formatted_text (pp);
2188 : 630822 : va_end (ap);
2189 : 630822 : }
2190 : :
2191 : :
2192 : : /* Output MESSAGE verbatim into BUFFER. */
2193 : : void
2194 : 42757 : pp_verbatim (pretty_printer *pp, const char *msg, ...)
2195 : : {
2196 : 42757 : va_list ap;
2197 : :
2198 : 42757 : va_start (ap, msg);
2199 : 42757 : text_info text (msg, &ap, errno);
2200 : 42757 : pp_format_verbatim (pp, &text);
2201 : 42757 : va_end (ap);
2202 : 42757 : }
2203 : :
2204 : :
2205 : :
2206 : : /* Have PRETTY-PRINTER start a new line. */
2207 : : void
2208 : 7385519 : pp_newline (pretty_printer *pp)
2209 : : {
2210 : 7385519 : obstack_1grow (pp_buffer (pp)->obstack, '\n');
2211 : 7385519 : pp_needs_newline (pp) = false;
2212 : 7385519 : pp_buffer (pp)->line_length = 0;
2213 : 7385519 : }
2214 : :
2215 : : /* Have PRETTY-PRINTER add a CHARACTER. */
2216 : : void
2217 : 429913876 : pp_character (pretty_printer *pp, int c)
2218 : : {
2219 : 429913876 : if (pp_is_wrapping_line (pp)
2220 : : /* If printing UTF-8, don't wrap in the middle of a sequence. */
2221 : 284 : && (((unsigned int) c) & 0xC0) != 0x80
2222 : 429914136 : && pp_remaining_character_count_for_line (pp) <= 0)
2223 : : {
2224 : 8 : pp_newline (pp);
2225 : 8 : if (ISSPACE (c))
2226 : : return;
2227 : : }
2228 : 429913876 : obstack_1grow (pp_buffer (pp)->obstack, c);
2229 : 429913876 : ++pp_buffer (pp)->line_length;
2230 : : }
2231 : :
2232 : : /* Append a STRING to the output area of PRETTY-PRINTER; the STRING may
2233 : : be line-wrapped if in appropriate mode. */
2234 : : void
2235 : 699636605 : pp_string (pretty_printer *pp, const char *str)
2236 : : {
2237 : 699636605 : gcc_checking_assert (str);
2238 : 699636605 : pp_maybe_wrap_text (pp, str, str + strlen (str));
2239 : 699636605 : }
2240 : :
2241 : : /* Append code point C to the output area of PRETTY-PRINTER, encoding it
2242 : : as UTF-8. */
2243 : :
2244 : : void
2245 : 137808 : pp_unicode_character (pretty_printer *pp, unsigned c)
2246 : : {
2247 : 137808 : static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
2248 : 137808 : static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
2249 : 137808 : size_t nbytes;
2250 : 137808 : uchar buf[6], *p = &buf[6];
2251 : :
2252 : 137808 : nbytes = 1;
2253 : 137808 : if (c < 0x80)
2254 : 94891 : *--p = c;
2255 : : else
2256 : : {
2257 : 85844 : do
2258 : : {
2259 : 85844 : *--p = ((c & 0x3F) | 0x80);
2260 : 85844 : c >>= 6;
2261 : 85844 : nbytes++;
2262 : : }
2263 : 85844 : while (c >= 0x3F || (c & limits[nbytes-1]));
2264 : 42917 : *--p = (c | masks[nbytes-1]);
2265 : : }
2266 : :
2267 : 137808 : pp_append_r (pp, (const char *)p, nbytes);
2268 : 137808 : }
2269 : :
2270 : : /* Append the leading N characters of STRING to the output area of
2271 : : PRETTY-PRINTER, quoting in hexadecimal non-printable characters.
2272 : : Setting N = -1 is as if N were set to strlen (STRING). The STRING
2273 : : may be line-wrapped if in appropriate mode. */
2274 : : static void
2275 : 615709 : pp_quoted_string (pretty_printer *pp, const char *str, size_t n /* = -1 */)
2276 : : {
2277 : 615709 : gcc_checking_assert (str);
2278 : :
2279 : 615709 : const char *last = str;
2280 : 615709 : const char *ps;
2281 : :
2282 : : /* Compute the length if not specified. */
2283 : 615709 : if (n == (size_t) -1)
2284 : 615668 : n = strlen (str);
2285 : :
2286 : 4388992 : for (ps = str; n; ++ps, --n)
2287 : : {
2288 : 3773283 : if (ISPRINT (*ps))
2289 : 3773226 : continue;
2290 : :
2291 : : /* Don't escape a valid UTF-8 extended char. */
2292 : 61 : const unsigned char *ups = (const unsigned char *) ps;
2293 : 61 : if (*ups & 0x80)
2294 : : {
2295 : 25 : unsigned int extended_char;
2296 : 25 : const int valid_utf8_len = decode_utf8_char (ups, n, &extended_char);
2297 : 25 : if (valid_utf8_len > 0)
2298 : : {
2299 : 4 : ps += valid_utf8_len - 1;
2300 : 4 : n -= valid_utf8_len - 1;
2301 : 4 : continue;
2302 : : }
2303 : : }
2304 : :
2305 : 57 : if (last < ps)
2306 : 4 : pp_maybe_wrap_text (pp, last, ps);
2307 : :
2308 : : /* Append the hexadecimal value of the character. Allocate a buffer
2309 : : that's large enough for a 32-bit char plus the hex prefix. */
2310 : 57 : char buf [11];
2311 : 57 : int n = sprintf (buf, "\\x%02x", (unsigned char)*ps);
2312 : 57 : pp_maybe_wrap_text (pp, buf, buf + n);
2313 : 57 : last = ps + 1;
2314 : : }
2315 : :
2316 : 615709 : pp_maybe_wrap_text (pp, last, ps);
2317 : 615709 : }
2318 : :
2319 : : /* Maybe print out a whitespace if needed. */
2320 : :
2321 : : void
2322 : 569705 : pp_maybe_space (pretty_printer *pp)
2323 : : {
2324 : 569705 : if (pp->padding != pp_none)
2325 : : {
2326 : 489806 : pp_space (pp);
2327 : 489806 : pp->padding = pp_none;
2328 : : }
2329 : 569705 : }
2330 : :
2331 : : // Add a newline to the pretty printer PP and flush formatted text.
2332 : :
2333 : : void
2334 : 3080450 : pp_newline_and_flush (pretty_printer *pp)
2335 : : {
2336 : 3080450 : pp_newline (pp);
2337 : 3080450 : pp_flush (pp);
2338 : 3080450 : pp_needs_newline (pp) = false;
2339 : 3080450 : }
2340 : :
2341 : : // Add a newline to the pretty printer PP, followed by indentation.
2342 : :
2343 : : void
2344 : 2157 : pp_newline_and_indent (pretty_printer *pp, int n)
2345 : : {
2346 : 2157 : pp_indentation (pp) += n;
2347 : 2157 : pp_newline (pp);
2348 : 2157 : pp_indent (pp);
2349 : 2157 : pp_needs_newline (pp) = false;
2350 : 2157 : }
2351 : :
2352 : : // Add separator C, followed by a single whitespace.
2353 : :
2354 : : void
2355 : 43867999 : pp_separate_with (pretty_printer *pp, char c)
2356 : : {
2357 : 43867999 : pp_character (pp, c);
2358 : 43867999 : pp_space (pp);
2359 : 43867999 : }
2360 : :
2361 : : /* Add a localized open quote, and if SHOW_COLOR is true, begin colorizing
2362 : : using the "quote" color. */
2363 : :
2364 : : void
2365 : 936932 : pp_begin_quote (pretty_printer *pp, bool show_color)
2366 : : {
2367 : 936932 : pp_string (pp, open_quote);
2368 : 936932 : pp_string (pp, colorize_start (show_color, "quote"));
2369 : 936932 : }
2370 : :
2371 : : /* If SHOW_COLOR is true, stop colorizing.
2372 : : Add a localized close quote. */
2373 : :
2374 : : void
2375 : 937855 : pp_end_quote (pretty_printer *pp, bool show_color)
2376 : : {
2377 : 937855 : pp_string (pp, colorize_stop (show_color));
2378 : 937855 : pp_string (pp, close_quote);
2379 : 937855 : }
2380 : :
2381 : :
2382 : : /* The string starting at P has LEN (at least 1) bytes left; if they
2383 : : start with a valid UTF-8 sequence, return the length of that
2384 : : sequence and set *VALUE to the value of that sequence, and
2385 : : otherwise return 0 and set *VALUE to (unsigned int) -1. */
2386 : :
2387 : : static int
2388 : 3771378574 : decode_utf8_char (const unsigned char *p, size_t len, unsigned int *value)
2389 : : {
2390 : 3771378574 : unsigned int t = *p;
2391 : :
2392 : 3771378574 : if (len == 0)
2393 : 0 : abort ();
2394 : 3771378574 : if (t & 0x80)
2395 : : {
2396 : : size_t utf8_len = 0;
2397 : : unsigned int ch;
2398 : : size_t i;
2399 : 13730 : for (t = *p; t & 0x80; t <<= 1)
2400 : 10150 : utf8_len++;
2401 : :
2402 : 3580 : if (utf8_len > len || utf8_len < 2 || utf8_len > 6)
2403 : : {
2404 : 18 : *value = (unsigned int) -1;
2405 : 18 : return 0;
2406 : : }
2407 : 3562 : ch = *p & ((1 << (7 - utf8_len)) - 1);
2408 : 10094 : for (i = 1; i < utf8_len; i++)
2409 : : {
2410 : 6536 : unsigned int u = p[i];
2411 : 6536 : if ((u & 0xC0) != 0x80)
2412 : : {
2413 : 4 : *value = (unsigned int) -1;
2414 : 4 : return 0;
2415 : : }
2416 : 6532 : ch = (ch << 6) | (u & 0x3F);
2417 : : }
2418 : 3558 : if ( (ch <= 0x7F && utf8_len > 1)
2419 : 3558 : || (ch <= 0x7FF && utf8_len > 2)
2420 : 3558 : || (ch <= 0xFFFF && utf8_len > 3)
2421 : 3558 : || (ch <= 0x1FFFFF && utf8_len > 4)
2422 : 3558 : || (ch <= 0x3FFFFFF && utf8_len > 5)
2423 : 3558 : || (ch >= 0xD800 && ch <= 0xDFFF))
2424 : : {
2425 : 0 : *value = (unsigned int) -1;
2426 : 0 : return 0;
2427 : : }
2428 : 3558 : *value = ch;
2429 : 3558 : return utf8_len;
2430 : : }
2431 : : else
2432 : : {
2433 : 3771374994 : *value = t;
2434 : 3771374994 : return 1;
2435 : : }
2436 : : }
2437 : :
2438 : : /* Allocator for identifier_to_locale and corresponding function to
2439 : : free memory. */
2440 : :
2441 : : void *(*identifier_to_locale_alloc) (size_t) = xmalloc;
2442 : : void (*identifier_to_locale_free) (void *) = free;
2443 : :
2444 : : /* Given IDENT, an identifier in the internal encoding, return a
2445 : : version of IDENT suitable for diagnostics in the locale character
2446 : : set: either IDENT itself, or a string, allocated using
2447 : : identifier_to_locale_alloc, converted to the locale character set
2448 : : and using escape sequences if not representable in the locale
2449 : : character set or containing control characters or invalid byte
2450 : : sequences. Existing backslashes in IDENT are not doubled, so the
2451 : : result may not uniquely specify the contents of an arbitrary byte
2452 : : sequence identifier. */
2453 : :
2454 : : const char *
2455 : 515950553 : identifier_to_locale (const char *ident)
2456 : : {
2457 : 515950553 : const unsigned char *uid = (const unsigned char *) ident;
2458 : 515950553 : size_t idlen = strlen (ident);
2459 : 515950553 : bool valid_printable_utf8 = true;
2460 : 515950553 : bool all_ascii = true;
2461 : 515950553 : size_t i;
2462 : :
2463 : 4287322297 : for (i = 0; i < idlen;)
2464 : : {
2465 : 3771371745 : unsigned int c;
2466 : 3771371745 : size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
2467 : 3771371745 : if (utf8_len == 0 || c <= 0x1F || (c >= 0x7F && c <= 0x9F))
2468 : : {
2469 : 1 : valid_printable_utf8 = false;
2470 : 1 : break;
2471 : : }
2472 : 3771371744 : if (utf8_len > 1)
2473 : 1777 : all_ascii = false;
2474 : 3771371744 : i += utf8_len;
2475 : : }
2476 : :
2477 : : /* If IDENT contains invalid UTF-8 sequences (which may occur with
2478 : : attributes putting arbitrary byte sequences in identifiers), or
2479 : : control characters, we use octal escape sequences for all bytes
2480 : : outside printable ASCII. */
2481 : 515950553 : if (!valid_printable_utf8)
2482 : : {
2483 : 1 : char *ret = (char *) identifier_to_locale_alloc (4 * idlen + 1);
2484 : 1 : char *p = ret;
2485 : 3 : for (i = 0; i < idlen; i++)
2486 : : {
2487 : 1 : if (uid[i] > 0x1F && uid[i] < 0x7F)
2488 : 0 : *p++ = uid[i];
2489 : : else
2490 : : {
2491 : 1 : sprintf (p, "\\%03o", uid[i]);
2492 : 1 : p += 4;
2493 : : }
2494 : : }
2495 : 1 : *p = 0;
2496 : 1 : return ret;
2497 : : }
2498 : :
2499 : : /* Otherwise, if it is valid printable ASCII, or printable UTF-8
2500 : : with the locale character set being UTF-8, IDENT is used. */
2501 : 515950552 : if (all_ascii || locale_utf8)
2502 : : return ident;
2503 : :
2504 : : /* Otherwise IDENT is converted to the locale character set if
2505 : : possible. */
2506 : : #if defined ENABLE_NLS && defined HAVE_LANGINFO_CODESET && HAVE_ICONV
2507 : 797 : if (locale_encoding != NULL)
2508 : : {
2509 : 797 : iconv_t cd = iconv_open (locale_encoding, "UTF-8");
2510 : 797 : bool conversion_ok = true;
2511 : 797 : char *ret = NULL;
2512 : 797 : if (cd != (iconv_t) -1)
2513 : : {
2514 : 797 : size_t ret_alloc = 4 * idlen + 1;
2515 : 797 : for (;;)
2516 : : {
2517 : : /* Repeat the whole conversion process as needed with
2518 : : larger buffers so non-reversible transformations can
2519 : : always be detected. */
2520 : 797 : ICONV_CONST char *inbuf = CONST_CAST (char *, ident);
2521 : 797 : char *outbuf;
2522 : 797 : size_t inbytesleft = idlen;
2523 : 797 : size_t outbytesleft = ret_alloc - 1;
2524 : 797 : size_t iconv_ret;
2525 : :
2526 : 797 : ret = (char *) identifier_to_locale_alloc (ret_alloc);
2527 : 797 : outbuf = ret;
2528 : :
2529 : 797 : if (iconv (cd, 0, 0, 0, 0) == (size_t) -1)
2530 : : {
2531 : : conversion_ok = false;
2532 : 797 : break;
2533 : : }
2534 : :
2535 : 797 : iconv_ret = iconv (cd, &inbuf, &inbytesleft,
2536 : : &outbuf, &outbytesleft);
2537 : 797 : if (iconv_ret == (size_t) -1 || inbytesleft != 0)
2538 : : {
2539 : 797 : if (errno == E2BIG)
2540 : : {
2541 : 0 : ret_alloc *= 2;
2542 : 0 : identifier_to_locale_free (ret);
2543 : 0 : ret = NULL;
2544 : 0 : continue;
2545 : : }
2546 : : else
2547 : : {
2548 : : conversion_ok = false;
2549 : : break;
2550 : : }
2551 : : }
2552 : 0 : else if (iconv_ret != 0)
2553 : : {
2554 : : conversion_ok = false;
2555 : : break;
2556 : : }
2557 : : /* Return to initial shift state. */
2558 : 0 : if (iconv (cd, 0, 0, &outbuf, &outbytesleft) == (size_t) -1)
2559 : : {
2560 : 0 : if (errno == E2BIG)
2561 : : {
2562 : 0 : ret_alloc *= 2;
2563 : 0 : identifier_to_locale_free (ret);
2564 : 0 : ret = NULL;
2565 : 0 : continue;
2566 : : }
2567 : : else
2568 : : {
2569 : : conversion_ok = false;
2570 : : break;
2571 : : }
2572 : : }
2573 : 0 : *outbuf = 0;
2574 : 0 : break;
2575 : 0 : }
2576 : 797 : iconv_close (cd);
2577 : 797 : if (conversion_ok)
2578 : : return ret;
2579 : : }
2580 : : }
2581 : : #endif
2582 : :
2583 : : /* Otherwise, convert non-ASCII characters in IDENT to UCNs. */
2584 : 797 : {
2585 : 797 : char *ret = (char *) identifier_to_locale_alloc (10 * idlen + 1);
2586 : 797 : char *p = ret;
2587 : 7601 : for (i = 0; i < idlen;)
2588 : : {
2589 : 6804 : unsigned int c;
2590 : 6804 : size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
2591 : 6804 : if (utf8_len == 1)
2592 : 5027 : *p++ = uid[i];
2593 : : else
2594 : : {
2595 : 1777 : sprintf (p, "\\U%08x", c);
2596 : 1777 : p += 10;
2597 : : }
2598 : 6804 : i += utf8_len;
2599 : : }
2600 : 797 : *p = 0;
2601 : 797 : return ret;
2602 : : }
2603 : : }
2604 : :
2605 : : /* Support for encoding URLs.
2606 : : See egmontkob/Hyperlinks_in_Terminal_Emulators.md
2607 : : ( https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda ).
2608 : :
2609 : : > A hyperlink is opened upon encountering an OSC 8 escape sequence with
2610 : : > the target URI. The syntax is
2611 : : >
2612 : : > OSC 8 ; params ; URI ST
2613 : : >
2614 : : > A hyperlink is closed with the same escape sequence, omitting the
2615 : : > parameters and the URI but keeping the separators:
2616 : : >
2617 : : > OSC 8 ; ; ST
2618 : : >
2619 : : > OSC (operating system command) is typically ESC ].
2620 : :
2621 : : Use BEL instead of ST, as that is currently rendered better in some
2622 : : terminal emulators that don't support OSC 8, like konsole. */
2623 : :
2624 : : /* If URL-printing is enabled, write an "open URL" escape sequence to PP
2625 : : for the given URL. */
2626 : :
2627 : : void
2628 : 32 : pp_begin_url (pretty_printer *pp, const char *url)
2629 : : {
2630 : 32 : if (!url)
2631 : : {
2632 : : /* Handle null URL by skipping all output here,
2633 : : and in the next pp_end_url. */
2634 : 12 : pp->m_skipping_null_url = true;
2635 : 12 : return;
2636 : : }
2637 : 20 : switch (pp->url_format)
2638 : : {
2639 : : case URL_FORMAT_NONE:
2640 : : break;
2641 : 4 : case URL_FORMAT_ST:
2642 : 4 : pp_string (pp, "\33]8;;");
2643 : 4 : pp_string (pp, url);
2644 : 4 : pp_string (pp, "\33\\");
2645 : 4 : break;
2646 : 4 : case URL_FORMAT_BEL:
2647 : 4 : pp_string (pp, "\33]8;;");
2648 : 4 : pp_string (pp, url);
2649 : 4 : pp_string (pp, "\a");
2650 : 4 : break;
2651 : 0 : default:
2652 : 0 : gcc_unreachable ();
2653 : : }
2654 : : }
2655 : :
2656 : : /* Helper function for pp_end_url and pp_format, return the "close URL" escape
2657 : : sequence string. */
2658 : :
2659 : : static const char *
2660 : 76 : get_end_url_string (pretty_printer *pp)
2661 : : {
2662 : 76 : switch (pp->url_format)
2663 : : {
2664 : : case URL_FORMAT_NONE:
2665 : : return "";
2666 : 52 : case URL_FORMAT_ST:
2667 : 52 : return "\33]8;;\33\\";
2668 : 16 : case URL_FORMAT_BEL:
2669 : 16 : return "\33]8;;\a";
2670 : 0 : default:
2671 : 0 : gcc_unreachable ();
2672 : : }
2673 : : }
2674 : :
2675 : : /* If URL-printing is enabled, write a "close URL" escape sequence to PP. */
2676 : :
2677 : : void
2678 : 48 : pp_end_url (pretty_printer *pp)
2679 : : {
2680 : 48 : if (pp->m_skipping_null_url)
2681 : : {
2682 : : /* We gracefully handle pp_begin_url (NULL) by omitting output for
2683 : : both begin and end. Here we handle the latter. */
2684 : 12 : pp->m_skipping_null_url = false;
2685 : 12 : return;
2686 : : }
2687 : 36 : if (pp->url_format != URL_FORMAT_NONE)
2688 : 24 : pp_string (pp, get_end_url_string (pp));
2689 : : }
2690 : :
2691 : : #if CHECKING_P
2692 : :
2693 : : namespace selftest {
2694 : :
2695 : : /* Smoketest for pretty_printer. */
2696 : :
2697 : : static void
2698 : 4 : test_basic_printing ()
2699 : : {
2700 : 4 : pretty_printer pp;
2701 : 4 : pp_string (&pp, "hello");
2702 : 4 : pp_space (&pp);
2703 : 4 : pp_string (&pp, "world");
2704 : :
2705 : 4 : ASSERT_STREQ ("hello world", pp_formatted_text (&pp));
2706 : 4 : }
2707 : :
2708 : : /* Helper function for testing pp_format.
2709 : : Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
2710 : : prints EXPECTED, assuming that pp_show_color is SHOW_COLOR. */
2711 : :
2712 : : static void
2713 : 228 : assert_pp_format_va (const location &loc, const char *expected,
2714 : : bool show_color, const char *fmt, va_list *ap)
2715 : : {
2716 : 228 : pretty_printer pp;
2717 : 228 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
2718 : :
2719 : 228 : text_info ti (fmt, ap, 0, nullptr, &rich_loc);
2720 : :
2721 : 228 : pp_show_color (&pp) = show_color;
2722 : 228 : pp_format (&pp, &ti);
2723 : 228 : pp_output_formatted_text (&pp);
2724 : 228 : ASSERT_STREQ_AT (loc, expected, pp_formatted_text (&pp));
2725 : 228 : }
2726 : :
2727 : : /* Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
2728 : : prints EXPECTED, with show_color disabled. */
2729 : :
2730 : : static void
2731 : 216 : assert_pp_format (const location &loc, const char *expected,
2732 : : const char *fmt, ...)
2733 : : {
2734 : 216 : va_list ap;
2735 : :
2736 : 216 : va_start (ap, fmt);
2737 : 216 : assert_pp_format_va (loc, expected, false, fmt, &ap);
2738 : 216 : va_end (ap);
2739 : 216 : }
2740 : :
2741 : : /* As above, but with colorization enabled. */
2742 : :
2743 : : static void
2744 : 12 : assert_pp_format_colored (const location &loc, const char *expected,
2745 : : const char *fmt, ...)
2746 : : {
2747 : : /* The tests of colorization assume the default color scheme.
2748 : : If GCC_COLORS is set, then the colors have potentially been
2749 : : overridden; skip the test. */
2750 : 12 : if (getenv ("GCC_COLORS"))
2751 : 0 : return;
2752 : :
2753 : 12 : va_list ap;
2754 : :
2755 : 12 : va_start (ap, fmt);
2756 : 12 : assert_pp_format_va (loc, expected, true, fmt, &ap);
2757 : 12 : va_end (ap);
2758 : : }
2759 : :
2760 : : /* Helper function for calling testing pp_format,
2761 : : by calling assert_pp_format with various numbers of arguments.
2762 : : These exist mostly to avoid having to write SELFTEST_LOCATION
2763 : : throughout test_pp_format. */
2764 : :
2765 : : #define ASSERT_PP_FORMAT_1(EXPECTED, FMT, ARG1) \
2766 : : SELFTEST_BEGIN_STMT \
2767 : : assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
2768 : : (ARG1)); \
2769 : : SELFTEST_END_STMT
2770 : :
2771 : : #define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \
2772 : : SELFTEST_BEGIN_STMT \
2773 : : assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
2774 : : (ARG1), (ARG2)); \
2775 : : SELFTEST_END_STMT
2776 : :
2777 : : #define ASSERT_PP_FORMAT_3(EXPECTED, FMT, ARG1, ARG2, ARG3) \
2778 : : SELFTEST_BEGIN_STMT \
2779 : : assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
2780 : : (ARG1), (ARG2), (ARG3)); \
2781 : : SELFTEST_END_STMT
2782 : :
2783 : : /* Verify that pp_format works, for various format codes. */
2784 : :
2785 : : static void
2786 : 4 : test_pp_format ()
2787 : : {
2788 : : /* Avoid introducing locale-specific differences in the results
2789 : : by hardcoding open_quote and close_quote. */
2790 : 4 : auto_fix_quotes fix_quotes;
2791 : :
2792 : : /* Verify that plain text is passed through unchanged. */
2793 : 4 : assert_pp_format (SELFTEST_LOCATION, "unformatted", "unformatted");
2794 : :
2795 : : /* Verify various individual format codes, in the order listed in the
2796 : : comment for pp_format above. For each code, we append a second
2797 : : argument with a known bit pattern (0x12345678), to ensure that we
2798 : : are consuming arguments correctly. */
2799 : 4 : ASSERT_PP_FORMAT_2 ("-27 12345678", "%d %x", -27, 0x12345678);
2800 : 4 : ASSERT_PP_FORMAT_2 ("-5 12345678", "%i %x", -5, 0x12345678);
2801 : 4 : ASSERT_PP_FORMAT_2 ("10 12345678", "%u %x", 10, 0x12345678);
2802 : 4 : ASSERT_PP_FORMAT_2 ("17 12345678", "%o %x", 15, 0x12345678);
2803 : 4 : ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678);
2804 : 4 : ASSERT_PP_FORMAT_2 ("-27 12345678", "%ld %x", (long)-27, 0x12345678);
2805 : 4 : ASSERT_PP_FORMAT_2 ("-5 12345678", "%li %x", (long)-5, 0x12345678);
2806 : 4 : ASSERT_PP_FORMAT_2 ("10 12345678", "%lu %x", (long)10, 0x12345678);
2807 : 4 : ASSERT_PP_FORMAT_2 ("17 12345678", "%lo %x", (long)15, 0x12345678);
2808 : 4 : ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%lx %x", (long)0xcafebabe,
2809 : : 0x12345678);
2810 : 4 : ASSERT_PP_FORMAT_2 ("-27 12345678", "%lld %x", (long long)-27, 0x12345678);
2811 : 4 : ASSERT_PP_FORMAT_2 ("-5 12345678", "%lli %x", (long long)-5, 0x12345678);
2812 : 4 : ASSERT_PP_FORMAT_2 ("10 12345678", "%llu %x", (long long)10, 0x12345678);
2813 : 4 : ASSERT_PP_FORMAT_2 ("17 12345678", "%llo %x", (long long)15, 0x12345678);
2814 : 4 : ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe,
2815 : : 0x12345678);
2816 : 4 : ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", HOST_WIDE_INT_C (-27),
2817 : : 0x12345678);
2818 : 4 : ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", HOST_WIDE_INT_C (-5),
2819 : : 0x12345678);
2820 : 4 : ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", HOST_WIDE_INT_UC (10),
2821 : : 0x12345678);
2822 : 4 : ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", HOST_WIDE_INT_C (15),
2823 : : 0x12345678);
2824 : 4 : ASSERT_PP_FORMAT_2 ("0xcafebabe 12345678", "%wx %x",
2825 : : HOST_WIDE_INT_C (0xcafebabe), 0x12345678);
2826 : 4 : ASSERT_PP_FORMAT_2 ("-27 12345678", "%zd %x", (ssize_t)-27, 0x12345678);
2827 : 4 : ASSERT_PP_FORMAT_2 ("-5 12345678", "%zi %x", (ssize_t)-5, 0x12345678);
2828 : 4 : ASSERT_PP_FORMAT_2 ("10 12345678", "%zu %x", (size_t)10, 0x12345678);
2829 : 4 : ASSERT_PP_FORMAT_2 ("17 12345678", "%zo %x", (size_t)15, 0x12345678);
2830 : 4 : ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%zx %x", (size_t)0xcafebabe,
2831 : : 0x12345678);
2832 : 4 : ASSERT_PP_FORMAT_2 ("-27 12345678", "%td %x", (ptrdiff_t)-27, 0x12345678);
2833 : 4 : ASSERT_PP_FORMAT_2 ("-5 12345678", "%ti %x", (ptrdiff_t)-5, 0x12345678);
2834 : 4 : ASSERT_PP_FORMAT_2 ("10 12345678", "%tu %x", (ptrdiff_t)10, 0x12345678);
2835 : 4 : ASSERT_PP_FORMAT_2 ("17 12345678", "%to %x", (ptrdiff_t)15, 0x12345678);
2836 : 4 : ASSERT_PP_FORMAT_2 ("1afebabe 12345678", "%tx %x", (ptrdiff_t)0x1afebabe,
2837 : : 0x12345678);
2838 : 4 : ASSERT_PP_FORMAT_2 ("1.000000 12345678", "%f %x", 1.0, 0x12345678);
2839 : 4 : ASSERT_PP_FORMAT_2 ("A 12345678", "%c %x", 'A', 0x12345678);
2840 : 4 : ASSERT_PP_FORMAT_2 ("hello world 12345678", "%s %x", "hello world",
2841 : : 0x12345678);
2842 : :
2843 : : /* Not nul-terminated. */
2844 : 4 : char arr[5] = { '1', '2', '3', '4', '5' };
2845 : 4 : ASSERT_PP_FORMAT_3 ("123 12345678", "%.*s %x", 3, arr, 0x12345678);
2846 : 4 : ASSERT_PP_FORMAT_3 ("1234 12345678", "%.*s %x", -1, "1234", 0x12345678);
2847 : 4 : ASSERT_PP_FORMAT_3 ("12345 12345678", "%.*s %x", 7, "12345", 0x12345678);
2848 : :
2849 : : /* We can't test for %p; the pointer is printed in an implementation-defined
2850 : : manner. */
2851 : 4 : ASSERT_PP_FORMAT_2 ("normal colored normal 12345678",
2852 : : "normal %rcolored%R normal %x",
2853 : : "error", 0x12345678);
2854 : 4 : assert_pp_format_colored
2855 : 4 : (SELFTEST_LOCATION,
2856 : : "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678",
2857 : : "normal %rcolored%R normal %x", "error", 0x12345678);
2858 : : /* TODO:
2859 : : %m: strerror(text->err_no) - does not consume a value from args_ptr. */
2860 : 4 : ASSERT_PP_FORMAT_1 ("% 12345678", "%% %x", 0x12345678);
2861 : 4 : ASSERT_PP_FORMAT_1 ("` 12345678", "%< %x", 0x12345678);
2862 : 4 : ASSERT_PP_FORMAT_1 ("' 12345678", "%> %x", 0x12345678);
2863 : 4 : ASSERT_PP_FORMAT_1 ("' 12345678", "%' %x", 0x12345678);
2864 : 4 : ASSERT_PP_FORMAT_3 ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678);
2865 : 4 : ASSERT_PP_FORMAT_2 ("abc 12345678", "%.3s %x", "abcdef", 0x12345678);
2866 : :
2867 : : /* Verify flag 'q'. */
2868 : 4 : ASSERT_PP_FORMAT_2 ("`foo' 12345678", "%qs %x", "foo", 0x12345678);
2869 : 4 : assert_pp_format_colored (SELFTEST_LOCATION,
2870 : : "`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x",
2871 : : "foo", 0x12345678);
2872 : : /* Verify "%@". */
2873 : 4 : {
2874 : 4 : diagnostic_event_id_t first (2);
2875 : 4 : diagnostic_event_id_t second (7);
2876 : :
2877 : 4 : ASSERT_PP_FORMAT_2 ("first `free' at (3); second `free' at (8)",
2878 : : "first %<free%> at %@; second %<free%> at %@",
2879 : : &first, &second);
2880 : 4 : assert_pp_format_colored
2881 : 4 : (SELFTEST_LOCATION,
2882 : : "first `[01m[Kfree[m[K' at [01;36m[K(3)[m[K;"
2883 : : " second `[01m[Kfree[m[K' at [01;36m[K(8)[m[K",
2884 : : "first %<free%> at %@; second %<free%> at %@",
2885 : : &first, &second);
2886 : : }
2887 : :
2888 : : /* Verify %Z. */
2889 : 4 : int v[] = { 1, 2, 3 };
2890 : 4 : ASSERT_PP_FORMAT_3 ("1, 2, 3 12345678", "%Z %x", v, 3, 0x12345678);
2891 : :
2892 : 4 : int v2[] = { 0 };
2893 : 4 : ASSERT_PP_FORMAT_3 ("0 12345678", "%Z %x", v2, 1, 0x12345678);
2894 : :
2895 : : /* Verify that combinations work, along with unformatted text. */
2896 : 4 : assert_pp_format (SELFTEST_LOCATION,
2897 : : "the quick brown fox jumps over the lazy dog",
2898 : : "the %s %s %s jumps over the %s %s",
2899 : : "quick", "brown", "fox", "lazy", "dog");
2900 : 4 : assert_pp_format (SELFTEST_LOCATION, "item 3 of 7", "item %i of %i", 3, 7);
2901 : 4 : assert_pp_format (SELFTEST_LOCATION, "problem with `bar' at line 10",
2902 : : "problem with %qs at line %i", "bar", 10);
2903 : :
2904 : : /* Verified numbered args. */
2905 : 4 : assert_pp_format (SELFTEST_LOCATION,
2906 : : "foo: second bar: first",
2907 : : "foo: %2$s bar: %1$s",
2908 : : "first", "second");
2909 : 4 : assert_pp_format (SELFTEST_LOCATION,
2910 : : "foo: 1066 bar: 1776",
2911 : : "foo: %2$i bar: %1$i",
2912 : : 1776, 1066);
2913 : 4 : assert_pp_format (SELFTEST_LOCATION,
2914 : : "foo: second bar: 1776",
2915 : : "foo: %2$s bar: %1$i",
2916 : : 1776, "second");
2917 : 4 : }
2918 : :
2919 : : /* A subclass of pretty_printer for use by test_prefixes_and_wrapping. */
2920 : :
2921 : 24 : class test_pretty_printer : public pretty_printer
2922 : : {
2923 : : public:
2924 : 24 : test_pretty_printer (enum diagnostic_prefixing_rule_t rule,
2925 : : int max_line_length)
2926 : 24 : {
2927 : 24 : pp_set_prefix (this, xstrdup ("PREFIX: "));
2928 : 24 : wrapping.rule = rule;
2929 : 24 : pp_set_line_maximum_length (this, max_line_length);
2930 : 24 : }
2931 : : };
2932 : :
2933 : : /* Verify that the various values of enum diagnostic_prefixing_rule_t work
2934 : : as expected, with and without line wrapping. */
2935 : :
2936 : : static void
2937 : 4 : test_prefixes_and_wrapping ()
2938 : : {
2939 : : /* Tests of the various prefixing rules, without wrapping.
2940 : : Newlines embedded in pp_string don't affect it; we have to
2941 : : explicitly call pp_newline. */
2942 : 4 : {
2943 : 4 : test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 0);
2944 : 4 : pp_string (&pp, "the quick brown fox");
2945 : 4 : pp_newline (&pp);
2946 : 4 : pp_string (&pp, "jumps over the lazy dog");
2947 : 4 : pp_newline (&pp);
2948 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
2949 : : "PREFIX: the quick brown fox\n"
2950 : : " jumps over the lazy dog\n");
2951 : 4 : }
2952 : 4 : {
2953 : 4 : test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 0);
2954 : 4 : pp_string (&pp, "the quick brown fox");
2955 : 4 : pp_newline (&pp);
2956 : 4 : pp_string (&pp, "jumps over the lazy dog");
2957 : 4 : pp_newline (&pp);
2958 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
2959 : : "the quick brown fox\n"
2960 : : "jumps over the lazy dog\n");
2961 : 4 : }
2962 : 4 : {
2963 : 4 : test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 0);
2964 : 4 : pp_string (&pp, "the quick brown fox");
2965 : 4 : pp_newline (&pp);
2966 : 4 : pp_string (&pp, "jumps over the lazy dog");
2967 : 4 : pp_newline (&pp);
2968 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
2969 : : "PREFIX: the quick brown fox\n"
2970 : : "PREFIX: jumps over the lazy dog\n");
2971 : 4 : }
2972 : :
2973 : : /* Tests of the various prefixing rules, with wrapping. */
2974 : 4 : {
2975 : 4 : test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 20);
2976 : 4 : pp_string (&pp, "the quick brown fox jumps over the lazy dog");
2977 : 4 : pp_newline (&pp);
2978 : 4 : pp_string (&pp, "able was I ere I saw elba");
2979 : 4 : pp_newline (&pp);
2980 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
2981 : : "PREFIX: the quick \n"
2982 : : " brown fox jumps \n"
2983 : : " over the lazy \n"
2984 : : " dog\n"
2985 : : " able was I ere I \n"
2986 : : " saw elba\n");
2987 : 4 : }
2988 : 4 : {
2989 : 4 : test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 20);
2990 : 4 : pp_string (&pp, "the quick brown fox jumps over the lazy dog");
2991 : 4 : pp_newline (&pp);
2992 : 4 : pp_string (&pp, "able was I ere I saw elba");
2993 : 4 : pp_newline (&pp);
2994 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
2995 : : "the quick brown fox \n"
2996 : : "jumps over the lazy \n"
2997 : : "dog\n"
2998 : : "able was I ere I \n"
2999 : : "saw elba\n");
3000 : 4 : }
3001 : 4 : {
3002 : 4 : test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 20);
3003 : 4 : pp_string (&pp, "the quick brown fox jumps over the lazy dog");
3004 : 4 : pp_newline (&pp);
3005 : 4 : pp_string (&pp, "able was I ere I saw elba");
3006 : 4 : pp_newline (&pp);
3007 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
3008 : : "PREFIX: the quick brown fox jumps over the lazy dog\n"
3009 : : "PREFIX: able was I ere I saw elba\n");
3010 : 4 : }
3011 : :
3012 : 4 : }
3013 : :
3014 : : /* Verify that URL-printing works as expected. */
3015 : :
3016 : : void
3017 : 4 : test_urls ()
3018 : : {
3019 : 4 : {
3020 : 4 : pretty_printer pp;
3021 : 4 : pp.url_format = URL_FORMAT_NONE;
3022 : 4 : pp_begin_url (&pp, "http://example.com");
3023 : 4 : pp_string (&pp, "This is a link");
3024 : 4 : pp_end_url (&pp);
3025 : 4 : ASSERT_STREQ ("This is a link",
3026 : : pp_formatted_text (&pp));
3027 : 4 : }
3028 : :
3029 : 4 : {
3030 : 4 : pretty_printer pp;
3031 : 4 : pp.url_format = URL_FORMAT_ST;
3032 : 4 : pp_begin_url (&pp, "http://example.com");
3033 : 4 : pp_string (&pp, "This is a link");
3034 : 4 : pp_end_url (&pp);
3035 : 4 : ASSERT_STREQ ("\33]8;;http://example.com\33\\This is a link\33]8;;\33\\",
3036 : : pp_formatted_text (&pp));
3037 : 4 : }
3038 : :
3039 : 4 : {
3040 : 4 : pretty_printer pp;
3041 : 4 : pp.url_format = URL_FORMAT_BEL;
3042 : 4 : pp_begin_url (&pp, "http://example.com");
3043 : 4 : pp_string (&pp, "This is a link");
3044 : 4 : pp_end_url (&pp);
3045 : 4 : ASSERT_STREQ ("\33]8;;http://example.com\aThis is a link\33]8;;\a",
3046 : : pp_formatted_text (&pp));
3047 : 4 : }
3048 : 4 : }
3049 : :
3050 : : /* Verify that we gracefully reject null URLs. */
3051 : :
3052 : : void
3053 : 4 : test_null_urls ()
3054 : : {
3055 : 4 : {
3056 : 4 : pretty_printer pp;
3057 : 4 : pp.url_format = URL_FORMAT_NONE;
3058 : 4 : pp_begin_url (&pp, nullptr);
3059 : 4 : pp_string (&pp, "This isn't a link");
3060 : 4 : pp_end_url (&pp);
3061 : 4 : ASSERT_STREQ ("This isn't a link",
3062 : : pp_formatted_text (&pp));
3063 : 4 : }
3064 : :
3065 : 4 : {
3066 : 4 : pretty_printer pp;
3067 : 4 : pp.url_format = URL_FORMAT_ST;
3068 : 4 : pp_begin_url (&pp, nullptr);
3069 : 4 : pp_string (&pp, "This isn't a link");
3070 : 4 : pp_end_url (&pp);
3071 : 4 : ASSERT_STREQ ("This isn't a link",
3072 : : pp_formatted_text (&pp));
3073 : 4 : }
3074 : :
3075 : 4 : {
3076 : 4 : pretty_printer pp;
3077 : 4 : pp.url_format = URL_FORMAT_BEL;
3078 : 4 : pp_begin_url (&pp, nullptr);
3079 : 4 : pp_string (&pp, "This isn't a link");
3080 : 4 : pp_end_url (&pp);
3081 : 4 : ASSERT_STREQ ("This isn't a link",
3082 : : pp_formatted_text (&pp));
3083 : 4 : }
3084 : 4 : }
3085 : :
3086 : : /* Verify that URLification works as expected. */
3087 : :
3088 : : static void
3089 : 40 : pp_printf_with_urlifier (pretty_printer *pp,
3090 : : const urlifier *urlifier,
3091 : : const char *msg, ...)
3092 : : {
3093 : 40 : va_list ap;
3094 : :
3095 : 40 : va_start (ap, msg);
3096 : 40 : text_info text (msg, &ap, errno);
3097 : 40 : pp_format (pp, &text, urlifier);
3098 : 40 : pp_output_formatted_text (pp, urlifier);
3099 : 40 : va_end (ap);
3100 : 40 : }
3101 : :
3102 : :
3103 : : void
3104 : 4 : test_urlification ()
3105 : : {
3106 : 4 : class test_urlifier : public urlifier
3107 : : {
3108 : : public:
3109 : : char *
3110 : 56 : get_url_for_quoted_text (const char *p, size_t sz) const final override
3111 : : {
3112 : 56 : if (!strncmp (p, "-foption", sz))
3113 : 44 : return xstrdup ("http://example.com");
3114 : : return nullptr;
3115 : : }
3116 : : };
3117 : :
3118 : 4 : auto_fix_quotes fix_quotes;
3119 : 4 : const test_urlifier urlifier;
3120 : :
3121 : : /* Uses of "%<" and "%>". */
3122 : 4 : {
3123 : 4 : {
3124 : 4 : pretty_printer pp;
3125 : 4 : pp.url_format = URL_FORMAT_NONE;
3126 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3127 : : "foo %<-foption%> %<unrecognized%> bar");
3128 : 4 : ASSERT_STREQ ("foo `-foption' `unrecognized' bar",
3129 : : pp_formatted_text (&pp));
3130 : 4 : }
3131 : 4 : {
3132 : 4 : pretty_printer pp;
3133 : 4 : pp.url_format = URL_FORMAT_ST;
3134 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3135 : : "foo %<-foption%> %<unrecognized%> bar");
3136 : 4 : ASSERT_STREQ
3137 : : ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
3138 : : " `unrecognized' bar",
3139 : : pp_formatted_text (&pp));
3140 : 4 : }
3141 : 4 : {
3142 : 4 : pretty_printer pp;
3143 : 4 : pp.url_format = URL_FORMAT_BEL;
3144 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3145 : : "foo %<-foption%> %<unrecognized%> bar");
3146 : 4 : ASSERT_STREQ
3147 : : ("foo `\33]8;;http://example.com\a-foption\33]8;;\a'"
3148 : : " `unrecognized' bar",
3149 : : pp_formatted_text (&pp));
3150 : 4 : }
3151 : : }
3152 : :
3153 : : /* Use of "%qs". */
3154 : 4 : {
3155 : 4 : pretty_printer pp;
3156 : 4 : pp.url_format = URL_FORMAT_ST;
3157 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3158 : : "foo %qs %qs bar",
3159 : : "-foption", "unrecognized");
3160 : 4 : ASSERT_STREQ
3161 : : ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
3162 : : " `unrecognized' bar",
3163 : : pp_formatted_text (&pp));
3164 : 4 : }
3165 : :
3166 : : /* Mixed usage of %< and %s, where the quoted string is built between
3167 : : a mixture of phase 1 and phase 2. */
3168 : 4 : {
3169 : 4 : pretty_printer pp;
3170 : 4 : pp.url_format = URL_FORMAT_ST;
3171 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3172 : : "foo %<-f%s%> bar",
3173 : : "option");
3174 : 4 : ASSERT_STREQ
3175 : : ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3176 : : pp_formatted_text (&pp));
3177 : 4 : }
3178 : :
3179 : : /* Likewise, where there is trailing phase 1 content within the
3180 : : quoted region. */
3181 : 4 : {
3182 : 4 : pretty_printer pp;
3183 : 4 : pp.url_format = URL_FORMAT_ST;
3184 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3185 : : "foo %<-f%sion%> bar %<-f%sion%> baz",
3186 : : "opt", "opt");
3187 : 4 : ASSERT_STREQ
3188 : : ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
3189 : : pp_formatted_text (&pp));
3190 : 4 : }
3191 : :
3192 : : /* Likewise. */
3193 : 4 : {
3194 : 4 : pretty_printer pp;
3195 : 4 : pp.url_format = URL_FORMAT_ST;
3196 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3197 : : "foo %<%sption%> bar %<-f%sion%> baz",
3198 : : "-fo", "opt");
3199 : 4 : ASSERT_STREQ
3200 : : ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
3201 : : pp_formatted_text (&pp));
3202 : 4 : }
3203 : :
3204 : : /* Another mixed usage of %< and %s, where the quoted string is built
3205 : : between a mixture of phase 1 and multiple phase 2. */
3206 : 4 : {
3207 : 4 : pretty_printer pp;
3208 : 4 : pp.url_format = URL_FORMAT_ST;
3209 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3210 : : "foo %<-f%s%s%> bar",
3211 : : "opt", "ion");
3212 : 4 : ASSERT_STREQ
3213 : : ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3214 : : pp_formatted_text (&pp));
3215 : 4 : }
3216 : :
3217 : : /* Mixed usage of %< and %s with a prefix. */
3218 : 4 : {
3219 : 4 : pretty_printer pp;
3220 : 4 : pp.url_format = URL_FORMAT_ST;
3221 : 4 : pp_set_prefix (&pp, xstrdup ("PREFIX"));
3222 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3223 : : "foo %<-f%s%> bar",
3224 : : "option");
3225 : 4 : ASSERT_STREQ
3226 : : ("PREFIXfoo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3227 : : pp_formatted_text (&pp));
3228 : 4 : }
3229 : :
3230 : : /* Example of mixed %< and %s with numbered args. */
3231 : 4 : {
3232 : 4 : pretty_printer pp;
3233 : 4 : pp.url_format = URL_FORMAT_ST;
3234 : 4 : pp_printf_with_urlifier (&pp, &urlifier,
3235 : : "foo %<-f%2$st%1$sn%> bar",
3236 : : "io", "op");
3237 : 4 : ASSERT_STREQ
3238 : : ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
3239 : : pp_formatted_text (&pp));
3240 : 4 : }
3241 : 4 : }
3242 : :
3243 : : /* Test multibyte awareness. */
3244 : 4 : static void test_utf8 ()
3245 : : {
3246 : :
3247 : : /* Check that pp_quoted_string leaves valid UTF-8 alone. */
3248 : 4 : {
3249 : 4 : pretty_printer pp;
3250 : 4 : const char *s = "\xf0\x9f\x98\x82";
3251 : 4 : pp_quoted_string (&pp, s);
3252 : 4 : ASSERT_STREQ (pp_formatted_text (&pp), s);
3253 : 4 : }
3254 : :
3255 : : /* Check that pp_quoted_string escapes non-UTF-8 nonprintable bytes. */
3256 : 4 : {
3257 : 4 : pretty_printer pp;
3258 : 4 : pp_quoted_string (&pp, "\xf0!\x9f\x98\x82");
3259 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
3260 : : "\\xf0!\\x9f\\x98\\x82");
3261 : 4 : }
3262 : :
3263 : : /* Check that pp_character will line-wrap at the beginning of a UTF-8
3264 : : sequence, but not in the middle. */
3265 : 4 : {
3266 : 4 : pretty_printer pp (3);
3267 : 4 : const char s[] = "---\xf0\x9f\x98\x82";
3268 : 32 : for (int i = 0; i != sizeof (s) - 1; ++i)
3269 : 28 : pp_character (&pp, s[i]);
3270 : 4 : pp_newline (&pp);
3271 : 28 : for (int i = 1; i != sizeof (s) - 1; ++i)
3272 : 24 : pp_character (&pp, s[i]);
3273 : 4 : pp_character (&pp, '-');
3274 : 4 : ASSERT_STREQ (pp_formatted_text (&pp),
3275 : : "---\n"
3276 : : "\xf0\x9f\x98\x82\n"
3277 : : "--\xf0\x9f\x98\x82\n"
3278 : : "-");
3279 : 4 : }
3280 : :
3281 : 4 : }
3282 : :
3283 : : /* Run all of the selftests within this file. */
3284 : :
3285 : : void
3286 : 4 : pretty_print_cc_tests ()
3287 : : {
3288 : 4 : test_basic_printing ();
3289 : 4 : test_pp_format ();
3290 : 4 : test_prefixes_and_wrapping ();
3291 : 4 : test_urls ();
3292 : 4 : test_null_urls ();
3293 : 4 : test_urlification ();
3294 : 4 : test_utf8 ();
3295 : 4 : }
3296 : :
3297 : : } // namespace selftest
3298 : :
3299 : : #endif /* CHECKING_P */
|