#include "copyright.h"
/****************************************************************** 
 *
 * util.c --
 *
 *     Shared utility functions.
 *
 *
 * $Author: mstoufer $
 * $Source: /home/g2/src/projects/NetLogger/src/nlv/util.c,v $
 * $RCSfile: util.c,v $
 * $Revision: 1.1.1.1 $
 * $Locker:  $
 * $Date: 2000/12/05 19:52:33 $
 *
 ****************************************************************** 
 */
static char rcsid[] = "$Id: util.c,v 1.1.1.1 2000/12/05 19:52:33 mstoufer Exp $";

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include "util.h"

#define FILE_ERR_CODE 1400
extern Nlv_err eobj;

/*
 *----------------------------------------------------------------------
 *
 * Split -- Split a string on whitespace (space or tab, although
 *          this is easily modified by changing the 'IS_WS' macro
 *          at the head of the function).
 *
 * Calls    : strcpy, malloc
 *
 * Algorithm:
 *            - Copy input.
 *            - Trim whitespace, {} chars, from both ends
 *            - Copy into new string, changing whitespace to
 *              null characters.
 *            - Point entries of input char*** array at the
 *              successive null-delimited fields.
 *
 *----------------------------------------------------------------------
 */

int Nlv_Split( char *str, int *num, char ***arr )
{
#define IS_WS(p) ( *(p) && ( *(p)==' ' || *(p)=='\t' ) )
  int ret, len;
  char *p, *s;
  int i;

  *num = 0;

  /* Copy string after starting ws */
  for ( p = str; IS_WS(p); ++p );
  len = strlen( p );
  s = (char *)malloc( len + 1 );
  strcpy( s, p );
  /* Trim from other end, too */
  for ( p = s + len - 1; IS_WS(p); *p-- = '\0' );
  p = s;
  /* Separate into substrings */
  if ( *p ) {
    while ( *p ) {
      if ( IS_WS(p) ) {
	*p++ = '\0';
	(*num)++;
	for ( ; IS_WS(p); *p++ = '\0' );
      }
      else
	p++;
    }
    (*num)++;
    p = s;
    /* Make array entries point at beginning of substrings */
    *arr = (char **)malloc( *num * sizeof(char *));
    (*arr)[0] = p++;
    for ( i=1; i < *num; i++ ) {
      while( *p++ );
      while( !*p ) p++;
      (*arr)[i] = p;
    }
  }
  else
    free( s );
  return *num;
}
#undef IS_WS

/*
 *----------------------------------------------------------------------
 *
 * RightStr -- Return pointer to 'n' rightmost chars of string.
 *
 * Calls    : strlen
 *
 * Algorithm: Subtract n from end of string, return pointer to
 *            position if >= start of string, else return
 *            start of string.
 *
 *----------------------------------------------------------------------
 */

char *Nlv_RightStr( char *str, int n )
{
  char *p = str + strlen(str) - n;
  return ( p >= str ? p : str );
}


/*
 *----------------------------------------------------------------------
 *
 * MatchField -- Find this string in the names[] array of 'fields'.
 *
 * Calls:
 *
 * Algorithm: 
 *
 *----------------------------------------------------------------------
 */

int Nlv_MatchField( Nlv_Fields* flds, char *matchme )
{
  int i;
  char *s;

  if ( !matchme )
    return -1;

  for ( i=0; i < flds->num; i++) {
    s = FIELD_NAME(flds,i);
    if ( !s ) {
       printf("No field at %d\n", i ); 
       return -1;
    }
    if (strcmp(s,matchme) == 0 )
      break;
  }
  return ( i < flds->num ? i : -1 );
} 

/*
 *----------------------------------------------------------------------
 *
 * IpStr2HostName -- Attempt to get the host name for the given
 *                   ip string from the system.
 *
 * Calls    : inet_addr, gethostbyaddr, strdup
 *
 * Algorithm: Return duplicate of original string if not found.
 *
 *----------------------------------------------------------------------
 */

char *
Nlv_IpStr2HostName(char *ipstr)
{
  char *func = "Nlv_IpStr2HostName";
    char *hn = NULL;
    unsigned int netaddr;
    struct hostent *hp = NULL;

    /*    fprintf(stderr,"<dbg>%s: INPUT='%s'\n",func,ipstr); */

    if ( ipstr &&
	 (netaddr = inet_addr(ipstr)) != -1 &&
        /* don't free hp!! ; it's a pointer to static storage */
         (hp = gethostbyaddr((char *)&netaddr, (int)sizeof(netaddr), AF_INET)) != NULL && 
	 hp->h_name != NULL &&
	 (hn = strdup(hp->h_name)) != NULL ) {
      /*      fprintf(stderr,"<dbg>%s: OUTPUT='%s'\n",func,hn); */
      return(hn); 
    }
    /*    fprintf(stderr,"<dbg>%s: OUTPUT='%s'\n",func,ipstr); */
    return( strdup(ipstr) );
}
/*
 *----------------------------------------------------------------------
 *
 * ParseMultiList -- Transform list into 2-D array.
 *
 * Calls    : 
 *
 * Algorithm: Break into separate arrays on '&' separator.
 *            We assume that there is always at least one field
 *            between separators.
 *
 *----------------------------------------------------------------------
 */
int
Nlv_ParseMultiList( int      numtok,
		    char   **tok, 
		    int    **num, 
		    char ****arr )
{
  int i, j, han, ctr;

  *arr = ( char ***) malloc( numtok * sizeof(char *));
  *num = (int *)malloc( numtok / 2 * sizeof(int) );

  han = 0;
  j   = 0;
  ctr = 0;
  while ( ctr < numtok ) {
    for ( i=ctr; i < numtok && strcmp("&",tok[i]) != 0; i++ );
    (*num)[han] = i - ctr;
    (*arr)[han] = ( char **)malloc( i *sizeof(char *));
    for ( j=ctr; j < i; j++ )
      (*arr)[han][j-ctr] = tok[j];
    ctr = j+1; /* skip separator */
    han++;
  }
  return han;
}

/*
 *----------------------------------------------------------------------
 *
 * Fields2String -- Dynamically allocate a new string and
 *                  copy a logline into it, replacing the
 *                  NULL chars used to break up fields with
 *                  spaces, so it can be printed.
 *
 * Calls:
 *
 * Algorithm: 
 *
 *----------------------------------------------------------------------
 */

char * Nlv_Fields2String( Nlv_Fields* flds )
{
  int  i;
  char *src, *dst, *ret;

  if ( !flds )
    return NULL;
  ret = (char * )malloc( NLV_MAXLINE * sizeof(char) );
  dst = ret;
  src = flds->raw;
  for ( i=0; i < flds->num; i++ ) {
    while ( *dst++ = *src++ );
    *(dst - 1) = '=';
    while( *dst++ = *src++ );
    if ( i < (flds->num-1) ) {
      while( *src == '\0' )
	++src;
      *(dst -1) = ' ';
    }
    else
      *(dst - 1) = '\0';
  }

  return ret;
}
#undef FILE_ERR_CODE

/*
 *----------------------------------------------------------------------
 *
 * CopySkipChars -- Make a copy of the input string, but don't include
 *                  any of the characters in the second argument.
 *
 * Calls: strlen, malloc
 *
 * Algorithm: straightforward
 *
 *----------------------------------------------------------------------
 */
char * Nlv_CopySkipChars( char *in, char *skip ) {
  char *skptr;
  char *newstr;
  char *p, *q;

  if ( NULL==in || NULL==skip )
    return NULL;
  newstr = (char *)malloc( strlen(in) + 1 );
  for ( p=in, q=newstr; *p; p++ ) {
    for ( skptr=skip; *skptr && *p != *skptr; skptr++ );
    if ( ! *skptr )
	*q++ = *p;
  }
  *q = '\0';
  return newstr;
}

/*
 *----------------------------------------------------------------------
 *
 * strupper -- Convert string to uppercase in-place.
 *
 * Calls: islower, toupper
 *
 * Algorithm: Simple loop
 *
 *----------------------------------------------------------------------
 */
void strupper( char *s )
{
  if ( s ) 
    for ( ; *s; s++ )
      if ( islower(*s) )
	*s = toupper(*s);
}

/*
 *----------------------------------------------------------------------
 *
 * fdread1line -- Read one CR-delimited line from a file
 *                descriptor.
 *
 * Return: 0 = no data
 *         1 = data
 *
 * Calls: memset, strchr, read
 *
 * Algorithm: 
 *             + Look for a CR in buffer
 *             + Found?
 *                 - yes: return chunk between last position
 *                        and CR, set last position just past
 *                        the CR.
 *                 - no: read more data from the file and
 *                       look for a CR, as above, except that
 *                       if none is found, return NULL.
 *
 *----------------------------------------------------------------------
 */

int fdread1line( int fd,    /* descriptor to read from */
		 char *buf, /* buffer to put data in */
		 char **pos,/* position to read from */
		 int  *len, /* length of line read, if any */
		 int blk )  /* block size */
{
  static char *p;
  int         nbytes;
  int         to_read;
  char       *next_cr;
  char       *s;

  /*  printf("enter fdread1line\n"); */

  if ( fd == -1 ) {
    p = buf;
    /*    printf("..init\n"); */
    return 0;
  }

  *len = 0;
  /*  printf("strchr of p\n"); */
  next_cr = strchr( p, '\n' );

  /* 1) CR found */
  if ( next_cr ) {
    /* printf("..cr found at %d\n", (int)(next_cr - buf)); */
    *next_cr = '\0';
    *pos = p;
    *len = (int)( next_cr - p + 1 );
     p   = next_cr + 1;
    return 1;
  }

  /* 2) shift and read */
  /* printf("shift and read\n"); */
  s       = buf;
  to_read = blk - 1;

  /* copy old section */
  /*  printf("copy old section\n"); */
  while ( *s++ = *p++ ) to_read--;
  s--;
  p = buf;

  /* read more data from file */
  /* printf("attempt to read %d bytes\n", to_read); */
  while ( (nbytes = read( fd, s, to_read )) == -1 &&
	  errno == EINTR ) 
    ;
  /* printf("read returns -1, EINTR\n"); */
  /* printf("read returned %d bytes\n",nbytes); */

  /* no data or failure? */
  if ( nbytes < 1 ) {
    if ( nbytes == -1 ) {
      NLV_FATAL_S2("Indexer reading from log failed", strerror(nbytes));
      exit(1);
    }
    else {
      /* printf("no data..\n"); */
      return 0;
    }
  }

  /* find CR in augmented buffer */
  *(s+nbytes) = '\0';
  next_cr = strchr( p, '\n');
  if ( next_cr ) {
    *next_cr = '\0';
    *pos = p;
    *len = (int)( next_cr - p + 1 );
    p    = next_cr + 1;
  }
  else {
    /*    printf("..no cr in augmented buffer\n"); */
    return 0;
  }
  
  /*  printf("leave fdread1line: success\n"); */
  return 1;
}

/*
 *----------------------------------------------------------------------
 *
 * select_sleep -- Sleep for {sec + usec} using select()
 *
 * Calls: select
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
void select_sleep( int sec, int usec )
{
  char *func = "select_sleep";
  struct timeval timeout;

  if ( sec > 0 || usec > 0 ) {
    timeout.tv_sec  = sec;
    timeout.tv_usec = usec;
    if (select(0,(fd_set *) 0,(fd_set *) 0,(fd_set *) 0,&timeout ) < 0 ) {
      Nlv_errPrintString( eobj, func, "sleep system call returned negative value.");
    }
  }
}

/*
 *----------------------------------------------------------------------
 *
 * chchar -- Change an instance of c1 to c2 in a NULL-terminated
 *           string.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
char *chchar( char *str, char c1, char c2 )
{
  char *p;
  if ( !str )
    return NULL;
  for ( p=str; *p; p++ )
    if ( *p == c1 )
      *p = c2;
  return str;
}         

/*
 *----------------------------------------------------------------------
 *
 *  stopwatch -- Start/stop a timer. 8 simultaneous timers
 *               are allowed.
 *
 * Calls: gettimeofday
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */
double stopwatch( int n  )
{
  static int started[8] = { 0,0,0,0,0,0,0,0 };
  static struct timeval sw[ 8 ];
  static struct timezone z = { 0, 0 }; /* GMT with no daylight sav */
  struct timeval click;
  double diff = 0.;
 
  if ( n < 0 || n > 7 ) return -1.;
  if ( !started[n] ) { 
     started[n] = 1;
     gettimeofday( &sw[n], &z ); 
  }
  else {
     started[n] = 0;
     gettimeofday( &click, &z );
     diff = (double)( click.tv_sec - sw[n].tv_sec ) 
          + (double)( click.tv_usec - sw[n].tv_usec ) / 1E6;
     timerclear( &sw[n] );
  }
  return diff; 
}
