/*
*   This file released to public domain by the copyright holders 
*   including ArrayComm, Inc.
*   Author: Ted Merrill
*/

/* rpn.c -- numerical processing filter;
*       irpn -- using 32bit integer stack (compile with -DIRPN)
*       rpn -- using 64bit real stack   (compile with -DRRPN)
*/

/*
*  Bugs:
*       (rpn only) Evaluation of constants with sscanf may allow
*               trailing garbage.
*/ 

/*
*  Usage Instructions:
*
*       {i|r}rpn { [-n <ntimes>] <command list> }...
*       {i|r}rpn -h             # for help message
*
*       In a cycle, the commands in a command list are executed.
*       The command list may be one or arguments each containing
*       zero or more words.
*       The cycle is repeated <ntimes> if specified >= 0,
*       or infinitely if ntimes is specified < 0;
*       otherwise, infinitely if there is any input or output
*       (read or write) else just once.
*       Commands use a single evaluation stack,
*       pushing or popping the top of stack as needed.
*       The stack is 32bit integer for irpn and 64bit real for rpn.
*       The dup:<index> command allows access to lower elements
*       of the stack.
*       At the beginning of each cycle the stack
*       is cleared; then the commands are executed upon the stack.
*       At the end of the command list any unused stack data is
*       thrown away.
*       The simplest command list that does anything is:
*               'read write'
*       which reads in a C language-type ascii integer numeral (irpn)
*       or floating point numeral (rpn),
*       converts to "binary" and puts on the stack, and then
*       pops it off the stack and writes it out as a numeral
*       followed by a newline.
*       Note that read and write use ascii numerals
*       as do readdec, readhex, readoct, writedec, writehex, writeoct.
*       Use readi32, readi16, readu16, writei32, etc. for binary
*       numbers.
*       Some more useful examples are:
*               'read ++ write' # adds 1 to every number.
*               'readi32 swapw swapb writei32'  # convert dec to sun
*               'read writehex' # does hex dump of file
*               'read writei32' # converts from C numerals to i32
*               'read writei16' # from C numerals to i16
*               'read 2047 min 0 max write'     # clips to [0..2047]
*               'readr32 writei32'      # fix binary real numbers
*
*       The complete list of commands is given below.
*       Execution terminates "successfully" at the time that
*       end of file is reached, or if all requested cycles
*       have completed.
*       It is legal to have a command list with writes but no reads
*       (a program that generates constants)
*       or with reads but no writes (data sink?).
*       SPECIAL FEATURE: if there are no read or write commands,
*       then an IMPLICIT write is done! also in this case, if -n was not
*       used, than an IMPLICIT break is done after the IMPLICIT write is done.
*       Examples:
*       rpn 300 11 /           # prints 300/11 once
*       rpn '300 11 /'         # prints 300/11 once
*       rpn -n 10 300 11 /     # prints 300/11 10 times
*       rpn -n 10 '300 11 /'   # prints 300/11 10 times
*       rpn -n -1 '300 11 /'   # prints 300/11 forever
*       rpn '300 11 / write'   # prints 300/11 forever
*
*       To keep life simple, all commands pop and push a fixed
*       number of stack elements; the "dup:index" command
*       (which allows a form of subscripting) requires a constant
*       index;
*       command lists cannot inherit data from previous command lists;
*       and in general, the input data has almost no effect
*       on the path of the program.
*       Thus this program can/does determine in advance whether stack
*       overflow/underflow would occur, and does not need to check
*       for this dynamically.
*       This also means that conditionals (currently supported
*       only for irpn) evaluate both branches.
*       If you need something more complicated, try a real
*       programming language.
*
*
*  Error Detection:
*       The program detects command buffer overflow,
*       stack underflow or overflow, command sequence with
*       no writes,
*       data stack non-zero at end of command sequence.
*       End of input file on read terminates the program normally,
*               and immediately.
*       Currently there is no i/o error detection.
*/

#include        <ctype.h>
#include        <stdio.h>
#include        <string.h>
#include        <stdlib.h>

#include    <allocparse.h>


static STRING HelpLines[] =
{
"Usage: [i]rpn [<initial_arg>]... <cmd-list>       # infinite loop",
"       [i]rpn [<initial_arg>]... { -n <ntimes> <cmd-list> }...",
"       [i]rpn -h       # for this message.",
"where:",
"   <cmd-list> is one or more args each containing zero or more words;",
"       quoting of arguments is often a good idea!",
"   <ntimes> is no. of times to execute <cmd-list>, or -1 for",
"       infinite loop.",
"   <initial_arg> is :  ",
"       -h  -- for this help message, and exit(0)  ",
"       -nobuf -- turn off buffering of stdout.   ",
"Function:",
"       Uses the commands in <cmd-list> to read data on to a stack,",
"   perform operations on it, and pop data off stack and write out.",
"   End-of-file on input silently stops program.",
"   Logic flow accomdated using if, while, loop, end, break and breakif.  ",
"Note: irpn uses 32bit integer stack and rpn uses 64bit float stack.",
"Examples:",
"       irpn read 10 min 20 max writehex",
"               In an infinite loop, reads a C-type integer numeral,",
"               windows between 10 and 20 and writes result out in hex.",
"       rpn 'readr32 2.5 * writer32'",
"               In an infinite loop, reads a 32bit float onto 64bit",
"               float stack, multiplies by 2.5, and writes out",
"               32bit float result.",
"Commands are:  ((*) = rpn only; (+) = irpn only).",
"read           same as readnum (below). Reads C numeral.",
"write          same as writenum (below). Writes decimal numeral.",
"read<type>     (for example, readi32 or readi32B).",
"write<type>    (for example, writei32 or writei32L).",
"               read (write) a different type, converting",
"       to (from) the type used for the stack;",
"       where <type> is one of (B=big-endian, L=little-endina):",
"       i16[B|L] 16bit signed integer (\"binary\").",
"       u16[B|L] 16bit unsigned integer (\"binary\").",
"       i32[B|L] 32bit signed integer (\"binary\").",
"       u32[B|L] 32bit unsigned integer;",
"                    treated as i32 if long long not available (\"binary\").",
"               NOTE: if neither B or L suffix given, native byte order used. ",
"       r32     32bit real number; rounded on read by irpn.",
"       r64     64bit real number; rounded on read by irpn.",
"               NOTE: r32, r64 format is cpu native format only  ",
"       num     ascii numeral;",
"               on read, irpn accepts C language decimal,",
"               octal and hex notations;",
"               rpn accepts only floating point",
"               constants (decimal point optional);",
"               both irpn and rpn write in decimal",
"               (floating point as needed for rpn).",
"       hex     ascii hex integer.",
"               (0x not expected or recognized).",
"               Truncates to integer for rpn write.",
"       dec     ascii decimal integer even if begins with zero.",
"               irpn not read floating point numerals!",
"               rpn reads/writes floating point numerals.",
"       oct     ascii octal integer.",
"               Truncates to integer for rpn write.",
"dup            pushes a copy of the top stack element.",
"               Same as dup:-1 .",
"dup:<index>    pushes a copy of the stack element indentified",
"               by the index, where index is 0 for the bottom",
"               (beginning) of the stack, 1 for the next...",
"               and is -1 for the top (item that can be popped)",
"               of the stack, -2 for the next down...",
"               Index should be a C language integer constant.",
"pop            throws away the top stack element.",
"[-][0x]{digit(s)}",
"               push the constant value indicated on stack.",
"               digits may be preceded with 0x for hex",
"               in which case a-f and A-F are allowed.",
"               The negation may be applied even to octal",
"               or hex numerals.",
"               If first digit is 0, octal assumed, unless",
"               0x precedes digits, in which case hexadecimal.",
"[-][digits][.][digits][{e|E}[-|+]digit[digit]]",
"               (at least one digit required).",
"               (* rpn only) (if evaluation as integer numeral fails)",
"               push the constant real value on the stack.",
"+              push(pop()+pop())",
"-              temp=pop();push(pop()-temp)",
"       Thus '5 3 -' results in the value 2 on the stack.",
"*              push(pop()*pop())",
"/              temp=pop();push(pop()/temp)",
"       Thus '7 3 /' results in the value 2 on the irpn stack.",
"++             increment value on top of stack",
"--             decrement value on top of stack",
"% (or mod)     (+) temp=pop(); push(pop()%temp)  MODULUS",
"<<             (+) temp=pop(); push(pop()<<temp)",
"       Thus '1 3 <<' results in value 8 on the stack .",
">>             (+) temp=pop(); push(pop()>>temp)",
"&              (+) temp=pop(); push(pop()&temp)  BITWISE AND",
"|              (+) temp=pop(); push(pop()|temp)  BITWISE OR",
"&&             (+) logical and",
"||             (+) logical or",
"==,<=,<,>=,>   (+) (in)equality operators",
"?              (+) temp1=pop();temp2=pop();pop()?temp1:temp2",
"abs            push(abs(pop())",
"max            push(max(pop(),pop())",
"min            push(min(pop(),pop())",
"pi             (*) put the constant pi (3.14159...) on stack.",
"e              (*) put the natural log base (2.718..) on stack",
"sin            (*) push(sin(pop()))    arg in radians!",
"cos            (*) push(cos(pop()))    arg in radians!",
"tan            (*) push(tan(pop()))    arg in radians!",
"atan           (*) push(atan(pop()))   result in radians!",
"asin           (*) push(asin(pop()))   result in radians!",
"acos           (*) push(acos(pop()))   result in radians!",
"atan2          (*) temp=pop();push(atan2(pop(),temp))",
"sqrt           (*) push(sqrt(pop()))",
"exp            (*) push(exp(pop()))    e^x",
"ln             (*) push(ln(pop()))     log_e(x)",
"^ or pow       (*) push(pow(pop()))    x^y",
"floor          (*) push(floor(pop()))  round negatively",
"ceil           (*) push(ceil(pop()))   round positively",
"round          (*) push(round(pop()))  round to nearest integer",
"loop (or {) ... end (or })     begin infinite loop", 
"break[:nlevels]        break n loop levels (default 1)",
"               (stack depth reduced to non-break case of end of loop) ",
"breakif[:nlevels]   break n loop levels (default 1) if popped value nonzero ",
"               (stack depth reduced to non-break case of end of loop) ",
"if ... end             jump to end if popped value is zero ",
"end   (or })           jump back to loop ; or target of if ",
"stop                                   stop program",
(STRING) NULL            /* help list terminator */
};



/* Stack type, integer or real:
*/
#ifdef IRPN
#define stacktype long long
#endif
#ifdef RRPN
#define stacktype double
#endif


/* library routines:
*/
#ifdef RRPN
double sin(),cos(),tan(),asin(),acos(),atan();
double atan2(),sqrt(),exp(),pow(),log();
double floor(),ceil();
#endif /*RRPN*/


/*
*  error information
*/
char *progname = "";    /* program name */

/*-P-
  Help printing routine
*/
static void PrintHelp()
{
    STRING *HelpLine = HelpLines;
    for ( ; *HelpLine; HelpLine++ )
        printf("%s\n", *HelpLine );
}


/*
*  Routines to get data from std input and to put to std output.
*       Remember, some of these routines are for "binary" data.
*/
union data_buf
{
    unsigned char bytes[8];
    int i32;
    unsigned int u32;
    int i32buf[2];
    short i16;
    short i16buf[4];
    unsigned short u16;
    /* signed! */ char i8;
    /* signed! */ char i8buf[8];
    unsigned char u8;
    unsigned char u8buf[8];
    float r32;
    float r32buf[2];
    double r64;
} ;


/*-P-*/
static stacktype geti32()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    return buf.i32;             /* concatenate bytes as integer */
}

/*-P-*/
static stacktype geti32B()
{
    union data_buf buf;
    long long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[0]<<24) | (buf.bytes[1]<<16) | 
        (buf.bytes[2]<<8) | buf.bytes[3];
    if ( (value & 0x80000000LL) != 0 ) value = - (0x100000000LL - value);
    return value;
}

/*-P-*/
static stacktype geti32L()
{
    union data_buf buf;
    long long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[3]<<24) | (buf.bytes[2]<<16) | 
        (buf.bytes[1]<<8) | buf.bytes[0];
    if ( (value & 0x80000000LL) != 0 ) value = - (0x100000000LL - value);
    return value;
}

/*-P-*/
static stacktype getu32()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    return buf.u32;             /* concatenate bytes as integer */
}

/*-P-*/
static stacktype getu32B()
{
    union data_buf buf;
    unsigned long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[0]<<24) | (buf.bytes[1]<<16) | 
        (buf.bytes[2]<<8) | buf.bytes[3];
    return value;
}

/*-P-*/
static stacktype getu32L()
{
    union data_buf buf;
    unsigned long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[3]<<24) | (buf.bytes[2]<<16) | 
        (buf.bytes[1]<<8) | buf.bytes[0];
    return value;
}

/*-P-*/
static stacktype getr32()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
#ifdef IRPN
    if ( buf.r32 < 0 ) buf.i32 = buf.r32 - 0.5;
    else buf.i32 = buf.r32 + 0.5;       /* round to nearest integer */
    return buf.i32;             /* concatenate bytes as integer */
#endif
#ifdef RRPN
    return buf.r32;
#endif
}

/*-P-*/
static stacktype getr64()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    buf.bytes[2] = getchar();
    buf.bytes[3] = getchar();
    buf.bytes[4] = getchar();
    buf.bytes[5] = getchar();
    buf.bytes[6] = getchar();
    buf.bytes[7] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
#ifdef IRPN
    if ( buf.r64 < 0 ) buf.i32 = buf.r64 - 0.5;
    else buf.i32 = buf.r64 + 0.5;       /* round to nearest integer */
    return buf.i32;             /* concatenate bytes as integer */
#endif /*IRPN*/
#ifdef RRPN
    return buf.r64;
#endif /*RRPN*/
}

/*-P-*/
static stacktype geti16()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    return (int) buf.i16;
}

/*-P-*/
static stacktype geti16B()
{
    union data_buf buf;
    long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[0]<<8) | buf.bytes[1];
    if ( (value & 0x8000) != 0 ) value = - (0x10000 - value);
    return value;
}

/*-P-*/
static stacktype geti16L()
{
    union data_buf buf;
    long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[1]<<8) | buf.bytes[0];
    if ( (value & 0x8000) != 0 ) value = - (0x10000 - value);
    return value;
}

/*-P-*/
static stacktype getu16()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    return (unsigned) buf.u16;
}

/*-P-*/
static stacktype getu16B()
{
    union data_buf buf;
    unsigned long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[0]<<8) | buf.bytes[1];
    return value;
}

/*-P-*/
static stacktype getu16L()
{
    union data_buf buf;
    unsigned long value;

    buf.bytes[0] = getchar();
    buf.bytes[1] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    value = (buf.bytes[1]<<8) | buf.bytes[0];
    return value;
}

/*-P-*/
static stacktype geti8()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    return (int) buf.i8;
}

/*-P-*/
static stacktype getu8()
{
    union data_buf buf;

    buf.bytes[0] = getchar();
    if (feof(stdin)) exit(0);   /* successful exit if went beyond eof*/
    return (int) buf.u8;
}

/*-P-*/
static stacktype getoct()
{
    unsigned long value;
    int code;
    code = scanf( "%lo", &value );
    if ( code == 1 ) return value;
    if ( code == EOF ) exit(0);
    fprintf( stderr, "rpn: Bad octal numeral.\n");
    exit(1);
    return 0;   /* make lint happy */
}

/*-P-*/
static stacktype getdec()
{
    double value;
    int code;

    /* ouch for irpn it would be a lot better to use %Lf (%llf ?)
    *   but library support is iffy... so use floating pt. instead...
    */
    code = scanf( "%lf", &value );
    if ( code == 1 ) return value;
    if ( code == EOF ) exit(0);
    fprintf( stderr, "rpn: Bad decimal numeral.\n");
    exit(1);
    return 0;   /* make lint happy */
}

/*-P-*/
static stacktype gethex()
{
    unsigned long value;
    int code;
    code = scanf( "%lx", &value );
    if ( code == 1 ) return value;
    if ( code == EOF ) exit(0);
    fprintf( stderr, "rpn: Bad hexadecimal numeral.\n");
    exit(1);
    return 0;   /* make lint happy */
}

#ifdef IRPN
/*-P- Get decimal, octal or hex integer depending on input*/
static stacktype getnum()
{
    register int charcode;
    int minusflag = 0;

    /* skip white space */
    while ( (charcode = getchar()) != EOF && charcode <= ' ');

    /* leading minus? */
    if ( charcode == '-' )
    {
        minusflag = 1;
        charcode = getchar();
    }

    /* end of file means immediate successful death */
    if ( charcode == EOF ) exit(0);

    /* must have a digit now */
    if ( ! isdigit( charcode ) )
    {
        fprintf( stderr, "rpn: Bad numerical input.\n" );
        exit(1);
    }
    if ( charcode == '0' )      /* leading zero */
    {
        charcode = getchar();
        if ( charcode == 'x' || charcode == 'X' )
        {               /* hex */
            if ( minusflag ) return -1.0 * gethex();
            else return gethex();
        }
        else
        {               /* octal */
            if ( minusflag ) return -1.0 * getoct();
            else return getoct();
        }
    }
    else        /* no leading zero */
    {
       ungetc( charcode, stdin );
       if ( minusflag ) return - getdec();
       else return getdec();
    }
}
#endif /*IRPN*/

#ifdef RRPN
/*-P-*/
static stacktype getnum()
{
    double value;
    int code;
    code = scanf("%lf", &value);
    if ( code == 1 ) return value;
    if ( code == EOF ) exit(0);
    fprintf(stderr, "rpn: bad numeric input.\n");
    exit(1);
    return 0;   /* make lint happy */
}
#endif /*RRPN*/

/*-P-*/
static void puti32( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.i32 = *ptr;
    putchar(buf.bytes[0]);
    putchar(buf.bytes[1]);
    putchar(buf.bytes[2]);
    putchar(buf.bytes[3]);
}

/*-P-*/
static void puti32B( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.i32 = *ptr;
    putchar((buf.i32>>24)&0xFF);
    putchar((buf.i32>>16)&0xFF);
    putchar((buf.i32>> 8)&0xFF);
    putchar((buf.i32>> 0)&0xFF);
}

/*-P-*/
static void puti32L( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.i32 = *ptr;
    putchar((buf.i32>> 0)&0xFF);
    putchar((buf.i32>> 8)&0xFF);
    putchar((buf.i32>>16)&0xFF);
    putchar((buf.i32>>24)&0xFF);
}

/*-P-*/
static void putu32( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.u32 = *ptr;
    putchar(buf.bytes[0]);
    putchar(buf.bytes[1]);
    putchar(buf.bytes[2]);
    putchar(buf.bytes[3]);
}

/*-P-*/
static void putu32B( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.u32 = *ptr;
    putchar((buf.u32>>24)&0xFF);
    putchar((buf.u32>>16)&0xFF);
    putchar((buf.u32>> 8)&0xFF);
    putchar((buf.u32>> 0)&0xFF);
}

/*-P-*/
static void putu32L( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.u32 = *ptr;
    putchar((buf.u32>> 0)&0xFF);
    putchar((buf.u32>> 8)&0xFF);
    putchar((buf.u32>>16)&0xFF);
    putchar((buf.u32>>24)&0xFF);
}

/*-P-*/
static void putr32( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.r32 = *ptr;
    putchar(buf.bytes[0]);
    putchar(buf.bytes[1]);
    putchar(buf.bytes[2]);
    putchar(buf.bytes[3]);
}

/*-P-*/
static void putr64( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.r64 = *ptr;
    putchar(buf.bytes[0]);
    putchar(buf.bytes[1]);
    putchar(buf.bytes[2]);
    putchar(buf.bytes[3]);
    putchar(buf.bytes[4]);
    putchar(buf.bytes[5]);
    putchar(buf.bytes[6]);
    putchar(buf.bytes[7]);
}

/*-P-*/
static void puti16( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.i16 = *ptr;
    putchar(buf.bytes[0]);
    putchar(buf.bytes[1]);
}

/*-P-*/
static void puti16B( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.i16 = *ptr;
    putchar((buf.i16>> 8)&0xFF);
    putchar((buf.i16>> 0)&0xFF);
}

/*-P-*/
static void puti16L( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.i16 = *ptr;
    putchar((buf.i16>> 0)&0xFF);
    putchar((buf.i16>> 8)&0xFF);
}

/*-P-*/
static void putu16( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.u16 = *ptr;
    putchar(buf.bytes[0]);
    putchar(buf.bytes[1]);
}

/*-P-*/
static void putu16B( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.u16 = *ptr;
    putchar((buf.u16>> 8)&0xFF);
    putchar((buf.u16>> 0)&0xFF);
}

/*-P-*/
static void putu16L( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.u16 = *ptr;
    putchar((buf.u16>> 0)&0xFF);
    putchar((buf.u16>> 8)&0xFF);
}

/*-P-*/
static void puti8( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.i8 = *ptr;
    putchar(buf.bytes[0]);
}

/*-P-*/
static void putu8( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    buf.u8 = *ptr;
    putchar(buf.bytes[0]);
}

/*-P-*/
static void puthex( ptr )
        stacktype *ptr;
{
    int value = *ptr;
    printf("%x\n", value );
}

/*-P-*/
static void putdec( ptr )
        stacktype *ptr;
{
#ifdef IRPN
    double value = *ptr;
    printf("%.0f\n", value );
#endif
#ifdef RRPN
    printf("%.12G\n", *ptr );
#endif
}

/*-P-*/
static void putoct( ptr )
        stacktype *ptr;
{
    int value = *ptr;
    printf("%o\n", value );
}

/*-P-
   function to parse a C-type numeral in a portion of a string
   parses only integers; used for command line numerals
*/
static char *parsenum( ptr, valptr, validptr )
        /* return value is pointer to following char*/
        char *ptr;      /* input, pointer to string to parse */
        int *valptr;    /* *valptr returned value of numeral */
        int *validptr;  /* if *validptr is returned as 1 */
{
    register int value = 0;
    int minusflag = 0;
    register int charcode;

    *validptr = 0;      /* not valid yet */

    /* skip whitespace */
    while ( ((charcode = *ptr++) > '\0') && (charcode <= ' ') );

    /* preceding minus? */
    if ( charcode == '-' )
    {
        minusflag = 1;
        charcode = *ptr++;
    }

    /* check if at end of string or invalid char */
    if ( ! isdigit( charcode ) ) return --ptr;
    
    /* leading zero? */
    if ( charcode == '0' )
    {
        /* leading 0x for hex? */
        charcode = *ptr++;
        if ( charcode == 'x' || charcode == 'X' )
        {               /* hex */
            for (;;)
            {
                charcode = *ptr++;
                if ( isdigit( charcode ) )
                        value = value*16 + ( charcode-'0' );
                else if ( charcode >= 'a' && charcode <= 'f' )
                        value = value*16 + ( charcode+10-'a' );
                else if ( charcode >= 'A' && charcode <= 'F' )
                        value = value*16 + ( charcode+10-'A' );
                else break;
                *validptr = 1;  /* got at least 1 valid char */
            }
            --ptr;
        }
        else
        {               /* octal */
            --ptr;      /* push back the digit we compared with x */
            *validptr = 1;      /* we got at least a zero digit */
            for (;;)
            {
                charcode = *ptr++;
                if ( charcode >= '0' && charcode <= '7' )
                        value = value*8 + (charcode - '0');
                else break;
            }
            --ptr;
        }
    }
    else        /* no leading zero */
    {
       *validptr = 1;   /* we have seen a valid digit */
       do
       {
           value = 10*value + (charcode-'0');
       }
       while ( charcode = *ptr++, isdigit(charcode) );
       --ptr;
    }
    *valptr = value;
    if ( minusflag ) *valptr = -value;
    return ptr;
}

/*-P-*/
static int mustparsenum( ptr )
        char *ptr;      /* string to parse */
{
    int value;
    int validflag = 0;
    if ( *(parsenum(ptr, &value, &validflag)) ||
                !validflag )
    {
        fprintf(stderr,"%s: Invalid number on command line: %s\n",
                        progname, ptr);
        exit(1);
    }
    return value;
}

#ifdef RRPN
/*-P-*/
static double multiparsenum( ptr )
        char *ptr;      /* string to parse */
{
    int ivalue;
    double dvalue;
    int validflag = 0;

    /* try integer parsing first */
    if ( ! ( *(parsenum(ptr, &ivalue, &validflag)) ||
                !validflag ) )
    return ivalue;
    /* then try real number  parsing */
    if ( sscanf( ptr, "%lf", &dvalue ) == 1 )
        /* ^^^^^^^^^ bug: can leave trailing garbage */
    return dvalue;
    {
        fprintf(stderr,"%s: Invalid number on command line: %s\n",
                        progname, ptr);
        exit(1);
    }
    return 0;   /* make lint happy */
}
#endif

/*-P-*/
static void swapbyte( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    register char temp;
#ifdef IRPN
    buf.i32 = *ptr;
#endif
#ifdef RRPN
    buf.r64 = *ptr;
#endif
    temp = buf.bytes[0];
    buf.bytes[0] = buf.bytes[1];
    buf.bytes[1] = temp;
    temp = buf.bytes[2];
    buf.bytes[2] = buf.bytes[3];
    buf.bytes[3] = temp;
#ifdef RRPN
    temp = buf.bytes[4];
    buf.bytes[4] = buf.bytes[5];
    buf.bytes[5] = temp;
    temp = buf.bytes[6];
    buf.bytes[6] = buf.bytes[7];
    buf.bytes[7] = temp;
#endif
#ifdef IRPN
    *ptr = buf.i32;
#endif
#ifdef RRPN
    *ptr = buf.r64;
#endif
}


/*-P-*/
static void swapword( ptr )
    stacktype *ptr;
{
    union data_buf buf;
    register short int temp;
#ifdef IRPN
    buf.i32 = *ptr;
#endif
#ifdef RRPN
    buf.r64 = *ptr;
#endif
    temp = buf.i16buf[0];
    buf.i16buf[0] = buf.i16buf[1];
    buf.i16buf[1] = temp;
#ifdef RRPN
    temp = buf.i16buf[2];
    buf.i16buf[2] = buf.i16buf[3];
    buf.i16buf[3] = temp;
#endif
#ifdef IRPN
    *ptr = buf.i32;
#endif
#ifdef RRPN
    *ptr = buf.r64;
#endif
}


/*
* reverse polish notation recording structure 
*/
enum  operation
{       null_cmd=0,
        readi8, readu8,
        readi16, readu16, readi32, readu32, readr32, readr64,
        readi16B, readu16B, readi32B, readu32B, 
        readi16L, readu16L, readi32L, readu32L, 
        readnum, readhex, readdec, readoct,
        writei8, writeu8,
        writei16, writeu16, writei32, writeu32, writer32, writer64,
        writei16B, writeu16B, writei32B, writeu32B, 
        writei16L, writeu16L, writei32L, writeu32L, 
        writenum, writehex, writedec, writeoct,
        dup, pop,
        constant, add, subtract, multiply, divide, modulus,
        increment, decrement, swapb, swapw,
#ifdef IRPN
        lshift, rshift,
        bitand, bitor, bitnot, conditional,
        and, or, not, eq, le, lt, ge, gt,
#endif /*IPRN*/
        abs_cmd, min, max,
#ifdef RRPN
        sin_cmd, cos_cmd, tan_cmd,
        asin_cmd, acos_cmd, atan_cmd, atan2_cmd,
        sqrt_cmd, pow_cmd, exp_cmd, ln_cmd,
        floor_cmd, ceil_cmd, round_cmd,
#endif /*RRPN*/
        if_cmd, endif_cmd, loop_cmd, endloop_cmd,
        breakif_cmd, break_cmd, stop_cmd,
        endinput_cmd
        };

#define MAX_CMD 800
#define MAX_STACK MAX_CMD  /* stack should always be less than ncmds*/

struct rpn_cmds
{
    enum operation cmd_code;
    stacktype data;
    struct rpn_cmds *jump;  /* for control operands */
} cmds[MAX_CMD+2];

/*-P-
* function to build up the rpn commands
*       Returns no. of args consumed.
*/
static int rpn_build( argv, nobreak )
        char **argv;
        int nobreak;    /* nobreak != 0 means no autobreak on no i/o */
{
    int argc = 0;       /* number of args eaten */
    char *cmd_line = 0; /* no arg in use yet */
    int cmdN = 0;       /* index into cmds */
    int stackDepth = 0; /* initially, nothing on stack */
    int loopNest = 0;   /* loop nesting */
    int ifNest = 0;     /* if nesting */
    int nwrites = 0;    /* initially, no writes done */
    int nreads = 0;     /* initially, no reads done */
    char *nextcmd = 0;
    char *paramcmd;     /* parameter to command, follows command */
    for (;;)
    {
        cmd_line = nextcmd;     /* next cmd in arg if any */
        if ( ! cmd_line || ! *cmd_line )
        {       /* Present arg not in use or used up! */
            cmd_line = *(argv++);
            if ( ! cmd_line ) break;    /* end of args? */
            /* is this actually the begin of a new loop? */
            if ( !strcmp( cmd_line, "-n" ) ) break;
            argc++;     /* we eat another argument! */
        }
        while ( *cmd_line && (*cmd_line <= ' ') ) cmd_line++;
                                /* strip blanks, tabs, etc. */
        if ( ! *cmd_line ) 
        {       /* null terminator? */
            nextcmd = 0;        /* no next command at this point */
            continue;
        }
        /* look ahead for end of this cmd and begin of next */
        for ( nextcmd = cmd_line;
                *nextcmd > ' ';
                nextcmd++ );    /* end of command */
        if ( *nextcmd )
        {
            *nextcmd = '\0';    /* null terminate this command */
            nextcmd++;          /* point to next command */
        }
        /* is there a :param appended to command? */
        for ( paramcmd = cmd_line;
                *paramcmd && *paramcmd != ':';
                paramcmd++) ;
        if ( *paramcmd )        /* paramcmd points at colon! */
        {
            *paramcmd++ = '\0'; /* null out colon and advance paramptr*/
        }
        else paramcmd = (char *)0;      /* else null pointer */

        /*
        *  Which command?
        */
        cmds[cmdN].cmd_code = null_cmd; /* default, not found */
        cmds[cmdN].data = 0;
        cmds[cmdN].jump = NULL;

        /* Commands that pop three and push 1 */
#ifdef IRPN
        if (!strcmp(cmd_line,"?")) cmds[cmdN].cmd_code = conditional;
#endif /*IRPN*/
        /* check for stack violation */
        if ( cmds[cmdN].cmd_code != null_cmd )
        {
            if ((stackDepth -= 2) <= 0 )
                        /* these reduce stack depth by 2*/
            {
                /* if stackDepth is now zero, it means that
                *  we went to -1 and then added 1...
                */
                fprintf(stderr, "rpn: Evaluation stack underflow.\n");
                exit(1);
            }
            goto NextCmd;
        }

        /* Commands that pop two and push 1 */
        if (!strcmp(cmd_line,"+")) cmds[cmdN].cmd_code = add;
        else if (!strcmp(cmd_line,"-")) cmds[cmdN].cmd_code = subtract;
        else if (!strcmp(cmd_line,"*")) cmds[cmdN].cmd_code = multiply;
        else if (!strcmp(cmd_line,"/")) cmds[cmdN].cmd_code = divide;
#ifdef IRPN
        else if (!strcmp(cmd_line,"%")) cmds[cmdN].cmd_code = modulus;
        else if (!strcmp(cmd_line,"mod")) cmds[cmdN].cmd_code = modulus;
        else if (!strcmp(cmd_line,"<<")) cmds[cmdN].cmd_code = lshift;
        else if (!strcmp(cmd_line,">>")) cmds[cmdN].cmd_code = rshift;
        else if (!strcmp(cmd_line,"&")) cmds[cmdN].cmd_code = bitand;
        else if (!strcmp(cmd_line,"|")) cmds[cmdN].cmd_code = bitor;
        else if (!strcmp(cmd_line,"&&")) cmds[cmdN].cmd_code = and;
        else if (!strcmp(cmd_line,"||")) cmds[cmdN].cmd_code = or;
        else if (!strcmp(cmd_line,"==")) cmds[cmdN].cmd_code = eq;
        else if (!strcmp(cmd_line,"<")) cmds[cmdN].cmd_code = lt;
        else if (!strcmp(cmd_line,"<=")) cmds[cmdN].cmd_code = le;
        else if (!strcmp(cmd_line,">")) cmds[cmdN].cmd_code = gt;
        else if (!strcmp(cmd_line,">=")) cmds[cmdN].cmd_code = ge;
#endif /*IRPN*/
        else if (!strcmp(cmd_line,"max")) cmds[cmdN].cmd_code = max;
        else if (!strcmp(cmd_line,"min")) cmds[cmdN].cmd_code = min;
#ifdef RRPN
        else if (!strcmp(cmd_line,"atan2")) cmds[cmdN].cmd_code = atan2_cmd;
#endif /*RRPN*/
        /* check for stack violation */
        if ( cmds[cmdN].cmd_code != null_cmd )
        {
            if ( ! (--stackDepth) )/* these reduce stack depth by 1*/
            {
                /* if stackDepth is now zero, it means that
                *  we went to -1 and then added 1...
                */
                fprintf(stderr, "rpn: Evaluation stack underflow.\n");
                exit(1);
            }
            goto NextCmd;
        }

        /* commands that pop and then push 1 */
        if (!strcmp(cmd_line,"++")) cmds[cmdN].cmd_code = increment;
        else if (!strcmp(cmd_line,"--")) cmds[cmdN].cmd_code = decrement;
        else if (!strcmp(cmd_line,"swapb")) cmds[cmdN].cmd_code = swapb;
        else if (!strcmp(cmd_line,"swapw")) cmds[cmdN].cmd_code = swapw;
#ifdef IRPN
        else if (!strcmp(cmd_line,"~")) cmds[cmdN].cmd_code = bitnot;
        else if (!strcmp(cmd_line,"!")) cmds[cmdN].cmd_code = not;
#endif /*IRPN*/
        else if (!strcmp(cmd_line,"abs")) cmds[cmdN].cmd_code = abs_cmd;
#ifdef RRPN
        else if (!strcmp(cmd_line,"sin")) cmds[cmdN].cmd_code = sin_cmd;
        else if (!strcmp(cmd_line,"cos")) cmds[cmdN].cmd_code = cos_cmd;
        else if (!strcmp(cmd_line,"tan")) cmds[cmdN].cmd_code = tan_cmd;
        else if (!strcmp(cmd_line,"atan")) cmds[cmdN].cmd_code = atan_cmd;
        else if (!strcmp(cmd_line,"asin")) cmds[cmdN].cmd_code = asin_cmd;
        else if (!strcmp(cmd_line,"acos")) cmds[cmdN].cmd_code = acos_cmd;
        else if (!strcmp(cmd_line,"sqrt")) cmds[cmdN].cmd_code = sqrt_cmd;
        else if (!strcmp(cmd_line,"exp")) cmds[cmdN].cmd_code = exp_cmd;
        else if (!strcmp(cmd_line,"pow")) cmds[cmdN].cmd_code = pow_cmd;
        else if (!strcmp(cmd_line,"^")) cmds[cmdN].cmd_code = pow_cmd;
        else if (!strcmp(cmd_line,"ln")) cmds[cmdN].cmd_code = ln_cmd;
        else if (!strcmp(cmd_line,"floor")) cmds[cmdN].cmd_code = floor_cmd;
        else if (!strcmp(cmd_line,"ceil")) cmds[cmdN].cmd_code = ceil_cmd;
        else if (!strcmp(cmd_line,"round")) cmds[cmdN].cmd_code = round_cmd;
#endif /*RRPN*/
        /* check for stack violation */
        if ( cmds[cmdN].cmd_code != null_cmd )
        {
            if ( ! stackDepth )
            {
                /* if stackDepth is now zero, it means that
                *  we went to -1 and then added 1...
                */
                fprintf(stderr, "rpn: Evaluation stack underflow.\n");
                exit(1);
            }
            goto NextCmd;
        }

        /* commands that pop 1 */
        if (!strcmp(cmd_line,"pop")) cmds[cmdN].cmd_code = pop;
        else if ( ! strcmp(cmd_line,"breakif") ) 
        {
            int nlevels = 1;
            cmds[cmdN].cmd_code = breakif_cmd; 
            /* ? nreads++; */
            if (paramcmd) nlevels = mustparsenum(paramcmd);
            *(int *)(&cmds[cmdN].data) = nlevels;
        }
        else if (!strcmp(cmd_line,"if") )
        {
            cmds[cmdN].cmd_code = if_cmd;
            *(int *)(&cmds[cmdN].data) = stackDepth;
            ifNest++;
        }
        else if (!strcmp(cmd_line,"write")) 
                cmds[cmdN].cmd_code = writenum, nwrites++;
        else if (!strcmp(cmd_line,"writei32")) 
                cmds[cmdN].cmd_code = writei32, nwrites++;
        else if (!strcmp(cmd_line,"writei32B")) 
                cmds[cmdN].cmd_code = writei32B, nwrites++;
        else if (!strcmp(cmd_line,"writei32L")) 
                cmds[cmdN].cmd_code = writei32L, nwrites++;
        else if (!strcmp(cmd_line,"writeu32")) 
                cmds[cmdN].cmd_code = writeu32, nwrites++;
        else if (!strcmp(cmd_line,"writeu32B")) 
                cmds[cmdN].cmd_code = writeu32B, nwrites++;
        else if (!strcmp(cmd_line,"writeu32L")) 
                cmds[cmdN].cmd_code = writeu32L, nwrites++;
        else if (!strcmp(cmd_line,"writei16")) 
                cmds[cmdN].cmd_code = writei16, nwrites++;
        else if (!strcmp(cmd_line,"writei16B")) 
                cmds[cmdN].cmd_code = writei16B, nwrites++;
        else if (!strcmp(cmd_line,"writei16L")) 
                cmds[cmdN].cmd_code = writei16L, nwrites++;
        else if (!strcmp(cmd_line,"writeu16")) 
                cmds[cmdN].cmd_code = writeu16, nwrites++;
        else if (!strcmp(cmd_line,"writeu16B")) 
                cmds[cmdN].cmd_code = writeu16B, nwrites++;
        else if (!strcmp(cmd_line,"writeu16L")) 
                cmds[cmdN].cmd_code = writeu16L, nwrites++;
        else if (!strcmp(cmd_line,"writei8")) 
                cmds[cmdN].cmd_code = writei8, nwrites++;
        else if (!strcmp(cmd_line,"writeu8")) 
                cmds[cmdN].cmd_code = writeu8, nwrites++;
        else if (!strcmp(cmd_line,"writer32")) 
                cmds[cmdN].cmd_code = writer32, nwrites++;
        else if (!strcmp(cmd_line,"writer64")) 
                cmds[cmdN].cmd_code = writer64, nwrites++;
        else if (!strcmp(cmd_line,"writenum")) 
                cmds[cmdN].cmd_code = writenum, nwrites++;
        else if (!strcmp(cmd_line,"writehex")) 
                cmds[cmdN].cmd_code = writehex, nwrites++;
        else if (!strcmp(cmd_line,"writedec")) 
                cmds[cmdN].cmd_code = writedec, nwrites++;
        else if (!strcmp(cmd_line,"writeoct")) 
                cmds[cmdN].cmd_code = writeoct, nwrites++;
        if ( cmds[cmdN].cmd_code != null_cmd )
        {
            if ( --stackDepth < 0 )
            {
                fprintf(stderr, "rpn: Evaluation stack underflow.\n");
                exit(1);
            }
            goto NextCmd;
        }

        /* commands that add 1 to stack depth */
        if (isdigit(cmd_line[0])
                || (cmd_line[0] == '-' && isdigit(cmd_line[1]))
#ifdef RRPN
                || (cmd_line[0] == '-' && cmd_line[1] == '.')
                || (cmd_line[0] == '.')
#endif /*RRPN*/
        )
        {
            cmds[cmdN].cmd_code = constant;
#ifdef RRPN
            cmds[cmdN].data = multiparsenum( cmd_line );
#else
            cmds[cmdN].data = mustparsenum( cmd_line );
#endif
        }
#ifdef RRPN
        else if ( ! strcmp(cmd_line,"pi") )
        {
            cmds[cmdN].cmd_code = constant;
            cmds[cmdN].data = 3.1415926536;
        }
        else if ( ! strcmp(cmd_line,"e") )
        {
            cmds[cmdN].cmd_code = constant;
            cmds[cmdN].data = 2.7182818284;
        }
#endif /*RRPN*/
        else if ( ! strcmp(cmd_line,"dup") )
        {
            int index;
            cmds[cmdN].cmd_code = dup;
            if (paramcmd) index = mustparsenum(paramcmd);
            else index = -1;
            if ( ( index < -stackDepth )
                 || ( index >= stackDepth ) )
            {
                fprintf(stderr, "rpn: %s:%d outside of stack range.\n",
                                cmd_line, index );
                exit(1);
            }
            /* make index be relative to stack pointer */
            if ( index >= 0 ) index -= stackDepth;
            /* and store index in the data field */
            *(int *)(&cmds[cmdN].data) = index;
        }
        else if ( ! strcmp(cmd_line,"read") ) cmds[cmdN].cmd_code = readnum, nreads++;
        else if ( ! strcmp(cmd_line,"readi32") ) cmds[cmdN].cmd_code = readi32, nreads++;
        else if ( ! strcmp(cmd_line,"readi32B") ) cmds[cmdN].cmd_code = readi32B, nreads++;
        else if ( ! strcmp(cmd_line,"readi32L") ) cmds[cmdN].cmd_code = readi32L, nreads++;
        else if ( ! strcmp(cmd_line,"readu32") ) cmds[cmdN].cmd_code = readu32, nreads++;
        else if ( ! strcmp(cmd_line,"readu32B") ) cmds[cmdN].cmd_code = readu32B, nreads++;
        else if ( ! strcmp(cmd_line,"readu32L") ) cmds[cmdN].cmd_code = readu32L, nreads++;
        else if ( ! strcmp(cmd_line,"readi16") ) cmds[cmdN].cmd_code = readi16, nreads++;
        else if ( ! strcmp(cmd_line,"readi16B") ) cmds[cmdN].cmd_code = readi16B, nreads++;
        else if ( ! strcmp(cmd_line,"readi16L") ) cmds[cmdN].cmd_code = readi16L, nreads++;
        else if ( ! strcmp(cmd_line,"readu16") ) cmds[cmdN].cmd_code = readu16, nreads++;
        else if ( ! strcmp(cmd_line,"readu16B") ) cmds[cmdN].cmd_code = readu16B, nreads++;
        else if ( ! strcmp(cmd_line,"readu16L") ) cmds[cmdN].cmd_code = readu16L, nreads++;
        else if ( ! strcmp(cmd_line,"readi8") ) cmds[cmdN].cmd_code = readi8, nreads++;
        else if ( ! strcmp(cmd_line,"readu8") ) cmds[cmdN].cmd_code = readu8, nreads++;
        else if ( ! strcmp(cmd_line,"readr32") ) cmds[cmdN].cmd_code = readr32, nreads++;
        else if ( ! strcmp(cmd_line,"readr64") ) cmds[cmdN].cmd_code = readr64, nreads++;
        else if ( ! strcmp(cmd_line,"readnum") ) cmds[cmdN].cmd_code = readnum, nreads++;
        else if ( ! strcmp(cmd_line,"readhex") ) cmds[cmdN].cmd_code = readhex, nreads++;
        else if ( ! strcmp(cmd_line,"readdec") ) cmds[cmdN].cmd_code = readdec, nreads++;
        else if ( ! strcmp(cmd_line,"readoct") ) cmds[cmdN].cmd_code = readoct, nreads++;
        /* stack overflow? */
        if ( cmds[cmdN].cmd_code != null_cmd )
        {
            if ( ++stackDepth > MAX_STACK )
            {
                fprintf(stderr, "rpn: Evaluation stack overflow.\n");
                exit(1);
            }
            goto NextCmd;
        }

        /* Commands that do not touch stack: */
        if ( ! strcmp(cmd_line,"break") ) 
        {
            int nlevels = 1;
            cmds[cmdN].cmd_code = break_cmd; 
            nreads++;
            if (paramcmd) nlevels = mustparsenum(paramcmd);
            *(int *)(&cmds[cmdN].data) = nlevels;
        }
        else if ( ! strcmp(cmd_line,"stop") ) cmds[cmdN].cmd_code = stop_cmd, nreads++;
        else if (!strcmp(cmd_line,"loop") || ! strcmp(cmd_line,"{"))
        {
            cmds[cmdN].cmd_code = loop_cmd;
            *(int *)(&cmds[cmdN].data) = stackDepth;
            loopNest++;
        }
        else if (!strcmp(cmd_line,"end") || ! strcmp(cmd_line,"}"))
        {   /* end of loop etc. */
            int cmdPrev;
            for ( cmdPrev = cmdN-1; cmdPrev >= 0; cmdPrev-- )
            {   /* search for matching loop statement */
                /* this will be one without a pointer yet */
                if ( (cmds[cmdPrev].cmd_code == loop_cmd ||
                      cmds[cmdPrev].cmd_code == if_cmd ) &&
                    cmds[cmdPrev].jump == NULL )
                {
                    break;
                }
            }
            if ( cmdPrev < 0 )
            {
                fprintf(stderr, "rpn: unbalanced end statement\n");
                exit(1);
            }
            if ( cmds[cmdPrev].cmd_code == if_cmd )
            {
                cmds[cmdN].cmd_code = endif_cmd;
                ifNest--;
            }
            else
            {
                cmds[cmdN].cmd_code = endloop_cmd;
                loopNest--;
            }
            /* make the two point at each other */
            cmds[cmdPrev].jump = &cmds[cmdN];
            cmds[cmdN].jump = &cmds[cmdPrev];
            cmds[cmdN].data = cmds[cmdPrev].data;
            if ( *(int *)(&cmds[cmdN].data) != stackDepth )
            {
                fprintf(stderr, "rpn: loop/if block leaves or removes data from stack\n");
                exit(1);
            }
        }
        if ( cmds[cmdN].cmd_code != null_cmd ) goto NextCmd;


        fprintf( stderr, "rpn: Unknown command: %s.\n", cmd_line);
        exit(1);

        NextCmd:
        if ( ++cmdN > MAX_CMD )
        {
            fprintf( stderr, "rpn: Too many commands.\n");
            exit(1);
        }
    }
    cmds[cmdN].cmd_code = endinput_cmd;
    if ( ifNest )
    {
        fprintf(stderr, "rpn: unbalanced if\n");
        exit(1);
    }
    if ( loopNest )
    {
        fprintf(stderr, "rpn: unbalanced loop\n");
        exit(1);
    }
    if ( nwrites == 0 && nreads == 0 )
    { /* no reads or writes; do implicit write */
        cmds[cmdN].cmd_code = writenum, nwrites++;
        if ( ++cmdN > MAX_CMD )
        {
            fprintf( stderr, "rpn: Too many commands.\n");
            exit(1);
        }
        if ( ! nobreak )
        {
            cmds[cmdN].cmd_code = break_cmd;
            if ( ++cmdN > MAX_CMD )
            {
                fprintf( stderr, "rpn: Too many commands.\n");
                exit(1);
            }
        }
        cmds[cmdN].cmd_code = endinput_cmd;
    }
    return argc;        /* return no. of args eaten */
}

/*
*  rpn evaluation stack
*/
stacktype rpn_stack[MAX_STACK+2];       /* evaluation stack */

/*-P-
*  function to actually process the data using parsed commmands
*/
static void process( ntimes )
        int ntimes;     /* negative for infinite loop */
{
    register struct rpn_cmds *cmdP;
    register stacktype *stackptr = rpn_stack;
    int times_count = ntimes;
    int times_decr;

    if ( ntimes >= 0 )
        times_decr = 1;
    else
        times_decr = 0; /* don't decrement if negative ntimes */

    /* loop thru cycles of commands */
    for (; times_count; times_count -= times_decr )
    {
        stackptr = rpn_stack;   /* clear stack */
        for (cmdP = cmds; ;cmdP++)      /* infinite loop on commands */
        {
            switch ( cmdP->cmd_code )
            {
                case readi32:
                    *stackptr++ = geti32();
                break;
                case readi32B:
                    *stackptr++ = geti32B();
                break;
                case readi32L:
                    *stackptr++ = geti32L();
                break;
                case readu32:
                    *stackptr++ = getu32();
                break;
                case readu32B:
                    *stackptr++ = getu32B();
                break;
                case readu32L:
                    *stackptr++ = getu32L();
                break;
                case readi16:
                    *stackptr++ = geti16();
                break;
                case readi16B:
                    *stackptr++ = geti16B();
                break;
                case readi16L:
                    *stackptr++ = geti16L();
                break;
                case readu16:
                    *stackptr++ = getu16();
                break;
                case readu16B:
                    *stackptr++ = getu16B();
                break;
                case readu16L:
                    *stackptr++ = getu16L();
                break;
                case readi8:
                    *stackptr++ = geti8();
                break;
                case readu8:
                    *stackptr++ = getu8();
                break;
                case readr32:
                    *stackptr++ = getr32();
                break;
                case readr64:
                    *stackptr++ = getr64();
                break;
                case readnum:
                    *stackptr++ = getnum();
                break;
                case readhex:
                    *stackptr++ = gethex();
                break;
                case readoct:
                    *stackptr++ = getoct();
                break;
                case readdec:
                    *stackptr++ = getdec();
                break;
                case writei32:
                    puti32( --stackptr );
                break;
                case writei32B:
                    puti32B( --stackptr );
                break;
                case writei32L:
                    puti32L( --stackptr );
                break;
                case writeu32:
                    putu32( --stackptr );
                break;
                case writeu32B:
                    putu32B( --stackptr );
                break;
                case writeu32L:
                    putu32L( --stackptr );
                break;
                case writei16:
                    puti16( --stackptr );
                break;
                case writei16L:
                    puti16L( --stackptr );
                break;
                case writei16B:
                    puti16B( --stackptr );
                break;
                case writeu16:
                    putu16( --stackptr );
                break;
                case writeu16B:
                    putu16B( --stackptr );
                break;
                case writeu16L:
                    putu16L( --stackptr );
                break;
                case writei8:
                    puti8( --stackptr );
                break;
                case writeu8:
                    putu8( --stackptr );
                break;
                case writer32:
                    putr32( --stackptr );
                break;
                case writer64:
                    putr64( --stackptr );
                break;
                case writenum:
                case writedec:
                    putdec( --stackptr );
                break;
                case writeoct:
                    putoct( --stackptr );
                break;
                case writehex:
                    puthex( --stackptr );
                break;
                case constant:
                    *stackptr++ = cmdP->data;
                break;
                case add:
                    stackptr--;
                    stackptr[-1] += stackptr[0];
                break;
                case subtract:
                    stackptr--;
                    stackptr[-1] -= stackptr[0];
                break;
                case multiply:
                    stackptr--;
                    stackptr[-1] *= stackptr[0];
                break;
                case divide:
                    stackptr--;
                    stackptr[-1] /= stackptr[0];
                break;
#ifdef IRPN
                case modulus:
                    stackptr--;
                    stackptr[-1] %= stackptr[0];
                break;
                case lshift:
                    stackptr--;
                    stackptr[-1] <<= stackptr[0];
                break;
                case rshift:
                    stackptr--;
                    stackptr[-1] >>= stackptr[0];
                break;
                case bitand:
                    stackptr--;
                    stackptr[-1] &= stackptr[0];
                break;
                case bitor:
                    stackptr--;
                    stackptr[-1] |= stackptr[0];
                break;
                case bitnot:
                    stackptr[-1] = ~stackptr[-1];
                break;
                case and:
                    stackptr--;
                    stackptr[-1] = (stackptr[-1] && stackptr[0]);
                break;
                case or:
                    stackptr--;
                    stackptr[-1] = (stackptr[-1] || stackptr[0]);
                break;
                case not:
                    stackptr[-1] = !stackptr[-1];
                break;
                case eq:
                    stackptr--;
                    stackptr[-1] = (stackptr[-1] == stackptr[0]);
                break;
                case lt:
                    stackptr--;
                    stackptr[-1] = (stackptr[-1] < stackptr[0]);
                break;
                case le:
                    stackptr--;
                    stackptr[-1] = (stackptr[-1] <= stackptr[0]);
                break;
                case gt:
                    stackptr--;
                    stackptr[-1] = (stackptr[-1] > stackptr[0]);
                break;
                case ge:
                    stackptr--;
                    stackptr[-1] = (stackptr[-1] >= stackptr[0]);
                break;
                case conditional:
                    stackptr -= 2;
                    stackptr[-1] = (stackptr[-1] ?
                                stackptr[0] : stackptr[1]);
                break;
#endif /*IRPN*/
                case max:
                    stackptr--;
                    if ( stackptr[-1] < stackptr[0] )
                        stackptr[-1] = stackptr[0];
                break;
                case min:
                    stackptr--;
                    if ( stackptr[-1] > stackptr[0] )
                        stackptr[-1] = stackptr[0];
                break;
                case abs_cmd:
                    if (stackptr[-1] < 0 ) stackptr[-1] = -stackptr[-1];
                break;
                case dup:
                    stackptr[0] = stackptr[*(int *)(&cmdP->data)];
                    stackptr++;
                break;
                case pop:
                    stackptr--;
                break;
                case increment:
                    stackptr[-1]++;
                break;
                case decrement:
                    stackptr[-1]--;
                break;
                case swapb:
                    swapbyte( stackptr-1 );
                break;
                case swapw:
                    swapword( stackptr-1 );
                break;
#ifdef RRPN
                case sin_cmd:
                    stackptr[-1] = sin(stackptr[-1]);
                break;
                case cos_cmd:
                    stackptr[-1] = cos(stackptr[-1]);
                break;
                case tan_cmd:
                    stackptr[-1] = tan(stackptr[-1]);
                break;
                case atan_cmd:
                    stackptr[-1] = atan(stackptr[-1]);
                break;
                case asin_cmd:
                    stackptr[-1] = asin(stackptr[-1]);
                break;
                case acos_cmd:
                    stackptr[-1] = acos(stackptr[-1]);
                break;
                case atan2_cmd:
                    stackptr--;
                    stackptr[-1] = atan2( stackptr[-1], stackptr[0] );
                break;
                case pow_cmd:
                    stackptr--;
                    stackptr[-1] = pow( stackptr[-1], stackptr[0] );
                break;
                case sqrt_cmd:
                    stackptr[-1] = sqrt(stackptr[-1]);
                break;
                case exp_cmd:
                    stackptr[-1] = exp(stackptr[-1]);
                break;
                case ln_cmd:
                    stackptr[-1] = log(stackptr[-1]);
                break;
                case floor_cmd:
                    stackptr[-1] = floor(stackptr[-1]);
                break;
                case ceil_cmd:
                    stackptr[-1] = ceil(stackptr[-1]);
                break;
                case round_cmd:
                    if ( stackptr[-1] < 0 )
                        stackptr[-1] = (int)(stackptr[-1] - 0.5);
                    else stackptr[-1] = (int)(stackptr[-1] + 0.5);
                break;
#endif /*RRPN*/
                case endinput_cmd:
                goto CmdEnd;
                case break_cmd:
                {
                    int nlevels = *(int *)(&cmdP->data);
                    for ( ; nlevels > 0 && cmdP->cmd_code != endinput_cmd; cmdP++ )
                    {   /* advance to end, or end of loop */
                        if ( cmdP->cmd_code == endloop_cmd ) nlevels--;
                    }
                    /* break outside of any loop construct breaks
                     * a -n directive
                     */
                    if ( cmdP->cmd_code == endinput_cmd )
                        return;
                    if ( cmdP->cmd_code == endloop_cmd )
                    {
                        int stackDepth = *(int *)(&cmdP->data);
                        stackptr = rpn_stack + stackDepth;   /* clean stack */
                    }
                    /* auto-increment will put us after end of loop */
                }
                break;
                case breakif_cmd:
                {   /* break if top stack element is nonzero */
                    int nlevels = *(int *)(&cmdP->data);
                    int testvalue = (int)*--stackptr;
                    if ( testvalue )
                    {
                        for ( ; nlevels > 0 && cmdP->cmd_code != endinput_cmd; cmdP++ )
                        {   /* advance to end, or end of loop */
                            if ( cmdP->cmd_code == endloop_cmd ) nlevels--;
                        }
                        /* break outside of any loop construct breaks
                         * a -n directive
                         */
                        if ( cmdP->cmd_code == endinput_cmd )
                            return;
                        if ( cmdP->cmd_code == endloop_cmd )
                        {
                            int stackDepth = *(int *)(&cmdP->data);
                            stackptr = rpn_stack + stackDepth;   /* clean stack */
                        }
                        /* auto-increment will put us after end of loop */
                    }
                }
                break;
                case stop_cmd:
                    exit(0);
                break;
                case loop_cmd:
                    /* do nothing */
                break;
                case endloop_cmd:
                {
                    int stackDepth = *(int *)(&cmdP->data);
                    stackptr = rpn_stack + stackDepth;   /* clean stack */
                    cmdP = cmdP->jump; /* goes to AFTER begin loop */
                }
                break;
                case if_cmd:
                {
                    int testvalue = (int)*--stackptr;
                    if ( testvalue == 0 )
                        cmdP = cmdP->jump;
                    /* auto-increment will advance past endif_cmd...
                     * stack pointer should be correct as is.
                     * */
                }
                break;
                case endif_cmd:
                {
                    int stackDepth = *(int *)(&cmdP->data);
                    stackptr = rpn_stack + stackDepth;   /* clean stack */
                }
                break;
                default:
                    fprintf( stderr, "rpn: Program error.\n");
                    exit(1);
                break;
            }   /* end switch on command */
        }   /* end infinite loop on commands */
        CmdEnd: ;
    }   /* end infinite loop on input data */
}

/*-E-*/
int main( argc, argv )
        int argc;
        char **argv;
{
    int ntimes;

    progname = *argv++;
    while ( *argv )
    {   /* initial arguments */
        if ( !strcmp( *argv, "-h" ) )
        {
            PrintHelp();
            exit(0);
        }
        else
        if ( !strcmp( *argv, "-nobuf" ) )
        {
            setbuf(stdout,0);
            argv++;
        }
        else break; /* must be command arguments */
    }
    if ( ! *argv )
    {   /* no args? */
        fprintf( stderr,
            "%s: Enter %s -h    for help message.\n",
            progname, progname );
        exit(1);
    }
    while ( *argv )
    {
        int nobreak = 0;
        if (!strcmp(*argv, "-n"))
        {               /* -n for number of times command */
            argv++;
            if (! *argv ) goto missing_arg;
            ntimes = mustparsenum( *argv );
            argv++;
            if (! *argv ) goto missing_arg;
            nobreak = 1;
        }
        else ntimes = -1;       /* infinite loop */
        argv += rpn_build( argv, nobreak );
        process( ntimes );
    }
    exit(0);    /* success */

    missing_arg:
    fprintf(stderr, "rpn: Missing argument.\n");
    exit(1);
    return 0;
}
