1 /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 1999, 2000, 2007 Free Software Foundation, Inc.
   4 
   5     This program is free software; you can redistribute it and/or modify
   6     it under the terms of the GNU General Public License as published by
   7     the Free Software Foundation; either version 3 of the License, or
   8     (at your option) any later version.
   9 
  10     This program is distributed in the hope that it will be useful,
  11     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13     GNU General Public License for more details.
  14 
  15     You should have received a copy of the GNU General Public License
  16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18 
  19 /** \file exception.c */
  20 
  21 /**
  22  * \addtogroup PedException
  23  *
  24  * \brief Exception handling.
  25  * 
  26  * There are a few types of exceptions: PED_EXCEPTION_INFORMATION,
  27  * PED_EXCEPTION_WARNING, PED_EXCEPTION_ERROR, PED_EXCEPTION_FATAL,
  28  * PED_EXCEPTION_BUG.
  29  *
  30  * They are "thrown" when one of the above events occur while executing
  31  * a libparted function. For example, if ped_device_open() fails
  32  * because the device doesn't exist, an exception will be thrown.
  33  * Exceptions contain text describing what the event was. It will give
  34  * at least one option for resolving the exception: PED_EXCEPTION_FIX,
  35  * PED_EXCEPTION_YES, PED_EXCEPTION_NO, PED_EXCEPTION_OK, PED_EXCEPTION_RETRY,
  36  * PED_EXCEPTION_IGNORE, PED_EXCEPTION_CANCEL. After an exception is thrown,
  37  * there are two things that can happen:
  38  *
  39  * -# an exception handler is called, which selects how the exception should be
  40  *    resolved (usually by asking the user). Also note: an exception handler may
  41  *    choose to return PED_EXCEPTION_UNHANDLED. In this case, a default action
  42  *    will be taken by libparted (respectively the code that threw the
  43  *    exception). In general, a default action will be "safe".
  44  * -# the exception is not handled, because the caller of the function wants to
  45  *    handle everything itself. In this case, PED_EXCEPTION_UNHANDLED is
  46  *    returned.
  47  *
  48  * @{
  49  */
  50 
  51 #include <config.h>
  52 
  53 #include <parted/parted.h>
  54 #include <parted/debug.h>
  55 
  56 #define N_(String) String
  57 #if ENABLE_NLS
  58 #  include <libintl.h>
  59 #  define _(String) dgettext (PACKAGE, String)
  60 #else
  61 #  define _(String) (String)
  62 #endif /* ENABLE_NLS */
  63 
  64 #include <stdio.h>
  65 #include <stdarg.h>
  66 #include <stdlib.h>
  67 
  68 int                             ped_exception = 0;
  69 
  70 static PedExceptionOption default_handler (PedException* ex);
  71 
  72 static PedExceptionHandler*     ex_handler = default_handler;
  73 static PedException*            ex = NULL;
  74 static int                      ex_fetch_count = 0;
  75 
  76 static char*    type_strings [] = {
  77         N_("Information"),
  78         N_("Warning"),
  79         N_("Error"),
  80         N_("Fatal"),
  81         N_("Bug"),
  82         N_("No Implementation")
  83 };
  84 
  85 static char*    option_strings [] = {
  86         N_("Fix"),
  87         N_("Yes"),
  88         N_("No"),
  89         N_("OK"),
  90         N_("Retry"),
  91         N_("Ignore"),
  92         N_("Cancel")
  93 };
  94 
  95 /**
  96  *  Return a string describing an exception type.
  97  */
  98 char*
  99 ped_exception_get_type_string (PedExceptionType ex_type)
 100 {
 101         return type_strings [ex_type - 1];
 102 }
 103 
 104 /* FIXME: move this out to the prospective math.c */
 105 /* FIXME: this can probably be done more efficiently */
 106 static int
 107 ped_log2 (int n)
 108 {
 109         int x;
 110 
 111         PED_ASSERT (n > 0, return -1);
 112 
 113         for (x=0; 1 << x <= n; x++);
 114 
 115         return x - 1;
 116 }
 117 
 118 /**
 119  * Return a string describing an exception option.
 120  */
 121 char*
 122 ped_exception_get_option_string (PedExceptionOption ex_opt)
 123 {
 124         return option_strings [ped_log2 (ex_opt)];
 125 }
 126 
 127 static PedExceptionOption
 128 default_handler (PedException* e)
 129 {
 130         if (e->type == PED_EXCEPTION_BUG)
 131                 fprintf (stderr,
 132                         _("A bug has been detected in GNU Parted.  "
 133                         "Refer to the web site of parted "
 134                         "http://www.gnu.org/software/parted/parted.html "
 135                         "for more informations of what could be useful "
 136                         "for bug submitting!  "
 137                         "Please email a bug report to "
 138                         "bug-parted@gnu.org containing at least the "
 139                         "version (%s) and the following message:  "),
 140                         VERSION);
 141         else
 142                 fprintf (stderr, "%s: ",
 143                          ped_exception_get_type_string (e->type));
 144         fprintf (stderr, "%s\n", e->message);
 145 
 146         switch (e->options) {
 147                 case PED_EXCEPTION_OK:
 148                 case PED_EXCEPTION_CANCEL:
 149                 case PED_EXCEPTION_IGNORE:
 150                         return e->options;
 151 
 152                 default:
 153                         return PED_EXCEPTION_UNHANDLED;
 154         }
 155 }
 156 
 157 /**
 158  * Set the exception handler.
 159  *
 160  * The exception handler should return ONE of the options set in ex->options,
 161  * indicating the way the event should be resolved.
 162  */
 163 void
 164 ped_exception_set_handler (PedExceptionHandler* handler)
 165 {
 166         if (handler)
 167                 ex_handler = handler;
 168         else
 169                 ex_handler = default_handler;
 170 }
 171 
 172 /**
 173  * Get the current exception handler.
 174  */
 175 PedExceptionHandler *
 176 ped_exception_get_handler (void)
 177 {
 178         if (ex_handler)
 179                 return ex_handler;
 180         return default_handler;
 181 }
 182 
 183 /**
 184  * Assert that the current exception has been resolved.
 185  */
 186 void
 187 ped_exception_catch ()
 188 {
 189         if (ped_exception) {
 190                 ped_exception = 0;
 191 
 192                 ped_free (ex->message);
 193                 ped_free (ex);
 194                 ex = NULL;
 195         }
 196 }
 197 
 198 static PedExceptionOption
 199 do_throw ()
 200 {
 201         PedExceptionOption      ex_opt;
 202 
 203         ped_exception = 1;
 204 
 205         if (ex_fetch_count) {
 206                 return PED_EXCEPTION_UNHANDLED;
 207         } else {
 208                 ex_opt = ex_handler (ex);
 209                 ped_exception_catch ();
 210                 return ex_opt;
 211         }
 212 }
 213 
 214 /**
 215  * Throw an exception.
 216  *
 217  * You can also use this in a program using libparted.
 218  * "message" is a printf-like format string, so you can do 
 219  *
 220  * \code
 221  * ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_CANCEL,
 222  *      "Can't open %s", file_name);
 223  * \endcode
 224  *
 225  * Returns the option selected to resolve the exception. If the exception was
 226  * unhandled, PED_EXCEPTION_UNHANDLED is returned.
 227  */
 228 PedExceptionOption
 229 ped_exception_throw (PedExceptionType ex_type,
 230                      PedExceptionOption ex_opts, const char* message, ...)
 231 {
 232         va_list         arg_list;
 233         int result;
 234         static int size = 1000;
 235 
 236         if (ex)
 237                 ped_exception_catch ();
 238 
 239         ex = (PedException*) malloc (sizeof (PedException));
 240         if (!ex)
 241                 goto no_memory;
 242 
 243         ex->type = ex_type;
 244         ex->options = ex_opts;
 245 
 246         while (1) {
 247                         ex->message = (char*) malloc (size);
 248                         if (!ex->message)
 249                                         goto no_memory;
 250 
 251                         va_start (arg_list, message);
 252                         result = vsnprintf (ex->message, size, message, arg_list);
 253                         va_end (arg_list);
 254 
 255                         if (result > -1 && result < size)
 256                                         break;
 257 
 258                         size += 10;
 259         }
 260 
 261         return do_throw ();
 262 
 263 no_memory:
 264         fputs ("Out of memory in exception handler!\n", stderr);
 265 
 266         va_start (arg_list, message);
 267         vfprintf (stderr, message, arg_list);
 268         va_end (arg_list);
 269 
 270         return PED_EXCEPTION_UNHANDLED;
 271 }
 272 
 273 /**
 274  * Rethrow an unhandled exception.
 275  * This means repeating the last ped_exception_throw() statement.
 276  */
 277 PedExceptionOption
 278 ped_exception_rethrow ()
 279 {
 280         return do_throw ();
 281 }
 282 
 283 /**
 284  * Indicates that exceptions should not go to the exception handler, but
 285  * passed up to the calling function(s).  All calls to
 286  * ped_exception_throw() will return PED_EXCEPTION_UNHANDLED.
 287  */
 288 void
 289 ped_exception_fetch_all ()
 290 {
 291         ex_fetch_count++;
 292 }
 293 
 294 /**
 295  * Indicates that the calling function does not want to accept any
 296  * responsibility for exceptions any more.
 297  *
 298  * \note a caller of that function may still want responsibility, so
 299  *      ped_exception_throw() may not invoke the exception handler.
 300  *
 301  * \warning every call to this function must have a preceding
 302  *      ped_exception_fetch_all().
 303  */
 304 void
 305 ped_exception_leave_all ()
 306 {
 307         PED_ASSERT (ex_fetch_count > 0, return);
 308         ex_fetch_count--;
 309 }
 310 
 311 /** @} */
 312