ncbistr.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:54k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: ncbistr.cpp,v $
  4.  * PRODUCTION Revision 1000.6  2004/06/01 19:09:21  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.109
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: ncbistr.cpp,v 1000.6 2004/06/01 19:09:21 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *   Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Authors:  Eugene Vasilchenko, Denis Vakatov
  35.  *
  36.  * File Description:
  37.  *   Some helper functions
  38.  *
  39.  */
  40. #include <ncbi_pch.hpp>
  41. #include <corelib/ncbistd.hpp>
  42. #include <corelib/ncbi_limits.h>
  43. #include <memory>
  44. #include <algorithm>
  45. #include <ctype.h>
  46. #include <errno.h>
  47. #include <stdio.h>
  48. BEGIN_NCBI_SCOPE
  49. inline
  50. std::string::size_type s_DiffPtr(const char* end, const char* start)
  51. {
  52.     if (end) {
  53.         return end - start;
  54.     }
  55.     return 0;
  56. }
  57. const char *const kEmptyCStr = "";
  58. #ifndef NCBI_OS_MSWIN
  59. const string* CNcbiEmptyString::m_Str = 0;
  60. const string& CNcbiEmptyString::FirstGet(void) {
  61.     static const string s_Str = "";
  62.     m_Str = &s_Str;
  63.     return s_Str;
  64. }
  65. #endif // NCBI_OS_MSWIN
  66. int NStr::CompareCase(const string& str, SIZE_TYPE pos, SIZE_TYPE n,
  67.                       const char* pattern)
  68. {
  69.     if (pos == NPOS  ||  !n  ||  str.length() <= pos) {
  70.         return *pattern ? -1 : 0;
  71.     }
  72.     if ( !*pattern ) {
  73.         return 1;
  74.     }
  75.     if (n == NPOS  ||  n > str.length() - pos) {
  76.         n = str.length() - pos;
  77.     }
  78.     const char* s = str.data() + pos;
  79.     while (n  &&  *pattern  &&  *s == *pattern) {
  80.         s++;  pattern++;  n--;
  81.     }
  82.     if (n == 0) {
  83.         return *pattern ? -1 : 0;
  84.     }
  85.     return *s - *pattern;
  86. }
  87. int NStr::CompareNocase(const string& str, SIZE_TYPE pos, SIZE_TYPE n,
  88.                         const char* pattern)
  89. {
  90.     if (pos == NPOS  ||  !n  ||  str.length() <= pos) {
  91.         return *pattern ? -1 : 0;
  92.     }
  93.     if ( !*pattern ) {
  94.         return 1;
  95.     }
  96.     if (n == NPOS  ||  n > str.length() - pos) {
  97.         n = str.length() - pos;
  98.     }
  99.     const char* s = str.data() + pos;
  100.     while (n  &&  *pattern  &&  toupper(*s) == toupper(*pattern)) {
  101.         s++;  pattern++;  n--;
  102.     }
  103.     if (n == 0) {
  104.         return *pattern ? -1 : 0;
  105.     }
  106.     return toupper(*s) - toupper(*pattern);
  107. }
  108. int NStr::CompareCase(const string& str, SIZE_TYPE pos, SIZE_TYPE n,
  109.                       const string& pattern)
  110. {
  111.     if (pos == NPOS  ||  !n  ||  str.length() <= pos) {
  112.         return pattern.empty() ? 0 : -1;
  113.     }
  114.     if ( pattern.empty() ) {
  115.         return 1;
  116.     }
  117.     if (n == NPOS  ||  n > str.length() - pos) {
  118.         n = str.length() - pos;
  119.     }
  120.     SIZE_TYPE n_cmp = n;
  121.     if (n_cmp > pattern.length()) {
  122.         n_cmp = pattern.length();
  123.     }
  124.     const char* s = str.data() + pos;
  125.     const char* p = pattern.data();
  126.     while (n_cmp  &&  *s == *p) {
  127.         s++;  p++;  n_cmp--;
  128.     }
  129.     if (n_cmp == 0) {
  130.         if (n == pattern.length())
  131.             return 0;
  132.         return n > pattern.length() ? 1 : -1;
  133.     }
  134.     return *s - *p;
  135. }
  136. int NStr::CompareNocase(const string& str, SIZE_TYPE pos, SIZE_TYPE n,
  137.                         const string& pattern)
  138. {
  139.     if (pos == NPOS  ||  !n  ||  str.length() <= pos) {
  140.         return pattern.empty() ? 0 : -1;
  141.     }
  142.     if ( pattern.empty() ) {
  143.         return 1;
  144.     }
  145.     if (n == NPOS  ||  n > str.length() - pos) {
  146.         n = str.length() - pos;
  147.     }
  148.     SIZE_TYPE n_cmp = n;
  149.     if (n_cmp > pattern.length()) {
  150.         n_cmp = pattern.length();
  151.     }
  152.     const char* s = str.data() + pos;
  153.     const char* p = pattern.data();
  154.     while (n_cmp  &&  toupper(*s) == toupper(*p)) {
  155.         s++;  p++;  n_cmp--;
  156.     }
  157.     if (n_cmp == 0) {
  158.         if (n == pattern.length())
  159.             return 0;
  160.         return n > pattern.length() ? 1 : -1;
  161.     }
  162.     return toupper(*s) - toupper(*p);
  163. }
  164. // NOTE: This code is used also in the CDirEntry::MatchesMask.
  165. bool NStr::MatchesMask(const char* str, const char* mask) 
  166. {
  167.     char c;
  168.     bool infinite = true;
  169.     while (infinite) {
  170.         // Analyze symbol in mask
  171.         switch ( c = *mask++ ) {
  172.         
  173.         case '':
  174.             return *str == '';
  175.         case '?':
  176.             if ( *str == '' ) {
  177.                 return false;
  178.             }
  179.             ++str;
  180.             break;
  181.         case '*':
  182.             c = *mask;
  183.             // Collapse multiple stars
  184.             while ( c == '*' ) {
  185.                 c = *++mask;
  186.             }
  187.             if (c == '') {
  188.                 return true;
  189.             }
  190.             // General case, use recursion
  191.             while ( *str ) {
  192.                 if ( MatchesMask(str, mask) ) {
  193.                     return true;
  194.                 }
  195.                 ++str;
  196.             }
  197.             return false;
  198.         default:
  199.             // Compare nonpattern character in mask and name
  200.             if ( c != *str++ ) {
  201.                 return false;
  202.             }
  203.             break;
  204.         }
  205.     }
  206.     return false;
  207. }
  208. char* NStr::ToLower(char* str)
  209. {
  210.     char* s;
  211.     for (s = str;  *str;  str++) {
  212.         *str = tolower(*str);
  213.     }
  214.     return s;
  215. }
  216. string& NStr::ToLower(string& str)
  217. {
  218.     NON_CONST_ITERATE (string, it, str) {
  219.         *it = tolower(*it);
  220.     }
  221.     return str;
  222. }
  223. char* NStr::ToUpper(char* str)
  224. {
  225.     char* s;
  226.     for (s = str;  *str;  str++) {
  227.         *str = toupper(*str);
  228.     }
  229.     return s;
  230. }
  231. string& NStr::ToUpper(string& str)
  232. {
  233.     NON_CONST_ITERATE (string, it, str) {
  234.         *it = toupper(*it);
  235.     }
  236.     return str;
  237. }
  238. int NStr::StringToNumeric(const string& str)
  239. {
  240.     if (str.empty()  ||  !isdigit(*str.begin())) {
  241.         return -1;
  242.     }
  243.     errno = 0;
  244.     char* endptr = 0;
  245.     unsigned long value = strtoul(str.c_str(), &endptr, 10);
  246.     if (errno  ||  !endptr  ||  value > (unsigned long) kMax_Int  ||
  247.         *endptr != ''  ||  endptr == str.c_str()) {
  248.         return -1;
  249.     }
  250.     return (int) value;
  251. }
  252. # define CHECK_ENDPTR(conv)                                           
  253.     if (check_endptr == eCheck_Need  &&  *endptr != '') {           
  254.         NCBI_THROW2(CStringException, eBadArgs,                       
  255.             "String cannot be converted to " conv " - trailing junk", 
  256.             s_DiffPtr(endptr, str.c_str()));                          
  257.     }
  258. int NStr::StringToInt(const string& str, int base /* = 10 */,
  259.                       ECheckEndPtr check_endptr   /* = eCheck_Need */ )
  260. {
  261.     errno = 0;
  262.     char* endptr = 0;
  263.     long value = strtol(str.c_str(), &endptr, base);
  264.     if (errno  ||  !endptr  ||  endptr == str.c_str()  ||
  265.         value < kMin_Int || value > kMax_Int) {
  266.         NCBI_THROW2(CStringException, eConvert,
  267.                     "String cannot be converted to int",
  268.                     s_DiffPtr(endptr, str.c_str()));
  269.     }
  270.     CHECK_ENDPTR("int");
  271.     return (int) value;
  272. }
  273. unsigned int NStr::StringToUInt(const string& str, int base /* =10 */,
  274.                                 ECheckEndPtr check_endptr   /* =eCheck_Need */)
  275. {
  276.     errno = 0;
  277.     char* endptr = 0;
  278.     unsigned long value = strtoul(str.c_str(), &endptr, base);
  279.     if (errno  ||  !endptr  ||  endptr == str.c_str()  ||  value > kMax_UInt) {
  280.         NCBI_THROW2(CStringException, eConvert,
  281.                     "String cannot be converted unsigned int",
  282.                     s_DiffPtr(endptr, str.c_str()));
  283.     }
  284.     CHECK_ENDPTR("unsigned int");
  285.     return (unsigned int) value;
  286. }
  287. long NStr::StringToLong(const string& str, int base /* = 10 */,
  288.                         ECheckEndPtr check_endptr   /* = eCheck_Need */ )
  289. {
  290.     errno = 0;
  291.     char* endptr = 0;
  292.     long value = strtol(str.c_str(), &endptr, base);
  293.     if (errno  ||  !endptr  ||  endptr == str.c_str()) {
  294.         NCBI_THROW2(CStringException, eConvert,
  295.                     "String cannot be converted to long",
  296.                     s_DiffPtr(endptr, str.c_str()));
  297.     }
  298.     CHECK_ENDPTR("long");
  299.     return value;
  300. }
  301. unsigned long NStr::StringToULong(const string& str, int base /*=10 */,
  302.                                   ECheckEndPtr check_endptr   /*=eCheck_Need*/)
  303. {
  304.     errno = 0;
  305.     char* endptr = 0;
  306.     unsigned long value = strtoul(str.c_str(), &endptr, base);
  307.     if (errno  ||  !endptr  ||  endptr == str.c_str()) {
  308.         NCBI_THROW2(CStringException, eConvert,
  309.                     "String cannot be converted to unsigned long",
  310.                     s_DiffPtr(endptr, str.c_str()));
  311.     }
  312.     CHECK_ENDPTR("unsigned long");
  313.     return value;
  314. }
  315. double NStr::StringToDouble(const string& str,
  316.                             ECheckEndPtr check_endptr /* = eCheck_Need */ )
  317. {
  318.     errno = 0;
  319.     char* endptr = 0;
  320.     double value = strtod(str.c_str(), &endptr);
  321.     if (errno  ||  !endptr  ||  endptr == str.c_str()) {
  322.         NCBI_THROW2(CStringException, eConvert,
  323.                     "String cannot be converted to double",
  324.                     s_DiffPtr(endptr, str.c_str()));
  325.     }
  326.     if (*(endptr - 1) != '.'  &&  *endptr == '.')
  327.         endptr++;
  328.     CHECK_ENDPTR("double");
  329.     return value;
  330. }
  331. string NStr::IntToString(long value, bool sign /* = false */ )
  332. {
  333.     char buffer[64];
  334.     ::sprintf(buffer, sign ? "%+ld" : "%ld", value);
  335.     return buffer;
  336. }
  337. void NStr::IntToString(string& out_str, long value, bool sign)
  338. {
  339.     char buffer[64];
  340.     ::sprintf(buffer, sign ? "%+ld" : "%ld", value);
  341.     out_str = buffer;
  342. }
  343. string NStr::UIntToString(unsigned long value)
  344. {
  345.     char buffer[64];
  346.     ::sprintf(buffer, "%lu", value);
  347.     return buffer;
  348. }
  349. void NStr::UIntToString(string& out_str, unsigned long value)
  350. {
  351.     char buffer[64];
  352.     ::sprintf(buffer, "%lu", value);
  353.     out_str = buffer;
  354. }
  355. string NStr::Int8ToString(Int8 value, bool sign /* = false */ )
  356. {
  357.     string ret;
  358.     NStr::Int8ToString(ret, value, sign);
  359.     return ret;
  360. }
  361. void NStr::Int8ToString(string& out_str, Int8 value, bool sign)
  362. {
  363.     const size_t kBufSize = (sizeof(value) * CHAR_BIT) / 3 + 2;
  364.     char buffer[kBufSize];
  365.     char* pos = buffer + kBufSize;
  366.     if (value == 0) {
  367.         *--pos = '0';
  368.     }
  369.     else {
  370.         bool is_negative = value < 0;
  371.         if ( is_negative )
  372.             value = -value;
  373.         do {
  374.             *--pos = char('0' + (value % 10));
  375.             value /= 10;
  376.         } while ( value );
  377.         if ( is_negative )
  378.             *--pos = '-';
  379.         else if ( sign )
  380.             *--pos = '+';
  381.     }
  382.     out_str.resize(0);
  383.     out_str.append(pos, buffer + kBufSize - pos);
  384. }
  385. Int8 NStr::StringToInt8(const string& str)
  386. {
  387.     bool sign = false;
  388.     const char* pc = str.c_str();
  389.     switch (*pc) {
  390.     case '-':
  391.         sign = true;
  392.         /*FALLTHRU*/
  393.     case '+':
  394.         ++pc;
  395.         /*FALLTHRU*/
  396.     default:
  397.         break;
  398.     }
  399.     Int8 n = 0;
  400.     Int8 limdiv = kMax_I8 / 10;
  401.     Int8 limoff = kMax_I8 % 10;
  402.     do {
  403.         if (!isdigit(*pc)) {
  404.             NCBI_THROW2(CStringException, eConvert,
  405.                         "String cannot be converted to Int8 - bad digit",
  406.                         s_DiffPtr(pc, str.c_str()));
  407.         }
  408.         int delta = *pc - '0';
  409.         n *= 10;
  410.         // Overflow checking
  411.         if (n > limdiv || (n == limdiv && delta > limoff)) {
  412.             NCBI_THROW2(CStringException, eConvert,
  413.                         "String cannot be converted Int8 - overflow",
  414.                         s_DiffPtr(pc, str.c_str()));
  415.         }
  416.         n += delta;
  417.     } while (*++pc);
  418.     return sign ? -n : n;
  419. }
  420. Uint8 NStr::StringToUInt8(const string& str, int base /* = 10  */)
  421. {
  422.     const char* pc = str.c_str();
  423.     if (*pc == '+')
  424.         ++pc;
  425.  
  426.     Uint8 n = 0;
  427.     Uint8 limdiv = kMax_UI8 / base;
  428.     int   limoff = int(kMax_UI8 % base);
  429.     do {
  430.         // Do a sanity check for common radixes
  431.         int ch = *pc; 
  432.         if (base == 10  &&  !isdigit(ch)              ||
  433.             base == 16  &&  !isxdigit(ch)             ||
  434.             base == 8   &&  (ch < '0'  ||  ch > '7')  ||
  435.             base == 2   &&  (ch < '0'  ||  ch > '1')) {
  436.             NCBI_THROW2(CStringException, eConvert,
  437.                         "String cannot be converted to UInt8 - bad digit",
  438.                         s_DiffPtr(pc, str.c_str()));
  439.         }
  440.         int delta;  // corresponding numeric value of *pc
  441.         if (isdigit(ch)) {
  442.             delta = ch - '0';
  443.         } else {
  444.             ch = tolower(ch);
  445.             // Got to be 'a' to 'f' because of previous sanity checks
  446.             delta = ch - 'a' + 10;
  447.         }
  448.         n *= base;
  449.         // Overflow checking
  450.         if (n > limdiv  ||  (n == limdiv  &&  delta > limoff)) {
  451.             NCBI_THROW2(CStringException, eConvert,
  452.                         "String cannot be converted to UInt8 - overflow",
  453.                         s_DiffPtr(pc, str.c_str()));
  454.         }
  455.         n += delta;
  456.     } while (*++pc);
  457.     return n;
  458. }
  459. void NStr::UInt8ToString(string& out_str, Uint8 value)
  460. {
  461.     const size_t kBufSize = (sizeof(value) * CHAR_BIT) / 3 + 2;
  462.     char buffer[kBufSize];
  463.     char* pos = buffer + kBufSize;
  464.     if ( value == 0 ) {
  465.         *--pos = '0';
  466.     }
  467.     else {
  468.         do {
  469.             *--pos = char('0' + (value % 10));
  470.             value /= 10;
  471.         } while ( value );
  472.     }
  473.     out_str.resize(0);
  474.     out_str.append(pos, buffer + kBufSize - pos);
  475. }
  476. string NStr::UInt8ToString(Uint8 value)
  477. {
  478.     string ret;
  479.     NStr::UInt8ToString(ret, value);
  480.     return ret;
  481. }
  482. // A maximal double precision used in the double to string conversion
  483. #if defined(NCBI_OS_MSWIN)
  484. const unsigned int kMaxDoublePrecision = 200;
  485. #else
  486. const unsigned int kMaxDoublePrecision = 308;
  487. #endif
  488. // A maximal size of a double value in a string form.
  489. // Exponent size + sign + dot + ending '' + max.precision
  490. const unsigned int kMaxDoubleStringSize = 308 + 3 + kMaxDoublePrecision;
  491. string NStr::DoubleToString(double value)
  492. {
  493.     char buffer[kMaxDoubleStringSize];
  494.     ::sprintf(buffer, "%g", value);
  495.     return buffer;
  496. }
  497. void NStr::DoubleToString(string& out_str, double value)
  498. {
  499.     char buffer[kMaxDoubleStringSize];
  500.     ::sprintf(buffer, "%g", value);
  501.     out_str = buffer;
  502. }
  503. string NStr::DoubleToString(double value, unsigned int precision)
  504. {
  505.     char buffer[kMaxDoubleStringSize];
  506.     SIZE_TYPE n = DoubleToString(value, precision, buffer,
  507.                                  kMaxDoubleStringSize);
  508.     buffer[n] = '';
  509.     return buffer;
  510. }
  511. SIZE_TYPE NStr::DoubleToString(double value, unsigned int precision,
  512.                                char* buf, SIZE_TYPE buf_size)
  513. {
  514.     char buffer[kMaxDoubleStringSize];
  515.     if (precision > kMaxDoublePrecision) {
  516.         precision = kMaxDoublePrecision;
  517.     }
  518.     int n = ::sprintf(buffer, "%.*f", (int) precision, value);
  519.     SIZE_TYPE n_copy = min((SIZE_TYPE) n, buf_size);
  520.     memcpy(buf, buffer, n_copy);
  521.     return n_copy;
  522. }
  523. string NStr::PtrToString(const void* value)
  524. {
  525.     char buffer[64];
  526.     ::sprintf(buffer, "%p", value);
  527.     return buffer;
  528. }
  529. void NStr::PtrToString(string& out_str, const void* value)
  530. {
  531.     char buffer[64];
  532.     ::sprintf(buffer, "%p", value);
  533.     out_str = buffer;
  534. }
  535. const void* NStr::StringToPtr(const string& str)
  536. {
  537.     void *ptr = NULL;
  538.     ::sscanf(str.c_str(), "%p", &ptr);
  539.     return ptr;
  540. }
  541. static const string s_kTrueString  = "true";
  542. static const string s_kFalseString = "false";
  543. static const string s_kTString     = "t";
  544. static const string s_kFString     = "f";
  545. static const string s_kYesString   = "yes";
  546. static const string s_kNoString    = "no";
  547. static const string s_kYString     = "y";
  548. static const string s_kNString     = "n";
  549. const string& NStr::BoolToString(bool value)
  550. {
  551.     return value ? s_kTrueString : s_kFalseString;
  552. }
  553. bool NStr::StringToBool(const string& str)
  554. {
  555.     if ( AStrEquiv(str, s_kTrueString,  PNocase())  ||
  556.          AStrEquiv(str, s_kTString,     PNocase())  ||
  557.          AStrEquiv(str, s_kYesString,   PNocase())  ||
  558.          AStrEquiv(str, s_kYString,     PNocase()) )
  559.         return true;
  560.     if ( AStrEquiv(str, s_kFalseString, PNocase())  ||
  561.          AStrEquiv(str, s_kFString,     PNocase())  ||
  562.          AStrEquiv(str, s_kNoString,    PNocase())  ||
  563.          AStrEquiv(str, s_kNString,     PNocase()) )
  564.         return false;
  565.     NCBI_THROW2(CStringException, eConvert,
  566.                 "String cannot be converted to bool", 0);
  567. }
  568. string NStr::FormatVarargs(const char* format, va_list args)
  569. {
  570. #ifdef HAVE_VASPRINTF
  571.     char* s;
  572.     int n = vasprintf(&s, format, args);
  573.     if (n >= 0) {
  574.         string str(s, n);
  575.         free(s);
  576.         return str;
  577.     } else {
  578.         return kEmptyStr;
  579.     }
  580. #elif defined(NCBI_COMPILER_GCC) && defined(NO_PUBSYNC)
  581.     CNcbiOstrstream oss;
  582.     oss.vform(format, args);
  583.     return CNcbiOstrstreamToString(oss);
  584. #elif defined(HAVE_VSNPRINTF)
  585.     // deal with implementation quirks
  586.     size_t size = 1024;
  587.     AutoPtr<char, ArrayDeleter<char> > buf(new char[size]);
  588.     buf.get()[size-1] = buf.get()[size-2] = 0;
  589.     size_t n = vsnprintf(buf.get(), size, format, args);
  590.     while (n >= size  ||  buf.get()[size-2]) {
  591.         if (buf.get()[size-1]) {
  592.             ERR_POST(Warning << "Buffer overrun by buggy vsnprintf");
  593.         }
  594.         size = max(size << 1, n);
  595.         buf.reset(new char[size]);
  596.         buf.get()[size-1] = buf.get()[size-2] = 0;
  597.         n = vsnprintf(buf.get(), size, format, args);
  598.     }
  599.     return (n > 0) ? string(buf.get(), n) : kEmptyStr;
  600. #elif defined(HAVE_VPRINTF)
  601.     char buf[1024];
  602.     buf[sizeof(buf) - 1] = 0;
  603.     vsprintf(buf, format, args);
  604.     if (buf[sizeof(buf) - 1]) {
  605.         ERR_POST(Warning << "Buffer overrun by vsprintf");
  606.     }
  607.     return buf;
  608. #else
  609. #  error Please port this code to your system.
  610. #endif
  611. }
  612. SIZE_TYPE NStr::FindNoCase(const string& str, const string& pattern,
  613.                            SIZE_TYPE start, SIZE_TYPE end, EOccurrence where)
  614. {
  615.     string    pat(pattern, 0, 1);
  616.     SIZE_TYPE l = pattern.size();
  617.     if (isupper(pat[0])) {
  618.         pat += (char)tolower(pat[0]);
  619.     } else if (islower(pat[0])) {
  620.         pat += (char)toupper(pat[0]);
  621.     }
  622.     if (where == eFirst) {
  623.         SIZE_TYPE pos = str.find_first_of(pat, start);
  624.         while (pos != NPOS  &&  pos <= end
  625.                &&  CompareNocase(str, pos, l, pattern) != 0) {
  626.             pos = str.find_first_of(pat, pos + 1);
  627.         }
  628.         return pos > end ? NPOS : pos;
  629.     } else { // eLast
  630.         SIZE_TYPE pos = str.find_last_of(pat, end);
  631.         while (pos != NPOS  &&  pos >= start
  632.                &&  CompareNocase(str, pos, l, pattern) != 0) {
  633.             if (pos == 0) {
  634.                 return NPOS;
  635.             }
  636.             pos = str.find_last_of(pat, pos - 1);
  637.         }
  638.         return pos < start ? NPOS : pos;
  639.     }
  640. }
  641. string NStr::TruncateSpaces(const string& str, ETrunc where)
  642. {
  643.     SIZE_TYPE beg = 0;
  644.     if (where == eTrunc_Begin  ||  where == eTrunc_Both) {
  645.         while (beg < str.length()  &&  isspace(str[beg]))
  646.             beg++;
  647.         if (beg == str.length())
  648.             return kEmptyStr;
  649.     }
  650.     SIZE_TYPE end = str.length() - 1;
  651.     if (where == eTrunc_End  ||  where == eTrunc_Both) {
  652.         while ( isspace(str[end]) )
  653.             end--;
  654.     }
  655.     _ASSERT( beg <= end );
  656.     return str.substr(beg, end - beg + 1);
  657. }
  658. string& NStr::Replace(const string& src,
  659.                       const string& search, const string& replace,
  660.                       string& dst, SIZE_TYPE start_pos, size_t max_replace)
  661. {
  662.     // source and destination should not be the same
  663.     if (&src == &dst) {
  664.         NCBI_THROW2(CStringException, eBadArgs,
  665.                     "String replace called with source == destination", 0);
  666.     }
  667.     dst = src;
  668.     if( start_pos + search.size() > src.size() ||
  669.         search == replace)
  670.         return dst;
  671.     for(size_t count = 0; !(max_replace && count >= max_replace); count++) {
  672.         start_pos = dst.find(search, start_pos);
  673.         if(start_pos == NPOS)
  674.             break;
  675.         dst.replace(start_pos, search.size(), replace);
  676.         start_pos += replace.size();
  677.     }
  678.     return dst;
  679. }
  680. string NStr::Replace(const string& src,
  681.                      const string& search, const string& replace,
  682.                      SIZE_TYPE start_pos, size_t max_replace)
  683. {
  684.     string dst;
  685.     return Replace(src, search, replace, dst, start_pos, max_replace);
  686. }
  687. list<string>& NStr::Split(const string& str, const string& delim,
  688.                           list<string>& arr, EMergeDelims merge)
  689. {
  690.     for (size_t pos = 0; ; ) {
  691.         size_t prev_pos = (merge == eMergeDelims
  692.                            ? str.find_first_not_of(delim, pos)
  693.                            : pos);
  694.         if (prev_pos == NPOS) {
  695.             break;
  696.         }
  697.         pos = str.find_first_of(delim, prev_pos);
  698.         if (pos == NPOS) {
  699.             arr.push_back(str.substr(prev_pos));
  700.             break;
  701.         } else {
  702.             arr.push_back(str.substr(prev_pos, pos - prev_pos));
  703.             ++pos;
  704.         }
  705.     }
  706.     return arr;
  707. }
  708. vector<string>& NStr::Tokenize(const string& str, const string& delim,
  709.                                vector<string>& arr, EMergeDelims merge)
  710. {
  711.     if (delim.empty()) {
  712.         arr.push_back(str);
  713.         return arr;
  714.     }
  715.     size_t pos, prev_pos;
  716.     // Count number of tokens to determine the array size
  717.     size_t tokens = 0;
  718.     for (pos = prev_pos = 0; pos < str.length(); ++pos) {
  719.         char c = str[pos];
  720.         size_t dpos = delim.find(c);
  721.         if (dpos != string::npos) ++tokens;
  722.     }
  723.     arr.reserve(arr.size() + tokens + 1);
  724.     // Tokenization
  725.     for (pos = 0; ; ) {
  726.         prev_pos = (merge == eMergeDelims ? str.find_first_not_of(delim, pos)
  727.                     : pos);
  728.         if (prev_pos == NPOS) {
  729.             break;
  730.         }
  731.         pos = str.find_first_of(delim, prev_pos);
  732.         if (pos == NPOS) {
  733.             arr.push_back(str.substr(prev_pos));
  734.             break;
  735.         } else {
  736.             arr.push_back(str.substr(prev_pos, pos - prev_pos));
  737.             ++pos;
  738.         }
  739.     }
  740.     return arr;
  741. }
  742. bool NStr::SplitInTwo(const string& str, const string& delim,
  743.                       string& str1, string& str2)
  744. {
  745.     SIZE_TYPE delim_pos = str.find_first_of(delim);
  746.     if (NPOS == delim_pos) {   // only one piece.
  747.         str1 = str;
  748.         str2 = kEmptyStr;
  749.         return false;
  750.     }
  751.     str1 = str.substr(0, delim_pos);
  752.     str2 = str.substr(delim_pos + 1); // skip only one delimiter character.
  753.     return true;
  754. }
  755. template <typename T>
  756. string s_NStr_Join(const T& arr, const string& delim)
  757. {
  758.     if (arr.empty()) {
  759.         return kEmptyStr;
  760.     }
  761.     string                       result = arr.front();
  762.     typename T::const_iterator   it     = arr.begin();
  763.     SIZE_TYPE needed = result.size();
  764.     while (++it != arr.end()) {
  765.         needed += delim.size() + it->size();
  766.     }
  767.     result.reserve(needed);
  768.     it = arr.begin();
  769.     while (++it != arr.end()) {
  770.         result += delim;
  771.         result += *it;
  772.     }
  773.     return result;
  774. }
  775. string NStr::Join(const list<string>& arr, const string& delim)
  776. {
  777.     return s_NStr_Join(arr, delim);
  778. }
  779. string NStr::Join(const vector<string>& arr, const string& delim)
  780. {
  781.     return s_NStr_Join(arr, delim);
  782. }
  783. string NStr::PrintableString(const string&      str,
  784.                              NStr::ENewLineMode nl_mode)
  785. {
  786.     static const char s_Hex[] = "0123456789ABCDEF";
  787.     ITERATE ( string, it, str ) {
  788.         if ( !isprint(*it) || *it == '"' || *it == '\' ) {
  789.             // bad character - convert via CNcbiOstrstream
  790.             CNcbiOstrstream out;
  791.             // write first good characters in one chunk
  792.             out.write(str.data(), it-str.begin());
  793.             // convert all other characters one by one
  794.             do {
  795.                 if ( isprint(*it) ) {
  796.                     // escape '"' and '\' anyway
  797.                     if ( *it == '"' || *it == '\' )
  798.                         out.put('\');
  799.                     out.put(*it);
  800.                 }
  801.                 else if (*it == 'n') {
  802.                     // newline needs special processing
  803.                     if (nl_mode == eNewLine_Quote) {
  804.                         out.write("\n", 2);
  805.                     }
  806.                     else {
  807.                         out.put('n');
  808.                     }
  809.                 } else {
  810.                     // all other non-printable characters need to be escaped
  811.                     out.put('\');
  812.                     if (*it == 't') {
  813.                         out.put('t');
  814.                     } else if (*it == 'r') {
  815.                         out.put('r');
  816.                     } else if (*it == 'v') {
  817.                         out.put('v');
  818.                     } else {
  819.                         // hex string for non-standard codes
  820.                         out.put('x');
  821.                         out.put(s_Hex[(unsigned char) *it >> 4]);
  822.                         out.put(s_Hex[(unsigned char) *it & 15]);
  823.                     }
  824.                 }
  825.             } while (++it < it_end); // it_end is from ITERATE macro
  826.             return CNcbiOstrstreamToString(out);
  827.         }
  828.     }
  829.     // all characters are good - return orignal string
  830.     return str;
  831. }
  832. string NStr::ParseEscapes(const string& str)
  833. {
  834.     string out;
  835.     out.reserve(str.size()); // can only be smaller
  836.     SIZE_TYPE pos = 0;
  837.     while (pos < str.size()) {
  838.         SIZE_TYPE pos2 = str.find('\', pos);
  839.         if (pos2 == NPOS) {
  840.             out += str.substr(pos);
  841.             break;
  842.         }
  843.         out += str.substr(pos, pos2 - pos);
  844.         if (++pos2 == str.size()) {
  845.             NCBI_THROW2(CStringException, eFormat,
  846.                         "Unterminated escape sequence", pos2);
  847.         }
  848.         switch (str[pos2]) {
  849.         case 'a':  out += 'a';  break;
  850.         case 'b':  out += 'b';  break;
  851.         case 'f':  out += 'f';  break;
  852.         case 'n':  out += 'n';  break;
  853.         case 'r':  out += 'r';  break;
  854.         case 't':  out += 't';  break;
  855.         case 'v':  out += 'v';  break;
  856.         case 'x':
  857.         {
  858.             pos = pos2 + 1;
  859.             while (pos2 <= pos  &&  pos2 + 1 < str.size()
  860.                    &&  isxdigit(str[pos2 + 1])) {
  861.                 ++pos2;
  862.             }
  863.             if (pos2 >= pos) {
  864.                 out += static_cast<char>
  865.                     (StringToUInt(str.substr(pos, pos2 - pos + 1), 16));
  866.             } else {
  867.                 NCBI_THROW2(CStringException, eFormat,
  868.                             "\x used with no following digits", pos);
  869.             }
  870.             break;
  871.         }
  872.         case '0':  case '1':  case '2':  case '3':
  873.         case '4':  case '5':  case '6':  case '7':
  874.         {
  875.             pos = pos2;
  876.             unsigned char c = str[pos2] - '0';
  877.             while (pos2 < pos + 3  &&  pos2 + 1 < str.size()
  878.                    &&  str[pos2 + 1] >= '0'  &&  str[pos2 + 1] <= '7') {
  879.                 c = (c << 3) | (str[++pos2] - '0');
  880.             }
  881.             out += c;
  882.         }
  883.         default:
  884.             out += str[pos2];
  885.         }
  886.         pos = pos2 + 1;
  887.     }
  888.     return out;
  889. }
  890. // Determines the end of an HTML <...> tag, accounting for attributes
  891. // and comments (the latter allowed only within <!...>).
  892. static SIZE_TYPE s_EndOfTag(const string& str, SIZE_TYPE start)
  893. {
  894.     _ASSERT(start < str.size()  &&  str[start] == '<');
  895.     bool comments_ok = (start + 1 < str.size()  &&  str[start + 1] == '!');
  896.     for (SIZE_TYPE pos = start + 1;  pos < str.size();  ++pos) {
  897.         switch (str[pos]) {
  898.         case '>': // found the end
  899.             return pos;
  900.         case '"': // start of "string"; advance to end
  901.             pos = str.find('"', pos + 1);
  902.             if (pos == NPOS) {
  903.                 NCBI_THROW2(CStringException, eFormat,
  904.                             "Unclosed string in HTML tag", start);
  905.                 // return pos;
  906.             }
  907.             break;
  908.         case '-': // possible start of -- comment --; advance to end
  909.             if (comments_ok  &&  pos + 1 < str.size()
  910.                 &&  str[pos + 1] == '-') {
  911.                 pos = str.find("--", pos + 2);
  912.                 if (pos == NPOS) {
  913.                     NCBI_THROW2(CStringException, eFormat,
  914.                                 "Unclosed comment in HTML tag", start);
  915.                     // return pos;
  916.                 } else {
  917.                     ++pos;
  918.                 }
  919.             }
  920.         }
  921.     }
  922.     NCBI_THROW2(CStringException, eFormat, "Unclosed HTML tag", start);
  923.     // return NPOS;
  924. }
  925. // Determines the end of an HTML &foo; character/entity reference
  926. // (which might not actually end with a semicolon :-/)
  927. static SIZE_TYPE s_EndOfReference(const string& str, SIZE_TYPE start)
  928. {
  929.     _ASSERT(start < str.size()  &&  str[start] == '&');
  930. #ifdef NCBI_STRICT_HTML_REFS
  931.     return str.find(';', start + 1);
  932. #else
  933.     SIZE_TYPE pos = str.find_first_not_of
  934.         ("#0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
  935.          start + 1);
  936.     if (pos == NPOS  ||  str[pos] == ';') {
  937.         return pos;
  938.     } else {
  939.         return pos - 1;
  940.     }
  941. #endif
  942. }
  943. static SIZE_TYPE s_VisibleWidth(const string& str, bool is_html)
  944. {
  945.     if (is_html) {
  946.         SIZE_TYPE width = 0, pos = 0;
  947.         for (;;) {
  948.             SIZE_TYPE pos2 = str.find_first_of("<&", pos);
  949.             if (pos2 == NPOS) {
  950.                 width += str.size() - pos;
  951.                 break;
  952.             } else {
  953.                 width += pos2 - pos;
  954.                 if (str[pos2] == '&') {
  955.                     ++width;
  956.                     pos = s_EndOfReference(str, pos);
  957.                 } else {
  958.                     pos = s_EndOfTag(str, pos);
  959.                 }
  960.                 if (pos == NPOS) {
  961.                     break;
  962.                 } else {
  963.                     ++pos;
  964.                 }
  965.             }
  966.         }
  967.         return width;
  968.     } else {
  969.         return str.size();
  970.     }
  971. }
  972. list<string>& NStr::Wrap(const string& str, SIZE_TYPE width,
  973.                          list<string>& arr, NStr::TWrapFlags flags,
  974.                          const string* prefix, const string* prefix1)
  975. {
  976.     if (prefix == 0) {
  977.         prefix = &kEmptyStr;
  978.     }
  979.     const string* pfx = prefix1 ? prefix1 : prefix;
  980.     SIZE_TYPE     pos = 0, len = str.size();
  981.     string        hyphen; // "-" or empty
  982.     bool          is_html  = flags & fWrap_HTMLPre ? true : false;
  983.     enum EScore { // worst to best
  984.         eForced,
  985.         ePunct,
  986.         eSpace,
  987.         eNewline
  988.     };
  989.     while (pos < len) {
  990.         SIZE_TYPE column     = s_VisibleWidth(*pfx, is_html);
  991.         SIZE_TYPE column0    = column;
  992.         // the next line will start at best_pos
  993.         SIZE_TYPE best_pos   = NPOS;
  994.         EScore    best_score = eForced;
  995.         for (SIZE_TYPE pos2 = pos;  pos2 < len && column <= width;
  996.              ++pos2, ++column) {
  997.             EScore    score     = eForced;
  998.             SIZE_TYPE score_pos = pos2;
  999.             char      c         = str[pos2];
  1000.             if (c == 'n') {
  1001.                 best_pos   = pos2;
  1002.                 best_score = eNewline;
  1003.                 break;
  1004.             } else if (isspace(c)) {
  1005.                 if (pos2 > 0  &&  isspace(str[pos2 - 1])) {
  1006.                     continue; // take the first space of a group
  1007.                 }
  1008.                 score = eSpace;
  1009.             } else if (is_html  &&  c == '<') {
  1010.                 // treat tags as zero-width...
  1011.                 pos2 = s_EndOfTag(str, pos2);
  1012.                 --column;
  1013.             } else if (is_html  &&  c == '&') {
  1014.                 // ...and references as single characters
  1015.                 pos2 = s_EndOfReference(str, pos2);
  1016.             } else if (ispunct(c)) {
  1017.                 if (c == '('  ||  c == '['  ||  c == '{'  ||  c == '<'
  1018.                     ||  c == '`') { // opening element
  1019.                     score = ePunct;
  1020.                 } else if (score_pos < len - 1) {
  1021.                     // Prefer breaking *after* most types of punctuation.
  1022.                     score = ePunct;
  1023.                     ++score_pos;
  1024.                 }
  1025.             }
  1026.             if (score >= best_score) {
  1027.                 best_pos   = score_pos;
  1028.                 best_score = score;
  1029.             }
  1030.             while (pos2 < len - 1  &&  str[pos2 + 1] == 'b') {
  1031.                 // Account for backspaces
  1032.                 ++pos2;
  1033.                 if (column > column0) {
  1034.                     --column;
  1035.                 }
  1036.             }
  1037.         }
  1038.         if (best_score != eNewline  &&  column <= width) {
  1039.             // If the whole remaining text can fit, don't split it...
  1040.             best_pos = len;
  1041.         } else if (best_score == eForced  &&  (flags & fWrap_Hyphenate)) {
  1042.             hyphen = "-";
  1043.             --best_pos;
  1044.         }
  1045.         arr.push_back(*pfx);
  1046.         {{ // eat backspaces and the characters (if any) that precede them
  1047.             string    line(str, pos, best_pos - pos);
  1048.             SIZE_TYPE bs = 0;
  1049.             while ((bs = line.find('b', bs)) != NPOS) {
  1050.                 if (bs > 0) {
  1051.                     line.erase(bs - 1, 2);
  1052.                 } else {
  1053.                     line.erase(0, 1);
  1054.                 }
  1055.             }
  1056.             arr.back() += line;
  1057.         }}
  1058.         arr.back() += hyphen;
  1059.         pos    = best_pos;
  1060.         pfx    = prefix;
  1061.         hyphen = kEmptyStr;
  1062.         if (best_score == eSpace) {
  1063.             // If breaking at a group of spaces, skip over the whole group
  1064.             while (pos < len  &&  isspace(str[pos])  &&  str[pos] != 'n') {
  1065.                 ++pos;
  1066.             }
  1067.         } else if (best_score == eNewline) {
  1068.             ++pos;
  1069.         }
  1070.         while (pos < len  &&  str[pos] == 'b') {
  1071.             ++pos;
  1072.         }
  1073.     }
  1074.     return arr;
  1075. }
  1076. list<string>& NStr::WrapList(const list<string>& l, SIZE_TYPE width,
  1077.                              const string& delim, list<string>& arr,
  1078.                              NStr::TWrapFlags flags, const string* prefix,
  1079.                              const string* prefix1)
  1080. {
  1081.     if (l.empty()) {
  1082.         return arr;
  1083.     }
  1084.     const string* pfx      = prefix1 ? prefix1 : prefix;
  1085.     string        s        = *pfx;
  1086.     bool          is_html  = flags & fWrap_HTMLPre ? true : false;
  1087.     SIZE_TYPE     column   = s_VisibleWidth(s,     is_html);
  1088.     SIZE_TYPE     delwidth = s_VisibleWidth(delim, is_html);
  1089.     bool          at_start = true;
  1090.     ITERATE (list<string>, it, l) {
  1091.         SIZE_TYPE term_width = s_VisibleWidth(*it, is_html);
  1092.         if (at_start) {
  1093.             if (column + term_width <= width) {
  1094.                 s += *it;
  1095.                 column += term_width;
  1096.                 at_start = false;
  1097.             } else {
  1098.                 // Can't fit, even on its own line; break separately.
  1099.                 Wrap(*it, width, arr, flags, prefix, pfx);
  1100.                 pfx      = prefix;
  1101.                 s        = *prefix;
  1102.                 column   = s_VisibleWidth(s, is_html);
  1103.                 at_start = true;
  1104.             }
  1105.         } else if (column + delwidth + term_width <= width) {
  1106.             s += delim;
  1107.             s += *it;
  1108.             column += delwidth + term_width;
  1109.             at_start = false;
  1110.         } else {
  1111.             // Can't fit on this line; break here and try again.
  1112.             arr.push_back(s);
  1113.             pfx      = prefix;
  1114.             s        = *prefix;
  1115.             column   = s_VisibleWidth(s, is_html);
  1116.             at_start = true;
  1117.             --it;
  1118.         }
  1119.     }
  1120.     arr.push_back(s);
  1121.     return arr;
  1122. }
  1123. #if !defined(HAVE_STRDUP)
  1124. extern char* strdup(const char* str)
  1125. {
  1126.     if ( !str )
  1127.         return 0;
  1128.     size_t size   = strlen(str) + 1;
  1129.     void*  result = malloc(size);
  1130.     return (char*) (result ? memcpy(result, str, size) : 0);
  1131. }
  1132. #endif
  1133. /////////////////////////////////////////////////////////////////////////////
  1134. //  CStringUTF8
  1135. void CStringUTF8::x_Append(const char* src)
  1136. {
  1137.     const char* srcBuf;
  1138.     size_t needed = 0;
  1139.     for (srcBuf = src; *srcBuf; ++srcBuf) {
  1140.         Uint1 ch = *srcBuf;
  1141.         if (ch < 0x80) {
  1142.             ++needed;
  1143.         } else {
  1144.             needed += 2;
  1145.         }
  1146.     }
  1147.     if ( !needed )
  1148.         return;
  1149.     reserve(length()+needed+1);
  1150.     for (srcBuf = src; *srcBuf; ++srcBuf) {
  1151.         Uint1 ch = *srcBuf;
  1152.         if (ch < 0x80) {
  1153.             append(1, ch);
  1154.         } else {
  1155.             append(1, Uint1((ch >> 6) | 0xC0));
  1156.             append(1, Uint1((ch & 0x3F) | 0x80));
  1157.         }
  1158.     }
  1159. }
  1160. #if defined(HAVE_WSTRING)
  1161. void CStringUTF8::x_Append(const wchar_t* src)
  1162. {
  1163.     const wchar_t* srcBuf;
  1164.     size_t needed = 0;
  1165.     for (srcBuf = src; *srcBuf; ++srcBuf) {
  1166.         Uint2 ch = *srcBuf;
  1167.         if (ch < 0x80) {
  1168.             ++needed;
  1169.         } else if (ch < 0x800) {
  1170.             needed += 2;
  1171.         } else {
  1172.             needed += 3;
  1173.         }
  1174.     }
  1175.     if ( !needed )
  1176.         return;
  1177.     reserve(length()+needed+1);
  1178.     for (srcBuf = src; *srcBuf; ++srcBuf) {
  1179.         Uint2 ch = *srcBuf;
  1180.         if (ch < 0x80) {
  1181.             append(1, ch);
  1182.         }
  1183.         else if (ch < 0x800) {
  1184.             append(1, Uint2((ch >> 6) | 0xC0));
  1185.             append(1, Uint2((ch & 0x3F) | 0x80));
  1186.         } else {
  1187.             append(1, Uint2((ch >> 12) | 0xE0));
  1188.             append(1, Uint2(((ch >> 6) & 0x3F) | 0x80));
  1189.             append(1, Uint2((ch & 0x3F) | 0x80));
  1190.         }
  1191.     }
  1192. }
  1193. #endif // HAVE_WSTRING
  1194. string CStringUTF8::AsAscii(void) const
  1195. {
  1196.     string result;
  1197.     const char* srcBuf;
  1198.     size_t needed = 0;
  1199.     bool bad = false;
  1200.     bool enough = true;
  1201.     for (srcBuf = c_str(); *srcBuf; ++srcBuf) {
  1202.         Uint1 ch = *srcBuf;
  1203.         if ((ch & 0x80) == 0) {
  1204.             ++needed;
  1205.         } else if ((ch & 0xE0) == 0xC0) {
  1206.             enough = (ch & 0x1F) <= 0x03;
  1207.             if (enough) {
  1208.                 ++needed;
  1209.                 ch = *(++srcBuf);
  1210.                 bad = (ch & 0xC0) != 0x80;
  1211.             }
  1212.         } else if ((ch & 0xF0) == 0xE0) {
  1213.             enough = false;
  1214.         } else {
  1215.             bad = true;
  1216.         }
  1217.         if (!enough) {
  1218.             NCBI_THROW2(CStringException, eConvert,
  1219.                         "Cannot convert UTF8 string to single-byte string",
  1220.                         s_DiffPtr(srcBuf,c_str()));
  1221.         }
  1222.         if (bad) {
  1223.             NCBI_THROW2(CStringException, eFormat,
  1224.                         "Wrong UTF8 format",
  1225.                         s_DiffPtr(srcBuf,c_str()));
  1226.         }
  1227.     }
  1228.     result.reserve( needed+1);
  1229.     for (srcBuf = c_str(); *srcBuf; ++srcBuf) {
  1230.         Uint1 chRes;
  1231.         size_t more;
  1232.         Uint1 ch = *srcBuf;
  1233.         if ((ch & 0x80) == 0) {
  1234.             chRes = ch;
  1235.             more = 0;
  1236.         } else {
  1237.             chRes = (ch & 0x1F);
  1238.             more = 1;
  1239.         }
  1240.         while (more--) {
  1241.             ch = *(++srcBuf);
  1242.             chRes = (chRes << 6) | (ch & 0x3F);
  1243.         }
  1244.         result += chRes;
  1245.     }
  1246.     return result;
  1247. }
  1248. #if defined(HAVE_WSTRING)
  1249. wstring CStringUTF8::AsUnicode(void) const
  1250. {
  1251.     wstring result;
  1252.     const char* srcBuf;
  1253.     size_t needed = 0;
  1254.     bool bad = false;
  1255.     for (srcBuf = c_str(); *srcBuf; ++srcBuf) {
  1256.         Uint1 ch = *srcBuf;
  1257.         if ((ch & 0x80) == 0) {
  1258.             ++needed;
  1259.         } else if ((ch & 0xE0) == 0xC0) {
  1260.             ++needed;
  1261.             ch = *(++srcBuf);
  1262.             bad = (ch & 0xC0) != 0x80;
  1263.         } else if ((ch & 0xF0) == 0xE0) {
  1264.             ++needed;
  1265.             ch = *(++srcBuf);
  1266.             bad = (ch & 0xC0) != 0x80;
  1267.             if (!bad) {
  1268.                 ch = *(++srcBuf);
  1269.                 bad = (ch & 0xC0) != 0x80;
  1270.             }
  1271.         } else {
  1272.             bad = true;
  1273.         }
  1274.         if (bad) {
  1275.             NCBI_THROW2(CStringException, eFormat,
  1276.                         "Wrong UTF8 format",
  1277.                         s_DiffPtr(srcBuf,c_str()));
  1278.         }
  1279.     }
  1280.     result.reserve( needed+1);
  1281.     for (srcBuf = c_str(); *srcBuf; ++srcBuf) {
  1282.         Uint2 chRes;
  1283.         size_t more;
  1284.         Uint1 ch = *srcBuf;
  1285.         if ((ch & 0x80) == 0) {
  1286.             chRes = ch;
  1287.             more = 0;
  1288.         } else if ((ch & 0xE0) == 0xC0) {
  1289.             chRes = (ch & 0x1F);
  1290.             more = 1;
  1291.         } else {
  1292.             chRes = (ch & 0x0F);
  1293.             more = 2;
  1294.         }
  1295.         while (more--) {
  1296.             ch = *(++srcBuf);
  1297.             chRes = (chRes << 6) | (ch & 0x3F);
  1298.         }
  1299.         result += chRes;
  1300.     }
  1301.     return result;
  1302. }
  1303. #endif // HAVE_WSTRING
  1304. END_NCBI_SCOPE
  1305. /*
  1306.  * ===========================================================================
  1307.  * $Log: ncbistr.cpp,v $
  1308.  * Revision 1000.6  2004/06/01 19:09:21  gouriano
  1309.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.109
  1310.  *
  1311.  * Revision 1.109  2004/05/26 19:21:25  ucko
  1312.  * FindNoCase: avoid looping in eLastMode when there aren't any full
  1313.  * matches but the first character of the string matches the first
  1314.  * character of the pattern.
  1315.  *
  1316.  * Revision 1.108  2004/05/14 13:59:27  gorelenk
  1317.  * Added include of ncbi_pch.hpp
  1318.  *
  1319.  * Revision 1.107  2004/03/11 18:49:48  gorelenk
  1320.  * Removed(condionaly) implementation of class CNcbiEmptyString.
  1321.  *
  1322.  * Revision 1.106  2004/03/05 12:26:43  ivanov
  1323.  * Moved CDirEntry::MatchesMask() to NStr class.
  1324.  *
  1325.  * Revision 1.105  2004/03/04 13:38:57  kuznets
  1326.  * + set of ToString conversion functions taking outout string as a parameter,
  1327.  * not a return value (should give a performance advantage in some cases)
  1328.  *
  1329.  * Revision 1.104  2004/02/19 16:44:55  vasilche
  1330.  * WorkShop compiler doesn't support static templates.
  1331.  *
  1332.  * Revision 1.103  2004/02/18 20:54:47  shomrat
  1333.  * bug fix (pos -> pos2)
  1334.  *
  1335.  * Revision 1.102  2003/12/12 20:06:44  rsmith
  1336.  * Take out un-needed include of stdarg.h (included in ncbistr.hpp).
  1337.  *
  1338.  * Revision 1.101  2003/12/12 20:04:24  rsmith
  1339.  * make sure stdarg.h is included to define va_list.
  1340.  *
  1341.  * Revision 1.100  2003/12/12 17:26:54  ucko
  1342.  * +FormatVarargs
  1343.  *
  1344.  * Revision 1.99  2003/12/01 20:45:47  ucko
  1345.  * Extend Join to handle vectors as well as lists (common code templatized).
  1346.  * Add ParseEscapes (inverse of PrintableString).
  1347.  *
  1348.  * Revision 1.98  2003/10/31 13:15:20  lavr
  1349.  * Fix typos in the log of the previous commit :-)
  1350.  *
  1351.  * Revision 1.97  2003/10/31 12:59:46  lavr
  1352.  * Better diagnostics messages from exceptions; some other cosmetic changes
  1353.  *
  1354.  * Revision 1.96  2003/10/03 15:16:02  ucko
  1355.  * NStr::Join: preallocate as much space as we need for result.
  1356.  *
  1357.  * Revision 1.95  2003/09/17 15:18:29  vasilche
  1358.  * Reduce memory allocations in NStr::PrintableString()
  1359.  *
  1360.  * Revision 1.94  2003/08/19 15:17:20  rsmith
  1361.  * Add NStr::SplitInTwo() function.
  1362.  *
  1363.  * Revision 1.93  2003/06/16 15:19:03  ucko
  1364.  * FindNoCase: always honor both start and end (oops).
  1365.  *
  1366.  * Revision 1.92  2003/05/22 20:09:29  gouriano
  1367.  * added UTF8 strings
  1368.  *
  1369.  * Revision 1.91  2003/05/14 21:52:09  ucko
  1370.  * Move FindNoCase out of line and reimplement it to avoid making
  1371.  * lowercase copies of both strings.
  1372.  *
  1373.  * Revision 1.90  2003/03/25 22:15:40  lavr
  1374.  * NStr::PrintableString():: Print NUL char as x00 instead of 
  1375.  *
  1376.  * Revision 1.89  2003/03/20 13:27:52  dicuccio
  1377.  * Oops.  Removed old code wrapped in #if 0...#endif.
  1378.  *
  1379.  * Revision 1.88  2003/03/20 13:27:11  dicuccio
  1380.  * Changed NStr::StringToPtr() - now symmetric with NSrt::PtrToString (there were
  1381.  * too many special cases).
  1382.  *
  1383.  * Revision 1.87  2003/03/17 12:49:26  dicuccio
  1384.  * Fixed indexing error in NStr::PtrToString() - buffer is 0-based index, not
  1385.  * 1-based
  1386.  *
  1387.  * Revision 1.86  2003/03/11 16:57:12  ucko
  1388.  * Process backspaces in NStr::Wrap, allowing in particular the use of
  1389.  * " b" ("-b") to indicate mid-word break (hyphenation) points.
  1390.  *
  1391.  * Revision 1.85  2003/03/10 18:57:08  kuznets
  1392.  * iterate->ITERATE
  1393.  *
  1394.  * Revision 1.84  2003/03/04 00:02:21  vakatov
  1395.  * NStr::PtrToString() -- use runtime check for "0x"
  1396.  * NStr::StringToPtr() -- minor polishing
  1397.  *
  1398.  * Revision 1.83  2003/02/27 15:34:01  lavr
  1399.  * Bugfix in converting string to double [spurious dots], some reformatting
  1400.  *
  1401.  * Revision 1.82  2003/02/26 21:07:52  siyan
  1402.  * Remove const for base parameter for StringToUInt8
  1403.  *
  1404.  * Revision 1.81  2003/02/26 20:34:11  siyan
  1405.  * Added/deleted whitespaces to conform to existing coding style
  1406.  *
  1407.  * Revision 1.80  2003/02/26 16:45:53  siyan
  1408.  * Reimplemented NStr::StringToUInt8 to support additional base parameters
  1409.  * that can take radix values such as 10(default), 16, 8, 2.
  1410.  * Reimplemented StringToPtr to support 64 bit addresses.
  1411.  *
  1412.  * Revision 1.79  2003/02/25 19:14:53  kuznets
  1413.  * NStr::StringToBool changed to understand YES/NO
  1414.  *
  1415.  * Revision 1.78  2003/02/25 15:43:40  dicuccio
  1416.  * Added #ifdef'd hack for MSVC's non-standard sprintf() in PtrToString(),
  1417.  * '%p' lacks a leading '0x'
  1418.  *
  1419.  * Revision 1.77  2003/02/25 14:43:53  dicuccio
  1420.  * Added handling of special NULL pointer encoding in StringToPtr()
  1421.  *
  1422.  * Revision 1.76  2003/02/24 20:25:59  gouriano
  1423.  * use template-based errno and parse exceptions
  1424.  *
  1425.  * Revision 1.75  2003/02/21 21:20:01  vakatov
  1426.  * Fixed some types, did some casts to avoid compilation warnings in 64-bit
  1427.  *
  1428.  * Revision 1.74  2003/02/20 18:41:28  dicuccio
  1429.  * Added NStr::StringToPtr()
  1430.  *
  1431.  * Revision 1.73  2003/02/11 22:11:03  ucko
  1432.  * Make NStr::WrapList a no-op if the input list is empty.
  1433.  *
  1434.  * Revision 1.72  2003/02/06 21:31:35  ucko
  1435.  * Fixed an off-by-one error in NStr::Wrap.
  1436.  *
  1437.  * Revision 1.71  2003/02/04 21:54:12  ucko
  1438.  * NStr::Wrap: when breaking on punctuation, try to position the break
  1439.  * *after* everything but opening delimiters.
  1440.  *
  1441.  * Revision 1.70  2003/01/31 03:39:11  lavr
  1442.  * Heed int->bool performance warning
  1443.  *
  1444.  * Revision 1.69  2003/01/27 20:06:59  ivanov
  1445.  * Get rid of compilation warnings in StringToUInt8() and DoubleToString()
  1446.  *
  1447.  * Revision 1.68  2003/01/24 16:59:27  ucko
  1448.  * Add an optional parameter to Split and Tokenize indicating whether to
  1449.  * merge adjacent delimiters; clean up WrapList slightly.
  1450.  *
  1451.  * Revision 1.67  2003/01/21 23:22:22  vakatov
  1452.  * NStr::Tokenize() to return reference, and not a new "vector<>".
  1453.  *
  1454.  * Revision 1.66  2003/01/21 20:08:01  ivanov
  1455.  * Added function NStr::DoubleToString(value, precision, buf, buf_size)
  1456.  *
  1457.  * Revision 1.65  2003/01/14 22:13:56  kuznets
  1458.  * Overflow check reimplemented for NStr::StringToInt
  1459.  *
  1460.  * Revision 1.64  2003/01/14 21:16:46  kuznets
  1461.  * +Nstr::Tokenize
  1462.  *
  1463.  * Revision 1.63  2003/01/13 14:47:16  kuznets
  1464.  * Implemented overflow checking for StringToInt8 function
  1465.  *
  1466.  * Revision 1.62  2003/01/10 22:17:06  kuznets
  1467.  * Implemented NStr::String2Int8
  1468.  *
  1469.  * Revision 1.61  2003/01/10 16:49:54  kuznets
  1470.  * Cosmetics
  1471.  *
  1472.  * Revision 1.60  2003/01/10 15:27:12  kuznets
  1473.  * Eliminated int -> bool performance warning
  1474.  *
  1475.  * Revision 1.59  2003/01/10 00:08:17  vakatov
  1476.  * + Int8ToString(),  UInt8ToString()
  1477.  *
  1478.  * Revision 1.58  2003/01/06 16:42:45  ivanov
  1479.  * + DoubleToString() with 'precision'
  1480.  *
  1481.  * Revision 1.57  2002/10/18 20:48:56  lavr
  1482.  * +ENewLine_Mode and 'n' translation in NStr::PrintableString()
  1483.  *
  1484.  * Revision 1.56  2002/10/17 14:41:20  ucko
  1485.  * * Make s_EndOf{Tag,Reference} actually static (oops).
  1486.  * * Pull width-determination code from WrapList into a separate function
  1487.  *   (s_VisibleWidth) and make Wrap{,List} call it for everything rather
  1488.  *   than assuming prefixes and delimiters to be plain text.
  1489.  * * Add a column variable to WrapList, as it may not equal s.size().
  1490.  *
  1491.  * Revision 1.55  2002/10/16 19:30:36  ucko
  1492.  * Add support for wrapping HTML <PRE> blocks.  (Not yet tested, but
  1493.  * behavior without fWrap_HTMLPre should stay the same.)
  1494.  *
  1495.  * Revision 1.54  2002/10/11 19:41:48  ucko
  1496.  * Clean up NStr::Wrap a bit more, doing away with the "limit" variables
  1497.  * for ease of potential extension.
  1498.  *
  1499.  * Revision 1.53  2002/10/03 14:44:35  ucko
  1500.  * Tweak the interfaces to NStr::Wrap* to avoid publicly depending on
  1501.  * kEmptyStr, removing the need for fWrap_UsePrefix1 in the process; also
  1502.  * drop fWrap_FavorPunct, as WrapList should be a better choice for such
  1503.  * situations.
  1504.  *
  1505.  * Revision 1.52  2002/10/02 20:15:09  ucko
  1506.  * Add Join, Wrap, and WrapList functions to NStr::.
  1507.  *
  1508.  * Revision 1.51  2002/09/04 15:16:57  lavr
  1509.  * Backslashed double quote (") in PrintableString()
  1510.  *
  1511.  * Revision 1.50  2002/07/15 18:17:25  gouriano
  1512.  * renamed CNcbiException and its descendents
  1513.  *
  1514.  * Revision 1.49  2002/07/11 14:18:28  gouriano
  1515.  * exceptions replaced by CNcbiException-type ones
  1516.  *
  1517.  * Revision 1.48  2002/05/02 15:25:37  ivanov
  1518.  * Added new parameter to String-to-X functions for skipping the check
  1519.  * the end of string on zero
  1520.  *
  1521.  * Revision 1.47  2002/04/11 21:08:03  ivanov
  1522.  * CVS log moved to end of the file
  1523.  *
  1524.  * Revision 1.46  2002/02/22 17:50:52  ivanov
  1525.  * Added compatible compare functions strcmp, strncmp, strcasecmp, strncasecmp.
  1526.  * Was speed-up some Compare..() functions.
  1527.  *
  1528.  * Revision 1.45  2001/08/30 00:36:45  vakatov
  1529.  * + NStr::StringToNumeric()
  1530.  * Also, well-groomed the code and get rid of some compilation warnings.
  1531.  *
  1532.  * Revision 1.44  2001/05/30 15:56:25  vakatov
  1533.  * NStr::CompareNocase, NStr::CompareCase -- get rid of the possible
  1534.  * compilation warning (ICC compiler:  "return statement missing").
  1535.  *
  1536.  * Revision 1.43  2001/05/17 15:04:59  lavr
  1537.  * Typos corrected
  1538.  *
  1539.  * Revision 1.42  2001/04/12 21:39:44  vakatov
  1540.  * NStr::Replace() -- check against source and dest. strings being the same
  1541.  *
  1542.  * Revision 1.41  2001/04/11 20:15:29  vakatov
  1543.  * NStr::PrintableString() -- cast "char" to "unsigned char".
  1544.  *
  1545.  * Revision 1.40  2001/03/16 19:38:35  grichenk
  1546.  * Added NStr::Split()
  1547.  *
  1548.  * Revision 1.39  2001/01/03 17:45:35  vakatov
  1549.  * + <ncbi_limits.h>
  1550.  *
  1551.  * Revision 1.38  2000/12/15 15:36:41  vasilche
  1552.  * Added header corelib/ncbistr.hpp for all string utility functions.
  1553.  * Optimized string utility functions.
  1554.  * Added assignment operator to CRef<> and CConstRef<>.
  1555.  * Add Upcase() and Locase() methods for automatic conversion.
  1556.  *
  1557.  * Revision 1.37  2000/12/12 14:20:36  vasilche
  1558.  * Added operator bool to CArgValue.
  1559.  * Various NStr::Compare() methods made faster.
  1560.  * Added class Upcase for printing strings to ostream with automatic conversion
  1561.  *
  1562.  * Revision 1.36  2000/12/11 20:42:50  vakatov
  1563.  * + NStr::PrintableString()
  1564.  *
  1565.  * Revision 1.35  2000/11/16 23:52:41  vakatov
  1566.  * Porting to Mac...
  1567.  *
  1568.  * Revision 1.34  2000/11/07 04:06:08  vakatov
  1569.  * kEmptyCStr (equiv. to NcbiEmptyCStr)
  1570.  *
  1571.  * Revision 1.33  2000/10/11 21:03:49  vakatov
  1572.  * Cleanup to avoid 64-bit to 32-bit values truncation, etc.
  1573.  * (reported by Forte6 Patch 109490-01)
  1574.  *
  1575.  * Revision 1.32  2000/08/03 20:21:29  golikov
  1576.  * Added predicate PCase for AStrEquiv
  1577.  * PNocase, PCase goes through NStr::Compare now
  1578.  *
  1579.  * Revision 1.31  2000/07/19 19:03:55  vakatov
  1580.  * StringToBool() -- short and case-insensitive versions of "true"/"false"
  1581.  * ToUpper/ToLower(string&) -- fixed
  1582.  *
  1583.  * Revision 1.30  2000/06/01 19:05:40  vasilche
  1584.  * NStr::StringToInt now reports errors for tailing symbols in release version
  1585.  * too
  1586.  *
  1587.  * Revision 1.29  2000/05/01 19:02:25  vasilche
  1588.  * Force argument in NStr::StringToInt() etc to be full number.
  1589.  * This check will be in DEBUG version for month.
  1590.  *
  1591.  * Revision 1.28  2000/04/19 18:36:04  vakatov
  1592.  * Fixed for non-zero "pos" in s_Compare()
  1593.  *
  1594.  * Revision 1.27  2000/04/17 04:15:08  vakatov
  1595.  * NStr::  extended Compare(), and allow case-insensitive string comparison
  1596.  * NStr::  added ToLower() and ToUpper()
  1597.  *
  1598.  * Revision 1.26  2000/04/04 22:28:09  vakatov
  1599.  * NStr::  added conversions for "long"
  1600.  *
  1601.  * Revision 1.25  2000/02/01 16:48:09  vakatov
  1602.  * CNcbiEmptyString::  more dancing around the Sun "feature" (see also R1.24)
  1603.  *
  1604.  * Revision 1.24  2000/01/20 16:24:42  vakatov
  1605.  * Kludging around the "NcbiEmptyString" to ensure its initialization when
  1606.  * it is used by the constructor of a statically allocated object
  1607.  * (I believe that it is actually just another Sun WorkShop compiler "feature")
  1608.  *
  1609.  * Revision 1.23  1999/12/28 18:55:43  vasilche
  1610.  * Reduced size of compiled object files:
  1611.  * 1. avoid inline or implicit virtual methods (especially destructors).
  1612.  * 2. avoid std::string's methods usage in inline methods.
  1613.  * 3. avoid string literals ("xxx") in inline methods.
  1614.  *
  1615.  * Revision 1.22  1999/12/17 19:04:09  vasilche
  1616.  * NcbiEmptyString made extern.
  1617.  *
  1618.  * Revision 1.21  1999/11/26 19:29:09  golikov
  1619.  * fix
  1620.  *
  1621.  * Revision 1.20  1999/11/26 18:45:17  golikov
  1622.  * NStr::Replace added
  1623.  *
  1624.  * Revision 1.19  1999/11/17 22:05:04  vakatov
  1625.  * [!HAVE_STRDUP]  Emulate "strdup()" -- it's missing on some platforms
  1626.  *
  1627.  * Revision 1.18  1999/10/13 16:30:30  vasilche
  1628.  * Fixed bug in PNocase which appears under GCC implementation of STL.
  1629.  *
  1630.  * Revision 1.17  1999/07/08 16:10:14  vakatov
  1631.  * Fixed a warning in NStr::StringToUInt()
  1632.  *
  1633.  * Revision 1.16  1999/07/06 15:21:06  vakatov
  1634.  * + NStr::TruncateSpaces(const string& str, ETrunc where=eTrunc_Both)
  1635.  *
  1636.  * Revision 1.15  1999/06/15 20:50:05  vakatov
  1637.  * NStr::  +BoolToString, +StringToBool
  1638.  *
  1639.  * Revision 1.14  1999/05/27 15:21:40  vakatov
  1640.  * Fixed all StringToXXX() functions
  1641.  *
  1642.  * Revision 1.13  1999/05/17 20:10:36  vasilche
  1643.  * Fixed bug in NStr::StringToUInt which cause an exception.
  1644.  *
  1645.  * Revision 1.12  1999/05/04 00:03:13  vakatov
  1646.  * Removed the redundant severity arg from macro ERR_POST()
  1647.  *
  1648.  * Revision 1.11  1999/04/22 14:19:04  vasilche
  1649.  * Added _TRACE_THROW() macro, which can be configured to produce coredump
  1650.  * at a point of throwing an exception.
  1651.  *
  1652.  * Revision 1.10  1999/04/14 21:20:33  vakatov
  1653.  * Dont use "snprintf()" as it is not quite portable yet
  1654.  *
  1655.  * Revision 1.9  1999/04/14 19:57:36  vakatov
  1656.  * Use limits from <ncbitype.h> rather than from <limits>.
  1657.  * [MSVC++]  fix for "snprintf()" in <ncbistd.hpp>.
  1658.  *
  1659.  * Revision 1.8  1999/04/09 19:51:37  sandomir
  1660.  * minor changes in NStr::StringToXXX - base added
  1661.  *
  1662.  * Revision 1.7  1999/01/21 16:18:04  sandomir
  1663.  * minor changes due to NStr namespace to contain string utility functions
  1664.  *
  1665.  * Revision 1.6  1999/01/11 22:05:50  vasilche
  1666.  * Fixed CHTML_font size.
  1667.  * Added CHTML_image input element.
  1668.  *
  1669.  * Revision 1.5  1998/12/28 17:56:39  vakatov
  1670.  * New CVS and development tree structure for the NCBI C++ projects
  1671.  *
  1672.  * Revision 1.4  1998/12/21 17:19:37  sandomir
  1673.  * VC++ fixes in ncbistd; minor fixes in Resource
  1674.  *
  1675.  * Revision 1.3  1998/12/17 21:50:45  sandomir
  1676.  * CNCBINode fixed in Resource; case insensitive string comparison predicate
  1677.  * added
  1678.  *
  1679.  * Revision 1.2  1998/12/15 17:36:33  vasilche
  1680.  * Fixed "double colon" bug in multithreaded version of headers.
  1681.  *
  1682.  * Revision 1.1  1998/12/15 15:43:22  vasilche
  1683.  * Added utilities to convert string <> int.
  1684.  * ===========================================================================
  1685.  */