Tuesday, December 22, 2009

Conditional Branching










































Conditional Branching


Several other commands provide additional
capabilities for enhancing the script execution environment, although
none of these additional commands interacts directly with the tfsscript() function as do exit, goto, gosub, and return. The one remaining command implementation that is worth detailing is the if command. The if
command provides MicroMonitor with the conditional branching
capabilities needed for any programming environment. The implementation
of if uses the facilities I’ve already introduced with gosub, goto, exit, and return.



Listing 8.6: Conditional Branch Support: The if Command.







char *IfHelp[] = {
"Conditional branching",
"-[t:] [{arg1} {compar} {arg2}] {action} [else action]",
" Numeric/logic compare:",
" gt lt le ge eq ne and or",
" 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;
void (*iffunc)(), (*elsefunc)();
long var1, var2;
char *testtype, *arg1, *arg2, *iftag, *elsetag;


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

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













The goal of the if command
is to make some type of test or comparison and, based on the result of
that test, to take some kind of action. The comparison is generally
based on comparing the content of a shell variable to some constant or
to another shell variable. The variables can be string or numerically
based. The -t option allows the user to
check for the presence of a character on the console port or to
determine if a file is compressed or not.


The basic syntax of the if command is



if {comparison between A & B is true}{perform actionX}



[else {perform actionY}]


or



if {-t testtype}{perform actionX}[else {perform actionY}]


where the test performed by testtype can be true or false.


The if command offers two
potential outcomes, so there must be two different function pointers to
load with the appropriate function that performs the requested action (iffunc and elsefunc). Referring to Listing 8.6, the command line is first tested for the –t option. If present, then the appropriate flag is set.



Listing 8.7: Parsing Else Clauses.






 /* First see if there is an 'else' present... */
for (arg=optind;arg<argc;arg++) {
if (!strcmp(argv[arg],"else")) {
if_else = 1;
break;
}
}

if (if_else) {
elsetag = argv[argc-1];
if (!strcmp(argv[argc-1],"exit")) {
offset = 2;
elsefunc = exitscript;
}
else if (!strcmp(argv[argc-1],"return")) {
offset = 2;
elsefunc = gosubret;
}
else if (!strcmp(argv[argc-2],"goto")) {
offset = 3;
elsefunc = gototag;
}
else if (!strcmp(argv[argc-2],"gosub")) {
offset = 3;
elsefunc = gosubtag;
}
else
return(CMD_PARAM_ERROR);
}

iftag = argv[argc-offset-1];
if (!strcmp(argv[argc-offset-1],"exit"))
iffunc = exitscript;
else if (!strcmp(argv[argc-offset-1],"return"))
iffunc = gosubret;
else if (!strcmp(argv[argc-offset-2],"goto"))
iffunc = gototag;
else if (!strcmp(argv[argc-offset-2],"gosub"))
iffunc = gosubtag;
else
return(CMD_PARAM_ERROR);














Further parsing (Listing 8.7) determines if the else keyword is present on the command line. If so, the branching code loads the elsefunc function pointer with the else action. This action can be a goto, gosub, exit, or return. Next, the following four-way branch loads the iffunc function pointer with a corresponding action.



Listing 8.8: Performing the Test or Comparison.






 if (testtype) {
if (!strcmp(testtype,"gc")) {
if (gotachar())
true=1;
}
else if (!strcmp(testtype,"ngc")) {
if (!gotachar())
true=1;
}
else if (!strcmp(testtype,"iscmp")) {
TFILE *tfp;

tfp = tfsstat(argv[optind]);
if (tfp) {
if (TFS_ISCPRS(tfp))
true=1;
}
else
printf("'%s' not found\n",argv[optind]);
}
else
return(CMD_PARAM_ERROR);
}
else {
arg1 = argv[optind];
testtype = argv[optind+1];
arg2 = argv[optind+2];

var1 = strtoul(arg1,(char **)0,0);
var2 = strtoul(arg2,(char **)0,0);

if (!strcmp(testtype,"gt")) {
if (var1 > 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,"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);
}













Next, referring to Listing 8.8, the test or comparison is executed, and the true flag is set based on the result.




Listing 8.9: Execute the Appropriate Action.






    /* 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)
iffunc(iftag);
else if (if_else)
elsefunc(elsetag);

return(CMD_SUCCESS);
}














Finally, based on the setting of the true flag and the content of the iffunc and elsefunc pointers, the conditional action is performed (Listing 8.9).
All in all, you get an amazing amount of script functionality from a
surprisingly small, simple piece of code. Refer to the CD for command
functions for read, item, sleep, echo, and other script-related commands.




































No comments: