view anagram/support/log.cpp @ 7:57b2cc9b87f7

Use memcpy instead of strncpy when we know the length anyway. Modern gcc seems to think it knows how to detect misuse of strncpy, but it's wrong (in fact: very, very wrong) and the path of least resistance is to not try to fight with it.
author David A. Holland
date Mon, 30 May 2022 23:47:52 -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 */