/* 
   Unix SMB/CIFS implementation.
   Samba utility functions
   Copyright (C) Andrew Tridgell 1992-2001
   Copyright (C) Simo Sorce      2001-2002
   
   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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"

/****************************************************************************
  Get the next token from a string, return False if none found
  handles double-quotes. 
Based on a routine by GJC@VILLAGE.COM. 
Extensively modified by Andrew.Tridgell@anu.edu.au
****************************************************************************/
BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
{
	char *s;
	BOOL quoted;
	size_t len=1;

	if (!ptr) return(False);

	s = *ptr;

	/* default to simple separators */
	if (!sep) sep = " \t\n\r";

	/* find the first non sep char */
	while (*s && strchr_m(sep,*s)) s++;
	
	/* nothing left? */
	if (! *s) return(False);
	
	/* copy over the token */
	for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) {
		if (*s == '\"') {
			quoted = !quoted;
		} else {
			len++;
			*buff++ = *s;
		}
	}
	
	*ptr = (*s) ? s+1 : s;  
	*buff = 0;
	
	return(True);
}



/****************************************************************************
This is like next_token but is not re-entrant and "remembers" the first 
parameter so you can pass NULL. This is useful for user interface code
but beware the fact that it is not re-entrant!
****************************************************************************/
static char *last_ptr=NULL;

BOOL next_token_nr(char **ptr,char *buff,char *sep, size_t bufsize)
{
	BOOL ret;
	if (!ptr) ptr = &last_ptr;

	ret = next_token(ptr, buff, sep, bufsize);
	last_ptr = *ptr;
	return ret;	
}

static uint16 tmpbuf[sizeof(pstring)];

void set_first_token(char *ptr)
{
	last_ptr = ptr;
}


/****************************************************************************
Convert list of tokens to array; dependent on above routine.
Uses last_ptr from above - bit of a hack.
****************************************************************************/
char **toktocliplist(int *ctok, char *sep)
{
	char *s=last_ptr;
	int ictok=0;
	char **ret, **iret;

	if (!sep) sep = " \t\n\r";

	while(*s && strchr_m(sep,*s)) s++;

	/* nothing left? */
	if (!*s) return(NULL);

	do {
		ictok++;
		while(*s && (!strchr_m(sep,*s))) s++;
		while(*s && strchr_m(sep,*s)) *s++=0;
	} while(*s);
	
	*ctok=ictok;
	s=last_ptr;
	
	if (!(ret=iret=malloc(ictok*sizeof(char *)))) return NULL;
	
	while(ictok--) {    
		*iret++=s;
		while(*s++);
		while(!*s) s++;
	}

	return ret;
}

/*******************************************************************
  case insensitive string compararison
********************************************************************/
int StrCaseCmp(const char *s, const char *t)
{
	pstring buf1, buf2;
	unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1));
	unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2));
	return strcmp(buf1,buf2);
}

/*******************************************************************
  case insensitive string compararison, length limited
********************************************************************/
int StrnCaseCmp(const char *s, const char *t, size_t n)
{
	pstring buf1, buf2;
	unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1));
	unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2));
	return strncmp(buf1,buf2,n);
}

/*******************************************************************
  compare 2 strings 
********************************************************************/
BOOL strequal(const char *s1, const char *s2)
{
	if (s1 == s2) return(True);
	if (!s1 || !s2) return(False);
  
	return(StrCaseCmp(s1,s2)==0);
}

/*******************************************************************
  compare 2 strings up to and including the nth char.
  ******************************************************************/
BOOL strnequal(const char *s1,const char *s2,size_t n)
{
  if (s1 == s2) return(True);
  if (!s1 || !s2 || !n) return(False);
  
  return(StrnCaseCmp(s1,s2,n)==0);
}

/*******************************************************************
  compare 2 strings (case sensitive)
********************************************************************/
BOOL strcsequal(const char *s1,const char *s2)
{
  if (s1 == s2) return(True);
  if (!s1 || !s2) return(False);
  
  return(strcmp(s1,s2)==0);
}

/***************************************************************************
Do a case-insensitive, whitespace-ignoring string compare.
***************************************************************************/
int strwicmp(const char *psz1, const char *psz2)
{
	/* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
	/* appropriate value. */
	if (psz1 == psz2)
		return (0);
	else if (psz1 == NULL)
		return (-1);
	else if (psz2 == NULL)
		return (1);

	/* sync the strings on first non-whitespace */
	while (1)
	{
		while (isspace((int)*psz1))
			psz1++;
		while (isspace((int)*psz2))
			psz2++;
		if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0'
		    || *psz2 == '\0')
			break;
		psz1++;
		psz2++;
	}
	return (*psz1 - *psz2);
}


/* Convert a string to upper case, but don't modify it */

char *strupper_static(const char *s)
{
	static pstring str;

	pstrcpy(str, s);
	strupper(str);

	return str;
}

/*******************************************************************
  convert a string to "normal" form
********************************************************************/
void strnorm(char *s)
{
  extern int case_default;
  if (case_default == CASE_UPPER)
    strupper(s);
  else
    strlower(s);
}

/*******************************************************************
check if a string is in "normal" case
********************************************************************/
BOOL strisnormal(char *s)
{
	extern int case_default;
	if (case_default == CASE_UPPER)
		return(!strhaslower(s));
	
	return(!strhasupper(s));
}


/****************************************************************************
  string replace
  NOTE: oldc and newc must be 7 bit characters
****************************************************************************/
void string_replace(char *s,char oldc,char newc)
{
	push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
	string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc));
	pull_ucs2(NULL, s, tmpbuf, -1, sizeof(tmpbuf), STR_TERMINATE);
}


/*******************************************************************
skip past some strings in a buffer
********************************************************************/
char *skip_string(char *buf,size_t n)
{
	while (n--)
		buf += strlen(buf) + 1;
	return(buf);
}

/*******************************************************************
 Count the number of characters in a string. Normally this will
 be the same as the number of bytes in a string for single byte strings,
 but will be different for multibyte.
********************************************************************/
size_t str_charnum(const char *s)
{
	push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
	return strlen_w(tmpbuf);
}

/*******************************************************************
trim the specified elements off the front and back of a string
********************************************************************/

BOOL trim_string(char *s,const char *front,const char *back)
{
	BOOL ret = False;
	size_t front_len;
	size_t back_len;
	size_t len;

	/* Ignore null or empty strings. */
	if (!s || (s[0] == '\0'))
		return False;

	front_len	= front? strlen(front) : 0;
	back_len	= back? strlen(back) : 0;

	len = strlen(s);

	if (front_len) {
		while (len && strncmp(s, front, front_len)==0) {
			memcpy(s, s+front_len, (len-front_len)+1);
			len -= front_len;
			ret=True;
		}
	}
	
	if (back_len) {
		while ((len >= back_len) && strncmp(s+len-back_len,back,back_len)==0) {
			s[len-back_len]='\0';
			len -= back_len;
			ret=True;
		}
	}
	return ret;
}


/****************************************************************************
does a string have any uppercase chars in it?
****************************************************************************/
BOOL strhasupper(const char *s)
{
	smb_ucs2_t *ptr;
	push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
	for(ptr=tmpbuf;*ptr;ptr++)
		if(isupper_w(*ptr)) return True;
	return(False);
}

/****************************************************************************
does a string have any lowercase chars in it?
****************************************************************************/
BOOL strhaslower(const char *s)
{
	smb_ucs2_t *ptr;
	push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
	for(ptr=tmpbuf;*ptr;ptr++)
		if(islower_w(*ptr)) return True;
	return(False);
}

/****************************************************************************
find the number of 'c' chars in a string
****************************************************************************/
size_t count_chars(const char *s,char c)
{
	smb_ucs2_t *ptr;
	int count;
	push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
	for(count=0,ptr=tmpbuf;*ptr;ptr++) if(*ptr==UCS2_CHAR(c)) count++;
	return(count);
}

/*******************************************************************
Return True if a string consists only of one particular character.
********************************************************************/

BOOL str_is_all(const char *s,char c)
{
	smb_ucs2_t *ptr;

	if(s == NULL) return False;
	if(!*s) return False;
  
	push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
	for(ptr=tmpbuf;*ptr;ptr++) if(*ptr!=UCS2_CHAR(c)) return False;

	return True;
}

/*******************************************************************
safe string copy into a known length string. maxlength does not
include the terminating zero.
********************************************************************/

char *safe_strcpy(char *dest,const char *src, size_t maxlength)
{
	size_t len;

	if (!dest) {
		DEBUG(0,("ERROR: NULL dest in safe_strcpy\n"));
		return NULL;
	}

	if (!src) {
		*dest = 0;
		return dest;
	}  

	len = strlen(src);

	if (len > maxlength) {
		DEBUG(0,("ERROR: string overflow by %d in safe_strcpy [%.50s]\n",
			 (int)(len-maxlength), src));
		len = maxlength;
	}
      
	memmove(dest, src, len);
	dest[len] = 0;
	return dest;
}  

/*******************************************************************
safe string cat into a string. maxlength does not
include the terminating zero.
********************************************************************/

char *safe_strcat(char *dest, const char *src, size_t maxlength)
{
	size_t src_len, dest_len;

	if (!dest) {
		DEBUG(0,("ERROR: NULL dest in safe_strcat\n"));
		return NULL;
	}

	if (!src) {
		return dest;
	}  
	
	src_len = strlen(src);
	dest_len = strlen(dest);
	
	if (src_len + dest_len > maxlength) {
		DEBUG(0,("ERROR: string overflow by %d in safe_strcat [%.50s]\n",
			 (int)(src_len + dest_len - maxlength), src));
		src_len = maxlength - dest_len;
	}
	
	memcpy(&dest[dest_len], src, src_len);
	dest[dest_len + src_len] = 0;
	return dest;
}

/*******************************************************************
 Paranoid strcpy into a buffer of given length (includes terminating
 zero. Strips out all but 'a-Z0-9' and the character in other_safe_chars
 and replaces with '_'. Deliberately does *NOT* check for multibyte
 characters. Don't change it !
********************************************************************/

char *alpha_strcpy(char *dest, const char *src, const char *other_safe_chars, size_t maxlength)
{
	size_t len, i;

	if (!dest) {
		DEBUG(0,("ERROR: NULL dest in alpha_strcpy\n"));
		return NULL;
	}

	if (!src) {
		*dest = 0;
		return dest;
	}  

	len = strlen(src);
	if (len >= maxlength)
		len = maxlength - 1;

	if (!other_safe_chars)
		other_safe_chars = "";

	for(i = 0; i < len; i++) {
		int val = (src[i] & 0xff);
		if(isupper(val) || islower(val) || isdigit(val) || strchr_m(other_safe_chars, val))
			dest[i] = src[i];
		else
			dest[i] = '_';
	}

	dest[i] = '\0';

	return dest;
}

/****************************************************************************
 Like strncpy but always null terminates. Make sure there is room!
 The variable n should always be one less than the available size.
****************************************************************************/

char *StrnCpy(char *dest,const char *src,size_t n)
{
	char *d = dest;
	if (!dest) return(NULL);
	if (!src) {
		*dest = 0;
		return(dest);
	}
	while (n-- && (*d++ = *src++)) ;
	*d = 0;
	return(dest);
}

/****************************************************************************
like strncpy but copies up to the character marker.  always null terminates.
returns a pointer to the character marker in the source string (src).
****************************************************************************/
char *strncpyn(char *dest, const char *src,size_t n, char c)
{
	char *p;
	size_t str_len;

	p = strchr_m(src, c);
	if (p == NULL)
	{
		DEBUG(5, ("strncpyn: separator character (%c) not found\n", c));
		return NULL;
	}

	str_len = PTR_DIFF(p, src);
	strncpy(dest, src, MIN(n, str_len));
	dest[str_len] = '\0';

	return p;
}


/*************************************************************
 Routine to get hex characters and turn them into a 16 byte array.
 the array can be variable length, and any non-hex-numeric
 characters are skipped.  "0xnn" or "0Xnn" is specially catered
 for.

 valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"

**************************************************************/
size_t strhex_to_str(char *p, size_t len, const char *strhex)
{
	size_t i;
	size_t num_chars = 0;
	unsigned char   lonybble, hinybble;
	char           *hexchars = "0123456789ABCDEF";
	char           *p1 = NULL, *p2 = NULL;

	for (i = 0; i < len && strhex[i] != 0; i++)
	{
		if (strnequal(hexchars, "0x", 2))
		{
			i++; /* skip two chars */
			continue;
		}

		if (!(p1 = strchr_m(hexchars, toupper(strhex[i]))))
		{
			break;
		}

		i++; /* next hex digit */

		if (!(p2 = strchr_m(hexchars, toupper(strhex[i]))))
		{
			break;
		}

		/* get the two nybbles */
		hinybble = PTR_DIFF(p1, hexchars);
		lonybble = PTR_DIFF(p2, hexchars);

		p[num_chars] = (hinybble << 4) | lonybble;
		num_chars++;

		p1 = NULL;
		p2 = NULL;
	}
	return num_chars;
}

/****************************************************************************
check if a string is part of a list
****************************************************************************/
BOOL in_list(char *s,char *list,BOOL casesensitive)
{
  pstring tok;
  char *p=list;

  if (!list) return(False);

  while (next_token(&p,tok,LIST_SEP,sizeof(tok))) {
    if (casesensitive) {
      if (strcmp(tok,s) == 0)
        return(True);
    } else {
      if (StrCaseCmp(tok,s) == 0)
        return(True);
    }
  }
  return(False);
}

/* this is used to prevent lots of mallocs of size 1 */
static char *null_string = NULL;

/****************************************************************************
set a string value, allocing the space for the string
****************************************************************************/
static BOOL string_init(char **dest,const char *src)
{
  size_t l;
  if (!src)     
    src = "";

  l = strlen(src);

  if (l == 0)
    {
      if (!null_string) {
        if((null_string = (char *)malloc(1)) == NULL) {
          DEBUG(0,("string_init: malloc fail for null_string.\n"));
          return False;
        }
        *null_string = 0;
      }
      *dest = null_string;
    }
  else
    {
      (*dest) = (char *)malloc(l+1);
      if ((*dest) == NULL) {
	      DEBUG(0,("Out of memory in string_init\n"));
	      return False;
      }

      pstrcpy(*dest,src);
    }
  return(True);
}

/****************************************************************************
free a string value
****************************************************************************/
void string_free(char **s)
{
  if (!s || !(*s)) return;
  if (*s == null_string)
    *s = NULL;
  SAFE_FREE(*s);
}

/****************************************************************************
set a string value, allocing the space for the string, and deallocating any 
existing space
****************************************************************************/
BOOL string_set(char **dest,const char *src)
{
  string_free(dest);

  return(string_init(dest,src));
}


/****************************************************************************
substitute a string for a pattern in another string. Make sure there is 
enough room!

This routine looks for pattern in s and replaces it with 
insert. It may do multiple replacements.

any of " ; ' $ or ` in the insert string are replaced with _
if len==0 then the string cannot be extended. This is different from the old
use of len==0 which was for no length checks to be done.
****************************************************************************/

void string_sub(char *s,const char *pattern, const char *insert, size_t len)
{
	char *p;
	ssize_t ls,lp,li, i;

	if (!insert || !pattern || !*pattern || !s)
		return;

	ls = (ssize_t)strlen(s);
	lp = (ssize_t)strlen(pattern);
	li = (ssize_t)strlen(insert);

	if (len == 0)
		len = ls + 1; /* len is number of *bytes* */

	while (lp <= ls && (p = strstr(s,pattern))) {
		if (ls + (li-lp) >= len) {
			DEBUG(0,("ERROR: string overflow by %d in string_sub(%.50s, %d)\n", 
				 (int)(ls + (li-lp) - len),
				 pattern, (int)len));
			break;
		}
		if (li != lp) {
			memmove(p+li,p+lp,strlen(p+lp)+1);
		}
		for (i=0;i<li;i++) {
			switch (insert[i]) {
			case '`':
			case '"':
			case '\'':
			case ';':
			case '$':
			case '%':
			case '\r':
			case '\n':
				p[i] = '_';
				break;
			default:
				p[i] = insert[i];
			}
		}
		s = p + li;
		ls += (li-lp);
	}
}

void fstring_sub(char *s,const char *pattern,const char *insert)
{
	string_sub(s, pattern, insert, sizeof(fstring));
}

void pstring_sub(char *s,const char *pattern,const char *insert)
{
	string_sub(s, pattern, insert, sizeof(pstring));
}

/* similar to string_sub, but it will accept only allocated strings
 * and may realloc them so pay attention at what you pass on no
 * pointers inside strings, no pstrings or const must be passed
 * as string.
 */

char *realloc_string_sub(char *string, const char *pattern, const char *insert)
{
	char *p, *in;
	char *s;
	ssize_t ls,lp,li,ld, i;

	if (!insert || !pattern || !*pattern || !string || !*string)
		return NULL;

	s = string;

	in = strdup(insert);
	if (!in) {
		DEBUG(0, ("realloc_string_sub: out of memory!\n"));
		return NULL;
	}
	ls = (ssize_t)strlen(s);
	lp = (ssize_t)strlen(pattern);
	li = (ssize_t)strlen(insert);
	ld = li - lp;
	for (i=0;i<li;i++) {
		switch (in[i]) {
			case '`':
			case '"':
			case '\'':
			case ';':
			case '$':
			case '%':
			case '\r':
			case '\n':
				in[i] = '_';
			default:
				/* ok */
				break;
		}
	}
	
	while ((p = strstr(s,pattern))) {
		if (ld > 0) {
			char *t = Realloc(string, ls + ld + 1);
			if (!t) {
				DEBUG(0, ("realloc_string_sub: out of memory!\n"));
				SAFE_FREE(in);
				return NULL;
			}
			string = t;
			p = t + (p - s);
		}
		if (li != lp) {
			memmove(p+li,p+lp,strlen(p+lp)+1);
		}
		memcpy(p, in, li);
		s = p + li;
		ls += ld;
	}
	SAFE_FREE(in);
	return string;
}

/****************************************************************************
similar to string_sub() but allows for any character to be substituted. 
Use with caution!
if len==0 then the string cannot be extended. This is different from the old
use of len==0 which was for no length checks to be done.
****************************************************************************/

void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
{
	char *p;
	ssize_t ls,lp,li;

	if (!insert || !pattern || !s)
		return;

	ls = (ssize_t)strlen(s);
	lp = (ssize_t)strlen(pattern);
	li = (ssize_t)strlen(insert);

	if (!*pattern)
		return;
	
	if (len == 0)
		len = ls + 1; /* len is number of *bytes* */
	
	while (lp <= ls && (p = strstr(s,pattern))) {
		if (ls + (li-lp) >= len) {
			DEBUG(0,("ERROR: string overflow by %d in all_string_sub(%.50s, %d)\n", 
				 (int)(ls + (li-lp) - len),
				 pattern, (int)len));
			break;
		}
		if (li != lp) {
			memmove(p+li,p+lp,strlen(p+lp)+1);
		}
		memcpy(p, insert, li);
		s = p + li;
		ls += (li-lp);
	}
}

/****************************************************************************
similar to all_string_sub but for unicode strings.
return a new allocate unicode string.
  similar to string_sub() but allows for any character to be substituted.
  Use with caution!
****************************************************************************/

smb_ucs2_t *all_string_sub_w(const smb_ucs2_t *s, const smb_ucs2_t *pattern,
				const smb_ucs2_t *insert)
{
	smb_ucs2_t *r, *rp;
	const smb_ucs2_t *sp;
	size_t	lr, lp, li, lt;

	if (!insert || !pattern || !*pattern || !s) return NULL;

	lt = (size_t)strlen_w(s);
	lp = (size_t)strlen_w(pattern);
	li = (size_t)strlen_w(insert);

	if (li > lp) {
		const smb_ucs2_t *st = s;
		int ld = li - lp;
		while ((sp = strstr_w(st, pattern))) {
			st = sp + lp;
			lt += ld;
		}
	}

	r = rp = (smb_ucs2_t *)malloc((lt + 1)*(sizeof(smb_ucs2_t)));
	if (!r) {
		DEBUG(0, ("all_string_sub_w: out of memory!\n"));
		return NULL;
	}

	while ((sp = strstr_w(s, pattern))) {
		memcpy(rp, s, (sp - s));
		rp += ((sp - s) / sizeof(smb_ucs2_t));
		memcpy(rp, insert, (li * sizeof(smb_ucs2_t)));
		s = sp + lp;
		rp += li;
	}
	lr = ((rp - r) / sizeof(smb_ucs2_t));
	if (lr < lt) {
		memcpy(rp, s, ((lt - lr) * sizeof(smb_ucs2_t)));
		rp += (lt - lr);
	}
	*rp = 0;

	return r;
}

smb_ucs2_t *all_string_sub_wa(smb_ucs2_t *s, const char *pattern,
					     const char *insert)
{
	wpstring p, i;

	if (!insert || !pattern || !s) return NULL;
	push_ucs2(NULL, p, pattern, sizeof(wpstring) - 1, STR_TERMINATE);
	push_ucs2(NULL, i, insert, sizeof(wpstring) - 1, STR_TERMINATE);
	return all_string_sub_w(s, p, i);
}

/****************************************************************************
 Splits out the front and back at a separator.
****************************************************************************/

void split_at_last_component(char *path, char *front, char sep, char *back)
{
	char *p = strrchr_m(path, sep);

	if (p != NULL)
		*p = 0;

	if (front != NULL)
		pstrcpy(front, path);

	if (p != NULL) {
		if (back != NULL)
			pstrcpy(back, p+1);
		*p = '\\';
	} else {
		if (back != NULL)
			back[0] = 0;
	}
}

/****************************************************************************
 Write an octal as a string.
****************************************************************************/

char *octal_string(int i)
{
	static char ret[64];
	if (i == -1)
		return "-1";
	slprintf(ret, sizeof(ret)-1, "0%o", i);
	return ret;
}


/****************************************************************************
 Truncate a string at a specified length.
****************************************************************************/

char *string_truncate(char *s, int length)
{
	if (s && strlen(s) > length)
		s[length] = 0;
	return s;
}

/****************************************************************************
 Strchr and strrchr_m are very hard to do on general multi-byte strings. 
 We convert via ucs2 for now.
****************************************************************************/

char *strchr_m(const char *s, char c)
{
	wpstring ws;
	pstring s2;
	smb_ucs2_t *p;

	push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
	p = strchr_w(ws, UCS2_CHAR(c));
	if (!p)
		return NULL;
	*p = 0;
	pull_ucs2_pstring(s2, ws);
	return (char *)(s+strlen(s2));
}

char *strrchr_m(const char *s, char c)
{
	wpstring ws;
	pstring s2;
	smb_ucs2_t *p;

	push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
	p = strrchr_w(ws, UCS2_CHAR(c));
	if (!p)
		return NULL;
	*p = 0;
	pull_ucs2_pstring(s2, ws);
	return (char *)(s+strlen(s2));
}

/*******************************************************************
 Convert a string to lower case.
********************************************************************/

void strlower_m(char *s)
{
	/* this is quite a common operation, so we want it to be
	   fast. We optimise for the ascii case, knowing that all our
	   supported multi-byte character sets are ascii-compatible
	   (ie. they match for the first 128 chars) */

	while (*s && !(((unsigned char)s[0]) & 0x7F))
		*s++ = tolower((unsigned char)*s);

	if (!*s)
		return;

	/* I assume that lowercased string takes the same number of bytes
	 * as source string even in UTF-8 encoding. (VIV) */
	unix_strlower(s,strlen(s)+1,s,strlen(s)+1);	
}

/*******************************************************************
 Duplicate convert a string to lower case.
********************************************************************/

char *strdup_lower(const char *s)
{
	char *t = strdup(s);
	if (t == NULL) {
		DEBUG(0, ("strdup_lower: Out of memory!\n"));
		return NULL;
	}
	strlower_m(t);
	return t;
}

/*******************************************************************
 Convert a string to upper case.
********************************************************************/

void strupper_m(char *s)
{
	/* this is quite a common operation, so we want it to be
	   fast. We optimise for the ascii case, knowing that all our
	   supported multi-byte character sets are ascii-compatible
	   (ie. they match for the first 128 chars) */

	while (*s && !(((unsigned char)s[0]) & 0x7F))
		*s++ = toupper((unsigned char)*s);

	if (!*s)
		return;

	/* I assume that lowercased string takes the same number of bytes
	 * as source string even in multibyte encoding. (VIV) */
	unix_strupper(s,strlen(s)+1,s,strlen(s)+1);	
}

/*******************************************************************
 Convert a string to upper case.
********************************************************************/

char *strdup_upper(const char *s)
{
	char *t = strdup(s);
	if (t == NULL) {
		DEBUG(0, ("strdup_upper: Out of memory!\n"));
		return NULL;
	}
	strupper_m(t);
	return t;
}

/*
  return a RFC2254 binary string representation of a buffer
  used in LDAP filters
  caller must free
*/
char *binary_string(char *buf, int len)
{
	char *s;
	int i, j;
	const char *hex = "0123456789ABCDEF";
	s = malloc(len * 3 + 1);
	if (!s)
		return NULL;
	for (j=i=0;i<len;i++) {
		s[j] = '\\';
		s[j+1] = hex[((unsigned char)buf[i]) >> 4];
		s[j+2] = hex[((unsigned char)buf[i]) & 0xF];
		j += 3;
	}
	s[j] = 0;
	return s;
}

/* Just a typesafety wrapper for snprintf into a pstring */

int pstr_sprintf(pstring s, const char *fmt, ...)
{
	va_list ap;
	int ret;

	va_start(ap, fmt);
	ret = vsnprintf(s, PSTRING_LEN, fmt, ap);
	va_end(ap);
	return ret;
}

/* Just a typesafety wrapper for snprintf into a fstring */

int fstr_sprintf(fstring s, const char *fmt, ...)
{
	va_list ap;
	int ret;

	va_start(ap, fmt);
	ret = vsnprintf(s, FSTRING_LEN, fmt, ap);
	va_end(ap);
	return ret;
}

#ifndef HAVE_STRNDUP
/*******************************************************************
 Some platforms don't have strndup.
********************************************************************/

 char *strndup(const char *s, size_t n)
{
	char *ret;
	
	n = strnlen(s, n);
	ret = malloc(n+1);
	if (!ret)
		return NULL;
	memcpy(ret, s, n);
	ret[n] = 0;

	return ret;
}
#endif

#ifndef HAVE_STRNLEN
/*******************************************************************
some platforms don't have strnlen
********************************************************************/
 size_t strnlen(const char *s, size_t n)
{
	int i;
	for (i=0; s[i] && i<n; i++)
		/* noop */ ;
	return i;
}
#endif

/***********************************************************
 List of Strings manipulation functions
***********************************************************/

#define S_LIST_ABS 16 /* List Allocation Block Size */

char **str_list_make(const char *string, const char *sep)
{
	char **list, **rlist;
	char *str, *s;
	int num, lsize;
	pstring tok;
	
	if (!string || !*string)
		return NULL;
	s = strdup(string);
	if (!s) {
		DEBUG(0,("str_list_make: Unable to allocate memory"));
		return NULL;
	}
	if (!sep) sep = LIST_SEP;
	
	num = lsize = 0;
	list = NULL;
	
	str = s;
	while (next_token(&str, tok, sep, sizeof(tok))) {		
		if (num == lsize) {
			lsize += S_LIST_ABS;
			rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
			if (!rlist) {
				DEBUG(0,("str_list_make: Unable to allocate memory"));
				str_list_free(&list);
				SAFE_FREE(s);
				return NULL;
			}
			else list = rlist;
			memset (&list[num], 0, ((sizeof(char**)) * (S_LIST_ABS +1)));
		}
		
		list[num] = strdup(tok);
		if (!list[num]) {
			DEBUG(0,("str_list_make: Unable to allocate memory"));
			str_list_free(&list);
			SAFE_FREE(s);
			return NULL;
		}
	
		num++;	
	}
	
	SAFE_FREE(s);
	return list;
}

BOOL str_list_copy(char ***dest, char **src)
{
	char **list, **rlist;
	int num, lsize;
	
	*dest = NULL;
	if (!src)
		return False;
	
	num = lsize = 0;
	list = NULL;
		
	while (src[num]) {
		if (num == lsize) {
			lsize += S_LIST_ABS;
			rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
			if (!rlist) {
				DEBUG(0,("str_list_copy: Unable to allocate memory"));
				str_list_free(&list);
				return False;
			}
			else list = rlist;
			memset (&list[num], 0, ((sizeof(char **)) * (S_LIST_ABS +1)));
		}
		
		list[num] = strdup(src[num]);
		if (!list[num]) {
			DEBUG(0,("str_list_copy: Unable to allocate memory"));
			str_list_free(&list);
			return False;
		}

		num++;
	}
	
	*dest = list;
	return True;	
}

/* return true if all the elemnts of the list matches exactly */

BOOL str_list_compare(char **list1, char **list2)
{
	int num;
	
	if (!list1 || !list2)
		return (list1 == list2); 
	
	for (num = 0; list1[num]; num++) {
		if (!list2[num])
			return False;
		if (!strcsequal(list1[num], list2[num]))
			return False;
	}
	if (list2[num])
		return False; /* if list2 has more elements than list1 fail */
	
	return True;
}

void str_list_free(char ***list)
{
	char **tlist;
	
	if (!list || !*list)
		return;
	tlist = *list;
	for(; *tlist; tlist++)
		SAFE_FREE(*tlist);
	SAFE_FREE(*list);
}

BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
{
	char *p, *s, *t;
	ssize_t ls, lp, li, ld, i, d;

	if (!list)
		return False;
	if (!pattern)
		return False;
	if (!insert)
		return False;

	lp = (ssize_t)strlen(pattern);
	li = (ssize_t)strlen(insert);
	ld = li -lp;
			
	while (*list) {
		s = *list;
		ls = (ssize_t)strlen(s);

		while ((p = strstr(s, pattern))) {
			t = *list;
			d = p -t;
			if (ld) {
				t = (char *) malloc(ls +ld +1);
				if (!t) {
					DEBUG(0,("str_list_substitute: Unable to allocate memory"));
					return False;
				}
				memcpy(t, *list, d);
				memcpy(t +d +li, p +lp, ls -d -lp +1);
				SAFE_FREE(*list);
				*list = t;
				ls += ld;
				s = t +d +li;
			}
			
			for (i = 0; i < li; i++) {
				switch (insert[i]) {
					case '`':
					case '"':
					case '\'':
					case ';':
					case '$':
					case '%':
					case '\r':
					case '\n':
						t[d +i] = '_';
						break;
					default:
						t[d +i] = insert[i];
				}
			}	
		}
		
		list++;
	}
	
	return True;
}