sync master with lastest vba changes
[ooovba.git] / dmake / getinp.c
blobc58e5ddbb20747bef33cc9db78a94168340de011
1 /* RCS $Id: getinp.c,v 1.10 2007-10-15 15:39:23 ihi Exp $
2 --
3 -- SYNOPSIS
4 -- Handle reading of input.
5 --
6 -- DESCRIPTION
7 -- The code in this file reads the input from the specified stream
8 -- into the provided buffer of size Buffer_size. In doing so it deletes
9 -- comments. Comments are delimited by the #, and
10 -- <nl> character sequences. An exception is \# which
11 -- is replaced by # in the input. Line continuations are signalled
12 -- at the end of a line and are recognized inside comments.
13 -- The line continuation is always <\><nl>.
15 -- If the file to read is NIL(FILE) then the Get_line routine returns the
16 -- next rule from the builtin rule table (Rule_tab from ruletab.c) if
17 -- there is one.
19 -- AUTHOR
20 -- Dennis Vadura, dvadura@dmake.wticorp.com
22 -- WWW
23 -- http://dmake.wticorp.com/
25 -- COPYRIGHT
26 -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
28 -- This program is NOT free software; you can redistribute it and/or
29 -- modify it under the terms of the Software License Agreement Provided
30 -- in the file <distribution-root>/readme/license.txt.
32 -- LOG
33 -- Use cvs log to obtain detailed change logs.
36 #include "extern.h"
38 #define IS_WHITE(A) ((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r'))
39 #define SCAN_WHITE(A) \
40 while( IS_WHITE(*A) ) A++;
42 static int _is_conditional ANSI((char*));
43 static int _handle_conditional ANSI((int, TKSTRPTR));
45 static int rule_ind = 0; /* index of rule when reading Rule_tab */
46 static int skip = FALSE; /* if true the skip input */
48 int partcomp( char* lhs, int opcode );
49 int parse_complex_expression( char *expr, char **expr_end, int opcode );
52 PUBLIC int
53 Get_line( buf, fil )/*
54 ======================
55 Read a line of input from the file stripping off comments. The routine
56 returns TRUE if EOF. If fil equals NIL(FILE) then the next line from
57 *Rule_tab[] is used. Rule_tab is either the buildin rule table or points
58 to the current environment (used by ReadEnvironment()).
59 The function returns TRUE if the input file/buffer was read to the end
60 and FALSE otherwise. */
61 char *buf;
62 FILE *fil;
64 extern char **Rule_tab;
65 register char *p;
66 register char *c;
67 char *q;
68 char *buf_org;
69 static int ignore = FALSE;
70 int cont = FALSE;
71 int pos = 0;
72 int res = 0;
73 register char *tmp = NIL(char);
75 DB_ENTER( "Get_line" );
77 if( Skip_to_eof ) {
78 Skip_to_eof = FALSE;
79 rule_ind = 0;
81 if( Verbose & V_MAKE )
82 Warning("Ignoring remainder of file %s", Filename());
84 DB_RETURN(TRUE);
87 if( fil == NIL(FILE) ) {
88 /* Reading the internal rule table. Set rule_ind to zero after the
89 * last entry so that ReadEnvironment() works as expected every time. */
91 while( (p = Rule_tab[ rule_ind++ ]) != NIL(char) )
92 /* The last test in this if *p != '~', handles the environment
93 * passing conventions used by MKS to pass arguments. We want to
94 * skip those environment entries. Also CYGWIN likes to export '!'
95 * prefixed environment variables that cause severe pain, axe them too */
96 if( !Readenv || (Readenv && (strchr(p,'=') != NIL(char)) && *p!='~' && *p!='!')){
97 strcpy( buf, p );
99 DB_PRINT( "io", ("Returning [%s]", buf) );
100 DB_RETURN( FALSE );
103 rule_ind = 0;
105 DB_PRINT( "io", ("Done Ruletab") );
106 DB_RETURN( TRUE );
109 buf_org = buf;
111 do_again:
112 do {
113 p = buf+pos;
114 /* fgets() reads at most one less than Buffer_size-pos characters. */
115 if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char)))
116 DB_RETURN( TRUE );
118 #ifdef _MPW
119 if ( p[0] == 10 && p[1] == COMMENT_CHAR)
120 p[0] = ' ';
121 #endif
123 Line_number++;
125 /* Set q to the last char in p before the \n\0. */
126 q = p+strlen(p)-2;
127 if( q >= p ) { /* Only check for special cases if p points
128 * to a non-empty line. */
130 /* ignore each RETURN at the end of a line before any further
131 * processing */
132 if( q[0] == '\r' && q[1] == '\n' ) {
133 q[0] = '\n';
134 q[1] = '\0';
135 q--;
137 /* you also have to deal with END_OF_FILE chars to process raw
138 * DOS-Files. Normally they are the last chars in file, but after
139 * working on these file with vi, there is an additional NEWLINE
140 * after the last END_OF_FILE. So if the second last char in the
141 * actual line is END_OF_FILE, you can skip the last char. Then
142 * you can search the line back until you find no more END_OF_FILE
143 * and nuke each you found by string termination. */
144 if( q[0] == '\032' )
145 q--;
146 while( q[1] == '\032' ) {
147 q[1] = '\0';
148 q--;
151 /* ignore input if ignore flag set and line ends in a continuation
152 character. */
154 if( ignore ) {
155 if( q[0] != CONTINUATION_CHAR || q[1] != '\n' ) ignore = FALSE;
156 *p = '\0';
157 continue;
160 /* If a comment is found the line does not end in \n anymore. */
161 c = Do_comment(p, &q, Group || (*buf == '\t') || (Notabs && *buf ==' '));
163 /* Does the end of the line end in a continuation sequence? */
165 if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) {
166 /* If the continuation was at the end of a comment then ignore the
167 * next input line, (or lines until we get one ending in just <nl>)
168 * else it's a continuation, so build the input line from several
169 * text lines on input. The maximum size of this is governened by
170 * Buffer_size */
171 if( q != p && q[-1] == CONTINUATION_CHAR ) {
172 strcpy( q, q+1 );
173 q--;
174 cont = FALSE;
176 else if( c != NIL(char) )
177 ignore = TRUE;
178 else
179 cont = TRUE; /* Keep the \<nl>. */
181 else {
182 cont = FALSE;
185 q = ( c == NIL(char) ) ? q+2 : c;
187 else { /* empty line or "" */
188 cont = FALSE;
189 ignore = FALSE;
190 q = p+strlen(p); /* strlen(p) is 1 or 0 */
193 pos += q-p;
195 while( (cont || !*buf) && (pos < Buffer_size-1) );
197 if( pos >= Buffer_size-1 )
198 Fatal( "Input line too long, increase MAXLINELENGTH" );
200 /* Lines that had comments don't contain \n anymore. */
201 /* ??? Continued lines that are followed by an empty or comment only
202 * line will end in \<nl>. */
203 if( (q > p) && (buf[ pos-1 ] == '\n') )
204 buf[ --pos ] = '\0'; /* Remove the final \n. */
206 /* STUPID AUGMAKE uses "include" at the start of a line as
207 * a signal to include a new file, so let's look for it.
208 * if we see it replace it by .INCLUDE: and stick this back
209 * into the buffer. We also allow GNU make if[n]eq/else/endif.
211 * These substitutions are made only if we are not parsing a group
212 * recipe. */
213 if( (p = DmStrSpn(buf, " \t\r\n")) == NIL(char) )
214 p = buf;
216 if (!Group) {
217 if( !strncmp( "include", p, 7 ) &&
218 (p[7] == ' ' || p[7] == '\t') )
219 tmp = DmStrJoin( ".INCLUDE:", p+7, -1, FALSE );
220 else if( !strncmp( "ifeq", p, 4 ) &&
221 (p[4] == ' ' || p[4] == '\t') )
222 tmp = DmStrJoin( ".IFEQ", p+4, -1, FALSE );
223 else if( !strncmp( "ifneq", p, 5 ) &&
224 (p[5] == ' ' || p[5] == '\t') )
225 tmp = DmStrJoin( ".IFNEQ", p+5, -1, FALSE );
226 else if( !strncmp( "elif", p, 4 ) &&
227 (p[4] == ' ' || p[4] == '\t') )
228 tmp = DmStrJoin( ".ELIF", p+4, -1, FALSE );
229 else if( !strncmp( "else", p, 4 ) &&
230 (p[4] == ' ' || p[4] == '\t' || p[4] == '\0') )
231 tmp = DmStrJoin( ".ELSE", p+4, -1, FALSE );
232 else if( !strncmp( "endif", p, 5 ) &&
233 (p[5] == ' ' || p[5] == '\t' || p[5] == '\0') )
234 tmp = DmStrJoin( ".END", p+5, -1, FALSE );
237 if( tmp != NIL(char)) {
238 strcpy( buf, tmp );
239 FREE( tmp );
240 tmp = NIL(char);
243 /* Now that we have the next line of input to make, we should check to
244 * see if it is a conditional expression. If it is then process it,
245 * otherwise pass it on to the parser. */
247 if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART ) {
248 TKSTR token;
250 SET_TOKEN( &token, p );
252 p = Get_token( &token, "", FALSE );
254 if( (res = _is_conditional(p)) != 0 ) /* ignore non-control special */
255 { /* targets */
256 res = _handle_conditional( res, &token );
257 skip = TRUE;
259 else {
260 CLEAR_TOKEN( &token );
261 res = TRUE;
265 if( skip ) {
266 buf = buf_org; /* ignore line just read in */
267 pos = 0;
268 skip = res;
269 goto do_again;
272 DB_PRINT( "io", ("Returning [%s]", buf) );
273 DB_RETURN( FALSE );
277 PUBLIC char *
278 Do_comment(str, pend, keep)/*
279 =============================
280 Search the input string looking for comment chars. If it contains
281 comment chars then NUKE the remainder of the line, if the comment
282 char is preceeded by \ then shift the remainder of the line left
283 by one char. */
284 char *str;
285 char **pend;
286 int keep;
288 char *c = str;
290 while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) {
291 if( Comment || State == NORMAL_SCAN )
292 if( c != str && c[-1] == ESCAPE_CHAR ) {
293 strcpy( c-1, c ); /* copy it left, due to \# */
294 if( pend ) (*pend)--; /* shift tail pointer left */
296 else {
297 /* Check/execute if shebang command is present. */
298 if( !No_exec
299 && c == str
300 && c[1] == '!'
301 && Line_number == 1
302 && Nestlevel() == 1 ) {
303 char *cmnd;
305 cmnd = Expand(c+2);
306 cmnd[strlen(cmnd)-1] = '\0'; /* strip last newline */
307 Current_target = Root;
308 #if defined(MSDOS)
309 Swap_on_exec = TRUE;
310 #endif
311 Wait_for_completion = TRUE;
312 Do_cmnd(&cmnd, FALSE, TRUE, Current_target, A_DEFAULT, TRUE);
313 #if defined(MSDOS)
314 Swap_on_exec = FALSE;
315 #endif
316 Wait_for_completion = FALSE;
317 FREE(cmnd);
320 *c = '\0'; /* a true comment so break */
321 break;
323 else {
324 if( keep )
325 c = NIL(char);
326 else
327 *c = '\0';
329 break;
333 return(c);
337 PUBLIC char *
338 Get_token( string, brk, anchor )/*
339 ==================================
340 Return the next token in string.
342 Returns empty string when no more tokens in string.
343 brk is a list of chars that also cause breaks in addition to space and
344 tab, but are themselves returned as tokens. if brk is NULL then the
345 remainder of the line is returned as a single token.
347 'anchor' if 1, says break on chars in the brk list, but only if
348 the entire token begins with the first char of the brk list, if
349 0 then any char of brk will cause a break to occurr.
351 If 'anchor' is 2, then break only seeing the first char in the break
352 list allowing only chars in the break list to form the prefix. */
354 TKSTRPTR string;
355 char *brk;
356 int anchor;
358 register char *s;
359 register char *curp = 0;
360 register char *t;
361 int done = FALSE;
362 char space[100];
364 DB_ENTER( "Get_token" );
366 s = string->tk_str; /* Get string parameters */
367 *s = string->tk_cchar; /* ... and strip leading w/s */
369 SCAN_WHITE( s );
371 DB_PRINT( "tok", ("What's left [%s]", s) );
373 if( !*s ) {
374 DB_PRINT( "tok", ("Returning NULL token") );
375 DB_RETURN( "" );
379 /* Build the space list. space contains all those chars that may possibly
380 * cause breaks. This includes the brk list as well as white space. */
382 if( brk != NIL(char) ) {
383 strcpy( space, " \t\r\n" );
384 strcat( space, brk );
386 else {
387 space[0] = 0xff; /* a char we know will not show up */
388 space[1] = 0;
392 /* Handle processing of quoted tokens. Note that this is disabled if
393 * brk is equal to NIL */
395 while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) {
396 s++;
397 if( string->tk_quote ) {
398 curp = s-1;
399 do { curp = strchr( curp+1, '\"' ); }
400 while( (curp != NIL(char)) && (*(curp+1) == '\"'));
402 if( curp == NIL(char) ) Fatal( "Unmatched quote in token" );
403 string->tk_quote = !string->tk_quote;
405 /* Check for "" case, and if found ignore it */
406 if( curp == s ) continue;
407 goto found_token;
409 else
410 SCAN_WHITE( s );
412 string->tk_quote = !string->tk_quote;
416 /* Check for a token break character at the beginning of the token.
417 * If found return the next set of break chars as a token. */
419 if( anchor == 2 && brk != NIL(char) ) {
420 curp = s;
421 while( *curp && (strchr(brk,*curp)!=NIL(char)) && (*curp!=*brk) ) curp++;
422 done = (*brk == *curp++);
424 else if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) {
425 curp = DmStrSpn( s, brk );
426 done = (anchor == 0) ? TRUE :
427 ((anchor == 1)?(*s == *brk) : (*brk == curp[-1]));
431 /* Scan for the next token in the list and return it less the break char
432 * that was used to terminate the token. It will possibly be returned in
433 * the next call to Get_token */
435 if( !done ) {
436 SCAN_WHITE( s );
438 t = s;
439 do {
440 done = TRUE;
441 curp = DmStrPbrk(t, space);
443 if( anchor && *curp && !IS_WHITE( *curp ) )
444 if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) {
445 t++;
446 done = FALSE;
449 while( !done );
451 if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++;
454 found_token:
455 string->tk_str = curp;
456 string->tk_cchar = *curp;
457 *curp = '\0';
459 DB_PRINT( "tok", ("Returning [%s]", s) );
460 DB_RETURN( s );
464 static int
465 _is_conditional( tg )/*
466 =======================
467 Look at tg and return it's value if it is a conditional identifier
468 otherwise return 0. */
469 char *tg;
471 DB_ENTER( "_is_conditional" );
473 tg++;
474 switch( *tg )
476 case 'I':
477 if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF );
478 else if( !strcmp( tg, "IFEQ" )) DB_RETURN( ST_IFEQ );
479 else if( !strcmp( tg, "IFNEQ" )) DB_RETURN( ST_IFNEQ );
480 break;
482 case 'E':
483 if( !strcmp( tg, "END" )) DB_RETURN( ST_END );
484 else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END );
485 else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE );
486 else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF );
487 break;
490 DB_RETURN( 0 );
495 #define SEEN_END 0x00
496 #define SEEN_IF 0x01
497 #define SEEN_ELSE 0x02
498 #define SEEN_ELIF 0x04
500 #define ACCEPT_IF 0x10
501 #define ACCEPT_ELIF 0x20
503 static int
504 _handle_conditional( opcode, tg )
505 int opcode;
506 TKSTRPTR tg;
508 static short action[MAX_COND_DEPTH];
509 static char ifcntl[MAX_COND_DEPTH];
510 char *cst;
511 char *lhs, *expr, *expr_end;
512 char *lop;
513 int result;
515 DB_ENTER( "_handle_conditional" );
517 switch( opcode ) {
518 case ST_ELIF:
519 if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) )
520 Fatal(".ELIF without a preceeding .IF" );
521 /*FALLTHRU*/
523 case ST_IF:
524 case ST_IFEQ:
525 case ST_IFNEQ:
526 if( opcode != ST_ELIF && (Nest_level+1) == MAX_COND_DEPTH )
527 Fatal( ".IF .ELSE ... .END nesting too deep" );
529 If_expand = TRUE;
530 expr = Expand( Get_token( tg, NIL(char), FALSE ));
531 If_expand = FALSE;
533 /* Remove CONTINUATION_CHAR<nl> and replace with " " so that line
534 * continuations are recognized as whitespace. */
535 for( cst=strchr(expr,CONTINUATION_CHAR); cst != NIL(char); cst=strchr(cst,CONTINUATION_CHAR) )
536 if( cst[1] == '\n' ) {
537 *cst = ' ';
538 cst[1] = ' ';
540 else
541 cst++;
543 lhs = expr;
544 SCAN_WHITE( lhs );
546 /* Parse the expression and get its logical result */
547 if ( ((lop = DmStrStr(lhs, "||" )) != NIL(char)) || ((lop = DmStrStr(lhs, "&&" )) != NIL(char)) )
548 result = parse_complex_expression( lhs, &expr_end, opcode );
549 else
550 result = partcomp( lhs, opcode );
552 if( expr != NIL(char) ) FREE( expr );
554 if( opcode != ST_ELIF ) {
555 Nest_level++;
556 action[Nest_level] = 1;
558 ifcntl[Nest_level] |= (opcode==ST_ELIF)?SEEN_ELIF:SEEN_IF;
560 if( result ) {
561 if( !(ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF)) ) {
562 action[ Nest_level ] = action[ Nest_level-1 ];
563 ifcntl[Nest_level] |= (opcode==ST_ELIF)?ACCEPT_ELIF:ACCEPT_IF;
565 else
566 action[Nest_level] = 1;
568 else
569 action[Nest_level] = 1;
570 break;
572 case ST_ELSE:
573 if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" );
574 if( ifcntl[Nest_level] & SEEN_ELSE )
575 Fatal( "Missing .IF or .ELIF before .ELSE" );
577 if( ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF) )
578 action[Nest_level] = 1;
579 else if( action[ Nest_level-1 ] != 1 )
580 action[ Nest_level ] ^= 0x1; /* flip between 0 and 1 */
582 ifcntl[Nest_level] |= SEEN_ELSE;
583 break;
585 case ST_END:
586 ifcntl[Nest_level] = SEEN_END;
587 Nest_level--;
588 if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" );
589 break;
592 DB_RETURN( action[ Nest_level ] );
595 /* uncomment to turn on expression debug statements */
596 /*#define PARSE_DEBUG */
597 #define PARSE_SKIP_WHITE(A) while( *A && ((*A==' ') || (*A=='\t')) ) A++;
599 #define OP_NONE 0
600 #define OP_AND 1
601 #define OP_OR 2
603 static int n = 1;
605 int parse_complex_expression( char *expr, char **expr_end, int opcode )
607 char *p = expr;
608 char *term_start = p;
609 char *term_end;
610 int local_term;
611 char *part;
612 int term_result = FALSE;
613 int final_result = TRUE;
614 unsigned int term_len;
615 unsigned int last_op = OP_NONE;
617 #ifdef PARSE_DEBUG
618 printf( "%d: parse_complex_expression( %s ): Opcode: %d\n", n, expr, opcode );
619 #endif
621 while ( 1 )
623 /* A new sub-expression */
624 local_term = TRUE;
625 if ( *p == '(' )
627 n++;
628 term_result = parse_complex_expression( p+1, &p, opcode );
629 n--;
630 PARSE_SKIP_WHITE( p );
631 term_start = p;
632 term_end = p;
633 local_term = FALSE;
635 else
636 term_end = p;
638 /* Lets do an operation!! */
639 if ( !(*p) /* at the end of the entire line */
640 || ((*p == '&') && (*(p+1) && (*(p+1)=='&'))) /* found an && */
641 || ((*p == '|') && (*(p+1) && (*(p+1)=='|'))) /* found an || */
642 || (*p == ')') ) /* at the end of our term */
644 /* Grab the sub-expression if we parsed it. Otherwise,
645 * it was a () subexpression and we don't need to evaluate
646 * it since that was already done.
648 if ( local_term == TRUE )
650 /* Back up 1 to the end of the actual term */
651 term_end--;
653 /* Evaluate the term */
654 PARSE_SKIP_WHITE( term_start );
655 term_len = term_end - term_start + 1;
656 part = MALLOC( term_len + 1, char );
657 strncpy( part, term_start, term_len );
658 *(part+term_len) = '\0';
659 #ifdef PARSE_DEBUG
660 printf( "%d: evaling '%s'\n", n, part );
661 #endif
662 term_result = partcomp( part, opcode );
663 #ifdef PARSE_DEBUG
664 printf( "%d: evaled, result %d\n", n, term_result );
665 #endif
666 FREE( part );
669 /* Do the actual logical operation using the _preceding_
670 * logical operator, NOT the one we just found.
672 if ( last_op == OP_AND )
673 final_result = final_result && term_result;
674 else if ( last_op == OP_OR )
675 final_result = final_result || term_result;
676 else
677 final_result = term_result;
678 #ifdef PARSE_DEBUG
679 printf( "%d: final_result:%d\n", n, final_result );
680 #endif
682 /* If we're not at the end of the line, just keep going */
683 if ( *p )
685 /* Recognize the operator we just found above */
686 if ( *p == '&' )
687 last_op = OP_AND;
688 else if ( *p == '|' )
689 last_op = OP_OR;
690 if ( *p != ')' )
691 p += 2;
693 /* Get the start of the next term */
694 PARSE_SKIP_WHITE( p );
695 term_start = p;
697 /* If this is the close of a term, we are done and return
698 * to our caller.
700 if ( *p == ')' )
702 p++;
703 break;
706 else break; /* At end of line, all done */
708 else if ( local_term == TRUE ) p++; /* Advance to next char in expression */
710 *expr_end = p;
712 #ifdef PARSE_DEBUG
713 printf( "%d: done, returning '%s', result %d\n", n, *expr_end, final_result );
714 #endif
715 return( final_result );
719 int partcomp( char* lhs, int opcode )
722 char *tok, *rhs, *op = 0;
723 int result, opsind;
724 const int localopscount=4;
725 char* localops[] = { "==", "!=", "<=", ">=" };
726 int lint, rint;
728 #define EQUAL 0
729 #define NOTEQUAL 1
730 #define LESS_EQUAL 2
731 #define GREATER_EQUAL 3
733 #ifdef PARSE_DEBUG
734 printf( "eval: %s\n", lhs);
735 #endif
737 opsind = 0;
738 if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
740 /* IF[N]EQ syntax is: .IF[N]EQ <1> <2>
741 * Here, step over first argument and get to <2> if it exists.
743 for( op = lhs; ((*op)&&(*op != ' ')&&(*op != '\t')); op++ );
744 if( *op ) op++; /* position op at start of <2> */
745 else op = NIL(char); /* only 1 argument given */
747 else
749 /* Find which logical operator we are to use for this expression,
750 * and jump to it */
751 while ( (opsind < localopscount) && ((op = DmStrStr(lhs, localops[opsind])) == NIL(char)) )
752 opsind++;
754 #ifdef PARSE_DEBUG
755 printf(" found op %d: %s\n", opsind, localops[opsind]);
756 #endif
759 /* If the opcode was IFEQ or IFNEQ and only 1 argument was given,
760 * or an unknown logical operator was encountered,
761 * return false if argument is empty string, true if !empty
763 if( op == NIL(char) )
764 result = (*lhs != '\0');
765 else
767 /* Make both characters of the operation the same, replacing the = in op[1]
768 * Its easier to deal with this way???
770 if( opcode != ST_IFEQ && opcode != ST_IFNEQ )
771 op[1] = op[0];
773 #ifdef PARSE_DEBUG
774 printf(" op:%s\n", op);
775 #endif
777 /* Isolate the left half of the expression */
778 if( lhs != op )
780 for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok-- );
781 tok[1] = '\0';
783 else
784 lhs = NIL(char); /* Left hand side is empty. */
786 /* Jump over the operation so we can grab the right half of the expression */
787 if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
788 op--;
789 else
790 op++;
792 /* Isolate the right half of the expression */
793 rhs = DmStrSpn( op+1, " \t" );
794 if( !*rhs ) rhs = NIL(char);
796 #ifdef PARSE_DEBUG
797 printf(" lhs:%s, rhs:%s\n", lhs, rhs);
798 #endif
800 /* Do the actual logical operation on the expression */
801 if ( opsind > NOTEQUAL )
803 switch( opsind )
805 case LESS_EQUAL:
806 case GREATER_EQUAL:
807 /* Ignore quotes around the arguments */
808 if ( lhs && lhs[0] == '"' ) lhs++;
809 if ( rhs && rhs[0] == '"' ) rhs++;
811 /* Empty strings evaluate to zero. */
812 lint = lhs ? atoi( lhs ) : 0;
813 rint = rhs ? atoi( rhs ) : 0;
814 result = ( lint >= rint ) ? TRUE : FALSE;
815 if ( opsind == LESS_EQUAL && lint != rint )
816 result = !result;
817 break;
818 default:
819 result = FALSE;
822 else
824 /* Use a simple string compare to determine equality */
825 if( (rhs == NIL(char)) || (lhs == NIL(char)) )
826 result = (rhs == lhs) ? TRUE : FALSE;
827 else
829 /* String off whitespace at the end of the right half of the expression */
830 tok = rhs + strlen( rhs );
831 for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--);
832 tok[1] = '\0';
834 result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE;
837 if( *op == '!' || opcode == ST_IFNEQ ) result = !result;
841 #ifdef PARSE_DEBUG
842 printf("partresult %d\n\n",result);
843 #endif
844 return result;