diff options
Diffstat (limited to 'dmake/getinp.c')
-rw-r--r-- | dmake/getinp.c | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/dmake/getinp.c b/dmake/getinp.c new file mode 100644 index 000000000000..5445193d1a90 --- /dev/null +++ b/dmake/getinp.c @@ -0,0 +1,848 @@ +/* RCS $Id: getinp.c,v 1.10 2007-10-15 15:39:23 ihi Exp $ +-- +-- SYNOPSIS +-- Handle reading of input. +-- +-- DESCRIPTION +-- The code in this file reads the input from the specified stream +-- into the provided buffer of size Buffer_size. In doing so it deletes +-- comments. Comments are delimited by the #, and +-- <nl> character sequences. An exception is \# which +-- is replaced by # in the input. Line continuations are signalled +-- at the end of a line and are recognized inside comments. +-- The line continuation is always <\><nl>. +-- +-- If the file to read is NIL(FILE) then the Get_line routine returns the +-- next rule from the builtin rule table (Rule_tab from ruletab.c) if +-- there is one. +-- +-- AUTHOR +-- Dennis Vadura, dvadura@dmake.wticorp.com +-- +-- WWW +-- http://dmake.wticorp.com/ +-- +-- COPYRIGHT +-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved. +-- +-- This program is NOT free software; you can redistribute it and/or +-- modify it under the terms of the Software License Agreement Provided +-- in the file <distribution-root>/readme/license.txt. +-- +-- LOG +-- Use cvs log to obtain detailed change logs. +*/ + +#include "extern.h" + +#define IS_WHITE(A) ((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r')) +#define SCAN_WHITE(A) \ + while( IS_WHITE(*A) ) A++; + +static int _is_conditional ANSI((char*)); +static int _handle_conditional ANSI((int, TKSTRPTR)); + +static int rule_ind = 0; /* index of rule when reading Rule_tab */ +static int skip = FALSE; /* if true the skip input */ + +int partcomp( char* lhs, int opcode ); +int parse_complex_expression( char *expr, char **expr_end, int opcode ); + + +PUBLIC int +Get_line( buf, fil )/* +====================== + Read a line of input from the file stripping off comments. The routine + returns TRUE if EOF. If fil equals NIL(FILE) then the next line from + *Rule_tab[] is used. Rule_tab is either the buildin rule table or points + to the current environment (used by ReadEnvironment()). + The function returns TRUE if the input file/buffer was read to the end + and FALSE otherwise. */ +char *buf; +FILE *fil; +{ + extern char **Rule_tab; + register char *p; + register char *c; + char *q; + char *buf_org; + static int ignore = FALSE; + int cont = FALSE; + int pos = 0; + int res = 0; + register char *tmp = NIL(char); + + DB_ENTER( "Get_line" ); + + if( Skip_to_eof ) { + Skip_to_eof = FALSE; + rule_ind = 0; + + if( Verbose & V_MAKE ) + Warning("Ignoring remainder of file %s", Filename()); + + DB_RETURN(TRUE); + } + + if( fil == NIL(FILE) ) { + /* Reading the internal rule table. Set rule_ind to zero after the + * last entry so that ReadEnvironment() works as expected every time. */ + + while( (p = Rule_tab[ rule_ind++ ]) != NIL(char) ) + /* The last test in this if *p != '~', handles the environment + * passing conventions used by MKS to pass arguments. We want to + * skip those environment entries. Also CYGWIN likes to export '!' + * prefixed environment variables that cause severe pain, axe them too */ + if( !Readenv || (Readenv && (strchr(p,'=') != NIL(char)) && *p!='~' && *p!='!')){ + strcpy( buf, p ); + + DB_PRINT( "io", ("Returning [%s]", buf) ); + DB_RETURN( FALSE ); + } + + rule_ind = 0; + + DB_PRINT( "io", ("Done Ruletab") ); + DB_RETURN( TRUE ); + } + + buf_org = buf; + +do_again: + do { + p = buf+pos; + /* fgets() reads at most one less than Buffer_size-pos characters. */ + if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char))) + DB_RETURN( TRUE ); + +#ifdef _MPW + if ( p[0] == 10 && p[1] == COMMENT_CHAR) + p[0] = ' '; +#endif + + Line_number++; + + /* Set q to the last char in p before the \n\0. */ + q = p+strlen(p)-2; + if( q >= p ) { /* Only check for special cases if p points + * to a non-empty line. */ + + /* ignore each RETURN at the end of a line before any further + * processing */ + if( q[0] == '\r' && q[1] == '\n' ) { + q[0] = '\n'; + q[1] = '\0'; + q--; + } + /* you also have to deal with END_OF_FILE chars to process raw + * DOS-Files. Normally they are the last chars in file, but after + * working on these file with vi, there is an additional NEWLINE + * after the last END_OF_FILE. So if the second last char in the + * actual line is END_OF_FILE, you can skip the last char. Then + * you can search the line back until you find no more END_OF_FILE + * and nuke each you found by string termination. */ + if( q[0] == '\032' ) + q--; + while( q[1] == '\032' ) { + q[1] = '\0'; + q--; + } + + /* ignore input if ignore flag set and line ends in a continuation + character. */ + + if( ignore ) { + if( q[0] != CONTINUATION_CHAR || q[1] != '\n' ) ignore = FALSE; + *p = '\0'; + continue; + } + + /* If a comment is found the line does not end in \n anymore. */ + c = Do_comment(p, &q, Group || (*buf == '\t') || (Notabs && *buf ==' ')); + + /* Does the end of the line end in a continuation sequence? */ + + if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) { + /* If the continuation was at the end of a comment then ignore the + * next input line, (or lines until we get one ending in just <nl>) + * else it's a continuation, so build the input line from several + * text lines on input. The maximum size of this is governened by + * Buffer_size */ + if( q != p && q[-1] == CONTINUATION_CHAR ) { + size_t len = strlen(q+1)+1; + memmove( q, q+1, len ); + q--; + cont = FALSE; + } + else if( c != NIL(char) ) + ignore = TRUE; + else + cont = TRUE; /* Keep the \<nl>. */ + } + else { + cont = FALSE; + } + + q = ( c == NIL(char) ) ? q+2 : c; + } + else { /* empty line or "" */ + cont = FALSE; + ignore = FALSE; + q = p+strlen(p); /* strlen(p) is 1 or 0 */ + } + + pos += q-p; + } + while( (cont || !*buf) && (pos < Buffer_size-1) ); + + if( pos >= Buffer_size-1 ) + Fatal( "Input line too long, increase MAXLINELENGTH" ); + + /* Lines that had comments don't contain \n anymore. */ + /* ??? Continued lines that are followed by an empty or comment only + * line will end in \<nl>. */ + if( (q > p) && (buf[ pos-1 ] == '\n') ) + buf[ --pos ] = '\0'; /* Remove the final \n. */ + + /* STUPID AUGMAKE uses "include" at the start of a line as + * a signal to include a new file, so let's look for it. + * if we see it replace it by .INCLUDE: and stick this back + * into the buffer. We also allow GNU make if[n]eq/else/endif. + * + * These substitutions are made only if we are not parsing a group + * recipe. */ + if( (p = DmStrSpn(buf, " \t\r\n")) == NIL(char) ) + p = buf; + + if (!Group) { + if( !strncmp( "include", p, 7 ) && + (p[7] == ' ' || p[7] == '\t') ) + tmp = DmStrJoin( ".INCLUDE:", p+7, -1, FALSE ); + else if( !strncmp( "ifeq", p, 4 ) && + (p[4] == ' ' || p[4] == '\t') ) + tmp = DmStrJoin( ".IFEQ", p+4, -1, FALSE ); + else if( !strncmp( "ifneq", p, 5 ) && + (p[5] == ' ' || p[5] == '\t') ) + tmp = DmStrJoin( ".IFNEQ", p+5, -1, FALSE ); + else if( !strncmp( "elif", p, 4 ) && + (p[4] == ' ' || p[4] == '\t') ) + tmp = DmStrJoin( ".ELIF", p+4, -1, FALSE ); + else if( !strncmp( "else", p, 4 ) && + (p[4] == ' ' || p[4] == '\t' || p[4] == '\0') ) + tmp = DmStrJoin( ".ELSE", p+4, -1, FALSE ); + else if( !strncmp( "endif", p, 5 ) && + (p[5] == ' ' || p[5] == '\t' || p[5] == '\0') ) + tmp = DmStrJoin( ".END", p+5, -1, FALSE ); + } + + if( tmp != NIL(char)) { + strcpy( buf, tmp ); + FREE( tmp ); + tmp = NIL(char); + } + + /* Now that we have the next line of input to make, we should check to + * see if it is a conditional expression. If it is then process it, + * otherwise pass it on to the parser. */ + + if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART ) { + TKSTR token; + + SET_TOKEN( &token, p ); + + p = Get_token( &token, "", FALSE ); + + if( (res = _is_conditional(p)) != 0 ) /* ignore non-control special */ + { /* targets */ + res = _handle_conditional( res, &token ); + skip = TRUE; + } + else { + CLEAR_TOKEN( &token ); + res = TRUE; + } + } + + if( skip ) { + buf = buf_org; /* ignore line just read in */ + pos = 0; + skip = res; + goto do_again; + } + + DB_PRINT( "io", ("Returning [%s]", buf) ); + DB_RETURN( FALSE ); +} + + +PUBLIC char * +Do_comment(str, pend, keep)/* +============================= + Search the input string looking for comment chars. If it contains + comment chars then NUKE the remainder of the line, if the comment + char is preceeded by \ then shift the remainder of the line left + by one char. */ +char *str; +char **pend; +int keep; +{ + char *c = str; + + while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) { + if( Comment || State == NORMAL_SCAN ) + if( c != str && c[-1] == ESCAPE_CHAR ) { + size_t len = strlen(c)+1; + memmove( c-1, c, len ); /* copy it left, due to \# */ + if( pend ) (*pend)--; /* shift tail pointer left */ + } + else { + /* Check/execute if shebang command is present. */ + if( !No_exec + && c == str + && c[1] == '!' + && Line_number == 1 + && Nestlevel() == 1 ) { + char *cmnd; + + cmnd = Expand(c+2); + cmnd[strlen(cmnd)-1] = '\0'; /* strip last newline */ + Current_target = Root; +#if defined(MSDOS) + Swap_on_exec = TRUE; +#endif + Wait_for_completion = TRUE; + Do_cmnd(&cmnd, FALSE, TRUE, Current_target, A_DEFAULT, TRUE); +#if defined(MSDOS) + Swap_on_exec = FALSE; +#endif + Wait_for_completion = FALSE; + FREE(cmnd); + } + + *c = '\0'; /* a true comment so break */ + break; + } + else { + if( keep ) + c = NIL(char); + else + *c = '\0'; + + break; + } + } + + return(c); +} + + +PUBLIC char * +Get_token( string, brk, anchor )/* +================================== + Return the next token in string. + + Returns empty string when no more tokens in string. + brk is a list of chars that also cause breaks in addition to space and + tab, but are themselves returned as tokens. if brk is NULL then the + remainder of the line is returned as a single token. + + 'anchor' if 1, says break on chars in the brk list, but only if + the entire token begins with the first char of the brk list, if + 0 then any char of brk will cause a break to occurr. + + If 'anchor' is 2, then break only seeing the first char in the break + list allowing only chars in the break list to form the prefix. */ + +TKSTRPTR string; +char *brk; +int anchor; +{ + register char *s; + register char *curp = 0; + register char *t; + int done = FALSE; + char space[100]; + + DB_ENTER( "Get_token" ); + + s = string->tk_str; /* Get string parameters */ + *s = string->tk_cchar; /* ... and strip leading w/s */ + + SCAN_WHITE( s ); + + DB_PRINT( "tok", ("What's left [%s]", s) ); + + if( !*s ) { + DB_PRINT( "tok", ("Returning NULL token") ); + DB_RETURN( "" ); + } + + + /* Build the space list. space contains all those chars that may possibly + * cause breaks. This includes the brk list as well as white space. */ + + if( brk != NIL(char) ) { + strcpy( space, " \t\r\n" ); + strcat( space, brk ); + } + else { + space[0] = 0xff; /* a char we know will not show up */ + space[1] = 0; + } + + + /* Handle processing of quoted tokens. Note that this is disabled if + * brk is equal to NIL */ + + while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) { + s++; + if( string->tk_quote ) { + curp = s-1; + do { curp = strchr( curp+1, '\"' ); } + while( (curp != NIL(char)) && (*(curp+1) == '\"')); + + if( curp == NIL(char) ) Fatal( "Unmatched quote in token" ); + string->tk_quote = !string->tk_quote; + + /* Check for "" case, and if found ignore it */ + if( curp == s ) continue; + goto found_token; + } + else + SCAN_WHITE( s ); + + string->tk_quote = !string->tk_quote; + } + + + /* Check for a token break character at the beginning of the token. + * If found return the next set of break chars as a token. */ + + if( anchor == 2 && brk != NIL(char) ) { + curp = s; + while( *curp && (strchr(brk,*curp)!=NIL(char)) && (*curp!=*brk) ) curp++; + done = (*brk == *curp++); + } + else if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) { + curp = DmStrSpn( s, brk ); + done = (anchor == 0) ? TRUE : + ((anchor == 1)?(*s == *brk) : (*brk == curp[-1])); + } + + + /* Scan for the next token in the list and return it less the break char + * that was used to terminate the token. It will possibly be returned in + * the next call to Get_token */ + + if( !done ) { + SCAN_WHITE( s ); + + t = s; + do { + done = TRUE; + curp = DmStrPbrk(t, space); + + if( anchor && *curp && !IS_WHITE( *curp ) ) + if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) { + t++; + done = FALSE; + } + } + while( !done ); + + if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++; + } + +found_token: + string->tk_str = curp; + string->tk_cchar = *curp; + *curp = '\0'; + + DB_PRINT( "tok", ("Returning [%s]", s) ); + DB_RETURN( s ); +} + + +static int +_is_conditional( tg )/* +======================= + Look at tg and return it's value if it is a conditional identifier + otherwise return 0. */ +char *tg; +{ + DB_ENTER( "_is_conditional" ); + + tg++; + switch( *tg ) + { + case 'I': + if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF ); + else if( !strcmp( tg, "IFEQ" )) DB_RETURN( ST_IFEQ ); + else if( !strcmp( tg, "IFNEQ" )) DB_RETURN( ST_IFNEQ ); + break; + + case 'E': + if( !strcmp( tg, "END" )) DB_RETURN( ST_END ); + else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END ); + else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE ); + else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF ); + break; + } + + DB_RETURN( 0 ); +} + + + +#define SEEN_END 0x00 +#define SEEN_IF 0x01 +#define SEEN_ELSE 0x02 +#define SEEN_ELIF 0x04 + +#define ACCEPT_IF 0x10 +#define ACCEPT_ELIF 0x20 + +static int +_handle_conditional( opcode, tg ) + int opcode; + TKSTRPTR tg; +{ + static short action[MAX_COND_DEPTH]; + static char ifcntl[MAX_COND_DEPTH]; + char *cst; + char *lhs, *expr, *expr_end; + char *lop; + int result; + + DB_ENTER( "_handle_conditional" ); + + switch( opcode ) { + case ST_ELIF: + if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) ) + Fatal(".ELIF without a preceeding .IF" ); + /*FALLTHRU*/ + + case ST_IF: + case ST_IFEQ: + case ST_IFNEQ: + if( opcode != ST_ELIF && (Nest_level+1) == MAX_COND_DEPTH ) + Fatal( ".IF .ELSE ... .END nesting too deep" ); + + If_expand = TRUE; + expr = Expand( Get_token( tg, NIL(char), FALSE )); + If_expand = FALSE; + + /* Remove CONTINUATION_CHAR<nl> and replace with " " so that line + * continuations are recognized as whitespace. */ + for( cst=strchr(expr,CONTINUATION_CHAR); cst != NIL(char); cst=strchr(cst,CONTINUATION_CHAR) ) + if( cst[1] == '\n' ) { + *cst = ' '; + cst[1] = ' '; + } + else + cst++; + + lhs = expr; + SCAN_WHITE( lhs ); + + /* Parse the expression and get its logical result */ + if ( ((lop = DmStrStr(lhs, "||" )) != NIL(char)) || ((lop = DmStrStr(lhs, "&&" )) != NIL(char)) ) + result = parse_complex_expression( lhs, &expr_end, opcode ); + else + result = partcomp( lhs, opcode ); + + if( expr != NIL(char) ) FREE( expr ); + + if( opcode != ST_ELIF ) { + Nest_level++; + action[Nest_level] = 1; + } + ifcntl[Nest_level] |= (opcode==ST_ELIF)?SEEN_ELIF:SEEN_IF; + + if( result ) { + if( !(ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF)) ) { + action[ Nest_level ] = action[ Nest_level-1 ]; + ifcntl[Nest_level] |= (opcode==ST_ELIF)?ACCEPT_ELIF:ACCEPT_IF; + } + else + action[Nest_level] = 1; + } + else + action[Nest_level] = 1; + break; + + case ST_ELSE: + if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" ); + if( ifcntl[Nest_level] & SEEN_ELSE ) + Fatal( "Missing .IF or .ELIF before .ELSE" ); + + if( ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF) ) + action[Nest_level] = 1; + else if( action[ Nest_level-1 ] != 1 ) + action[ Nest_level ] ^= 0x1; /* flip between 0 and 1 */ + + ifcntl[Nest_level] |= SEEN_ELSE; + break; + + case ST_END: + ifcntl[Nest_level] = SEEN_END; + Nest_level--; + if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" ); + break; + } + + DB_RETURN( action[ Nest_level ] ); +} + +/* uncomment to turn on expression debug statements */ +/*#define PARSE_DEBUG */ +#define PARSE_SKIP_WHITE(A) while( *A && ((*A==' ') || (*A=='\t')) ) A++; + +#define OP_NONE 0 +#define OP_AND 1 +#define OP_OR 2 + +static int n = 1; + +int parse_complex_expression( char *expr, char **expr_end, int opcode ) +{ + char *p = expr; + char *term_start = p; + char *term_end; + int local_term; + char *part; + int term_result = FALSE; + int final_result = TRUE; + unsigned int term_len; + unsigned int last_op = OP_NONE; + + #ifdef PARSE_DEBUG + printf( "%d: parse_complex_expression( %s ): Opcode: %d\n", n, expr, opcode ); + #endif + + while ( 1 ) + { + /* A new sub-expression */ + local_term = TRUE; + if ( *p == '(' ) + { + n++; + term_result = parse_complex_expression( p+1, &p, opcode ); + n--; + PARSE_SKIP_WHITE( p ); + term_start = p; + term_end = p; + local_term = FALSE; + } + else + term_end = p; + + /* Lets do an operation!! */ + if ( !(*p) /* at the end of the entire line */ + || ((*p == '&') && (*(p+1) && (*(p+1)=='&'))) /* found an && */ + || ((*p == '|') && (*(p+1) && (*(p+1)=='|'))) /* found an || */ + || (*p == ')') ) /* at the end of our term */ + { + /* Grab the sub-expression if we parsed it. Otherwise, + * it was a () subexpression and we don't need to evaluate + * it since that was already done. + */ + if ( local_term == TRUE ) + { + /* Back up 1 to the end of the actual term */ + term_end--; + + /* Evaluate the term */ + PARSE_SKIP_WHITE( term_start ); + term_len = term_end - term_start + 1; + part = MALLOC( term_len + 1, char ); + strncpy( part, term_start, term_len ); + *(part+term_len) = '\0'; + #ifdef PARSE_DEBUG + printf( "%d: evaling '%s'\n", n, part ); + #endif + term_result = partcomp( part, opcode ); + #ifdef PARSE_DEBUG + printf( "%d: evaled, result %d\n", n, term_result ); + #endif + FREE( part ); + } + + /* Do the actual logical operation using the _preceding_ + * logical operator, NOT the one we just found. + */ + if ( last_op == OP_AND ) + final_result = final_result && term_result; + else if ( last_op == OP_OR ) + final_result = final_result || term_result; + else + final_result = term_result; + #ifdef PARSE_DEBUG + printf( "%d: final_result:%d\n", n, final_result ); + #endif + + /* If we're not at the end of the line, just keep going */ + if ( *p ) + { + /* Recognize the operator we just found above */ + if ( *p == '&' ) + last_op = OP_AND; + else if ( *p == '|' ) + last_op = OP_OR; + if ( *p != ')' ) + p += 2; + + /* Get the start of the next term */ + PARSE_SKIP_WHITE( p ); + term_start = p; + + /* If this is the close of a term, we are done and return + * to our caller. + */ + if ( *p == ')' ) + { + p++; + break; + } + } + else break; /* At end of line, all done */ + } + else if ( local_term == TRUE ) p++; /* Advance to next char in expression */ + } + *expr_end = p; + + #ifdef PARSE_DEBUG + printf( "%d: done, returning '%s', result %d\n", n, *expr_end, final_result ); + #endif + return( final_result ); +} + + +int partcomp( char* lhs, int opcode ) +{ + + char *tok, *rhs, *op = 0; + int result, opsind; + const int localopscount=4; + char* localops[] = { "==", "!=", "<=", ">=" }; + int lint, rint; + +#define EQUAL 0 +#define NOTEQUAL 1 +#define LESS_EQUAL 2 +#define GREATER_EQUAL 3 + + #ifdef PARSE_DEBUG + printf( "eval: %s\n", lhs); + #endif + + opsind = 0; + if( opcode == ST_IFEQ || opcode == ST_IFNEQ ) + { + /* IF[N]EQ syntax is: .IF[N]EQ <1> <2> + * Here, step over first argument and get to <2> if it exists. + */ + for( op = lhs; ((*op)&&(*op != ' ')&&(*op != '\t')); op++ ); + if( *op ) op++; /* position op at start of <2> */ + else op = NIL(char); /* only 1 argument given */ + } + else + { + /* Find which logical operator we are to use for this expression, + * and jump to it */ + while ( (opsind < localopscount) && ((op = DmStrStr(lhs, localops[opsind])) == NIL(char)) ) + opsind++; + + #ifdef PARSE_DEBUG + printf(" found op %d: %s\n", opsind, localops[opsind]); + #endif + } + + /* If the opcode was IFEQ or IFNEQ and only 1 argument was given, + * or an unknown logical operator was encountered, + * return false if argument is empty string, true if !empty + */ + if( op == NIL(char) ) + result = (*lhs != '\0'); + else + { + /* Make both characters of the operation the same, replacing the = in op[1] + * Its easier to deal with this way??? + */ + if( opcode != ST_IFEQ && opcode != ST_IFNEQ ) + op[1] = op[0]; + + #ifdef PARSE_DEBUG + printf(" op:%s\n", op); + #endif + + /* Isolate the left half of the expression */ + if( lhs != op ) + { + for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok-- ); + tok[1] = '\0'; + } + else + lhs = NIL(char); /* Left hand side is empty. */ + + /* Jump over the operation so we can grab the right half of the expression */ + if( opcode == ST_IFEQ || opcode == ST_IFNEQ ) + op--; + else + op++; + + /* Isolate the right half of the expression */ + rhs = DmStrSpn( op+1, " \t" ); + if( !*rhs ) rhs = NIL(char); + + #ifdef PARSE_DEBUG + printf(" lhs:%s, rhs:%s\n", lhs, rhs); + #endif + + /* Do the actual logical operation on the expression */ + if ( opsind > NOTEQUAL ) + { + switch( opsind ) + { + case LESS_EQUAL: + case GREATER_EQUAL: + /* Ignore quotes around the arguments */ + if ( lhs && lhs[0] == '"' ) lhs++; + if ( rhs && rhs[0] == '"' ) rhs++; + + /* Empty strings evaluate to zero. */ + lint = lhs ? atoi( lhs ) : 0; + rint = rhs ? atoi( rhs ) : 0; + result = ( lint >= rint ) ? TRUE : FALSE; + if ( opsind == LESS_EQUAL && lint != rint ) + result = !result; + break; + default: + result = FALSE; + } + } + else + { + /* Use a simple string compare to determine equality */ + if( (rhs == NIL(char)) || (lhs == NIL(char)) ) + result = (rhs == lhs) ? TRUE : FALSE; + else + { + /* String off whitespace at the end of the right half of the expression */ + tok = rhs + strlen( rhs ); + for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--); + tok[1] = '\0'; + + result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE; + } + + if( *op == '!' || opcode == ST_IFNEQ ) result = !result; + } + } + + #ifdef PARSE_DEBUG + printf("partresult %d\n\n",result); + #endif + return result; +} + |