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*
ped_exception_get_type_string(PedExceptionType ex_type)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
ped_log2(int n)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*
ped_exception_get_option_string(PedExceptionOption ex_opt)122 ped_exception_get_option_string (PedExceptionOption ex_opt)
123 {
124 return option_strings [ped_log2 (ex_opt)];
125 }
126
127 static PedExceptionOption
default_handler(PedException * e)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
ped_exception_set_handler(PedExceptionHandler * handler)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 *
ped_exception_get_handler(void)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
ped_exception_catch()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
do_throw()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
ped_exception_throw(PedExceptionType ex_type,PedExceptionOption ex_opts,const char * message,...)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
ped_exception_rethrow()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
ped_exception_fetch_all()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
ped_exception_leave_all()305 ped_exception_leave_all ()
306 {
307 PED_ASSERT (ex_fetch_count > 0, return);
308 ex_fetch_count--;
309 }
310
311 /** @} */
312
313