summaryrefslogtreecommitdiff
path: root/apt-pkg/contrib/error.h
blob: bcaa7c99592fe7545c60be39f0be51449a7a7639 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
// -*- mode: cpp; mode: fold -*-
// Description								/*{{{*/
// $Id: error.h,v 1.8 2001/05/07 05:06:52 jgg Exp $
/* ######################################################################
   
   Global Erorr Class - Global error mechanism

   This class has a single global instance. When a function needs to 
   generate an error condition, such as a read error, it calls a member
   in this class to add the error to a stack of errors. 
   
   By using a stack the problem with a scheme like errno is removed and
   it allows a very detailed account of what went wrong to be transmitted
   to the UI for display. (Errno has problems because each function sets
   errno to 0 if it didn't have an error thus eraseing erno in the process
   of cleanup)
   
   Several predefined error generators are provided to handle common 
   things like errno. The general idea is that all methods return a bool.
   If the bool is true then things are OK, if it is false then things 
   should start being undone and the stack should unwind under program
   control.
   
   A Warning should not force the return of false. Things did not fail, but
   they might have had unexpected problems. Errors are stored in a FIFO
   so Pop will return the first item..
   
   I have some thoughts about extending this into a more general UI<-> 
   Engine interface, ie allowing the Engine to say 'The disk is full' in 
   a dialog that says 'Panic' and 'Retry'.. The error generator functions
   like errno, Warning and Error return false always so this is normal:
     if (open(..))
        return _error->Errno(..);
   
   This source is placed in the Public Domain, do with it what you will
   It was originally written by Jason Gunthorpe.
   
   ##################################################################### */
									/*}}}*/
#ifndef PKGLIB_ERROR_H
#define PKGLIB_ERROR_H

#include <apt-pkg/macros.h>

#include <iostream>
#include <list>
#include <string>

#include <stddef.h>
#include <stdarg.h>

class GlobalError							/*{{{*/
{
public:									/*{{{*/
	/** \brief a message can have one of following severity */
	enum MsgType {
		/** \brief Message will be printed instantly as it is likely that
			this error will lead to a complete crash */
		FATAL = 40,
		/** \brief An error does hinder the correct execution and should be corrected */
		ERROR = 30,
		/** \brief indicates problem that can lead to errors later on */
		WARNING = 20,
		/** \brief deprecation warnings, old fallback behavior, … */
		NOTICE = 10,
		/** \brief for developers only in areas it is hard to print something directly */
		DEBUG = 0
	};

	/** \brief add a fatal error message with errno to the list
	 *
	 *  \param Function name of the function generating the error
	 *  \param Description format string for the error message
	 *
	 *  \return \b false
	 */
	bool FatalE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;

	/** \brief add an Error message with errno to the list
	 *
	 *  \param Function name of the function generating the error
	 *  \param Description format string for the error message
	 *
	 *  \return \b false
	 */
	bool Errno(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;

	/** \brief add a warning message with errno to the list
	 *
	 *  A warning should be considered less severe than an error and
	 *  may be ignored by the client.
	 *
	 *  \param Function Name of the function generates the warning.
	 *  \param Description Format string for the warning message.
	 *
	 *  \return \b false
	 */
	bool WarningE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;

	/** \brief add a notice message with errno to the list
	 *
	 *  \param Function name of the function generating the error
	 *  \param Description format string for the error message
	 *
	 *  \return \b false
	 */
	bool NoticeE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;

	/** \brief add a debug message with errno to the list
	 *
	 *  \param Function name of the function generating the error
	 *  \param Description format string for the error message
	 *
	 *  \return \b false
	 */
	bool DebugE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;

	/** \brief adds an errno message with the given type
	 *
	 * \param type of the error message
	 * \param Function which failed
	 * \param Description of the error
	 */
	bool InsertErrno(MsgType const &type, const char* Function,
			 const char* Description,...) APT_PRINTF(4) APT_COLD;

	/** \brief adds an errno message with the given type
	 *
	 * args needs to be initialized with va_start and terminated
	 * with va_end by the caller. msgSize is also an out-parameter
	 * in case the msgSize was not enough to store the complete message.
	 *
	 * \param type of the error message
	 * \param Function which failed
	 * \param Description is the format string for args
	 * \param args list from a printf-like function
	 * \param errsv is the errno the error is for
	 * \param msgSize is the size of the char[] used to store message
	 * \return true if the message was added, false if not - the caller
	 * should call this method again in that case
	 */
	bool InsertErrno(MsgType type, const char* Function,
			 const char* Description, va_list &args,
			 int const errsv, size_t &msgSize) APT_COLD;

	/** \brief add an fatal error message to the list
	 *
	 *  Most of the stuff we consider as "error" is also "fatal" for
	 *  the user as the application will not have the expected result,
	 *  but a fatal message here means that it gets printed directly
	 *  to stderr in addition to adding it to the list as the error
	 *  leads sometimes to crashes and a maybe duplicated message
	 *  is better than "Segfault" as the only displayed text
	 *
	 *  \param Description Format string for the fatal error message.
	 *
	 *  \return \b false
	 */
	bool Fatal(const char *Description,...) APT_PRINTF(2) APT_COLD;

	/** \brief add an Error message to the list
	 *
	 *  \param Description Format string for the error message.
	 *
	 *  \return \b false
	 */
	bool Error(const char *Description,...) APT_PRINTF(2) APT_COLD;

	/** \brief add a warning message to the list
	 *
	 *  A warning should be considered less severe than an error and
	 *  may be ignored by the client.
	 *
	 *  \param Description Format string for the message
	 *
	 *  \return \b false
	 */
	bool Warning(const char *Description,...) APT_PRINTF(2) APT_COLD;

	/** \brief add a notice message to the list
	 *
	 *  A notice should be considered less severe than an error or a
	 *  warning and can be ignored by the client without further problems
	 *  for some times, but he should consider fixing the problem.
	 *  This error type can be used for e.g. deprecation warnings of options.
	 *
	 *  \param Description Format string for the message
	 *
	 *  \return \b false
	 */
	bool Notice(const char *Description,...) APT_PRINTF(2) APT_COLD;

	/** \brief add a debug message to the list
	 *
	 *  \param Description Format string for the message
	 *
	 *  \return \b false
	 */
	bool Debug(const char *Description,...) APT_PRINTF(2) APT_COLD;

	/** \brief adds an error message with the given type
	 *
	 * \param type of the error message
	 * \param Description of the error
	 */
	bool Insert(MsgType const &type, const char* Description,...) APT_PRINTF(3) APT_COLD;

	/** \brief adds an error message with the given type
	 *
	 * args needs to be initialized with va_start and terminated
	 * with va_end by the caller. msgSize is also an out-parameter
	 * in case the msgSize was not enough to store the complete message.
	 *
	 * \param type of the error message
	 * \param Description is the format string for args
	 * \param args list from a printf-like function
	 * \param msgSize is the size of the char[] used to store message
	 * \return true if the message was added, false if not - the caller
	 * should call this method again in that case
	 */
	bool Insert(MsgType type, const char* Description,
			 va_list &args, size_t &msgSize) APT_COLD;

	/** \brief is an error in the list?
	 *
	 *  \return \b true if an error is included in the list, \b false otherwise
	 */
	inline bool PendingError() const APT_PURE {return PendingFlag;};

	/** \brief convert a stored error to a return code
	 *
	 *  Put simply, the entire concept of PendingError() is flawed :/.
         *
         *  The typical "if (PendingError()) return false;" check that is
         *  strewn throughout the codebase "compounds", making it impossible
         *  for there to be any nuance about the notion of "error" when a
         *  subsystem needs to fail but a higher-level system needs to work.
         *
         *  However, the codebase is also horribly broken with respect to
         *  errors, as it fails to use C++ exceptions when warranted and
         *  instead relies on this insane indirect error mechanism to check
         *  the failure status of a constructor. What is thereby needed is
         *  a way to clear the PendingError() flag without also discarding
         *  the underlying errors, so we have to convert them to warnings.
         *
	 *  \return \b false
	 */
	bool ReturnError() APT_COLD;

	/** \brief is the list empty?
	 *
	 *  Can be used to check if the current stack level doesn't include
	 *  anything equal or more severe than a given threshold, defaulting
	 *  to warning level for historic reasons.
	 *
	 *  \param threshold minimum level considered
	 *
	 *  \return \b true if the list is empty, \b false otherwise
	 */
	bool empty(MsgType const &threshold = WARNING) const APT_PURE;

	/** \brief returns and removes the first (or last) message in the list
	 *
	 *  \param[out] Text message of the first/last item
	 *
	 *  \return \b true if the message was an error, \b false otherwise
	 */
	bool PopMessage(std::string &Text);

	/** \brief clears the list of messages */
	void Discard();

	/** \brief outputs the list of messages to the given stream
	 *
	 *  Note that all messages are discarded, even undisplayed ones.
	 *
	 *  \param[out] out output stream to write the messages in
	 *  \param threshold minimum level considered
	 *  \param mergeStack if true recursively dumps the entire stack
	 */
	void DumpErrors(std::ostream &out, MsgType const &threshold = WARNING,
			bool const &mergeStack = true);

	/** \brief dumps the list of messages to std::cerr
	 *
	 *  Note that all messages are discarded, also the notices
	 *  displayed or not.
	 *
	 *  \param threshold minimum level printed
	 */
	void inline DumpErrors(MsgType const &threshold) {
		DumpErrors(std::cerr, threshold);
	}

        // mvo: we do this instead of using a default parameter in the
        //      previous declaration to avoid a (subtle) API break for
        //      e.g. sigc++ and mem_fun0
	/** \brief dumps the messages of type WARNING or higher to std::cerr
	 *
	 *  Note that all messages are discarded, displayed or not.
	 *
	 */
	void inline DumpErrors() {
                DumpErrors(WARNING);
	}

	/** \brief put the current Messages into the stack
	 *
	 *  All "old" messages will be pushed into a stack to
	 *  them later back, but for now the Message query will be
	 *  empty and performs as no messages were present before.
	 *
	 * The stack can be as deep as you want - all stack operations
	 * will only operate on the last element in the stack.
	 */
	void PushToStack();

	/** \brief throw away all current messages */
	void RevertToStack();

	/** \brief merge current and stack together */
	void MergeWithStack();

	/** \brief return the deep of the stack */
	size_t StackCount() const APT_PURE {
		return Stacks.size();
	}

	GlobalError();
									/*}}}*/
private:								/*{{{*/
	struct Item {
		std::string Text;
		MsgType Type;

		Item(char const *Text, MsgType const &Type) :
			Text(Text), Type(Type) {};

		APT_HIDDEN friend std::ostream& operator<< (std::ostream &out, Item i) {
			switch(i.Type) {
			case FATAL:
			case ERROR: out << 'E'; break;
			case WARNING: out << 'W'; break;
			case NOTICE: out << 'N'; break;
			case DEBUG: out << 'D'; break;
			}
			out << ": ";
			std::string::size_type line_start = 0;
			std::string::size_type line_end;
			while ((line_end = i.Text.find_first_of("\n\r", line_start)) != std::string::npos) {
				if (line_start != 0)
				   out << std::endl << "   ";
				out << i.Text.substr(line_start, line_end - line_start);
				line_start = i.Text.find_first_not_of("\n\r", line_end + 1);
				if (line_start == std::string::npos)
				   break;
			}
			if (line_start == 0)
			   out << i.Text;
			else if (line_start != std::string::npos)
			   out << std::endl << "   " << i.Text.substr(line_start);
			return out;
		}
	};

	std::list<Item> Messages;
	bool PendingFlag;

	struct MsgStack {
		std::list<Item> Messages;
		bool const PendingFlag;

		MsgStack(std::list<Item> const &Messages, bool const &Pending) :
			 Messages(Messages), PendingFlag(Pending) {};
	};

	std::list<MsgStack> Stacks;
									/*}}}*/
};
									/*}}}*/

// The 'extra-ansi' syntax is used to help with collisions. 
GlobalError *_GetErrorObj();
#define _error _GetErrorObj()

#endif