/* General utility routines for GDB, the GNU debugger.
- Copyright (C) 1986-2016 Free Software Foundation, Inc.
+ Copyright (C) 1986-2017 Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
-#include "dyn-string.h"
#include <ctype.h>
#include "gdb_wait.h"
#include "event-top.h"
#endif
#include <signal.h>
-#include "timeval-utils.h"
#include "gdbcmd.h"
#include "serial.h"
#include "bfd.h"
#include "readline/readline.h"
-#include "gdb_sys_time.h"
-#include <time.h>
+#include <chrono>
#include "gdb_usleep.h"
#include "interps.h"
#include "gdb_regex.h"
+#include "job-control.h"
+#include "common/selftest.h"
+#include "common/gdb_optional.h"
+#include "cp-support.h"
+#include <algorithm>
#if !HAVE_DECL_MALLOC
extern PTR malloc (); /* ARI: PTR */
Modified in prompt_for_continue and defaulted_query.
Used in report_command_stats. */
-static struct timeval prompt_for_continue_wait_time;
+static std::chrono::steady_clock::duration prompt_for_continue_wait_time;
/* A flag indicating whether to timestamp debugging messages. */
static int debug_timestamp = 0;
-/* Nonzero if we have job control. */
-
-int job_control;
-
/* Nonzero means that strings with character values >0x7F should be printed
as octal escapes. Zero means just print the value (e.g. it's an
international character, and the terminal or window can cope.) */
/* String to be printed before warning messages, if any. */
-char *warning_pre_print = "\nwarning: ";
+const char *warning_pre_print = "\nwarning: ";
int pagination_enabled = 1;
static void
because while they use the "cleanup API" they are not part of the
"cleanup API". */
-static void
-do_freeargv (void *arg)
-{
- freeargv ((char **) arg);
-}
-
-struct cleanup *
-make_cleanup_freeargv (char **arg)
-{
- return make_cleanup (do_freeargv, arg);
-}
-
-static void
-do_dyn_string_delete (void *arg)
-{
- dyn_string_delete ((dyn_string_t) arg);
-}
-
-struct cleanup *
-make_cleanup_dyn_string_delete (dyn_string_t arg)
-{
- return make_cleanup (do_dyn_string_delete, arg);
-}
-
-static void
-do_bfd_close_cleanup (void *arg)
-{
- gdb_bfd_unref ((bfd *) arg);
-}
-
-struct cleanup *
-make_cleanup_bfd_unref (bfd *abfd)
-{
- return make_cleanup (do_bfd_close_cleanup, abfd);
-}
-
-/* Helper function which does the work for make_cleanup_fclose. */
-
-static void
-do_fclose_cleanup (void *arg)
-{
- FILE *file = (FILE *) arg;
-
- fclose (file);
-}
-
-/* Return a new cleanup that closes FILE. */
-
-struct cleanup *
-make_cleanup_fclose (FILE *file)
-{
- return make_cleanup (do_fclose_cleanup, file);
-}
-
-/* Helper function which does the work for make_cleanup_obstack_free. */
-
-static void
-do_obstack_free (void *arg)
-{
- struct obstack *ob = (struct obstack *) arg;
-
- obstack_free (ob, NULL);
-}
-
-/* Return a new cleanup that frees OBSTACK. */
-
-struct cleanup *
-make_cleanup_obstack_free (struct obstack *obstack)
-{
- return make_cleanup (do_obstack_free, obstack);
-}
-
-static void
-do_ui_file_delete (void *arg)
-{
- ui_file_delete ((struct ui_file *) arg);
-}
-
-struct cleanup *
-make_cleanup_ui_file_delete (struct ui_file *arg)
-{
- return make_cleanup (do_ui_file_delete, arg);
-}
-
-/* Helper function for make_cleanup_ui_out_redirect_pop. */
-
-static void
-do_ui_out_redirect_pop (void *arg)
-{
- struct ui_out *uiout = (struct ui_out *) arg;
-
- if (ui_out_redirect (uiout, NULL) < 0)
- warning (_("Cannot restore redirection of the current output protocol"));
-}
-
-/* Return a new cleanup that pops the last redirection by ui_out_redirect
- with NULL parameter. */
-
-struct cleanup *
-make_cleanup_ui_out_redirect_pop (struct ui_out *uiout)
-{
- return make_cleanup (do_ui_out_redirect_pop, uiout);
-}
-
static void
do_free_section_addr_info (void *arg)
{
return make_cleanup (do_free_section_addr_info, addrs);
}
-struct restore_integer_closure
-{
- int *variable;
- int value;
-};
-
-static void
-restore_integer (void *p)
-{
- struct restore_integer_closure *closure
- = (struct restore_integer_closure *) p;
-
- *(closure->variable) = closure->value;
-}
-
-/* Remember the current value of *VARIABLE and make it restored when
- the cleanup is run. */
-
-struct cleanup *
-make_cleanup_restore_integer (int *variable)
-{
- struct restore_integer_closure *c = XNEW (struct restore_integer_closure);
-
- c->variable = variable;
- c->value = *variable;
-
- return make_cleanup_dtor (restore_integer, (void *) c, xfree);
-}
-
-/* Remember the current value of *VARIABLE and make it restored when
- the cleanup is run. */
-
-struct cleanup *
-make_cleanup_restore_uinteger (unsigned int *variable)
-{
- return make_cleanup_restore_integer ((int *) variable);
-}
-
/* Helper for make_cleanup_unpush_target. */
static void
return make_cleanup (do_unpush_target, ops);
}
-/* Helper for make_cleanup_htab_delete compile time checking the types. */
-
-static void
-do_htab_delete_cleanup (void *htab_voidp)
-{
- htab_t htab = (htab_t) htab_voidp;
-
- htab_delete (htab);
-}
-
-/* Return a new cleanup that deletes HTAB. */
-
-struct cleanup *
-make_cleanup_htab_delete (htab_t htab)
-{
- return make_cleanup (do_htab_delete_cleanup, htab);
-}
-
-struct restore_ui_out_closure
-{
- struct ui_out **variable;
- struct ui_out *value;
-};
-
-static void
-do_restore_ui_out (void *p)
-{
- struct restore_ui_out_closure *closure
- = (struct restore_ui_out_closure *) p;
-
- *(closure->variable) = closure->value;
-}
-
-/* Remember the current value of *VARIABLE and make it restored when
- the cleanup is run. */
-
-struct cleanup *
-make_cleanup_restore_ui_out (struct ui_out **variable)
-{
- struct restore_ui_out_closure *c = XNEW (struct restore_ui_out_closure);
-
- c->variable = variable;
- c->value = *variable;
-
- return make_cleanup_dtor (do_restore_ui_out, (void *) c, xfree);
-}
-
-struct restore_ui_file_closure
-{
- struct ui_file **variable;
- struct ui_file *value;
-};
-
-static void
-do_restore_ui_file (void *p)
-{
- struct restore_ui_file_closure *closure
- = (struct restore_ui_file_closure *) p;
-
- *(closure->variable) = closure->value;
-}
-
-/* Remember the current value of *VARIABLE and make it restored when
- the cleanup is run. */
-
-struct cleanup *
-make_cleanup_restore_ui_file (struct ui_file **variable)
-{
- struct restore_ui_file_closure *c = XNEW (struct restore_ui_file_closure);
-
- c->variable = variable;
- c->value = *variable;
-
- return make_cleanup_dtor (do_restore_ui_file, (void *) c, xfree);
-}
-
/* Helper for make_cleanup_value_free_to_mark. */
static void
return make_cleanup (do_value_free_to_mark, mark);
}
-/* Helper for make_cleanup_value_free. */
-
-static void
-do_value_free (void *value)
-{
- value_free ((struct value *) value);
-}
-
-/* Free VALUE. */
-
-struct cleanup *
-make_cleanup_value_free (struct value *value)
-{
- return make_cleanup (do_value_free, value);
-}
-
-/* Helper for make_cleanup_free_so. */
-
-static void
-do_free_so (void *arg)
-{
- struct so_list *so = (struct so_list *) arg;
-
- free_so (so);
-}
-
-/* Make cleanup handler calling free_so for SO. */
-
-struct cleanup *
-make_cleanup_free_so (struct so_list *so)
-{
- return make_cleanup (do_free_so, so);
-}
-
-/* Helper for make_cleanup_restore_current_language. */
-
-static void
-do_restore_current_language (void *p)
-{
- enum language saved_lang = (enum language) (uintptr_t) p;
-
- set_language (saved_lang);
-}
-
-/* Remember the current value of CURRENT_LANGUAGE and make it restored when
- the cleanup is run. */
-
-struct cleanup *
-make_cleanup_restore_current_language (void)
-{
- enum language saved_lang = current_language->la_language;
-
- return make_cleanup (do_restore_current_language,
- (void *) (uintptr_t) saved_lang);
-}
-
-/* Helper function for make_cleanup_clear_parser_state. */
-
-static void
-do_clear_parser_state (void *ptr)
-{
- struct parser_state **p = (struct parser_state **) ptr;
-
- *p = NULL;
-}
-
-/* Clean (i.e., set to NULL) the parser state variable P. */
-
-struct cleanup *
-make_cleanup_clear_parser_state (struct parser_state **p)
-{
- return make_cleanup (do_clear_parser_state, (void *) p);
-}
-
/* This function is useful for cleanups.
Do
(*deprecated_warning_hook) (string, args);
else
{
- struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
-
+ gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
if (target_supports_terminal_ours ())
{
- make_cleanup_restore_target_terminal ();
- target_terminal_ours_for_output ();
+ term_state.emplace ();
+ target_terminal::ours_for_output ();
}
if (filtered_printing_initialized ())
wrap_here (""); /* Force out any buffered output. */
fputs_unfiltered (warning_pre_print, gdb_stderr);
vfprintf_unfiltered (gdb_stderr, string, args);
fprintf_unfiltered (gdb_stderr, "\n");
-
- do_cleanups (old_chain);
}
}
}
void
-error_stream (struct ui_file *stream)
+error_stream (const string_file &stream)
{
- char *message = ui_file_xstrdup (stream, NULL);
-
- make_cleanup (xfree, message);
- error (("%s"), message);
+ error (("%s"), stream.c_str ());
}
/* Emit a message and abort. */
static void ATTRIBUTE_NORETURN
abort_with_message (const char *msg)
{
- if (gdb_stderr == NULL)
+ if (current_ui == NULL)
fputs (msg, stderr);
else
fputs_unfiltered (msg, gdb_stderr);
static int dejavu;
int quit_p;
int dump_core_p;
- char *reason;
- struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+ std::string reason;
/* Don't allow infinite error/warning recursion. */
{
style similar to a compiler error message. Include extra detail
so that the user knows that they are living on the edge. */
{
- char *msg;
-
- msg = xstrvprintf (fmt, ap);
- reason = xstrprintf ("%s:%d: %s: %s\n"
- "A problem internal to GDB has been detected,\n"
- "further debugging may prove unreliable.",
- file, line, problem->name, msg);
- xfree (msg);
- make_cleanup (xfree, reason);
+ std::string msg = string_vprintf (fmt, ap);
+ reason = string_printf ("%s:%d: %s: %s\n"
+ "A problem internal to GDB has been detected,\n"
+ "further debugging may prove unreliable.",
+ file, line, problem->name, msg.c_str ());
}
/* Fall back to abort_with_message if gdb_stderr is not set up. */
- if (gdb_stderr == NULL)
+ if (current_ui == NULL)
{
- fputs (reason, stderr);
+ fputs (reason.c_str (), stderr);
abort_with_message ("\n");
}
/* Try to get the message out and at the start of a new line. */
+ gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
if (target_supports_terminal_ours ())
{
- make_cleanup_restore_target_terminal ();
- target_terminal_ours_for_output ();
+ term_state.emplace ();
+ target_terminal::ours_for_output ();
}
if (filtered_printing_initialized ())
begin_line ();
if (problem->should_quit != internal_problem_ask
|| !confirm
|| !filtered_printing_initialized ())
- fprintf_unfiltered (gdb_stderr, "%s\n", reason);
+ fprintf_unfiltered (gdb_stderr, "%s\n", reason.c_str ());
if (problem->should_quit == internal_problem_ask)
{
if (!confirm || !filtered_printing_initialized ())
quit_p = 1;
else
- quit_p = query (_("%s\nQuit this debugging session? "), reason);
+ quit_p = query (_("%s\nQuit this debugging session? "),
+ reason.c_str ());
}
else if (problem->should_quit == internal_problem_yes)
quit_p = 1;
if (problem->should_dump_core == internal_problem_ask)
{
- if (!can_dump_core_warn (LIMIT_MAX, reason))
+ if (!can_dump_core_warn (LIMIT_MAX, reason.c_str ()))
dump_core_p = 0;
else if (!filtered_printing_initialized ())
dump_core_p = 1;
/* Default (yes/batch case) is to dump core. This leaves a GDB
`dropping' so that it is easier to see that something went
wrong in GDB. */
- dump_core_p = query (_("%s\nCreate a core file of GDB? "), reason);
+ dump_core_p = query (_("%s\nCreate a core file of GDB? "),
+ reason.c_str ());
}
}
else if (problem->should_dump_core == internal_problem_yes)
- dump_core_p = can_dump_core_warn (LIMIT_MAX, reason);
+ dump_core_p = can_dump_core_warn (LIMIT_MAX, reason.c_str ());
else if (problem->should_dump_core == internal_problem_no)
dump_core_p = 0;
else
}
dejavu = 0;
- do_cleanups (cleanup);
}
static struct internal_problem internal_error_problem = {
/* Dummy functions to keep add_prefix_cmd happy. */
static void
-set_internal_problem_cmd (char *args, int from_tty)
+set_internal_problem_cmd (const char *args, int from_tty)
{
}
static void
-show_internal_problem_cmd (char *args, int from_tty)
+show_internal_problem_cmd (const char *args, int from_tty)
{
}
show_doc = xstrprintf (_("Show what GDB does when %s is detected."),
problem->name);
- add_prefix_cmd ((char*) problem->name,
+ add_prefix_cmd (problem->name,
class_maintenance, set_internal_problem_cmd, set_doc,
set_cmd_list,
concat ("maintenance set ", problem->name, " ",
(char *) NULL),
0/*allow-unknown*/, &maintenance_set_cmdlist);
- add_prefix_cmd ((char*) problem->name,
+ add_prefix_cmd (problem->name,
class_maintenance, show_internal_problem_cmd, show_doc,
show_cmd_list,
concat ("maintenance show ", problem->name, " ",
}
/* Return a newly allocated string, containing the PREFIX followed
- by the system error message for errno (separated by a colon).
-
- The result must be deallocated after use. */
+ by the system error message for errno (separated by a colon). */
-static char *
+static std::string
perror_string (const char *prefix)
{
char *err;
- char *combined;
err = safe_strerror (errno);
- combined = (char *) xmalloc (strlen (err) + strlen (prefix) + 3);
- strcpy (combined, prefix);
- strcat (combined, ": ");
- strcat (combined, err);
-
- return combined;
+ return std::string (prefix) + ": " + err;
}
/* Print the system error message for errno, and also mention STRING
void
throw_perror_with_name (enum errors errcode, const char *string)
{
- char *combined;
-
- combined = perror_string (string);
- make_cleanup (xfree, combined);
+ std::string combined = perror_string (string);
/* I understand setting these is a matter of taste. Still, some people
may clear errno but not know about bfd_error. Doing this here is not
bfd_set_error (bfd_error_no_error);
errno = 0;
- throw_error (errcode, _("%s."), combined);
+ throw_error (errcode, _("%s."), combined.c_str ());
}
/* See throw_perror_with_name, ERRCODE defaults here to GENERIC_ERROR. */
void
perror_warning_with_name (const char *string)
{
- char *combined;
-
- combined = perror_string (string);
- warning (_("%s"), combined);
- xfree (combined);
+ std::string combined = perror_string (string);
+ warning (_("%s"), combined.c_str ());
}
/* Print the system error message for ERRCODE, and also mention STRING
if (sync_quit_force_run)
{
sync_quit_force_run = 0;
- quit_force (NULL, stdin == instream);
+ quit_force (NULL, 0);
}
#ifdef __MSDOS__
\f
-/* A cleanup function that calls regfree. */
+/* An RAII class that sets up to handle input and then tears down
+ during destruction. */
-static void
-do_regfree_cleanup (void *r)
+class scoped_input_handler
{
- regfree ((regex_t *) r);
-}
-
-/* Create a new cleanup that frees the compiled regular expression R. */
+public:
-struct cleanup *
-make_regfree_cleanup (regex_t *r)
-{
- return make_cleanup (do_regfree_cleanup, r);
-}
-
-/* Return an xmalloc'd error message resulting from a regular
- expression compilation failure. */
-
-char *
-get_regcomp_error (int code, regex_t *rx)
-{
- size_t length = regerror (code, rx, NULL, 0);
- char *result = (char *) xmalloc (length);
-
- regerror (code, rx, result, length);
- return result;
-}
+ scoped_input_handler ()
+ : m_quit_handler (&quit_handler, default_quit_handler),
+ m_ui (NULL)
+ {
+ target_terminal::ours ();
+ ui_register_input_event_handler (current_ui);
+ if (current_ui->prompt_state == PROMPT_BLOCKED)
+ m_ui = current_ui;
+ }
-/* Compile a regexp and throw an exception on error. This returns a
- cleanup to free the resulting pattern on success. RX must not be
- NULL. */
+ ~scoped_input_handler ()
+ {
+ if (m_ui != NULL)
+ ui_unregister_input_event_handler (m_ui);
+ }
-struct cleanup *
-compile_rx_or_error (regex_t *pattern, const char *rx, const char *message)
-{
- int code;
+ DISABLE_COPY_AND_ASSIGN (scoped_input_handler);
- gdb_assert (rx != NULL);
+private:
- code = regcomp (pattern, rx, REG_NOSUB);
- if (code != 0)
- {
- char *err = get_regcomp_error (code, pattern);
+ /* Save and restore the terminal state. */
+ target_terminal::scoped_restore_terminal_state m_term_state;
- make_cleanup (xfree, err);
- error (("%s: %s"), message, err);
- }
+ /* Save and restore the quit handler. */
+ scoped_restore_tmpl<quit_handler_ftype *> m_quit_handler;
- return make_regfree_cleanup (pattern);
-}
+ /* The saved UI, if non-NULL. */
+ struct ui *m_ui;
+};
\f
static int ATTRIBUTE_PRINTF (1, 0)
defaulted_query (const char *ctlstr, const char defchar, va_list args)
{
- int ans2;
int retval;
int def_value;
char def_answer, not_def_answer;
- char *y_string, *n_string, *question, *prompt;
- /* Used to add duration we waited for user to respond to
- prompt_for_continue_wait_time. */
- struct timeval prompt_started, prompt_ended, prompt_delta;
- struct cleanup *old_chain;
+ const char *y_string, *n_string;
/* Set up according to which answer is the default. */
if (defchar == '\0')
if (!confirm || server_command)
return def_value;
- old_chain = make_cleanup_restore_target_terminal ();
-
/* If input isn't coming from the user directly, just say what
question we're asking, and then answer the default automatically. This
way, important error messages don't get lost when talking to GDB
over a pipe. */
- if (! input_from_terminal_p ())
+ if (current_ui->instream != current_ui->stdin_stream
+ || !input_interactive_p (current_ui)
+ /* Restrict queries to the main UI. */
+ || current_ui != main_ui)
{
- target_terminal_ours_for_output ();
+ target_terminal::scoped_restore_terminal_state term_state;
+ target_terminal::ours_for_output ();
wrap_here ("");
vfprintf_filtered (gdb_stdout, ctlstr, args);
y_string, n_string, def_answer);
gdb_flush (gdb_stdout);
- do_cleanups (old_chain);
return def_value;
}
if (deprecated_query_hook)
{
- int res;
-
- res = deprecated_query_hook (ctlstr, args);
- do_cleanups (old_chain);
- return res;
+ target_terminal::scoped_restore_terminal_state term_state;
+ return deprecated_query_hook (ctlstr, args);
}
/* Format the question outside of the loop, to avoid reusing args. */
- question = xstrvprintf (ctlstr, args);
- make_cleanup (xfree, question);
- prompt = xstrprintf (_("%s%s(%s or %s) %s"),
- annotation_level > 1 ? "\n\032\032pre-query\n" : "",
- question, y_string, n_string,
- annotation_level > 1 ? "\n\032\032query\n" : "");
- make_cleanup (xfree, prompt);
+ std::string question = string_vprintf (ctlstr, args);
+ std::string prompt
+ = string_printf (_("%s%s(%s or %s) %s"),
+ annotation_level > 1 ? "\n\032\032pre-query\n" : "",
+ question.c_str (), y_string, n_string,
+ annotation_level > 1 ? "\n\032\032query\n" : "");
- /* Used for calculating time spend waiting for user. */
- gettimeofday (&prompt_started, NULL);
+ /* Used to add duration we waited for user to respond to
+ prompt_for_continue_wait_time. */
+ using namespace std::chrono;
+ steady_clock::time_point prompt_started = steady_clock::now ();
- /* We'll need to handle input. */
- target_terminal_ours ();
- make_cleanup_override_quit_handler (default_quit_handler);
+ scoped_input_handler prepare_input;
while (1)
{
char *response, answer;
gdb_flush (gdb_stdout);
- response = gdb_readline_wrapper (prompt);
+ response = gdb_readline_wrapper (prompt.c_str ());
if (response == NULL) /* C-d */
{
}
/* Add time spend in this routine to prompt_for_continue_wait_time. */
- gettimeofday (&prompt_ended, NULL);
- timeval_sub (&prompt_delta, &prompt_ended, &prompt_started);
- timeval_add (&prompt_for_continue_wait_time,
- &prompt_for_continue_wait_time, &prompt_delta);
+ prompt_for_continue_wait_time += steady_clock::now () - prompt_started;
if (annotation_level > 1)
printf_filtered (("\n\032\032post-query\n"));
- do_cleanups (old_chain);
return retval;
}
\f
static int
host_char_to_target (struct gdbarch *gdbarch, int c, int *target_c)
{
- struct obstack host_data;
char the_char = c;
- struct cleanup *cleanups;
int result = 0;
- obstack_init (&host_data);
- cleanups = make_cleanup_obstack_free (&host_data);
+ auto_obstack host_data;
convert_between_encodings (target_charset (gdbarch), host_charset (),
(gdb_byte *) &the_char, 1, 1,
*target_c = *(char *) obstack_base (&host_data);
}
- do_cleanups (cleanups);
return result;
}
/* String to indent by if the wrap occurs. Must not be NULL if wrap_column
is non-zero. */
-static char *wrap_indent;
+static const char *wrap_indent;
/* Column number on the screen where wrap_buffer begins, or 0 if wrapping
is not in effect. */
static int wrap_column;
\f
-/* Inialize the number of lines per page and chars per line. */
+/* Initialize the number of lines per page and chars per line. */
void
init_page_info (void)
/* Readline should have fetched the termcap entry for us.
Only try to use tgetnum function if rl_get_screen_size
did not return a useful value. */
- if (((rows <= 0) && (tgetnum ("li") < 0))
+ if (((rows <= 0) && (tgetnum ((char *) "li") < 0))
/* Also disable paging if inside Emacs. $EMACS was used
before Emacs v25.1, $INSIDE_EMACS is used since then. */
|| getenv ("EMACS") || getenv ("INSIDE_EMACS"))
return wrap_buffer != NULL;
}
-/* Helper for make_cleanup_restore_page_info. */
-
-static void
-do_restore_page_info_cleanup (void *arg)
-{
- set_screen_size ();
- set_width ();
-}
-
-/* Provide cleanup for restoring the terminal size. */
-
-struct cleanup *
-make_cleanup_restore_page_info (void)
+set_batch_flag_and_restore_page_info::set_batch_flag_and_restore_page_info ()
+ : m_save_lines_per_page (lines_per_page),
+ m_save_chars_per_line (chars_per_line),
+ m_save_batch_flag (batch_flag)
{
- struct cleanup *back_to;
-
- back_to = make_cleanup (do_restore_page_info_cleanup, NULL);
- make_cleanup_restore_uinteger (&lines_per_page);
- make_cleanup_restore_uinteger (&chars_per_line);
-
- return back_to;
+ batch_flag = 1;
+ init_page_info ();
}
-/* Temporarily set BATCH_FLAG and the associated unlimited terminal size.
- Provide cleanup for restoring the original state. */
-
-struct cleanup *
-set_batch_flag_and_make_cleanup_restore_page_info (void)
+set_batch_flag_and_restore_page_info::~set_batch_flag_and_restore_page_info ()
{
- struct cleanup *back_to = make_cleanup_restore_page_info ();
-
- make_cleanup_restore_integer (&batch_flag);
- batch_flag = 1;
- init_page_info ();
+ batch_flag = m_save_batch_flag;
+ chars_per_line = m_save_chars_per_line;
+ lines_per_page = m_save_lines_per_page;
- return back_to;
+ set_screen_size ();
+ set_width ();
}
/* Set the screen size based on LINES_PER_PAGE and CHARS_PER_LINE. */
}
static void
-set_width_command (char *args, int from_tty, struct cmd_list_element *c)
+set_width_command (const char *args, int from_tty, struct cmd_list_element *c)
{
set_screen_size ();
set_width ();
}
static void
-set_height_command (char *args, int from_tty, struct cmd_list_element *c)
+set_height_command (const char *args, int from_tty, struct cmd_list_element *c)
{
set_screen_size ();
}
{
char *ignore;
char cont_prompt[120];
+ struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
/* Used to add duration we waited for user to respond to
prompt_for_continue_wait_time. */
- struct timeval prompt_started, prompt_ended, prompt_delta;
- struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
-
- gettimeofday (&prompt_started, NULL);
+ using namespace std::chrono;
+ steady_clock::time_point prompt_started = steady_clock::now ();
if (annotation_level > 1)
printf_unfiltered (("\n\032\032pre-prompt-for-continue\n"));
beyond the end of the screen. */
reinitialize_more_filter ();
- /* We'll need to handle input. */
- make_cleanup_restore_target_terminal ();
- target_terminal_ours ();
- make_cleanup_override_quit_handler (default_quit_handler);
+ scoped_input_handler prepare_input;
/* Call gdb_readline_wrapper, not readline, in order to keep an
event loop running. */
make_cleanup (xfree, ignore);
/* Add time spend in this routine to prompt_for_continue_wait_time. */
- gettimeofday (&prompt_ended, NULL);
- timeval_sub (&prompt_delta, &prompt_ended, &prompt_started);
- timeval_add (&prompt_for_continue_wait_time,
- &prompt_for_continue_wait_time, &prompt_delta);
+ prompt_for_continue_wait_time += steady_clock::now () - prompt_started;
if (annotation_level > 1)
printf_unfiltered (("\n\032\032post-prompt-for-continue\n"));
do_cleanups (old_chain);
}
-/* Initalize timer to keep track of how long we waited for the user. */
+/* Initialize timer to keep track of how long we waited for the user. */
void
reset_prompt_for_continue_wait_time (void)
{
- static const struct timeval zero_timeval = { 0 };
+ using namespace std::chrono;
- prompt_for_continue_wait_time = zero_timeval;
+ prompt_for_continue_wait_time = steady_clock::duration::zero ();
}
/* Fetch the cumulative time spent in prompt_for_continue. */
-struct timeval
-get_prompt_for_continue_wait_time (void)
+std::chrono::steady_clock::duration
+get_prompt_for_continue_wait_time ()
{
return prompt_for_continue_wait_time;
}
used to force out output from the wrap_buffer. */
void
-wrap_here (char *indent)
+wrap_here (const char *indent)
{
/* This should have been allocated, but be paranoid anyway. */
if (!wrap_buffer)
|| batch_flag
|| (lines_per_page == UINT_MAX && chars_per_line == UINT_MAX)
|| top_level_interpreter () == NULL
- || ui_out_is_mi_like_p (interp_ui_out (top_level_interpreter ())))
+ || interp_ui_out (top_level_interpreter ())->is_mi_like_p ())
{
fputs_unfiltered (linebuffer, stream);
return;
/* Print prefix and suffix after each line. */
static int new_line = 1;
static int return_p = 0;
- static char *prev_prefix = "";
- static char *prev_suffix = "";
+ static const char *prev_prefix = "";
+ static const char *prev_suffix = "";
if (*string == '\n')
return_p = 0;
vfprintf_maybe_filtered (struct ui_file *stream, const char *format,
va_list args, int filter)
{
- char *linebuffer;
- struct cleanup *old_cleanups;
-
- linebuffer = xstrvprintf (format, args);
- old_cleanups = make_cleanup (xfree, linebuffer);
- fputs_maybe_filtered (linebuffer, stream, filter);
- do_cleanups (old_cleanups);
+ std::string linebuffer = string_vprintf (format, args);
+ fputs_maybe_filtered (linebuffer.c_str (), stream, filter);
}
void
vfprintf_unfiltered (struct ui_file *stream, const char *format, va_list args)
{
- char *linebuffer;
- struct cleanup *old_cleanups;
-
- linebuffer = xstrvprintf (format, args);
- old_cleanups = make_cleanup (xfree, linebuffer);
+ std::string linebuffer = string_vprintf (format, args);
if (debug_timestamp && stream == gdb_stdlog)
{
- struct timeval tm;
- char *timestamp;
+ using namespace std::chrono;
int len, need_nl;
- gettimeofday (&tm, NULL);
+ steady_clock::time_point now = steady_clock::now ();
+ seconds s = duration_cast<seconds> (now.time_since_epoch ());
+ microseconds us = duration_cast<microseconds> (now.time_since_epoch () - s);
- len = strlen (linebuffer);
+ len = linebuffer.size ();
need_nl = (len > 0 && linebuffer[len - 1] != '\n');
- timestamp = xstrprintf ("%ld:%ld %s%s",
- (long) tm.tv_sec, (long) tm.tv_usec,
- linebuffer,
- need_nl ? "\n": "");
- make_cleanup (xfree, timestamp);
- fputs_unfiltered (timestamp, stream);
+ std::string timestamp = string_printf ("%ld.%06ld %s%s",
+ (long) s.count (),
+ (long) us.count (),
+ linebuffer.c_str (),
+ need_nl ? "\n": "");
+ fputs_unfiltered (timestamp.c_str (), stream);
}
else
- fputs_unfiltered (linebuffer, stream);
- do_cleanups (old_cleanups);
+ fputs_unfiltered (linebuffer.c_str (), stream);
}
void
}
}
-/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
- differences in whitespace. Returns 0 if they match, non-zero if they
- don't (slightly different than strcmp()'s range of return values).
+/* True if CH is a character that can be part of a symbol name. I.e.,
+ either a number, a letter, or a '_'. */
- As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
- This "feature" is useful when searching for matching C++ function names
- (such as if the user types 'break FOO', where FOO is a mangled C++
- function). */
+static bool
+valid_identifier_name_char (int ch)
+{
+ return (isalnum (ch) || ch == '_');
+}
-int
-strcmp_iw (const char *string1, const char *string2)
+/* Skip to end of token, or to END, whatever comes first. Input is
+ assumed to be a C++ operator name. */
+
+static const char *
+cp_skip_operator_token (const char *token, const char *end)
{
- while ((*string1 != '\0') && (*string2 != '\0'))
+ const char *p = token;
+ while (p != end && !isspace (*p) && *p != '(')
{
- while (isspace (*string1))
+ if (valid_identifier_name_char (*p))
{
- string1++;
+ while (p != end && valid_identifier_name_char (*p))
+ p++;
+ return p;
}
- while (isspace (*string2))
+ else
{
+ /* Note, ordered such that among ops that share a prefix,
+ longer comes first. This is so that the loop below can
+ bail on first match. */
+ static const char *ops[] =
+ {
+ "[",
+ "]",
+ "~",
+ ",",
+ "-=", "--", "->", "-",
+ "+=", "++", "+",
+ "*=", "*",
+ "/=", "/",
+ "%=", "%",
+ "|=", "||", "|",
+ "&=", "&&", "&",
+ "^=", "^",
+ "!=", "!",
+ "<<=", "<=", "<<", "<",
+ ">>=", ">=", ">>", ">",
+ "==", "=",
+ };
+
+ for (const char *op : ops)
+ {
+ size_t oplen = strlen (op);
+ size_t lencmp = std::min<size_t> (oplen, end - p);
+
+ if (strncmp (p, op, lencmp) == 0)
+ return p + lencmp;
+ }
+ /* Some unidentified character. Return it. */
+ return p + 1;
+ }
+ }
+
+ return p;
+}
+
+/* Advance STRING1/STRING2 past whitespace. */
+
+static void
+skip_ws (const char *&string1, const char *&string2, const char *end_str2)
+{
+ while (isspace (*string1))
+ string1++;
+ while (string2 < end_str2 && isspace (*string2))
+ string2++;
+}
+
+/* True if STRING points at the start of a C++ operator name. START
+ is the start of the string that STRING points to, hence when
+ reading backwards, we must not read any character before START. */
+
+static bool
+cp_is_operator (const char *string, const char *start)
+{
+ return ((string == start
+ || !valid_identifier_name_char (string[-1]))
+ && strncmp (string, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+ && !valid_identifier_name_char (string[CP_OPERATOR_LEN]));
+}
+
+/* If *NAME points at an ABI tag, skip it and return true. Otherwise
+ leave *NAME unmodified and return false. (see GCC's abi_tag
+ attribute), such names are demangled as e.g.,
+ "function[abi:cxx11]()". */
+
+static bool
+skip_abi_tag (const char **name)
+{
+ const char *p = *name;
+
+ if (startswith (p, "[abi:"))
+ {
+ p += 5;
+
+ while (valid_identifier_name_char (*p))
+ p++;
+
+ if (*p == ']')
+ {
+ p++;
+ *name = p;
+ return true;
+ }
+ }
+ return false;
+}
+
+/* See utils.h. */
+
+int
+strncmp_iw_with_mode (const char *string1, const char *string2,
+ size_t string2_len, strncmp_iw_mode mode,
+ enum language language,
+ completion_match_for_lcd *match_for_lcd)
+{
+ const char *string1_start = string1;
+ const char *end_str2 = string2 + string2_len;
+ bool skip_spaces = true;
+ bool have_colon_op = (language == language_cplus
+ || language == language_rust
+ || language == language_fortran);
+
+ while (1)
+ {
+ if (skip_spaces
+ || ((isspace (*string1) && !valid_identifier_name_char (*string2))
+ || (isspace (*string2) && !valid_identifier_name_char (*string1))))
+ {
+ skip_ws (string1, string2, end_str2);
+ skip_spaces = false;
+ }
+
+ /* Skip [abi:cxx11] tags in the symbol name if the lookup name
+ doesn't include them. E.g.:
+
+ string1: function[abi:cxx1](int)
+ string2: function
+
+ string1: function[abi:cxx1](int)
+ string2: function(int)
+
+ string1: Struct[abi:cxx1]::function()
+ string2: Struct::function()
+
+ string1: function(Struct[abi:cxx1], int)
+ string2: function(Struct, int)
+ */
+ if (string2 == end_str2
+ || (*string2 != '[' && !valid_identifier_name_char (*string2)))
+ {
+ const char *abi_start = string1;
+
+ /* There can be more than one tag. */
+ while (*string1 == '[' && skip_abi_tag (&string1))
+ ;
+
+ if (match_for_lcd != NULL && abi_start != string1)
+ match_for_lcd->mark_ignored_range (abi_start, string1);
+
+ while (isspace (*string1))
+ string1++;
+ }
+
+ if (*string1 == '\0' || string2 == end_str2)
+ break;
+
+ /* Handle the :: operator. */
+ if (have_colon_op && string1[0] == ':' && string1[1] == ':')
+ {
+ if (*string2 != ':')
+ return 1;
+
+ string1++;
string2++;
+
+ if (string2 == end_str2)
+ break;
+
+ if (*string2 != ':')
+ return 1;
+
+ string1++;
+ string2++;
+
+ while (isspace (*string1))
+ string1++;
+ while (string2 < end_str2 && isspace (*string2))
+ string2++;
+ continue;
+ }
+
+ /* Handle C++ user-defined operators. */
+ else if (language == language_cplus
+ && *string1 == 'o')
+ {
+ if (cp_is_operator (string1, string1_start))
+ {
+ /* An operator name in STRING1. Check STRING2. */
+ size_t cmplen
+ = std::min<size_t> (CP_OPERATOR_LEN, end_str2 - string2);
+ if (strncmp (string1, string2, cmplen) != 0)
+ return 1;
+
+ string1 += cmplen;
+ string2 += cmplen;
+
+ if (string2 != end_str2)
+ {
+ /* Check for "operatorX" in STRING2. */
+ if (valid_identifier_name_char (*string2))
+ return 1;
+
+ skip_ws (string1, string2, end_str2);
+ }
+
+ /* Handle operator(). */
+ if (*string1 == '(')
+ {
+ if (string2 == end_str2)
+ {
+ if (mode == strncmp_iw_mode::NORMAL)
+ return 0;
+ else
+ {
+ /* Don't break for the regular return at the
+ bottom, because "operator" should not
+ match "operator()", since this open
+ parentheses is not the parameter list
+ start. */
+ return *string1 != '\0';
+ }
+ }
+
+ if (*string1 != *string2)
+ return 1;
+
+ string1++;
+ string2++;
+ }
+
+ while (1)
+ {
+ skip_ws (string1, string2, end_str2);
+
+ /* Skip to end of token, or to END, whatever comes
+ first. */
+ const char *end_str1 = string1 + strlen (string1);
+ const char *p1 = cp_skip_operator_token (string1, end_str1);
+ const char *p2 = cp_skip_operator_token (string2, end_str2);
+
+ cmplen = std::min (p1 - string1, p2 - string2);
+ if (p2 == end_str2)
+ {
+ if (strncmp (string1, string2, cmplen) != 0)
+ return 1;
+ }
+ else
+ {
+ if (p1 - string1 != p2 - string2)
+ return 1;
+ if (strncmp (string1, string2, cmplen) != 0)
+ return 1;
+ }
+
+ string1 += cmplen;
+ string2 += cmplen;
+
+ if (*string1 == '\0' || string2 == end_str2)
+ break;
+ if (*string1 == '(' || *string2 == '(')
+ break;
+ }
+
+ continue;
+ }
}
+
if (case_sensitivity == case_sensitive_on && *string1 != *string2)
break;
if (case_sensitivity == case_sensitive_off
&& (tolower ((unsigned char) *string1)
!= tolower ((unsigned char) *string2)))
break;
- if (*string1 != '\0')
+
+ /* If we see any non-whitespace, non-identifier-name character
+ (any of "()<>*&" etc.), then skip spaces the next time
+ around. */
+ if (!isspace (*string1) && !valid_identifier_name_char (*string1))
+ skip_spaces = true;
+
+ string1++;
+ string2++;
+ }
+
+ if (string2 == end_str2)
+ {
+ if (mode == strncmp_iw_mode::NORMAL)
{
- string1++;
- string2++;
+ /* Strip abi tag markers from the matched symbol name.
+ Usually the ABI marker will be found on function name
+ (automatically added because the function returns an
+ object marked with an ABI tag). However, it's also
+ possible to see a marker in one of the function
+ parameters, for example.
+
+ string2 (lookup name):
+ func
+ symbol name:
+ function(some_struct[abi:cxx11], int)
+
+ and for completion LCD computation we want to say that
+ the match was for:
+ function(some_struct, int)
+ */
+ if (match_for_lcd != NULL)
+ {
+ while ((string1 = strstr (string1, "[abi:")) != NULL)
+ {
+ const char *abi_start = string1;
+
+ /* There can be more than one tag. */
+ while (skip_abi_tag (&string1) && *string1 == '[')
+ ;
+
+ if (abi_start != string1)
+ match_for_lcd->mark_ignored_range (abi_start, string1);
+ }
+ }
+
+ return 0;
}
+ else
+ return (*string1 != '\0' && *string1 != '(');
}
- return (*string1 != '\0' && *string1 != '(') || (*string2 != '\0');
+ else
+ return 1;
+}
+
+/* See utils.h. */
+
+int
+strncmp_iw (const char *string1, const char *string2, size_t string2_len)
+{
+ return strncmp_iw_with_mode (string1, string2, string2_len,
+ strncmp_iw_mode::NORMAL, language_minimal);
+}
+
+/* See utils.h. */
+
+int
+strcmp_iw (const char *string1, const char *string2)
+{
+ return strncmp_iw_with_mode (string1, string2, strlen (string2),
+ strncmp_iw_mode::MATCH_PARAMS, language_minimal);
}
/* This is like strcmp except that it ignores whitespace and treats
** at index 0.
*/
int
-subset_compare (char *string_to_compare, char *template_string)
+subset_compare (const char *string_to_compare, const char *template_string)
{
int match;
&setdebuglist, &showdebuglist);
}
+/* See utils.h. */
+
+CORE_ADDR
+address_significant (gdbarch *gdbarch, CORE_ADDR addr)
+{
+ /* Truncate address to the significant bits of a target address,
+ avoiding shifts larger or equal than the width of a CORE_ADDR.
+ The local variable ADDR_BIT stops the compiler reporting a shift
+ overflow when it won't occur. */
+ int addr_bit = gdbarch_significant_addr_bit (gdbarch);
+
+ if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
+ addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
+
+ return addr;
+}
+
const char *
paddress (struct gdbarch *gdbarch, CORE_ADDR addr)
{
return addr;
}
-char *
+gdb::unique_xmalloc_ptr<char>
gdb_realpath (const char *filename)
{
/* On most hosts, we rely on canonicalize_file_name to compute
we might not be able to display the original casing in a given
path. */
if (len > 0 && len < MAX_PATH)
- return xstrdup (buf);
+ return gdb::unique_xmalloc_ptr<char> (xstrdup (buf));
}
#else
{
char *rp = canonicalize_file_name (filename);
if (rp != NULL)
- return rp;
+ return gdb::unique_xmalloc_ptr<char> (rp);
}
#endif
/* This system is a lost cause, just dup the buffer. */
- return xstrdup (filename);
+ return gdb::unique_xmalloc_ptr<char> (xstrdup (filename));
}
+#if GDB_SELF_TEST
+
+static void
+gdb_realpath_check_trailer (const char *input, const char *trailer)
+{
+ gdb::unique_xmalloc_ptr<char> result = gdb_realpath (input);
+
+ size_t len = strlen (result.get ());
+ size_t trail_len = strlen (trailer);
+
+ SELF_CHECK (len >= trail_len
+ && strcmp (result.get () + len - trail_len, trailer) == 0);
+}
+
+static void
+gdb_realpath_tests ()
+{
+ /* A file which contains a directory prefix. */
+ gdb_realpath_check_trailer ("./xfullpath.exp", "/xfullpath.exp");
+ /* A file which contains a directory prefix. */
+ gdb_realpath_check_trailer ("../../defs.h", "/defs.h");
+ /* A one-character filename. */
+ gdb_realpath_check_trailer ("./a", "/a");
+ /* A file in the root directory. */
+ gdb_realpath_check_trailer ("/root_file_which_should_exist",
+ "/root_file_which_should_exist");
+ /* A file which does not have a directory prefix. */
+ gdb_realpath_check_trailer ("xfullpath.exp", "xfullpath.exp");
+ /* A one-char filename without any directory prefix. */
+ gdb_realpath_check_trailer ("a", "a");
+ /* An empty filename. */
+ gdb_realpath_check_trailer ("", "");
+}
+
+#endif /* GDB_SELF_TEST */
+
/* Return a copy of FILENAME, with its directory prefix canonicalized
by gdb_realpath. */
-char *
+gdb::unique_xmalloc_ptr<char>
gdb_realpath_keepfile (const char *filename)
{
const char *base_name = lbasename (filename);
char *dir_name;
- char *real_path;
char *result;
/* Extract the basename of filename, and return immediately
a copy of filename if it does not contain any directory prefix. */
if (base_name == filename)
- return xstrdup (filename);
+ return gdb::unique_xmalloc_ptr<char> (xstrdup (filename));
dir_name = (char *) alloca ((size_t) (base_name - filename + 2));
/* Allocate enough space to store the dir_name + plus one extra
/* Canonicalize the directory prefix, and build the resulting
filename. If the dirname realpath already contains an ending
directory separator, avoid doubling it. */
- real_path = gdb_realpath (dir_name);
+ gdb::unique_xmalloc_ptr<char> path_storage = gdb_realpath (dir_name);
+ const char *real_path = path_storage.get ();
if (IS_DIR_SEPARATOR (real_path[strlen (real_path) - 1]))
result = concat (real_path, base_name, (char *) NULL);
else
result = concat (real_path, SLASH_STRING, base_name, (char *) NULL);
- xfree (real_path);
- return result;
+ return gdb::unique_xmalloc_ptr<char> (result);
}
/* Return PATH in absolute form, performing tilde-expansion if necessary.
PATH cannot be NULL or the empty string.
- This does not resolve symlinks however, use gdb_realpath for that.
- Space for the result is allocated with malloc.
- If the path is already absolute, it is strdup'd.
- If there is a problem computing the absolute path, the path is returned
- unchanged (still strdup'd). */
+ This does not resolve symlinks however, use gdb_realpath for that. */
-char *
+gdb::unique_xmalloc_ptr<char>
gdb_abspath (const char *path)
{
gdb_assert (path != NULL && path[0] != '\0');
if (path[0] == '~')
- return tilde_expand (path);
+ return gdb::unique_xmalloc_ptr<char> (tilde_expand (path));
if (IS_ABSOLUTE_PATH (path))
- return xstrdup (path);
+ return gdb::unique_xmalloc_ptr<char> (xstrdup (path));
/* Beware the // my son, the Emacs barfs, the botch that catch... */
- return concat (current_directory,
- IS_DIR_SEPARATOR (current_directory[strlen (current_directory) - 1])
- ? "" : SLASH_STRING,
- path, (char *) NULL);
+ return gdb::unique_xmalloc_ptr<char>
+ (concat (current_directory,
+ IS_DIR_SEPARATOR (current_directory[strlen (current_directory) - 1])
+ ? "" : SLASH_STRING,
+ path, (char *) NULL));
}
ULONGEST
/* Simple, portable version of dirname that does not modify its
argument. */
-char *
+std::string
ldirname (const char *filename)
{
+ std::string dirname;
const char *base = lbasename (filename);
- char *dirname;
while (base > filename && IS_DIR_SEPARATOR (base[-1]))
--base;
if (base == filename)
- return NULL;
+ return dirname;
- dirname = (char *) xmalloc (base - filename + 2);
- memcpy (dirname, filename, base - filename);
+ dirname = std::string (filename, base - filename);
/* On DOS based file systems, convert "d:foo" to "d:.", so that we
create "d:./bar" later instead of the (different) "d:/bar". */
&& !IS_DIR_SEPARATOR (filename[0]))
dirname[base++ - filename] = '.';
- dirname[base - filename] = '\0';
return dirname;
}
-/* Call libiberty's buildargv, and return the result.
- If buildargv fails due to out-of-memory, call nomem.
- Therefore, the returned value is guaranteed to be non-NULL,
- unless the parameter itself is NULL. */
+/* See utils.h. */
-char **
-gdb_buildargv (const char *s)
+void
+gdb_argv::reset (const char *s)
{
char **argv = buildargv (s);
if (s != NULL && argv == NULL)
malloc_failure (0);
- return argv;
+
+ freeargv (m_argv);
+ m_argv = argv;
}
int
return make_cleanup (do_bpstat_clear_actions_cleanup, NULL);
}
-/* Check for GCC >= 4.x according to the symtab->producer string. Return minor
- version (x) of 4.x in such case. If it is not GCC or it is GCC older than
- 4.x return -1. If it is GCC 5.x or higher return INT_MAX. */
-
-int
-producer_is_gcc_ge_4 (const char *producer)
-{
- int major, minor;
-
- if (! producer_is_gcc (producer, &major, &minor))
- return -1;
- if (major < 4)
- return -1;
- if (major > 4)
- return INT_MAX;
- return minor;
-}
-
-/* Returns nonzero if the given PRODUCER string is GCC and sets the MAJOR
- and MINOR versions when not NULL. Returns zero if the given PRODUCER
- is NULL or it isn't GCC. */
-
-int
-producer_is_gcc (const char *producer, int *major, int *minor)
-{
- const char *cs;
-
- if (producer != NULL && startswith (producer, "GNU "))
- {
- int maj, min;
-
- if (major == NULL)
- major = &maj;
- if (minor == NULL)
- minor = &min;
-
- /* Skip any identifier after "GNU " - such as "C11" "C++" or "Java".
- A full producer string might look like:
- "GNU C 4.7.2"
- "GNU Fortran 4.8.2 20140120 (Red Hat 4.8.2-16) -mtune=generic ..."
- "GNU C++14 5.0.0 20150123 (experimental)"
- */
- cs = &producer[strlen ("GNU ")];
- while (*cs && !isspace (*cs))
- cs++;
- if (*cs && isspace (*cs))
- cs++;
- if (sscanf (cs, "%d.%d", major, minor) == 2)
- return 1;
- }
-
- /* Not recognized as GCC. */
- return 0;
-}
/* Helper for make_cleanup_free_char_ptr_vec. */
return p;
}
-/* Provide a prototype to silence -Wmissing-prototypes. */
-extern initialize_file_ftype _initialize_utils;
-
void
_initialize_utils (void)
{
add_internal_problem_command (&internal_error_problem);
add_internal_problem_command (&internal_warning_problem);
add_internal_problem_command (&demangler_warning_problem);
+
+#if GDB_SELF_TEST
+ selftests::register_test ("gdb_realpath", gdb_realpath_tests);
+#endif
}