Update ooo320-m1
[ooovba.git] / dmake / getinp.c
blob91219228fde89a178b18ba36854c822a4f3c9b26
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 * And finally it is possible to do "env 'GGG HHH'='some value' bash"
97 * which causes that there are env variables with spaces in the name
98 * defined which causes dmake to malfunction too */
99 char *equal = strchr(p,'=');
100 char *space = strchr(p,' ');
101 if( !Readenv || (Readenv && (equal != NIL(char)) && (space == NIL(char) || space > equal) && *p!='~' && *p!='!')){
102 strcpy( buf, p );
104 DB_PRINT( "io", ("Returning [%s]", buf) );
105 DB_RETURN( FALSE );
109 rule_ind = 0;
111 DB_PRINT( "io", ("Done Ruletab") );
112 DB_RETURN( TRUE );
115 buf_org = buf;
117 do_again:
118 do {
119 p = buf+pos;
120 /* fgets() reads at most one less than Buffer_size-pos characters. */
121 if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char)))
122 DB_RETURN( TRUE );
124 #ifdef _MPW
125 if ( p[0] == 10 && p[1] == COMMENT_CHAR)
126 p[0] = ' ';
127 #endif
129 Line_number++;
131 /* Set q to the last char in p before the \n\0. */
132 q = p+strlen(p)-2;
133 if( q >= p ) { /* Only check for special cases if p points
134 * to a non-empty line. */
136 /* ignore each RETURN at the end of a line before any further
137 * processing */
138 if( q[0] == '\r' && q[1] == '\n' ) {
139 q[0] = '\n';
140 q[1] = '\0';
141 q--;
143 /* you also have to deal with END_OF_FILE chars to process raw
144 * DOS-Files. Normally they are the last chars in file, but after
145 * working on these file with vi, there is an additional NEWLINE
146 * after the last END_OF_FILE. So if the second last char in the
147 * actual line is END_OF_FILE, you can skip the last char. Then
148 * you can search the line back until you find no more END_OF_FILE
149 * and nuke each you found by string termination. */
150 if( q[0] == '\032' )
151 q--;
152 while( q[1] == '\032' ) {
153 q[1] = '\0';
154 q--;
157 /* ignore input if ignore flag set and line ends in a continuation
158 character. */
160 if( ignore ) {
161 if( q[0] != CONTINUATION_CHAR || q[1] != '\n' ) ignore = FALSE;
162 *p = '\0';
163 continue;
166 /* If a comment is found the line does not end in \n anymore. */
167 c = Do_comment(p, &q, Group || (*buf == '\t') || (Notabs && *buf ==' '));
169 /* Does the end of the line end in a continuation sequence? */
171 if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) {
172 /* If the continuation was at the end of a comment then ignore the
173 * next input line, (or lines until we get one ending in just <nl>)
174 * else it's a continuation, so build the input line from several
175 * text lines on input. The maximum size of this is governened by
176 * Buffer_size */
177 if( q != p && q[-1] == CONTINUATION_CHAR ) {
178 size_t len = strlen(q+1)+1;
179 memmove( q, q+1, len );
180 q--;
181 cont = FALSE;
183 else if( c != NIL(char) )
184 ignore = TRUE;
185 else
186 cont = TRUE; /* Keep the \<nl>. */
188 else {
189 cont = FALSE;
192 q = ( c == NIL(char) ) ? q+2 : c;
194 else { /* empty line or "" */
195 cont = FALSE;
196 ignore = FALSE;
197 q = p+strlen(p); /* strlen(p) is 1 or 0 */
200 pos += q-p;
202 while( (cont || !*buf) && (pos < Buffer_size-1) );
204 if( pos >= Buffer_size-1 )
205 Fatal( "Input line too long, increase MAXLINELENGTH" );
207 /* Lines that had comments don't contain \n anymore. */
208 /* ??? Continued lines that are followed by an empty or comment only
209 * line will end in \<nl>. */
210 if( (q > p) && (buf[ pos-1 ] == '\n') )
211 buf[ --pos ] = '\0'; /* Remove the final \n. */
213 /* STUPID AUGMAKE uses "include" at the start of a line as
214 * a signal to include a new file, so let's look for it.
215 * if we see it replace it by .INCLUDE: and stick this back
216 * into the buffer. We also allow GNU make if[n]eq/else/endif.
218 * These substitutions are made only if we are not parsing a group
219 * recipe. */
220 if( (p = DmStrSpn(buf, " \t\r\n")) == NIL(char) )
221 p = buf;
223 if (!Group) {
224 if( !strncmp( "include", p, 7 ) &&
225 (p[7] == ' ' || p[7] == '\t') )
226 tmp = DmStrJoin( ".INCLUDE:", p+7, -1, FALSE );
227 else if( !strncmp( "ifeq", p, 4 ) &&
228 (p[4] == ' ' || p[4] == '\t') )
229 tmp = DmStrJoin( ".IFEQ", p+4, -1, FALSE );
230 else if( !strncmp( "ifneq", p, 5 ) &&
231 (p[5] == ' ' || p[5] == '\t') )
232 tmp = DmStrJoin( ".IFNEQ", p+5, -1, FALSE );
233 else if( !strncmp( "elif", p, 4 ) &&
234 (p[4] == ' ' || p[4] == '\t') )
235 tmp = DmStrJoin( ".ELIF", p+4, -1, FALSE );
236 else if( !strncmp( "else", p, 4 ) &&
237 (p[4] == ' ' || p[4] == '\t' || p[4] == '\0') )
238 tmp = DmStrJoin( ".ELSE", p+4, -1, FALSE );
239 else if( !strncmp( "endif", p, 5 ) &&
240 (p[5] == ' ' || p[5] == '\t' || p[5] == '\0') )
241 tmp = DmStrJoin( ".END", p+5, -1, FALSE );
244 if( tmp != NIL(char)) {
245 strcpy( buf, tmp );
246 FREE( tmp );
247 tmp = NIL(char);
250 /* Now that we have the next line of input to make, we should check to
251 * see if it is a conditional expression. If it is then process it,
252 * otherwise pass it on to the parser. */
254 if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART ) {
255 TKSTR token;
257 SET_TOKEN( &token, p );
259 p = Get_token( &token, "", FALSE );
261 if( (res = _is_conditional(p)) != 0 ) /* ignore non-control special */
262 { /* targets */
263 res = _handle_conditional( res, &token );
264 skip = TRUE;
266 else {
267 CLEAR_TOKEN( &token );
268 res = TRUE;
272 if( skip ) {
273 buf = buf_org; /* ignore line just read in */
274 pos = 0;
275 skip = res;
276 goto do_again;
279 DB_PRINT( "io", ("Returning [%s]", buf) );
280 DB_RETURN( FALSE );
284 PUBLIC char *
285 Do_comment(str, pend, keep)/*
286 =============================
287 Search the input string looking for comment chars. If it contains
288 comment chars then NUKE the remainder of the line, if the comment
289 char is preceeded by \ then shift the remainder of the line left
290 by one char. */
291 char *str;
292 char **pend;
293 int keep;
295 char *c = str;
297 while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) {
298 if( Comment || State == NORMAL_SCAN )
299 if( c != str && c[-1] == ESCAPE_CHAR ) {
300 size_t len = strlen(c)+1;
301 memmove( c-1, c, len ); /* copy it left, due to \# */
302 if( pend ) (*pend)--; /* shift tail pointer left */
304 else {
305 /* Check/execute if shebang command is present. */
306 if( !No_exec
307 && c == str
308 && c[1] == '!'
309 && Line_number == 1
310 && Nestlevel() == 1 ) {
311 char *cmnd;
313 cmnd = Expand(c+2);
314 cmnd[strlen(cmnd)-1] = '\0'; /* strip last newline */
315 Current_target = Root;
316 #if defined(MSDOS)
317 Swap_on_exec = TRUE;
318 #endif
319 Wait_for_completion = TRUE;
320 Do_cmnd(&cmnd, FALSE, TRUE, Current_target, A_DEFAULT, TRUE);
321 #if defined(MSDOS)
322 Swap_on_exec = FALSE;
323 #endif
324 Wait_for_completion = FALSE;
325 FREE(cmnd);
328 *c = '\0'; /* a true comment so break */
329 break;
331 else {
332 if( keep )
333 c = NIL(char);
334 else
335 *c = '\0';
337 break;
341 return(c);
345 PUBLIC char *
346 Get_token( string, brk, anchor )/*
347 ==================================
348 Return the next token in string.
350 Returns empty string when no more tokens in string.
351 brk is a list of chars that also cause breaks in addition to space and
352 tab, but are themselves returned as tokens. if brk is NULL then the
353 remainder of the line is returned as a single token.
355 'anchor' if 1, says break on chars in the brk list, but only if
356 the entire token begins with the first char of the brk list, if
357 0 then any char of brk will cause a break to occurr.
359 If 'anchor' is 2, then break only seeing the first char in the break
360 list allowing only chars in the break list to form the prefix. */
362 TKSTRPTR string;
363 char *brk;
364 int anchor;
366 register char *s;
367 register char *curp = 0;
368 register char *t;
369 int done = FALSE;
370 char space[100];
372 DB_ENTER( "Get_token" );
374 s = string->tk_str; /* Get string parameters */
375 *s = string->tk_cchar; /* ... and strip leading w/s */
377 SCAN_WHITE( s );
379 DB_PRINT( "tok", ("What's left [%s]", s) );
381 if( !*s ) {
382 DB_PRINT( "tok", ("Returning NULL token") );
383 DB_RETURN( "" );
387 /* Build the space list. space contains all those chars that may possibly
388 * cause breaks. This includes the brk list as well as white space. */
390 if( brk != NIL(char) ) {
391 strcpy( space, " \t\r\n" );
392 strcat( space, brk );
394 else {
395 space[0] = 0xff; /* a char we know will not show up */
396 space[1] = 0;
400 /* Handle processing of quoted tokens. Note that this is disabled if
401 * brk is equal to NIL */
403 while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) {
404 s++;
405 if( string->tk_quote ) {
406 curp = s-1;
407 do { curp = strchr( curp+1, '\"' ); }
408 while( (curp != NIL(char)) && (*(curp+1) == '\"'));
410 if( curp == NIL(char) ) Fatal( "Unmatched quote in token" );
411 string->tk_quote = !string->tk_quote;
413 /* Check for "" case, and if found ignore it */
414 if( curp == s ) continue;
415 goto found_token;
417 else
418 SCAN_WHITE( s );
420 string->tk_quote = !string->tk_quote;
424 /* Check for a token break character at the beginning of the token.
425 * If found return the next set of break chars as a token. */
427 if( anchor == 2 && brk != NIL(char) ) {
428 curp = s;
429 while( *curp && (strchr(brk,*curp)!=NIL(char)) && (*curp!=*brk) ) curp++;
430 done = (*brk == *curp++);
432 else if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) {
433 curp = DmStrSpn( s, brk );
434 done = (anchor == 0) ? TRUE :
435 ((anchor == 1)?(*s == *brk) : (*brk == curp[-1]));
439 /* Scan for the next token in the list and return it less the break char
440 * that was used to terminate the token. It will possibly be returned in
441 * the next call to Get_token */
443 if( !done ) {
444 SCAN_WHITE( s );
446 t = s;
447 do {
448 done = TRUE;
449 curp = DmStrPbrk(t, space);
451 if( anchor && *curp && !IS_WHITE( *curp ) )
452 if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) {
453 t++;
454 done = FALSE;
457 while( !done );
459 if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++;
462 found_token:
463 string->tk_str = curp;
464 string->tk_cchar = *curp;
465 *curp = '\0';
467 DB_PRINT( "tok", ("Returning [%s]", s) );
468 DB_RETURN( s );
472 static int
473 _is_conditional( tg )/*
474 =======================
475 Look at tg and return it's value if it is a conditional identifier
476 otherwise return 0. */
477 char *tg;
479 DB_ENTER( "_is_conditional" );
481 tg++;
482 switch( *tg )
484 case 'I':
485 if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF );
486 else if( !strcmp( tg, "IFEQ" )) DB_RETURN( ST_IFEQ );
487 else if( !strcmp( tg, "IFNEQ" )) DB_RETURN( ST_IFNEQ );
488 break;
490 case 'E':
491 if( !strcmp( tg, "END" )) DB_RETURN( ST_END );
492 else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END );
493 else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE );
494 else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF );
495 break;
498 DB_RETURN( 0 );
503 #define SEEN_END 0x00
504 #define SEEN_IF 0x01
505 #define SEEN_ELSE 0x02
506 #define SEEN_ELIF 0x04
508 #define ACCEPT_IF 0x10
509 #define ACCEPT_ELIF 0x20
511 static int
512 _handle_conditional( opcode, tg )
513 int opcode;
514 TKSTRPTR tg;
516 static short action[MAX_COND_DEPTH];
517 static char ifcntl[MAX_COND_DEPTH];
518 char *cst;
519 char *lhs, *expr, *expr_end;
520 char *lop;
521 int result;
523 DB_ENTER( "_handle_conditional" );
525 switch( opcode ) {
526 case ST_ELIF:
527 if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) )
528 Fatal(".ELIF without a preceeding .IF" );
529 /*FALLTHRU*/
531 case ST_IF:
532 case ST_IFEQ:
533 case ST_IFNEQ:
534 if( opcode != ST_ELIF && (Nest_level+1) == MAX_COND_DEPTH )
535 Fatal( ".IF .ELSE ... .END nesting too deep" );
537 If_expand = TRUE;
538 expr = Expand( Get_token( tg, NIL(char), FALSE ));
539 If_expand = FALSE;
541 /* Remove CONTINUATION_CHAR<nl> and replace with " " so that line
542 * continuations are recognized as whitespace. */
543 for( cst=strchr(expr,CONTINUATION_CHAR); cst != NIL(char); cst=strchr(cst,CONTINUATION_CHAR) )
544 if( cst[1] == '\n' ) {
545 *cst = ' ';
546 cst[1] = ' ';
548 else
549 cst++;
551 lhs = expr;
552 SCAN_WHITE( lhs );
554 /* Parse the expression and get its logical result */
555 if ( ((lop = DmStrStr(lhs, "||" )) != NIL(char)) || ((lop = DmStrStr(lhs, "&&" )) != NIL(char)) )
556 result = parse_complex_expression( lhs, &expr_end, opcode );
557 else
558 result = partcomp( lhs, opcode );
560 if( expr != NIL(char) ) FREE( expr );
562 if( opcode != ST_ELIF ) {
563 Nest_level++;
564 action[Nest_level] = 1;
566 ifcntl[Nest_level] |= (opcode==ST_ELIF)?SEEN_ELIF:SEEN_IF;
568 if( result ) {
569 if( !(ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF)) ) {
570 action[ Nest_level ] = action[ Nest_level-1 ];
571 ifcntl[Nest_level] |= (opcode==ST_ELIF)?ACCEPT_ELIF:ACCEPT_IF;
573 else
574 action[Nest_level] = 1;
576 else
577 action[Nest_level] = 1;
578 break;
580 case ST_ELSE:
581 if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" );
582 if( ifcntl[Nest_level] & SEEN_ELSE )
583 Fatal( "Missing .IF or .ELIF before .ELSE" );
585 if( ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF) )
586 action[Nest_level] = 1;
587 else if( action[ Nest_level-1 ] != 1 )
588 action[ Nest_level ] ^= 0x1; /* flip between 0 and 1 */
590 ifcntl[Nest_level] |= SEEN_ELSE;
591 break;
593 case ST_END:
594 ifcntl[Nest_level] = SEEN_END;
595 Nest_level--;
596 if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" );
597 break;
600 DB_RETURN( action[ Nest_level ] );
603 /* uncomment to turn on expression debug statements */
604 /*#define PARSE_DEBUG */
605 #define PARSE_SKIP_WHITE(A) while( *A && ((*A==' ') || (*A=='\t')) ) A++;
607 #define OP_NONE 0
608 #define OP_AND 1
609 #define OP_OR 2
611 static int n = 1;
613 int parse_complex_expression( char *expr, char **expr_end, int opcode )
615 char *p = expr;
616 char *term_start = p;
617 char *term_end;
618 int local_term;
619 char *part;
620 int term_result = FALSE;
621 int final_result = TRUE;
622 unsigned int term_len;
623 unsigned int last_op = OP_NONE;
625 #ifdef PARSE_DEBUG
626 printf( "%d: parse_complex_expression( %s ): Opcode: %d\n", n, expr, opcode );
627 #endif
629 while ( 1 )
631 /* A new sub-expression */
632 local_term = TRUE;
633 if ( *p == '(' )
635 n++;
636 term_result = parse_complex_expression( p+1, &p, opcode );
637 n--;
638 PARSE_SKIP_WHITE( p );
639 term_start = p;
640 term_end = p;
641 local_term = FALSE;
643 else
644 term_end = p;
646 /* Lets do an operation!! */
647 if ( !(*p) /* at the end of the entire line */
648 || ((*p == '&') && (*(p+1) && (*(p+1)=='&'))) /* found an && */
649 || ((*p == '|') && (*(p+1) && (*(p+1)=='|'))) /* found an || */
650 || (*p == ')') ) /* at the end of our term */
652 /* Grab the sub-expression if we parsed it. Otherwise,
653 * it was a () subexpression and we don't need to evaluate
654 * it since that was already done.
656 if ( local_term == TRUE )
658 /* Back up 1 to the end of the actual term */
659 term_end--;
661 /* Evaluate the term */
662 PARSE_SKIP_WHITE( term_start );
663 term_len = term_end - term_start + 1;
664 part = MALLOC( term_len + 1, char );
665 strncpy( part, term_start, term_len );
666 *(part+term_len) = '\0';
667 #ifdef PARSE_DEBUG
668 printf( "%d: evaling '%s'\n", n, part );
669 #endif
670 term_result = partcomp( part, opcode );
671 #ifdef PARSE_DEBUG
672 printf( "%d: evaled, result %d\n", n, term_result );
673 #endif
674 FREE( part );
677 /* Do the actual logical operation using the _preceding_
678 * logical operator, NOT the one we just found.
680 if ( last_op == OP_AND )
681 final_result = final_result && term_result;
682 else if ( last_op == OP_OR )
683 final_result = final_result || term_result;
684 else
685 final_result = term_result;
686 #ifdef PARSE_DEBUG
687 printf( "%d: final_result:%d\n", n, final_result );
688 #endif
690 /* If we're not at the end of the line, just keep going */
691 if ( *p )
693 /* Recognize the operator we just found above */
694 if ( *p == '&' )
695 last_op = OP_AND;
696 else if ( *p == '|' )
697 last_op = OP_OR;
698 if ( *p != ')' )
699 p += 2;
701 /* Get the start of the next term */
702 PARSE_SKIP_WHITE( p );
703 term_start = p;
705 /* If this is the close of a term, we are done and return
706 * to our caller.
708 if ( *p == ')' )
710 p++;
711 break;
714 else break; /* At end of line, all done */
716 else if ( local_term == TRUE ) p++; /* Advance to next char in expression */
718 *expr_end = p;
720 #ifdef PARSE_DEBUG
721 printf( "%d: done, returning '%s', result %d\n", n, *expr_end, final_result );
722 #endif
723 return( final_result );
727 int partcomp( char* lhs, int opcode )
730 char *tok, *rhs, *op = 0;
731 int result, opsind;
732 const int localopscount=4;
733 char* localops[] = { "==", "!=", "<=", ">=" };
734 int lint, rint;
736 #define EQUAL 0
737 #define NOTEQUAL 1
738 #define LESS_EQUAL 2
739 #define GREATER_EQUAL 3
741 #ifdef PARSE_DEBUG
742 printf( "eval: %s\n", lhs);
743 #endif
745 opsind = 0;
746 if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
748 /* IF[N]EQ syntax is: .IF[N]EQ <1> <2>
749 * Here, step over first argument and get to <2> if it exists.
751 for( op = lhs; ((*op)&&(*op != ' ')&&(*op != '\t')); op++ );
752 if( *op ) op++; /* position op at start of <2> */
753 else op = NIL(char); /* only 1 argument given */
755 else
757 /* Find which logical operator we are to use for this expression,
758 * and jump to it */
759 while ( (opsind < localopscount) && ((op = DmStrStr(lhs, localops[opsind])) == NIL(char)) )
760 opsind++;
762 #ifdef PARSE_DEBUG
763 printf(" found op %d: %s\n", opsind, localops[opsind]);
764 #endif
767 /* If the opcode was IFEQ or IFNEQ and only 1 argument was given,
768 * or an unknown logical operator was encountered,
769 * return false if argument is empty string, true if !empty
771 if( op == NIL(char) )
772 result = (*lhs != '\0');
773 else
775 /* Make both characters of the operation the same, replacing the = in op[1]
776 * Its easier to deal with this way???
778 if( opcode != ST_IFEQ && opcode != ST_IFNEQ )
779 op[1] = op[0];
781 #ifdef PARSE_DEBUG
782 printf(" op:%s\n", op);
783 #endif
785 /* Isolate the left half of the expression */
786 if( lhs != op )
788 for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok-- );
789 tok[1] = '\0';
791 else
792 lhs = NIL(char); /* Left hand side is empty. */
794 /* Jump over the operation so we can grab the right half of the expression */
795 if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
796 op--;
797 else
798 op++;
800 /* Isolate the right half of the expression */
801 rhs = DmStrSpn( op+1, " \t" );
802 if( !*rhs ) rhs = NIL(char);
804 #ifdef PARSE_DEBUG
805 printf(" lhs:%s, rhs:%s\n", lhs, rhs);
806 #endif
808 /* Do the actual logical operation on the expression */
809 if ( opsind > NOTEQUAL )
811 switch( opsind )
813 case LESS_EQUAL:
814 case GREATER_EQUAL:
815 /* Ignore quotes around the arguments */
816 if ( lhs && lhs[0] == '"' ) lhs++;
817 if ( rhs && rhs[0] == '"' ) rhs++;
819 /* Empty strings evaluate to zero. */
820 lint = lhs ? atoi( lhs ) : 0;
821 rint = rhs ? atoi( rhs ) : 0;
822 result = ( lint >= rint ) ? TRUE : FALSE;
823 if ( opsind == LESS_EQUAL && lint != rint )
824 result = !result;
825 break;
826 default:
827 result = FALSE;
830 else
832 /* Use a simple string compare to determine equality */
833 if( (rhs == NIL(char)) || (lhs == NIL(char)) )
834 result = (rhs == lhs) ? TRUE : FALSE;
835 else
837 /* String off whitespace at the end of the right half of the expression */
838 tok = rhs + strlen( rhs );
839 for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--);
840 tok[1] = '\0';
842 result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE;
845 if( *op == '!' || opcode == ST_IFNEQ ) result = !result;
849 #ifdef PARSE_DEBUG
850 printf("partresult %d\n\n",result);
851 #endif
852 return result;