diff options
-rw-r--r-- | miscutils/bc.c | 130 |
1 files changed, 76 insertions, 54 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c index c819decff..dd01f5409 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c | |||
@@ -169,13 +169,14 @@ | |||
169 | 169 | ||
170 | typedef enum BcStatus { | 170 | typedef enum BcStatus { |
171 | BC_STATUS_SUCCESS, | 171 | BC_STATUS_SUCCESS, |
172 | BC_STATUS_FAILURE, | ||
172 | 173 | ||
173 | // BC_STATUS_ALLOC_ERR, | 174 | // BC_STATUS_ALLOC_ERR, |
174 | // BC_STATUS_INPUT_EOF, | 175 | // BC_STATUS_INPUT_EOF, |
175 | BC_STATUS_BIN_FILE, | 176 | // BC_STATUS_BIN_FILE, |
176 | // BC_STATUS_PATH_IS_DIR, | 177 | // BC_STATUS_PATH_IS_DIR, |
177 | 178 | ||
178 | BC_STATUS_LEX_BAD_CHAR, | 179 | // BC_STATUS_LEX_BAD_CHAR, |
179 | BC_STATUS_LEX_NO_STRING_END, | 180 | BC_STATUS_LEX_NO_STRING_END, |
180 | BC_STATUS_LEX_NO_COMMENT_END, | 181 | BC_STATUS_LEX_NO_COMMENT_END, |
181 | BC_STATUS_LEX_EOF, | 182 | BC_STATUS_LEX_EOF, |
@@ -239,12 +240,13 @@ typedef enum BcStatus { | |||
239 | // Keep enum above and messages below in sync! | 240 | // Keep enum above and messages below in sync! |
240 | static const char *const bc_err_msgs[] = { | 241 | static const char *const bc_err_msgs[] = { |
241 | NULL, | 242 | NULL, |
243 | "", | ||
242 | // "memory allocation error", | 244 | // "memory allocation error", |
243 | // "I/O error", | 245 | // "I/O error", |
244 | "file is not text:", | 246 | // "file is not text:", |
245 | // "path is a directory:", | 247 | // "path is a directory:", |
246 | 248 | ||
247 | "bad character", | 249 | // "bad character", |
248 | "string end could not be found", | 250 | "string end could not be found", |
249 | "comment end could not be found", | 251 | "comment end could not be found", |
250 | "end of file", | 252 | "end of file", |
@@ -321,8 +323,6 @@ typedef struct BcVec { | |||
321 | 323 | ||
322 | #define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), bc_id_free)) | 324 | #define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), bc_id_free)) |
323 | 325 | ||
324 | #define BC_READ_BIN_CHAR(c) ((((c) < ' ' && !isspace((c))) || (c) > '~')) | ||
325 | |||
326 | typedef signed char BcDig; | 326 | typedef signed char BcDig; |
327 | 327 | ||
328 | typedef struct BcNum { | 328 | typedef struct BcNum { |
@@ -1138,6 +1138,18 @@ static void quit(void) | |||
1138 | exit(0); | 1138 | exit(0); |
1139 | } | 1139 | } |
1140 | 1140 | ||
1141 | static int bc_error(const char *fmt, ...) | ||
1142 | { | ||
1143 | va_list p; | ||
1144 | |||
1145 | va_start(p, fmt); | ||
1146 | bb_verror_msg(fmt, p, NULL); | ||
1147 | va_end(p); | ||
1148 | if (!G.ttyin) | ||
1149 | exit(1); | ||
1150 | return BC_STATUS_FAILURE; | ||
1151 | } | ||
1152 | |||
1141 | static void bc_vec_grow(BcVec *v, size_t n) | 1153 | static void bc_vec_grow(BcVec *v, size_t n) |
1142 | { | 1154 | { |
1143 | size_t cap = v->cap * 2; | 1155 | size_t cap = v->cap * 2; |
@@ -1289,54 +1301,63 @@ static size_t bc_map_index(const BcVec *v, const void *ptr) | |||
1289 | 1301 | ||
1290 | static BcStatus bc_read_line(BcVec *vec, const char *prompt) | 1302 | static BcStatus bc_read_line(BcVec *vec, const char *prompt) |
1291 | { | 1303 | { |
1292 | int i; | 1304 | bool bad_chars; |
1293 | signed char c; | ||
1294 | 1305 | ||
1295 | bc_vec_npop(vec, vec->len); | 1306 | do { |
1307 | int i; | ||
1308 | char c; | ||
1296 | 1309 | ||
1297 | fflush_and_check(); | 1310 | bad_chars = 0; |
1311 | bc_vec_npop(vec, vec->len); | ||
1312 | |||
1313 | fflush_and_check(); | ||
1298 | #if ENABLE_FEATURE_BC_SIGNALS | 1314 | #if ENABLE_FEATURE_BC_SIGNALS |
1299 | if (bb_got_signal) { // ^C was pressed | 1315 | if (bb_got_signal) { // ^C was pressed |
1300 | intr: | 1316 | intr: |
1301 | bb_got_signal = 0; // resets G_interrupt to zero | 1317 | bb_got_signal = 0; // resets G_interrupt to zero |
1302 | fputs(IS_BC | 1318 | fputs(IS_BC |
1303 | ? "\ninterrupt (type \"quit\" to exit)\n" | 1319 | ? "\ninterrupt (type \"quit\" to exit)\n" |
1304 | : "\ninterrupt (type \"q\" to exit)\n" | 1320 | : "\ninterrupt (type \"q\" to exit)\n" |
1305 | , stderr); | 1321 | , stderr); |
1306 | } | 1322 | } |
1307 | #endif | 1323 | #endif |
1308 | if (G.ttyin && !G_posix) | 1324 | if (G.ttyin && !G_posix) |
1309 | fputs(prompt, stderr); | 1325 | fputs(prompt, stderr); |
1310 | fflush_and_check(); | ||
1311 | 1326 | ||
1312 | #if ENABLE_FEATURE_BC_SIGNALS | 1327 | #if ENABLE_FEATURE_BC_SIGNALS |
1313 | errno = 0; | 1328 | errno = 0; |
1314 | #endif | 1329 | #endif |
1315 | do { | 1330 | do { |
1316 | i = fgetc(stdin); | 1331 | i = fgetc(stdin); |
1317 | 1332 | if (i == EOF) { | |
1318 | if (i == EOF) { | ||
1319 | #if ENABLE_FEATURE_BC_SIGNALS | 1333 | #if ENABLE_FEATURE_BC_SIGNALS |
1320 | // Both conditions appear simultaneously, check both just in case | 1334 | // Both conditions appear simultaneously, check both just in case |
1321 | if (errno == EINTR || bb_got_signal) { | 1335 | if (errno == EINTR || bb_got_signal) { |
1322 | // ^C was pressed | 1336 | // ^C was pressed |
1323 | clearerr(stdin); | 1337 | clearerr(stdin); |
1324 | goto intr; | 1338 | goto intr; |
1325 | } | 1339 | } |
1326 | #endif | 1340 | #endif |
1327 | if (ferror(stdin)) | 1341 | if (ferror(stdin)) |
1328 | quit(); // this emits error message | 1342 | quit(); // this emits error message |
1329 | G.eof = 1; | 1343 | G.eof = 1; |
1330 | // Note: EOF does not append '\n', therefore: | 1344 | // Note: EOF does not append '\n', therefore: |
1331 | // printf 'print 123\n' | bc - works | 1345 | // printf 'print 123\n' | bc - works |
1332 | // printf 'print 123' | bc - fails (syntax error) | 1346 | // printf 'print 123' | bc - fails (syntax error) |
1333 | break; | 1347 | break; |
1334 | } | 1348 | } |
1335 | 1349 | ||
1336 | c = (signed char) i; | 1350 | if ((i < ' ' && i != '\t' && i != '\r' && i != '\n') // also allow '\v' '\f'? |
1337 | if (i > UCHAR_MAX || BC_READ_BIN_CHAR(c)) return BC_STATUS_BIN_FILE; | 1351 | || i > 0x7e |
1338 | bc_vec_push(vec, &c); | 1352 | ) { |
1339 | } while (c != '\n'); | 1353 | // Bad chars on this line, ignore entire line |
1354 | bc_error("illegal character 0x%02x", i); | ||
1355 | bad_chars = 1; | ||
1356 | } | ||
1357 | c = (char) i; | ||
1358 | bc_vec_push(vec, &c); | ||
1359 | } while (i != '\n'); | ||
1360 | } while (bad_chars); | ||
1340 | 1361 | ||
1341 | bc_vec_pushByte(vec, '\0'); | 1362 | bc_vec_pushByte(vec, '\0'); |
1342 | 1363 | ||
@@ -1352,7 +1373,10 @@ static char* bc_read_file(const char *path) | |||
1352 | buf = xmalloc_open_read_close(path, &size); | 1373 | buf = xmalloc_open_read_close(path, &size); |
1353 | 1374 | ||
1354 | for (i = 0; i < size; ++i) { | 1375 | for (i = 0; i < size; ++i) { |
1355 | if (BC_READ_BIN_CHAR(buf[i])) { | 1376 | char c = buf[i]; |
1377 | if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'? | ||
1378 | || c > 0x7e | ||
1379 | ) { | ||
1356 | free(buf); | 1380 | free(buf); |
1357 | buf = NULL; | 1381 | buf = NULL; |
1358 | break; | 1382 | break; |
@@ -3162,7 +3186,7 @@ static BcStatus bc_lex_token(BcLex *l) | |||
3162 | } | 3186 | } |
3163 | else { | 3187 | else { |
3164 | l->t.t = BC_LEX_INVALID; | 3188 | l->t.t = BC_LEX_INVALID; |
3165 | s = BC_STATUS_LEX_BAD_CHAR; | 3189 | s = bc_error("bad character '%c'", '&'); |
3166 | } | 3190 | } |
3167 | 3191 | ||
3168 | break; | 3192 | break; |
@@ -3291,7 +3315,7 @@ static BcStatus bc_lex_token(BcLex *l) | |||
3291 | ++l->i; | 3315 | ++l->i; |
3292 | } | 3316 | } |
3293 | else | 3317 | else |
3294 | s = BC_STATUS_LEX_BAD_CHAR; | 3318 | s = bc_error("bad character '%c'", c); |
3295 | break; | 3319 | break; |
3296 | } | 3320 | } |
3297 | 3321 | ||
@@ -3353,7 +3377,7 @@ static BcStatus bc_lex_token(BcLex *l) | |||
3353 | } | 3377 | } |
3354 | else { | 3378 | else { |
3355 | l->t.t = BC_LEX_INVALID; | 3379 | l->t.t = BC_LEX_INVALID; |
3356 | s = BC_STATUS_LEX_BAD_CHAR; | 3380 | s = bc_error("bad character '%c'", c); |
3357 | } | 3381 | } |
3358 | 3382 | ||
3359 | break; | 3383 | break; |
@@ -3362,7 +3386,7 @@ static BcStatus bc_lex_token(BcLex *l) | |||
3362 | default: | 3386 | default: |
3363 | { | 3387 | { |
3364 | l->t.t = BC_LEX_INVALID; | 3388 | l->t.t = BC_LEX_INVALID; |
3365 | s = BC_STATUS_LEX_BAD_CHAR; | 3389 | s = bc_error("bad character '%c'", c); |
3366 | break; | 3390 | break; |
3367 | } | 3391 | } |
3368 | } | 3392 | } |
@@ -3473,7 +3497,7 @@ static BcStatus dc_lex_token(BcLex *l) | |||
3473 | else if (c2 == '>') | 3497 | else if (c2 == '>') |
3474 | l->t.t = BC_LEX_OP_REL_GE; | 3498 | l->t.t = BC_LEX_OP_REL_GE; |
3475 | else | 3499 | else |
3476 | return BC_STATUS_LEX_BAD_CHAR; | 3500 | return bc_error("bad character '%c'", c); |
3477 | 3501 | ||
3478 | ++l->i; | 3502 | ++l->i; |
3479 | break; | 3503 | break; |
@@ -3490,7 +3514,7 @@ static BcStatus dc_lex_token(BcLex *l) | |||
3490 | if (isdigit(l->buf[l->i])) | 3514 | if (isdigit(l->buf[l->i])) |
3491 | s = bc_lex_number(l, c); | 3515 | s = bc_lex_number(l, c); |
3492 | else | 3516 | else |
3493 | s = BC_STATUS_LEX_BAD_CHAR; | 3517 | s = bc_error("bad character '%c'", c); |
3494 | break; | 3518 | break; |
3495 | } | 3519 | } |
3496 | 3520 | ||
@@ -3524,7 +3548,7 @@ static BcStatus dc_lex_token(BcLex *l) | |||
3524 | default: | 3548 | default: |
3525 | { | 3549 | { |
3526 | l->t.t = BC_LEX_INVALID; | 3550 | l->t.t = BC_LEX_INVALID; |
3527 | s = BC_STATUS_LEX_BAD_CHAR; | 3551 | s = bc_error("bad character '%c'", c); |
3528 | break; | 3552 | break; |
3529 | } | 3553 | } |
3530 | } | 3554 | } |
@@ -6938,7 +6962,7 @@ static BcStatus bc_vm_file(const char *file) | |||
6938 | 6962 | ||
6939 | G.prog.file = file; | 6963 | G.prog.file = file; |
6940 | data = bc_read_file(file); | 6964 | data = bc_read_file(file); |
6941 | if (!data) return bc_vm_error(BC_STATUS_BIN_FILE, file, 0); | 6965 | if (!data) return bc_error("file '%s' is not text", file); |
6942 | 6966 | ||
6943 | bc_lex_file(&G.prs.l, file); | 6967 | bc_lex_file(&G.prs.l, file); |
6944 | s = bc_vm_process(data); | 6968 | s = bc_vm_process(data); |
@@ -7021,8 +7045,6 @@ static BcStatus bc_vm_stdin(void) | |||
7021 | bc_vec_npop(&buffer, buffer.len); | 7045 | bc_vec_npop(&buffer, buffer.len); |
7022 | } | 7046 | } |
7023 | 7047 | ||
7024 | if (s == BC_STATUS_BIN_FILE) s = bc_vm_error(s, G.prs.l.f, 0); | ||
7025 | |||
7026 | if (str) | 7048 | if (str) |
7027 | s = bc_vm_error(BC_STATUS_LEX_NO_STRING_END, G.prs.l.f, | 7049 | s = bc_vm_error(BC_STATUS_LEX_NO_STRING_END, G.prs.l.f, |
7028 | G.prs.l.line); | 7050 | G.prs.l.line); |