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 */
+
+