Blame src/xstring.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <string.h>
Packit 8f70b4
#include <mbswidth.h>
Packit 8f70b4
#include "xstring.h"
Packit 8f70b4
#include "trio.h"
Packit 8f70b4
#include "c-ctype.h"
Packit 8f70b4
CDECL_BEGIN
Packit 8f70b4
#include "memcasecmp.h"
Packit 8f70b4
CDECL_END
Packit 8f70b4
Packit 8f70b4
void xstring::get_space(size_t s)
Packit 8f70b4
{
Packit 8f70b4
   get_space2(s,32);
Packit 8f70b4
}
Packit 8f70b4
void xstring::get_space2(size_t s,size_t g)
Packit 8f70b4
{
Packit 8f70b4
   if(!buf)
Packit 8f70b4
      buf=(char*)xmalloc(size=s+1);
Packit 8f70b4
   else if(size
Packit 8f70b4
      buf=(char*)xrealloc(buf,size=(s|(g-1))+1);
Packit 8f70b4
   buf[s]=0;
Packit 8f70b4
}
Packit 8f70b4
void xstring::shrink_space()
Packit 8f70b4
{
Packit 8f70b4
   enum { MIN_SIZE=128 };
Packit 8f70b4
   if(buf && size>MIN_SIZE)
Packit 8f70b4
      buf=(char*)xrealloc(buf,((len+1)|(MIN_SIZE-1))+1);
Packit 8f70b4
}
Packit 8f70b4
char *xstring::add_space(size_t s)
Packit 8f70b4
{
Packit 8f70b4
   if(size<=len+s)
Packit 8f70b4
      get_space(len+s);
Packit 8f70b4
   return get_non_const()+len;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void xstring::init(const char *s,int len)
Packit 8f70b4
{
Packit 8f70b4
   init();
Packit 8f70b4
   nset(s,len);
Packit 8f70b4
}
Packit 8f70b4
void xstring::init(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   init();
Packit 8f70b4
   set(s);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::nset(const char *s,int len)
Packit 8f70b4
{
Packit 8f70b4
   if(!s)
Packit 8f70b4
   {
Packit 8f70b4
      xfree(buf);
Packit 8f70b4
      init();
Packit 8f70b4
      return *this;
Packit 8f70b4
   }
Packit 8f70b4
   this->len=len;
Packit 8f70b4
   if(s==buf)
Packit 8f70b4
      return *this;
Packit 8f70b4
   if(s>buf && s
Packit 8f70b4
   {
Packit 8f70b4
      memmove(buf,s,len);
Packit 8f70b4
      get_space(len);
Packit 8f70b4
      return *this;
Packit 8f70b4
   }
Packit 8f70b4
   get_space(len);
Packit 8f70b4
   memcpy(buf,s,len);
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::set(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   return nset(s,xstrlen(s));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring::xstring(const xstring_clonable& c)
Packit 8f70b4
{
Packit 8f70b4
   init();
Packit 8f70b4
   if(!c.buf)
Packit 8f70b4
      return;
Packit 8f70b4
   len=c.len;
Packit 8f70b4
   get_space(c.len);
Packit 8f70b4
   memcpy(buf,c.buf,c.len);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::set_allocated(char *s)
Packit 8f70b4
{
Packit 8f70b4
   if(!s) {
Packit 8f70b4
      unset();
Packit 8f70b4
      return *this;
Packit 8f70b4
   }
Packit 8f70b4
   len=strlen(s);
Packit 8f70b4
   size=len+1;
Packit 8f70b4
   xfree(buf);
Packit 8f70b4
   buf=s;
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::move_here(xstring& o)
Packit 8f70b4
{
Packit 8f70b4
   if(!o) {
Packit 8f70b4
      unset();
Packit 8f70b4
      return *this;
Packit 8f70b4
   }
Packit 8f70b4
   len=o.len; o.len=0;
Packit 8f70b4
   size=o.size; o.size=0;
Packit 8f70b4
   xfree(buf);
Packit 8f70b4
   buf=o.buf; o.buf=0;
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
void xstring::swap(xstring& o)
Packit 8f70b4
{
Packit 8f70b4
   buf=replace_value(o.buf,buf);
Packit 8f70b4
   size=replace_value(o.size,size);
Packit 8f70b4
   len=replace_value(o.len,len);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::append(const char *s,size_t s_len)
Packit 8f70b4
{
Packit 8f70b4
   if(!s || s_len==0)
Packit 8f70b4
      return *this;
Packit 8f70b4
   get_space(len+s_len);
Packit 8f70b4
   memcpy(buf+len,s,s_len);
Packit 8f70b4
   len+=s_len;
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::append(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   return append(s,strlen(s));
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::append(char c)
Packit 8f70b4
{
Packit 8f70b4
   get_space(len+1);
Packit 8f70b4
   buf[len++]=c;
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::append_padding(int len,char c)
Packit 8f70b4
{
Packit 8f70b4
   memset(add_space(len),c,len);
Packit 8f70b4
   add_commit(len);
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool xstring::begins_with(const char *o_buf,size_t o_len) const
Packit 8f70b4
{
Packit 8f70b4
   if(len
Packit 8f70b4
      return false;
Packit 8f70b4
   if(buf==o_buf)
Packit 8f70b4
      return true;
Packit 8f70b4
   if(!buf || !o_buf)
Packit 8f70b4
      return false;
Packit 8f70b4
   if(o_len==0)
Packit 8f70b4
      return true;
Packit 8f70b4
   return !memcmp(buf,o_buf,o_len);
Packit 8f70b4
}
Packit 8f70b4
bool xstring::ends_with(const char *o_buf,size_t o_len) const
Packit 8f70b4
{
Packit 8f70b4
   if(len
Packit 8f70b4
      return false;
Packit 8f70b4
   if(buf+len-o_len==o_buf)
Packit 8f70b4
      return true;
Packit 8f70b4
   if(!buf || !o_buf)
Packit 8f70b4
      return false;
Packit 8f70b4
   if(o_len==0)
Packit 8f70b4
      return true;
Packit 8f70b4
   return !memcmp(buf+len-o_len,o_buf,o_len);
Packit 8f70b4
}
Packit 8f70b4
bool xstring::eq(const char *o_buf,size_t o_len) const
Packit 8f70b4
{
Packit 8f70b4
   return len==o_len && begins_with(o_buf,o_len);
Packit 8f70b4
}
Packit 8f70b4
bool xstring::eq_nc(const char *o_buf,size_t o_len) const
Packit 8f70b4
{
Packit 8f70b4
   if(len!=o_len)
Packit 8f70b4
      return false;
Packit 8f70b4
   if(buf==o_buf)
Packit 8f70b4
      return true;
Packit 8f70b4
   if(!buf || !o_buf)
Packit 8f70b4
      return false;
Packit 8f70b4
   return !memcasecmp(buf,o_buf,o_len);
Packit 8f70b4
}
Packit 8f70b4
int xstring::cmp(const char *o_buf,size_t o_len) const
Packit 8f70b4
{
Packit 8f70b4
   if(buf!=o_buf) {
Packit 8f70b4
      if(!buf)
Packit 8f70b4
	 return -1;
Packit 8f70b4
      if(!o_buf)
Packit 8f70b4
	 return 1;
Packit 8f70b4
      size_t cmp_len=len;
Packit 8f70b4
      if(cmp_len>o_len)
Packit 8f70b4
	 cmp_len=o_len;
Packit 8f70b4
      if(cmp_len>0) {
Packit 8f70b4
	 int cmp_res=memcmp(buf,o_buf,cmp_len);
Packit 8f70b4
	 if(cmp_res)
Packit 8f70b4
	    return cmp_res;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(len==o_len)
Packit 8f70b4
      return 0;
Packit 8f70b4
   return len-o_len;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static size_t vstrlen(va_list va0)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   VA_COPY(va,va0);
Packit 8f70b4
   size_t len=0;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      const char *s=va_arg(va,const char *);
Packit 8f70b4
      if(!s)
Packit 8f70b4
	 break;
Packit 8f70b4
      len+=strlen(s);
Packit 8f70b4
   }
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return len;
Packit 8f70b4
}
Packit 8f70b4
static void vstrcpy(char *buf,va_list va0)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   VA_COPY(va,va0);
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      const char *s=va_arg(va,const char *);
Packit 8f70b4
      if(!s)
Packit 8f70b4
	 break;
Packit 8f70b4
      size_t s_len=strlen(s);
Packit 8f70b4
      memcpy(buf,s,s_len);
Packit 8f70b4
      buf+=s_len;
Packit 8f70b4
   }
Packit 8f70b4
   *buf=0;
Packit 8f70b4
   va_end(va);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::vappend(va_list va)
Packit 8f70b4
{
Packit 8f70b4
   size_t va_len=vstrlen(va);
Packit 8f70b4
   get_space(len+va_len);
Packit 8f70b4
   vstrcpy(buf+len,va);
Packit 8f70b4
   len+=va_len;
Packit 8f70b4
   return *this;;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::vappend(...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,this);
Packit 8f70b4
   vappend(va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return *this;;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::vset(...)
Packit 8f70b4
{
Packit 8f70b4
   truncate(0);
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,this);
Packit 8f70b4
   vappend(va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void xstring::truncate(size_t n)
Packit 8f70b4
{
Packit 8f70b4
   if(n
Packit 8f70b4
      set_length(n);
Packit 8f70b4
}
Packit 8f70b4
void xstring::truncate_at(char c)
Packit 8f70b4
{
Packit 8f70b4
   if(!buf)
Packit 8f70b4
      return;
Packit 8f70b4
   char *p=(char*)memchr(buf,c,len);
Packit 8f70b4
   if(p)
Packit 8f70b4
   {
Packit 8f70b4
      *p=0;
Packit 8f70b4
      len=p-buf;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::set_substr(int start,size_t sublen,const char *s,size_t s_len)
Packit 8f70b4
{
Packit 8f70b4
   if(start+sublen>len)
Packit 8f70b4
      sublen=len-start;
Packit 8f70b4
   if(sublen
Packit 8f70b4
      get_space(len+s_len-sublen);
Packit 8f70b4
   if(sublen!=s_len)
Packit 8f70b4
      memmove(buf+start+s_len,buf+start+sublen,len-(start+sublen)+1);
Packit 8f70b4
   memcpy(buf+start,s,s_len);
Packit 8f70b4
   len+=s_len-sublen;
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::set_substr(int start,size_t sublen,const char *s)
Packit 8f70b4
{
Packit 8f70b4
   return set_substr(start,sublen,s,xstrlen(s));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool xstring::chomp(char c)
Packit 8f70b4
{
Packit 8f70b4
   if(!len || buf[len-1]!=c)
Packit 8f70b4
      return false;
Packit 8f70b4
   buf[--len]=0;
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
void xstring::rtrim(char c)
Packit 8f70b4
{
Packit 8f70b4
   while(chomp(c));
Packit 8f70b4
}
Packit 8f70b4
unsigned xstring::skip_all(unsigned i,char c) const
Packit 8f70b4
{
Packit 8f70b4
   while(i
Packit 8f70b4
      i++;
Packit 8f70b4
   return i;
Packit 8f70b4
}
Packit 8f70b4
int xstring::instr(char c) const
Packit 8f70b4
{
Packit 8f70b4
   char *pos=(char*)memchr(buf,c,len);
Packit 8f70b4
   if(!pos)
Packit 8f70b4
      return -1;
Packit 8f70b4
   return pos-buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::vappendf(const char *format, va_list ap)
Packit 8f70b4
{
Packit 8f70b4
   if(size-len<32 || size-len>512)
Packit 8f70b4
      get_space(len+strlen(format)+32);
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      va_list tmp;
Packit 8f70b4
      VA_COPY(tmp,ap);
Packit 8f70b4
      int res=vsnprintf(buf+len, size-len, format, tmp);
Packit 8f70b4
      va_end(tmp);
Packit 8f70b4
      if(res<0)
Packit 8f70b4
	 return *this; // error
Packit 8f70b4
      if((size_t)res
Packit 8f70b4
      {
Packit 8f70b4
	 set_length(len+res);
Packit 8f70b4
	 return *this;
Packit 8f70b4
      }
Packit 8f70b4
      get_space((size_t)res>size-len ? len+res+1 : len+(size-len)*2);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::setf(const char *format, ...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va, format);
Packit 8f70b4
   vsetf(format, va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::appendf(const char *format, ...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va, format);
Packit 8f70b4
   vappendf(format, va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
// don't use it in nested loops
Packit 8f70b4
xstring& xstring::get_tmp()
Packit 8f70b4
{
Packit 8f70b4
   static xstring revolver[16];
Packit 8f70b4
   static int i;
Packit 8f70b4
Packit 8f70b4
   enum { MAX_REVOLVER_SIZE=0x1000 };
Packit 8f70b4
   if(revolver[i].size>MAX_REVOLVER_SIZE)
Packit 8f70b4
      revolver[i].shrink_space();
Packit 8f70b4
Packit 8f70b4
   int next=(i+1)&1;;
Packit 8f70b4
   xstring& tmp=revolver[i];
Packit 8f70b4
   // keep the oldest tmp clear to trigger NULL dereference
Packit 8f70b4
   tmp.move_here(revolver[next]);
Packit 8f70b4
   i=next;
Packit 8f70b4
   return tmp;
Packit 8f70b4
}
Packit 8f70b4
char *xstring::tmp_buf(int n)
Packit 8f70b4
{
Packit 8f70b4
   xstring& buf=get_tmp();
Packit 8f70b4
   buf.get_space(n-1);	// get_space adds a tail byte again
Packit 8f70b4
   return buf.get_non_const();
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::format(const char *fmt, ...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,fmt);
Packit 8f70b4
   xstring& res=vformat(fmt, va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
xstring &xstring::cat(const char *first,...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,first);
Packit 8f70b4
   xstring& str=get_tmp(first);
Packit 8f70b4
   str.vappend(va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return str;
Packit 8f70b4
}
Packit 8f70b4
xstring &xstring::join(const char *sep,int n,...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,n);
Packit 8f70b4
   xstring& str=get_tmp();
Packit 8f70b4
   str.truncate(0);
Packit 8f70b4
   while(n-->0)
Packit 8f70b4
   {
Packit 8f70b4
      const char *a=va_arg(va,const char*);
Packit 8f70b4
      if(!a || !*a)
Packit 8f70b4
	 continue;
Packit 8f70b4
      if(str.length())
Packit 8f70b4
	 str.append(sep);
Packit 8f70b4
      str.append(a);
Packit 8f70b4
   }
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return str;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *xstring_c::vset(...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,this);
Packit 8f70b4
   size_t va_len=vstrlen(va);
Packit 8f70b4
   if(!buf || strlen(buf)
Packit 8f70b4
      buf=(char*)xrealloc(buf,va_len+1);
Packit 8f70b4
   vstrcpy(buf,va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool xstring::is_binary() const
Packit 8f70b4
{
Packit 8f70b4
   unsigned bin_count=0;
Packit 8f70b4
   for(unsigned i=0; i
Packit 8f70b4
      bin_count+=((unsigned char)buf[i] < 32);
Packit 8f70b4
   return bin_count*32>len;
Packit 8f70b4
}
Packit 8f70b4
const char *xstring::hexdump_to(xstring& buf) const
Packit 8f70b4
{
Packit 8f70b4
   int len=length();
Packit 8f70b4
   const char *s=get();
Packit 8f70b4
   while(len-->0)
Packit 8f70b4
      buf.appendf("%02X",(unsigned char)*s++);
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
const char *xstring::hexdump() const
Packit 8f70b4
{
Packit 8f70b4
   return hexdump_to(get_tmp(""));
Packit 8f70b4
}
Packit 8f70b4
const char *xstring::dump() const
Packit 8f70b4
{
Packit 8f70b4
   return dump_to(get_tmp(""));
Packit 8f70b4
}
Packit 8f70b4
const char *xstring::dump_to(xstring& buf) const
Packit 8f70b4
{
Packit 8f70b4
   if(is_binary()) {
Packit 8f70b4
   binary:
Packit 8f70b4
      if(len<1024) {
Packit 8f70b4
	 buf.append("
Packit 8f70b4
	 hexdump_to(buf);
Packit 8f70b4
	 buf.append('>');
Packit 8f70b4
      } else {
Packit 8f70b4
	 buf.appendf("<long binary, %d bytes>",(int)length());
Packit 8f70b4
      }
Packit 8f70b4
   } else {
Packit 8f70b4
      int old_buf_len=buf.length();
Packit 8f70b4
      int len=length();
Packit 8f70b4
      const char *s=get();
Packit 8f70b4
      size_t invalid=0;
Packit 8f70b4
      while(len>0) {
Packit 8f70b4
	 int ch_len=mblen(s,len);
Packit 8f70b4
	 int ch_width=-1;
Packit 8f70b4
	 if(ch_len<1) {
Packit 8f70b4
	    ch_len=1;
Packit 8f70b4
	 } else {
Packit 8f70b4
	    ch_width=mbsnwidth(s,ch_len,0);
Packit 8f70b4
	 }
Packit 8f70b4
	 if(ch_width>=0) {
Packit 8f70b4
	    buf.append(s,ch_len);
Packit 8f70b4
	 } else {
Packit 8f70b4
	    while(ch_len>0) {
Packit 8f70b4
	       buf.appendf("\\%03o",(unsigned char)*s++);
Packit 8f70b4
	       ch_len--;
Packit 8f70b4
	       len--;
Packit 8f70b4
	       invalid++;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 s+=ch_len;
Packit 8f70b4
	 len-=ch_len;
Packit 8f70b4
      }
Packit 8f70b4
      if(invalid*32>length()) {
Packit 8f70b4
	 buf.truncate(old_buf_len);
Packit 8f70b4
	 goto binary;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int xstring0::_url_decode(size_t len,int flags)
Packit 8f70b4
{
Packit 8f70b4
   if(!buf)
Packit 8f70b4
      return 0;
Packit 8f70b4
   char *store=buf;
Packit 8f70b4
   const char *p=buf;
Packit 8f70b4
   int rest=len;
Packit 8f70b4
   while(rest>0)
Packit 8f70b4
   {
Packit 8f70b4
      if(rest>=3 && *p=='%' && c_isxdigit(p[1]) && c_isxdigit(p[2]))
Packit 8f70b4
      {
Packit 8f70b4
	 int n;
Packit 8f70b4
	 if(sscanf(p+1,"%2x",&n)==1)
Packit 8f70b4
	 {
Packit 8f70b4
	    *store++=n;
Packit 8f70b4
	    p+=3;
Packit 8f70b4
	    rest-=3;
Packit 8f70b4
	    continue;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else if(*p=='+' && (flags&URL_DECODE_PLUS))
Packit 8f70b4
      {
Packit 8f70b4
	 *store++=' ';
Packit 8f70b4
	 p++;
Packit 8f70b4
	 rest--;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      *store++=*p++;
Packit 8f70b4
      rest--;
Packit 8f70b4
   }
Packit 8f70b4
   return store-buf;
Packit 8f70b4
}
Packit 8f70b4
int xstring0::_hex_decode(size_t len)
Packit 8f70b4
{
Packit 8f70b4
   if(!buf)
Packit 8f70b4
      return 0;
Packit 8f70b4
   char *store=buf;
Packit 8f70b4
   const char *p=store;
Packit 8f70b4
   int rest=len;
Packit 8f70b4
   while(rest>=2)
Packit 8f70b4
   {
Packit 8f70b4
      int n;
Packit 8f70b4
      if(!c_isxdigit(p[0]) || !c_isxdigit(p[1])
Packit 8f70b4
      || sscanf(p,"%2x",&n)!=1)
Packit 8f70b4
	 break;
Packit 8f70b4
      *store++=n;
Packit 8f70b4
      p+=2;
Packit 8f70b4
      rest-=2;
Packit 8f70b4
   }
Packit 8f70b4
   return store-buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::url_decode(int flags)
Packit 8f70b4
{
Packit 8f70b4
   set_length(_url_decode(length(),flags));
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring_c& xstring_c::url_decode(int flags)
Packit 8f70b4
{
Packit 8f70b4
   set_length(_url_decode(length(),flags));
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::hex_decode()
Packit 8f70b4
{
Packit 8f70b4
   set_length(_hex_decode(length()));
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* Encode the unsafe characters in a given string, producing %XX encoded string. */
Packit 8f70b4
#define need_quote(c) (c_iscntrl((c)) \
Packit 8f70b4
   || (!(flags&URL_ALLOW_8BIT) && !c_isascii((c))) \
Packit 8f70b4
   || strchr(unsafe,(c)))
Packit 8f70b4
xstring& xstring::append_url_encoded(const char *s,int len,const char *unsafe,unsigned flags)
Packit 8f70b4
{
Packit 8f70b4
   if(!s)
Packit 8f70b4
      return *this;
Packit 8f70b4
   add_space(len+len/4);
Packit 8f70b4
   while(len-->0)
Packit 8f70b4
   {
Packit 8f70b4
      char c=*s++;
Packit 8f70b4
      if (need_quote(c))
Packit 8f70b4
	 appendf("%%%02X",(unsigned char)c);
Packit 8f70b4
      else
Packit 8f70b4
	 append(c);
Packit 8f70b4
   }
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::c_lc() {
Packit 8f70b4
   char *p=buf;
Packit 8f70b4
   int r=len;
Packit 8f70b4
   while(r>0) {
Packit 8f70b4
      *p=c_tolower(*p);
Packit 8f70b4
      p++,r--;
Packit 8f70b4
   }
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
xstring& xstring::c_ucfirst() {
Packit 8f70b4
   char *p=buf;
Packit 8f70b4
   int r=len;
Packit 8f70b4
   bool first=true;
Packit 8f70b4
   while(r>0) {
Packit 8f70b4
      if(c_isalpha(*p)) {
Packit 8f70b4
	 *p=(first?c_toupper(*p):c_tolower(*p));
Packit 8f70b4
	 first=false;
Packit 8f70b4
      } else {
Packit 8f70b4
	 first=true;
Packit 8f70b4
      }
Packit 8f70b4
      p++,r--;
Packit 8f70b4
   }
Packit 8f70b4
   return *this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const xstring xstring::null;
Packit 8f70b4
const xstring_c xstring_c::null;