// 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 Rüdiger Loos's format class
+// ideas taken from Rudiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_PARSING_HPP
#define BOOST_FORMAT_PARSING_HPP
-#ifdef BOOST_MSVC
-#ifdef isdigit
-#undef isdigit
-#endif
-#endif // MSVC workaround
-#include <locale>
+#include <boost/format/format_class.hpp>
+#include <boost/throw_exception.hpp>
+#include <boost/assert.hpp>
-
-#include "boost/format/format_class.hpp"
-#include "boost/throw_exception.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) )
+ 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 stream, so we can use its locale and call narrow.
+ // 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() && std::isdigit(s[start], os.rdbuf()->getloc() ) ) {
+ while(start<s.size() && wrap_isdigit(s[start], os) ) {
char cur_ch = os.narrow( s[start], 0);
- assert(cur_ch != 0 ); // since we called isdigit, this should not happen.
+ 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
+ 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)
+ 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 stream, so we can use its locale and call narrow.
+ // 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
{
- assert( pos_p != 0);
+ 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() && std::isdigit(buf[*pos_p],os.rdbuf()->getloc())) ++(*pos_p);
+ while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
if(buf[*pos_p]==os.widen('$')) ++(*pos_p);
}
}
// 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());
+ 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)
+ 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 stream merely to call 'widen' member function in the desired locale.
+ // 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 succeded (possibly ignoring errors if exceptions disabled)
+ // 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;
- assert( pos_p != 0);
- typename std::basic_string<Ch, Tr>::size_type &i1 = *pos_p,
- i0;
+ 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;
- }
+ 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'))
+ 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() && std::isdigit(buf[i1], os.rdbuf()->getloc()))
+ while (i1 < buf.size() && wrap_isdigit(buf[i1], os))
++i1;
- if (i1!=i0)
+ 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;
- }
+ 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:
+
+ 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;
+ {
+ // 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;
+ 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() && std::isdigit(buf[i1], os.rdbuf()->getloc()))
+ while (i1<buf.size() && wrap_isdigit(buf[i1], os))
i1++;
-
- if (i1!=i0)
+
+ if (i1!=i0)
{ fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }
parse_precision:
- if( i1>=buf.size()) {
+ if( i1>=buf.size()) {
maybe_throw_exception(exceptions);
return true;
}
// handle precision spec
- if (buf[i1]==os.widen('.'))
+ if (buf[i1]==os.widen('.'))
{
- ++i1;
- skip_asterisk(buf, &i1, os);
- i0 = i1; // save position before digits
- while (i1<buf.size() && std::isdigit(buf[i1], os.rdbuf()->getloc()))
- ++i1;
-
- if(i1==i0)
- fpar->ref_state_.precision_ = 0;
- else
- fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
+ ++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')) )
+ 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('|') )
+
+ if( in_brackets && buf[i1]==os.widen('|') )
{
- ++i1;
- return true;
+ ++i1;
+ return true;
}
- switch (os.narrow(buf[i1], 0) )
+ switch (os.narrow(buf[i1], 0) )
{
case 'X':
- fpar->ref_state_.flags_ |= std::ios_base::uppercase;
+ 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;
-
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ ++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;
+ 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;
+ 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;
+ // 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 '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);
+ 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);
+ if( i1<buf.size() && buf[i1]==os.widen('|') )
+ {
+ ++i1;
+ return true;
+ }
+ else maybe_throw_exception(exceptions);
}
return true;
}
// format :: parse(..)
template<class Ch, class Traits>
-void basic_format<Ch, Traits> ::parse(const string_t & buf)
+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;
+ 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 )
+ i1=0;
+ while( (i1=buf.find(arg_mark,i1)) != string_t::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(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() && std::isdigit(buf[i1],oss_.rdbuf()->getloc())) ++i1;
+ 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 )
+ while( (i1=buf.find(arg_mark,i1)) != string_t::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;
+ piece += buf.substr(i0, i1-i0) + buf[i1];
+ i1+=2; i0=i1;
+ continue;
}
- assert( static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);
+ 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();
+ 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;
+ continue;
if(argN ==format_item_t::argN_no_posit)
- ordered_args=false;
+ 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
- assert(cur_it == num_items);
-
+ 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( !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
- }
+ {
+ 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;
- }
+ 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);