diff options
Diffstat (limited to 'boost/format/parsing.hpp')
-rw-r--r-- | boost/format/parsing.hpp | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/boost/format/parsing.hpp b/boost/format/parsing.hpp new file mode 100644 index 000000000000..a461314f9828 --- /dev/null +++ b/boost/format/parsing.hpp @@ -0,0 +1,457 @@ +// -*- C++ -*- +// Boost general library 'format' --------------------------- +// See http://www.boost.org for updates, documentation, and revision history. + +// (C) Samuel Krempp 2001 +// krempp@crans.ens-cachan.fr +// Permission to copy, use, modify, sell and +// distribute this software is granted provided this copyright notice appears +// in all copies. This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. + +// ideas taken from Rudiger Loos's format class +// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing) + +// ------------------------------------------------------------------------------ +// parsing.hpp : implementation of the parsing member functions +// ( parse, parse_printf_directive) +// ------------------------------------------------------------------------------ + + +#ifndef BOOST_FORMAT_PARSING_HPP +#define BOOST_FORMAT_PARSING_HPP + + +#include <boost/format/format_class.hpp> +//#include <boost/throw_exception.hpp> +//#include <boost/assert.hpp> + + +namespace boost { +namespace io { +namespace detail { + + template<class Ch, class Stream> inline + bool wrap_isdigit(Ch c, Stream &os) + { +#ifndef BOOST_NO_LOCALE_ISIDIGIT + return std::isdigit(c, os.rdbuf()->getloc() ); +# else + using namespace std; + return isdigit(c); +#endif + } //end- wrap_isdigit(..) + + template<class Res, class Ch, class Tr> inline + Res str2int(const std::basic_string<Ch, Tr>& s, + typename std::basic_string<Ch, Tr>::size_type start, + BOOST_IO_STD basic_ios<Ch,Tr> &os, + const Res = Res(0) ) + // Input : char string, with starting index + // a basic_ios& merely to call its widen/narrow member function in the desired locale. + // Effects : reads s[start:] and converts digits into an integral n, of type Res + // Returns : n + { + Res n = 0; + while(start<s.size() && wrap_isdigit(s[start], os) ) { + char cur_ch = os.narrow( s[start], 0); + BOOST_ASSERT(cur_ch != 0 ); // since we called isdigit, this should not happen. + n *= 10; + n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard + ++start; + } + return n; + } + + template<class Ch, class Tr> + void skip_asterisk(const std::basic_string<Ch,Tr> & buf, + typename std::basic_string<Ch,Tr>::size_type * pos_p, + BOOST_IO_STD basic_ios<Ch, Tr> &os) + // skip printf's "asterisk-fields" directives in the format-string buf + // Input : char string, with starting index *pos_p + // a basic_ios& merely to call its widen/narrow member function in the desired locale. + // Effects : advance *pos_p by skipping printf's asterisk fields. + // Returns : nothing + { + using namespace std; + BOOST_ASSERT( pos_p != 0); + if(*pos_p >= buf.size() ) return; + if(buf[ *pos_p]==os.widen('*')) { + ++ (*pos_p); + while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p); + if(buf[*pos_p]==os.widen('$')) ++(*pos_p); + } + } + + + inline void maybe_throw_exception( unsigned char exceptions) + // auxiliary func called by parse_printf_directive + // for centralising error handling + // it either throws if user sets the corresponding flag, or does nothing. + { + if(exceptions & io::bad_format_string_bit) + boost::throw_exception(io::bad_format_string()); + } + + + + template<class Ch, class Tr> + bool parse_printf_directive(const std::basic_string<Ch, Tr> & buf, + typename std::basic_string<Ch, Tr>::size_type * pos_p, + detail::format_item<Ch, Tr> * fpar, + BOOST_IO_STD basic_ios<Ch,Tr> &os, + unsigned char exceptions) + // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ] + // a basic_ios& merely to call its widen/narrow member function in the desired locale. + // a bitset'excpetions' telling whether to throw exceptions on errors. + // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled) + // false if it failed so bad that the directive should be printed verbatim + // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive + // - *fpar is set with the parameters read in the directive + { + typedef format_item<Ch, Tr> format_item_t; + BOOST_ASSERT( pos_p != 0); + typename std::basic_string<Ch, Tr>::size_type &i1 = *pos_p, + i0; + fpar->argN_ = format_item_t::argN_no_posit; // if no positional-directive + + bool in_brackets=false; + if(buf[i1]==os.widen('|')) + { + in_brackets=true; + if( ++i1 >= buf.size() ) { + maybe_throw_exception(exceptions); + return false; + } + } + + // the flag '0' would be picked as a digit for argument order, but here it's a flag : + if(buf[i1]==os.widen('0')) + goto parse_flags; + + // handle argument order (%2$d) or possibly width specification: %2d + i0 = i1; // save position before digits + while (i1 < buf.size() && wrap_isdigit(buf[i1], os)) + ++i1; + if (i1!=i0) + { + if( i1 >= buf.size() ) { + maybe_throw_exception(exceptions); + return false; + } + int n=str2int(buf,i0, os, int(0) ); + + // %N% case : this is already the end of the directive + if( buf[i1] == os.widen('%') ) + { + fpar->argN_ = n-1; + ++i1; + if( in_brackets) + maybe_throw_exception(exceptions); + // but don't return. maybe "%" was used in lieu of '$', so we go on. + else return true; + } + + if ( buf[i1]==os.widen('$') ) + { + fpar->argN_ = n-1; + ++i1; + } + else + { + // non-positionnal directive + fpar->ref_state_.width_ = n; + fpar->argN_ = format_item_t::argN_no_posit; + goto parse_precision; + } + } + + parse_flags: + // handle flags + while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h or ' ' + { + // misc switches + switch (os.narrow(buf[i1], 0)) + { + case '\'' : break; // no effect yet. (painful to implement) + case 'l': + case 'h': // short/long modifier : for printf-comaptibility (no action needed) + break; + case '-': + fpar->ref_state_.flags_ |= std::ios_base::left; + break; + case '=': + fpar->pad_scheme_ |= format_item_t::centered; + break; + case ' ': + fpar->pad_scheme_ |= format_item_t::spacepad; + break; + case '+': + fpar->ref_state_.flags_ |= std::ios_base::showpos; + break; + case '0': + fpar->pad_scheme_ |= format_item_t::zeropad; + // need to know alignment before really setting flags, + // so just add 'zeropad' flag for now, it will be processed later. + break; + case '#': + fpar->ref_state_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase; + break; + default: + goto parse_width; + } + ++i1; + } // loop on flag. + if( i1>=buf.size()) { + maybe_throw_exception(exceptions); + return true; + } + + parse_width: + // handle width spec + skip_asterisk(buf, &i1, os); // skips 'asterisk fields' : *, or *N$ + i0 = i1; // save position before digits + while (i1<buf.size() && wrap_isdigit(buf[i1], os)) + i1++; + + if (i1!=i0) + { fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); } + + parse_precision: + if( i1>=buf.size()) { + maybe_throw_exception(exceptions); + return true; + } + // handle precision spec + if (buf[i1]==os.widen('.')) + { + ++i1; + skip_asterisk(buf, &i1, os); + i0 = i1; // save position before digits + while (i1<buf.size() && wrap_isdigit(buf[i1], os)) + ++i1; + + if(i1==i0) + fpar->ref_state_.precision_ = 0; + else + fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) ); + } + + // handle formatting-type flags : + while( i1<buf.size() && + ( buf[i1]==os.widen('l') || buf[i1]==os.widen('L') || buf[i1]==os.widen('h')) ) + ++i1; + if( i1>=buf.size()) { + maybe_throw_exception(exceptions); + return true; + } + + if( in_brackets && buf[i1]==os.widen('|') ) + { + ++i1; + return true; + } + switch (os.narrow(buf[i1], 0) ) + { + case 'X': + fpar->ref_state_.flags_ |= std::ios_base::uppercase; + case 'p': // pointer => set hex. + case 'x': + fpar->ref_state_.flags_ &= ~std::ios_base::basefield; + fpar->ref_state_.flags_ |= std::ios_base::hex; + break; + + case 'o': + fpar->ref_state_.flags_ &= ~std::ios_base::basefield; + fpar->ref_state_.flags_ |= std::ios_base::oct; + break; + + case 'E': + fpar->ref_state_.flags_ |= std::ios_base::uppercase; + case 'e': + fpar->ref_state_.flags_ &= ~std::ios_base::floatfield; + fpar->ref_state_.flags_ |= std::ios_base::scientific; + + fpar->ref_state_.flags_ &= ~std::ios_base::basefield; + fpar->ref_state_.flags_ |= std::ios_base::dec; + break; + + case 'f': + fpar->ref_state_.flags_ &= ~std::ios_base::floatfield; + fpar->ref_state_.flags_ |= std::ios_base::fixed; + case 'u': + case 'd': + case 'i': + fpar->ref_state_.flags_ &= ~std::ios_base::basefield; + fpar->ref_state_.flags_ |= std::ios_base::dec; + break; + + case 'T': + ++i1; + if( i1 >= buf.size()) + maybe_throw_exception(exceptions); + else + fpar->ref_state_.fill_ = buf[i1]; + fpar->pad_scheme_ |= format_item_t::tabulation; + fpar->argN_ = format_item_t::argN_tabulation; + break; + case 't': + fpar->ref_state_.fill_ = os.widen(' '); + fpar->pad_scheme_ |= format_item_t::tabulation; + fpar->argN_ = format_item_t::argN_tabulation; + break; + + case 'G': + fpar->ref_state_.flags_ |= std::ios_base::uppercase; + break; + case 'g': // 'g' conversion is default for floats. + fpar->ref_state_.flags_ &= ~std::ios_base::basefield; + fpar->ref_state_.flags_ |= std::ios_base::dec; + + // CLEAR all floatield flags, so stream will CHOOSE + fpar->ref_state_.flags_ &= ~std::ios_base::floatfield; + break; + + case 'C': + case 'c': + fpar->truncate_ = 1; + break; + case 'S': + case 's': + fpar->truncate_ = fpar->ref_state_.precision_; + fpar->ref_state_.precision_ = -1; + break; + case 'n' : + fpar->argN_ = format_item_t::argN_ignored; + break; + default: + maybe_throw_exception(exceptions); + } + ++i1; + + if( in_brackets ) + { + if( i1<buf.size() && buf[i1]==os.widen('|') ) + { + ++i1; + return true; + } + else maybe_throw_exception(exceptions); + } + return true; + } + +} // detail namespace +} // io namespace + + +// ----------------------------------------------- +// format :: parse(..) + +template<class Ch, class Traits> +void basic_format<Ch, Traits> ::parse(const string_t & buf) + // parse the format-string +{ + using namespace std; + const Ch arg_mark = oss_.widen('%'); + bool ordered_args=true; + int max_argN=-1; + typename string_t::size_type i1=0; + int num_items=0; + + // A: find upper_bound on num_items and allocates arrays + i1=0; + while( (i1=buf.find(arg_mark,i1)) != string::npos ) + { + if( i1+1 >= buf.size() ) { + if(exceptions() & io::bad_format_string_bit) + boost::throw_exception(io::bad_format_string()); // must not end in "bla bla %" + else break; // stop there, ignore last '%' + } + if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##" + ++i1; + + // in case of %N% directives, dont count it double (wastes allocations..) : + while(i1 < buf.size() && io::detail::wrap_isdigit(buf[i1],oss_)) ++i1; + if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1; + + ++num_items; + } + items_.assign( num_items, format_item_t() ); + + // B: Now the real parsing of the format string : + num_items=0; + i1 = 0; + typename string_t::size_type i0 = i1; + bool special_things=false; + int cur_it=0; + while( (i1=buf.find(arg_mark,i1)) != string::npos ) + { + string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_; + + if( buf[i1+1] == buf[i1] ) // escaped mark, '%%' + { + piece += buf.substr(i0, i1-i0) + buf[i1]; + i1+=2; i0=i1; + continue; + } + BOOST_ASSERT( static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0); + + if(i1!=i0) piece += buf.substr(i0, i1-i0); + ++i1; + + bool parse_ok; + parse_ok = io::detail::parse_printf_directive(buf, &i1, &items_[cur_it], oss_, exceptions()); + if( ! parse_ok ) continue; // the directive will be printed verbatim + + i0=i1; + items_[cur_it].compute_states(); // process complex options, like zeropad, into stream params. + + int argN=items_[cur_it].argN_; + if(argN == format_item_t::argN_ignored) + continue; + if(argN ==format_item_t::argN_no_posit) + ordered_args=false; + else if(argN == format_item_t::argN_tabulation) special_things=true; + else if(argN > max_argN) max_argN = argN; + ++num_items; + ++cur_it; + } // loop on %'s + BOOST_ASSERT(cur_it == num_items); + + // store the final piece of string + string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_; + piece += buf.substr(i0); + + if( !ordered_args) + { + if(max_argN >= 0 ) // dont mix positional with non-positionnal directives + { + if(exceptions() & io::bad_format_string_bit) + boost::throw_exception(io::bad_format_string()); + // else do nothing. => positionnal arguments are processed as non-positionnal + } + // set things like it would have been with positional directives : + int non_ordered_items = 0; + for(int i=0; i< num_items; ++i) + if(items_[i].argN_ == format_item_t::argN_no_posit) + { + items_[i].argN_ = non_ordered_items; + ++non_ordered_items; + } + max_argN = non_ordered_items-1; + } + + // C: set some member data : + items_.resize(num_items); + + if(special_things) style_ |= special_needs; + num_args_ = max_argN + 1; + if(ordered_args) style_ |= ordered; + else style_ &= ~ordered; +} + +} // namespace boost + + +#endif // BOOST_FORMAT_PARSING_HPP |