diff options
-rw-r--r-- | miscutils/bc.c | 183 |
1 files changed, 78 insertions, 105 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c index c92f6f813..2570e8313 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c | |||
@@ -694,7 +694,6 @@ struct globals { | |||
694 | IF_FEATURE_BC_SIGNALS(smallint ttyin;) | 694 | IF_FEATURE_BC_SIGNALS(smallint ttyin;) |
695 | IF_FEATURE_CLEAN_UP(smallint exiting;) | 695 | IF_FEATURE_CLEAN_UP(smallint exiting;) |
696 | smallint in_read; | 696 | smallint in_read; |
697 | smallint use_stdin; | ||
698 | 697 | ||
699 | BcParse prs; | 698 | BcParse prs; |
700 | BcProgram prog; | 699 | BcProgram prog; |
@@ -704,7 +703,8 @@ struct globals { | |||
704 | unsigned err_line; | 703 | unsigned err_line; |
705 | 704 | ||
706 | BcVec files; | 705 | BcVec files; |
707 | BcVec stdin_buffer; | 706 | BcVec input_buffer; |
707 | FILE *input_fp; | ||
708 | 708 | ||
709 | char *env_args; | 709 | char *env_args; |
710 | 710 | ||
@@ -1317,8 +1317,8 @@ static int bad_input_byte(char c) | |||
1317 | return 0; | 1317 | return 0; |
1318 | } | 1318 | } |
1319 | 1319 | ||
1320 | // Note: it _appends_ data from the stdin to vec. | 1320 | // Note: it _appends_ data from fp to vec. |
1321 | static void bc_read_line(BcVec *vec) | 1321 | static void bc_read_line(BcVec *vec, FILE *fp) |
1322 | { | 1322 | { |
1323 | again: | 1323 | again: |
1324 | fflush_and_check(); | 1324 | fflush_and_check(); |
@@ -1326,6 +1326,17 @@ static void bc_read_line(BcVec *vec) | |||
1326 | #if ENABLE_FEATURE_BC_SIGNALS | 1326 | #if ENABLE_FEATURE_BC_SIGNALS |
1327 | if (G_interrupt) { // ^C was pressed | 1327 | if (G_interrupt) { // ^C was pressed |
1328 | intr: | 1328 | intr: |
1329 | if (fp != stdin) { | ||
1330 | // ^C while running a script (bc SCRIPT): die. | ||
1331 | // We do not return to interactive prompt: | ||
1332 | // user might be running us from a shell, | ||
1333 | // and SCRIPT might be intended to terminate | ||
1334 | // (e.g. contain a "halt" stmt). | ||
1335 | // ^C dropping user into a bc prompt instead of | ||
1336 | // the shell would be unexpected. | ||
1337 | xfunc_die(); | ||
1338 | } | ||
1339 | // ^C while interactive input | ||
1329 | G_interrupt = 0; | 1340 | G_interrupt = 0; |
1330 | // GNU bc says "interrupted execution." | 1341 | // GNU bc says "interrupted execution." |
1331 | // GNU dc says "Interrupt!" | 1342 | // GNU dc says "Interrupt!" |
@@ -1333,14 +1344,14 @@ static void bc_read_line(BcVec *vec) | |||
1333 | } | 1344 | } |
1334 | 1345 | ||
1335 | # if ENABLE_FEATURE_EDITING | 1346 | # if ENABLE_FEATURE_EDITING |
1336 | if (G_ttyin) { | 1347 | if (G_ttyin && fp == stdin) { |
1337 | int n, i; | 1348 | int n, i; |
1338 | # define line_buf bb_common_bufsiz1 | 1349 | # define line_buf bb_common_bufsiz1 |
1339 | n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE); | 1350 | n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE); |
1340 | if (n <= 0) { // read errors or EOF, or ^D, or ^C | 1351 | if (n <= 0) { // read errors or EOF, or ^D, or ^C |
1341 | if (n == 0) // ^C | 1352 | if (n == 0) // ^C |
1342 | goto intr; | 1353 | goto intr; |
1343 | bc_vec_pushZeroByte(vec); | 1354 | bc_vec_pushZeroByte(vec); // ^D or EOF (or error) |
1344 | return; | 1355 | return; |
1345 | } | 1356 | } |
1346 | i = 0; | 1357 | i = 0; |
@@ -1359,60 +1370,38 @@ static void bc_read_line(BcVec *vec) | |||
1359 | bool bad_chars = 0; | 1370 | bool bad_chars = 0; |
1360 | size_t len = vec->len; | 1371 | size_t len = vec->len; |
1361 | 1372 | ||
1362 | IF_FEATURE_BC_SIGNALS(errno = 0;) | ||
1363 | do { | 1373 | do { |
1364 | c = fgetc(stdin); | 1374 | #if ENABLE_FEATURE_BC_SIGNALS |
1365 | #if ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING | 1375 | if (G_interrupt) { |
1366 | // Both conditions appear simultaneously, check both just in case | 1376 | // ^C was pressed: ignore entire line, get another one |
1367 | if (errno == EINTR || G_interrupt) { | 1377 | vec->len = len; |
1368 | // ^C was pressed | ||
1369 | clearerr(stdin); | ||
1370 | goto intr; | 1378 | goto intr; |
1371 | } | 1379 | } |
1372 | #endif | 1380 | #endif |
1381 | c = fgetc(fp); | ||
1373 | if (c == EOF) { | 1382 | if (c == EOF) { |
1374 | if (ferror(stdin)) | 1383 | if (ferror(fp)) |
1375 | quit(); // this emits error message | 1384 | bb_perror_msg_and_die("input error"); |
1376 | // Note: EOF does not append '\n', therefore: | 1385 | // Note: EOF does not append '\n' |
1377 | // printf 'print 123\n' | bc - works | ||
1378 | // printf 'print 123' | bc - fails (syntax error) | ||
1379 | break; | 1386 | break; |
1380 | } | 1387 | } |
1381 | bad_chars |= bad_input_byte(c); | 1388 | bad_chars |= bad_input_byte(c); |
1382 | bc_vec_pushByte(vec, (char)c); | 1389 | bc_vec_pushByte(vec, (char)c); |
1383 | } while (c != '\n'); | 1390 | } while (c != '\n'); |
1391 | |||
1384 | if (bad_chars) { | 1392 | if (bad_chars) { |
1385 | // Bad chars on this line, ignore entire line | 1393 | // Bad chars on this line |
1386 | vec->len = len; | 1394 | if (!G.prog.file) { // stdin |
1387 | goto again; | 1395 | // ignore entire line, get another one |
1396 | vec->len = len; | ||
1397 | goto again; | ||
1398 | } | ||
1399 | bb_perror_msg_and_die("file '%s' is not text", G.prog.file); | ||
1388 | } | 1400 | } |
1389 | bc_vec_pushZeroByte(vec); | 1401 | bc_vec_pushZeroByte(vec); |
1390 | } | 1402 | } |
1391 | } | 1403 | } |
1392 | 1404 | ||
1393 | static char* bc_read_file(const char *path) | ||
1394 | { | ||
1395 | char *buf; | ||
1396 | size_t size = ((size_t) -1); | ||
1397 | size_t i; | ||
1398 | |||
1399 | // Never returns NULL (dies on errors) | ||
1400 | buf = xmalloc_xopen_read_close(path, &size); | ||
1401 | |||
1402 | for (i = 0; i < size; ++i) { | ||
1403 | char c = buf[i]; | ||
1404 | if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'? | ||
1405 | || c > 0x7e | ||
1406 | ) { | ||
1407 | free(buf); | ||
1408 | buf = NULL; | ||
1409 | break; | ||
1410 | } | ||
1411 | } | ||
1412 | |||
1413 | return buf; | ||
1414 | } | ||
1415 | |||
1416 | static void bc_num_setToZero(BcNum *n, size_t scale) | 1405 | static void bc_num_setToZero(BcNum *n, size_t scale) |
1417 | { | 1406 | { |
1418 | n->len = 0; | 1407 | n->len = 0; |
@@ -2912,7 +2901,7 @@ static bool bc_lex_more_input(BcLex *l) | |||
2912 | size_t str; | 2901 | size_t str; |
2913 | bool comment; | 2902 | bool comment; |
2914 | 2903 | ||
2915 | bc_vec_pop_all(&G.stdin_buffer); | 2904 | bc_vec_pop_all(&G.input_buffer); |
2916 | 2905 | ||
2917 | // This loop is complex because the vm tries not to send any lines that end | 2906 | // This loop is complex because the vm tries not to send any lines that end |
2918 | // with a backslash to the parser. The reason for that is because the parser | 2907 | // with a backslash to the parser. The reason for that is because the parser |
@@ -2921,18 +2910,18 @@ static bool bc_lex_more_input(BcLex *l) | |||
2921 | comment = false; | 2910 | comment = false; |
2922 | str = 0; | 2911 | str = 0; |
2923 | for (;;) { | 2912 | for (;;) { |
2924 | size_t prevlen = G.stdin_buffer.len; | 2913 | size_t prevlen = G.input_buffer.len; |
2925 | char *string; | 2914 | char *string; |
2926 | 2915 | ||
2927 | bc_read_line(&G.stdin_buffer); | 2916 | bc_read_line(&G.input_buffer, G.input_fp); |
2928 | // No more input means EOF | 2917 | // No more input means EOF |
2929 | if (G.stdin_buffer.len <= prevlen + 1) // (we expect +1 for NUL byte) | 2918 | if (G.input_buffer.len <= prevlen + 1) // (we expect +1 for NUL byte) |
2930 | break; | 2919 | break; |
2931 | 2920 | ||
2932 | string = G.stdin_buffer.v + prevlen; | 2921 | string = G.input_buffer.v + prevlen; |
2933 | while (*string) { | 2922 | while (*string) { |
2934 | char c = *string; | 2923 | char c = *string; |
2935 | if (string == G.stdin_buffer.v || string[-1] != '\\') { | 2924 | if (string == G.input_buffer.v || string[-1] != '\\') { |
2936 | if (IS_BC) | 2925 | if (IS_BC) |
2937 | str ^= (c == '"'); | 2926 | str ^= (c == '"'); |
2938 | else { | 2927 | else { |
@@ -2954,7 +2943,7 @@ static bool bc_lex_more_input(BcLex *l) | |||
2954 | } | 2943 | } |
2955 | } | 2944 | } |
2956 | if (str != 0 || comment) { | 2945 | if (str != 0 || comment) { |
2957 | G.stdin_buffer.len--; // backstep over the trailing NUL byte | 2946 | G.input_buffer.len--; // backstep over the trailing NUL byte |
2958 | continue; | 2947 | continue; |
2959 | } | 2948 | } |
2960 | 2949 | ||
@@ -2963,21 +2952,20 @@ static bool bc_lex_more_input(BcLex *l) | |||
2963 | // if it is not, then it's EOF, and looping back | 2952 | // if it is not, then it's EOF, and looping back |
2964 | // to bc_read_line() will detect it: | 2953 | // to bc_read_line() will detect it: |
2965 | string -= 2; | 2954 | string -= 2; |
2966 | if (string >= G.stdin_buffer.v && *string == '\\') { | 2955 | if (string >= G.input_buffer.v && *string == '\\') { |
2967 | G.stdin_buffer.len--; | 2956 | G.input_buffer.len--; |
2968 | continue; | 2957 | continue; |
2969 | } | 2958 | } |
2970 | 2959 | ||
2971 | break; | 2960 | break; |
2972 | } | 2961 | } |
2973 | 2962 | ||
2974 | l->buf = G.stdin_buffer.v; | 2963 | l->buf = G.input_buffer.v; |
2975 | l->i = 0; | 2964 | l->i = 0; |
2976 | //bb_error_msg("G.stdin_buffer.len:%d '%s'", G.stdin_buffer.len, G.stdin_buffer.v); | 2965 | // bb_error_msg("G.input_buffer.len:%d '%s'", G.input_buffer.len, G.input_buffer.v); |
2977 | l->len = G.stdin_buffer.len - 1; // do not include NUL | 2966 | l->len = G.input_buffer.len - 1; // do not include NUL |
2978 | 2967 | ||
2979 | G.use_stdin = (l->len != 0); | 2968 | return l->len != 0; |
2980 | return G.use_stdin; | ||
2981 | } | 2969 | } |
2982 | 2970 | ||
2983 | static BC_STATUS zbc_lex_next(BcLex *l) | 2971 | static BC_STATUS zbc_lex_next(BcLex *l) |
@@ -2989,22 +2977,23 @@ static BC_STATUS zbc_lex_next(BcLex *l) | |||
2989 | 2977 | ||
2990 | l->line += l->newline; | 2978 | l->line += l->newline; |
2991 | G.err_line = l->line; | 2979 | G.err_line = l->line; |
2992 | 2980 | l->newline = false; | |
2993 | l->t.t = BC_LEX_EOF; | ||
2994 | //this NL handling is bogus | ||
2995 | l->newline = (l->i == l->len); | ||
2996 | if (l->newline) { | ||
2997 | if (!G.use_stdin || !bc_lex_more_input(l)) | ||
2998 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2999 | // here it's guaranteed that l->i is below l->len | ||
3000 | l->newline = false; | ||
3001 | } | ||
3002 | 2981 | ||
3003 | // Loop until failure or we don't have whitespace. This | 2982 | // Loop until failure or we don't have whitespace. This |
3004 | // is so the parser doesn't get inundated with whitespace. | 2983 | // is so the parser doesn't get inundated with whitespace. |
3005 | // Comments are also BC_LEX_WHITESPACE tokens and eaten here. | 2984 | // Comments are also BC_LEX_WHITESPACE tokens and eaten here. |
3006 | s = BC_STATUS_SUCCESS; | 2985 | s = BC_STATUS_SUCCESS; |
3007 | do { | 2986 | do { |
2987 | l->t.t = BC_LEX_EOF; | ||
2988 | if (l->i == l->len) { | ||
2989 | if (!G.input_fp) | ||
2990 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2991 | if (!bc_lex_more_input(l)) { | ||
2992 | G.input_fp = NULL; | ||
2993 | RETURN_STATUS(BC_STATUS_SUCCESS); | ||
2994 | } | ||
2995 | // here it's guaranteed that l->i is below l->len | ||
2996 | } | ||
3008 | dbg_lex("next string to parse:'%.*s'", | 2997 | dbg_lex("next string to parse:'%.*s'", |
3009 | (int)(strchrnul(l->buf + l->i, '\n') - (l->buf + l->i)), | 2998 | (int)(strchrnul(l->buf + l->i, '\n') - (l->buf + l->i)), |
3010 | l->buf + l->i); | 2999 | l->buf + l->i); |
@@ -5351,7 +5340,7 @@ static BC_STATUS zbc_program_read(void) | |||
5351 | G.in_read = 1; | 5340 | G.in_read = 1; |
5352 | 5341 | ||
5353 | bc_char_vec_init(&buf); | 5342 | bc_char_vec_init(&buf); |
5354 | bc_read_line(&buf); | 5343 | bc_read_line(&buf, stdin); |
5355 | 5344 | ||
5356 | bc_parse_create(&parse, BC_PROG_READ); | 5345 | bc_parse_create(&parse, BC_PROG_READ); |
5357 | bc_lex_file(&parse.l); | 5346 | bc_lex_file(&parse.l); |
@@ -6931,60 +6920,44 @@ static BC_STATUS zbc_vm_process(const char *text) | |||
6931 | # define zbc_vm_process(...) (zbc_vm_process(__VA_ARGS__), BC_STATUS_SUCCESS) | 6920 | # define zbc_vm_process(...) (zbc_vm_process(__VA_ARGS__), BC_STATUS_SUCCESS) |
6932 | #endif | 6921 | #endif |
6933 | 6922 | ||
6934 | static BC_STATUS zbc_vm_file(const char *file) | 6923 | static BC_STATUS zbc_vm_execute_FILE(FILE *fp, const char *filename) |
6935 | { | 6924 | { |
6936 | // So far bc/dc have no way to include a file from another file, | 6925 | // So far bc/dc have no way to include a file from another file, |
6937 | // therefore we know G.prog.file == NULL on entry | 6926 | // therefore we know G.prog.file == NULL on entry |
6938 | //const char *sv_file; | 6927 | //const char *sv_file; |
6939 | char *data; | ||
6940 | BcStatus s; | 6928 | BcStatus s; |
6941 | BcFunc *main_func; | ||
6942 | BcInstPtr *ip; | ||
6943 | |||
6944 | data = bc_read_file(file); | ||
6945 | if (!data) RETURN_STATUS(bc_error_fmt("file '%s' is not text", file)); | ||
6946 | 6929 | ||
6947 | //sv_file = G.prog.file; | 6930 | G.prog.file = filename; |
6948 | G.prog.file = file; | 6931 | G.input_fp = fp; |
6949 | bc_lex_file(&G.prs.l); | 6932 | bc_lex_file(&G.prs.l); |
6950 | s = zbc_vm_process(data); | ||
6951 | if (s) goto err; | ||
6952 | |||
6953 | main_func = bc_program_func(BC_PROG_MAIN); | ||
6954 | ip = bc_vec_item(&G.prog.stack, 0); | ||
6955 | |||
6956 | if (main_func->code.len < ip->idx) | ||
6957 | s = bc_error_fmt("file '%s' is not executable", file); | ||
6958 | 6933 | ||
6959 | err: | 6934 | do { |
6960 | //G.prog.file = sv_file; | 6935 | s = zbc_vm_process(""); |
6936 | // We do not stop looping on errors here if reading stdin. | ||
6937 | // Example: start interactive bc and enter "return". | ||
6938 | // It should say "'return' not in a function" | ||
6939 | // but should not exit. | ||
6940 | } while (G.input_fp == stdin); | ||
6961 | G.prog.file = NULL; | 6941 | G.prog.file = NULL; |
6962 | free(data); | ||
6963 | RETURN_STATUS(s); | 6942 | RETURN_STATUS(s); |
6964 | } | 6943 | } |
6965 | #if ERRORS_ARE_FATAL | 6944 | #if ERRORS_ARE_FATAL |
6966 | # define zbc_vm_file(...) (zbc_vm_file(__VA_ARGS__), BC_STATUS_SUCCESS) | 6945 | # define zbc_vm_execute_FILE(...) (zbc_vm_execute_FILE(__VA_ARGS__), BC_STATUS_SUCCESS) |
6967 | #endif | 6946 | #endif |
6968 | 6947 | ||
6969 | static BC_STATUS zbc_vm_stdin(void) | 6948 | static BC_STATUS zbc_vm_file(const char *file) |
6970 | { | 6949 | { |
6971 | BcStatus s; | 6950 | BcStatus s; |
6951 | FILE *fp; | ||
6972 | 6952 | ||
6973 | //G.prog.file = NULL; - already is | 6953 | fp = xfopen_for_read(file); |
6974 | bc_lex_file(&G.prs.l); | 6954 | s = zbc_vm_execute_FILE(fp, file); |
6955 | fclose(fp); | ||
6975 | 6956 | ||
6976 | G.use_stdin = 1; | ||
6977 | do { | ||
6978 | s = zbc_vm_process(""); | ||
6979 | // We do not stop looping on errors here. | ||
6980 | // Example: start interactive bc and enter "return". | ||
6981 | // It should say "'return' not in a function" | ||
6982 | // but should not exit. | ||
6983 | } while (G.use_stdin); | ||
6984 | RETURN_STATUS(s); | 6957 | RETURN_STATUS(s); |
6985 | } | 6958 | } |
6986 | #if ERRORS_ARE_FATAL | 6959 | #if ERRORS_ARE_FATAL |
6987 | # define zbc_vm_stdin(...) (zbc_vm_stdin(__VA_ARGS__), BC_STATUS_SUCCESS) | 6960 | # define zbc_vm_file(...) (zbc_vm_file(__VA_ARGS__), BC_STATUS_SUCCESS) |
6988 | #endif | 6961 | #endif |
6989 | 6962 | ||
6990 | #if ENABLE_BC | 6963 | #if ENABLE_BC |
@@ -7257,7 +7230,7 @@ static BC_STATUS zbc_vm_exec(void) | |||
7257 | } | 7230 | } |
7258 | 7231 | ||
7259 | if (IS_BC || (option_mask32 & BC_FLAG_I)) | 7232 | if (IS_BC || (option_mask32 & BC_FLAG_I)) |
7260 | s = zbc_vm_stdin(); | 7233 | s = zbc_vm_execute_FILE(stdin, /*filename:*/ NULL); |
7261 | 7234 | ||
7262 | RETURN_STATUS(s); | 7235 | RETURN_STATUS(s); |
7263 | } | 7236 | } |
@@ -7287,7 +7260,7 @@ static void bc_program_free(void) | |||
7287 | bc_num_free(&G.prog.last); | 7260 | bc_num_free(&G.prog.last); |
7288 | bc_num_free(&G.prog.zero); | 7261 | bc_num_free(&G.prog.zero); |
7289 | bc_num_free(&G.prog.one); | 7262 | bc_num_free(&G.prog.one); |
7290 | bc_vec_free(&G.stdin_buffer); | 7263 | bc_vec_free(&G.input_buffer); |
7291 | } | 7264 | } |
7292 | 7265 | ||
7293 | static void bc_vm_free(void) | 7266 | static void bc_vm_free(void) |
@@ -7352,7 +7325,7 @@ static void bc_program_init(void) | |||
7352 | bc_vec_init(&G.prog.stack, sizeof(BcInstPtr), NULL); | 7325 | bc_vec_init(&G.prog.stack, sizeof(BcInstPtr), NULL); |
7353 | bc_vec_push(&G.prog.stack, &ip); | 7326 | bc_vec_push(&G.prog.stack, &ip); |
7354 | 7327 | ||
7355 | bc_char_vec_init(&G.stdin_buffer); | 7328 | bc_char_vec_init(&G.input_buffer); |
7356 | } | 7329 | } |
7357 | 7330 | ||
7358 | static int bc_vm_init(const char *env_len) | 7331 | static int bc_vm_init(const char *env_len) |