/*
 * $Id$
 *
 * $Log$
 * Revision 1.2  1996/05/01 12:08:38  gunter
 * Replace several strcpy by memmove
 *
 * Revision 1.1.1.1  1996/03/08 15:32:57  mclareni
 * Kuip
 *
 */
/*CMZ :  2.06/03 09/01/95  10.05.13  by  N.Cremel*/
/*-- Author :    Alfred Nathaniel   07/08/92*/
/* kkern.c */

#include "kuip/kuip.h"

/*
 * On some systems, e.g. IBM C/370, realloc() becomes very expensive if
 * there are a lot of allocated blocks and the number of bytes spills into
 * a different bucket size.  We can lessen the effect by allocating for
 * string operations (strdup(), mstrcat()), a larger initial block size
 * in order to make realloc() effectively a null operation.
 */
#ifndef ALLOC_MIN_BYTES
# define ALLOC_MIN_BYTES 1
#endif


/*
 * return length of Fortran string ignoring trailing blanks
 */
size_t fstrlen( const char *str,
               size_t len )
{
  long *p;
  static int blanks = 0;

  if( len >= (sizeof blanks) * 4 ) {

    /* check single characters until next word boundary */
    switch( (long)&str[len] & ( (sizeof blanks) - 1 ) ) {
    case 7:
      if( str[--len] != ' ' ) return len + 1;
    case 6:
      if( str[--len] != ' ' ) return len + 1;
    case 5:
      if( str[--len] != ' ' ) return len + 1;
    case 4:
      if( str[--len] != ' ' ) return len + 1;
    case 3:
      if( str[--len] != ' ' ) return len + 1;
    case 2:
      if( str[--len] != ' ' ) return len + 1;
    case 1:
      if( str[--len] != ' ' ) return len + 1;
    }

    if( blanks == 0 )
      memset( (char*)&blanks, ' ', (sizeof blanks) );

    /* check for full words of blanks */
    for( p = (long*)&str[len]; len >= (sizeof blanks) && *--p == blanks; )
      len -= (sizeof blanks);
  }

  /* check single characters again */
  while( len > 0 && str[len-1] == ' ' )
    len--;

  return len;
}


/*
 * copy Fortran string to allocated memory stripping trailing blanks
 */
char *fstrdup( const char *str,
              size_t len )
{
  return strndup( str, fstrlen( str, len ) );
}


/*
 * like fstrdup() but return NULL if string is empty
 */
char *fstr0dup( const char *str,
               size_t len )
{
  size_t n = fstrlen( str, len );
  if( n == 0 )
    return NULL;
  return strndup( str, n );
}


/*
 * like fstrdup() but trim leading blanks
 */
char *fstrtrim( const char *str,
               size_t len )
{
  while( len > 0 && *str == ' ' ) {
    len--;
    str++;
  }
  return fstrdup( str, len );
}


/*
 * like fstr0dup() but trim leading blanks
 */
char *fstr0trim( const char *str,
                size_t len )
{
  while( len > 0 && *str == ' ' ) {
    len--;
    str++;
  }
  return fstr0dup( str, len );
}


/*
 * assign a Fortran string and return the logical length
 */
size_t fstrset( char *str,
               size_t len,
               const char *cstr )
{
  size_t n = 0;
  size_t i;

  memset( str, ' ', len );
  if( cstr != NULL ) {
    for( i = 0; i < len && cstr[i] != '\0'; i++ ) {
      if( (str[i] = cstr[i]) != ' ' )
        n = i + 1;
    }
#if 0
    Unfortunately we cannot do this test for truncation because KUGETC/S
    is sometimes called with a short dummy string to load things for KUGETL
    if( cstr[i] != '\0' ) {
      while( cstr[i] == ' ' )
        i++;
      if( cstr[i] != '\0' ) {
        printf(
" *** A character parameter has been defined with insufficient length = %d\n",
               len );
      }
    }
#endif
  }
  return n;
}


/*
 * create a Fortran string array from a pointer array
 */
char *fstrvec( char **pstr,
              size_t n,
              size_t *mlen )
{
  char *fstr;

  if( n > 0 ) {
    int *plen = (int*)calloc( n, sizeof(int) );
    int len = 1;
    int nlen;
    int i;

    for( i = 0; i < n; i++ ) {
      if( pstr[i] != NULL ) {
        plen[i] = strlen( pstr[i] );
        if( plen[i] > len )
          len = plen[i];
      }
    }

    /* vector elements are padded to multiple of 8 */
    *mlen = len = ((len - 1) / 8 + 1 ) * 8;
    nlen = n * len;
    fstr = malloc( nlen + 1 );
    memset( fstr, ' ', nlen );
    fstr[nlen] = '\0';

    for( i = 0; i < n; i++ ) {
      if( plen[i] > 0 ) {
        memcpy( fstr + i * len, pstr[i], plen[i] );
      }
    }
    free( (char*)plen );
  }

  else {
    int nlen = 8;
    fstr = malloc( nlen + 1 );
    memset( fstr, ' ', nlen );
    fstr[nlen] = '\0';
  }

  return fstr;
}


/*
 * like strtod() but (*tail != '\0') can be used to test for bad numbers
 * trailing blanks are ignored
 */
double fstrtod( const char *str,
               char **tail )
{
  double value = strtod( str, tail );
  const char *ptail = *tail;

  if( *str == '\0'
     || ( ptail == str + 1 && !isdigit( str[0] ) ) ) {
    /*
     * in case str == ""
     * and stupid VMS RTL accepts "+" and "-" as valid numbers
     */
    *tail = "error";
  }
  else if( ptail != str ) {
    while( **tail == ' ' )
      (*tail)++;                /* ignore trailing blanks */
  }

  return value;
}


/*
 * like strtol() but (*tail != '\0') can be used to test for bad numbers
 * trailing blanks are ignored
 */
int fstrtoi( const char *str,
            char **tail )
{
  int value = strtol( str, tail, 10 );
  const char *ptail = *tail;

  if( *str == '\0' ) {
    *tail = "error";
  }
  else if( ptail != str ) {
    while( **tail == ' ' )
      (*tail)++;                /* ignore trailing blanks */
  }

  return value;
}


/*
 * convert double value into string representation with prec significant digits
 * equivalent to "%G" which behaves improperly on VAX/VMS
 */
char *strfromd9( double dval,
               size_t prec )
{
  static char buffer[32];
  char *digit = "0123456789";
  char *p = buffer;
  double round = 5;
  double d;
  int i;

  if( dval == 0 )
    return "0";

  if( prec <= 0 )
    prec = 6;

  for( i = 0; i < prec; i++ )
    round *= 0.1;

  if( dval < 0 ) {
    *p++ = '-';
    dval = -dval;
  }

  d = dval;
  while( d < 1 ) {
    d *= 10;
    round *= 0.1;
  }
  while( d >= 10 ) {
    d *= 0.1;
    round *= 10;
  }
  dval += round;

  if( dval < 1 && dval >= 0.0001 ) {

    *p++ = '0';
    *p++ = '.';
    while( dval < 0.1 ) {
      *p++ = '0';
      dval *= 10;
    }
    for( i = 0; i < prec; i++ ) {
      int ival;
      dval *= 10;
      ival = dval;
      *p++ = digit[ival];
      dval -= ival;
    }
    while( p[-1] == '0' )
      p--;
    if( p[-1] == '.' )
      p--;
  }

  else {
    int nexp = 0;
    int ndot = 0;

    if( dval >= 1000000 ) {
      while( dval >= 10 ) {
        dval *= 0.1;
        nexp++;
      }
    }

    while( dval < 1 ) {
      dval *= 10;
      nexp--;
    }

    while( dval >= 10 ) {
      dval *= 0.1;
      ndot++;
    }

    for( i = 0; i < prec; i++ ) {
      int ival = dval;
      *p++ = digit[ival];
      if( i == ndot )
        *p++ = '.';
      dval = (dval - ival) * 10;
    }

    if( ndot < prec ) {
      while( p[-1] == '0' )
        p--;
    }
    if( p[-1] == '.' )
      p--;

    if( nexp != 0 ) {
      *p++ = 'E';
      if( nexp < 0 ) {
        *p++ = '-';
        nexp = -nexp;
      }
      else
        *p++ = '+';

      if( nexp >= 100 ) {
        *p++ = digit[nexp/100];
        nexp %= 100;
      }
      *p++ = digit[nexp/10];
      *p++ = digit[nexp%10];
    }
  }

  *p = '\0';
  return buffer;
}


/*
 * convert int value into string representation with prec significant digits
 * equivalent to "%.<n>d" which behaves improperly on VAX/VMS
 */
char *strfromi( int ival,
               size_t prec )
{
  static char buffer[3][32];
  static int nbuf = 0;
  char *p;
  int n;
  int i;

  if( ++nbuf >= 3 )
    nbuf = 0;
  p = buffer[nbuf];

  if( ival < 0 ) {
    *p++ = '-';
    ival = -ival;
  }

  n = ival;
  while( n >= 10 ) {
    n /= 10;
    prec--;
  }

  for( i = 0; i < prec - 1; i++ )
    *p++ = '0';

  sprintf( p, "%d", ival );

  return buffer[nbuf];
}


#ifndef HAVE_MEMMOVE
/*
 * memory move with possible overlap
 */
void *memmove( void *vdst,
              const void *vsrc,
              size_t n )
{
  char *dst = (char*)vdst;
  const char *src = (char*)vsrc;
  if( dst != src && n > 0 ) {

    if( dst > src && src + n < dst ) {
      while( n > 0 ) {
        n--;
        dst[n] = src[n];
      }
    }
    else
      memcpy( dst, src, n );
  }
  return dst;
}
#endif


#ifndef HAVE_STRCASECMP
/*
 * case insensitive string compare
 */
int strcasecmp( const char *str1,
               const char *str2 )
{
  return strncasecmp( str1, str2, strlen( str2 ) + 1 );
}


/*
 * case insensitive string compare of n characters
 */
int strncasecmp( const char *str1,
                const char *str2,
                size_t n )
{
  while( n > 0 ) {
    int c1 = *str1;
    int c2 = *str2;

    if( isupper( c1 ) )
      c1 = tolower( c1 );

    if( isupper( c2 ) )
      c2 = tolower( c2 );

    if( c1 != c2 )
      return c1 - c2;

    str1++;
    str2++;
    n--;
  }
  return 0;
}
#endif


/*
 * find last occurence of anyone character
 */
char *strrpbrk( const char *str1,
               const char *str2 )
{
  char *p = strpbrk( str1, str2 );
  if( p != NULL ) {
    char *s;
    while( (s = strpbrk( p + 1, str2 )) != NULL )
      p = s;
  }
  return p;
}


#ifndef HAVE_STRRSTR
/*
 * find last occurence of substring
 */
char *strrstr( const char *str1,
              const char *str2 )
{
  if( str1 == NULL || str2 == NULL )
    return NULL;

  else if( *str2 == '\0' )
    return (char*)str1;

  else {
    char *p = strstr( str1, str2 );
    if( p != NULL ) {
      char *s;
      while( (s = strstr( p + 1, str2 )) != NULL )
        p = s;
    }
    return p;
  }
}
#endif


/*
 * copy string to allocated memory
 */
char *Strdup( const char *str )
{
  size_t bytes = strlen( str ) + 1;
  if( bytes < ALLOC_MIN_BYTES )
    bytes = ALLOC_MIN_BYTES;
  return strcpy( malloc( bytes ), str );
}


/*
 * like strdup() but return NULL if string is NULL or ""
 */
char *str0dup( const char *str )
{
  if( str == NULL || str[0] == '\0' )
    return NULL;
  return Strdup( str );
}


/*
 * like strdup() but string is n characters long and not terminated
 */
char *strndup( const char *str,
              size_t len )
{
  size_t bytes = len + 1;
  char *p;

  if( bytes < ALLOC_MIN_BYTES )
    bytes = ALLOC_MIN_BYTES;

  p = strncpy( malloc( bytes ), str, len );
  p[len] = '\0';
  return p;
}


/*
 * strdup() of character representation of integer n
 */
char *stridup( int n )
{
  char buf[16];
  sprintf( buf, "%d", n );
  return Strdup( buf );
}


/*
 * like strdup() but concatenate two strings
 */
char *str2dup( const char *str1,
              const char *str2 )
{
  return mstrcat( Strdup( str1 ), str2 );
}


/*
 * like strdup() but concatenate three strings
 */
char *str3dup( const char *str1,
              const char *str2,
              const char *str3 )
{
  return mstr2cat( Strdup( str1 ), str2, str3 );
}


/*
 * like strdup() but concatenate four strings
 */
char *str4dup( const char *str1,
              const char *str2,
              const char *str3,
              const char *str4 )
{
  return mstr3cat( Strdup( str1 ), str2, str3, str4 );
}


/*
 * like strdup() but concatenate five strings
 */
char *str5dup( const char *str1,
              const char *str2,
              const char *str3,
              const char *str4,
              const char *str5 )
{
  return mstr4cat( Strdup( str1 ), str2, str3, str4, str5 );
}


/*
 * concatenate two strings if the first one is allocated
 */
char *mstrcat( char *str1,
              const char *str2 )
{
  if( str1 == NULL )
    return Strdup( str2 );
  else {
    size_t len1 = strlen( str1 );
    size_t len2 = strlen( str2 );
    size_t bytes = len1 + len2 + 1;
    if( bytes < ALLOC_MIN_BYTES )
      bytes = ALLOC_MIN_BYTES;
    str1 = realloc( str1, bytes );
    /* strcpy( &str1[len1], str2 ); */
    memmove( &str1[len1], str2, strlen(str2) +1);
    return str1;
  }
}


/*
 * like mstrcat() but concatenate three strings
 */
char *mstr2cat( char *str1,
               const char *str2,
               const char *str3 )
{
  return mstrcat( mstrcat( str1, str2 ), str3 );
}


/*
 * like mstrcat() but concatenate four strings
 */
char *mstr3cat( char *str1,
               const char *str2,
               const char *str3,
               const char *str4 )
{
  return mstr2cat( mstrcat( str1, str2 ), str3, str4 );
}


/*
 * like mstrcat() but concatenate five strings
 */
char *mstr4cat( char *str1,
               const char *str2,
               const char *str3,
               const char *str4,
               const char *str5 )
{
  return mstr3cat( mstrcat( str1, str2 ), str3, str4, str5 );
}


/*
 * like mstrcat() but the second string is not terminated
 */
char *mstrncat( char *str1,
               const char *str2,
               size_t len2 )
{
  if( len2 > 0 ) {
    size_t len1 = strlen( str1 );
    size_t bytes = len1 + len2 + 1;
    if( bytes < ALLOC_MIN_BYTES )
      bytes = ALLOC_MIN_BYTES;
    str1 = realloc( str1, bytes );
    strncpy( &str1[len1], str2, len2 );
    str1[len1+len2] = '\0';
  }
  return str1;
}


/*
 * append n times the character c to the allocated string
 */
char *mstrccat( char *str,
               char c,
               size_t n )
{
  if( n > 0 ) {
    size_t len = strlen( str );
    size_t bytes = len + n + 1;
    if( bytes < ALLOC_MIN_BYTES )
      bytes = ALLOC_MIN_BYTES;
    str = realloc( str, bytes );
    while( n-- > 0 )
      str[len++] = c;
    str[len] = '\0';
  }
  return str;
}


/*
 * append the string representation of an integer to the allocated string
 */
char *mstricat( char *str,
               int n )
{
  char nstr[16];

  sprintf( nstr, "%d", n );
  return mstrcat( str, nstr );
}


/*
 * find the maximum string length in an array of strings
 * if n == 0 the array is NULL terminated
 */
size_t mstrlen( char **str,
               size_t n )
{
  size_t nmax = (n != 0) ? n : (~0 >> 1);
  size_t mlen = 0;
  int i;

  for( i = 0; i < nmax; i++ ) {
    if( str[i] != NULL ) {
      size_t len = strlen( str[i] );
      if( len > mlen )
        mlen = len;
    }
    else if( n == 0 )
      break;
  }

  return mlen;
}


/*
 * protect blanks inside quoted strings and do strtok( str, " " )
 */
char *strqtok( char *str )
{
  char *tok;
  char *p;

  if( str != NULL ) {
    int quoted = 0;
    for( p = str; *p != '\0'; p++ ) {
      if( *p == '\'' )
        quoted = !quoted;
      else if( *p == ' ' && quoted )
        *p = '\n';
    }
  }

  tok = strtok( str, " " );
  if( tok != NULL ) {
    for( p = tok; *p != '\0'; p++ ) {
      if( *p == '\n' )
        *p = ' ';
    }
  }
  return tok;
}


/*
 * convert a string to lowercase in place
 */
char *strlower( char *str )
{
  if( str != NULL ) {
    char *p;
    for( p = str; *p != '\0'; p++ )
      if( isupper( *p ) )
        *p = tolower( *p );
  }
  return str;
}


/*
 * convert a string to uppercase in place
 */
char *strupper( char *str )
{
  if( str != NULL ) {
    char *p;
    for( p = str; *p != '\0'; p++ )
      if( islower( *p ) )
        *p = toupper( *p );
  }
  return str;
}


/*
 * trim leading and trailing blanks
 */
char *strtrim( char *str )
{
  int n = strlen( str );
  while( n > 0 && str[n-1] == ' ' )
    n--;
  str[n] = '\0';
  for( n = 0; str[n] == ' '; )
    n++;
  if( n > 0 )  memmove( str, str + n, strlen(str+n) + 1);
/*  Overlapping, use memmove    strcpy( str, str + n );*/
  return str;
}


/*
 * replace tabs by an equivalent number of blanks and remove trailing blanks
 * argument string must be malloced
 */
char *struntab( char *line )
{
  char *p;
  int i;

  while( (p = strchr( line, '\t' )) != NULL ) {
    int n = p - line;
    char *newline = strndup( line, n );
    newline = mstrccat( newline, ' ', 8 - n % 8 );
    newline = mstrcat( newline, p + 1 );
    free( line );
    line = newline;
  }

  for( i = strlen( line ); i > 0; i-- ) {
    if( line[i-1] != ' ' && line[i-1] != '\n' ) {
      break;
    }
  }
  line[i] = '\0';

  return line;
}

/*
 * replace blanks inside a string with '0'
 */
char *strblk0( char *str )
{
  int i;

  for( i = 0; i < strlen( str ); i++) {
     if (str[i] == ' ') str[i] = '0';
  }

  return str;
}


/*
 * Calculate a 32-bit checksum for the file content.
 */
int checksum( const char *path )
{
  static int bogus_crc = 0x12345678;
  FILE *stream = fopen( path, "r" );

  if( stream != NULL ) {
    unsigned crc = 0;
    int c;
    while( (c = getc( stream )) != EOF ) {
      int carry = crc & 1;
      crc = ( ( crc >> 1 ) & 0x7FFFFFFF ) + ( c & 0xFF );
      if( carry )
        crc |= 0x80000000;
    }
    fclose( stream );
    return crc;
  }
  return ++bogus_crc;
}


#ifdef IBM370
/*
 * return dummy process id
 */
int getpid()
{
  return 1;
}
#endif


#ifdef IBMVM
/*
 * copy file modification date into stamp buffer
 */
int get_stamp( path, stamp )
     char *path;
     TimeStamp *stamp;
{
  FILE *stream;
  char cmd[120];
  char *p;

  sprintf( cmd, "LISTFILE %s (DATE EXEC", path );
  while( (p = strchr( cmd, '.' )) != NULL )
    *p = ' ';

  if( system( cmd ) == 0
     && (stream = fopen( "CMS.EXEC", "r" )) != NULL
     && fgets( cmd, (sizeof cmd), stream ) != NULL ) {
    fclose( stream );
    remove( "CMS.EXEC" );
    strncpy( *stamp, cmd + 63, 17 );
    (*stamp)[17] = '\0';
    return 0;
  }
  return -1;
}
#endif


#ifdef NEED_STRDUP
# ifdef strdup
#  undef strdup
# endif
char *strdup( const char* str )
{
  return Strdup( str );
}
#endif
