123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- // -*- 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.hpp>
- #include <boost/throw_exception.hpp>
- #include <boost/assert.hpp>
- namespace boost {
- namespace io {
- namespace detail {
- template<class Stream> inline
- bool wrap_isdigit(char 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> inline
- Res str2int(const std::string& s,
- std::string::size_type start,
- BOOST_IO_STD ios &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 = s[start];
- 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;
- }
- void skip_asterisk(const std::string & buf,
- std::string::size_type * pos_p,
- BOOST_IO_STD ios &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]=='*') {
- ++ (*pos_p);
- while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
- if(buf[*pos_p]=='$') ++(*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());
- }
-
- bool parse_printf_directive(const std::string & buf,
- std::string::size_type * pos_p,
- detail::format_item * fpar,
- BOOST_IO_STD ios &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 format_item_t;
- BOOST_ASSERT( pos_p != 0);
- std::string::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]=='|')
- {
- 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]=='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] == '%' )
- {
- 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]=='$' )
- {
- 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 (buf[i1])
- {
- 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::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::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::showpoint | std::ios::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]=='.')
- {
- ++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]=='l' || buf[i1]=='L' || buf[i1]=='h') )
- ++i1;
- if( i1>=buf.size()) {
- maybe_throw_exception(exceptions);
- return true;
- }
-
- if( in_brackets && buf[i1]=='|' )
- {
- ++i1;
- return true;
- }
- switch (buf[i1])
- {
- case 'X':
- fpar->ref_state_.flags_ |= std::ios::uppercase;
- case 'p': // pointer => set hex.
- case 'x':
- fpar->ref_state_.flags_ &= ~std::ios::basefield;
- fpar->ref_state_.flags_ |= std::ios::hex;
- break;
-
- case 'o':
- fpar->ref_state_.flags_ &= ~std::ios::basefield;
- fpar->ref_state_.flags_ |= std::ios::oct;
- break;
- case 'E':
- fpar->ref_state_.flags_ |= std::ios::uppercase;
- case 'e':
- fpar->ref_state_.flags_ &= ~std::ios::floatfield;
- fpar->ref_state_.flags_ |= std::ios::scientific;
- fpar->ref_state_.flags_ &= ~std::ios::basefield;
- fpar->ref_state_.flags_ |= std::ios::dec;
- break;
-
- case 'f':
- fpar->ref_state_.flags_ &= ~std::ios::floatfield;
- fpar->ref_state_.flags_ |= std::ios::fixed;
- case 'u':
- case 'd':
- case 'i':
- fpar->ref_state_.flags_ &= ~std::ios::basefield;
- fpar->ref_state_.flags_ |= std::ios::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_ = ' ';
- fpar->pad_scheme_ |= format_item_t::tabulation;
- fpar->argN_ = format_item_t::argN_tabulation;
- break;
- case 'G':
- fpar->ref_state_.flags_ |= std::ios::uppercase;
- break;
- case 'g': // 'g' conversion is default for floats.
- fpar->ref_state_.flags_ &= ~std::ios::basefield;
- fpar->ref_state_.flags_ |= std::ios::dec;
- // CLEAR all floatield flags, so stream will CHOOSE
- fpar->ref_state_.flags_ &= ~std::ios::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]=='|' )
- {
- ++i1;
- return true;
- }
- else maybe_throw_exception(exceptions);
- }
- return true;
- }
- } // detail namespace
- } // io namespace
- // -----------------------------------------------
- // format :: parse(..)
- void basic_format::parse(const string_t & buf)
- // parse the format-string
- {
- using namespace std;
- const char arg_mark = '%';
- bool ordered_args=true;
- int max_argN=-1;
- 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;
- 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
|