The Functions Beneath the Command Name

The Functions Beneath the Command Name

Thus, docommand() is responsible for tokenization, shell variables , and symbols. It parses the command line, converting it to a list of tokens that is then passed to the command function for command-specific processing. Each command function receives this token or argument list through standard int argc , char *argv[] , parameters just like main() in a standalone program. Each command function is assumed to return some integer value representing the type of success it had in dealing with the command line. This CLI defines three different return values:

CMD_SUCCESS

Tells docommand() that the command-specific function com pleted successfully.

CMD_FAILURE

Tells docommand() that the command-specific function did not succeed but that there is no need for docommand() to print any error message, because the command-specific function has already printed any necessary messages.

CMD_PARAM_ERROR

Tells docommand() that the command-specific function did not receive a valid set of arguments, and the generic syntax error message can be output by docommand() .

If the command function returns CMD_SUCCESS or CMD_FAILURE , there is nothing else to do except possibly log the fact that the command failed. If the command function returns CMD_PARAM_ERROR , then docommand() prints out a generic syntax error message. In this case, docommand() assumes that the second string in the table of strings within the command-specific help text is a usage message and prints it. (See Add_Help[] array in Listing 5.3.) The usage message gives the user some idea of the correct syntax for the attempted command.

This arrangement requires that each commands help strings conform to a certain syntax. The first string in the list is a brief description of the command. The second is the usage message, which follows the common format of braces {} indicating required arguments and brackets [] indicating optional arguments. The remaining lines are command specific. A NULL pointer terminates the list.

This design supports several different help- related functions some provided by docommand() and some provided by the help command. By stepping through the command table, the help command can list each command in a tabulated format. Alternatively each command can be printed with the one-line description provided by the commands help-text array. Finally, when given a command name, help can print the entire block of help text for the specified command. All this command-specific help is generated in a generic way without the involvement of any of the individual command functions. The only requirement is that the first two strings in the help text table have special meaning and that the list of strings be NULL - terminated .

Listing 5.3 is help text and function code for a simple command called add. This command accepts two arguments and prints the result. For the sake of this discussion, I add a requirement that both numbers must be greater than zero. If invoked with a v option, the result is placed in a shell variable and not printed.

Listing 5.3: Help Text and Function Code for the add Command.
image from book
 char *Add_Help[] =  {     "Add two numbers together",     "-[v:] {num1} {num2}",     "Options:",     " -v {varname}   put result in shell var 'varname'",     (char *)0, }; int Add(int argc, char *argv[]) {     char *varname;     int opt, val1, val2, result;          varname = (char *)0;     while((opt = getopt(argc,argv,"v:")) != -1)     {         switch(opt)          {         case 'v':             varname = optarg;             break;         default:             return(CMD_FAILURE);         }     }     if (argc != optind+2)     {         return(CMD_PARAM_ERROR);     }     val1 = atoi(argv[optind]);     val2 = atoi(argv[optind+1]);     if ((val1 <= 0)  (val2 <= 0))     {         printf("Argument out of range\n");         return(CMD_FAILURE);     }     result = val1 + val2;     if (varname != (char *)0)         shell_sprintf(varname,"%d",result);     else         printf("%d + %d = %d\n",val1,val2,result);     return(CMD_SUCCESS); } 
image from book
 

Notice that add can return any of the three different return values. If the return value from getopt() [2] falls through to the default case of the switch statement (indicating that an unexpected option was entered), the return is CMD_FAILURE. This occurs because the getopt() function prints out an error message itself, so there is no need for any additional error message. If there arent exactly two arguments after option processing, the CMD_PARAM_ERROR is returned, and docommand() prints out a syntax error message plus the usage text in the Add_Help[] array. CMD_FAILURE is also returned when the two values are checked for being greater than zero. Because this function prints out a specific message letting the user know that something is wrong, docommand() should not print anything but must still be aware that something went wrong. Finally, if the command completes successfully, the function returns CMD_SUCCESS , and no further action is taken by docommand() . The command function itself does not need to do any of the generic error message printing; it is handled by the command processor code in docommand().

Also notice that getopt() is a very handy way to allow a command-line syntax to be somewhat variable. The value of optind after the getopt() loop will be the index into the argument list just after the last option processed on the command line; hence, all options are assumed to precede arguments in this case. Commands within MicroMontior can use getopt() not just to process command options that override some defaults but also to redefine what the argument list is in some cases. Because getopt() uses static variables to keep track of how much of the current command line has been processed, docommand() must re-initialize these variables prior to passing control to the command function.

Listing 5.4 shows the portion of docommand() that parses the command table and demonstrates how docommand() uses the help array format.

Listing 5.4: Parsing the Command Table.
image from book
 int docommand(char *cmdline, int verbose) {     int ret, argc;     char *argv[ARGCNT];     struct monCommand *cmdptr;         ...     /* Step through the command table looking for a match between      * the first token of the command line and a command name.      */     cmdptr = cmdlist;     while(cmdptr->name) {         if (strcmp(argv[0],cmdptr->name) == 0)             break;         cmdptr++;     }     if (cmdptr->name) {         ret = cmdptr->func(argc,argv);         /* If command returns parameter error, then print the second          * string in that commands help text as the usage text.  If          * the second string is an empty string, then print a generic          * "no arguments" string as the usage message.          */         if (ret == CMD_PARAM_ERROR) {             char *usage;             usage = cmdptr->helptxt[1];             printf("Command line error...\n");                 printf("Usage: %s %s\n",                         cmdptr->name,*usage ? usage : "(no args/opts)");         }         return(ret);     }     /* If command is not in command table, then see if it is in      * the file system.      */     if (tfsstat(argv[0])) {         int err;         err = tfsrun(argv,verbose);         if (err != TFS_OKAY) {             printf("%s: %s\n",argv[0], (char *)tfsctrl(TFS_ERRMSG,err,0));         }         return(CMD_SUCCESS);     }     else         printf("Command not found: %s\n",argv[0]);     return(CMD_NOT_FOUND); } 
image from book
 

Listing 5.4 is only a portion of the docommand() function but an adequate chunk for the sake of this discussion. The code assumes that the command line has been turned into a list of white-space delimited tokens in a char *argv[] type of list. The content of argv[0] is the command name and is used to find a match in the command table. If the end of the command table is reached, the name pointer will be NULL , indicating that the input command is invalid. If name is not NULL , the code can safely assume that the loop through the command list has found a match between a command in the table and the first argument of the command. This results in a call to the function that corresponds to the command string entered. The return value from this function is then used to determine if docommand() needs to print any additional error message. Note, once again, that the command-specific code is not required to deal with any of the generic error messaging; the code simply returns CMD_PARAM_ERROR to docommand() , and a standard message is printed.

Note 

At the point where the program determines there is no match between argv[0] and a command in the table, one more test is done to see if argv[0] is a script in the flash file system. Ill say more on scripts and the flash file system in Chapters 6, 7, and 8.

[2] The getopt() (get options) function is commonly used in UNIX programs to provide a common way to deal with command line options (optional single-character arguments that are assumed to be preceded with a dash and may require arguments of their own).



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