2 // Boost general library 'format' ---------------------------
3 // See http://www.boost.org for updates, documentation, and revision history.
5 // (C) Samuel Krempp 2001
6 // krempp@crans.ens-cachan.fr
7 // Permission to copy, use, modify, sell and
8 // distribute this software is granted provided this copyright notice appears
9 // in all copies. This software is provided "as is" without express or implied
10 // warranty, and with no claim as to its suitability for any purpose.
12 // ideas taken from Rüdiger Loos's format class
13 // and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
15 // ------------------------------------------------------------------------------
16 // parsing.hpp : implementation of the parsing member functions
17 // ( parse, parse_printf_directive)
18 // ------------------------------------------------------------------------------
21 #ifndef BOOST_FORMAT_PARSING_HPP
22 #define BOOST_FORMAT_PARSING_HPP
28 #endif // MSVC workaround
34 #include "boost/format/format_class.hpp"
35 #include "boost/throw_exception.hpp"
41 template<class Res, class Ch, class Tr> inline
42 Res str2int(const std::basic_string<Ch, Tr>& s,
43 typename std::basic_string<Ch, Tr>::size_type start,
44 BOOST_IO_STD basic_ios<Ch,Tr> &os,
46 // Input : char string, with starting index
47 // a stream, so we can use its locale and call narrow.
48 // Effects : reads s[start:] and converts digits into an integral n, of type Res
52 while(start<s.size() && std::isdigit(s[start], os.rdbuf()->getloc() ) ) {
53 char cur_ch = os.narrow( s[start], 0);
54 assert(cur_ch != 0 ); // since we called isdigit, this should not happen.
56 n += cur_ch - '0'; // §22.2.1.1.2 of the C++ standard
62 template<class Ch, class Tr>
63 void skip_asterisk(const std::basic_string<Ch,Tr> & buf,
64 typename std::basic_string<Ch,Tr>::size_type * pos_p,
65 BOOST_IO_STD basic_ios<Ch, Tr> &os)
66 // skip printf's "asterisk-fields" directives in the format-string buf
67 // Input : char string, with starting index *pos_p
68 // a stream, so we can use its locale and call narrow.
69 // Effects : advance *pos_p by skipping printf's asterisk fields.
73 if(*pos_p >= buf.size() ) return;
74 if(buf[ *pos_p]==os.widen('*')) {
76 while (*pos_p < buf.size() && std::isdigit(buf[*pos_p],os.rdbuf()->getloc())) ++(*pos_p);
77 if(buf[*pos_p]==os.widen('$')) ++(*pos_p);
82 inline void maybe_throw_exception( unsigned char exceptions)
83 // auxiliary func called by parse_printf_directive
84 // for centralising error handling
85 // it either throws if user sets the corresponding flag, or does nothing.
87 if(exceptions & io::bad_format_string_bit)
88 boost::throw_exception(io::bad_format_string());
93 template<class Ch, class Tr>
94 bool parse_printf_directive(const std::basic_string<Ch, Tr> & buf,
95 typename std::basic_string<Ch, Tr>::size_type * pos_p,
96 detail::format_item<Ch, Tr> * fpar,
97 BOOST_IO_STD basic_ios<Ch,Tr> &os,
98 unsigned char exceptions)
99 // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
100 // a stream merely to call 'widen' member function in the desired locale.
101 // a bitset'excpetions' telling whether to throw exceptions on errors.
102 // Returns : true if parse somehow succeded (possibly ignoring errors if exceptions disabled)
103 // false if it failed so bad that the directive should be printed verbatim
104 // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
105 // - *fpar is set with the parameters read in the directive
107 typedef format_item<Ch, Tr> format_item_t;
109 typename std::basic_string<Ch, Tr>::size_type &i1 = *pos_p,
111 fpar->argN_ = format_item_t::argN_no_posit; // if no positional-directive
113 bool in_brackets=false;
114 if(buf[i1]==os.widen('|'))
117 if( ++i1 >= buf.size() ) {
118 maybe_throw_exception(exceptions);
123 // the flag '0' would be picked as a digit for argument order, but here it's a flag :
124 if(buf[i1]==os.widen('0'))
127 // handle argument order (%2$d) or possibly width specification: %2d
128 i0 = i1; // save position before digits
129 while (i1 < buf.size() && std::isdigit(buf[i1], os.rdbuf()->getloc()))
133 if( i1 >= buf.size() ) {
134 maybe_throw_exception(exceptions);
137 int n=str2int(buf,i0, os, int(0) );
139 // %N% case : this is already the end of the directive
140 if( buf[i1] == os.widen('%') )
145 maybe_throw_exception(exceptions);
146 // but don't return. maybe "%" was used in lieu of '$', so we go on.
150 if ( buf[i1]==os.widen('$') )
157 // non-positionnal directive
158 fpar->ref_state_.width_ = n;
159 fpar->argN_ = format_item_t::argN_no_posit;
160 goto parse_precision;
166 while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h or ' '
169 switch (os.narrow(buf[i1], 0))
171 case '\'' : break; // no effect yet. (painful to implement)
173 case 'h': // short/long modifier : for printf-comaptibility (no action needed)
176 fpar->ref_state_.flags_ |= std::ios_base::left;
179 fpar->pad_scheme_ |= format_item_t::centered;
182 fpar->pad_scheme_ |= format_item_t::spacepad;
185 fpar->ref_state_.flags_ |= std::ios_base::showpos;
188 fpar->pad_scheme_ |= format_item_t::zeropad;
189 // need to know alignment before really setting flags,
190 // so just add 'zeropad' flag for now, it will be processed later.
193 fpar->ref_state_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
200 if( i1>=buf.size()) {
201 maybe_throw_exception(exceptions);
207 skip_asterisk(buf, &i1, os); // skips 'asterisk fields' : *, or *N$
208 i0 = i1; // save position before digits
209 while (i1<buf.size() && std::isdigit(buf[i1], os.rdbuf()->getloc()))
213 { fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }
216 if( i1>=buf.size()) {
217 maybe_throw_exception(exceptions);
220 // handle precision spec
221 if (buf[i1]==os.widen('.'))
224 skip_asterisk(buf, &i1, os);
225 i0 = i1; // save position before digits
226 while (i1<buf.size() && std::isdigit(buf[i1], os.rdbuf()->getloc()))
230 fpar->ref_state_.precision_ = 0;
232 fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
235 // handle formatting-type flags :
236 while( i1<buf.size() &&
237 ( buf[i1]==os.widen('l') || buf[i1]==os.widen('L') || buf[i1]==os.widen('h')) )
239 if( i1>=buf.size()) {
240 maybe_throw_exception(exceptions);
244 if( in_brackets && buf[i1]==os.widen('|') )
249 switch (os.narrow(buf[i1], 0) )
252 fpar->ref_state_.flags_ |= std::ios_base::uppercase;
253 case 'p': // pointer => set hex.
255 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
256 fpar->ref_state_.flags_ |= std::ios_base::hex;
260 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
261 fpar->ref_state_.flags_ |= std::ios_base::oct;
265 fpar->ref_state_.flags_ |= std::ios_base::uppercase;
267 fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
268 fpar->ref_state_.flags_ |= std::ios_base::scientific;
270 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
271 fpar->ref_state_.flags_ |= std::ios_base::dec;
275 fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
276 fpar->ref_state_.flags_ |= std::ios_base::fixed;
280 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
281 fpar->ref_state_.flags_ |= std::ios_base::dec;
286 if( i1 >= buf.size())
287 maybe_throw_exception(exceptions);
289 fpar->ref_state_.fill_ = buf[i1];
290 fpar->pad_scheme_ |= format_item_t::tabulation;
291 fpar->argN_ = format_item_t::argN_tabulation;
294 fpar->ref_state_.fill_ = os.widen(' ');
295 fpar->pad_scheme_ |= format_item_t::tabulation;
296 fpar->argN_ = format_item_t::argN_tabulation;
300 fpar->ref_state_.flags_ |= std::ios_base::uppercase;
302 case 'g': // 'g' conversion is default for floats.
303 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
304 fpar->ref_state_.flags_ |= std::ios_base::dec;
306 // CLEAR all floatield flags, so stream will CHOOSE
307 fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
316 fpar->truncate_ = fpar->ref_state_.precision_;
317 fpar->ref_state_.precision_ = -1;
320 fpar->argN_ = format_item_t::argN_ignored;
323 maybe_throw_exception(exceptions);
329 if( i1<buf.size() && buf[i1]==os.widen('|') )
334 else maybe_throw_exception(exceptions);
339 } // detail namespace
343 // -----------------------------------------------
344 // format :: parse(..)
346 template<class Ch, class Traits>
347 void basic_format<Ch, Traits> ::parse(const string_t & buf)
348 // parse the format-string
351 const Ch arg_mark = oss_.widen('%');
352 bool ordered_args=true;
354 typename string_t::size_type i1=0;
357 // A: find upper_bound on num_items and allocates arrays
359 while( (i1=buf.find(arg_mark,i1)) != string::npos )
361 if( i1+1 >= buf.size() ) {
362 if(exceptions() & io::bad_format_string_bit)
363 boost::throw_exception(io::bad_format_string()); // must not end in "bla bla %"
364 else break; // stop there, ignore last '%'
366 if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
369 // in case of %N% directives, dont count it double (wastes allocations..) :
370 while(i1 < buf.size() && std::isdigit(buf[i1],oss_.rdbuf()->getloc())) ++i1;
371 if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1;
375 items_.assign( num_items, format_item_t() );
377 // B: Now the real parsing of the format string :
380 typename string_t::size_type i0 = i1;
381 bool special_things=false;
383 while( (i1=buf.find(arg_mark,i1)) != string::npos )
385 string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
387 if( buf[i1+1] == buf[i1] ) // escaped mark, '%%'
389 piece += buf.substr(i0, i1-i0) + buf[i1];
393 assert( static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);
395 if(i1!=i0) piece += buf.substr(i0, i1-i0);
399 parse_ok = io::detail::parse_printf_directive(buf, &i1, &items_[cur_it], oss_, exceptions());
400 if( ! parse_ok ) continue; // the directive will be printed verbatim
403 items_[cur_it].compute_states();
404 int argN=items_[cur_it].argN_;
405 if(argN == format_item_t::argN_ignored)
407 if(argN ==format_item_t::argN_no_posit)
409 else if(argN == format_item_t::argN_tabulation) special_things=true;
410 else if(argN > max_argN) max_argN = argN;
414 assert(cur_it == num_items);
416 // store the final piece of string
417 string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
418 piece += buf.substr(i0);
422 if(max_argN >= 0 ) // dont mix positional with non-positionnal directives
424 if(exceptions() & io::bad_format_string_bit)
425 boost::throw_exception(io::bad_format_string());
426 // else do nothing. => positionnal arguments are processed as non-positionnal
428 // set things like it would have been with positional directives :
429 int non_ordered_items = 0;
430 for(int i=0; i< num_items; ++i)
431 if(items_[i].argN_ == format_item_t::argN_no_posit)
433 items_[i].argN_ = non_ordered_items;
436 max_argN = non_ordered_items-1;
439 // C: set some member data :
440 items_.resize(num_items);
442 if(special_things) style_ |= special_needs;
443 num_args_ = max_argN + 1;
444 if(ordered_args) style_ |= ordered;
445 else style_ &= ~ordered;
451 #endif // BOOST_FORMAT_PARSING_HPP