/*
 * File:	string.c
 *
 * Author:	Ulli Horlacher (framstag@rus.uni-stuttgart.de)
 *
 * Contrib.:    Ingo Wilken (Ingo.Wilken@Informatik.Uni-Oldenburg.DE)
 *		Christoph 'GNUish' Goern (goern@beuel.rhein.de)
 * 		Joerg 'Mufti' Scheurich (Joerg.Scheurich@RUS.Uni-Stuttgart.DE)
 * 
 *
 * History:	
 * 
 *   1995-08-12	Framstag	initial version
 *   1996-02-05	Framstag	faster str_trim function
 *   1996-05-07	Framstag	added strbeq()
 *   1997-01-14	GNUish		added strerror()
 *   1997-02-03	Framstag	included snprintf() from apache-source
 *   1997-02-04	GNUish		added the #ifdef for snprintf()
 *   1997-02-07	Ingo		improved simplematch()
 *   1997-02-23	Framstag	modified str_* function names
 *				added str_beq_nocase and str_neq_nocase
 *   1997-10-03	Framstag	better BSD support in strerror()
 *   1997-12-12	Framstag	simplematch now supports the regular expressions
 *                              [A-Z], [^ABC] and [^A-Z]
 *   1998-01-05	Mufti		fixed bug in str_trim()
 *   1998-04-01	Framstag	fixed bug in [a-b] match in simplematch()
 *   2000-12-10 Framstag	added fgetl() (substitution for fgets())
 *   2001-02-01 Framstag	fixed bug in fgetl() 
 *
 * Extended string functions for the sendfile package, which are not found
 * in the standard C library.
 * Look at string.h for a list of the functions.
 *
 * Copyright © 1995-2001 Ulli Horlacher
 * This file is covered by the GNU General Public License
 */

#include "config.h"

#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>

#include "string.h"

/*
#ifdef IRIX
  int toupper(char);
  int tolower(char);
#endif
*/


/*
 * str_trim - substitute multiple white spaces with one space
 *            and delete heading and trailing whitespace
 *
 * INPUT:  string  - string to trim
 *
 * OUTPUT: string  - trimmed string
 *
 * RETURN: trimmed string
 */
char *str_trim(char *string) {
  char *rp,	/* reading string pointer */
       *wp;	/* writing string pointer */
  int ws;	/* white space flag */

  ws=0;
  
  if (*string) {
    
    /* loop over string */
    for (wp=rp=string; *rp; rp++) {
      
      /* is it a white space? */
      if (*rp==' ' || *rp=='\t' || *rp=='\n' || *rp=='\r') {
	
	/* store a blank if the last character was not a white space */
	if (!ws) {
	  *wp++=' ';
	  ws=1;
	}
	
      } else /* no white space */ {
	
	/* store the character */
	*wp++=*rp;
	ws=0;
      }
      
    }

    /* delete trailing blank */
    if (ws) wp--;
    *wp=0;
    
  }

  return(string);
}


/*
 * str_toupper - transform string to upper case
 *
 * INPUT:  string  - string to transform
 *
 * OUTPUT: string  - transformed string
 *
 * RETURN: string  - transformed string
 */
char *str_toupper(char *string) {
  char *cp;

  /* change each character to it's upper case pedant */
  for (cp=string; *cp; cp++) *cp = (char) toupper(*cp);

  return(string);
}


/*
 * str_tolower - transform string to lower case
 *
 * INPUT:  string  - string to transform
 *
 * OUTPUT: string  - transformed string
 *
 * RETURN: string  - transformed string
 */
char *str_tolower(char *string) {
  char *cp;

  /* change each character to it's upper case pedant */
  for (cp=string; *cp; cp++) *cp = (char) tolower(*cp);

  return(string);
}


/*
 * str_beq  - string begin equal test
 *
 * INPUT:  s1  - string 1
 *         s1  - string 1
 *
 * RETURN: 1 if begin is equal, 0 if begin is not equal
 */
int str_beq(const char *s1, const char *s2) {
  int len;
  if (strlen(s1) < strlen(s2)) 
    len=strlen(s1); 
  else 
    len=strlen(s2);
  if (strncmp(s1,s2,len) == 0) 
    return(1); 
  else 
    return(0);
}


/*
 * str_neq_nocase  - string equal test until length n, ignoring case
 *
 * INPUT:  s1  - string 1
 *         s1  - string 1
 *         len - max length to compare
 *
 * RETURN: 1 if is equal, 0 if is not equal (until size len)
 */
int str_neq_nocase(const char *s1, const char *s2, int len) {
  int i;
  char a,b;
  for (i=0;i<len;i++) {
    a=s1[i];
    b=s2[i];
    if (tolower(a)!=tolower(b)) return(0);
  }
  return(1);
}


/*
 * str_beq_nocase  - string begin equal test, ignoring case
 *
 * INPUT:  s1  - string 1
 *         s1  - string 1
 *
 * RETURN: 1 if begin is equal, 0 if begin is not equal
 */
int str_beq_nocase(const char *s1, const char *s2) {
  int len;
  if (strlen(s1)<strlen(s2)) len=strlen(s1); else len=strlen(s2);
  return(str_neq_nocase(s1,s2,len));
}


/***************************************************************
 *  simplematch:
 *
 *  simple wildcard matching
 *
 *  ?           matches any single character
 *  *           matches any string
 *  [abc]  	matches 'a' or 'b' or 'c'
 *              within [] the characters '?', '*' and '[' are no wildcards
 *  [^abc]  	matches every character but not 'a' or 'b' or 'c'
 *  [a-d]  	matches 'a' or 'b' or 'c' or 'd' (range)
 *              this is equal to [abcd]
 *  [^a-d]  	matches every character but not 'a' or 'b' or 'c' or 'd' (range)
 *              this is equal to [^abcd]
 *  \?          test for "true" '?'   alternative:  [?]
 *  \*          test for "true" '*'   alternative:  [*]
 *  \[          test for "true" '['   alternative:  [[]
 *  \a          Bell
 *  \b          BackSpace
 *  \f          FormFeed
 *  \n          NewLine
 *  \r          Return
 *  \t          Tab
 *  \v          FormFeed
 *  \\          Backslash
 *
 *  ACHTUNG:  "[]" (empty brackets) always fails!
 *
 * Copyright (C) 1991 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  This software is provided "as is" without express or
 * implied warranty.
 */

/*
#include <stddef.h>
#define NULL        (char *)0
*/

#define SM_MATCH        1
#define SM_NOMATCH      0
#define SM_ERROR       -1        /* Fehler im Pattern */


static char sm_escchar( c )
char c;
{
    switch( c ) {
        case 'a': c = '\a'; break;
        case 'b': c = '\b'; break;
        case 'f': c = '\f'; break;
        case 'n': c = '\n'; break;
        case 'r': c = '\r'; break;
        case 't': c = '\t'; break;
        case 'v': c = '\v'; break;
    }
    return(c);
}


int simplematch( text, pattern, nocase )
char *text, *pattern;
int nocase;
{
  char *retrypat = NULL, *retrytxt;
  unsigned char c,c1,c2;
  int notfound;

  retrytxt = text;
  
  while (*text || *pattern) {
    c = *(pattern++);
    if (nocase) c = tolower(c);

    switch (c) {
      case '*':
      	  retrypat = pattern;
          retrytxt = text;
          break;
      case '[':
          notfound = 1;
          if (*pattern == '^') {
	    notfound = 0;
	    pattern++;
	  }
          while ((c = *(pattern++)) != ']') {
	    if (c == '\\') c = sm_escchar(*(pattern++));
	    if (c == '\0') return(SM_ERROR);
	    if (*pattern == '-') {
	      c1 = c;
	      c2 = *(++pattern);
	      if (c2 == ']' || c2 == '\0' || c1 == '[') return(SM_ERROR);
	      if ((*text >= c1 && *text <= c2) || 
	          (tolower(*text) >= tolower(c1) && 
	           tolower(*text) <= tolower(c2) && nocase)) 
		notfound = !notfound;
	      pattern++;
	    } else if (*text == c || (nocase && tolower(*text) == tolower(c)))
	        notfound = !notfound;
          }
          if (notfound) {
            pattern = retrypat;
            text = ++retrytxt;
          }
      case '?':  
          if (*(text++) == '\0') return(SM_NOMATCH);
          break;
      case '\\':  
          c = sm_escchar(*(pattern++));
          if (c == '\0') return(SM_ERROR);
      default :        
          if (*text == c || (nocase && tolower(*text) == c)) {
            if (*text) text++;
          } else {
            if (*text) {
  	      pattern = retrypat;
	      text = ++retrytxt;
            } else
	      return(SM_NOMATCH);
          }
    }
    
    if( pattern == NULL ) return(SM_NOMATCH);
  }    
  
  return(SM_MATCH);
}


#ifdef SM_TEST
#include <stdio.h>

main()
{
    char s[1000],p[1000];

    while(1) {
        printf("Text   : "); fflush(stdout); scanf("%s",s);
        printf("Pattern: "); fflush(stdout); scanf("%s",p);

        printf("Result case-sensitive  : %d\n",simplematch(s,p,0));   
        printf("Result case-insensitive: %d\n\n",simplematch(s,p,1));
    }
}
#endif


#if !defined(HAVE_STRERROR)
/*
 * strerror - like strerror on many Unices, but not on SunOS 4.1.4
 *
 * INPUT:  int     - error number to look up
 *
 * OUTPUT: none
 *
 * RETURN: string  - string of the error number
 */
char *strerror(int errornumber) {
#if __GLIBC__ < 2 && !defined(BSD)
  extern int sys_nerr;
  extern char *sys_errlist[];
#endif
  static char buffer[40];

  if ((unsigned int) errornumber < sys_nerr) {
    return (char *)(sys_errlist[errornumber]);
  }

  (void) sprintf(buffer, "Unknown error: %d", errornumber);
  return (buffer);
}
#endif


#if !defined(HAVE_SNPRINTF)
  #include "snprintf.c"
#endif


/* sfgetl: secure version of gets() : read a text line from a stream
 * 
 * INPUT: s	- string
 *        z	- size of string
 *        f	- input stream
 * 
 * OUTPUT: s	- read line (correctly terminated with \0)
 * 
 * RETURN: s on success, NULL on error
 * 
 * Remark: if line is longer than z, munch it
 */
char *sfgetl(char *s, int z, FILE *f) {
  char *S;
  int c;

  S=fgets(s,z,f);

  /* munch to EOL or EOF if there are chars pending */
  if (S && *s && s[strlen(s)-1] != '\n') {
    do { 
      c=fgetc(f); 
    } while (c != EOF && c != '\n' && c != 0);
  }
  
  return(S);
}
