summaryrefslogtreecommitdiffstats
path: root/libparted/exception.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libparted/exception.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/libparted/exception.c b/libparted/exception.c
new file mode 100644
index 0000000..1d95853
--- /dev/null
+++ b/libparted/exception.c
@@ -0,0 +1,313 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999-2000, 2007-2014, 2019-2023 Free Software Foundation,
+ Inc.
+
+ 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/>.
+*/
+
+/** \file exception.c */
+
+/**
+ * \addtogroup PedException
+ *
+ * \brief Exception handling.
+ *
+ * There are a few types of exceptions: PED_EXCEPTION_INFORMATION,
+ * PED_EXCEPTION_WARNING, PED_EXCEPTION_ERROR, PED_EXCEPTION_FATAL,
+ * PED_EXCEPTION_BUG.
+ *
+ * They are "thrown" when one of the above events occur while executing
+ * a libparted function. For example, if ped_device_open() fails
+ * because the device doesn't exist, an exception will be thrown.
+ * Exceptions contain text describing what the event was. It will give
+ * at least one option for resolving the exception: PED_EXCEPTION_FIX,
+ * PED_EXCEPTION_YES, PED_EXCEPTION_NO, PED_EXCEPTION_OK, PED_EXCEPTION_RETRY,
+ * PED_EXCEPTION_IGNORE, PED_EXCEPTION_CANCEL. After an exception is thrown,
+ * there are two things that can happen:
+ *
+ * -# an exception handler is called, which selects how the exception should be
+ * resolved (usually by asking the user). Also note: an exception handler may
+ * choose to return PED_EXCEPTION_UNHANDLED. In this case, a default action
+ * will be taken by libparted (respectively the code that threw the
+ * exception). In general, a default action will be "safe".
+ * -# the exception is not handled, because the caller of the function wants to
+ * handle everything itself. In this case, PED_EXCEPTION_UNHANDLED is
+ * returned.
+ *
+ * @{
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/exception.h>
+
+#define N_(String) String
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+int ped_exception = 0;
+
+static PedExceptionOption default_handler (PedException* ex);
+
+static PedExceptionHandler* ex_handler = default_handler;
+static PedException* ex = NULL;
+static int ex_fetch_count = 0;
+
+static const char *const type_strings [] = {
+ N_("Information"),
+ N_("Warning"),
+ N_("Error"),
+ N_("Fatal"),
+ N_("Bug"),
+ N_("No Implementation")
+};
+
+static const char *const option_strings [] = {
+ N_("Fix"),
+ N_("Yes"),
+ N_("No"),
+ N_("OK"),
+ N_("Retry"),
+ N_("Ignore"),
+ N_("Cancel")
+};
+
+/**
+ * Return a string describing an exception type.
+ */
+char*
+ped_exception_get_type_string (PedExceptionType ex_type)
+{
+ return (char *) type_strings [ex_type - 1];
+}
+
+/* FIXME: move this out to the prospective math.c */
+/* FIXME: this can probably be done more efficiently */
+static int _GL_ATTRIBUTE_PURE
+ped_log2 (int n)
+{
+ int x;
+
+ PED_ASSERT (n > 0);
+
+ for (x=0; 1 << x <= n; x++);
+
+ return x - 1;
+}
+
+/**
+ * Return a string describing an exception option.
+ */
+char*
+ped_exception_get_option_string (PedExceptionOption ex_opt)
+{
+ return (char *) option_strings [ped_log2 (ex_opt)];
+}
+
+static PedExceptionOption
+default_handler (PedException* e)
+{
+ if (e->type == PED_EXCEPTION_BUG)
+ fprintf (stderr,
+ _("A bug has been detected in GNU Parted. "
+ "Refer to the web site of parted "
+ "http://www.gnu.org/software/parted/parted.html "
+ "for more information of what could be useful "
+ "for bug submitting! "
+ "Please email a bug report to "
+ "%s containing at least the "
+ "version (%s) and the following message: "),
+ PACKAGE_BUGREPORT, VERSION);
+ else
+ fprintf (stderr, "%s: ",
+ ped_exception_get_type_string (e->type));
+ fprintf (stderr, "%s\n", e->message);
+
+ switch (e->options) {
+ case PED_EXCEPTION_OK:
+ case PED_EXCEPTION_CANCEL:
+ case PED_EXCEPTION_IGNORE:
+ return e->options;
+
+ default:
+ return PED_EXCEPTION_UNHANDLED;
+ }
+}
+
+/**
+ * Set the exception handler.
+ *
+ * The exception handler should return ONE of the options set in ex->options,
+ * indicating the way the event should be resolved.
+ */
+void
+ped_exception_set_handler (PedExceptionHandler* handler)
+{
+ if (handler)
+ ex_handler = handler;
+ else
+ ex_handler = default_handler;
+}
+
+/**
+ * Get the current exception handler.
+ */
+PedExceptionHandler *
+ped_exception_get_handler (void)
+{
+ if (ex_handler)
+ return ex_handler;
+ return default_handler;
+}
+
+/**
+ * Assert that the current exception has been resolved.
+ */
+void
+ped_exception_catch ()
+{
+ if (ped_exception) {
+ ped_exception = 0;
+ free (ex->message);
+ free (ex);
+ ex = NULL;
+ }
+}
+
+static PedExceptionOption
+do_throw ()
+{
+ PedExceptionOption ex_opt;
+
+ ped_exception = 1;
+
+ if (ex_fetch_count) {
+ return PED_EXCEPTION_UNHANDLED;
+ } else {
+ ex_opt = ex_handler (ex);
+ ped_exception_catch ();
+ return ex_opt;
+ }
+}
+
+/**
+ * Throw an exception.
+ *
+ * You can also use this in a program using libparted.
+ * "message" is a printf-like format string, so you can do
+ *
+ * \code
+ * ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_CANCEL,
+ * "Can't open %s", file_name);
+ * \endcode
+ *
+ * Returns the option selected to resolve the exception. If the exception was
+ * unhandled, PED_EXCEPTION_UNHANDLED is returned.
+ */
+PedExceptionOption
+ped_exception_throw (PedExceptionType ex_type,
+ PedExceptionOption ex_opts, const char* message, ...)
+{
+ va_list arg_list;
+ int result;
+ static int size = 1000;
+
+ if (ex)
+ ped_exception_catch ();
+
+ ex = (PedException*) malloc (sizeof (PedException));
+ if (!ex)
+ goto no_memory;
+
+ ex->type = ex_type;
+ ex->options = ex_opts;
+
+ while (message) {
+ ex->message = (char*) malloc (size * sizeof (char));
+ if (!ex->message)
+ goto no_memory;
+
+ va_start (arg_list, message);
+ result = vsnprintf (ex->message, size, message, arg_list);
+ va_end (arg_list);
+
+ if (result > -1 && result < size)
+ break;
+
+ size += 10;
+ free (ex->message);
+ }
+
+ return do_throw ();
+
+no_memory:
+ fputs ("Out of memory in exception handler!\n", stderr);
+
+ va_start (arg_list, message);
+ vfprintf (stderr, message, arg_list);
+ va_end (arg_list);
+
+ return PED_EXCEPTION_UNHANDLED;
+}
+
+/**
+ * Rethrow an unhandled exception.
+ * This means repeating the last ped_exception_throw() statement.
+ */
+PedExceptionOption
+ped_exception_rethrow ()
+{
+ return do_throw ();
+}
+
+/**
+ * Indicates that exceptions should not go to the exception handler, but
+ * passed up to the calling function(s). All calls to
+ * ped_exception_throw() will return PED_EXCEPTION_UNHANDLED.
+ */
+void
+ped_exception_fetch_all ()
+{
+ ex_fetch_count++;
+}
+
+/**
+ * Indicates that the calling function does not want to accept any
+ * responsibility for exceptions any more.
+ *
+ * \note a caller of that function may still want responsibility, so
+ * ped_exception_throw() may not invoke the exception handler.
+ *
+ * \warning every call to this function must have a preceding
+ * ped_exception_fetch_all().
+ */
+void
+ped_exception_leave_all ()
+{
+ PED_ASSERT (ex_fetch_count > 0);
+ ex_fetch_count--;
+}
+
+/** @} */