]>
Commit | Line | Data |
---|---|---|
578bfd0a AL |
1 | // -*- mode: cpp; mode: fold -*- |
2 | // Description /*{{{*/ | |
233b185f | 3 | // $Id: error.h,v 1.8 2001/05/07 05:06:52 jgg Exp $ |
578bfd0a AL |
4 | /* ###################################################################### |
5 | ||
6 | Global Erorr Class - Global error mechanism | |
7 | ||
8 | This class has a single global instance. When a function needs to | |
9 | generate an error condition, such as a read error, it calls a member | |
10 | in this class to add the error to a stack of errors. | |
11 | ||
12 | By using a stack the problem with a scheme like errno is removed and | |
13 | it allows a very detailed account of what went wrong to be transmitted | |
14 | to the UI for display. (Errno has problems because each function sets | |
15 | errno to 0 if it didn't have an error thus eraseing erno in the process | |
16 | of cleanup) | |
17 | ||
18 | Several predefined error generators are provided to handle common | |
19 | things like errno. The general idea is that all methods return a bool. | |
20 | If the bool is true then things are OK, if it is false then things | |
21 | should start being undone and the stack should unwind under program | |
22 | control. | |
23 | ||
24 | A Warning should not force the return of false. Things did not fail, but | |
25 | they might have had unexpected problems. Errors are stored in a FIFO | |
26 | so Pop will return the first item.. | |
27 | ||
28 | I have some thoughts about extending this into a more general UI<-> | |
29 | Engine interface, ie allowing the Engine to say 'The disk is full' in | |
30 | a dialog that says 'Panic' and 'Retry'.. The error generator functions | |
31 | like errno, Warning and Error return false always so this is normal: | |
32 | if (open(..)) | |
33 | return _error->Errno(..); | |
34 | ||
35 | This source is placed in the Public Domain, do with it what you will | |
36 | It was originally written by Jason Gunthorpe. | |
37 | ||
38 | ##################################################################### */ | |
39 | /*}}}*/ | |
578bfd0a AL |
40 | #ifndef PKGLIB_ERROR_H |
41 | #define PKGLIB_ERROR_H | |
42 | ||
2893f7b5 | 43 | #include <apt-pkg/macros.h> |
13500573 | 44 | |
98ee7cd3 DK |
45 | #include <iostream> |
46 | #include <list> | |
578bfd0a | 47 | #include <string> |
578bfd0a | 48 | |
453b82a3 | 49 | #include <stddef.h> |
98ee7cd3 DK |
50 | #include <stdarg.h> |
51 | ||
52 | class GlobalError /*{{{*/ | |
578bfd0a | 53 | { |
98ee7cd3 DK |
54 | public: /*{{{*/ |
55 | /** \brief a message can have one of following severity */ | |
56 | enum MsgType { | |
57 | /** \brief Message will be printed instantly as it is likely that | |
58 | this error will lead to a complete crash */ | |
59 | FATAL = 40, | |
60 | /** \brief An error does hinder the correct execution and should be corrected */ | |
61 | ERROR = 30, | |
62 | /** \brief indicates problem that can lead to errors later on */ | |
63 | WARNING = 20, | |
64 | /** \brief deprecation warnings, old fallback behavior, … */ | |
65 | NOTICE = 10, | |
66 | /** \brief for developers only in areas it is hard to print something directly */ | |
67 | DEBUG = 0 | |
68 | }; | |
578bfd0a | 69 | |
98ee7cd3 DK |
70 | /** \brief add a fatal error message with errno to the list |
71 | * | |
72 | * \param Function name of the function generating the error | |
73 | * \param Description format string for the error message | |
74 | * | |
75 | * \return \b false | |
76 | */ | |
453b82a3 | 77 | bool FatalE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD; |
578bfd0a | 78 | |
98ee7cd3 DK |
79 | /** \brief add an Error message with errno to the list |
80 | * | |
81 | * \param Function name of the function generating the error | |
82 | * \param Description format string for the error message | |
83 | * | |
84 | * \return \b false | |
85 | */ | |
453b82a3 | 86 | bool Errno(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD; |
578bfd0a | 87 | |
98ee7cd3 DK |
88 | /** \brief add a warning message with errno to the list |
89 | * | |
90 | * A warning should be considered less severe than an error and | |
91 | * may be ignored by the client. | |
92 | * | |
93 | * \param Function Name of the function generates the warning. | |
94 | * \param Description Format string for the warning message. | |
95 | * | |
96 | * \return \b false | |
97 | */ | |
453b82a3 | 98 | bool WarningE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD; |
578bfd0a | 99 | |
98ee7cd3 DK |
100 | /** \brief add a notice message with errno to the list |
101 | * | |
102 | * \param Function name of the function generating the error | |
103 | * \param Description format string for the error message | |
104 | * | |
105 | * \return \b false | |
106 | */ | |
453b82a3 | 107 | bool NoticeE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD; |
98ee7cd3 DK |
108 | |
109 | /** \brief add a debug message with errno to the list | |
110 | * | |
111 | * \param Function name of the function generating the error | |
112 | * \param Description format string for the error message | |
113 | * | |
114 | * \return \b false | |
115 | */ | |
453b82a3 | 116 | bool DebugE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD; |
98ee7cd3 | 117 | |
cd7bbc47 DK |
118 | /** \brief adds an errno message with the given type |
119 | * | |
120 | * \param type of the error message | |
121 | * \param Function which failed | |
122 | * \param Description of the error | |
123 | */ | |
124 | bool InsertErrno(MsgType const &type, const char* Function, | |
453b82a3 | 125 | const char* Description,...) APT_PRINTF(4) APT_COLD; |
cd7bbc47 | 126 | |
c7873955 DK |
127 | /** \brief adds an errno message with the given type |
128 | * | |
129 | * args needs to be initialized with va_start and terminated | |
130 | * with va_end by the caller. msgSize is also an out-parameter | |
131 | * in case the msgSize was not enough to store the complete message. | |
132 | * | |
133 | * \param type of the error message | |
134 | * \param Function which failed | |
135 | * \param Description is the format string for args | |
136 | * \param args list from a printf-like function | |
137 | * \param errsv is the errno the error is for | |
138 | * \param msgSize is the size of the char[] used to store message | |
139 | * \return true if the message was added, false if not - the caller | |
140 | * should call this method again in that case | |
141 | */ | |
142 | bool InsertErrno(MsgType type, const char* Function, | |
143 | const char* Description, va_list &args, | |
a02db58f | 144 | int const errsv, size_t &msgSize) APT_COLD; |
c7873955 | 145 | |
98ee7cd3 DK |
146 | /** \brief add an fatal error message to the list |
147 | * | |
148 | * Most of the stuff we consider as "error" is also "fatal" for | |
149 | * the user as the application will not have the expected result, | |
150 | * but a fatal message here means that it gets printed directly | |
646ebd92 | 151 | * to stderr in addition to adding it to the list as the error |
98ee7cd3 DK |
152 | * leads sometimes to crashes and a maybe duplicated message |
153 | * is better than "Segfault" as the only displayed text | |
154 | * | |
155 | * \param Description Format string for the fatal error message. | |
156 | * | |
157 | * \return \b false | |
158 | */ | |
453b82a3 | 159 | bool Fatal(const char *Description,...) APT_PRINTF(2) APT_COLD; |
98ee7cd3 DK |
160 | |
161 | /** \brief add an Error message to the list | |
162 | * | |
163 | * \param Description Format string for the error message. | |
164 | * | |
165 | * \return \b false | |
166 | */ | |
453b82a3 | 167 | bool Error(const char *Description,...) APT_PRINTF(2) APT_COLD; |
98ee7cd3 DK |
168 | |
169 | /** \brief add a warning message to the list | |
170 | * | |
171 | * A warning should be considered less severe than an error and | |
172 | * may be ignored by the client. | |
173 | * | |
174 | * \param Description Format string for the message | |
175 | * | |
176 | * \return \b false | |
177 | */ | |
453b82a3 | 178 | bool Warning(const char *Description,...) APT_PRINTF(2) APT_COLD; |
98ee7cd3 DK |
179 | |
180 | /** \brief add a notice message to the list | |
181 | * | |
182 | * A notice should be considered less severe than an error or a | |
183 | * warning and can be ignored by the client without further problems | |
184 | * for some times, but he should consider fixing the problem. | |
185 | * This error type can be used for e.g. deprecation warnings of options. | |
186 | * | |
187 | * \param Description Format string for the message | |
188 | * | |
189 | * \return \b false | |
190 | */ | |
453b82a3 | 191 | bool Notice(const char *Description,...) APT_PRINTF(2) APT_COLD; |
98ee7cd3 DK |
192 | |
193 | /** \brief add a debug message to the list | |
194 | * | |
195 | * \param Description Format string for the message | |
196 | * | |
197 | * \return \b false | |
198 | */ | |
453b82a3 | 199 | bool Debug(const char *Description,...) APT_PRINTF(2) APT_COLD; |
98ee7cd3 | 200 | |
cd7bbc47 DK |
201 | /** \brief adds an error message with the given type |
202 | * | |
203 | * \param type of the error message | |
204 | * \param Description of the error | |
205 | */ | |
453b82a3 | 206 | bool Insert(MsgType const &type, const char* Description,...) APT_PRINTF(3) APT_COLD; |
cd7bbc47 | 207 | |
c7873955 DK |
208 | /** \brief adds an error message with the given type |
209 | * | |
210 | * args needs to be initialized with va_start and terminated | |
211 | * with va_end by the caller. msgSize is also an out-parameter | |
212 | * in case the msgSize was not enough to store the complete message. | |
213 | * | |
214 | * \param type of the error message | |
215 | * \param Description is the format string for args | |
216 | * \param args list from a printf-like function | |
217 | * \param msgSize is the size of the char[] used to store message | |
218 | * \return true if the message was added, false if not - the caller | |
219 | * should call this method again in that case | |
220 | */ | |
221 | bool Insert(MsgType type, const char* Description, | |
453b82a3 | 222 | va_list &args, size_t &msgSize) APT_COLD; |
c7873955 | 223 | |
98ee7cd3 DK |
224 | /** \brief is an error in the list? |
225 | * | |
226 | * \return \b true if an error is included in the list, \b false otherwise | |
227 | */ | |
a02db58f | 228 | inline bool PendingError() const APT_PURE {return PendingFlag;}; |
98ee7cd3 | 229 | |
baec76f5 JF |
230 | /** \brief convert a stored error to a return code |
231 | * | |
232 | * Put simply, the entire concept of PendingError() is flawed :/. | |
233 | * | |
234 | * The typical "if (PendingError()) return false;" check that is | |
235 | * strewn throughout the codebase "compounds", making it impossible | |
236 | * for there to be any nuance about the notion of "error" when a | |
237 | * subsystem needs to fail but a higher-level system needs to work. | |
238 | * | |
239 | * However, the codebase is also horribly broken with respect to | |
240 | * errors, as it fails to use C++ exceptions when warranted and | |
241 | * instead relies on this insane indirect error mechanism to check | |
242 | * the failure status of a constructor. What is thereby needed is | |
243 | * a way to clear the PendingError() flag without also discarding | |
244 | * the underlying errors, so we have to convert them to warnings. | |
245 | * | |
246 | * \return \b false | |
247 | */ | |
248 | bool ReturnError() APT_COLD; | |
249 | ||
98ee7cd3 DK |
250 | /** \brief is the list empty? |
251 | * | |
646ebd92 DK |
252 | * Can be used to check if the current stack level doesn't include |
253 | * anything equal or more severe than a given threshold, defaulting | |
254 | * to warning level for historic reasons. | |
98ee7cd3 | 255 | * |
646ebd92 | 256 | * \param threshold minimum level considered |
98ee7cd3 | 257 | * |
646ebd92 | 258 | * \return \b true if the list is empty, \b false otherwise |
98ee7cd3 | 259 | */ |
a02db58f | 260 | bool empty(MsgType const &threshold = WARNING) const APT_PURE; |
98ee7cd3 DK |
261 | |
262 | /** \brief returns and removes the first (or last) message in the list | |
263 | * | |
264 | * \param[out] Text message of the first/last item | |
265 | * | |
266 | * \return \b true if the message was an error, \b false otherwise | |
267 | */ | |
268 | bool PopMessage(std::string &Text); | |
269 | ||
270 | /** \brief clears the list of messages */ | |
271 | void Discard(); | |
272 | ||
273 | /** \brief outputs the list of messages to the given stream | |
274 | * | |
646ebd92 | 275 | * Note that all messages are discarded, even undisplayed ones. |
98ee7cd3 DK |
276 | * |
277 | * \param[out] out output stream to write the messages in | |
646ebd92 DK |
278 | * \param threshold minimum level considered |
279 | * \param mergeStack if true recursively dumps the entire stack | |
98ee7cd3 | 280 | */ |
12be8a62 | 281 | void DumpErrors(std::ostream &out, MsgType const &threshold = WARNING, |
c4ba7c44 | 282 | bool const &mergeStack = true); |
98ee7cd3 DK |
283 | |
284 | /** \brief dumps the list of messages to std::cerr | |
285 | * | |
286 | * Note that all messages are discarded, also the notices | |
287 | * displayed or not. | |
288 | * | |
12be8a62 | 289 | * \param threshold minimum level printed |
98ee7cd3 | 290 | */ |
ca0d389c | 291 | void inline DumpErrors(MsgType const &threshold) { |
12be8a62 | 292 | DumpErrors(std::cerr, threshold); |
98ee7cd3 DK |
293 | } |
294 | ||
ca0d389c MV |
295 | // mvo: we do this instead of using a default parameter in the |
296 | // previous declaration to avoid a (subtle) API break for | |
297 | // e.g. sigc++ and mem_fun0 | |
298 | /** \brief dumps the messages of type WARNING or higher to std::cerr | |
299 | * | |
300 | * Note that all messages are discarded, displayed or not. | |
301 | * | |
302 | */ | |
303 | void inline DumpErrors() { | |
304 | DumpErrors(WARNING); | |
305 | } | |
306 | ||
c4ba7c44 DK |
307 | /** \brief put the current Messages into the stack |
308 | * | |
309 | * All "old" messages will be pushed into a stack to | |
310 | * them later back, but for now the Message query will be | |
311 | * empty and performs as no messages were present before. | |
312 | * | |
313 | * The stack can be as deep as you want - all stack operations | |
314 | * will only operate on the last element in the stack. | |
315 | */ | |
316 | void PushToStack(); | |
317 | ||
318 | /** \brief throw away all current messages */ | |
319 | void RevertToStack(); | |
320 | ||
321 | /** \brief merge current and stack together */ | |
322 | void MergeWithStack(); | |
323 | ||
324 | /** \brief return the deep of the stack */ | |
a02db58f | 325 | size_t StackCount() const APT_PURE { |
c4ba7c44 DK |
326 | return Stacks.size(); |
327 | } | |
328 | ||
98ee7cd3 DK |
329 | GlobalError(); |
330 | /*}}}*/ | |
331 | private: /*{{{*/ | |
332 | struct Item { | |
333 | std::string Text; | |
334 | MsgType Type; | |
335 | ||
336 | Item(char const *Text, MsgType const &Type) : | |
337 | Text(Text), Type(Type) {}; | |
338 | ||
82153438 | 339 | APT_HIDDEN friend std::ostream& operator<< (std::ostream &out, Item i) { |
98ee7cd3 DK |
340 | switch(i.Type) { |
341 | case FATAL: | |
82153438 DK |
342 | case ERROR: out << 'E'; break; |
343 | case WARNING: out << 'W'; break; | |
344 | case NOTICE: out << 'N'; break; | |
345 | case DEBUG: out << 'D'; break; | |
98ee7cd3 | 346 | } |
82153438 DK |
347 | out << ": "; |
348 | std::string::size_type line_start = 0; | |
349 | std::string::size_type line_end; | |
350 | while ((line_end = i.Text.find_first_of("\n\r", line_start)) != std::string::npos) { | |
351 | if (line_start != 0) | |
352 | out << std::endl << " "; | |
353 | out << i.Text.substr(line_start, line_end - line_start); | |
354 | line_start = i.Text.find_first_not_of("\n\r", line_end + 1); | |
355 | if (line_start == std::string::npos) | |
356 | break; | |
357 | } | |
358 | if (line_start == 0) | |
359 | out << i.Text; | |
360 | else if (line_start != std::string::npos) | |
361 | out << std::endl << " " << i.Text.substr(line_start); | |
362 | return out; | |
98ee7cd3 DK |
363 | } |
364 | }; | |
365 | ||
366 | std::list<Item> Messages; | |
367 | bool PendingFlag; | |
368 | ||
c4ba7c44 | 369 | struct MsgStack { |
95278287 | 370 | std::list<Item> Messages; |
c4ba7c44 DK |
371 | bool const PendingFlag; |
372 | ||
373 | MsgStack(std::list<Item> const &Messages, bool const &Pending) : | |
374 | Messages(Messages), PendingFlag(Pending) {}; | |
375 | }; | |
376 | ||
377 | std::list<MsgStack> Stacks; | |
98ee7cd3 | 378 | /*}}}*/ |
578bfd0a | 379 | }; |
98ee7cd3 | 380 | /*}}}*/ |
578bfd0a | 381 | |
6f27a7fc AL |
382 | // The 'extra-ansi' syntax is used to help with collisions. |
383 | GlobalError *_GetErrorObj(); | |
384 | #define _error _GetErrorObj() | |
578bfd0a AL |
385 | |
386 | #endif |