/* * This file contains main() and the startup code for Mathomatic, * which is a small computer algebra system written entirely in C. * * Copyright (C) 1987-2009 George Gesslein II. * * 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. * * 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. */ #if !LIBRARY /* This comments out this whole file if using as symbolic math library. */ #include "includes.h" #if UNIX || CYGWIN #include #include #endif #if TRAP_FPERRORS /* Do not use this define! Currently does not work. */ #include #endif /* * Display invocation usage info. */ void usage() { printf(_("\nMathomatic version %s computer algebra system\n\n"), VERSION); printf(_("Usage: %s [ options ] [ input_files ]\n\n"), prog_name); printf(_("Options:\n")); printf(_(" -b Enable bold color mode.\n")); printf(_(" -c Toggle color mode.\n")); printf(_(" -h Display this help and exit.\n")); printf(_(" -m number Specify a memory size multiplier.\n")); printf(_(" -q Set quiet mode (don't display prompts).\n")); printf(_(" -r Disable readline.\n")); printf(_(" -s level Set enforced security level for session.\n")); printf(_(" -t Set test mode.\n")); printf(_(" -u Set unbuffered output.\n")); printf(_(" -v Display version information, then exit.\n")); printf(_(" -w Wide output mode, sets unlimited width.\n")); printf(_(" -x Enable HTML/XHTML output mode.\n")); printf(_("\nPlease refer to the %s man page for more information.\n"), prog_name); } int main(argc, argv) int argc; char **argv; { extern char *optarg; /* set by getopt(3) */ extern int optind; int i; char *cp = NULL; double numerator, denominator; double new_size; int coption = false, boption = false, wide_flag = false; #if I18N /* Initialize internationalization so that messages are in the right language. */ setlocale(LC_ALL, ""); textdomain(prog_name); bindtextdomain(prog_name, LOCALEDIR); #endif #if CYGWIN dir_path = strdup(dirname_win(argv[0])); /* set dir_path to this executable's directory */ #endif /* initialize the global variables */ init_gvars(); gfp = stdout; /* process command line options */ while ((i = getopt(argc, argv, "s:bqrtchuvwxm:")) >= 0) { switch (i) { case 's': #if SECURE fprintf(stderr, _("%s: Compiled for maximum security, therefore no need for setting security level.\n"), prog_name); #else security_level = atoi(optarg); #endif break; case 'w': wide_flag = true; break; case 'b': boption = true; break; case 'c': coption++; break; case 'x': html_flag = true; wide_flag = true; break; case 'm': new_size = strtod(optarg, &cp) * DEFAULT_N_TOKENS; if (cp == NULL || *cp || new_size <= 0 || new_size >= (INT_MAX / 3)) { 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 'u': setbuf(stdout, NULL); /* make output unbuffered */ setbuf(stderr, NULL); break; case 'h': usage(); exit(0); case 'v': version_report(); exit(0); default: usage(); exit(2); } } if (n_tokens < 100) { fprintf(stderr, _("%s: Expression array size too small.\n"), prog_name); exit(2); } 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")) && prog_name) { snprintf(history_filename_storage, sizeof(history_filename_storage), "%s/.%s_history", cp, prog_name); history_filename = history_filename_storage; } rl_initialize(); /* initialize readline */ using_history(); /* initialize readline history */ stifle_history(500); /* maximum of 500 entries */ rl_inhibit_completion = true; /* turn off readline file name completion */ #if false /* not 100% tested and this might confuse the user with the -c toggle */ color_flag = (tigetnum("colors") >= 8); /* autoset color output mode */ #endif #if !SECURE read_history(history_filename); /* restore readline history of previous session */ #endif } #endif #if UNIX || CYGWIN get_screen_size(); #endif if (html_flag) { printf("
\n");
	}
	if (!test_mode && !quiet_mode) {
#if	SECURE
		printf(_("Secure "));
#else
		if (security_level >= 2)
			printf(_("Secure "));
#endif
		printf("Mathomatic version %s (www.mathomatic.org)\n", VERSION);
		printf("Copyright (C) 1987-2009 George Gesslein II.\n");
		printf(_("%d equation spaces available, %ld Kbytes per equation space.\n"),
		    N_EQUATIONS, (long) n_tokens * sizeof(token_type) * 2L / 1000L);
	}
#if	!SECURE
	/* read the user options initialization file */
	if (!test_mode && !load_rc()) {
		fprintf(stderr, _("%s: Error loading set options from \"%s\".\n"), prog_name, rc_file);
	}
#endif
	if (wide_flag) {
		screen_columns = 0;
		screen_rows = 0;
	}
	if (coption & 1) {
		color_flag = !color_flag;
	}
	if (boption) {
		color_flag = true;
		bold_colors = true;
	}
	if (test_mode) {
		color_flag = false;
	}
	if (!quiet_mode && color_flag) {
		printf(_("%s%s color mode enabled"), html_flag ? "HTML" : "ANSI", bold_colors ? " bold" : "");
		if (!boption) {
			printf(_("; disable with the -c option or \"set no color\".\n"));
		} else {
			printf(".\n");
		}
	}
	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;
		}
	} else {
		if (!set_signals()) {
			fprintf(stderr, _("signal(2) setting failed.\n"));
		}
		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"));
		}
#if	!SECURE
		/* read in files specified on the command line, exit if error */
		for (i = optind; i < argc && argv[i]; i++) {
			if (strcmp(argv[i], "-") == 0) {
				read_stdin();
			} else if (!read_cmd(argv[i])) {
				fprintf(stderr, _("Read of file \"%s\" failed.\n"), argv[i]);
				exit_program(1);
			}
		}
#endif
	}
	read_stdin();		/* main input/output loop */
	exit_program(0);	/* exit Mathomatic, doesn't return */
	return(0);		/* so the compiler doesn't complain */
}

/*
 * Repeatedly read a line of text from standard input and process the expression or command.
 */
void
read_stdin()
{
	char	*cp = NULL;

	for (;;) {
		default_color();
		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.
 *
 * Return true on success.
 */
int
set_signals()
{
	int	rv = true;

	if (signal(SIGFPE, fphandler) == SIG_ERR)
		rv = false;
#if	TRAP_FPERRORS
	if (feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW) < 0)
		rv = false;
#endif
	if (signal(SIGINT, inthandler) == SIG_ERR)
		rv = false;
	if (signal(SIGQUIT, inthandler) == SIG_ERR)
		rv = false;
#if	UNIX || CYGWIN
	if (signal(SIGWINCH, resizehandler) == SIG_ERR)
		rv = false;
#endif
#if	TIMEOUT_SECONDS
	if (signal(SIGALRM, alarmhandler) == SIG_ERR)
		rv = false;
	alarm(TIMEOUT_SECONDS);
#endif
	return rv;
}

#if	CYGWIN
/*
 * dirname(3) function for Microsoft Windows.
 * dirname(3) strips the non-directory suffix from a filename.
 */
char *
dirname_win(cp)
char	*cp;	/* string containing filename to modify */
{
	int	i;

	if (cp == NULL)
		return(".");
	i = strlen(cp);
	while (i >= 0 && cp[i] != '\\' && cp[i] != '/')
		i--;
	if (i < 0)
		return(".");
	cp[i] = '\0';
	return(cp);
}
#endif

#if	!SECURE
/*
 * Load set options from startup file "~/.mathomaticrc".
 *
 * Return false if there was an error reading the startup file,
 * otherwise return true.
 */
int
load_rc()
{
	FILE	*fp = NULL;
	char	buf[MAX_CMD_LEN];
	char	*cp;
	int	rv = true;

	cp = getenv("HOME");
	if (cp) {
		snprintf(rc_file, sizeof(rc_file), "%s/%s", cp, ".mathomaticrc");
		fp = fopen(rc_file, "r");
	}
#if	CYGWIN
	if (fp == NULL) {
		snprintf(rc_file, sizeof(rc_file), "%s/%s", dir_path, "mathomatic.rc");
		fp = fopen(rc_file, "r");
	}
#endif
	if (fp == NULL) {
		return true;
	}
	while ((cp = fgets(buf, sizeof(buf), fp)) != NULL) {
		set_error_level(cp);
		if (!set_options(cp))
			rv = false;
	}
	fclose(fp);
	return rv;
}
#endif

/*
 * Floating point exception handler.
 * Floating point exceptions are currently ignored.
 *
 * I do not know the proper function calls that should be here to make the TRAP_FPERRORS define work.
 * Currently it goes in an endless loop when an FP exception occurs and TRAP_FPERRORS is true,
 * because when this function returns, the exception immediately happens again.
 * Therefore, do not define TRAP_FPERRORS!
 */
void
fphandler(sig)
int	sig;
{
#if	TRAP_FPERRORS
	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.\n"));
		return;
	case 2:
		printf(_("\nPress Control-C once more to quit program.\n"));
		return;
	default:
		/* abruptly quit this program */
		printf(_("\nRepeatedly interrupted; Quitting...\n"));
		exit_program(1);
	}
}

/*
 * Alarm signal handler.
 */
void
alarmhandler(sig)
int	sig;
{
	printf(_("\nTimeout, quitting...\n"));
	exit_program(1);
}

#if	UNIX || CYGWIN
/*
 * Get the screen (window) width and height from the operating system.
 *
 * Sets the global integers screen_columns and screen_rows.
 */
void
get_screen_size()
{
	struct winsize	ws;

	ws.ws_col = 0;
	ws.ws_row = 0;
	if (ioctl(1, TIOCGWINSZ, &ws) >= 0) {
		if (ws.ws_col > 0) {
			screen_columns = ws.ws_col;
		}
		if (ws.ws_row > 0) {
			screen_rows = ws.ws_row;
		}
	}
}

/*
 * 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) { if (write_history(history_filename)) { /* save readline history */ fprintf(stderr, _("%s: Unable to save readline history into file \"%s\".\n"), prog_name, history_filename); } } #endif if (exit_value == 0 && !quiet_mode && !html_flag) printf(_("Thank you for using Mathomatic!\n")); exit(exit_value); } #endif