/* Copyright (C) 2000 MySQL 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 is originally from the mysql distribution. Coded by monty */ #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif #include <my_global.h> #include <my_sys.h> #include <m_string.h> #include <m_ctype.h> #ifdef HAVE_FCONVERT #include <floatingpoint.h> #endif extern gptr sql_alloc(unsigned size); extern void sql_element_free(void *ptr); #include "sql_string.h" /***************************************************************************** ** String functions *****************************************************************************/ bool String::real_alloc(uint32 arg_length) { arg_length=ALIGN_SIZE(arg_length+1); str_length=0; if (Alloced_length < arg_length) { free(); if (!(Ptr=(char*) my_malloc(arg_length,MYF(MY_WME)))) return TRUE; Alloced_length=arg_length; alloced=1; } Ptr[0]=0; return FALSE; } /* ** Check that string is big enough. Set string[alloc_length] to 0 ** (for C functions) */ bool String::realloc(uint32 alloc_length) { uint32 len=ALIGN_SIZE(alloc_length+1); if (Alloced_length < len) { char *new_ptr; if (alloced) { if ((new_ptr= (char*) my_realloc(Ptr,len,MYF(MY_WME)))) { Ptr=new_ptr; Alloced_length=len; } else return TRUE; // Signal error } else if ((new_ptr= (char*) my_malloc(len,MYF(MY_WME)))) { if (str_length) // Avoid bugs in memcpy on AIX memcpy(new_ptr,Ptr,str_length); new_ptr[str_length]=0; Ptr=new_ptr; Alloced_length=len; alloced=1; } else return TRUE; // Signal error } Ptr[alloc_length]=0; // This make other funcs shorter return FALSE; } bool String::set(longlong num, CHARSET_INFO *cs) { uint l=20*cs->mbmaxlen+1; if (alloc(l)) return TRUE; if (cs->cset->snprintf == my_snprintf_8bit) { str_length=(uint32) (longlong10_to_str(num,Ptr,-10)-Ptr); } else { str_length=cs->cset->snprintf(cs,Ptr,l,"%d",num); } str_charset=cs; return FALSE; } bool String::set(ulonglong num, CHARSET_INFO *cs) { uint l=20*cs->mbmaxlen+1; if (alloc(l)) return TRUE; if (cs->cset->snprintf == my_snprintf_8bit) { str_length=(uint32) (longlong10_to_str(num,Ptr,10)-Ptr); } else { str_length=cs->cset->snprintf(cs,Ptr,l,"%d",num); } str_charset=cs; return FALSE; } bool String::set(double num,uint decimals, CHARSET_INFO *cs) { char buff[331]; str_charset=cs; if (decimals >= NOT_FIXED_DEC) { sprintf(buff,"%.14g",num); // Enough for a DATETIME return copy(buff, (uint32) strlen(buff), &my_charset_latin1, cs); } #ifdef HAVE_FCONVERT int decpt,sign; char *pos,*to; VOID(fconvert(num,(int) decimals,&decpt,&sign,buff+1)); if (!my_isdigit(&my_charset_latin1, buff[1])) { // Nan or Inf pos=buff+1; if (sign) { buff[0]='-'; pos=buff; } return copy(pos,(uint32) strlen(pos), &my_charset_latin1, cs); } if (alloc((uint32) ((uint32) decpt+3+decimals))) return TRUE; to=Ptr; if (sign) *to++='-'; pos=buff+1; if (decpt < 0) { /* value is < 0 */ *to++='0'; if (!decimals) goto end; *to++='.'; if ((uint32) -decpt > decimals) decpt= - (int) decimals; decimals=(uint32) ((int) decimals+decpt); while (decpt++ < 0) *to++='0'; } else if (decpt == 0) { *to++= '0'; if (!decimals) goto end; *to++='.'; } else { while (decpt-- > 0) *to++= *pos++; if (!decimals) goto end; *to++='.'; } while (decimals--) *to++= *pos++; end: *to=0; str_length=(uint32) (to-Ptr); return FALSE; #else #ifdef HAVE_SNPRINTF buff[sizeof(buff)-1]=0; // Safety snprintf(buff,sizeof(buff)-1, "%.*f",(int) decimals,num); #else sprintf(buff,"%.*f",(int) decimals,num); #endif return copy(buff,(uint32) strlen(buff), &my_charset_latin1, cs); #endif } bool String::copy() { if (!alloced) { Alloced_length=0; // Force realloc return realloc(str_length); } return FALSE; } bool String::copy(const String &str) { if (alloc(str.str_length)) return TRUE; str_length=str.str_length; bmove(Ptr,str.Ptr,str_length); // May be overlapping Ptr[str_length]=0; str_charset=str.str_charset; return FALSE; } bool String::copy(const char *str,uint32 arg_length, CHARSET_INFO *cs) { if (alloc(arg_length)) return TRUE; if ((str_length=arg_length)) memcpy(Ptr,str,arg_length); Ptr[arg_length]=0; str_charset=cs; return FALSE; } /* Copy with charset convertion */ bool String::copy(const char *str,uint32 arg_length, CHARSET_INFO *from, CHARSET_INFO *to) { uint32 new_length=to->mbmaxlen*arg_length; int cnvres; my_wc_t wc; const uchar *s=(const uchar *)str; const uchar *se=s+arg_length; uchar *d, *de; if (alloc(new_length)) return TRUE; d=(uchar *)Ptr; de=d+new_length; for (str_length=new_length ; s < se && d < de ; ) { if ((cnvres=from->cset->mb_wc(from,&wc,s,se)) > 0 ) { s+=cnvres; } else if (cnvres==MY_CS_ILSEQ) { s++; wc='?'; } else break; outp: if((cnvres=to->cset->wc_mb(to,wc,d,de)) >0 ) { d+=cnvres; } else if (cnvres==MY_CS_ILUNI && wc!='?') { wc='?'; goto outp; } else break; } Ptr[new_length]=0; length((uint32) (d-(uchar *)Ptr)); str_charset=to; return FALSE; } /* This is used by mysql.cc */ bool String::fill(uint32 max_length,char fill_char) { if (str_length > max_length) Ptr[str_length=max_length]=0; else { if (realloc(max_length)) return TRUE; bfill(Ptr+str_length,max_length-str_length,fill_char); str_length=max_length; } return FALSE; } void String::strip_sp() { while (str_length && my_isspace(str_charset,Ptr[str_length-1])) str_length--; } bool String::append(const String &s) { if (s.length()) { if (realloc(str_length+s.length())) return TRUE; memcpy(Ptr+str_length,s.ptr(),s.length()); str_length+=s.length(); } return FALSE; } bool String::append(const char *s,uint32 arg_length) { if (!arg_length) // Default argument if (!(arg_length= (uint32) strlen(s))) return FALSE; if (realloc(str_length+arg_length)) return TRUE; memcpy(Ptr+str_length,s,arg_length); str_length+=arg_length; return FALSE; } #ifdef TO_BE_REMOVED bool String::append(FILE* file, uint32 arg_length, myf my_flags) { if (realloc(str_length+arg_length)) return TRUE; if (my_fread(file, (byte*) Ptr + str_length, arg_length, my_flags)) { shrink(str_length); return TRUE; } str_length+=arg_length; return FALSE; } #endif bool String::append(IO_CACHE* file, uint32 arg_length) { if (realloc(str_length+arg_length)) return TRUE; if (my_b_read(file, (byte*) Ptr + str_length, arg_length)) { shrink(str_length); return TRUE; } str_length+=arg_length; return FALSE; } uint32 String::numchars() { #ifdef USE_MB register uint32 n=0,mblen; register const char *mbstr=Ptr; register const char *end=mbstr+str_length; if (use_mb(str_charset)) { while (mbstr < end) { if ((mblen=my_ismbchar(str_charset, mbstr,end))) mbstr+=mblen; else ++mbstr; ++n; } return n; } else #endif return str_length; } int String::charpos(int i,uint32 offset) { #ifdef USE_MB register uint32 mblen; register const char *mbstr=Ptr+offset; register const char *end=Ptr+str_length; if (use_mb(str_charset)) { if (i<=0) return i; while (i && mbstr < end) { if ((mblen=my_ismbchar(str_charset, mbstr,end))) mbstr+=mblen; else ++mbstr; --i; } if ( INT_MAX32-i <= (int) (mbstr-Ptr-offset)) return INT_MAX32; else return (int) ((mbstr-Ptr-offset)+i); } else #endif return i; } int String::strstr(const String &s,uint32 offset) { if (s.length()+offset <= str_length) { if (!s.length()) return ((int) offset); // Empty string is always found register const char *str = Ptr+offset; register const char *search=s.ptr(); const char *end=Ptr+str_length-s.length()+1; const char *search_end=s.ptr()+s.length(); skip: while (str != end) { if (*str++ == *search) { register char *i,*j; i=(char*) str; j=(char*) search+1; while (j != search_end) if (*i++ != *j++) goto skip; return (int) (str-Ptr) -1; } } } return -1; } /* Search after a string without regarding to case This needs to be replaced when we have character sets per string */ int String::strstr_case(const String &s,uint32 offset) { if (s.length()+offset <= str_length) { if (!s.length()) return ((int) offset); // Empty string is always found register const char *str = Ptr+offset; register const char *search=s.ptr(); const char *end=Ptr+str_length-s.length()+1; const char *search_end=s.ptr()+s.length(); skip: while (str != end) { if (str_charset->sort_order[*str++] == str_charset->sort_order[*search]) { register char *i,*j; i=(char*) str; j=(char*) search+1; while (j != search_end) if (str_charset->sort_order[*i++] != str_charset->sort_order[*j++]) goto skip; return (int) (str-Ptr) -1; } } } return -1; } /* ** Search string from end. Offset is offset to the end of string */ int String::strrstr(const String &s,uint32 offset) { if (s.length() <= offset && offset <= str_length) { if (!s.length()) return offset; // Empty string is always found register const char *str = Ptr+offset-1; register const char *search=s.ptr()+s.length()-1; const char *end=Ptr+s.length()-2; const char *search_end=s.ptr()-1; skip: while (str != end) { if (*str-- == *search) { register char *i,*j; i=(char*) str; j=(char*) search-1; while (j != search_end) if (*i-- != *j--) goto skip; return (int) (i-Ptr) +1; } } } return -1; } /* ** replace substring with string ** If wrong parameter or not enough memory, do nothing */ bool String::replace(uint32 offset,uint32 arg_length,const String &to) { long diff = (long) to.length()-(long) arg_length; if (offset+arg_length <= str_length) { if (diff < 0) { if (to.length()) memcpy(Ptr+offset,to.ptr(),to.length()); bmove(Ptr+offset+to.length(),Ptr+offset+arg_length, str_length-offset-arg_length); } else { if (diff) { if (realloc(str_length+(uint32) diff)) return TRUE; bmove_upp(Ptr+str_length+diff,Ptr+str_length, str_length-offset-arg_length); } if (to.length()) memcpy(Ptr+offset,to.ptr(),to.length()); } str_length+=(uint32) diff; } return FALSE; } // added by Holyfoot for "geometry" needs int String::reserve(uint32 space_needed, uint32 grow_by) { if (Alloced_length < str_length + space_needed) { if (realloc(Alloced_length + max(space_needed, grow_by) - 1)) return TRUE; } return FALSE; } void String::qs_append(const char *str) { int len = (int)strlen(str); memcpy(Ptr + str_length, str, len + 1); str_length += len; } void String::qs_append(double d) { char *buff = Ptr + str_length; sprintf(buff,"%.14g", d); str_length += (int)strlen(buff); } void String::qs_append(double *d) { double ld; float8get(ld, (char*) d); qs_append(ld); } void String::qs_append(const char &c) { Ptr[str_length] = c; str_length += sizeof(c); } int sortcmp(const String *x,const String *y) { const char *s= x->ptr(); const char *t= y->ptr(); uint32 x_len=x->length(),y_len=y->length(),len=min(x_len,y_len); if (use_strnxfrm(x->str_charset)) { #ifndef CMP_ENDSPACE while (x_len && my_isspace(x->str_charset,s[x_len-1])) x_len--; while (y_len && my_isspace(x->str_charset,t[y_len-1])) y_len--; #endif return my_strnncoll(x->str_charset, (unsigned char *)s,x_len,(unsigned char *)t,y_len); } else { x_len-=len; // For easy end space test y_len-=len; if (x->str_charset->sort_order) { while (len--) { if (x->str_charset->sort_order[(uchar) *s++] != x->str_charset->sort_order[(uchar) *t++]) return ((int) x->str_charset->sort_order[(uchar) s[-1]] - (int) x->str_charset->sort_order[(uchar) t[-1]]); } } else { while (len--) { if (*s++ != *t++) return ((int) s[-1] - (int) t[-1]); } } #ifndef CMP_ENDSPACE /* Don't compare end space in strings */ { if (y_len) { const char *end=t+y_len; for (; t != end ; t++) if (!my_isspace(x->str_charset,*t)) return -1; } else { const char *end=s+x_len; for (; s != end ; s++) if (!my_isspace(x->str_charset,*s)) return 1; } return 0; } #else return (int) (x_len-y_len); #endif /* CMP_ENDSPACE */ } } int stringcmp(const String *x,const String *y) { const char *s= x->ptr(); const char *t= y->ptr(); uint32 x_len=x->length(),y_len=y->length(),len=min(x_len,y_len); while (len--) { if (*s++ != *t++) return ((int) (uchar) s[-1] - (int) (uchar) t[-1]); } return (int) (x_len-y_len); } String *copy_if_not_alloced(String *to,String *from,uint32 from_length) { if (from->Alloced_length >= from_length) return from; if (from->alloced || !to || from == to) { (void) from->realloc(from_length); return from; } if (to->realloc(from_length)) return from; // Actually an error if ((to->str_length=min(from->str_length,from_length))) memcpy(to->Ptr,from->Ptr,to->str_length); to->str_charset=from->str_charset; return to; }