/*
 * This file contains main() and the startup code for Mathomatic.
 * Leave this file out when compiling for the symbolic math library.
 *
 * Copyright (C) 1987-2007 George Gesslein II.
 *
 * Output to stderr is only done in this file.  The rest of the code
 * should not output to stderr.
 *
 * 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".
 *
 * Only one POWER operator is allowed per level in the storage format,
 * and no other operators may be on that level.  Same with the FACTORIAL
 * and MODULUS operators.
 *
 * Any number of TIMES and DIVIDE operators may be on the same
 * level, because they are simple multiplicative class operators.
 * The same for PLUS and MINUS, because they are additive class operators.
 *
 * See the file "am.h" to start understanding the Mathomatic code and
 * to adjust memory usage.
 */

#if	!LIBRARY
#include "includes.h"
#include <sys/ioctl.h>
#include <termios.h>

static void usage();
static int set_signals();

int
main(argc, argv)
int	argc;
char	**argv;
{
	extern char	*optarg;
	extern int	optind;

	int		i;
	char		*cp;
	double		numerator, denominator;
	double		multiplier;
	int		coption = false;

#if	UNIX
	prog_name = strdup(basename(argv[0]));		/* set prog_name to this executable's filename */
#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, "qrtchuvm:")) >= 0) {
		switch (i) {
		case 'c':
			coption++;
			break;
		case 'h':
			readline_enabled = false;
			html_flag = true;
			break;
		case 'm':
			multiplier = strtod(optarg, &cp) * DEFAULT_N_TOKENS;
			if (*cp || multiplier < 100 || multiplier >= (INT_MAX / 3)) {
				fprintf(stderr, _("%s: Invalid memory size multiplier specified.\n"), prog_name);
				exit(1);
			}
			n_tokens = (int) multiplier;
			break;
		case 'q':
			quiet_mode = true;
			break;
		case 'r':
			readline_enabled = false;
			break;
		case 't':
			readline_enabled = false;
			test_mode = true;
			break;
		case 'u':
			setbuf(stdout, NULL);	/* make standard output unbuffered */
			break;
		case 'v':
			version_report();
			exit(0);
		default:
			usage();
		}
	}
	if (test_mode || html_flag) {
		screen_columns = 0;
		screen_rows = 0;
	} else {
		get_screen_size();
	}
	if (!init_mem()) {
		fprintf(stderr, _("%s: Not enough memory.\n"), prog_name);
		exit(2);
	}
	if (html_flag) {
		printf("<pre>\n");
	}
	if (!test_mode && !quiet_mode) {
#if	SECURE
		printf(_("Secure "));
#endif
		printf(_("Mathomatic version %s (www.mathomatic.org)\n"), VERSION);
		printf(_("Copyright (C) 1987-2007 George Gesslein II.\n"));
		printf(_("This is free software; see the source for copying conditions.  There is NO\n"));
		printf(_("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
		printf(_("%d equation spaces available, %lld kilobytes per equation space.\n"),
		    N_EQUATIONS, (long long) n_tokens * sizeof(token_type) * 2LL / 1000LL);
	}
#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 (test_mode) {
		color_flag = false;
	}
	if (coption & 1) {
		color_flag = !color_flag;
	}
	if (!test_mode && !quiet_mode && color_flag) {
		printf(_("%s color mode enabled.\n"), html_flag ? "HTML" : "ANSI");
	}
	if ((i = setjmp(jmp_save)) != 0) {
		/* for error handling */
		clean_up();
		switch (i) {
		case 13:
			printf(_("Operation abruptly aborted.\n"));
			break;
		case 14:
			error(_("Expression too large."));
		default:
			printf(_("Operation aborted.\n"));
			break;
		}
	} else {
		if (!set_signals()) {
			fprintf(stderr, _("%s: signal(2) setting failed.\n"), prog_name);
		}
		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 on the command line, exit if error */
		for (i = optind; i < argc; i++) {
			if (!read_cmd(argv[i])) {
				exit_program(1);
			}
		}
#endif
	}
	/* main input/output loop */
	for (;;) {
		default_color();
		snprintf(prompt_str, sizeof(prompt_str), "%d%s", cur_equation + 1, html_flag ? HTML_PROMPT : PROMPT);
		if ((cp = get_string((char *) tlhs, n_tokens * sizeof(token_type))) == NULL)
			break;
		process(cp);
	}
	exit_program(0);
}

/*
 * Display invocation usage info and exit with error.
 */
static void
usage()
{
	fprintf(stderr, _("Mathomatic version %s - automatic algebraic manipulator\n\n"), VERSION);
	fprintf(stderr, _("Usage: %s [ options ] [ input_files ]\n\n"), prog_name);
	fprintf(stderr, _("Options:\n"));
	fprintf(stderr, _("  -c               Toggle color mode.\n"));
	fprintf(stderr, _("  -h               Enable HTML output mode and disable readline.\n"));
	fprintf(stderr, _("  -m number        Specify a memory size multiplier.\n"));
	fprintf(stderr, _("  -q               Set quiet mode (don't display prompts).\n"));
	fprintf(stderr, _("  -r               Disable readline.\n"));
	fprintf(stderr, _("  -t               Set test mode.\n"));
	fprintf(stderr, _("  -u               Set unbuffered output.\n"));
	fprintf(stderr, _("  -v               Display version number, compile flags, and memory usage.\n"));
	exit(1);
}

/*
 * All signal(2) initialization goes here.
 *
 * Return true on success.
 */
static int
set_signals()
{
	int	rv = true;

	if (signal(SIGFPE, fphandler) == SIG_ERR)
		rv = false;
	if (signal(SIGINT, inthandler) == SIG_ERR)
		rv = false;
	if (signal(SIGWINCH, resizehandler) == SIG_ERR)
		rv = false;
#if	TIMEOUT_SECONDS
	if (signal(SIGALRM, alarmhandler) == SIG_ERR)
		rv = false;
	alarm(TIMEOUT_SECONDS);
#endif
	return rv;
}

#if	CYGWIN
/*
 * dirname(3) function for Windows.
 */
char	*
dirname_win(cp)
char	*cp;
{
	int	i;

	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 "~/.mathomaticrc".
 *
 * Return false if error encountered.
 */
int
load_rc()
{
	FILE	*fp;
	char	buf[MAX_CMD_LEN];
	char	*cp;
	int	rv = true;

#if	CYGWIN
	snprintf(buf, sizeof(buf), "%s/%s", dir_path, RC_FILE);
	fp = fopen(buf, "r");
	if (fp == NULL) {
		return true;
	}
#else
	cp = getenv("HOME");
	if (cp == NULL)
		return true;
	snprintf(buf, sizeof(buf), "%s/%s", cp, RC_FILE);
	fp = fopen(buf, "r");
	if (fp == NULL)
		return true;
#endif
	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.
 * Usually doesn't work in most operating systems.
 */
void
fphandler(sig)
int	sig;
{
	error(_("Floating point exception."));
}

/*
 * Control-C (interrupt) handler.
 * Abruptly quit this program.
 */
void
inthandler(sig)
int	sig;
{
	printf(_("\nControl-C pressed, quitting...\n"));
	exit_program(1);
}

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

/*
 * Get the screen (window) width and height.
 */
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) {
			screen_columns = ws.ws_col;
		}
		if (ws.ws_row > 0 && screen_rows) {
			screen_rows = ws.ws_row;
		}
	}
}

/*
 * Window resize handler.
 */
void
resizehandler(sig)
int	sig;
{
	get_screen_size();
}

/*
 * 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();
/*	printf("\n"); */
	if (html_flag) {
		printf("</pre>\n");
	}
	exit(exit_value);
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1