Fixed problem in DeviceSettings::strParam, returned wrong string
[avr-sim.git] / src / Format.cpp
bloba9c94152be8e8a032b0d42654a739cabedca1d25
1 /*
2 * avr-sim: An atmel AVR simulator
3 * Copyright (C) 2008 Tom Haber
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "Format.h"
20 #include <ctype.h>
21 #include <iostream>
23 namespace util {
24 void empty_buf(std::ostringstream & os) {
25 static const std::string emptyStr;
26 os.str(emptyStr);
29 // applies centered / left / right padding to the string s.
30 // Effects : string s is padded.
31 void do_pad(std::string & s, std::streamsize w, const char c,
32 std::ios_base::fmtflags f, bool center) {
34 std::streamsize n=w-s.size();
35 if(n<=0)
36 return;
38 if( center ){
39 s.reserve(w); // allocate once for the 2 inserts
40 const std::streamsize n1 = n /2, n0 = n - n1;
41 s.insert(s.begin(), n0, c);
42 s.append(n1, c);
43 } else {
44 if(f & std::ios_base::left)
45 s.append(n, c);
46 else
47 s.insert(s.begin(), n, c);
51 format::format(const char *str)
52 : style(0), cur_arg(0), num_args(0), dumped(false),
53 items(), oss(), exceptions_flag(all_error_bits) {
55 state0.set_by_stream(oss);
57 std::string emptyStr;
58 if( str == 0 )
59 str = emptyStr.c_str();
61 parse( str );
64 format::format(const std::string & s)
65 : style(0), cur_arg(0), num_args(0), dumped(false),
66 items(), oss(), exceptions_flag(all_error_bits) {
68 state0.set_by_stream(oss);
69 parse(s);
72 format::format(const format & x)
73 : style(x.style), cur_arg(x.cur_arg), num_args(x.num_args), dumped(false),
74 items(x.items), prefix(x.prefix), bound(x.bound),
75 oss(), // <- we obviously can't copy x.oss
76 state0(x.state0), exceptions_flag(x.exceptions_flag){
78 state0.apply_on(oss);
81 format & format::operator =(const format & x) {
82 if( this == &x )
83 return *this;
85 state0 = x.state0;
86 state0.apply_on(oss);
88 // plus all the other (trivial) assignments :
89 exceptions_flag = x.exceptions_flag;
90 items = x.items;
91 prefix = x.prefix;
92 bound=x.bound;
93 style=x.style;
94 cur_arg=x.cur_arg;
95 num_args=x.num_args;
96 dumped=x.dumped;
97 return *this;
100 // empty the string buffers (except bound arguments, see clear_binds() )
101 // and make the format object ready for formatting a new set of arguments
102 format & format::clear() {
103 for(unsigned long i=0; i<items.size(); ++i) {
104 items[i].state = items[i].ref_state;
106 // clear converted strings only if the corresponding argument is not bound :
107 if( bound.size()==0 || !bound[ items[i].argN ] ) items[i].res.resize(0);
110 cur_arg=0; dumped=false;
112 // maybe first arg is bound:
113 if(bound.size() != 0)
114 while( (cur_arg < num_args) && bound[cur_arg] ) ++cur_arg;
116 return *this;
119 // cancel all bindings, and clear()
120 format & format::clear_binds() {
121 bound.resize(0);
122 clear();
124 return *this;
127 // cancel the binding of ONE argument, and clear()
128 format & format::clear_bind(int argN) {
129 if(argN<1 || argN > num_args || bound.size()==0 || !bound[argN-1] ) {
130 if( exceptions() & out_of_range_bit )
131 throw out_of_range(); // arg not in range.
132 else
133 return *this;
136 bound[argN-1]=false;
137 clear();
139 return *this;
142 std::string format::str() const {
143 dumped=true;
145 if( items.size()==0 )
146 return prefix;
148 if( cur_arg < num_args )
149 if( exceptions() & too_few_args_bit )
150 throw too_few_args(); // not enough variables have been supplied !
152 unsigned long sz = prefix.size();
153 unsigned long i;
154 for(i=0; i < items.size(); ++i)
155 sz += items[i].res.size() + items[i].appendix.size();
157 std::string res;
158 res.reserve(sz);
159 res += prefix;
161 for(i=0; i < items.size(); ++i) {
162 const format_item & item = items[i];
163 res += item.res;
164 if( item.argN == format_item::argN_tabulation) {
165 std::streamsize n = item.state.width - res.size();
167 if( n > 0 )
168 res.append( n, item.state.fill );
171 res += item.appendix;
174 return res;
177 // Input : char string, with starting index
178 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
179 // Effects : reads s[start:] and converts digits into an integral n, of type Res
180 // Returns : n
181 template<class Res>
182 inline Res str2int(const std::string & s,
183 std::string::size_type start,
184 std::ios & os, const Res = Res(0) ) {
185 Res n = 0;
187 while( start<s.size() && isdigit(s[start]) ) {
188 char cur_ch = os.narrow( s[start], 0);
189 n *= 10;
190 n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard
191 ++start;
194 return n;
197 // skip printf's "asterisk-fields" directives in the format-string buf
198 // Input : char string, with starting index *pos_p
199 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
200 // Effects : advance *pos_p by skipping printf's asterisk fields.
201 // Returns : nothing
202 static void skip_asterisk(const std::string & buf,
203 std::string::size_type *pos_p,
204 std::ios &os) {
205 using namespace std;
206 if(*pos_p >= buf.size() ) return;
207 if(buf[ *pos_p]==os.widen('*')) {
208 ++ (*pos_p);
209 while (*pos_p < buf.size() && isdigit(buf[*pos_p])) ++(*pos_p);
210 if(buf[*pos_p]==os.widen('$')) ++(*pos_p);
214 // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
215 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
216 // a bitset'exceptions' telling whether to throw exceptions on errors.
217 // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled)
218 // false if it failed so bad that the directive should be printed verbatim
219 // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
220 // - *fpar is set with the parameters read in the directive
221 bool parse_printf_directive(const std::string & buf,
222 std::string::size_type *pos_p,
223 format::format_item *fpar, std::ios & os,
224 unsigned char exceptions) {
225 std::string::size_type & i1 = *pos_p, i0;
226 fpar->argN = format::format_item::argN_no_posit; // if no positional-directive
228 bool in_brackets=false;
229 if(buf[i1]==os.widen('|')) {
230 in_brackets=true;
231 if( ++i1 >= buf.size() ) {
232 if(exceptions & format::bad_format_string_bit)
233 throw bad_format_string();
235 return false;
239 // the flag '0' would be picked as a digit for argument order, but here it's a flag :
240 if(buf[i1]==os.widen('0'))
241 goto parse_flags;
243 // handle argument order (%2$d) or possibly width specification: %2d
244 i0 = i1; // save position before digits
245 while( i1 < buf.size() && isdigit(buf[i1]) )
246 ++i1;
248 if( i1 != i0 ) {
249 if( i1 >= buf.size() ) {
250 if( exceptions & format::bad_format_string_bit )
251 throw bad_format_string();
253 return false;
256 int n=str2int(buf,i0, os, int(0) );
257 // %N% case : this is already the end of the directive
259 if( buf[i1] == os.widen('%') ) {
260 fpar->argN = n-1;
261 ++i1;
263 if( in_brackets && (exceptions & format::bad_format_string_bit) )
264 throw bad_format_string();
266 // but don't return. maybe "%" was used in lieu of '$', so we go on.
267 else return true;
271 if ( buf[i1]==os.widen('$') ) {
272 fpar->argN = n-1;
273 ++i1;
274 } else {
275 // non-positionnal directive
276 fpar->ref_state.width = n;
277 fpar->argN = format::format_item::argN_no_posit;
278 goto parse_precision;
282 parse_flags:
283 // handle flags
285 while ( i1 <buf.size() ) {
286 // as long as char is one of + - = # 0 l h or ' '
287 // misc switches
289 switch( os.narrow(buf[i1], 0) ) {
290 case '\'' : break; // no effect yet. (painful to implement)
291 case 'l':
292 case 'h': // short/long modifier : for printf-comaptibility (no action needed)
293 break;
295 case '-':
296 fpar->ref_state.flags |= std::ios_base::left;
297 break;
299 case '=':
300 fpar->pad_scheme |= format::format_item::centered;
301 break;
303 case ' ':
304 fpar->pad_scheme |= format::format_item::spacepad;
305 break;
307 case '+':
308 fpar->ref_state.flags |= std::ios_base::showpos;
309 break;
311 case '0':
312 fpar->pad_scheme |= format::format_item::zeropad;
313 // need to know alignment before really setting flags,
314 // so just add 'zeropad' flag for now, it will be processed later.
315 break;
317 case '#':
318 fpar->ref_state.flags |= std::ios_base::showpoint | std::ios_base::showbase;
319 break;
321 default:
322 goto parse_width;
325 ++i1;
326 } // loop on flag.
328 if( i1>=buf.size()) {
329 if( exceptions & format::bad_format_string_bit )
330 throw bad_format_string();
332 return true;
335 parse_width:
336 // handle width spec
337 skip_asterisk(buf, &i1, os); // skips 'asterisk fields' : *, or *N$
338 i0 = i1; // save position before digits
339 while (i1<buf.size() && isdigit(buf[i1]))
340 i1++;
342 if( i1!=i0 ) {
343 fpar->ref_state.width = str2int( buf,i0, os, std::streamsize(0) );
346 parse_precision:
347 if( i1>=buf.size()) {
348 if( exceptions & format::bad_format_string_bit )
349 throw bad_format_string();
350 return true;
353 // handle precision spec
354 if (buf[i1]==os.widen('.')) {
355 ++i1;
356 skip_asterisk(buf, &i1, os);
357 i0 = i1; // save position before digits
358 while (i1<buf.size() && isdigit(buf[i1]))
359 ++i1;
361 if(i1==i0)
362 fpar->ref_state.precision = 0;
363 else
364 fpar->ref_state.precision = str2int(buf,i0, os, std::streamsize(0) );
367 // handle formatting-type flags :
369 while( i1<buf.size() &&
370 ( buf[i1]==os.widen('l') || buf[i1]==os.widen('L') || buf[i1]==os.widen('h')) )
371 ++i1;
373 if( i1>=buf.size()) {
374 if( exceptions & format::bad_format_string_bit )
375 throw bad_format_string();
377 return true;
380 if( in_brackets && buf[i1]==os.widen('|') ) {
381 ++i1;
382 return true;
385 switch (os.narrow(buf[i1], 0) ) {
386 case 'X':
387 fpar->ref_state.flags |= std::ios_base::uppercase;
389 case 'p': // pointer => set hex.
390 case 'x':
391 fpar->ref_state.flags &= ~std::ios_base::basefield;
392 fpar->ref_state.flags |= std::ios_base::hex;
393 break;
395 case 'o':
396 fpar->ref_state.flags &= ~std::ios_base::basefield;
397 fpar->ref_state.flags |= std::ios_base::oct;
398 break;
400 case 'E':
401 fpar->ref_state.flags |= std::ios_base::uppercase;
402 case 'e':
403 fpar->ref_state.flags &= ~std::ios_base::floatfield;
404 fpar->ref_state.flags |= std::ios_base::scientific;
405 fpar->ref_state.flags &= ~std::ios_base::basefield;
406 fpar->ref_state.flags |= std::ios_base::dec;
407 break;
409 case 'f':
410 fpar->ref_state.flags &= ~std::ios_base::floatfield;
411 fpar->ref_state.flags |= std::ios_base::fixed;
412 case 'u':
413 case 'd':
414 case 'i':
415 fpar->ref_state.flags &= ~std::ios_base::basefield;
416 fpar->ref_state.flags |= std::ios_base::dec;
417 break;
419 case 'T':
420 ++i1;
421 if( i1 >= buf.size()) {
422 if( exceptions & format::bad_format_string_bit )
423 throw bad_format_string();
424 } else {
425 fpar->ref_state.fill = buf[i1];
428 fpar->pad_scheme |= format::format_item::tabulation;
429 fpar->argN = format::format_item::argN_tabulation;
430 break;
432 case 't':
433 fpar->ref_state.fill = os.widen(' ');
434 fpar->pad_scheme |= format::format_item::tabulation;
435 fpar->argN = format::format_item::argN_tabulation;
436 break;
438 case 'G':
439 fpar->ref_state.flags |= std::ios_base::uppercase;
440 break;
442 case 'g': // 'g' conversion is default for floats.
443 fpar->ref_state.flags &= ~std::ios_base::basefield;
444 fpar->ref_state.flags |= std::ios_base::dec;
445 // CLEAR all floatield flags, so stream will CHOOSE
446 fpar->ref_state.flags &= ~std::ios_base::floatfield;
447 break;
449 case 'C':
450 case 'c':
451 fpar->truncate = 1;
452 break;
454 case 'S':
455 case 's':
456 fpar->truncate = fpar->ref_state.precision;
457 fpar->ref_state.precision = -1;
458 break;
460 case 'n' :
461 fpar->argN = format::format_item::argN_ignored;
462 break;
464 default:
465 if( exceptions & format::bad_format_string_bit )
466 throw bad_format_string();
469 ++i1;
471 if( in_brackets ) {
472 if( i1<buf.size() && buf[i1]==os.widen('|') ) {
473 ++i1;
474 return true;
475 } else if( exceptions & format::bad_format_string_bit )
476 throw bad_format_string();
479 return true;
482 // parse the format-string
483 void format::parse(const std::string & buf) {
484 using namespace std;
485 const char arg_mark = oss.widen('%');
486 bool ordered_args=true;
487 int max_argN=-1;
488 string::size_type i1=0;
489 int num_items=0;
491 // A: find upper_bound on num_items and allocates arrays
492 i1=0;
493 while( (i1=buf.find(arg_mark,i1)) != string::npos ) {
494 if( i1+1 >= buf.size() ) {
495 if( exceptions() & bad_format_string_bit)
496 throw bad_format_string(); // must not end in "bla bla %"
498 else break; // stop there, ignore last '%'
501 if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
502 ++i1;
504 // in case of %N% directives, dont count it double (wastes allocations..) :
505 while(i1 < buf.size() && isdigit(buf[i1])) ++i1;
506 if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1;
508 ++num_items;
512 items.assign( num_items, format_item() );
514 // B: Now the real parsing of the format string :
515 num_items=0;
516 i1 = 0;
517 string::size_type i0 = i1;
518 bool special_things=false;
519 int cur_it=0;
520 while( (i1=buf.find(arg_mark,i1)) != string::npos ) {
521 string & piece = (cur_it==0) ? prefix : items[cur_it-1].appendix;
523 // escaped mark, '%%'
524 if( buf[i1+1] == buf[i1] ) {
525 piece += buf.substr(i0, i1-i0) + buf[i1];
526 i1+=2; i0=i1;
527 continue;
530 if(i1!=i0) piece += buf.substr(i0, i1-i0);
531 ++i1;
533 bool parse_ok;
534 parse_ok = parse_printf_directive(buf, &i1, &items[cur_it], oss, exceptions());
535 if( ! parse_ok ) continue; // the directive will be printed verbatim
537 i0=i1;
538 items[cur_it].compute_states(); // process complex options, like zeropad, into stream params.
540 int argN=items[cur_it].argN;
542 if( argN == format_item::argN_ignored )
543 continue;
545 if(argN ==format_item::argN_no_posit)
546 ordered_args=false;
547 else if(argN == format_item::argN_tabulation)
548 special_things=true;
549 else if(argN > max_argN)
550 max_argN = argN;
552 ++num_items;
553 ++cur_it;
554 } // loop on %'s
556 // store the final piece of string
557 string & piece = (cur_it==0) ? prefix : items[cur_it-1].appendix;
558 piece += buf.substr(i0);
560 if( !ordered_args) {
561 // dont mix positional with non-positionnal directives
562 if(max_argN >= 0 ) {
563 if( exceptions() & bad_format_string_bit )
564 throw bad_format_string();
565 // else do nothing. => positional arguments are processed as non-positional
568 // set things like it would have been with positional directives :
569 int non_ordered_items = 0;
570 for(int i=0; i< num_items; ++i)
571 if(items[i].argN == format_item::argN_no_posit) {
572 items[i].argN = non_ordered_items;
573 ++non_ordered_items;
575 max_argN = non_ordered_items-1;
578 // C: set some member data :
579 items.resize(num_items);
581 if(special_things)
582 style |= special_needs;
584 num_args = max_argN + 1;
586 if(ordered_args)
587 style |= ordered;
588 else
589 style &= ~ordered;
592 // effect: "return os << str(f);" but we can try to do it faster
593 std::ostream & operator <<(std::ostream & os, const format & f) {
594 if( f.items.size() == 0 ) {
595 os << f.prefix;
596 } else {
597 if( f.cur_arg < f.num_args )
598 if( f.exceptions() & format::too_few_args_bit )
599 throw too_few_args(); // not enough variables have been supplied !
601 if( f.style & format::special_needs) {
602 os << f.str();
603 } else {
604 // else we dont have to count chars output, so we dump directly to os :
605 os << f.prefix;
606 for(unsigned long i=0; i<f.items.size(); ++i) {
608 const format::format_item & item = f.items[i];
609 os << item.res;
610 os << item.appendix;
615 f.dumped=true;
616 return os;