os/ossrv/ossrv_pub/boost_apis/boost/format/parsing.hpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // ----------------------------------------------------------------------------
     2 // parsing.hpp :  implementation of the parsing member functions
     3 //                      ( parse, parse_printf_directive)
     4 // ----------------------------------------------------------------------------
     5 
     6 //  Copyright Samuel Krempp 2003. Use, modification, and distribution are
     7 //  subject to the Boost Software License, Version 1.0. (See accompanying
     8 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
     9 
    10 // see http://www.boost.org/libs/format for library home page
    11 
    12 // ----------------------------------------------------------------------------
    13 
    14 #ifndef BOOST_FORMAT_PARSING_HPP
    15 #define BOOST_FORMAT_PARSING_HPP
    16 
    17 
    18 #include <boost/format/format_class.hpp>
    19 #include <boost/throw_exception.hpp>
    20 #include <boost/assert.hpp>
    21 
    22 
    23 namespace boost {
    24 namespace io {
    25 namespace detail {
    26 
    27 #if defined(BOOST_NO_STD_LOCALE)
    28     // streams will be used for narrow / widen. but these methods are not const
    29     template<class T>
    30     T& const_or_not(const T& x) { 
    31         return const_cast<T&> (x);
    32     }
    33 #else
    34     template<class T>
    35     const T& const_or_not(const T& x) { 
    36         return x;
    37     }
    38 #endif
    39 
    40     template<class Ch, class Facet> inline
    41     char wrap_narrow(const Facet& fac, Ch c, char deflt) {
    42         return const_or_not(fac).narrow(c, deflt);
    43     }
    44 
    45     template<class Ch, class Facet> inline
    46     bool wrap_isdigit(const Facet& fac, Ch c) {
    47 #if ! defined( BOOST_NO_LOCALE_ISDIGIT )
    48         return fac.is(std::ctype<Ch>::digit, c);
    49 # else
    50         using namespace std;
    51         return isdigit(c); 
    52 #endif 
    53     }
    54  
    55     template<class Iter, class Facet> 
    56     Iter wrap_scan_notdigit(const Facet & fac, Iter beg, Iter end) {
    57         using namespace std;
    58         for( ; beg!=end && wrap_isdigit(fac, *beg); ++beg) ;
    59         return beg;
    60     }
    61 
    62 
    63     // Input : [start, last) iterators range and a
    64     //          a Facet to use its widen/narrow member function
    65     // Effects : read sequence and convert digits into integral n, of type Res
    66     // Returns : n
    67     template<class Res, class Iter, class Facet>
    68     Iter str2int (const Iter & start, const Iter & last, Res & res, 
    69                  const Facet& fac) 
    70     {
    71         using namespace std;
    72         Iter it;
    73         res=0;
    74         for(it=start; it != last && wrap_isdigit(fac, *it); ++it ) {
    75             char cur_ch = wrap_narrow(fac, *it, 0); // cant fail.
    76             res *= 10;
    77             res += cur_ch - '0'; // 22.2.1.1.2.13 of the C++ standard
    78         }
    79         return it;
    80     }
    81 
    82     // skip printf's "asterisk-fields" directives in the format-string buf
    83     // Input : char string, with starting index *pos_p
    84     //         a Facet merely to use its widen/narrow member function
    85     // Effects : advance *pos_p by skipping printf's asterisk fields.
    86     // Returns : nothing
    87     template<class Iter, class Facet>
    88     Iter skip_asterisk(Iter start, Iter last, const Facet& fac) 
    89     {
    90         using namespace std;
    91         ++ start;
    92         start = wrap_scan_notdigit(fac, start, last);
    93         if(start!=last && *start== const_or_not(fac).widen( '$') )
    94             ++start;
    95         return start;
    96     }
    97 
    98 
    99     // auxiliary func called by parse_printf_directive
   100     // for centralising error handling
   101     // it either throws if user sets the corresponding flag, or does nothing.
   102     inline void maybe_throw_exception(unsigned char exceptions, 
   103                                       std::size_t pos, std::size_t size)
   104     {
   105         if(exceptions & io::bad_format_string_bit)
   106             boost::throw_exception(io::bad_format_string(pos, size) );
   107     }
   108     
   109 
   110     // Input: the position of a printf-directive in the format-string
   111     //    a basic_ios& merely to use its widen/narrow member function
   112     //    a bitset'exceptions' telling whether to throw exceptions on errors.
   113     // Returns:
   114     //  true if parse succeeded (ignore some errors if exceptions disabled)
   115     //  false if it failed so bad that the directive should be printed verbatim
   116     // Effects:
   117     //  start is incremented so that *start is the first char after
   118     //     this directive
   119     //  *fpar is set with the parameters read in the directive
   120     template<class Ch, class Tr, class Alloc, class Iter, class Facet>
   121     bool parse_printf_directive(Iter & start, const Iter& last, 
   122                                 detail::format_item<Ch, Tr, Alloc> * fpar,
   123                                 const Facet& fac,
   124                                 std::size_t offset, unsigned char exceptions)
   125     {
   126         typedef typename basic_format<Ch, Tr, Alloc>::format_item_t format_item_t;
   127 
   128         fpar->argN_ = format_item_t::argN_no_posit;  // if no positional-directive
   129         bool precision_set = false;
   130         bool in_brackets=false;
   131         Iter start0 = start;
   132         std::size_t fstring_size = last-start0+offset;
   133         if(*start== const_or_not(fac).widen( '|')) {
   134             in_brackets=true;
   135             if( ++start >= last ) {
   136                 maybe_throw_exception(exceptions, start-start0 + offset, fstring_size);
   137                 return false;
   138             }
   139         }
   140 
   141         // the flag '0' would be picked as a digit for argument order, but here it's a flag :
   142         if(*start== const_or_not(fac).widen( '0')) 
   143             goto parse_flags;
   144 
   145         // handle argument order (%2$d)  or possibly width specification: %2d
   146         if(wrap_isdigit(fac, *start)) {
   147             int n;
   148             start = str2int(start, last, n, fac);
   149             if( start >= last ) {
   150                 maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
   151                 return false;
   152             }
   153             
   154             // %N% case : this is already the end of the directive
   155             if( *start ==  const_or_not(fac).widen( '%') ) {
   156                 fpar->argN_ = n-1;
   157                 ++start;
   158                 if( in_brackets) 
   159                     maybe_throw_exception(exceptions, start-start0+offset, fstring_size); 
   160                 // but don't return.  maybe "%" was used in lieu of '$', so we go on.
   161                 else
   162                     return true;
   163             }
   164 
   165             if ( *start== const_or_not(fac).widen( '$') ) {
   166                 fpar->argN_ = n-1;
   167                 ++start;
   168             } 
   169             else {
   170                 // non-positionnal directive
   171                 fpar->fmtstate_.width_ = n;
   172                 fpar->argN_  = format_item_t::argN_no_posit;
   173                 goto parse_precision;
   174             }
   175         }
   176     
   177       parse_flags: 
   178         // handle flags
   179         while ( start != last) { // as long as char is one of + - = _ # 0 l h   or ' '
   180             // misc switches
   181             switch ( wrap_narrow(fac, *start, 0)) {
   182             case '\'' : break; // no effect yet. (painful to implement)
   183             case 'l':
   184             case 'h':  // short/long modifier : for printf-comaptibility (no action needed)
   185                 break;
   186             case '-':
   187                 fpar->fmtstate_.flags_ |= std::ios_base::left;
   188                 break;
   189             case '=':
   190                 fpar->pad_scheme_ |= format_item_t::centered;
   191                 break;
   192             case '_':
   193                 fpar->fmtstate_.flags_ |= std::ios_base::internal;
   194                 break;
   195             case ' ':
   196                 fpar->pad_scheme_ |= format_item_t::spacepad;
   197                 break;
   198             case '+':
   199                 fpar->fmtstate_.flags_ |= std::ios_base::showpos;
   200                 break;
   201             case '0':
   202                 fpar->pad_scheme_ |= format_item_t::zeropad;
   203                 // need to know alignment before really setting flags,
   204                 // so just add 'zeropad' flag for now, it will be processed later.
   205                 break;
   206             case '#':
   207                 fpar->fmtstate_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
   208                 break;
   209             default:
   210                 goto parse_width;
   211             }
   212             ++start;
   213         } // loop on flag.
   214 
   215         if( start>=last) {
   216             maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
   217             return true; 
   218         }
   219       parse_width:
   220         // handle width spec
   221         // first skip 'asterisk fields' :  *, or *N$
   222         if(*start == const_or_not(fac).widen( '*') )
   223             start = skip_asterisk(start, last, fac); 
   224         if(start!=last && wrap_isdigit(fac, *start))
   225             start = str2int(start, last, fpar->fmtstate_.width_, fac);
   226 
   227       parse_precision:
   228         if( start>= last) { 
   229             maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
   230             return true;
   231         }
   232         // handle precision spec
   233         if (*start== const_or_not(fac).widen( '.')) {
   234             ++start;
   235             if(start != last && *start == const_or_not(fac).widen( '*') )
   236                 start = skip_asterisk(start, last, fac); 
   237             if(start != last && wrap_isdigit(fac, *start)) {
   238                 start = str2int(start, last, fpar->fmtstate_.precision_, fac);
   239                 precision_set = true;
   240             }
   241             else
   242                 fpar->fmtstate_.precision_ =0;
   243         }
   244     
   245         // handle  formatting-type flags :
   246         while( start != last && ( *start== const_or_not(fac).widen( 'l') 
   247                                   || *start== const_or_not(fac).widen( 'L') 
   248                                   || *start== const_or_not(fac).widen( 'h')) )
   249             ++start;
   250         if( start>=last) {
   251             maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
   252             return true;
   253         }
   254 
   255         if( in_brackets && *start== const_or_not(fac).widen( '|') ) {
   256             ++start;
   257             return true;
   258         }
   259         switch ( wrap_narrow(fac, *start, 0) ) {
   260         case 'X':
   261             fpar->fmtstate_.flags_ |= std::ios_base::uppercase;
   262         case 'p': // pointer => set hex.
   263         case 'x':
   264             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
   265             fpar->fmtstate_.flags_ |= std::ios_base::hex;
   266             break;
   267 
   268         case 'o':
   269             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
   270             fpar->fmtstate_.flags_ |=  std::ios_base::oct;
   271             break;
   272 
   273         case 'E':
   274             fpar->fmtstate_.flags_ |=  std::ios_base::uppercase;
   275         case 'e':
   276             fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield;
   277             fpar->fmtstate_.flags_ |=  std::ios_base::scientific;
   278 
   279             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
   280             fpar->fmtstate_.flags_ |=  std::ios_base::dec;
   281             break;
   282       
   283         case 'f':
   284             fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield;
   285             fpar->fmtstate_.flags_ |=  std::ios_base::fixed;
   286         case 'u':
   287         case 'd':
   288         case 'i':
   289             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
   290             fpar->fmtstate_.flags_ |=  std::ios_base::dec;
   291             break;
   292 
   293         case 'T':
   294             ++start;
   295             if( start >= last)
   296                 maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
   297             else
   298                 fpar->fmtstate_.fill_ = *start;
   299             fpar->pad_scheme_ |= format_item_t::tabulation;
   300             fpar->argN_ = format_item_t::argN_tabulation; 
   301             break;
   302         case 't': 
   303             fpar->fmtstate_.fill_ = const_or_not(fac).widen( ' ');
   304             fpar->pad_scheme_ |= format_item_t::tabulation;
   305             fpar->argN_ = format_item_t::argN_tabulation; 
   306             break;
   307 
   308         case 'G':
   309             fpar->fmtstate_.flags_ |= std::ios_base::uppercase;
   310             break;
   311         case 'g': // 'g' conversion is default for floats.
   312             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
   313             fpar->fmtstate_.flags_ |=  std::ios_base::dec;
   314 
   315             // CLEAR all floatield flags, so stream will CHOOSE
   316             fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield; 
   317             break;
   318 
   319         case 'C':
   320         case 'c': 
   321             fpar->truncate_ = 1;
   322             break;
   323         case 'S':
   324         case 's': 
   325             if(precision_set) // handle truncation manually, with own parameter.
   326                 fpar->truncate_ = fpar->fmtstate_.precision_;
   327             fpar->fmtstate_.precision_ = 6; // default stream precision.
   328             break;
   329         case 'n' :  
   330             fpar->argN_ = format_item_t::argN_ignored;
   331             break;
   332         default: 
   333             maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
   334         }
   335         ++start;
   336 
   337         if( in_brackets ) {
   338             if( start != last && *start== const_or_not(fac).widen( '|') ) {
   339                 ++start;
   340                 return true;
   341             }
   342             else  maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
   343         }
   344         return true;
   345     }
   346     // -end parse_printf_directive()
   347 
   348     template<class String, class Facet>
   349     int upper_bound_from_fstring(const String& buf, 
   350                                  const typename String::value_type arg_mark,
   351                                  const Facet& fac, 
   352                                  unsigned char exceptions) 
   353     {
   354         // quick-parsing of the format-string to count arguments mark (arg_mark, '%')
   355         // returns : upper bound on the number of format items in the format strings
   356         using namespace boost::io;
   357         typename String::size_type i1=0;
   358         int num_items=0;
   359         while( (i1=buf.find(arg_mark,i1)) != String::npos ) {
   360             if( i1+1 >= buf.size() ) {
   361                 if(exceptions & bad_format_string_bit)
   362                     boost::throw_exception(bad_format_string(i1, buf.size() )); // must not end in ".. %"
   363                 else break; // stop there, ignore last '%'
   364             }
   365             if(buf[i1+1] == buf[i1] ) {// escaped "%%"
   366                 i1+=2; continue; 
   367             }
   368 
   369             ++i1;
   370             // in case of %N% directives, dont count it double (wastes allocations..) :
   371             i1 = detail::wrap_scan_notdigit(fac, buf.begin()+i1, buf.end()) - buf.begin();
   372             if( i1 < buf.size() && buf[i1] == arg_mark )
   373                 ++i1;
   374             ++num_items;
   375         }
   376         return num_items;
   377     }
   378     template<class String> inline
   379     void append_string(String& dst, const String& src, 
   380                        const typename String::size_type beg, 
   381                        const typename String::size_type end) {
   382 #if !defined(BOOST_NO_STRING_APPEND)
   383         dst.append(src.begin()+beg, src.begin()+end);
   384 #else
   385         dst += src.substr(beg, end-beg);
   386 #endif
   387     }
   388 
   389 } // detail namespace
   390 } // io namespace
   391 
   392 
   393 
   394 // -----------------------------------------------
   395 //  format :: parse(..)
   396 
   397     template<class Ch, class Tr, class Alloc>
   398     basic_format<Ch, Tr, Alloc>& basic_format<Ch, Tr, Alloc>:: 
   399     parse (const string_type& buf) {
   400         // parse the format-string 
   401         using namespace std;
   402 #if !defined(BOOST_NO_STD_LOCALE)
   403         const std::ctype<Ch> & fac = BOOST_USE_FACET( std::ctype<Ch>, getloc());
   404 #else
   405         io::basic_oaltstringstream<Ch, Tr, Alloc> fac; 
   406         //has widen and narrow even on compilers without locale
   407 #endif
   408 
   409         const Ch arg_mark = io::detail::const_or_not(fac).widen( '%');
   410         bool ordered_args=true; 
   411         int max_argN=-1;
   412 
   413         // A: find upper_bound on num_items and allocates arrays
   414         int num_items = io::detail::upper_bound_from_fstring(buf, arg_mark, fac, exceptions());
   415         make_or_reuse_data(num_items);
   416 
   417         // B: Now the real parsing of the format string :
   418         num_items=0;
   419         typename string_type::size_type i0=0, i1=0;
   420         typename string_type::const_iterator it;
   421         bool special_things=false;
   422         int cur_item=0;
   423         while( (i1=buf.find(arg_mark,i1)) != string_type::npos ) {
   424             string_type & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_;
   425             if( buf[i1+1] == buf[i1] ) { // escaped mark, '%%' 
   426                 io::detail::append_string(piece, buf, i0, i1+1);
   427                 i1+=2; i0=i1;
   428                 continue; 
   429             }
   430             BOOST_ASSERT(  static_cast<unsigned int>(cur_item) < items_.size() || cur_item==0);
   431 
   432             if(i1!=i0)
   433                 io::detail::append_string(piece, buf, i0, i1);
   434             ++i1;
   435             it = buf.begin()+i1;
   436             bool parse_ok = io::detail::parse_printf_directive(
   437                 it, buf.end(), &items_[cur_item], fac, i1, exceptions());
   438             i1 = it - buf.begin();
   439             if( ! parse_ok ) // the directive will be printed verbatim
   440                 continue; 
   441             i0=i1;
   442             items_[cur_item].compute_states(); // process complex options, like zeropad, into params
   443 
   444             int argN=items_[cur_item].argN_;
   445             if(argN == format_item_t::argN_ignored)
   446                 continue;
   447             if(argN ==format_item_t::argN_no_posit)
   448                 ordered_args=false;
   449             else if(argN == format_item_t::argN_tabulation) special_things=true;
   450             else if(argN > max_argN) max_argN = argN;
   451             ++num_items;
   452             ++cur_item;
   453         } // loop on %'s
   454         BOOST_ASSERT(cur_item == num_items);
   455     
   456         // store the final piece of string
   457         {
   458             string_type & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_;
   459             io::detail::append_string(piece, buf, i0, buf.size());
   460         }
   461     
   462         if( !ordered_args) {
   463             if(max_argN >= 0 ) {  // dont mix positional with non-positionnal directives
   464                 if(exceptions() & io::bad_format_string_bit)
   465                     boost::throw_exception(io::bad_format_string(max_argN, 0));
   466                 // else do nothing. => positionnal arguments are processed as non-positionnal
   467             }
   468             // set things like it would have been with positional directives :
   469             int non_ordered_items = 0;
   470             for(int i=0; i< num_items; ++i)
   471                 if(items_[i].argN_ == format_item_t::argN_no_posit) {
   472                     items_[i].argN_ = non_ordered_items;
   473                     ++non_ordered_items;
   474                 }
   475             max_argN = non_ordered_items-1;
   476         }
   477     
   478         // C: set some member data :
   479         items_.resize(num_items, format_item_t(io::detail::const_or_not(fac).widen( ' ')) );
   480 
   481         if(special_things) style_ |= special_needs;
   482         num_args_ = max_argN + 1;
   483         if(ordered_args) style_ |=  ordered;
   484         else style_ &= ~ordered;
   485         return *this;
   486     }
   487 
   488 } // namespace boost
   489 
   490 
   491 #endif //  BOOST_FORMAT_PARSING_HPP