/* 
   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, const 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, const 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, const 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(const 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, deallocating any existing space, and allocing the space
 for the string
****************************************************************************/

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 may 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 allocated 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 re-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 elements of the list match 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;
}