aboutsummaryrefslogtreecommitdiff
path: root/gdb/extension.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/extension.c')
-rw-r--r--gdb/extension.c871
1 files changed, 871 insertions, 0 deletions
diff --git a/gdb/extension.c b/gdb/extension.c
new file mode 100644
index 0000000..4392dec
--- /dev/null
+++ b/gdb/extension.c
@@ -0,0 +1,871 @@
+/* Interface between gdb and its extension languages.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Note: With few exceptions, external functions and variables in this file
+ have "ext_lang" in the name, and no other symbol in gdb does. */
+
+#include "defs.h"
+#include <signal.h>
+#include "auto-load.h"
+#include "breakpoint.h"
+#include "event-top.h"
+#include "extension.h"
+#include "extension-priv.h"
+#include "observer.h"
+#include "cli/cli-script.h"
+#include "python/python.h"
+
+/* Iterate over all external extension languages, regardless of whether the
+ support has been compiled in or not.
+ This does not include GDB's own scripting language. */
+
+#define ALL_EXTENSION_LANGUAGES(i, extlang) \
+ for (/*int*/ i = 0, extlang = extension_languages[0]; \
+ extlang != NULL; \
+ extlang = extension_languages[++i])
+
+/* Iterate over all external extension languages that are supported.
+ This does not include GDB's own scripting language. */
+
+#define ALL_ENABLED_EXTENSION_LANGUAGES(i, extlang) \
+ for (/*int*/ i = 0, extlang = extension_languages[0]; \
+ extlang != NULL; \
+ extlang = extension_languages[++i]) \
+ if (extlang->ops != NULL)
+
+static script_sourcer_func source_gdb_script;
+static objfile_script_sourcer_func source_gdb_objfile_script;
+
+/* GDB's own scripting language.
+ This exists, in part, to support auto-loading ${prog}-gdb.gdb scripts. */
+
+static const struct extension_language_script_ops
+ extension_language_gdb_script_ops =
+{
+ source_gdb_script,
+ source_gdb_objfile_script,
+ auto_load_gdb_scripts_enabled
+};
+
+const struct extension_language_defn extension_language_gdb =
+{
+ EXT_LANG_GDB,
+ "gdb",
+ "GDB",
+
+ /* We fall back to interpreting a script as a GDB script if it doesn't
+ match the other scripting languages, but for consistency's sake
+ give it a formal suffix. */
+ ".gdb",
+ "-gdb.gdb",
+
+ /* cli_control_type: This is never used: GDB's own scripting language
+ has a variety of control types (if, while, etc.). */
+ commands_control,
+
+ &extension_language_gdb_script_ops,
+
+ /* The rest of the extension language interface isn't supported by GDB's own
+ extension/scripting language. */
+ NULL
+};
+
+/* NULL-terminated table of all external (non-native) extension languages.
+
+ The order of appearance in the table is important.
+ When multiple extension languages provide the same feature, for example
+ a pretty-printer for a particular type, which one gets used?
+ The algorithm employed here is "the first one wins". For example, in
+ the case of pretty-printers this means the first one to provide a
+ pretty-printed value is the one that is used. This algorithm is employed
+ throughout. */
+
+static const struct extension_language_defn * const extension_languages[] =
+{
+ /* To preserve existing behaviour, python should always appear first. */
+ &extension_language_python,
+ NULL
+};
+
+/* Return a pointer to the struct extension_language_defn object of
+ extension language LANG.
+ This always returns a non-NULL pointer, even if support for the language
+ is not compiled into this copy of GDB. */
+
+const struct extension_language_defn *
+get_ext_lang_defn (enum extension_language lang)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ gdb_assert (lang != EXT_LANG_NONE);
+
+ if (lang == EXT_LANG_GDB)
+ return &extension_language_gdb;
+
+ ALL_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->language == lang)
+ return extlang;
+ }
+
+ gdb_assert_not_reached ("unable to find extension_language_defn");
+}
+
+/* Return TRUE if FILE has extension EXTENSION. */
+
+static int
+has_extension (const char *file, const char *extension)
+{
+ int file_len = strlen (file);
+ int extension_len = strlen (extension);
+
+ return (file_len > extension_len
+ && strcmp (&file[file_len - extension_len], extension) == 0);
+}
+
+/* Return the extension language of FILE, or NULL if
+ the extension language of FILE is not recognized.
+ This is done by looking at the file's suffix. */
+
+const struct extension_language_defn *
+get_ext_lang_of_file (const char *file)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (has_extension (file, extlang->suffix))
+ return extlang;
+ }
+
+ return NULL;
+}
+
+/* Return non-zero if support for the specified extension language
+ is compiled in. */
+
+int
+ext_lang_present_p (const struct extension_language_defn *extlang)
+{
+ return extlang->script_ops != NULL;
+}
+
+/* Return non-zero if the specified extension language has successfully
+ initialized. */
+
+int
+ext_lang_initialized_p (const struct extension_language_defn *extlang)
+{
+ if (extlang->ops != NULL)
+ {
+ /* This method is required. */
+ gdb_assert (extlang->ops->initialized != NULL);
+ return extlang->ops->initialized (extlang);
+ }
+
+ return 0;
+}
+
+/* Throw an error indicating EXTLANG is not supported in this copy of GDB. */
+
+void
+throw_ext_lang_unsupported (const struct extension_language_defn *extlang)
+{
+ error (_("Scripting in the \"%s\" language is not supported"
+ " in this copy of GDB."),
+ ext_lang_capitalized_name (extlang));
+}
+
+/* Methods for GDB's own extension/scripting language. */
+
+/* The extension_language_script_ops.script_sourcer "method". */
+
+static void
+source_gdb_script (const struct extension_language_defn *extlang,
+ FILE *stream, const char *file)
+{
+ script_from_file (stream, file);
+}
+
+/* The extension_language_script_ops.objfile_script_sourcer "method". */
+
+static void
+source_gdb_objfile_script (const struct extension_language_defn *extlang,
+ struct objfile *objfile,
+ FILE *stream, const char *file)
+{
+ script_from_file (stream, file);
+}
+
+/* Accessors for "public" attributes of struct extension_language. */
+
+/* Return the "name" field of EXTLANG. */
+
+const char *
+ext_lang_name (const struct extension_language_defn *extlang)
+{
+ return extlang->name;
+}
+
+/* Return the "capitalized_name" field of EXTLANG. */
+
+const char *
+ext_lang_capitalized_name (const struct extension_language_defn *extlang)
+{
+ return extlang->capitalized_name;
+}
+
+/* Return the "suffix" field of EXTLANG. */
+
+const char *
+ext_lang_suffix (const struct extension_language_defn *extlang)
+{
+ return extlang->suffix;
+}
+
+/* Return the "auto_load_suffix" field of EXTLANG. */
+
+const char *
+ext_lang_auto_load_suffix (const struct extension_language_defn *extlang)
+{
+ return extlang->auto_load_suffix;
+}
+
+/* extension_language_script_ops wrappers. */
+
+/* Return the script "sourcer" function for EXTLANG.
+ This is the function that loads and processes a script.
+ If support for this language isn't compiled in, NULL is returned. */
+
+script_sourcer_func *
+ext_lang_script_sourcer (const struct extension_language_defn *extlang)
+{
+ if (extlang->script_ops == NULL)
+ return NULL;
+
+ /* The extension language is required to implement this function. */
+ gdb_assert (extlang->script_ops->script_sourcer != NULL);
+
+ return extlang->script_ops->script_sourcer;
+}
+
+/* Return the objfile script "sourcer" function for EXTLANG.
+ This is the function that loads and processes a script for a particular
+ objfile.
+ If support for this language isn't compiled in, NULL is returned. */
+
+objfile_script_sourcer_func *
+ext_lang_objfile_script_sourcer (const struct extension_language_defn *extlang)
+{
+ if (extlang->script_ops == NULL)
+ return NULL;
+
+ /* The extension language is required to implement this function. */
+ gdb_assert (extlang->script_ops->objfile_script_sourcer != NULL);
+
+ return extlang->script_ops->objfile_script_sourcer;
+}
+
+/* Return non-zero if auto-loading of EXTLANG scripts is enabled.
+ Zero is returned if support for this language isn't compiled in. */
+
+int
+ext_lang_auto_load_enabled (const struct extension_language_defn *extlang)
+{
+ if (extlang->script_ops == NULL)
+ return 0;
+
+ /* The extension language is required to implement this function. */
+ gdb_assert (extlang->script_ops->auto_load_enabled != NULL);
+
+ return extlang->script_ops->auto_load_enabled (extlang);
+}
+
+/* Functions that iterate over all extension languages.
+ These only iterate over external extension languages, not including
+ GDB's own extension/scripting language, unless otherwise indicated. */
+
+/* Wrapper to call the extension_language_ops.finish_initialization "method"
+ for each compiled-in extension language. */
+
+void
+finish_ext_lang_initialization (void)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->ops->finish_initialization != NULL)
+ extlang->ops->finish_initialization (extlang);
+ }
+}
+
+/* Invoke the appropriate extension_language_ops.eval_from_control_command
+ method to perform CMD, which is a list of commands in an extension language.
+
+ This function is what implements, for example:
+
+ python
+ print 42
+ end
+
+ in a GDB script. */
+
+void
+eval_ext_lang_from_control_command (struct command_line *cmd)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->cli_control_type == cmd->control_type)
+ {
+ if (extlang->ops->eval_from_control_command != NULL)
+ {
+ extlang->ops->eval_from_control_command (extlang, cmd);
+ return;
+ }
+ /* The requested extension language is not supported in this GDB. */
+ throw_ext_lang_unsupported (extlang);
+ }
+ }
+
+ gdb_assert_not_reached ("unknown extension language in command_line");
+}
+
+/* Search for and load scripts for OBJFILE written in extension languages.
+ This includes GDB's own scripting language.
+
+ This function is what implements the loading of OBJFILE-gdb.py and
+ OBJFILE-gdb.gdb. */
+
+void
+auto_load_ext_lang_scripts_for_objfile (struct objfile *objfile)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ extlang = &extension_language_gdb;
+ if (ext_lang_auto_load_enabled (extlang))
+ auto_load_objfile_script (objfile, extlang);
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (ext_lang_auto_load_enabled (extlang))
+ auto_load_objfile_script (objfile, extlang);
+ }
+}
+
+/* Interface to type pretty-printers implemented in an extension language. */
+
+/* Call this at the start when preparing to pretty-print a type.
+ The result is a pointer to an opaque object (to the caller) to be passed
+ to apply_ext_lang_type_printers and free_ext_lang_type_printers.
+
+ We don't know in advance which extension language will provide a
+ pretty-printer for the type, so all are initialized. */
+
+struct ext_lang_type_printers *
+start_ext_lang_type_printers (void)
+{
+ struct ext_lang_type_printers *printers
+ = XCNEW (struct ext_lang_type_printers);
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->ops->start_type_printers != NULL)
+ extlang->ops->start_type_printers (extlang, printers);
+ }
+
+ return printers;
+}
+
+/* Iteratively try the type pretty-printers specified by PRINTERS
+ according to the standard search order (specified by extension_languages),
+ returning the result of the first one that succeeds.
+ If there was an error, or if no printer succeeds, then NULL is returned. */
+
+char *
+apply_ext_lang_type_printers (struct ext_lang_type_printers *printers,
+ struct type *type)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ char *result = NULL;
+ enum ext_lang_rc rc;
+
+ if (extlang->ops->apply_type_printers == NULL)
+ continue;
+ rc = extlang->ops->apply_type_printers (extlang, printers, type,
+ &result);
+ switch (rc)
+ {
+ case EXT_LANG_RC_OK:
+ gdb_assert (result != NULL);
+ return result;
+ case EXT_LANG_RC_ERROR:
+ return NULL;
+ case EXT_LANG_RC_NOP:
+ break;
+ default:
+ gdb_assert_not_reached ("bad return from apply_type_printers");
+ }
+ }
+
+ return NULL;
+}
+
+/* Call this after pretty-printing a type to release all memory held
+ by PRINTERS. */
+
+void
+free_ext_lang_type_printers (struct ext_lang_type_printers *printers)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->ops->free_type_printers != NULL)
+ extlang->ops->free_type_printers (extlang, printers);
+ }
+
+ xfree (printers);
+}
+
+/* Try to pretty-print a value of type TYPE located at VALADDR
+ + EMBEDDED_OFFSET, which came from the inferior at address ADDRESS
+ + EMBEDDED_OFFSET, onto stdio stream STREAM according to OPTIONS.
+ VAL is the whole object that came from ADDRESS. VALADDR must point to
+ the head of VAL's contents buffer.
+ Returns non-zero if the value was successfully pretty-printed.
+
+ Extension languages are tried in the order specified by
+ extension_languages. The first one to provide a pretty-printed
+ value "wins".
+
+ If an error is encountered in a pretty-printer, no further extension
+ languages are tried.
+ Note: This is different than encountering a memory error trying to read a
+ value for pretty-printing. Here we're referring to, e.g., programming
+ errors that trigger an exception in the extension language. */
+
+int
+apply_ext_lang_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
+ int embedded_offset, CORE_ADDR address,
+ struct ui_file *stream, int recurse,
+ const struct value *val,
+ const struct value_print_options *options,
+ const struct language_defn *language)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ enum ext_lang_rc rc;
+
+ if (extlang->ops->apply_val_pretty_printer == NULL)
+ continue;
+ rc = extlang->ops->apply_val_pretty_printer (extlang, type, valaddr,
+ embedded_offset, address,
+ stream, recurse, val,
+ options, language);
+ switch (rc)
+ {
+ case EXT_LANG_RC_OK:
+ return 1;
+ case EXT_LANG_RC_ERROR:
+ return 0;
+ case EXT_LANG_RC_NOP:
+ break;
+ default:
+ gdb_assert_not_reached ("bad return from apply_val_pretty_printer");
+ }
+ }
+
+ return 0;
+}
+
+/* GDB access to the "frame filter" feature.
+ FRAME is the source frame to start frame-filter invocation. FLAGS is an
+ integer holding the flags for printing. The following elements of
+ the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS:
+ PRINT_LEVEL is a flag indicating whether to print the frame's
+ relative level in the output. PRINT_FRAME_INFO is a flag that
+ indicates whether this function should print the frame
+ information, PRINT_ARGS is a flag that indicates whether to print
+ frame arguments, and PRINT_LOCALS, likewise, with frame local
+ variables. ARGS_TYPE is an enumerator describing the argument
+ format, OUT is the output stream to print. FRAME_LOW is the
+ beginning of the slice of frames to print, and FRAME_HIGH is the
+ upper limit of the frames to count. Returns EXT_LANG_BT_ERROR on error,
+ or EXT_LANG_BT_COMPLETED on success.
+
+ Extension languages are tried in the order specified by
+ extension_languages. The first one to provide a filter "wins".
+ If there is an error (EXT_LANG_BT_ERROR) it is reported immediately
+ rather than trying filters in other extension languages. */
+
+enum ext_lang_bt_status
+apply_ext_lang_frame_filter (struct frame_info *frame, int flags,
+ enum ext_lang_frame_args args_type,
+ struct ui_out *out,
+ int frame_low, int frame_high)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ enum ext_lang_bt_status status;
+
+ if (extlang->ops->apply_frame_filter == NULL)
+ continue;
+ status = extlang->ops->apply_frame_filter (extlang, frame, flags,
+ args_type, out,
+ frame_low, frame_high);
+ /* We use the filters from the first extension language that has
+ applicable filters. Also, an error is reported immediately
+ rather than continue trying. */
+ if (status != EXT_LANG_BT_NO_FILTERS)
+ return status;
+ }
+
+ return EXT_LANG_BT_NO_FILTERS;
+}
+
+/* Update values held by the extension language when OBJFILE is discarded.
+ New global types must be created for every such value, which must then be
+ updated to use the new types.
+ The function typically just iterates over all appropriate values and
+ calls preserve_one_value for each one.
+ COPIED_TYPES is used to prevent cycles / duplicates and is passed to
+ preserve_one_value. */
+
+void
+preserve_ext_lang_values (struct objfile *objfile, htab_t copied_types)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->ops->preserve_values != NULL)
+ extlang->ops->preserve_values (extlang, objfile, copied_types);
+ }
+}
+
+/* If there is a stop condition implemented in an extension language for
+ breakpoint B, return a pointer to the extension language's definition.
+ Otherwise return NULL.
+ If SKIP_LANG is not EXT_LANG_NONE, skip checking this language.
+ This is for the case where we're setting a new condition: Only one
+ condition is allowed, so when setting a condition for any particular
+ extension language, we need to check if any other extension language
+ already has a condition set. */
+
+const struct extension_language_defn *
+get_breakpoint_cond_ext_lang (struct breakpoint *b,
+ enum extension_language skip_lang)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->language != skip_lang
+ && extlang->ops->breakpoint_has_cond != NULL
+ && extlang->ops->breakpoint_has_cond (extlang, b))
+ return extlang;
+ }
+
+ return NULL;
+}
+
+/* Return whether a stop condition for breakpoint B says to stop.
+ True is also returned if there is no stop condition for B. */
+
+int
+breakpoint_ext_lang_cond_says_stop (struct breakpoint *b)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+ enum ext_lang_bp_stop stop = EXT_LANG_BP_STOP_UNSET;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ /* There is a rule that a breakpoint can have at most one of any of a
+ CLI or extension language condition. However, Python hacks in "finish
+ breakpoints" on top of the "stop" check, so we have to call this for
+ every language, even if we could first determine whether a "stop"
+ method exists. */
+ if (extlang->ops->breakpoint_cond_says_stop != NULL)
+ {
+ enum ext_lang_bp_stop this_stop
+ = extlang->ops->breakpoint_cond_says_stop (extlang, b);
+
+ if (this_stop != EXT_LANG_BP_STOP_UNSET)
+ {
+ /* Even though we have to check every extension language, only
+ one of them can return yes/no (because only one of them
+ can have a "stop" condition). */
+ gdb_assert (stop == EXT_LANG_BP_STOP_UNSET);
+ stop = this_stop;
+ }
+ }
+ }
+
+ return stop == EXT_LANG_BP_STOP_NO ? 0 : 1;
+}
+
+/* ^C/SIGINT support.
+ This requires cooperation with the extension languages so the support
+ is defined here. */
+
+/* This flag tracks quit requests when we haven't called out to an
+ extension language. it also holds quit requests when we transition to
+ an extension language that doesn't have cooperative SIGINT handling. */
+static int quit_flag;
+
+/* The current extension language we've called out to, or
+ extension_language_gdb if there isn't one.
+ This must be set everytime we call out to an extension language, and reset
+ to the previous value when it returns. Note that the previous value may
+ be a different (or the same) extension language. */
+static const struct extension_language_defn *active_ext_lang
+ = &extension_language_gdb;
+
+/* Return the currently active extension language. */
+
+const struct extension_language_defn *
+get_active_ext_lang (void)
+{
+ return active_ext_lang;
+}
+
+/* Install a SIGINT handler. */
+
+static void
+install_sigint_handler (const struct signal_handler *handler_state)
+{
+ gdb_assert (handler_state->handler_saved);
+
+ signal (SIGINT, handler_state->handler);
+}
+
+/* Install GDB's SIGINT handler, storing the previous version in *PREVIOUS.
+ As a simple optimization, if the previous version was GDB's SIGINT handler
+ then mark the previous handler as not having been saved, and thus it won't
+ be restored. */
+
+static void
+install_gdb_sigint_handler (struct signal_handler *previous)
+{
+ /* Save here to simplify comparison. */
+ RETSIGTYPE (*handle_sigint_for_compare) () = handle_sigint;
+
+ previous->handler = signal (SIGINT, handle_sigint);
+ if (previous->handler != handle_sigint_for_compare)
+ previous->handler_saved = 1;
+ else
+ previous->handler_saved = 0;
+}
+
+/* Set the currently active extension language to NOW_ACTIVE.
+ The result is a pointer to a malloc'd block of memory to pass to
+ restore_active_ext_lang.
+
+ N.B. This function must be called every time we call out to an extension
+ language, and the result must be passed to restore_active_ext_lang
+ afterwards.
+
+ If there is a pending SIGINT it is "moved" to the now active extension
+ language, if it supports cooperative SIGINT handling (i.e., it provides
+ {clear,set,check}_quit_flag methods). If the extension language does not
+ support cooperative SIGINT handling, then the SIGINT is left queued and
+ we require the non-cooperative extension language to call check_quit_flag
+ at appropriate times.
+ It is important for the extension language to call check_quit_flag if it
+ installs its own SIGINT handler to prevent the situation where a SIGINT
+ is queued on entry, extension language code runs for a "long" time possibly
+ serving one or more SIGINTs, and then returns. Upon return, if
+ check_quit_flag is not called, the original SIGINT will be thrown.
+ Non-cooperative extension languages are free to install their own SIGINT
+ handler but the original must be restored upon return, either itself
+ or via restore_active_ext_lang. */
+
+struct active_ext_lang_state *
+set_active_ext_lang (const struct extension_language_defn *now_active)
+{
+ struct active_ext_lang_state *previous
+ = XCNEW (struct active_ext_lang_state);
+
+ previous->ext_lang = active_ext_lang;
+ active_ext_lang = now_active;
+
+ /* If the newly active extension language uses cooperative SIGINT handling
+ then ensure GDB's SIGINT handler is installed. */
+ if (now_active->language == EXT_LANG_GDB
+ || now_active->ops->check_quit_flag != NULL)
+ install_gdb_sigint_handler (&previous->sigint_handler);
+
+ /* If there's a SIGINT recorded in the cooperative extension languages,
+ move it to the new language, or save it in GDB's global flag if the newly
+ active extension language doesn't use cooperative SIGINT handling. */
+ if (check_quit_flag ())
+ set_quit_flag ();
+
+ return previous;
+}
+
+/* Restore active extension language from PREVIOUS. */
+
+void
+restore_active_ext_lang (struct active_ext_lang_state *previous)
+{
+ const struct extension_language_defn *current = active_ext_lang;
+
+ active_ext_lang = previous->ext_lang;
+
+ /* Restore the previous SIGINT handler if one was saved. */
+ if (previous->sigint_handler.handler_saved)
+ install_sigint_handler (&previous->sigint_handler);
+
+ /* If there's a SIGINT recorded in the cooperative extension languages,
+ move it to the new language, or save it in GDB's global flag if the newly
+ active extension language doesn't use cooperative SIGINT handling. */
+ if (check_quit_flag ())
+ set_quit_flag ();
+
+ xfree (previous);
+}
+
+/* Clear the quit flag.
+ The flag is cleared in all extension languages,
+ not just the currently active one. */
+
+void
+clear_quit_flag (void)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->ops->clear_quit_flag != NULL)
+ extlang->ops->clear_quit_flag (extlang);
+ }
+
+ quit_flag = 0;
+}
+
+/* Set the quit flag.
+ This only sets the flag in the currently active extension language.
+ If the currently active extension language does not have cooperative
+ SIGINT handling, then GDB's global flag is set, and it is up to the
+ extension language to call check_quit_flag. The extension language
+ is free to install its own SIGINT handler, but we still need to handle
+ the transition. */
+
+void
+set_quit_flag (void)
+{
+ if (active_ext_lang->ops != NULL
+ && active_ext_lang->ops->set_quit_flag != NULL)
+ active_ext_lang->ops->set_quit_flag (active_ext_lang);
+ else
+ quit_flag = 1;
+}
+
+/* Return true if the quit flag has been set, false otherwise.
+ Note: The flag is cleared as a side-effect.
+ The flag is checked in all extension languages that support cooperative
+ SIGINT handling, not just the current one. This simplifies transitions. */
+
+int
+check_quit_flag (void)
+{
+ int i, result = 0;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ if (extlang->ops->check_quit_flag != NULL)
+ if (extlang->ops->check_quit_flag (extlang) != 0)
+ result = 1;
+ }
+
+ /* This is written in a particular way to avoid races. */
+ if (quit_flag)
+ {
+ quit_flag = 0;
+ result = 1;
+ }
+
+ return result;
+}
+
+/* Called via an observer before gdb prints its prompt.
+ Iterate over the extension languages giving them a chance to
+ change the prompt. The first one to change the prompt wins,
+ and no further languages are tried. */
+
+static void
+ext_lang_before_prompt (const char *current_gdb_prompt)
+{
+ int i;
+ const struct extension_language_defn *extlang;
+
+ ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+ {
+ enum ext_lang_rc rc;
+
+ if (extlang->ops->before_prompt == NULL)
+ continue;
+ rc = extlang->ops->before_prompt (extlang, current_gdb_prompt);
+ switch (rc)
+ {
+ case EXT_LANG_RC_OK:
+ case EXT_LANG_RC_ERROR:
+ return;
+ case EXT_LANG_RC_NOP:
+ break;
+ default:
+ gdb_assert_not_reached ("bad return from before_prompt");
+ }
+ }
+}
+
+extern initialize_file_ftype _initialize_extension;
+
+void
+_initialize_extension (void)
+{
+ observer_attach_before_prompt (ext_lang_before_prompt);
+}