41faa44b02
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
848 lines
25 KiB
C
848 lines
25 KiB
C
/* RCS $Id: getinp.c,v 1.10 2007-10-15 15:39:23 ihi Exp $
|
|
--
|
|
-- 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.
|
|
--
|
|
-- 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 );
|
|
|
|
|
|
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. */
|
|
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;
|
|
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. */
|
|
|
|
while( (p = Rule_tab[ rule_ind++ ]) != NIL(char) )
|
|
/* 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 */
|
|
if( !Readenv || (Readenv && (strchr(p,'=') != NIL(char)) && *p!='~' && *p!='!')){
|
|
strcpy( buf, p );
|
|
|
|
DB_PRINT( "io", ("Returning [%s]", buf) );
|
|
DB_RETURN( FALSE );
|
|
}
|
|
|
|
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. */
|
|
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. */
|
|
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--;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* If a comment is found the line does not end in \n anymore. */
|
|
c = Do_comment(p, &q, Group || (*buf == '\t') || (Notabs && *buf ==' '));
|
|
|
|
/* Does the end of the line end in a continuation sequence? */
|
|
|
|
if( (q[0] == CONTINUATION_CHAR) && (q[1] == '\n')) {
|
|
/* 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 ) {
|
|
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 {
|
|
cont = FALSE;
|
|
}
|
|
|
|
q = ( c == NIL(char) ) ? q+2 : c;
|
|
}
|
|
else { /* empty line or "" */
|
|
cont = FALSE;
|
|
ignore = FALSE;
|
|
q = p+strlen(p); /* strlen(p) is 1 or 0 */
|
|
}
|
|
|
|
pos += q-p;
|
|
}
|
|
while( (cont || !*buf) && (pos < Buffer_size-1) );
|
|
|
|
if( pos >= Buffer_size-1 )
|
|
Fatal( "Input line too long, increase MAXLINELENGTH" );
|
|
|
|
/* 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. */
|
|
|
|
/* 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 ) {
|
|
size_t len = strlen(c)+1;
|
|
memmove( c-1, c, len ); /* copy it left, due to \# */
|
|
if( pend ) (*pend)--; /* shift tail pointer left */
|
|
}
|
|
else {
|
|
/* Check/execute if shebang command is present. */
|
|
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)
|
|
Swap_on_exec = TRUE;
|
|
#endif
|
|
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);
|
|
}
|
|
|
|
*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;
|
|
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;
|
|
|
|
case 'E':
|
|
if( !strcmp( tg, "END" )) DB_RETURN( ST_END );
|
|
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;
|
|
}
|
|
|
|
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;
|
|
{
|
|
static short action[MAX_COND_DEPTH];
|
|
static char ifcntl[MAX_COND_DEPTH];
|
|
char *cst;
|
|
char *lhs, *expr, *expr_end;
|
|
char *lop;
|
|
int result;
|
|
|
|
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 );
|
|
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 );
|
|
}
|
|
|
|
|
|
int partcomp( char* lhs, int opcode )
|
|
{
|
|
|
|
char *tok, *rhs, *op = 0;
|
|
int result, opsind;
|
|
const int localopscount=4;
|
|
char* localops[] = { "==", "!=", "<=", ">=" };
|
|
int lint, rint;
|
|
|
|
#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 )
|
|
{
|
|
/* 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 */
|
|
}
|
|
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
|
|
}
|
|
|
|
/* 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
|
|
*/
|
|
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 )
|
|
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';
|
|
}
|
|
else
|
|
lhs = NIL(char); /* Left hand side is empty. */
|
|
|
|
/* Jump over the operation so we can grab the right half of the expression */
|
|
if( opcode == ST_IFEQ || opcode == ST_IFNEQ )
|
|
op--;
|
|
else
|
|
op++;
|
|
|
|
/* Isolate the right half of the expression */
|
|
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 )
|
|
{
|
|
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. */
|
|
lint = lhs ? atoi( lhs ) : 0;
|
|
rint = rhs ? atoi( rhs ) : 0;
|
|
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 */
|
|
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 */
|
|
tok = rhs + strlen( rhs );
|
|
for( tok=tok-1; (tok != lhs) && ((*tok == ' ')||(*tok == '\t')); tok--);
|
|
tok[1] = '\0';
|
|
|
|
result = (strcmp( lhs, rhs ) == 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
if( *op == '!' || opcode == ST_IFNEQ ) result = !result;
|
|
}
|
|
}
|
|
|
|
#ifdef PARSE_DEBUG
|
|
printf("partresult %d\n\n",result);
|
|
#endif
|
|
return result;
|
|
}
|
|
|