www.pudn.com > tfs.rar > if.c


/* if.c:
 *  This file started off as the file for the "if" command in the monitor,
 *  and has since grown into the file that contains the commands that only
 *  make sense if they are used in scripts; hence, a prerequisite to these
 *  commands is that TFS is included in the build.
 *  It also contains the script runner portion of TFS.  The name of this
 *  file should have been changed to tfsscript.c!
 *  
 *  if:
 *  Supports the monitor's ability to do conditional branching within
 *  scripts executed through TFS.
 *
 *  goto:
 *  Tag based jumping in a script.
 *
 *  item:
 *  A simple way to process a list (similar to KSH's 'for' construct).
 *
 *  General notice:
 *  This code is part of a boot-monitor package developed as a generic base
 *  platform for embedded system designs.  As such, it is likely to be
 *  distributed to various projects beyond the control of the original
 *  author.  Please notify the author of any enhancements made or bugs found
 *  so that all may benefit from the changes.  In addition, notification back
 *  to the author will allow the new user to pick up changes that may have
 *  been made by other users after this version of the code was distributed.
 *
 *  Note1: the majority of this code was edited with 4-space tabs.
 *  Note2: as more and more contributions are accepted, the term "author"
 *         is becoming a mis-representation of credit.
 *
 *  Original author:    Ed Sutter
 *  Email:              esutter@lucent.com
 *  Phone:              908-582-2351
 */
#include "config.h"
#include "genlib.h"
#include "stddefs.h"
#include "tfs.h"
#include "tfsprivate.h"
//#include "ether.h"
//#include "cli.h"

#if INCLUDE_TFSSCRIPT

#define MAXGOSUBDEPTH   15
static long ReturnToTbl[MAXGOSUBDEPTH+1];
static int  ReturnToDepth, ReturnToTfd, ScriptIsRunning;
static char *ScriptGotoTag;
int ScriptExitFlag;

/* If:
 *  A simple test/action statement.
 *  Currently, the only action supported is "goto tag". 
 *  Syntax:
 *      if ARG1 compar ARG2 {action} [else action]
 *      if -t TEST {action} [else action]
 */
char *IfHelp[] = {
    "Conditional branching",
    "-[t:v] [{arg1} {compar} {arg2}] {action} [else action]",
    " Numeric/logic compare:",
    "  gt lt le ge eq ne and or xor",
    " String compare:",
    "  seq sne",
    " Other tests (-t args):",
    "  gc, ngc, iscmp {filename}",
    " Action:",
    " goto tag | gosub tag | exit | return",
    0,
};

int
If(int argc, char *argv[])
{
    int     opt, arg, true, if_else, offset, verbose;
    void    (*iffunc)(), (*elsefunc)();
    long    var1, var2;
    char    *testtype, *arg1, *arg2, *iftag, *elsetag;

    verbose = 0;
    testtype = 0;
    while((opt=getopt(argc,argv,"vt:")) != -1) {
        switch(opt) {
        case 'v':
            verbose = 1;
            break;
        case 't':
            testtype = optarg;
            break;
        default:
            return(CMD_PARAM_ERROR);
        }
    }

    elsetag = 0;
    elsefunc = 0;
    offset = true = if_else = 0;

    /* First see if there is an 'else' present... */
    for (arg=optind;arg var2)
                true = 1;
        }
        else if (!strcmp(testtype,"lt")) {
            if (var1 < var2)
                true = 1;
        }
        else if (!strcmp(testtype,"le")) {
            if (var1 <= var2)
                true = 1;
        }
        else if (!strcmp(testtype,"ge")) {
            if (var1 >= var2)
                true = 1;
        }
        else if (!strcmp(testtype,"eq")) {
            if (var1 == var2)
                true = 1;
        }
        else if (!strcmp(testtype,"ne")) {
            if (var1 != var2)
                true = 1;
        }
        else if (!strcmp(testtype,"and")) {
            if (var1 & var2)
                true = 1;
        }
        else if (!strcmp(testtype,"xor")) {
            if (var1 ^ var2)
                true = 1;
        }
        else if (!strcmp(testtype,"or")) {
            if (var1 | var2)
                true = 1;
        }
        else if (!strcmp(testtype,"seq")) {
            if (!strcmp(arg1,arg2))
                true = 1;
        }
        else if (!strcmp(testtype,"sne")) {
            if (strcmp(arg1,arg2))
                true = 1;
        }
        else
            return(CMD_PARAM_ERROR);
    }
    
    /* If the true flag is set, call the 'if' function.
     * If the true flag is clear, and "else" was found on the command
     * line, then call the 'else' function...
     */
    if (true) {
        if (verbose)
            printf("TRUE\n");
        iffunc(iftag);
    }
    else {
        if (verbose)
            printf("FALSE\n");
        if (if_else)
            elsefunc(elsetag);
    }

    return(CMD_SUCCESS);
}

int
InAScript(void)
{
    if (ScriptIsRunning)
        return(1);
    else {
        printf("Invalid from outside a script\n");
        return(0);
    }
}

void
exitscript(char *ignored)
{
    if (InAScript()) {
        ScriptExitFlag = EXIT_SCRIPT;
    }
}


char *ExitHelp[] = {
    "Exit a script",
    "-[r]",
    "Options:",
    " -r   remove script after exit",
    0,
};

int
Exit(int argc, char *argv[])
{
    ScriptExitFlag = EXIT_SCRIPT;
    if ((argc == 2) && (!strcmp(argv[1],"-r")))
        ScriptExitFlag |= REMOVE_SCRIPT;
    return(CMD_SUCCESS);
}

char *GotoHelp[] = {
    "Branch to file tag",
    "{tagname}",
    0,
};

int
Goto(int argc, char *argv[])
{
    if (argc != 2)
        return(CMD_PARAM_ERROR);
    gototag(argv[1]);
    return(CMD_SUCCESS);
}


char *GosubHelp[] = {
    "Call a subroutine",
    "{tagname}",
    0,
};

int
Gosub(int argc, char *argv[])
{
    if (argc != 2)
        return(CMD_PARAM_ERROR);
    gosubtag(argv[1]);
    return(CMD_SUCCESS);
}

char *ReturnHelp[] = {
    "Return from subroutine",
    "",
    0,
};

int
Return(int argc, char *argv[])
{
    if (argc != 1)
        return(CMD_PARAM_ERROR);
    gosubret(0);
    return(CMD_SUCCESS);
}

/* Item:
 *  This is a simple replacement for the KSH "for" construct...
 *  It allows the user to build a list of strings and retrieve one at a time.
 *  Basically, the items can be thought of as a table.  The value of idx
 *  (starting with 1) is used to index into the list of items and place
 *  that item in the shell variable "var".
 *  Syntax:
 *      item {idx} {var} {item1} {item2} {item3} ....
 */
char *ItemHelp[] = {
    "Extract an item from a list",
    "{idx} {stor_var} [item1] [item2] ...",
    "Note: idx=1 retrieves item1",
    0,
};

int
Item(int argc, char *argv[])
{
    int idx;
    char *varname;

    if (argc < 3)
        return(CMD_PARAM_ERROR);

    idx = atoi(argv[1]);
    varname = argv[2];

    if ((idx < 1) || (idx > (argc-3))) {
        setenv(varname,0);
        return(CMD_SUCCESS);
    }

    idx += 2;
    setenv(varname,argv[idx]);
    return(CMD_SUCCESS);
}

/* Read():
 *  Simple interactive shell variable entry.  
 *  Syntax:
 *  read [options] [var1] [var2] [...]
 *  Options:
 *      -c      wait for any character to be received.
 *      -twww   timeout after 'www' milliseconds (approximate) of 
 *              waiting for input.
 *
 */

char *ReadHelp[] = {
    "Interactive shellvar entry",
    "-[ct:] {var1} [var2] ... ",
    " -c      wait for any input",
    " -t ###  initial msec timeout",
    " -T ###  msec timeout per char",
    0,
};

int
Read(argc,argv)
int argc;
char    *argv[];
{
    int i, reached_eol, opt, waitfor1, waitfor2;
    char    buf[64], *space, *bp;

    waitfor1 = waitfor2 = 0;
    while((opt=getopt(argc,argv,"ct:T:")) != -1) {
        switch(opt) {
        case 'c':   /* Wait for any character entered (like hitakey). */
            while(!gotachar()) 
#if INCLUDE_ETHERNET
                pollethernet()
#endif
            ;
            getchar();  /* Flush it from the input buffer, once received. */
            break;
        case 't':
            waitfor1 = strtol(optarg,(char **)0,0);
            waitfor1 *= LoopsPerSecond/1000;
            break;
        case 'T':
            waitfor2 = strtol(optarg,(char **)0,0);
            waitfor2 *= LoopsPerSecond/1000;
            break;
        default:
            return(CMD_PARAM_ERROR);
        }
    }

    if (argc == optind)
        return(CMD_SUCCESS);

    /* Timeout waiting for first character... */
    if (waitfor1) {
        while(!gotachar()) {
            if (waitfor1-- <= 0)
                return(CMD_FAILURE);
#if INCLUDE_ETHERNET
            pollethernet();
#endif
        }
    }

    /* Timeout waiting for every character... */
    if (getline_t(buf,sizeof(buf)-1,waitfor2) == 0)
        return(CMD_FAILURE);

    bp = buf;
    reached_eol = 0;
    for(i=optind;i= MAXGOSUBDEPTH) {
            printf("Max return-to depth reached\n");
            return;
        }
        ReturnToTbl[ReturnToDepth] = tfstell(ReturnToTfd);
        ReturnToDepth++;
        gototag(tag);
    }
}

void
gosubret(char *ignored)
{
    if (InAScript()) {
        if (ReturnToDepth <= 0)
            printf("Nothing to return to\n");
        else {
            ReturnToDepth--;
            tfsseek(ReturnToTfd, ReturnToTbl[ReturnToDepth], TFS_BEGIN);
        }
    }
}

/* tfsscript():
 *  Treat the file as a list of commands that should be executed
 *  as monitor commands.  Step through each line and execute it.
 *  The tfsDocommand() function pointer is used so that the application
 *  can assign a different command interpreter to the script capbilities
 *  of the monitor.  To really take advantage of this, the command interpreter
 *  that is reassigned, should allow monitor commands to run if the command
 *  does not exist in the application's command list.
 *  
 *  Scripts can call other scripts that call other scripts (etc...) and
 *  that works fine because tfsscript() will just use more stack space but
 *  it eventually returns through the same function call tree.  A script can
 *  also call a COFF, ELF or AOUT file and expect it to return as long as
 *  the executable returns through the same point into which it was started
 *  (the entrypoint).  If the executable uses mon_appexit() to terminate, 
 *  then the calling script will not regain control.
 */

int
tfsscript(TFILE *fp,int verbose)
{
    char    lcpy[CMDLINESIZE], *sv;
    int     tfd, lnsize, lno, verbosity, rslt;

    /* TFS does not support calling a script from within a subroutine. */
    if (ReturnToDepth != 0)
        return(TFSERR_SCRIPTINSUB);

    tfd = tfsopen(fp->name,TFS_RDONLY,0);
    if (tfd < 0)
        return(tfd);

    lno = 0;
    ReturnToTfd = tfd;
    ScriptIsRunning++;
    
    while(1) {
        lno++;
        lnsize = tfsgetline(tfd,lcpy,CMDLINESIZE);
        if (lnsize == 0)    /* end of file? */
            break;
        if (lnsize < 0) {
            printf("tfsscript(): %s\n",tfserrmsg(lnsize));
            break;
        }
        if ((lcpy[0] == '\r') || (lcpy[0] == '\n')) /* empty line? */
            continue;

        lcpy[lnsize-1] = 0;         /* Remove the newline */

        /* Just in case the goto tag was set outside a script, */
        /* clear it now. */
        if (ScriptGotoTag) {
            free(ScriptGotoTag);
            ScriptGotoTag = (char *)0;
        }

        ScriptExitFlag = 0;

        /* Execute the command line.
         * If the shell variable "SCRIPTVERBOSE" is set, then enable
         * verbosity for this command; else use what was passed in 
         * the parameter list of the function.  Note that the variable
         * is tested for each command so that verbosity can be enabled
         * or disabled within a script.
         * If the command returns a status that indicates that there was
         * some parameter error, then exit the script.
         */
        sv = getenv("SCRIPTVERBOSE");
        if (sv)
            verbosity = atoi(sv);
        else
            verbosity = verbose;

        rslt = tfsDocommand(lcpy,verbosity);
        switch(rslt) {
            case CMD_PARAM_ERROR:
            case CMD_NOT_FOUND:
                printf("Terminating script '%s' at line %d\n",
                    TFS_NAME(fp),lno);
                ScriptExitFlag = 1;
                break;
        }
        

        /* Check for exit flag.  If set, then in addition to terminating the
         * script, clear the return depth here so that the "missing return"
         * warning  is not printed.  This is done because there is likely
         * to be a subroutine with an exit in it and this should not
         * cause a warning.
         */
        if (ScriptExitFlag) {
            ReturnToDepth = 0;
            break;
        }

        /* If ScriptGotoTag is set, then attempt to reposition the line 
         * pointer to the line that contains the tag.
         */
        if (ScriptGotoTag) {
            int     tlen;

            tlen = strlen(ScriptGotoTag);
            tfsseek(tfd,0,TFS_BEGIN);
            while(1) {
                lnsize = tfsgetline(tfd,lcpy,CMDLINESIZE);
                if (lnsize == 0) {
                    printf("Tag '%s' not found\n",ScriptGotoTag+2);
                    free(ScriptGotoTag);
                    ReturnToDepth = 0;
                    ScriptGotoTag = (char *)0;
                    tfsclose(tfd,0);
                    return(TFS_OKAY);
                }
                if (!strncmp(lcpy,ScriptGotoTag,tlen)) {
                    free(ScriptGotoTag);
                    ScriptGotoTag = (char *)0;
                    break;
                }
            }
        }

#if INCLUDE_ETHERNET
        /* After each line, poll ethernet interface. */
        pollethernet();
#endif
    }
    tfsclose(tfd,0);
    if (ScriptExitFlag & REMOVE_SCRIPT)
        tfsunlink(fp->name);
    if (ReturnToDepth != 0) {
        printf("Warning: '%s' missing return.\n",fp->name);
        ReturnToDepth = 0;
    }
    ScriptIsRunning--;
    return(TFS_OKAY);
}

#else   /* INCLUDE_TFSSCRIPT */

int
tfsscript(TFILE *fp,int verbose)
{
    return(TFSERR_NOTAVAILABLE);
}

#endif