X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Futils.c;h=af50cf09d6a07f3b415f7dbbb556a1d141623642;hb=2434b0199ddcd9419c55742552953b0a79d00ebc;hp=b3720f6de39de6259f54ed6842c15b4403a4ab47;hpb=abbffc8924ff61b64d86ee89775c466e59a32c63;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/utils.c b/gdb/utils.c index b3720f6de3..af50cf09d6 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -1,6 +1,6 @@ /* General utility routines for GDB, the GNU debugger. - Copyright (C) 1986-2014 Free Software Foundation, Inc. + Copyright (C) 1986-2017 Free Software Foundation, Inc. This file is part of GDB. @@ -18,7 +18,6 @@ along with this program. If not, see . */ #include "defs.h" -#include "dyn-string.h" #include #include "gdb_wait.h" #include "event-top.h" @@ -38,7 +37,6 @@ #endif #include -#include "timeval-utils.h" #include "gdbcmd.h" #include "serial.h" #include "bfd.h" @@ -62,12 +60,13 @@ #include "readline/readline.h" -#include -#include +#include #include "gdb_usleep.h" #include "interps.h" #include "gdb_regex.h" +#include "job-control.h" +#include "common/selftest.h" #if !HAVE_DECL_MALLOC extern PTR malloc (); /* ARI: PTR */ @@ -99,29 +98,12 @@ static void set_width (void); 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 quit immediately if Control-C is typed now, rather - than waiting until QUIT is executed. Be careful in setting this; - code which executes with immediate_quit set has to be very careful - about being able to deal with being interrupted at any time. It is - almost always better to use QUIT; the only exception I can think of - is being able to quit out of a system call (using EINTR loses if - the SIGINT happens between the previous QUIT and the system call). - To immediately quit in the case in which a SIGINT happens between - the previous QUIT and setting immediate_quit (desirable anytime we - expect to block), call QUIT after setting immediate_quit. */ - -int immediate_quit; - /* 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.) */ @@ -138,7 +120,7 @@ show_sevenbit_strings (struct ui_file *file, int from_tty, /* 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 @@ -155,116 +137,14 @@ show_pagination_enabled (struct ui_file *file, int from_tty, 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 (arg); -} - -struct cleanup * -make_cleanup_bfd_unref (bfd *abfd) -{ - return make_cleanup (do_bfd_close_cleanup, abfd); -} - -static void -do_close_cleanup (void *arg) -{ - int *fd = arg; - - close (*fd); -} - -struct cleanup * -make_cleanup_close (int fd) -{ - int *saved_fd = xmalloc (sizeof (fd)); - - *saved_fd = fd; - return make_cleanup_dtor (do_close_cleanup, saved_fd, xfree); -} - -/* Helper function which does the work for make_cleanup_fclose. */ - -static void -do_fclose_cleanup (void *arg) -{ - 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 = 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 (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 = 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")); + uiout->redirect (NULL); } /* Return a new cleanup that pops the last redirection by ui_out_redirect @@ -279,7 +159,7 @@ make_cleanup_ui_out_redirect_pop (struct ui_out *uiout) static void do_free_section_addr_info (void *arg) { - free_section_addr_info (arg); + free_section_addr_info ((struct section_addr_info *) arg); } struct cleanup * @@ -297,7 +177,8 @@ struct restore_integer_closure static void restore_integer (void *p) { - struct restore_integer_closure *closure = p; + struct restore_integer_closure *closure + = (struct restore_integer_closure *) p; *(closure->variable) = closure->value; } @@ -308,8 +189,7 @@ restore_integer (void *p) struct cleanup * make_cleanup_restore_integer (int *variable) { - struct restore_integer_closure *c = - xmalloc (sizeof (struct restore_integer_closure)); + struct restore_integer_closure *c = XNEW (struct restore_integer_closure); c->variable = variable; c->value = *variable; @@ -331,7 +211,7 @@ make_cleanup_restore_uinteger (unsigned int *variable) static void do_unpush_target (void *arg) { - struct target_ops *ops = arg; + struct target_ops *ops = (struct target_ops *) arg; unpush_target (ops); } @@ -344,52 +224,6 @@ make_cleanup_unpush_target (struct target_ops *ops) 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_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_file_closure -{ - struct ui_file **variable; - struct ui_file *value; -}; - -static void -do_restore_ui_file (void *p) -{ - struct restore_ui_file_closure *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 @@ -412,7 +246,7 @@ make_cleanup_value_free_to_mark (struct value *mark) static void do_value_free (void *value) { - value_free (value); + value_free ((struct value *) value); } /* Free VALUE. */ @@ -423,46 +257,6 @@ 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 = 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 = (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 @@ -492,7 +286,7 @@ make_cleanup_clear_parser_state (struct parser_state **p) void free_current_contents (void *ptr) { - void **location = ptr; + void **location = (void **) ptr; if (location == NULL) internal_error (__FILE__, __LINE__, @@ -519,8 +313,13 @@ vwarning (const char *string, va_list args) (*deprecated_warning_hook) (string, args); else { + struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); + if (target_supports_terminal_ours ()) - target_terminal_ours (); + { + make_cleanup_restore_target_terminal (); + target_terminal_ours_for_output (); + } if (filtered_printing_initialized ()) wrap_here (""); /* Force out any buffered output. */ gdb_flush (gdb_stdout); @@ -528,6 +327,8 @@ vwarning (const char *string, va_list args) fputs_unfiltered (warning_pre_print, gdb_stderr); vfprintf_unfiltered (gdb_stderr, string, args); fprintf_unfiltered (gdb_stderr, "\n"); + + do_cleanups (old_chain); } } @@ -542,12 +343,9 @@ verror (const char *string, va_list args) } 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. */ @@ -725,7 +523,10 @@ internal_vproblem (struct internal_problem *problem, /* Try to get the message out and at the start of a new line. */ if (target_supports_terminal_ours ()) - target_terminal_ours (); + { + make_cleanup_restore_target_terminal (); + target_terminal_ours_for_output (); + } if (filtered_printing_initialized ()) begin_line (); @@ -877,8 +678,8 @@ add_internal_problem_command (struct internal_problem *problem) char *set_doc; char *show_doc; - set_cmd_list = xmalloc (sizeof (*set_cmd_list)); - show_cmd_list = xmalloc (sizeof (*set_cmd_list)); + set_cmd_list = XNEW (struct cmd_list_element *); + show_cmd_list = XNEW (struct cmd_list_element *); *set_cmd_list = NULL; *show_cmd_list = NULL; @@ -950,23 +751,15 @@ add_internal_problem_command (struct internal_problem *problem) } /* Return a newly allocated string, containing the PREFIX followed - by the system error message for errno (separated by a colon). + by the system error message for errno (separated by a colon). */ - The result must be deallocated after use. */ - -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 @@ -976,10 +769,7 @@ perror_string (const char *prefix) 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 @@ -987,7 +777,7 @@ throw_perror_with_name (enum errors errcode, const char *string) 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. */ @@ -1004,11 +794,8 @@ perror_with_name (const char *string) 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 @@ -1037,10 +824,12 @@ print_sys_errmsg (const char *string, int errcode) void quit (void) { + struct ui *ui = current_ui; + if (sync_quit_force_run) { sync_quit_force_run = 0; - quit_force (NULL, stdin == instream); + quit_force (NULL, 0); } #ifdef __MSDOS__ @@ -1058,6 +847,20 @@ quit (void) #endif } +/* See defs.h. */ + +void +maybe_quit (void) +{ + if (sync_quit_force_run) + quit (); + + quit_handler (); + + if (deprecated_interactive_hook) + deprecated_interactive_hook (); +} + /* Called when a memory allocation fails, with the number of bytes of memory requested in SIZE. */ @@ -1108,62 +911,54 @@ print_spaces (int n, struct ui_file *file) /* Print a host address. */ void -gdb_print_host_address (const void *addr, struct ui_file *stream) +gdb_print_host_address_1 (const void *addr, struct ui_file *stream) { fprintf_filtered (stream, "%s", host_address_to_string (addr)); } - -/* A cleanup function that calls regfree. */ +/* See utils.h. */ -static void -do_regfree_cleanup (void *r) +char * +make_hex_string (const gdb_byte *data, size_t length) { - regfree (r); -} + char *result = (char *) xmalloc (length * 2 + 1); + char *p; + size_t i; -/* Create a new cleanup that frees the compiled regular expression R. */ - -struct cleanup * -make_regfree_cleanup (regex_t *r) -{ - return make_cleanup (do_regfree_cleanup, r); + p = result; + for (i = 0; i < length; ++i) + p += xsnprintf (p, 3, "%02x", data[i]); + *p = '\0'; + return result; } -/* 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 = xmalloc (length); +/* A cleanup that simply calls ui_unregister_input_event_handler. */ - regerror (code, rx, result, length); - return result; +static void +ui_unregister_input_event_handler_cleanup (void *ui) +{ + ui_unregister_input_event_handler ((struct ui *) 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. */ +/* Set up to handle input. */ -struct cleanup * -compile_rx_or_error (regex_t *pattern, const char *rx, const char *message) +static struct cleanup * +prepare_to_handle_input (void) { - int code; + struct cleanup *old_chain; - gdb_assert (rx != NULL); + old_chain = make_cleanup_restore_target_terminal (); + target_terminal_ours (); - code = regcomp (pattern, rx, REG_NOSUB); - if (code != 0) - { - char *err = get_regcomp_error (code, pattern); + ui_register_input_event_handler (current_ui); + if (current_ui->prompt_state == PROMPT_BLOCKED) + make_cleanup (ui_unregister_input_event_handler_cleanup, current_ui); - make_cleanup (xfree, err); - error (("%s: %s"), message, err); - } + make_cleanup_override_quit_handler (default_quit_handler); - return make_regfree_cleanup (pattern); + return old_chain; } @@ -1181,15 +976,13 @@ compile_rx_or_error (regex_t *pattern, const char *rx, const char *message) static int ATTRIBUTE_PRINTF (1, 0) defaulted_query (const char *ctlstr, const char defchar, va_list args) { - int answer; int ans2; int retval; int def_value; char def_answer, not_def_answer; - char *y_string, *n_string, *question; - /* Used to add duration we waited for user to respond to - prompt_for_continue_wait_time. */ - struct timeval prompt_started, prompt_ended, prompt_delta; + const char *y_string, *n_string; + char *question, *prompt; + struct cleanup *old_chain; /* Set up according to which answer is the default. */ if (defchar == '\0') @@ -1226,8 +1019,14 @@ defaulted_query (const char *ctlstr, const char defchar, va_list args) 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) { + old_chain = make_cleanup_restore_target_terminal (); + + target_terminal_ours_for_output (); wrap_here (""); vfprintf_filtered (gdb_stdout, ctlstr, args); @@ -1236,72 +1035,52 @@ defaulted_query (const char *ctlstr, const char defchar, va_list args) y_string, n_string, def_answer); gdb_flush (gdb_stdout); + do_cleanups (old_chain); return def_value; } if (deprecated_query_hook) { - return deprecated_query_hook (ctlstr, args); + int res; + + old_chain = make_cleanup_restore_target_terminal (); + res = deprecated_query_hook (ctlstr, args); + do_cleanups (old_chain); + return res; } /* Format the question outside of the loop, to avoid reusing args. */ question = xstrvprintf (ctlstr, args); + old_chain = 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); + + /* 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 (); - /* Used for calculating time spend waiting for user. */ - gettimeofday (&prompt_started, NULL); + prepare_to_handle_input (); while (1) { - wrap_here (""); /* Flush any buffered output. */ - gdb_flush (gdb_stdout); - - if (annotation_level > 1) - printf_filtered (("\n\032\032pre-query\n")); + char *response, answer; - fputs_filtered (question, gdb_stdout); - printf_filtered (_("(%s or %s) "), y_string, n_string); - - if (annotation_level > 1) - printf_filtered (("\n\032\032query\n")); - - wrap_here (""); gdb_flush (gdb_stdout); + response = gdb_readline_wrapper (prompt); - answer = fgetc (stdin); - - /* We expect fgetc to block until a character is read. But - this may not be the case if the terminal was opened with - the NONBLOCK flag. In that case, if there is nothing to - read on stdin, fgetc returns EOF, but also sets the error - condition flag on stdin and errno to EAGAIN. With a true - EOF, stdin's error condition flag is not set. - - A situation where this behavior was observed is a pseudo - terminal on AIX. */ - while (answer == EOF && ferror (stdin) && errno == EAGAIN) - { - /* Not a real EOF. Wait a little while and try again until - we read something. */ - clearerr (stdin); - gdb_usleep (10000); - answer = fgetc (stdin); - } - - clearerr (stdin); /* in case of C-d */ - if (answer == EOF) /* C-d */ + if (response == NULL) /* C-d */ { printf_filtered ("EOF [assumed %c]\n", def_answer); retval = def_value; break; } - /* Eat rest of input line, to EOF or newline. */ - if (answer != '\n') - do - { - ans2 = fgetc (stdin); - clearerr (stdin); - } - while (ans2 != EOF && ans2 != '\n' && ans2 != '\r'); + + answer = response[0]; + xfree (response); if (answer >= 'a') answer -= 040; @@ -1316,8 +1095,7 @@ defaulted_query (const char *ctlstr, const char defchar, va_list args) specify the required input or have it default by entering nothing. */ if (answer == def_answer - || (defchar != '\0' && - (answer == '\n' || answer == '\r' || answer == EOF))) + || (defchar != '\0' && answer == '\0')) { retval = def_value; break; @@ -1328,14 +1106,11 @@ defaulted_query (const char *ctlstr, const char defchar, va_list args) } /* 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; - xfree (question); if (annotation_level > 1) printf_filtered (("\n\032\032post-query\n")); + do_cleanups (old_chain); return retval; } @@ -1401,13 +1176,10 @@ query (const char *ctlstr, ...) 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, @@ -1419,7 +1191,6 @@ host_char_to_target (struct gdbarch *gdbarch, int c, int *target_c) *target_c = *(char *) obstack_base (&host_data); } - do_cleanups (cleanups); return result; } @@ -1515,7 +1286,7 @@ parse_escape (struct gdbarch *gdbarch, const char **string_ptr) /* 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 call for printing things which are independent of the language + 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 @@ -1655,14 +1426,14 @@ static char *wrap_pointer; /* 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; -/* 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) @@ -1696,9 +1467,10 @@ 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)) - /* Also disable paging if inside EMACS. */ - || getenv ("EMACS")) + 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")) { /* The number of lines per page is not mentioned in the terminal description or EMACS evironment variable is set. This probably @@ -1712,6 +1484,9 @@ init_page_info (void) #endif } + /* We handle SIGWINCH ourselves. */ + rl_catch_sigwinch = 0; + set_screen_size (); set_width (); } @@ -1811,19 +1586,33 @@ set_height_command (char *args, int from_tty, struct cmd_list_element *c) set_screen_size (); } +/* See utils.h. */ + +void +set_screen_width_and_height (int width, int height) +{ + lines_per_page = height; + chars_per_line = width; + + set_screen_size (); + set_width (); +} + /* Wait, so the user can read what's on the screen. Prompt the user - to continue by pressing RETURN. */ + to continue by pressing RETURN. 'q' is also provided because + telling users what to do in the prompt is more user-friendly than + expecting them to think of Ctrl-C/SIGINT. */ static void prompt_for_continue (void) { 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; - - 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")); @@ -1833,71 +1622,58 @@ prompt_for_continue (void) if (annotation_level > 1) strcat (cont_prompt, "\n\032\032prompt-for-continue\n"); - /* We must do this *before* we call gdb_readline, else it will eventually - call us -- thinking that we're trying to print beyond the end of the - screen. */ + /* We must do this *before* we call gdb_readline_wrapper, else it + will eventually call us -- thinking that we're trying to print + beyond the end of the screen. */ reinitialize_more_filter (); - immediate_quit++; - QUIT; + prepare_to_handle_input (); - /* We'll need to handle input. */ - target_terminal_ours (); - - /* On a real operating system, the user can quit with SIGINT. - But not on GO32. - - 'q' is provided on all systems so users don't have to change habits - from system to system, and because telling them what to do in - the prompt is more user-friendly than expecting them to think of - SIGINT. */ - /* Call readline, not gdb_readline, because GO32 readline handles control-C - whereas control-C to gdb_readline will cause the user to get dumped - out to DOS. */ + /* Call gdb_readline_wrapper, not readline, in order to keep an + event loop running. */ ignore = gdb_readline_wrapper (cont_prompt); + 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")); - if (ignore) + if (ignore != NULL) { char *p = ignore; while (*p == ' ' || *p == '\t') ++p; if (p[0] == 'q') - quit (); - xfree (ignore); + /* Do not call quit here; there is no possibility of SIGINT. */ + throw_quit ("Quit"); } - immediate_quit--; /* Now we have to do this again, so that GDB will know that it doesn't need to save the ---Type --- line at the top of the screen. */ reinitialize_more_filter (); dont_repeat (); /* Forget prev cmd -- CR won't repeat it. */ + + 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; } @@ -1933,7 +1709,7 @@ reinitialize_more_filter (void) 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) @@ -2003,7 +1779,7 @@ puts_filtered_tabular (char *string, int width, int right) if (right) spaces += width - stringlen; - spacebuf = alloca (spaces + 1); + spacebuf = (char *) alloca (spaces + 1); spacebuf[spaces] = '\0'; while (spaces--) spacebuf[spaces] = ' '; @@ -2056,7 +1832,7 @@ fputs_maybe_filtered (const char *linebuffer, struct ui_file *stream, || 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; @@ -2202,8 +1978,8 @@ puts_debug (char *prefix, char *string, char *suffix) /* 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; @@ -2320,21 +2096,21 @@ vfprintf_unfiltered (struct ui_file *stream, const char *format, va_list args) old_cleanups = make_cleanup (xfree, linebuffer); 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 (now.time_since_epoch ()); + microseconds us = duration_cast (now.time_since_epoch () - s); len = strlen (linebuffer); 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, need_nl ? "\n": ""); + fputs_unfiltered (timestamp.c_str (), stream); } else fputs_unfiltered (linebuffer, stream); @@ -2504,41 +2280,72 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name, } } -/* 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). +/* Modes of operation for strncmp_iw_with_mode. */ - 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). */ +enum class strncmp_iw_mode +{ + /* Work like strncmp, while ignoring whitespace. */ + NORMAL, -int -strcmp_iw (const char *string1, const char *string2) + /* Like NORMAL, but also apply the strcmp_iw hack. I.e., + string1=="FOO(PARAMS)" matches string2=="FOO". */ + MATCH_PARAMS, +}; + +/* Helper for strncmp_iw and strcmp_iw. */ + +static int +strncmp_iw_with_mode (const char *string1, const char *string2, + size_t string2_len, strncmp_iw_mode mode) { - while ((*string1 != '\0') && (*string2 != '\0')) + const char *end_str2 = string2 + string2_len; + + while (1) { while (isspace (*string1)) - { - string1++; - } - while (isspace (*string2)) - { - string2++; - } + string1++; + while (string2 < end_str2 && isspace (*string2)) + string2++; + if (*string1 == '\0' || string2 == end_str2) + break; 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') - { - string1++; - string2++; - } + + string1++; + string2++; + } + + if (string2 == end_str2) + { + if (mode == strncmp_iw_mode::NORMAL) + 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); +} + +/* 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); } /* This is like strcmp except that it ignores whitespace and treats @@ -2672,32 +2479,19 @@ streq (const char *lhs, const char *rhs) ** 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; if (template_string != (char *) NULL && string_to_compare != (char *) NULL && strlen (string_to_compare) <= strlen (template_string)) match = - (strncmp - (template_string, string_to_compare, strlen (string_to_compare)) == 0); + (startswith (template_string, string_to_compare)); else match = 0; return match; } -static void -pagination_on_command (char *arg, int from_tty) -{ - pagination_enabled = 1; -} - -static void -pagination_off_command (char *arg, int from_tty) -{ - pagination_enabled = 0; -} - static void show_debug_timestamp (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) @@ -2729,8 +2523,6 @@ Setting this to \"unlimited\" or zero causes GDB never pause during output."), show_lines_per_page, &setlist, &showlist); - init_page_info (); - add_setshow_boolean_cmd ("pagination", class_support, &pagination_enabled, _("\ Set state of GDB output pagination."), _("\ @@ -2742,14 +2534,6 @@ Turning pagination off is an alternative to \"set height unlimited\"."), show_pagination_enabled, &setlist, &showlist); - if (xdb_commands) - { - add_com ("am", class_support, pagination_on_command, - _("Enable pagination")); - add_com ("sm", class_support, pagination_off_command, - _("Disable pagination")); - } - add_setshow_boolean_cmd ("sevenbit-strings", class_support, &sevenbit_strings, _("\ Set printing of 8-bit characters in strings as \\nnn."), _("\ @@ -2811,7 +2595,7 @@ print_core_address (struct gdbarch *gdbarch, CORE_ADDR address) hashval_t core_addr_hash (const void *ap) { - const CORE_ADDR *addrp = ap; + const CORE_ADDR *addrp = (const CORE_ADDR *) ap; return *addrp; } @@ -2821,8 +2605,8 @@ core_addr_hash (const void *ap) int core_addr_eq (const void *ap, const void *bp) { - const CORE_ADDR *addr_ap = ap; - const CORE_ADDR *addr_bp = bp; + const CORE_ADDR *addr_ap = (const CORE_ADDR *) ap; + const CORE_ADDR *addr_bp = (const CORE_ADDR *) bp; return *addr_ap == *addr_bp; } @@ -2865,56 +2649,109 @@ string_to_core_addr (const char *my_string) return addr; } -char * +gdb::unique_xmalloc_ptr gdb_realpath (const char *filename) { - /* The MS Windows method. If we don't have realpath, we assume we - don't have symlinks and just canonicalize to a Windows absolute - path. GetFullPath converts ../ and ./ in relative paths to - absolute paths, filling in current drive if one is not given - or using the current directory of a specified drive (eg, "E:foo"). - It also converts all forward slashes to back slashes. */ - /* The file system is case-insensitive but case-preserving. - So we do not lowercase the path. Otherwise, we might not - be able to display the original casing in a given path. */ +/* On most hosts, we rely on canonicalize_file_name to compute + the FILENAME's realpath. + + But the situation is slightly more complex on Windows, due to some + versions of GCC which were reported to generate paths where + backlashes (the directory separator) were doubled. For instance: + c:\\some\\double\\slashes\\dir + ... instead of ... + c:\some\double\slashes\dir + Those double-slashes were getting in the way when comparing paths, + for instance when trying to insert a breakpoint as follow: + (gdb) b c:/some/double/slashes/dir/foo.c:4 + No source file named c:/some/double/slashes/dir/foo.c:4. + (gdb) b c:\some\double\slashes\dir\foo.c:4 + No source file named c:\some\double\slashes\dir\foo.c:4. + To prevent this from happening, we need this function to always + strip those extra backslashes. While canonicalize_file_name does + perform this simplification, it only works when the path is valid. + Since the simplification would be useful even if the path is not + valid (one can always set a breakpoint on a file, even if the file + does not exist locally), we rely instead on GetFullPathName to + perform the canonicalization. */ + #if defined (_WIN32) { char buf[MAX_PATH]; DWORD len = GetFullPathName (filename, MAX_PATH, buf, NULL); + /* The file system is case-insensitive but case-preserving. + So it is important we do not lowercase the path. Otherwise, + 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 (xstrdup (buf)); } #else { char *rp = canonicalize_file_name (filename); if (rp != NULL) - return rp; + return gdb::unique_xmalloc_ptr (rp); } #endif /* This system is a lost cause, just dup the buffer. */ - return xstrdup (filename); + return gdb::unique_xmalloc_ptr (xstrdup (filename)); } +#if GDB_SELF_TEST + +static void +gdb_realpath_check_trailer (const char *input, const char *trailer) +{ + gdb::unique_xmalloc_ptr 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 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 (xstrdup (filename)); - dir_name = alloca ((size_t) (base_name - filename + 2)); + dir_name = (char *) alloca ((size_t) (base_name - filename + 2)); /* Allocate enough space to store the dir_name + plus one extra character sometimes needed under Windows (see below), and then the closing \000 character. */ @@ -2934,40 +2771,37 @@ gdb_realpath_keepfile (const char *filename) /* 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 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 (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 gdb_abspath (const char *path) { gdb_assert (path != NULL && path[0] != '\0'); if (path[0] == '~') - return tilde_expand (path); + return gdb::unique_xmalloc_ptr (tilde_expand (path)); if (IS_ABSOLUTE_PATH (path)) - return xstrdup (path); + return gdb::unique_xmalloc_ptr (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 + (concat (current_directory, + IS_DIR_SEPARATOR (current_directory[strlen (current_directory) - 1]) + ? "" : SLASH_STRING, + path, (char *) NULL)); } ULONGEST @@ -2986,23 +2820,6 @@ align_down (ULONGEST v, int n) return (v & -n); } -/* See utils.h. */ - -LONGEST -gdb_sign_extend (LONGEST value, int bit) -{ - gdb_assert (bit >= 1 && bit <= 8 * sizeof (LONGEST)); - - if (((value >> (bit - 1)) & 1) != 0) - { - LONGEST signbit = ((LONGEST) 1) << (bit - 1); - - value = (value ^ signbit) - signbit; - } - - return value; -} - /* Allocation function for the libiberty hash table which uses an obstack. The obstack is passed as DATA. */ @@ -3027,122 +2844,22 @@ dummy_obstack_deallocate (void *object, void *data) return; } -/* The bit offset of the highest byte in a ULONGEST, for overflow - checking. */ - -#define HIGH_BYTE_POSN ((sizeof (ULONGEST) - 1) * HOST_CHAR_BIT) - -/* True (non-zero) iff DIGIT is a valid digit in radix BASE, - where 2 <= BASE <= 36. */ - -static int -is_digit_in_base (unsigned char digit, int base) -{ - if (!isalnum (digit)) - return 0; - if (base <= 10) - return (isdigit (digit) && digit < base + '0'); - else - return (isdigit (digit) || tolower (digit) < base - 10 + 'a'); -} - -static int -digit_to_int (unsigned char c) -{ - if (isdigit (c)) - return c - '0'; - else - return tolower (c) - 'a' + 10; -} - -/* As for strtoul, but for ULONGEST results. */ - -ULONGEST -strtoulst (const char *num, const char **trailer, int base) -{ - unsigned int high_part; - ULONGEST result; - int minus = 0; - int i = 0; - - /* Skip leading whitespace. */ - while (isspace (num[i])) - i++; - - /* Handle prefixes. */ - if (num[i] == '+') - i++; - else if (num[i] == '-') - { - minus = 1; - i++; - } - - if (base == 0 || base == 16) - { - if (num[i] == '0' && (num[i + 1] == 'x' || num[i + 1] == 'X')) - { - i += 2; - if (base == 0) - base = 16; - } - } - - if (base == 0 && num[i] == '0') - base = 8; - - if (base == 0) - base = 10; - - if (base < 2 || base > 36) - { - errno = EINVAL; - return 0; - } - - result = high_part = 0; - for (; is_digit_in_base (num[i], base); i += 1) - { - result = result * base + digit_to_int (num[i]); - high_part = high_part * base + (unsigned int) (result >> HIGH_BYTE_POSN); - result &= ((ULONGEST) 1 << HIGH_BYTE_POSN) - 1; - if (high_part > 0xff) - { - errno = ERANGE; - result = ~ (ULONGEST) 0; - high_part = 0; - minus = 0; - break; - } - } - - if (trailer != NULL) - *trailer = &num[i]; - - result = result + ((ULONGEST) high_part << HIGH_BYTE_POSN); - if (minus) - return -result; - else - return result; -} - /* 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 = 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". */ @@ -3150,23 +2867,21 @@ ldirname (const char *filename) && !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 @@ -3207,7 +2922,7 @@ gdb_bfd_errmsg (bfd_error_type error_tag, char **matching) + strlen (AMBIGUOUS_MESS2); for (p = matching; *p; p++) ret_len += strlen (*p) + 1; - ret = xmalloc (ret_len + 1); + ret = (char *) xmalloc (ret_len + 1); retp = ret; make_cleanup (xfree, ret); @@ -3273,41 +2988,52 @@ make_bpstat_clear_actions_cleanup (void) int producer_is_gcc_ge_4 (const char *producer) { - const char *cs; int major, minor; - if (producer == NULL) - { - /* For unknown compilers expect their behavior is not compliant. For GCC - this case can also happen for -gdwarf-4 type units supported since - gcc-4.5. */ - - return -1; - } + if (! producer_is_gcc (producer, &major, &minor)) + return -1; + if (major < 4) + return -1; + if (major > 4) + return INT_MAX; + return minor; +} - /* Skip any identifier after "GNU " - such as "C++" or "Java". */ +/* 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. */ - if (strncmp (producer, "GNU ", strlen ("GNU ")) != 0) - { - /* For non-GCC compilers expect their behavior is not compliant. */ +int +producer_is_gcc (const char *producer, int *major, int *minor) +{ + const char *cs; - return -1; - } - cs = &producer[strlen ("GNU ")]; - while (*cs && !isdigit (*cs)) - cs++; - if (sscanf (cs, "%d.%d", &major, &minor) != 2) + if (producer != NULL && startswith (producer, "GNU ")) { - /* Not recognized as GCC. */ - - return -1; + int maj, min; + + if (major == NULL) + major = &maj; + if (minor == NULL) + minor = &min; + + /* Skip any identifier after "GNU " - such as "C11" or "C++". + 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; } - if (major < 4) - return -1; - if (major > 4) - return INT_MAX; - return minor; + /* Not recognized as GCC. */ + return 0; } /* Helper for make_cleanup_free_char_ptr_vec. */ @@ -3315,7 +3041,7 @@ producer_is_gcc_ge_4 (const char *producer) static void do_free_char_ptr_vec (void *arg) { - VEC (char_ptr) *char_ptr_vec = arg; + VEC (char_ptr) *char_ptr_vec = (VEC (char_ptr) *) arg; free_char_ptr_vec (char_ptr_vec); } @@ -3358,7 +3084,8 @@ substitute_path_component (char **stringp, const char *from, const char *to) { char *string_new; - string_new = xrealloc (string, (strlen (string) + to_len + 1)); + string_new + = (char *) xrealloc (string, (strlen (string) + to_len + 1)); /* Relocate the current S pointer. */ s = s - string + string_new; @@ -3419,9 +3146,9 @@ wait_to_die_with_timeout (pid_t pid, int *status, int timeout) sa.sa_flags = 0; sigaction (SIGALRM, &sa, &old_sa); #else - void (*ofunc) (); + sighandler_t ofunc; - ofunc = (void (*)()) signal (SIGALRM, sigalrm_handler); + ofunc = signal (SIGALRM, sigalrm_handler); #endif alarm (timeout); @@ -3469,14 +3196,14 @@ gdb_filename_fnmatch (const char *pattern, const char *string, int flags) /* Replace '\' by '/' in both strings. */ - pattern_slash = alloca (strlen (pattern) + 1); + pattern_slash = (char *) alloca (strlen (pattern) + 1); strcpy (pattern_slash, pattern); pattern = pattern_slash; for (; *pattern_slash != 0; pattern_slash++) if (IS_DIR_SEPARATOR (*pattern_slash)) *pattern_slash = '/'; - string_slash = alloca (strlen (string) + 1); + string_slash = (char *) alloca (strlen (string) + 1); strcpy (string_slash, string); string = string_slash; for (; *string_slash != 0; string_slash++) @@ -3492,6 +3219,83 @@ gdb_filename_fnmatch (const char *pattern, const char *string, int flags) return fnmatch (pattern, string, flags); } +/* Return the number of path elements in PATH. + / = 1 + /foo = 2 + /foo/ = 2 + foo/bar = 2 + foo/ = 1 */ + +int +count_path_elements (const char *path) +{ + int count = 0; + const char *p = path; + + if (HAS_DRIVE_SPEC (p)) + { + p = STRIP_DRIVE_SPEC (p); + ++count; + } + + while (*p != '\0') + { + if (IS_DIR_SEPARATOR (*p)) + ++count; + ++p; + } + + /* Backup one if last character is /, unless it's the only one. */ + if (p > path + 1 && IS_DIR_SEPARATOR (p[-1])) + --count; + + /* Add one for the file name, if present. */ + if (p > path && !IS_DIR_SEPARATOR (p[-1])) + ++count; + + return count; +} + +/* Remove N leading path elements from PATH. + N must be non-negative. + If PATH has more than N path elements then return NULL. + If PATH has exactly N path elements then return "". + See count_path_elements for a description of how we do the counting. */ + +const char * +strip_leading_path_elements (const char *path, int n) +{ + int i = 0; + const char *p = path; + + gdb_assert (n >= 0); + + if (n == 0) + return p; + + if (HAS_DRIVE_SPEC (p)) + { + p = STRIP_DRIVE_SPEC (p); + ++i; + } + + while (i < n) + { + while (*p != '\0' && !IS_DIR_SEPARATOR (*p)) + ++p; + if (*p == '\0') + { + if (i + 1 == n) + return ""; + return NULL; + } + ++p; + ++i; + } + + return p; +} + /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_utils; @@ -3501,4 +3305,8 @@ _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_tests); +#endif }