Line data 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'))
39 : #define SCAN_WHITE(A) \
40 : while( IS_WHITE(*A) ) A++;
41 :
42 : static int _is_conditional ANSI((char*));
43 : static int _handle_conditional ANSI((int, TKSTRPTR));
44 :
45 : static int rule_ind = 0; /* index of rule when reading Rule_tab */
46 : static int skip = FALSE; /* if true the skip input */
47 :
48 : int partcomp( char* lhs, int opcode );
49 : int parse_complex_expression( char *expr, char **expr_end, int opcode );
50 :
51 :
52 : PUBLIC int
53 474522 : 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;
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 = FALSE;
70 474522 : int cont = FALSE;
71 474522 : int pos = 0;
72 474522 : int res = 0;
73 474522 : register char *tmp = NIL(char);
74 :
75 : DB_ENTER( "Get_line" );
76 :
77 474522 : if( Skip_to_eof ) {
78 0 : Skip_to_eof = FALSE;
79 0 : rule_ind = 0;
80 :
81 0 : if( Verbose & V_MAKE )
82 0 : Warning("Ignoring remainder of file %s", Filename());
83 :
84 0 : DB_RETURN(TRUE);
85 : }
86 :
87 474522 : 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. */
90 :
91 271952 : 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 135608 : char *equal = strchr(p,'=');
100 135608 : char *space = strchr(p,' ');
101 135608 : if( !Readenv || (Readenv && (equal != NIL(char)) && (space == NIL(char) || space > equal) && *p!='~' && *p!='!')){
102 135608 : strcpy( buf, p );
103 :
104 : DB_PRINT( "io", ("Returning [%s]", buf) );
105 135608 : DB_RETURN( FALSE );
106 : }
107 : }
108 :
109 368 : rule_ind = 0;
110 :
111 : DB_PRINT( "io", ("Done Ruletab") );
112 368 : DB_RETURN( TRUE );
113 : }
114 :
115 338546 : buf_org = buf;
116 :
117 : do_again:
118 : do {
119 1132042 : p = buf+pos;
120 : /* fgets() reads at most one less than Buffer_size-pos characters. */
121 1132042 : if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char)))
122 4592 : DB_RETURN( TRUE );
123 :
124 : #ifdef _MPW
125 : if ( p[0] == 10 && p[1] == COMMENT_CHAR)
126 : p[0] = ' ';
127 : #endif
128 :
129 1127450 : Line_number++;
130 :
131 : /* Set q to the last char in p before the \n\0. */
132 1127450 : q = p+strlen(p)-2;
133 1127450 : 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 979442 : if( q[0] == '\r' && q[1] == '\n' ) {
139 0 : q[0] = '\n';
140 0 : q[1] = '\0';
141 0 : 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 979442 : if( q[0] == '\032' )
151 0 : q--;
152 1958884 : while( q[1] == '\032' ) {
153 0 : q[1] = '\0';
154 0 : q--;
155 : }
156 :
157 : /* ignore input if ignore flag set and line ends in a continuation
158 : character. */
159 :
160 979442 : if( ignore ) {
161 10 : if( q[0] != CONTINUATION_CHAR || q[1] != '\n' ) ignore = FALSE;
162 10 : *p = '\0';
163 10 : continue;
164 : }
165 :
166 : /* If a comment is found the line does not end in \n anymore. */
167 979432 : 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 979432 : 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 169020 : if( q != p && q[-1] == CONTINUATION_CHAR ) {
178 184 : size_t len = strlen(q+1)+1;
179 184 : memmove( q, q+1, len );
180 184 : q--;
181 184 : cont = FALSE;
182 : }
183 84234 : else if( c != NIL(char) )
184 2 : ignore = TRUE;
185 : else
186 84232 : cont = TRUE; /* Keep the \<nl>. */
187 : }
188 : else {
189 895014 : cont = FALSE;
190 : }
191 :
192 979432 : q = ( c == NIL(char) ) ? q+2 : c;
193 : }
194 : else { /* empty line or "" */
195 148008 : cont = FALSE;
196 148008 : ignore = FALSE;
197 148008 : q = p+strlen(p); /* strlen(p) is 1 or 0 */
198 : }
199 :
200 1127440 : pos += q-p;
201 : }
202 1127450 : while( (cont || !*buf) && (pos < Buffer_size-1) );
203 :
204 878182 : if( pos >= Buffer_size-1 )
205 0 : 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 878182 : if( (q > p) && (buf[ pos-1 ] == '\n') )
211 814960 : 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 878182 : if( (p = DmStrSpn(buf, " \t\r\n")) == NIL(char) )
221 0 : p = buf;
222 :
223 878182 : if (!Group) {
224 878182 : if( !strncmp( "include", p, 7 ) &&
225 0 : (p[7] == ' ' || p[7] == '\t') )
226 0 : tmp = DmStrJoin( ".INCLUDE:", p+7, -1, FALSE );
227 878182 : else if( !strncmp( "ifeq", p, 4 ) &&
228 0 : (p[4] == ' ' || p[4] == '\t') )
229 0 : tmp = DmStrJoin( ".IFEQ", p+4, -1, FALSE );
230 878182 : else if( !strncmp( "ifneq", p, 5 ) &&
231 0 : (p[5] == ' ' || p[5] == '\t') )
232 0 : tmp = DmStrJoin( ".IFNEQ", p+5, -1, FALSE );
233 878182 : else if( !strncmp( "elif", p, 4 ) &&
234 0 : (p[4] == ' ' || p[4] == '\t') )
235 0 : tmp = DmStrJoin( ".ELIF", p+4, -1, FALSE );
236 878182 : else if( !strncmp( "else", p, 4 ) &&
237 0 : (p[4] == ' ' || p[4] == '\t' || p[4] == '\0') )
238 0 : tmp = DmStrJoin( ".ELSE", p+4, -1, FALSE );
239 878182 : else if( !strncmp( "endif", p, 5 ) &&
240 0 : (p[5] == ' ' || p[5] == '\t' || p[5] == '\0') )
241 0 : tmp = DmStrJoin( ".END", p+5, -1, FALSE );
242 : }
243 :
244 878182 : if( tmp != NIL(char)) {
245 0 : strcpy( buf, tmp );
246 0 : FREE( tmp );
247 0 : tmp = NIL(char);
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 878182 : if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART ) {
255 : TKSTR token;
256 :
257 329978 : SET_TOKEN( &token, p );
258 :
259 329978 : p = Get_token( &token, "", FALSE );
260 :
261 329978 : if( (res = _is_conditional(p)) != 0 ) /* ignore non-control special */
262 : { /* targets */
263 306000 : res = _handle_conditional( res, &token );
264 306000 : skip = TRUE;
265 : }
266 : else {
267 23978 : CLEAR_TOKEN( &token );
268 23978 : res = TRUE;
269 : }
270 : }
271 :
272 878182 : if( skip ) {
273 544228 : buf = buf_org; /* ignore line just read in */
274 544228 : pos = 0;
275 544228 : skip = res;
276 544228 : goto do_again;
277 : }
278 :
279 : DB_PRINT( "io", ("Returning [%s]", buf) );
280 333954 : DB_RETURN( FALSE );
281 : }
282 :
283 :
284 : PUBLIC char *
285 979432 : 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;
294 : {
295 979432 : char *c = str;
296 :
297 1959232 : while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) {
298 231600 : if( Comment || State == NORMAL_SCAN )
299 183192 : if( c != str && c[-1] == ESCAPE_CHAR ) {
300 368 : size_t len = strlen(c)+1;
301 368 : memmove( c-1, c, len ); /* copy it left, due to \# */
302 368 : if( pend ) (*pend)--; /* shift tail pointer left */
303 : }
304 : else {
305 : /* Check/execute if shebang command is present. */
306 182088 : if( !No_exec
307 182088 : && c == str
308 144430 : && c[1] == '!'
309 0 : && Line_number == 1
310 0 : && Nestlevel() == 1 ) {
311 : char *cmnd;
312 :
313 0 : cmnd = Expand(c+2);
314 0 : cmnd[strlen(cmnd)-1] = '\0'; /* strip last newline */
315 0 : Current_target = Root;
316 : #if defined(MSDOS)
317 : Swap_on_exec = TRUE;
318 : #endif
319 0 : Wait_for_completion = TRUE;
320 0 : Do_cmnd(&cmnd, FALSE, TRUE, Current_target, A_DEFAULT, TRUE);
321 : #if defined(MSDOS)
322 : Swap_on_exec = FALSE;
323 : #endif
324 0 : Wait_for_completion = FALSE;
325 0 : FREE(cmnd);
326 : }
327 :
328 182088 : *c = '\0'; /* a true comment so break */
329 182088 : break;
330 : }
331 : else {
332 49144 : if( keep )
333 2984 : c = NIL(char);
334 : else
335 46160 : *c = '\0';
336 :
337 49144 : break;
338 : }
339 : }
340 :
341 979432 : return(c);
342 : }
343 :
344 :
345 : PUBLIC char *
346 1649488 : Get_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 :
362 : TKSTRPTR string;
363 : char *brk;
364 : int anchor;
365 : {
366 : register char *s;
367 1649488 : register char *curp = 0;
368 : register char *t;
369 1649488 : int done = FALSE;
370 : char space[100];
371 :
372 : DB_ENTER( "Get_token" );
373 :
374 1649488 : s = string->tk_str; /* Get string parameters */
375 1649488 : *s = string->tk_cchar; /* ... and strip leading w/s */
376 :
377 1649488 : SCAN_WHITE( s );
378 :
379 : DB_PRINT( "tok", ("What's left [%s]", s) );
380 :
381 1649488 : if( !*s ) {
382 : DB_PRINT( "tok", ("Returning NULL token") );
383 173606 : DB_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 1475882 : if( brk != NIL(char) ) {
391 1138344 : strcpy( space, " \t\r\n" );
392 1138344 : strcat( space, brk );
393 : }
394 : else {
395 337538 : space[0] = 0xff; /* a char we know will not show up */
396 337538 : 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 2953324 : while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) {
404 3120 : s++;
405 3120 : if( string->tk_quote ) {
406 1560 : curp = s-1;
407 1560 : do { curp = strchr( curp+1, '\"' ); }
408 1560 : while( (curp != NIL(char)) && (*(curp+1) == '\"'));
409 :
410 1560 : if( curp == NIL(char) ) Fatal( "Unmatched quote in token" );
411 1560 : string->tk_quote = !string->tk_quote;
412 :
413 : /* Check for "" case, and if found ignore it */
414 1560 : if( curp == s ) continue;
415 1560 : goto found_token;
416 : }
417 : else
418 1560 : SCAN_WHITE( s );
419 :
420 1560 : 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 1474322 : if( anchor == 2 && brk != NIL(char) ) {
428 299298 : curp = s;
429 299298 : while( *curp && (strchr(brk,*curp)!=NIL(char)) && (*curp!=*brk) ) curp++;
430 299298 : done = (*brk == *curp++);
431 : }
432 1175024 : else if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) {
433 26650 : curp = DmStrSpn( s, brk );
434 53300 : done = (anchor == 0) ? TRUE :
435 26650 : ((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 1474322 : if( !done ) {
444 1174668 : SCAN_WHITE( s );
445 :
446 1174668 : t = s;
447 : do {
448 1365102 : done = TRUE;
449 1365102 : curp = DmStrPbrk(t, space);
450 :
451 1365102 : if( anchor && *curp && !IS_WHITE( *curp ) )
452 194366 : if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) {
453 190434 : t++;
454 190434 : done = FALSE;
455 : }
456 : }
457 1365102 : while( !done );
458 :
459 1174668 : if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++;
460 : }
461 :
462 : found_token:
463 1475882 : string->tk_str = curp;
464 1475882 : string->tk_cchar = *curp;
465 1475882 : *curp = '\0';
466 :
467 : DB_PRINT( "tok", ("Returning [%s]", s) );
468 1475882 : DB_RETURN( s );
469 : }
470 :
471 :
472 : static int
473 329978 : _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;
478 : {
479 : DB_ENTER( "_is_conditional" );
480 :
481 329978 : tg++;
482 329978 : switch( *tg )
483 : {
484 : case 'I':
485 154304 : if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF );
486 16886 : else if( !strcmp( tg, "IFEQ" )) DB_RETURN( ST_IFEQ );
487 16886 : else if( !strcmp( tg, "IFNEQ" )) DB_RETURN( ST_IFNEQ );
488 16886 : break;
489 :
490 : case 'E':
491 172444 : if( !strcmp( tg, "END" )) DB_RETURN( ST_END );
492 171046 : else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END );
493 35026 : else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE );
494 4656 : else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF );
495 3862 : break;
496 : }
497 :
498 23978 : DB_RETURN( 0 );
499 : }
500 :
501 :
502 :
503 : #define SEEN_END 0x00
504 : #define SEEN_IF 0x01
505 : #define SEEN_ELSE 0x02
506 : #define SEEN_ELIF 0x04
507 :
508 : #define ACCEPT_IF 0x10
509 : #define ACCEPT_ELIF 0x20
510 :
511 : static int
512 306000 : _handle_conditional( opcode, tg )
513 : int opcode;
514 : TKSTRPTR tg;
515 : {
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;
522 :
523 : DB_ENTER( "_handle_conditional" );
524 :
525 306000 : switch( opcode ) {
526 : case ST_ELIF:
527 794 : if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) )
528 0 : Fatal(".ELIF without a preceeding .IF" );
529 : /*FALLTHRU*/
530 :
531 : case ST_IF:
532 : case ST_IFEQ:
533 : case ST_IFNEQ:
534 138212 : if( opcode != ST_ELIF && (Nest_level+1) == MAX_COND_DEPTH )
535 0 : Fatal( ".IF .ELSE ... .END nesting too deep" );
536 :
537 138212 : If_expand = TRUE;
538 138212 : expr = Expand( Get_token( tg, NIL(char), FALSE ));
539 138212 : If_expand = FALSE;
540 :
541 : /* Remove CONTINUATION_CHAR<nl> and replace with " " so that line
542 : * continuations are recognized as whitespace. */
543 138400 : for( cst=strchr(expr,CONTINUATION_CHAR); cst != NIL(char); cst=strchr(cst,CONTINUATION_CHAR) )
544 188 : if( cst[1] == '\n' ) {
545 188 : *cst = ' ';
546 188 : cst[1] = ' ';
547 : }
548 : else
549 0 : cst++;
550 :
551 138212 : lhs = expr;
552 138212 : SCAN_WHITE( lhs );
553 :
554 : /* Parse the expression and get its logical result */
555 138212 : if ( ((lop = DmStrStr(lhs, "||" )) != NIL(char)) || ((lop = DmStrStr(lhs, "&&" )) != NIL(char)) )
556 8856 : result = parse_complex_expression( lhs, &expr_end, opcode );
557 : else
558 129356 : result = partcomp( lhs, opcode );
559 :
560 138212 : if( expr != NIL(char) ) FREE( expr );
561 :
562 138212 : if( opcode != ST_ELIF ) {
563 137418 : Nest_level++;
564 137418 : action[Nest_level] = 1;
565 : }
566 138212 : ifcntl[Nest_level] |= (opcode==ST_ELIF)?SEEN_ELIF:SEEN_IF;
567 :
568 138212 : if( result ) {
569 40128 : if( !(ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF)) ) {
570 40126 : action[ Nest_level ] = action[ Nest_level-1 ];
571 40126 : ifcntl[Nest_level] |= (opcode==ST_ELIF)?ACCEPT_ELIF:ACCEPT_IF;
572 : }
573 : else
574 2 : action[Nest_level] = 1;
575 : }
576 : else
577 98084 : action[Nest_level] = 1;
578 138212 : break;
579 :
580 : case ST_ELSE:
581 30370 : if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" );
582 30370 : if( ifcntl[Nest_level] & SEEN_ELSE )
583 0 : Fatal( "Missing .IF or .ELIF before .ELSE" );
584 :
585 30370 : if( ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF) )
586 14492 : action[Nest_level] = 1;
587 15878 : else if( action[ Nest_level-1 ] != 1 )
588 10054 : action[ Nest_level ] ^= 0x1; /* flip between 0 and 1 */
589 :
590 30370 : ifcntl[Nest_level] |= SEEN_ELSE;
591 30370 : break;
592 :
593 : case ST_END:
594 137418 : ifcntl[Nest_level] = SEEN_END;
595 137418 : Nest_level--;
596 137418 : if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" );
597 137418 : break;
598 : }
599 :
600 306000 : DB_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++;
606 :
607 : #define OP_NONE 0
608 : #define OP_AND 1
609 : #define OP_OR 2
610 :
611 : static int n = 1;
612 :
613 9414 : int parse_complex_expression( char *expr, char **expr_end, int opcode )
614 : {
615 9414 : char *p = expr;
616 9414 : char *term_start = p;
617 : char *term_end;
618 : int local_term;
619 : char *part;
620 9414 : int term_result = FALSE;
621 9414 : int final_result = TRUE;
622 : unsigned int term_len;
623 9414 : unsigned int last_op = OP_NONE;
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 358752 : local_term = TRUE;
633 358752 : if ( *p == '(' )
634 : {
635 558 : n++;
636 558 : term_result = parse_complex_expression( p+1, &p, opcode );
637 558 : n--;
638 558 : PARSE_SKIP_WHITE( p );
639 558 : term_start = p;
640 558 : term_end = p;
641 558 : local_term = FALSE;
642 : }
643 : else
644 358194 : term_end = p;
645 :
646 : /* Lets do an operation!! */
647 358752 : if ( !(*p) /* at the end of the entire line */
648 349896 : || ((*p == '&') && (*(p+1) && (*(p+1)=='&'))) /* found an && */
649 341528 : || ((*p == '|') && (*(p+1) && (*(p+1)=='|'))) /* found an || */
650 334788 : || (*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 24522 : if ( local_term == TRUE )
657 : {
658 : /* Back up 1 to the end of the actual term */
659 23964 : term_end--;
660 :
661 : /* Evaluate the term */
662 23964 : PARSE_SKIP_WHITE( term_start );
663 23964 : term_len = term_end - term_start + 1;
664 23964 : part = MALLOC( term_len + 1, char );
665 23964 : strncpy( part, term_start, term_len );
666 23964 : *(part+term_len) = '\0';
667 : #ifdef PARSE_DEBUG
668 : printf( "%d: evaling '%s'\n", n, part );
669 : #endif
670 23964 : term_result = partcomp( part, opcode );
671 : #ifdef PARSE_DEBUG
672 : printf( "%d: evaled, result %d\n", n, term_result );
673 : #endif
674 23964 : FREE( part );
675 : }
676 :
677 : /* Do the actual logical operation using the _preceding_
678 : * logical operator, NOT the one we just found.
679 : */
680 24522 : if ( last_op == OP_AND )
681 8368 : final_result = final_result && term_result;
682 16154 : else if ( last_op == OP_OR )
683 6740 : final_result = final_result || term_result;
684 : else
685 9414 : 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 39630 : if ( *p )
692 : {
693 : /* Recognize the operator we just found above */
694 15666 : if ( *p == '&' )
695 8368 : last_op = OP_AND;
696 7298 : else if ( *p == '|' )
697 6740 : last_op = OP_OR;
698 15666 : if ( *p != ')' )
699 15108 : p += 2;
700 :
701 : /* Get the start of the next term */
702 15666 : PARSE_SKIP_WHITE( p );
703 15666 : term_start = p;
704 :
705 : /* If this is the close of a term, we are done and return
706 : * to our caller.
707 : */
708 15666 : if ( *p == ')' )
709 : {
710 558 : p++;
711 558 : break;
712 : }
713 : }
714 8856 : else break; /* At end of line, all done */
715 : }
716 334230 : else if ( local_term == TRUE ) p++; /* Advance to next char in expression */
717 349338 : }
718 9414 : *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 9414 : return( final_result );
724 : }
725 :
726 :
727 153320 : int partcomp( char* lhs, int opcode )
728 : {
729 :
730 153320 : char *tok, *rhs, *op = 0;
731 : int result, opsind;
732 153320 : const int localopscount=4;
733 153320 : char* localops[] = { "==", "!=", "<=", ">=" };
734 :
735 : #define EQUAL 0
736 : #define NOTEQUAL 1
737 : #define LESS_EQUAL 2
738 : #define GREATER_EQUAL 3
739 :
740 : #ifdef PARSE_DEBUG
741 : printf( "eval: %s\n", lhs);
742 : #endif
743 :
744 153320 : opsind = 0;
745 153320 : if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
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 0 : for( op = lhs; ((*op)&&(*op != ' ')&&(*op != '\t')); op++ );
751 0 : if( *op ) op++; /* position op at start of <2> */
752 0 : else op = NIL(char); /* 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 390512 : while ( (opsind < localopscount) && ((op = DmStrStr(lhs, localops[opsind])) == NIL(char)) )
759 83872 : 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 153320 : if( op == NIL(char) )
771 0 : 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 153320 : if( opcode != ST_IFEQ && opcode != ST_IFNEQ )
778 153320 : 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 153320 : if( lhs != op )
786 : {
787 153136 : for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok-- );
788 153136 : tok[1] = '\0';
789 : }
790 : else
791 184 : lhs = NIL(char); /* Left hand side is empty. */
792 :
793 : /* Jump over the operation so we can grab the right half of the expression */
794 153320 : if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
795 0 : op--;
796 : else
797 153320 : op++;
798 :
799 : /* Isolate the right half of the expression */
800 153320 : rhs = DmStrSpn( op+1, " \t" );
801 153320 : if( !*rhs ) rhs = NIL(char);
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 153320 : if ( opsind > NOTEQUAL )
809 : {
810 2384 : switch( opsind )
811 : {
812 : case LESS_EQUAL:
813 : case GREATER_EQUAL:
814 : /* Ignore quotes around the arguments */
815 2384 : if ( lhs && lhs[0] == '"' ) lhs++;
816 2384 : if ( rhs && rhs[0] == '"' ) rhs++;
817 :
818 : /* Empty strings evaluate to zero. */
819 2384 : int lint = lhs ? atoi( lhs ) : 0;
820 2384 : int rint = rhs ? atoi( rhs ) : 0;
821 2384 : result = ( lint >= rint ) ? TRUE : FALSE;
822 2384 : if ( opsind == LESS_EQUAL && lint != rint )
823 736 : result = !result;
824 2384 : break;
825 : default:
826 0 : result = FALSE;
827 : }
828 : }
829 : else
830 : {
831 : /* Use a simple string compare to determine equality */
832 150936 : if( (rhs == NIL(char)) || (lhs == NIL(char)) )
833 184 : result = (rhs == lhs) ? TRUE : FALSE;
834 : else
835 : {
836 : /* String off whitespace at the end of the right half of the expression */
837 150752 : tok = rhs + strlen( rhs );
838 150752 : for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--);
839 150752 : tok[1] = '\0';
840 :
841 150752 : result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE;
842 : }
843 :
844 150936 : if( *op == '!' || opcode == ST_IFNEQ ) result = !result;
845 : }
846 : }
847 :
848 : #ifdef PARSE_DEBUG
849 : printf("partresult %d\n\n",result);
850 : #endif
851 153320 : return result;
852 : }
853 :
|