Merge fixes and tests for jra's broken strstr_m() function from 3.0
[samba.git] / source / lib / util_str.c
index 2224a24ab31375609916c2917a42d3eb7fda366d..b8cf052862f3e07dd55992ad848f688f50903372 100644 (file)
@@ -1,8 +1,10 @@
 /* 
    Unix SMB/CIFS implementation.
    Samba utility functions
+   
    Copyright (C) Andrew Tridgell 1992-2001
    Copyright (C) Simo Sorce      2001-2002
+   Copyright (C) Martin Pool     2003
    
    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
 
 #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
-****************************************************************************/
+/**
+ * @file
+ * @brief String utilities.
+ **/
 
+/**
+ * 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(const char **ptr,char *buff, const char *sep, size_t bufsize)
 {
-       const char *s;
+       char *s;
+       char *pbuf;
        BOOL quoted;
        size_t len=1;
 
        if (!ptr)
                return(False);
 
-       s = *ptr;
+       s = (char *)*ptr;
 
        /* default to simple separators */
        if (!sep)
@@ -52,34 +60,35 @@ BOOL next_token(const char **ptr,char *buff, const char *sep, size_t bufsize)
                return(False);
        
        /* copy over the token */
+       pbuf = buff;
        for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) {
-               if (*s == '\"') {
+               if (*s == '\"' || *s == '\'') {
                        quoted = !quoted;
                } else {
                        len++;
-                       *buff++ = *s;
+                       *pbuf++ = *s;
                }
        }
        
        *ptr = (*s) ? s+1 : s;  
-       *buff = 0;
+       *pbuf = 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;
+static const char *last_ptr=NULL;
 
 BOOL next_token_nr(const char **ptr,char *buff, const char *sep, size_t bufsize)
 {
        BOOL ret;
        if (!ptr)
-               ptr = (const char **)&last_ptr;
+               ptr = &last_ptr;
 
        ret = next_token(ptr, buff, sep, bufsize);
        last_ptr = *ptr;
@@ -93,14 +102,14 @@ 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;
+       char *s=(char *)last_ptr;
        int ictok=0;
        char **ret, **iret;
 
@@ -123,7 +132,7 @@ char **toktocliplist(int *ctok, const char *sep)
        } while(*s);
        
        *ctok=ictok;
-       s=last_ptr;
+       s=(char *)last_ptr;
        
        if (!(ret=iret=malloc(ictok*sizeof(char *))))
                return NULL;
@@ -139,22 +148,91 @@ char **toktocliplist(int *ctok, const char *sep)
        return ret;
 }
 
-/*******************************************************************
- Case insensitive string compararison.
-********************************************************************/
-
+/**
+ * Case insensitive string compararison.
+ *
+ * iconv does not directly give us a way to compare strings in
+ * arbitrary unix character sets -- all we can is convert and then
+ * compare.  This is expensive.
+ *
+ * As an optimization, we do a first pass that considers only the
+ * prefix of the strings that is entirely 7-bit.  Within this, we
+ * check whether they have the same value.
+ *
+ * Hopefully this will often give the answer without needing to copy.
+ * In particular it should speed comparisons to literal ascii strings
+ * or comparisons of strings that are "obviously" different.
+ *
+ * If we find a non-ascii character we fall back to converting via
+ * iconv.
+ *
+ * This should never be slower than convering the whole thing, and
+ * often faster.
+ *
+ * A different optimization would be to compare for bitwise equality
+ * in the binary encoding.  (It would be possible thought hairy to do
+ * both simultaneously.)  But in that case if they turn out to be
+ * different, we'd need to restart the whole thing.
+ *
+ * Even better is to implement strcasecmp for each encoding and use a
+ * function pointer. 
+ **/
 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);
+
+       const char * ps, * pt;
+       size_t size;
+       smb_ucs2_t *buffer_s, *buffer_t;
+       int ret;
+
+       for (ps = s, pt = t; ; ps++, pt++) {
+               char us, ut;
+
+               if (!*ps && !*pt)
+                       return 0; /* both ended */
+               else if (!*ps)
+                       return -1; /* s is a prefix */
+               else if (!*pt)
+                       return +1; /* t is a prefix */
+               else if ((*ps & 0x80) || (*pt & 0x80))
+                       /* not ascii anymore, do it the hard way from here on in */
+                       break;
+
+               us = toupper(*ps);
+               ut = toupper(*pt);
+               if (us == ut)
+                       continue;
+               else if (us < ut)
+                       return -1;
+               else if (us > ut)
+                       return +1;
+       }
+
+       size = push_ucs2_allocate(&buffer_s, s);
+       if (size == (size_t)-1) {
+               return strcmp(s, t); 
+               /* Not quite the right answer, but finding the right one
+                  under this failure case is expensive, and it's pretty close */
+       }
+       
+       size = push_ucs2_allocate(&buffer_t, t);
+       if (size == (size_t)-1) {
+               SAFE_FREE(buffer_s);
+               return strcmp(s, t); 
+               /* Not quite the right answer, but finding the right one
+                  under this failure case is expensive, and it's pretty close */
+       }
+       
+       ret = strcasecmp_w(buffer_s, buffer_t);
+       SAFE_FREE(buffer_s);
+       SAFE_FREE(buffer_t);
+       return ret;
 }
 
-/*******************************************************************
- Case insensitive string compararison, length limited.
-********************************************************************/
 
+/**
+ Case insensitive string compararison, length limited.
+**/
 int StrnCaseCmp(const char *s, const char *t, size_t n)
 {
        pstring buf1, buf2;
@@ -163,10 +241,11 @@ int StrnCaseCmp(const char *s, const char *t, size_t n)
        return strncmp(buf1,buf2,n);
 }
 
-/*******************************************************************
- Compare 2 strings.
-********************************************************************/
-
+/**
+ * Compare 2 strings.
+ *
+ * @note The comparison is case-insensitive.
+ **/
 BOOL strequal(const char *s1, const char *s2)
 {
        if (s1 == s2)
@@ -177,10 +256,11 @@ BOOL strequal(const char *s1, const char *s2)
        return(StrCaseCmp(s1,s2)==0);
 }
 
-/*******************************************************************
- Compare 2 strings up to and including the nth char.
-******************************************************************/
-
+/**
+ * Compare 2 strings up to and including the nth char.
+ *
+ * @note The comparison is case-insensitive.
+ **/
 BOOL strnequal(const char *s1,const char *s2,size_t n)
 {
   if (s1 == s2)
@@ -191,9 +271,9 @@ BOOL strnequal(const char *s1,const char *s2,size_t n)
   return(StrnCaseCmp(s1,s2,n)==0);
 }
 
-/*******************************************************************
+/**
  Compare 2 strings (case sensitive).
-********************************************************************/
+**/
 
 BOOL strcsequal(const char *s1,const char *s2)
 {
@@ -205,9 +285,9 @@ BOOL strcsequal(const char *s1,const char *s2)
   return(strcmp(s1,s2)==0);
 }
 
-/***************************************************************************
+/**
 Do a case-insensitive, whitespace-ignoring string compare.
-***************************************************************************/
+**/
 
 int strwicmp(const char *psz1, const char *psz2)
 {
@@ -236,36 +316,36 @@ int strwicmp(const char *psz1, const char *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);
+       strupper_m(str);
 
        return str;
 }
 
-/*******************************************************************
+/**
  Convert a string to "normal" form.
-********************************************************************/
+**/
 
 void strnorm(char *s)
 {
        extern int case_default;
        if (case_default == CASE_UPPER)
-               strupper(s);
+               strupper_m(s);
        else
-               strlower(s);
+               strlower_m(s);
 }
 
-/*******************************************************************
+/**
  Check if a string is in "normal" case.
-********************************************************************/
+**/
 
 BOOL strisnormal(const char *s)
 {
@@ -277,21 +357,43 @@ BOOL strisnormal(const char *s)
 }
 
 
-/****************************************************************************
+/**
  String replace.
  NOTE: oldc and newc must be 7 bit characters
-****************************************************************************/
+**/
 
-void string_replace(char *s,char oldc,char newc)
+void string_replace(pstring s,char oldc,char newc)
 {
-       push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+       unsigned char *p;
+
+       /* 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) */
+
+       for (p = (unsigned char *)s; *p; p++) {
+               if (*p & 0x80) /* mb string - slow path. */
+                       break;
+               if (*p == oldc)
+                       *p = newc;
+       }
+
+       if (!*p)
+               return;
+
+       /* Slow (mb) path. */
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+       /* With compose characters we must restart from the beginning. JRA. */
+       p = s;
+#endif
+       push_ucs2(NULL, tmpbuf, p, sizeof(tmpbuf), STR_TERMINATE);
        string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc));
-       pull_ucs2(NULL, s, tmpbuf, -1, sizeof(tmpbuf), STR_TERMINATE);
+       pull_ucs2(NULL, p, tmpbuf, -1, sizeof(tmpbuf), STR_TERMINATE);
 }
 
-/*******************************************************************
+/**
  Skip past some strings in a buffer.
-********************************************************************/
+**/
 
 char *skip_string(char *buf,size_t n)
 {
@@ -300,11 +402,11 @@ char *skip_string(char *buf,size_t n)
        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)
 {
@@ -313,11 +415,11 @@ size_t str_charnum(const char *s)
        return strlen_w(tmpbuf2);
 }
 
-/*******************************************************************
+/**
  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_ascii_charnum(const char *s)
 {
@@ -326,9 +428,62 @@ size_t str_ascii_charnum(const char *s)
        return strlen(tmpbuf2);
 }
 
-/*******************************************************************
+BOOL trim_char(char *s,char cfront,char cback)
+{
+       BOOL ret = False;
+       char *ep;
+       char *fp = s;
+
+       /* Ignore null or empty strings. */
+       if (!s || (s[0] == '\0'))
+               return False;
+
+       if (cfront) {
+               while (*fp && *fp == cfront)
+                       fp++;
+               if (!*fp) {
+                       /* We ate the string. */
+                       s[0] = '\0';
+                       return True;
+               }
+               if (fp != s)
+                       ret = True;
+       }
+
+       ep = fp + strlen(fp) - 1;
+       if (cback) {
+               /* Attempt ascii only. Bail for mb strings. */
+               while ((ep >= fp) && (*ep == cback)) {
+                       ret = True;
+                       if ((ep > fp) && (((unsigned char)ep[-1]) & 0x80)) {
+                               /* Could be mb... bail back to tim_string. */
+                               char fs[2], bs[2];
+                               if (cfront) {
+                                       fs[0] = cfront;
+                                       fs[1] = '\0';
+                               }
+                               bs[0] = cback;
+                               bs[1] = '\0';
+                               return trim_string(s, cfront ? fs : NULL, bs);
+                       } else {
+                               ep--;
+                       }
+               }
+               if (ep < fp) {
+                       /* We ate the string. */
+                       s[0] = '\0';
+                       return True;
+               }
+       }
+
+       ep[1] = '\0';
+       memmove(s, fp, ep-fp+2);
+       return ret;
+}
+
+/**
  Trim the specified elements off the front and back of a string.
-********************************************************************/
+**/
 
 BOOL trim_string(char *s,const char *front,const char *back)
 {
@@ -348,7 +503,9 @@ BOOL trim_string(char *s,const char *front,const char *back)
 
        if (front_len) {
                while (len && strncmp(s, front, front_len)==0) {
-                       memcpy(s, s+front_len, (len-front_len)+1);
+                       /* Must use memmove here as src & dest can
+                        * easily overlap. Found by valgrind. JRA. */
+                       memmove(s, s+front_len, (len-front_len)+1);
                        len -= front_len;
                        ret=True;
                }
@@ -364,9 +521,9 @@ BOOL trim_string(char *s,const char *front,const char *back)
        return ret;
 }
 
-/****************************************************************************
+/**
  Does a string have any uppercase chars in it?
-****************************************************************************/
+**/
 
 BOOL strhasupper(const char *s)
 {
@@ -378,9 +535,9 @@ BOOL strhasupper(const char *s)
        return(False);
 }
 
-/****************************************************************************
+/**
  Does a string have any lowercase chars in it?
-****************************************************************************/
+**/
 
 BOOL strhaslower(const char *s)
 {
@@ -392,66 +549,57 @@ BOOL strhaslower(const char *s)
        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);
-}
+       smb_ucs2_t *alloc_tmpbuf = NULL;
 
-/*******************************************************************
-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 (push_ucs2_allocate(&alloc_tmpbuf, s) == (size_t)-1) {
+               return 0;
+       }
 
-       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;
+       for(count=0,ptr=alloc_tmpbuf;*ptr;ptr++)
+               if(*ptr==UCS2_CHAR(c))
+                       count++;
 
-       return True;
+       SAFE_FREE(alloc_tmpbuf);
+       return(count);
 }
 
-/*******************************************************************
+/**
  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)
+char *safe_strcpy_fn(const char *fn, int line, char *dest,const char *src, size_t maxlength)
 {
        size_t len;
 
        if (!dest) {
-               DEBUG(0,("ERROR: NULL dest in safe_strcpy\n"));
+               DEBUG(0,("ERROR: NULL dest in safe_strcpy, called from [%s][%d]\n", fn, line));
                return NULL;
        }
 
+#ifdef DEVELOPER
+       clobber_region(fn,line,dest, maxlength+1);
+#endif
+
        if (!src) {
                *dest = 0;
                return dest;
        }  
 
-       len = strlen(src);
+       len = strnlen(src, maxlength+1);
 
        if (len > maxlength) {
-               DEBUG(0,("ERROR: string overflow by %d in safe_strcpy [%.50s]\n",
-                        (int)(len-maxlength), src));
+               DEBUG(0,("ERROR: string overflow by %lu (%lu - %lu) in safe_strcpy [%.50s]\n",
+                        (unsigned long)(len-maxlength), (unsigned long)len, 
+                        (unsigned long)maxlength, src));
                len = maxlength;
        }
       
@@ -460,50 +608,60 @@ char *safe_strcpy(char *dest,const char *src, size_t maxlength)
        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)
+**/
+char *safe_strcat_fn(const char *fn, int line, 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"));
+               DEBUG(0,("ERROR: NULL dest in safe_strcat, called from [%s][%d]\n", fn, line));
                return NULL;
        }
 
        if (!src)
                return dest;
        
-       src_len = strlen(src);
-       dest_len = strlen(dest);
-       
+       src_len = strnlen(src, maxlength + 1);
+       dest_len = strnlen(dest, maxlength + 1);
+
+#ifdef DEVELOPER
+       clobber_region(fn, line, dest + dest_len, maxlength + 1 - dest_len);
+#endif
+
        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;
+               if (maxlength > dest_len) {
+                       memcpy(&dest[dest_len], src, maxlength - dest_len);
+               }
+               dest[maxlength] = 0;
+               return NULL;
        }
-       
+
        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)
+**/
+char *alpha_strcpy_fn(const char *fn, int line, char *dest, const char *src, const char *other_safe_chars, size_t maxlength)
 {
        size_t len, i;
 
+#ifdef DEVELOPER
+       clobber_region(fn, line, dest, maxlength);
+#endif
+
        if (!dest) {
-               DEBUG(0,("ERROR: NULL dest in alpha_strcpy\n"));
+               DEBUG(0,("ERROR: NULL dest in alpha_strcpy, called from [%s][%d]\n", fn, line));
                return NULL;
        }
 
@@ -532,36 +690,51 @@ char *alpha_strcpy(char *dest, const char *src, const char *other_safe_chars, si
        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 *StrnCpy_fn(const char *fn, int line,char *dest,const char *src,size_t n)
 {
        char *d = dest;
-       if (!dest)
+
+#ifdef DEVELOPER
+       clobber_region(fn, line, dest, n+1);
+#endif
+
+       if (!dest) {
+               DEBUG(0,("ERROR: NULL dest in StrnCpy, called from [%s][%d]\n", fn, line));
                return(NULL);
+       }
+
        if (!src) {
                *dest = 0;
                return(dest);
        }
-       while (n-- && (*d++ = *src++))
-               ;
+       
+       while (n-- && (*d = *src)) {
+               d++;
+               src++;
+       }
+
        *d = 0;
        return(dest);
 }
 
-/****************************************************************************
+#if 0
+/**
  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)
+static char *strncpyn(char *dest, const char *src, size_t n, char c)
 {
        char *p;
        size_t str_len;
 
+#ifdef DEVELOPER
+       clobber_region(dest, n+1);
+#endif
        p = strchr_m(src, c);
        if (p == NULL) {
                DEBUG(5, ("strncpyn: separator character (%c) not found\n", c));
@@ -574,8 +747,9 @@ char *strncpyn(char *dest, const char *src, size_t n, char c)
 
        return p;
 }
+#endif
 
-/*************************************************************
+/**
  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
@@ -583,7 +757,7 @@ char *strncpyn(char *dest, const char *src, size_t n, char c)
 
  valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
 
-**************************************************************/
+**/
 
 size_t strhex_to_str(char *p, size_t len, const char *strhex)
 {
@@ -620,9 +794,25 @@ size_t strhex_to_str(char *p, size_t len, const char *strhex)
        return num_chars;
 }
 
-/****************************************************************************
+/**
+ * Routine to print a buffer as HEX digits, into an allocated string.
+ */
+
+void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer)
+{
+       int i;
+       char *hex_buffer;
+
+       *out_hex_buffer = smb_xmalloc((len*2)+1);
+       hex_buffer = *out_hex_buffer;
+
+       for (i = 0; i < len; i++)
+               slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]);
+}
+
+/**
  Check if a string is part of a list.
-****************************************************************************/
+**/
 
 BOOL in_list(char *s,char *list,BOOL casesensitive)
 {
@@ -647,9 +837,9 @@ BOOL in_list(char *s,char *list,BOOL casesensitive)
 /* 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)
 {
@@ -678,9 +868,9 @@ static BOOL string_init(char **dest,const char *src)
        return(True);
 }
 
-/****************************************************************************
+/**
  Free a string value.
-****************************************************************************/
+**/
 
 void string_free(char **s)
 {
@@ -691,10 +881,10 @@ void string_free(char **s)
        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)
 {
@@ -702,7 +892,7 @@ BOOL string_set(char **dest,const char *src)
        return(string_init(dest,src));
 }
 
-/****************************************************************************
+/**
  Substitute a string for a pattern in another string. Make sure there is 
  enough room!
 
@@ -712,7 +902,7 @@ BOOL string_set(char **dest,const char *src)
  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)
 {
@@ -729,7 +919,7 @@ void string_sub(char *s,const char *pattern, const char *insert, size_t len)
        if (len == 0)
                len = ls + 1; /* len is number of *bytes* */
 
-       while (lp <= ls && (p = strstr(s,pattern))) {
+       while (lp <= ls && (p = strstr_m(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),
@@ -770,12 +960,12 @@ 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)
 {
@@ -814,8 +1004,9 @@ char *realloc_string_sub(char *string, const char *pattern, const char *insert)
                }
        }
        
-       while ((p = strstr(s,pattern))) {
+       while ((p = strstr_m(s,pattern))) {
                if (ld > 0) {
+                       int offset = PTR_DIFF(s,string);
                        char *t = Realloc(string, ls + ld + 1);
                        if (!t) {
                                DEBUG(0, ("realloc_string_sub: out of memory!\n"));
@@ -823,7 +1014,7 @@ char *realloc_string_sub(char *string, const char *pattern, const char *insert)
                                return NULL;
                        }
                        string = t;
-                       p = t + (p - s);
+                       p = t + offset + (p - s);
                }
                if (li != lp) {
                        memmove(p+li,p+lp,strlen(p+lp)+1);
@@ -836,12 +1027,12 @@ char *realloc_string_sub(char *string, const char *pattern, const char *insert)
        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)
 {
@@ -861,7 +1052,7 @@ void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
        if (len == 0)
                len = ls + 1; /* len is number of *bytes* */
        
-       while (lp <= ls && (p = strstr(s,pattern))) {
+       while (lp <= ls && (p = strstr_m(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),
@@ -877,14 +1068,14 @@ void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
        }
 }
 
-/****************************************************************************
+/**
  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,
+static 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;
@@ -942,11 +1133,12 @@ smb_ucs2_t *all_string_sub_wa(smb_ucs2_t *s, const char *pattern,
        return all_string_sub_w(s, p, i);
 }
 
-/****************************************************************************
+#if 0
+/**
  Splits out the front and back at a separator.
-****************************************************************************/
+**/
 
-void split_at_last_component(char *path, char *front, char sep, char *back)
+static void split_at_last_component(char *path, char *front, char sep, char *back)
 {
        char *p = strrchr_m(path, sep);
 
@@ -965,10 +1157,11 @@ void split_at_last_component(char *path, char *front, char sep, char *back)
                        back[0] = 0;
        }
 }
+#endif
 
-/****************************************************************************
+/**
  Write an octal as a string.
-****************************************************************************/
+**/
 
 const char *octal_string(int i)
 {
@@ -980,27 +1173,46 @@ const char *octal_string(int i)
 }
 
 
-/****************************************************************************
+/**
  Truncate a string at a specified length.
-****************************************************************************/
+**/
 
-char *string_truncate(char *s, int length)
+char *string_truncate(char *s, unsigned 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)
+char *strchr_m(const char *src, char c)
 {
        wpstring ws;
        pstring s2;
        smb_ucs2_t *p;
+       const 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) */
+
+       for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
+               if (*s == c)
+                       return (char *)s;
+       }
+
+       if (!*s)
+               return NULL;
+
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+       /* With compose characters we must restart from the beginning. JRA. */
+       s = src;
+#endif
 
        push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
        p = strchr_w(ws, UCS2_CHAR(c));
@@ -1012,13 +1224,69 @@ char *strchr_m(const char *s, char c)
 }
 
 char *strrchr_m(const char *s, char c)
+{
+       /* 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). Also, in Samba
+          we only search for ascii characters in 'c' and that
+          in all mb character sets with a compound character
+          containing c, if 'c' is not a match at position
+          p, then p[-1] > 0x7f. JRA. */
+
+       {
+               size_t len = strlen(s);
+               const char *cp = s;
+               BOOL got_mb = False;
+
+               if (len == 0)
+                       return NULL;
+               cp += (len - 1);
+               do {
+                       if (c == *cp) {
+                               /* Could be a match. Part of a multibyte ? */
+                               if ((cp > s) && (((unsigned char)cp[-1]) & 0x80)) {
+                                       /* Yep - go slow :-( */
+                                       got_mb = True;
+                                       break;
+                               }
+                               /* No - we have a match ! */
+                               return (char *)cp;
+                       }
+               } while (cp-- != s);
+               if (!got_mb)
+                       return NULL;
+       }
+
+       /* String contained a non-ascii char. Slow path. */
+       {
+               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));
+       }
+}
+
+/***********************************************************************
+ Return the equivalent of doing strrchr 'n' times - always going
+ backwards.
+***********************************************************************/
+
+char *strnrchr_m(const char *s, char c, unsigned int n)
 {
        wpstring ws;
        pstring s2;
        smb_ucs2_t *p;
 
        push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
-       p = strrchr_w(ws, UCS2_CHAR(c));
+       p = strnrchr_w(ws, UCS2_CHAR(c), n);
        if (!p)
                return NULL;
        *p = 0;
@@ -1026,85 +1294,155 @@ char *strrchr_m(const char *s, char c)
        return (char *)(s+strlen(s2));
 }
 
-/*******************************************************************
Convert a string to lower case.
-********************************************************************/
+/***********************************************************************
strstr_m - We convert via ucs2 for now.
+***********************************************************************/
 
-void strlower_m(char *s)
+char *strstr_m(const char *src, const char *findstr)
 {
-       /* this is quite a common operation, so we want it to be
-          fast. We optimise for the ascii case, knowing that all our
+       smb_ucs2_t *p;
+       smb_ucs2_t *src_w, *find_w;
+       const char *s;
+       char *s2;
+       char *retp;
+
+       size_t findstr_len = 0;
+       size_t find_w_len;
+
+       /* for correctness */
+       if (!findstr[0]) {
+               return src;
+       }
+
+       /* Samba does single character findstr calls a *lot*. */
+       if (findstr[1] == '\0')
+               return strchr_m(src, *findstr);
+
+       /* 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);
+       for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
+               if (*s == *findstr) {
+                       if (!findstr_len) 
+                               findstr_len = strlen(findstr);
+
+                       if (strncmp(s, findstr, findstr_len) == 0) {
+                               return (char *)s;
+                       }
+               }
+       }
 
        if (!*s)
-               return;
+               return NULL;
 
-       /* 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);     
-}
+#if 1 /* def BROKEN_UNICODE_COMPOSE_CHARACTERS */
+       /* 'make check' fails unless we do this */
 
-/*******************************************************************
- Duplicate convert a string to lower case.
-********************************************************************/
+       /* With compose characters we must restart from the beginning. JRA. */
+       s = src;
+#endif
 
-char *strdup_lower(const char *s)
-{
-       char *t = strdup(s);
-       if (t == NULL) {
-               DEBUG(0, ("strdup_lower: Out of memory!\n"));
+       if (push_ucs2_allocate(&src_w, src) == (size_t)-1) {
+               DEBUG(0,("strstr_m: src malloc fail\n"));
                return NULL;
        }
-       strlower_m(t);
-       return t;
+       
+       if (push_ucs2_allocate(&find_w, findstr) == (size_t)-1) {
+               SAFE_FREE(src_w);
+               DEBUG(0,("strstr_m: find malloc fail\n"));
+               return NULL;
+       }
+
+       p = strstr_w(src_w, find_w);
+
+       if (!p) {
+               SAFE_FREE(src_w);
+               SAFE_FREE(find_w);
+               return NULL;
+       }
+       
+       *p = 0;
+       if (pull_ucs2_allocate(&s2, src_w) == (size_t)-1) {
+               SAFE_FREE(src_w);
+               SAFE_FREE(find_w);
+               DEBUG(0,("strstr_m: dest malloc fail\n"));
+               return NULL;
+       }
+       retp = (char *)(s+strlen(s2));
+       SAFE_FREE(src_w);
+       SAFE_FREE(find_w);
+       SAFE_FREE(s2);
+       return retp;
 }
 
-/*******************************************************************
- Convert a string to upper case.
-********************************************************************/
+/**
+ Convert a string to lower case.
+**/
 
-void strupper_m(char *s)
+void strlower_m(char *s)
 {
+       size_t len;
+
        /* 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);
+       while (*s && !(((unsigned char)s[0]) & 0x80)) {
+               *s = tolower((unsigned char)*s);
+               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);     
+        * as source string even in UTF-8 encoding. (VIV) */
+       len = strlen(s) + 1;
+       errno = 0;
+       unix_strlower(s,len,s,len);     
+       /* Catch mb conversion errors that may not terminate. */
+       if (errno)
+               s[len-1] = '\0';
 }
 
-/*******************************************************************
+/**
  Convert a string to upper case.
-********************************************************************/
+**/
 
-char *strdup_upper(const char *s)
+void strupper_m(char *s)
 {
-       char *t = strdup(s);
-       if (t == NULL) {
-               DEBUG(0, ("strdup_upper: Out of memory!\n"));
-               return NULL;
+       size_t len;
+
+       /* 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]) & 0x80)) {
+               *s = toupper((unsigned char)*s);
+               s++;
        }
-       strupper_m(t);
-       return t;
+
+       if (!*s)
+               return;
+
+       /* I assume that lowercased string takes the same number of bytes
+        * as source string even in multibyte encoding. (VIV) */
+       len = strlen(s) + 1;
+       errno = 0;
+       unix_strupper(s,len,s,len);     
+       /* Catch mb conversion errors that may not terminate. */
+       if (errno)
+               s[len-1] = '\0';
 }
 
-/*******************************************************************
+/**
  Return a RFC2254 binary string representation of a buffer.
  Used in LDAP filters.
  Caller must free.
-********************************************************************/
+**/
 
 char *binary_string(char *buf, int len)
 {
@@ -1124,9 +1462,9 @@ char *binary_string(char *buf, int len)
        return s;
 }
 
-/*******************************************************************
+/**
  Just a typesafety wrapper for snprintf into a pstring.
-********************************************************************/
+**/
 
  int pstr_sprintf(pstring s, const char *fmt, ...)
 {
@@ -1139,11 +1477,12 @@ char *binary_string(char *buf, int len)
        return ret;
 }
 
-/*******************************************************************
+
+/**
  Just a typesafety wrapper for snprintf into a fstring.
-********************************************************************/
+**/
 
- int fstr_sprintf(fstring s, const char *fmt, ...)
+int fstr_sprintf(fstring s, const char *fmt, ...)
 {
        va_list ap;
        int ret;
@@ -1154,10 +1493,11 @@ char *binary_string(char *buf, int len)
        return ret;
 }
 
+
 #ifndef HAVE_STRNDUP
-/*******************************************************************
+/**
  Some platforms don't have strndup.
-********************************************************************/
+**/
 
  char *strndup(const char *s, size_t n)
 {
@@ -1175,9 +1515,9 @@ char *binary_string(char *buf, int len)
 #endif
 
 #ifndef HAVE_STRNLEN
-/*******************************************************************
+/**
  Some platforms don't have strnlen
-********************************************************************/
+**/
 
  size_t strnlen(const char *s, size_t n)
 {
@@ -1188,9 +1528,9 @@ char *binary_string(char *buf, int len)
 }
 #endif
 
-/***********************************************************
+/**
  List of Strings manipulation functions
-***********************************************************/
+**/
 
 #define S_LIST_ABS 16 /* List Allocation Block Size */
 
@@ -1283,10 +1623,9 @@ BOOL str_list_copy(char ***dest, const char **src)
        return True;    
 }
 
-/***********************************************************
- Return true if all the elements of the list match exactly.
-***********************************************************/
-
+/**
+ * Return true if all the elements of the list match exactly.
+ **/
 BOOL str_list_compare(char **list1, char **list2)
 {
        int num;
@@ -1318,6 +1657,35 @@ void str_list_free(char ***list)
        SAFE_FREE(*list);
 }
 
+/******************************************************************************
+ version of standard_sub_basic() for string lists; uses alloc_sub_basic() 
+ for the work
+ *****************************************************************************/
+BOOL str_list_sub_basic( char **list, const char *smb_name )
+{
+       char *s, *tmpstr;
+       
+       while ( *list ) {
+               s = *list;
+               tmpstr = alloc_sub_basic(smb_name, s);
+               if ( !tmpstr ) {
+                       DEBUG(0,("str_list_sub_basic: alloc_sub_basic() return NULL!\n"));
+                       return False;
+               }
+
+               *list = tmpstr;
+                       
+               list++;
+       }
+
+       return True;
+}
+
+/******************************************************************************
+ substritute a specific pattern in a string list
+ *****************************************************************************/
 BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
 {
        char *p, *s, *t;
@@ -1338,7 +1706,7 @@ BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
                s = *list;
                ls = (ssize_t)strlen(s);
 
-               while ((p = strstr(s, pattern))) {
+               while ((p = strstr_m(s, pattern))) {
                        t = *list;
                        d = p -t;
                        if (ld) {
@@ -1373,6 +1741,7 @@ BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
                        }       
                }
                
+               
                list++;
        }
        
@@ -1381,6 +1750,7 @@ BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
 
 
 #define IPSTR_LIST_SEP ","
+#define IPSTR_LIST_CHAR        ','
 
 /**
  * Add ip string representation to ipstr list. Used also
@@ -1395,19 +1765,20 @@ BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
  *         reallocated to new length
  **/
 
-char* ipstr_list_add(char** ipstr_list, const struct in_addr *ip)
+char* ipstr_list_add(char** ipstr_list, const struct ip_service *service)
 {
        char* new_ipstr = NULL;
        
        /* arguments checking */
-       if (!ipstr_list || !ip) return NULL;
+       if (!ipstr_list || !service) return NULL;
 
        /* attempt to convert ip to a string and append colon separator to it */
        if (*ipstr_list) {
-               asprintf(&new_ipstr, "%s%s%s", *ipstr_list, IPSTR_LIST_SEP,inet_ntoa(*ip));
+               asprintf(&new_ipstr, "%s%s%s:%d", *ipstr_list, IPSTR_LIST_SEP,
+                       inet_ntoa(service->ip), service->port);
                SAFE_FREE(*ipstr_list);
        } else {
-               asprintf(&new_ipstr, "%s", inet_ntoa(*ip));
+               asprintf(&new_ipstr, "%s:%d", inet_ntoa(service->ip), service->port);
        }
        *ipstr_list = new_ipstr;
        return *ipstr_list;
@@ -1424,7 +1795,7 @@ char* ipstr_list_add(char** ipstr_list, const struct in_addr *ip)
  * @return pointer to allocated ip string
  **/
  
-char* ipstr_list_make(char** ipstr_list, const struct in_addr* ip_list, int ip_count)
+char* ipstr_list_make(char** ipstr_list, const struct ip_service* ip_list, int ip_count)
 {
        int i;
        
@@ -1443,7 +1814,8 @@ char* ipstr_list_make(char** ipstr_list, const struct in_addr* ip_list, int ip_c
 
 /**
  * Parse given ip string list into array of ip addresses
- * (as in_addr structures)
+ * (as ip_service structures)  
+ *    e.g. 192.168.1.100:389,192.168.1.78, ...
  *
  * @param ipstr ip string list to be parsed 
  * @param ip_list pointer to array of ip addresses which is
@@ -1451,28 +1823,40 @@ char* ipstr_list_make(char** ipstr_list, const struct in_addr* ip_list, int ip_c
  * @return number of succesfully parsed addresses
  **/
  
-int ipstr_list_parse(const char* ipstr_list, struct in_addr** ip_list)
+int ipstr_list_parse(const char* ipstr_list, struct ip_service **ip_list)
 {
        fstring token_str;
-       int count;
+       size_t count;
+       int i;
 
-       if (!ipstr_list || !ip_list) return 0;
+       if (!ipstr_list || !ip_list) 
+               return 0;
+       
+       count = count_chars(ipstr_list, IPSTR_LIST_CHAR) + 1;
+       if ( (*ip_list = (struct ip_service*)malloc(count * sizeof(struct ip_service))) == NULL ) {
+               DEBUG(0,("ipstr_list_parse: malloc failed for %lu entries\n", (unsigned long)count));
+               return 0;
+       }
        
-       for (*ip_list = NULL, count = 0;
-            next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN);
-            count++) {
-            
+       for ( i=0; 
+               next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN) && i<count; 
+               i++ ) 
+       {
                struct in_addr addr;
+               unsigned port = 0;      
+               char *p = strchr(token_str, ':');
+               
+               if (p) {
+                       *p = 0;
+                       port = atoi(p+1);
+               }
 
                /* convert single token to ip address */
                if ( (addr.s_addr = inet_addr(token_str)) == INADDR_NONE )
                        break;
-               
-               /* prepare place for another in_addr structure */
-               *ip_list = Realloc(*ip_list, (count + 1) * sizeof(struct in_addr));
-               if (!*ip_list) return -1;
-               
-               (*ip_list)[count] = addr;
+                               
+               (*ip_list)[i].ip = addr;
+               (*ip_list)[i].port = port;
        }
        
        return count;
@@ -1491,19 +1875,14 @@ void ipstr_list_free(char* ipstr_list)
 }
 
 
-/***********************************************************
+/**
  Unescape a URL encoded string, in place.
-***********************************************************/
+**/
 
 void rfc1738_unescape(char *buf)
 {
        char *p=buf;
 
-       while ((p=strchr_m(p,'+')))
-               *p = ' ';
-
-       p = buf;
-
        while (p && *p && (p=strchr_m(p,'%'))) {
                int c1 = p[1];
                int c2 = p[2];
@@ -1533,9 +1912,9 @@ void rfc1738_unescape(char *buf)
 
 static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-/***************************************************************************
-decode a base64 string into a DATA_BLOB - simple and slow algorithm
 ***************************************************************************/
+/**
+ * Decode a base64 string into a DATA_BLOB - simple and slow algorithm
+ **/
 DATA_BLOB base64_decode_data_blob(const char *s)
 {
        int bit_offset, byte_offset, idx, i, n;
@@ -1562,34 +1941,36 @@ DATA_BLOB base64_decode_data_blob(const char *s)
                s++; i++;
        }
 
+       if (*s == '=') n -= 1;
+
        /* fix up length */
        decoded.length = n;
        return decoded;
 }
 
-/***************************************************************************
-decode a base64 string in-place - wrapper for the above
-***************************************************************************/
-void base64_decode(char *s)
+/**
+ * Decode a base64 string in-place - wrapper for the above
+ **/
+void base64_decode_inplace(char *s)
 {
        DATA_BLOB decoded = base64_decode_data_blob(s);
        memcpy(s, decoded.data, decoded.length);
-       data_blob_free(&decoded);
-
        /* null terminate */
        s[decoded.length] = '\0';
-}
 
-/***************************************************************************
-encode a base64 string into a malloc()ed string caller to free.
+       data_blob_free(&decoded);
+}
 
-From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
-***************************************************************************/
+/**
+ * Encode a base64 string into a malloc()ed string caller to free.
+ *
+ *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
+ **/
 char * base64_encode_data_blob(DATA_BLOB data)
 {
        int bits = 0;
        int char_count = 0;
-       int out_cnt = 0;
+       size_t out_cnt = 0;
        size_t len = data.length;
        size_t output_len = data.length * 2;
        char *result = malloc(output_len); /* get us plenty of space */
@@ -1625,12 +2006,25 @@ char * base64_encode_data_blob(DATA_BLOB data)
     return result;
 }
 
-#ifdef VALGRIND
-size_t valgrind_strlen(const char *s)
+/* read a SMB_BIG_UINT from a string */
+SMB_BIG_UINT STR_TO_SMB_BIG_UINT(const char *nptr, const char **entptr)
 {
-       size_t count;
-       for(count = 0; *s++; count++)
-               ;
-       return count;
+
+       SMB_BIG_UINT val = -1;
+       const char *p = nptr;
+       
+       while (p && *p && isspace(*p))
+               p++;
+#ifdef LARGE_SMB_OFF_T
+       sscanf(p,"%llu",&val);  
+#else /* LARGE_SMB_OFF_T */
+       sscanf(p,"%lu",&val);
+#endif /* LARGE_SMB_OFF_T */
+       if (entptr) {
+               while (p && *p && isdigit(*p))
+                       p++;
+               *entptr = p;
+       }
+
+       return val;
 }
-#endif