Chapter 8: Executing Scripts

Now that the monitor design includes a flash file system and a command line interface, I will extend the system so that a file can execute as a script. Actually, the scripting feature turns out to be a pretty simple addition one that adds a lot of flexibility to MicroMonitor. A single script-running function, with the help of a few script-running-specific commands in MicroMonitors command set, provides a versatile environment for simple script execution.

The Script Runner

The script runner function ( tfsscript() ) (see Listing 8.1) treats a file as a list of commands. The function reads each line of the file and executes the line as a command. Some commands affect the script runner directly, for example goto , gosub , return , and exit . I will discuss these commands in detail later.

Listing 8.1: tfsscript() .
image from book
 int tfsscript(TFILE *fp, int verbose) {     char    lcpy[CMDLINESIZE];     int     tfd, lnsize;     /* 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);     ReturnToTfd = tfd;          while(1) {         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: */         tfsDocommand(lcpy,verbose);         /* 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;                 }             }         }     }     tfsclose(tfd,0);     if (ScriptExitFlag & REMOVE_SCRIPT)         tfsunlink(fp->name);     if (ReturnToDepth != 0) {         printf("Warning: '%s' missing return.\n",fp->name);         ReturnToDepth = 0;     }     return(TFS_OKAY); } 
image from book
 

Initially ignoring a few of the global variables used in Listing 8.1, lets begin with the basics. The tfsscript() function starts by opening a TFS file by calling tfsopen() [1] . Assuming the file exists, the script runner then enters a loop that repeats for each line in the file. At the top, the loop calls tfsgetline() to retrieve the next line in the file. If the line is empty, the loop immediately skips to the next line; otherwise , the line is passed to the function pointed to by tfsDocommand . The tfsDocommand variable is a function pointer that is loaded with a pointer to the docommand() function by default. (I will explain why this is a loaded function pointer instead of just a normal function call in a later chapter.) Ignoring the ScriptGotoTag branch for a moment, it becomes clear that the script runner is not terribly complex. The function steps through each line of the script file and passes that line to the command handler. When the function reaches the end of the file, the function closes and returns.

Exit

The global variables in tfsscript() provide the script runner with the ability to jump to tags ( goto command) and branch to ( gosub command) or return from ( return command) subroutines. At any point in the script, the exit command can cause the whole thing to terminate. Each of these commands ( goto , gosub , return , and exit ) execute through docommand() . Listing 8.2 shows the exit command.

Listing 8.2: The exit Command.
image from book
 int ScriptExitFlag; 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); } 
image from book
 

If a line in the script contains the command line exit , the above code executes out of tfsscript() , and, as a result, the global variable ScriptExitFlag is set to EXIT_SCRIPT . If the r option is present, the REMOVE_SCRIPT bit would also be set in the global variable ScriptExitFlag . Referring to the tfsscript() function of Listing 8.1, notice that after tfsDocommand() returns, the code looks to see if the ScriptExitFlag variable has been modified. If ScriptExitFlag is non-zero , the loop is terminated , and the file is closed. If the REMOVE_SCRIPT bit is set in ScriptExitFlag , then the script is automatically removed.

Goto

The tfsscript() function also provides one global ScriptGotoTag variable. The syntax of the goto command is goto { tagname } , so the goto command sets up the ScriptGotoTag (a char pointer see Listing 8.3) to point to the tag that was passed from the command line as the parameter of a goto . Referring to the tfsscript() function (see Listing 8.1), if the ScriptGotoTag variable is set after the return from tfsDocommand() , the currently opened script is searched for the line that starts with a pound sign, a space, and the tag. For example, the line goto TOPOFLOOP causes tfsscript() to search through the currently running script for the line # TOPOFLOOP. The file pointer is then set to this new location in the script, and execution continues.

Listing 8.3: The goto Command.
image from book
 char    *ScriptGotoTag; /* gototag():  *  Used with tfsscript to allow a command to adjust the pointer into the  *  script that is currently being executed.  It simply populates the  *  "ScriptGotoTag" pointer with the tag that should be branched to next.  */ void gototag(char *tag) {     if (ScriptGotoTag)         free(ScriptGotoTag);     ScriptGotoTag = malloc(strlen(tag)+8);     sprintf(ScriptGotoTag,"# %s",tag); } char *GotoHelp[] = {     "Branch to file tag",     "{tagname}",     0, }; int Goto(int argc, char *argv[]) {     if (argc != 2)         return(-1);     gototag(argv[1]);     return(CMD_SUCCESS); } 
image from book
 

gosub and return

To wrap up the details of tfsscript() , I describe the script runners ability to branch to and return from subroutines: gosub and return .

Listing 8.4: The gosub and return Commands.
image from book
 char *GosubHelp[] = {     "Call a subroutine",     "{tagname}",     0, }; int Gosub(int argc, char *argv[]) {     if (argc != 2)         return(-1);     gosubtag(argv[1]);     return(CMD_SUCCESS); } char *ReturnHelp[] = {     "Return from subroutine",     "",     0, }; int Return(int argc, char *argv[]) {     if (argc != 1)         return(-1);     gosubret(0);     return(CMD_SUCCESS); } 
image from book
 

The front ends to each of these commands (Listing 8.4) are essentially identical except that one calls gosubtag() and one calls gosubret() (Listing 8.5).

Listing 8.5: gosub and return Under the Hood.
image from book
 #define MAXGOSUBDEPTH   15 static long ReturnToTbl[MAXGOSUBDEPTH+1]; static int  ReturnToDepth, ReturnToTfd; void gosubtag(char *tag) {     if (ReturnToDepth >= MAXGOSUBDEPTH) {         printf("Max return-to depth reached\n");         return;     }     ReturnToTbl[ReturnToDepth] = tfstell(ReturnToTfd);     ReturnToDepth++;     gototag(tag); } void gosubret(char *ignored) {     if (ReturnToDepth <= 0)         printf("Nothing to return to\n");     else {         ReturnToDepth--;         tfsseek(ReturnToTfd, ReturnToTbl[ReturnToDepth], TFS_BEGIN);     } } 
image from book
 

The gosub command calls gosubtag() , which records the location in the file to which the subroutine must return. The next element in the ReturnToTbl[] array is populated with the value returned by tfstell() . The ReturnToTbl[] array is essentially a stack of return locations (in the form of file offsets). Function gosubtag() then calls gototag() , which I discussed earlier. The return command unwinds the calling linkage by calling gosubret() . Function gosubret() pops the return location from the ReturnToTbl[] table and returns to the appropriate point in the file (using tfsseek() ). The return stack depth is decreased by one, and a seek to that return point in the file is performed. The maximum call nesting is limited by the size of the ReturnToTbl[] table. The gosubtag() function tests for calls that exceed this limit.

[1] Notice that we are discussing the monitor code. The monitor not only provides the application with the ability to use TFS, but the monitor itself uses the TFS API.



Embedded Systems Firmware Demystified
Embedded Systems Firmware Demystified (With CD-ROM)
ISBN: 1578200997
EAN: 2147483647
Year: 2002
Pages: 118
Authors: Ed Sutter

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net