Implement putstr and putstrn in ui_file

In my tour of the ui_file subsystem, I found that fputstr and fputstrn
can be simplified.  The _filtered forms are never used (and IMO
unlikely to ever be used) and so can be removed.  And, the interface
can be simplified by removing a callback function and moving the
implementation directly to ui_file.

A new self-test is included.  Previously, I think nothing was testing
this code.

Regression tested on x86-64 Fedora 34.
This commit is contained in:
Tom Tromey 2021-12-31 10:40:02 -07:00
parent 28a4e64dd1
commit d53fd721a1
10 changed files with 158 additions and 139 deletions

View file

@ -477,6 +477,7 @@ SELFTESTS_SRCS = \
unittests/style-selftests.c \
unittests/tracepoint-selftests.c \
unittests/tui-selftests.c \
unittests/ui-file-selftests.c \
unittests/unpack-selftests.c \
unittests/utils-selftests.c \
unittests/vec-utils-selftests.c \

View file

@ -191,8 +191,8 @@ ioscm_open_port (scm_t_port_type *port_type, long mode_bits, scm_t_bits stream)
/* Support for connecting Guile's stdio ports to GDB's stdio ports. */
/* Like fputstrn_filtered, but don't escape characters, except nul.
Also like fputs_filtered, but a length is specified. */
/* Print a string S, length SIZE, but don't escape characters, except
nul. */
static void
fputsn_filtered (const char *s, size_t size, struct ui_file *stream)

View file

@ -48,16 +48,6 @@ mi_console_file::write (const char *buf, long length_buf)
this->flush ();
}
/* Write C to STREAM's in an async-safe way. */
static int
do_fputc_async_safe (int c, ui_file *stream)
{
char ch = c;
stream->write_async_safe (&ch, 1);
return c;
}
void
mi_console_file::write_async_safe (const char *buf, long length_buf)
{
@ -65,12 +55,11 @@ mi_console_file::write_async_safe (const char *buf, long length_buf)
if (m_quote)
{
m_raw->write_async_safe (&m_quote, 1);
fputstrn_unfiltered (buf, length_buf, m_quote, do_fputc_async_safe,
m_raw);
m_raw->putstrn (buf, length_buf, m_quote, true);
m_raw->write_async_safe (&m_quote, 1);
}
else
fputstrn_unfiltered (buf, length_buf, 0, do_fputc_async_safe, m_raw);
m_raw->putstrn (buf, length_buf, 0, true);
char nl = '\n';
m_raw->write_async_safe (&nl, 1);
@ -91,14 +80,13 @@ mi_console_file::flush ()
if (m_quote)
{
fputc_unfiltered (m_quote, m_raw);
fputstrn_unfiltered (buf, length_buf, m_quote, fputc_unfiltered,
m_raw);
m_raw->putstrn (buf, length_buf, m_quote);
fputc_unfiltered (m_quote, m_raw);
fputc_unfiltered ('\n', m_raw);
}
else
{
fputstrn_unfiltered (buf, length_buf, 0, fputc_unfiltered, m_raw);
m_raw->putstrn (buf, length_buf, 0);
fputc_unfiltered ('\n', m_raw);
}
gdb_flush (m_raw);

View file

@ -1866,7 +1866,7 @@ mi_print_exception (const char *token, const struct gdb_exception &exception)
if (exception.message == NULL)
fputs_unfiltered ("unknown error", mi->raw_stdout);
else
fputstr_unfiltered (exception.what (), '"', mi->raw_stdout);
mi->raw_stdout->putstr (exception.what (), '"');
fputs_unfiltered ("\"", mi->raw_stdout);
switch (exception.error)

View file

@ -135,7 +135,7 @@ mi_ui_out::do_field_string (int fldno, int width, ui_align align,
fprintf_unfiltered (stream, "%s=", fldname);
fprintf_unfiltered (stream, "\"");
if (string)
fputstr_unfiltered (string, '"', stream);
stream->putstr (string, '"');
fprintf_unfiltered (stream, "\"");
}

View file

@ -47,13 +47,15 @@ ui_file::printf (const char *format, ...)
void
ui_file::putstr (const char *str, int quoter)
{
fputstr_unfiltered (str, quoter, this);
while (*str)
printchar (*str++, quoter, false);
}
void
ui_file::putstrn (const char *str, int n, int quoter)
ui_file::putstrn (const char *str, int n, int quoter, bool async_safe)
{
fputstrn_unfiltered (str, n, quoter, fputc_unfiltered, this);
for (int i = 0; i < n; i++)
printchar (str[i], quoter, async_safe);
}
int
@ -68,6 +70,67 @@ ui_file::vprintf (const char *format, va_list args)
vfprintf_unfiltered (this, format, args);
}
/* See ui-file.h. */
void
ui_file::printchar (int c, int quoter, bool async_safe)
{
char buf[4];
int out = 0;
c &= 0xFF; /* Avoid sign bit follies */
if (c < 0x20 /* Low control chars */
|| (c >= 0x7F && c < 0xA0) /* DEL, High controls */
|| (sevenbit_strings && c >= 0x80))
{ /* high order bit set */
buf[out++] = '\\';
switch (c)
{
case '\n':
buf[out++] = 'n';
break;
case '\b':
buf[out++] = 'b';
break;
case '\t':
buf[out++] = 't';
break;
case '\f':
buf[out++] = 'f';
break;
case '\r':
buf[out++] = 'r';
break;
case '\033':
buf[out++] = 'e';
break;
case '\007':
buf[out++] = 'a';
break;
default:
{
buf[out++] = '0' + ((c >> 6) & 0x7);
buf[out++] = '0' + ((c >> 3) & 0x7);
buf[out++] = '0' + ((c >> 0) & 0x7);
break;
}
}
}
else
{
if (quoter != 0 && (c == '\\' || c == quoter))
buf[out++] = '\\';
buf[out++] = c;
}
if (async_safe)
this->write_async_safe (buf, out);
else
this->write (buf, out);
}
void

View file

@ -34,12 +34,22 @@ public:
void printf (const char *, ...) ATTRIBUTE_PRINTF (2, 3);
/* Print a string whose delimiter is QUOTER. Note that these
routines should only be called for printing things which are
independent of the language of the program being debugged. */
/* Print a NUL-terminated string whose delimiter is QUOTER. Note
that these routines should only be called for printing things
which are independent of the language of the program being
debugged.
This will normally escape backslashes and instances of QUOTER.
If QUOTER is 0, it won't escape backslashes or any quoting
character. As a side effect, if you pass the backslash character
as the QUOTER, this will escape backslashes as usual, but not any
other quoting character. */
void putstr (const char *str, int quoter);
void putstrn (const char *str, int n, int quoter);
/* Like putstr, but only print the first N characters of STR. If
ASYNC_SAFE is true, then the output is done via the
write_async_safe method. */
void putstrn (const char *str, int n, int quoter, bool async_safe = false);
int putc (int c);
@ -96,6 +106,13 @@ public:
default. */
return false;
}
private:
/* Helper function for putstr and putstrn. Print the character C on
this stream as part of the contents of a literal string whose
delimiter is QUOTER. */
void printchar (int c, int quoter, bool async_safe);
};
typedef std::unique_ptr<ui_file> ui_file_up;

View file

@ -0,0 +1,62 @@
/* Self tests for ui_file
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "gdbsupport/selftest.h"
#include "ui-file.h"
namespace selftests {
namespace file {
static void
check_one (const char *str, int quoter, const char *result)
{
string_file out;
out.putstr (str, quoter);
SELF_CHECK (out.string () == result);
}
static void
run_tests ()
{
check_one ("basic stuff: \\", '\\',
"basic stuff: \\\\");
check_one ("more basic stuff: \\Q", 'Q',
"more basic stuff: \\\\\\Q");
check_one ("more basic stuff: \\Q", '\0',
"more basic stuff: \\Q");
check_one ("weird stuff: \x1f\x90\n\b\t\f\r\033\007", '\\',
"weird stuff: \\037\\220\\n\\b\\t\\f\\r\\e\\a");
scoped_restore save_7 = make_scoped_restore (&sevenbit_strings, true);
check_one ("more weird stuff: \xa5", '\\',
"more weird stuff: \\245");
}
} /* namespace file*/
} /* namespace selftests */
void _initialize_ui_file_selftest ();
void
_initialize_ui_file_selftest ()
{
selftests::register_test ("ui-file",
selftests::file::run_tests);
}

View file

@ -1129,103 +1129,6 @@ parse_escape (struct gdbarch *gdbarch, const char **string_ptr)
return target_char;
}
/* Print the character C on STREAM as part of the contents of a literal
string whose delimiter is QUOTER. Note that this routine should only
be called for printing things which are independent of the language
of the program being debugged.
printchar will normally escape backslashes and instances of QUOTER. If
QUOTER is 0, printchar won't escape backslashes or any quoting character.
As a side effect, if you pass the backslash character as the QUOTER,
printchar will escape backslashes as usual, but not any other quoting
character. */
static void
printchar (int c, do_fputc_ftype do_fputc, ui_file *stream, int quoter)
{
c &= 0xFF; /* Avoid sign bit follies */
if (c < 0x20 || /* Low control chars */
(c >= 0x7F && c < 0xA0) || /* DEL, High controls */
(sevenbit_strings && c >= 0x80))
{ /* high order bit set */
do_fputc ('\\', stream);
switch (c)
{
case '\n':
do_fputc ('n', stream);
break;
case '\b':
do_fputc ('b', stream);
break;
case '\t':
do_fputc ('t', stream);
break;
case '\f':
do_fputc ('f', stream);
break;
case '\r':
do_fputc ('r', stream);
break;
case '\033':
do_fputc ('e', stream);
break;
case '\007':
do_fputc ('a', stream);
break;
default:
{
do_fputc ('0' + ((c >> 6) & 0x7), stream);
do_fputc ('0' + ((c >> 3) & 0x7), stream);
do_fputc ('0' + ((c >> 0) & 0x7), stream);
break;
}
}
}
else
{
if (quoter != 0 && (c == '\\' || c == quoter))
do_fputc ('\\', stream);
do_fputc (c, stream);
}
}
/* Print the character C on STREAM as part of the contents of a
literal string whose delimiter is QUOTER. Note that these routines
should only be call for printing things which are independent of
the language of the program being debugged. */
void
fputstr_filtered (const char *str, int quoter, struct ui_file *stream)
{
while (*str)
printchar (*str++, fputc_filtered, stream, quoter);
}
void
fputstr_unfiltered (const char *str, int quoter, struct ui_file *stream)
{
while (*str)
printchar (*str++, fputc_unfiltered, stream, quoter);
}
void
fputstrn_filtered (const char *str, int n, int quoter,
struct ui_file *stream)
{
for (int i = 0; i < n; i++)
printchar (str[i], fputc_filtered, stream, quoter);
}
void
fputstrn_unfiltered (const char *str, int n, int quoter,
do_fputc_ftype do_fputc, struct ui_file *stream)
{
for (int i = 0; i < n; i++)
printchar (str[i], do_fputc, stream, quoter);
}
/* Number of lines per page or UINT_MAX if paging is disabled. */
static unsigned int lines_per_page;

View file

@ -464,21 +464,6 @@ extern void print_spaces_filtered (int, struct ui_file *);
extern const char *n_spaces (int);
extern void fputstr_filtered (const char *str, int quotr,
struct ui_file * stream);
extern void fputstr_unfiltered (const char *str, int quotr,
struct ui_file * stream);
extern void fputstrn_filtered (const char *str, int n, int quotr,
struct ui_file * stream);
typedef int (*do_fputc_ftype) (int c, ui_file *stream);
extern void fputstrn_unfiltered (const char *str, int n, int quotr,
do_fputc_ftype do_fputc,
struct ui_file * stream);
/* Return nonzero if filtered printing is initialized. */
extern int filtered_printing_initialized (void);