Diff for /parser3/src/main/pa_charset.C between versions 1.1 and 1.8

version 1.1, 2001/12/15 21:28:21 version 1.8, 2001/12/27 19:57:09
Line 1 Line 1
 /** @file  /** @file
         Parser: Charset connection implementation.          Parser: Charset connection implementation.
   
         Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)          Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
         Author: Alexander Petrosyan <paf@design.ru> (http://paf.design.ru)          Author: Alexander Petrosyan<paf@design.ru>(http://paf.design.ru)
   
         $Id$          $Id$
 */  */
   
 #include "pa_charset.h"  #include "pa_charset.h"
 //#include "pa_exception.h"  
 //#include "pa_common.h"  
 //#include "pa_threads.h"  
   
 #ifdef XML  #ifdef XML
 #       include <util/TransENameMap.hpp>  #include "libxml/encoding.h"
 #       include <util/XML256TableTranscoder.hpp>  
 #       include <util/PlatformUtils.hpp>  
 #       include <PlatformSupport/XalanTranscodingServices.hpp>  
 #endif  #endif
   
 // globals  // globals
Line 47  inline unsigned int to_wchar_code(const Line 41  inline unsigned int to_wchar_code(const
         if(!cstr || !*cstr)          if(!cstr || !*cstr)
                 return 0;                  return 0;
         if(cstr[1]==0)          if(cstr[1]==0)
                 return (unsigned int)(unsigned char)cstr[0];                  return(unsigned int)(unsigned char)cstr[0];
   
         char *error_pos;          char *error_pos;
         return (unsigned int)strtol(cstr, &error_pos, 0);          return(unsigned int)strtol(cstr, &error_pos, 0);
 }  }
 inline bool to_bool(const char *cstr) {  inline bool to_bool(const char *cstr) {
         return cstr && *cstr!=0;          return cstr && *cstr!=0;
Line 64  static void element2ctypes(unsigned char Line 58  static void element2ctypes(unsigned char
   
         ctypes_table[c]|=bit;          ctypes_table[c]|=bit;
         if(group_offset>=0)          if(group_offset>=0)
                 tables[cbits_offset+group_offset+c/8] |= 1 << (c%8);                  tables[cbits_offset+group_offset+c/8] |= 1<<(c%8);
 }  }
 static void element2case(unsigned char from, unsigned char to,  static void element2case(unsigned char from, unsigned char to,
                                                  unsigned char *tables) {                                                   unsigned char *tables) {
Line 77  static void element2case(unsigned char f Line 71  static void element2case(unsigned char f
         fcc_table[from]=to; fcc_table[to]=from;          fcc_table[from]=to; fcc_table[to]=from;
 }  }
   
   /// @test custom encodings
 #ifdef XML  #ifdef XML
   /*
 template <class TType> class ENameMapFor2 : public ENameMap {  template <class TType> class ENameMapFor2 : public ENameMap {
 public :  public:
     // -----------------------------------------------------------------------      // -----------------------------------------------------------------------
     //  Constructors and Destructor      //  Constructors and Destructor
     // -----------------------------------------------------------------------      // -----------------------------------------------------------------------
Line 88  public : Line 84  public :
         , const XMLCh* const                        fromTable          , const XMLCh* const                        fromTable
         , const XMLTransService::TransRec* const    toTable          , const XMLTransService::TransRec* const    toTable
         , const unsigned int                        toTableSize          , const unsigned int                        toTableSize
                 ) : ENameMap(encodingName),                  ): ENameMap(encodingName),
                 ffromTable(fromTable),                  ffromTable(fromTable),
                 ftoTable(toTable),                  ftoTable(toTable),
                 ftoTableSize(toTableSize) {}                  ftoTableSize(toTableSize) {}
Line 108  private: Line 104  private:
         const XMLTransService::TransRec* const    ftoTable;          const XMLTransService::TransRec* const    ftoTable;
         const unsigned int                        ftoTableSize;          const unsigned int                        ftoTableSize;
   
 private :  private:
     // -----------------------------------------------------------------------      // -----------------------------------------------------------------------
     //  Unimplemented constructors and operators      //  Unimplemented constructors and operators
     // -----------------------------------------------------------------------      // -----------------------------------------------------------------------
Line 127  public : Line 123  public :
         , const unsigned int                        toTableSize          , const unsigned int                        toTableSize
                 ) : XML256TableTranscoder(encodingName, blockSize, fromTable, toTable, toTableSize) {}                  ) : XML256TableTranscoder(encodingName, blockSize, fromTable, toTable, toTableSize) {}
   
 private :  private:
     XML256TableTranscoder2();      XML256TableTranscoder2();
     XML256TableTranscoder2(const XML256TableTranscoder2&);      XML256TableTranscoder2(const XML256TableTranscoder2&);
     void operator=(const XML256TableTranscoder2&);      void operator=(const XML256TableTranscoder2&);
 };  };
   */
 #endif  #endif
   
 // methods  // methods
   
 extern "C" unsigned char pcre_default_tables[]; // pcre/chartables.c  extern "C" unsigned char pcre_default_tables[]; // pcre/chartables.c
 Charset::Charset(Pool& apool, const String& aname, const String *file_spec) : Pooled(apool),  Charset::Charset(Pool& apool, const String& aname, const String *request_file_spec): Pooled(apool),
         fname(apool) {          fname(aname) {
         // fname  
         char *name_cstr=(char *)malloc(aname.size());  
         memcpy(name_cstr, aname.cstr(String::UL_AS_IS), aname.size());  
         fname.APPEND_CLEAN(name_cstr, aname.size(), 0, 0);  
   
         if(file_spec) {          const char *name_cstr=fname.cstr();
   
           if(request_file_spec) {
                 fisUTF8=false;                  fisUTF8=false;
                 loadDefinition(*file_spec);                  loadDefinition(*request_file_spec);
 #ifdef XML  #ifdef XML
                 addEncoding(name_cstr);                  addEncoding(name_cstr);
 #endif  #endif
         } else {          } else {
                 fisUTF8=true;                  fisUTF8=true;
                 // grab default onces [for UTF-8 so to be able to make a-z => A-Z                  // grab default onces [for UTF-8 so to be able to make a-z =>A-Z
                 memcpy(pcre_tables, pcre_default_tables, sizeof(pcre_tables));                  memcpy(pcre_tables, pcre_default_tables, sizeof(pcre_tables));
         }          }
   
Line 167  Charset::~Charset() { Line 162  Charset::~Charset() {
 #endif  #endif
 }  }
   
 void Charset::loadDefinition(const String& file_spec) {  void Charset::loadDefinition(const String& request_file_spec) {
         // pcre_tables          // pcre_tables
         // lowcase, flipcase, bits digit+word+whitespace, masks          // lowcase, flipcase, bits digit+word+whitespace, masks
   
Line 175  void Charset::loadDefinition(const Strin Line 170  void Charset::loadDefinition(const Strin
         // don't know the size there          // don't know the size there
         memset(pcre_tables, 0, sizeof(pcre_tables));           memset(pcre_tables, 0, sizeof(pcre_tables)); 
         prepare_case_tables(pcre_tables);          prepare_case_tables(pcre_tables);
         cstr2ctypes(pcre_tables, (const unsigned char *)"*+?{^.$|()[", ctype_meta);          cstr2ctypes(pcre_tables,(const unsigned char *)"*+?{^.$|()[", ctype_meta);
   
         // charset          // charset
         memset(fromTable, 0, sizeof(fromTable));          memset(fromTable, 0, sizeof(fromTable));
         toTable=(XMLTransService::TransRec *)calloc(          toTable=(Charset_TransRec *)calloc(sizeof(Charset_TransRec)*MAX_CHARSET_UNI_CODES);
                         sizeof(XMLTransService::TransRec)*MAX_CHARSET_UNI_CODES);  
         toTableSize=0;          toTableSize=0;
         // strangly vital          // strangly vital
         toTable[toTableSize].intCh=0;          toTable[toTableSize].intCh=0;
Line 188  void Charset::loadDefinition(const Strin Line 182  void Charset::loadDefinition(const Strin
         toTableSize++;          toTableSize++;
   
         // loading text          // loading text
         char *data=file_read_text(pool(), file_spec);          char *data=file_read_text(pool(), request_file_spec);
   
         // ignore header          // ignore header
         getrow(&data);          getrow(&data);
Line 218  void Charset::loadDefinition(const Strin Line 212  void Charset::loadDefinition(const Strin
                                 // charset                                  // charset
                                 if(toTableSize>MAX_CHARSET_UNI_CODES)                                  if(toTableSize>MAX_CHARSET_UNI_CODES)
                                         throw Exception(0, 0,                                          throw Exception(0, 0,
                                                 &file_spec,                                                  &request_file_spec,
                                                 "charset must contain not more then %d unicode values", MAX_CHARSET_UNI_CODES);                                                  "charset must contain not more then %d unicode values", MAX_CHARSET_UNI_CODES);
   
                                 XMLCh unicode=(XMLCh)to_wchar_code(cell);                                  XMLCh unicode=(XMLCh)to_wchar_code(cell);
Line 242  void Charset::loadDefinition(const Strin Line 236  void Charset::loadDefinition(const Strin
   
 #ifdef XML  #ifdef XML
 void Charset::addEncoding(const char *name_cstr) {  void Charset::addEncoding(const char *name_cstr) {
   /*
         // addEncoding          // addEncoding
         XalanDOMString sencoding(name_cstr);          XalanDOMString sencoding(name_cstr);
         const XMLCh* const auto_encoding_cstr=sencoding.c_str();          const XMLCh* const auto_encoding_cstr=sencoding.c_str();
Line 258  void Charset::addEncoding(const char *na Line 253  void Charset::addEncoding(const char *na
                         , toTable                          , toTable
                         , toTableSize                          , toTableSize
                 ));                  ));
   */
 }  }
   
 void Charset::initTranscoder(const String *source, const char *name_cstr) {  void Charset::initTranscoder(const String *source, const char *name_cstr) {
         XMLTransService::Codes resValue;          transcoder=xmlFindCharEncodingHandler(name_cstr);
         transcoder=XMLPlatformUtils::fgTransService->makeNewTranscoderFor(name_cstr, resValue, 60);  
         if(!transcoder)          if(!transcoder)
                 throw Exception(0, 0,                  throw Exception(0, 0,
                         source,                          source,
Line 290  XMLByte Charset::xlatOneTo(const XMLCh t Line 285  XMLByte Charset::xlatOneTo(const XMLCh t
     XMLByte         curByte = 0;      XMLByte         curByte = 0;
     do {      do {
         // Calc the mid point of the low and high offset.          // Calc the mid point of the low and high offset.
         const unsigned int midOfs = ((hiOfs - lowOfs) / 2) + lowOfs;          const unsigned int midOfs =((hiOfs - lowOfs) / 2)+lowOfs;
   
         //  If our test char is greater than the mid point char, then          //  If our test char is greater than the mid point char, then
         //  we move up to the upper half. Else we move to the lower          //  we move up to the upper half. Else we move to the lower
         //  half. If its equal, then its our guy.          //  half. If its equal, then its our guy.
         if (toXlat > toTable[midOfs].intCh)          if(toXlat>toTable[midOfs].intCh)
             lowOfs = midOfs;              lowOfs = midOfs;
                 else if (toXlat < toTable[midOfs].intCh)                  else if(toXlat<toTable[midOfs].intCh)
                         hiOfs = midOfs;                          hiOfs = midOfs;
                 else                  else
                         return toTable[midOfs].extCh;                          return toTable[midOfs].extCh;
         } while(lowOfs + 1 < hiOfs);          } while(lowOfs+1<hiOfs);
   
     return '?';      return '?';
 }  }
Line 310  void Charset::transcode(Pool& pool, Line 305  void Charset::transcode(Pool& pool,
         const Charset& source_charset, const void *source_body, size_t source_content_length,          const Charset& source_charset, const void *source_body, size_t source_content_length,
         const Charset& dest_charset, const void *& dest_body, size_t& dest_content_length          const Charset& dest_charset, const void *& dest_body, size_t& dest_content_length
         ) {          ) {
           if(!source_content_length) {
                   dest_body=0;
                   dest_content_length=0;
                   return;
           }
   
         switch((source_charset.isUTF8()?0x10:0x00)|(dest_charset.isUTF8()?0x01:0x00)) {          switch((source_charset.isUTF8()?0x10:0x00)|(dest_charset.isUTF8()?0x01:0x00)) {
                 default: // 0x00                  default: // 0x00
                         source_charset.transcodeToCharset(pool, dest_charset,                          source_charset.transcodeToCharset(pool, dest_charset,
Line 386  void Charset::transcodeToUTF8(Pool& pool Line 387  void Charset::transcodeToUTF8(Pool& pool
         const XMLByte* srcEnd=(const XMLByte*)source_body+source_content_length;          const XMLByte* srcEnd=(const XMLByte*)source_body+source_content_length;
         XMLByte* outPtr=dest_body;          XMLByte* outPtr=dest_body;
   
     while (srcPtr < srcEnd) {      while(srcPtr<srcEnd) {
         uint curVal = fromTable[*srcPtr];          uint curVal = fromTable[*srcPtr];
                 if(!curVal) {                  if(!curVal) {
             // use the replacement character              // use the replacement character
             *outPtr++ = '?';              *outPtr++= '?';
             srcPtr ++;              srcPtr++;
             continue;              continue;
         }          }
   
         // Figure out how many bytes we need          // Figure out how many bytes we need
         unsigned int encodedBytes;          unsigned int encodedBytes;
         if (curVal < 0x80)          if(curVal<0x80)
             encodedBytes = 1;              encodedBytes = 1;
         else if (curVal < 0x800)          else if(curVal<0x800)
             encodedBytes = 2;              encodedBytes = 2;
         else if (curVal < 0x10000)          else if(curVal<0x10000)
             encodedBytes = 3;              encodedBytes = 3;
         else if (curVal < 0x200000)          else if(curVal<0x200000)
             encodedBytes = 4;              encodedBytes = 4;
         else if (curVal < 0x4000000)          else if(curVal<0x4000000)
             encodedBytes = 5;              encodedBytes = 5;
         else if (curVal <= 0x7FFFFFFF)          else if(curVal<= 0x7FFFFFFF)
             encodedBytes = 6;              encodedBytes = 6;
         else {          else {
             // use the replacement character              // use the replacement character
             *outPtr++ = '?';              *outPtr++= '?';
             srcPtr ++;              srcPtr++;
             continue;              continue;
         }          }
   
Line 424  void Charset::transcodeToUTF8(Pool& pool Line 425  void Charset::transcodeToUTF8(Pool& pool
   
         //  And spit out the bytes. We spit them out in reverse order          //  And spit out the bytes. We spit them out in reverse order
         //  here, so bump up the output pointer and work down as we go.          //  here, so bump up the output pointer and work down as we go.
         outPtr += encodedBytes;          outPtr+= encodedBytes;
         switch(encodedBytes) {          switch(encodedBytes) {
             case 6 : *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);              case 6: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                      curVal >>= 6;                       curVal>>= 6;
             case 5 : *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);              case 5: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                      curVal >>= 6;                       curVal>>= 6;
             case 4 : *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);              case 4: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                      curVal >>= 6;                       curVal>>= 6;
             case 3 : *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);              case 3: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                      curVal >>= 6;                       curVal>>= 6;
             case 2 : *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);              case 2: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                      curVal >>= 6;                       curVal>>= 6;
             case 1 : *--outPtr = XMLByte(curVal | gFirstByteMark[encodedBytes]);              case 1: *--outPtr = XMLByte(curVal | gFirstByteMark[encodedBytes]);
         }          }
   
         // Add the encoded bytes back in again to indicate we've eaten them          // Add the encoded bytes back in again to indicate we've eaten them
         outPtr += encodedBytes;          outPtr+= encodedBytes;
     }      }
   
         // return          // return
Line 458  void Charset::transcodeFromUTF8(Pool& po Line 459  void Charset::transcodeFromUTF8(Pool& po
         XMLByte* outPtr=dest_body;          XMLByte* outPtr=dest_body;
   
     //  We now loop until we either run out of input data      //  We now loop until we either run out of input data
     while (srcPtr < srcEnd) {      while(srcPtr<srcEnd) {
         // Get the next leading byte out          // Get the next leading byte out
         const XMLByte firstByte = *srcPtr;          const XMLByte firstByte = *srcPtr;
   
         // Special-case ASCII, which is a leading byte value of <= 127          // Special-case ASCII, which is a leading byte value of<= 127
         if (firstByte <= 127) {          if(firstByte<= 127) {
             *outPtr++ = firstByte;              *outPtr++= firstByte;
             srcPtr++;              srcPtr++;
             continue;              continue;
         }          }
Line 473  void Charset::transcodeFromUTF8(Pool& po Line 474  void Charset::transcodeFromUTF8(Pool& po
         const unsigned int trailingBytes = gUTFBytes[firstByte];          const unsigned int trailingBytes = gUTFBytes[firstByte];
   
         //  If there are not enough source bytes to do this one, then we          //  If there are not enough source bytes to do this one, then we
         //  are done. Note that we done >= here because we are implicitly          //  are done. Note that we done>= here because we are implicitly
         //  counting the 1 byte we get no matter what.          //  counting the 1 byte we get no matter what.
         if (srcPtr + trailingBytes >= srcEnd)          if(srcPtr+trailingBytes>= srcEnd)
             break;              break;
   
         // Looks ok, so lets build up the value          // Looks ok, so lets build up the value
Line 492  void Charset::transcodeFromUTF8(Pool& po Line 493  void Charset::transcodeFromUTF8(Pool& po
             default:              default:
                 throw Exception(0, 0,                  throw Exception(0, 0,
                                         0,                                          0,
                                         "transcodeFromUTF8 error: wrong trailingBytes value (%d)", trailingBytes);                                          "transcodeFromUTF8 error: wrong trailingBytes value(%d)", trailingBytes);
         }          }
         tmpVal-=gUTFOffsets[trailingBytes];          tmpVal-=gUTFOffsets[trailingBytes];
   
         //  If it will fit into a single char, then put it in. Otherwise          //  If it will fit into a single char, then put it in. Otherwise
         //  fail [*encode it as a surrogate pair. If its not valid, use the          //  fail [*encode it as a surrogate pair. If its not valid, use the
         //  replacement char.*]          //  replacement char.*]
         if (!(tmpVal & 0xFFFF0000))          if(!(tmpVal & 0xFFFF0000))
             *outPtr++ = xlatOneTo(tmpVal);              *outPtr++= xlatOneTo(tmpVal);
                 else                  else
                         throw Exception(0, 0,                          throw Exception(0, 0,
                                 0,                                  0,
                                 "transcodeFromUTF8 error: too big tmpVal (0x%08X)", tmpVal);                                  "transcodeFromUTF8 error: too big tmpVal(0x%08X)", tmpVal);
         }          }
   
         // return          // return
Line 516  void Charset::transcodeFromUTF8(Pool& po Line 517  void Charset::transcodeFromUTF8(Pool& po
 void Charset::transcodeToCharset(Pool& pool,  void Charset::transcodeToCharset(Pool& pool,
                                                                            const Charset& dest_charset,                                                                             const Charset& dest_charset,
                                                                            const void *source_body, size_t source_content_length,                                                                             const void *source_body, size_t source_content_length,
                                                                            const void *& adest_body, size_t& dest_content_length) const {                                                                             const void *& adest_body, size_t& adest_content_length) const {
         throw Exception(0, 0,          if(&dest_charset==this) {
                 0,                  adest_body=source_body;
                 "transcodeToCharset not supported(yet)");                  adest_content_length=source_content_length;
 /*          } else {
         void *dest_body;                  size_t dest_content_length=source_content_length;
                   unsigned char *dest_body=(unsigned char *)pool.malloc(dest_content_length);
   
         dest_body=pool.malloc(dest_content_length=source_content_length);                  const XMLByte* srcPtr=(const XMLByte*)source_body;
         // dummy                  const XMLByte* srcEnd=(const XMLByte*)source_body+source_content_length;
         memset(dest_body, '?', dest_content_length);  
                   for(XMLByte* outPtr=dest_body; srcPtr<srcEnd; srcPtr++) {
                           XMLCh curVal = fromTable[*srcPtr];
                           if(curVal) 
                                   *outPtr++=dest_charset.xlatOneTo(curVal);
                           else {
                                   // use the replacement character
                                   *outPtr++= '?';
                           }       
                   }
   
         adest_body=dest_body;*/                  adest_body=dest_body;
                   adest_content_length=dest_content_length;
           }
 }                         }                       
   
 #ifdef XML  #ifdef XML
 const char *Charset::transcode_cstr(const XalanDOMString& s) {   const char *Charset::transcode_cstr(GdomeDOMString *s) { 
         const unsigned int len=s.size()*2;          if(!transcoder)
         XMLByte* dest=(XMLByte *)malloc((len+1)*sizeof(XMLByte));                  throw Exception(0, 0,
         bool error=true;                          0,
         try {                          "transcode_cstr no transcoder");
                 if(transcoder) {  
                         unsigned int charsEaten;          int inlen=gdome_str_length(s);
                         unsigned int size=transcoder->transcodeTo(          int outlen=inlen+1; // max
                                 s.c_str(), s.length(),          char *out=(char *)malloc(outlen*sizeof(char));
                                 dest, len,          
                                 charsEaten,          int size=transcoder->output(
                                 XMLTranscoder::UnRep_RepChar //UnRep_Throw                  (unsigned char*)out, &outlen,
                         );                  (const unsigned char*)s->str, &inlen);
                         dest[size]=0;          if(size<0)
                         error=false;                  throw Exception(0, 0,
                 }                          0,
         } catch(XMLException& e) {                          "transcode_cstr failed (%d)", size);
                 Exception::provide_source(pool(), 0, e);  
         }          out[size]=0;
         return (const char *)dest;          return out;
 }  }
 String& Charset::transcode(const XalanDOMString& s) {   String& Charset::transcode(GdomeDOMString *s) { 
         return *NEW String(pool(), transcode_cstr(s));           return *NEW String(pool(), transcode_cstr(s)); 
 }  }
   
 std::auto_ptr<XalanDOMString> Charset::transcode_buf(const char *buf, size_t buf_size) {   /// @test less memory using -maybe- xmlParserInputBufferCreateMem
         unsigned int dest_size=0;  GdomeDOMString *Charset::transcode_buf(const char *buf, size_t buf_size) { 
         XMLCh* dest=(XMLCh *)malloc((buf_size+1)*sizeof(XMLCh));          if(!transcoder)
         unsigned char *charSizes=(unsigned char *)malloc(buf_size*sizeof(unsigned char));                  throw Exception(0, 0,
         XalanDOMString *result;                          0,
         try {                          "transcode_buf no transcoder");
                 if(transcoder) {  
                         unsigned int bytesEaten;          int outlen=buf_size*6/*max*/+1;
                         unsigned int dest_size=transcoder->transcodeFrom(          unsigned char *out=(unsigned char*)malloc(outlen*sizeof(unsigned char));
                                 (unsigned char *)buf,          int size=transcoder->input(
                                 (const unsigned int)buf_size,                  out, &outlen,
                                 dest, (const unsigned int)buf_size,                  (const unsigned char *)buf,  (int *)&buf_size);
                                 bytesEaten,          if(size<0)
                                 charSizes                  throw Exception(0, 0,
                         );                          0,
                         result=new XalanDOMString(dest, dest_size);                          "transcode_buf failed (%d)", size);
                 }  
         } catch(XMLException& e) {          out[size]=0;
                 Exception::provide_source(pool(), 0, e);          return gdome_str_mkref_own((gchar*)out);
                 result=0; //calm, compiler  
         }  
           
         return std::auto_ptr<XalanDOMString>(result);  
 }  }
 std::auto_ptr<XalanDOMString> Charset::transcode(const String& s) {   GdomeDOMString *Charset::transcode(const String& s) { 
         const char *cstr=s.cstr(String::UL_UNSPECIFIED);          const char *cstr=s.cstr(String::UL_UNSPECIFIED);
   
         return transcode_buf(cstr, strlen(cstr));           return transcode_buf(cstr, strlen(cstr)); 

Removed from v.1.1  
changed lines
  Added in v.1.8


E-mail: