1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/lib/util_str.c

965 lines
24 KiB
C
Raw Normal View History

/*
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 "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 (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 no length check is performed
****************************************************************************/
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 || !s) return;
ls = (ssize_t)strlen(s);
lp = (ssize_t)strlen(pattern);
li = (ssize_t)strlen(insert);
if (!*pattern) return;
while (lp <= ls && (p = strstr(s,pattern))) {
if (len && (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 allows for any character to be substituted.
Use with caution!
if len==0 then no length check is performed
****************************************************************************/
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;
while (lp <= ls && (p = strstr(s,pattern))) {
if (len && (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.
len is the number of bytes, not chars
similar to string_sub() but allows for any character to be substituted.
Use with caution!
if len==0 then no length check is performed
****************************************************************************/
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, *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) {
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)
{
/* 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);
}
/*******************************************************************
convert a string to upper case
********************************************************************/
void strupper_m(char *s)
{
/* 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);
}
/*
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;
}