Mercurial > hg > CbC > CbC_gcc
diff gcc/pretty-print.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | f6334be47118 |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/gcc/pretty-print.c Sun Aug 21 07:07:55 2011 +0900 +++ b/gcc/pretty-print.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,6 +1,5 @@ /* Various declarations for language-independent pretty-print subroutines. - Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010 - Free Software Foundation, Inc. + Copyright (C) 2003-2017 Free Software Foundation, Inc. Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net> This file is part of GCC. @@ -24,14 +23,720 @@ #include "coretypes.h" #include "intl.h" #include "pretty-print.h" +#include "diagnostic-color.h" +#include "selftest.h" #if HAVE_ICONV #include <iconv.h> #endif -/* A pointer to the formatted diagnostic message. */ -#define pp_formatted_text_data(PP) \ - ((const char *) obstack_base (pp_base (PP)->buffer->obstack)) +#ifdef __MINGW32__ + +/* Replacement for fputs() that handles ANSI escape codes on Windows NT. + Contributed by: Liu Hao (lh_mouse at 126 dot com) + + XXX: This file is compiled into libcommon.a that will be self-contained. + It looks like that these functions can be put nowhere else. */ + +#include <io.h> +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +/* Write all bytes in [s,s+n) into the specified stream. + Errors are ignored. */ +static void +write_all (HANDLE h, const char *s, size_t n) +{ + size_t rem = n; + DWORD step; + + while (rem != 0) + { + if (rem <= UINT_MAX) + step = rem; + else + step = UINT_MAX; + if (!WriteFile (h, s + n - rem, step, &step, NULL)) + break; + rem -= step; + } +} + +/* Find the beginning of an escape sequence. + There are two cases: + 1. If the sequence begins with an ESC character (0x1B) and a second + character X in [0x40,0x5F], returns X and stores a pointer to + the third character into *head. + 2. If the sequence begins with a character X in [0x80,0x9F], returns + (X-0x40) and stores a pointer to the second character into *head. + Stores the number of ESC character(s) in *prefix_len. + Returns 0 if no such sequence can be found. */ +static int +find_esc_head (int *prefix_len, const char **head, const char *str) +{ + int c; + const char *r = str; + int escaped = 0; + + for (;;) + { + c = (unsigned char) *r; + if (c == 0) + { + /* Not found. */ + return 0; + } + if (escaped && 0x40 <= c && c <= 0x5F) + { + /* Found (case 1). */ + *prefix_len = 2; + *head = r + 1; + return c; + } + if (0x80 <= c && c <= 0x9F) + { + /* Found (case 2). */ + *prefix_len = 1; + *head = r + 1; + return c - 0x40; + } + ++r; + escaped = c == 0x1B; + } +} + +/* Find the terminator of an escape sequence. + str should be the value stored in *head by a previous successful + call to find_esc_head(). + Returns 0 if no such sequence can be found. */ +static int +find_esc_terminator (const char **term, const char *str) +{ + int c; + const char *r = str; + + for (;;) + { + c = (unsigned char) *r; + if (c == 0) + { + /* Not found. */ + return 0; + } + if (0x40 <= c && c <= 0x7E) + { + /* Found. */ + *term = r; + return c; + } + ++r; + } +} + +/* Handle a sequence of codes. Sequences that are invalid, reserved, + unrecognized or unimplemented are ignored silently. + There isn't much we can do because of lameness of Windows consoles. */ +static void +eat_esc_sequence (HANDLE h, int esc_code, + const char *esc_head, const char *esc_term) +{ + /* Numbers in an escape sequence cannot be negative, because + a minus sign in the middle of it would have terminated it. */ + long n1, n2; + char *eptr, *delim; + CONSOLE_SCREEN_BUFFER_INFO sb; + COORD cr; + /* ED and EL parameters. */ + DWORD cnt, step; + long rows; + /* SGR parameters. */ + WORD attrib_add, attrib_rm; + const char *param; + + switch (MAKEWORD (esc_code, *esc_term)) + { + /* ESC [ n1 'A' + Move the cursor up by n1 characters. */ + case MAKEWORD ('[', 'A'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the topmost boundary. */ + if (cr.Y > n1) + cr.Y -= n1; + else + cr.Y = 0; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'B' + Move the cursor down by n1 characters. */ + case MAKEWORD ('[', 'B'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the bottommost boundary. */ + if (sb.dwSize.Y - cr.Y > n1) + cr.Y += n1; + else + cr.Y = sb.dwSize.Y; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'C' + Move the cursor right by n1 characters. */ + case MAKEWORD ('[', 'C'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the rightmost boundary. */ + if (sb.dwSize.X - cr.X > n1) + cr.X += n1; + else + cr.X = sb.dwSize.X; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'D' + Move the cursor left by n1 characters. */ + case MAKEWORD ('[', 'D'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the leftmost boundary. */ + if (cr.X > n1) + cr.X -= n1; + else + cr.X = 0; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'E' + Move the cursor to the beginning of the n1-th line downwards. */ + case MAKEWORD ('[', 'E'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + cr.X = 0; + /* Stop at the bottommost boundary. */ + if (sb.dwSize.Y - cr.Y > n1) + cr.Y += n1; + else + cr.Y = sb.dwSize.Y; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'F' + Move the cursor to the beginning of the n1-th line upwards. */ + case MAKEWORD ('[', 'F'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + cr.X = 0; + /* Stop at the topmost boundary. */ + if (cr.Y > n1) + cr.Y -= n1; + else + cr.Y = 0; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'G' + Move the cursor to the (1-based) n1-th column. */ + case MAKEWORD ('[', 'G'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + n1 -= 1; + /* Stop at the leftmost or rightmost boundary. */ + if (n1 < 0) + cr.X = 0; + else if (n1 > sb.dwSize.X) + cr.X = sb.dwSize.X; + else + cr.X = n1; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 ';' n2 'H' + ESC [ n1 ';' n2 'f' + Move the cursor to the (1-based) n1-th row and + (also 1-based) n2-th column. */ + case MAKEWORD ('[', 'H'): + case MAKEWORD ('[', 'f'): + if (esc_head == esc_term) + { + /* Both parameters are omitted and set to 1 by default. */ + n1 = 1; + n2 = 1; + } + else if (!(delim = (char *) memchr (esc_head, ';', + esc_term - esc_head))) + { + /* Only the first parameter is given. The second one is + set to 1 by default. */ + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + n2 = 1; + } + else + { + /* Both parameters are given. The first one shall be + terminated by the semicolon. */ + n1 = strtol (esc_head, &eptr, 10); + if (eptr != delim) + break; + n2 = strtol (delim + 1, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + n1 -= 1; + n2 -= 1; + /* The cursor position shall be relative to the view coord of + the console window, which is usually smaller than the actual + buffer. FWIW, the 'appropriate' solution will be shrinking + the buffer to match the size of the console window, + destroying scrollback in the process. */ + n1 += sb.srWindow.Top; + n2 += sb.srWindow.Left; + /* Stop at the topmost or bottommost boundary. */ + if (n1 < 0) + cr.Y = 0; + else if (n1 > sb.dwSize.Y) + cr.Y = sb.dwSize.Y; + else + cr.Y = n1; + /* Stop at the leftmost or rightmost boundary. */ + if (n2 < 0) + cr.X = 0; + else if (n2 > sb.dwSize.X) + cr.X = sb.dwSize.X; + else + cr.X = n2; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'J' + Erase display. */ + case MAKEWORD ('[', 'J'): + if (esc_head == esc_term) + /* This is one of the very few codes whose parameters have + a default value of zero. */ + n1 = 0; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + /* The cursor is not necessarily in the console window, which + makes the behavior of this code harder to define. */ + switch (n1) + { + case 0: + /* If the cursor is in or above the window, erase from + it to the bottom of the window; otherwise, do nothing. */ + cr = sb.dwCursorPosition; + cnt = sb.dwSize.X - sb.dwCursorPosition.X; + rows = sb.srWindow.Bottom - sb.dwCursorPosition.Y; + break; + case 1: + /* If the cursor is in or under the window, erase from + it to the top of the window; otherwise, do nothing. */ + cr.X = 0; + cr.Y = sb.srWindow.Top; + cnt = sb.dwCursorPosition.X + 1; + rows = sb.dwCursorPosition.Y - sb.srWindow.Top; + break; + case 2: + /* Erase the entire window. */ + cr.X = sb.srWindow.Left; + cr.Y = sb.srWindow.Top; + cnt = 0; + rows = sb.srWindow.Bottom - sb.srWindow.Top + 1; + break; + default: + /* Erase the entire buffer. */ + cr.X = 0; + cr.Y = 0; + cnt = 0; + rows = sb.dwSize.Y; + break; + } + if (rows < 0) + break; + cnt += rows * sb.dwSize.X; + FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step); + FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step); + } + break; + + /* ESC [ n1 'K' + Erase line. */ + case MAKEWORD ('[', 'K'): + if (esc_head == esc_term) + /* This is one of the very few codes whose parameters have + a default value of zero. */ + n1 = 0; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + switch (n1) + { + case 0: + /* Erase from the cursor to the end. */ + cr = sb.dwCursorPosition; + cnt = sb.dwSize.X - sb.dwCursorPosition.X; + break; + case 1: + /* Erase from the cursor to the beginning. */ + cr = sb.dwCursorPosition; + cr.X = 0; + cnt = sb.dwCursorPosition.X + 1; + break; + default: + /* Erase the entire line. */ + cr = sb.dwCursorPosition; + cr.X = 0; + cnt = sb.dwSize.X; + break; + } + FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step); + FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step); + } + break; + + /* ESC [ n1 ';' n2 'm' + Set SGR parameters. Zero or more parameters will follow. */ + case MAKEWORD ('[', 'm'): + attrib_add = 0; + attrib_rm = 0; + if (esc_head == esc_term) + { + /* When no parameter is given, reset the console. */ + attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + attrib_rm = -1; /* Removes everything. */ + goto sgr_set_it; + } + param = esc_head; + do + { + /* Parse a parameter. */ + n1 = strtol (param, &eptr, 10); + if (*eptr != ';' && eptr != esc_term) + goto sgr_set_it; + + switch (n1) + { + case 0: + /* Reset. */ + attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + attrib_rm = -1; /* Removes everything. */ + break; + case 1: + /* Bold. */ + attrib_add |= FOREGROUND_INTENSITY; + break; + case 4: + /* Underline. */ + attrib_add |= COMMON_LVB_UNDERSCORE; + break; + case 5: + /* Blink. */ + /* XXX: It is not BLINKING at all! */ + attrib_add |= BACKGROUND_INTENSITY; + break; + case 7: + /* Reverse. */ + attrib_add |= COMMON_LVB_REVERSE_VIDEO; + break; + case 22: + /* No bold. */ + attrib_add &= ~FOREGROUND_INTENSITY; + attrib_rm |= FOREGROUND_INTENSITY; + break; + case 24: + /* No underline. */ + attrib_add &= ~COMMON_LVB_UNDERSCORE; + attrib_rm |= COMMON_LVB_UNDERSCORE; + break; + case 25: + /* No blink. */ + /* XXX: It is not BLINKING at all! */ + attrib_add &= ~BACKGROUND_INTENSITY; + attrib_rm |= BACKGROUND_INTENSITY; + break; + case 27: + /* No reverse. */ + attrib_add &= ~COMMON_LVB_REVERSE_VIDEO; + attrib_rm |= COMMON_LVB_REVERSE_VIDEO; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + /* Foreground color. */ + attrib_add &= ~(FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + n1 -= 30; + if (n1 & 1) + attrib_add |= FOREGROUND_RED; + if (n1 & 2) + attrib_add |= FOREGROUND_GREEN; + if (n1 & 4) + attrib_add |= FOREGROUND_BLUE; + attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + break; + case 38: + /* Reserved for extended foreground color. + Don't know how to handle parameters remaining. + Bail out. */ + goto sgr_set_it; + case 39: + /* Reset foreground color. */ + /* Set to grey. */ + attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + /* Background color. */ + attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + n1 -= 40; + if (n1 & 1) + attrib_add |= BACKGROUND_RED; + if (n1 & 2) + attrib_add |= BACKGROUND_GREEN; + if (n1 & 4) + attrib_add |= BACKGROUND_BLUE; + attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + break; + case 48: + /* Reserved for extended background color. + Don't know how to handle parameters remaining. + Bail out. */ + goto sgr_set_it; + case 49: + /* Reset background color. */ + /* Set to black. */ + attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + break; + } + + /* Prepare the next parameter. */ + param = eptr + 1; + } + while (param != esc_term); + +sgr_set_it: + /* 0xFFFF removes everything. If it is not the case, + care must be taken to preserve old attributes. */ + if (attrib_rm != 0xFFFF && GetConsoleScreenBufferInfo (h, &sb)) + { + attrib_add |= sb.wAttributes & ~attrib_rm; + } + SetConsoleTextAttribute (h, attrib_add); + break; + } +} + +int +mingw_ansi_fputs (const char *str, FILE *fp) +{ + const char *read = str; + HANDLE h; + DWORD mode; + int esc_code, prefix_len; + const char *esc_head, *esc_term; + + h = (HANDLE) _get_osfhandle (_fileno (fp)); + if (h == INVALID_HANDLE_VALUE) + return EOF; + + /* Don't mess up stdio functions with Windows APIs. */ + fflush (fp); + + if (GetConsoleMode (h, &mode)) + /* If it is a console, translate ANSI escape codes as needed. */ + for (;;) + { + if ((esc_code = find_esc_head (&prefix_len, &esc_head, read)) == 0) + { + /* Write all remaining characters, then exit. */ + write_all (h, read, strlen (read)); + break; + } + if (find_esc_terminator (&esc_term, esc_head) == 0) + /* Ignore incomplete escape sequences at the moment. + FIXME: The escape state shall be cached for further calls + to this function. */ + break; + write_all (h, read, esc_head - prefix_len - read); + eat_esc_sequence (h, esc_code, esc_head, esc_term); + read = esc_term + 1; + } + else + /* If it is not a console, write everything as-is. */ + write_all (h, read, strlen (read)); + + _close ((intptr_t) h); + return 1; +} + +#endif /* __MINGW32__ */ + +static void pp_quoted_string (pretty_printer *, const char *, size_t = -1); + +/* Overwrite the given location/range within this text_info's rich_location. + For use e.g. when implementing "+" in client format decoders. */ + +void +text_info::set_location (unsigned int idx, location_t loc, bool show_caret_p) +{ + gcc_checking_assert (m_richloc); + m_richloc->set_range (line_table, idx, loc, show_caret_p); +} + +location_t +text_info::get_location (unsigned int index_of_location) const +{ + gcc_checking_assert (m_richloc); + + if (index_of_location == 0) + return m_richloc->get_loc (); + else + return UNKNOWN_LOCATION; +} + +// Default construct an output buffer. + +output_buffer::output_buffer () + : formatted_obstack (), + chunk_obstack (), + obstack (&formatted_obstack), + cur_chunk_array (), + stream (stderr), + line_length (), + digit_buffer (), + flush_p (true) +{ + obstack_init (&formatted_obstack); + obstack_init (&chunk_obstack); +} + +// Release resources owned by an output buffer at the end of lifetime. + +output_buffer::~output_buffer () +{ + obstack_free (&chunk_obstack, NULL); + obstack_free (&formatted_obstack, NULL); +} + /* Format an integer given by va_arg (ARG, type-specifier T) where type-specifier is a precision modifier as indicated by PREC. F is @@ -95,7 +800,74 @@ pp_write_text_to_stream (pretty_printer *pp) { const char *text = pp_formatted_text (pp); - fputs (text, pp->buffer->stream); +#ifdef __MINGW32__ + mingw_ansi_fputs (text, pp_buffer (pp)->stream); +#else + fputs (text, pp_buffer (pp)->stream); +#endif + pp_clear_output_area (pp); +} + +/* As pp_write_text_to_stream, but for GraphViz label output. + + Flush the formatted text of pretty-printer PP onto the attached stream. + Replace characters in PPF that have special meaning in a GraphViz .dot + file. + + This routine is not very fast, but it doesn't have to be as this is only + be used by routines dumping intermediate representations in graph form. */ + +void +pp_write_text_as_dot_label_to_stream (pretty_printer *pp, bool for_record) +{ + const char *text = pp_formatted_text (pp); + const char *p = text; + FILE *fp = pp_buffer (pp)->stream; + + for (;*p; p++) + { + bool escape_char; + switch (*p) + { + /* Print newlines as a left-aligned newline. */ + case '\n': + fputs ("\\l", fp); + escape_char = true; + break; + + /* The following characters are only special for record-shape nodes. */ + case '|': + case '{': + case '}': + case '<': + case '>': + case ' ': + escape_char = for_record; + break; + + /* The following characters always have to be escaped + for use in labels. */ + case '\\': + /* There is a bug in some (f.i. 2.36.0) versions of graphiz + ( http://www.graphviz.org/mantisbt/view.php?id=2524 ) related to + backslash as last char in label. Let's avoid triggering it. */ + gcc_assert (*(p + 1) != '\0'); + /* Fall through. */ + case '"': + escape_char = true; + break; + + default: + escape_char = false; + break; + } + + if (escape_char) + fputc ('\\', fp); + + fputc (*p, fp); + } + pp_clear_output_area (pp); } @@ -147,15 +919,14 @@ static inline void pp_append_r (pretty_printer *pp, const char *start, int length) { - obstack_grow (pp->buffer->obstack, start, length); - pp->buffer->line_length += length; + output_buffer_append_r (pp_buffer (pp), start, length); } /* Insert enough spaces into the output area of PRETTY-PRINTER to bring the column position to the current indentation level, assuming that a newline has just been written to the buffer. */ void -pp_base_indent (pretty_printer *pp) +pp_indent (pretty_printer *pp) { int n = pp_indentation (pp); int i; @@ -174,7 +945,9 @@ %wd, %wi, %wo, %wu, %wx: HOST_WIDE_INT versions. %c: character. %s: string. - %p: pointer. + %p: pointer (printed in a host-dependent manner). + %r: if pp_show_color(pp), switch to color identified by const char *. + %R: if pp_show_color(pp), reset color. %m: strerror(text->err_no) - does not consume a value from args_ptr. %%: '%'. %<: opening quote. @@ -185,6 +958,8 @@ integer. %Ns: likewise, but length specified as constant in the format string. Flag 'q': quote formatted text (must come immediately after '%'). + %Z: Requires two arguments - array of int, and len. Prints elements + of the array. Arguments can be used sequentially, or through %N$ resp. *N$ notation Nth argument after the format string. If %N$ / *N$ @@ -197,13 +972,13 @@ A format string can have at most 30 arguments. */ /* Formatting phases 1 and 2: render TEXT->format_spec plus - TEXT->args_ptr into a series of chunks in PP->buffer->args[]. - Phase 3 is in pp_base_format_text. */ + TEXT->args_ptr into a series of chunks in pp_buffer (PP)->args[]. + Phase 3 is in pp_output_formatted_text. */ void -pp_base_format (pretty_printer *pp, text_info *text) +pp_format (pretty_printer *pp, text_info *text) { - output_buffer *buffer = pp->buffer; + output_buffer *buffer = pp_buffer (pp); const char *p; const char **args; struct chunk_info *new_chunk_array; @@ -220,7 +995,7 @@ args = new_chunk_array->args; /* Formatting phase 1: split up TEXT->format_spec into chunks in - PP->buffer->args[]. Even-numbered chunks are to be output + pp_buffer (PP)->args[]. Even-numbered chunks are to be output verbatim, odd-numbered chunks are format specifiers. %m, %%, %<, %>, and %' are replaced with the appropriate text at this point. */ @@ -249,18 +1024,37 @@ continue; case '<': - obstack_grow (&buffer->chunk_obstack, - open_quote, strlen (open_quote)); - p++; - continue; + { + obstack_grow (&buffer->chunk_obstack, + open_quote, strlen (open_quote)); + const char *colorstr + = colorize_start (pp_show_color (pp), "quote"); + obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr)); + p++; + continue; + } case '>': + { + const char *colorstr = colorize_stop (pp_show_color (pp)); + obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr)); + } + /* FALLTHRU */ case '\'': obstack_grow (&buffer->chunk_obstack, close_quote, strlen (close_quote)); p++; continue; + case 'R': + { + const char *colorstr = colorize_stop (pp_show_color (pp)); + obstack_grow (&buffer->chunk_obstack, colorstr, + strlen (colorstr)); + p++; + continue; + } + case 'm': { const char *errstr = xstrerror (text->err_no); @@ -415,13 +1209,34 @@ gcc_assert (!wide || precision == 0); if (quote) - pp_string (pp, open_quote); + { + pp_string (pp, open_quote); + pp_string (pp, colorize_start (pp_show_color (pp), "quote")); + } switch (*p) { + case 'r': + pp_string (pp, colorize_start (pp_show_color (pp), + va_arg (*text->args_ptr, + const char *))); + break; + case 'c': - pp_character (pp, va_arg (*text->args_ptr, int)); - break; + { + /* When quoting, print alphanumeric, punctuation, and the space + character unchanged, and all others in hexadecimal with the + "\x" prefix. Otherwise print them all unchanged. */ + int chr = va_arg (*text->args_ptr, int); + if (ISPRINT (chr) || !quote) + pp_character (pp, chr); + else + { + const char str [2] = { chr, '\0' }; + pp_quoted_string (pp, str, 1); + } + break; + } case 'd': case 'i': @@ -442,7 +1257,10 @@ break; case 's': - pp_string (pp, va_arg (*text->args_ptr, const char *)); + if (quote) + pp_quoted_string (pp, va_arg (*text->args_ptr, const char *)); + else + pp_string (pp, va_arg (*text->args_ptr, const char *)); break; case 'p': @@ -458,6 +1276,23 @@ (pp, *text->args_ptr, precision, unsigned, "u"); break; + case 'Z': + { + int *v = va_arg (*text->args_ptr, int *); + unsigned len = va_arg (*text->args_ptr, unsigned); + + for (unsigned i = 0; i < len; ++i) + { + pp_scalar (pp, "%i", v[i]); + if (i < len - 1) + { + pp_comma (pp); + pp_space (pp); + } + } + break; + } + case 'x': if (wide) pp_scalar (pp, HOST_WIDE_INT_PRINT_HEX, @@ -496,7 +1331,13 @@ } s = va_arg (*text->args_ptr, const char *); - pp_append_text (pp, s, s + n); + + /* Append the lesser of precision and strlen (s) characters + from the array (which need not be a nul-terminated string). + Negative precision is treated as if it were omitted. */ + size_t len = n < 0 ? strlen (s) : strnlen (s, n); + + pp_append_text (pp, s, s + len); } break; @@ -506,22 +1347,30 @@ gcc_assert (pp_format_decoder (pp)); ok = pp_format_decoder (pp) (pp, text, p, - precision, wide, plus, hash); + precision, wide, plus, hash, quote, + formatters[argno]); gcc_assert (ok); } } if (quote) - pp_string (pp, close_quote); + { + pp_string (pp, colorize_stop (pp_show_color (pp))); + pp_string (pp, close_quote); + } obstack_1grow (&buffer->chunk_obstack, '\0'); *formatters[argno] = XOBFINISH (&buffer->chunk_obstack, const char *); } -#ifdef ENABLE_CHECKING - for (; argno < PP_NL_ARGMAX; argno++) - gcc_assert (!formatters[argno]); -#endif + if (CHECKING_P) + for (; argno < PP_NL_ARGMAX; argno++) + gcc_assert (!formatters[argno]); + + /* If the client supplied a postprocessing object, call its "handle" + hook here. */ + if (pp->m_format_postprocessor) + pp->m_format_postprocessor->handle (pp); /* Revert to normal obstack and wrapping mode. */ buffer->obstack = &buffer->formatted_obstack; @@ -532,7 +1381,7 @@ /* Format of a message pointed to by TEXT. */ void -pp_base_output_formatted_text (pretty_printer *pp) +pp_output_formatted_text (pretty_printer *pp) { unsigned int chunk; output_buffer *buffer = pp_buffer (pp); @@ -542,7 +1391,7 @@ gcc_assert (buffer->obstack == &buffer->formatted_obstack); gcc_assert (buffer->line_length == 0); - /* This is a third phase, first 2 phases done in pp_base_format_args. + /* This is a third phase, first 2 phases done in pp_format_args. Now we actually print it. */ for (chunk = 0; args[chunk]; chunk++) pp_string (pp, args[chunk]); @@ -556,7 +1405,7 @@ /* Helper subroutine of output_verbatim and verbatim. Do the appropriate settings needed by BUFFER for a verbatim formatting. */ void -pp_base_format_verbatim (pretty_printer *pp, text_info *text) +pp_format_verbatim (pretty_printer *pp, text_info *text) { /* Set verbatim mode. */ pp_wrapping_mode_t oldmode = pp_set_verbatim_wrapping (pp); @@ -569,22 +1418,33 @@ pp_wrapping_mode (pp) = oldmode; } -/* Flush the content of BUFFER onto the attached stream. */ +/* Flush the content of BUFFER onto the attached stream. This + function does nothing unless pp->output_buffer->flush_p. */ void -pp_base_flush (pretty_printer *pp) +pp_flush (pretty_printer *pp) { + pp_clear_state (pp); + if (!pp->buffer->flush_p) + return; pp_write_text_to_stream (pp); + fflush (pp_buffer (pp)->stream); +} + +/* Flush the content of BUFFER onto the attached stream independently + of the value of pp->output_buffer->flush_p. */ +void +pp_really_flush (pretty_printer *pp) +{ pp_clear_state (pp); - fputc ('\n', pp->buffer->stream); - fflush (pp->buffer->stream); - pp_needs_newline (pp) = false; + pp_write_text_to_stream (pp); + fflush (pp_buffer (pp)->stream); } /* Sets the number of maximum characters per line PRETTY-PRINTER can output in line-wrapping mode. A LENGTH value 0 suppresses line-wrapping. */ void -pp_base_set_line_maximum_length (pretty_printer *pp, int length) +pp_set_line_maximum_length (pretty_printer *pp, int length) { pp_line_cutoff (pp) = length; pp_set_real_maximum_length (pp); @@ -592,15 +1452,16 @@ /* Clear PRETTY-PRINTER output area text info. */ void -pp_base_clear_output_area (pretty_printer *pp) +pp_clear_output_area (pretty_printer *pp) { - obstack_free (pp->buffer->obstack, obstack_base (pp->buffer->obstack)); - pp->buffer->line_length = 0; + obstack_free (pp_buffer (pp)->obstack, + obstack_base (pp_buffer (pp)->obstack)); + pp_buffer (pp)->line_length = 0; } /* Set PREFIX for PRETTY-PRINTER. */ void -pp_base_set_prefix (pretty_printer *pp, const char *prefix) +pp_set_prefix (pretty_printer *pp, const char *prefix) { pp->prefix = prefix; pp_set_real_maximum_length (pp); @@ -610,7 +1471,7 @@ /* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */ void -pp_base_destroy_prefix (pretty_printer *pp) +pp_destroy_prefix (pretty_printer *pp) { if (pp->prefix != NULL) { @@ -621,7 +1482,7 @@ /* Write out PRETTY-PRINTER's prefix. */ void -pp_base_emit_prefix (pretty_printer *pp) +pp_emit_prefix (pretty_printer *pp) { if (pp->prefix != NULL) { @@ -634,7 +1495,7 @@ case DIAGNOSTICS_SHOW_PREFIX_ONCE: if (pp->emitted_prefix) { - pp_base_indent (pp); + pp_indent (pp); break; } pp_indentation (pp) += 3; @@ -653,19 +1514,33 @@ /* Construct a PRETTY-PRINTER with PREFIX and of MAXIMUM_LENGTH characters per line. */ -void -pp_construct (pretty_printer *pp, const char *prefix, int maximum_length) + +pretty_printer::pretty_printer (const char *p, int l) + : buffer (new (XCNEW (output_buffer)) output_buffer ()), + prefix (), + padding (pp_none), + maximum_length (), + indent_skip (), + wrapping (), + format_decoder (), + m_format_postprocessor (NULL), + emitted_prefix (), + need_newline (), + translate_identifiers (true), + show_color () { - memset (pp, 0, sizeof (pretty_printer)); - pp->buffer = XCNEW (output_buffer); - obstack_init (&pp->buffer->chunk_obstack); - obstack_init (&pp->buffer->formatted_obstack); - pp->buffer->obstack = &pp->buffer->formatted_obstack; - pp->buffer->stream = stderr; - pp_line_cutoff (pp) = maximum_length; - pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_ONCE; - pp_set_prefix (pp, prefix); - pp_translate_identifiers (pp) = true; + pp_line_cutoff (this) = l; + /* By default, we emit prefixes once per message. */ + pp_prefixing_rule (this) = DIAGNOSTICS_SHOW_PREFIX_ONCE; + pp_set_prefix (this, p); +} + +pretty_printer::~pretty_printer () +{ + if (m_format_postprocessor) + delete m_format_postprocessor; + buffer->~output_buffer (); + XDELETE (buffer); } /* Append a string delimited by START and END to the output area of @@ -674,10 +1549,10 @@ whitespace if appropriate. The caller must ensure that it is safe to do so. */ void -pp_base_append_text (pretty_printer *pp, const char *start, const char *end) +pp_append_text (pretty_printer *pp, const char *start, const char *end) { /* Emit prefix and skip whitespace if we're starting a new line. */ - if (pp->buffer->line_length == 0) + if (pp_buffer (pp)->line_length == 0) { pp_emit_prefix (pp); if (pp_is_wrapping_line (pp)) @@ -690,31 +1565,25 @@ /* Finishes constructing a NULL-terminated character string representing the PRETTY-PRINTED text. */ const char * -pp_base_formatted_text (pretty_printer *pp) +pp_formatted_text (pretty_printer *pp) { - obstack_1grow (pp->buffer->obstack, '\0'); - return pp_formatted_text_data (pp); + return output_buffer_formatted_text (pp_buffer (pp)); } /* Return a pointer to the last character emitted in PRETTY-PRINTER's output area. A NULL pointer means no character available. */ const char * -pp_base_last_position_in_text (const pretty_printer *pp) +pp_last_position_in_text (const pretty_printer *pp) { - const char *p = NULL; - struct obstack *text = pp->buffer->obstack; - - if (obstack_base (text) != obstack_next_free (text)) - p = ((const char *) obstack_next_free (text)) - 1; - return p; + return output_buffer_last_position_in_text (pp_buffer (pp)); } /* Return the amount of characters PRETTY-PRINTER can accept to make a full line. Meaningful only in line-wrapping mode. */ int -pp_base_remaining_character_count_for_line (pretty_printer *pp) +pp_remaining_character_count_for_line (pretty_printer *pp) { - return pp->maximum_length - pp->buffer->line_length; + return pp->maximum_length - pp_buffer (pp)->line_length; } @@ -729,7 +1598,6 @@ text.err_no = errno; text.args_ptr = ≈ text.format_spec = msg; - text.locus = NULL; pp_format (pp, &text); pp_output_formatted_text (pp); va_end (ap); @@ -747,7 +1615,6 @@ text.err_no = errno; text.args_ptr = ≈ text.format_spec = msg; - text.locus = NULL; pp_format_verbatim (pp, &text); va_end (ap); } @@ -756,15 +1623,16 @@ /* Have PRETTY-PRINTER start a new line. */ void -pp_base_newline (pretty_printer *pp) +pp_newline (pretty_printer *pp) { - obstack_1grow (pp->buffer->obstack, '\n'); - pp->buffer->line_length = 0; + obstack_1grow (pp_buffer (pp)->obstack, '\n'); + pp_needs_newline (pp) = false; + pp_buffer (pp)->line_length = 0; } /* Have PRETTY-PRINTER add a CHARACTER. */ void -pp_base_character (pretty_printer *pp, int c) +pp_character (pretty_printer *pp, int c) { if (pp_is_wrapping_line (pp) && pp_remaining_character_count_for_line (pp) <= 0) @@ -773,29 +1641,96 @@ if (ISSPACE (c)) return; } - obstack_1grow (pp->buffer->obstack, c); - ++pp->buffer->line_length; + obstack_1grow (pp_buffer (pp)->obstack, c); + ++pp_buffer (pp)->line_length; } /* Append a STRING to the output area of PRETTY-PRINTER; the STRING may be line-wrapped if in appropriate mode. */ void -pp_base_string (pretty_printer *pp, const char *str) +pp_string (pretty_printer *pp, const char *str) +{ + gcc_checking_assert (str); + pp_maybe_wrap_text (pp, str, str + strlen (str)); +} + +/* Append the leading N characters of STRING to the output area of + PRETTY-PRINTER, quoting in hexadecimal non-printable characters. + Setting N = -1 is as if N were set to strlen (STRING). The STRING + may be line-wrapped if in appropriate mode. */ +static void +pp_quoted_string (pretty_printer *pp, const char *str, size_t n /* = -1 */) { - pp_maybe_wrap_text (pp, str, str + (str ? strlen (str) : 0)); + gcc_checking_assert (str); + + const char *last = str; + const char *ps; + + /* Compute the length if not specified. */ + if (n == (size_t) -1) + n = strlen (str); + + for (ps = str; n; ++ps, --n) + { + if (ISPRINT (*ps)) + continue; + + if (last < ps) + pp_maybe_wrap_text (pp, last, ps - 1); + + /* Append the hexadecimal value of the character. Allocate a buffer + that's large enough for a 32-bit char plus the hex prefix. */ + char buf [11]; + int n = sprintf (buf, "\\x%02x", (unsigned char)*ps); + pp_maybe_wrap_text (pp, buf, buf + n); + last = ps + 1; + } + + pp_maybe_wrap_text (pp, last, ps); } /* Maybe print out a whitespace if needed. */ void -pp_base_maybe_space (pretty_printer *pp) +pp_maybe_space (pretty_printer *pp) { - if (pp_base (pp)->padding != pp_none) + if (pp->padding != pp_none) { pp_space (pp); - pp_base (pp)->padding = pp_none; + pp->padding = pp_none; } } + +// Add a newline to the pretty printer PP and flush formatted text. + +void +pp_newline_and_flush (pretty_printer *pp) +{ + pp_newline (pp); + pp_flush (pp); + pp_needs_newline (pp) = false; +} + +// Add a newline to the pretty printer PP, followed by indentation. + +void +pp_newline_and_indent (pretty_printer *pp, int n) +{ + pp_indentation (pp) += n; + pp_newline (pp); + pp_indent (pp); + pp_needs_newline (pp) = false; +} + +// Add separator C, followed by a single whitespace. + +void +pp_separate_with (pretty_printer *pp, char c) +{ + pp_character (pp, c); + pp_space (pp); +} + /* The string starting at P has LEN (at least 1) bytes left; if they start with a valid UTF-8 sequence, return the length of that @@ -1019,3 +1954,211 @@ return ret; } } + +#if CHECKING_P + +namespace selftest { + +/* Smoketest for pretty_printer. */ + +static void +test_basic_printing () +{ + pretty_printer pp; + pp_string (&pp, "hello"); + pp_space (&pp); + pp_string (&pp, "world"); + + ASSERT_STREQ ("hello world", pp_formatted_text (&pp)); +} + +/* Helper function for testing pp_format. + Verify that pp_format (FMT, ...) followed by pp_output_formatted_text + prints EXPECTED, assuming that pp_show_color is SHOW_COLOR. */ + +static void +assert_pp_format_va (const location &loc, const char *expected, + bool show_color, const char *fmt, va_list *ap) +{ + pretty_printer pp; + text_info ti; + rich_location rich_loc (line_table, UNKNOWN_LOCATION); + + ti.format_spec = fmt; + ti.args_ptr = ap; + ti.err_no = 0; + ti.x_data = NULL; + ti.m_richloc = &rich_loc; + + pp_show_color (&pp) = show_color; + pp_format (&pp, &ti); + pp_output_formatted_text (&pp); + ASSERT_STREQ_AT (loc, expected, pp_formatted_text (&pp)); +} + +/* Verify that pp_format (FMT, ...) followed by pp_output_formatted_text + prints EXPECTED, with show_color disabled. */ + +static void +assert_pp_format (const location &loc, const char *expected, + const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + assert_pp_format_va (loc, expected, false, fmt, &ap); + va_end (ap); +} + +/* As above, but with colorization enabled. */ + +static void +assert_pp_format_colored (const location &loc, const char *expected, + const char *fmt, ...) +{ + /* The tests of colorization assume the default color scheme. + If GCC_COLORS is set, then the colors have potentially been + overridden; skip the test. */ + if (getenv ("GCC_COLORS")) + return; + + va_list ap; + + va_start (ap, fmt); + assert_pp_format_va (loc, expected, true, fmt, &ap); + va_end (ap); +} + +/* Helper function for calling testing pp_format, + by calling assert_pp_format with various numbers of arguments. + These exist mostly to avoid having to write SELFTEST_LOCATION + throughout test_pp_format. */ + +#define ASSERT_PP_FORMAT_1(EXPECTED, FMT, ARG1) \ + SELFTEST_BEGIN_STMT \ + assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \ + (ARG1)); \ + SELFTEST_END_STMT + +#define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \ + SELFTEST_BEGIN_STMT \ + assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \ + (ARG1), (ARG2)); \ + SELFTEST_END_STMT + +#define ASSERT_PP_FORMAT_3(EXPECTED, FMT, ARG1, ARG2, ARG3) \ + SELFTEST_BEGIN_STMT \ + assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \ + (ARG1), (ARG2), (ARG3)); \ + SELFTEST_END_STMT + +/* Verify that pp_format works, for various format codes. */ + +static void +test_pp_format () +{ + /* Avoid introducing locale-specific differences in the results + by hardcoding open_quote and close_quote. */ + const char *old_open_quote = open_quote; + const char *old_close_quote = close_quote; + open_quote = "`"; + close_quote = "'"; + + /* Verify that plain text is passed through unchanged. */ + assert_pp_format (SELFTEST_LOCATION, "unformatted", "unformatted"); + + /* Verify various individual format codes, in the order listed in the + comment for pp_format above. For each code, we append a second + argument with a known bit pattern (0x12345678), to ensure that we + are consuming arguments correctly. */ + ASSERT_PP_FORMAT_2 ("-27 12345678", "%d %x", -27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%i %x", -5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%u %x", 10, 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%o %x", 15, 0x12345678); + ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678); + ASSERT_PP_FORMAT_2 ("-27 12345678", "%ld %x", (long)-27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%li %x", (long)-5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%lu %x", (long)10, 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%lo %x", (long)15, 0x12345678); + ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%lx %x", (long)0xcafebabe, + 0x12345678); + ASSERT_PP_FORMAT_2 ("-27 12345678", "%lld %x", (long long)-27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%lli %x", (long long)-5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%llu %x", (long long)10, 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%llo %x", (long long)15, 0x12345678); + ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe, + 0x12345678); + ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", (HOST_WIDE_INT)-27, 0x12345678); + ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", (HOST_WIDE_INT)-5, 0x12345678); + ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", (unsigned HOST_WIDE_INT)10, + 0x12345678); + ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", (HOST_WIDE_INT)15, 0x12345678); + ASSERT_PP_FORMAT_2 ("0xcafebabe 12345678", "%wx %x", (HOST_WIDE_INT)0xcafebabe, + 0x12345678); + ASSERT_PP_FORMAT_2 ("A 12345678", "%c %x", 'A', 0x12345678); + ASSERT_PP_FORMAT_2 ("hello world 12345678", "%s %x", "hello world", + 0x12345678); + + /* Not nul-terminated. */ + char arr[5] = { '1', '2', '3', '4', '5' }; + ASSERT_PP_FORMAT_3 ("123 12345678", "%.*s %x", 3, arr, 0x12345678); + ASSERT_PP_FORMAT_3 ("1234 12345678", "%.*s %x", -1, "1234", 0x12345678); + ASSERT_PP_FORMAT_3 ("12345 12345678", "%.*s %x", 7, "12345", 0x12345678); + + /* We can't test for %p; the pointer is printed in an implementation-defined + manner. */ + ASSERT_PP_FORMAT_2 ("normal colored normal 12345678", + "normal %rcolored%R normal %x", + "error", 0x12345678); + assert_pp_format_colored + (SELFTEST_LOCATION, + "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678", + "normal %rcolored%R normal %x", "error", 0x12345678); + /* TODO: + %m: strerror(text->err_no) - does not consume a value from args_ptr. */ + ASSERT_PP_FORMAT_1 ("% 12345678", "%% %x", 0x12345678); + ASSERT_PP_FORMAT_1 ("` 12345678", "%< %x", 0x12345678); + ASSERT_PP_FORMAT_1 ("' 12345678", "%> %x", 0x12345678); + ASSERT_PP_FORMAT_1 ("' 12345678", "%' %x", 0x12345678); + ASSERT_PP_FORMAT_3 ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678); + ASSERT_PP_FORMAT_2 ("abc 12345678", "%.3s %x", "abcdef", 0x12345678); + + /* Verify flag 'q'. */ + ASSERT_PP_FORMAT_2 ("`foo' 12345678", "%qs %x", "foo", 0x12345678); + assert_pp_format_colored (SELFTEST_LOCATION, + "`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x", + "foo", 0x12345678); + + /* Verify %Z. */ + int v[] = { 1, 2, 3 }; + ASSERT_PP_FORMAT_3 ("1, 2, 3 12345678", "%Z %x", v, 3, 0x12345678); + + int v2[] = { 0 }; + ASSERT_PP_FORMAT_3 ("0 12345678", "%Z %x", v2, 1, 0x12345678); + + /* Verify that combinations work, along with unformatted text. */ + assert_pp_format (SELFTEST_LOCATION, + "the quick brown fox jumps over the lazy dog", + "the %s %s %s jumps over the %s %s", + "quick", "brown", "fox", "lazy", "dog"); + assert_pp_format (SELFTEST_LOCATION, "item 3 of 7", "item %i of %i", 3, 7); + assert_pp_format (SELFTEST_LOCATION, "problem with `bar' at line 10", + "problem with %qs at line %i", "bar", 10); + + /* Restore old values of open_quote and close_quote. */ + open_quote = old_open_quote; + close_quote = old_close_quote; +} + +/* Run all of the selftests within this file. */ + +void +pretty_print_c_tests () +{ + test_basic_printing (); + test_pp_format (); +} + +} // namespace selftest + +#endif /* CHECKING_P */