home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / calculat / rpn30src.zip / RPN.C < prev    next >
C/C++ Source or Header  |  1990-05-30  |  11KB  |  398 lines

  1. /**
  2.  ** RPN
  3.  **
  4.  ** An rpn-style calculator, all thanks to Messrs. Hewlett and Packard.
  5.  **
  6.  ** The calculator's input machinery is implemented as a DFA, and
  7.  ** represents the bulk of the code.  Once specific functions are
  8.  ** recognized, they are performed easily.  "Wrapped around" the DFA
  9.  ** is some character pre-processing (in main()) that moves the display
  10.  ** around on the screen.
  11.  **
  12.  ** v3.0  90.05.29
  13.  **    Touchup from v2.3 feedback, new functions (esp. linear regr.),
  14.  **    constants.  Constants loadable from config. file.
  15.  **    Save-file output added.
  16.  **    RPNINST.EXE changes screen colors.
  17.  **    etc. etc. etc.
  18.  ** v2.3  90.05.02bobmon (aka ram)
  19.  **    Fix Register-0 bug, hex-digit (^C) bug (in process.c)
  20.  **    Check a configuration file for hex chars, colors
  21.  ** v2.1  90.01.04ram
  22.  **/
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <conio.h>
  26. #include <process.h>    /** for the shell escape **/
  27. #include <signal.h>    /** Needed for FPE handling. **/
  28. #include <setjmp.h>    /** Needed for FPE handling. **/
  29. #include <float.h>    /** Needed for FPE handling. **/
  30. #define MAIN
  31. #include "rpn.h"
  32. #include "display.h"
  33. #include "rpnio.h"
  34. #include "debug.h"
  35. #include "ftns.h"    /** new_const() needs to access memory[] **/
  36.  
  37. extern int cdecl directvideo;    /** Fast (IBM-compatible) screen accesses **/
  38.  
  39. const char whitespace[] = ", \t\n";    /* These separate option-line tokens */
  40. static char *option_token;        /* This passes the option to ftns */
  41.  
  42.  
  43. /**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
  44.  
  45. static char div_err[] = "(int) / by 0";
  46. static char ovr_err[] = "(int) overflow";
  47. char const *fpe_err[] =
  48. {
  49.     "no error", ovr_err, div_err, "bad operation",
  50.     (div_err + 6), (ovr_err + 6), "underflow", "precision", "UNKNOWN!"
  51. };
  52.  
  53. static jmp_buf fpe_jmp_buf;
  54.  
  55. void fpe_handler(int sig, int type, int *reglist)
  56. {
  57.     switch (type) {
  58.     case FPE_ZERODIVIDE: sig = 4;  break;    /** in order of likelihood **/
  59.     case FPE_OVERFLOW:     sig = 5;  break;
  60.     case FPE_UNDERFLOW:     sig = 6;  break;
  61.     case FPE_INTDIV0:     sig = 2;  break;
  62.     case FPE_INTOVFLOW:     sig = 1;  break;
  63.     case FPE_INVALID:     sig = 3;  break;
  64.     case FPE_INEXACT:     sig = 7;  break;
  65.     default: sig = 8;
  66.     }
  67.     longjmp(fpe_jmp_buf,sig);
  68. }
  69.  
  70. /**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
  71.  
  72. /*
  73. | This thing checks for and processes the new-hex-digit options.
  74. | If option_token is HEXCHAR then we're getting new characters.
  75. | If option_token is HEXVAL then we're getting the quasi-ASCII values
  76. | of the new characters.
  77. | Otherwise it's not a new-hex-digit option, so return with failure.
  78. */
  79. static int new_hex(void)
  80. {
  81.     int type, key;
  82.     char *token;
  83.  
  84.     if (0 == strcmp(option_token, "HEXVAL"))
  85.     type = 1;
  86.     else if (0 == strcmp(option_token, "HEXCHAR"))
  87.     type = 0;
  88.     else
  89.     return FALSE;    /* Not a new-hex-digit option */
  90.  
  91.     for (key = 10; key < MAX_BASE; ++key) {
  92.     if ( NULL == (token = strtok(NULL, whitespace)) ) {
  93.         fprintf(stderr, "digits past %d not specified.\n", key);
  94.         return FALSE;
  95.     }
  96.     DBG_FPRINTF((errfile,"token %s<<<, key: %d\n", token, key));
  97.  
  98.     digits[key] = ( type  ?  (int)strtol(token,NULL,0)  :  token[0] );
  99.     }
  100.     return TRUE;
  101. }
  102.  
  103. /**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
  104.  
  105. static int new_position(void)
  106. {
  107.     int top;
  108.     char *token;
  109.     if (0 == strcmp(option_token, "TOP"))
  110.     top = 1;
  111.     else if (0 == strcmp(option_token, "LEFT"))
  112.     top = 0;
  113.     else
  114.     return FALSE;    /* Not a position option */
  115.  
  116.     if ( NULL == (token = strtok(NULL, whitespace)) ) {
  117.     fprintf(stderr,"No position specified.\n");
  118.     return FALSE;
  119.     }
  120.     DBG_FPRINTF((errfile,"token %s<<<\n", token));
  121.     if (top)
  122.     TOP = (int)strtol(token, NULL, 0);
  123.     else
  124.     LEFT = (int)strtol(token, NULL, 0);
  125.     return TRUE;
  126. }
  127.  
  128. /**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
  129.  
  130. static int new_const(void)
  131. {
  132.     int i;
  133.     char *token;
  134.     if (0 != strcmp(option_token, "CONST"))
  135.     return FALSE;    /* Not a constant-maker option */
  136.  
  137.     if ( NULL != (token = strtok(NULL, whitespace))
  138.     && (0 <= (i = (int)strtol(token, NULL, NULL))) && i < MEMSIZE
  139.     && NULL != (token = strtok(NULL, whitespace)) )
  140.     {
  141.     memory[i] = strtod(token, NULL);
  142.     return TRUE;
  143.     }
  144.  
  145.     fprintf(stderr,"Bad CONST option %s<<<\n", option_token);
  146.     return FALSE;
  147. }
  148.  
  149. /**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
  150. /*
  151. | For each line in the options file, check it against the
  152. | valid options; if it matches one process it accordingly, then
  153. | continue with further lines.
  154. | Unrecognized options are ignored.
  155. */
  156. static int config_file(char *filename)
  157. {
  158.     FILE *rpn_file;            /* configuration file */
  159.     char options[161];
  160.  
  161.     if (NULL == (rpn_file = fopen(filename, "r")))
  162.     return FALSE;
  163.  
  164.     DBG_FPRINTF((errfile,"config file: %s\n", rpn_file));
  165.  
  166.     while ( fgets(options, 160, rpn_file) ) {
  167.  
  168.     DBG_FPRINTF((errfile,"options: %s<<<\n", options));
  169.  
  170.     option_token = strtok(options, whitespace);
  171.     if ( (option_token == NULL) || (option_token[0] == '#') )
  172.         continue;    /** it's a comment **/
  173.         
  174.     DBG_FPRINTF((errfile,"option_token %s<<<\n", option_token));
  175.  
  176.     if (0 == strcmp(option_token,"BIOS")) {
  177.         directvideo = 0;        /** BIOS screen calls **/
  178.         continue;
  179.     }
  180.  
  181.     if (new_hex())
  182.         continue;    /* ...the while() */
  183.  
  184.     if (new_position())
  185.         continue;    /* ...the while() */
  186.  
  187.     if (new_const())
  188.         continue;    /* ...the while() */
  189.  
  190.     /* ...else fall through to... */
  191.     DBG_FPRINTF((errfile,"BAD option: %s<<<\n", options));
  192.     fprintf(stderr, "%s:  bad option\n", options);
  193.     }
  194.     fclose(rpn_file);
  195.     return TRUE;
  196. }
  197.  
  198. /**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
  199.  
  200. static void orig_display(void)
  201. {
  202.     if (help_flag > 0) {
  203.     help_flag = 3;
  204.     toggle_help();
  205.     }
  206.     close_display();
  207. }
  208.  
  209. /**\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\**/
  210.  
  211. const char badfilemsg[] = "Bad or missing config file %s\n";
  212. static char full_filename[] = "\CONFIG.RPN";
  213. static char syntax_msg[] =
  214.     "command-line options:  -b <-t top> <-l left> <-c [config-file]>\n";
  215.  
  216. void cdecl main(int argc, char *argv[])
  217. {
  218.     int key, result;
  219.     char *rpn_env;
  220.     char *shell;
  221.  
  222.  
  223.     start_debug();    /* depending on debug.h, this may be null */
  224.  
  225.     directvideo = 1;        /** screen writes to video RAM **/
  226.  
  227.     /*
  228.     | Do any command-line options
  229.     */
  230.     for (++argv; argc > 1; ++argv, --argc) {
  231.  
  232.     if (0 == strcmp(*argv,"-b"))
  233.         directvideo = 0;        /** BIOS screen calls **/
  234.  
  235.     /*
  236.     | Repositioning the display window?
  237.     */
  238.     else if (0 == strcmp(*argv,"-t")) {
  239.         TOP = (int)strtol(*(++argv), NULL, 0);
  240.         DBG_FPRINTF((errfile,"new TOP: %d\n", TOP));
  241.         --argc;
  242.     } else if (0 == strcmp(*argv,"-l")) {
  243.         LEFT = (int)strtol(*(++argv), NULL, 0);
  244.         DBG_FPRINTF((errfile,"new LEFT: %d\n", LEFT));
  245.         --argc;
  246.     }
  247.  
  248.     /*
  249.     | Use a configuration file?
  250.     |
  251.     | If so:  then if the next arg doesn't look like an option, try to
  252.     | open it as a config file.  Otherwise, try the RPN env. variable,
  253.     | then the current & root directories.  If those fail, complain.
  254.     */
  255.     else if (0 == strcmp(*argv,"-c")) {
  256.         if ( *(argv+1) && ('-' != (*(argv+1))[0]) ) {
  257.         if ( !config_file(*(++argv)) )
  258.             fprintf(stderr, badfilemsg, *argv);
  259.         --argc;
  260.         } else if (
  261.         (NULL == (rpn_env = getenv("RPN")) || !config_file(rpn_env))
  262.         && !config_file(full_filename+1)    /** Current dir? **/
  263.         && !config_file(full_filename)    )    /** Root dir?    **/
  264.         {
  265.         fprintf(stderr, badfilemsg, full_filename+1);
  266.         }
  267.     }
  268.  
  269.     /*
  270.     | Otherwise, this is an unrecognized command-line option.
  271.     */
  272.     else
  273.         fprintf(stderr, syntax_msg);
  274.     }
  275.  
  276.     startup();        /** init. cursor & display positions, scroll-lock **/
  277.     init_machine();    /** ...the calculator automaton, that is. **/
  278.  
  279.     /*
  280.     | Setup to come back to this point if a floating-point error occurs.
  281.     */
  282.     result = setjmp(fpe_jmp_buf);
  283.     if (0 == result)        /** ...i.e., initial call to setjmp()... **/
  284.     open_display();
  285.     else {
  286.     _fpreset();
  287.     prterr("float", fpe_err[result]);
  288.     }
  289.  
  290.     /*
  291.     | Here it is --- the central loop of the calculator.  Get a key,
  292.     | do something about it, get another key, do something about it...
  293.     |
  294.     | If the key has to do with calculator input, then the process()
  295.     | function does the actual work.  If it is messing with the display,
  296.     | then do the work right here in the main body.  Process() may
  297.     | return a Termination-causing result.
  298.     */
  299.     do {
  300.     key = getkey();
  301.     if (key == -1)        /** Caught a change in keyboard status, **/
  302.         result = OK_VAL;    /**   i.e. the SCROLL-LOCK key        **/
  303.  
  304.     else if (orig_sl != scrolllock) {
  305.         result = NO_CHANGE;
  306.         orig_display();
  307.         if ((key == MOVELEFT) && (LEFT > 0)) {
  308.         --LEFT;
  309.         } else if ((key == MOVERIGHT) && (LEFT < 80-D_WIDTH)) {
  310.         ++LEFT;
  311.         } else if ((key == MOVEUP) && (TOP > 0)) {
  312.         --TOP;
  313.         } else if ((key == MOVEDOWN) && (TOP < 25-D_DEPTH)) {
  314.         ++TOP;
  315.         }
  316.         open_display();
  317.  
  318.         if (key == FTN_1 || key == FTN_2 || key == FTN_3
  319.         || key == FTN_10 || key == S_FTN_10 || key == C_FTN_10)
  320.         {
  321.         prterr("Turn off ScrollLock", "first. \\");
  322.         }
  323.     }
  324.  
  325.     else if (key == FTN_1) {
  326.         toggle_help();
  327.         result = NO_CHANGE;
  328.     }
  329.  
  330.     else if (key == FTN_3) {
  331.         if ((++notation) > 2)
  332.         notation = 0;        /** toggle display format **/
  333.         result = OK_VAL;
  334.     }
  335.  
  336.     else if (key == FTN_9) {
  337.         prterr("Please", "don't repeat that");
  338.         result = NO_CHANGE;
  339.     }
  340.  
  341.     else if (key == FTN_10)
  342.         result = QUIT_VAL;
  343.  
  344.     else if (key == C_FTN_10)    /** 0x18 == ctrl-X **/
  345.         result = ABORT_VAL;
  346.  
  347.     /*
  348.     | Not merely feeping creaturism, but Falloping Geaturism!
  349.     */
  350.     else if (key == S_FTN_10) {
  351.         shell = getenv("COMSPEC");
  352.         if (!shell)
  353.         prterr("spawn", "can't find shell");
  354.         else {
  355.         if (savefile) {
  356.             write_save = TRUE;
  357.             close_savefile();
  358.         } else if (write_save)
  359.             prterr("write_save", "MACHINE");
  360.  
  361.         prterr("To resume RPN, exit ", shell);
  362.         spawnl(P_WAIT, shell, NULL);
  363.         _fpreset();
  364.  
  365.         if (write_save) {
  366.             write_save = FALSE;
  367.             open_savefile(filename);
  368.         }
  369.         open_display();
  370.         }
  371.         result = OK_VAL;
  372.     }
  373.  
  374.     else {
  375.         signal(SIGFPE, fpe_handler);
  376.             result = process(key);
  377.     }
  378.  
  379.  
  380.     /*-------------------------------------------------------------*\
  381.     | Now do something based on the result of processing the key.    |
  382.     \*-------------------------------------------------------------*/
  383.         if (result == INIT_VAL)
  384.         init_machine();
  385.  
  386.         if (result != NO_CHANGE)
  387.         display();
  388.  
  389.     } while (result != QUIT_VAL && result != ABORT_VAL);
  390.  
  391.     if (result == QUIT_VAL)
  392.     orig_display();
  393.     if (0 != savefile)
  394.     close_savefile();
  395.  
  396.     shutdown();
  397. }
  398.