/* * lftp - file transfer program * * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net) * * 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 3 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, see . */ #include #include #include #include "xstring.h" #include "trio.h" #include "c-ctype.h" CDECL_BEGIN #include "memcasecmp.h" CDECL_END void xstring::get_space(size_t s) { get_space2(s,32); } void xstring::get_space2(size_t s,size_t g) { if(!buf) buf=(char*)xmalloc(size=s+1); else if(sizeMIN_SIZE) buf=(char*)xrealloc(buf,((len+1)|(MIN_SIZE-1))+1); } char *xstring::add_space(size_t s) { if(size<=len+s) get_space(len+s); return get_non_const()+len; } void xstring::init(const char *s,int len) { init(); nset(s,len); } void xstring::init(const char *s) { init(); set(s); } xstring& xstring::nset(const char *s,int len) { if(!s) { xfree(buf); init(); return *this; } this->len=len; if(s==buf) return *this; if(s>buf && so_len) cmp_len=o_len; if(cmp_len>0) { int cmp_res=memcmp(buf,o_buf,cmp_len); if(cmp_res) return cmp_res; } } if(len==o_len) return 0; return len-o_len; } static size_t vstrlen(va_list va0) { va_list va; VA_COPY(va,va0); size_t len=0; for(;;) { const char *s=va_arg(va,const char *); if(!s) break; len+=strlen(s); } va_end(va); return len; } static void vstrcpy(char *buf,va_list va0) { va_list va; VA_COPY(va,va0); for(;;) { const char *s=va_arg(va,const char *); if(!s) break; size_t s_len=strlen(s); memcpy(buf,s,s_len); buf+=s_len; } *buf=0; va_end(va); } xstring& xstring::vappend(va_list va) { size_t va_len=vstrlen(va); get_space(len+va_len); vstrcpy(buf+len,va); len+=va_len; return *this;; } xstring& xstring::vappend(...) { va_list va; va_start(va,this); vappend(va); va_end(va); return *this;; } xstring& xstring::vset(...) { truncate(0); va_list va; va_start(va,this); vappend(va); va_end(va); return *this; } void xstring::truncate(size_t n) { if(nlen) sublen=len-start; if(sublen512) get_space(len+strlen(format)+32); for(;;) { va_list tmp; VA_COPY(tmp,ap); int res=vsnprintf(buf+len, size-len, format, tmp); va_end(tmp); if(res<0) return *this; // error if((size_t)ressize-len ? len+res+1 : len+(size-len)*2); } } xstring& xstring::setf(const char *format, ...) { va_list va; va_start(va, format); vsetf(format, va); va_end(va); return *this; } xstring& xstring::appendf(const char *format, ...) { va_list va; va_start(va, format); vappendf(format, va); va_end(va); return *this; } // don't use it in nested loops xstring& xstring::get_tmp() { static xstring revolver[16]; static int i; enum { MAX_REVOLVER_SIZE=0x1000 }; if(revolver[i].size>MAX_REVOLVER_SIZE) revolver[i].shrink_space(); int next=(i+1)&15; xstring& tmp=revolver[i]; // keep the oldest tmp clear to trigger NULL dereference tmp.move_here(revolver[next]); i=next; return tmp; } char *xstring::tmp_buf(int n) { xstring& buf=get_tmp(); buf.get_space(n-1); // get_space adds a tail byte again return buf.get_non_const(); } xstring& xstring::format(const char *fmt, ...) { va_list va; va_start(va,fmt); xstring& res=vformat(fmt, va); va_end(va); return res; } xstring &xstring::cat(const char *first,...) { va_list va; va_start(va,first); xstring& str=get_tmp(first); str.vappend(va); va_end(va); return str; } xstring &xstring::join(const char *sep,int n,...) { va_list va; va_start(va,n); xstring& str=get_tmp(); str.truncate(0); while(n-->0) { const char *a=va_arg(va,const char*); if(!a || !*a) continue; if(str.length()) str.append(sep); str.append(a); } va_end(va); return str; } const char *xstring_c::vset(...) { va_list va; va_start(va,this); size_t va_len=vstrlen(va); if(!buf || strlen(buf)len; } const char *xstring::hexdump_to(xstring& buf) const { int len=length(); const char *s=get(); while(len-->0) buf.appendf("%02X",(unsigned char)*s++); return buf; } const char *xstring::hexdump() const { return hexdump_to(get_tmp("")); } const char *xstring::dump() const { return dump_to(get_tmp("")); } const char *xstring::dump_to(xstring& buf) const { if(is_binary()) { binary: if(len<1024) { buf.append("'); } else { buf.appendf("",(int)length()); } } else { int old_buf_len=buf.length(); int len=length(); const char *s=get(); size_t invalid=0; while(len>0) { int ch_len=mblen(s,len); int ch_width=-1; if(ch_len<1) { ch_len=1; } else { ch_width=mbsnwidth(s,ch_len,0); } if(ch_width>=0) { buf.append(s,ch_len); } else { while(ch_len>0) { buf.appendf("\\%03o",(unsigned char)*s++); ch_len--; len--; invalid++; } } s+=ch_len; len-=ch_len; } if(invalid*32>length()) { buf.truncate(old_buf_len); goto binary; } } return buf; } int xstring0::_url_decode(size_t len,int flags) { if(!buf) return 0; char *store=buf; const char *p=buf; int rest=len; while(rest>0) { if(rest>=3 && *p=='%' && c_isxdigit(p[1]) && c_isxdigit(p[2])) { int n; if(sscanf(p+1,"%2x",&n)==1) { *store++=n; p+=3; rest-=3; continue; } } else if(*p=='+' && (flags&URL_DECODE_PLUS)) { *store++=' '; p++; rest--; continue; } *store++=*p++; rest--; } return store-buf; } int xstring0::_hex_decode(size_t len) { if(!buf) return 0; char *store=buf; const char *p=store; int rest=len; while(rest>=2) { int n; if(!c_isxdigit(p[0]) || !c_isxdigit(p[1]) || sscanf(p,"%2x",&n)!=1) break; *store++=n; p+=2; rest-=2; } return store-buf; } xstring& xstring::url_decode(int flags) { set_length(_url_decode(length(),flags)); return *this; } xstring_c& xstring_c::url_decode(int flags) { set_length(_url_decode(length(),flags)); return *this; } xstring& xstring::hex_decode() { set_length(_hex_decode(length())); return *this; } /* Encode the unsafe characters in a given string, producing %XX encoded string. */ #define need_quote(c) (c_iscntrl((c)) \ || (!(flags&URL_ALLOW_8BIT) && !c_isascii((c))) \ || strchr(unsafe,(c))) xstring& xstring::append_url_encoded(const char *s,int len,const char *unsafe,unsigned flags) { if(!s) return *this; add_space(len+len/4); while(len-->0) { char c=*s++; if (need_quote(c)) appendf("%%%02X",(unsigned char)c); else append(c); } return *this; } xstring& xstring::c_lc() { char *p=buf; int r=len; while(r>0) { *p=c_tolower(*p); p++,r--; } return *this; } xstring& xstring::c_ucfirst() { char *p=buf; int r=len; bool first=true; while(r>0) { if(c_isalpha(*p)) { *p=(first?c_toupper(*p):c_tolower(*p)); first=false; } else { first=true; } p++,r--; } return *this; } const xstring xstring::null; const xstring_c xstring_c::null;