IF_FEATURE_BC_SIGNALS(smallint ttyin;)
IF_FEATURE_CLEAN_UP(smallint exiting;)
smallint in_read;
- smallint use_stdin;
BcParse prs;
BcProgram prog;
unsigned err_line;
BcVec files;
- BcVec stdin_buffer;
+ BcVec input_buffer;
+ FILE *input_fp;
char *env_args;
return 0;
}
-// Note: it _appends_ data from the stdin to vec.
-static void bc_read_line(BcVec *vec)
+// Note: it _appends_ data from fp to vec.
+static void bc_read_line(BcVec *vec, FILE *fp)
{
again:
fflush_and_check();
#if ENABLE_FEATURE_BC_SIGNALS
if (G_interrupt) { // ^C was pressed
intr:
+ if (fp != stdin) {
+ // ^C while running a script (bc SCRIPT): die.
+ // We do not return to interactive prompt:
+ // user might be running us from a shell,
+ // and SCRIPT might be intended to terminate
+ // (e.g. contain a "halt" stmt).
+ // ^C dropping user into a bc prompt instead of
+ // the shell would be unexpected.
+ xfunc_die();
+ }
+ // ^C while interactive input
G_interrupt = 0;
// GNU bc says "interrupted execution."
// GNU dc says "Interrupt!"
}
# if ENABLE_FEATURE_EDITING
- if (G_ttyin) {
+ if (G_ttyin && fp == stdin) {
int n, i;
# define line_buf bb_common_bufsiz1
n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE);
if (n <= 0) { // read errors or EOF, or ^D, or ^C
if (n == 0) // ^C
goto intr;
- bc_vec_pushZeroByte(vec);
+ bc_vec_pushZeroByte(vec); // ^D or EOF (or error)
return;
}
i = 0;
bool bad_chars = 0;
size_t len = vec->len;
- IF_FEATURE_BC_SIGNALS(errno = 0;)
do {
- c = fgetc(stdin);
-#if ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING
- // Both conditions appear simultaneously, check both just in case
- if (errno == EINTR || G_interrupt) {
- // ^C was pressed
- clearerr(stdin);
+#if ENABLE_FEATURE_BC_SIGNALS
+ if (G_interrupt) {
+ // ^C was pressed: ignore entire line, get another one
+ vec->len = len;
goto intr;
}
#endif
+ c = fgetc(fp);
if (c == EOF) {
- if (ferror(stdin))
- quit(); // this emits error message
- // Note: EOF does not append '\n', therefore:
- // printf 'print 123\n' | bc - works
- // printf 'print 123' | bc - fails (syntax error)
+ if (ferror(fp))
+ bb_perror_msg_and_die("input error");
+ // Note: EOF does not append '\n'
break;
}
bad_chars |= bad_input_byte(c);
bc_vec_pushByte(vec, (char)c);
} while (c != '\n');
+
if (bad_chars) {
- // Bad chars on this line, ignore entire line
- vec->len = len;
- goto again;
+ // Bad chars on this line
+ if (!G.prog.file) { // stdin
+ // ignore entire line, get another one
+ vec->len = len;
+ goto again;
+ }
+ bb_perror_msg_and_die("file '%s' is not text", G.prog.file);
}
bc_vec_pushZeroByte(vec);
}
}
-static char* bc_read_file(const char *path)
-{
- char *buf;
- size_t size = ((size_t) -1);
- size_t i;
-
- // Never returns NULL (dies on errors)
- buf = xmalloc_xopen_read_close(path, &size);
-
- for (i = 0; i < size; ++i) {
- char c = buf[i];
- if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
- || c > 0x7e
- ) {
- free(buf);
- buf = NULL;
- break;
- }
- }
-
- return buf;
-}
-
static void bc_num_setToZero(BcNum *n, size_t scale)
{
n->len = 0;
size_t str;
bool comment;
- bc_vec_pop_all(&G.stdin_buffer);
+ bc_vec_pop_all(&G.input_buffer);
// This loop is complex because the vm tries not to send any lines that end
// with a backslash to the parser. The reason for that is because the parser
comment = false;
str = 0;
for (;;) {
- size_t prevlen = G.stdin_buffer.len;
+ size_t prevlen = G.input_buffer.len;
char *string;
- bc_read_line(&G.stdin_buffer);
+ bc_read_line(&G.input_buffer, G.input_fp);
// No more input means EOF
- if (G.stdin_buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
+ if (G.input_buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
break;
- string = G.stdin_buffer.v + prevlen;
+ string = G.input_buffer.v + prevlen;
while (*string) {
char c = *string;
- if (string == G.stdin_buffer.v || string[-1] != '\\') {
+ if (string == G.input_buffer.v || string[-1] != '\\') {
if (IS_BC)
str ^= (c == '"');
else {
}
}
if (str != 0 || comment) {
- G.stdin_buffer.len--; // backstep over the trailing NUL byte
+ G.input_buffer.len--; // backstep over the trailing NUL byte
continue;
}
// if it is not, then it's EOF, and looping back
// to bc_read_line() will detect it:
string -= 2;
- if (string >= G.stdin_buffer.v && *string == '\\') {
- G.stdin_buffer.len--;
+ if (string >= G.input_buffer.v && *string == '\\') {
+ G.input_buffer.len--;
continue;
}
break;
}
- l->buf = G.stdin_buffer.v;
+ l->buf = G.input_buffer.v;
l->i = 0;
-//bb_error_msg("G.stdin_buffer.len:%d '%s'", G.stdin_buffer.len, G.stdin_buffer.v);
- l->len = G.stdin_buffer.len - 1; // do not include NUL
+// bb_error_msg("G.input_buffer.len:%d '%s'", G.input_buffer.len, G.input_buffer.v);
+ l->len = G.input_buffer.len - 1; // do not include NUL
- G.use_stdin = (l->len != 0);
- return G.use_stdin;
+ return l->len != 0;
}
static BC_STATUS zbc_lex_next(BcLex *l)
l->line += l->newline;
G.err_line = l->line;
-
- l->t.t = BC_LEX_EOF;
-//this NL handling is bogus
- l->newline = (l->i == l->len);
- if (l->newline) {
- if (!G.use_stdin || !bc_lex_more_input(l))
- RETURN_STATUS(BC_STATUS_SUCCESS);
- // here it's guaranteed that l->i is below l->len
- l->newline = false;
- }
+ l->newline = false;
// Loop until failure or we don't have whitespace. This
// is so the parser doesn't get inundated with whitespace.
// Comments are also BC_LEX_WHITESPACE tokens and eaten here.
s = BC_STATUS_SUCCESS;
do {
+ l->t.t = BC_LEX_EOF;
+ if (l->i == l->len) {
+ if (!G.input_fp)
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+ if (!bc_lex_more_input(l)) {
+ G.input_fp = NULL;
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+ }
+ // here it's guaranteed that l->i is below l->len
+ }
dbg_lex("next string to parse:'%.*s'",
(int)(strchrnul(l->buf + l->i, '\n') - (l->buf + l->i)),
l->buf + l->i);
G.in_read = 1;
bc_char_vec_init(&buf);
- bc_read_line(&buf);
+ bc_read_line(&buf, stdin);
bc_parse_create(&parse, BC_PROG_READ);
bc_lex_file(&parse.l);
# define zbc_vm_process(...) (zbc_vm_process(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
-static BC_STATUS zbc_vm_file(const char *file)
+static BC_STATUS zbc_vm_execute_FILE(FILE *fp, const char *filename)
{
// So far bc/dc have no way to include a file from another file,
// therefore we know G.prog.file == NULL on entry
//const char *sv_file;
- char *data;
BcStatus s;
- BcFunc *main_func;
- BcInstPtr *ip;
-
- data = bc_read_file(file);
- if (!data) RETURN_STATUS(bc_error_fmt("file '%s' is not text", file));
- //sv_file = G.prog.file;
- G.prog.file = file;
+ G.prog.file = filename;
+ G.input_fp = fp;
bc_lex_file(&G.prs.l);
- s = zbc_vm_process(data);
- if (s) goto err;
-
- main_func = bc_program_func(BC_PROG_MAIN);
- ip = bc_vec_item(&G.prog.stack, 0);
-
- if (main_func->code.len < ip->idx)
- s = bc_error_fmt("file '%s' is not executable", file);
-err:
- //G.prog.file = sv_file;
+ do {
+ s = zbc_vm_process("");
+ // We do not stop looping on errors here if reading stdin.
+ // Example: start interactive bc and enter "return".
+ // It should say "'return' not in a function"
+ // but should not exit.
+ } while (G.input_fp == stdin);
G.prog.file = NULL;
- free(data);
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
-# define zbc_vm_file(...) (zbc_vm_file(__VA_ARGS__), BC_STATUS_SUCCESS)
+# define zbc_vm_execute_FILE(...) (zbc_vm_execute_FILE(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
-static BC_STATUS zbc_vm_stdin(void)
+static BC_STATUS zbc_vm_file(const char *file)
{
BcStatus s;
+ FILE *fp;
- //G.prog.file = NULL; - already is
- bc_lex_file(&G.prs.l);
+ fp = xfopen_for_read(file);
+ s = zbc_vm_execute_FILE(fp, file);
+ fclose(fp);
- G.use_stdin = 1;
- do {
- s = zbc_vm_process("");
- // We do not stop looping on errors here.
- // Example: start interactive bc and enter "return".
- // It should say "'return' not in a function"
- // but should not exit.
- } while (G.use_stdin);
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
-# define zbc_vm_stdin(...) (zbc_vm_stdin(__VA_ARGS__), BC_STATUS_SUCCESS)
+# define zbc_vm_file(...) (zbc_vm_file(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
#if ENABLE_BC
}
if (IS_BC || (option_mask32 & BC_FLAG_I))
- s = zbc_vm_stdin();
+ s = zbc_vm_execute_FILE(stdin, /*filename:*/ NULL);
RETURN_STATUS(s);
}
bc_num_free(&G.prog.last);
bc_num_free(&G.prog.zero);
bc_num_free(&G.prog.one);
- bc_vec_free(&G.stdin_buffer);
+ bc_vec_free(&G.input_buffer);
}
static void bc_vm_free(void)
bc_vec_init(&G.prog.stack, sizeof(BcInstPtr), NULL);
bc_vec_push(&G.prog.stack, &ip);
- bc_char_vec_init(&G.stdin_buffer);
+ bc_char_vec_init(&G.input_buffer);
}
static int bc_vm_init(const char *env_len)