/* **********************************************************************
 * *                                                                    *
 * *              String Support Routines For C Programs                *
 * *                                                                    *
 * **********************************************************************
 * File: libs/user/str_subs.c
 *
 *          COPYRIGHT (c) 1991, 1996, 1999, 2001, 2002, 2003 BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sigtypes.h"
#include "user_lib.h"

/* Function prototypes for local functions
 */

/* ==============================================================
 * Routine to do a version of the strcpy function, which produces
 * strings which are safe to be used within filenames.  This means
 * it will strip all control codes, /, ., space, etc.
 */
void strcpy_filesafe
(char* sOutput_a,
 const char* sInput_a )
{
  size_t iLength, iIn, iOut;

  iLength = strlen( sInput_a );
  iOut=0;
  for (iIn=0; iIn<iLength; iIn++)
    {
      char iChar = sInput_a[iIn];
      if (isgraph(iChar))
	{
	  switch(iChar)
	    {
	    case '/':  /* Confused with subdirectory */
	    case '.':  /* Confused with deliminator in ts file format */
	      break;
	    default:
	      sOutput_a[iOut++]=iChar;
	      break;
	    }
	}
    }
  sOutput_a[iOut++]=0;  /* Want null termination */
}

/* ==============================================================
 * Routine to do a safe version of the strncpy function.  It will strip
 * all control codes, and fill the whole target string, with a definate
 * null code at the end.  All control codes are replaced with a period.
 * History:
 *   jmh   26 Aug 1991    Init.
 */
void strncpy_safe
(char* output_a,
 const char* input_a,
 SINT4 isize_a )
{
  size_t ilength, index;

  strncpy_filter( output_a, input_a, isize_a );

  ilength = strlen( input_a );
  for (index=ilength; index<(isize_a-1); index++)
    output_a[index] = ' ';

  output_a[isize_a-1] = 0;
}

/* ==============================================================
 * Routine to do a filtered version of the strncpy function.  It will strip
 * all control codes, replacing them with periods.
 * History:
 *   jmh   27 Aug 1991    Init.
 */
void strncpy_filter
( char* output_a,
  const char* input_a,
  SINT4 isize_a )
{
  size_t ilength, index;
  UINT2 ichar;

  ilength = strlen( input_a );

  /* Add terminating null, if there is space
   */
  if (ilength<isize_a) output_a[ilength] = 0;

  /* Clip the string if it is to long
   */
  if (ilength>isize_a) ilength = isize_a;

  /* Copy and filter all the characters
   */
  for (index=0; index<ilength; index++)
    {
      ichar = (UINT2)(UINT1)input_a[index];
      if ( ((ichar>31)&&(ichar<127)) || ((ichar>159)&&(ichar<255)) )
	output_a[index] = ichar;
      else
	output_a[index] = '.';
    }
}

/* ============================================================================
 * Routine pad the output string up to the size using the input string.
 */
void strnpad
( char* output_a,
  const char* input_a,
  SINT4 isize_a )
{
  while( strlen(output_a) < isize_a ) 
    strncat( output_a, input_a, 
	     MIN(MAX( isize_a-strlen(output_a),0), strlen(input_a) ) );
}

/* ============================================================================
 * Routine to convert a spaced padded string of the specified size to a null terminated
 * string in internal storage.
 */
char* nullify
  (volatile const char* input_a,
   volatile const SINT4 isize_a )
{
  static char string[81];
  SINT4 isize;

  /* Clip the size processed to the internal buffer
   */
  isize = MAX( 0, MIN( 80, isize_a ));

  /* Copy it, and tack on a null termination
   */
  strncpy( string, (char *)input_a, isize );
  string[isize] = 0;

  /* Loop through setting spaces to zeros, until the first non-space character
   */
  isize--;
  while ( (isize>=0) && (string[isize] == ' ') )
    { string[isize] = 0; isize--; }

  return string;
}

/* ============================================================================
 * Routine to convert a spaced padded string of the specified to a null terminated
 * string.  Result is in external storage, assumed to be long enough to hold the
 * size+1.
 */
char* nullify_ncpy
(volatile char* sdest_a,
 volatile const char* source_a,
 const SINT4 isize_a )
{
  SINT4 isize;
  isize = isize_a;

  /* Copy it, and tack on a null termination
   */
  strncpy( (char *)sdest_a, (char *)source_a, isize );
  sdest_a[isize] = 0;

  /* Loop through setting spaces to zeros, until the first non-space character
   */
  isize--;
  while ( (isize>=0) && (sdest_a[isize] == ' ') )
    { sdest_a[isize] = 0; isize--; }

  return (char *)sdest_a;
}

/* ============================================================================
 * Routine to convert a null terminated string to a space terminated string of
 * a known number of characters.
 */
void spacify_ncpy
(volatile char* output_a,
 volatile const char* input_a,
 const SINT4 isize_a )
{
  SINT4 input_size, index;

  input_size = strlen( (char *)input_a );

  /* Copy it, and tack on a null termination
   */
  strncpy( (char *)output_a, (char *)input_a, isize_a );

  /* Loop through the string beyond the last copied character, setting all
   * to spaces.
   */
  for ( index=input_size; index<isize_a; index++ )
    {
      output_a[index] = ' ';
    }
}

/* ====================================================================
 * This function returns the number of characters before the last non-space
 * character in the passed string.
 */ 
SINT4 itrimlen
  (volatile const char *string_a, /* String to search */
   SINT4 ilength_a		  /* Number of characters to search */
   )
{   
  SINT4 ilength;

  ilength = ilength_a;
  while ( (ilength>0) && (string_a[ilength-1]==' ') )
    {
      ilength--;
    }
      
  return ilength;
}

/* ======================================================================
 * Initialize, read, write, or return information about BFIFO's.  The
 * read and write routines return -1 on error, i.e., no character
 * available, or FIFO full.  These routines may be used without the
 * need for semaphore locks as long as no more than one process writes
 * to a given FIFO buffer, and no more than one process reads from
 * same.
 */
void bfifo_init( volatile void *bfifo_a, SINT4 structsize_a )
{
  volatile BFIFO(1) *fifo = bfifo_a ;
  fifo->nSlots   = (structsize_a - BFIFO_HSIZE) / sizeof(fifo->buf[0]) ;
  fifo->readptr  = 0 ;
  fifo->writeptr = 0 ;
}
/* ===================================================
 * Read 1 character (actually a UINT2), return -1 if none.
 */
SINT4 bfifo_read( volatile void *bfifo_a )
{
  volatile BFIFO(1) *fifo = bfifo_a ; UINT2 chr ;
  if( fifo->writeptr == fifo->readptr ) return -1 ;
  chr = fifo->buf[ fifo->readptr ] ;
  if( ++fifo->readptr >= fifo->nSlots ) fifo->readptr = 0 ;
  return( chr ) ;
}
/* ===================================================
 * Read an array of characters, return -1 if none.  Return actual count
 * otherwise.  Count may be smaller if there is less data there.
 */
SINT4 bfifo_read_array( volatile void *bfifo_a, UINT1 *chrs_a, SINT4 iMaxCount_a )
{
  volatile BFIFO(1) *fifo = bfifo_a ; SINT4 iUsed, iCount, iUsedToEnd, ii;
  UINT2 readptr = fifo->readptr ;

  iUsed = bfifo_used( bfifo_a );
  if( iUsed<=0 ) return -1 ;

  iCount = MIN( iMaxCount_a, iUsed );

  iUsedToEnd = fifo->nSlots - readptr;
  if( iCount <= iUsedToEnd ) {
    for( ii=0 ; ii < iCount ; ii++ ) {
      chrs_a[ii] = 0xFF & fifo->buf[ ii + readptr ] ;
    }
    readptr += iCount;
    if( readptr >= fifo->nSlots ) readptr = 0 ;
    fifo->readptr = readptr ;	/* Must be indivisable operation */
    return iCount;
  }
  /* If buffer wraps, do it in two operations, these should always
   * work
   */
  ii = bfifo_read_array( bfifo_a, chrs_a, iUsedToEnd );
  if (ii != iUsedToEnd) fprintf( stderr, "bfifo_read_array internal error 1\n" );
  ii = bfifo_read_array( bfifo_a, &chrs_a[iUsedToEnd], iCount-iUsedToEnd );
  if (ii != (iCount-iUsedToEnd)) fprintf( stderr, "bfifo_read_array internal error 2\n" );
  return iCount;

}
/* ===================================================
 * Write 1 character (actually a UINT2), return -1 if there was no
 * space, otherwise the character count of 1.
 */
SINT4 bfifo_write( volatile void *bfifo_a, UINT2 chr_a )
{
  volatile BFIFO(1) *fifo = bfifo_a ;
  UINT2 writeptr = fifo->writeptr ;
  SINT4 iFree = bfifo_free( bfifo_a ) ;
  if( iFree <= 0 ) return( -1 ) ;

  fifo->buf[ writeptr ] = chr_a ;
  if( ++writeptr >= fifo->nSlots ) writeptr = 0 ;
  fifo->writeptr = writeptr ;	/* Must be indivisable operation */
  return( 1 ) ;
}

/* ===============================================================
 * Write array into the bfifo. 
 * Returns number of bytes written, -1 if there is not enough space.
 */
SINT4 bfifo_write_array
( volatile void *bfifo_a, const UINT1 *chrs_a, SINT4 iCount_a )
{
  volatile BFIFO(1) *fifo = bfifo_a ; 
  UINT2 writeptr = fifo->writeptr ;
  SINT4 iFree, iFreeToEnd, ii ;
  iFree = bfifo_free( bfifo_a ) ;
  if( iCount_a > iFree ) return( -1 ) ;

  iFreeToEnd = fifo->nSlots - writeptr;
  if( iCount_a <= iFreeToEnd ) {
    for( ii=0 ; ii < iCount_a ; ii++ ) {
      fifo->buf[ writeptr + ii ] = chrs_a[ii] ;
    }
    writeptr += iCount_a ;
    if( writeptr >= fifo->nSlots ) writeptr = 0 ;
    fifo->writeptr = writeptr ;	/* Must be indivisable operation */
    return( iCount_a ) ;
  }
  /* If buffer wraps, do it in two operations
   */
  ii = bfifo_write_array( bfifo_a, chrs_a, iFreeToEnd );
  if (ii != iFreeToEnd) fprintf( stderr, "bfifo_write_array internal error 1\n" );
  ii = bfifo_write_array( bfifo_a, &chrs_a[iFreeToEnd], iCount_a-iFreeToEnd );
  if (ii != (iCount_a-iFreeToEnd)) fprintf( stderr, "bfifo_write_array internal error 2\n" );

  return( iCount_a ) ;
}

/* =================================================
 * Returns the number of bytes of data in the fifo
 */
SINT4 bfifo_used( volatile void *bfifo_a )
{
  volatile BFIFO(1) *fifo = bfifo_a ; SINT4 iused ;
  iused = (fifo->writeptr - fifo->readptr) ;
  if( iused < 0 ) iused += fifo->nSlots ;
  return( iused ) ;
}
/* =================================================
 * Returns the number of free bytes in the fifo
 */
SINT4 bfifo_free( volatile void *bfifo_a )
{
  volatile BFIFO(1) *fifo = bfifo_a ; SINT4 iused ;
  iused = (fifo->writeptr - fifo->readptr) ;
  if( iused < 0 ) iused += fifo->nSlots ;
  return( (fifo->nSlots-1) - iused ) ;
}
      
/* ===========================================
 * Parse a string to return a token, using SIGMET rules.  Return NULL
 * when the string is done.
 */
char *sigtok
( char *sline_a, 
  UINT4 *ioffset_a )		/* Modifies offset each time. */
{
  char *start, *sch ; UINT1 iQuotes = 0 ;

  /* First skip all leading whitespace to identify the start of the
   * token.  We're all done if that leaves us at the end of the line.
   */
  start = sline_a + *ioffset_a ;
  start += strspn( start, " \t\v\r\n\f" ) ;
  if( *start == '\0' ) return( NULL ) ;

  /* Scan for the end of the token, but keep together any text that we
   * find within double quotes.
   */
  for( sch = start ; *sch != '\0' ; sch++ ) {
    if( *sch == '"' ) iQuotes++ ;
    else if( ( (iQuotes & 1) == 0 ) &&
	     (NULL != strchr( " \t\v\r\n\f", *sch )) ) break ;
  }

  /* If we are about to return a string that begins and ends with a
   * double quote, then remove those outer quotes entirely.  We do not
   * do this for quoted strings that are embedded in the token itself.
   */
  if( (start != (sch-1)) && (*start == '"') && (*(sch-1) == '"') ) {
    start++ ; *(sch-1) = '\0';
  }

  /* Compute new offset for beginning the next search, and terminate
   * the present token if it is not terminated already.
   */
  *ioffset_a = (UINT4)(sch - sline_a) ;
  if( *sch ) { *sch = '\0' ; (*ioffset_a)++ ; }

  return( start ) ;
}

/* ==========================================
 * Copies the input string to the output string, replacing all non
 * printable charcters with \ notation.  It stops at the first null,
 * or when the output length is reached.  The output string will
 * always be null terminated.
 */
void PrintFromString
( char *sPrintableString_a,    /* Printable String output */
  const char *sRawString_a,    /* Raw String input */
  SINT4 iOutLength_a )	       /* Length of the output string */
{
  SINT4 iOut, iIn, iInLength;

  iOut = 0;
  iInLength = strlen(sRawString_a);

  iIn = 0;
  while( (iIn<iInLength) && ((iOut+1)<iOutLength_a) )
    {
      char ch = sRawString_a[iIn] ;
      if     ( ch == '\\'  )
	{ 
	  if(!((iOut+2)<iOutLength_a)) goto DONE;
	  sPrintableString_a[iOut++] = '\\';
	  sPrintableString_a[iOut++] = '\\';
	}
      else if( ch == '"'   )
	{ 
	  if(!((iOut+2)<iOutLength_a)) goto DONE;
	  sPrintableString_a[iOut++] = '\\';
	  sPrintableString_a[iOut++] = '\"';
	}
      else if( isprint(ch) ) 
	sPrintableString_a[iOut++] = ch;
      else
	switch( ch )
	  {
	  case '\b':
	    if(!((iOut+2)<iOutLength_a)) goto DONE;
	    sPrintableString_a[iOut++] = '\\';
	    sPrintableString_a[iOut++] = 'b';
	    break;
	  case '\f':
	    if(!((iOut+2)<iOutLength_a)) goto DONE;
	    sPrintableString_a[iOut++] = '\\';
	    sPrintableString_a[iOut++] = 'f';
	    break;
	  case '\n':
	    if(!((iOut+2)<iOutLength_a)) goto DONE;
	    sPrintableString_a[iOut++] = '\\';
	    sPrintableString_a[iOut++] = 'n';
	    break;
	  case '\r':
	    if(!((iOut+2)<iOutLength_a)) goto DONE;
	    sPrintableString_a[iOut++] = '\\';
	    sPrintableString_a[iOut++] = 'r';
	    break;
	  case '\t':
	    if(!((iOut+2)<iOutLength_a)) goto DONE;
	    sPrintableString_a[iOut++] = '\\';
	    sPrintableString_a[iOut++] = 't';
	    break;
	  case '\v':
	    if(!((iOut+2)<iOutLength_a)) goto DONE;
	    sPrintableString_a[iOut++] = '\\';
	    sPrintableString_a[iOut++] = 'v';
	    break;
	  default:
	    if(!(iOut+4)<iOutLength_a) goto DONE;
	    sprintf( &sPrintableString_a[iOut], "\\%3.3o", ch ) ;
	    iOut += 4;
	    break;
	  }
      iIn++;
    }
  /* Done, add the null termination */
 DONE:
  sPrintableString_a[iOut] = 0;

}

/* ==========================================
 * Copies the input string to the output string, replacing all \
 * notation strings with the correct non printable character.
 * It stops at the first null, or when the output length is reached.
 * The output string will always be null terminated.  The output
 * string might have multiple nulls in it if the input contains
 * \000.
 */
void StringFromPrint
( char *sRawString_a,              /* Raw output strint to fill in */
  const char *sPrintableString_a,  /* Printable input string */
  SINT4 iOutLength_a )		   /* Length of the output string */
{
  SINT4 iIn, iOut, iInLength;

  iInLength = strlen(sPrintableString_a);

  iIn = 0;
  iOut = 0;
  while( (iIn<iInLength) && ((iOut+1)<iOutLength_a) )
    {
      char ch = sPrintableString_a[iIn++];
      if( ch == '\\' ) 
	{
	  SINT4 iOctal ;
	  switch( sPrintableString_a[iIn])
	    {
	    case '\\': ch = '\\' ; iIn++ ; break;
	    case 'b':  ch = '\b' ; iIn++ ; break;
	    case 'f':  ch = '\f' ; iIn++ ; break;
	    case 'n':  ch = '\n' ; iIn++ ; break;
	    case 'r':  ch = '\r' ; iIn++ ; break;
	    case 't':  ch = '\t' ; iIn++ ; break;
	    case 'v':  ch = '\v' ; iIn++ ; break;
	    case '"':  ch = '"'  ; iIn++ ; break;
	    default:
	      if( 1 == sscanf( &sPrintableString_a[iIn], "%3o", &iOctal ) )
		{
		  ch = (0xFF & iOctal) ; iIn += 3 ;
		}
	      else
		{
		  ch = 0;
		}
	      break;
	    }
	}
      sRawString_a[iOut++] = ch ;
    }
}
