/*
    (c) 1995-2001 by Wilhelm Noeker (wnoeker@t-online.de)

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA

 */

/*========================================================================*\
 |  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 <string.h>
#include "strstuff.h"

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



/*
 * Like strncpy(), but guarantees a null delimiter.
 */
UBYTE *str_ncpy( UBYTE *t, UBYTE *s, size_t n )
    {
    strncpy( t, s, n );
    t[ n-1 ] = '\0';
    return t;
    }



/*
 * Set up a to_upper[] table that can convert all national characters from
 * the ISO Latin-1 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 'sz' : ) */
                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, we'll take advantage of this! */
    }



/*
 * 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 str_ncmp( 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;
    }
}

