/* 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 #include "compiler.h" #ifdef __STDHEADERS__ #include #include #include /*#include */ /*#include */ /*#include */ #include #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 = \nor\n LET := \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 \nTo delete a function type:\n DELETE (\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 ['='] \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); } }