diff --git a/src/utils/conftree.cpp b/src/utils/conftree.cpp new file mode 100755 index 00000000..5e842a93 --- /dev/null +++ b/src/utils/conftree.cpp @@ -0,0 +1,671 @@ +#ifndef lint +static char rcsid [] = "@(#$Id: conftree.cpp,v 1.1 2005-11-12 14:24:33 dockes Exp $ (C) 2003 J.F.Dockes"; +#endif +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef TEST_CONFTREE + +#include // for access(2) +#include + +#include +#include + +#include "conftree.h" + +#ifndef NO_NAMESPACES +using namespace std; +using std::list; +#endif // NO_NAMESPACES + +#ifndef MIN +#define MIN(A,B) ((A)<(B) ? (A) : (B)) +#endif + +static void trimstring(string &s, const char *ws = " \t") +{ + string::size_type pos = s.find_first_not_of(ws); + if (pos == string::npos) { + s = ""; + return; + } + s.replace(0, pos, ""); + + pos = s.find_last_not_of(ws); + if (pos != string::npos && pos != s.length()-1) + s.replace(pos+1, string::npos, ""); +} + +#define LL 1024 +void ConfSimple::parseinput(istream &input) +{ + string submapkey; + char cline[LL]; + bool appending = false; + string line; + + for (;;) { + input.getline(cline, LL-1); + //fprintf(stderr, "Line: '%s'\n", cline); + if (!input.good()) { + if (input.bad()) { + status = STATUS_ERROR; + //fprintf(stderr, "ConfSimple:parseinput: fatal error\n"); + return; + } + // Must be eof ? + break; + } + + int ll = strlen(cline); + while (ll > 0 && (cline[ll-1] == '\n' || cline[ll-1] == '\r')) { + cline[ll-1] = 0; + ll--; + } + + if (appending) + line += cline; + else + line = cline; + + // Note that we trim whitespace before checking for backslash-eol + // This avoids invisible problems + trimstring(line); + if (line.empty()) + continue; + if (line[line.length() - 1] == '\\') { + line.erase(line.length() - 1); + appending = true; + continue; + } + appending = false; + + if (line[0] == '[') { + trimstring(line, "[]"); + submapkey = line; + continue; + } + + // Look for first equal sign + string::size_type eqpos = line.find("="); + if (eqpos == string::npos) + continue; + + // Compute name and value, trim white space + string nm, val; + nm = line.substr(0, eqpos); + trimstring(nm); + val = line.substr(eqpos+1, string::npos); + trimstring(val); + + if (nm.length() == 0) + continue; + + map >::iterator s; + s = submaps.find(submapkey); + if (s != submaps.end()) { + // submap already exists + map &sm = s->second; + sm[nm] = val; + } else { + map newmap; + newmap[nm] = val; + submaps[submapkey] = newmap; + } + + } +} + +ConfSimple::ConfSimple(string *d, int readonly) +{ + filename = ""; + data = d; + status = readonly ? STATUS_RO : STATUS_RW; + + stringstream input(*d, ios::in); + parseinput(input); +} + +ConfSimple::ConfSimple(const char *fname, int readonly) +{ + filename = string(fname); + data = 0; + + ifstream input; + if (readonly) { + input.open(fname, ios::in); + status = STATUS_RO; + } else { + ios::openmode mode = ios::in|ios::out; + // It seems that there is no separate 'create if not exists' + // open flag. Have to truncate to create, but dont want to do + // this to an existing file ! + if (access(fname, 0) < 0) { + mode |= ios::trunc; + } + input.open(fname, mode); + if (input.is_open()) { + status = STATUS_RW; + } else { + input.clear(); + input.open(fname, ios::in); + if (input.is_open()) { + status = STATUS_RO; + } + } + } + + if (!input.is_open()) { + status = STATUS_ERROR; + return; + } + + // Parse + parseinput(input); +} + +ConfSimple::StatusCode ConfSimple::getStatus() +{ + switch (status) { + case STATUS_RO: return STATUS_RO; + case STATUS_RW: return STATUS_RW; + default: return STATUS_ERROR; + } +} + +int ConfSimple::get(const string &nm, string &value, const string &sk) +{ + if (status == STATUS_ERROR) + return 0; + + // Find submap + map >::iterator ss; + if ((ss = submaps.find(sk)) == submaps.end()) + return 0; + + // Find named value + map::iterator s; + if ((s = ss->second.find(nm)) == ss->second.end()) + return 0; + value = s->second; + return 1; +} + +const char *ConfSimple::get(const char *nm, const char *sk) +{ + if (status == STATUS_ERROR) + return 0; + if (sk == 0) + sk = ""; + // Find submap + map >::iterator ss; + if ((ss = submaps.find(sk)) == submaps.end()) + return 0; + + // Find named value + map::iterator s; + if ((s = ss->second.find(nm)) == ss->second.end()) + return 0; + return (s->second).c_str(); +} + +static ConfSimple::WalkerCode swalker(void *f, const char *nm, + const char *value) +{ + ostream *output = (ostream *)f; + if (!nm || !strcmp(nm, "")) + *output << "\n[" << value << "]\n"; + else + *output << nm << " = " << value << "\n"; + return ConfSimple::WALK_CONTINUE; +} + +int ConfSimple::set(const std::string &nm, const std::string &value, + const string &sk) +{ + if (status != STATUS_RW) + return 0; + + // Preprocess value: we don't want nl's in there, and we want to keep + // lines to a reasonable length + if (value.find_first_of("\n\r") != string::npos) { + return 0; + } + + string value1; + string::size_type pos = 0; + if (value.length() < 60) { + value1 = value; + } else { + while (pos < value.length()) { + string::size_type len = MIN(60, value.length() - pos); + value1 += value.substr(pos, len); + pos += len; + if (pos < value.length()) + value1 += "\\\n"; + } + } + + map >::iterator ss; + if ((ss = submaps.find(sk)) == submaps.end()) { + map submap; + submap[nm] = value1; + submaps[sk] = submap; + + } else { + ss->second[nm] = value1; + } + + if (filename.length()) { + ofstream output(filename.c_str(), ios::out|ios::trunc); + if (!output.is_open()) + return 0; + if (sortwalk(swalker, &output) != WALK_CONTINUE) { + return 0; + } + return 1; + } else if (data) { + ostringstream output(*data, ios::out | ios::trunc); + if (sortwalk(swalker, &output) != WALK_CONTINUE) { + return 0; + } + return 1; + } else { + // ?? + return 0; + } +} + +// Add parameter to file +int ConfSimple::set(const char *nm, const char *value, const char *sk) +{ + string ssk = (sk == 0) ? string("") : string(sk); + return set(string(nm), string(value), ssk); +} + +ConfSimple::WalkerCode +ConfSimple::sortwalk(WalkerCode (*walker)(void *,const char *,const char *), + void *clidata) +{ + // For all submaps: + for (map >::iterator sit = submaps.begin(); + sit != submaps.end(); sit++) { + + // Possibly emit submap name: + if (!sit->first.empty() && walker(clidata, "", sit->first.c_str()) + == WALK_STOP) + return WALK_STOP; + + // Walk submap + map &sm = sit->second; + for (map::iterator it = sm.begin();it != sm.end(); + it++) { + if (walker(clidata, it->first.c_str(), it->second.c_str()) + == WALK_STOP) + return WALK_STOP; + } + } + return WALK_CONTINUE; +} + +#include +void ConfSimple::list() +{ + sortwalk(swalker, &std::cout); +} + +static ConfSimple::WalkerCode lwalker(void *l, const char *nm, const char *) +{ + list *lst = (list *)l; + if (nm && *nm) + lst->push_back(nm); + return ConfSimple::WALK_CONTINUE; +} + +list ConfSimple::getKeys() +{ + std::list mylist; + sortwalk(lwalker, &mylist); + return mylist; +} + +static inline void path_catslash(std::string &s) { + if (s.empty() || s[s.length() - 1] != '/') + s += '/'; +} + +int ConfTree::get(const std::string &name, string &value, const string &sk) +{ + if (sk.empty() || sk[0] != '/') { + // fprintf(stderr, "Looking in global space"); + return ConfSimple::get(name, value, sk); + } + + // Get writable copy of subkey path + string msk = sk; + + // Handle the case where the config file path has an ending / and not + // the input sk + path_catslash(msk); + + // Look in subkey and up its parents until root ('') + for (;;) { + //fprintf(stderr,"Looking for '%s' in '%s'\n", + //name.c_str(), msk.c_str()); + if (ConfSimple::get(name, value, msk)) + return 1; + string::size_type pos = msk.rfind("/"); + if (pos != string::npos) { + msk.replace(pos, string::npos, ""); + } else + break; + } + return 0; +} + +bool ConfTree::stringToStrings(const string &s, std::list &tokens) +{ + string current; + tokens.clear(); + enum states {SPACE, TOKEN, INQUOTE, ESCAPE}; + states state = SPACE; + for (unsigned int i = 0; i < s.length(); i++) { + switch (s[i]) { + case '"': + switch(state) { + case SPACE: + state=INQUOTE; continue; + case TOKEN: + current += '"'; + continue; + case INQUOTE: + tokens.push_back(current); + current = ""; + state = SPACE; + continue; + case ESCAPE: + current += '"'; + state = INQUOTE; + continue; + } + break; + case '\\': + switch(state) { + case SPACE: + case TOKEN: + current += '\\'; + state=TOKEN; + continue; + case INQUOTE: + state = ESCAPE; + continue; + case ESCAPE: + current += '\\'; + state = INQUOTE; + continue; + } + break; + + case ' ': + case '\t': + switch(state) { + case SPACE: + continue; + case TOKEN: + tokens.push_back(current); + current = ""; + state = SPACE; + continue; + case INQUOTE: + case ESCAPE: + current += s[i]; + continue; + } + break; + + default: + switch(state) { + case ESCAPE: + state = INQUOTE; + break; + case SPACE: + state = TOKEN; + break; + case TOKEN: + case INQUOTE: + break; + } + current += s[i]; + } + } + switch(state) { + case SPACE: + break; + case TOKEN: + tokens.push_back(current); + break; + case INQUOTE: + case ESCAPE: + return false; + } + return true; +} + +bool ConfTree::stringToBool(const string &s) +{ + if (isdigit(s[0])) { + int val = atoi(s.c_str()); + return val ? true : false; + } + if (strchr("yYoOtT", s[0])) + return true; + return false; +} + + +#else // TEST_CONFTREE + +#include +#include + +#include +#include +#include + +#include "conftree.h" + +using namespace std; + +static char *thisprog; + +ConfSimple::WalkerCode mywalker(void *, const char *nm, const char *value) +{ + if (!nm || nm[0] == 0) + printf("\n[%s]\n", value); + else + printf("'%s' -> '%s'\n", nm, value); + return ConfSimple::WALK_CONTINUE; +} + +const char *longvalue = +"Donnees012345678901234567890123456789012345678901234567890123456789AA" +"0123456789012345678901234567890123456789012345678901234567890123456789FIN" + ; + +void stringtest() +{ + string s; + ConfSimple c(&s); + cout << "Initial:" << endl; + c.list(); + if (c.set("nom", "avec nl \n 2eme ligne", "")) { + fprintf(stderr, "set with embedded nl succeeded !\n"); + exit(1); + } + if (!c.set(string("parm1"), string("1"), string("subkey1"))) { + fprintf(stderr, "Set error"); + exit(1); + } + if (!c.set("sparm", "Parametre \"string\" bla", "s2")) { + fprintf(stderr, "Set error"); + exit(1); + } + if (!c.set("long", longvalue, "")) { + fprintf(stderr, "Set error"); + exit(1); + } + + cout << "Final:" << endl; + c.list(); +} + +static char usage [] = + "testconftree [opts] filename\n" + "[-w] : read/write test.\n" + "[-s] : string parsing test. Filename must hold parm 'strings'\n" + "[-q] nm sect : subsection test: look for nm in 'sect' which can be ''\n" + "[-S] : string io test. No filename in this case\n" + ; + +void Usage() { + fprintf(stderr, "%s:%s\n", thisprog, usage); + exit(1); +} +static int op_flags; +#define OPT_MOINS 0x1 +#define OPT_w 0x2 +#define OPT_q 0x4 +#define OPT_s 0x8 +#define OPT_S 0x10 + +int main(int argc, char **argv) +{ + const char *nm = 0; + const char *sub = 0; + + thisprog = argv[0]; + argc--; argv++; + + while (argc > 0 && **argv == '-') { + (*argv)++; + if (!(**argv)) + /* Cas du "adb - core" */ + Usage(); + while (**argv) + switch (*(*argv)++) { + case 'w': op_flags |= OPT_w; break; + case 's': op_flags |= OPT_s; break; + case 'S': op_flags |= OPT_S; break; + case 'q': + op_flags |= OPT_q; + if (argc < 3) + Usage(); + nm = *(++argv);argc--; + sub = *(++argv);argc--; + goto b1; + + default: Usage(); break; + } + b1: argc--; argv++; + } + + if ((op_flags & OPT_S)) { + if (argc != 0) + Usage(); + stringtest(); + } else { + if (argc < 1) + Usage(); + + const char *filename = *argv++;argc--; + + if (op_flags & OPT_w) { + ConfSimple parms(filename); + if (parms.getStatus() != ConfSimple::STATUS_ERROR) { + // It's ok for the file to not exist here + + const char *cp = parms.get("mypid"); + if (cp) { + printf("Value for mypid is '%s'\n", cp); + } else { + printf("mypid not set\n"); + } + cp = parms.get("unstring"); + if (cp) { + printf("Value for unstring is '%s'\n", cp); + } else { + printf("unstring not set\n"); + } + string myval; + if (parms.get(string("unstring"), myval, "")) { + printf("std::string value for 'unstring' is '%s'\n", + myval.c_str()); + } else { + printf("unstring not set (std::string)\n"); + } + } + char spid[100]; + sprintf(spid, "%d", getpid()); + parms.set("mypid", spid); + + ostringstream ost;; + ost << "mypid" << getpid(); + parms.set(ost.str(), spid, ""); + + parms.set("unstring", "Une jolie phrase pour essayer"); + } else if (op_flags & OPT_q) { + ConfTree parms(filename, 0); + if (parms.getStatus() == ConfSimple::STATUS_ERROR) { + fprintf(stderr, "Error opening or parsing file\n"); + exit(1); + } + string value; + if (!parms.get(nm, value, sub)) { + fprintf(stderr, "name '%s' not found in '%s'\n", nm, sub); + exit(1); + } + printf("%s : '%s' = '%s'\n", sub, nm, value.c_str()); + exit(0); + } else if (op_flags & OPT_s) { + ConfSimple parms(filename, 1); + if (parms.getStatus() == ConfSimple::STATUS_ERROR) { + cerr << "Cant open /parse conf file " << filename << endl; + exit(1); + } + + string source; + if (!parms.get(string("strings"), source, "")) { + cerr << "Cant get param 'strings'" << endl; + exit(1); + } + cout << "source: [" << source << "]" << endl; + list strings; + if (!ConfTree::stringToStrings(source, strings)) { + cerr << "parse failed" << endl; + exit(1); + } + + for (list::iterator it = strings.begin(); + it != strings.end(); it++) { + cout << "[" << *it << "]" << endl; + } + + } else { + ConfSimple parms(filename, 1); + if (parms.getStatus() == ConfSimple::STATUS_ERROR) { + fprintf(stderr, "Open failed\n"); + exit(1); + } + printf("LIST\n");parms.list(); + printf("KEYS\n"); + list keys = parms.getKeys(); + for (list::iterator it = keys.begin();it!=keys.end();it++) + printf("%s\n", (*it).c_str()); + //printf("WALK\n");parms.sortwalk(mywalker, 0); + } + } +} + +#endif diff --git a/src/utils/conftree.h b/src/utils/conftree.h new file mode 100755 index 00000000..49d403ba --- /dev/null +++ b/src/utils/conftree.h @@ -0,0 +1,151 @@ +#ifndef _CONFTREE_H_ +#define _CONFTREE_H_ +/** + * A simple configuration file implementation. + * + * Configuration files have lines like 'name = value', and/or like '[subkey]' + * + * Lines like '[subkeyname]' in the file define subsections, with independant + * configuration namespaces. + * + * Whitespace around name and value is insignificant. + * + * Values can be queried for, or set. (the file is then rewritten). + * The names are case-sensitive but don't count on it either. + * Any line without a '=' is discarded when rewriting the file. + * All 'set' calls currently cause an immediate file rewrite. + */ +#include +#include +#include +// rh7.3 likes iostream better... +#if defined(__GNUC__) && __GNUC__ < 3 +#include +#else +#include +#endif + +#ifndef NO_NAMESPACES +using std::string; +using std::list; +using std::map; +#endif // NO_NAMESPACES + +/** + * Manages a simple configuration file with subsections. + */ +class ConfSimple { + public: + enum StatusCode {STATUS_ERROR=0, STATUS_RO=1, STATUS_RW=2}; + private: + string filename; // set if we're working with a file + string *data; // set if we're working with an in-memory string + map > submaps; + StatusCode status; + void parseinput(std::istream &input); + + public: + /** + * Build the object by reading content from file. + */ + ConfSimple(const char *fname, int readonly = 0); + + /** + * Build the object by reading content from a string + */ + ConfSimple(string *data, int readonly = 0); + + virtual ~ConfSimple() {}; + + /** + * Get value for named parameter, from specified subsection (looks in + * global space if sk is empty). + * @return 0 if name not found, 1 else + */ + virtual int get(const std::string &name, string &value, const string &sk); + virtual int get(const std::string &name, string &value) { + return get(name, value, string("")); + } + /** + * See comments for std::string variant + * @return 0 if name not found, const C string else + */ + virtual const char *get(const char *name, const char *sk = 0); + + /** + * Set value for named parameter in specified subsection (or global) + * @return 0 for error, 1 else + */ + int set(const std::string &nm, const std::string &val, const string &sk); + int set(const char *name, const char *value, const char *sk = 0); + + virtual StatusCode getStatus(); + + /** + * Walk the configuration values, calling function for each. + * The function is called with a null nm when changing subsections (the + * value is then the new subsection name) + * @return WALK_STOP when/if the callback returns WALK_STOP, + * WALK_CONTINUE else (got to end of config) + */ + enum WalkerCode {WALK_STOP, WALK_CONTINUE}; + virtual WalkerCode sortwalk(WalkerCode + (*wlkr)(void *cldata, const char *nm, + const char *val), + void *clidata); + void list(); + /** + * Return all key names: + */ + std::list getKeys(); +}; + +/** + * This is a configuration class which attaches tree-like signification to the + * submap names. + * + * If a given variable is not found in the specified section, it will be + * looked up the tree of section names, and in the global space. + * + * submap names should be '/' separated paths (ie: /sub1/sub2). No checking + * is done, but else the class adds no functionality to ConfSimple. + * + * NOTE: contrary to common behaviour, the global or root space is NOT + * designated by '/' but by '' (empty subkey). A '/' subkey will not + * be searched at all. + */ +class ConfTree : public ConfSimple { + + /* Dont want this to be accessible: keep only the string-based one */ + virtual const char *get(const char *, const char *) {return 0;} + + public: + /** + * Build the object by reading content from file. + */ + ConfTree(const char *fname, int readonly = 0) + : ConfSimple(fname, readonly) {} + virtual ~ConfTree() {}; + + /** + * Get value for named parameter, from specified subsection, or its + * parents. + * @return 0 if name not found, 1 else + */ + virtual int get(const std::string &name, string &value, const string &sk); + + virtual int get(const char *name, string &value, const char *sk) { + return get(string(name), value, sk ? string(sk) : string("")); + } + /** + * Parse input stream into vector of strings. + * + * Token delimiter is " \t" except inside dquotes. dquote inside + * dquotes can be escaped with \ etc... + */ + static bool stringToStrings(const string &s, std::list &tokens); + static bool stringToBool(const string &s); +}; + + +#endif /*_CONFTREE_H_ */ diff --git a/src/utils/debuglog.cpp b/src/utils/debuglog.cpp new file mode 100755 index 00000000..0a71b1f6 --- /dev/null +++ b/src/utils/debuglog.cpp @@ -0,0 +1,393 @@ +#ifndef lint +static char rcsid [] = "@(#$Id: debuglog.cpp,v 1.1 2005-11-12 14:24:33 dockes Exp $ (C) 2002 OKYZ"; +#endif +#ifndef TEST_DEBUGLOG + +#define __USE_GNU +#include +#include +#include +#include + +#ifdef INCLUDE_NEW_H +#include +#endif + +#include + +#include "debuglog.h" + +#ifndef freeZ +#define freeZ(X) {if (X) {free(X);X=0;}} +#endif + +#ifndef NO_NAMESPACES +using namespace std; +namespace DebugLog { + +#endif // NO_NAMESPACES + +class DebugLogWriter { + public: + virtual ~DebugLogWriter() {} + virtual int put(const char *s) = 0; +}; + +class DLFWImpl; +class DebugLogFileWriter : public DebugLogWriter { + DLFWImpl *impl; + public: + DebugLogFileWriter(); + ~DebugLogFileWriter(); + virtual const char *getfilename(); + virtual int setfilename(const char *fname, int trnc = 1); + virtual int put(const char *s); +}; + +class DLFWImpl { + char *filename; + FILE *fp; + int truncate; + public: + // Open output file if needed, return 0 if ok + void maybeopenfp() { + if (fp) + return; + if (filename == 0) + return; + if (!strcmp(filename, "stdout")) { + fp = stdout; + } else if (!strcmp(filename, "stderr")) { + fp = stderr; + } else { + fp = fopen(filename, (truncate) ? "w" : "a"); + if (fp) + setvbuf(fp, 0, _IOLBF, 0); + } + return; + } + + void maybeclosefp() { +#ifdef DEBUGDEBUG + fprintf(stderr, "DebugLogImpl::maybeclosefp: filename %p, fp %p\n", + filename, fp); +#endif + // Close current file if open, and not stdout/stderr + if (fp && (filename == 0 || + (strcmp(filename, "stdout") && + strcmp(filename, "stderr")))) { + fclose(fp); + } + fp = 0; + freeZ(filename); + } + + public: + + DLFWImpl() : filename(0), fp(0), truncate(1) { + setfilename("stderr", 0); + } + ~DLFWImpl() { + maybeclosefp(); + } + int setfilename(const char *fn, int trnc) { + maybeclosefp(); + filename = strdup(fn); + truncate = trnc; + return 0; + } + const char *getfilename() { + return filename; + } + int put(const char *s) { + maybeopenfp(); + if (fp) + return fputs(s, fp); + return -1; + } +}; + +DebugLogFileWriter::DebugLogFileWriter() +{ + impl = new DLFWImpl; +} + +DebugLogFileWriter::~DebugLogFileWriter() +{ + delete impl; +} + +int DebugLogFileWriter::setfilename(const char *fn, int trnc) { + return impl ? impl->setfilename(fn, trnc) : -1; +} + +const char *DebugLogFileWriter::getfilename() +{ + return impl ? impl->getfilename() : 0; +} + +int DebugLogFileWriter::put(const char *s) +{ + return impl ? impl->put(s) : -1; +}; + + + +#ifdef _WINDOWS +#include +static void datestring(char *d) { + SYSTEMTIME buf; + GetLocalTime(&buf); + int year = buf.wYear % 100; + + sprintf(d, "%02d%02d%02d%02d%02d%02d", year, int(buf.wMonth), + int(buf.wDay), int(buf.wHour), int(buf.wMinute), int(buf.wSecond)); +} +#define vsnprintf _vsnprintf + +#else // !WINDOWS -> + +#include +static void datestring(char *d) +{ + struct tm *tmp; + time_t tim = time((time_t)0); + tmp = localtime(&tim); + int year = tmp->tm_year % 100; + sprintf(d, "%02d%02d%02d%02d%02d%02d", year, tmp->tm_mon+1, + tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +} + +#endif // !WINDOWS + +void +DebugLog::prolog(int lev, const char *f, int line) +{ + if (!writer) + return; + if (dodate) { + char dts[100]; + datestring(dts); + writer->put(dts); + } + char buf[100]; + sprintf(buf, ":%d:", lev); + writer->put(buf); +#if DEBUGLOG_USE_THREADS + sprintf(buf, "%lx:", (unsigned long)pthread_self()); + writer->put(buf); +#endif + writer->put(f); + sprintf(buf, ":%d:", line); + writer->put(buf); +} + +void +DebugLog::log(const char *s ...) +{ + if (!writer) + return; + va_list ap; + va_start(ap,s); + +#ifdef HAVE_VASPRINTF_nono // not sure vasprintf is really such a great idea + char *buf; + vasprintf(&buf, s, ap); + if (buf) { +#else + char buf[4096]; + // It's possible that they also wouldn't have vsnprintf but what then ? + vsnprintf(buf, 4096, s, ap); + { +#endif + writer->put(buf); + } + +#ifdef HAVE_VASPRINTF_nono + if (buf) + free(buf); +#endif +} + +void +DebugLog::setloglevel(int lev) +{ + debuglevel = lev; + while (!levels.empty()) + levels.pop(); + pushlevel(lev); +} + +void DebugLog::pushlevel(int lev) +{ + debuglevel = lev; + levels.push(lev); +} + +void DebugLog::poplevel() +{ + if (levels.empty()) + debuglevel = 0; + if (levels.size() > 1) + levels.pop(); + debuglevel = levels.top(); +} + + +//////////////////////////////////////////////////////////// +// Global functions +////////////////////////////////////// +static DebugLogFileWriter lwriter; +static DebugLogFileWriter *theWriter = &lwriter; +const char *getfilename() +{ + return theWriter ? theWriter->getfilename() : 0; +} +int setfilename(const char *fname, int trnc) +{ + return theWriter ? theWriter->setfilename(fname, trnc) : -1; +} + +#if DEBUGLOG_USE_THREADS +#include +static pthread_key_t dbl_key; +static pthread_once_t key_once = PTHREAD_ONCE_INIT; + +static void thrdatadel(void *data) +{ + // fprintf(stderr, "DebugLog:: thrdatadel: %p\n", data); + DebugLog *dbl = (DebugLog *)data; + delete dbl; + pthread_setspecific(dbl_key, 0); +} +static void once_routine(void) +{ + int status; + status = pthread_key_create(&dbl_key, thrdatadel); + if (status != 0) { + fprintf(stderr, "debuglog: cant initialize pthread " + "thread private storage key\n"); + abort(); + } +} + +DebugLog *getdbl() +{ + int status = pthread_once(&key_once, once_routine); + if (status != 0) { + fprintf(stderr, "debuglog: cant initialize pthread " + "thread private storage key (pthread_once)\n"); + abort(); + } + DebugLog *dbl; + if (!(dbl = (DebugLog *)pthread_getspecific(dbl_key))) { + dbl = new DebugLog; + dbl->setwriter(theWriter); + status = pthread_setspecific(dbl_key, dbl); + if (status) { + fprintf(stderr, "debuglog: cant initialize pthread " + "thread private storage key (pthread_setspecific)\n"); + abort(); + } + } + return dbl; +} + +#else // No threads -> + +static DebugLog *dbl; +DebugLog *getdbl() +{ + if (!dbl) { + dbl = new DebugLog; + dbl->setwriter(theWriter); + } + return dbl; +} +#endif + +#ifndef NO_NAMESPACES +} +#endif // NO_NAMESPACES + +////////////////////////////////////////// TEST DRIVER ////////////////// +#else /* TEST_DEBUGLOG */ + +#include +#include "debuglog.h" + +#if DEBUGLOG_USE_THREADS +#define TEST_THREADS +#endif + +#ifdef TEST_THREADS +#include +#endif + +const int iloop = 5; +void *thread_test(void *data) +{ + const char *s = (const char *)data; + int lev = atoi(s); + DebugLog::getdbl()->setloglevel(DEBDEB); + for (int i = 1; i < iloop;i++) { + switch (lev) { + case 1: LOGFATAL(("Thread: %s count: %d\n", s, i));break; + case 2: LOGERR(("Thread: %s count: %d\n", s, i));break; + default: + case 3: LOGINFO(("Thread: %s count: %d\n", s, i));break; + } + sleep(1); + } + return 0; +} + + +int +main(int argc, char **argv) +{ +#ifdef TEST_THREADS + pthread_t t1, t2, t3; + + char name1[20]; + strcpy(name1, "1"); + pthread_create(&t1, 0, thread_test, name1); + + char name2[20]; + strcpy(name2, "2"); + pthread_create(&t2, 0, thread_test, name2); + + char name3[20]; + strcpy(name3, "3"); + pthread_create(&t3, 0, thread_test, name3); + + DebugLog::getdbl()->setloglevel(DEBDEB); + for (int i = 1; i < iloop;i++) { + LOGINFO(("LOGGING FROM MAIN\n")); + sleep(1); + } + sleep(2); + exit(0); +#else + LOGFATAL(("FATAL\n","Val")); + DebugLog::getdbl()->logdate(1); + LOGERR(("ERR\n","Val")); + LOGINFO(("INFO\n","Val")); + LOGDEB0(("DEBUG %s\n","valeur")); + + int lev; + printf("Testing push. Initial level: %d\n", DebugLog::getdbl()->getlevel()); + for (lev = 0; lev < 4;lev++) { + DebugLog::getdbl()->pushlevel(lev); + printf("Lev now %d\n", DebugLog::getdbl()->getlevel()); + } + printf("Testing pop\n"); + for (lev = 0; lev < 7;lev++) { + DebugLog::getdbl()->poplevel(); + printf("Lev now %d\n", DebugLog::getdbl()->getlevel()); + } +#endif +} + + +#endif /* TEST_DEBUGLOG */ diff --git a/src/utils/debuglog.h b/src/utils/debuglog.h new file mode 100755 index 00000000..0eac11be --- /dev/null +++ b/src/utils/debuglog.h @@ -0,0 +1,101 @@ +#ifndef _DEBUGLOG_H_ +#define _DEBUGLOG_H_ +/* Macros for log and debug messages */ +#include + +#ifndef NO_NAMESPACES +namespace DebugLog { + using std::stack; +#endif // NO_NAMESPACES + +#ifndef DEBUGLOG_USE_THREADS +#define DEBUGLOG_USE_THREADS 1 +#endif + +#define DEBFATAL 1 +#define DEBERR 2 +#define DEBINFO 3 +#define DEBDEB 4 +#define DEBDEB0 5 +#define DEBDEB1 6 +#define DEBDEB2 7 +#define DEBDEB3 8 + +#ifndef STATICVERBOSITY +#define STATICVERBOSITY DEBDEB0 +#endif + +class DebugLogWriter; + +class DebugLog { + std::stack levels; + int debuglevel; + int dodate; + DebugLogWriter *writer; + public: + DebugLog() : debuglevel(-1), dodate(0), writer(0) {} + DebugLog(DebugLogWriter *w) : debuglevel(-1), dodate(0), writer(w) {} + virtual ~DebugLog() {} + virtual void setwriter(DebugLogWriter *w) {writer = w;} + virtual DebugLogWriter *getwriter() {return writer;} + virtual void prolog(int lev, const char *srcfname, int line); + virtual void log(const char *s ...); + virtual void setloglevel(int lev); + inline int getlevel() {return debuglevel;} + virtual void pushlevel(int lev); + virtual void poplevel(); + virtual void logdate(int onoff) {dodate = onoff;} +}; + +extern DebugLog *getdbl(); +extern const char *getfilename(); +extern int setfilename(const char *fname, int trnc = 1); +#if STATICVERBOSITY >= DEBFATAL +#define LOGFATAL(X) {if (DebugLog::getdbl()->getlevel()>=DEBFATAL){DebugLog::getdbl()->prolog(DEBFATAL,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGFATAL(X) +#endif +#if STATICVERBOSITY >= DEBERR +#define LOGERR(X) {if (DebugLog::getdbl()->getlevel()>=DEBERR){DebugLog::getdbl()->prolog(DEBERR,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGERR(X) +#endif +#if STATICVERBOSITY >= DEBINFO +#define LOGINFO(X) {if (DebugLog::getdbl()->getlevel()>=DEBINFO){DebugLog::getdbl()->prolog(DEBINFO,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGINFO(X) +#endif +#if STATICVERBOSITY >= DEBDEB +#define LOGDEB(X) {if (DebugLog::getdbl()->getlevel()>=DEBDEB){DebugLog::getdbl()->prolog(DEBDEB,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGDEB(X) +#endif +#if STATICVERBOSITY >= DEBDEB0 +#define LOGDEB0(X) {if (DebugLog::getdbl()->getlevel()>=DEBDEB0){DebugLog::getdbl()->prolog(DEBDEB0,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGDEB0(X) +#endif +#if STATICVERBOSITY >= DEBDEB1 +#define LOGDEB1(X) {if (DebugLog::getdbl()->getlevel()>=DEBDEB1){DebugLog::getdbl()->prolog(DEBDEB1,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGDEB1(X) +#endif +#if STATICVERBOSITY >= DEBDEB2 +#define LOGDEB2(X) {if (DebugLog::getdbl()->getlevel()>=DEBDEB2){DebugLog::getdbl()->prolog(DEBDEB2,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGDEB2(X) +#endif +#if STATICVERBOSITY >= DEBDEB3 +#define LOGDEB3(X) {if (DebugLog::getdbl()->getlevel()>=DEBDEB3){DebugLog::getdbl()->prolog(DEBDEB3,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGDEB3(X) +#endif +#if STATICVERBOSITY >= DEBDEB4 +#define LOGDEB4(X) {if (DebugLog::getdbl()->getlevel()>=DEBDEB4){DebugLog::getdbl()->prolog(DEBDEB4,__FILE__,__LINE__) ;DebugLog::getdbl()->log X;}} +#else +#define LOGDEB4(X) +#endif +#ifndef NO_NAMESPACES +} +#endif // NO_NAMESPACES +#endif /* _DEBUGLOG_H_ */