Mercurial > ~dholland > hg > ag > index.cgi
diff anagram/support/log.cpp @ 0:13d2b8934445
Import AnaGram (near-)release tree into Mercurial.
author | David A. Holland |
---|---|
date | Sat, 22 Dec 2007 17:52:45 -0500 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anagram/support/log.cpp Sat Dec 22 17:52:45 2007 -0500 @@ -0,0 +1,333 @@ +/* + * AnaGram, A System for Syntax Directed Programming + * Copyright 1993-2002 Parsifal Software. All Rights Reserved. + * Copyright 2006 David A. Holland. All Rights Reserved. + * See the file COPYING for license and usage terms. + * + * log.cpp - logging module. + * + * To use the logging stuff, turn on INCLUDE_LOGGING here and in + * the source files you want to log from. Then create a file called + * "aglog.ctl" contaning lines of the form + * +foo + * -foo + * to turn logging on or off (respectively) for LOGSECTION names + * matching "foo". With Unix builds, "foo" can be a shell glob (like + * "check_*") but owing to the lack of library support this is not + * supported in Windows. The control settings are applied in order. + * + * The log entries land in the file "ag.log", which is truncated with + * every new execution. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#ifdef AG_ON_UNIX +#include <fnmatch.h> +#endif + +#ifdef VACLGUI +//#include <icritsec.hpp> +//#include "resource.h" +#endif + +#include "port.h" + +#include "agstring.h" +#include "agstack.h" +#include "textfile.h" + +//#define INCLUDE_LOGGING +#include "log.h" + +#define PATH_LOGFILE "ag.log" +#define PATH_CTLFILE "aglog.ctl" +#define INDENT 3 + +#ifdef INCLUDE_LOGGING + +static int initialized = 0; +static FILE *logfile = NULL; +static LogSwitch masterswitch = LOG_OFF; +static int indent = 0; +static LogSection *sectionstack = NULL; + +// This must be a pointer, not a global object, in case someone wants to +// log from a global constructor somewhere. +static AgStack<AgString> *matches; + + +//////////////////////////////////////////////////////////// + +static int readcontrol(void) { + LOGSECTION_ON("readcontrol"); + + text_file control(PATH_CTLFILE); + if ((char *)control == 0) { + //printf("Log: failed to open control file %s\n", PATH_CTLFILE); + return -1; + } + //printf("Log: control file read\n"); + + unsigned num_control_lines = control.size().y; + + //printf("Log: trying to log something\n"); + LOGV(num_control_lines); + + //printf("Log: processing control file\n"); + for (unsigned i = 0; i < num_control_lines; i++) { + char *line = control.line(i); + while (*line==' ') { + line++; + } + + char *p = strchr(line, '\n'); + if (p) { + *p = 0; + if (p != line && p[-1]=='\r') { + *(--p) = 0; + } + } + else { + p = line + strlen(line); + } + while (p != line && p[-1]==' ') { + *(--p) = 0; + } + + if (*line==0 || *line=='#') { + continue; + } + + LOGV(line); + + if ((*line!='+' && *line!='-') || strlen(line)==1) { + LOGS("...line is invalid"); + continue; + } + + matches->push(AgString(line)); + } + + return 0; +} + +static LogSwitch checkcontrol(const char *name) { + unsigned i, n = matches->size(); + for (i=0; i<n; i++) { + const char *ctl = (*matches)[i].pointer(); + assert(ctl[0] == '+' || ctl[0] == '-'); + + int matched; +#ifdef AG_ON_UNIX + matched = !fnmatch(ctl+1, name, 0); +#else + if (!strcmp(ctl+1, "*")) { + matched = 1; + } + else { + matched = !strcmp(ctl+1, name); + } +#endif + + if (matched) { + return ctl[0] == '+' ? LOG_ON : LOG_OFF; + } + } + + return LOG_MAYBE; +} + +//////////////////////////////////////////////////////////// + +static int loginit(void) { + if (initialized) { + return 0; + } + + //printf("Log: initializing\n"); + logfile = fopen(PATH_LOGFILE, "w"); + if (!logfile) { + //printf("Log: failed to open output file %s\n", PATH_LOGFILE); + return -1; + } + //printf("Log: opened output file\n"); + fprintf(logfile, "*** AnaGram log file ***\n"); + + masterswitch = LOG_ON; + initialized = 1; + matches = new AgStack<AgString>; + + if (readcontrol()) { + fprintf(logfile, "\nReading control file %s failed\n", PATH_CTLFILE); + fclose(logfile); + logfile = NULL; + initialized = 0; + delete matches; + matches = NULL; + masterswitch = LOG_OFF; + return -1; + } + + masterswitch = LOG_OFF; + + //printf("Log: done initializing\n"); + return 0; +} + +#if 0 /* no way to reach this */ +static void logclose(void) { + if (initialized) { + + assert(logfile != NULL); + fclose(logfile); + logfile = NULL; + + delete matches; + matches = NULL; + + masterswitch = LOG_OFF; + initialized = 0; + } +} +#endif /* 0 */ + +LogSection::LogSection(const char *name_, LogSwitch caller_requested) { + if (loginit()) return; + + previous = sectionstack; + sectionstack = this; + name = name_; + saved_switch = masterswitch; + + // get setting instructed via the control file + masterswitch = LOG_ON; + LogSwitch ctl_setting = checkcontrol(name); + masterswitch = LOG_OFF; + + // the control file takes precedence, unless it says "maybe" + LogSwitch new_setting; + if (ctl_setting != LOG_MAYBE) { + new_setting = ctl_setting; + } + else { + new_setting = caller_requested; + } + + if (new_setting == LOG_ON) { + masterswitch = LOG_ON; + logeol(), dolog("BEGIN "), dolog(name), dolog(" "), logtime(); + indent += INDENT; + } +} + +LogSection::~LogSection() { + if (!initialized) return; + + if (masterswitch == LOG_ON) { + indent -= INDENT; + logeol(), dolog("END "), dolog(name), dolog(" "), logtime(); + } + masterswitch = saved_switch; + sectionstack = previous; +} + +void LogSection::logstack(void) { + LOGSECTION("logsectionstack"); + LogSection *s = sectionstack; + while (s) { + logeol(), dolog(s->name); + s = s->previous; + } +} + +void logeol(void) { + if (loginit() || masterswitch == LOG_OFF) return; + + assert(logfile != NULL); + fprintf(logfile, "\n%*s", indent, ""); + fflush(logfile); +} + +static void logf(const char *fmt, ...) { + if (loginit() || masterswitch == LOG_OFF) return; + + assert(logfile != NULL); + va_list ap; + va_start(ap, fmt); + vfprintf(logfile, fmt, ap); + va_end(ap); + fflush(logfile); +} + +void dolog(const char *s) { + logf("%s", s); +} + +void dolog(const AgString &s) { + logf("%s", s.pointer()); +} + +void dolog(void *p) { + logf("%p", p); +} + +void dolog(int n) { + logf("%d", n); +} + +void dolog(cint p) { + logf("(%d, %d)", p.x, p.y); +} + +void logtime(void) { + char buf[64]; + time_t timer = time(NULL); + struct tm *t = gmtime(&timer); + strftime(buf, sizeof(buf), "%Y%m%d %T", t); + logf("%s", buf); +} + +#ifdef VACLGUI + +void dolog(const IString &s) { + logf("%s", (const char *) s); +} + +void dolog(const IPair &p) { + logf("%s", p.asString()); +} + +void dolog(const IRectangle &r) { + logf("%s", r.asString()); +} + +void logexception(IException &ie) { + if (loginit() || masterswitch == LOG_OFF) return; + + LOGSECTION_ON("Exception"); + logeol(), dolog(ie.name()), dolog(ie.text()); + int n = ie.locationCount(); + for (int i = 0; i < n; i++) { + const IExceptionLocation *loc = ie.locationAtIndex(i); + logeol(), dolog("File: "), dolog(loc->fileName()); + logeol(), dolog("Line: "), dolog(loc->lineNumber()); + logeol(), dolog("Function: "), dolog(loc->functionName()); + } +} + +#endif /* VACLGUI */ + +#else /* not INCLUDE_LOGGING */ + +#ifdef __IBMCPP__ +/* without this, the librarian bombs */ +void ibmcpp_is_stupid_in_log_cpp(void) {} +#endif + +#endif /* INCLUDE_LOGGING */ + +