#include "defs.h"
#include "exceptions.h"
#include "breakpoint.h"
#include "target.h"
#include "inferior.h"
#include "annotate.h"
#include "ui-out.h"
#include "gdb_assert.h"
#include "gdb_string.h"
#include "serial.h"
const struct gdb_exception exception_none = { 0, NO_ERROR, NULL };
enum catcher_state {
CATCHER_CREATED,
CATCHER_RUNNING,
CATCHER_RUNNING_1,
CATCHER_ABORTING
};
enum catcher_action {
CATCH_ITER,
CATCH_ITER_1,
CATCH_THROWING
};
struct catcher
{
enum catcher_state state;
EXCEPTIONS_SIGJMP_BUF buf;
volatile struct gdb_exception *exception;
int mask;
struct ui_out *saved_uiout;
struct cleanup *saved_cleanup_chain;
struct catcher *prev;
};
static struct catcher *current_catcher;
EXCEPTIONS_SIGJMP_BUF *
exceptions_state_mc_init (struct ui_out *func_uiout,
volatile struct gdb_exception *exception,
return_mask mask)
{
struct catcher *new_catcher = XZALLOC (struct catcher);
exception->reason = 0;
exception->error = NO_ERROR;
exception->message = NULL;
new_catcher->exception = exception;
new_catcher->mask = mask;
new_catcher->saved_uiout = uiout;
uiout = func_uiout;
new_catcher->saved_cleanup_chain = save_cleanups ();
new_catcher->prev = current_catcher;
current_catcher = new_catcher;
new_catcher->state = CATCHER_CREATED;
return &new_catcher->buf;
}
static void
catcher_pop (void)
{
struct catcher *old_catcher = current_catcher;
current_catcher = old_catcher->prev;
restore_cleanups (old_catcher->saved_cleanup_chain);
uiout = old_catcher->saved_uiout;
xfree (old_catcher);
}
static int
exceptions_state_mc (enum catcher_action action)
{
switch (current_catcher->state)
{
case CATCHER_CREATED:
switch (action)
{
case CATCH_ITER:
current_catcher->state = CATCHER_RUNNING;
return 1;
default:
internal_error (__FILE__, __LINE__, _("bad state"));
}
case CATCHER_RUNNING:
switch (action)
{
case CATCH_ITER:
catcher_pop ();
return 0;
case CATCH_ITER_1:
current_catcher->state = CATCHER_RUNNING_1;
return 1;
case CATCH_THROWING:
current_catcher->state = CATCHER_ABORTING;
return 1;
default:
internal_error (__FILE__, __LINE__, _("bad switch"));
}
case CATCHER_RUNNING_1:
switch (action)
{
case CATCH_ITER:
catcher_pop ();
return 0;
case CATCH_ITER_1:
current_catcher->state = CATCHER_RUNNING;
return 0;
case CATCH_THROWING:
current_catcher->state = CATCHER_ABORTING;
return 1;
default:
internal_error (__FILE__, __LINE__, _("bad switch"));
}
case CATCHER_ABORTING:
switch (action)
{
case CATCH_ITER:
{
struct gdb_exception exception = *current_catcher->exception;
if (current_catcher->mask & RETURN_MASK (exception.reason))
{
catcher_pop ();
return 0;
}
catcher_pop ();
throw_exception (exception);
}
default:
internal_error (__FILE__, __LINE__, _("bad state"));
}
default:
internal_error (__FILE__, __LINE__, _("bad switch"));
}
}
int
exceptions_state_mc_action_iter (void)
{
return exceptions_state_mc (CATCH_ITER);
}
int
exceptions_state_mc_action_iter_1 (void)
{
return exceptions_state_mc (CATCH_ITER_1);
}
NORETURN void
throw_exception (struct gdb_exception exception)
{
quit_flag = 0;
immediate_quit = 0;
bpstat_clear_actions (stop_bpstat);
disable_current_display ();
do_cleanups (ALL_CLEANUPS);
if (target_can_async_p () && !target_executing)
do_exec_cleanups (ALL_CLEANUPS);
if (sync_execution)
do_exec_error_cleanups (ALL_CLEANUPS);
exceptions_state_mc (CATCH_THROWING);
*current_catcher->exception = exception;
EXCEPTIONS_SIGLONGJMP (current_catcher->buf, exception.reason);
}
static char *last_message;
NORETURN void
deprecated_throw_reason (enum return_reason reason)
{
struct gdb_exception exception;
memset (&exception, 0, sizeof exception);
exception.reason = reason;
switch (reason)
{
case RETURN_QUIT:
break;
case RETURN_ERROR:
exception.error = GENERIC_ERROR;
break;
default:
internal_error (__FILE__, __LINE__, _("bad switch"));
}
throw_exception (exception);
}
static void
print_flush (void)
{
struct serial *gdb_stdout_serial;
if (deprecated_error_begin_hook)
deprecated_error_begin_hook ();
target_terminal_ours ();
wrap_here ("");
gdb_flush (gdb_stdout);
gdb_flush (gdb_stderr);
gdb_stdout_serial = serial_fdopen (1);
if (gdb_stdout_serial)
{
serial_drain_output (gdb_stdout_serial);
serial_un_fdopen (gdb_stdout_serial);
}
annotate_error_begin ();
}
static void
print_exception (struct ui_file *file, struct gdb_exception e)
{
const char *start;
const char *end;
if (!ui_out_is_mi_like_p (uiout))
{
for (start = e.message; start != NULL; start = end)
{
end = strchr (start, '\n');
if (end == NULL)
fputs_filtered (start, file);
else
{
end++;
ui_file_write (file, start, end - start);
}
}
fprintf_filtered (file, "\n");
}
switch (e.reason)
{
case RETURN_QUIT:
annotate_quit ();
break;
case RETURN_ERROR:
annotate_error ();
break;
default:
internal_error (__FILE__, __LINE__, _("Bad switch."));
}
}
void
exception_print (struct ui_file *file, struct gdb_exception e)
{
if (e.reason < 0 && e.message != NULL)
{
print_flush ();
print_exception (file, e);
}
}
void
exception_fprintf (struct ui_file *file, struct gdb_exception e,
const char *prefix, ...)
{
if (e.reason < 0 && e.message != NULL)
{
va_list args;
print_flush ();
va_start (args, prefix);
vfprintf_filtered (file, prefix, args);
va_end (args);
print_exception (file, e);
}
}
void
print_any_exception (struct ui_file *file, const char *prefix,
struct gdb_exception e)
{
if (e.reason < 0 && e.message != NULL)
{
target_terminal_ours ();
wrap_here ("");
gdb_flush (gdb_stdout);
annotate_error_begin ();
if (prefix != NULL && prefix[0] != '\0')
fputs_filtered (prefix, file);
print_exception (file, e);
}
}
NORETURN static void
throw_it (enum return_reason reason, enum errors error, const char *fmt,
va_list ap) ATTR_NORETURN;
NORETURN static void
throw_it (enum return_reason reason, enum errors error, const char *fmt,
va_list ap)
{
struct gdb_exception e;
char *new_message;
new_message = xstrvprintf (fmt, ap);
xfree (last_message);
last_message = new_message;
e.reason = reason;
e.error = error;
e.message = last_message;
throw_exception (e);
}
NORETURN void
throw_verror (enum errors error, const char *fmt, va_list ap)
{
throw_it (RETURN_ERROR, error, fmt, ap);
}
NORETURN void
throw_vfatal (const char *fmt, va_list ap)
{
throw_it (RETURN_QUIT, NO_ERROR, fmt, ap);
}
NORETURN void
throw_error (enum errors error, const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
throw_it (RETURN_ERROR, error, fmt, args);
va_end (args);
}
int
catch_exceptions (struct ui_out *uiout,
catch_exceptions_ftype *func,
void *func_args,
return_mask mask)
{
return catch_exceptions_with_msg (uiout, func, func_args, NULL, mask);
}
struct gdb_exception
catch_exception (struct ui_out *uiout,
catch_exception_ftype *func,
void *func_args,
return_mask mask)
{
volatile struct gdb_exception exception;
TRY_CATCH (exception, mask)
{
(*func) (uiout, func_args);
}
return exception;
}
int
catch_exceptions_with_msg (struct ui_out *uiout,
catch_exceptions_ftype *func,
void *func_args,
char **gdberrmsg,
return_mask mask)
{
volatile struct gdb_exception exception;
volatile int val = 0;
TRY_CATCH (exception, mask)
{
val = (*func) (uiout, func_args);
}
print_any_exception (gdb_stderr, NULL, exception);
gdb_assert (val >= 0);
gdb_assert (exception.reason <= 0);
if (exception.reason < 0)
{
if (gdberrmsg != NULL)
{
if (exception.message != NULL)
*gdberrmsg = xstrdup (exception.message);
else
*gdberrmsg = NULL;
}
return exception.reason;
}
return val;
}
int
catch_errors (catch_errors_ftype *func, void *func_args, char *errstring,
return_mask mask)
{
volatile int val = 0;
volatile struct gdb_exception exception;
TRY_CATCH (exception, mask)
{
val = func (func_args);
}
print_any_exception (gdb_stderr, errstring, exception);
if (exception.reason != 0)
return 0;
return val;
}
int
catch_command_errors (catch_command_errors_ftype * command,
char *arg, int from_tty, return_mask mask)
{
volatile struct gdb_exception e;
TRY_CATCH (e, mask)
{
command (arg, from_tty);
}
print_any_exception (gdb_stderr, NULL, e);
if (e.reason < 0)
return 0;
return 1;
}