home *** CD-ROM | disk | FTP | other *** search
- /**
- ** 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();
- }
-