+/* Implements the dbx 'func' command. */
+
+static void
+func_command (const char *arg, int from_tty)
+{
+ if (arg == NULL)
+ return;
+
+ struct frame_info *frame = find_frame_for_function (arg);
+ if (frame == NULL)
+ error (_("'%s' not within current stack frame."), arg);
+ if (frame != get_selected_frame (NULL))
+ {
+ select_frame (frame);
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+ }
+}
+
+/* The qcs command line flags for the "frame apply" commands. Keep
+ this in sync with the "thread apply" commands. */
+
+using qcs_flag_option_def
+ = gdb::option::flag_option_def<qcs_flags>;
+
+static const gdb::option::option_def fr_qcs_flags_option_defs[] = {
+ qcs_flag_option_def {
+ "q", [] (qcs_flags *opt) { return &opt->quiet; },
+ N_("Disables printing the frame location information."),
+ },
+
+ qcs_flag_option_def {
+ "c", [] (qcs_flags *opt) { return &opt->cont; },
+ N_("Print any error raised by COMMAND and continue."),
+ },
+
+ qcs_flag_option_def {
+ "s", [] (qcs_flags *opt) { return &opt->silent; },
+ N_("Silently ignore any errors or empty output produced by COMMAND."),
+ },
+};
+
+/* Create an option_def_group array for all the "frame apply" options,
+ with FLAGS and SET_BT_OPTS as context. */
+
+static inline std::array<gdb::option::option_def_group, 2>
+make_frame_apply_options_def_group (qcs_flags *flags,
+ set_backtrace_options *set_bt_opts)
+{
+ return {{
+ { {fr_qcs_flags_option_defs}, flags },
+ { {set_backtrace_option_defs}, set_bt_opts },
+ }};
+}
+
+/* Apply a GDB command to all stack frames, or a set of identified frames,
+ or innermost COUNT frames.
+ With a negative COUNT, apply command on outermost -COUNT frames.
+
+ frame apply 3 info frame Apply 'info frame' to frames 0, 1, 2
+ frame apply -3 info frame Apply 'info frame' to outermost 3 frames.
+ frame apply all x/i $pc Apply 'x/i $pc' cmd to all frames.
+ frame apply all -s p local_var_no_idea_in_which_frame
+ If a frame has a local variable called
+ local_var_no_idea_in_which_frame, print frame
+ and value of local_var_no_idea_in_which_frame.
+ frame apply all -s -q p local_var_no_idea_in_which_frame
+ Same as before, but only print the variable value.
+ frame apply level 2-5 0 4-7 -s p i = i + 1
+ Adds 1 to the variable i in the specified frames.
+ Note that i will be incremented twice in
+ frames 4 and 5. */
+
+/* Apply a GDB command to COUNT stack frames, starting at TRAILING.
+ CMD starts with 0 or more qcs flags followed by the GDB command to apply.
+ COUNT -1 means all frames starting at TRAILING. WHICH_COMMAND is used
+ for error messages. */
+
+static void
+frame_apply_command_count (const char *which_command,
+ const char *cmd, int from_tty,
+ struct frame_info *trailing, int count)
+{
+ qcs_flags flags;
+ set_backtrace_options set_bt_opts = user_set_backtrace_options;
+
+ auto group = make_frame_apply_options_def_group (&flags, &set_bt_opts);
+ gdb::option::process_options
+ (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group);
+
+ validate_flags_qcs (which_command, &flags);
+
+ if (cmd == NULL || *cmd == '\0')
+ error (_("Please specify a command to apply on the selected frames"));
+
+ /* The below will restore the current inferior/thread/frame.
+ Usually, only the frame is effectively to be restored.
+ But in case CMD switches of inferior/thread, better restore
+ these also. */
+ scoped_restore_current_thread restore_thread;
+
+ /* These options are handled quite deep in the unwind machinery, so
+ we get to pass them down by swapping globals. */
+ scoped_restore restore_set_backtrace_options
+ = make_scoped_restore (&user_set_backtrace_options, set_bt_opts);
+
+ for (frame_info *fi = trailing; fi && count--; fi = get_prev_frame (fi))
+ {
+ QUIT;
+
+ select_frame (fi);
+ try
+ {
+ std::string cmd_result;
+ {
+ /* In case CMD switches of inferior/thread/frame, the below
+ restores the inferior/thread/frame. FI can then be
+ set to the selected frame. */
+ scoped_restore_current_thread restore_fi_current_frame;
+
+ cmd_result = execute_command_to_string
+ (cmd, from_tty, gdb_stdout->term_out ());
+ }
+ fi = get_selected_frame (_("frame apply "
+ "unable to get selected frame."));
+ if (!flags.silent || cmd_result.length () > 0)
+ {
+ if (!flags.quiet)
+ print_stack_frame (fi, 1, LOCATION, 0);
+ printf_filtered ("%s", cmd_result.c_str ());
+ }
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ fi = get_selected_frame (_("frame apply "
+ "unable to get selected frame."));
+ if (!flags.silent)
+ {
+ if (!flags.quiet)
+ print_stack_frame (fi, 1, LOCATION, 0);
+ if (flags.cont)
+ printf_filtered ("%s\n", ex.what ());
+ else
+ throw;
+ }
+ }
+ }
+}
+
+/* Completer for the "frame apply ..." commands. */
+
+static void
+frame_apply_completer (completion_tracker &tracker, const char *text)
+{
+ const auto group = make_frame_apply_options_def_group (nullptr, nullptr);
+ if (gdb::option::complete_options
+ (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+ return;
+
+ complete_nested_command_line (tracker, text);
+}
+
+/* Completer for the "frame apply" commands. */
+
+static void
+frame_apply_level_cmd_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char */*word*/)
+{
+ /* Do this explicitly because there's an early return below. */
+ tracker.set_use_custom_word_point (true);
+
+ number_or_range_parser levels (text);
+
+ /* Skip the LEVEL list to find the options and command args. */
+ try
+ {
+ while (!levels.finished ())
+ {
+ /* Call for effect. */
+ levels.get_number ();
+
+ if (levels.in_range ())
+ levels.skip_range ();
+ }
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ /* get_number throws if it parses a negative number, for
+ example. But a seemingly negative number may be the start of
+ an option instead. */
+ }
+
+ const char *cmd = levels.cur_tok ();
+
+ if (cmd == text)
+ {
+ /* No level list yet. */
+ return;
+ }
+
+ /* Check if we're past a valid LEVEL already. */
+ if (levels.finished ()
+ && cmd > text && !isspace (cmd[-1]))
+ return;
+
+ /* We're past LEVELs, advance word point. */
+ tracker.advance_custom_word_point_by (cmd - text);
+ text = cmd;
+
+ frame_apply_completer (tracker, text);
+}
+
+/* Completer for the "frame apply all" command. */
+
+void
+frame_apply_all_cmd_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char */*word*/)
+{
+ frame_apply_completer (tracker, text);
+}
+
+/* Completer for the "frame apply COUNT" command. */
+
+static void
+frame_apply_cmd_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char */*word*/)
+{
+ const char *cmd = text;
+
+ int count = get_number_trailer (&cmd, 0);
+ if (count == 0)
+ return;
+
+ /* Check if we're past a valid COUNT already. */
+ if (cmd > text && !isspace (cmd[-1]))
+ return;
+
+ /* We're past COUNT, advance word point. */
+ tracker.advance_custom_word_point_by (cmd - text);
+ text = cmd;
+
+ frame_apply_completer (tracker, text);
+}
+
+/* Implementation of the "frame apply level" command. */
+
+static void
+frame_apply_level_command (const char *cmd, int from_tty)
+{
+ if (!target_has_stack)
+ error (_("No stack."));
+
+ bool level_found = false;
+ const char *levels_str = cmd;
+ number_or_range_parser levels (levels_str);
+
+ /* Skip the LEVEL list to find the flags and command args. */
+ while (!levels.finished ())
+ {
+ /* Call for effect. */
+ levels.get_number ();
+
+ level_found = true;
+ if (levels.in_range ())
+ levels.skip_range ();
+ }
+
+ if (!level_found)
+ error (_("Missing or invalid LEVEL... argument"));
+
+ cmd = levels.cur_tok ();
+
+ /* Redo the LEVELS parsing, but applying COMMAND. */
+ levels.init (levels_str);
+ while (!levels.finished ())
+ {
+ const int level_beg = levels.get_number ();
+ int n_frames;
+
+ if (levels.in_range ())
+ {
+ n_frames = levels.end_value () - level_beg + 1;
+ levels.skip_range ();
+ }
+ else
+ n_frames = 1;
+
+ frame_apply_command_count ("frame apply level", cmd, from_tty,
+ leading_innermost_frame (level_beg), n_frames);
+ }
+}
+
+/* Implementation of the "frame apply all" command. */
+
+static void
+frame_apply_all_command (const char *cmd, int from_tty)
+{
+ if (!target_has_stack)
+ error (_("No stack."));
+
+ frame_apply_command_count ("frame apply all", cmd, from_tty,
+ get_current_frame (), INT_MAX);
+}
+
+/* Implementation of the "frame apply" command. */
+
+static void
+frame_apply_command (const char* cmd, int from_tty)
+{
+ int count;
+ struct frame_info *trailing;
+
+ if (!target_has_stack)
+ error (_("No stack."));
+
+ if (cmd == NULL)
+ error (_("Missing COUNT argument."));
+ count = get_number_trailer (&cmd, 0);
+ if (count == 0)
+ error (_("Invalid COUNT argument."));
+
+ if (count < 0)
+ {
+ trailing = trailing_outermost_frame (-count);
+ count = -1;
+ }
+ else
+ trailing = get_current_frame ();
+
+ frame_apply_command_count ("frame apply", cmd, from_tty,
+ trailing, count);
+}
+
+/* Implementation of the "faas" command. */
+
+static void
+faas_command (const char *cmd, int from_tty)
+{
+ if (cmd == NULL || *cmd == '\0')
+ error (_("Please specify a command to apply on all frames"));
+ std::string expanded = std::string ("frame apply all -s ") + cmd;
+ execute_command (expanded.c_str (), from_tty);
+}
+
+
+/* Find inner-mode frame with frame address ADDRESS. Return NULL if no
+ matching frame can be found. */
+
+static struct frame_info *
+find_frame_for_address (CORE_ADDR address)
+{
+ struct frame_id id;
+ struct frame_info *fid;
+
+ id = frame_id_build_wild (address);
+
+ /* If (s)he specifies the frame with an address, he deserves
+ what (s)he gets. Still, give the highest one that matches.
+ (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't
+ know). */
+ for (fid = get_current_frame ();
+ fid != NULL;
+ fid = get_prev_frame (fid))
+ {
+ if (frame_id_eq (id, get_frame_id (fid)))
+ {
+ struct frame_info *prev_frame;
+
+ while (1)
+ {
+ prev_frame = get_prev_frame (fid);
+ if (!prev_frame
+ || !frame_id_eq (id, get_frame_id (prev_frame)))
+ break;
+ fid = prev_frame;
+ }
+ return fid;
+ }
+ }
+ return NULL;
+}
+
+\f
+
+/* Commands with a prefix of `frame apply'. */
+static struct cmd_list_element *frame_apply_cmd_list = NULL;
+
+/* Commands with a prefix of `frame'. */
+static struct cmd_list_element *frame_cmd_list = NULL;
+
+/* Commands with a prefix of `select frame'. */
+static struct cmd_list_element *select_frame_cmd_list = NULL;
+
+/* Commands with a prefix of `info frame'. */
+static struct cmd_list_element *info_frame_cmd_list = NULL;
+