/* -*- Mode: C -*-
 *
 * $Header: /home/gene/library/website/docsrc/wel/src/RCS/tab.cpp,v 395.1 2008/04/20 17:25:48 gene Exp $
 *
 * Copyright (c) 2006 Gene Michael Stover.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

#include "this.h"

using std::cout;

/*
 */
TCHAR TAB_tab = '\t';
TCHAR TAB_endofrecord = '\n';
TCHAR TAB_escape = '\\';

/*
 * Print a string for tabular format.  Escape character if
 * necessary.
 */
static MyString
S_Encode (MyString const &src)
{
  std::basic_ostringstream<TCHAR> strm;
  MyString::const_iterator i;

  for (i = src.begin (); i != src.end (); ++i) {
    if (*i == TAB_tab || *i == TAB_endofrecord || *i == TAB_escape) {
      strm << TAB_escape;
    }
    strm << *i;
  }
  return strm.str ();
}

/*
 */
static MyString
S_EventTypeToString (WORD w)
{
  TCHAR *p, str[50];

  switch (w) {
  case EVENTLOG_ERROR_TYPE: p = TEXT ("EVENTLOG_ERROR_TYPE"); break;
  case EVENTLOG_AUDIT_FAILURE: p = TEXT ("EVENTLOG_AUDIT_FAILURE"); break;
  case EVENTLOG_AUDIT_SUCCESS: p = TEXT ("EVENTLOG_AUDIT_SUCCESS"); break;
  case EVENTLOG_INFORMATION_TYPE:
    p = TEXT ("EVENTLOG_INFORMATION_TYPE");
    break;
  case EVENTLOG_WARNING_TYPE: p = TEXT ("EVENTLOG_WARNING_TYPE"); break;
  default:
    StringCbPrintf (str, sizeof str, TEXT ("%u"), (unsigned) w);
    p = str;
  }
  return p;
}

static char S_id[] = "$Id: tab.cpp,v 395.1 2008/04/20 17:25:48 gene Exp $ Copyright (c) 2006 Gene Michael Stover.  All rights reserved.";

/*
 * Return a newly allocated argv[] of the EVENTLOGRECORD's
 * message strings.
 */
static TCHAR **
S_GetArgv (EVENTLOGRECORD *x)
{
  TCHAR **argv = NULL;
  char *p;
  DWORD i;
  int sz;

  sz = LPTR, x->NumStrings * sizeof *argv;
  argv = (TCHAR **) LocalAlloc (LPTR, sz);
  if (argv != NULL) {
    /*
     * Initialize argv.  Each element in argv[] will point to one of
     * the argument strings.
     */
    p = x->StringOffset + (char *) x;
    for (i = 0; i < x->NumStrings; ++i) {
      argv[i] = (TCHAR *) p;
      while (*p != '\0') ++p;
      while (*p == '\0') ++p;           /* Skip the trailing zeros */
    }
  } else {
    fprintf (stderr, "\n%s:%d: LocalAlloc (LPTR, %d) failed", __FILE__,
             __LINE__, x->NumStrings * sizeof *argv);
  }
  return argv;
}

/*
 */
static MyString
S_GetConcatenatedMessageString (EVENTLOGRECORD *x)
{
  std::basic_ostringstream<TCHAR> strm;
  TCHAR *p;
  DWORD i;

  assert (x != NULL);
  strm << TEXT ("(concat) ");
  p = x->StringOffset + (char *) x;
  for (i = 0; i < x->NumStrings; ++i) {
    if (i > 0) strm << TCHAR (' ');
    while (*p != TCHAR ('\0')) {
      strm << *p;
      ++p;
    }
    ++p; /* skip the terminator */
  }
  return strm.str ();
}

/*
 * Return a pointer to a new, formatted message string.
 * Release the message string with "LocalFree" when you are
 * done with it.
 */
static MyString
S_GetFormattedMessageString (EVENTLOGRECORD *x)
{
  TCHAR *msg = NULL;
  TCHAR **argv;
  static DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | 
    FORMAT_MESSAGE_FROM_HMODULE;
  DWORD dw;

  assert (x != NULL);
  argv = S_GetArgv (x);
  if (argv != NULL) {
    dw = FormatMessage(flags,            /* DWORD dwFlags */
                       NULL,             /* LPCVOID lpSource */
                       x->EventID,       /* DWORD dwMessageId, */
                       0,                /* DWORD dwLanguageId */
                       (LPTSTR) &msg,    /* LPTSTR lpBuffer */
                       1,                /* DWORD nSize */
                       argv);            /* va_list* Arguments */
    if (dw > 0 && msg != NULL) {
      /* good */
    } else {
#if 0
      fprintf (stderr, "\n%s:%d:", __FILE__, __LINE__);
      fprintf (stderr, " FormatMessage: %u", (unsigned) GetLastError ());
#endif
    }
    LocalFree (argv);
  } else {
    fprintf (stderr, "\n%s:%d: S_GetArgv failed", __FILE__, __LINE__);
  }
  return msg == NULL ? TEXT ("") : msg;
}

/*
 * Return a pointer to a new, formatted message string.
 * Release the message string with "LocalFree" when you are
 * done with it.
 */
static MyString
S_MessageString (EVENTLOGRECORD *x)
{
  MyString msg;

  msg = S_GetFormattedMessageString (x);
  if (msg.empty ()) {
    msg = S_GetConcatenatedMessageString (x);
  }
  return msg;
}

/*
 */
static MyString
S_TimeToString (DWORD dw)
{
  time_t tt;
  struct tm stm;
  char str[100];

  tt = (time_t) dw;
  if (localtime (&tt) != NULL) {
    memcpy (&stm, localtime (&tt), sizeof stm);
    strftime (str, sizeof str, "%Y-%b-%dT%H:%M:%S", &stm);
  } else {
    str[0] = '\0';
  }
  return str;
}

/*
 * Print an Event Log Record in tabular form.
 */
extern "C" void
TAB_Print (EVENTLOGRECORD *x)
{
  static Boolean isFirstTime = TRUE;

  if (isFirstTime) {
    isFirstTime = FALSE;
    cout << TAB_endofrecord;
    cout << "; ";
    cout << "RecordNumber" << TAB_tab
         << "TimeGenerated" << TAB_tab
         << "TimeWritten" << TAB_tab
         << "EventID" << TAB_tab
         << "EventType" << TAB_tab
         << "EventCategory" << TAB_tab
         << "ClosingRecordNumber" << TAB_endofrecord;
  }
  cout << x->RecordNumber << TAB_tab
       << S_Encode (S_TimeToString (x->TimeGenerated)) << TAB_tab
       << S_Encode (S_TimeToString (x->TimeWritten)) << TAB_tab
       << x->EventID << TAB_tab
       << S_EventTypeToString (x->EventType) << TAB_tab
       << x->EventCategory << TAB_tab
       << x->ClosingRecordNumber << TAB_tab
       << S_Encode (S_MessageString (x)) << TAB_endofrecord;
}

/* --- end of file --- */
