/*
Copyright (c) 2000-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file	dkstr.c	String functions module.
*/



#include "dk.h"
#include "dkmem.h"

#if DK_HAVE_STRING_H
#include <string.h>
#endif
#if DK_HAVE_WCHAR_H
#include <wchar.h>
#endif
#if DK_HAVE_CTYPE_H
#include <ctype.h>
#endif


#line 58 "dkstr.ctr"




/**	Inside the dkstr module.
*/
#define DK_STR_C 1

#include "dkstr.h"



int dkstr_casecmp DK_P2(char *, a, char *, b)
{
  int back = 0;
#if DK_HAVE_STRCASECMP
  
  back = strcasecmp(a,b);
#else
#if DK_HAVE_STRICMP || DK_HAVE__STRICMP
  
#if DK_HAVE_STRICMP
  back = stricmp(a,b);
#else
  back = _stricmp(a,b);
#endif
#else
  int ende;
  char aval, bval, *aptr, *bptr;
  
  if(a && b) {
    aptr = a; bptr = b;
    ende = 0;
    while((ende == 0) && (back == 0)) {
      aval = *aptr; bval = *bptr;
      if(aval && bval) {
	if(islower(aval)) aval = toupper(aval);
	if(islower(bval)) bval = toupper(bval);
	if(aval != bval) {
	  ende = 1;
	  if(aval > bval) back = 1;
	  else            back = -1;
	} else {
	  aptr++; bptr++;
	}
      } else {
	ende = 1;
	if(aval) { back = 1; }
	if(bval) { back = -1; }
      }
    }
  }
#endif
#endif
  
  return back;
}



char *dkstr_dup DK_P1(char *, p)
{
  char *back = NULL;
  
  if(p) {
    size_t len;
    len = strlen(p);
    len++;
    back = dk_new(char,len);
    if(back) {
      strcpy(back,p);
    }
  } 
  return back;
}



char *
dkstr_chr	DK_P2(char *, str, int, c)
{
  char *back = NULL;
  
  if(str) {
#if DK_HAVE_STRCHR
    back = strchr(str,c);
#else
    char *ptr;
    ptr = str;
    while((*ptr) && (!back)) {
      if(((int)(*ptr)) == c) {
	back = ptr;
      } else {
        ptr++;
      }
    }
#endif
  } 
  return back;
}



char *
dkstr_rchr       DK_P2(char *, str, int, c)
{
  char *back = NULL;
  if(str) {
#if DK_HAVE_STRRCHR
    back = strrchr(str,c);
#else
    char *ptr;
    ptr = str;
    while(*ptr) {
      if(((int)(*ptr)) == c) { back = ptr; }
      ptr++;
    }
#endif
  }
  return back;
}



/**	The set of whitespace characters.
*/
static char default_whitespace_set[] = { " \t\r\n" };



char *
dkstr_start	DK_P2(char *, str, char *, whsp)
{
  char *back = NULL;
  char *ptr, *wh;
  
  if(str) {
    wh = (whsp ? whsp : default_whitespace_set);
    ptr = str;
    while((*ptr) && (!back)) {
      if(dkstr_chr(wh,((int)(*ptr)))) {
	ptr++;
      } else {
	back = ptr;
      }
    }
  } 
  return back;
}



void
dkstr_chomp	DK_P2(char *, str, char *, whsp)
{
  char *wh;
  char *ptr;
  char *x;
  
  if(str) {
    wh = (whsp ? whsp : default_whitespace_set);
    x = NULL; ptr = str;
    while(*ptr) {
      if(dkstr_chr(wh,((int)(*ptr)))) {
	if(!x) {
	  x = ptr;
	}
      } else {
	x = NULL;
      }
      ptr++;
    }
    if(x) { *x = '\0'; }
  }
  
}



char    *
dkstr_next	DK_P2(char *, str, char *, whsp)
{
  char *back = NULL;
  char *ptr, *wh;
  int state;
  
  if(str) {
    ptr = str;
    wh = (whsp ? whsp : default_whitespace_set);
    state = 0;
    while((state < 2) && (*ptr)) {
      if(dkstr_chr(wh,((int)(*ptr)))) {
	if(state == 1) {
	  state = 2;
	  *(ptr++) = '\0';
	  back = dkstr_start(ptr,wh);
	}
	ptr++;
      } else {
	state = 1;
	ptr++;
      }
    }
  } 
  return back;
}



/**	Keywords to set boolean values.
*/
static char *bool_keywords[] = {
  (char *)"0",
  (char *)"n$o",
  (char *)"of$f",
  (char *)"f$alse",
  (char *)"-",
  (char *)"+",
  (char *)"1",
  (char *)"y$es",
  (char *)"on",
  (char *)"ok",
  (char *)"t$rue",
  NULL
};



int
dkstr_is_bool DK_P1(char *, str)
{
  int back = 0;
  
  if(str) {
    if(dkstr_array_abbr(bool_keywords,str,'$',0) >= 0) {
      back = 1;
    }
  } 
  return back;
}



int
dkstr_is_on DK_P1(char *, str)
{
  int back = 0;
  
  if(str) {
    if(dkstr_array_abbr(&(bool_keywords[5]),str,'$',0) >= 0) {
      back = 1;
    }
  } 
  return back;
}



int
dkstr_array_index DK_P3(char **, array, char *, str, int, cs)
{
  int back = -1;
  char **ptr; int i;
  
  if(array && str) {
    i = 0; ptr = array;
    while((*ptr) && (back == -1)) {
      if(cs) {
	if(strcmp(*ptr,str) == 0) {
	  back = i;
	}
      } else {
	if(dkstr_casecmp(*ptr,str) == 0) {
	  back = i;
	}
      }
      if(back == -1) {
	ptr++; i++;
      }
    }
  } 
  return back;
}



/**	Find length of an array.
	@param	cmd	The array.
	@return	Number of strings in the array.
*/
static
int
array_length DK_P1(char **, cmd)
{
  int back = -1;
  if(cmd) {
    back = 0;
    while(*(cmd++)) {
      back++;
    }
  }
  return back;
}



int
dkstr_find_multi_part_cmd DK_P3(char **, cmd, char ***, cmdset, int, cs)
{
  int back = -1;
  char ***lfdcmdset;
  int l1, l2, i, running, this_is_it;
  if(cmd && cmdset) {
    lfdcmdset = cmdset;
    l1 = array_length(cmd); running = 0;
    while((back == -1) && *lfdcmdset) {
      l2 = array_length(*lfdcmdset);
      if(l1 == l2) {
	this_is_it = 1;
	for(i = 0; i < l1; i++) {
	  if(cs) {
	    if(strcmp(cmd[i], (*lfdcmdset)[i])) {
	      this_is_it = 0;
	    }
	  } else {
	    if(dkstr_casecmp(cmd[i], (*lfdcmdset)[i])) {
	      this_is_it = 0;
	    }
	  }
	}
	if(this_is_it) {
	  back = running;
	}
      }
      lfdcmdset++; running++;
    }
  }
  return back;
}



/**	Wide characters which represent whitespaces.
*/
static wchar_t default_w_whitespace_set[] = {
  (wchar_t)' ', (wchar_t)'\t', (wchar_t)'\r', (wchar_t)'\n',
  (wchar_t)0
};



wchar_t *
dkstr_w_chr DK_P2(wchar_t *, str, wchar_t, c)
{
  wchar_t *back = NULL;
#if !DK_HAVE_WCSCHR
  wchar_t *ptr;
#endif
  if(str) {
#if DK_HAVE_WCSCHR
    back = wcschr(str,c);
#else
    ptr = str;
    while((*ptr) && (!back)) {
      if(*ptr == c) { back = ptr; }
      ptr++;
    }
#endif
  }
  return back;
}



wchar_t *
dkstr_w_rchr DK_P2(wchar_t *, str, wchar_t, c)
{
  wchar_t *back = NULL;
#if !DK_HAVE_WCSRCHR
  wchar_t *ptr;
#endif
  if(str) {
#if DK_HAVE_WCSRCHR
    back = wcsrchr(str,c);
#else
    ptr = str;
    while(*ptr) {
      if(*ptr == c) { back = ptr; }
      ptr++;
    }
#endif
  }
  return back;
}



wchar_t *
dkstr_w_start DK_P2(wchar_t *, str, wchar_t *, whsp)
{
  wchar_t *back = NULL;
  wchar_t *w, *ptr;

  if(str) {
    w = (whsp ? whsp : default_w_whitespace_set);
    ptr = str;
    while((*ptr) && (!back)) {
      if(!dkstr_w_chr(w,*ptr)) {
	back = ptr;
      }
      ptr++;
    }
  }
  return back;
}



void
dkstr_w_chomp DK_P2(wchar_t *, str, wchar_t *, whsp)
{
  wchar_t *back = NULL;
  wchar_t *ptr, *w;
  w = (whsp ? whsp : default_w_whitespace_set);
  ptr = str;
  while(*ptr) {
    if(!dkstr_w_chr(w,*ptr)) {
      back = ptr;
    }
    ptr++;
  }
  if(back) {
    back++;
    *back = (wchar_t)0;
  }
}



wchar_t *
dkstr_w_next DK_P2(wchar_t *, str, wchar_t *, whsp)
{
  wchar_t *back = NULL;
  wchar_t *w, *ptr;
  w = (whsp ? whsp : default_w_whitespace_set);
  ptr = dkstr_w_start(str,w);
  if(ptr) {
    while((*ptr) && (!back)) {
      if(dkstr_w_chr(w,*ptr)) {
	back = ptr;
      }
      ptr++;
    }
    if(back) {
      *(back++) = (wchar_t)0;
      back = dkstr_w_start(back,w);
    }
  }
  return back;
}



int
dkstr_w_casecmp DK_P2(wchar_t *, a, wchar_t *, b)
{
  int back = 0;
  if(a && b) {
#if DK_HAVE_WCSCASECMP && DK_HAVE_WCHAR_H
    back = wcscasecmp(a,b);
#else
#if DK_HAVE_WCSICMP
    back = wcsicmp(a,b);
#else
    int cc;
    wchar_t c1, c2, *ptr1, *ptr2;
    ptr1 = a; ptr2 = b; cc = 1;
    while(cc) {
      if(*ptr1) {
	if(*ptr2) {
	  c1 = *ptr1; c2 = *ptr2;
          if((c1 >= 65) && (c1 <= 90)) {
	    c1 += 32;
	  }
	  if((c2 >= 65) && (c2 <= 90)) {
	    c2 += 32;
	  }
	  if(c1 > c2) {
	    back = 1; cc = 0;
	  } else {
	    if(c1 < c2) {
	      back = -1; cc = 0;
	    } else {
	      ptr1++; ptr2++;
	    }
	  }
	} else {
	  back = 1; cc = 0;
	}
      } else {
	if(*ptr2) {
	  back = -1; cc = 0;
	} else {
	  cc = 0;
	}
      }
    }
#endif
#endif
  } else {
    if(a) {
      back = 1;
    } else {
      back = -1;
    }
  }
  return back;
}



int
dkstr_w_cmp DK_P2(wchar_t *, a, wchar_t *, b)
{
  int back = 0;
#if !DK_HAVE_WCSCMP
  int cc;
  wchar_t c1, c2, *ptr1, *ptr2;
#endif
  if(a && b) {
#if DK_HAVE_WCSCMP
    back = wcscmp(a,b);
#else
    ptr1 = a; ptr2 = b; cc = 1;
    while(cc) {
      if(*ptr1) {
	if(*ptr2) {
	  c1 = *ptr1; c2 = *ptr2;
	  if(c1 > c2) {
	    back = 1; cc = 0;
	  } else {
	    if(c1 < c2) {
	      back = -1; cc = 0;
	    } else {
	      ptr1++; ptr2++;
	    }
	  }
	} else {
	  back = 1; cc = 0;
	}
      } else {
	if(*ptr2) {
	  back = -1; cc = 0;
	} else {
	  cc = 0;
	}
      }
    }
#endif
  } else {
    if(a) {
      back = 1;
    } else {
      back = -1;
    }
  }
  return back;
}



size_t
dkstr_w_len DK_P1(wchar_t *, str)
{
  size_t back = 0;
#if !DK_HAVE_WCSLEN
  wchar_t *ptr;
#endif
  if(str) {
#if DK_HAVE_WCSLEN
    back = wcslen(str);
#else
    ptr = str;
    while(*ptr) {
      ptr++; back++;
    }
#endif
  }
  return back;
}



void
dkstr_w_cpy DK_P2(wchar_t *, d, wchar_t *, s)
{
#if DK_HAVE_WCSCPY
  if(d && s) { wcscpy(d,s); }
#else
  wchar_t *sptr, *dptr;
  if(d && s) {
    dptr = d; sptr = s;
    while(*sptr) {
      *(dptr++) = *(sptr++);
    }
    *dptr = (wchar_t)0;
  }
#endif
}



wchar_t *
dkstr_w_dup DK_P1(wchar_t *, str)
{
  wchar_t *back = NULL;
  size_t lgt;
  if(str) {
    lgt = dkstr_w_len(str); lgt++;
    back = dk_new(wchar_t,lgt);
    if(back) {
      dkstr_w_cpy(back,str);
    }
  }
  return back;
}


size_t
dkstr_explode DK_P4(char **,array,size_t,sz,char *,str,char *,whsp)
{
  size_t back = 0;
  char *wh, *current, *next;
  char **ptr;
  size_t i;
  if(array && (sz > 1) && str) {
    wh = (whsp ? whsp : default_whitespace_set);
    ptr = array; i = sz;
    while(i--) { *(ptr++) = NULL; }
    ptr = array; i = 0;
    current = dkstr_start(str, wh);
    while(current && (i < (sz - 1))) {
      next = dkstr_next(current,wh);
      *(ptr++) = current; i++;
      back++;
      current = next;
    }
  }
  return back;
}



size_t
dkstr_w_explode DK_P4(wchar_t **,array,size_t,sz,wchar_t *,str,wchar_t *,whsp)
{
  size_t back = 0;
  wchar_t *wh, *current, *next;
  wchar_t **ptr;
  size_t i;
  if(array && (sz > 1) && str) {
    wh = (whsp ? whsp : default_w_whitespace_set);
    ptr = array; i = sz;
    while(i--) { *(ptr++) = NULL; }
    ptr = array; i = 0;
    current = dkstr_w_start(str, wh);
    while(current && (i < (sz - 1))) {
      next = dkstr_w_next(current,wh);
      *(ptr++) = current; i++; back++;
      current = next;
    }
  }
  return back;
}



int
dkstr_is_abbr DK_P4(char *,line,char *,pattern,char,spec,int,cs)
{
  int back = 0;
  char cl, cp, *lptr, *pptr; int afterspec, cc;
  
  if(line && pattern) {
    lptr = line; pptr = pattern; afterspec = 0; cc = 1;
    while(cc) {
      if(*pptr) {
        if((!afterspec) && (*pptr == spec)) {
	  afterspec = 1; pptr++;
	} else {
	  if(*lptr) {
	    cl = *lptr; cp = *pptr;
	    if(!cs) {
	      if(islower(cl)) cl = toupper(cl);
	      if(islower(cp)) cp = toupper(cp);
	    }
	    if(cl == cp) {
	      lptr++; pptr++;
	    } else {
	      cc = 0; back = 0;
	    }
	  } else {
	    cc = 0;
	    if(afterspec) back = 1;
	  }
	}
      } else {
        cc = 0;
	if(!(*lptr)) {
	  back = 1;
	}
      }
    }
  }
  
  return back;
}



int
dkstr_array_abbr DK_P4(char **, array, char *, str, char, sp, int, cs)
{
  int back = -1;
  char **ptr; int i;
  
  if(array && str) {
    i = 0; ptr = array;
    while((*ptr) && (back == -1)) {
      
      if(dkstr_is_abbr(str,*ptr,sp,cs)) {
        back = i;
      }
      if(back == -1) {
	ptr++; i++;
      }
      
    }
  } 
  return back;
}



int
dkstr_find_multi_part_abbr DK_P4(char **,cmd,char ***,cmdset,char,s,int,cs)
{
  int back = -1;
  char ***lfdcmdset;
  int l1, l2, i, running, this_is_it;
  if(cmd && cmdset) {
    lfdcmdset = cmdset;
    l1 = array_length(cmd); running = 0;
    while((back == -1) && *lfdcmdset) {
      l2 = array_length(*lfdcmdset);
      if(l1 == l2) {
	this_is_it = 1;
	for(i = 0; i < l1; i++) {
	  if(!dkstr_is_abbr(cmd[i],(*lfdcmdset)[i],s,cs)) {
	    this_is_it = 0;
	  }
	}
	if(this_is_it) {
	  back = running;
	}
      }
      lfdcmdset++; running++;
    }
  }
  return back;
}



/**	Characters allowed as first character of an identifier.
*/
static char id_start[] = {
"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
};



/**	Characters allowed after the first character of an identifier.
*/
static char id_cont[] = {
"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
};



int
dkstr_is_identifier DK_P1(char *,s)
{
  int back = 0;
  char *ptr;
  
  if(s) {
    ptr = s;
    if(dkstr_chr(id_start, *ptr)) {
      back = 1; ptr++;
      while(back && (*ptr)) {
	if(!dkstr_chr(id_cont, *ptr)) {
	  back = 0;
	}
	ptr++;
      }
    }
  } 
  return back;
}



char *
dkstr_unquote DK_P2(char *,str,char *,quo)
{
  char *back = NULL;
  char c, *ptr;
  
  if(str) {
    back = str;
    if(dkstr_chr(quo, *back)) {
      c = *(back++);
      ptr = dkstr_rchr(back, c);
      if(ptr) {
	*ptr = '\0';
      }
    }
  } 
  return back;
}



