mirror of
https://github.com/mfillpot/mathomatic.git
synced 2026-01-08 04:29:39 +00:00
562 lines
16 KiB
C
562 lines
16 KiB
C
/*
|
|
* This file contains main() and the startup code for Mathomatic,
|
|
* which is a computer algebra system written in the C programming language.
|
|
*
|
|
* Copyright (C) 1987-2012 George Gesslein II.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
The chief copyright holder can be contacted at gesslein@mathomatic.org, or
|
|
George Gesslein II, P.O. Box 224, Lansing, NY 14882-0224 USA.
|
|
|
|
*/
|
|
|
|
/*
|
|
* Output to stderr is only done in this file. The rest of the Mathomatic code
|
|
* should not output to stderr; error messages should use error() or go to stdout.
|
|
* One reason for this is so that Mathomatic stdout can be redirected or piped,
|
|
* catching all output.
|
|
*
|
|
* This program only supports binary and unary operators.
|
|
* Unary operators are implemented as a binary operation with a dummy operand.
|
|
*
|
|
* In the storage format, each level of parentheses is indicated
|
|
* by a level number (origin 1). The deeper the level, the
|
|
* higher the level number.
|
|
*
|
|
* The storage format for expressions is a fixed size array of elements
|
|
* "token_type", which may be a CONSTANT, VARIABLE, or OPERATOR.
|
|
* The array always alternates between operand (CONSTANT or VARIABLE)
|
|
* and OPERATOR. There is a separate integer for each array which
|
|
* contains the current length of the expression stored in the array.
|
|
* This length is always odd and must never exceed "n_tokens".
|
|
*
|
|
* In the storage format,
|
|
* any number of TIMES and DIVIDE operators may be on the same level of parentheses,
|
|
* because they are similar and the most basic multiplicative class operators.
|
|
* The same for the PLUS and MINUS operators, because they are similar (additive class).
|
|
* All other operators are only allowed one single operator per level of parentheses,
|
|
* and no same nor different operators may be with it on that level within the current grouping.
|
|
*
|
|
* Most of the expression manipulation and comparison routines are recursive,
|
|
* calling themselves for each level of parentheses.
|
|
*
|
|
* Note that equation space numbers internally are 1 less than the equation
|
|
* space numbers displayed. That is because internal equation space numbers
|
|
* are origin 0 array indexes, while displayed equation numbers are origin 1.
|
|
*
|
|
* See the file "am.h" to start understanding the Mathomatic code and
|
|
* to adjust memory usage.
|
|
*
|
|
* C types "long long" and "long double" are not used at all in Mathomatic,
|
|
* because many architectures and compilers do not support them. These large C
|
|
* types can be used in the Mathomatic Prime Number Tools though, producing
|
|
* larger prime numbers and quickly on a 64-bit computer. 64 bits is the size
|
|
* of a double, so Mathomatic runs optimally on a 64-bit system.
|
|
*/
|
|
|
|
#if !LIBRARY /* This comments out this whole file if compiling as the symbolic math library. */
|
|
|
|
#include "includes.h"
|
|
#if !NO_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
|
|
#if WIN32_CONSOLE_COLORS
|
|
#include <windows.h>
|
|
#include <wincon.h>
|
|
|
|
HANDLE hOut;
|
|
#endif
|
|
|
|
/*
|
|
* Display invocation usage info.
|
|
*/
|
|
void
|
|
usage(fp)
|
|
FILE *fp;
|
|
{
|
|
fprintf(fp, _("Mathomatic computer algebra system, version %s\n"), VERSION);
|
|
fprintf(fp, _("Usage: %s [ options ] [ input_files or input ]\n\n"), prog_name);
|
|
fprintf(fp, _("Options:\n"));
|
|
fprintf(fp, _(" -a Enable alternative color mode.\n"));
|
|
fprintf(fp, _(" -b Enable bold color mode.\n"));
|
|
fprintf(fp, _(" -c Toggle color mode.\n"));
|
|
fprintf(fp, _(" -d Set demo mode (no pausing).\n"));
|
|
fprintf(fp, _(" -e Process expressions and commands on the command line.\n"));
|
|
fprintf(fp, _(" -h Display this help and exit.\n"));
|
|
fprintf(fp, _(" -m number Specify a memory size multiplier.\n"));
|
|
fprintf(fp, _(" -q Set quiet mode (don't display prompts).\n"));
|
|
fprintf(fp, _(" -r Disable readline or editline.\n"));
|
|
fprintf(fp, _(" -s level:time Set enforced security level and max time for user's session.\n"));
|
|
fprintf(fp, _(" -t Set test mode. Use when comparing program output.\n"));
|
|
fprintf(fp, _(" -u Set unbuffered output with input echo.\n"));
|
|
fprintf(fp, _(" -v Display version number, then exit successfully.\n"));
|
|
fprintf(fp, _(" -w Wide output mode, sets unlimited width.\n"));
|
|
fprintf(fp, _(" -x Enable HTML/XHTML output mode.\n"));
|
|
fprintf(fp, _("\nPlease refer to the man page for details (type \"man mathomatic\" in shell).\n"));
|
|
}
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
#if NO_GETOPT_H /* if no getopt.h is available */
|
|
extern char *optarg; /* set by getopt(3) */
|
|
extern int optind;
|
|
#endif
|
|
|
|
int i, rv;
|
|
char *cp = NULL;
|
|
double numerator, denominator;
|
|
double new_size = 0;
|
|
int aoption = false, coption = false, boption = false, wide_flag = false;
|
|
int exit_value = 0;
|
|
unsigned int time_out_seconds = 0;
|
|
|
|
#if WIN32_CONSOLE_COLORS
|
|
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
#endif
|
|
|
|
#if I18N
|
|
/* Initialize internationalization so that messages are in the right language. */
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(prog_name, LOCALEDIR);
|
|
textdomain(prog_name);
|
|
#endif
|
|
|
|
#if CYGWIN || MINGW
|
|
dir_path = strdup(dirname_win(argv[0])); /* set dir_path to this executable's directory */
|
|
#endif
|
|
/* initialize the global variables */
|
|
init_gvars();
|
|
default_out = stdout; /* set default_out to any file you want to redirect output to */
|
|
gfp = default_out;
|
|
get_screen_size();
|
|
|
|
/* process command line options */
|
|
while ((i = getopt(argc, argv, "s:abqrtdchuvwxm:e")) >= 0) {
|
|
switch (i) {
|
|
case 's':
|
|
if (optarg) {
|
|
security_level = strtod(optarg, &cp);
|
|
#if SECURE
|
|
if (security_level != 4) {
|
|
fprintf(stderr, _("%s: Already compiled for maximum security (level 4), therefore setting security level ignored.\n"), prog_name);
|
|
}
|
|
#endif
|
|
}
|
|
if (optarg == NULL || cp == NULL || (cp == optarg && *cp != ':') || (*cp != '\0' && *cp != ':')) {
|
|
fprintf(stderr, _("%s: Error in setting security level.\n"), prog_name);
|
|
exit(2);
|
|
}
|
|
if (*cp == ':') {
|
|
time_out_seconds = strtol(cp + 1, &cp, 10);
|
|
if (!(*cp == '\0' && time_out_seconds > 0)) {
|
|
fprintf(stderr, _("%s: Error in setting time out seconds.\n"), prog_name);
|
|
exit(2);
|
|
}
|
|
}
|
|
#if !SECURE
|
|
if (time_out_seconds > 0) {
|
|
printf(_("Security level is %d, time out seconds is %d.\n"), security_level, time_out_seconds);
|
|
}
|
|
#endif
|
|
break;
|
|
case 'w':
|
|
wide_flag = true;
|
|
break;
|
|
case 'a':
|
|
aoption = true;
|
|
break;
|
|
case 'b':
|
|
boption = true;
|
|
break;
|
|
case 'c':
|
|
coption++;
|
|
break;
|
|
case 'x':
|
|
html_flag = 1;
|
|
wide_flag = true;
|
|
break;
|
|
case 'm':
|
|
if (optarg)
|
|
new_size = strtod(optarg, &cp) * DEFAULT_N_TOKENS;
|
|
if (optarg == NULL || cp == NULL || *cp || new_size <= 0 || new_size >= (INT_MAX / sizeof(token_type))) {
|
|
fprintf(stderr, _("%s: Invalid memory size multiplier specified.\n"), prog_name);
|
|
exit(2);
|
|
}
|
|
n_tokens = (int) new_size;
|
|
break;
|
|
case 'q':
|
|
quiet_mode = true;
|
|
break;
|
|
case 'r':
|
|
readline_enabled = false;
|
|
break;
|
|
case 't':
|
|
readline_enabled = false;
|
|
wide_flag = true;
|
|
test_mode = true;
|
|
break;
|
|
case 'd':
|
|
demo_mode = true;
|
|
break;
|
|
case 'u':
|
|
echo_input = true;
|
|
setbuf(stdout, NULL); /* make output unbuffered */
|
|
setbuf(stderr, NULL);
|
|
break;
|
|
case 'h':
|
|
usage(stdout);
|
|
exit(0);
|
|
case 'v':
|
|
/* Don't be fancy, this may be used to test for existence. */
|
|
printf(_("Mathomatic version %s\n"), VERSION);
|
|
exit(0);
|
|
case 'e':
|
|
eoption = true;
|
|
autoselect = false;
|
|
break;
|
|
default:
|
|
usage(stdout);
|
|
exit(2);
|
|
}
|
|
}
|
|
if (n_tokens < 100 || n_tokens >= (INT_MAX / sizeof(token_type))) {
|
|
fprintf(stderr, _("%s: Standard expression array size %d out of range!\n"), prog_name, n_tokens);
|
|
}
|
|
if (!init_mem()) {
|
|
fprintf(stderr, _("%s: Not enough memory.\n"), prog_name);
|
|
exit(2);
|
|
}
|
|
#if READLINE
|
|
if (readline_enabled) { /* readline_enabled flag must not change after this */
|
|
if ((cp = getenv("HOME"))) {
|
|
#if MINGW
|
|
snprintf(history_filename_storage, sizeof(history_filename_storage), "%s/matho_history", cp);
|
|
#else
|
|
snprintf(history_filename_storage, sizeof(history_filename_storage), "%s/.matho_history", cp);
|
|
#endif
|
|
history_filename = history_filename_storage;
|
|
}
|
|
using_history(); /* initialize readline history */
|
|
rl_initialize(); /* initialize readline */
|
|
stifle_history(500); /* maximum of 500 entries */
|
|
rl_inhibit_completion = true; /* turn off readline file name completion */
|
|
#if 0 /* not 100% tested and this might confuse the user with the -c toggle */
|
|
#if !WIN32_CONSOLE_COLORS
|
|
if (!html_flag) { /* If doing ANSI color: */
|
|
color_flag = (tigetnum("colors") >= 8); /* autoset color output mode. Requires ncurses. */
|
|
}
|
|
#endif
|
|
#endif
|
|
#if !SECURE
|
|
if (security_level <= 3) {
|
|
read_history(history_filename); /* restore readline history of previous session */
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
if (html_flag) {
|
|
printf("<pre>\n");
|
|
}
|
|
if (!test_mode && !quiet_mode && !eoption) {
|
|
display_startup_message(stdout);
|
|
}
|
|
fflush(stdout);
|
|
#if !SECURE
|
|
/* read the user options initialization file */
|
|
if (security_level <= 3 && !test_mode && !demo_mode && !load_rc(true, NULL)) {
|
|
fprintf(stderr, _("%s: Error loading startup set options from \"%s\".\n"), prog_name, rc_file);
|
|
fprintf(stderr, _("Use the \"set no save\" command to startup with the program defaults every time.\n\n"));
|
|
}
|
|
#endif
|
|
if (wide_flag) {
|
|
screen_columns = 0;
|
|
screen_rows = 0;
|
|
}
|
|
if (coption & 1) {
|
|
color_flag = !color_flag;
|
|
}
|
|
if (boption) {
|
|
color_flag = 1;
|
|
bold_colors = 1;
|
|
}
|
|
if (color_flag && aoption) {
|
|
color_flag = 2;
|
|
}
|
|
if (test_mode) {
|
|
color_flag = 0;
|
|
} else if (!quiet_mode && !eoption) {
|
|
if (color_flag) {
|
|
#if WIN32_CONSOLE_COLORS
|
|
if (color_flag == 2) {
|
|
printf(_("%s%s color mode enabled"), html_flag ? "HTML" : "ANSI", bold_colors ? " bold" : "");
|
|
} else {
|
|
printf(_("%s%s color mode enabled"), html_flag ? "HTML" : "WIN32 CONSOLE", bold_colors ? " bold" : "");
|
|
}
|
|
#else
|
|
printf(_("%s%s color mode enabled"), html_flag ? "HTML" : "ANSI", bold_colors ? " bold" : "");
|
|
#endif
|
|
printf(_("; manage by typing \"help color\".\n"));
|
|
} else {
|
|
printf(_("Color mode turned off; manage it by typing \"help color\".\n"));
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
if ((i = setjmp(jmp_save)) != 0) {
|
|
/* for error handling */
|
|
clean_up();
|
|
switch (i) {
|
|
case 14:
|
|
error(_("Expression too large."));
|
|
default:
|
|
printf(_("Operation aborted.\n"));
|
|
break;
|
|
}
|
|
previous_return_value = 0;
|
|
if (eoption)
|
|
exit_value = 1;
|
|
} else {
|
|
if ((rv = set_signals(time_out_seconds))) {
|
|
fprintf(stderr, _("C signal handler setting failed!\n"));
|
|
#if DEBUG
|
|
fprintf(stderr, "Failing signal is \"%s\".\n", strsignal(rv));
|
|
#endif
|
|
exit_value = 2;
|
|
}
|
|
if (!f_to_fraction(0.5, &numerator, &denominator)
|
|
|| numerator != 1.0 || denominator != 2.0
|
|
|| !f_to_fraction(1.0/3.0, &numerator, &denominator)
|
|
|| numerator != 1.0 || denominator != 3.0) {
|
|
fprintf(stderr, _("%s: Cannot convert any floating point values to fractions!\n"), prog_name);
|
|
fprintf(stderr, _("Roots will not work properly.\n"));
|
|
exit_value = 2;
|
|
}
|
|
if (max_memory_usage() <= 0) {
|
|
fprintf(stderr, _("%s: Calculated maximum memory usage overflows a long integer!\n"), prog_name);
|
|
exit_value = 2;
|
|
}
|
|
if (eoption) {
|
|
/* process expressions and commands from the command line */
|
|
for (i = optind; i < argc && argv[i]; i++) {
|
|
if (!display_process(argv[i])) {
|
|
exit_value = 1;
|
|
}
|
|
}
|
|
} else {
|
|
#if SECURE
|
|
if (!quiet_mode && !test_mode)
|
|
printf(_("Anything done here is temporary.\n"));
|
|
if (optind < argc) {
|
|
warning(_("File arguments ignored in high security mode."));
|
|
}
|
|
#else
|
|
if (!quiet_mode && !test_mode) {
|
|
if (optind < argc) {
|
|
printf(_("Reading in file%s specified on the command line...\n"), (optind == (argc - 1)) ? "" : "s");
|
|
} else {
|
|
if (security_level >= 2) {
|
|
printf(_("Anything done here is temporary.\n"));
|
|
} else {
|
|
printf(_("Anything done here is temporary, unless it is saved or redirected.\n"));
|
|
}
|
|
}
|
|
}
|
|
/* read in files specified on the command line, exit if error */
|
|
for (i = optind; i < argc && argv[i]; i++) {
|
|
if (strcmp(argv[i], "-") == 0) {
|
|
main_io_loop();
|
|
} else if (!read_file(argv[i])) {
|
|
fflush(NULL); /* flush all output */
|
|
fprintf(stderr, _("Read of file \"%s\" failed.\n"), argv[i]);
|
|
exit_program(1);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
if (!eoption)
|
|
main_io_loop(); /* main input/output loop */
|
|
exit_program(exit_value); /* exit Mathomatic, doesn't return */
|
|
return(exit_value); /* so the compiler doesn't complain */
|
|
}
|
|
|
|
/*
|
|
* Repeatedly read a line of text from standard input and process the expression or command.
|
|
*/
|
|
void
|
|
main_io_loop(void)
|
|
{
|
|
char *cp = NULL;
|
|
|
|
for (;;) {
|
|
error_str = NULL;
|
|
warning_str = NULL;
|
|
default_color(false);
|
|
snprintf(prompt_str, sizeof(prompt_str), "%d%s", cur_equation + 1, html_flag ? HTML_PROMPT_STR : PROMPT_STR);
|
|
if ((cp = get_string((char *) tlhs, n_tokens * sizeof(token_type))) == NULL)
|
|
break;
|
|
process(cp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* All signal(2) initialization goes here.
|
|
* Attach all necessary C signals to their handler functions.
|
|
*
|
|
* Return zero on success, or a non-zero unsettable signal number on error.
|
|
*/
|
|
int
|
|
set_signals(time_out_seconds)
|
|
unsigned int time_out_seconds;
|
|
{
|
|
int rv = 0;
|
|
|
|
if (signal(SIGFPE, fphandler) == SIG_ERR)
|
|
rv = SIGFPE;
|
|
if (signal(SIGINT, inthandler) == SIG_ERR)
|
|
rv = SIGINT;
|
|
if (signal(SIGTERM, exithandler) == SIG_ERR)
|
|
rv = SIGTERM;
|
|
#if 0 /* Crashes entire shell window when readline is used and SIGHUP received. */
|
|
if (signal(SIGHUP, exithandler) == SIG_ERR)
|
|
rv = SIGHUP;
|
|
#endif
|
|
#if UNIX || CYGWIN
|
|
if (signal(SIGWINCH, resizehandler) == SIG_ERR)
|
|
rv = SIGWINCH;
|
|
#endif
|
|
#if 0 /* Crashes entire shell when readline is used. */
|
|
if (signal(SIGALRM, alarmhandler) == SIG_ERR)
|
|
rv = SIGALRM;
|
|
#endif
|
|
#if !MINGW
|
|
if (time_out_seconds > 0) {
|
|
alarm(time_out_seconds);
|
|
#if TIMEOUT_SECONDS
|
|
} else {
|
|
alarm(TIMEOUT_SECONDS);
|
|
#endif
|
|
}
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Floating point exception handler.
|
|
* Floating point exceptions are currently ignored.
|
|
*/
|
|
void
|
|
fphandler(sig)
|
|
int sig;
|
|
{
|
|
#if DEBUG
|
|
warning("Floating point exception.");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Fancy Control-C (interrupt) signal handler.
|
|
* Interrupts processing and returns to main prompt through a polling mechanism.
|
|
* If it can't, repeated calls terminate this program.
|
|
*/
|
|
void
|
|
inthandler(sig)
|
|
int sig;
|
|
{
|
|
abort_flag++;
|
|
switch (abort_flag) {
|
|
case 0:
|
|
case 1:
|
|
/* wait for graceful abort */
|
|
printf(_("\nUser interrupt signal received; three times in a row quits Mathomatic.\n"));
|
|
return;
|
|
case 2:
|
|
printf(_("\nPress Control-C once more to quit program.\n"));
|
|
return;
|
|
default:
|
|
/* abruptly quit this program */
|
|
printf(_("\nRepeatedly interrupted; returning to operating system...\n"));
|
|
exit_program(1);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* Alarm signal handler.
|
|
*/
|
|
void
|
|
alarmhandler(sig)
|
|
int sig;
|
|
{
|
|
printf(_("\nTimeout, quitting...\n"));
|
|
exit_program(1);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Signal handler for proper exiting to the operating system.
|
|
*/
|
|
void
|
|
exithandler(sig)
|
|
int sig;
|
|
{
|
|
exit_program(1);
|
|
}
|
|
|
|
#if UNIX || CYGWIN
|
|
/*
|
|
* Window resize signal handler.
|
|
*/
|
|
void
|
|
resizehandler(sig)
|
|
int sig;
|
|
{
|
|
if (screen_columns)
|
|
get_screen_size();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Properly exit this program and return to the operating system.
|
|
*/
|
|
void
|
|
exit_program(exit_value)
|
|
int exit_value; /* zero if OK, non-zero indicates error return */
|
|
{
|
|
reset_attr();
|
|
if (html_flag) {
|
|
printf("</pre>\n");
|
|
}
|
|
#if READLINE && !SECURE
|
|
if (readline_enabled && security_level <= 3) {
|
|
write_history(history_filename); /* save readline history */
|
|
}
|
|
#endif
|
|
if (exit_value == 0 && !quiet_mode && !eoption && !html_flag) {
|
|
printf(_("ByeBye!! from Mathomatic.\n"));
|
|
}
|
|
#if VALGRIND
|
|
printf("Deallocating all Mathomatic allocated memory for valgrind memory leak checking...\n");
|
|
printf("If you are not using valgrind, please compile without -DVALGRIND.\n");
|
|
free_mem(); /* Free all known memory buffers to check for memory leaks with something like valgrind(1). */
|
|
#endif
|
|
exit(exit_value);
|
|
}
|
|
#endif
|