view anagram/support/log.cpp @ 21:1c9dac05d040

Add lint-style FALLTHROUGH annotations to fallthrough cases. (in the parse engine and thus the output code) Document this, because the old output causes warnings with gcc10.
author David A. Holland
date Mon, 13 Jun 2022 00:04:38 -0400
parents 13d2b8934445
children
line wrap: on
line source

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