/* condparser.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2004-2014 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "condparser.h"
#include "aguix/lowlevelfunc.h"
#include "aguix/awindow.h"
#include "aguix/acontainer.h"
#include "aguix/fieldlistview.h"
#include "aguix/button.h"
#include "execlass.h"
#include "fileentry.hh"
#include "worker.h"
#include "datei.h"
#include "worker_locale.h"
#include "nwc_path.hh"

struct CondParser::deftokens CondParser::keywords[] = {
  { "lasterror", CondParser::TOK_LASTERROR },
  { "true", CondParser::TOK_TRUE },
  { "false", CondParser::TOK_FALSE },
  { "toNum", CondParser::TOK_TONUM },
  { "toStr", CondParser::TOK_TOSTR },
  { "isReg", CondParser::TOK_ISREG },
  { "isSock", CondParser::TOK_ISSOCK },
  { "isFIFO", CondParser::TOK_ISFIFO },
  { "isSymlink", CondParser::TOK_ISSYMLINK },
  { "isBrokenSymlink", CondParser::TOK_ISBROKENSYMLINK },
  { "size", CondParser::TOK_SIZE },
  { "perm", CondParser::TOK_PERM },
  { "toLower", CondParser::TOK_TOLOWER },
  { "toUpper", CondParser::TOK_TOUPPER },
  { "contentStr", CondParser::TOK_CONTENTSTR },
  { "contentNum", CondParser::TOK_CONTENTNUM },
  { "name", CondParser::TOK_NAME },
  { "fullname", CondParser::TOK_FULLNAME },
  { "isLocal", CondParser::TOK_ISLOCAL }
};

struct CondParser::flaghelp CondParser::flags[] = {
  { "true", -1, 44 },
  { "false", -1, 45 },
  { "toNum(...)", -1, 51 },
  { "toStr(...)", -1, 52 },
  { "isReg()", 579, -1 },
  { "isSock()", 580, -1 },
  { "isFIFO()", 581, -1 },
  { "isSymlink()", 582, -1 },
  { "isBrokenSymlink()", 1036, -1 },
  { "isLocal()", 626, -1 },
  { "size()", 583, -1 },
  { "perm(mask)", 584, -1 },
  { "toLower(...)", 585, -1 },
  { "toUpper(...)", 586, -1 },
  { "contentStr(<Start>[,<Length>])", 587, -1 },
  { "contentNum(<Start>[,<Length>])", 588, -1 },
  { "name()", 589, -1 },
  { "fullname()", 590, -1 },
  { "${...}", -1, 48 },
  { "?{...}", -1, 47 },
  { "{f}", 591, -1 },
  { "{F}", 592, -1 },
  { "lasterror", -1, 42 }
};

CondParser::CondParser()
{
  curpos = 0;
  tokenval = -1;
  ignore = 0;
  scanbuf = NULL;
  scanbuflen = -1;
  lasterror = 0;
  fe = NULL;
  ignoreContent = false;
}

CondParser::~CondParser()
{
}

int CondParser::lookup( const char *s )
{
  unsigned int p;

  for ( p = 0; p < ( sizeof(keywords) / sizeof( keywords[0] ) ); p++ )
    if ( strcasecmp( keywords[p].str, s ) == 0 )
      return keywords[p].token;
  return TOK_NONE;
}

int CondParser::lex()
{
  int t;
  int base = 10;

  while ( 1 ) {
    if ( curpos >= scanbuflen ) break;
    t = scanbuf[curpos++];
    if ( isspace( t ) ) {
    } else if ( isdigit( t ) ) {
      tokenval = t - '0';
      if ( tokenval == 0 ) {
        base = 8;
      } else {
        base = 10;
      }
      while ( isdigit( t = scanbuf[curpos++] ) ) {
        if ( ( t - '0' ) >= base ) t = '0';
	tokenval = base * tokenval + ( t - '0' );
      }
      curpos--;
      return TOK_NUM;
    } else if ( isalpha( t ) || ( t == '_' ) ) {
      int b = 0;

      while ( isalnum( t ) || ( t == '_' ) ) {
        lexbuf[b] = t;
	t = scanbuf[curpos++];
	b = b + 1;
	if ( ( b + 1 ) >= BUFSIZE ) {
	  // Pufferueberlauf
          break;
        }
      }
      lexbuf[b] = '\0';
      if ( t != '\0' ) {
	curpos--;
      }
      return lookup( lexbuf );
    } else if ( t == '"' ) {
      int b = 0;
      int bs = 0;

      for ( ;; ) {
	t = scanbuf[curpos++];
	if ( t == EOF )
	  break;
	if ( ( t == '\\' ) && ( bs == 0 ) ) {
	  bs = 1;
	  continue;
	}
	if ( ( t == '"' ) && ( bs == 0 ) )
	  break;
	bs = 0;
        lexbuf[b] = t;
	b = b + 1;
	if ( ( b + 1 ) >= BUFSIZE ) {
	  // Pufferueberlauf
          break;
        }
      }
      lexbuf[b] = '\0';
      return TOK_STRING;
    } else if ( t == '&' ) {
      t = scanbuf[curpos++];
      if ( t == '&' )
	return TOK_P_AND;
      return TOK_NONE;
    } else if ( t == '|' ) {
      t = scanbuf[curpos++];
      if ( t == '|' )
	return TOK_P_OR;
      return TOK_NONE;
    } else if ( t == '=' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return TOK_E;
      return TOK_NONE;
    } else if ( t == '!' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return TOK_NE;
      return TOK_NONE;
    } else if ( t == '<' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return TOK_LE;
      curpos--;
      return TOK_LT;
    } else if ( t == '>' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return TOK_GE;
      curpos--;
      return TOK_GT;
    } else if ( t == '~' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return TOK_REGEQ;
      return TOK_NONE;
    } else if ( t == '\0' ) {
      return TOK_DONE;
    } else {
      tokenval = TOK_NONE;
      return t;
    }
  }
  return TOK_DONE;
}

int CondParser::match( int t )
{
  if ( lookahead == t ) {
    lookahead = lex();
  } else {
    return 1;
  }
  return 0;
}

char *CondParser::readBrace()
{
  int startpos, endpos;
  int bracecount = 0;
  int t, ig = 0, len, readpos;
  char *tstr;
  
  startpos = curpos;
  if ( scanbuf[curpos] == '{' ) {
    do {
      t = scanbuf[curpos++];
      if ( t == '\0' ) break;
      else if ( ( t == '\\' ) && ( ig == 0 ) ) {
        ig = 1;
        continue;
      } else if ( ( t == '{' ) && ( ig == 0 ) ) {
        bracecount++;
      } else if ( ( t == '}' ) && ( ig == 0 ) ) {
        bracecount--;
      }
      ig = 0;
    } while ( bracecount > 0 );
  }
  endpos = curpos;

  len = endpos - startpos;
  tstr = (char*)_allocsafe( len + 1 );
  if ( len > 0 ) {
      strncpy( tstr, scanbuf + startpos, len );
  }
  tstr[len] = '\0';
  /* now turn backslashed {/} to normal {/} and remove first and last brace */
  t = 0; readpos = 1;
  for( readpos = 1; readpos < ( len - 1); readpos++ ) {
    if ( ( tstr[readpos] == '\\' ) &&
         ( ( tstr[readpos + 1 ] == '{' ) || ( tstr[readpos + 1] == '}' ) ) ) {
      tstr[t] = tstr[readpos + 1];
      readpos++;
    } else {
      tstr[t] = tstr[readpos];
    }
    t++;
  }
  tstr[t] = '\0';
  return tstr;
}

int CondParser::do_parse()
{
  erg_t *e = NULL;
  int erg;
  
  lookahead = lex();
  if ( lookahead != TOK_DONE ) {
    e = expr();
  }
  if ( e == NULL ) return -1; /* error */
  erg = e->numval;
  _freesafe( e->strval );
  _freesafe( e );
  if ( lookahead != TOK_DONE ) return -1; /* Not at the end => error */
  return ( erg == 0 ) ? 0 : 1;
}

CondParser::erg_t * CondParser::expr()
{
  erg_t *e = NULL, *e2;
  int oldignore;
  int l;
  
  if ( ( e = factor() ) == NULL ) return NULL;
  while ( 1 ) {
    switch ( lookahead ) {
      case TOK_P_AND:
	if ( match( TOK_P_AND ) ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

        oldignore = ignore;
        if ( e->numval == 0 ) ignore = 1;

	if ( ( e2 = factor() ) == NULL ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

        ignore = oldignore;

        if ( e->numval != 0 ) {
          e->numval = e2->numval;
        }
        _freesafe( e->strval );
        e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
        e->wasstring = 0;
        sprintf( e->strval, "%d", e->numval );
        _freesafe( e2->strval );
        _freesafe( e2 );
        break;
      case TOK_P_OR:
	if ( match( TOK_P_OR ) ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

        oldignore = ignore;
        if ( e->numval != 0 ) ignore = 1;

	if ( ( e2 = factor() ) == NULL ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

        ignore = oldignore;

        if ( e->numval == 0 ) {
          e->numval = e2->numval;
        }
        _freesafe( e->strval );
        e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
        e->wasstring = 0;
        sprintf( e->strval, "%d", e->numval );
        _freesafe( e2->strval );
        _freesafe( e2 );
        break;
      case TOK_E:
      case TOK_NE:
      case TOK_LT:
      case TOK_LE:
      case TOK_GT:
      case TOK_GE:
      case TOK_REGEQ:
        l = lookahead;
	if ( match( lookahead ) ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

	if ( ( e2 = factor() ) == NULL ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

        if ( ( e->wasstring == 1 ) && ( e2->wasstring == 1 ) ) {
          /* only do strcmp when both are strings */
          switch ( l ) {
            case TOK_NE:
              e->numval = ( strcmp( e->strval, e2->strval ) != 0 );
              break;
            case TOK_LT:
              e->numval = ( strcmp( e->strval, e2->strval ) < 0 );
              break;
            case TOK_LE:
              e->numval = ( strcmp( e->strval, e2->strval ) <= 0 );
              break;
            case TOK_GT:
              e->numval = ( strcmp( e->strval, e2->strval ) > 0 );
              break;
            case TOK_GE:
              e->numval = ( strcmp( e->strval, e2->strval ) >= 0 );
              break;
	    case TOK_REGEQ:
	      e->numval = ( re.match( e2->strval, e->strval ) == true );
              //TODO:bei false könnte man e2 und e vertauschen
	      break;
            default:
              e->numval = ( strcmp( e->strval, e2->strval ) == 0 );
              break;
          }
        } else {
          switch ( l ) {
            case TOK_NE:
              e->numval = ( e->numval != e2->numval );
              break;
            case TOK_LT:
              e->numval = ( e->numval < e2->numval );
              break;
            case TOK_LE:
              e->numval = ( e->numval <= e2->numval );
              break;
            case TOK_GT:
              e->numval = ( e->numval > e2->numval );
              break;
            case TOK_GE:
              e->numval = ( e->numval >= e2->numval );
              break;
	    case TOK_REGEQ:
	      // regular expression without strings is always false
	      e->numval = 0;
	      break;
            default:
              e->numval = ( e->numval == e2->numval );
              break;
          }
        }
        _freesafe( e->strval );
        e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
        e->wasstring = 0;
        sprintf( e->strval, "%d", e->numval );
        _freesafe( e2->strval );
        _freesafe( e2 );
        break;
      default:
	return e;
    }
  }
  return e;
}

#define W_FREEERG(e) do { \
  if ( (e) != NULL ) { \
    _freesafe( (e)->strval ); \
    _freesafe( (e) ); \
  } \
} while( 0 );

CondParser::erg_t *CondParser::factor()
{
  erg_t *e = NULL, *e2 = NULL;
  int tnum;
  char *tstr, *tstr2;
  ExeClass *ec;
  int exeerror;
  const char *cstr;
  int retval;

  switch ( lookahead ) {
    case '(':
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      if ( match( ')' ) ) {
        W_FREEERG( e );
        return NULL;
      }
      break;
    case '-':
      if ( match( '-' ) ) return NULL;
      tnum = tokenval;
      if ( match( TOK_NUM ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->numval = tnum * -1;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_NUM:
      tnum = tokenval;
      if ( match( TOK_NUM ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->numval = tnum;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_TRUE:
      if ( match( TOK_TRUE ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->numval = 1;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_FALSE:
      if ( match( TOK_FALSE ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->numval = 0;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_LASTERROR:
      if ( match( TOK_LASTERROR ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->numval = getLastError();
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      resetLastError();
      break;
    case '?':
    case '$':
      tstr = readBrace();
      if ( tstr != NULL ) {
        if ( ignore == 0 ) {
          /*execute tstr */
          tstr2 = replaceFlags( tstr /*, a_max( EXE_STRING_LEN - 1024, 256 ) */);
          _freesafe( tstr );
          tstr = tstr2;
  
          if ( tstr != NULL ) {
            if ( fe->findOutputAndRV( tstr, &cstr, &retval ) != 0 ) {
              e = (erg_t*)_allocsafe( sizeof( erg_t ) );
              ec = new ExeClass();
              if ( fe != NULL ) {
                tstr2 = NWC::Path::parentDir( fe->fullname, NULL );
                if ( tstr2 != NULL ) {
                  ec->cdDir( tstr2 );
                  _freesafe( tstr2 );
                }
              }
              ec->addCommand( "%s", tstr );
              if ( ec->getOutputAndRV( &(e->strval), &retval, &exeerror ) == 0 ) {
                if ( lookahead == '?' ) {
                  e->numval = retval;
                  if ( e->strval != NULL ) _freesafe( e->strval );
                  e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
                  e->wasstring = 0;
                  sprintf( e->strval, "%d", e->numval );
                } else {
                  e->wasstring = 1;
                  if ( e->strval == NULL ) {
                    e->strval = dupstring( "" );
                  }
                  e->numval = atoi( e->strval );
                }
  
                fe->addOutput( tstr, e->strval, retval );
              } else {
                e->strval = dupstring( "0" );
                e->wasstring = 1;
                e->numval = atoi( e->strval );
              }
              delete ec;
            
              if ( exeerror != 0 ) {
#if 0
//TODO: Requester are not possible at the moment because
//      this thread cannot access GUI functions currently
//TODO: I should show content of error file just like in wpucontext
                if ( worker->getRequester() != NULL ) {
                  char *reqstr;
                  reqstr = (char*)_allocsafe( strlen( catalog.getLocale( 473 ) ) + strlen( tstr ) + 1 );
                  sprintf( reqstr, catalog.getLocale( 473 ), tstr );
                  worker->getRequester()->request( catalog.getLocale( 347 ), reqstr, catalog.getLocale( 11 ) );
                  _freesafe( reqstr );
                }
#endif
                W_FREEERG( e );
                _freesafe( tstr );
                return NULL;
              }
            } else {
              if ( lookahead == '?' ) {
                e = (erg_t*)_allocsafe( sizeof( erg_t ) );
                e->numval = retval;
                e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
                e->wasstring = 0;
                sprintf( e->strval, "%d", e->numval );
              } else {
                e = (erg_t*)_allocsafe( sizeof( erg_t ) );
                e->strval = dupstring( cstr );
                e->wasstring = 1;
                e->numval = atoi( e->strval );
              }
            }
            _freesafe( tstr );
          }
        } else {
          e = (erg_t*)_allocsafe( sizeof( erg_t ) );
          e->strval = dupstring( "0" );
          e->wasstring = 1;
          e->numval = atoi( e->strval );
          _freesafe( tstr );
        }
      }
      if ( match( lookahead ) ) {
        W_FREEERG( e );
        return NULL;
      }
      break;
    case TOK_STRING:
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 1;
      if ( ignore == 1 ) {
        e->strval = dupstring( "0" );
      } else {
        e->strval = replaceFlags( lexbuf /*, a_max( EXE_STRING_LEN - 1024, 256 )*/, false );
        if ( e->strval == NULL ) e->strval = dupstring( "0" );
      }
      e->numval = atoi( e->strval );
      if ( match( TOK_STRING ) ) {
        W_FREEERG( e );
        return NULL;
      }
      break;
    case TOK_TONUM:
      if ( match( TOK_TONUM ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      if ( match( ')' ) ) {
        W_FREEERG( e );
        return NULL;
      }
      _freesafe( e->strval );
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_TOSTR:
      if ( match( TOK_TOSTR ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      if ( match( ')' ) ) {
        W_FREEERG( e );
        return NULL;
      }
      _freesafe( e->strval );
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 1;
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_ISREG:
      if ( match( TOK_ISREG ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
        e->numval = ( S_ISREG( fe->mode() ) ) ? 1 : 0;
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_ISSOCK:
      if ( match( TOK_ISSOCK ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
#ifdef S_ISSOCK
        e->numval = ( S_ISSOCK( fe->mode() ) ) ? 1 : 0;
#else
        e->numval = 0;
#endif
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_ISFIFO:
      if ( match( TOK_ISFIFO ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
        e->numval = ( S_ISFIFO( fe->mode() ) ) ? 1 : 0;
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_ISSYMLINK:
      if ( match( TOK_ISSYMLINK ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
        e->numval = ( fe->isLink == true ) ? 1 : 0;
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_ISBROKENSYMLINK:
      if ( match( TOK_ISBROKENSYMLINK ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
        e->numval = ( fe->isLink == true && fe->isCorrupt == true ) ? 1 : 0;
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_SIZE:
      if ( match( TOK_SIZE ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
        e->numval = fe->size();
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_PERM:
      if ( match( TOK_PERM ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      if ( match( ')' ) ) {
        W_FREEERG( e );
        return NULL;
      }
      _freesafe( e->strval );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
        e->numval = ( fe->mode() & e->numval );
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case TOK_TOLOWER:
      if ( match( TOK_TOLOWER ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      if ( match( ')' ) ) {
        W_FREEERG( e );
        return NULL;
      }
      for ( tstr = e->strval; *tstr != '\0'; tstr++ ) {
        *tstr = tolower( *tstr );
      }
      break;
    case TOK_TOUPPER:
      if ( match( TOK_TOUPPER ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      if ( match( ')' ) ) {
        W_FREEERG( e );
        return NULL;
      }
      for ( tstr = e->strval; *tstr != '\0'; tstr++ ) {
        *tstr = toupper( *tstr );
      }
      break;
    case TOK_CONTENTSTR:
      if ( match( TOK_CONTENTSTR ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      e2 = NULL;
      if ( lookahead == ',' ) {
        if ( match( ',' ) ) {
          W_FREEERG( e );
          return NULL;
        }
        if ( ( e2 = expr() ) == NULL ) {
          W_FREEERG( e );
          return NULL;
        }       
      }
      if ( match( ')' ) ) {
        W_FREEERG( e );
        W_FREEERG( e2 );
        return NULL;
      }
      if ( fe == NULL ) {
        e->numval = 0;
        e->wasstring = 0;
        _freesafe( e->strval );
        e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
        sprintf( e->strval, "%d", e->numval );
      } else {
	if ( ignoreContent == false ) {
	  if ( e2 != NULL ) {
	    tstr = fe->getContentStr( e->numval, e2->numval );
	  } else {
	    tstr = fe->getContentStr( e->numval );
	  }
	} else {
	  tstr = dupstring( "" );
	}
        if ( tstr != NULL ) {
          _freesafe( e->strval );
          e->strval = tstr;
          e->numval = atoi( e->strval );
          e->wasstring = 1;
        } else {
          e->numval = 0;
          e->wasstring = 0;
          _freesafe( e->strval );
          e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
          sprintf( e->strval, "%d", e->numval );
        }
      }
      W_FREEERG( e2 );
      break;
    case TOK_CONTENTNUM:
      if ( match( TOK_CONTENTNUM ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( ( e = expr() ) == NULL ) return NULL;
      e2 = NULL;
      if ( lookahead == ',' ) {
        if ( match( ',' ) ) {
          W_FREEERG( e );
          return NULL;
        }
        if ( ( e2 = expr() ) == NULL ) {
          W_FREEERG( e );
          return NULL;
        }       
      }
      if ( match( ')' ) ) {
        W_FREEERG( e );
        W_FREEERG( e2 );
        return NULL;
      }
      if ( fe == NULL ) {
        e->numval = 0;
        e->wasstring = 0;
        _freesafe( e->strval );
        e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
        sprintf( e->strval, "%d", e->numval );
      } else {
	if ( ignoreContent == false ) {
	  if ( e2 != NULL ) {
	    e->numval = fe->getContentNum( e->numval, e2->numval );
	  } else {
	    e->numval = fe->getContentNum( e->numval );
	  }
	} else {
	  e->numval = 0;
	}
        e->wasstring = 0;
        _freesafe( e->strval );
        e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
        sprintf( e->strval, "%d", e->numval );
      }
      W_FREEERG( e2 );
      break;
    case TOK_NAME:
      if ( match( TOK_NAME ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      if ( fe == NULL ) {
        e->strval = dupstring( "0" );
      } else {
        e->strval = dupstring( fe->name );
      }
      e->wasstring = 1;
      e->numval = atoi( e->strval );
      break;
    case TOK_FULLNAME:
      if ( match( TOK_FULLNAME ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      if ( fe == NULL ) {
        e->strval = dupstring( "0" );
      } else {
        e->strval = dupstring( fe->fullname );
      }
      e->wasstring = 1;
      e->numval = atoi( e->strval );
      break;
    case TOK_ISLOCAL:
      if ( match( TOK_ISLOCAL ) ) return NULL;
      if ( match( '(' ) ) return NULL;
      if ( match( ')' ) ) return NULL;
      e = (erg_t*)_allocsafe( sizeof( erg_t ) );
      e->wasstring = 0;
      if ( fe == NULL ) {
        e->numval = 0;
      } else {
        e->numval = worker_islocal( fe->fullname );
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    default:
      return NULL;
  }
  return e;
}

int CondParser::parse( const char *str, FileEntry *tfe )
{
  int erg;
  
  if ( str == NULL ) return -1;
  fe = tfe;
  scanbuf = str;
  scanbuflen = strlen( str );
  curpos = 0;
  erg = do_parse();
  scanbuf = NULL;
  scanbuflen = -1;
  fe = NULL;
  return erg;
}

int CondParser::getLastError() const
{
  return lasterror;
}

void CondParser::resetLastError()
{
  lasterror = 0;
}

#define PARSECAT( str1 ) \
do { \
  if ( ( quote == true ) && ( force_noquoting == false ) ) { \
    dstr[dpos] = '\0'; \
    newdstr = AGUIX_catTrustedAndUnTrusted( dstr, ( str1 ) ); \
    if ( (int)strlen( newdstr ) >= maxlen ) { \
      _freesafe( dstr ); \
      maxlen = 2 * (int)strlen( newdstr ); \
      dstr = (char*)_allocsafe( maxlen + 1 ); \
    } \
    snprintf( dstr, maxlen, "%s", newdstr ); \
    _freesafe( newdstr ); \
    dstr[maxlen] = '\0'; \
    dpos = (int)strlen( dstr ); \
  } else { \
    tlen = (int)strlen( ( str1 ) ); \
    if ( ( dpos + tlen ) >= maxlen ) { \
      maxlen = ( dpos + tlen ) * 2; \
      newdstr = (char*)_allocsafe( maxlen + 1 ); \
      strncpy( newdstr, dstr, dpos ); \
      _freesafe( dstr ); \
      dstr = newdstr; \
    } \
    strcpy( &dstr[dpos], ( str1 ) ); \
    dpos += (int)strlen( ( str1 ) ); \
  } \
} while ( 0 );

#define PUTCHARDSTR( ch ) \
do { \
  if ( dpos >= maxlen ) { \
    maxlen = dpos * 2; \
    newdstr = (char*)_allocsafe( maxlen + 1 ); \
    strncpy( newdstr, dstr, dpos ); \
    _freesafe( dstr ); \
    dstr = newdstr; \
  } \
  dstr[dpos++] = ch; \
} while ( 0 );

char *CondParser::replaceFlags( const char *sstr, bool quote )
{
  char *dstr = NULL, *buf1, ch, *newdstr;
  int spos=0,dpos=0,bracketcount=0,bufpos=0;
  int mode=0;
  int tlen;
  bool ende;
  char *tstr;
  std::string str1;
  char *flagbuf;
  int flagbuf_size;
  bool force_noquoting;
  int maxlen;
  
  if ( sstr == NULL ) return NULL;
  maxlen = strlen( sstr );
  dstr = (char*)_allocsafe( maxlen + 1 );
  flagbuf_size = strlen( sstr );
  flagbuf = (char*)_allocsafe( flagbuf_size + 1 );
  if(dstr!=NULL) {
    for(ende=false;ende==false;) {
      ch=sstr[spos++];
      if(ch=='\0') break;
      switch(mode) {
        case 1:
          // we are in a open bracket and waiting for close bracket
          if(ch=='{') {
            // other flag, perhaps useless, but perhaps it is in a String-requester
            // in any case just overtake it in the buffer
            if ( bufpos < flagbuf_size ) flagbuf[ bufpos++ ] = ch;
            bracketcount++;
          } else if(ch=='}') {
            // a closeing bracket
            if(bracketcount>1) {
              // this is a bracket in the bracket
              if ( bufpos < flagbuf_size ) flagbuf[ bufpos++ ] = ch;
            } else {
              // this is the awaited closebracket
              flagbuf[ bufpos ] = 0;
              // now flagbuf contains a flag which must be parsed
              mode=3;
            }
            bracketcount--;
          } else if(ch=='\\') {
            // backslash are only resolved on toplevel (bracketcount==0)
            // so overtake also this
            if ( bufpos < flagbuf_size ) flagbuf[ bufpos++ ] = ch;
            mode=4;
          } else {
            if ( bufpos < flagbuf_size ) flagbuf[ bufpos++ ] = ch;
          }
          break;
        case 2:
          PUTCHARDSTR( ch );
          mode=0;
          break;
        case 4:
          if ( bufpos < flagbuf_size ) flagbuf[ bufpos++ ] = ch;
          mode=1;
          break;
        default:
          // we are in no bracket
          if(ch=='\\') {
            // next character is protected and will be overtaken
            mode=2;
          } else if(ch!='{') {
            // a normal character
            PUTCHARDSTR( ch );
          } else {
            mode=1;
            bracketcount++;
            bufpos=0;
          }
          break;
      }
      if(mode==3) {
        if ( flagbuf[0] == '-' ) {
          force_noquoting = true;
          buf1 = flagbuf + 1;
        } else {
          force_noquoting = false;
          buf1 = flagbuf;
        }
        // parse buf1
        if ( strncmp( buf1, "f", 1 ) == 0 ) {
          if ( fe != NULL ) {
            tstr = fe->name;
            if ( tstr != NULL ) {
              PARSECAT( tstr );
            }
          }
        } else if ( strncmp( buf1, "F", 1 ) == 0 ) {
          if ( fe != NULL ) {
            tstr = fe->fullname;
            if ( tstr != NULL ) {
              PARSECAT( tstr );
            }
          }
        }
        mode=0;
      }
    }
    dstr[dpos]=0;
  }
  _freesafe( flagbuf );
  return dstr;
}

const char *CondParser::requestFlag()
{
  AGUIX *aguix = Worker::getAGUIX();
  AWindow *win;
  FieldListView *lv;
  AGMessage *msg;
  int endmode=-1;
  const char *returnstr;
  int i, trow, nr;
  
  win = new AWindow( aguix, 10, 10, 10, 10, catalog.getLocale( 338 ), AWindow::AWINDOW_DIALOG );
  win->create();

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 3 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );
  ac1->setBorderWidth( 5 );

  win->addMultiLineText( catalog.getLocale( 578 ), *ac1, 0, 0, NULL, NULL );

  lv = (FieldListView*)ac1->add( new FieldListView( aguix,
                                                    0,
                                                    0,
                                                    400,
                                                    200,
                                                    0 ),
                                 0, 1, AContainer::CO_MIN );
  lv->setHBarState(2);
  lv->setVBarState(2);

  lv->setNrOfFields( 3 );
  lv->setFieldWidth( 1, 3 );
  
  nr = sizeof( flags ) / sizeof( flags[0] );
  
  for ( i = 0; i < nr; i++ ) {
    trow = lv->addRow();
    lv->setText( trow, 0, flags[i].flag );
    if ( flags[i].catID >= 0 ) {
      lv->setText( trow, 2, catalog.getLocale( flags[i].catID ) );
    } else {
      lv->setText( trow, 2, catalog.getLocaleFlag( flags[i].catflagID ) );
    }
    lv->setPreColors( trow, FieldListView::PRECOLOR_ONLYACTIVE );
    lv->setData( trow, i );
  }
  
  AContainer *ac1_1 = ac1->add( new AContainer( win, 2, 1 ), 0, 2 );
  ac1_1->setMinSpace( 5 );
  ac1_1->setMaxSpace( -1 );
  ac1_1->setBorderWidth( 0 );
  Button *okb =(Button*)ac1_1->add( new Button( aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 11 ),
                                                0 ), 0, 0, AContainer::CO_FIX );
  Button *cancelb = (Button*)ac1_1->add( new Button( aguix,
						     0,
						     0,
						     catalog.getLocale( 8 ),
						     0 ), 1, 0, AContainer::CO_FIX );

  win->setDoTabCycling( true );
  win->contMaximize( true );
  win->show();
  for(;endmode==-1;) {
    msg=aguix->WaitMessage(win);
    if(msg!=NULL) {
      switch(msg->type) {
        case AG_CLOSEWINDOW:
          if(msg->closewindow.window==win->getWindow()) endmode=1;
          break;
        case AG_BUTTONCLICKED:
          if(msg->button.button==okb) endmode=0;
          else if(msg->button.button==cancelb) endmode=1;
          break;
      }
      aguix->ReplyMessage(msg);
    }
  }
  
  returnstr = NULL;
  if(endmode==0) {
    // ok
    trow = lv->getActiveRow();
    if ( lv->isValidRow( trow ) == true ) {
      i = lv->getData( trow, 0 );
      returnstr = flags[i].flag;
    }
  }
  
  delete win;

  return returnstr;
}

char *CondParser::getOutput( const char *str, FileEntry *tfe )
{
  if ( ( str == NULL ) || ( tfe == NULL ) ) return NULL;
  char *erg = NULL;
  int retval = 0;
  int exeerror = 0;
  ExeClass *ec;
  char *tstr, *tstr2;

  fe = tfe;
  tstr = replaceFlags( str );
  
  if ( tstr != NULL ) {
    ec = new ExeClass();
    tstr2 = NWC::Path::parentDir( fe->fullname, NULL );
    if ( tstr2 != NULL ) {
      ec->cdDir( tstr2 );
      _freesafe( tstr2 );
    }
    ec->addCommand( "%s", tstr );
    if ( ec->getOutputAndRV( &erg, &retval, &exeerror ) != 0 ) {
      erg = NULL;
    }
    delete ec;
    _freesafe( tstr );
  }
  fe = NULL;
  return erg;
}

void CondParser::setIgnoreContent( bool nv )
{
  ignoreContent = nv;
}

bool CondParser::getIgnoreContent() const
{
  return ignoreContent;
}
