// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: error.cc,v 1.11 2002/03/26 07:38:58 jgg Exp $
/* ######################################################################
-
- Global Erorr Class - Global error mechanism
+
+ Global Error Class - Global error mechanism
We use a simple STL vector to store each error record. A PendingFlag
is kept which indicates when the vector contains a Sever error.
-
+
This source is placed in the Public Domain, do with it what you will
It was originally written by Jason Gunthorpe.
-
+
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
+#include <config.h>
+
#include <apt-pkg/error.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <list>
#include <iostream>
#include <errno.h>
#include <stdio.h>
-#include <string>
-#include <stdarg.h>
+#include <stdlib.h>
#include <unistd.h>
+#include <string>
+#include <cstring>
+#include <algorithm>
-#include "config.h"
- /*}}}*/
-
-using namespace std;
+ /*}}}*/
// Global Error Object /*{{{*/
/* If the implementation supports posix threads then the accessor function
Per-Thread error object is maintained in much the same manner as libc
manages errno */
#if defined(_POSIX_THREADS) && defined(HAVE_PTHREAD)
- #include <pthread.h>
-
- static pthread_key_t ErrorKey;
- static void ErrorDestroy(void *Obj) {delete (GlobalError *)Obj;};
- static void KeyAlloc() {pthread_key_create(&ErrorKey,ErrorDestroy);};
-
- GlobalError *_GetErrorObj()
- {
- static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once,KeyAlloc);
-
- void *Res = pthread_getspecific(ErrorKey);
- if (Res == 0)
- pthread_setspecific(ErrorKey,Res = new GlobalError);
- return (GlobalError *)Res;
- }
+ #include <pthread.h>
+
+ static pthread_key_t ErrorKey;
+ static void ErrorDestroy(void *Obj) {delete (GlobalError *)Obj;};
+ static void KeyAlloc() {pthread_key_create(&ErrorKey,ErrorDestroy);};
+
+ GlobalError *_GetErrorObj() {
+ static pthread_once_t Once = PTHREAD_ONCE_INIT;
+ pthread_once(&Once,KeyAlloc);
+
+ void *Res = pthread_getspecific(ErrorKey);
+ if (Res == 0)
+ pthread_setspecific(ErrorKey,Res = new GlobalError);
+ return (GlobalError *)Res;
+ }
#else
- GlobalError *_GetErrorObj()
- {
- static GlobalError *Obj = new GlobalError;
- return Obj;
- }
+ GlobalError *_GetErrorObj() {
+ static GlobalError *Obj = new GlobalError;
+ return Obj;
+ }
#endif
/*}}}*/
-
// GlobalError::GlobalError - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-GlobalError::GlobalError() : List(0), PendingFlag(false)
-{
+GlobalError::GlobalError() : PendingFlag(false) {}
+ /*}}}*/
+// GlobalError::FatalE, Errno, WarningE, NoticeE and DebugE - Add to the list/*{{{*/
+#define GEMessage(NAME, TYPE) \
+bool GlobalError::NAME (const char *Function, const char *Description,...) { \
+ va_list args; \
+ size_t msgSize = 400; \
+ int const errsv = errno; \
+ bool retry; \
+ do { \
+ va_start(args,Description); \
+ retry = InsertErrno(TYPE, Function, Description, args, errsv, msgSize); \
+ va_end(args); \
+ } while (retry); \
+ return false; \
}
+GEMessage(FatalE, FATAL)
+GEMessage(Errno, ERROR)
+GEMessage(WarningE, WARNING)
+GEMessage(NoticeE, NOTICE)
+GEMessage(DebugE, DEBUG)
+#undef GEMessage
/*}}}*/
-// GlobalError::Errno - Get part of the error string from errno /*{{{*/
-// ---------------------------------------------------------------------
-/* Function indicates the stdlib function that failed and Description is
- a user string that leads the text. Form is:
- Description - Function (errno: strerror)
- Carefull of the buffer overrun, sprintf.
- */
-bool GlobalError::Errno(const char *Function,const char *Description,...)
-{
- va_list args;
- va_start(args,Description);
-
- // sprintf the description
- char S[400];
- vsnprintf(S,sizeof(S),Description,args);
- snprintf(S + strlen(S),sizeof(S) - strlen(S),
- " - %s (%i %s)",Function,errno,strerror(errno));
-
- // Put it on the list
- Item *Itm = new Item;
- Itm->Text = S;
- Itm->Error = true;
- Insert(Itm);
-
- PendingFlag = true;
-
- return false;
+// GlobalError::InsertErrno - Get part of the errortype string from errno/*{{{*/
+bool GlobalError::InsertErrno(MsgType const &type, const char *Function,
+ const char *Description,...) {
+ va_list args;
+ size_t msgSize = 400;
+ int const errsv = errno;
+ bool retry;
+ do {
+ va_start(args,Description);
+ retry = InsertErrno(type, Function, Description, args, errsv, msgSize);
+ va_end(args);
+ } while (retry);
+ return false;
}
/*}}}*/
-// GlobalError::WarningE - Get part of the warn string from errno /*{{{*/
-// ---------------------------------------------------------------------
-/* Function indicates the stdlib function that failed and Description is
- a user string that leads the text. Form is:
- Description - Function (errno: strerror)
- Carefull of the buffer overrun, sprintf.
- */
-bool GlobalError::WarningE(const char *Function,const char *Description,...)
-{
- va_list args;
- va_start(args,Description);
-
- // sprintf the description
- char S[400];
- vsnprintf(S,sizeof(S),Description,args);
- snprintf(S + strlen(S),sizeof(S) - strlen(S)," - %s (%i %s)",Function,errno,strerror(errno));
-
- // Put it on the list
- Item *Itm = new Item;
- Itm->Text = S;
- Itm->Error = false;
- Insert(Itm);
-
- return false;
+// GlobalError::InsertErrno - formats an error message with the errno /*{{{*/
+bool GlobalError::InsertErrno(MsgType type, const char* Function,
+ const char* Description, va_list &args,
+ int const errsv, size_t &msgSize) {
+ char* S = (char*) malloc(msgSize);
+ int const n = snprintf(S, msgSize, "%s - %s (%i: %s)", Description,
+ Function, errsv, strerror(errsv));
+ if (n > -1 && ((unsigned int) n) < msgSize);
+ else {
+ if (n > -1)
+ msgSize = n + 1;
+ else
+ msgSize *= 2;
+ free(S);
+ return true;
+ }
+
+ bool const geins = Insert(type, S, args, msgSize);
+ free(S);
+ return geins;
}
/*}}}*/
-// GlobalError::Error - Add an error to the list /*{{{*/
-// ---------------------------------------------------------------------
-/* Just vsprintfs and pushes */
-bool GlobalError::Error(const char *Description,...)
-{
- va_list args;
- va_start(args,Description);
-
- // sprintf the description
- char S[400];
- vsnprintf(S,sizeof(S),Description,args);
-
- // Put it on the list
- Item *Itm = new Item;
- Itm->Text = S;
- Itm->Error = true;
- Insert(Itm);
-
- PendingFlag = true;
-
- return false;
+// GlobalError::Fatal, Error, Warning, Notice and Debug - Add to the list/*{{{*/
+#define GEMessage(NAME, TYPE) \
+bool GlobalError::NAME (const char *Description,...) { \
+ va_list args; \
+ size_t msgSize = 400; \
+ bool retry; \
+ do { \
+ va_start(args,Description); \
+ retry = Insert(TYPE, Description, args, msgSize); \
+ va_end(args); \
+ } while (retry); \
+ return false; \
}
+GEMessage(Fatal, FATAL)
+GEMessage(Error, ERROR)
+GEMessage(Warning, WARNING)
+GEMessage(Notice, NOTICE)
+GEMessage(Debug, DEBUG)
+#undef GEMessage
/*}}}*/
-// GlobalError::Warning - Add a warning to the list /*{{{*/
-// ---------------------------------------------------------------------
-/* This doesn't set the pending error flag */
-bool GlobalError::Warning(const char *Description,...)
+// GlobalError::Insert - Add a errotype message to the list /*{{{*/
+bool GlobalError::Insert(MsgType const &type, const char *Description,...)
{
- va_list args;
- va_start(args,Description);
-
- // sprintf the description
- char S[400];
- vsnprintf(S,sizeof(S),Description,args);
-
- // Put it on the list
- Item *Itm = new Item;
- Itm->Text = S;
- Itm->Error = false;
- Insert(Itm);
-
- return false;
+ va_list args;
+ size_t msgSize = 400;
+ bool retry;
+ do {
+ va_start(args,Description);
+ retry = Insert(type, Description, args, msgSize);
+ va_end(args);
+ } while (retry);
+ return false;
+}
+ /*}}}*/
+// GlobalError::Insert - Insert a new item at the end /*{{{*/
+bool GlobalError::Insert(MsgType type, const char* Description,
+ va_list &args, size_t &msgSize) {
+ char* S = (char*) malloc(msgSize);
+ int const n = vsnprintf(S, msgSize, Description, args);
+ if (n > -1 && ((unsigned int) n) < msgSize);
+ else {
+ if (n > -1)
+ msgSize = n + 1;
+ else
+ msgSize *= 2;
+ free(S);
+ return true;
+ }
+
+ Item const m(S, type);
+ Messages.push_back(m);
+
+ if (type == ERROR || type == FATAL)
+ PendingFlag = true;
+
+ if (type == FATAL || type == DEBUG)
+ std::clog << m << std::endl;
+
+ free(S);
+ return false;
}
/*}}}*/
// GlobalError::PopMessage - Pulls a single message out /*{{{*/
-// ---------------------------------------------------------------------
-/* This should be used in a loop checking empty() each cycle. It returns
- true if the message is an error. */
-bool GlobalError::PopMessage(string &Text)
-{
- if (List == 0)
- return false;
-
- bool Ret = List->Error;
- Text = List->Text;
- Item *Old = List;
- List = List->Next;
- delete Old;
-
- // This really should check the list to see if only warnings are left..
- if (List == 0)
- PendingFlag = false;
-
- return Ret;
+bool GlobalError::PopMessage(std::string &Text) {
+ if (Messages.empty() == true)
+ return false;
+
+ Item const msg = Messages.front();
+ Messages.pop_front();
+
+ bool const Ret = (msg.Type == ERROR || msg.Type == FATAL);
+ Text = msg.Text;
+ if (PendingFlag == false || Ret == false)
+ return Ret;
+
+ // check if another error message is pending
+ for (std::list<Item>::const_iterator m = Messages.begin();
+ m != Messages.end(); ++m)
+ if (m->Type == ERROR || m->Type == FATAL)
+ return Ret;
+
+ PendingFlag = false;
+ return Ret;
}
/*}}}*/
// GlobalError::DumpErrors - Dump all of the errors/warns to cerr /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void GlobalError::DumpErrors()
-{
- // Print any errors or warnings found
- string Err;
- while (empty() == false)
- {
- bool Type = PopMessage(Err);
- if (Type == true)
- cerr << "E: " << Err << endl;
- else
- cerr << "W: " << Err << endl;
- }
+void GlobalError::DumpErrors(std::ostream &out, MsgType const &threshold,
+ bool const &mergeStack) {
+ if (mergeStack == true)
+ for (std::list<MsgStack>::const_reverse_iterator s = Stacks.rbegin();
+ s != Stacks.rend(); ++s)
+ std::copy(s->Messages.begin(), s->Messages.end(), std::front_inserter(Messages));
+
+ std::for_each(Messages.begin(), Messages.end(), [&threshold, &out](Item const &m) {
+ if (m.Type >= threshold)
+ out << m << std::endl;
+ });
+
+ Discard();
}
/*}}}*/
-// GlobalError::Discard - Discard /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void GlobalError::Discard()
-{
- while (List != 0)
- {
- Item *Old = List;
- List = List->Next;
- delete Old;
- }
-
- PendingFlag = false;
-};
+// GlobalError::Discard - Discard /*{{{*/
+void GlobalError::Discard() {
+ Messages.clear();
+ PendingFlag = false;
+}
/*}}}*/
-// GlobalError::Insert - Insert a new item at the end /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void GlobalError::Insert(Item *Itm)
-{
- Item **End = &List;
- for (Item *I = List; I != 0; I = I->Next)
- End = &I->Next;
- Itm->Next = *End;
- *End = Itm;
+// GlobalError::empty - does our error list include anything? /*{{{*/
+bool GlobalError::empty(MsgType const &threshold) const {
+ if (PendingFlag == true)
+ return false;
+
+ if (Messages.empty() == true)
+ return true;
+
+ return std::find_if(Messages.begin(), Messages.end(), [&threshold](Item const &m) {
+ return m.Type >= threshold;
+ }) == Messages.end();
+}
+ /*}}}*/
+// GlobalError::PushToStack /*{{{*/
+void GlobalError::PushToStack() {
+ Stacks.emplace_back(Messages, PendingFlag);
+ Discard();
+}
+ /*}}}*/
+// GlobalError::RevertToStack /*{{{*/
+void GlobalError::RevertToStack() {
+ Discard();
+ MsgStack pack = Stacks.back();
+ Messages = pack.Messages;
+ PendingFlag = pack.PendingFlag;
+ Stacks.pop_back();
+}
+ /*}}}*/
+// GlobalError::MergeWithStack /*{{{*/
+void GlobalError::MergeWithStack() {
+ MsgStack pack = Stacks.back();
+ Messages.splice(Messages.begin(), pack.Messages);
+ PendingFlag = PendingFlag || pack.PendingFlag;
+ Stacks.pop_back();
}
/*}}}*/