]> git.lyx.org Git - lyx.git/blob - boost/boost/format/parsing.hpp
use boost::format
[lyx.git] / boost / boost / format / parsing.hpp
1 // -*- C++ -*-
2 //  Boost general library 'format'   ---------------------------
3 //  See http://www.boost.org for updates, documentation, and revision history.
4
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.
11
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)
14
15 // ------------------------------------------------------------------------------
16 // parsing.hpp :  implementation of the parsing member functions
17 //                      ( parse, parse_printf_directive)
18 // ------------------------------------------------------------------------------
19
20
21 #ifndef BOOST_FORMAT_PARSING_HPP
22 #define BOOST_FORMAT_PARSING_HPP
23
24 #ifdef BOOST_MSVC
25 #ifdef isdigit
26 #undef isdigit
27 #endif
28 #endif // MSVC workaround
29
30 #include <locale>
31
32
33
34 #include "boost/format/format_class.hpp"
35 #include "boost/throw_exception.hpp"
36
37 namespace boost {
38 namespace io {
39 namespace detail {
40
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,
45               const Res = Res(0)  )
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
49     // Returns : n
50   {
51     Res n = 0;
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.
55       n *= 10;
56       n += cur_ch - '0'; // §22.2.1.1.2 of the C++ standard
57       ++start;
58     }
59     return n;
60   }
61
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.
70     // Returns : nothing
71   {
72     assert( pos_p);
73     if(*pos_p >= buf.size() ) return;
74     if(buf[ *pos_p]==os.widen('*')) {
75       ++ (*pos_p);
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);
78     }
79   }
80
81
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.
86   {
87     if(exceptions & io::bad_format_string_bit)
88           boost::throw_exception(io::bad_format_string());
89   }
90
91
92
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
106   {
107     typedef format_item<Ch, Tr>  format_item_t;
108     assert( pos_p);
109     typename std::basic_string<Ch, Tr>::size_type       &i1 = *pos_p,
110                                                         i0;
111     fpar->argN_ = format_item_t::argN_no_posit;  // if no positional-directive
112
113     bool in_brackets=false;
114     if(buf[i1]==os.widen('|'))
115       {
116         in_brackets=true;
117         if( ++i1 >= buf.size() ) {
118           maybe_throw_exception(exceptions);
119           return false;
120         }
121       }
122
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'))
125       goto parse_flags;
126
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()))
130       ++i1;
131     if (i1!=i0)
132       {
133         if( i1 >= buf.size() ) {
134           maybe_throw_exception(exceptions);
135           return false;
136         }
137         int n=str2int(buf,i0, os, int(0) );
138
139         // %N% case : this is already the end of the directive
140         if( buf[i1] == os.widen('%') )
141           {
142             fpar->argN_ = n-1;
143             ++i1;
144             if( in_brackets)
145               maybe_throw_exception(exceptions);
146               // but don't return.  maybe "%" was used in lieu of '$', so we go on.
147             else return true;
148           }
149
150         if ( buf[i1]==os.widen('$') )
151           {
152             fpar->argN_ = n-1;
153             ++i1;
154           }
155         else
156           {
157             // non-positionnal directive
158             fpar->ref_state_.width_ = n;
159             fpar->argN_  = format_item_t::argN_no_posit;
160             goto parse_precision;
161           }
162       }
163
164   parse_flags:
165     // handle flags
166     while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h   or ' '
167       {
168         // misc switches
169         switch (os.narrow(buf[i1], 0))
170           {
171           case '\'' : break; // no effect yet. (painful to implement)
172           case 'l':
173           case 'h':  // short/long modifier : for printf-comaptibility (no action needed)
174              break;
175           case '-':
176             fpar->ref_state_.flags_ |= std::ios_base::left;
177             break;
178           case '=':
179             fpar->pad_scheme_ |= format_item_t::centered;
180             break;
181           case ' ':
182             fpar->pad_scheme_ |= format_item_t::spacepad;
183             break;
184           case '+':
185             fpar->ref_state_.flags_ |= std::ios_base::showpos;
186             break;
187           case '0':
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.
191             break;
192           case '#':
193             fpar->ref_state_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
194             break;
195           default:
196             goto parse_width;
197           }
198         ++i1;
199       } // loop on flag.
200     if( i1>=buf.size()) {
201       maybe_throw_exception(exceptions);
202       return true;
203     }
204
205   parse_width:
206     // handle width spec
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()))
210       i1++;
211
212     if (i1!=i0)
213       { fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }
214
215   parse_precision:
216     if( i1>=buf.size()) {
217       maybe_throw_exception(exceptions);
218       return true;
219     }
220     // handle precision spec
221     if (buf[i1]==os.widen('.'))
222       {
223         ++i1;
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()))
227           ++i1;
228
229         if(i1==i0)
230           fpar->ref_state_.precision_ = 0;
231         else
232           fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
233       }
234
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')) )
238       ++i1;
239     if( i1>=buf.size()) {
240       maybe_throw_exception(exceptions);
241       return true;
242     }
243
244     if( in_brackets && buf[i1]==os.widen('|') )
245       {
246         ++i1;
247         return true;
248       }
249     switch (os.narrow(buf[i1], 0) )
250       {
251       case 'X':
252         fpar->ref_state_.flags_ |= std::ios_base::uppercase;
253       case 'p': // pointer => set hex.
254       case 'x':
255         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
256         fpar->ref_state_.flags_ |= std::ios_base::hex;
257         break;
258
259       case 'o':
260         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
261         fpar->ref_state_.flags_ |=  std::ios_base::oct;
262         break;
263
264       case 'E':
265         fpar->ref_state_.flags_ |=  std::ios_base::uppercase;
266       case 'e':
267         fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
268         fpar->ref_state_.flags_ |=  std::ios_base::scientific;
269
270         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
271         fpar->ref_state_.flags_ |=  std::ios_base::dec;
272         break;
273
274       case 'f':
275         fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
276         fpar->ref_state_.flags_ |=  std::ios_base::fixed;
277       case 'u':
278       case 'd':
279       case 'i':
280         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
281         fpar->ref_state_.flags_ |=  std::ios_base::dec;
282         break;
283
284       case 'T':
285         ++i1;
286         if( i1 >= buf.size())
287           maybe_throw_exception(exceptions);
288         else
289           fpar->ref_state_.fill_ = buf[i1];
290         fpar->pad_scheme_ |= format_item_t::tabulation;
291         fpar->argN_ = format_item_t::argN_tabulation;
292         break;
293       case 't':
294         fpar->ref_state_.fill_ = os.widen(' ');
295         fpar->pad_scheme_ |= format_item_t::tabulation;
296         fpar->argN_ = format_item_t::argN_tabulation;
297         break;
298
299       case 'G':
300         fpar->ref_state_.flags_ |= std::ios_base::uppercase;
301         break;
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;
305
306         // CLEAR all floatield flags, so stream will CHOOSE
307         fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
308         break;
309
310       case 'C':
311       case 'c':
312         fpar->truncate_ = 1;
313         break;
314       case 'S':
315       case 's':
316         fpar->truncate_ = fpar->ref_state_.precision_;
317         fpar->ref_state_.precision_ = -1;
318         break;
319       case 'n' :
320         fpar->argN_ = format_item_t::argN_ignored;
321         break;
322       default:
323         maybe_throw_exception(exceptions);
324       }
325     ++i1;
326
327     if( in_brackets )
328       {
329         if( i1<buf.size() && buf[i1]==os.widen('|') )
330           {
331             ++i1;
332             return true;
333           }
334         else  maybe_throw_exception(exceptions);
335       }
336     return true;
337   }
338
339 } // detail namespace
340 } // io namespace
341
342
343 // -----------------------------------------------
344 //  format :: parse(..)
345
346 template<class Ch, class Traits>
347 void basic_format<Ch, Traits> ::parse(const string_t & buf)
348   // parse the format-string
349 {
350     using namespace std;
351     const Ch arg_mark = oss_.widen('%');
352     bool ordered_args=true;
353     int max_argN=-1;
354     typename string_t::size_type i1=0;
355     int num_items=0;
356
357     // A: find upper_bound on num_items and allocates arrays
358     i1=0;
359     while( (i1=buf.find(arg_mark,i1)) != string::npos )
360     {
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 '%'
365       }
366       if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
367       ++i1;
368
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;
372
373       ++num_items;
374     }
375     items_.assign( num_items, format_item_t() );
376
377     // B: Now the real parsing of the format string :
378     num_items=0;
379     i1 = 0;
380     typename string_t::size_type i0 = i1;
381     bool special_things=false;
382     int cur_it=0;
383     while( (i1=buf.find(arg_mark,i1)) != string::npos )
384     {
385       string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
386
387       if( buf[i1+1] == buf[i1] ) // escaped mark, '%%'
388       {
389         piece += buf.substr(i0, i1-i0) + buf[i1];
390         i1+=2; i0=i1;
391         continue;
392       }
393       assert(  static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);
394
395       if(i1!=i0) piece += buf.substr(i0, i1-i0);
396       ++i1;
397
398       bool parse_ok;
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
401
402       i0=i1;
403       items_[cur_it].compute_states();
404       int argN=items_[cur_it].argN_;
405       if(argN == format_item_t::argN_ignored)
406         continue;
407       if(argN ==format_item_t::argN_no_posit)
408         ordered_args=false;
409       else if(argN == format_item_t::argN_tabulation) special_things=true;
410       else if(argN > max_argN) max_argN = argN;
411       ++num_items;
412       ++cur_it;
413     } // loop on %'s
414     assert(cur_it == num_items);
415
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);
419
420     if( !ordered_args)
421     {
422       if(max_argN >= 0 )  // dont mix positional with non-positionnal directives
423         {
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
427         }
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)
432           {
433             items_[i].argN_ = non_ordered_items;
434             ++non_ordered_items;
435           }
436       max_argN = non_ordered_items-1;
437     }
438
439     // C: set some member data :
440     items_.resize(num_items);
441
442     if(special_things) style_ |= special_needs;
443     num_args_ = max_argN + 1;
444     if(ordered_args) style_ |=  ordered;
445     else style_ &= ~ordered;
446 }
447
448 } // namespace boost
449
450
451 #endif //  BOOST_FORMAT_PARSING_HPP