/* wpucontext.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2002-2023 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 "wpucontext.h"
#include "scriptop.h"
#include "execlass.h"
#include "nmextlist.hh"
#include "simplelist.hh"
#include "worker.h"
#include "dnd.h"
#include "worker_locale.h"
#include "aguix/aguix.h"
#include "aguix/solidbutton.h"
#include "aguix/button.h"
#include "aguix/awindow.h"
#include "aguix/bevelbox.h"
#include "datei.h"
#include <iostream>
#include "nmexternfe.hh"
#include "nwc_fsentry.hh"
#include "listermode.h"
#include "nwc_path.hh"
#include "virtualdirmode.hh"
#include <cinttypes>

void WPUStack::push( const std::string &elem )
{
    stack.push_back( elem );
}

std::string WPUStack::pop()
{
    if ( stack.empty() ) return "";
    std::string s = stack.back();
    stack.pop_back();
    return s;
}

std::string WPUStack::top() const
{
    if ( stack.empty() ) return "";
    return stack.back();
}

std::string WPUStack::stackEntry( int pos ) const
{
    if ( pos < 0 || (size_t)pos >= stack.size() ) return "";
    return stack.at( pos );
}

bool WPUStack::isEmpty() const
{
    return stack.empty();
}

size_t WPUStack::size() const
{
    return stack.size();
}

/**************
 * WPUContext *
 **************/

struct wpucontext_deftokens {
  const char *str;
  enum WPUContext::ifp_t token;
} keywords[] = {
  { "isEmpty", WPUContext::IFP_ISEMPTY },
  { "size", WPUContext::IFP_SIZE },
  { "lasterror", WPUContext::IFP_LASTERROR },
  { "true", WPUContext::IFP_TRUE },
  { "false", WPUContext::IFP_FALSE },
  { "filelistEmpty", WPUContext::IFP_FILELISTEMPTY },
  { "toNum", WPUContext::IFP_TONUM },
  { "toStr", WPUContext::IFP_TOSTR },
  { "isLocal", WPUContext::IFP_ISLOCAL },
  { NULL, WPUContext::IFP_NONE }
};

//TODO vielleicht auch noch ONLYACTIVE unterstützen
WPUContext::WPUContext( Worker *w, const command_list_t &coms, ActionMessage *msg ) : acoms( coms )
{
  struct wpucontext_deftokens *p;

  pc = 0;

  acoms = coms;

  extlist[0] = NULL;
  extlist[1] = NULL;
  worker = w;

  m_progress_callback.reset( new WPUProgressCallback( worker ) );
  m_filesizes_calculated = false;

  curpos = 0;
  tokenval = -1;
  lastentry = 0;
  lastchar = -1;
  ignore = 0;
  scanbuf = NULL;
  scanbuflen = -1;
  for ( p = keywords; p->token != IFP_NONE; p++ ) {
    ifp_insert( p->str, p->token );
  }
  lasterror = 0;
  
  userwin = NULL;
  cancelb = NULL;
  bb1 = NULL;
  bb2 = NULL;
  prosb = NULL;
  texts = NULL;
  nr_of_text = 0;
  progress = 100;

  activemode = worker->getActiveLister()->getActiveMode();
  nonactivemode = worker->getOtherLister( worker->getActiveLister() )->getActiveMode();
  destmode = NULL;

  m_recursive = false;
  m_take_dirs = false;
  m_use_virtual_temp_copies = false;
  m_use_extended_basename = true;

  if ( msg != NULL ) {
    if ( msg->mode != ActionMessage::AM_MODE_NORMAL ) {
      if ( msg->mode == ActionMessage::AM_MODE_DNDACTION ) {
        const DNDMsg *dndmsg = msg->dndmsg;
        ListerMode *lm1;

        destmode = dndmsg->getDestMode();

        lm1 = dndmsg->getSourceMode();
        if ( lm1 != NULL ) {
            extlist[0] = new NMExtList();
            extlist[0]->createExtList( lm1, msg, false );
            extlist[0]->setTakeDirs( m_take_dirs );
        }
        activemode = lm1;
        nonactivemode = worker->getOtherLister( dndmsg->getSourceLister() )->getActiveMode();
      } else if ( msg->mode == ActionMessage::AM_MODE_SPECIAL ) {
        if ( activemode != NULL ) {
            extlist[0] = new NMExtList();
            extlist[0]->createExtList( activemode, msg, false );
            extlist[0]->setTakeDirs( m_take_dirs );
        }
      } else if ( msg->mode == ActionMessage::AM_MODE_SPECIFIC_ENTRIES ) {
        if ( activemode != NULL ) {
            extlist[0] = new NMExtList();
            extlist[0]->createExtList( activemode, msg, false );
            extlist[0]->setTakeDirs( m_take_dirs );
        }
      } else if ( msg->mode == ActionMessage::AM_MODE_ON_DEMAND ) {
          if ( activemode != NULL ) {
              extlist[0] = new NMExtList();
              extlist[0]->createExtList( activemode, msg, false );
              extlist[0]->setTakeDirs( m_take_dirs );
          }
          if ( nonactivemode != NULL ) {
              extlist[1] = new NMExtList();
              extlist[1]->createExtList( nonactivemode, msg, false );
              extlist[1]->setTakeDirs( m_take_dirs );
          }
      }
    }
  }
}

WPUContext::WPUContext( WPUContext &other,
                        ActionMessage *msg ) :
    WPUContext( msg->getWorker(),
                {},
                msg )
{
    m_stacks = other.getStacks();

    m_recursive = other.m_recursive;
    m_take_dirs = other.m_take_dirs;
    m_use_virtual_temp_copies = other.m_use_virtual_temp_copies;
    m_use_extended_basename = other.m_use_extended_basename;
}


WPUContext::~WPUContext()
{
  closeWin();

  if ( extlist[0] != NULL ) {
    delete extlist[0];
  }
  if ( extlist[1] != NULL ) {
    delete extlist[1];
  }
}

int WPUContext::continueAtLabel( const char *label )
{
  /* find label and set pc to position */
  size_t pos = 0;
  // whithout useful label jump to the end to avoid problems
  if ( label != NULL ) {
    if ( strlen( label ) > 0 ) {
      while ( pos < acoms.size() ) {
          auto &fp = acoms.at( pos );
          if ( strcmp( fp->getName(), "ScriptOp" ) == 0 ) {
              auto sop = std::dynamic_pointer_cast< ScriptOp >( fp );
              if ( sop->isLabel( label ) == true ) {
                  break;
              }
          }
          pos++;
      }
    } else pos = acoms.size();
  } else pos = acoms.size();
  pc = pos;  /* when label was not found, finish whole execution
                because it's a error in list and I don't want to
                do something wrong */
  if ( pc >= acoms.size() ) return 1;
  return 0;
}

void WPUContext::push( int nr, std::string elem )
{
    buildStack( nr );
    (*m_stacks)[ nr ].push( elem );
}

std::string WPUContext::pop( int nr )
{
    buildStack( nr );
    if ( (*m_stacks)[ nr ].size() < 1 ) {
        lasterror = WPU_LASTERROR_STACKEMPTY;
        // we can still call top because it will return ""
    }
    return (*m_stacks)[ nr ].pop();
}

std::string WPUContext::top( int nr )
{
    buildStack( nr );
    if ( (*m_stacks)[ nr ].size() < 1 ) {
        lasterror = WPU_LASTERROR_STACKEMPTY;
        // we can still call top because it will return ""
    }
    return (*m_stacks)[ nr ].top();
}

std::string WPUContext::stackEntry( int nr, int pos )
{
    if ( ! m_stacks || nr >= (int)m_stacks->size() ) {
        lasterror = WPU_LASTERROR_INVALID_STACK;
        return "";
    }

    return (*m_stacks)[ nr ].stackEntry( pos );
}

bool WPUContext::isEmpty( int nr )
{
    buildStack( nr );
    return (*m_stacks)[ nr ].isEmpty();
}

int WPUContext::size( int nr )
{
    buildStack( nr );
    return (int)(*m_stacks)[ nr ].size();
}

int WPUContext::next( std::shared_ptr< WPUContext > wpu, ActionMessage *msg )
{
  AGUIX *aguix = worker->getAGUIX();
  AGMessage *agmsg;
  bool cancel = false;

  if ( pc >= acoms.size() ) return 1;
  auto &fp = acoms.at( pc );
  if ( fp ) {
    fp->run( wpu, msg );
    pc++;
    
    if ( userwin != NULL ) {
      while ( ( agmsg = aguix->GetMessage( userwin ) ) != NULL ) {
        if ( agmsg->type == AG_BUTTONCLICKED ) {
          if ( agmsg->button.button == cancelb )
            cancel = true;
        } else if ( agmsg->type == AG_CLOSEWINDOW ) {
          if ( agmsg->closewindow.window == userwin->getWindow() )
            cancel = true;
        }
        aguix->ReplyMessage( agmsg );
      }
    }
  } else {
    return 1;
  }
  return ( cancel == true ) ? 1 : 0;
}

/*
 * buildStack
 * builds stack up to nr !
 */
void WPUContext::buildStack( int nr )
{
    if ( ! m_stacks ) {
        m_stacks = std::make_shared< std::vector< WPUStack > >();
    }

    if ( nr < (int)m_stacks->size() ) return;

    m_stacks->resize( nr + 1 );
}

int WPUContext::openWin()
{
  AGUIX *aguix;
  int tx, ty, tw;
  
  if ( userwin == NULL ) {
    aguix = worker->getAGUIX();
    userwin = new AWindow( aguix, 10, 10, 100, 50, catalog.getLocale( 499 ), AWindow::AWINDOW_DIALOG );
    userwin->create();
    tx = ty = 5;
    nr_of_text = 1;
    texts = new Text*[nr_of_text];
    texts[0] = (Text*)userwin->add( new Text( aguix, tx, ty, "" ) );
    ty += texts[0]->getHeight() + 5;
    bb1 = (BevelBox*)userwin->add( new BevelBox( aguix, tx, ty, 10, 10, 1 ) );
    prosb = (SolidButton*)userwin->add( new SolidButton( aguix, tx + 1, ty + 1, 10, "", "button-special1-fg", "button-special1-bg", 0 ) );
    bb1->resize( bb1->getWidth(), prosb->getHeight() + 2 );
    ty += bb1->getHeight() + 5;
    bb2 = (BevelBox*)userwin->add( new BevelBox( aguix, tx, ty, 10, 2, 0 ) );
    ty += bb2->getHeight() + 5;
    cancelb = (Button*)userwin->add( new Button( aguix, tx, ty, catalog.getLocale( 8 ), 0 ) );
    ty += cancelb->getHeight() + 5;
    userwin->maximizeX();
    userwin->maximizeY();

    GUIElement *tbutton[1];
    tbutton[0] = cancelb;
    tw = AGUIX::scaleElementsW( userwin->getWidth(), 5, 0, -1, false, false, tbutton, NULL, 1 );
    
    if ( tw > userwin->getWidth() ) {
      userwin->resize( tw, userwin->getHeight() );
    }
    
    tw = userwin->getWidth();
    bb1->resize( tw - 10, bb1->getHeight() );
    setWinProgress( progress );
    bb2->resize( tw - 10, bb2->getHeight() );
    
    userwin->setMinSize( userwin->getWidth(), userwin->getHeight() );
    userwin->setMaxSize( userwin->getWidth(), userwin->getHeight() );
    userwin->show();
  }
  return 0;
}

int WPUContext::closeWin()
{
  if ( userwin != NULL ) {
    delete userwin;
    delete [] texts;
    userwin = NULL;
  }
  return 0;
}

WPUContext::parse_com_str_t WPUContext::parse( const char *str1, std::string &return_str, int maxlen, bool quote, parse_persist_t persist )
{
    parse_com_str_t res = PARSE_ERROR;
  
    if ( worker != NULL ) {
        res = parseComStr( str1, maxlen,
                           quote, return_str);

        if ( extlist[0] != NULL ) {
            if ( extlist[0]->modified() ) {
                if ( persist != PERSIST_NOTHING ) {
                    worker->storePathPersIfInProgress( extlist[0]->getFirstFullname() );
                }

                m_pathdb_filename = extlist[0]->getFirstFullname();
            } else {
                if ( extlist[0]->getFirstFullname().empty() ) {
                    int id = extlist[0]->initEnum();
                    extlist[0]->getFirstElement( id );
                    extlist[0]->closeEnum( id );
                }

                if ( persist == PERSIST_ALL ) {
                    worker->storePathPersIfInProgress( NWC::Path::dirname( extlist[0]->getFirstFullname() ) );
                }

                m_pathdb_filename = extlist[0]->getFirstFullname();
            }
        }
    }
    return res;
}

int WPUContext::ifp_lookup( const char *s )
{
  int p;

  for ( p = lastentry; p > 0; p = p - 1 )
    if ( strcasecmp( ifp_symtable[p].str, s ) == 0 )
      return p;
  return 0;
}

int WPUContext::ifp_insert( const char *s, enum ifp_t tok )
{
  int len;

  len = strlen( s );
  if ( lastentry + 1 >= IFP_SYMTABLE_MAX ) {
    // Symboltabelle voll
    return -1;
  }
  if ( lastchar + len + 1 >= STRMAX ) {
    // Lexemfeld voll
    return -1;
  }
  lastentry = lastentry + 1;
  ifp_symtable[lastentry].token = tok;
  ifp_symtable[lastentry].str = &lexemes[lastchar + 1];
  lastchar = lastchar + len + 1;
  strcpy( ifp_symtable[lastentry].str, s );
  return lastentry;
}

int WPUContext::ifp_lex()
{
  int t;

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

      while ( isalnum( t ) || ( t == '_' ) ) {
	ifp_lexbuf[b] = t;
	t = scanbuf[curpos++];
	b = b + 1;
	if ( ( b + 1 ) >= IFP_BUFSIZE ) {
	  // Pufferueberlauf
          break;
        }
      }
      ifp_lexbuf[b] = '\0';
      if ( t != '\0' ) {
	curpos--;
      }
      p = ifp_lookup( ifp_lexbuf );
      if ( p == 0 ) {
	return IFP_NONE;
      }
      tokenval = p;
      return ifp_symtable[p].token;
    } 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;
	ifp_lexbuf[b] = t;
	b = b + 1;
	if ( ( b + 1 ) >= IFP_BUFSIZE ) {
	  // Pufferueberlauf
          break;
        }
      }
      ifp_lexbuf[b] = '\0';
      return IFP_STRING;
    } else if ( t == '&' ) {
      t = scanbuf[curpos++];
      if ( t == '&' )
	return IFP_P_AND;
      return IFP_NONE;
    } else if ( t == '|' ) {
      t = scanbuf[curpos++];
      if ( t == '|' )
	return IFP_P_OR;
      return IFP_NONE;
    } else if ( t == '=' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return IFP_E;
      return IFP_NONE;
    } else if ( t == '!' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return IFP_NE;
      return IFP_NONE;
    } else if ( t == '<' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return IFP_LE;
      curpos--;
      return IFP_LT;
    } else if ( t == '>' ) {
      t = scanbuf[curpos++];
      if ( t == '=' )
        return IFP_GE;
      curpos--;
      return IFP_GT;
    } else if ( t == '\0' ) {
      return IFP_DONE;
    } else {
      tokenval = IFP_NONE;
      return t;
    }
  }
  return IFP_DONE;
}

int WPUContext::ifp_match( int t )
{
  if ( lookahead == t ) {
    lookahead = ifp_lex();
  } else {
    return 1;
  }
  return 0;
}

char *WPUContext::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 WPUContext::ifp_parse()
{
  struct ifp_erg *e = NULL;
  int erg;
  
  lookahead = ifp_lex();
  if ( lookahead != IFP_DONE ) {
    e = ifp_expr();
  }
  if ( e == NULL ) return -1; /* error */
  erg = e->numval;
  _freesafe( e->strval );
  _freesafe( e );
  if ( lookahead != IFP_DONE ) return -1; /* Not at the end => error */
  return ( erg == 0 ) ? 0 : 1;
}

struct WPUContext::ifp_erg * WPUContext::ifp_expr()
{
  struct ifp_erg *e = NULL, *e2;
  int oldignore;
  int l;
  
  if ( ( e = ifp_factor() ) == NULL ) return NULL;
  while ( 1 ) {
    switch ( lookahead ) {
      case IFP_P_AND:
	if ( ifp_match( IFP_P_AND ) ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

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

	if ( ( e2 = ifp_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 IFP_P_OR:
	if ( ifp_match( IFP_P_OR ) ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

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

	if ( ( e2 = ifp_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 IFP_E:
      case IFP_NE:
      case IFP_LT:
      case IFP_LE:
      case IFP_GT:
      case IFP_GE:
        l = lookahead;
	if ( ifp_match( lookahead ) ) {
          _freesafe( e->strval );
          _freesafe( e );
          return NULL;
        }

	if ( ( e2 = ifp_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 IFP_NE:
              e->numval = ( strcmp( e->strval, e2->strval ) != 0 );
              break;
            case IFP_LT:
              e->numval = ( strcmp( e->strval, e2->strval ) < 0 );
              break;
            case IFP_LE:
              e->numval = ( strcmp( e->strval, e2->strval ) <= 0 );
              break;
            case IFP_GT:
              e->numval = ( strcmp( e->strval, e2->strval ) > 0 );
              break;
            case IFP_GE:
              e->numval = ( strcmp( e->strval, e2->strval ) >= 0 );
              break;
            default:
              e->numval = ( strcmp( e->strval, e2->strval ) == 0 );
              break;
          }
        } else {
          switch ( l ) {
            case IFP_NE:
              e->numval = ( e->numval != e2->numval );
              break;
            case IFP_LT:
              e->numval = ( e->numval < e2->numval );
              break;
            case IFP_LE:
              e->numval = ( e->numval <= e2->numval );
              break;
            case IFP_GT:
              e->numval = ( e->numval > e2->numval );
              break;
            case IFP_GE:
              e->numval = ( e->numval >= e2->numval );
              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;
}

struct WPUContext::ifp_erg *WPUContext::ifp_factor()
{
  struct ifp_erg *e = NULL;
  int tnum;
  char *tstr;
  int exeerror;

  switch ( lookahead ) {
    case '(':
      if ( ifp_match( '(' ) ) return NULL;
      if ( ( e = ifp_expr() ) == NULL ) return NULL;
      if ( ifp_match( ')' ) ) {
        _freesafe( e->strval );
        _freesafe( e );
        return NULL;
      }
      break;
    case '-':
      if ( ifp_match( '-' ) ) return NULL;
      tnum = tokenval;
      if ( ifp_match( IFP_NUM ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      e->numval = tnum * -1;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case IFP_NUM:
      tnum = tokenval;
      if ( ifp_match( IFP_NUM ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      e->numval = tnum;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case IFP_ISEMPTY:
      if ( ifp_match( IFP_ISEMPTY ) ) return NULL;
      if ( ifp_match( '(' ) ) return NULL;
      tnum = tokenval;
      if ( ifp_match( IFP_NUM ) ) return NULL;
      if ( ifp_match( ')' ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      if ( ( tnum < 0 ) || ( ignore == 1 ) ) {
        e->numval = 0;
      } else {
        e->numval = isEmpty( tnum );
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case IFP_FILELISTEMPTY:
      if ( ifp_match( IFP_FILELISTEMPTY ) ) return NULL;
      if ( ifp_match( '(' ) ) return NULL;
      tnum = tokenval;
      if ( ifp_match( IFP_NUM ) ) return NULL;
      if ( ifp_match( ')' ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      if ( ( tnum < 0 ) || ( tnum > 1 ) || ( ignore == 1 ) ) {
        e->numval = 0;
      } else {
        e->numval = filelistEmpty( ( tnum == 0 ) ? false : true );
      }
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case IFP_TRUE:
      if ( ifp_match( IFP_TRUE ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      e->numval = 1;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case IFP_FALSE:
      if ( ifp_match( IFP_FALSE ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      e->numval = 0;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    case IFP_SIZE:
      if ( ifp_match( IFP_SIZE ) ) return NULL;
      if ( ifp_match( '(' ) ) return NULL;
      tnum = tokenval;
      if ( ifp_match( IFP_NUM ) ) return NULL;
      if ( ifp_match( ')' ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      if ( ( tnum < 0 ) || ( ignore == 1 ) ) {
        e->numval = 0;
      } else {
        e->numval = size( tnum );
      }
      e->wasstring = 0;
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      sprintf( e->strval, "%d", e->numval );
      break;
    case IFP_LASTERROR:
      if ( ifp_match( IFP_LASTERROR ) ) return NULL;
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      e->numval = getLastError();
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      resetLastError();
      break;
    case '?':
      tstr = readBrace();
      if ( tstr != NULL ) {
        if ( ignore == 0 ) {
          /*execute tstr */
          std::string res_str;

          if ( parse( tstr, res_str, a_max( EXE_STRING_LEN - 1024, 256 ),
                      true, PERSIST_FLAGS ) == PARSE_SUCCESS ) {
              _freesafe( tstr );
              tstr = dupstring( res_str.c_str() );
          } else {
              _freesafe( tstr );
              tstr = NULL;
          }
  
          if ( tstr != NULL ) {
            e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
            std::unique_ptr<ExeClass> ec( new ExeClass( worker ) );
            ec->cdDir( getBaseDir() );
            ec->addCommand( "%s", tstr );
            e->numval = ec->getReturnCode( &exeerror );
            e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
            e->wasstring = 0;
            sprintf( e->strval, "%d", e->numval );
            
            if ( exeerror != 0 ) {
              if ( worker->getRequester() != NULL ) {
                char *reqstr;
                std::string error, str1;
                
                reqstr = (char*)_allocsafe( strlen( catalog.getLocale( 749 ) ) + strlen( tstr ) + 1 );
                sprintf( reqstr, catalog.getLocale( 749 ), tstr );
                
                ec->readErrorOutput( error, 1024 );
                
                str1 = reqstr;
                str1 += error;
                _freesafe( reqstr );

                RefCount<AFontWidth> lencalc( new AFontWidth( worker->getAGUIX(), NULL ) );
                auto ts = std::make_shared< TextStorageString >( str1, lencalc );

                worker->getRequester()->request( catalog.getLocale( 347 ), ts, catalog.getLocale( 11 ) );
              }
              _freesafe( e->strval );
              _freesafe( e );
              _freesafe( tstr );
              return NULL;
            }
            _freesafe( tstr );
          }
        } else {
          e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
          e->strval = dupstring( "0" );
          e->wasstring = 1;
          e->numval = atoi( e->strval );
          _freesafe( tstr );
        }
      }
      if ( ifp_match( '?' ) ) {
        if ( e != NULL ) {
          _freesafe( e->strval );
          _freesafe( e );
        }
        return NULL;
      }
      break;
    case '$':
      tstr = readBrace();
      if ( tstr != NULL ) {
        if ( ignore == 0 ) {
          std::string res_str;

          if ( parse( tstr, res_str, a_max( EXE_STRING_LEN - 1024, 256 ),
                      true, PERSIST_FLAGS ) == PARSE_SUCCESS ) {
              _freesafe( tstr );
              tstr = dupstring( res_str.c_str() );
          } else {
              _freesafe( tstr );
              tstr = NULL;
          }
  
          if ( tstr != NULL ) {
            e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
            std::unique_ptr<ExeClass> ec( new ExeClass( worker ) );
            ec->cdDir( getBaseDir() );
            ec->addCommand( "%s", tstr );
            e->strval = ec->getOutput( &exeerror );
            e->wasstring = 1;
            if ( e->strval == NULL ) {
              e->strval = dupstring( "" );
            }
            e->numval = atoi( e->strval );

            if ( exeerror != 0 ) {
              if ( worker->getRequester() != NULL ) {
                char *reqstr;
                std::string error, str1;
                
                reqstr = (char*)_allocsafe( strlen( catalog.getLocale( 749 ) ) + strlen( tstr ) + 1 );
                sprintf( reqstr, catalog.getLocale( 749 ), tstr );
                
                ec->readErrorOutput( error, 1024 );
                
                str1 = reqstr;
                str1 += error;
                _freesafe( reqstr );

                RefCount<AFontWidth> lencalc( new AFontWidth( worker->getAGUIX(), NULL ) );
                auto ts = std::make_shared< TextStorageString >( str1, lencalc );

                worker->getRequester()->request( catalog.getLocale( 347 ), ts, catalog.getLocale( 11 ) );
              }
              _freesafe( e->strval );
              _freesafe( e );
              _freesafe( tstr );
              return NULL;
            }
            _freesafe( tstr );
          }
        } else {
          e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
          e->strval = dupstring( "0" );
          e->wasstring = 1;
          e->numval = atoi( e->strval );
          _freesafe( tstr );
        }
      }
      if ( ifp_match( '$' ) ) {
        if ( e != NULL ) {
          _freesafe( e->strval );
          _freesafe( e );
        }
        return NULL;
      }
      break;
    case IFP_STRING:
      e = (struct ifp_erg*)_allocsafe( sizeof( struct ifp_erg ) );
      e->wasstring = 1;
      if ( ignore == 1 ) {
        e->strval = dupstring( "0" );
      } else {
          std::string res_str;

          if ( parse( ifp_lexbuf, res_str, a_max( EXE_STRING_LEN - 1024, 256 ), false, PERSIST_FLAGS ) == PARSE_SUCCESS ) {
              e->strval = dupstring( res_str.c_str() );
          } else {
              e->strval = dupstring( "0" );
          }
      }
      e->numval = atoi( e->strval );
      if ( ifp_match( IFP_STRING ) ) {
        _freesafe( e->strval );
        _freesafe( e );
        return NULL;
      }
      break;
    case IFP_TONUM:
      if ( ifp_match( IFP_TONUM ) ) return NULL;
      if ( ifp_match( '(' ) ) return NULL;
      if ( ( e = ifp_expr() ) == NULL ) return NULL;
      if ( ifp_match( ')' ) ) {
        _freesafe( e->strval );
        _freesafe( 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 IFP_TOSTR:
      if ( ifp_match( IFP_TOSTR ) ) return NULL;
      if ( ifp_match( '(' ) ) return NULL;
      if ( ( e = ifp_expr() ) == NULL ) return NULL;
      if ( ifp_match( ')' ) ) {
        _freesafe( e->strval );
        _freesafe( 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 IFP_ISLOCAL:
      if ( ifp_match( IFP_ISLOCAL ) ) return NULL;
      if ( ifp_match( '(' ) ) return NULL;
      if ( ( e = ifp_expr() ) == NULL ) return NULL;
      if ( ifp_match( ')' ) ) {
        _freesafe( e->strval );
        _freesafe( e );
        return NULL;
      }
      e->numval = worker_islocal( e->strval );
      _freesafe( e->strval );
      e->strval = (char*)_allocsafe( A_BYTESFORNUMBER( e->numval ) );
      e->wasstring = 0;
      sprintf( e->strval, "%d", e->numval );
      break;
    default:
      return NULL;
  }
  return e;
}

int WPUContext::parse_if( const char *str)
{
  int erg;
  
  if ( str == NULL ) return -1;
  scanbuf = str;
  scanbuflen = strlen( str );
  curpos = 0;
  erg = ifp_parse();
  scanbuf = NULL;
  scanbuflen = -1;
  return erg;
}

bool WPUContext::filelistEmpty( bool other )
{
  buildExtList();
  return extlist[ ( other == true ) ? 1 : 0 ]->empty();
}

void WPUContext::buildExtList()
{
  if ( worker != NULL ) {
    if ( extlist[0] == NULL ) {
      extlist[0] = new NMExtList();
      if ( activemode != NULL ) {
          extlist[0]->createExtList( activemode, m_recursive );
      }
      extlist[0]->setTakeDirs( m_take_dirs );
    }
    if ( extlist[1] == NULL ) {
      extlist[1] = new NMExtList();
      if ( nonactivemode != NULL ) {
          extlist[1]->createExtList( nonactivemode, m_recursive );
      }
      extlist[1]->setTakeDirs( m_take_dirs );
    }
  }
}

int WPUContext::getLastError()
{
  return lasterror;
}

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

int WPUContext::getCurrentLine()
{
  return pc;
}

void WPUContext::setRecursive( bool nv )
{
  m_recursive = nv;
}

bool WPUContext::getRecursive() const
{
  return m_recursive;
}

void WPUContext::setWinProgress( int nv )
{
  progress = nv;
  char buf[ A_BYTESFORNUMBER( progress ) + 2 + 1 ];
  if ( ( progress < 0 ) || ( progress > 100 ) ) progress = 0;
  if ( userwin != NULL ) {
    prosb->resize( ( bb1->getWidth() - 2 ) * progress / 100, prosb->getHeight() );
    sprintf( buf, "%d %%", progress );
    prosb->setText( buf );
  }
}

void WPUContext::setWinText( const char *str )
{
  AGUIX *aguix = worker->getAGUIX();
  Text **ntexts;
  int i, lines;
  char **liness;
  int ty, tw;
  
  if ( userwin == NULL ) return;

  if ( str == NULL ) {
    ntexts = new Text*[1];
    ntexts[0] = texts[0];
    for ( i = 1; i < nr_of_text; i++ ) {
      userwin->remove( texts[i] );
      delete texts[i];
    }
    delete [] texts;
    texts = ntexts;
    nr_of_text = 1;
    texts[0]->setText( "" );
    ty = texts[0]->getY() + texts[0]->getHeight() + 5;
  } else {
    lines = createLines( str, &liness );
    ntexts = new Text*[lines];
    if ( lines >= nr_of_text ) {
      ty = 5;
      for ( i = 0; i < nr_of_text; i++ ) {
        ntexts[i] = texts[i];
        ntexts[i]->setText( liness[i] );
        ty += ntexts[i]->getHeight() + 5;
      }
      for ( i = nr_of_text; i < lines; i++ ) {
        ntexts[i] = (Text*)userwin->add( new Text( aguix, 5, ty, liness[i] ) );
        ty += ntexts[i]->getHeight() + 5;
      }
    } else {
      ty = 5;
      for ( i = 0; i < lines; i++ ) {
        ntexts[i] = texts[i];
        ntexts[i]->setText( liness[i] );
        ty += ntexts[i]->getHeight() + 5;
      }
      for ( i = lines; i < nr_of_text; i++ ) {
        userwin->remove( texts[i] );
        delete texts[i];
      }
    }
    delete [] texts;
    texts = ntexts;
    nr_of_text = lines;
    for ( i = 0; i < lines; i++ )
      _freesafe( liness[i] );
    _freesafe( liness );
  }
  bb1->move( bb1->getX(), ty );
  prosb->move( prosb->getX(), bb1->getY() + 1 );
  ty += bb1->getHeight() + 5;
  bb2->move( bb2->getX(), ty );
  ty += bb2->getHeight() + 5;
  cancelb->move( cancelb->getX(), ty );
  ty += cancelb->getHeight() + 5;
  userwin->maximizeX();
  userwin->resize( userwin->getWidth(), ty );
  
  tw = userwin->getWidth();
  bb1->resize( tw - 10, bb1->getHeight() );
  i = ( tw - 10 - 2 ) * progress / 100;
  if ( i < 2 ) i = 2;
  prosb->resize( i, prosb->getHeight() );
  bb2->resize( tw - 10, bb2->getHeight() );

  GUIElement *tbutton[1];
  tbutton[0] = cancelb;
  AGUIX::scaleElementsW( userwin->getWidth(), 5, 0, -1, false, false, tbutton, NULL, 1 );
  userwin->setMinSize( userwin->getWidth(), userwin->getHeight() );
  userwin->setMaxSize( userwin->getWidth(), userwin->getHeight() );
}

const char *WPUContext::getBaseDir()
{
    if ( worker != NULL && activemode != NULL && m_base_dir.empty() ) {
        m_base_dir = activemode->getCurrentDirectory();
    }

    return m_base_dir.c_str();
}

bool WPUContext::filelistModified( bool other, bool reset )
{
    buildExtList();

    bool m = extlist[ ( other == true ) ? 1 : 0 ]->modified();

    if ( reset ) {
        extlist[ ( other == true ) ? 1 : 0 ]->resetModifedFlag();
    }

    return m;
}

void WPUContext::setTakeDirs( bool nv )
{
  m_take_dirs = nv;

  for ( int i = 0; i < 2; i++ ) {
    if ( extlist[i] != NULL ) {
      extlist[i]->setTakeDirs( nv );
    }
  }
}

bool WPUContext::getTakeDirs() const
{
    return m_take_dirs;
}

void WPUContext::setUseVirtualTempCopies( bool nv )
{
    m_use_virtual_temp_copies = nv;
}

bool WPUContext::getUseVirtualTempCopies() const
{
    return m_use_virtual_temp_copies;
}

void WPUContext::setUseExtendedBasename( bool nv )
{
    m_use_extended_basename = nv;
}

bool WPUContext::getUseExtendedBasename() const
{
    return m_use_extended_basename;
}

std::string WPUContext::getTempName4File( const char *filename )
{
  if ( filename == NULL ) return "";

  calculateFilesizes();

  TemporaryFileCache::TemporaryFileCacheEntry e = worker->getTempCopy( filename, m_progress_callback.get() );

  if ( ! e.isValid() ) return "";

  m_temp_files.push_back( e );

  return e.getTempName();
}

WPUProgressCallback::WPUProgressCallback( Worker *w ) : m_worker( w ), m_complete_sizes( 0 ), m_bytes_already_copied( 0 )
{
    m_last_update = time( NULL );
}

WPUProgressCallback::~WPUProgressCallback()
{
    if ( m_worker ) {
        m_worker->removeStatebarText( m_text );
    }
}

CopyfileProgressCallback::callback_return_t WPUProgressCallback::progress_callback( loff_t current_bytes,
                                                                                    loff_t total_bytes )
{
    time_t now = time( NULL );

    if ( m_worker ) {
        if ( now != m_last_update ||
             total_bytes == 0 ||
             current_bytes == total_bytes ) {

            m_worker->removeStatebarText( m_text );

            if ( total_bytes != 0 && current_bytes < total_bytes ) {
                if ( m_complete_sizes > 0 && m_complete_sizes > total_bytes ) {
                    float v = current_bytes;
                    v += m_bytes_already_copied;
                    v *= 100.0;
                    v /= m_complete_sizes;

                    std::string s = AGUIXUtils::formatStringToString( catalog.getLocale( 969 ),
                                                                      (float)current_bytes * 100.0 / total_bytes,
                                                                      v );
                    m_text = TimedText( s, now, now + 5 );
                } else {
                    std::string s = AGUIXUtils::formatStringToString( catalog.getLocale( 968 ),
                                                                      (float)current_bytes * 100.0 / total_bytes );
                    m_text = TimedText( s, now, now + 5 );
                }

                m_worker->pushStatebarText( m_text );

                m_last_update = now;
            }
        }
    }

    if ( current_bytes == total_bytes ) {
        m_bytes_already_copied += total_bytes;
    }

    return COPYFILE_CONTINUE;
}

void WPUProgressCallback::setCompleteFilesizes( loff_t sum )
{
    m_complete_sizes = sum;
}

void WPUContext::calculateFilesizes()
{
    if ( m_filesizes_calculated ) return;

    loff_t sum = 0;

    buildExtList();

    for ( int i = 0; i < 2; i++ ) {
        if ( extlist[i] ) {
            int id = extlist[i]->initEnum();
            NM_extern_fe *efe = extlist[i]->getFirstElement( id );
            while ( efe != NULL ) {
                if ( efe->getFullname( false ) ) {
                    NWC::FSEntry f( efe->getFullname( false ) );
                    
                    if ( f.isReg( true ) ) {
                        loff_t filesize = 0;
                        if ( f.isLink() == true ) {
                            filesize = f.stat_dest_size();
                        } else {
                            filesize = f.stat_size();
                        }

                        sum += filesize;
                    }
                }

                efe = extlist[i]->getNextElement( id );
            }
            extlist[i]->closeEnum( id );
        }
    }

    m_filesizes_calculated = true;

    if ( m_progress_callback.get() ) {
        m_progress_callback->setCompleteFilesizes( sum );
    }
}

class NM_CLI_String
{
public:
    NM_CLI_String( const size_t maxlen, const bool quote ) : m_maxlen( maxlen ),
                                                             m_quote( quote ),
                                                             m_error_occurred( false )
    {}

    bool add_str( const std::string str1, const bool force_noquoting )
    {
        return add_str( str1.c_str(), force_noquoting );
    }

    bool add_str( const char *str1, const bool force_noquoting )
    {
        if ( ! str1 ) return false;

        if ( m_quote == true && force_noquoting == false ) {

            char *newdstr = AGUIX_catTrustedAndUnTrusted( m_current_str.c_str(), str1 );

            if ( ! newdstr ) {
                m_error_occurred = true;
                return false;
            }

            if ( strlen( newdstr ) >= m_maxlen ) {
                _freesafe( newdstr );
                m_error_occurred = true;
                return false;
            }

            m_current_str = newdstr;
            m_last_valid_str = m_current_str;

            _freesafe( newdstr );
        } else {
            size_t tlen = strlen( str1 );

            if ( m_current_str.size() + tlen >= m_maxlen ) {
                m_error_occurred = true;
                return false;
            }

            m_current_str += str1;
            m_last_valid_str = m_current_str;
        }

        return true;
    }

    bool add_char( const char ch )
    {
        if ( m_current_str.size() + 1 >= m_maxlen ) {
            m_error_occurred = true;
            return false;
        }

        m_current_str.push_back( ch );

        return true;
    }

    std::string get_last_valid_string() const
    {
        if ( ! m_error_occurred ) return m_current_str;

        return m_last_valid_str;
    }

    bool is_error_occurred() const
    {
        return m_error_occurred;
    }

    size_t get_current_length() const
    {
        return m_current_str.size();
    }
private:
    std::string m_current_str;
    std::string m_last_valid_str;
    const size_t m_maxlen;
    const bool m_quote;
    bool m_error_occurred;
};

WPUContext::parse_com_str_t WPUContext::parseComStr( const char *sstr,
                                                     int maxlen, bool quote,
                                                     std::string &return_str )
{
    char ch;
    int spos = 0, bracketcount = 0, bufpos = 0;
    int mode = 0;
    bool ende;
    char *tstr;
    int id;
    bool cancel = false;
    int num;
    std::string str1;
    char *flagbuf;
    int flagbuf_size;
    NM_CLI_String res_cli_str( maxlen, quote );
    const bool use_temp_copies_option = m_use_virtual_temp_copies;
    const bool use_extended_basenames = m_use_extended_basename;

    flagbuf_size = strlen( sstr );
    flagbuf = (char*)_allocsafe( flagbuf_size + 1 );
    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:
                if ( ! res_cli_str.add_char( ch ) ) {
                    ende = true;
                }
                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
                    if ( ! res_cli_str.add_char( ch ) ) {
                        ende = true;
                    }
                } else {
                    mode = 1;
                    bracketcount++;
                    bufpos = 0;
                }
                break;
        }
        if ( mode == 3 ) {
            const char *buf1 = flagbuf;
            bool force_noquoting = false;
            bool force_notempfiles = false;

            while ( true ) {
                if ( buf1[0] == '-' ) {
                    force_noquoting = true;
                    buf1++;
                } else if ( buf1[0] == '*' ) {
                    force_notempfiles = true;
                    buf1++;
                } else {
                    break;
                }
            }
            // parse buf1
            if ( strncmp( buf1, "Rs", 2 ) == 0 ) {
                // Stringrequest
                tstr = StringRequest( buf1, maxlen - res_cli_str.get_current_length() );
                if ( tstr != NULL ) {
                    if ( ! res_cli_str.add_str( tstr, force_noquoting ) ) {
                        ende = true;
                    }
                    _freesafe( tstr );
                } else {
                    cancel = true;
                    ende = true;
                }
            } else if ( strncmp( buf1, "scripts", 7 ) == 0 ) {
                // dir of the scripts
                str1 = Worker::getDataDir();
                str1 += "/scripts";
                if ( ! res_cli_str.add_str( str1, force_noquoting ) ) {
                    ende = true;
                }
            } else if ( strncmp( buf1, "pop", 3 ) == 0 ) {
                if ( strlen( buf1 ) > 3 ) {
                    num = atoi( buf1 + 3 );
                    if ( num < 0 ) num = 0;
                } else {
                    num = 0;
                }
                if ( size( num ) > 0 ) {
                    str1 = pop( num );
                    if ( ! res_cli_str.add_str( str1, force_noquoting ) ) {
                        ende = true;
                    }
                }
            } else if ( strncmp( buf1, "top", 3 ) == 0 ) {
                if ( strlen( buf1 ) > 3 ) {
                    num = atoi( buf1 + 3 );
                    if ( num < 0 ) num = 0;
                } else {
                    num = 0;
                }
                if ( size( num ) > 0 ) {
                    str1 = top( num );
                    if ( ! res_cli_str.add_str( str1, force_noquoting ) ) {
                        ende = true;
                    }
                }
            } else if ( strncmp( buf1, "size", 4 ) == 0 ) {
                if ( strlen( buf1 ) > 4 ) {
                    num = atoi( buf1 + 4 );
                    if ( num < 0 ) num = 0;
                } else {
                    num = 0;
                }

                char buf2[ A_BYTESFORNUMBER( int ) + 2 ];

                sprintf( buf2, "%d", size( num ) );
                str1 = buf2;
                if ( ! res_cli_str.add_str( str1, force_noquoting ) ) {
                    ende = true;
                }
            } else if ( strncmp( buf1, "p", 1 ) == 0 ) {
                // path
                if ( activemode ) {
                    std::string cwd = activemode->getCurrentDirectory();

                    if ( ! cwd.empty() ) {
                        if ( ! res_cli_str.add_str( cwd, force_noquoting ) ) {
                            ende = true;
                        }

                        Worker::pushCommandLog( catalog.getLocale( 1416 ),
                                                cwd.c_str(),
                                                "",
                                                cwd.c_str() );
                    }
                }
            } else if ( strncmp( buf1, "op", 2 ) == 0 ) {
                // other side
                if ( nonactivemode ) {
                    std::string cwd = nonactivemode->getCurrentDirectory();

                    if ( ! cwd.empty() ) {
                        if ( ! res_cli_str.add_str( cwd, force_noquoting ) ) {
                            ende = true;
                        }

                        Worker::pushCommandLog( catalog.getLocale( 1416 ),
                                                cwd.c_str(),
                                                "",
                                                cwd.c_str() );
                    }
                }
            } else if ( strncmp( buf1, "lp", 2 ) == 0 ) {
                // left side

                ListerMode *lm;

                lm = worker->getLister( 0 )->getActiveMode();

                if ( lm ) {
                    std::string cwd = lm->getCurrentDirectory();

                    if ( ! cwd.empty() ) {
                        if ( ! res_cli_str.add_str( cwd, force_noquoting ) ) {
                            ende = true;
                        }

                        Worker::pushCommandLog( catalog.getLocale( 1416 ),
                                                cwd.c_str(),
                                                "",
                                                cwd.c_str() );
                    }
                }
            } else if ( strncmp( buf1, "rp", 2 ) == 0 ) {
                // right side

                ListerMode *lm;

                lm = worker->getLister( 1 )->getActiveMode();

                if ( lm ) {
                    std::string cwd = lm->getCurrentDirectory();

                    if ( ! cwd.empty() ) {
                        if ( ! res_cli_str.add_str( cwd, force_noquoting ) ) {
                            ende = true;
                        }

                        Worker::pushCommandLog( catalog.getLocale( 1416 ),
                                                cwd.c_str(),
                                                "",
                                                cwd.c_str() );
                    }
                }
            } else if ( strncmp( buf1, "dp", 2 ) == 0 ) {
                // destination path
                if ( destmode ) {
                    std::string cwd = destmode->getCurrentDirectory();

                    if ( ! cwd.empty() ) {
                        if ( ! res_cli_str.add_str( cwd, force_noquoting ) ) {
                            ende = true;
                        }

                        Worker::pushCommandLog( catalog.getLocale( 1416 ),
                                                cwd.c_str(),
                                                "",
                                                cwd.c_str() );
                    }
                }
            } else if ( strncmp( buf1, "v", 1 ) == 0 ) {
                // env variable
                tstr = getenv( buf1 + 1 );
                if ( tstr != NULL ) {
                    if ( ! res_cli_str.add_str( tstr, force_noquoting ) ) {
                        ende = true;
                    }
                }
            } else if ( strncmp( buf1, "random", 6 ) == 0 ) {
                long r = lrand48();
                std::string s = AGUIXUtils::formatStringToString( "%" PRIu32, (uint32_t)r );
                if ( ! res_cli_str.add_str( s, force_noquoting ) ) {
                    ende = true;
                }
            } else if ( strncmp( buf1, "currenttime", 11 ) == 0 ) {
                time_t now = time( NULL );
                std::string s = AGUIXUtils::formatStringToString( "%" PRIu64, (uint64_t)now );
                if ( ! res_cli_str.add_str( s, force_noquoting ) ) {
                    ende = true;
                }
            } else {
                // file flag or unknown
                bool otherside, nounselect, noext;
                int tpos;

                enum parse_mode {
                    FLAG_PARSE_START = 0,
                    FLAG_PARSE_SIDE_DONE = 1,
                    FLAG_PARSE_SELECT_DONE = 2,
                    FLAG_PARSE_FILE_DONE = 3,
                    FLAG_PARSE_TIME = 4,
                    FLAG_PARSE_DONE = 5,
                    FLAG_PARSE_INVALID = 6
                } tmode;

                enum file_mode {
                    FILE_MODE_UNSET = -1,
                    FILE_MODE_FIRST_BASENAME = 0,
                    FILE_MODE_ALL_BASENAME = 1,
                    FILE_MODE_FIRST_FULLNAME = 2,
                    FILE_MODE_ALL_FULLNAME = 3,
                    FILE_MODE_TEMPNAME = 4,
                    FILE_MODE_EXTENSION = 5,
                    FILE_MODE_SIZE = 6,
                    FILE_MODE_MTIME = 7,
                    FILE_MODE_CTIME = 8
                } filemode;

                filemode = FILE_MODE_UNSET;
                otherside = false;
                nounselect = false;
                noext = false;
                tpos = 0;
                tmode = FLAG_PARSE_START;
                for( ; tmode < FLAG_PARSE_DONE && tpos < 4; ) {
                    ch = buf1[ tpos++ ];
                    if ( ch == 'o' ) {
                        if ( tmode == FLAG_PARSE_START ) {
                            otherside = true;
                            tmode = FLAG_PARSE_SIDE_DONE;
                        } else {
                            // an "o" at the wrong position
                            tmode = FLAG_PARSE_INVALID;
                        }
                    } else if ( ch == 'u' ) {
                        if ( tmode < FLAG_PARSE_SELECT_DONE ) {
                            nounselect = true;
                            tmode = FLAG_PARSE_SELECT_DONE;
                        } else {
                            tmode = FLAG_PARSE_INVALID;
                        }
                    } else if ( ch == 'f' && tmode < FLAG_PARSE_FILE_DONE ) {
                        filemode = FILE_MODE_FIRST_BASENAME;
                        tmode = FLAG_PARSE_FILE_DONE;
                    } else if ( ch == 'a' && tmode < FLAG_PARSE_FILE_DONE ) {
                        filemode = FILE_MODE_ALL_BASENAME;
                        tmode = FLAG_PARSE_FILE_DONE;
                    } else if ( ch == 'F' && tmode < FLAG_PARSE_FILE_DONE ) {
                        filemode = FILE_MODE_FIRST_FULLNAME;
                        tmode = FLAG_PARSE_FILE_DONE;
                    } else if ( ch == 'A' && tmode < FLAG_PARSE_FILE_DONE ) {
                        filemode = FILE_MODE_ALL_FULLNAME;
                        tmode = FLAG_PARSE_FILE_DONE;
                    } else if ( ch == 'E' && tmode == FLAG_PARSE_FILE_DONE ) {
                        noext = true;
                        tmode = FLAG_PARSE_DONE;
                    } else if ( ch == 'E' && tmode < FLAG_PARSE_FILE_DONE ) {
                        // force no unselect for now
                        nounselect = true;
                        filemode = FILE_MODE_EXTENSION;
                        tmode = FLAG_PARSE_DONE;
                    } else if ( ch == 's' && tmode < FLAG_PARSE_FILE_DONE ) {
                        // force no unselect for now
                        nounselect = true;
                        filemode = FILE_MODE_SIZE;
                        tmode = FLAG_PARSE_DONE;
                    } else if ( ch == 'T' && tmode <= FLAG_PARSE_FILE_DONE ) {
                        tmode = FLAG_PARSE_TIME;
                    } else if ( ch == 'm' && tmode == FLAG_PARSE_TIME ) {
                        // force no unselect for now
                        nounselect = true;
                        filemode = FILE_MODE_MTIME;
                        tmode = FLAG_PARSE_DONE;
                    } else if ( ch == 'c' && tmode == FLAG_PARSE_TIME ) {
                        // force no unselect for now
                        nounselect = true;
                        filemode = FILE_MODE_CTIME;
                        tmode = FLAG_PARSE_DONE;
                    } else if ( ch == 't' && tmode < FLAG_PARSE_FILE_DONE ) {
                        filemode = FILE_MODE_TEMPNAME;
                        tmode = FLAG_PARSE_DONE;  // no E allowed so directly change to mode 4
                    } else if ( ch == '\0' ) {
                        tmode = FLAG_PARSE_DONE;
                    } else {
                        filemode = FILE_MODE_UNSET;
                        tmode = FLAG_PARSE_INVALID;
                    }
                }
                if ( filemode >= FILE_MODE_FIRST_BASENAME && filemode <= FILE_MODE_CTIME ) {
                    // we have a valid flag
                    bool takefirst = true;
                    NM_extern_fe *efe1;
                    NMExtList *l;
                    bool efeskip;

                    buildExtList();
                    
                    if ( filemode == FILE_MODE_ALL_BASENAME || filemode == FILE_MODE_ALL_FULLNAME ) takefirst = false;
                    if ( otherside == true ) l = extlist[1];
                    else l = extlist[0];
                    id = l->initEnum();
                    efe1 = (NM_extern_fe*)l->getFirstElement( id );
                    for ( int c = 0;; ) {
                        if ( efe1 == NULL ) break;
              
                        efeskip = false;
                        if ( ( efe1->getDirFinished() != 0 ) &&
                             ( m_take_dirs == false ) ) {
                            efeskip = true;
                        } else {
                            std::string name_string;
                            const char *name_cstring = NULL;

                            // this is a valid entry
                            if ( use_temp_copies_option == true &&
                                 force_notempfiles == false &&
                                 worker_islocal( efe1->getFullname( false ) ) == 0 ) {

                                name_string = getTempName4File( efe1->getFullname( false ) );
                                if ( ! name_string.empty() ) {
                                    name_cstring = name_string.c_str();
                                }
                            } else {
                                if ( filemode == FILE_MODE_TEMPNAME ) {
                                    name_string = getTempName4File( efe1->getFullname( false ) );
                                    if ( ! name_string.empty() ) {
                                        name_cstring = name_string.c_str();
                                    }
                                } else if ( filemode == FILE_MODE_EXTENSION ) {
                                    auto extension = efe1->getExtension();

                                    if ( extension ) {
                                        name_string = *extension;
                                    } else {
                                        name_string.clear();
                                    }
                                    name_cstring = name_string.c_str();
                                } else if ( filemode == FILE_MODE_SIZE ) {
                                    loff_t s = 0;
                                    const FileEntry *fe = efe1->getFE();

                                    if ( fe ) {
                                        if ( fe->isLink && ! fe->isCorrupt ) {
                                            s = fe->dsize();
                                        } else {
                                            s = fe->size();
                                        }
                                    }

                                    name_string = AGUIXUtils::formatStringToString( "%" PRIu64, (uint64_t)s );
                                    if ( ! name_string.empty() ) {
                                        name_cstring = name_string.c_str();
                                    }
                                } else if ( filemode == FILE_MODE_MTIME ) {
                                    time_t t = 0;
                                    const FileEntry *fe = efe1->getFE();

                                    if ( fe ) {
                                        if ( fe->isLink && ! fe->isCorrupt ) {
                                            t = fe->dlastmod();
                                        } else {
                                            t = fe->lastmod();
                                        }
                                    }

                                    name_string = AGUIXUtils::formatStringToString( "%" PRIu64, (uint64_t)t );
                                    if ( ! name_string.empty() ) {
                                        name_cstring = name_string.c_str();
                                    }
                                } else if ( filemode == FILE_MODE_CTIME ) {
                                    time_t t = 0;
                                    const FileEntry *fe = efe1->getFE();

                                    if ( fe ) {
                                        if ( fe->isLink && ! fe->isCorrupt ) {
                                            t = fe->dlastchange();
                                        } else {
                                            t = fe->lastchange();
                                        }
                                    }

                                    name_string = AGUIXUtils::formatStringToString( "%" PRIu64, (uint64_t)t );
                                    if ( ! name_string.empty() ) {
                                        name_cstring = name_string.c_str();
                                    }
                                } else if ( filemode == FILE_MODE_FIRST_BASENAME ||
                                            filemode == FILE_MODE_ALL_BASENAME ) {
                                    std::string cwd;

                                    if ( use_extended_basenames ) {
                                        // do it only for virtualdirmode at the moment
                                        // even though it works for normalmode as well
                                        if ( ! otherside && activemode ) {
                                            if ( dynamic_cast< VirtualDirMode *>( activemode ) ) { 
                                                cwd = activemode->getCurrentDirectory();
                                            }
                                        } else if ( otherside && nonactivemode ) {
                                            if ( dynamic_cast< VirtualDirMode *>( nonactivemode ) ) { 
                                                cwd = nonactivemode->getCurrentDirectory();
                                            }
                                        }
                                    }

                                    name_cstring = efe1->getName( noext );

                                    if ( ! cwd.empty() ) {
                                        name_string = NWC::Path::get_extended_basename( cwd,
                                                                                        efe1->getFullname( noext ) );
                                        if ( ! name_string.empty() ) {
                                            name_cstring = name_string.c_str();
                                        }
                                    }
                                } else {
                                    name_cstring = efe1->getFullname( noext );
                                }
                            }

                            if ( name_cstring != NULL ) {
                                if ( c > 0 ) {
                                    if ( ! res_cli_str.add_char( ' ' ) ) {
                                        ende = true;
                                    }
                                }
                                if ( ende == false && ! res_cli_str.add_str( name_cstring, force_noquoting ) ) {
                                    ende = true;
                                }

                                Worker::pushCommandLog( catalog.getLocale( 1416 ),
                                                        efe1->getFullname( false ),
                                                        "",
                                                        efe1->getFullname( false ) );
                            }               
                            if ( ende == true ) break;
                        }
                        for (;;) {
                            if ( nounselect == false ) {
                                // remove efe1;
                                l->removeElement( efe1 );
                                // now deactivate LVC if not NULL
                                if ( efe1->getFE() != NULL ) {
                                    if ( otherside == false ) {
                                        // in my list
                                        activemode->setEntrySelectionState( efe1->getFullname( false ), false );
                                    } else {
                                        if ( nonactivemode ) {
                                            nonactivemode->setEntrySelectionState( efe1->getFullname( false ), false );
                                        }
                                    }
                                }
                                delete efe1;
                                if ( ! takefirst ) {
                                    efe1 = (NM_extern_fe*)l->getFirstElement( id );
                                } else {
                                    efe1 = NULL;
                                }
                            } else {
                                if ( ! takefirst ) {
                                    efe1 = (NM_extern_fe*)l->getNextElement( id );
                                } else {
                                    efe1 = NULL;
                                }
                            }
                            if ( efe1 == NULL ) break;
                            if ( ( efe1->getDirFinished() == 0 ) ||
                                 ( m_take_dirs == true ) ) {
                                break;
                            }
                        }

                        if ( efeskip == true ) continue;
                        if ( c == 0 && takefirst == true ) break;
                        c = 1;
                    }
                    l->closeEnum( id );
                }
            }
            mode = 0;
        }
    }
    _freesafe( flagbuf );

    if ( cancel == true ) {
        return PARSE_ERROR;
    }

    if ( res_cli_str.is_error_occurred() ) {
        RefCount<AFontWidth> lencalc( new AFontWidth( Worker::getAGUIX(), NULL ) );

        std::string message = catalog.getLocale( 938 );
        message += sstr;

        auto ts = std::make_shared< TextStorageString >( message, lencalc );

        std::string buttons = catalog.getLocale( 629 );
        buttons += "|";
        buttons += catalog.getLocale( 8 );

        int erg = worker->getRequester()->request( catalog.getLocale( 347 ), ts, buttons.c_str() );

        if ( erg == 1 ) {
            return PARSE_ERROR;
        }
    }

    return_str = res_cli_str.get_last_valid_string();
    return PARSE_SUCCESS;
}

char *WPUContext::StringRequest( const char *sstr, int maxlen )
{
    char *istr=NULL,ch;
    char *defstr=NULL;
    char *ristr=NULL,*rdefstr=NULL;
    int spos=0,bracketcount=0;
    int istart,iend,defstart,defend;
    int mode;
    char *buttonstr,*textstr,*return_str;
    int erg;
  
    spos=2; // buf1 starts with Rs
    istart=iend=defstart=defend=-1;
    mode=0;
    for(;mode<4;) {
        ch=sstr[spos++];
        if(ch=='\0') break;
        switch(mode) {
            case 0:
                // we are in no bracket
                if(ch=='{') {
                    mode=1;
                    bracketcount++;
                    istart=spos;
                }
                break;
            case 1:
                // in the infotext
                if(ch=='{') bracketcount++;
                else if(ch=='}') {
                    if(bracketcount==1) {
                        iend=spos-2;
                        mode=2;
                    }
                    bracketcount--;
                }
                break;
            case 2:
                // we are in no bracket
                if(ch=='{') {
                    mode=3;
                    bracketcount++;
                    defstart=spos;
                }
                break;
            case 3:
                // in the defaulttext
                if(ch=='{') bracketcount++;
                else if(ch=='}') {
                    if(bracketcount==1) {
                        defend=spos-2;
                        mode=4;
                    }
                    bracketcount--;
                }
                break;
            default:
                break;
        }
    }
    if((istart>=0)&&(iend>=istart)) {
        // we have an infotext
        istr=dupstring(&sstr[istart]);
        istr[iend-istart+1]=0;

        std::string res_str;

        if ( parseComStr( istr, maxlen, false, res_str ) == PARSE_SUCCESS ) {
            ristr = dupstring( res_str.c_str() );
        }
        _freesafe(istr);
    }
    if((defstart>=0)&&(defend>=istart)) {
        // we have a defaulttext
        defstr=dupstring(&sstr[defstart]);
        defstr[defend-defstart+1]=0;

        std::string res_str;

        if ( parseComStr( defstr, maxlen, false, res_str ) == PARSE_SUCCESS ) {
            rdefstr = dupstring( res_str.c_str() );
        }
        _freesafe(defstr);
    }
  
    textstr=NULL;
    if(ristr!=NULL) {
        if(strlen(ristr)>0) textstr=ristr;
        else _freesafe(ristr);
    }
    if(textstr==NULL) textstr=dupstring(catalog.getLocale(200));
    buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
                                strlen(catalog.getLocale(8))+1);
    sprintf(buttonstr,"%s|%s",catalog.getLocale(11),
            catalog.getLocale(8));
    erg = worker->getRequester()->string_request( catalog.getLocale( 123 ),
                                                  textstr,
                                                  ( rdefstr != NULL ) ? rdefstr : "",
                                                  buttonstr,
                                                  &return_str );
    _freesafe(buttonstr);
    _freesafe(textstr);
    if(rdefstr!=NULL) _freesafe(rdefstr);
    if(erg!=0) {
        _freesafe(return_str);
        return NULL;
    }
    return return_str;
}

const std::string &WPUContext::getPathDBFilename() const
{
    return m_pathdb_filename;
}

std::shared_ptr< std::vector< WPUStack > > WPUContext::getStacks()
{
    return m_stacks;
}
