| /* |
| * ***************************************************************************** |
| * |
| * 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; |
| } |