/* 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= 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> 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; }