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