RPN32 - Ukázkový program rpn32.c



Instrukční sada  •  Seznam instrukcí  •  Funkce API  •  Header  •  Příklady  •  Download



#include <stdio.h>
#include <ctype.h>      // isspace
#include <limits.h>     // CHAR_BIT
#include <stdarg.h>     // va_*
#include <stdlib.h>     // malloc, strtoul
#include <string.h>     // strstr, strcpy, strlen

#include "rpn32.h"



/*============================================================================*\
                                VYSTUP / VYPIS
\*============================================================================*/


// Uvodni retezec vypisu hlaseni
//
static char const       *reportprefixp = NULL;


/*
 * VYPIS HLASENI A OSTATNICH RADEK TEXTU
 */

#define  Outline(l)     puts(l)

static int Report(char const *formatp, ...)
{
    int     numch;
    va_list args;

    if(reportprefixp != NULL)
        for(numch = 0;  reportprefixp[numch] != '\0';  ++numch)
            putchar(reportprefixp[numch]);
    va_start(args, formatp);
    numch = vprintf(formatp, args);
    va_end(args);
    return(numch);
}


/*----------------------------------------------------------------------------*\
                                NAPOVEDA
\*----------------------------------------------------------------------------*/


// Rizeni vypisu - "default" hodnoty
//
#define  DFLT_VIEW_MODE_LIST    RPN32_VIEW_MODE_STR_ADDRESS
#define _DFLT_VIEW_MODE_TRACE   RPN32_VIEW_MODE_STR_SGN                     \
                                RPN32_VIEW_MODE_STR_DEC                     \
                                RPN32_VIEW_MODE_STR_STACK                   \
                                RPN32_VIEW_MODE_STR_REG_LASTX
#define  DFLT_VIEW_MODE_TRACE   _DFLT_VIEW_MODE_TRACE                       \
                                RPN32_VIEW_MODE_STR_ADDRESS
#define  DFLT_VIEW_MODE_RESULT  _DFLT_VIEW_MODE_TRACE                       \
                                RPN32_VIEW_MODE_STR_LINE


// Retezec uvozujici komentar v programu
//
#define  DFLT_LINE_COMMENT      "#"



/*............................................................................*\
                                KOMPLETNI TEXTY
\*............................................................................*/


// "Options" na "command-line"
//
#define  HELP_TEXT_OPTIONS                                                      \
"Use:\n"                                                                        \
"# rpn32 [options] pl1 [pl2 [ ... plN]] [= [[[regT] regZ] regY] regX] [=]\n"    \
"# rpn32 [options] filename               [[[[regT] regZ] regY] regX] [=]\n"    \
"\n"                                                                            \
"Options:\n"                                                                    \
"+--------+---------------------------------------------+------------------+\n" \
"| Option |                  Description                |     Default      |\n" \
"+--------+---------------------------------------------+------------------+\n" \
"| -s<N>  | Starting label                              | from begin       |\n" \
"| -l[S]  | LISTING alowed [mode]                       | suppressed [" DFLT_VIEW_MODE_LIST     "]   |\n"\
"| -L[S]  | LISTING ONLY (executing suppressed) [mode]  | suppressed [" DFLT_VIEW_MODE_LIST     "]   |\n"\
"| -u     | MEMORY ussage (reg's, labels & RTN pool)    | suppressed       |\n" \
"| -U     | MEMORY ussage ONLY (executing suppressed)   | suppressed       |\n" \
"| -m<S>  | TRACE & PAUSE [mode]                        | ["    DFLT_VIEW_MODE_TRACE    "]          |\n"\
"| -t     | TRACE alowed                                | suppressed       |\n" \
"| -r[S]  | RESULT [mode] (none -> like TRACE & PAUSE)  | ["    DFLT_VIEW_MODE_RESULT   "]          |\n"\
"| -e<S>  | Report prefix string                        | \"\"               |\n"\
"| -c<S>  | Comment string                              | \"" DFLT_LINE_COMMENT "\"              |\n"\
"+--------+---------------------------------------------+------------------+\n"


// Argumenty na "command-line"
//
#define  HELP_TEXT_ARGUMENTS                                                    \
"Arguments:\n"                                                                  \
"+--------------------------+----------------------------------------------+\n" \
"|         Argument         |                 Description                  |\n" \
"+--------------------------+----------------------------------------------+\n" \
"| pl1, pl2, ... plN        | Program lines (missing are read from stdin)  |\n" \
"| filename                 | File of program lines (\"-\" -> stdin is used) |\n"\
"| =                        | End of program                               |\n" \
"| regX, regY, regZ, regT   | Registers of RPN-stack (LCN alowed)          |\n" \
"| =                        | Interactive mode (type \"hlp\" to learn more)  |\n"\
"+--------------------------+----------------------------------------------+\n" \
"Loop Control Number (LCN) format: COUNTER,FINAL[,INCREMENT]\n"


// Znaky "formatovaciho retezce"
//
#define  HELP_TEXT_MODE_FORMAT                                                  \
"Characters of MODE (outputs control):\n"                                       \
"+------+-------------------------+------+---------------------------------+\n" \
"| Chr  |       Description       | Chr  |           Description           |\n" \
"+------+-------------------------+------+---------------------------------+\n" \
"|  " RPN32_VIEW_MODE_STR_SGN "   | Signed                  |"                  \
  " " RPN32_VIEW_MODE_STR_REG_X  RPN32_VIEW_MODE_STR_REG_Y  RPN32_VIEW_MODE_STR_REG_Z  RPN32_VIEW_MODE_STR_REG_T " | Registers of RPN-stack          |\n"\
"|  " RPN32_VIEW_MODE_STR_UNS "   | Unsigned                |"                  \
 "  " RPN32_VIEW_MODE_STR_STACK "   | All registers of RPN-stack      |\n"      \
"|  " RPN32_VIEW_MODE_STR_DEC "   | Decimal                 |"                  \
 "  " RPN32_VIEW_MODE_STR_REG_LASTX "   | LASTx register                  |\n"  \
"|  " RPN32_VIEW_MODE_STR_OCT "   | Octal                   |"                  \
 "  " RPN32_VIEW_MODE_STR_FLAGS "   | Flags                           |\n"      \
"|  " RPN32_VIEW_MODE_STR_HEX "   | Hexadecimal             |"                  \
 "  " RPN32_VIEW_MODE_STR_RTN "   | RTN pool level                  |\n"        \
"|  " RPN32_VIEW_MODE_STR_INDIRECT "   | Indirect                |"             \
 "  " RPN32_VIEW_MODE_STR_LOCNUM "   | Local registers pool level      |\n" \
"|  " RPN32_VIEW_MODE_STR_LOCREG "   | Local register          |"               \
 "  " RPN32_VIEW_MODE_STR_ADDRESS "   | Address of program line         |\n"    \
"|  " RPN32_VIEW_MODE_STR_SEPARATOR "   | Separator reg. numbers  |"            \
 "  " RPN32_VIEW_MODE_STR_CODE "   | Machine code                    |\n"       \
"|  " RPN32_VIEW_MODE_STR_COMPAT "   | RPNPROC compatibility   |"               \
 "  " RPN32_VIEW_MODE_STR_USUAL "   | Usual combination [" RPN32_VIEW_MODE_STR_MODE_USUAL "]         |\n"\
"|  " RPN32_VIEW_MODE_STR_ESCAPE "   | Escape character        |"               \
 "  " RPN32_VIEW_MODE_STR_LINE "   | Suppress program line mnemonics |\n"       \
"| <N>  | Memory register number  |"                                            \
 "      | Any other character is stored   |\n"      \
"+------+-------------------------+------+---------------------------------+\n"


// Priklady
//
#define  HELP_TEXT_EXAMPLES_MODE_1                                              \
                                "-m"                                            \
                                RPN32_VIEW_MODE_STR_UNS                         \
                                RPN32_VIEW_MODE_STR_HEX                         \
                                RPN32_VIEW_MODE_STR_REG_X                       \
                                RPN32_VIEW_MODE_STR_SGN                         \
                                RPN32_VIEW_MODE_STR_DEC                         \
                                RPN32_VIEW_MODE_STR_REG_X                       \
                                RPN32_VIEW_MODE_STR_UNS                         \
                                RPN32_VIEW_MODE_STR_HEX                         \
                                RPN32_VIEW_MODE_STR_REG_LASTX                   \
                                RPN32_VIEW_MODE_STR_SGN                         \
                                RPN32_VIEW_MODE_STR_DEC                         \
                                RPN32_VIEW_MODE_STR_REG_LASTX                   \
                                "0"                                             \
                                RPN32_VIEW_MODE_STR_SEPARATOR                   \
                                "1"                                             \
                                RPN32_VIEW_MODE_STR_INDIRECT "0"                \
                                RPN32_VIEW_MODE_STR_INDIRECT "1"                \
                                RPN32_VIEW_MODE_STR_ADDRESS
#define  HELP_TEXT_EXAMPLES_MODE_2                                              \
                                "\""                                            \
                                "-m\\c\\o\\un\\t\\d\\own\\=0"                   \
                                RPN32_VIEW_MODE_STR_LINE                        \
                                "\""
//
#define  HELP_TEXT_EXAMPLES                                                     \
"Examples:\n"                                                                   \
"# rpn32 <example.rpn\n"                                                        \
"# rpn32 + \"*\" = 12 0xAD 077\n"                                               \
"# rpn32 + x = 12 0xAD 077\n"                                                   \
"# rpn32 " HELP_TEXT_EXAMPLES_MODE_1 " -t example.rpn\n"                        \
"# rpn32 sto0 lbl0 rcl0 count pse isg0 gto0 = 4,18,2\n"                         \
"# rpn32 lcn sto0 lbl0 rcl0 count pse isg0 gto0 = 2 18 4\n"                     \
"# rpn32 " HELP_TEXT_EXAMPLES_MODE_2 " -r sto0 lbl0 pse dsz0 gto0 = 6\n"        \
"# rpn32 example.rpn =\n"                                                       \
"# rpn32 + \"*\" = 12 0xAD 077 =\n"                                             \
"# rpn32 =\n"                                                                   \
"# rpn32 ?reg19 =\n"                                                            \
"# substi <battery.rpn | rpn32 -t -s20 - = 36\n"


// Interactivni mod
//
#define  HELP_TEXT_INTERACTIVE                                                  \
"Interactive mode commands:\n"                                                  \
"+---------+---------------------------------------------------------------+\n" \
"| Command |                          Description                          |\n" \
"+---------+-------------------+-------------------------------------------+\n" \
"| hlp     | HELP              | This text                                 |\n" \
"| lst[S]  | LIST [mode]       | List of loaded program                    |\n" \
"| mem     | MEMORY usage      | List of used reg's, labels & RTN pool     |\n" \
"| spy[S]  | SPY [mode]     *) | Current state (registers, program line)   |\n" \
"| sst[S]  | SST [mode]     *) | SINGLE STEP & start of SINGLE STEP mode   |\n" \
"| cmd     | COMMAND           | End of SINGLE STEP mode                   |\n" \
"| run[S]  | RUN [mode]     *) | Run program from current program line     |\n" \
"| mod<S>  | MODE <mode>       | Set or...                                 |\n" \
"| mod     | MODE              | ...display default MODE                   |\n" \
"| bye     | BYE               | End of program                            |\n" \
"+---------+-------------------+-------------------------------------------+\n" \
"  *) MODE: <none> .... default MODE,\n"                                        \
"           <-> ....... inhibit,\n"                                             \
"           <+> ....... by command-line option \"-m\".\n"



/*============================================================================*\
                                SPOLECNE
\*============================================================================*/


// Znaky jednotlivych "options" na "command-line"
//
#define  OPTCHAR_1ST        '-'
//
#define  OPTCHAR_START      's'
#define  OPTCHAR_LSTONLY    'L'
#define  OPTCHAR_LIST       'l'
#define  OPTCHAR_MEMONLY    'U'
#define  OPTCHAR_MEMUSAGE   'u'
#define  OPTCHAR_MODE       'm'
#define  OPTCHAR_TRACE      't'
#define  OPTCHAR_RESULT     'r'
#define  OPTCHAR_REPORT     'e'
#define  OPTCHAR_COMMENT    'c'
#define  OPTCHAR_HELP       'h'
//
#define  OPTARGP(a)         (*(a) + 2)
//
static char const           *optmodelstp = DFLT_VIEW_MODE_LIST,
                            *optmodetrcp = DFLT_VIEW_MODE_TRACE,
                            *optmoderesp = DFLT_VIEW_MODE_RESULT,
                            *optcommentp = DFLT_LINE_COMMENT;


// Handle vrstvy "rpn32"
//
static rpn32hndl_t          hndp;



/*
 * CALLBACKY PRO VYPIS
 */

static int cb_rpn32_view(char const *linep,
                         void       *userdata)
{
    Report("%s%s\n", (char *)userdata, linep);
    return(RPN32_RTNVAL_DONE);
}

static int cb_rpn32_listmem(char const *linep,
                            void       *userdata)
{
    userdata = userdata;

    Outline(linep);
    return(RPN32_RTNVAL_DONE);
}



/*
 * TEXTOVA FORMA NAVRATOVE HODNOTY
 */

#define _RTNVAL_TEXT_IMOP       "Immediate operand"
#define _RTNVAL_TEXT_NUMBER     "Number of operands"
#define _RTNVAL_TEXT_OPERAND    "Bad operand"
#define _RTNVAL_TEXT_LBLDUP     "Label duplicated"
#define _RTNVAL_TEXT_MEMREQ     "Memory request"
#define _RTNVAL_TEXT_MEMORY     "Memory allocate"
#define _RTNVAL_TEXT_LBLOVF     "Label # overflow"
#define _RTNVAL_TEXT_LBLNEX     "Label nonexistent"
#define _RTNVAL_TEXT_ILLEGAL    "Illegal instruction"
#define _RTNVAL_TEXT_REGOVF     "Register # overflow"
#define _RTNVAL_TEXT_RTNOVF     "Return stack overflow"
#define _RTNVAL_TEXT_DIVBY0     "Divide by zero"
#define _RTNVAL_TEXT_INVALID    "Invalid data"

static char const *rtnval_text(int rtnval)
{
    static char const   *texpt[] = {RPN32_RTNVAL_ERROR_LIST(_RTNVAL_TEXT)};

    if(rtnval == RPN32_RTNVAL_STOP)
        return("Break");
    rtnval = RPN32_RTNVAL_ERROR_INDEX(rtnval);
    if((unsigned)rtnval >= sizeof(texpt) / sizeof(*texpt))
        return("(unknown)");
    return(texpt[rtnval]);
}



/*============================================================================*\
                                INICIALIZACE
\*============================================================================*/


/*----------------------------------------------------------------------------*\
                            OPTIONS NA COMMAND-LINE
\*----------------------------------------------------------------------------*/


// Prompty vypisu
//
#define  REPORT_PROMPT(t)           #t "> "
#define  REPORT_PROMPT_INIT         REPORT_PROMPT(INI)


// Inicializacni hodnoty
//
#define  INITOPT_FLAGS_LIST         001
#define  INITOPT_FLAGS_EXEC         002
#define  INITOPT_FLAGS_MEMUSAGE     010
//
struct initopts
{
    unsigned    flags;              // "INITOPT_FLAGS_*"
    int         startlbl;           // starting label
    int         runcbtype;          // "RPN32_RUN_CB_TYPE_*"
};



/*
 * CISLO NAVESTI
 */

static int init_options_startlbl(char const *argp)
{
    int     startlbl;
    char    *endp;

    startlbl = strtoul(argp, &endp, 0);
    if(endp != argp)
        if(*endp == '\0')
            return(startlbl);
    return(-1);
}



/*
 * VSTUPNI BOD
 */

static int init_options(int             argc,
                        char            *argv[],
                        struct initopts *initoptp)
{
#define  RETURN_MISSING()                                                   \
    if(*OPTARGP(argv) == '\0')                                              \
        return(Report(REPORT_PROMPT_INIT "Missing value: \"%s\"\n", *argv), -1)

    while(--argc)
    {
        ++argv;
        if(argv[0][0] == OPTCHAR_1ST)
            switch(argv[0][1])
            {
            case OPTCHAR_START:
                initoptp->startlbl = init_options_startlbl(OPTARGP(argv));
                if(initoptp->startlbl >= 0)
                    continue;
                Report(REPORT_PROMPT_INIT "Bad value (label): \"%s\"\n",
                                                                OPTARGP(argv));
                return(-1);

            case OPTCHAR_LSTONLY:
                initoptp->flags &= ~INITOPT_FLAGS_EXEC;

            case OPTCHAR_LIST:
                initoptp->flags |= INITOPT_FLAGS_LIST;
                if(*OPTARGP(argv) != '\0')
                    optmodelstp = OPTARGP(argv);
                continue;

            case OPTCHAR_MEMONLY:
                initoptp->flags &= ~INITOPT_FLAGS_EXEC;

            case OPTCHAR_MEMUSAGE:
                initoptp->flags |= INITOPT_FLAGS_MEMUSAGE;
                continue;

            case OPTCHAR_MODE:
                if(*OPTARGP(argv) != '\0')
                    optmodetrcp = OPTARGP(argv);
                continue;

            case OPTCHAR_TRACE:
                initoptp->flags |= INITOPT_FLAGS_EXEC;
                initoptp->runcbtype = RPN32_RUN_CB_TYPE_TRACE;
                continue;

            case OPTCHAR_RESULT:
                optmoderesp = OPTARGP(argv);
                continue;

            case OPTCHAR_REPORT:
                reportprefixp = OPTARGP(argv);
                continue;

            case OPTCHAR_COMMENT:
                RETURN_MISSING();
                optcommentp = OPTARGP(argv);
                continue;

            case OPTCHAR_HELP:
                puts(HELP_TEXT_OPTIONS);
                puts(HELP_TEXT_ARGUMENTS);
                puts(HELP_TEXT_MODE_FORMAT);
                puts(HELP_TEXT_EXAMPLES);
                return(-1);
            }
        break;
    }
    if(*optmoderesp == '\0')
        optmoderesp = optmodetrcp;
    return(argc);

#undef   RETURN_MISSING
}



/*----------------------------------------------------------------------------*\
                            NACTENI RPN PROGRAMU
\*----------------------------------------------------------------------------*/


// Spolecne casti callbacku
//
#define  Cbinitjob_memory(p)    ((p)->memoryv.memp = malloc((p)->memoryv.size))
#define  Cbinitjob_unknown(p)                                               \
                Report(REPORT_PROMPT_INIT "Unknown line: \"%s\"\n", (p)->linep)
/*
  rpn32initarg_t *p;
 */


// Cislo radku vstupniho souboru resp. argumentu na "command-line"
//
#define  LINENO_START           1
#define  Lineno_error(n)        Report(REPORT_PROMPT_INIT "Line #%u\n", (n))



/*............................................................................*\
                                Z COMMAND-LINE
\*............................................................................*/


// Test: je to ?
//
#define _ARG_2CHARS(a, b)       ((a) | (b) << CHAR_BIT)

#define _ARG_2CHARS_SEPARATOR   '='             // oddelovac argumentu
#define _ARG_2CHARS_XMULTI      'x'             // alternativni znak nasobeni
#define _ARG_2CHARS_STDIN       OPTCHAR_1ST     // nahradni jmeno pro "stdin"
//
#define  ARG_2CHARS_IS(k, t)    (_ARG_2CHARS((t)[0],        (t)[1]) ==      \
                                 _ARG_2CHARS(_ARG_2CHARS_##k, '\0'))



/*
 * CALLBACK
 */

struct fromargs
{
    unsigned    lineno;
    int         argc;
    char        **argv;
};

static int cb_init_job_fromarg(int            type,
                               rpn32initarg_t *iap,
                               void           *userdata)
{
#define  fromp      ((struct fromargs *)userdata)

    switch(type)
    {
    case RPN32_INIT_CB_TYPE_MEMORY:
        Cbinitjob_memory(iap);
        return(RPN32_RTNVAL_DONE);

    case RPN32_INIT_CB_TYPE_UNKNOWN:
        Cbinitjob_unknown(iap);
        return(RPN32_RTNVAL_DONE);
    }
    if(fromp->argc > 0)
        if(!ARG_2CHARS_IS(SEPARATOR, *fromp->argv))
        {
            iap->linep  =  ARG_2CHARS_IS(XMULTI, *fromp->argv)  ?
                                            RPN32_TEXT_CODE_STR_MUL
                                        :
                                            *fromp->argv;
            --fromp->argc;
            ++fromp->argv;
            ++fromp->lineno;
            return(RPN32_RTNVAL_DONE);
        }
    return(RPN32_RTNVAL_STOP);

#undef   fromp
}



/*
 * VSTUPNI BOD
 */

static int init_job_fromarg(int  *argcp,
                            char *argv[])
{
    int             test;
    struct fromargs fromv;

    fromv.argc = *argcp;
    fromv.argv = argv;
    fromv.lineno = LINENO_START - 1;
    test = rpn32_init(&hndp, cb_init_job_fromarg, &fromv);
    *argcp -= fromv.argc;
    if(RPN32_RTNVAL_IS_ERROR(test))
        Lineno_error(fromv.lineno);
    return(test);
}



/*............................................................................*\
                                ZE SOUBORU
\*............................................................................*/


// Vycteni radky textu ze souboru
//
#define  Line_get(l, f)     (fgets(l, sizeof(l) - 1, f) != NULL)
#define  Line_term(l)       ((l)[strlen(l) - 1] = '\0')


// Velikost (pocet znaku) jedne radky
//
#define  LINE_SIZE          80



/*
 * CALLBACK
 */

struct fromfiles
{
    FILE        *fp;
    char        *commentp;
    unsigned    lineno;
    char        linet[LINE_SIZE];
};

static int cb_init_job_fromfile(int            type,
                                rpn32initarg_t *iap,
                                void           *userdata)
{
#define  fromp      ((struct fromfiles *)userdata)

    switch(type)
    {
    case RPN32_INIT_CB_TYPE_MEMORY:
        Cbinitjob_memory(iap);
        return(RPN32_RTNVAL_DONE);

    case RPN32_INIT_CB_TYPE_UNKNOWN:
        if(fromp->commentp != NULL)
        {
            *fromp->commentp = *optcommentp;
            fromp->commentp = NULL;
        }
        Cbinitjob_unknown(iap);
        return(RPN32_RTNVAL_DONE);
    }
    while(Line_get(fromp->linet, fromp->fp))
    {
        Line_term(fromp->linet);
        iap->linep = fromp->linet;
        ++fromp->lineno;
        if(( fromp->commentp = strstr(iap->linep, optcommentp)) != NULL)
            *fromp->commentp = '\0';
        if(*iap->linep == '\0')
            continue;
        return(RPN32_RTNVAL_DONE);
    }
    return(RPN32_RTNVAL_STOP);

#undef   fromp
}



/*
 * VSTUPNI BOD
 */

static int init_job_fromfile(FILE *fp)
{
    int                 test;
    struct fromfiles    fromv;

    fromv.fp       = fp;
    fromv.commentp = NULL;
    fromv.lineno   = LINENO_START;
    test = rpn32_init(&hndp, cb_init_job_fromfile, &fromv);
    if(RPN32_RTNVAL_IS_ERROR(test))
        Lineno_error(fromv.lineno);
    return(test);
}


/*............................................................................*\
                            Z OBOU MOZNYCH ZDROJU
\*............................................................................*/


/*
 * VSTUPNI BOD
 */

static int init_job(int  argc,
                    char *argv[])
{
    int     test,
            inca;
    FILE    *fp;

    if(argc <= 0)
        return(init_job_fromfile(stdin));
    do
    {
        switch(0)
        {
        default:
            if(ARG_2CHARS_IS(STDIN, *argv))
            {
                fp = stdin;
                break;
            }
            if((fp = fopen(*argv, "r")) != NULL)
                break;
            inca = argc;
            test = init_job_fromarg(&inca, argv);
            continue;
        }
        test = init_job_fromfile(fp);
        fclose(fp);
        inca = 1;
    } while(0);
    if(RPN32_RTNVAL_IS_ERROR(test))
        return(test);
    argc -= inca;
    argv += inca;
    inca = RPN32_RTNVAL_DONE;
    while(argc > 0)
    {
        if(ARG_2CHARS_IS(SEPARATOR, *argv))
            inca = RPN32_RTNVAL_STOP;
        else
            if(RPN32_RTNVAL_IS_ERROR(test = rpn32_tenter(hndp, *argv)))
            {
                Report(REPORT_PROMPT_INIT "Bad value (value): \"%s\"\n", *argv);
                return(test);
            }
            else
                inca = RPN32_RTNVAL_DONE;
        --argc;
        ++argv;
    }
    return(inca);
}



/*============================================================================*\
                            PROVADENI RPN PROGRAMU
\*============================================================================*/


// Prompty
//
#define  VIEW_PROMPT_ERROR      REPORT_PROMPT(ERR)
#define  VIEW_PROMPT_TRACE      REPORT_PROMPT(TRC)
#define  VIEW_PROMPT_PSE        REPORT_PROMPT(PSE)
#define  VIEW_PROMPT_RESULT     REPORT_PROMPT(RES)
#define  VIEW_PROMPT_NONE       REPORT_PROMPT(>>>)
#define  VIEW_PROMPT_MODE       REPORT_PROMPT(MOD)
//
#define  ERRC_PROMPT_RUN        REPORT_PROMPT(RUN)
#define  ERRC_PROMPT_LIST       REPORT_PROMPT(LST)
#define  ERRC_PROMPT_SST        REPORT_PROMPT(SST)
#define  ERRC_PROMPT_GTO        REPORT_PROMPT(GTO)
#define  ERRC_PROMPT_INIT       REPORT_PROMPT_INIT


// Chybova hlaseni
//
#define  Errorcode(t, c)        Report(ERRC_PROMPT_##t "%d: %s\n",          \
                                       (c),                                 \
                                       rtnval_text(c))
#define  Errorline(m)           rpn32_view(hndp,                            \
                                           DFLT_VIEW_MODE_##m,              \
                                           cb_rpn32_view,                   \
                                           VIEW_PROMPT_ERROR)



/*
 * VYPIS PROGRAMU
 */

static int listing(char const *modep)
{
    int     test;

    if(modep[0] == '\0')
        modep = optmodelstp;
    test = rpn32_list(hndp, modep, cb_rpn32_listmem, NULL);
    if(!RPN32_RTNVAL_IS_ERROR(test))
        return(0);
    Errorcode(LIST, test);
    Errorline(LIST);
    return(-1);
}



/*
 * VYPISY: "TRACE" NEBO "PSE"
 */

static int cb_rpn32_run(int  type,
                        void *userdata)
{
    return(rpn32_view(hndp,
                      userdata,
                      cb_rpn32_view,
                      type == RPN32_RUN_CB_TYPE_TRACE  ?
                            VIEW_PROMPT_TRACE
                        :
                            VIEW_PROMPT_PSE));
}



/*
 * VSTUPNI BOD
 */

static int execute(int        type,
                   char const *modetrcp,
                   char const *moderesp)
{
#define  test   type

    test = rpn32_run(hndp, type, cb_rpn32_run, (void *)modetrcp);
    if(RPN32_RTNVAL_IS_ERROR(test))
    {
        Errorcode(RUN, test);
        Errorline(TRACE);
        return(-1);
    }
    rpn32_view(hndp, moderesp, cb_rpn32_view, VIEW_PROMPT_RESULT);
    return(0);

#undef   test
}



/*============================================================================*\
                                INTERAKTIVNI MOD
\*============================================================================*/


// "MODE" pro vypis/zobrazeni
//
static char                             interactivemodet[LINE_SIZE] =
                                        DFLT_VIEW_MODE_TRACE;



/*
 * URCENI "MODE"
 */

#define  INTERACTIVE_MODE_LINE0_DFLT    '\0'
#define  INTERACTIVE_MODE_LINE0_NONE    '-'     // bez vypisu
#define  INTERACTIVE_MODE_LINE0_OPT     '+'     // podle "optmodetrcp"

static char const *interactive_mode(char const *linep)
{
    switch(linep[0])
    {
    case INTERACTIVE_MODE_LINE0_DFLT:
        linep = interactivemodet;
        break;

    case INTERACTIVE_MODE_LINE0_NONE:
        linep = NULL;
        break;

    case INTERACTIVE_MODE_LINE0_OPT:
        linep = optmodetrcp;
    }
    return(linep);
}



// Navratove hodnoty
//
#define  INTERACTIVE_RTNVAL_BYE         (RPN32_RTNVAL_USER + 1)
#define  INTERACTIVE_RTNVAL_NONE        (RPN32_RTNVAL_USER + 0)
#define  INTERACTIVE_RTNVAL_CMD          RPN32_RTNVAL_STOP
#define  INTERACTIVE_RTNVAL_SST          RPN32_RTNVAL_DONE



/*
 * COMMAND NELZE VYKONAT
 */

static int interactive_sorry(char const *texp)
{
    printf("Sorry... (%s)\n", texp);
    return(INTERACTIVE_RTNVAL_NONE);
}

static int interactive_sorry_arg()
{
    return(interactive_sorry("Argument not expected"));
}



// Vypis/zobrazeni
//
#define  interactive_spy(m)                                                 \
         rpn32_view(hndp, interactive_mode(m), cb_rpn32_view, VIEW_PROMPT_NONE)
#define  interactive_spy_dflt()                                             \
         interactive_spy(interactivemodet)



/*----------------------------------------------------------------------------*\
                            JEDNOTLIVE COMMANDY
\*----------------------------------------------------------------------------*/


// Textovy tvar prikazu
//
#define  INTERACTIVE_CMD_TEXT_LIST      "lst"
#define  INTERACTIVE_CMD_TEXT_MEMUSAGE  "mem"
#define  INTERACTIVE_CMD_TEXT_SPY       "spy"
#define  INTERACTIVE_CMD_TEXT_SST       "sst"
#define  INTERACTIVE_CMD_TEXT_RUN       "run"
#define  INTERACTIVE_CMD_TEXT_COMMAND   "cmd"
#define  INTERACTIVE_CMD_TEXT_MODE      "mod"
#define  INTERACTIVE_CMD_TEXT_HELP      "hlp"
#define  INTERACTIVE_CMD_TEXT_BYE       "bye"
//
#define  INTERACTIVE_CMD_LEN            3


// Spolecne promenne
//
struct inters
{
    char const  *prompt_;
    int         (*fncp)(struct inters *);
    char const  *linep,
                *sstmodep;
    char        linet[LINE_SIZE];
};
//
static char const           interSSTprompt_[] = REPORT_PROMPT(SST),
                            interCMDprompt_[] = REPORT_PROMPT(CMD);
//
#define  INTERS_IS(k, p)    ((p)->prompt_ == inter##k##prompt_)
#define  INTERS_SET(k, p)   ((p)->prompt_ =  inter##k##prompt_)
#define  INTERS_PROMPT(p)   ((p)->prompt_)



/*
 * VYPIS
 */

static int interactive_cmd_LIST(struct inters *interp)
{
    listing(interp->linep);
    return(INTERACTIVE_RTNVAL_NONE);
}



/*
 * VYPIS ROZSAHU CISEL PAMETOVYCH REGISTRU A NAVESTI
 */

#define  Memusage()     rpn32_mem(hndp, cb_rpn32_listmem, NULL)

static int interactive_cmd_MEMUSAGE(struct inters *interp)
{
    if(interp->linep[0] != '\0')
        return(interactive_sorry_arg());
    Memusage();
    return(INTERACTIVE_RTNVAL_NONE);
}



/*
 * AKTUALNI HODNOTY
 */

static int interactive_cmd_SPY(struct inters *interp)
{
    interactive_spy(interp->linep);
    return(INTERACTIVE_RTNVAL_NONE);
}



/*
 * SINGLE STEP
 */

static int interactive_one(struct inters *);

static int cb_rpn32_sst(int  type,
                        void *userdata)
{
#define  interp     ((struct inters *)userdata)

    type = type;

    INTERS_SET(SST, interp);
    rpn32_view(hndp, interp->sstmodep, cb_rpn32_view, VIEW_PROMPT_NONE);
    return(interactive_one(interp));

#undef   interp
}

static int interactive_sst_run(struct inters *interp)
{
    int     test;

    test = rpn32_run(hndp, RPN32_RUN_CB_TYPE_TRACE, cb_rpn32_sst, interp);
    if(!RPN32_RTNVAL_IS_ERROR(test))
        return(test);
    Errorcode(SST, test);
    Errorline(TRACE);
    return(INTERACTIVE_RTNVAL_CMD);
}

static int interactive_sst_run_mode(struct inters *interp)
{
    char    modet[LINE_SIZE - INTERACTIVE_CMD_LEN];

    strcpy(modet, interp->sstmodep);
    interp->sstmodep = modet;
    return(interactive_sst_run(interp));
}

static int interactive_sst(struct inters *interp)
{
    if((interp->sstmodep = interactive_mode(interp->linep)) != NULL)
        return(interactive_sst_run_mode(interp));
    interp->sstmodep = interactivemodet;
    return(interactive_sst_run(interp));
}

static int interactive_cmd_SST(struct inters *interp)
{
    if(INTERS_IS(CMD, interp))
        return(interactive_sst(interp));
    interp->fncp = interactive_sst;
    return(INTERACTIVE_RTNVAL_CMD);
}



/*
 * KONEC REZIMU SIGNLE STEP
 */

static int interactive_cmd_COMMAND(struct inters *interp)
{
    if(interp->linep[0] != '\0')
        return(interactive_sorry_arg());
    return(INTERACTIVE_RTNVAL_CMD);
}



/*
 * SPUSTENI/POKRACOVANI PROGRAMU
 */

static int interactive_run(struct inters *interp)
{
    int         runcbtype;
    char const  *modep;

    runcbtype = RPN32_RUN_CB_TYPE_TRACE;
    if((modep = interactive_mode(interp->linep)) == NULL)
    {
        modep = interactivemodet;
        runcbtype = RPN32_RUN_CB_TYPE_PSE;
    }
    execute(runcbtype, modep, modep);
    return(INTERACTIVE_RTNVAL_CMD);
}

static int interactive_cmd_RUN(struct inters *interp)
{
    interp->fncp = interactive_run;
    return(INTERACTIVE_RTNVAL_CMD);
}



/*
 * NASTAVENI "MODE" PRO VYPIS/ZOBRAZENI
 */

static int interactive_cmd_MODE(struct inters *interp)
{
    char const  *linep;

    linep = interp->linep;
    if(linep[0] != '\0')
        strcpy(interactivemodet, linep);    // urcite se vejde, viz "LINE_SIZE"
    else
        Report(VIEW_PROMPT_MODE "%s\n", interactivemodet);
    return(INTERACTIVE_RTNVAL_NONE);
}



/*
 * NAPOVEDA
 */

static int interactive_cmd_HELP(struct inters *interp)
{
    if(interp->linep[0] != '\0')
        return(interactive_sorry_arg());
    Outline(HELP_TEXT_INTERACTIVE);
    Outline(HELP_TEXT_MODE_FORMAT);
    return(INTERACTIVE_RTNVAL_NONE);
}



/*
 * KONEC PROGRAMU
 */

static int interactive_cmd_BYE(struct inters *interp)
{
    if(interp->linep[0] != '\0')
        return(interactive_sorry_arg());
    return(INTERACTIVE_RTNVAL_BYE);
}



/*
 * ZPRACOVANI PROGRAMOVE RADKY
 */

static int interactive_now(struct inters *interp)
{
    int     test;

    if(RPN32_RTNVAL_IS_ERROR(test = rpn32_now(hndp, interp->linep)))
        return(interactive_sorry(rtnval_text(test)));
    switch(test)
    {
    case RPN32_NOW_RTNVAL_YES:
        printf("YES\n");
        break;

    case RPN32_NOW_RTNVAL_NO:
        printf("NO\n");
        break;

    default:
        interactive_spy_dflt();
    }
    return(INTERACTIVE_RTNVAL_NONE);
}



/*----------------------------------------------------------------------------*\
                        ZPRACOVANI VSTUPU Z KLAVESNICE
\*----------------------------------------------------------------------------*/


/*
 * POROVNANI RETEZCU
 */

static int interactive_one_cmd_compare(char const *linep,
                                   char const *namep)
{
    while(tolower(*linep) == *namep)
    {
        ++linep;
        ++namep;
        if(*namep == '\0')
            return(0);
    }
    return(-1);
}



/*
 * ROZLISENI COMMANDU
 */

static int (*interactive_one_cmd(char const *linep))(struct inters *)
{
#define  SORTIMENT_ITEM(k)  {INTERACTIVE_CMD_TEXT_##k, interactive_cmd_##k}

    int         idx;
    static struct
    {
        char    namet[INTERACTIVE_CMD_LEN + 1];
        int     (*fncp)(struct inters *);
    } const sortiment[] =  {SORTIMENT_ITEM(LIST),
                            SORTIMENT_ITEM(MEMUSAGE),
                            SORTIMENT_ITEM(SPY),
                            SORTIMENT_ITEM(SST),
                            SORTIMENT_ITEM(COMMAND),
                            SORTIMENT_ITEM(RUN),
                            SORTIMENT_ITEM(MODE),
                            SORTIMENT_ITEM(HELP),
                            SORTIMENT_ITEM(BYE)};

    for(idx = 0;  idx < sizeof(sortiment) / sizeof(*sortiment);  ++idx)
        if(!interactive_one_cmd_compare(linep, sortiment[idx].namet))
            return(sortiment[idx].fncp);
    return(NULL);

#undef   SORTIMENT_ITEM
}



/*
 * PRESKOCENI "NEPLATNYCH" ZNAKU
 */

static char const *interactive_one_skip(char const *linep)
{
    while(isspace(*linep))
        ++linep;
    return(linep);
}



/*
 * JEDNA "OTACKA"
 */

static int interactive_one(struct inters *interp)
{
    int     (*fncp)(struct inters *);

    while(1)
    {
        printf(INTERS_PROMPT(interp));
        if(!Line_get(interp->linet, stdin))
        {
            putchar('\n');
            return(INTERACTIVE_RTNVAL_BYE);
        }
        Line_term(interp->linet);
        interp->linep = interactive_one_skip(interp->linet);
        if(interp->linep[0] == '\0')
        {
            if(INTERS_IS(SST, interp))
                return(INTERACTIVE_RTNVAL_SST);
            interactive_spy_dflt();
            continue;
        }
        if((fncp = interactive_one_cmd(interp->linep)) != NULL)
        {
            int     test;

            interp->linep = interactive_one_skip(
                            interp->linep + INTERACTIVE_CMD_LEN);
            if((test = (*fncp)(interp))  == INTERACTIVE_RTNVAL_NONE)
                continue;
            return(test);
        }
        interp->fncp = interactive_now;
        return(INTERACTIVE_RTNVAL_CMD);
    }
}



/*
 * VSTUPNI BOD
 */

static void interactive()
{
    struct inters   interv;

    reportprefixp = NULL;
    interv.fncp   = NULL;
    interv.linep  = NULL;
    while(1)
    {
        INTERS_SET(CMD, &interv);
        if(interactive_one(&interv) == INTERACTIVE_RTNVAL_BYE)
            break;
        if(interv.fncp == NULL)
            continue;
        (*interv.fncp)(&interv);
        interv.fncp = NULL;
    }
}



/*============================================================================*\
                                    PROGRAM
\*============================================================================*/


/*
 * INICIALIZACE
 */

static int initialize(int argc,
                      char *argv[])
{
    int             test;
    struct initopts initoptv;

    initoptv.flags     = INITOPT_FLAGS_EXEC;
    initoptv.startlbl  = -1;
    initoptv.runcbtype = RPN32_RUN_CB_TYPE_PSE;

    if((test = init_options(argc, argv, &initoptv)) < 0)
        return(test);
    argv += argc - test;
    argc = test;
    if(RPN32_RTNVAL_IS_ERROR(test = init_job(argc, argv)))
    {
        Errorcode(INIT, test);
        if(hndp != NULL)
            Errorline(LIST);
        return(-1);
    }
    if(test == RPN32_RTNVAL_STOP)
        return(1);
    if(initoptv.flags & INITOPT_FLAGS_LIST)
        if(listing(optmodelstp))
            return(-1);
    if(initoptv.flags & INITOPT_FLAGS_MEMUSAGE)
        Memusage();
    if(!(initoptv.flags & INITOPT_FLAGS_EXEC))
        return(0);
    if(initoptv.startlbl >= 0)
        if(RPN32_RTNVAL_IS_ERROR(test = rpn32_gto(hndp, initoptv.startlbl)))
        {
            Errorcode(GTO, test);
            return(-1);
        }
        else
            if(initoptv.runcbtype != RPN32_RUN_CB_TYPE_TRACE)
                interactive_spy(optmodetrcp);
    return(execute(initoptv.runcbtype, optmodetrcp, optmoderesp));
}



/*
 * VSTUPNI BOD
 */

int main(int argc, char *argv[])
{
    if((argc = initialize(argc, argv)) < 0)
        return(1);
    if(argc > 0)
        interactive();
    return(0);
}