/* "rp.c" copyright 1993-2006 by Franklin Webber
 * This source code may be freely copied if it is not modified.  It may
 * be modified if this copyright notice (the first 7 lines of the file)
 * is included unchanged at the beginning of every modified version.
 * The author requests that you send a copy of any modified version to him
 * at Franklin.Webber@computer.org .
 */
#define THE_VERSION "1T"
#define THE_DATE    "2006 May 1"

/* This program creates a desk calculator in which arithmetic expressions
 * are entered in Reverse Polish order (i.e., like some Hewlett-Packard
 * calculators), hence the name "rp".
 *
 * To compile:
 *   under DOS: "tcc rp.c"
 *   under Win: "bcc32 rp.c"
 *   under Unix: "gcc -DUNIX -o rp rp.c -lm"
 *
 * To execute:
 *   "rp [-{precision}] [-h] [-v] [-n] [-c] [-o]"
 * where
 *     [-{precision}] is the number of digits displayed after the decimal point
 *     [-h] indicates horizontal display style (default if input from console)
 *     [-v] indicates vertical display style (needs ANSI cursor control)
 *     [-n] indicates null display style (default if input was redirected)
 *     [-c] shows command set at startup
 *     [-o] shows command-line options
 *
 * Commands:
 *   Enter or ; or Space  : push current entry onto the stack
 *   Cntrl-p              : pop stack
 *   Cntrl-d              : duplicate top stack element
 *   Cntrl-x              : exchange top two stack elements
 *   Cntrl-f,b            : cycle stack forward, backward
 *   Cntrl-n              : negate top stack element
 *   Cntrl-i              : invert top stack element
 *   Cntrl-r              : sqrt
 *   Cntrl-e,l            : exp, ln
 *   Cntrl-a,s,c,t        : asin, sin, cos, tan
 *   e,p                  : e, pi
 *   +,-,*,/              : operate on top two stack elements
 *   <, >                 : increase, decrease precision
 *   ?                    : display command set (not in null display style)
 *   #                    : display top of stack (null display style only)
 *   @                    : display top as ASCII byte, then pop (null style)
 *   Escape, EoF, Cntrl-z : exit the program
 *   ^ can substitute for Cntrl (useful in scripts)
 */
#include <math.h>
#include <float.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#if defined (UNIX)
#include <unistd.h>
#include <termio.h>
struct termio ttystate, ttysave;
#elif defined (__WIN32__)
#include <io.h>
#include <conio.h>
#elif defined (__MSDOS__)
#include <bios.h>
#else
#error UNKNOWN PLATFORM
#endif

/* Define names for some ASCII chars.
 */
#define ESCAPE  '\033'
#define RUBOUT  '\177'
#define CNTRL_P '\020'
#define CNTRL_D '\004'
#define CNTRL_X '\030'
#define CNTRL_F '\006'
#define CNTRL_B '\002'
#define CNTRL_N '\016'
#define CNTRL_I '\011'
#define CNTRL_R '\022'
#define CNTRL_E '\005'
#define CNTRL_L '\014'
#define CNTRL_A '\001'
#define CNTRL_S '\023'
#define CNTRL_C '\003'
#define CNTRL_T '\024'
#define CNTRL_Z '\032'

/* Define booleans.
 */
typedef int Bool;
#define False     0
#define True      1
#define Or       ||
#define And      &&
#define Not       !

/* Declare flags determined by the command line.
 */
Bool commandSet = False;
Bool styleSpecified = False;
Bool inputRedirected = False;
Bool outputRedirected = False;

/* Declare global error data.
 */
int errors = 0;
int sigval = 0;

/* Declare the floating point type used for all operands.
 * (rp doesn't really work for type "long double" because the math library
 *  functions only work on type "double".  And the loss of precision with
 *  type "float" is very noticeable.  So "double" is really the only choice.)
 */
typedef double Real;
#define REAL_MAX DBL_MAX
#define REAL_MIN DBL_MIN
#define ZERO  ((Real)0.0)
#define ONE   ((Real)1.0)
char realFixed [] = "lf";
char realFloat [] = "lE";

/* Define some limits on operand values.
 * Trigonometric functions will not be applied to any Real whose
 * absolute value is larger than TRIG_MAX (ANSI C implementations
 * are supposed to handle such values but some don't).
 */
#define TRIG_MAX  ((Real)1e+14)

/* Declare the display style, which is a choice of different ways in
 * which the program can display the cells of the calculator's stack:
 * horizontal:  displays the top few stack cells on a single line;
 * vertical:    displays the stack from the bottom right of the screen upward;
 * null:        displays only the top of the stack when commanded to.
 * For vertical style the program requires a monitor that recognizes ANSI
 * cursor control.  In that case, ANSI.SYS must be installed when running DOS.
 */
typedef int Style;
#define Horizontal  2
#define Vertical    1
#define Null        0
Style style = Horizontal;

/* Define the screen coordinates of the command entry point,
 * which is one row below the stack display
 * (used only in vertical display style).
 *   2 <= COMMAND_ROW <= 24
 *   1 <= COMMAND_COL <= 79-WIDTH
 * The first screen row and column are numbered 1.
 */
#define COMMAND_ROW 24
#define COMMAND_COL 58

/* Define the maximum number of stack elements visible.
 * (used only in horizontal display style).
 */
#define VISIBLE 3

/* Define the maximum stack depth, and
 * declare a stack of operands and its current depth.
 *   0 <= depth <= DEPTH.
 */
#define DEPTH 100
int depth = 0;
Real stack [DEPTH];

/* Define the display width of a stack element.
 * The display width satisfies several constraints.  First, the width
 * must be expressed using a fixed number of decimal digits so that it can be 
 * packed into a fixed length format string.  Two digits is the only good
 * choice so
 *   10 <= WIDTH < 100
 * Second, the width must be large enough that the number of leading digits
 * in a fixed point number will always be positive for any precision.
 * See below for the analysis that implies
 *   12 <= WIDTH.
 * Third, the width must be large enough to display a floating point number
 * regardless of the precision.  See below for the analysis that implies
 *   17 <= WIDTH.
 * Finally, the width must be small enough that in the horizontal display style
 * VISIBLE+1 elements can be displayed on one line with one space
 * between them.  So
 *   WIDTH < 80/(VISIBLE+1).
 * 4 elements per line means
 *   WIDTH < 20.
 * 5 elements per line would be possible if WIDTH==15 but then,
 * according to the analysis below, precision <= 8.
 */
#define WIDTH 17

/* Declare variables that control the display of a stack element.
 * Precision is the number of digits after the decimal point.
 * Digits is the maximum number of digits before the decimal point.
 * In fixed point 2 bytes in addition to the precision and the digits must
 * be allowed in the display: one for the decimal point itself and
 * one for an optional sign, so
 *   digits + precision + 2 == WIDTH.
 * In floating point up to 5 more bytes must be allowed for the 3-digit
 * exponent, its sign, and an 'E'; also digits==1, so
 *   precision + 8 <= WIDTH.
 * The precision will be packed into a fixed length format string so
 * it must be expressed using exactly one decimal digit:
 *   0 <= precision <= 9.
 * Finally, there must be at least one leading digit
 * (exactly one when using floating point):
 *   0 < digits.
 * These constraints are used to limit the choice of WIDTH in the previous
 * paragraph, i.e., 17 <= WIDTH for floating point.  Also, digits will be
 * derived from precision and WIDTH when displaying fixed point:
 *   digits = WIDTH - precision - 2.
 */
int precision = 2;
int digits;

/* Define the conditions for underflow and overflow of the output;
 * when either condition is satisfied,
 * the output will use the floating point format instead of fixed point.
 *   tooSmall == pow (10.0, -precision)
 *   tooLarge == pow (10.0, digits)
 */
Real tooSmall;
Real tooLarge;
#define rabs(x) ((Real)fabs((double)x))
#define TOO_SMALL(x) (rabs(x) < tooSmall)
#define TOO_LARGE(x) (rabs(x) >= tooLarge)

/* Declare variables to hold the format strings used in I/O of stack elements.
 * The fixed point format should be "%<width>.<precision>lf" for type double
 * and the floating point format should be "%<width>.<precision>lE".
 * <width> takes two digits and <precision> takes one,
 * for a total of 8 bytes, 7 printing and a final null, in the format string.
 * The input format should be simply "%lf" for type double.
 */
char fixedFormat [8];
char floatFormat [8];
char inputFormat [8];

/************************************************/
/* Implement a function to call when changing the precision.
 * This function computes digits, format strings, overflow and underflow.
 */
static void SetFormat (void)
{
    if (precision > 9) precision = 9;
    if (precision < 0) precision = 0;
    digits = WIDTH - precision - 2;
    tooSmall = pow (10.0, -precision);
    tooLarge = pow (10.0, digits);
    if (style == Null)
    {
        sprintf (fixedFormat, "%%.%1d%-.2s", precision, realFixed);
        sprintf (floatFormat, "%%.%1d%-.2s", precision, realFloat);
    }
    else
    {
        sprintf (fixedFormat, "%%%2d.%1d%-.2s", WIDTH, precision, realFixed);
        sprintf (floatFormat, "%%%2d.%1d%-.2s", WIDTH, precision, realFloat);
    }
    sprintf (inputFormat, "%%%-.2s", realFixed);
}

/************************************************/
/* Implement cursor control operations.
 * Define the ANSI escape sequences necessary for vertical display style.
 */
#define ATTN "\033\133"
#define NORMAL 0
#define REVERSE 7

static void PutCursorSave (void)
{
    printf (ATTN);
    printf ("s");
}

static void PutCursorRestore (void)
{
    printf (ATTN);
    printf ("u");
}

static void PutCursor (int row, int col)
{
    printf (ATTN);
    printf ("%d;%df", row, col);
}

static void PutMode (int nat)
{
    printf (ATTN);
    printf ("%dm", nat);
}

/************************************************/
/* Implement stack operations: Empty, Push, Pop, Cycle.
 */
static void PutError (void)
{
    errors++;
    if (style != Null)
        putchar ('\a');
}

static void EmptyStack (void)
{
    int i;
    for (i=0; i<DEPTH; i++)
        stack [i] = ZERO;
    depth = 0;
}

static void PushStack (Real real)
{
    int i;
    if (depth < DEPTH)
        depth++;
    else
        PutError ();
    for (i=depth-1; i>0; i--)
        stack [i] = stack [i-1];
    stack [0] = real;
}

static Real PopStack (void)
{
    Real real = stack [0];
    int i;
    if (depth > 0)
        depth--;
    else
        PutError ();
    for (i=0; i<depth; i++)
        stack [i] = stack [i+1];
    return real;
}

static void CycleStackForward (void)
{
    Real real;
    int i;
    if (depth < 2)
        return;
    real = stack [depth-1];
    for (i=depth-1; i>0; i--)
        stack [i] = stack [i-1];
    stack [0] = real;
}

static void CycleStackBackward (void)
{
    Real real;
    int i;
    if (depth < 2)
        return;
    real = stack [0];
    for (i=0; i<depth; i++)
        stack [i] = stack [i+1];
    stack [depth-1] = real;
}

/************************************************/
/* Implement display operations.
 * Display the stack on lines COMMAND_ROW-1 through COMMAND_ROW-depth
 * in vertical display style and
 * display the top 4 stack elements on one line in horizontal display style.
 */
static void PutBlank (void)
{
    int i;
    for (i=0; i<WIDTH; i++)
        putchar (' ');
}

static void PutElement (Real real)
{
    if (real != ZERO And
        (TOO_LARGE (real) Or
         TOO_SMALL (real)))
        printf (floatFormat, real);
    else
        printf (fixedFormat, real);
}

static void PutHorizontalStack (void)
{
    int i;
    putchar ('\r');
    PutBlank ();
    for (i=0; i<VISIBLE; i++)
    {
        if (i < depth)
            PutElement (stack [i]);
        else
            PutBlank ();
        putchar (' ');
    }
    putchar ('\r');
}

static void PutVerticalStack (void)
{
    int i;
    int shown = depth;
    if (depth > COMMAND_ROW-2)
        shown = COMMAND_ROW-2;
    PutCursor (COMMAND_ROW-shown-1, COMMAND_COL); /* clear an extra row in */
    PutBlank ();                                  /* case stack was popped */
    for (i=shown-1; i>=0; i--)
    {
        PutCursor (COMMAND_ROW-i-1, COMMAND_COL);
        PutElement (stack [i]);                   /* write stack values */
    }
    PutCursor (COMMAND_ROW, COMMAND_COL);         /* clear an extra row */
    PutBlank ();                                  /* for entering new data */
    PutCursor (COMMAND_ROW, COMMAND_COL);
}

static void PutStack (void)
{
    switch (style) {
    case Horizontal:
        PutHorizontalStack ();
        break;
    case Vertical:
        PutVerticalStack ();
        break;
    }
}

static void PutCommandSet (void)
{
    printf ("\"rp -o\" shows command-line options\n");
    printf ("Enter or ; or Space to push number onto stack\n");
    printf ("Cntrl- p(op), d(uplicate), (e)x(change)\n");
    printf ("Cntrl- f(orward), b(ackward)\n");
    printf ("Cntrl- n(egate), i(nvert), (sq)r(oot), e(xp), l(n)\n");
    printf ("Cntrl- a(sin), s(in), c(os), t(an)\n");
    printf ("e, p(i)\n");
    printf ("+,-,*,/\n");
    printf ("<, > adjusts precision\n");
    printf ("? displays command set except in null display style\n");
    printf ("# displays top of stack in null display style\n");
    printf ("@ displays top as ASCII byte, then pops, in null display style\n");
    printf ("^ can substitute for Cntrl (useful in scripts)\n");
    printf ("Escape or EoF or Cntrl-z to exit\n");
}

static void PutHelp (void)
{
    int i;
    switch (style) {
    case Horizontal:
        putchar ('\r');
        for (i=0; i<=VISIBLE; i++)
            PutBlank ();
        for (i=0; i<VISIBLE; i++)
            putchar (' ');
        putchar ('\r');
        PutCommandSet ();
        break;
    case Vertical:
        PutCursorRestore ();
        PutMode (NORMAL);
        PutCommandSet ();
        PutMode (REVERSE);
        PutCursorSave ();
        break;
    }
}

/************************************************/
/* Implement the calculator's commands.
 * A number being entered on the command line is stored in a buffer.
 */
char buffer [WIDTH];
int count = 0;

static Bool IsNumeric (int ch)
{
    return isdigit (ch) Or
           ch == '.' Or
           (count > 0 And (ch == 'E' Or
                           (buffer [count-1] == 'E' And (ch == '+' Or
                                                         ch == '-'))));
}

static int ToControl (int ch)
{
    if ('A' <= ch And ch <= 'Z')
        return ch - 'A' + CNTRL_A;
    if ('a' <= ch And ch <= 'z')
        return ch - 'a' + CNTRL_A;
    return ch;
}

static int ToAscii (Real real)
{
    if (real < 0.)
        return 0;
    if (real > 255.)
        return 255;
    return (int) floor ((double) real);
}

static void DoRubout (void)
{
    if (count > 0)
    {
        count--;
        if (style != Null)
        {
            putchar ('\b');
            putchar (' ');
            putchar ('\b');
        }
    }
}

static void DoNumeric (int ch)
{
    if (count < WIDTH-1)
    {
        buffer [count++] = ch;
        if (style != Null)
            putchar (ch);
    }
}

static void DoNumber (void)
{
    Real value;
    if (count > 0)
    {
        buffer [count] = '\0';
        count = 0;
        if (sscanf (buffer, inputFormat, &value) == 1)
            PushStack (value);
        else
            PutError ();
    }
}

static void DoNullary (int ch)
{
    switch (ch) {
    case CNTRL_F:
        CycleStackForward ();
        break;
    case CNTRL_B:
        CycleStackBackward ();
        break;
    case 'e':
        PushStack ((Real) 2.7182818284590452354);
        break;
    case 'p':
        PushStack ((Real) 3.14159265358979323846);
        break;
    }
}

static void DoUnary (int ch)
{
    Real s;
    if (depth < 1)
        return;
    s = stack [0];
    switch (ch) {
    case CNTRL_P:
        (void) PopStack ();
        break;
    case CNTRL_D:
        PushStack (s);
        break;
    case CNTRL_N:
        stack [0] = -s;
        break;
    case CNTRL_I:
        if (s != ZERO)
            stack [0] = ONE / s;
        else
            PutError ();
        break;
    case CNTRL_R:
        if (s >= ZERO)
            stack [0] = (Real) sqrt ((double) s);
        else
            PutError ();
        break;
    case CNTRL_E:
        if (s <= (Real) log ((double) REAL_MAX))
            stack [0] = (Real) exp ((double) s);
        else
            PutError ();
        break;
    case CNTRL_L:
        if (s > ZERO)
            stack [0] = (Real) log ((double) s);
        else
            PutError ();
        break;
    case CNTRL_A:
        if (rabs (s) <= ONE)
            stack [0] = (Real) asin ((double) s);
        else
            PutError ();
        break;
    case CNTRL_S:
        if (rabs (s) <= TRIG_MAX)
            stack [0] = (Real) sin ((double) s);
        else
            PutError ();
        break;
    case CNTRL_C:
        if (rabs (s) <= TRIG_MAX)
            stack [0] = (Real) cos ((double) s);
        else
            PutError ();
        break;
    case CNTRL_T:
        if (rabs (s) <= TRIG_MAX)
            stack [0] = (Real) tan ((double) s);
        else
            PutError ();
        break;
    case '#':
        if (style == Null)
            PutElement (s);
        break;
    case '@':
        if (style == Null)
            putchar (ToAscii (PopStack ()));
        break;
    } 
}

static void DoBinary (int ch)
{
    Real s,t;
    if (depth < 1)
        return;
    if (depth==1 And ch=='-')
    {                            /* special case: user probably expects */
        PushStack (ZERO);        /* stack [1] to be zero                */
        CycleStackForward ();    /* even though he didn't enter it,     */
    }                            /* e.g. '5' then '-' yields -5         */
    if (depth < 2)
        return;
    s = stack [0];
    t = stack [1];
    switch (ch) {
    case CNTRL_X:
        stack [0] = t;
        stack [1] = s;
        break;
    case '+':
        if ((s > ZERO And t >  REAL_MAX - s) Or
            (s < ZERO And t < -REAL_MAX - s))
            PutError ();
        else
        {
            (void) PopStack ();
            stack [0] = t + s;
        }
        break;
    case '-':
        if ((s > ZERO And t < -REAL_MAX + s) Or
            (s < ZERO And t >  REAL_MAX + s))
            PutError ();
        else
        {
            (void) PopStack ();
            stack [0] = t - s;
        }
        break;
    case '*':
        s = rabs (s);
        t = rabs (t);
        if ((s > ONE And t > REAL_MAX/s) Or
            (ZERO < s And s < ONE And ZERO < t And t < REAL_MIN/s))
            PutError ();
        else
        {
            s = PopStack ();
            stack [0] *= s;
        }
        break;
    case '/':
        s = rabs (s);
        t = rabs (t);
        if ((s > ONE And ZERO < t And t < REAL_MIN*s) Or
            (ZERO < s And s < ONE And t > REAL_MAX*s) Or
            s == ZERO)
            PutError ();
        else
        {
            s = PopStack ();
            stack [0] /= s;
        }
        break;
    }
}

static void DoCommand (int ch)
{
    switch (ch) {
    case '?':
        PutHelp (); break;
    case '<':
        precision++; SetFormat (); break;
    case '>':
        precision--; SetFormat (); break;
    case CNTRL_F:
    case CNTRL_B:
    case 'e':
    case 'p':
        DoNullary (ch); break;
    case CNTRL_P:
    case CNTRL_D:
    case CNTRL_N:
    case CNTRL_I:
    case CNTRL_R:
    case CNTRL_E:
    case CNTRL_L:
    case CNTRL_A:
    case CNTRL_S:
    case CNTRL_C:
    case CNTRL_T:
    case '#':
    case '@':
        DoUnary (ch); break;
    case CNTRL_X:
    case '+':
    case '-':
    case '*':
    case '/':
        DoBinary (ch); break;
    }
}

/* If the escape char or cntrl-z is typed or EOF is seen,
 * return False to quit the program.
 * If a backspace is typed, delete the most recent char in the buffer.
 * If a digit is typed, put it at the end of the buffer.
 * Any other char may be a command, so convert a non-empty buffer into
 * a number on the top of the stack and execute the command.
 * Convert ^<letter> to Cntrl-<letter> before processing the char.
 */
static Bool DoChar (int ch)
{
    static Bool cntrl = False;

    if (cntrl == True)
        ch = ToControl (ch);
    cntrl = (ch == '^');

    if (ch == EOF Or
        ch == CNTRL_Z Or
        ch == ESCAPE)
        return False;
    if (ch == '\b' Or
        ch == RUBOUT)
        DoRubout ();
    else if (IsNumeric (ch))
        DoNumeric (ch);
    else
    {
        DoNumber ();
        DoCommand (ch);
        PutStack ();
    }
    return True;
}

/************************************************/
/* Implement startup and shutdown.
 */

/* Define error return macros.
 */
#define EXIT_(err)            { fprintf (stderr,"\n"); exit (err); }
#define EXIT0_(str,err)       { fprintf (stderr,str); EXIT_(err); }
#define EXIT1_(str,a1,err)    { fprintf (stderr,str,a1); EXIT_(err); }

/* Give help on how to invoke the program.
 */
#define USAGE_EXIT_(str) { PutUsage (); EXIT0_(str,1) }

static void PutUsage (void)
{
    fprintf (stderr, "copyright 1993-2006 by Franklin Webber\n");
    fprintf (stderr, "version %s of %s\n", THE_VERSION, THE_DATE);
    fprintf (stderr, "usage: rp [-{precision}] [-h] [-v] [-n] [-o] [-c]\n");
    fprintf (stderr, "where\n");
    fprintf (stderr, "    [-{precision}] specifies number of digits ");
    fprintf (stderr, "shown after the decimal point\n");
    fprintf (stderr, "    [-h] indicates horizontal display style ");
    fprintf (stderr, "(default if input from console)\n");
    fprintf (stderr, "    [-v] indicates vertical display style ");
    fprintf (stderr, "(needs ANSI cursor control)\n");
    fprintf (stderr, "    [-n] indicates null display style ");
    fprintf (stderr, "(default if input was redirected)\n");
    fprintf (stderr, "    [-c] shows command set at startup\n");
    fprintf (stderr, "    [-o] shows command-line options\n");
}

static void InitializeSwitch (char* str)
{
    if (Not strcmp (str, "h"))
    {
        if (styleSpecified And style != Horizontal)
            USAGE_EXIT_(">> you may specify at most one style")
        style = Horizontal;
        styleSpecified = True;
    }
    else if (Not strcmp (str, "v"))
    {
        if (styleSpecified And style != Vertical)
            USAGE_EXIT_(">> you may specify at most one style")
        style = Vertical;
        styleSpecified = True;
    }
    else if (Not strcmp (str, "n"))
    {
        if (styleSpecified And style != Null)
            USAGE_EXIT_(">> you may specify at most one style")
        style = Null;
        styleSpecified = True;
        commandSet = False;
    }
    else if (Not strcmp (str, "c"))
    {
        if (style != Null)
            commandSet = True;
    }
    else if (Not strcmp (str, "o"))
        USAGE_EXIT_(" ")
    else
        if (sscanf (str, "%d", &precision) != 1)
            USAGE_EXIT_(">> you may specify only integer precision")
}

static void InitializeTerminal (void)
{
#if defined (UNIX)

    if (Not isatty (0))
    {
        inputRedirected = True;
        if (Not styleSpecified)
            style = Null;
        return;
    }
    if (ioctl (0, TCGETA, &ttysave) < 0)
        EXIT0_("rp: cannot load terminal state", 2)
    ttystate = ttysave;
    ttystate.c_iflag &= ~IXON;
    ttystate.c_oflag &= ~OCRNL;
    ttystate.c_lflag &= ~ICANON;
    ttystate.c_lflag &= ~ISIG;
    ttystate.c_lflag &= ~ECHO;
    ttystate.c_cc [VMIN] = 1;
    ttystate.c_cc [VTIME] = 0;
    if (ioctl (0, TCSETA, &ttystate) < 0)
        EXIT0_("rp: cannot store terminal state", 2)

#elif defined (__WIN32__)

    if (Not isatty (0))
    {
        inputRedirected = True;
        if (Not styleSpecified)
            style = Null;
        setmode (fileno (stdin), O_BINARY);
    }
    if (Not isatty (1))
    {
        outputRedirected = True;
        setmode (fileno (stdout), O_BINARY);
    }

#else /* defined (__MSDOS__) */

    union REGS before, after;
    before.x.ax = 0x4400;
    before.x.bx = 0;                /* stdin */
    int86 (0x21, &before, &after);
    if (after.x.flags & 1)
        EXIT0_("rp: cannot get device data", 2)
    if ((after.x.dx & 0x81) != 0x81)
    {
        inputRedirected = True;
        if (Not styleSpecified)
            style = Null;
        setmode (fileno (stdin), O_BINARY);
    }
    before.x.ax = 0x4400;
    before.x.bx = 1;                /* stdout */
    int86 (0x21, &before, &after);
    if (after.x.flags & 1)
        EXIT0_("rp: cannot get device data", 2)
    if ((after.x.dx & 0x82) != 0x82)
    {
        outputRedirected = True;
        setmode (fileno (stdout), O_BINARY);
    }

#endif
}

static void FinalizeTerminal (void)
{
#if defined (UNIX)

    if (inputRedirected)
        return;
    if (ioctl (0, TCSETA, &ttysave) < 0)
        EXIT0_("rp: cannot restore terminal state", 2);

#else /* defined (__WIN32__) || defined (__MSDOS__) */

    if (inputRedirected)
        setmode (fileno (stdin), O_TEXT);
    if (outputRedirected)
        setmode (fileno (stdout), O_TEXT);

#endif
}

/* This handler for floating point exceptions should never be called,
 * but if it is, it notes this error and reinitializes itself
 * for the next error.
 */
static void FPE_handler (int sig)
{
    sigval = sig;
    PutError ();
    (void) signal (SIGFPE, &FPE_handler);
}

static void Initialize (int argc, char* argv [])
{
    while (argc--> 1)
        if (argv [argc] [0] == '-')
            InitializeSwitch (argv [argc] + 1);
        else
            USAGE_EXIT_(">> you may specify only options beginning with '-'")

    InitializeTerminal ();

    SetFormat ();
    EmptyStack ();
    if (commandSet)
        PutCommandSet ();
    if (style == Vertical)
    {
        PutMode (REVERSE);
        PutCursorSave ();
    }
    PutStack ();

    (void) signal (SIGFPE, &FPE_handler);
}

static void Finalize (void)
{
    (void) signal (SIGFPE, SIG_DFL);

    if (style == Vertical)
    {
        PutCursorRestore ();
        PutMode (NORMAL);
    }

    FinalizeTerminal ();
    printf ("\n");
}

#ifdef UNIX
#define getchr() getchar()
#else                         /* assume Microsoft if not Unix */
#define getchr() (inputRedirected ? getchar () : getch ())
#endif

/************************************************/
int main (int argc, char* argv [])
{
    Initialize (argc, argv);
    while (DoChar (getchr ()));
    Finalize ();
    return 0;
}
