/*========================================================================*\
 |  File: strstuff.c                                   Date: 20 Sep 1997  |
 *------------------------------------------------------------------------*
 |   Some string functions, similar to those from the standard library    |
 |                  in <string.h>, but more flexible.                     |
 |   You must call str_setup() before using any of these functions !!!    |
 |                                                                        |
\*========================================================================*/

#include <stddef.h>
#include <stdio.h>
#include "strstuff.h"

UBYTE to_upper[ 256 ];            /* conversion table */
int   is_space[ 256 ];            /* lookup table */
int bordermode, case_sense;



/*
 * Set up a to_upper[] table that can convert all national characters from
 * the ISO 8859-1/ECMA 94 charset to uppercase, not only 'a'-'z'.
 * Depending on the global variable <case_sense> this table may just as 
 * well do nothing at all.  
 * Similarly, the is_space[] table which is built here may call anything 
 * but alphanumerics a 'space', depending on the setting of <bordermode>.
 */
void str_setup( int bm, int cs )
{  
  int c, d;

  bordermode = bm;
  case_sense = cs;              /* set global variables */
  for( c = 0; c < 256; c++ )
    {
      to_upper[ c ] = c;
      is_space[ c ] = ( bordermode < 2 || (c & 0x7f) <= ' ' );
    }
  for( c = 'a'; c <= 'z'; c++ )
    {
      d = c + 'A' - 'a';
      to_upper[ c ] = d;
      is_space[ c ] = 0;
      is_space[ d ] = 0;
    }
  for( c = '0'; c <= '9'; c++ )
    {
      is_space[ c ] = 0;
    }
  for( c = 224; c < 256; c++ )
    if( c != 247 )
      {                         /* 247 is the division sign -:- */
        d = c - 32;
        is_space[ c ] = 0;
        is_space[ d ] = 0;
        if( c != 255 )          /* and don't convert '"y' to 'ss' : ) */
          to_upper[ c ] = d;
      }
  if( case_sense )              /* destroy the to_upper[] table again */
    for( c = 0; c < 256; c++ )
      to_upper[ c ] = c;
  is_space[ 0 ] = 0;            /* important, will be taken advantage of! */
}



/* 
 * str_cmp() will work like strcmp() if( case_sense && bordermode == 3 ),
 * but can change its behaviour depending on those variables.
 * Will also indicate by special (but legal) return values if <s> was an
 * abbreviation of <t> or vice versa.
 */
int str_cmp( UBYTE *s, UBYTE *t )
{
  if( bordermode < 3 )
    {                           /* modes where number of 'spaces' doesn't count */
      while( is_space[ *s ] )
        s++;                    /* advance to first word */
      while( is_space[ *t ] )
        t++;
      while( to_upper[ *s ] == to_upper[ *t ] )
        {
          if( *s == '\0' )
            return 0;
          s++;
          t++;
          if( !bordermode || (is_space[ *s ] && is_space[ *t ]) )
            {
              /* both at the end of a word OR in sloppy bordermode */
              while( is_space[ *s ] )
                s++;            /* skip the spaces */
              while( is_space[ *t ] )
                t++;
            }
        }
    }
  else
    {
      while( to_upper[ *s ] == to_upper[ *t ] )
        {
          if( *s == '\0' )
            return 0;
          s++;
          t++;
        }
    }
  if( *s == '\0' )
    return STR_SHORTER;
  else if( *t == '\0' )
    return STR_LONGER;
  else
    return( to_upper[ *s ] - to_upper[ *t ] );
}



/* 
 * The same as str_cmp(), but will only compare up to <n> characters.
 * (Only two lines of code are different.)
 */
int strn_cmp( UBYTE *s, UBYTE *t, size_t n )
{
  if( bordermode < 3 )
    {
      while( is_space[ *s ] )
        s++;
      while( is_space[ *t ] )
        t++;
      while( to_upper[ *s ] == to_upper[ *t ] )
        {
          if( --n == 0 || *s == '\0' )
            return 0;           /* that's the difference */
          s++;
          t++;
          if( !bordermode || (is_space[ *s ] && is_space[ *t ]) )
            {
              while( is_space[ *s ] )
                s++;
              while( is_space[ *t ] )
                t++;
            }
        }
    }
  else
    {
      while( to_upper[ *s ] == to_upper[ *t ] )
        {
          if( --n == 0 || *s == '\0' )
            return 0;           /* and here once more */
          s++;
          t++;
        }
    }
  if( *s == '\0' )
    return STR_SHORTER;
  else if( *t == '\0' )
    return STR_LONGER;
  else
    return( to_upper[ *s ] - to_upper[ *t ] );
}



/* 
 * Find a copy of t in s, like strstr( )does.
 * Note that bordermode has NO effect here!
 */
UBYTE *str_str( UBYTE *s, UBYTE *t )           
{
  int c;

  while( *s )
    {
      c = str_cmp( s, t );
      if( c == 0 || c == STR_LONGER )
        return s;
      s++;
    }
  return NULL;
}



/* 
 * Some user info printed to <stdout>.
 */
void print_strstat( void )
{
  printf( "string processing:\n" );
  printf( "  upper-/lowercase: " );
  if( case_sense )
    printf( "strict\n" );
  else
    printf( "'ABC'='abc'\n" );
  printf( "  word delimiters:  " );
  switch( bordermode )
    {
    case 0:
      printf( "'a,b c'='abc'\n" );
      break;
    case 1:
      printf( "'a, b, c'='a,b c'\n" );
      break;
    case 2:
      printf( "'a  b  c'='a b c'\n" );
      break;
    case 3:
      printf( "strict\n" );
      break;
    }
}

