forked from CleverRaven/Cataclysm-DDA
-
Notifications
You must be signed in to change notification settings - Fork 0
/
string_formatter.cpp
136 lines (121 loc) · 3.86 KB
/
string_formatter.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "string_formatter.h"
#include <stdexcept>
#include <exception>
#include "cata_assert.h"
char cata::string_formatter::consume_next_input()
{
return current_index_in_format < format.size() ? format[current_index_in_format++] : '\0';
}
char cata::string_formatter::get_current_input() const
{
return current_index_in_format < format.size() ? format[current_index_in_format] : '\0';
}
bool cata::string_formatter::consume_next_input_if( const char c )
{
if( c == get_current_input() ) {
consume_next_input();
return true;
} else {
return false;
}
}
void cata::string_formatter::read_flags()
{
static const std::string flag_characters( "#0 +'I-" );
while( flag_characters.find( get_current_input() ) != std::string::npos ) {
current_format.push_back( consume_next_input() );
}
}
bool cata::string_formatter::has_digit() const
{
const char c = get_current_input();
return c >= '0' && c <= '9';
}
cata::optional<int> cata::string_formatter::read_argument_index()
{
const char c = get_current_input();
// can't use has_digit because '0' is not allowed as first character
if( c >= '1' && c <= '9' ) {
const size_t pos = format.find_first_not_of( "012345678", current_index_in_format + 1 );
if( pos == std::string::npos || format[pos] != '$' ) {
return cata::nullopt;
}
const int result = parse_integer() - 1; // arguments are 1-based
// We already know this is true because of the `find_first_not_of` check above.
const bool had_next = consume_next_input_if( '$' );
( void ) had_next;
cata_assert( had_next );
return result;
} else {
return cata::nullopt;
}
}
int cata::string_formatter::parse_integer( )
{
int result = 0;
while( has_digit() ) {
// TODO: Check for overflow
result = result * 10 + ( consume_next_input() - '0' );
}
return result;
}
cata::optional<int> cata::string_formatter::read_number_or_argument_index()
{
if( consume_next_input_if( '*' ) ) {
if( !has_digit() ) {
return current_argument_index++;
}
const int index = parse_integer() - 1; // format specifiers are 1-based
if( !consume_next_input_if( '$' ) ) {
throw_error( "expected '$' after field precision" );
}
return index;
}
while( has_digit() ) {
current_format.push_back( consume_next_input() );
}
return cata::nullopt;
}
cata::optional<int> cata::string_formatter::read_width()
{
return read_number_or_argument_index();
}
cata::optional<int> cata::string_formatter::read_precision()
{
if( !consume_next_input_if( '.' ) ) {
return cata::nullopt;
}
current_format.push_back( '.' );
return read_number_or_argument_index();
}
void cata::string_formatter::throw_error( const std::string &msg ) const
{
throw std::runtime_error( msg + " at: \"" + format.substr( 0,
current_index_in_format ) + "|" + format.substr( current_index_in_format ) + "\"" );
}
std::string cata::handle_string_format_error()
{
try {
throw;
} catch( const std::exception &err ) {
return err.what();
}
}
void cata::string_formatter::add_long_long_length_modifier()
{
const char *modifier;
#if !defined(__USE_MINGW_ANSI_STDIO) && (defined(__MINGW32__) || defined(__MINGW64__))
// mingw does not support '%llu' normally (it does with __USE_MINGW_ANSI_STDIO)
// instead one has to use '%I64u'/'I32u'
if( sizeof( signed long long int ) == 4 ) {
modifier = "I32";
} else {
static_assert( sizeof( signed long long int ) == 8,
"unexpected size of long long, format specifier 'I64' does not work" );
modifier = "I64";
}
#else
modifier = "ll";
#endif
current_format.insert( current_format.size() - 1, modifier );
}