/*************************************************************************\
                  strstuff.c: a set of string functions, 
      more powerful than those from the standard library in <string.h>
\*************************************************************************/

#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;

void str_setup(int bm, int cs)
/* Set up a conversion 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, too, may call anything but alphanumerics a 'space', depending on
 * the setting of <bordermode>.
 */
{
  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! */
}


int str_cmp(UBYTE *s, UBYTE *t)
/* 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.
 */
{
  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]);
}


int strn_cmp(UBYTE *s, UBYTE *t, size_t n)
/* The same as str_cmp, but will only compare up to <n> characters.
 * (Only two lines of code are different.)
 */
{
  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]);
}


UBYTE *str_str(UBYTE *s, UBYTE *t)
/* find a copy of t in s, like strstr() does */
{
  int c;

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


void print_strstat(void)
/* some user info printed to <stdout> */
{
  printf("string processing:\n");
  printf("  upper-/lowercase: ");
  if (case_sense) printf("fussy\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("fussy\n"); break;
  }
}
