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