/* * 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 #endif #if WIN32_CONSOLE_COLORS #include #include 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("
\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("
\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