sl@0: // ---------------------------------------------------------------------------- sl@0: // parsing.hpp : implementation of the parsing member functions sl@0: // ( parse, parse_printf_directive) sl@0: // ---------------------------------------------------------------------------- sl@0: sl@0: // Copyright Samuel Krempp 2003. Use, modification, and distribution are sl@0: // subject to the Boost Software License, Version 1.0. (See accompanying sl@0: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) sl@0: sl@0: // see http://www.boost.org/libs/format for library home page sl@0: sl@0: // ---------------------------------------------------------------------------- sl@0: sl@0: #ifndef BOOST_FORMAT_PARSING_HPP sl@0: #define BOOST_FORMAT_PARSING_HPP sl@0: sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: sl@0: namespace boost { sl@0: namespace io { sl@0: namespace detail { sl@0: sl@0: #if defined(BOOST_NO_STD_LOCALE) sl@0: // streams will be used for narrow / widen. but these methods are not const sl@0: template sl@0: T& const_or_not(const T& x) { sl@0: return const_cast (x); sl@0: } sl@0: #else sl@0: template sl@0: const T& const_or_not(const T& x) { sl@0: return x; sl@0: } sl@0: #endif sl@0: sl@0: template inline sl@0: char wrap_narrow(const Facet& fac, Ch c, char deflt) { sl@0: return const_or_not(fac).narrow(c, deflt); sl@0: } sl@0: sl@0: template inline sl@0: bool wrap_isdigit(const Facet& fac, Ch c) { sl@0: #if ! defined( BOOST_NO_LOCALE_ISDIGIT ) sl@0: return fac.is(std::ctype::digit, c); sl@0: # else sl@0: using namespace std; sl@0: return isdigit(c); sl@0: #endif sl@0: } sl@0: sl@0: template sl@0: Iter wrap_scan_notdigit(const Facet & fac, Iter beg, Iter end) { sl@0: using namespace std; sl@0: for( ; beg!=end && wrap_isdigit(fac, *beg); ++beg) ; sl@0: return beg; sl@0: } sl@0: sl@0: sl@0: // Input : [start, last) iterators range and a sl@0: // a Facet to use its widen/narrow member function sl@0: // Effects : read sequence and convert digits into integral n, of type Res sl@0: // Returns : n sl@0: template sl@0: Iter str2int (const Iter & start, const Iter & last, Res & res, sl@0: const Facet& fac) sl@0: { sl@0: using namespace std; sl@0: Iter it; sl@0: res=0; sl@0: for(it=start; it != last && wrap_isdigit(fac, *it); ++it ) { sl@0: char cur_ch = wrap_narrow(fac, *it, 0); // cant fail. sl@0: res *= 10; sl@0: res += cur_ch - '0'; // 22.2.1.1.2.13 of the C++ standard sl@0: } sl@0: return it; sl@0: } sl@0: sl@0: // skip printf's "asterisk-fields" directives in the format-string buf sl@0: // Input : char string, with starting index *pos_p sl@0: // a Facet merely to use its widen/narrow member function sl@0: // Effects : advance *pos_p by skipping printf's asterisk fields. sl@0: // Returns : nothing sl@0: template sl@0: Iter skip_asterisk(Iter start, Iter last, const Facet& fac) sl@0: { sl@0: using namespace std; sl@0: ++ start; sl@0: start = wrap_scan_notdigit(fac, start, last); sl@0: if(start!=last && *start== const_or_not(fac).widen( '$') ) sl@0: ++start; sl@0: return start; sl@0: } sl@0: sl@0: sl@0: // auxiliary func called by parse_printf_directive sl@0: // for centralising error handling sl@0: // it either throws if user sets the corresponding flag, or does nothing. sl@0: inline void maybe_throw_exception(unsigned char exceptions, sl@0: std::size_t pos, std::size_t size) sl@0: { sl@0: if(exceptions & io::bad_format_string_bit) sl@0: boost::throw_exception(io::bad_format_string(pos, size) ); sl@0: } sl@0: sl@0: sl@0: // Input: the position of a printf-directive in the format-string sl@0: // a basic_ios& merely to use its widen/narrow member function sl@0: // a bitset'exceptions' telling whether to throw exceptions on errors. sl@0: // Returns: sl@0: // true if parse succeeded (ignore some errors if exceptions disabled) sl@0: // false if it failed so bad that the directive should be printed verbatim sl@0: // Effects: sl@0: // start is incremented so that *start is the first char after sl@0: // this directive sl@0: // *fpar is set with the parameters read in the directive sl@0: template sl@0: bool parse_printf_directive(Iter & start, const Iter& last, sl@0: detail::format_item * fpar, sl@0: const Facet& fac, sl@0: std::size_t offset, unsigned char exceptions) sl@0: { sl@0: typedef typename basic_format::format_item_t format_item_t; sl@0: sl@0: fpar->argN_ = format_item_t::argN_no_posit; // if no positional-directive sl@0: bool precision_set = false; sl@0: bool in_brackets=false; sl@0: Iter start0 = start; sl@0: std::size_t fstring_size = last-start0+offset; sl@0: if(*start== const_or_not(fac).widen( '|')) { sl@0: in_brackets=true; sl@0: if( ++start >= last ) { sl@0: maybe_throw_exception(exceptions, start-start0 + offset, fstring_size); sl@0: return false; sl@0: } sl@0: } sl@0: sl@0: // the flag '0' would be picked as a digit for argument order, but here it's a flag : sl@0: if(*start== const_or_not(fac).widen( '0')) sl@0: goto parse_flags; sl@0: sl@0: // handle argument order (%2$d) or possibly width specification: %2d sl@0: if(wrap_isdigit(fac, *start)) { sl@0: int n; sl@0: start = str2int(start, last, n, fac); sl@0: if( start >= last ) { sl@0: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: return false; sl@0: } sl@0: sl@0: // %N% case : this is already the end of the directive sl@0: if( *start == const_or_not(fac).widen( '%') ) { sl@0: fpar->argN_ = n-1; sl@0: ++start; sl@0: if( in_brackets) sl@0: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: // but don't return. maybe "%" was used in lieu of '$', so we go on. sl@0: else sl@0: return true; sl@0: } sl@0: sl@0: if ( *start== const_or_not(fac).widen( '$') ) { sl@0: fpar->argN_ = n-1; sl@0: ++start; sl@0: } sl@0: else { sl@0: // non-positionnal directive sl@0: fpar->fmtstate_.width_ = n; sl@0: fpar->argN_ = format_item_t::argN_no_posit; sl@0: goto parse_precision; sl@0: } sl@0: } sl@0: sl@0: parse_flags: sl@0: // handle flags sl@0: while ( start != last) { // as long as char is one of + - = _ # 0 l h or ' ' sl@0: // misc switches sl@0: switch ( wrap_narrow(fac, *start, 0)) { sl@0: case '\'' : break; // no effect yet. (painful to implement) sl@0: case 'l': sl@0: case 'h': // short/long modifier : for printf-comaptibility (no action needed) sl@0: break; sl@0: case '-': sl@0: fpar->fmtstate_.flags_ |= std::ios_base::left; sl@0: break; sl@0: case '=': sl@0: fpar->pad_scheme_ |= format_item_t::centered; sl@0: break; sl@0: case '_': sl@0: fpar->fmtstate_.flags_ |= std::ios_base::internal; sl@0: break; sl@0: case ' ': sl@0: fpar->pad_scheme_ |= format_item_t::spacepad; sl@0: break; sl@0: case '+': sl@0: fpar->fmtstate_.flags_ |= std::ios_base::showpos; sl@0: break; sl@0: case '0': sl@0: fpar->pad_scheme_ |= format_item_t::zeropad; sl@0: // need to know alignment before really setting flags, sl@0: // so just add 'zeropad' flag for now, it will be processed later. sl@0: break; sl@0: case '#': sl@0: fpar->fmtstate_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase; sl@0: break; sl@0: default: sl@0: goto parse_width; sl@0: } sl@0: ++start; sl@0: } // loop on flag. sl@0: sl@0: if( start>=last) { sl@0: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: return true; sl@0: } sl@0: parse_width: sl@0: // handle width spec sl@0: // first skip 'asterisk fields' : *, or *N$ sl@0: if(*start == const_or_not(fac).widen( '*') ) sl@0: start = skip_asterisk(start, last, fac); sl@0: if(start!=last && wrap_isdigit(fac, *start)) sl@0: start = str2int(start, last, fpar->fmtstate_.width_, fac); sl@0: sl@0: parse_precision: sl@0: if( start>= last) { sl@0: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: return true; sl@0: } sl@0: // handle precision spec sl@0: if (*start== const_or_not(fac).widen( '.')) { sl@0: ++start; sl@0: if(start != last && *start == const_or_not(fac).widen( '*') ) sl@0: start = skip_asterisk(start, last, fac); sl@0: if(start != last && wrap_isdigit(fac, *start)) { sl@0: start = str2int(start, last, fpar->fmtstate_.precision_, fac); sl@0: precision_set = true; sl@0: } sl@0: else sl@0: fpar->fmtstate_.precision_ =0; sl@0: } sl@0: sl@0: // handle formatting-type flags : sl@0: while( start != last && ( *start== const_or_not(fac).widen( 'l') sl@0: || *start== const_or_not(fac).widen( 'L') sl@0: || *start== const_or_not(fac).widen( 'h')) ) sl@0: ++start; sl@0: if( start>=last) { sl@0: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: return true; sl@0: } sl@0: sl@0: if( in_brackets && *start== const_or_not(fac).widen( '|') ) { sl@0: ++start; sl@0: return true; sl@0: } sl@0: switch ( wrap_narrow(fac, *start, 0) ) { sl@0: case 'X': sl@0: fpar->fmtstate_.flags_ |= std::ios_base::uppercase; sl@0: case 'p': // pointer => set hex. sl@0: case 'x': sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::basefield; sl@0: fpar->fmtstate_.flags_ |= std::ios_base::hex; sl@0: break; sl@0: sl@0: case 'o': sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::basefield; sl@0: fpar->fmtstate_.flags_ |= std::ios_base::oct; sl@0: break; sl@0: sl@0: case 'E': sl@0: fpar->fmtstate_.flags_ |= std::ios_base::uppercase; sl@0: case 'e': sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield; sl@0: fpar->fmtstate_.flags_ |= std::ios_base::scientific; sl@0: sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::basefield; sl@0: fpar->fmtstate_.flags_ |= std::ios_base::dec; sl@0: break; sl@0: sl@0: case 'f': sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield; sl@0: fpar->fmtstate_.flags_ |= std::ios_base::fixed; sl@0: case 'u': sl@0: case 'd': sl@0: case 'i': sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::basefield; sl@0: fpar->fmtstate_.flags_ |= std::ios_base::dec; sl@0: break; sl@0: sl@0: case 'T': sl@0: ++start; sl@0: if( start >= last) sl@0: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: else sl@0: fpar->fmtstate_.fill_ = *start; sl@0: fpar->pad_scheme_ |= format_item_t::tabulation; sl@0: fpar->argN_ = format_item_t::argN_tabulation; sl@0: break; sl@0: case 't': sl@0: fpar->fmtstate_.fill_ = const_or_not(fac).widen( ' '); sl@0: fpar->pad_scheme_ |= format_item_t::tabulation; sl@0: fpar->argN_ = format_item_t::argN_tabulation; sl@0: break; sl@0: sl@0: case 'G': sl@0: fpar->fmtstate_.flags_ |= std::ios_base::uppercase; sl@0: break; sl@0: case 'g': // 'g' conversion is default for floats. sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::basefield; sl@0: fpar->fmtstate_.flags_ |= std::ios_base::dec; sl@0: sl@0: // CLEAR all floatield flags, so stream will CHOOSE sl@0: fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield; sl@0: break; sl@0: sl@0: case 'C': sl@0: case 'c': sl@0: fpar->truncate_ = 1; sl@0: break; sl@0: case 'S': sl@0: case 's': sl@0: if(precision_set) // handle truncation manually, with own parameter. sl@0: fpar->truncate_ = fpar->fmtstate_.precision_; sl@0: fpar->fmtstate_.precision_ = 6; // default stream precision. sl@0: break; sl@0: case 'n' : sl@0: fpar->argN_ = format_item_t::argN_ignored; sl@0: break; sl@0: default: sl@0: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: } sl@0: ++start; sl@0: sl@0: if( in_brackets ) { sl@0: if( start != last && *start== const_or_not(fac).widen( '|') ) { sl@0: ++start; sl@0: return true; sl@0: } sl@0: else maybe_throw_exception(exceptions, start-start0+offset, fstring_size); sl@0: } sl@0: return true; sl@0: } sl@0: // -end parse_printf_directive() sl@0: sl@0: template sl@0: int upper_bound_from_fstring(const String& buf, sl@0: const typename String::value_type arg_mark, sl@0: const Facet& fac, sl@0: unsigned char exceptions) sl@0: { sl@0: // quick-parsing of the format-string to count arguments mark (arg_mark, '%') sl@0: // returns : upper bound on the number of format items in the format strings sl@0: using namespace boost::io; sl@0: typename String::size_type i1=0; sl@0: int num_items=0; sl@0: while( (i1=buf.find(arg_mark,i1)) != String::npos ) { sl@0: if( i1+1 >= buf.size() ) { sl@0: if(exceptions & bad_format_string_bit) sl@0: boost::throw_exception(bad_format_string(i1, buf.size() )); // must not end in ".. %" sl@0: else break; // stop there, ignore last '%' sl@0: } sl@0: if(buf[i1+1] == buf[i1] ) {// escaped "%%" sl@0: i1+=2; continue; sl@0: } sl@0: sl@0: ++i1; sl@0: // in case of %N% directives, dont count it double (wastes allocations..) : sl@0: i1 = detail::wrap_scan_notdigit(fac, buf.begin()+i1, buf.end()) - buf.begin(); sl@0: if( i1 < buf.size() && buf[i1] == arg_mark ) sl@0: ++i1; sl@0: ++num_items; sl@0: } sl@0: return num_items; sl@0: } sl@0: template inline sl@0: void append_string(String& dst, const String& src, sl@0: const typename String::size_type beg, sl@0: const typename String::size_type end) { sl@0: #if !defined(BOOST_NO_STRING_APPEND) sl@0: dst.append(src.begin()+beg, src.begin()+end); sl@0: #else sl@0: dst += src.substr(beg, end-beg); sl@0: #endif sl@0: } sl@0: sl@0: } // detail namespace sl@0: } // io namespace sl@0: sl@0: sl@0: sl@0: // ----------------------------------------------- sl@0: // format :: parse(..) sl@0: sl@0: template sl@0: basic_format& basic_format:: sl@0: parse (const string_type& buf) { sl@0: // parse the format-string sl@0: using namespace std; sl@0: #if !defined(BOOST_NO_STD_LOCALE) sl@0: const std::ctype & fac = BOOST_USE_FACET( std::ctype, getloc()); sl@0: #else sl@0: io::basic_oaltstringstream fac; sl@0: //has widen and narrow even on compilers without locale sl@0: #endif sl@0: sl@0: const Ch arg_mark = io::detail::const_or_not(fac).widen( '%'); sl@0: bool ordered_args=true; sl@0: int max_argN=-1; sl@0: sl@0: // A: find upper_bound on num_items and allocates arrays sl@0: int num_items = io::detail::upper_bound_from_fstring(buf, arg_mark, fac, exceptions()); sl@0: make_or_reuse_data(num_items); sl@0: sl@0: // B: Now the real parsing of the format string : sl@0: num_items=0; sl@0: typename string_type::size_type i0=0, i1=0; sl@0: typename string_type::const_iterator it; sl@0: bool special_things=false; sl@0: int cur_item=0; sl@0: while( (i1=buf.find(arg_mark,i1)) != string_type::npos ) { sl@0: string_type & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_; sl@0: if( buf[i1+1] == buf[i1] ) { // escaped mark, '%%' sl@0: io::detail::append_string(piece, buf, i0, i1+1); sl@0: i1+=2; i0=i1; sl@0: continue; sl@0: } sl@0: BOOST_ASSERT( static_cast(cur_item) < items_.size() || cur_item==0); sl@0: sl@0: if(i1!=i0) sl@0: io::detail::append_string(piece, buf, i0, i1); sl@0: ++i1; sl@0: it = buf.begin()+i1; sl@0: bool parse_ok = io::detail::parse_printf_directive( sl@0: it, buf.end(), &items_[cur_item], fac, i1, exceptions()); sl@0: i1 = it - buf.begin(); sl@0: if( ! parse_ok ) // the directive will be printed verbatim sl@0: continue; sl@0: i0=i1; sl@0: items_[cur_item].compute_states(); // process complex options, like zeropad, into params sl@0: sl@0: int argN=items_[cur_item].argN_; sl@0: if(argN == format_item_t::argN_ignored) sl@0: continue; sl@0: if(argN ==format_item_t::argN_no_posit) sl@0: ordered_args=false; sl@0: else if(argN == format_item_t::argN_tabulation) special_things=true; sl@0: else if(argN > max_argN) max_argN = argN; sl@0: ++num_items; sl@0: ++cur_item; sl@0: } // loop on %'s sl@0: BOOST_ASSERT(cur_item == num_items); sl@0: sl@0: // store the final piece of string sl@0: { sl@0: string_type & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_; sl@0: io::detail::append_string(piece, buf, i0, buf.size()); sl@0: } sl@0: sl@0: if( !ordered_args) { sl@0: if(max_argN >= 0 ) { // dont mix positional with non-positionnal directives sl@0: if(exceptions() & io::bad_format_string_bit) sl@0: boost::throw_exception(io::bad_format_string(max_argN, 0)); sl@0: // else do nothing. => positionnal arguments are processed as non-positionnal sl@0: } sl@0: // set things like it would have been with positional directives : sl@0: int non_ordered_items = 0; sl@0: for(int i=0; i< num_items; ++i) sl@0: if(items_[i].argN_ == format_item_t::argN_no_posit) { sl@0: items_[i].argN_ = non_ordered_items; sl@0: ++non_ordered_items; sl@0: } sl@0: max_argN = non_ordered_items-1; sl@0: } sl@0: sl@0: // C: set some member data : sl@0: items_.resize(num_items, format_item_t(io::detail::const_or_not(fac).widen( ' ')) ); sl@0: sl@0: if(special_things) style_ |= special_needs; sl@0: num_args_ = max_argN + 1; sl@0: if(ordered_args) style_ |= ordered; sl@0: else style_ &= ~ordered; sl@0: return *this; sl@0: } sl@0: sl@0: } // namespace boost sl@0: sl@0: sl@0: #endif // BOOST_FORMAT_PARSING_HPP