blob: d870072bef6022301df01a1296e660f676b8587a [file] [log] [blame]
/*
* *****************************************************************************
*
* Copyright 2018 Gavin D. Howard
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* *****************************************************************************
*
* Code common to all of bc.
*
*/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <status.h>
#include <io.h>
#include <bc.h>
void bc_sig(int sig) {
if (sig == SIGINT) {
if (write(2, bc_sig_msg, sizeof(bc_sig_msg) - 1) >= 0)
bcg.sig_int += bcg.sig_int == bcg.sig_int_catches;
}
else bcg.sig_other = 1;
}
BcStatus bc_error(BcStatus st) {
if (!st || st >= BC_STATUS_POSIX_NAME_LEN) return BC_STATUS_SUCCESS;
fprintf(stderr, "\n%s error: %s\n\n", bc_err_types[bc_err_type_indices[st]],
bc_err_descs[st]);
return st * !bcg.interactive;
}
BcStatus bc_error_file(BcStatus st, const char *file, size_t line) {
if (!st || !file || st >= BC_STATUS_POSIX_NAME_LEN) return BC_STATUS_SUCCESS;
fprintf(stderr, "\n%s error: %s\n", bc_err_types[bc_err_type_indices[st]],
bc_err_descs[st]);
fprintf(stderr, " %s", file);
fprintf(stderr, &":%d\n\n"[3 * !line], line);
return st * !bcg.interactive;
}
BcStatus bc_posix_error(BcStatus st, const char *file,
size_t line, const char *msg)
{
int s = bcg.std, w = bcg.warn;
if (!(s || w) || st < BC_STATUS_POSIX_NAME_LEN || !file)
return BC_STATUS_SUCCESS;
fprintf(stderr, "\n%s %s: %s\n", bc_err_types[bc_err_type_indices[st]],
s ? "error" : "warning", bc_err_descs[st]);
if (msg) fprintf(stderr, " %s\n", msg);
fprintf(stderr, " %s", file);
fprintf(stderr, &":%d\n\n"[3 * !line], line);
return st * !!s;
}
BcStatus bc_process(Bc *bc, const char *text) {
BcStatus st = bc_lex_text(&bc->parse.lex, text);
if (st && (st = bc_error_file(st, bc->parse.lex.file, bc->parse.lex.line)))
return st;
while (bc->parse.lex.token.type != BC_LEX_EOF) {
st = bc_parse_parse(&bc->parse);
if (st == BC_STATUS_LIMITS) {
st = BC_STATUS_IO_ERR;
if (putchar('\n') == EOF) return st;
if (printf("BC_BASE_MAX = %zu\n", (size_t) BC_MAX_BASE) < 0 ||
printf("BC_DIM_MAX = %zu\n", (size_t) BC_MAX_DIM) < 0 ||
printf("BC_SCALE_MAX = %zu\n", (size_t) BC_MAX_SCALE) < 0 ||
printf("BC_STRING_MAX = %zu\n", (size_t) BC_MAX_STRING) < 0 ||
printf("Max Exponent = %ld\n", (long) LONG_MAX) < 0 ||
printf("Number of Vars = %zu\n", (size_t) SIZE_MAX) < 0)
{
return st;
}
if (putchar('\n') == EOF) return st;
}
else if (st == BC_STATUS_QUIT || bcg.sig_other ||
(st && (st = bc_error_file(st, bc->parse.lex.file, bc->parse.lex.line))))
{
return st;
}
}
if (BC_PARSE_CAN_EXEC(&bc->parse)) {
st = bc_program_exec(&bc->prog);
if (bcg.interactive) fflush(stdout);
if (st && st != BC_STATUS_QUIT) st = bc_error(st);
}
return st;
}
BcStatus bc_file(Bc *bc, const char *file) {
BcStatus st;
char *data;
BcFunc *main_func;
BcInstPtr *ip;
bc->prog.file = file;
if ((st = bc_io_fread(file, &data))) return st;
bc_lex_init(&bc->parse.lex, file);
if ((st = bc_process(bc, data))) goto err;
main_func = bc_vec_item(&bc->prog.funcs, BC_PROGRAM_MAIN);
ip = bc_vec_item(&bc->prog.stack, 0);
assert(main_func && ip);
if (main_func->code.len > ip->idx) st = BC_STATUS_EXEC_FILE_NOT_EXECUTABLE;
err:
free(data);
return st;
}
BcStatus bc_concat(char **buffer, size_t *n, char *buf, size_t total_len) {
char *temp;
if (total_len > *n) {
if (!(temp = realloc(*buffer, total_len + 1))) return BC_STATUS_MALLOC_FAIL;
*buffer = temp;
*n = total_len;
}
strcat(*buffer, buf);
return BC_STATUS_SUCCESS;
}
BcStatus bc_stdin(Bc *bc) {
BcStatus st;
char *buf, *buffer;
size_t n, bufn, slen, total_len;
bool string, comment;
bc->prog.file = bc_program_stdin_name;
bc_lex_init(&bc->parse.lex, bc_program_stdin_name);
n = BC_BUF_SIZE;
bufn = BC_BUF_SIZE;
buffer = malloc(BC_BUF_SIZE + 1);
if (!buffer) return BC_STATUS_MALLOC_FAIL;
buffer[0] = '\0';
buf = malloc(BC_BUF_SIZE + 1);
if (!buf) {
st = BC_STATUS_MALLOC_FAIL;
goto buf_err;
}
string = comment = false;
st = BC_STATUS_SUCCESS;
// The following loop is complicated 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 treats
// a backslash newline combo as whitespace, per the bc spec.
// Thus, the parser will expect more stuff. That is also
// the case with strings and comments.
while ((!st || (st != BC_STATUS_QUIT)) &&
!(st = bc_io_getline(&buf, &bufn, stdin)))
{
size_t len, i;
len = strlen(buf);
slen = strlen(buffer);
total_len = slen + len;
if (len == 1 && buf[0] == '"') string = !string;
else if (len > 1 || comment) {
for (i = 0; i < len; ++i) {
char c;
bool notend;
notend = len > i + 1;
c = buf[i];
if (c == '"') string = !string;
else if (c == '/' && notend && !comment && buf[i + 1] == '*') {
comment = true;
break;
}
else if (c == '*' && notend && comment && buf[i + 1] == '/')
comment = false;
}
if (string || comment || buf[len - 2] == '\\') {
if ((st = bc_concat(&buffer, &n, buf, total_len))) goto exit_err;
continue;
}
}
if ((st = bc_concat(&buffer, &n, buf, total_len))) goto exit_err;
st = bc_process(bc, buffer);
buffer[0] = '\0';
}
// I/O error will always happen when stdin is
// closed. It's not a problem in that case.
st = st == BC_STATUS_IO_ERR ? BC_STATUS_SUCCESS : st;
exit_err:
free(buf);
buf_err:
free(buffer);
return st;
}
BcStatus bc_main(unsigned int flags, unsigned int filec, char *filev[]) {
BcStatus status;
Bc bc;
struct sigaction sa;
size_t i;
bcg.interactive = (flags & BC_FLAG_I) || (isatty(0) && isatty(1));
bcg.std = flags & BC_FLAG_S;
bcg.warn = flags & BC_FLAG_W;
if ((status = bc_program_init(&bc.prog))) return status;
if ((status = bc_parse_init(&bc.parse, &bc.prog))) goto parse_err;
sigemptyset(&sa.sa_mask);
sa.sa_handler = bc_sig;
sa.sa_flags = 0;
if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGPIPE, &sa, NULL) < 0 ||
sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0)
{
status = BC_STATUS_EXEC_SIGACTION_FAIL;
goto err;
}
if ((bcg.interactive && !(flags & BC_FLAG_Q)) &&
(printf("%s", bc_header) < 0))
{
status = BC_STATUS_IO_ERR;
goto err;
}
if (flags & BC_FLAG_L) {
bc_lex_init(&bc.parse.lex, bc_lib_name);
if ((status = bc_lex_text(&bc.parse.lex, bc_lib))) goto err;
while (!status && bc.parse.lex.token.type != BC_LEX_EOF)
status = bc_parse_parse(&bc.parse);
if (status) goto err;
// Make sure to execute the math library.
if ((status = bc_program_exec(&bc.prog))) goto err;
}
for (i = 0; !bcg.sig_other && !status && i < filec; ++i)
status = bc_file(&bc, filev[i]);
if (status || bcg.sig_other) goto err;
status = bc_stdin(&bc);
err:
status = status == BC_STATUS_QUIT ? BC_STATUS_SUCCESS : status;
bc_parse_free(&bc.parse);
parse_err:
bc_program_free(&bc.prog);
return status;
}