#include "copyright.h"
/****************************************************************** 
 *
 * parseclass.c --
 *
 *     Implementation of parsing class. Break raw data into
 *     (name,value) pairs.
 *
 *
 * $Author: mstoufer $
 * $Source: /home/g2/src/projects/NetLogger/src/nlv/parseclass.c,v $
 * $RCSfile: parseclass.c,v $
 * $Revision: 1.1.1.1 $
 * $Locker:  $
 * $Date: 2000/12/05 19:52:32 $
 *
 ****************************************************************** 
 */
static char rcsid[] = "$Id: parseclass.c,v 1.1.1.1 2000/12/05 19:52:32 mstoufer Exp $";

#include "parseclass.h"
#include "ulm.h"
#include <string.h>

/*========= DATA =====================================================*/

#define T Nlv_pa
struct T {
  Nlv_Fields* fields;
};
extern Nlv_err eobj;
#define FILE_ERR_CODE 1300
/*========= FUNCTIONS ================================================*/

/*
 *----------------------------------------------------------------------
 *
 * New -- Allocate new object.
 *
 * Calls: malloc
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
T Nlv_paNew( )
{
  T this = (T)malloc( sizeof( struct T ) );
  this->fields   = 0;
  return this;
}

/*
 *----------------------------------------------------------------------
 *
 * Process -- Break log line into fields.
 *
 * Calls: strchr, strrchr
 *
 * Algorithm: The EQ symbol separates name,value pairs. First, we
 *            want to count these and allocate the integer arrays
 *            'names' and 'vals' which hold the indices of subsequent
 *            name and value strings. The original raw data will
 *            be peppered with \0's so that raw[index] will give the
 *            proper string if index is in names[] or vals[].
 * 
 *            The actual algorithm to get these indices is a simple
 *            strchr() search.
 *
 *            The following illegal formats are caught:
 *              1. no name=value pairs
 *              2. space or tab next to delimiter
 *              3. no spaces between successive delimiters
 *              4. Starts with '='
 *
 *----------------------------------------------------------------------
 */

Nlv_Fields *Nlv_paProcess( T this, LogLine ln)
{
  char *func = "Nlv_paProcess";
  char EQ = '=';
  char *nmStart, 
    *eq, 
    *nextEq , 
    *p1, 
    *p2,
    *eos;
  int i, j, eqCnt;
  Nlv_EInfo einfo;

  /* Clear data */
  if ( ! this->fields )
     this->fields = (Nlv_Fields *)calloc(1, sizeof(struct Nlv_Fields));
  else
    CLEAR_FIELDS(this->fields);

  /* Strip leading spaces */
  for ( ; *ln && (*ln == ' ' || *ln == '\t'); ln++ );
  if ( !(*ln) )
    goto error;

  /* Put raw data into 'raw' */
  this->fields->raw = ln;

  /*  printf("--------------------\nLine: %s\n", ln); */

  /* Count number of EQ chars */
  for ( eqCnt=0, nextEq=ln; 
	(nextEq=strchr(nextEq,EQ)) != NULL; 
	eos=nextEq++, eqCnt++ );
  if ( eqCnt == 0 ) {
    printf("ERROR: No '%c' in line \"%s\"\n", EQ, ln );  
    goto error;
  }

  /* Remember end-of-string */
  for ( ; *eos != '\0'; eos++ );

  /* Allocate names, vals arrays */
  this->fields->names = (int *)malloc( (sizeof (int)) * eqCnt );
  this->fields->vals =  (int *)malloc( (sizeof (int)) * eqCnt );

  nmStart = ln;
  eq      = strchr(ln,EQ);
  *eq     = '\0';

  /* 
   * Main loop 
   */
  for ( i=0; i < eqCnt; i++ ) {
    /* No spaces allowed on either side of equals sign */
    if ( *(eq-1) == ' ' || *(eq-1) == '\t' ||
         *(eq+1) == ' ' || *(eq+1) == '\t' ) {
      printf("%s: Parse error: No spaces allowed on either side of delimiter (%c)\n",
	    func,EQ);
      goto error;
    }
    /* Assign name, value field pointers */
    this->fields->names[i] = (int)(nmStart - ln);
    this->fields->vals[i]  = (int)(eq - ln + 1);
    /*
     * Look for the next field, unless we are
     * on our last iteration.
     */
    if ( i != (eqCnt-1) ) {
      /* Find next equals sign */
      nextEq = strchr(eq+1,EQ);
      if ( nextEq == NULL ) {
	  int j;
	  fprintf(stderr,"%s: Parse error: ULM format corrupted\n", func);
	  for ( j=0; j <= i; j++ )
	  {
	      fprintf(stderr,"   Field %d:'%s'='%s'\n",
		      j,
		      this->fields->names[j] ? (char*)(ln + this->fields->names[j]):"null", 
		      this->fields->vals[j] ? (char*)(ln + this->fields->vals[j]):"null" );
	  }
	  goto error;
      }
      *nextEq = '\0';
      /* Find whitespace before next name */
      for( nmStart = eq+1; *nmStart != ' ' && *nmStart != '\t'; nmStart++ );
      /* 
       * Find first char of next name.
       * Fill intervening whitespace with nul characters.
       */
      for( nmStart; *nmStart == ' ' || *nmStart == '\t'; nmStart++ )
	*nmStart = '\0';
      /* At least one letter between successive equals signs */
      if ( *nmStart == EQ ) {
	printf("%s: Parse error: Need at least 1 letter between successive delimiters (%c)\n",
	       func,EQ);
	goto error;
      }
      eq = nextEq;
    } 
    /*
     * On the last iteration, nul terminate the string.
     */
    else {
      for( p1 = (eos-1); *p1 == '\n' || *p1 == ' ' || *p1 == '\t'; p1-- ); 
      if ( p1 == eq ) {
	printf("%s: Parse error: Need at least 1 letter for last field value\n",
	       func);
	goto error;
      }
      *(p1+1) = '\0';
    }
  } /* main loop */

  /** Debug dump of parsed fields **
   *  for( i=0; i < eqCnt; i++ )
   * printf("field[%d]: %s=%s\n",i,ln+this->fields->names[i],ln+this->fields->vals[i]);
   */

  this->fields->num = eqCnt;

  return this->fields;

error:
  printf("**PARSE ERROR**\n");
  return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * GetNameAt -- Get name at index.
 *
 * Calls: 
 *
 * Algorithm: array lookup
 *
 *----------------------------------------------------------------------
 */

char *GetNameAt( T this, int index )
{
  if ( index < 0 || index > this->fields->num )
    return NULL;
  return (char *)&(this->fields->raw[this->fields->names[index]]);
}

/*
 *----------------------------------------------------------------------
 *
 * GetValAt -- Get value at index
 *
 * Calls: 
 *
 * Algorithm: array lookup
 *
 *----------------------------------------------------------------------
 */
char *GetValAt( T this, int index ) 
{
  if ( index < 0 || index > this->fields->num )
    return NULL;
  return (char *)&(this->fields->raw[this->fields->vals[index]]);
}

/*
 *----------------------------------------------------------------------
 *
 * GetLineTime -- Get JUST the time of a line *in s*. Do NOT modify the
 *                line. Go as fast as possible.
 *
 * Calls    :  strstr, strtol, isspace
 *
 * Algorithm: Seek & Parsify
 *
 *----------------------------------------------------------------------
 */

void Nlv_paGetLineTime( LogLine line, double *r )
{
  char *func = "Nlv_paGetLineTime";
#ifdef OLD_DATE_FORMAT
  char *p[2], *e[2];
  char numstr[64];
  long num[2];
  int i, cnt;
  
  *r = -1.;

  if ( !(p[0] = strstr( line, NLV_SEC_FLD ) ) ||
       !(p[1] = strstr( line, NLV_USEC_FLD) ) ) {
    return;
  }
  for ( i=0; i < 2; i++ ) {
    while ( *(p[i]) && *(p[i]) != '=' && !isspace(*(p[i])) )
      p[i]++;
    if ( *(p[i]) != '=' )
      return;
    for ( cnt=0, e[i] = ++p[i]; *(e[i]) && !isspace(*(e[i])); e[i]++, cnt++ )
      numstr[cnt] = *e[i];
    if ( cnt==0 )
      return;
    numstr[cnt] = '\0';
    num[i] = strtol( &numstr[0], NULL, 10 );
  }
  *r = (double)num[0] + ( (double)num[1] / NLV_TM_TO_SEC );
#else
  static char eq = '=';
  char *p;
  int   i;

  if ( NULL == (p = strstr(line,ULM_DATE_FLD)) )
    return;
  for( ; *p && *p != eq && !isspace(*p); p++ );
  if ( *p != eq )
    return;
  *r = Nlv_pdCalcDate( ++p ); 
#endif
}

/*
 *----------------------------------------------------------------------
 *
 * GetLineTime2 -- Get JUST the time of a line. Do NOT modify the
 *                line. Go as fast as possible.
 *
 * Calls    :  strstr, strtol, isspace
 *
 * Algorithm: We need this version for GetLastTime; this way, we
 *            don't have to slow down the indexer thread with a mutex.
 *
 *----------------------------------------------------------------------
 */

void Nlv_paGetLineTime2( LogLine line, double *r )
#ifdef OLD_DATE_FORMAT
{
  char *p[2], *e[2];
  char numstr[64];
  long num[2];
  int i, cnt;
  
  *r = -1.;

  if ( !(p[0] = strstr( line, NLV_SEC_FLD ) ) ||
       !(p[1] = strstr( line, NLV_USEC_FLD) ) ) {
    return;
  }
  for ( i=0; i < 2; i++ ) {
    while ( *(p[i]) && *(p[i]) != '=' && !isspace(*(p[i])) )
      p[i]++;
    if ( *(p[i]) != '=' )
      return;
    for ( cnt=0, e[i] = ++p[i]; *(e[i]) && !isspace(*(e[i])); e[i]++, cnt++ )
      numstr[cnt] = *e[i];
    if ( cnt==0 )
      return;
    numstr[cnt] = '\0';
    num[i] = strtol( &numstr[0], NULL, 10 );
  }
  *r = (double)num[0] + ( (double)num[1] / NLV_TM_TO_SEC );
}
#else
{
  static char eq = '=';
  char *p;
  int   i;

  if ( NULL == (p = strstr(line,ULM_DATE_FLD)) )
    return;
  for( ; *p && *p != eq && !isspace(*p); p++ );
  if ( *p != eq )
    return;
  *r = Nlv_pdCalcDate2( ++p ); 
}
#endif

#undef T
#undef FILE_ERR_CODE
