]> git.lyx.org Git - lyx.git/blob - boost/boost/format/parsing.hpp
boost 1.30.2
[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 Rudiger 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
25 #include <boost/format/format_class.hpp>
26 #include <boost/throw_exception.hpp>
27 #include <boost/assert.hpp>
28
29
30 namespace boost {
31 namespace io {
32 namespace detail {
33
34   template<class Ch, class Stream> inline
35   bool wrap_isdigit(Ch c, Stream &os) 
36   {
37 #ifndef BOOST_NO_LOCALE_ISIDIGIT
38     return std::isdigit(c, os.rdbuf()->getloc() );
39 # else
40     using namespace std;
41     return isdigit(c); 
42 #endif 
43   } //end- wrap_isdigit(..)
44
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,
49               const Res = Res(0)  ) 
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
53     // Returns : n
54   {
55     Res n = 0;
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.
59       n *= 10;
60       n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard
61       ++start;
62     }
63     return n;
64   }
65
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.
74     // Returns : nothing
75   {
76     using namespace std;
77     BOOST_ASSERT( pos_p != 0);
78     if(*pos_p >= buf.size() ) return;
79     if(buf[ *pos_p]==os.widen('*')) {
80       ++ (*pos_p);
81       while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
82       if(buf[*pos_p]==os.widen('$')) ++(*pos_p);
83     }
84   }
85
86
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.
91   {
92     if(exceptions & io::bad_format_string_bit)
93           boost::throw_exception(io::bad_format_string());
94   }
95     
96
97
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
111   {
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,      
115                                                         i0; 
116     fpar->argN_ = format_item_t::argN_no_posit;  // if no positional-directive
117
118     bool in_brackets=false;
119     if(buf[i1]==os.widen('|'))
120       {
121         in_brackets=true;
122         if( ++i1 >= buf.size() ) {
123           maybe_throw_exception(exceptions);
124           return false;
125         }
126       }
127
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')) 
130       goto parse_flags;
131
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))
135       ++i1;
136     if (i1!=i0) 
137       {
138         if( i1 >= buf.size() ) {
139           maybe_throw_exception(exceptions);
140           return false;
141         }
142         int n=str2int(buf,i0, os, int(0) );
143         
144         // %N% case : this is already the end of the directive
145         if( buf[i1] == os.widen('%') ) 
146           {
147             fpar->argN_ = n-1;
148             ++i1;
149             if( in_brackets) 
150               maybe_throw_exception(exceptions); 
151               // but don't return.  maybe "%" was used in lieu of '$', so we go on.
152             else return true;
153           }
154
155         if ( buf[i1]==os.widen('$') ) 
156           {
157             fpar->argN_ = n-1;
158             ++i1;
159           } 
160         else  
161           {
162             // non-positionnal directive
163             fpar->ref_state_.width_ = n;
164             fpar->argN_  = format_item_t::argN_no_posit;
165             goto parse_precision;
166           }
167       }
168     
169   parse_flags: 
170     // handle flags
171     while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h   or ' '
172       {  
173         // misc switches
174         switch (os.narrow(buf[i1], 0)) 
175           {
176           case '\'' : break; // no effect yet. (painful to implement)
177           case 'l':
178           case 'h':  // short/long modifier : for printf-comaptibility (no action needed)
179              break;
180           case '-':
181             fpar->ref_state_.flags_ |= std::ios_base::left;
182             break;
183           case '=':
184             fpar->pad_scheme_ |= format_item_t::centered;
185             break;
186           case ' ':
187             fpar->pad_scheme_ |= format_item_t::spacepad;
188             break;
189           case '+':
190             fpar->ref_state_.flags_ |= std::ios_base::showpos;
191             break;
192           case '0':
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.
196             break;
197           case '#':
198             fpar->ref_state_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
199             break;
200           default:
201             goto parse_width;
202           }
203         ++i1;
204       } // loop on flag.
205     if( i1>=buf.size()) {
206       maybe_throw_exception(exceptions);
207       return true; 
208     }
209
210   parse_width:
211     // handle width spec
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))
215       i1++;
216     
217     if (i1!=i0) 
218       { fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }
219
220   parse_precision:
221     if( i1>=buf.size()) { 
222       maybe_throw_exception(exceptions);
223       return true;
224     }
225     // handle precision spec
226     if (buf[i1]==os.widen('.'))  
227       {
228         ++i1;
229         skip_asterisk(buf, &i1, os);
230         i0 = i1;  // save position before digits
231         while (i1<buf.size() && wrap_isdigit(buf[i1], os))
232           ++i1;
233
234         if(i1==i0)
235           fpar->ref_state_.precision_ = 0;
236         else 
237           fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
238       }
239     
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')) )
243       ++i1;
244     if( i1>=buf.size()) {
245       maybe_throw_exception(exceptions);
246       return true;
247     }
248     
249     if( in_brackets && buf[i1]==os.widen('|') ) 
250       {
251         ++i1;
252         return true;
253       }
254     switch (os.narrow(buf[i1], 0) )  
255       {
256       case 'X':
257         fpar->ref_state_.flags_ |= std::ios_base::uppercase;
258       case 'p': // pointer => set hex.
259       case 'x':
260         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
261         fpar->ref_state_.flags_ |= std::ios_base::hex;
262         break;
263       
264       case 'o':
265         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
266         fpar->ref_state_.flags_ |=  std::ios_base::oct;
267         break;
268
269       case 'E':
270         fpar->ref_state_.flags_ |=  std::ios_base::uppercase;
271       case 'e':
272         fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
273         fpar->ref_state_.flags_ |=  std::ios_base::scientific;
274
275         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
276         fpar->ref_state_.flags_ |=  std::ios_base::dec;
277         break;
278       
279       case 'f':
280         fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
281         fpar->ref_state_.flags_ |=  std::ios_base::fixed;
282       case 'u':
283       case 'd':
284       case 'i':
285         fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
286         fpar->ref_state_.flags_ |=  std::ios_base::dec;
287         break;
288
289       case 'T':
290         ++i1;
291         if( i1 >= buf.size())
292           maybe_throw_exception(exceptions);
293         else
294           fpar->ref_state_.fill_ = buf[i1];
295         fpar->pad_scheme_ |= format_item_t::tabulation;
296         fpar->argN_ = format_item_t::argN_tabulation; 
297         break;
298       case 't': 
299         fpar->ref_state_.fill_ = os.widen(' ');
300         fpar->pad_scheme_ |= format_item_t::tabulation;
301         fpar->argN_ = format_item_t::argN_tabulation; 
302         break;
303
304       case 'G':
305         fpar->ref_state_.flags_ |= std::ios_base::uppercase;
306         break;
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;
310
311         // CLEAR all floatield flags, so stream will CHOOSE
312         fpar->ref_state_.flags_ &= ~std::ios_base::floatfield; 
313         break;
314
315       case 'C':
316       case 'c': 
317         fpar->truncate_ = 1;
318         break;
319       case 'S':
320       case 's': 
321         fpar->truncate_ = fpar->ref_state_.precision_;
322         fpar->ref_state_.precision_ = -1;
323         break;
324       case 'n' :  
325         fpar->argN_ = format_item_t::argN_ignored;
326         break;
327       default: 
328         maybe_throw_exception(exceptions);
329       }
330     ++i1;
331
332     if( in_brackets )
333       {
334         if( i1<buf.size() && buf[i1]==os.widen('|') ) 
335           {
336             ++i1;
337             return true;
338           }
339         else  maybe_throw_exception(exceptions);
340       }
341     return true;
342   }
343
344 } // detail namespace
345 } // io namespace
346
347
348 // -----------------------------------------------
349 //  format :: parse(..)
350
351 template<class Ch, class Traits>
352 void basic_format<Ch, Traits> ::parse(const string_t & buf) 
353   // parse the format-string
354 {
355     using namespace std;
356     const Ch arg_mark = oss_.widen('%');
357     bool ordered_args=true; 
358     int max_argN=-1;
359     typename string_t::size_type i1=0;
360     int num_items=0;
361     
362     // A: find upper_bound on num_items and allocates arrays
363     i1=0; 
364     while( (i1=buf.find(arg_mark,i1)) != string_t::npos ) 
365     {
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 '%'
370       }
371       if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
372       ++i1;
373       
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;
377
378       ++num_items;
379     }
380     items_.assign( num_items, format_item_t() );
381     
382     // B: Now the real parsing of the format string :
383     num_items=0;
384     i1 = 0;
385     typename string_t::size_type i0 = i1;
386     bool special_things=false;
387     int cur_it=0;
388     while( (i1=buf.find(arg_mark,i1)) != string_t::npos ) 
389     {
390       string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
391
392       if( buf[i1+1] == buf[i1] ) // escaped mark, '%%'
393       {
394         piece += buf.substr(i0, i1-i0) + buf[i1]; 
395         i1+=2; i0=i1;
396         continue; 
397       }
398       BOOST_ASSERT(  static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);
399
400       if(i1!=i0) piece += buf.substr(i0, i1-i0);
401       ++i1;
402       
403       bool parse_ok;
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
406
407       i0=i1;
408       items_[cur_it].compute_states(); // process complex options, like zeropad, into stream params.
409
410       int argN=items_[cur_it].argN_;
411       if(argN == format_item_t::argN_ignored)
412         continue;
413       if(argN ==format_item_t::argN_no_posit)
414         ordered_args=false;
415       else if(argN == format_item_t::argN_tabulation) special_things=true;
416       else if(argN > max_argN) max_argN = argN;
417       ++num_items;
418       ++cur_it;
419     } // loop on %'s
420     BOOST_ASSERT(cur_it == num_items);
421     
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);
425     
426     if( !ordered_args) 
427     {
428       if(max_argN >= 0 )  // dont mix positional with non-positionnal directives
429         {
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
433         }
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) 
438           {
439             items_[i].argN_ = non_ordered_items;
440             ++non_ordered_items;
441           }
442       max_argN = non_ordered_items-1;
443     }
444     
445     // C: set some member data :
446     items_.resize(num_items);
447
448     if(special_things) style_ |= special_needs;
449     num_args_ = max_argN + 1;
450     if(ordered_args) style_ |=  ordered;
451     else style_ &= ~ordered;
452 }
453
454 } // namespace boost
455
456
457 #endif //  BOOST_FORMAT_PARSING_HPP