office-gobmx/dmake/getinp.c

854 lines
25 KiB
C
Raw Normal View History

/* RCS $Id: getinp.c,v 1.10 2007-10-15 15:39:23 ihi Exp $
2000-09-22 09:33:37 -05:00
--
-- SYNOPSIS
-- Handle reading of input.
--
-- DESCRIPTION
-- The code in this file reads the input from the specified stream
-- into the provided buffer of size Buffer_size. In doing so it deletes
-- comments. Comments are delimited by the #, and
-- <nl> character sequences. An exception is \# which
-- is replaced by # in the input. Line continuations are signalled
-- at the end of a line and are recognized inside comments.
-- The line continuation is always <\><nl>.
--
-- If the file to read is NIL(FILE) then the Get_line routine returns the
-- next rule from the builtin rule table (Rule_tab from ruletab.c) if
-- there is one.
2000-09-22 09:33:37 -05:00
--
-- AUTHOR
-- Dennis Vadura, dvadura@dmake.wticorp.com
--
-- WWW
-- http://dmake.wticorp.com/
--
-- COPYRIGHT
-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
--
-- This program is NOT free software; you can redistribute it and/or
-- modify it under the terms of the Software License Agreement Provided
-- in the file <distribution-root>/readme/license.txt.
--
-- LOG
-- Use cvs log to obtain detailed change logs.
*/
#include "extern.h"
#define IS_WHITE(A) ((A == ' ') || (A == '\t') || (A == '\n') || (A == '\r'))
#define SCAN_WHITE(A) \
while( IS_WHITE(*A) ) A++;
static int _is_conditional ANSI((char*));
static int _handle_conditional ANSI((int, TKSTRPTR));
static int rule_ind = 0; /* index of rule when reading Rule_tab */
static int skip = FALSE; /* if true the skip input */
int partcomp( char* lhs, int opcode );
int parse_complex_expression( char *expr, char **expr_end, int opcode );
2000-09-22 09:33:37 -05:00
PUBLIC int
Get_line( buf, fil )/*
======================
Read a line of input from the file stripping off comments. The routine
returns TRUE if EOF. If fil equals NIL(FILE) then the next line from
*Rule_tab[] is used. Rule_tab is either the buildin rule table or points
to the current environment (used by ReadEnvironment()).
The function returns TRUE if the input file/buffer was read to the end
and FALSE otherwise. */
2000-09-22 09:33:37 -05:00
char *buf;
FILE *fil;
{
extern char **Rule_tab;
register char *p;
register char *c;
char *q;
char *buf_org;
static int ignore = FALSE;
int cont = FALSE;
int pos = 0;
int res = 0;
2000-09-22 09:33:37 -05:00
register char *tmp = NIL(char);
DB_ENTER( "Get_line" );
if( Skip_to_eof ) {
Skip_to_eof = FALSE;
rule_ind = 0;
if( Verbose & V_MAKE )
Warning("Ignoring remainder of file %s", Filename());
DB_RETURN(TRUE);
}
if( fil == NIL(FILE) ) {
/* Reading the internal rule table. Set rule_ind to zero after the
* last entry so that ReadEnvironment() works as expected every time. */
2000-09-22 09:33:37 -05:00
while( (p = Rule_tab[ rule_ind++ ]) != NIL(char) ) {
2000-09-22 09:33:37 -05:00
/* The last test in this if *p != '~', handles the environment
* passing conventions used by MKS to pass arguments. We want to
* skip those environment entries. Also CYGWIN likes to export '!'
* prefixed environment variables that cause severe pain, axe them too.
* And finally it is possible to do "env 'GGG HHH'='some value' bash"
* which causes that there are env variables with spaces in the name
* defined which causes dmake to malfunction too */
char *equal = strchr(p,'=');
char *space = strchr(p,' ');
if( !Readenv || (Readenv && (equal != NIL(char)) && (space == NIL(char) || space > equal) && *p!='~' && *p!='!')){
2000-09-22 09:33:37 -05:00
strcpy( buf, p );
DB_PRINT( "io", ("Returning [%s]", buf) );
DB_RETURN( FALSE );
}
}
2000-09-22 09:33:37 -05:00
rule_ind = 0;
DB_PRINT( "io", ("Done Ruletab") );
DB_RETURN( TRUE );
}
buf_org = buf;
do_again:
do {
p = buf+pos;
/* fgets() reads at most one less than Buffer_size-pos characters. */
2000-09-22 09:33:37 -05:00
if(feof( fil ) || (fgets( p, Buffer_size-pos, fil ) == NIL(char)))
DB_RETURN( TRUE );
#ifdef _MPW
if ( p[0] == 10 && p[1] == COMMENT_CHAR)
p[0] = ' ';
#endif
Line_number++;
/* Set q to the last char in p before the \n\0. */
2000-09-22 09:33:37 -05:00
q = p+strlen(p)-2;
if( q >= p ) { /* Only check for special cases if p points
* to a non-empty line. */
/* ignore each RETURN at the end of a line before any further
* processing */
if( q[0] == '\r' && q[1] == '\n' ) {
q[0] = '\n';
q[1] = '\0';
q--;
}
/* you also have to deal with END_OF_FILE chars to process raw
* DOS-Files. Normally they are the last chars in file, but after
* working on these file with vi, there is an additional NEWLINE
* after the last END_OF_FILE. So if the second last char in the
* actual line is END_OF_FILE, you can skip the last char. Then
* you can search the line back until you find no more END_OF_FILE
* and nuke each you found by string termination. */
if( q[0] == '\032' )
q--;
while( q[1] == '\032' ) {
q[1] = '\0';
q--;
}
2000-09-22 09:33:37 -05:00
/* ignore input if ignore flag set and line ends in a continuation
character. */
if( ignore ) {
if( q[0] != CONTINUATION_CHAR || q[1] != '\n' ) ignore = FALSE;
*p = '\0';
continue;
}
2000-09-22 09:33:37 -05:00
/* If a comment is found the line does not end in \n anymore. */
c = Do_comment(p, &q, Group || (*buf == '\t') || (Notabs && *buf ==' '));
2000-09-22 09:33:37 -05:00
/* Does the end of the line end in a continuation sequence? */
2000-09-22 09:33:37 -05:00
if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) {
2000-09-22 09:33:37 -05:00
/* If the continuation was at the end of a comment then ignore the
* next input line, (or lines until we get one ending in just <nl>)
* else it's a continuation, so build the input line from several
* text lines on input. The maximum size of this is governened by
* Buffer_size */
if( q != p && q[-1] == CONTINUATION_CHAR ) {
CWS-TOOLING: integrate CWS cmcfixes62 2009-09-22 21:19:24 +0200 cmc r276365 : #i100000# that's the most plausible explanation, -D$(CVER) is needed by scp2 under windows 2009-09-19 16:10:36 +0200 cmc r276303 : #i100000# revert this for no partically good reason 2009-09-18 14:16:47 +0200 cmc r276279 : remove extra build depend 2009-09-18 08:56:15 +0200 cmc r276265 : #i100000# rerun autoconf 2009-09-17 14:59:38 +0200 cmc r276245 : CWS-TOOLING: rebase CWS cmcfixes62 to trunk@276192 (milestone: DEV300:m59) 2009-09-10 16:40:49 +0200 pl r276041 : fix warning 2009-09-10 11:34:55 +0200 pl r276026 : disable warning from system header 2009-09-09 19:30:45 +0200 pl r276013 : use osl_getAsciiFunctionSymbol instead of strange cast 2009-09-09 19:25:32 +0200 pl r276012 : add some casting hacks to make compile on solaris cc 2009-09-09 19:10:48 +0200 pl r276009 : add missing Configure option for solaris sparce 32bit 2009-09-09 18:37:05 +0200 pl r276008 : merge fix for #i104525# into moved code 2009-09-07 13:28:08 +0200 cmc r275894 : CWS-TOOLING: rebase CWS cmcfixes62 to trunk@275801 (milestone: DEV300:m57) 2009-08-28 12:36:27 +0200 cmc r275523 : CWS-TOOLING: rebase CWS cmcfixes62 to trunk@275331 (milestone: DEV300:m56) 2009-08-26 12:56:18 +0200 cmc r275413 : #i104088# use the right headers in the right place 2009-08-25 17:07:50 +0200 cmc r275372 : #i104500# make openssl build on linux that isn't x86 or x86_64 2009-08-25 13:08:48 +0200 cmc r275347 : #i89237# pretty picswitch up 2009-08-25 10:58:09 +0200 cmc r275342 : #i104489# remove last unused code in starmath 2009-08-25 10:23:33 +0200 cmc r275338 : #i104088# clean up the define post-move 2009-08-24 13:46:57 +0200 cmc r275310 : #i104088# 64bit odbc api changes 2009-08-24 13:42:52 +0200 cmc r275309 : #i89237# unify linux platform mks under a generic parent 2009-08-24 13:19:52 +0200 cmc r275307 : #i104455# remove unused code 2009-08-24 13:10:56 +0200 cmc r275306 : #i104088# 64bit odbc api changes 2009-08-24 10:07:50 +0200 cmc r275293 : #i104306# move recent-used to vcl 2009-08-24 09:32:08 +0200 cmc r275291 : #i104306# move recent-used to vcl 2009-08-23 22:01:07 +0200 cmc r275286 : #i104306# move recently-used to vcl 2009-08-22 15:25:25 +0200 cmc r275285 : #i104088# 64bit odbc api changes 2009-08-21 17:52:40 +0200 cmc r275268 : #i104306# move recently-used to vcl 2009-08-21 14:38:26 +0200 cmc r275257 : #i104408 make these headers ready for c++0x 2009-08-21 14:20:19 +0200 cmc r275251 : #i104406# avoid overlapping strcpy to avoid bustage on recent toolchains 2009-08-21 14:14:25 +0200 cmc r275250 : #i104385# silence 64bit warnings
2009-10-01 04:16:17 -05:00
size_t len = strlen(q+1)+1;
memmove( q, q+1, len );
q--;
cont = FALSE;
}
else if( c != NIL(char) )
ignore = TRUE;
else
cont = TRUE; /* Keep the \<nl>. */
}
else {
2000-09-22 09:33:37 -05:00
cont = FALSE;
}
q = ( c == NIL(char) ) ? q+2 : c;
2000-09-22 09:33:37 -05:00
}
else { /* empty line or "" */
2000-09-22 09:33:37 -05:00
cont = FALSE;
ignore = FALSE;
q = p+strlen(p); /* strlen(p) is 1 or 0 */
2000-09-22 09:33:37 -05:00
}
pos += q-p;
}
while( (cont || !*buf) && (pos < Buffer_size-1) );
if( pos >= Buffer_size-1 )
Fatal( "Input line too long, increase MAXLINELENGTH" );
2000-09-22 09:33:37 -05:00
/* Lines that had comments don't contain \n anymore. */
/* ??? Continued lines that are followed by an empty or comment only
* line will end in \<nl>. */
if( (q > p) && (buf[ pos-1 ] == '\n') )
buf[ --pos ] = '\0'; /* Remove the final \n. */
2000-09-22 09:33:37 -05:00
/* STUPID AUGMAKE uses "include" at the start of a line as
* a signal to include a new file, so let's look for it.
* if we see it replace it by .INCLUDE: and stick this back
* into the buffer. We also allow GNU make if[n]eq/else/endif.
*
* These substitutions are made only if we are not parsing a group
* recipe. */
if( (p = DmStrSpn(buf, " \t\r\n")) == NIL(char) )
p = buf;
if (!Group) {
if( !strncmp( "include", p, 7 ) &&
(p[7] == ' ' || p[7] == '\t') )
tmp = DmStrJoin( ".INCLUDE:", p+7, -1, FALSE );
else if( !strncmp( "ifeq", p, 4 ) &&
(p[4] == ' ' || p[4] == '\t') )
tmp = DmStrJoin( ".IFEQ", p+4, -1, FALSE );
else if( !strncmp( "ifneq", p, 5 ) &&
(p[5] == ' ' || p[5] == '\t') )
tmp = DmStrJoin( ".IFNEQ", p+5, -1, FALSE );
else if( !strncmp( "elif", p, 4 ) &&
(p[4] == ' ' || p[4] == '\t') )
tmp = DmStrJoin( ".ELIF", p+4, -1, FALSE );
else if( !strncmp( "else", p, 4 ) &&
(p[4] == ' ' || p[4] == '\t' || p[4] == '\0') )
tmp = DmStrJoin( ".ELSE", p+4, -1, FALSE );
else if( !strncmp( "endif", p, 5 ) &&
(p[5] == ' ' || p[5] == '\t' || p[5] == '\0') )
tmp = DmStrJoin( ".END", p+5, -1, FALSE );
}
if( tmp != NIL(char)) {
strcpy( buf, tmp );
FREE( tmp );
tmp = NIL(char);
}
/* Now that we have the next line of input to make, we should check to
* see if it is a conditional expression. If it is then process it,
* otherwise pass it on to the parser. */
if( *(p = DmStrSpn(buf, " \t\r\n")) == CONDSTART ) {
TKSTR token;
SET_TOKEN( &token, p );
p = Get_token( &token, "", FALSE );
if( (res = _is_conditional(p)) != 0 ) /* ignore non-control special */
{ /* targets */
res = _handle_conditional( res, &token );
skip = TRUE;
}
else {
CLEAR_TOKEN( &token );
res = TRUE;
}
}
if( skip ) {
buf = buf_org; /* ignore line just read in */
pos = 0;
skip = res;
goto do_again;
}
DB_PRINT( "io", ("Returning [%s]", buf) );
DB_RETURN( FALSE );
}
PUBLIC char *
Do_comment(str, pend, keep)/*
=============================
Search the input string looking for comment chars. If it contains
comment chars then NUKE the remainder of the line, if the comment
char is preceeded by \ then shift the remainder of the line left
by one char. */
char *str;
char **pend;
int keep;
{
char *c = str;
while( (c = strchr(c, COMMENT_CHAR)) != NIL(char) ) {
if( Comment || State == NORMAL_SCAN )
if( c != str && c[-1] == ESCAPE_CHAR ) {
CWS-TOOLING: integrate CWS cmcfixes62 2009-09-22 21:19:24 +0200 cmc r276365 : #i100000# that's the most plausible explanation, -D$(CVER) is needed by scp2 under windows 2009-09-19 16:10:36 +0200 cmc r276303 : #i100000# revert this for no partically good reason 2009-09-18 14:16:47 +0200 cmc r276279 : remove extra build depend 2009-09-18 08:56:15 +0200 cmc r276265 : #i100000# rerun autoconf 2009-09-17 14:59:38 +0200 cmc r276245 : CWS-TOOLING: rebase CWS cmcfixes62 to trunk@276192 (milestone: DEV300:m59) 2009-09-10 16:40:49 +0200 pl r276041 : fix warning 2009-09-10 11:34:55 +0200 pl r276026 : disable warning from system header 2009-09-09 19:30:45 +0200 pl r276013 : use osl_getAsciiFunctionSymbol instead of strange cast 2009-09-09 19:25:32 +0200 pl r276012 : add some casting hacks to make compile on solaris cc 2009-09-09 19:10:48 +0200 pl r276009 : add missing Configure option for solaris sparce 32bit 2009-09-09 18:37:05 +0200 pl r276008 : merge fix for #i104525# into moved code 2009-09-07 13:28:08 +0200 cmc r275894 : CWS-TOOLING: rebase CWS cmcfixes62 to trunk@275801 (milestone: DEV300:m57) 2009-08-28 12:36:27 +0200 cmc r275523 : CWS-TOOLING: rebase CWS cmcfixes62 to trunk@275331 (milestone: DEV300:m56) 2009-08-26 12:56:18 +0200 cmc r275413 : #i104088# use the right headers in the right place 2009-08-25 17:07:50 +0200 cmc r275372 : #i104500# make openssl build on linux that isn't x86 or x86_64 2009-08-25 13:08:48 +0200 cmc r275347 : #i89237# pretty picswitch up 2009-08-25 10:58:09 +0200 cmc r275342 : #i104489# remove last unused code in starmath 2009-08-25 10:23:33 +0200 cmc r275338 : #i104088# clean up the define post-move 2009-08-24 13:46:57 +0200 cmc r275310 : #i104088# 64bit odbc api changes 2009-08-24 13:42:52 +0200 cmc r275309 : #i89237# unify linux platform mks under a generic parent 2009-08-24 13:19:52 +0200 cmc r275307 : #i104455# remove unused code 2009-08-24 13:10:56 +0200 cmc r275306 : #i104088# 64bit odbc api changes 2009-08-24 10:07:50 +0200 cmc r275293 : #i104306# move recent-used to vcl 2009-08-24 09:32:08 +0200 cmc r275291 : #i104306# move recent-used to vcl 2009-08-23 22:01:07 +0200 cmc r275286 : #i104306# move recently-used to vcl 2009-08-22 15:25:25 +0200 cmc r275285 : #i104088# 64bit odbc api changes 2009-08-21 17:52:40 +0200 cmc r275268 : #i104306# move recently-used to vcl 2009-08-21 14:38:26 +0200 cmc r275257 : #i104408 make these headers ready for c++0x 2009-08-21 14:20:19 +0200 cmc r275251 : #i104406# avoid overlapping strcpy to avoid bustage on recent toolchains 2009-08-21 14:14:25 +0200 cmc r275250 : #i104385# silence 64bit warnings
2009-10-01 04:16:17 -05:00
size_t len = strlen(c)+1;
memmove( c-1, c, len ); /* copy it left, due to \# */
2000-09-22 09:33:37 -05:00
if( pend ) (*pend)--; /* shift tail pointer left */
}
else {
/* Check/execute if shebang command is present. */
2000-09-22 09:33:37 -05:00
if( !No_exec
&& c == str
&& c[1] == '!'
&& Line_number == 1
&& Nestlevel() == 1 ) {
char *cmnd;
cmnd = Expand(c+2);
cmnd[strlen(cmnd)-1] = '\0'; /* strip last newline */
Current_target = Root;
#if defined(MSDOS)
2000-09-22 09:33:37 -05:00
Swap_on_exec = TRUE;
#endif
2000-09-22 09:33:37 -05:00
Wait_for_completion = TRUE;
Do_cmnd(&cmnd, FALSE, TRUE, Current_target, A_DEFAULT, TRUE);
#if defined(MSDOS)
Swap_on_exec = FALSE;
#endif
Wait_for_completion = FALSE;
FREE(cmnd);
2000-09-22 09:33:37 -05:00
}
*c = '\0'; /* a true comment so break */
break;
}
else {
if( keep )
c = NIL(char);
else
*c = '\0';
break;
}
}
return(c);
}
PUBLIC char *
Get_token( string, brk, anchor )/*
==================================
Return the next token in string.
Returns empty string when no more tokens in string.
brk is a list of chars that also cause breaks in addition to space and
tab, but are themselves returned as tokens. if brk is NULL then the
remainder of the line is returned as a single token.
'anchor' if 1, says break on chars in the brk list, but only if
the entire token begins with the first char of the brk list, if
0 then any char of brk will cause a break to occurr.
If 'anchor' is 2, then break only seeing the first char in the break
list allowing only chars in the break list to form the prefix. */
TKSTRPTR string;
char *brk;
int anchor;
{
register char *s;
register char *curp = 0;
2000-09-22 09:33:37 -05:00
register char *t;
int done = FALSE;
char space[100];
DB_ENTER( "Get_token" );
s = string->tk_str; /* Get string parameters */
*s = string->tk_cchar; /* ... and strip leading w/s */
SCAN_WHITE( s );
DB_PRINT( "tok", ("What's left [%s]", s) );
if( !*s ) {
DB_PRINT( "tok", ("Returning NULL token") );
DB_RETURN( "" );
}
/* Build the space list. space contains all those chars that may possibly
* cause breaks. This includes the brk list as well as white space. */
if( brk != NIL(char) ) {
strcpy( space, " \t\r\n" );
strcat( space, brk );
}
else {
space[0] = 0xff; /* a char we know will not show up */
space[1] = 0;
}
/* Handle processing of quoted tokens. Note that this is disabled if
* brk is equal to NIL */
while( *s == '\"' && ((brk != NIL(char)) || !string->tk_quote) ) {
s++;
if( string->tk_quote ) {
curp = s-1;
do { curp = strchr( curp+1, '\"' ); }
while( (curp != NIL(char)) && (*(curp+1) == '\"'));
if( curp == NIL(char) ) Fatal( "Unmatched quote in token" );
string->tk_quote = !string->tk_quote;
/* Check for "" case, and if found ignore it */
if( curp == s ) continue;
goto found_token;
}
else
SCAN_WHITE( s );
string->tk_quote = !string->tk_quote;
}
/* Check for a token break character at the beginning of the token.
* If found return the next set of break chars as a token. */
if( anchor == 2 && brk != NIL(char) ) {
curp = s;
while( *curp && (strchr(brk,*curp)!=NIL(char)) && (*curp!=*brk) ) curp++;
done = (*brk == *curp++);
}
else if( (brk != NIL(char)) && (strchr( brk, *s ) != NIL(char)) ) {
curp = DmStrSpn( s, brk );
done = (anchor == 0) ? TRUE :
((anchor == 1)?(*s == *brk) : (*brk == curp[-1]));
}
/* Scan for the next token in the list and return it less the break char
* that was used to terminate the token. It will possibly be returned in
* the next call to Get_token */
if( !done ) {
SCAN_WHITE( s );
t = s;
do {
done = TRUE;
curp = DmStrPbrk(t, space);
if( anchor && *curp && !IS_WHITE( *curp ) )
if( ((anchor == 1)?*curp:DmStrSpn(curp,brk)[-1]) != *brk ) {
t++;
done = FALSE;
}
}
while( !done );
if( (curp == s) && (strchr(brk, *curp) != NIL(char)) ) curp++;
}
found_token:
string->tk_str = curp;
string->tk_cchar = *curp;
*curp = '\0';
DB_PRINT( "tok", ("Returning [%s]", s) );
DB_RETURN( s );
}
static int
_is_conditional( tg )/*
=======================
Look at tg and return it's value if it is a conditional identifier
otherwise return 0. */
char *tg;
{
DB_ENTER( "_is_conditional" );
tg++;
switch( *tg )
{
case 'I':
if( !strcmp( tg, "IF" )) DB_RETURN( ST_IF );
else if( !strcmp( tg, "IFEQ" )) DB_RETURN( ST_IFEQ );
else if( !strcmp( tg, "IFNEQ" )) DB_RETURN( ST_IFNEQ );
break;
2000-09-22 09:33:37 -05:00
case 'E':
if( !strcmp( tg, "END" )) DB_RETURN( ST_END );
2000-09-22 09:33:37 -05:00
else if( !strcmp( tg, "ENDIF")) DB_RETURN( ST_END );
else if( !strcmp( tg, "ELSE" )) DB_RETURN( ST_ELSE );
else if( !strcmp( tg, "ELIF" )) DB_RETURN( ST_ELIF );
break;
2000-09-22 09:33:37 -05:00
}
DB_RETURN( 0 );
}
#define SEEN_END 0x00
#define SEEN_IF 0x01
#define SEEN_ELSE 0x02
#define SEEN_ELIF 0x04
#define ACCEPT_IF 0x10
#define ACCEPT_ELIF 0x20
static int
_handle_conditional( opcode, tg )
int opcode;
TKSTRPTR tg;
2000-09-22 09:33:37 -05:00
{
static short action[MAX_COND_DEPTH];
static char ifcntl[MAX_COND_DEPTH];
char *cst;
char *lhs, *expr, *expr_end;
char *lop;
int result;
2000-09-22 09:33:37 -05:00
DB_ENTER( "_handle_conditional" );
switch( opcode ) {
case ST_ELIF:
if( !(ifcntl[Nest_level] & SEEN_IF) || (ifcntl[Nest_level]&SEEN_ELSE) )
Fatal(".ELIF without a preceeding .IF" );
/*FALLTHRU*/
case ST_IF:
case ST_IFEQ:
case ST_IFNEQ:
if( opcode != ST_ELIF && (Nest_level+1) == MAX_COND_DEPTH )
Fatal( ".IF .ELSE ... .END nesting too deep" );
If_expand = TRUE;
expr = Expand( Get_token( tg, NIL(char), FALSE ));
If_expand = FALSE;
/* Remove CONTINUATION_CHAR<nl> and replace with " " so that line
* continuations are recognized as whitespace. */
for( cst=strchr(expr,CONTINUATION_CHAR); cst != NIL(char); cst=strchr(cst,CONTINUATION_CHAR) )
if( cst[1] == '\n' ) {
*cst = ' ';
cst[1] = ' ';
}
else
cst++;
lhs = expr;
SCAN_WHITE( lhs );
/* Parse the expression and get its logical result */
if ( ((lop = DmStrStr(lhs, "||" )) != NIL(char)) || ((lop = DmStrStr(lhs, "&&" )) != NIL(char)) )
result = parse_complex_expression( lhs, &expr_end, opcode );
2000-09-22 09:33:37 -05:00
else
result = partcomp( lhs, opcode );
if( expr != NIL(char) ) FREE( expr );
if( opcode != ST_ELIF ) {
Nest_level++;
action[Nest_level] = 1;
}
ifcntl[Nest_level] |= (opcode==ST_ELIF)?SEEN_ELIF:SEEN_IF;
if( result ) {
if( !(ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF)) ) {
action[ Nest_level ] = action[ Nest_level-1 ];
ifcntl[Nest_level] |= (opcode==ST_ELIF)?ACCEPT_ELIF:ACCEPT_IF;
}
else
action[Nest_level] = 1;
}
else
action[Nest_level] = 1;
break;
case ST_ELSE:
if( Nest_level <= 0 ) Fatal( ".ELSE without .IF" );
if( ifcntl[Nest_level] & SEEN_ELSE )
Fatal( "Missing .IF or .ELIF before .ELSE" );
if( ifcntl[Nest_level] & (ACCEPT_IF|ACCEPT_ELIF) )
action[Nest_level] = 1;
else if( action[ Nest_level-1 ] != 1 )
action[ Nest_level ] ^= 0x1; /* flip between 0 and 1 */
ifcntl[Nest_level] |= SEEN_ELSE;
break;
case ST_END:
ifcntl[Nest_level] = SEEN_END;
Nest_level--;
if( Nest_level < 0 ) Fatal( "Unmatched .END[IF]" );
break;
}
DB_RETURN( action[ Nest_level ] );
}
/* uncomment to turn on expression debug statements */
/*#define PARSE_DEBUG */
#define PARSE_SKIP_WHITE(A) while( *A && ((*A==' ') || (*A=='\t')) ) A++;
#define OP_NONE 0
#define OP_AND 1
#define OP_OR 2
static int n = 1;
int parse_complex_expression( char *expr, char **expr_end, int opcode )
{
char *p = expr;
char *term_start = p;
char *term_end;
int local_term;
char *part;
int term_result = FALSE;
int final_result = TRUE;
unsigned int term_len;
unsigned int last_op = OP_NONE;
#ifdef PARSE_DEBUG
printf( "%d: parse_complex_expression( %s ): Opcode: %d\n", n, expr, opcode );
#endif
while ( 1 )
{
/* A new sub-expression */
local_term = TRUE;
if ( *p == '(' )
{
n++;
term_result = parse_complex_expression( p+1, &p, opcode );
n--;
PARSE_SKIP_WHITE( p );
term_start = p;
term_end = p;
local_term = FALSE;
}
else
term_end = p;
/* Lets do an operation!! */
if ( !(*p) /* at the end of the entire line */
|| ((*p == '&') && (*(p+1) && (*(p+1)=='&'))) /* found an && */
|| ((*p == '|') && (*(p+1) && (*(p+1)=='|'))) /* found an || */
|| (*p == ')') ) /* at the end of our term */
{
/* Grab the sub-expression if we parsed it. Otherwise,
* it was a () subexpression and we don't need to evaluate
* it since that was already done.
*/
if ( local_term == TRUE )
{
/* Back up 1 to the end of the actual term */
term_end--;
/* Evaluate the term */
PARSE_SKIP_WHITE( term_start );
term_len = term_end - term_start + 1;
part = MALLOC( term_len + 1, char );
strncpy( part, term_start, term_len );
*(part+term_len) = '\0';
#ifdef PARSE_DEBUG
printf( "%d: evaling '%s'\n", n, part );
#endif
term_result = partcomp( part, opcode );
#ifdef PARSE_DEBUG
printf( "%d: evaled, result %d\n", n, term_result );
#endif
FREE( part );
}
/* Do the actual logical operation using the _preceding_
* logical operator, NOT the one we just found.
*/
if ( last_op == OP_AND )
final_result = final_result && term_result;
else if ( last_op == OP_OR )
final_result = final_result || term_result;
else
final_result = term_result;
#ifdef PARSE_DEBUG
printf( "%d: final_result:%d\n", n, final_result );
#endif
/* If we're not at the end of the line, just keep going */
if ( *p )
{
/* Recognize the operator we just found above */
if ( *p == '&' )
last_op = OP_AND;
else if ( *p == '|' )
last_op = OP_OR;
if ( *p != ')' )
p += 2;
/* Get the start of the next term */
PARSE_SKIP_WHITE( p );
term_start = p;
/* If this is the close of a term, we are done and return
* to our caller.
*/
if ( *p == ')' )
{
p++;
break;
}
}
else break; /* At end of line, all done */
}
else if ( local_term == TRUE ) p++; /* Advance to next char in expression */
}
*expr_end = p;
#ifdef PARSE_DEBUG
printf( "%d: done, returning '%s', result %d\n", n, *expr_end, final_result );
#endif
return( final_result );
}
2000-09-22 09:33:37 -05:00
int partcomp( char* lhs, int opcode )
{
char *tok, *rhs, *op = 0;
int result, opsind;
2000-09-22 09:33:37 -05:00
const int localopscount=4;
char* localops[] = { "==", "!=", "<=", ">=" };
2000-09-22 09:33:37 -05:00
#define EQUAL 0
#define NOTEQUAL 1
#define LESS_EQUAL 2
#define GREATER_EQUAL 3
#ifdef PARSE_DEBUG
printf( "eval: %s\n", lhs);
#endif
opsind = 0;
if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
2000-09-22 09:33:37 -05:00
{
/* IF[N]EQ syntax is: .IF[N]EQ <1> <2>
* Here, step over first argument and get to <2> if it exists.
*/
for( op = lhs; ((*op)&&(*op != ' ')&&(*op != '\t')); op++ );
if( *op ) op++; /* position op at start of <2> */
else op = NIL(char); /* only 1 argument given */
2000-09-22 09:33:37 -05:00
}
else
{
/* Find which logical operator we are to use for this expression,
* and jump to it */
while ( (opsind < localopscount) && ((op = DmStrStr(lhs, localops[opsind])) == NIL(char)) )
opsind++;
#ifdef PARSE_DEBUG
printf(" found op %d: %s\n", opsind, localops[opsind]);
#endif
}
2000-09-22 09:33:37 -05:00
/* If the opcode was IFEQ or IFNEQ and only 1 argument was given,
* or an unknown logical operator was encountered,
* return false if argument is empty string, true if !empty
*/
2000-09-22 09:33:37 -05:00
if( op == NIL(char) )
result = (*lhs != '\0');
else
{
/* Make both characters of the operation the same, replacing the = in op[1]
* Its easier to deal with this way???
*/
if( opcode != ST_IFEQ && opcode != ST_IFNEQ )
2000-09-22 09:33:37 -05:00
op[1] = op[0];
#ifdef PARSE_DEBUG
printf(" op:%s\n", op);
#endif
/* Isolate the left half of the expression */
if( lhs != op )
{
for( tok = op-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok-- );
tok[1] = '\0';
2000-09-22 09:33:37 -05:00
}
else
lhs = NIL(char); /* Left hand side is empty. */
2000-09-22 09:33:37 -05:00
/* Jump over the operation so we can grab the right half of the expression */
if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
op--;
2000-09-22 09:33:37 -05:00
else
op++;
/* Isolate the right half of the expression */
2000-09-22 09:33:37 -05:00
rhs = DmStrSpn( op+1, " \t" );
if( !*rhs ) rhs = NIL(char);
#ifdef PARSE_DEBUG
printf(" lhs:%s, rhs:%s\n", lhs, rhs);
#endif
/* Do the actual logical operation on the expression */
if ( opsind > NOTEQUAL )
{
switch( opsind )
{
2000-09-22 09:33:37 -05:00
case LESS_EQUAL:
case GREATER_EQUAL:
/* Ignore quotes around the arguments */
if ( lhs && lhs[0] == '"' ) lhs++;
if ( rhs && rhs[0] == '"' ) rhs++;
/* Empty strings evaluate to zero. */
int lint = lhs ? atoi( lhs ) : 0;
int rint = rhs ? atoi( rhs ) : 0;
2000-09-22 09:33:37 -05:00
result = ( lint >= rint ) ? TRUE : FALSE;
if ( opsind == LESS_EQUAL && lint != rint )
result = !result;
break;
default:
result = FALSE;
}
}
else
{
/* Use a simple string compare to determine equality */
2000-09-22 09:33:37 -05:00
if( (rhs == NIL(char)) || (lhs == NIL(char)) )
result = (rhs == lhs) ? TRUE : FALSE;
else
{
/* String off whitespace at the end of the right half of the expression */
2000-09-22 09:33:37 -05:00
tok = rhs + strlen( rhs );
for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--);
2000-09-22 09:33:37 -05:00
tok[1] = '\0';
result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE;
2000-09-22 09:33:37 -05:00
}
2000-09-22 09:33:37 -05:00
if( *op == '!' || opcode == ST_IFNEQ ) result = !result;
}
}
#ifdef PARSE_DEBUG
printf("partresult %d\n\n",result);
#endif
2000-09-22 09:33:37 -05:00
return result;
}
2000-09-22 09:33:37 -05:00