Bug Summary

File:dmake/getinp.c
Location:line 459, column 27
Description:Null pointer passed as an argument to a 'nonnull' parameter

Annotated Source Code

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>.
14--
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.
18--
19-- AUTHOR
20-- Dennis Vadura, dvadura@dmake.wticorp.com
21--
22-- WWW
23-- http://dmake.wticorp.com/
24--
25-- COPYRIGHT
26-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
27--
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.
31--
32-- LOG
33-- Use cvs log to obtain detailed change logs.
34*/
35
36#include "extern.h"
37
38#define IS_WHITE(A)((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r')) ((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r'))
39#define SCAN_WHITE(A)while( ((*A == ' ') || (*A == '\t') || (*A == '\n') || (*A ==
'\r')) ) A++;
\
40 while( IS_WHITE(*A)((*A == ' ') || (*A == '\t') || (*A == '\n') || (*A == '\r')) ) A++;
41
42static int _is_conditional ANSI((char*))(char*);
43static int _handle_conditional ANSI((int, TKSTRPTR))(int, TKSTRPTR);
44
45static int rule_ind = 0; /* index of rule when reading Rule_tab */
46static int skip = FALSE0; /* if true the skip input */
47
48int partcomp( char* lhs, int opcode );
49int parse_complex_expression( char *expr, char **expr_end, int opcode );
50
51
52PUBLIC int
53Get_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. */
61char *buf;
62FILE *fil;
63{
64 extern char **Rule_tab;
65 register char *p;
66 register char *c;
67 char *q;
68 char *buf_org;
69 static int ignore = FALSE0;
70 int cont = FALSE0;
71 int pos = 0;
72 int res = 0;
73 register char *tmp = NIL(char)((char*)((void*)0));
74
75 DB_ENTER( "Get_line" );
76
77 if( Skip_to_eof ) {
78 Skip_to_eof = FALSE0;
79 rule_ind = 0;
80
81 if( Verbose & V_MAKE0x10 )
82 Warning("Ignoring remainder of file %s", Filename());
83
84 DB_RETURN(TRUE)return (1);
85 }
86
87 if( fil == NIL(FILE)((FILE*)((void*)0)) ) {
88 /* Reading the internal rule table. Set rule_ind to zero after the
89 * last entry so that ReadEnvironment() works as expected every time. */
90
91 while( (p = Rule_tab[ rule_ind++ ]) != NIL(char)((char*)((void*)0)) ) {
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)((char*)((void*)0))) && (space == NIL(char)((char*)((void*)0)) || space > equal) && *p!='~' && *p!='!')){
102 strcpy( buf, p );
103
104 DB_PRINT( "io", ("Returning [%s]", buf) );
105 DB_RETURN( FALSE )return (0);
106 }
107 }
108
109 rule_ind = 0;
110
111 DB_PRINT( "io", ("Done Ruletab") );
112 DB_RETURN( TRUE )return (1);
113 }
114
115 buf_org = buf;
116
117do_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)((char*)((void*)0))))
122 DB_RETURN( TRUE )return (1);
123
124#ifdef _MPW
125 if ( p[0] == 10 && p[1] == COMMENT_CHAR'#')
126 p[0] = ' ';
127#endif
128
129 Line_number++;
130
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. */
135
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--;
142 }
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--;
155 }
156
157 /* ignore input if ignore flag set and line ends in a continuation
158 character. */
159
160 if( ignore ) {
161 if( q[0] != CONTINUATION_CHAR'\\' || q[1] != '\n' ) ignore = FALSE0;
162 *p = '\0';
163 continue;
164 }
165
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 ==' '));
168
169 /* Does the end of the line end in a continuation sequence? */
170
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 = FALSE0;
182 }
183 else if( c != NIL(char)((char*)((void*)0)) )
184 ignore = TRUE1;
185 else
186 cont = TRUE1; /* Keep the \<nl>. */
187 }
188 else {
189 cont = FALSE0;
190 }
191
192 q = ( c == NIL(char)((char*)((void*)0)) ) ? q+2 : c;
193 }
194 else { /* empty line or "" */
195 cont = FALSE0;
196 ignore = FALSE0;
197 q = p+strlen(p); /* strlen(p) is 1 or 0 */
198 }
199
200 pos += q-p;
201 }
202 while( (cont || !*buf) && (pos < Buffer_size-1) );
203
204 if( pos >= Buffer_size-1 )
205 Fatal( "Input line too long, increase MAXLINELENGTH" );
206
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. */
212
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.
217 *
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)((char*)((void*)0)) )
221 p = buf;
222
223 if (!Group) {
224 if( !strncmp( "include", p, 7 ) &&
225 (p[7] == ' ' || p[7] == '\t') )
226 tmp = DmStrJoin( ".INCLUDE:", p+7, -1, FALSE0 );
227 else if( !strncmp( "ifeq", p, 4 ) &&
228 (p[4] == ' ' || p[4] == '\t') )
229 tmp = DmStrJoin( ".IFEQ", p+4, -1, FALSE0 );
230 else if( !strncmp( "ifneq", p, 5 ) &&
231 (p[5] == ' ' || p[5] == '\t') )
232 tmp = DmStrJoin( ".IFNEQ", p+5, -1, FALSE0 );
233 else if( !strncmp( "elif", p, 4 ) &&
234 (p[4] == ' ' || p[4] == '\t') )
235 tmp = DmStrJoin( ".ELIF", p+4, -1, FALSE0 );
236 else if( !strncmp( "else", p, 4 ) &&
237 (p[4] == ' ' || p[4] == '\t' || p[4] == '\0') )
238 tmp = DmStrJoin( ".ELSE", p+4, -1, FALSE0 );
239 else if( !strncmp( "endif", p, 5 ) &&
240 (p[5] == ' ' || p[5] == '\t' || p[5] == '\0') )
241 tmp = DmStrJoin( ".END", p+5, -1, FALSE0 );
242 }
243
244 if( tmp != NIL(char)((char*)((void*)0))) {
245 strcpy( buf, tmp );
246 FREE( tmp )free((char*)(tmp));
247 tmp = NIL(char)((char*)((void*)0));
248 }
249
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. */
253
254 if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART'.' ) {
255 TKSTR token;
256
257 SET_TOKEN( &token, p )(&token)->tk_str = (p); (&token)->tk_cchar = *(
p); (&token)->tk_quote = 1;
;
258
259 p = Get_token( &token, "", FALSE0 );
260
261 if( (res = _is_conditional(p)) != 0 ) /* ignore non-control special */
262 { /* targets */
263 res = _handle_conditional( res, &token );
264 skip = TRUE1;
265 }
266 else {
267 CLEAR_TOKEN( &token )*(&token)->tk_str = (&token)->tk_cchar;
268 res = TRUE1;
269 }
270 }
271
272 if( skip ) {
273 buf = buf_org; /* ignore line just read in */
274 pos = 0;
275 skip = res;
276 goto do_again;
277 }
278
279 DB_PRINT( "io", ("Returning [%s]", buf) );
280 DB_RETURN( FALSE )return (0);
281}
282
283
284PUBLIC char *
285Do_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. */
291char *str;
292char **pend;
293int keep;
294{
295 char *c = str;
296
297 while( (c = strchr(c, COMMENT_CHAR'#')) != NIL(char)((char*)((void*)0)) ) {
298 if( Comment || State == NORMAL_SCAN0 )
299 if( c != str && c[-1] == ESCAPE_CHAR*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 */
303 }
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;
312
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 = TRUE1;
318#endif
319 Wait_for_completion = TRUE1;
320 Do_cmnd(&cmnd, FALSE0, TRUE1, Current_target, A_DEFAULT0x00000, TRUE1);
321#if defined(MSDOS)
322 Swap_on_exec = FALSE0;
323#endif
324 Wait_for_completion = FALSE0;
325 FREE(cmnd)free((char*)(cmnd));
326 }
327
328 *c = '\0'; /* a true comment so break */
329 break;
330 }
331 else {
332 if( keep )
333 c = NIL(char)((char*)((void*)0));
334 else
335 *c = '\0';
336
337 break;
338 }
339 }
340
341 return(c);
342}
343
344
345PUBLIC char *
346Get_token( string, brk, anchor )/*
347==================================
348 Return the next token in string.
349
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.
354
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.
358
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. */
361
362TKSTRPTR string;
363char *brk;
364int anchor;
365{
366 register char *s;
367 register char *curp = 0;
368 register char *t;
369 int done = FALSE0;
370 char space[100];
371
372 DB_ENTER( "Get_token" );
373
374 s = string->tk_str; /* Get string parameters */
375 *s = string->tk_cchar; /* ... and strip leading w/s */
376
377 SCAN_WHITE( s )while( ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s ==
'\r')) ) s++;
;
378
379 DB_PRINT( "tok", ("What's left [%s]", s) );
380
381 if( !*s ) {
1
Taking false branch
382 DB_PRINT( "tok", ("Returning NULL token") );
383 DB_RETURN( "" )return ("");
384 }
385
386
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. */
389
390 if( brk != NIL(char)((char*)((void*)0)) ) {
2
Taking false branch
391 strcpy( space, " \t\r\n" );
392 strcat( space, brk );
393 }
394 else {
395 space[0] = 0xff; /* a char we know will not show up */
396 space[1] = 0;
397 }
398
399
400 /* Handle processing of quoted tokens. Note that this is disabled if
401 * brk is equal to NIL */
402
403 while( *s == '\"' && ((brk != NIL(char)((char*)((void*)0))) || !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)((char*)((void*)0))) && (*(curp+1) == '\"'));
409
410 if( curp == NIL(char)((char*)((void*)0)) ) Fatal( "Unmatched quote in token" );
411 string->tk_quote = !string->tk_quote;
412
413 /* Check for "" case, and if found ignore it */
414 if( curp == s ) continue;
415 goto found_token;
416 }
417 else
418 SCAN_WHITE( s )while( ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s ==
'\r')) ) s++;
;
419
420 string->tk_quote = !string->tk_quote;
421 }
422
423
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. */
426
427 if( anchor == 2 && brk != NIL(char)((char*)((void*)0)) ) {
428 curp = s;
429 while( *curp && (strchr(brk,*curp)!=NIL(char)((char*)((void*)0))) && (*curp!=*brk) ) curp++;
430 done = (*brk == *curp++);
431 }
432 else if( (brk != NIL(char)((char*)((void*)0))) && (strchr( brk, *s ) != NIL(char)((char*)((void*)0))) ) {
433 curp = DmStrSpn( s, brk );
434 done = (anchor == 0) ? TRUE1 :
435 ((anchor == 1)?(*s == *brk) : (*brk == curp[-1]));
436 }
437
438
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 */
442
443 if( !done ) {
3
Taking true branch
444 SCAN_WHITE( s )while( ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s ==
'\r')) ) s++;
;
445
446 t = s;
447 do {
4
Loop condition is false. Exiting loop
448 done = TRUE1;
449 curp = DmStrPbrk(t, space);
450
451 if( anchor && *curp && !IS_WHITE( *curp )((*curp == ' ') || (*curp == '\t') || (*curp == '\n') || (*curp
== '\r'))
)
452 if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) {
453 t++;
454 done = FALSE0;
455 }
456 }
457 while( !done );
458
459 if( (curp == s) && (strchr(brk, *curp) != NIL(char)((char*)((void*)0))) ) curp++;
5
Null pointer passed as an argument to a 'nonnull' parameter
460 }
461
462found_token:
463 string->tk_str = curp;
464 string->tk_cchar = *curp;
465 *curp = '\0';
466
467 DB_PRINT( "tok", ("Returning [%s]", s) );
468 DB_RETURN( s )return (s);
469}
470
471
472static 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. */
477char *tg;
478{
479 DB_ENTER( "_is_conditional" );
480
481 tg++;
482 switch( *tg )
483 {
484 case 'I':
485 if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF )return (1);
486 else if( !strcmp( tg, "IFEQ" )) DB_RETURN( ST_IFEQ )return (13);
487 else if( !strcmp( tg, "IFNEQ" )) DB_RETURN( ST_IFNEQ )return (14);
488 break;
489
490 case 'E':
491 if( !strcmp( tg, "END" )) DB_RETURN( ST_END )return (3);
492 else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END )return (3);
493 else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE )return (2);
494 else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF )return (10);
495 break;
496 }
497
498 DB_RETURN( 0 )return (0);
499}
500
501
502
503#define SEEN_END0x00 0x00
504#define SEEN_IF0x01 0x01
505#define SEEN_ELSE0x02 0x02
506#define SEEN_ELIF0x04 0x04
507
508#define ACCEPT_IF0x10 0x10
509#define ACCEPT_ELIF0x20 0x20
510
511static int
512_handle_conditional( opcode, tg )
513 int opcode;
514 TKSTRPTR tg;
515{
516 static short action[MAX_COND_DEPTH20];
517 static char ifcntl[MAX_COND_DEPTH20];
518 char *cst;
519 char *lhs, *expr, *expr_end;
520 char *lop;
521 int result;
522
523 DB_ENTER( "_handle_conditional" );
524
525 switch( opcode ) {
526 case ST_ELIF10:
527 if( !(ifcntl[Nest_level] & SEEN_IF0x01) || (ifcntl[Nest_level]&SEEN_ELSE0x02) )
528 Fatal(".ELIF without a preceeding .IF" );
529 /*FALLTHRU*/
530
531 case ST_IF1:
532 case ST_IFEQ13:
533 case ST_IFNEQ14:
534 if( opcode != ST_ELIF10 && (Nest_level+1) == MAX_COND_DEPTH20 )
535 Fatal( ".IF .ELSE ... .END nesting too deep" );
536
537 If_expand = TRUE1;
538 expr = Expand( Get_token( tg, NIL(char)((char*)((void*)0)), FALSE0 ));
539 If_expand = FALSE0;
540
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)((char*)((void*)0)); cst=strchr(cst,CONTINUATION_CHAR'\\') )
544 if( cst[1] == '\n' ) {
545 *cst = ' ';
546 cst[1] = ' ';
547 }
548 else
549 cst++;
550
551 lhs = expr;
552 SCAN_WHITE( lhs )while( ((*lhs == ' ') || (*lhs == '\t') || (*lhs == '\n') || (
*lhs == '\r')) ) lhs++;
;
553
554 /* Parse the expression and get its logical result */
555 if ( ((lop = DmStrStr(lhs, "||" )) != NIL(char)((char*)((void*)0))) || ((lop = DmStrStr(lhs, "&&" )) != NIL(char)((char*)((void*)0))) )
556 result = parse_complex_expression( lhs, &expr_end, opcode );
557 else
558 result = partcomp( lhs, opcode );
559
560 if( expr != NIL(char)((char*)((void*)0)) ) FREE( expr )free((char*)(expr));
561
562 if( opcode != ST_ELIF10 ) {
563 Nest_level++;
564 action[Nest_level] = 1;
565 }
566 ifcntl[Nest_level] |= (opcode==ST_ELIF10)?SEEN_ELIF0x04:SEEN_IF0x01;
567
568 if( result ) {
569 if( !(ifcntl[Nest_level] & (ACCEPT_IF0x10|ACCEPT_ELIF0x20)) ) {
570 action[ Nest_level ] = action[ Nest_level-1 ];
571 ifcntl[Nest_level] |= (opcode==ST_ELIF10)?ACCEPT_ELIF0x20:ACCEPT_IF0x10;
572 }
573 else
574 action[Nest_level] = 1;
575 }
576 else
577 action[Nest_level] = 1;
578 break;
579
580 case ST_ELSE2:
581 if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" );
582 if( ifcntl[Nest_level] & SEEN_ELSE0x02 )
583 Fatal( "Missing .IF or .ELIF before .ELSE" );
584
585 if( ifcntl[Nest_level] & (ACCEPT_IF0x10|ACCEPT_ELIF0x20) )
586 action[Nest_level] = 1;
587 else if( action[ Nest_level-1 ] != 1 )
588 action[ Nest_level ] ^= 0x1; /* flip between 0 and 1 */
589
590 ifcntl[Nest_level] |= SEEN_ELSE0x02;
591 break;
592
593 case ST_END3:
594 ifcntl[Nest_level] = SEEN_END0x00;
595 Nest_level--;
596 if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" );
597 break;
598 }
599
600 DB_RETURN( action[ Nest_level ] )return (action[ Nest_level ]);
601}
602
603/* uncomment to turn on expression debug statements */
604/*#define PARSE_DEBUG */
605#define PARSE_SKIP_WHITE(A)while( *A && ((*A==' ') || (*A=='\t')) ) A++; while( *A && ((*A==' ') || (*A=='\t')) ) A++;
606
607#define OP_NONE0 0
608#define OP_AND1 1
609#define OP_OR2 2
610
611static int n = 1;
612
613int parse_complex_expression( char *expr, char **expr_end, int opcode )
614{
615 char *p = expr;
616 char *term_start = p;
617 char *term_end;
618 int local_term;
619 char *part;
620 int term_result = FALSE0;
621 int final_result = TRUE1;
622 unsigned int term_len;
623 unsigned int last_op = OP_NONE0;
624
625 #ifdef PARSE_DEBUG
626 printf( "%d: parse_complex_expression( %s ): Opcode: %d\n", n, expr, opcode );
627 #endif
628
629 while ( 1 )
630 {
631 /* A new sub-expression */
632 local_term = TRUE1;
633 if ( *p == '(' )
634 {
635 n++;
636 term_result = parse_complex_expression( p+1, &p, opcode );
637 n--;
638 PARSE_SKIP_WHITE( p )while( *p && ((*p==' ') || (*p=='\t')) ) p++;;
639 term_start = p;
640 term_end = p;
641 local_term = FALSE0;
642 }
643 else
644 term_end = p;
645
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 */
651 {
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.
655 */
656 if ( local_term == TRUE1 )
657 {
658 /* Back up 1 to the end of the actual term */
659 term_end--;
660
661 /* Evaluate the term */
662 PARSE_SKIP_WHITE( term_start )while( *term_start && ((*term_start==' ') || (*term_start
=='\t')) ) term_start++;
;
663 term_len = term_end - term_start + 1;
664 part = MALLOC( term_len + 1, char )(char*) malloc((unsigned int)(term_len + 1)*(size_t)sizeof(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 )free((char*)(part));
675 }
676
677 /* Do the actual logical operation using the _preceding_
678 * logical operator, NOT the one we just found.
679 */
680 if ( last_op == OP_AND1 )
681 final_result = final_result && term_result;
682 else if ( last_op == OP_OR2 )
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
689
690 /* If we're not at the end of the line, just keep going */
691 if ( *p )
692 {
693 /* Recognize the operator we just found above */
694 if ( *p == '&' )
695 last_op = OP_AND1;
696 else if ( *p == '|' )
697 last_op = OP_OR2;
698 if ( *p != ')' )
699 p += 2;
700
701 /* Get the start of the next term */
702 PARSE_SKIP_WHITE( p )while( *p && ((*p==' ') || (*p=='\t')) ) p++;;
703 term_start = p;
704
705 /* If this is the close of a term, we are done and return
706 * to our caller.
707 */
708 if ( *p == ')' )
709 {
710 p++;
711 break;
712 }
713 }
714 else break; /* At end of line, all done */
715 }
716 else if ( local_term == TRUE1 ) p++; /* Advance to next char in expression */
717 }
718 *expr_end = p;
719
720 #ifdef PARSE_DEBUG
721 printf( "%d: done, returning '%s', result %d\n", n, *expr_end, final_result );
722 #endif
723 return( final_result );
724}
725
726
727int partcomp( char* lhs, int opcode )
728{
729
730 char *tok, *rhs, *op = 0;
731 int result, opsind;
732 const int localopscount=4;
733 char* localops[] = { "==", "!=", "<=", ">=" };
734
735#define EQUAL0 0
736#define NOTEQUAL1 1
737#define LESS_EQUAL2 2
738#define GREATER_EQUAL3 3
739
740 #ifdef PARSE_DEBUG
741 printf( "eval: %s\n", lhs);
742 #endif
743
744 opsind = 0;
745 if( opcode == ST_IFEQ13 || opcode == ST_IFNEQ14 )
746 {
747 /* IF[N]EQ syntax is: .IF[N]EQ <1> <2>
748 * Here, step over first argument and get to <2> if it exists.
749 */
750 for( op = lhs; ((*op)&&(*op != ' ')&&(*op != '\t')); op++ );
751 if( *op ) op++; /* position op at start of <2> */
752 else op = NIL(char)((char*)((void*)0)); /* only 1 argument given */
753 }
754 else
755 {
756 /* Find which logical operator we are to use for this expression,
757 * and jump to it */
758 while ( (opsind < localopscount) && ((op = DmStrStr(lhs, localops[opsind])) == NIL(char)((char*)((void*)0))) )
759 opsind++;
760
761 #ifdef PARSE_DEBUG
762 printf(" found op %d: %s\n", opsind, localops[opsind]);
763 #endif
764 }
765
766 /* If the opcode was IFEQ or IFNEQ and only 1 argument was given,
767 * or an unknown logical operator was encountered,
768 * return false if argument is empty string, true if !empty
769 */
770 if( op == NIL(char)((char*)((void*)0)) )
771 result = (*lhs != '\0');
772 else
773 {
774 /* Make both characters of the operation the same, replacing the = in op[1]
775 * Its easier to deal with this way???
776 */
777 if( opcode != ST_IFEQ13 && opcode != ST_IFNEQ14 )
778 op[1] = op[0];
779
780 #ifdef PARSE_DEBUG
781 printf(" op:%s\n", op);
782 #endif
783
784 /* Isolate the left half of the expression */
785 if( lhs != op )
786 {
787 for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok-- );
788 tok[1] = '\0';
789 }
790 else
791 lhs = NIL(char)((char*)((void*)0)); /* Left hand side is empty. */
792
793 /* Jump over the operation so we can grab the right half of the expression */
794 if( opcode == ST_IFEQ13 || opcode == ST_IFNEQ14 )
795 op--;
796 else
797 op++;
798
799 /* Isolate the right half of the expression */
800 rhs = DmStrSpn( op+1, " \t" );
801 if( !*rhs ) rhs = NIL(char)((char*)((void*)0));
802
803 #ifdef PARSE_DEBUG
804 printf(" lhs:%s, rhs:%s\n", lhs, rhs);
805 #endif
806
807 /* Do the actual logical operation on the expression */
808 if ( opsind > NOTEQUAL1 )
809 {
810 switch( opsind )
811 {
812 case LESS_EQUAL2:
813 case GREATER_EQUAL3:
814 /* Ignore quotes around the arguments */
815 if ( lhs && lhs[0] == '"' ) lhs++;
816 if ( rhs && rhs[0] == '"' ) rhs++;
817
818 /* Empty strings evaluate to zero. */
819 int lint = lhs ? atoi( lhs ) : 0;
820 int rint = rhs ? atoi( rhs ) : 0;
821 result = ( lint >= rint ) ? TRUE1 : FALSE0;
822 if ( opsind == LESS_EQUAL2 && lint != rint )
823 result = !result;
824 break;
825 default:
826 result = FALSE0;
827 }
828 }
829 else
830 {
831 /* Use a simple string compare to determine equality */
832 if( (rhs == NIL(char)((char*)((void*)0))) || (lhs == NIL(char)((char*)((void*)0))) )
833 result = (rhs == lhs) ? TRUE1 : FALSE0;
834 else
835 {
836 /* String off whitespace at the end of the right half of the expression */
837 tok = rhs + strlen( rhs );
838 for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--);
839 tok[1] = '\0';
840
841 result = (strcmp( lhs, rhs ) == 0) ? TRUE1 : FALSE0;
842 }
843
844 if( *op == '!' || opcode == ST_IFNEQ14 ) result = !result;
845 }
846 }
847
848 #ifdef PARSE_DEBUG
849 printf("partresult %d\n\n",result);
850 #endif
851 return result;
852}
853