bc: handle BIN_FILE and LEX_BAD_CHAR errors at the site of detection
The most informative message can be generated at the location where error is detected. The "error codes" are stupid: print error meesage immediately, then just return "there was an error" indicator. All error codes will be converted. For now, converting these two. For now, this and following changes will degrade error messages quality. For example, file name and line number printouts may be lost. This will be re-added later. This change anlso fixes handling of invalid stdin input: this used to cause interactive bc to exit: .... >>> ς bc: illegal character 0xcf bc: illegal character 0x82 >>> _ function old new delta bc_error - 42 +42 bc_lex_token 1333 1369 +36 dc_lex_token 675 695 +20 bc_read_line 311 325 +14 bc_num_a 456 454 -2 bc_err_msgs 188 184 -4 bc_num_ulong 95 85 -10 bc_vm_run 1984 1955 -29 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/4 up/down: 112/-45) Total: 67 bytes text data bss dec hex filename 987828 485 7296 995609 f3119 busybox_old 987929 485 7296 995710 f317e busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
d4744adf35
commit
c1c2470f84
140
miscutils/bc.c
140
miscutils/bc.c
@ -169,13 +169,14 @@
|
|||||||
|
|
||||||
typedef enum BcStatus {
|
typedef enum BcStatus {
|
||||||
BC_STATUS_SUCCESS,
|
BC_STATUS_SUCCESS,
|
||||||
|
BC_STATUS_FAILURE,
|
||||||
|
|
||||||
// BC_STATUS_ALLOC_ERR,
|
// BC_STATUS_ALLOC_ERR,
|
||||||
// BC_STATUS_INPUT_EOF,
|
// BC_STATUS_INPUT_EOF,
|
||||||
BC_STATUS_BIN_FILE,
|
// BC_STATUS_BIN_FILE,
|
||||||
// BC_STATUS_PATH_IS_DIR,
|
// BC_STATUS_PATH_IS_DIR,
|
||||||
|
|
||||||
BC_STATUS_LEX_BAD_CHAR,
|
// BC_STATUS_LEX_BAD_CHAR,
|
||||||
BC_STATUS_LEX_NO_STRING_END,
|
BC_STATUS_LEX_NO_STRING_END,
|
||||||
BC_STATUS_LEX_NO_COMMENT_END,
|
BC_STATUS_LEX_NO_COMMENT_END,
|
||||||
BC_STATUS_LEX_EOF,
|
BC_STATUS_LEX_EOF,
|
||||||
@ -239,12 +240,13 @@ typedef enum BcStatus {
|
|||||||
// Keep enum above and messages below in sync!
|
// Keep enum above and messages below in sync!
|
||||||
static const char *const bc_err_msgs[] = {
|
static const char *const bc_err_msgs[] = {
|
||||||
NULL,
|
NULL,
|
||||||
|
"",
|
||||||
// "memory allocation error",
|
// "memory allocation error",
|
||||||
// "I/O error",
|
// "I/O error",
|
||||||
"file is not text:",
|
// "file is not text:",
|
||||||
// "path is a directory:",
|
// "path is a directory:",
|
||||||
|
|
||||||
"bad character",
|
// "bad character",
|
||||||
"string end could not be found",
|
"string end could not be found",
|
||||||
"comment end could not be found",
|
"comment end could not be found",
|
||||||
"end of file",
|
"end of file",
|
||||||
@ -321,8 +323,6 @@ typedef struct BcVec {
|
|||||||
|
|
||||||
#define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), bc_id_free))
|
#define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), bc_id_free))
|
||||||
|
|
||||||
#define BC_READ_BIN_CHAR(c) ((((c) < ' ' && !isspace((c))) || (c) > '~'))
|
|
||||||
|
|
||||||
typedef signed char BcDig;
|
typedef signed char BcDig;
|
||||||
|
|
||||||
typedef struct BcNum {
|
typedef struct BcNum {
|
||||||
@ -1138,6 +1138,18 @@ static void quit(void)
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bc_error(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list p;
|
||||||
|
|
||||||
|
va_start(p, fmt);
|
||||||
|
bb_verror_msg(fmt, p, NULL);
|
||||||
|
va_end(p);
|
||||||
|
if (!G.ttyin)
|
||||||
|
exit(1);
|
||||||
|
return BC_STATUS_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
static void bc_vec_grow(BcVec *v, size_t n)
|
static void bc_vec_grow(BcVec *v, size_t n)
|
||||||
{
|
{
|
||||||
size_t cap = v->cap * 2;
|
size_t cap = v->cap * 2;
|
||||||
@ -1289,54 +1301,63 @@ static size_t bc_map_index(const BcVec *v, const void *ptr)
|
|||||||
|
|
||||||
static BcStatus bc_read_line(BcVec *vec, const char *prompt)
|
static BcStatus bc_read_line(BcVec *vec, const char *prompt)
|
||||||
{
|
{
|
||||||
int i;
|
bool bad_chars;
|
||||||
signed char c;
|
|
||||||
|
|
||||||
bc_vec_npop(vec, vec->len);
|
|
||||||
|
|
||||||
fflush_and_check();
|
|
||||||
#if ENABLE_FEATURE_BC_SIGNALS
|
|
||||||
if (bb_got_signal) { // ^C was pressed
|
|
||||||
intr:
|
|
||||||
bb_got_signal = 0; // resets G_interrupt to zero
|
|
||||||
fputs(IS_BC
|
|
||||||
? "\ninterrupt (type \"quit\" to exit)\n"
|
|
||||||
: "\ninterrupt (type \"q\" to exit)\n"
|
|
||||||
, stderr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (G.ttyin && !G_posix)
|
|
||||||
fputs(prompt, stderr);
|
|
||||||
fflush_and_check();
|
|
||||||
|
|
||||||
#if ENABLE_FEATURE_BC_SIGNALS
|
|
||||||
errno = 0;
|
|
||||||
#endif
|
|
||||||
do {
|
do {
|
||||||
i = fgetc(stdin);
|
int i;
|
||||||
|
char c;
|
||||||
|
|
||||||
if (i == EOF) {
|
bad_chars = 0;
|
||||||
|
bc_vec_npop(vec, vec->len);
|
||||||
|
|
||||||
|
fflush_and_check();
|
||||||
#if ENABLE_FEATURE_BC_SIGNALS
|
#if ENABLE_FEATURE_BC_SIGNALS
|
||||||
// Both conditions appear simultaneously, check both just in case
|
if (bb_got_signal) { // ^C was pressed
|
||||||
if (errno == EINTR || bb_got_signal) {
|
intr:
|
||||||
// ^C was pressed
|
bb_got_signal = 0; // resets G_interrupt to zero
|
||||||
clearerr(stdin);
|
fputs(IS_BC
|
||||||
goto intr;
|
? "\ninterrupt (type \"quit\" to exit)\n"
|
||||||
}
|
: "\ninterrupt (type \"q\" to exit)\n"
|
||||||
#endif
|
, stderr);
|
||||||
if (ferror(stdin))
|
|
||||||
quit(); // this emits error message
|
|
||||||
G.eof = 1;
|
|
||||||
// Note: EOF does not append '\n', therefore:
|
|
||||||
// printf 'print 123\n' | bc - works
|
|
||||||
// printf 'print 123' | bc - fails (syntax error)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
if (G.ttyin && !G_posix)
|
||||||
|
fputs(prompt, stderr);
|
||||||
|
|
||||||
c = (signed char) i;
|
#if ENABLE_FEATURE_BC_SIGNALS
|
||||||
if (i > UCHAR_MAX || BC_READ_BIN_CHAR(c)) return BC_STATUS_BIN_FILE;
|
errno = 0;
|
||||||
bc_vec_push(vec, &c);
|
#endif
|
||||||
} while (c != '\n');
|
do {
|
||||||
|
i = fgetc(stdin);
|
||||||
|
if (i == EOF) {
|
||||||
|
#if ENABLE_FEATURE_BC_SIGNALS
|
||||||
|
// Both conditions appear simultaneously, check both just in case
|
||||||
|
if (errno == EINTR || bb_got_signal) {
|
||||||
|
// ^C was pressed
|
||||||
|
clearerr(stdin);
|
||||||
|
goto intr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (ferror(stdin))
|
||||||
|
quit(); // this emits error message
|
||||||
|
G.eof = 1;
|
||||||
|
// Note: EOF does not append '\n', therefore:
|
||||||
|
// printf 'print 123\n' | bc - works
|
||||||
|
// printf 'print 123' | bc - fails (syntax error)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((i < ' ' && i != '\t' && i != '\r' && i != '\n') // also allow '\v' '\f'?
|
||||||
|
|| i > 0x7e
|
||||||
|
) {
|
||||||
|
// Bad chars on this line, ignore entire line
|
||||||
|
bc_error("illegal character 0x%02x", i);
|
||||||
|
bad_chars = 1;
|
||||||
|
}
|
||||||
|
c = (char) i;
|
||||||
|
bc_vec_push(vec, &c);
|
||||||
|
} while (i != '\n');
|
||||||
|
} while (bad_chars);
|
||||||
|
|
||||||
bc_vec_pushByte(vec, '\0');
|
bc_vec_pushByte(vec, '\0');
|
||||||
|
|
||||||
@ -1352,7 +1373,10 @@ static char* bc_read_file(const char *path)
|
|||||||
buf = xmalloc_open_read_close(path, &size);
|
buf = xmalloc_open_read_close(path, &size);
|
||||||
|
|
||||||
for (i = 0; i < size; ++i) {
|
for (i = 0; i < size; ++i) {
|
||||||
if (BC_READ_BIN_CHAR(buf[i])) {
|
char c = buf[i];
|
||||||
|
if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
|
||||||
|
|| c > 0x7e
|
||||||
|
) {
|
||||||
free(buf);
|
free(buf);
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
break;
|
break;
|
||||||
@ -3162,7 +3186,7 @@ static BcStatus bc_lex_token(BcLex *l)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
l->t.t = BC_LEX_INVALID;
|
l->t.t = BC_LEX_INVALID;
|
||||||
s = BC_STATUS_LEX_BAD_CHAR;
|
s = bc_error("bad character '%c'", '&');
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -3291,7 +3315,7 @@ static BcStatus bc_lex_token(BcLex *l)
|
|||||||
++l->i;
|
++l->i;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
s = BC_STATUS_LEX_BAD_CHAR;
|
s = bc_error("bad character '%c'", c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3353,7 +3377,7 @@ static BcStatus bc_lex_token(BcLex *l)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
l->t.t = BC_LEX_INVALID;
|
l->t.t = BC_LEX_INVALID;
|
||||||
s = BC_STATUS_LEX_BAD_CHAR;
|
s = bc_error("bad character '%c'", c);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -3362,7 +3386,7 @@ static BcStatus bc_lex_token(BcLex *l)
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
l->t.t = BC_LEX_INVALID;
|
l->t.t = BC_LEX_INVALID;
|
||||||
s = BC_STATUS_LEX_BAD_CHAR;
|
s = bc_error("bad character '%c'", c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3473,7 +3497,7 @@ static BcStatus dc_lex_token(BcLex *l)
|
|||||||
else if (c2 == '>')
|
else if (c2 == '>')
|
||||||
l->t.t = BC_LEX_OP_REL_GE;
|
l->t.t = BC_LEX_OP_REL_GE;
|
||||||
else
|
else
|
||||||
return BC_STATUS_LEX_BAD_CHAR;
|
return bc_error("bad character '%c'", c);
|
||||||
|
|
||||||
++l->i;
|
++l->i;
|
||||||
break;
|
break;
|
||||||
@ -3490,7 +3514,7 @@ static BcStatus dc_lex_token(BcLex *l)
|
|||||||
if (isdigit(l->buf[l->i]))
|
if (isdigit(l->buf[l->i]))
|
||||||
s = bc_lex_number(l, c);
|
s = bc_lex_number(l, c);
|
||||||
else
|
else
|
||||||
s = BC_STATUS_LEX_BAD_CHAR;
|
s = bc_error("bad character '%c'", c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3524,7 +3548,7 @@ static BcStatus dc_lex_token(BcLex *l)
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
l->t.t = BC_LEX_INVALID;
|
l->t.t = BC_LEX_INVALID;
|
||||||
s = BC_STATUS_LEX_BAD_CHAR;
|
s = bc_error("bad character '%c'", c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6938,7 +6962,7 @@ static BcStatus bc_vm_file(const char *file)
|
|||||||
|
|
||||||
G.prog.file = file;
|
G.prog.file = file;
|
||||||
data = bc_read_file(file);
|
data = bc_read_file(file);
|
||||||
if (!data) return bc_vm_error(BC_STATUS_BIN_FILE, file, 0);
|
if (!data) return bc_error("file '%s' is not text", file);
|
||||||
|
|
||||||
bc_lex_file(&G.prs.l, file);
|
bc_lex_file(&G.prs.l, file);
|
||||||
s = bc_vm_process(data);
|
s = bc_vm_process(data);
|
||||||
@ -7021,8 +7045,6 @@ static BcStatus bc_vm_stdin(void)
|
|||||||
bc_vec_npop(&buffer, buffer.len);
|
bc_vec_npop(&buffer, buffer.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s == BC_STATUS_BIN_FILE) s = bc_vm_error(s, G.prs.l.f, 0);
|
|
||||||
|
|
||||||
if (str)
|
if (str)
|
||||||
s = bc_vm_error(BC_STATUS_LEX_NO_STRING_END, G.prs.l.f,
|
s = bc_vm_error(BC_STATUS_LEX_NO_STRING_END, G.prs.l.f,
|
||||||
G.prs.l.line);
|
G.prs.l.line);
|
||||||
|
Loading…
Reference in New Issue
Block a user