]> git.lyx.org Git - lyx.git/blob - boost/boost/format/parsing.hpp
Boost 1.31.0
[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 //  Permission to copy, use, modify, sell and
7 //  distribute this software is granted provided this copyright notice appears
8 //  in all copies. This software is provided "as is" without express or implied
9 //  warranty, and with no claim as to its suitability for any purpose.
10
11 // ideas taken from Rudiger Loos's format class
12 // and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
13
14 // ------------------------------------------------------------------------------
15 // parsing.hpp :  implementation of the parsing member functions
16 //                      ( parse, parse_printf_directive)
17 // ------------------------------------------------------------------------------
18
19
20 #ifndef BOOST_FORMAT_PARSING_HPP
21 #define BOOST_FORMAT_PARSING_HPP
22
23
24 #include <boost/format/format_class.hpp>
25 #include <boost/throw_exception.hpp>
26 #include <boost/assert.hpp>
27
28
29 namespace boost {
30 namespace io {
31 namespace detail {
32
33     template<class Ch, class Stream> inline
34     bool wrap_isdigit(Ch c, Stream &os) {
35 #if ! defined( BOOST_NO_LOCALE_ISIDIGIT )
36         return std::isdigit(c, os.rdbuf()->getloc() );
37 # else
38         using namespace std;
39         return isdigit(c); 
40 #endif 
41     } //end- wrap_isdigit(..)
42  
43     template<class Iter, class Stream> 
44     Iter wrap_scan_notdigit(Iter beg, Iter end, const Stream & os) {
45         using namespace std;
46         for( ; beg!=end && wrap_isdigit(*beg,os); ++beg) ;
47         return beg;
48     }
49
50
51     template<class Res, class Iter, class Stream>
52     Iter str2int(const Iter & start, const Iter & last, Res & res, Stream &os) 
53         // Input : [start, last) iterators range and a
54         //          a basic_ios& merely to use its widen/narrow member function
55         // Effects : reads sequence and converts digits into an integral n, of type Res
56         // Returns : n
57     {
58         using namespace std;
59         Iter it;
60         res=0;
61         for(it=start; it != last && wrap_isdigit(*it, os); ++it ) {
62             char cur_ch = os.narrow( *it, 0); // cant fail.
63             res *= 10;
64             res += cur_ch - '0'; // 22.2.1.1.2.13 of the C++ standard
65         }
66         return it;
67     }
68
69     template<class Iter, class Stream>
70     Iter skip_asterisk(Iter start, Iter last, Stream &os) 
71         // skip printf's "asterisk-fields" directives in the format-string buf
72         // Input : char string, with starting index *pos_p
73         //         a basic_ios& merely to use its widen/narrow member function
74         // Effects : advance *pos_p by skipping printf's asterisk fields.
75         // Returns : nothing
76     {
77         using namespace std;
78         ++ start;
79         start = wrap_scan_notdigit(start, last, os);
80         if(start!=last && *start== os.widen('$') )
81             ++start;
82         return start;
83     }
84
85
86     inline void maybe_throw_exception( unsigned char exceptions)
87         // auxiliary func called by parse_printf_directive
88         // for centralising error handling
89         // it either throws if user sets the corresponding flag, or does nothing.
90     {
91         if(exceptions & io::bad_format_string_bit)
92             boost::throw_exception(io::bad_format_string());
93     }
94     
95
96
97     template<class Ch, class Tr, class Iter, class Stream>
98     bool parse_printf_directive(Iter & start, const Iter& last, 
99                                 detail::format_item<Ch, Tr> * fpar,
100                                 Stream &os,
101                                 unsigned char exceptions)
102         // Input: a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
103         //        a basic_ios& merely to use its widen/narrow member function
104         //        a bitset'excpetions' telling whether to throw exceptions on errors.
105         // Returns: true if parse somehow succeeded (ignore some errors if exceptions disabled)
106         //          false if it failed so bad that the directive should be printed verbatim
107         // Effects:  *pos_p is incremented so that buf[*pos_p] is the first char after the directive
108         //           *fpar is set with the parameters read in the directive
109     {
110         typedef format_item<Ch, Tr>  format_item_t;
111         //BOOST_ASSERT( pos_p != 0);
112
113         fpar->argN_ = format_item_t::argN_no_posit;  // if no positional-directive
114         bool precision_set = false;
115         bool in_brackets=false;
116         if(*start== os.widen('|')) {
117             in_brackets=true;
118             if( ++start >= last ) {
119                 maybe_throw_exception(exceptions);
120                 return false;
121             }
122         }
123
124         // the flag '0' would be picked as a digit for argument order, but here it's a flag :
125         if(*start== os.widen('0')) 
126             goto parse_flags;
127
128         // handle argument order (%2$d)  or possibly width specification: %2d
129         if(wrap_isdigit(*start, os)) {
130             int n;
131             start = str2int(start, last, n, os);
132             if( start >= last ) {
133                 maybe_throw_exception(exceptions);
134                 return false;
135             }
136             
137             // %N% case : this is already the end of the directive
138             if( *start ==  os.widen('%') ) {
139                 fpar->argN_ = n-1;
140                 ++start;
141                 if( in_brackets) 
142                     maybe_throw_exception(exceptions); 
143                 // but don't return.  maybe "%" was used in lieu of '$', so we go on.
144                 else
145                     return true;
146             }
147
148             if ( *start== os.widen('$') ) {
149                 fpar->argN_ = n-1;
150                 ++start;
151             } 
152             else {
153                 // non-positionnal directive
154                 fpar->fmtstate_.width_ = n;
155                 fpar->argN_  = format_item_t::argN_no_posit;
156                 goto parse_precision;
157             }
158         }
159     
160       parse_flags: 
161         // handle flags
162         while ( start != last) { // as long as char is one of + - = _ # 0 l h   or ' '
163             // misc switches
164             switch ( os.narrow(*start, 0)) {
165             case '\'' : break; // no effect yet. (painful to implement)
166             case 'l':
167             case 'h':  // short/long modifier : for printf-comaptibility (no action needed)
168                 break;
169             case '-':
170                 fpar->fmtstate_.flags_ |= std::ios_base::left;
171                 break;
172             case '=':
173                 fpar->pad_scheme_ |= format_item_t::centered;
174                 break;
175             case '_':
176                 fpar->fmtstate_.flags_ |= std::ios_base::internal;
177                 break;
178             case ' ':
179                 fpar->pad_scheme_ |= format_item_t::spacepad;
180                 break;
181             case '+':
182                 fpar->fmtstate_.flags_ |= std::ios_base::showpos;
183                 break;
184             case '0':
185                 fpar->pad_scheme_ |= format_item_t::zeropad;
186                 // need to know alignment before really setting flags,
187                 // so just add 'zeropad' flag for now, it will be processed later.
188                 break;
189             case '#':
190                 fpar->fmtstate_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
191                 break;
192             default:
193                 goto parse_width;
194             }
195             ++start;
196         } // loop on flag.
197
198         if( start>=last) {
199             maybe_throw_exception(exceptions);
200             return true; 
201         }
202       parse_width:
203         // handle width spec
204         // first skip 'asterisk fields' :  *, or *N$
205         if(*start == os.widen('*') )
206             start = skip_asterisk(start, last, os); 
207         if(start!=last && wrap_isdigit(*start, os))
208             start = str2int(start, last, fpar->fmtstate_.width_, os);
209
210       parse_precision:
211         if( start>= last) { 
212             maybe_throw_exception(exceptions);
213             return true;
214         }
215         // handle precision spec
216         if (*start== os.widen('.')) {
217             ++start;
218             if(start != last && *start == os.widen('*') )
219                 start = skip_asterisk(start, last, os); 
220             if(start != last && wrap_isdigit(*start, os)) {
221                 start = str2int(start, last, fpar->fmtstate_.precision_, os);
222                 precision_set = true;
223             }
224             else
225                 fpar->fmtstate_.precision_ =0;
226         }
227     
228         // handle  formatting-type flags :
229         while( start != last && 
230                ( *start== os.widen('l') || *start== os.widen('L') || *start== os.widen('h')) )
231             ++start;
232         if( start>=last) {
233             maybe_throw_exception(exceptions);
234             return true;
235         }
236
237         if( in_brackets && *start== os.widen('|') ) {
238             ++start;
239             return true;
240         }
241         switch ( os.narrow(*start, 0) ) {
242         case 'X':
243             fpar->fmtstate_.flags_ |= std::ios_base::uppercase;
244         case 'p': // pointer => set hex.
245         case 'x':
246             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
247             fpar->fmtstate_.flags_ |= std::ios_base::hex;
248             break;
249
250         case 'o':
251             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
252             fpar->fmtstate_.flags_ |=  std::ios_base::oct;
253             break;
254
255         case 'E':
256             fpar->fmtstate_.flags_ |=  std::ios_base::uppercase;
257         case 'e':
258             fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield;
259             fpar->fmtstate_.flags_ |=  std::ios_base::scientific;
260
261             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
262             fpar->fmtstate_.flags_ |=  std::ios_base::dec;
263             break;
264       
265         case 'f':
266             fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield;
267             fpar->fmtstate_.flags_ |=  std::ios_base::fixed;
268         case 'u':
269         case 'd':
270         case 'i':
271             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
272             fpar->fmtstate_.flags_ |=  std::ios_base::dec;
273             break;
274
275         case 'T':
276             ++start;
277             if( start >= last)
278                 maybe_throw_exception(exceptions);
279             else
280                 fpar->fmtstate_.fill_ = *start;
281             fpar->pad_scheme_ |= format_item_t::tabulation;
282             fpar->argN_ = format_item_t::argN_tabulation; 
283             break;
284         case 't': 
285             fpar->fmtstate_.fill_ = os.widen(' ');
286             fpar->pad_scheme_ |= format_item_t::tabulation;
287             fpar->argN_ = format_item_t::argN_tabulation; 
288             break;
289
290         case 'G':
291             fpar->fmtstate_.flags_ |= std::ios_base::uppercase;
292             break;
293         case 'g': // 'g' conversion is default for floats.
294             fpar->fmtstate_.flags_ &= ~std::ios_base::basefield;
295             fpar->fmtstate_.flags_ |=  std::ios_base::dec;
296
297             // CLEAR all floatield flags, so stream will CHOOSE
298             fpar->fmtstate_.flags_ &= ~std::ios_base::floatfield; 
299             break;
300
301         case 'C':
302         case 'c': 
303             fpar->truncate_ = 1;
304             break;
305         case 'S':
306         case 's': 
307             if(precision_set) // handle truncation manually, with own parameter.
308                 fpar->truncate_ = fpar->fmtstate_.precision_;
309             fpar->fmtstate_.precision_ = 6; // default stream precision.
310             break;
311         case 'n' :  
312             fpar->argN_ = format_item_t::argN_ignored;
313             break;
314         default: 
315             maybe_throw_exception(exceptions);
316         }
317         ++start;
318
319         if( in_brackets ) {
320             if( start != last && *start== os.widen('|') ) {
321                 ++start;
322                 return true;
323             }
324             else  maybe_throw_exception(exceptions);
325         }
326         return true;
327     }
328
329
330     template<class string_t, class Stream>
331     int upper_bound_from_fstring(const string_t& buf, 
332                                  const typename string_t::value_type arg_mark,
333                                  Stream& os,  // just to carry the locale
334                                  unsigned char exceptions) {
335         // quick-parsing of the format-string to count arguments mark (arg_mark, '%')
336         // returns : upper bound on the number of format items in the format strings
337         typename string_t::size_type i1=0;
338         int num_items=0;
339         while( (i1=buf.find(arg_mark,i1)) != string_t::npos ) {
340             if( i1+1 >= buf.size() ) {
341                 if(exceptions & io::bad_format_string_bit)
342                     boost::throw_exception(io::bad_format_string()); // must not end in ".. %"
343                 else break; // stop there, ignore last '%'
344             }
345             if(buf[i1+1] == buf[i1] ) {// escaped "%%"
346                 i1+=2; continue; 
347             }
348
349             ++i1;
350             // in case of %N% directives, dont count it double (wastes allocations..) :
351             i1 = wrap_scan_notdigit(buf.begin()+i1, buf.end(), os) - buf.begin();
352             if( i1 < buf.size() && buf[i1] == arg_mark )
353                 ++i1;
354             ++num_items;
355         }
356         return num_items;
357     }
358 } // detail namespace
359 } // io namespace
360
361
362
363 // -----------------------------------------------
364 //  format :: parse(..)
365
366     template<class Ch, class Tr>
367     basic_format<Ch, Tr>& basic_format<Ch, Tr>:: parse(const string_t& buf) {
368         // parse the format-string 
369         using namespace std;
370
371
372         const Ch arg_mark = oss_.widen('%');
373         bool ordered_args=true; 
374         int max_argN=-1;
375
376         // A: find upper_bound on num_items and allocates arrays
377         int num_items = io::detail::upper_bound_from_fstring(buf, arg_mark, oss_, exceptions());
378         make_or_reuse_data(num_items);
379
380         // B: Now the real parsing of the format string :
381         num_items=0;
382         typename string_t::size_type i0=0, i1=0;
383         typename string_t::const_iterator it;
384         bool special_things=false;
385         int cur_item=0;
386         while( (i1=buf.find(arg_mark,i1)) != string_t::npos ) {
387             string_t & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_;
388             if( buf[i1+1] == buf[i1] ) { // escaped mark, '%%' 
389                 piece += buf.substr(i0, i1+1-i0);
390                 i1+=2; i0=i1;
391                 continue; 
392             }
393             BOOST_ASSERT(  static_cast<unsigned int>(cur_item) < items_.size() || cur_item==0);
394
395             if(i1!=i0)
396                 piece += buf.substr(i0, i1-i0);
397             ++i1;
398             it = buf.begin()+i1;
399             bool parse_ok = io::detail::parse_printf_directive(
400                 it, buf.end(), &items_[cur_item], oss_, exceptions());
401             i1 = it - buf.begin();
402             if( ! parse_ok ) // the directive will be printed verbatim
403                 continue; 
404             i0=i1;
405             items_[cur_item].compute_states(); // process complex options, like zeropad, into params
406
407             int argN=items_[cur_item].argN_;
408             if(argN == format_item_t::argN_ignored)
409                 continue;
410             if(argN ==format_item_t::argN_no_posit)
411                 ordered_args=false;
412             else if(argN == format_item_t::argN_tabulation) special_things=true;
413             else if(argN > max_argN) max_argN = argN;
414             ++num_items;
415             ++cur_item;
416         } // loop on %'s
417         BOOST_ASSERT(cur_item == num_items);
418     
419         // store the final piece of string
420         {
421             string_t & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_;
422             piece += buf.substr(i0);
423         }
424     
425         if( !ordered_args) {
426             if(max_argN >= 0 ) {  // dont mix positional with non-positionnal directives
427                 if(exceptions() & io::bad_format_string_bit)
428                     boost::throw_exception(io::bad_format_string());
429                 // else do nothing. => positionnal arguments are processed as non-positionnal
430             }
431             // set things like it would have been with positional directives :
432             int non_ordered_items = 0;
433             for(int i=0; i< num_items; ++i)
434                 if(items_[i].argN_ == format_item_t::argN_no_posit) {
435                     items_[i].argN_ = non_ordered_items;
436                     ++non_ordered_items;
437                 }
438             max_argN = non_ordered_items-1;
439         }
440     
441         // C: set some member data :
442         items_.resize(num_items, format_item_t(oss_.fill()) );
443
444         if(special_things) style_ |= special_needs;
445         num_args_ = max_argN + 1;
446         if(ordered_args) style_ |=  ordered;
447         else style_ &= ~ordered;
448         return *this;
449     }
450
451 } // namespace boost
452
453
454 #endif //  BOOST_FORMAT_PARSING_HPP