home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS - Coast to Coast
/
simteldosarchivecoasttocoast2.iso
/
calculat
/
rpn30src.zip
/
RPN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-30
|
11KB
|
398 lines
/**
** RPN
**
** An rpn-style calculator, all thanks to Messrs. Hewlett and Packard.
**
** The calculator's input machinery is implemented as a DFA, and
** represents the bulk of the code. Once specific functions are
** recognized, they are performed easily. "Wrapped around" the DFA
** is some character pre-processing (in main()) that moves the display
** around on the screen.
**
** v3.0 90.05.29
** Touchup from v2.3 feedback, new functions (esp. linear regr.),
** constants. Constants loadable from config. file.
** Save-file output added.
** RPNINST.EXE changes screen colors.
** etc. etc. etc.
** v2.3 90.05.02bobmon (aka ram)
** Fix Register-0 bug, hex-digit (^C) bug (in process.c)
** Check a configuration file for hex chars, colors
** v2.1 90.01.04ram
**/
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <process.h> /** for the shell escape **/
#include <signal.h> /** Needed for FPE handling. **/
#include <setjmp.h> /** Needed for FPE handling. **/
#include <float.h> /** Needed for FPE handling. **/
#define MAIN
#include "rpn.h"
#include "display.h"
#include "rpnio.h"
#include "debug.h"
#include "ftns.h" /** new_const() needs to access memory[] **/
extern int cdecl directvideo; /** Fast (IBM-compatible) screen accesses **/
const char whitespace[] = ", \t\n"; /* These separate option-line tokens */
static char *option_token; /* This passes the option to ftns */
/**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
static char div_err[] = "(int) / by 0";
static char ovr_err[] = "(int) overflow";
char const *fpe_err[] =
{
"no error", ovr_err, div_err, "bad operation",
(div_err + 6), (ovr_err + 6), "underflow", "precision", "UNKNOWN!"
};
static jmp_buf fpe_jmp_buf;
void fpe_handler(int sig, int type, int *reglist)
{
switch (type) {
case FPE_ZERODIVIDE: sig = 4; break; /** in order of likelihood **/
case FPE_OVERFLOW: sig = 5; break;
case FPE_UNDERFLOW: sig = 6; break;
case FPE_INTDIV0: sig = 2; break;
case FPE_INTOVFLOW: sig = 1; break;
case FPE_INVALID: sig = 3; break;
case FPE_INEXACT: sig = 7; break;
default: sig = 8;
}
longjmp(fpe_jmp_buf,sig);
}
/**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
/*
| This thing checks for and processes the new-hex-digit options.
| If option_token is HEXCHAR then we're getting new characters.
| If option_token is HEXVAL then we're getting the quasi-ASCII values
| of the new characters.
| Otherwise it's not a new-hex-digit option, so return with failure.
*/
static int new_hex(void)
{
int type, key;
char *token;
if (0 == strcmp(option_token, "HEXVAL"))
type = 1;
else if (0 == strcmp(option_token, "HEXCHAR"))
type = 0;
else
return FALSE; /* Not a new-hex-digit option */
for (key = 10; key < MAX_BASE; ++key) {
if ( NULL == (token = strtok(NULL, whitespace)) ) {
fprintf(stderr, "digits past %d not specified.\n", key);
return FALSE;
}
DBG_FPRINTF((errfile,"token %s<<<, key: %d\n", token, key));
digits[key] = ( type ? (int)strtol(token,NULL,0) : token[0] );
}
return TRUE;
}
/**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
static int new_position(void)
{
int top;
char *token;
if (0 == strcmp(option_token, "TOP"))
top = 1;
else if (0 == strcmp(option_token, "LEFT"))
top = 0;
else
return FALSE; /* Not a position option */
if ( NULL == (token = strtok(NULL, whitespace)) ) {
fprintf(stderr,"No position specified.\n");
return FALSE;
}
DBG_FPRINTF((errfile,"token %s<<<\n", token));
if (top)
TOP = (int)strtol(token, NULL, 0);
else
LEFT = (int)strtol(token, NULL, 0);
return TRUE;
}
/**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
static int new_const(void)
{
int i;
char *token;
if (0 != strcmp(option_token, "CONST"))
return FALSE; /* Not a constant-maker option */
if ( NULL != (token = strtok(NULL, whitespace))
&& (0 <= (i = (int)strtol(token, NULL, NULL))) && i < MEMSIZE
&& NULL != (token = strtok(NULL, whitespace)) )
{
memory[i] = strtod(token, NULL);
return TRUE;
}
fprintf(stderr,"Bad CONST option %s<<<\n", option_token);
return FALSE;
}
/**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
/*
| For each line in the options file, check it against the
| valid options; if it matches one process it accordingly, then
| continue with further lines.
| Unrecognized options are ignored.
*/
static int config_file(char *filename)
{
FILE *rpn_file; /* configuration file */
char options[161];
if (NULL == (rpn_file = fopen(filename, "r")))
return FALSE;
DBG_FPRINTF((errfile,"config file: %s\n", rpn_file));
while ( fgets(options, 160, rpn_file) ) {
DBG_FPRINTF((errfile,"options: %s<<<\n", options));
option_token = strtok(options, whitespace);
if ( (option_token == NULL) || (option_token[0] == '#') )
continue; /** it's a comment **/
DBG_FPRINTF((errfile,"option_token %s<<<\n", option_token));
if (0 == strcmp(option_token,"BIOS")) {
directvideo = 0; /** BIOS screen calls **/
continue;
}
if (new_hex())
continue; /* ...the while() */
if (new_position())
continue; /* ...the while() */
if (new_const())
continue; /* ...the while() */
/* ...else fall through to... */
DBG_FPRINTF((errfile,"BAD option: %s<<<\n", options));
fprintf(stderr, "%s: bad option\n", options);
}
fclose(rpn_file);
return TRUE;
}
/**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
static void orig_display(void)
{
if (help_flag > 0) {
help_flag = 3;
toggle_help();
}
close_display();
}
/**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
const char badfilemsg[] = "Bad or missing config file %s\n";
static char full_filename[] = "\CONFIG.RPN";
static char syntax_msg[] =
"command-line options: -b <-t top> <-l left> <-c [config-file]>\n";
void cdecl main(int argc, char *argv[])
{
int key, result;
char *rpn_env;
char *shell;
start_debug(); /* depending on debug.h, this may be null */
directvideo = 1; /** screen writes to video RAM **/
/*
| Do any command-line options
*/
for (++argv; argc > 1; ++argv, --argc) {
if (0 == strcmp(*argv,"-b"))
directvideo = 0; /** BIOS screen calls **/
/*
| Repositioning the display window?
*/
else if (0 == strcmp(*argv,"-t")) {
TOP = (int)strtol(*(++argv), NULL, 0);
DBG_FPRINTF((errfile,"new TOP: %d\n", TOP));
--argc;
} else if (0 == strcmp(*argv,"-l")) {
LEFT = (int)strtol(*(++argv), NULL, 0);
DBG_FPRINTF((errfile,"new LEFT: %d\n", LEFT));
--argc;
}
/*
| Use a configuration file?
|
| If so: then if the next arg doesn't look like an option, try to
| open it as a config file. Otherwise, try the RPN env. variable,
| then the current & root directories. If those fail, complain.
*/
else if (0 == strcmp(*argv,"-c")) {
if ( *(argv+1) && ('-' != (*(argv+1))[0]) ) {
if ( !config_file(*(++argv)) )
fprintf(stderr, badfilemsg, *argv);
--argc;
} else if (
(NULL == (rpn_env = getenv("RPN")) || !config_file(rpn_env))
&& !config_file(full_filename+1) /** Current dir? **/
&& !config_file(full_filename) ) /** Root dir? **/
{
fprintf(stderr, badfilemsg, full_filename+1);
}
}
/*
| Otherwise, this is an unrecognized command-line option.
*/
else
fprintf(stderr, syntax_msg);
}
startup(); /** init. cursor & display positions, scroll-lock **/
init_machine(); /** ...the calculator automaton, that is. **/
/*
| Setup to come back to this point if a floating-point error occurs.
*/
result = setjmp(fpe_jmp_buf);
if (0 == result) /** ...i.e., initial call to setjmp()... **/
open_display();
else {
_fpreset();
prterr("float", fpe_err[result]);
}
/*
| Here it is --- the central loop of the calculator. Get a key,
| do something about it, get another key, do something about it...
|
| If the key has to do with calculator input, then the process()
| function does the actual work. If it is messing with the display,
| then do the work right here in the main body. Process() may
| return a Termination-causing result.
*/
do {
key = getkey();
if (key == -1) /** Caught a change in keyboard status, **/
result = OK_VAL; /** i.e. the SCROLL-LOCK key **/
else if (orig_sl != scrolllock) {
result = NO_CHANGE;
orig_display();
if ((key == MOVELEFT) && (LEFT > 0)) {
--LEFT;
} else if ((key == MOVERIGHT) && (LEFT < 80-D_WIDTH)) {
++LEFT;
} else if ((key == MOVEUP) && (TOP > 0)) {
--TOP;
} else if ((key == MOVEDOWN) && (TOP < 25-D_DEPTH)) {
++TOP;
}
open_display();
if (key == FTN_1 || key == FTN_2 || key == FTN_3
|| key == FTN_10 || key == S_FTN_10 || key == C_FTN_10)
{
prterr("Turn off ScrollLock", "first. \\");
}
}
else if (key == FTN_1) {
toggle_help();
result = NO_CHANGE;
}
else if (key == FTN_3) {
if ((++notation) > 2)
notation = 0; /** toggle display format **/
result = OK_VAL;
}
else if (key == FTN_9) {
prterr("Please", "don't repeat that");
result = NO_CHANGE;
}
else if (key == FTN_10)
result = QUIT_VAL;
else if (key == C_FTN_10) /** 0x18 == ctrl-X **/
result = ABORT_VAL;
/*
| Not merely feeping creaturism, but Falloping Geaturism!
*/
else if (key == S_FTN_10) {
shell = getenv("COMSPEC");
if (!shell)
prterr("spawn", "can't find shell");
else {
if (savefile) {
write_save = TRUE;
close_savefile();
} else if (write_save)
prterr("write_save", "MACHINE");
prterr("To resume RPN, exit ", shell);
spawnl(P_WAIT, shell, NULL);
_fpreset();
if (write_save) {
write_save = FALSE;
open_savefile(filename);
}
open_display();
}
result = OK_VAL;
}
else {
signal(SIGFPE, fpe_handler);
result = process(key);
}
/*-------------------------------------------------------------*\
| Now do something based on the result of processing the key. |
\*-------------------------------------------------------------*/
if (result == INIT_VAL)
init_machine();
if (result != NO_CHANGE)
display();
} while (result != QUIT_VAL && result != ABORT_VAL);
if (result == QUIT_VAL)
orig_display();
if (0 != savefile)
close_savefile();
shutdown();
}