summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkanreki <32443233+kanreki@users.noreply.github.com>2021-09-14 12:15:53 -0700
committerJohn Wiegley <johnw@newartisans.com>2021-12-08 16:17:17 -0800
commit586abd208221761a6c93bc4568513e9cd4dc287d (patch)
treed3d6c8da7e4584665bb705c25a91c5c66083de65
parented5886921bcce0d1a261a37aa83bf135259b7d21 (diff)
downloadfork-ledger-586abd208221761a6c93bc4568513e9cd4dc287d.tar.gz
fork-ledger-586abd208221761a6c93bc4568513e9cd4dc287d.tar.bz2
fork-ledger-586abd208221761a6c93bc4568513e9cd4dc287d.zip
Use correct int return type for stream input operations
This makes it safe to compare results to -1 to indicate EOF, regardless of whether char is considered signed or unsigned; and so eliminates compiler warnings on platforms such as ARM. Fixes bug #2058.
-rw-r--r--src/amount.cc8
-rw-r--r--src/annotate.cc34
-rw-r--r--src/commodity.cc29
-rw-r--r--src/query.cc32
-rw-r--r--src/query.h3
-rw-r--r--src/times.cc15
-rw-r--r--src/token.cc105
-rw-r--r--src/token.h2
-rw-r--r--src/utils.h24
-rw-r--r--test/regress/2058_1.test8
-rw-r--r--test/regress/2058_2.test11
11 files changed, 126 insertions, 145 deletions
diff --git a/src/amount.cc b/src/amount.cc
index 4eacdd09..0527c979 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -970,13 +970,13 @@ namespace {
void parse_quantity(std::istream& in, string& value)
{
char buf[256];
- char c = peek_next_nonws(in);
+ int c = peek_next_nonws(in);
int max = 255;
char *p = buf;
if (c == '-') {
*p++ = c;
max--;
- in.get(c);
+ in.get();
}
READ_INTO(in, p, max, c,
std::isdigit(c) || c == '.' || c == ',');
@@ -1005,10 +1005,10 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
commodity_t::flags_t comm_flags = COMMODITY_STYLE_DEFAULTS;
- char c = peek_next_nonws(in);
+ int c = peek_next_nonws(in);
if (c == '-') {
negative = true;
- in.get(c);
+ in.get();
c = peek_next_nonws(in);
}
diff --git a/src/annotate.cc b/src/annotate.cc
index 27261f57..e0fb0d08 100644
--- a/src/annotate.cc
+++ b/src/annotate.cc
@@ -86,33 +86,33 @@ void annotation_t::parse(std::istream& in)
return;
char buf[256];
- char c = peek_next_nonws(in);
+ int c = peek_next_nonws(in);
if (c == '{') {
if (price)
throw_(amount_error, _("Commodity specifies more than one price"));
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == '{') {
- in.get(c);
+ in.get();
add_flags(ANNOTATION_PRICE_NOT_PER_UNIT);
}
c = peek_next_nonws(in);
if (c == '=') {
- in.get(c);
+ in.get();
add_flags(ANNOTATION_PRICE_FIXATED);
}
READ_INTO(in, buf, 255, c, c != '}');
if (c == '}') {
- in.get(c);
+ in.get();
if (has_flags(ANNOTATION_PRICE_NOT_PER_UNIT)) {
- c = static_cast<char>(in.peek());
+ c = in.peek();
if (c != '}')
throw_(amount_error, _("Commodity lot price lacks double closing brace"));
else
- in.get(c);
+ in.get();
}
} else {
throw_(amount_error, _("Commodity lot price lacks closing brace"));
@@ -128,18 +128,18 @@ void annotation_t::parse(std::istream& in)
if (date)
throw_(amount_error, _("Commodity specifies more than one date"));
- in.get(c);
+ in.get();
READ_INTO(in, buf, 255, c, c != ']');
if (c == ']')
- in.get(c);
+ in.get();
else
throw_(amount_error, _("Commodity date lacks closing bracket"));
date = parse_date(buf);
}
else if (c == '(') {
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == '@') {
in.clear();
in.seekg(pos, std::ios::beg);
@@ -150,13 +150,13 @@ void annotation_t::parse(std::istream& in)
throw_(amount_error,
_("Commodity specifies more than one valuation expresion"));
- in.get(c);
+ in.get();
READ_INTO(in, buf, 255, c, c != ')');
if (c == ')') {
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == ')')
- in.get(c);
+ in.get();
else
throw_(amount_error,
_("Commodity valuation expression lacks closing parentheses"));
@@ -172,7 +172,7 @@ void annotation_t::parse(std::istream& in)
READ_INTO(in, buf, 255, c, c != ')');
if (c == ')')
- in.get(c);
+ in.get();
else
throw_(amount_error, _("Commodity tag lacks closing parenthesis"));
diff --git a/src/commodity.cc b/src/commodity.cc
index ebe388c8..29c04e59 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -297,12 +297,12 @@ void commodity_t::parse_symbol(std::istream& in, string& symbol)
std::istream::pos_type pos = in.tellg();
char buf[256];
- char c = peek_next_nonws(in);
+ int c = peek_next_nonws(in);
if (c == '"') {
- in.get(c);
+ in.get();
READ_INTO(in, buf, 255, c, c != '"');
if (c == '"')
- in.get(c);
+ in.get();
else
throw_(amount_error, _("Quoted commodity symbol lacks closing quote"));
} else {
@@ -310,46 +310,45 @@ void commodity_t::parse_symbol(std::istream& in, string& symbol)
while (_p - buf < 255 && in.good() && ! in.eof() && c != '\n') {
std::size_t bytes = 0;
std::ptrdiff_t size = _p - buf;
- unsigned char d = static_cast<unsigned char>(c);
// Check for the start of a UTF-8 multi-byte encoded string
- if (d >= 192 && d <= 223 && size < 254)
+ if (c >= 192 && c <= 223 && size < 254)
bytes = 2;
- else if (d >= 224 && d <= 239 && size < 253)
+ else if (c >= 224 && c <= 239 && size < 253)
bytes = 3;
- else if (d >= 240 && d <= 247 && size < 252)
+ else if (c >= 240 && c <= 247 && size < 252)
bytes = 4;
- else if (d >= 248 && d <= 251 && size < 251)
+ else if (c >= 248 && c <= 251 && size < 251)
bytes = 5;
- else if (d >= 252 && d <= 253 && size < 250)
+ else if (c >= 252 && c <= 253 && size < 250)
bytes = 6;
- else if (d >= 254) // UTF-8 encoding error
+ else if (c >= 254) // UTF-8 encoding error
break;
if (bytes > 0) { // we're looking at a UTF-8 encoding
for (std::size_t i = 0; i < bytes; i++) {
- in.get(c);
+ c = in.get();
if (in.bad() || in.eof())
throw_(amount_error, _("Invalid UTF-8 encoding for commodity name"));
*_p++ = c;
}
}
- else if (invalid_chars[static_cast<unsigned char>(c)]) {
+ else if (invalid_chars[c]) {
break;
}
else {
- in.get(c);
+ c = in.get();
if (in.eof())
break;
if (c == '\\') {
- in.get(c);
+ c = in.get();
if (in.eof())
throw_(amount_error, _("Backslash at end of commodity name"));
}
*_p++ = c;
}
- c = static_cast<char>(in.peek());
+ c = in.peek();
}
*_p = '\0';
diff --git a/src/query.cc b/src/query.cc
index 883bea40..0eb7dd85 100644
--- a/src/query.cc
+++ b/src/query.cc
@@ -220,37 +220,9 @@ test_ident:
return token_t(token_t::UNKNOWN);
}
-void query_t::lexer_t::token_t::unexpected()
+void query_t::lexer_t::token_t::expected(char wanted)
{
- kind_t prev_kind = kind;
-
- kind = UNKNOWN;
-
- switch (prev_kind) {
- case END_REACHED:
- throw_(parse_error, _("Unexpected end of expression"));
- case TERM:
- throw_(parse_error, _f("Unexpected string '%1%'") % *value);
- default:
- throw_(parse_error, _f("Unexpected token '%1%'") % symbol());
- }
-}
-
-void query_t::lexer_t::token_t::expected(char wanted, char c)
-{
- kind = UNKNOWN;
-
- if (c == '\0' || c == -1) {
- if (wanted == '\0' || wanted == -1)
- throw_(parse_error, _("Unexpected end"));
- else
- throw_(parse_error, _f("Missing '%1%'") % wanted);
- } else {
- if (wanted == '\0' || wanted == -1)
- throw_(parse_error, _f("Invalid char '%1%'") % c);
- else
- throw_(parse_error, _f("Invalid char '%1%' (wanted '%2%')") % c % wanted);
- }
+ throw_(parse_error, _f("Missing '%1%'") % wanted);
}
expr_t::ptr_op_t
diff --git a/src/query.h b/src/query.h
index f7d48d32..3297cbd6 100644
--- a/src/query.h
+++ b/src/query.h
@@ -191,8 +191,7 @@ public:
#endif
}
- void unexpected();
- void expected(char wanted, char c = '\0');
+ void expected(char wanted);
};
token_t token_cache;
diff --git a/src/times.cc b/src/times.cc
index bab4e0de..127cdd69 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -1621,17 +1621,10 @@ void date_parser_t::lexer_t::token_t::unexpected()
void date_parser_t::lexer_t::token_t::expected(char wanted, char c)
{
- if (c == '\0' || c == -1) {
- if (wanted == '\0' || wanted == -1)
- throw_(date_error, _("Unexpected end"));
- else
- throw_(date_error, _f("Missing '%1%'") % wanted);
- } else {
- if (wanted == '\0' || wanted == -1)
- throw_(date_error, _f("Invalid char '%1%'") % c);
- else
- throw_(date_error, _f("Invalid char '%1%' (wanted '%2%')") % c % wanted);
- }
+ if (wanted == '\0')
+ throw_(date_error, _f("Invalid char '%1%'") % c);
+ else
+ throw_(date_error, _f("Invalid char '%1%' (wanted '%2%')") % c % wanted);
}
namespace {
diff --git a/src/token.cc b/src/token.cc
index 76bf5106..63865829 100644
--- a/src/token.cc
+++ b/src/token.cc
@@ -38,7 +38,7 @@ namespace ledger {
int expr_t::token_t::parse_reserved_word(std::istream& in)
{
- char c = static_cast<char>(in.peek());
+ int c = in.peek();
if (c == 'a' || c == 'd' || c == 'e' || c == 'f' ||
c == 'i' || c == 'o' || c == 'n' || c == 't') {
@@ -131,7 +131,8 @@ void expr_t::token_t::parse_ident(std::istream& in)
kind = IDENT;
length = 0;
- char c, buf[256];
+ int c;
+ char buf[256];
READ_INTO_(in, buf, 255, c, length, std::isalnum(c) || c == '_');
value.set_string(buf);
@@ -146,9 +147,9 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
if (! in.good())
throw_(parse_error, _("Input stream no longer valid"));
- char c = peek_next_nonws(in);
+ int c = peek_next_nonws(in);
- if (in.eof() || c == -1) {
+ if (in.eof()) {
kind = TOK_EOF;
return;
}
@@ -162,10 +163,10 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
switch (c) {
case '&':
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == '&') {
- in.get(c);
+ in.get();
kind = KW_AND;
length = 2;
break;
@@ -173,10 +174,10 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
kind = KW_AND;
break;
case '|':
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == '|') {
- in.get(c);
+ in.get();
kind = KW_OR;
length = 2;
break;
@@ -185,23 +186,23 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
case '(':
- in.get(c);
+ in.get();
kind = LPAREN;
break;
case ')':
- in.get(c);
+ in.get();
kind = RPAREN;
break;
case '[': {
- in.get(c);
+ in.get();
char buf[256];
READ_INTO_(in, buf, 255, c, length, c != ']');
if (c != ']')
expected(']', c);
- in.get(c);
+ in.get();
length++;
date_interval_t timespan(buf);
@@ -222,7 +223,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
READ_INTO_(in, buf, 4095, c, length, c != delim);
if (c != delim)
expected(delim, c);
- in.get(c);
+ in.get();
length++;
kind = VALUE;
value.set_string(buf);
@@ -230,10 +231,10 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
}
case '{': {
- in.get(c);
+ in.get();
amount_t temp;
temp.parse(in, PARSE_NO_MIGRATE);
- in.get(c);
+ c = in.get();
if (c != '}')
expected('}', c);
length++;
@@ -243,10 +244,10 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
}
case '!':
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == '=') {
- in.get(c);
+ in.get();
symbol[1] = c;
symbol[2] = '\0';
kind = NEQUAL;
@@ -254,7 +255,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
}
else if (c == '~') {
- in.get(c);
+ in.get();
symbol[1] = c;
symbol[2] = '\0';
kind = NMATCH;
@@ -265,10 +266,10 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
case '-':
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == '>') {
- in.get(c);
+ in.get();
symbol[1] = c;
symbol[2] = '\0';
kind = ARROW;
@@ -278,27 +279,26 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
kind = MINUS;
break;
case '+':
- in.get(c);
+ in.get();
kind = PLUS;
break;
case '*':
- in.get(c);
+ in.get();
kind = STAR;
break;
case '?':
- in.get(c);
+ in.get();
kind = QUERY;
break;
case ':':
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
kind = COLON;
break;
case '/': {
- in.get(c);
+ in.get();
if (pflags.has_flags(PARSE_OP_CONTEXT)) { // operator context
kind = SLASH;
} else { // terminal context
@@ -307,7 +307,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
READ_INTO_(in, buf, 4095, c, length, c != '/');
if (c != '/')
expected('/', c);
- in.get(c);
+ in.get();
length++;
kind = VALUE;
@@ -317,10 +317,10 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
}
case '=':
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
if (c == '~') {
- in.get(c);
+ in.get();
symbol[1] = c;
symbol[2] = '\0';
kind = MATCH;
@@ -328,7 +328,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
}
else if (c == '=') {
- in.get(c);
+ in.get();
symbol[1] = c;
symbol[2] = '\0';
kind = EQUAL;
@@ -339,10 +339,9 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
case '<':
- in.get(c);
- if (static_cast<char>(in.peek()) == '=') {
- in.get(c);
- symbol[1] = c;
+ in.get();
+ if (in.peek() == '=') {
+ symbol[1] = in.get();
symbol[2] = '\0';
kind = LESSEQ;
length = 2;
@@ -352,10 +351,9 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
case '>':
- in.get(c);
- if (static_cast<char>(in.peek()) == '=') {
- in.get(c);
- symbol[1] = c;
+ in.get();
+ if (in.peek() == '=') {
+ symbol[1] = in.get();
symbol[2] = '\0';
kind = GREATEREQ;
length = 2;
@@ -365,17 +363,17 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
break;
case '.':
- in.get(c);
+ in.get();
kind = DOT;
break;
case ',':
- in.get(c);
+ in.get();
kind = COMMA;
break;
case ';':
- in.get(c);
+ in.get();
kind = SEMI;
break;
@@ -420,7 +418,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags)
if (in.fail() || ! in.good())
throw_(parse_error, _("Failed to reset input stream"));
- c = static_cast<char>(in.peek());
+ c = in.peek();
if (c != -1) {
if (! std::isalpha(c) && c != '_')
expected('\0', c);
@@ -503,19 +501,20 @@ void expr_t::token_t::unexpected(const char wanted)
}
}
-void expr_t::token_t::expected(const char wanted, char c)
+void expr_t::token_t::expected(const char wanted, const int c)
{
- if (c == '\0' || c == -1) {
- if (wanted == '\0' || wanted == -1)
+ if (c == -1) {
+ if (wanted == '\0')
throw_(parse_error, _("Unexpected end"));
else
throw_(parse_error, _f("Missing '%1%'") % wanted);
} else {
- if (wanted == '\0' || wanted == -1)
- throw_(parse_error, _f("Invalid char '%1%'") % c);
+ char ch = c;
+ if (wanted == '\0')
+ throw_(parse_error, _f("Invalid char '%1%'") % ch);
else
throw_(parse_error,
- _f("Invalid char '%1%' (wanted '%2%')") % c % wanted);
+ _f("Invalid char '%1%' (wanted '%2%')") % ch % wanted);
}
}
diff --git a/src/token.h b/src/token.h
index 5d9f1fac..db592475 100644
--- a/src/token.h
+++ b/src/token.h
@@ -126,7 +126,7 @@ struct expr_t::token_t : public noncopyable
void next(std::istream& in, const parse_flags_t& flags);
void rewind(std::istream& in);
void unexpected(const char wanted = '\0');
- void expected(const char wanted, const char c = '\0');
+ void expected(const char wanted, const int c);
void expected(const kind_t wanted);
};
diff --git a/src/utils.h b/src/utils.h
index c9146dd7..9af1251a 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -540,25 +540,25 @@ inline char * next_element(char * buf, bool variable = false) {
return NULL;
}
-inline char peek_next_nonws(std::istream& in) {
- char c = static_cast<char>(in.peek());
+inline int peek_next_nonws(std::istream& in) {
+ int c = in.peek();
while (in.good() && ! in.eof() && std::isspace(c)) {
- in.get(c);
- c = static_cast<char>(in.peek());
+ in.get();
+ c = in.peek();
}
return c;
}
#define READ_INTO(str, targ, size, var, cond) { \
char * _p = targ; \
- var = static_cast<char>(str.peek()); \
+ var = str.peek(); \
while (str.good() && ! str.eof() && var != '\n' && \
(cond) && _p - targ < size) { \
- str.get(var); \
+ var = str.get(); \
if (str.eof()) \
break; \
if (var == '\\') { \
- str.get(var); \
+ var = str.get(); \
if (in.eof()) \
break; \
switch (var) { \
@@ -572,22 +572,22 @@ inline char peek_next_nonws(std::istream& in) {
} \
} \
*_p++ = var; \
- var = static_cast<char>(str.peek()); \
+ var = str.peek(); \
} \
*_p = '\0'; \
}
#define READ_INTO_(str, targ, size, var, idx, cond) { \
char * _p = targ; \
- var = static_cast<char>(str.peek()); \
+ var = str.peek(); \
while (str.good() && ! str.eof() && var != '\n' && \
(cond) && _p - targ < size) { \
- str.get(var); \
+ var = str.get(); \
if (str.eof()) \
break; \
idx++; \
if (var == '\\') { \
- str.get(var); \
+ var = str.get(); \
if (in.eof()) \
break; \
switch (var) { \
@@ -602,7 +602,7 @@ inline char peek_next_nonws(std::istream& in) {
idx++; \
} \
*_p++ = var; \
- var = static_cast<char>(str.peek()); \
+ var = str.peek(); \
} \
*_p = '\0'; \
}
diff --git a/test/regress/2058_1.test b/test/regress/2058_1.test
new file mode 100644
index 00000000..06043007
--- /dev/null
+++ b/test/regress/2058_1.test
@@ -0,0 +1,8 @@
+2021/1/2 Test
+ A $1.00
+ B
+
+test -p 'last %^@' bal -> 1
+__ERROR__
+Error: Invalid char '%'
+end test
diff --git a/test/regress/2058_2.test b/test/regress/2058_2.test
new file mode 100644
index 00000000..3c6c2a7e
--- /dev/null
+++ b/test/regress/2058_2.test
@@ -0,0 +1,11 @@
+2021/1/2 Test
+ A $1.00
+ B
+
+test --limit 'date>=[2020/12/31' register A -> 1
+__ERROR__
+While parsing value expression:
+ (date>=[2020/12/31)&((account =~ /A/))
+
+Error: Missing ']'
+end test