/*  PhysCalc - Main program - March 1989
    Copyright (C) 1989, 1990  Marty White

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <stdio.h>
#include "compiler.h"

#ifdef __STDHEADERS__
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
/*#include <conio.h>*/
/*#include <dos.h>*/
/*#include <alloc.h>*/
#include <memory.h>
#endif

#include "physcalc.h"
#include "physdecl.h"

#ifdef __PROTOTYPES__
LOCAL void initvars(void);
#endif

EXPORT struct varstruct *var;	/* initialized by initvars() */
EXPORT struct ufstruct *userfunc = NULL; /* Holds user functions */
EXPORT int echo=TRUE, error;
EXPORT char *errormsg;
#ifdef TRACE
EXPORT int trace=FALSE;
#endif

/** FUNCTIONS *********************************************************/

#ifndef __TURBOC__		/* Turbo has built in working function */
EXPORT void clrscr()	/* Clear screen - compiler dependent */
{
	puts("\33[2J");		/* ANSI code to clear screen */
}
#endif

EXPORT int pause_for_user()
{	/* Probably compiler/operating system dependent.
	This is supposed to display a message, wait for a keypress,
	not echo it, erase the current line and leave the cursor where
	it started. */
	int c;

	printf("Press any key to continue ");
#ifdef __TURBOC__
	c = getch();			/* should get 1 char, no echo */
	if (!c)
		getch();
#else
#ifdef __DESMETC__
	c = ci();
	if (!c)
		ci();
#else
	c = getchar();			/* Will NOT work right! */
#endif
#endif
	printf("\r%30s\r","");
	return c;
}

EXPORT void printerror()
{
	static char *errors[7] = {
		"Computational error",
		"Unbalanced parenthesis",
		"Syntax error",
		"Unknown function",
		"Unknown variable",
		"Type mismatch",
		"Out of memory"
	};

	if (echo) {
		if (error>0 && error<8)
			printf("%s.\n",errors[error-1]);
		if (error<-1 || error>7)
			printf("Unknown error #%d\n",error);
		if (errormsg)
			printf("%s.\n",errormsg);
	}
}

EXPORT NODEP solve(s)
char const **s;
/* Take a string expression, return an evaluated node */
{
	NODEP n,n2;

	n = parse(s);
	if (!error)
		n2 = evaluate(n);
	deallocnode(n);
	if (error)
		return NULL;
	return n2;
}

LOCAL void initvars()
{	/* Create variable PREVIOUS */
	static char previous[] = "PREVIOUS";

	if ((var = malloc(sizeof(struct varstruct)))==NULL)
			out_of_memory("initvars()");
	var->next = NULL;
	var->value = NULL;
	var->name = previous;	/* Create permament PREVIOUS variable */
	var->rflag = FALSE;
}

void printmyaddress()
{
}

/***** Command functions *****/

LOCAL void help(s)
char const *s;
{
    printf("You may type a command or an expression for immediate evaluation.\n");
    printf("Operators are: + - * / ^ ! not and or mod ( )\n");
    printf("Built in functions are: exp(), ln(), log(), sqrt(), fact(), sin(), cos(),\n");
    printf("  tan(), arcsin(), arccos(), arctan(), and integrate(f,a,b[,p]).\n");
    printf("Built in commands are: HELP, QUIT, INFO, LET, DEFINE, LIST, DELETE, FACTOR\n");
    printf("  TRACE, FRACTION, BASE, DIMENSION, UNIT, SAVE, LOAD, and DOS.\n");
}

LOCAL void info(s)	/* INFO cmd */
char const *s;
{
	printf("PhysCalc Version %s by Marty White\n",VERSION);
	/* Compiler dependent */
#ifdef __BORLANDC__
    printf("Compiled on %s at %s with Borland C++ %d.\n",
		__DATE__,__TIME__,__TURBOC__);
#else
#ifdef __TURBOC__
	printf("Compiled on %s at %s with Turbo C %d.\n",
		__DATE__,__TIME__,__TURBOC__);
#endif
#endif
#ifdef __DESMETC__
	printf("Compiled with DeSmet C.\n");
#endif
	printf("Maximum fundamental dimensions: %d\n",MAXDIM);
	printf("There are currently %d nodes allocated.\n",nodecount);
    #ifdef __TURBOC__
    printf("At least %luK of memory is available.\n", (unsigned long) coreleft() / 1024);
    #endif
}

LOCAL void do_let(s)	/* Assigns an expresion to a variable */
char const *s;
{			/* TD: fix spacing allowances */
	/* Syntax:	LET variable = literal string
					Uses direct substitution, variables & functions are not
					calculated until needed.
				LET variable := value
					Evaluates immediately, saving time & memory */
	int flag;
	char buf[NAMELEN],answer[SMALLBUF];
	NODEP n,n2;
	struct varstruct *v,*v2;
	static char msg[] =
"Error: missing '='. To use LET type:\n  LET <variable name> = <expression>\nor\n  LET <variable name> := <expression>\n";

	strupr(s);
	scan_symbol(&s,buf);
	if (buf[0]=='\0') {
		error = -1;
		errormsg = msg;
		return;
	}
	flag = FALSE;
	skipspc(s);
	if (*s==':') {
		flag = TRUE;	/* colon found */
		s++;
	}
	if (!*s=='=') {
		error = -1;
		errormsg = msg;
		return;
	}
	s++;
	skipspc(s);
	n = parse(&s);	/* convert to a node tree */
	if (error) {
		deallocnode(n);
		return;
	}
	if (flag) {		/* evaluate expression NOW instead of later */
		n2 = evaluate(n);
		deallocnode(n);
		if (error) {
			deallocnode(n2);
			return;
		}
		n = n2;
	}

	/* The 1st variable, "PREVIOUS", is not user alterable */
	v = var;
	while (v->next) {   /* search for duplicate name or end of list */
		v = v->next;
		if (!strcmp(v->name,buf)) {
			if (echo) {
				printf("Replace %s = ",v->name);
				printexpr(v->value);
				printf(" ?");
				fgets(answer, sizeof(answer), stdin);
				printf("\n");
			} else
				answer[0] = 'Y';
			if (toupper(answer[0])=='Y') {
				deallocnode(v->value);
				v->value = n;
				if (echo) printf("Assigned.\n");
				return;
			}
		}
	}
	if ((v2=malloc(sizeof(struct varstruct)))==NULL) {
		error = MEMERR;
		return;
	}
	if ((v2->name = strdup(buf))==NULL) {
		error = MEMERR;
		return;
	}
	v2->value = n;
	v2->rflag = FALSE;
	v2->next = NULL;
	v->next = v2;
	if (echo) printf("Assigned.\n");
}

LOCAL void define_uf(s)	/* Define a function */
char const *s;	/* text to parse */
{
	char buf[NAMELEN],buf2[NAMELEN];
	NODEP n,n2,n3;
	struct ufstruct *u,*u2;
	static char msg[] = "Bad function definition.\n";

	strupr(s);
	scan_symbol(&s,buf);	/* Scan function name */
	skipspc(s);
	if (!buf[0] || *s!='(') {
		error = -1;
		errormsg = msg;
		return;
	}
	n2 = alloclnode(LNODE);
	do {
		s++;					/* Skip '(' or ',' */
		scan_symbol(&s,buf2);	/* Scan dummy var name */
		n3 = allocsnode(buf2);
		linknode(n2,n3);
		skipspc(s);
	} while (*s==',');
	if (*s != ')') {
		error = -1;
		errormsg = msg;
		deallocnode(n2);
		return;
	}
	s++;
	skipspc(s);
	if (*s=='=')
		s++;
	n = parse(&s);		/* parse function expression */
	if (error) {
		deallocnode(n);
		deallocnode(n2);
		return;
	}

	if (u = userfunc) {
		do {
			if (!strcmp(u->name,buf)) {
				deallocnode(u->dummy);
				deallocnode(u->value);
				u->dummy = n2;
				u->value = n;
				if (echo) printf("Redefined.\n");
				return;
			}
		} while (u->next && (u=u->next));
	}

	if ((u2 = malloc(sizeof(struct ufstruct)))==NULL) {
		error = MEMERR;
		return;
	}
	if (u)
		u->next = u2;
	else
		userfunc = u2;
	if ((u2->name = strdup(buf))==NULL) {
		error = MEMERR;
		return;
	}
	u2->value = n;
	u2->dummy = n2;
	u2->next = NULL;
	if (echo) printf("Defined.\n");
}

EXPORT void output_list(fp,vars,funcs,verbose)
FILE *fp;
struct varstruct const *vars;
struct ufstruct const *funcs;
int verbose;
{	/* Output a list of all variables and/or user-functions. If
	verbose is true, output in a format from which the original
	expression can be reconstructed. */

	int j;

	while (vars) {
		fprintf(fp,"%s%s = ", verbose?"LET ":"", vars->name);
		fprintexpr(fp,vars->value);
		fprintf(fp,"\n");
		vars = vars->next;
	}

	while (funcs) {
		fprintf(fp, "%s%s(", verbose?"DEFINE ":"", funcs->name);
		j=0;
		while (funcs->dummy->data->list[j]) {
			fprintexpr(fp,funcs->dummy->data->list[j]);
			if (funcs->dummy->data->list[++j])
				fprintf(fp,",");
		}
		fprintf(fp,") = ");
		fprintexpr(fp, funcs->value);
		fprintf(fp,"\n");
		funcs = funcs->next;
	}
}

LOCAL void list(s)
char const *s;
{	/* output all variables and/or user-functions to console */
	skipspc(s);
	strupr(s);
	output_list(stdout,
			strncmp(s,"FUNC",4) ? var : NULL,
			strncmp(s,"VAR",3) ? userfunc : NULL,
			FALSE);
}

LOCAL void delete(s)	/* erase a variable or user-function */
char const *s;
{	/* a function name must be followed by an open paren to distinguish from
		a variable */

	char name[NAMELEN];
	int flag;
	struct varstruct *v,*v2;
	struct ufstruct *u,*u2;

	strupr(s);
	scan_symbol(&s,name);
	if (name[0]=='\0') {
		error = -1;
		errormsg =
"To delete a variable type:\n  DELETE <variable name>\nTo delete a function type:\n  DELETE <function name>(\n";
		return;
	}
	if (*s=='(') {	/* delete a function */
		u2 = NULL;
		if (u = userfunc)
			do {
				if (!strcmp(u->name,name)) {
					free(u->name);
					deallocnode(u->value);
					deallocnode(u->dummy);
					if (u2)
						u2->next = u->next;
					free(u);
					if (echo) printf("Function deleted.\n");
					return;
				}
				u2 = u;
				u = u->next;
			} while (u);
		if (echo) printf("No user-defined function '%s'.\n",name);
	} else {	/* delete a variable */
		flag = FALSE;
		v = var;
		v2 = NULL;
		do {
			if (!strcmp(v->name,name)) {
				free(v->name);
				deallocnode(v->value);
				if (v2)
					v2->next = v->next;
				free(v);
				flag = TRUE;
			} else
				v2 = v;
			v = v->next;
		} while (v);
		if (echo) {
			if (flag)
				printf("Variable Deleted.\n");
			else
				printf("No user-defined variable '%s'.\n",name);
		}
	}
}

LOCAL void quit(s)
char const *s;
{
	exit(0);
}

#ifdef TRACE
LOCAL void trace_cmd(s)	/* set expression tracing mode */
char const *s;
{
	strupr(s);
	trace = !!strcmp(s,"OFF");
	printf("Tracing is o%s.\n",trace?"n":"ff");
}
#endif

LOCAL void base_cmd(s)
char const *s;
{
	int b;

	skipspc(s);
	if (*s=='=')
		s++;
	skipspc(s);
	if (!*s) {
		printf("Default numeric base is %d.\n", defaultbase);
		return;
	}
	b = atoi(s);
	if (b>0 && b<37) {
		printf("New default numeric base is %d.\n", b);
		defaultbase = b;
	} else
		printf("Invalid base.  Usage:  BASE ['='] <base, in base 10>\n");
}

LOCAL void save_cmd(s)
char *s;
{	/* Save some or all variables, functions, dimensions, and units by
	writing human-readable text to a file from which the original
	expressions can be reconstructed.  Only those expressions defined
	since the original loading will be saved unless the qualifier
	"ALL" is used. */

	int saveall=FALSE;

	if (!strncmp(s,"ALL ",4)) {
		s+=4;
		saveall = TRUE;
	}
	printf("Saving '%s'...\n",s);
	save_data(s,saveall);
	if (echo) printf("Done.\n");
}

LOCAL void load_cmd(s)
char *s;
{	/* Load definitions from a file */
#ifdef TRACE
	if (trace) printf("Loading '%s'...\n",s);
#endif
	load_data(s);
	if (echo) printf("Done.\n");
}

LOCAL void do_system(s)
char *s;
{	/* Operating system escape */
#ifdef __DESMETC__
	char shell[128],parms[128];

	if (getenv("COMSPEC",shell)==0) {
		error = -1;
        errormsg = "Error: No COMSPEC environment variable.\n";
		return;
	}
	strcpy(parms,"/c");
	strncat(parms,s,124);
	exec(shell, parms);
#else
	if (system(s)) {
		error = -1;
        errormsg = "Error: Unable to run shell.\n";
	}
	/*
	error = -1;
    errormsg = "Shell is unavailable.\n";
	return;
	*/
#endif
}

EXPORT void do_cmd(buf)
char *buf;
{	/* This is the start of command processing.  If the buffer starts
	with a keyword, invoke that command, else assume it is an
	expression to be evaluated. */

	char *s;
	int i,len;
	NODEP n;
	static struct {
		char *name;
#ifndef __PROTOTYPES__
		void (*cmdptr)();
#else
		void (*cmdptr)(char const *s);
#endif
	} commands[] = {
		"DIMENSION",define_dimension,
		"UNIT",define_unit,
		"LET",do_let,
		"DEFINE",define_uf,
		"FRACTION",fraction_cmd,
		"LIST",list,
		"FACTOR",factor,
		"DELETE",delete,
		"SAVE",save_cmd,
		"LOAD",load_cmd,
		"INFO",info,
		"HELP",help,
		"?",querry,
		"SYSTEM",do_system,
		"QUIT",quit,
		"EXIT",quit,
		"BYE", quit,
		"BASE",base_cmd,
#ifdef TRACE
		"TRACE",trace_cmd,
#endif
		"",NULL
	};

	error = 0;			/* Initialize flags */
	errormsg = NULL;
	trimspc(buf);		/* Compress/eliminate extra whitespace */

	/*strupr(buf);*/
        s = buf;
        while (*s && !isspace(*s)) {
            *s = toupper(*s);
            ++s;
        }

	if (*buf=='\0')
		return;
	s = buf;
	for (i=0; commands[i].cmdptr; i++) {	/* scan for keywords */
		len = strlen(commands[i].name);
		if (!strncmp(buf,commands[i].name,len)) {
			s += len;
			trimspc(s);
			(*commands[i].cmdptr)( s );
			if (error)
				printerror();
			return;
		}
	}
        strupr(buf);
	n = solve(&s);		/* No keyword found, evaluate it */
	if (error) {
		printerror();
		if (*s)
			printf("--> %s\n",s);
		return;
	}
	skipspc(s);		/* Expression was successfully evaluated, */
	if (*s) {		/* so print out the answer in the desired units. */
        if (*s=='=') {
            /* printf("Automatically converting to '%s'...\n", s+1); */
			printanswer(n,s+1);
        } else {
			error = SYNERR;
			printerror();
			printf("--> %s\n",s);
			return;
		}
	} else
		printanswer(n,NULL);
	deallocnode(var->value);
	var->value = n;	/* save valid result in var PREVIOUS */
}

LOCAL char *GetStartupDir(argv0)
char *argv0;
{
    int len = strlen(argv0);
    char *s = strchr(argv0, '\0');

    if (!len || !s)
        return NULL;

    while (len) {
        if (*--s == '\\') {
            char *path = malloc(len + 1);
            strncpy(path, argv0, len);
            path[len] = '\0';
            return path;
        }
        len--;
    }
    return strdup(argv0);
}

void LegalCrap()
{
    printf(
        "\nPhysCalc\n"
        "Copyright (C) 1990 Marty White\n\n"
        "This program is free software; you can redistribute it and/or\n"
        "modify it under the terms of the GNU General Public License\n"
        "as published by the Free Software Foundation; either version 2\n"
        "of the License, or (at your option) any later version.\n\n"
        "This program is distributed in the hope that it will be useful,\n"
        "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
        "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
        "GNU General Public License for more details.\n\n"
        "You should have received a copy of the GNU General Public License\n"
        "along with this program; if not, write to the Free Software\n"
        "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n"
    );

}

int main(argc, argv)
int argc;
char *argv[];
{	/* Main program: Init vars, load unit & help data, enter main loop */
	static char buf[LARGEBUF];
	int quick=FALSE, argi=1, cmdline=FALSE;
    char datapath[65];
    char *startpath = GetStartupDir(argv[0]);

	buf[0]='\0';
	initnodes();
	initvars();

	if (argc > 1) {
		if (!stricmp(argv[1],"?") || !stricmp(argv[1],"/?") || !stricmp(argv[1],"-?")) {
			printf("Usage: physcalc [/Q] [expression]\n\n");
			return;
		}
		if (!stricmp(argv[1],"/q")) {
			quick=TRUE;
			argi++;
		}
	}
	while (argi < argc) {
		strcat(buf, argv[argi]);
		strcat(buf," ");
		argi++;
	}
	trimspc(buf);
	if (buf[0]) {
		quick=cmdline=TRUE;
	}

	if (!quick) {
        LegalCrap();
        /* pause_for_user(); */
        printf("\nEnter HELP for a very brief summary of commands.\n");
	}

    /* printf("Loading unit conversion data...\n"); */
#ifndef __GNUC__
    strcpy(datapath, startpath);
#else
    datapath[0] = '\0';
#endif
    strcat(datapath, "physcalc.phy");
    load_data(datapath);

	remember_old();

	if (cmdline) {
		do_cmd(buf);
	} else
		while (TRUE) {
			printf(">");
			fgets(buf, sizeof(buf), stdin);
			do_cmd(buf);
		}
}



syntax highlighted by Code2HTML, v. 0.9.1