summaryrefslogtreecommitdiff
path: root/sql/item_strfunc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_strfunc.cc')
-rw-r--r--sql/item_strfunc.cc1719
1 files changed, 1719 insertions, 0 deletions
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
new file mode 100644
index 00000000000..3050125f410
--- /dev/null
+++ b/sql/item_strfunc.cc
@@ -0,0 +1,1719 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* This file defines all string functions
+** Warning: Some string functions doesn't always put and end-null on a String
+** (This shouldn't be neaded)
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include <m_ctype.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "md5.h"
+
+String empty_string("");
+
+uint nr_of_decimals(const char *str)
+{
+ if ((str=strchr(str,'.')))
+ {
+ const char *start= ++str;
+ for ( ; isdigit(*str) ; str++) ;
+ return (uint) (str-start);
+ }
+ return 0;
+}
+
+double Item_str_func::val()
+{
+ String *res;
+ res=val_str(&str_value);
+ return res ? atof(res->c_ptr()) : 0.0;
+}
+
+longlong Item_str_func::val_int()
+{
+ String *res;
+ res=val_str(&str_value);
+ return res ? strtoll(res->c_ptr(),NULL,10) : (longlong) 0;
+}
+
+
+String *Item_func_md5::val_str(String *str)
+{
+ String * sptr= args[0]->val_str(str);
+ if (sptr)
+ {
+ MD5_CTX context;
+ unsigned char digest[16];
+
+ null_value=0;
+ MD5Init (&context);
+ MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length());
+ MD5Final (digest, &context);
+ str->alloc(32); // Ensure that memory is free
+ sprintf((char *) str->ptr(),
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[0], digest[1], digest[2], digest[3],
+ digest[4], digest[5], digest[6], digest[7],
+ digest[8], digest[9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);
+ str->length((uint) 32);
+ return str;
+ }
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_md5::fix_length_and_dec()
+{
+ max_length=32;
+}
+
+/*
+** Concatinate args with the following premissess
+** If only one arg which is ok, return value of arg
+** Don't reallocate val_str() if not absolute necessary.
+*/
+
+String *Item_func_concat::val_str(String *str)
+{
+ String *res,*res2,*use_as_buff;
+ uint i;
+
+ null_value=0;
+ if (!(res=args[0]->val_str(str)))
+ goto null;
+ use_as_buff= &tmp_value;
+ for (i=1 ; i < arg_count ; i++)
+ {
+ if (res->length() == 0)
+ {
+ if (!(res=args[i]->val_str(str)))
+ goto null;
+ }
+ else
+ {
+ if (!(res2=args[i]->val_str(use_as_buff)))
+ goto null;
+ if (res2->length() == 0)
+ continue;
+ if (res->length()+res2->length() > max_allowed_packet)
+ goto null; // Error check
+ if (res->alloced_length() >= res->length()+res2->length())
+ { // Use old buffer
+ res->append(*res2);
+ }
+ else if (str->alloced_length() >= res->length()+res2->length())
+ {
+ if (str == res2)
+ str->replace(0,0,*res);
+ else
+ {
+ str->copy(*res);
+ str->append(*res2);
+ }
+ res=str;
+ }
+ else if (res == &tmp_value)
+ {
+ if (res->append(*res2)) // Must be a blob
+ goto null;
+ }
+ else if (res2 == &tmp_value)
+ { // This can happend only 1 time
+ if (tmp_value.replace(0,0,*res))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str; // Put next arg here
+ }
+ else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
+ res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length())
+ {
+ /*
+ This happens really seldom:
+ In this case res2 is sub string of tmp_value. We will
+ now work in place in tmp_value to set it to res | res2
+ */
+ /* Chop the last characters in tmp_value that isn't in res2 */
+ tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) +
+ res2->length());
+ /* Place res2 at start of tmp_value, remove chars before res2 */
+ if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()),
+ *res))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str; // Put next arg here
+ }
+ else
+ { // Two big const strings
+ if (tmp_value.alloc(max_length) ||
+ tmp_value.copy(*res) ||
+ tmp_value.append(*res2))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str;
+ }
+ }
+ }
+ return res;
+
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_concat::fix_length_and_dec()
+{
+ max_length=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ max_length+=args[i]->max_length;
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+
+/*
+** concat with separator. First arg is the separator
+** concat_ws takes at least two arguments.
+*/
+
+String *Item_func_concat_ws::val_str(String *str)
+{
+ char tmp_str_buff[10];
+ String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff)),
+ *sep_str, *res, *res2,*use_as_buff;
+ uint i;
+
+ null_value=0;
+ if (!(sep_str= separator->val_str(&tmp_sep_str)))
+ goto null;
+
+ use_as_buff= &tmp_value;
+ str->length(0);
+ res=str;
+
+ // Skip until non-null and non-empty argument is found.
+ // If not, return the empty string
+ for (i=0; !(res= args[i]->val_str(str)) || !res->length(); i++)
+ {
+ if ((i + 1) == arg_count)
+ return &empty_string;
+ }
+
+ for (i++; i < arg_count ; i++)
+ {
+ if (!(res2= args[i]->val_str(use_as_buff)) || !res2->length())
+ continue;
+ else
+ {
+ if (res->length() + sep_str->length() + res2->length() >
+ max_allowed_packet)
+ goto null; // Error check
+ if (res->alloced_length() >=
+ res->length() + sep_str->length() + res2->length())
+ { // Use old buffer
+ res->append(*sep_str); // res->length() > 0 always
+ res->append(*res2);
+ use_as_buff= &tmp_value;
+ }
+ else if (str->alloced_length() >=
+ res->length() + sep_str->length() + res2->length())
+ {
+ str->copy(*res);
+ str->append(*sep_str);
+ str->append(*res2);
+ res=str;
+ use_as_buff= &tmp_value;
+ }
+ else if (res == &tmp_value)
+ {
+ if ((res->length() && res->append(*sep_str)) || res->append(*res2))
+ goto null; // Must be a blob
+ }
+ else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
+ res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length())
+ {
+ /*
+ This happens really seldom:
+ In this case res2 is sub string of tmp_value. We will
+ now work in place in tmp_value to set it to res | res2
+ */
+ /* Chop the last characters in tmp_value that isn't in res2 */
+ tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) +
+ res2->length());
+ /* Place res2 at start of tmp_value, remove chars before res2 */
+ if (res->append(*sep_str))
+ goto null;
+ if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()),
+ *res))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str; // Put next arg here
+ }
+ else
+ { // Two big const strings
+ if (tmp_value.alloc(max_length) ||
+ tmp_value.copy(*res) ||
+ tmp_value.append(*sep_str) ||
+ tmp_value.append(*res2))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str;
+ }
+ }
+ }
+ return res;
+
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_concat_ws::fix_length_and_dec()
+{
+ max_length=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ max_length+=args[i]->max_length;
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_reverse::val_str(String *str)
+{
+ String *res = args[0]->val_str(str);
+ char *ptr,*end;
+
+ if ((null_value=args[0]->null_value))
+ return 0;
+ /* An empty string is a special case as the string pointer may be null */
+ if (!res->length())
+ return &empty_string;
+ res=copy_if_not_alloced(str,res,res->length());
+ ptr = (char *) res->ptr();
+ end=ptr+res->length();
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ String tmpstr;
+ tmpstr.copy(*res);
+ char *tmp = (char *) tmpstr.ptr() + tmpstr.length();
+ register uint32 l;
+ while (ptr < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end)))
+ tmp-=l, memcpy(tmp,ptr,l), ptr+=l;
+ else
+ *--tmp=*ptr++;
+ }
+ memcpy((char *) res->ptr(),(char *) tmpstr.ptr(), res->length());
+ }
+ else
+#endif /* USE_MB */
+ {
+ char tmp;
+ while (ptr < end)
+ {
+ tmp=*ptr;
+ *ptr++=*--end;
+ *end=tmp;
+ }
+ }
+ return res;
+}
+
+
+void Item_func_reverse::fix_length_and_dec()
+{
+ max_length = args[0]->max_length;
+}
+
+/*
+** Replace all occurences of string2 in string1 with string3.
+** Don't reallocate val_str() if not neaded
+*/
+
+/* TODO: Fix that this works with binary strings when using USE_MB */
+
+String *Item_func_replace::val_str(String *str)
+{
+ String *res,*res2,*res3;
+ int offset=0;
+ uint from_length,to_length;
+ bool alloced=0;
+#ifdef USE_MB
+ const char *ptr,*end,*strend,*search,*search_end;
+ register uint32 l;
+#endif
+
+ null_value=0;
+ res=args[0]->val_str(str);
+ if (args[0]->null_value)
+ goto null;
+ res2=args[1]->val_str(&tmp_value);
+ if (args[1]->null_value)
+ goto null;
+
+ if (res2->length() == 0)
+ return res;
+#ifndef USE_MB
+ if ((offset=res->strstr(*res2)) < 0)
+ return res;
+#else
+ if (!use_mb(default_charset_info) && (offset=res->strstr(*res2)) < 0)
+ return res;
+#endif
+ if (!(res3=args[2]->val_str(&tmp_value2)))
+ goto null;
+ from_length= res2->length();
+ to_length= res3->length();
+
+#ifdef USE_MB
+ if (use_mb(default_charset_info))
+ {
+ search=res2->ptr();
+ search_end=search+from_length;
+redo:
+ ptr=res->ptr()+offset;
+ strend=res->ptr()+res->length();
+ end=strend-from_length+1;
+ while (ptr < end)
+ {
+ if (*ptr == *search)
+ {
+ register char *i,*j;
+ i=(char*) ptr+1; j=(char*) search+1;
+ while (j != search_end)
+ if (*i++ != *j++) goto skipp;
+ offset=ptr-res->ptr();
+ if (res->length()-from_length + to_length > max_allowed_packet)
+ goto null;
+ if (!alloced)
+ {
+ alloced=1;
+ res=copy_if_not_alloced(str,res,res->length()+to_length);
+ }
+ res->replace((uint) offset,from_length,*res3);
+ goto redo;
+ }
+skipp:
+ if ((l=my_ismbchar(default_charset_info, ptr,strend))) ptr+=l;
+ else ++ptr;
+ }
+ }
+ else
+#endif /* USE_MB */
+ do
+ {
+ if (res->length()-from_length + to_length > max_allowed_packet)
+ goto null;
+ if (!alloced)
+ {
+ alloced=1;
+ res=copy_if_not_alloced(str,res,res->length()+to_length);
+ }
+ res->replace((uint) offset,from_length,*res3);
+ offset+=(int) to_length;
+ }
+ while ((offset=res->strstr(*res2,(uint) offset)) >0);
+ return res;
+
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_replace::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ int diff=(int) (args[2]->max_length - args[1]->max_length);
+ if (diff > 0 && args[1]->max_length)
+ { // Calculate of maxreplaces
+ max_length= max_length/args[1]->max_length;
+ max_length= (max_length+1)*(uint) diff;
+ }
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_insert::val_str(String *str)
+{
+ String *res,*res2;
+ uint start,length;
+
+ null_value=0;
+ res=args[0]->val_str(str);
+ res2=args[3]->val_str(&tmp_value);
+ start=(uint) args[1]->val_int()-1;
+ length=(uint) args[2]->val_int();
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
+ args[3]->null_value)
+ goto null; /* purecov: inspected */
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !args[0]->binary)
+ {
+ start=res->charpos(start);
+ length=res->charpos(length,start);
+ }
+#endif
+ if (start > res->length()+1)
+ return res; // Wrong param; skipp insert
+ if (length > res->length()-start)
+ length=res->length()-start;
+ if (res->length() - length + res2->length() > max_allowed_packet)
+ goto null; // OOM check
+ res=copy_if_not_alloced(str,res,res->length());
+ res->replace(start,length,*res2);
+ return res;
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_insert::fix_length_and_dec()
+{
+ max_length=args[0]->max_length+args[3]->max_length;
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_lcase::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ res->casedn();
+ return res;
+}
+
+
+String *Item_func_ucase::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ res->caseup();
+ return res;
+}
+
+
+String *Item_func_left::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ long length =(long) args[1]->val_int();
+
+ if ((null_value=args[0]->null_value))
+ return 0;
+ if (length <= 0)
+ return &empty_string;
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ length = res->charpos(length);
+#endif
+ if (res->length() > (ulong) length)
+ { // Safe even if const arg
+ if (!res->alloced_length())
+ { // Don't change const str
+ str_value= *res; // Not malloced string
+ res= &str_value;
+ }
+ res->length((uint) length);
+ }
+ return res;
+}
+
+
+void Item_str_func::left_right_max_length()
+{
+ max_length=args[0]->max_length;
+ if (args[1]->const_item())
+ {
+ int length=(int) args[1]->val_int();
+ if (length <= 0)
+ max_length=0;
+ else
+ set_if_smaller(max_length,(uint) length);
+ }
+}
+
+
+void Item_func_left::fix_length_and_dec()
+{
+ left_right_max_length();
+}
+
+
+String *Item_func_right::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ long length =(long) args[1]->val_int();
+
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ if (length <= 0)
+ return &empty_string; /* purecov: inspected */
+ if (res->length() <= (uint) length)
+ return res; /* purecov: inspected */
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ uint start=res->numchars()-(uint) length;
+ if (start<=0) return res;
+ start=res->charpos(start);
+ tmp_value.set(*res,start,res->length()-start);
+ }
+ else
+#endif
+ {
+ tmp_value.set(*res,(res->length()- (uint) length),(uint) length);
+ }
+ return &tmp_value;
+}
+
+
+void Item_func_right::fix_length_and_dec()
+{
+ left_right_max_length();
+}
+
+
+String *Item_func_substr::val_str(String *str)
+{
+ String *res = args[0]->val_str(str);
+ int32 start = (int32) args[1]->val_int()-1;
+ int32 length = arg_count == 3 ? (int32) args[2]->val_int() : INT_MAX32;
+ int32 tmp_length;
+
+ if ((null_value=(args[0]->null_value || args[1]->null_value ||
+ (arg_count == 3 && args[2]->null_value))))
+ return 0; /* purecov: inspected */
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ start=res->charpos(start);
+ length=res->charpos(length,start);
+ }
+#endif
+ if (start < 0 || (uint) start+1 > res->length() || length <= 0)
+ return &empty_string;
+
+ tmp_length=(int32) res->length()-start;
+ length=min(length,tmp_length);
+
+ if (!start && res->length() == (uint) length)
+ return res;
+ tmp_value.set(*res,(uint) start,(uint) length);
+ return &tmp_value;
+}
+
+
+void Item_func_substr::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+
+ if (args[1]->const_item())
+ {
+ int32 start=(int32) args[1]->val_int()-1;
+ if (start < 0 || start >= (int32) max_length)
+ max_length=0; /* purecov: inspected */
+ else
+ max_length-= (uint) start;
+ }
+ if (arg_count == 3 && args[2]->const_item())
+ {
+ int32 length= (int32) args[2]->val_int();
+ if (length <= 0)
+ max_length=0; /* purecov: inspected */
+ else
+ set_if_smaller(max_length,(uint) length);
+ }
+}
+
+
+String *Item_func_substr_index::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ String *delimeter =args[1]->val_str(&tmp_value);
+ int32 count = (int32) args[2]->val_int();
+ uint offset;
+
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value)
+ { // string and/or delim are null
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ uint delimeter_length=delimeter->length();
+ if (!res->length() || !delimeter_length || !count)
+ return &empty_string; // Wrong parameters
+
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ const char *ptr=res->ptr();
+ const char *strend = ptr+res->length();
+ const char *end=strend-delimeter_length+1;
+ const char *search=delimeter->ptr();
+ const char *search_end=search+delimeter_length;
+ int32 n=0,c=count,pass;
+ register uint32 l;
+ for (pass=(count>0);pass<2;++pass)
+ {
+ while (ptr < end)
+ {
+ if (*ptr == *search)
+ {
+ register char *i,*j;
+ i=(char*) ptr+1; j=(char*) search+1;
+ while (j != search_end)
+ if (*i++ != *j++) goto skipp;
+ if (pass==0) ++n;
+ else if (!--c) break;
+ ptr+=delimeter_length;
+ continue;
+ }
+ skipp:
+ if ((l=my_ismbchar(default_charset_info, ptr,strend))) ptr+=l;
+ else ++ptr;
+ } /* either not found or got total number when count<0 */
+ if (pass == 0) /* count<0 */
+ {
+ c+=n+1;
+ if (c<=0) return res; /* not found, return original string */
+ ptr=res->ptr();
+ }
+ else
+ {
+ if (c) return res; /* Not found, return original string */
+ if (count>0) /* return left part */
+ {
+ tmp_value.set(*res,0,ptr-res->ptr());
+ }
+ else /* return right part */
+ {
+ ptr+=delimeter_length;
+ tmp_value.set(*res,ptr-res->ptr(),strend-ptr);
+ }
+ }
+ }
+ }
+ else
+#endif /* USE_MB */
+ {
+ if (count > 0)
+ { // start counting from the beginning
+ for (offset=0 ;; offset+=delimeter_length)
+ {
+ if ((int) (offset=res->strstr(*delimeter,offset)) < 0)
+ return res; // Didn't find, return org string
+ if (!--count)
+ {
+ tmp_value.set(*res,0,offset);
+ break;
+ }
+ }
+ }
+ else
+ { // Start counting at end
+ for (offset=res->length() ; ; offset-=delimeter_length)
+ {
+ if ((int) (offset=res->strrstr(*delimeter,offset)) < 0)
+ return res; // Didn't find, return org string
+ if (!++count)
+ {
+ offset+=delimeter_length;
+ tmp_value.set(*res,offset,res->length()- offset);
+ break;
+ }
+ }
+ }
+ }
+ return (&tmp_value);
+}
+
+/*
+** The trim functions are extension to ANSI SQL because they trim substrings
+** They ltrim() and rtrim() functions are optimized for 1 byte strings
+** They also return the original string if possible, else they return
+** a substring that points at the original string.
+*/
+
+
+String *Item_func_ltrim::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ String *remove_str=args[1]->val_str(&tmp);
+ uint remove_length;
+ LINT_INIT(remove_length);
+
+ if (!remove_str || (remove_length=remove_str->length()) == 0 ||
+ remove_length > res->length())
+ return res;
+
+ char *ptr=(char*) res->ptr();
+ char *end=ptr+res->length();
+ if (remove_length == 1)
+ {
+ char chr=(*remove_str)[0];
+ while (ptr != end && *ptr == chr)
+ ptr++;
+ }
+ else
+ {
+ const char *r_ptr=remove_str->ptr();
+ end-=remove_length;
+ while (ptr < end && !memcmp(ptr,r_ptr,remove_length))
+ ptr+=remove_length;
+ end+=remove_length;
+ }
+ if (ptr == res->ptr())
+ return res;
+ tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr));
+ return &tmp_value;
+}
+
+
+String *Item_func_rtrim::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ String *remove_str=args[1]->val_str(&tmp);
+ uint remove_length;
+ LINT_INIT(remove_length);
+
+ if (!remove_str || (remove_length=remove_str->length()) == 0 ||
+ remove_length > res->length())
+ return res;
+
+ char *ptr=(char*) res->ptr();
+ char *end=ptr+res->length();
+#ifdef USE_MB
+ char *p=ptr;
+ register uint32 l;
+#endif
+ if (remove_length == 1)
+ {
+ char chr=(*remove_str)[0];
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ while (ptr < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end))) ptr+=l,p=ptr;
+ else ++ptr;
+ }
+ ptr=p;
+ }
+#endif
+ while (ptr != end && end[-1] == chr)
+ end--;
+ }
+ else
+ {
+ const char *r_ptr=remove_str->ptr();
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ loop:
+ while (ptr + remove_length < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end))) ptr+=l;
+ else ++ptr;
+ }
+ if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
+ {
+ end-=remove_length;
+ ptr=p;
+ goto loop;
+ }
+ }
+ else
+#endif /* USE_MB */
+ {
+ while (ptr + remove_length < end &&
+ !memcmp(end-remove_length,r_ptr,remove_length))
+ end-=remove_length;
+ }
+ }
+ if (end == res->ptr()+res->length())
+ return res;
+ tmp_value.set(*res,0,(uint) (end-res->ptr()));
+ return &tmp_value;
+}
+
+
+String *Item_func_trim::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ String *remove_str=args[1]->val_str(&tmp);
+ uint remove_length;
+ LINT_INIT(remove_length);
+
+ if (!remove_str || (remove_length=remove_str->length()) == 0 ||
+ remove_length > res->length())
+ return res;
+
+ char *ptr=(char*) res->ptr();
+ char *end=ptr+res->length();
+ const char *r_ptr=remove_str->ptr();
+ while (ptr+remove_length < end && !memcmp(ptr,r_ptr,remove_length))
+ ptr+=remove_length;
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ char *p=ptr;
+ register uint32 l;
+ loop:
+ while (ptr + remove_length < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end))) ptr+=l;
+ else ++ptr;
+ }
+ if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
+ {
+ end-=remove_length;
+ ptr=p;
+ goto loop;
+ }
+ ptr=p;
+ }
+ else
+#endif /* USE_MB */
+ {
+ while (ptr + remove_length < end &&
+ !memcmp(end-remove_length,r_ptr,remove_length))
+ end-=remove_length;
+ }
+ if (ptr == res->ptr() && end == ptr+res->length())
+ return res;
+ tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr));
+ return &tmp_value;
+}
+
+
+String *Item_func_password::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0;
+ if (res->length() == 0)
+ return &empty_string;
+ make_scrambled_password(tmp_value,res->c_ptr());
+ str->set(tmp_value,16);
+ return str;
+}
+
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+String *Item_func_encrypt::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+
+#ifdef HAVE_CRYPT
+ char salt[3],*salt_ptr;
+ if ((null_value=args[0]->null_value))
+ return 0;
+ if (res->length() == 0)
+ return &empty_string;
+
+ if (arg_count == 1)
+ { // generate random salt
+ time_t timestamp=current_thd->query_start();
+ salt[0] = bin_to_ascii(timestamp & 0x3f);
+ salt[1] = bin_to_ascii((timestamp >> 5) & 0x3f);
+ salt[2] = 0;
+ salt_ptr=salt;
+ }
+ else
+ { // obtain salt from the first two bytes
+ String *salt_str=args[1]->val_str(&tmp_value);
+ if ((null_value= (args[1]->null_value || salt_str->length() < 2)))
+ return 0;
+ salt_ptr= salt_str->c_ptr();
+ }
+ pthread_mutex_lock(&LOCK_crypt);
+ char *tmp=crypt(res->c_ptr(),salt_ptr);
+ str->set(tmp,strlen(tmp));
+ str->copy();
+ pthread_mutex_unlock(&LOCK_crypt);
+ return str;
+#else
+ null_value=1;
+ return 0;
+#endif /* HAVE_CRYPT */
+}
+
+void Item_func_encode::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ maybe_null=args[0]->maybe_null;
+}
+
+String *Item_func_encode::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ sql_crypt.init();
+ sql_crypt.encode((char*) res->ptr(),res->length());
+ return res;
+}
+
+String *Item_func_decode::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ sql_crypt.init();
+ sql_crypt.decode((char*) res->ptr(),res->length());
+ return res;
+}
+
+
+String *Item_func_database::val_str(String *str)
+{
+ if (!current_thd->db)
+ str->length(0);
+ else
+ str->set((const char*) current_thd->db,strlen(current_thd->db));
+ return str;
+}
+
+String *Item_func_user::val_str(String *str)
+{
+ THD *thd=current_thd;
+ if (str->copy((const char*) thd->user,strlen(thd->user)) ||
+ str->append('@') ||
+ str->append(thd->host ? thd->host : thd->ip ? thd->ip : ""))
+ return &empty_string;
+ return str;
+}
+
+void Item_func_soundex::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ set_if_bigger(max_length,4);
+}
+
+
+ /*
+ If alpha, map input letter to soundex code.
+ If not alpha and remove_garbage is set then skipp to next char
+ else return 0
+ */
+
+extern "C" {
+extern char *soundex_map; // In mysys/static.c
+}
+
+static char get_scode(char *ptr)
+{
+ uchar ch=toupper(*ptr);
+ if (ch < 'A' || ch > 'Z')
+ {
+ // Thread extended alfa (country spec)
+ return '0'; // as vokal
+ }
+ return(soundex_map[ch-'A']);
+}
+
+
+String *Item_func_soundex::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ char last_ch,ch;
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+
+ if (str_value.alloc(max(res->length(),4)))
+ return str; /* purecov: inspected */
+ char *to= (char *) str_value.ptr();
+ char *from= (char *) res->ptr(), *end=from+res->length();
+
+ while (from != end && isspace(*from)) // Skipp pre-space
+ from++; /* purecov: inspected */
+ if (from == end)
+ return &empty_string; // No alpha characters.
+ *to++ = toupper(*from); // Copy first letter
+ last_ch = get_scode(from); // code of the first letter
+ // for the first 'double-letter check.
+ // Loop on input letters until
+ // end of input (null) or output
+ // letter code count = 3
+ for (from++ ; from < end ; from++)
+ {
+ if (!isalpha(*from))
+ continue;
+ ch=get_scode(from);
+ if ((ch != '0') && (ch != last_ch)) // if not skipped or double
+ {
+ *to++ = ch; // letter, copy to output
+ last_ch = ch; // save code of last input letter
+ } // for next double-letter check
+ }
+ for (end=(char*) str_value.ptr()+4 ; to < end ; to++)
+ *to = '0';
+ *to=0; // end string
+ str_value.length((uint) (to-str_value.ptr()));
+ return &str_value;
+}
+
+
+/*
+** Change a number to format '3,333,333,333.000'
+** This should be 'internationalized' sometimes.
+*/
+
+Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org)
+{
+ decimals=(uint) set_zone(dec,0,30);
+}
+
+
+String *Item_func_format::val_str(String *str)
+{
+ double nr =args[0]->val();
+ uint32 diff,length,str_length;
+ uint dec;
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ dec= decimals ? decimals+1 : 0;
+ str->set(nr,decimals);
+ str_length=str->length();
+ if (nr < 0)
+ str_length--; // Don't count sign
+ length=str->length()+(diff=(str_length- dec-1)/3);
+ if (diff)
+ {
+ char *tmp,*pos;
+ str=copy_if_not_alloced(&tmp_str,str,length);
+ str->length(length);
+ tmp=(char*) str->ptr()+length - dec-1;
+ for (pos=(char*) str->ptr()+length ; pos != tmp; pos--)
+ pos[0]=pos[- (int) diff];
+ while (diff)
+ {
+ pos[0]=pos[-(int) diff]; pos--;
+ pos[0]=pos[-(int) diff]; pos--;
+ pos[0]=pos[-(int) diff]; pos--;
+ pos[0]=',';
+ pos--;
+ diff--;
+ }
+ }
+ return str;
+}
+
+
+void Item_func_elt::fix_length_and_dec()
+{
+ max_length=0;
+ decimals=0;
+ for (uint i=1 ; i < arg_count ; i++)
+ {
+ set_if_bigger(max_length,args[i]->max_length);
+ set_if_bigger(decimals,args[i]->decimals);
+ }
+ maybe_null=1; // NULL if wrong first arg
+ used_tables_cache|=item->used_tables();
+}
+
+
+void Item_func_elt::update_used_tables()
+{
+ Item_func::update_used_tables();
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+}
+
+
+double Item_func_elt::val()
+{
+ uint tmp;
+ if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ return args[tmp-1]->val();
+}
+
+longlong Item_func_elt::val_int()
+{
+ uint tmp;
+ if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return args[tmp-1]->val_int();
+}
+
+String *Item_func_elt::val_str(String *str)
+{
+ uint tmp;
+ if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count)
+ {
+ null_value=1;
+ return NULL;
+ }
+ null_value=0;
+ return args[tmp-1]->val_str(str);
+}
+
+
+void Item_func_make_set::fix_length_and_dec()
+{
+ max_length=arg_count-1;
+ for (uint i=1 ; i < arg_count ; i++)
+ max_length+=args[i]->max_length;
+ used_tables_cache|=item->used_tables();
+}
+
+
+void Item_func_make_set::update_used_tables()
+{
+ Item_func::update_used_tables();
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+}
+
+
+String *Item_func_make_set::val_str(String *str)
+{
+ ulonglong bits;
+ bool first_found=0;
+ Item **ptr=args;
+ String *result=&empty_string;
+
+ bits=item->val_int();
+ if ((null_value=item->null_value))
+ return NULL;
+
+ if (arg_count < 64)
+ bits &= ((ulonglong) 1 << arg_count)-1;
+
+ for (; bits; bits >>= 1, ptr++)
+ {
+ if (bits & 1)
+ {
+ String *res= (*ptr)->val_str(str);
+ if (res) // Skipp nulls
+ {
+ if (!first_found)
+ { // First argument
+ first_found=1;
+ if (res != str)
+ result=res; // Use original string
+ else
+ {
+ if (tmp_str.copy(*res)) // Don't use 'str'
+ return &empty_string;
+ result= &tmp_str;
+ }
+ }
+ else
+ {
+ if (result != &tmp_str)
+ { // Copy data to tmp_str
+ if (tmp_str.alloc(result->length()+res->length()+1) ||
+ tmp_str.copy(*result))
+ return &empty_string;
+ result= &tmp_str;
+ }
+ if (tmp_str.append(',') || tmp_str.append(*res))
+ return &empty_string;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+
+String *Item_func_char::val_str(String *str)
+{
+ str->length(0);
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ int32 num=(int32) args[i]->val_int();
+ if (!args[i]->null_value)
+#ifdef USE_MB
+ if (use_mb(default_charset_info))
+ {
+ if (num&0xFF000000L) {
+ str->append((char)(num>>24));
+ goto b2;
+ } else if (num&0xFF0000L) {
+b2: str->append((char)(num>>16));
+ goto b1;
+ } else if (num&0xFF00L) {
+b1: str->append((char)(num>>8));
+ }
+ }
+#endif
+ str->append((char)num);
+ }
+ str->realloc(str->length()); // Add end 0 (for Purify)
+ return str;
+}
+
+
+inline String* alloc_buffer(String *res,String *str,String *tmp_value,
+ ulong length)
+{
+ if (res->alloced_length() < length)
+ {
+ if (str->alloced_length() >= length)
+ {
+ (void) str->copy(*res);
+ str->length(length);
+ return str;
+ }
+ else
+ {
+ if (tmp_value->alloc(length))
+ return 0;
+ (void) tmp_value->copy(*res);
+ tmp_value->length(length);
+ return tmp_value;
+ }
+ }
+ res->length(length);
+ return res;
+}
+
+
+void Item_func_repeat::fix_length_and_dec()
+{
+ if (args[1]->const_item())
+ {
+ max_length=(long) (args[0]->max_length * args[1]->val_int());
+ if (max_length >= MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+ }
+ else
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+/*
+** Item_func_repeat::str is carefully written to avoid reallocs
+** as much as possible at the cost of a local buffer
+*/
+
+String *Item_func_repeat::val_str(String *str)
+{
+ uint length,tot_length;
+ char *to;
+ long count= (long) args[1]->val_int();
+ String *res =args[0]->val_str(str);
+
+ if (args[0]->null_value || args[1]->null_value)
+ goto err; // string and/or delim are null
+ null_value=0;
+ if (count <= 0) // For nicer SQL code
+ return &empty_string;
+ if (count == 1) // To avoid reallocs
+ return res;
+ length=res->length();
+ if (length > max_allowed_packet/count)// Safe length check
+ goto err; // Probably an error
+ tot_length= length*(uint) count;
+ if (!(res= alloc_buffer(res,str,&tmp_value,tot_length)))
+ goto err;
+
+ to=(char*) res->ptr()+length;
+ while (--count)
+ {
+ memcpy(to,res->ptr(),length);
+ to+=length;
+ }
+ return (res);
+
+err:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_rpad::fix_length_and_dec()
+{
+ if (args[1]->const_item())
+ {
+ uint32 length= (uint32) args[1]->val_int();
+ max_length=max(args[0]->max_length,length);
+ if (max_length >= MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+ }
+ else
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_rpad::val_str(String *str)
+{
+ uint32 res_length,length_pad;
+ char *to;
+ const char *ptr_pad;
+ int32 count= (int32) args[1]->val_int();
+ String *res =args[0]->val_str(str);
+ String *rpad = args[2]->val_str(str);
+
+ if (!res || args[1]->null_value || !rpad)
+ goto err;
+ null_value=0;
+ if (count <= (int32) (res_length=res->length()))
+ return (res); // String to pad is big enough
+ length_pad= rpad->length();
+ if ((ulong) count > max_allowed_packet || args[2]->null_value || !length_pad)
+ goto err;
+ if (!(res= alloc_buffer(res,str,&tmp_value,count)))
+ goto err;
+
+ to= (char*) res->ptr()+res_length;
+ ptr_pad=rpad->ptr();
+ for (count-= res_length; (uint32) count > length_pad; count-= length_pad)
+ {
+ memcpy(to,ptr_pad,length_pad);
+ to+= length_pad;
+ }
+ memcpy(to,ptr_pad,(size_t) count);
+ return (res);
+
+ err:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_lpad::fix_length_and_dec()
+{
+ if (args[1]->const_item())
+ {
+ uint32 length= (uint32) args[1]->val_int();
+ max_length=max(args[0]->max_length,length);
+ if (max_length >= MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+ }
+ else
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_lpad::val_str(String *str)
+{
+ uint32 res_length,length_pad;
+ char *to;
+ const char *ptr_pad;
+ ulong count= (long) args[1]->val_int();
+ String *res= args[0]->val_str(str);
+ String *lpad= args[2]->val_str(str);
+
+ if (!res || args[1]->null_value || !lpad)
+ goto err;
+ null_value=0;
+ if (count <= (res_length=res->length()))
+ return (res); // String to pad is big enough
+ length_pad= lpad->length();
+ if (count > max_allowed_packet || args[2]->null_value || !length_pad)
+ goto err;
+
+ if (res->alloced_length() < count)
+ {
+ if (str->alloced_length() >= count)
+ {
+ memcpy((char*) str->ptr()+(count-res_length),res->ptr(),res_length);
+ res=str;
+ }
+ else
+ {
+ if (tmp_value.alloc(count))
+ goto err;
+ memcpy((char*) tmp_value.ptr()+(count-res_length),res->ptr(),res_length);
+ res=&tmp_value;
+ }
+ }
+ else
+ bmove_upp((char*) res->ptr()+count,res->ptr()+res_length,res_length);
+ res->length(count);
+
+ to= (char*) res->ptr();
+ ptr_pad= lpad->ptr();
+ for (count-= res_length; count > length_pad; count-= length_pad)
+ {
+ memcpy(to,ptr_pad,length_pad);
+ to+= length_pad;
+ }
+ memcpy(to,ptr_pad,(size_t) count);
+ return (res);
+
+ err:
+ null_value=1;
+ return 0;
+}
+
+
+String *Item_func_conv::val_str(String *str)
+{
+ String *res= args[0]->val_str(str);
+ char *endptr,ans[65],*ptr;
+ longlong dec;
+ int from_base= (int) args[1]->val_int();
+ int to_base= (int) args[2]->val_int();
+
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
+ abs(to_base) > 36 || abs(to_base) < 2 ||
+ abs(from_base) > 36 || abs(from_base) < 2 || !(res->length()))
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ if (from_base < 0)
+ dec= strtoll(res->c_ptr(),&endptr,-from_base);
+ else
+ dec= (longlong) strtoull(res->c_ptr(),&endptr,from_base);
+ ptr= longlong2str(dec,ans,to_base);
+ if (str->copy(ans,(uint32) (ptr-ans)))
+ return &empty_string;
+ return str;
+}
+
+#include <my_dir.h> // For my_stat
+
+String *Item_load_file::val_str(String *str)
+{
+ String *file_name;
+ File file;
+ MY_STAT stat_info;
+ DBUG_ENTER("load_file");
+
+ if (!(file_name= args[0]->val_str(str)) ||
+ !(current_thd->master_access & FILE_ACL) ||
+ !my_stat(file_name->c_ptr(), &stat_info, MYF(MY_FAE)))
+ goto err;
+ if (!(stat_info.st_mode & S_IROTH))
+ {
+ /* my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), file_name->c_ptr()); */
+ goto err;
+ }
+ if (stat_info.st_size > (long) max_allowed_packet)
+ {
+ /* my_error(ER_TOO_LONG_STRING, MYF(0), file_name->c_ptr()); */
+ goto err;
+ }
+ if (tmp_value.alloc(stat_info.st_size))
+ goto err;
+ if ((file = my_open(file_name->c_ptr(), O_RDONLY, MYF(0))) < 0)
+ goto err;
+ if (my_read(file, (byte*) tmp_value.ptr(), stat_info.st_size, MYF(MY_NABP)))
+ {
+ my_close(file, MYF(0));
+ goto err;
+ }
+ tmp_value.length(stat_info.st_size);
+ my_close(file, MYF(0));
+ null_value = 0;
+ return &tmp_value;
+
+err:
+ null_value = 1;
+ DBUG_RETURN(0);
+}
+
+
+String* Item_func_export_set::val_str(String* str)
+{
+ ulonglong the_set = (ulonglong) args[0]->val_int();
+ String yes_buf, *yes;
+ yes = args[1]->val_str(&yes_buf);
+ String no_buf, *no;
+ no = args[2]->val_str(&no_buf);
+ String *sep = NULL, sep_buf ;
+
+ uint num_set_values = 64;
+ ulonglong mask = 0x1;
+ str->length(0);
+
+ /* Check if some argument is a NULL value */
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ switch(arg_count) {
+ case 5:
+ num_set_values = (uint) args[4]->val_int();
+ if (num_set_values > 64)
+ num_set_values=64;
+ if (args[4]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ /* Fall through */
+ case 4:
+ if (!(sep = args[3]->val_str(&sep_buf))) // Only true if NULL
+ {
+ null_value=1;
+ return 0;
+ }
+ break;
+ case 3:
+ sep_buf.set(",", 1);
+ sep = &sep_buf;
+ }
+ null_value=0;
+
+ for (uint i = 0; i < num_set_values; i++, mask = (mask << 1))
+ {
+ if (the_set & mask)
+ str->append(*yes);
+ else
+ str->append(*no);
+ if(i != num_set_values - 1)
+ str->append(*sep);
+ }
+ return str;
+}
+
+void Item_func_export_set::fix_length_and_dec()
+{
+ uint length=max(args[1]->max_length,args[2]->max_length);
+ uint sep_length=(arg_count > 3 ? args[3]->max_length : 1);
+ max_length=length*64+sep_length*63;
+}
+
+String* Item_func_inet_ntoa::val_str(String* str)
+{
+ uchar buf[8], *p;
+ ulonglong n = (ulonglong) args[0]->val_int();
+ char num[4];
+ // we do not know if args[0] is NULL until we have called
+ // some val function on it if args[0] is not a constant!
+ if ((null_value=args[0]->null_value))
+ return 0; // Null value
+ str->length(0);
+ int8store(buf,n);
+
+ // now we can assume little endian
+ // we handle the possibility of an 8-byte IP address
+ // however, we do not want to confuse those who are just using
+ // 4 byte ones
+
+ for (p= buf + 8; p > buf+4 && p[-1] == 0 ; p-- ) ;
+ num[3]='.';
+ while (p-- > buf)
+ {
+ uint c = *p;
+ uint n1,n2; // Try to avoid divisions
+ n1= c / 100; // 100 digits
+ c-= n1*100;
+ n2= c / 10; // 10 digits
+ c-=n2*10; // last digit
+ num[0]=(char) n1+'0';
+ num[1]=(char) n2+'0';
+ num[2]=(char) c+'0';
+ uint length=(n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero
+
+ (void) str->append(num+4-length,length);
+ }
+ str->length(str->length()-1); // Remove last '.';
+ return str;
+}