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 Rudiger 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
25 #include <boost/format/format_class.hpp>
26 #include <boost/throw_exception.hpp>
27 #include <boost/assert.hpp>
34 template<class Ch, class Stream> inline
35 bool wrap_isdigit(Ch c, Stream &os)
37 #ifndef BOOST_NO_LOCALE_ISIDIGIT
38 return std::isdigit(c, os.rdbuf()->getloc() );
43 } //end- wrap_isdigit(..)
45 template<class Res, class Ch, class Tr> inline
46 Res str2int(const std::basic_string<Ch, Tr>& s,
47 typename std::basic_string<Ch, Tr>::size_type start,
48 BOOST_IO_STD basic_ios<Ch,Tr> &os,
50 // Input : char string, with starting index
51 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
52 // Effects : reads s[start:] and converts digits into an integral n, of type Res
56 while(start<s.size() && wrap_isdigit(s[start], os) ) {
57 char cur_ch = os.narrow( s[start], 0);
58 BOOST_ASSERT(cur_ch != 0 ); // since we called isdigit, this should not happen.
60 n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard
66 template<class Ch, class Tr>
67 void skip_asterisk(const std::basic_string<Ch,Tr> & buf,
68 typename std::basic_string<Ch,Tr>::size_type * pos_p,
69 BOOST_IO_STD basic_ios<Ch, Tr> &os)
70 // skip printf's "asterisk-fields" directives in the format-string buf
71 // Input : char string, with starting index *pos_p
72 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
73 // Effects : advance *pos_p by skipping printf's asterisk fields.
77 BOOST_ASSERT( pos_p != 0);
78 if(*pos_p >= buf.size() ) return;
79 if(buf[ *pos_p]==os.widen('*')) {
81 while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
82 if(buf[*pos_p]==os.widen('$')) ++(*pos_p);
87 inline void maybe_throw_exception( unsigned char exceptions)
88 // auxiliary func called by parse_printf_directive
89 // for centralising error handling
90 // it either throws if user sets the corresponding flag, or does nothing.
92 if(exceptions & io::bad_format_string_bit)
93 boost::throw_exception(io::bad_format_string());
98 template<class Ch, class Tr>
99 bool parse_printf_directive(const std::basic_string<Ch, Tr> & buf,
100 typename std::basic_string<Ch, Tr>::size_type * pos_p,
101 detail::format_item<Ch, Tr> * fpar,
102 BOOST_IO_STD basic_ios<Ch,Tr> &os,
103 unsigned char exceptions)
104 // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
105 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
106 // a bitset'excpetions' telling whether to throw exceptions on errors.
107 // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled)
108 // false if it failed so bad that the directive should be printed verbatim
109 // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
110 // - *fpar is set with the parameters read in the directive
112 typedef format_item<Ch, Tr> format_item_t;
113 BOOST_ASSERT( pos_p != 0);
114 typename std::basic_string<Ch, Tr>::size_type &i1 = *pos_p,
116 fpar->argN_ = format_item_t::argN_no_posit; // if no positional-directive
118 bool in_brackets=false;
119 if(buf[i1]==os.widen('|'))
122 if( ++i1 >= buf.size() ) {
123 maybe_throw_exception(exceptions);
128 // the flag '0' would be picked as a digit for argument order, but here it's a flag :
129 if(buf[i1]==os.widen('0'))
132 // handle argument order (%2$d) or possibly width specification: %2d
133 i0 = i1; // save position before digits
134 while (i1 < buf.size() && wrap_isdigit(buf[i1], os))
138 if( i1 >= buf.size() ) {
139 maybe_throw_exception(exceptions);
142 int n=str2int(buf,i0, os, int(0) );
144 // %N% case : this is already the end of the directive
145 if( buf[i1] == os.widen('%') )
150 maybe_throw_exception(exceptions);
151 // but don't return. maybe "%" was used in lieu of '$', so we go on.
155 if ( buf[i1]==os.widen('$') )
162 // non-positionnal directive
163 fpar->ref_state_.width_ = n;
164 fpar->argN_ = format_item_t::argN_no_posit;
165 goto parse_precision;
171 while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h or ' '
174 switch (os.narrow(buf[i1], 0))
176 case '\'' : break; // no effect yet. (painful to implement)
178 case 'h': // short/long modifier : for printf-comaptibility (no action needed)
181 fpar->ref_state_.flags_ |= std::ios_base::left;
184 fpar->pad_scheme_ |= format_item_t::centered;
187 fpar->pad_scheme_ |= format_item_t::spacepad;
190 fpar->ref_state_.flags_ |= std::ios_base::showpos;
193 fpar->pad_scheme_ |= format_item_t::zeropad;
194 // need to know alignment before really setting flags,
195 // so just add 'zeropad' flag for now, it will be processed later.
198 fpar->ref_state_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
205 if( i1>=buf.size()) {
206 maybe_throw_exception(exceptions);
212 skip_asterisk(buf, &i1, os); // skips 'asterisk fields' : *, or *N$
213 i0 = i1; // save position before digits
214 while (i1<buf.size() && wrap_isdigit(buf[i1], os))
218 { fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }
221 if( i1>=buf.size()) {
222 maybe_throw_exception(exceptions);
225 // handle precision spec
226 if (buf[i1]==os.widen('.'))
229 skip_asterisk(buf, &i1, os);
230 i0 = i1; // save position before digits
231 while (i1<buf.size() && wrap_isdigit(buf[i1], os))
235 fpar->ref_state_.precision_ = 0;
237 fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
240 // handle formatting-type flags :
241 while( i1<buf.size() &&
242 ( buf[i1]==os.widen('l') || buf[i1]==os.widen('L') || buf[i1]==os.widen('h')) )
244 if( i1>=buf.size()) {
245 maybe_throw_exception(exceptions);
249 if( in_brackets && buf[i1]==os.widen('|') )
254 switch (os.narrow(buf[i1], 0) )
257 fpar->ref_state_.flags_ |= std::ios_base::uppercase;
258 case 'p': // pointer => set hex.
260 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
261 fpar->ref_state_.flags_ |= std::ios_base::hex;
265 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
266 fpar->ref_state_.flags_ |= std::ios_base::oct;
270 fpar->ref_state_.flags_ |= std::ios_base::uppercase;
272 fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
273 fpar->ref_state_.flags_ |= std::ios_base::scientific;
275 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
276 fpar->ref_state_.flags_ |= std::ios_base::dec;
280 fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
281 fpar->ref_state_.flags_ |= std::ios_base::fixed;
285 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
286 fpar->ref_state_.flags_ |= std::ios_base::dec;
291 if( i1 >= buf.size())
292 maybe_throw_exception(exceptions);
294 fpar->ref_state_.fill_ = buf[i1];
295 fpar->pad_scheme_ |= format_item_t::tabulation;
296 fpar->argN_ = format_item_t::argN_tabulation;
299 fpar->ref_state_.fill_ = os.widen(' ');
300 fpar->pad_scheme_ |= format_item_t::tabulation;
301 fpar->argN_ = format_item_t::argN_tabulation;
305 fpar->ref_state_.flags_ |= std::ios_base::uppercase;
307 case 'g': // 'g' conversion is default for floats.
308 fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
309 fpar->ref_state_.flags_ |= std::ios_base::dec;
311 // CLEAR all floatield flags, so stream will CHOOSE
312 fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
321 fpar->truncate_ = fpar->ref_state_.precision_;
322 fpar->ref_state_.precision_ = -1;
325 fpar->argN_ = format_item_t::argN_ignored;
328 maybe_throw_exception(exceptions);
334 if( i1<buf.size() && buf[i1]==os.widen('|') )
339 else maybe_throw_exception(exceptions);
344 } // detail namespace
348 // -----------------------------------------------
349 // format :: parse(..)
351 template<class Ch, class Traits>
352 void basic_format<Ch, Traits> ::parse(const string_t & buf)
353 // parse the format-string
356 const Ch arg_mark = oss_.widen('%');
357 bool ordered_args=true;
359 typename string_t::size_type i1=0;
362 // A: find upper_bound on num_items and allocates arrays
364 while( (i1=buf.find(arg_mark,i1)) != string_t::npos )
366 if( i1+1 >= buf.size() ) {
367 if(exceptions() & io::bad_format_string_bit)
368 boost::throw_exception(io::bad_format_string()); // must not end in "bla bla %"
369 else break; // stop there, ignore last '%'
371 if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
374 // in case of %N% directives, dont count it double (wastes allocations..) :
375 while(i1 < buf.size() && io::detail::wrap_isdigit(buf[i1],oss_)) ++i1;
376 if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1;
380 items_.assign( num_items, format_item_t() );
382 // B: Now the real parsing of the format string :
385 typename string_t::size_type i0 = i1;
386 bool special_things=false;
388 while( (i1=buf.find(arg_mark,i1)) != string_t::npos )
390 string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
392 if( buf[i1+1] == buf[i1] ) // escaped mark, '%%'
394 piece += buf.substr(i0, i1-i0) + buf[i1];
398 BOOST_ASSERT( static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);
400 if(i1!=i0) piece += buf.substr(i0, i1-i0);
404 parse_ok = io::detail::parse_printf_directive(buf, &i1, &items_[cur_it], oss_, exceptions());
405 if( ! parse_ok ) continue; // the directive will be printed verbatim
408 items_[cur_it].compute_states(); // process complex options, like zeropad, into stream params.
410 int argN=items_[cur_it].argN_;
411 if(argN == format_item_t::argN_ignored)
413 if(argN ==format_item_t::argN_no_posit)
415 else if(argN == format_item_t::argN_tabulation) special_things=true;
416 else if(argN > max_argN) max_argN = argN;
420 BOOST_ASSERT(cur_it == num_items);
422 // store the final piece of string
423 string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
424 piece += buf.substr(i0);
428 if(max_argN >= 0 ) // dont mix positional with non-positionnal directives
430 if(exceptions() & io::bad_format_string_bit)
431 boost::throw_exception(io::bad_format_string());
432 // else do nothing. => positionnal arguments are processed as non-positionnal
434 // set things like it would have been with positional directives :
435 int non_ordered_items = 0;
436 for(int i=0; i< num_items; ++i)
437 if(items_[i].argN_ == format_item_t::argN_no_posit)
439 items_[i].argN_ = non_ordered_items;
442 max_argN = non_ordered_items-1;
445 // C: set some member data :
446 items_.resize(num_items);
448 if(special_things) style_ |= special_needs;
449 num_args_ = max_argN + 1;
450 if(ordered_args) style_ |= ordered;
451 else style_ &= ~ordered;
457 #endif // BOOST_FORMAT_PARSING_HPP