Blame src/CmdExec.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <stddef.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include "CmdExec.h"
Packit 8f70b4
#include "xstring.h"
Packit 8f70b4
#include "SignalHook.h"
Packit 8f70b4
#include "alias.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "ResMgr.h"
Packit 8f70b4
#include "module.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "QueueFeeder.h"
Packit 8f70b4
#include "LocalDir.h"
Packit 8f70b4
#include "ConnectionSlot.h"
Packit 8f70b4
#include "DummyProto.h"
Packit 8f70b4
Packit 8f70b4
#define RL_PROMPT_START_IGNORE	'\001'
Packit 8f70b4
#define RL_PROMPT_END_IGNORE	'\002'
Packit 8f70b4
Packit 8f70b4
#define super SessionJob
Packit 8f70b4
#define waiting_num waiting.count()
Packit 8f70b4
Packit 8f70b4
static ResType lftp_cmd_vars[] = {
Packit 8f70b4
   {"cmd:default-protocol",	 "ftp",	  0,0},
Packit 8f70b4
   {"cmd:long-running",		 "30",	  ResMgr::UNumberValidate,0},
Packit 8f70b4
   {"cmd:remote-completion",	 "on",	  ResMgr::BoolValidate,0},
Packit 8f70b4
   {"cmd:prompt",		 "lftp \\S\\? \\u\\@\\h:\\w> ",0,0},
Packit 8f70b4
   {"cmd:default-title",	 "lftp \\h:\\w",0,0},
Packit 8f70b4
   {"cmd:ls-default",		 "",	  0,0},
Packit 8f70b4
   {"cmd:csh-history",		 "off",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:verify-path",		 "yes",	  ResMgr::BoolValidate,0},
Packit 8f70b4
   {"cmd:verify-path-cached",	 "no",	  ResMgr::BoolValidate,0},
Packit 8f70b4
   {"cmd:verify-host",		 "yes",	  ResMgr::BoolValidate,0},
Packit 8f70b4
   {"cmd:at-exit",		 "",	  0,0},
Packit 8f70b4
   {"cmd:at-exit-bg",		 "",	  0,0},
Packit 8f70b4
   {"cmd:at-exit-fg",		 "",	  0,0},
Packit 8f70b4
   {"cmd:at-background",	 "",	  0,0},
Packit 8f70b4
   {"cmd:at-terminate",		 "",	  0,0},
Packit 8f70b4
   {"cmd:at-finish",		 "",	  0,0},
Packit 8f70b4
   {"cmd:at-queue-finish",	 "",	  0,0},
Packit 8f70b4
   {"cmd:fail-exit",		 "no",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:verbose",		 "no",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:interactive",		 "auto",  ResMgr::TriBoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:show-status",		 "yes",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:move-background",	 "yes",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:move-background-detach","yes",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:set-term-status",	 "no",	  ResMgr::BoolValidate,0},
Packit 8f70b4
   {"cmd:term-status",		 "",	  0, 0},
Packit 8f70b4
   {"cmd:trace",		 "no",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {"cmd:parallel",		 "1",	  ResMgr::UNumberValidate,0},
Packit 8f70b4
   {"cmd:queue-parallel",	 "1",	  ResMgr::UNumberValidate,0},
Packit 8f70b4
   {"cmd:cls-exact-time",	 "yes",	  ResMgr::BoolValidate,ResMgr::NoClosure},
Packit 8f70b4
   {0}
Packit 8f70b4
};
Packit 8f70b4
static ResDecls lftp_cmd_vars_register(lftp_cmd_vars);
Packit 8f70b4
Packit 8f70b4
CmdExec	 *CmdExec::cwd_owner;
Packit 8f70b4
CmdExec	 *CmdExec::chain;
Packit 8f70b4
JobRef<CmdExec> CmdExec::top;
Packit 8f70b4
Packit 8f70b4
void  CmdExec::SaveCWD()
Packit 8f70b4
{
Packit 8f70b4
   if(!cwd)
Packit 8f70b4
      cwd=new LocalDirectory;
Packit 8f70b4
   cwd->SetFromCWD();
Packit 8f70b4
   if(cwd_owner==0)
Packit 8f70b4
      cwd_owner=this;
Packit 8f70b4
}
Packit 8f70b4
int  CmdExec::RestoreCWD()
Packit 8f70b4
{
Packit 8f70b4
   if(cwd_owner==this)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(cwd==0)
Packit 8f70b4
      return -1;
Packit 8f70b4
   const char *err=cwd->Chdir();
Packit 8f70b4
   if(!err)
Packit 8f70b4
   {
Packit 8f70b4
      cwd_owner=this;
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const char *name=cwd->GetName();
Packit 8f70b4
   eprintf(_("Warning: chdir(%s) failed: %s\n"),name?name:"?",err);
Packit 8f70b4
   return -1;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::FeedCmd(const char *c)
Packit 8f70b4
{
Packit 8f70b4
   partial_cmd=false;
Packit 8f70b4
   start_time=now;
Packit 8f70b4
   cmd_buf.Put(c);
Packit 8f70b4
};
Packit 8f70b4
Packit 8f70b4
void CmdExec::PrependCmd(const char *c)
Packit 8f70b4
{
Packit 8f70b4
   start_time=now;
Packit 8f70b4
Packit 8f70b4
   int len=strlen(c);
Packit 8f70b4
   int nl=(len>0 && c[len-1]!='\n');
Packit 8f70b4
Packit 8f70b4
   if(nl)
Packit 8f70b4
      cmd_buf.Prepend("\n",1);
Packit 8f70b4
   cmd_buf.Prepend(c,len);
Packit 8f70b4
Packit 8f70b4
   if(alias_field>0)
Packit 8f70b4
      alias_field+=len+nl;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int CmdExec::find_cmd(const char *cmd_name,const struct cmd_rec **ret)
Packit 8f70b4
{
Packit 8f70b4
   int part=0;
Packit 8f70b4
   const cmd_rec *c=dyn_cmd_table?dyn_cmd_table.get():static_cmd_table;
Packit 8f70b4
   const int count=dyn_cmd_table?dyn_cmd_table.count():static_cmd_table_length;
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      if(!strcasecmp(c->name,cmd_name))
Packit 8f70b4
      {
Packit 8f70b4
	 *ret=c;
Packit 8f70b4
	 return 1;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strncasecmp(c->name,cmd_name,strlen(cmd_name)))
Packit 8f70b4
      {
Packit 8f70b4
	 part++;
Packit 8f70b4
	 *ret=c;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(part!=1)
Packit 8f70b4
      *ret=0;
Packit 8f70b4
   return part;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
CMD(lcd);
Packit 8f70b4
Packit 8f70b4
void  CmdExec::exec_parsed_command()
Packit 8f70b4
{
Packit 8f70b4
   switch(condition)
Packit 8f70b4
   {
Packit 8f70b4
   case(COND_ANY):
Packit 8f70b4
      if(exit_code!=0 && ResMgr::QueryBool("cmd:fail-exit",0))
Packit 8f70b4
      {
Packit 8f70b4
         failed_exit_code=exit_code;
Packit 8f70b4
	 while(feeder)
Packit 8f70b4
	    RemoveFeeder();
Packit 8f70b4
	 cmd_buf.Empty();
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case(COND_AND):
Packit 8f70b4
      if(exit_code!=0)
Packit 8f70b4
	 return;
Packit 8f70b4
      break;
Packit 8f70b4
   case(COND_OR):
Packit 8f70b4
      if(exit_code==0)
Packit 8f70b4
	 return;
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   prev_exit_code=exit_code;
Packit 8f70b4
   exit_code=1;
Packit 8f70b4
Packit 8f70b4
   if(interactive)
Packit 8f70b4
   {
Packit 8f70b4
      SignalHook::ResetCount(SIGINT);
Packit 8f70b4
      SignalHook::ResetCount(SIGHUP);
Packit 8f70b4
      SignalHook::ResetCount(SIGTSTP);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   bool did_default=false;
Packit 8f70b4
Packit 8f70b4
   if(ResMgr::QueryBool("cmd:trace",0))
Packit 8f70b4
   {
Packit 8f70b4
      xstring_ca c(args->CombineQuoted());
Packit 8f70b4
      printf("+ %s\n",c.get());
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
restart:
Packit 8f70b4
Packit 8f70b4
   const struct cmd_rec *c;
Packit 8f70b4
   const char *cmd_name=args->getarg(0);
Packit 8f70b4
   if(!cmd_name)
Packit 8f70b4
      return;
Packit 8f70b4
   int part=find_cmd(cmd_name,&c);
Packit 8f70b4
   if(part<=0)
Packit 8f70b4
      eprintf(_("Unknown command `%s'.\n"),cmd_name);
Packit 8f70b4
   else if(part>1)
Packit 8f70b4
      eprintf(_("Ambiguous command `%s'.\n"),cmd_name);
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      if(RestoreCWD()==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 if(c->creator!=cmd_lcd)
Packit 8f70b4
	    return;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      args->setarg(0,c->name); // in case it was abbreviated
Packit 8f70b4
      args->rewind();
Packit 8f70b4
Packit 8f70b4
      xstring_ca cmdline(args->Combine());   // save the cmdline
Packit 8f70b4
      Job *new_job=0;
Packit 8f70b4
Packit 8f70b4
      if(c->creator==0)
Packit 8f70b4
      {
Packit 8f70b4
	 if(did_default)
Packit 8f70b4
	 {
Packit 8f70b4
	    eprintf(_("Module for command `%s' did not register the command.\n"),cmd_name);
Packit 8f70b4
	    exit_code=1;
Packit 8f70b4
	    return;
Packit 8f70b4
	 }
Packit 8f70b4
	 new_job=default_cmd();
Packit 8f70b4
	 did_default=true;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 new_job=c->creator(this);
Packit 8f70b4
      }
Packit 8f70b4
      if(new_job==this || builtin)
Packit 8f70b4
      {
Packit 8f70b4
	 if(builtin==BUILTIN_EXEC_RESTART)
Packit 8f70b4
	 {
Packit 8f70b4
	    builtin=BUILTIN_NONE;
Packit 8f70b4
	    goto restart;
Packit 8f70b4
	 }
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      RevertToSavedSession();
Packit 8f70b4
      if(new_job) {
Packit 8f70b4
	 if(!new_job->cmdline)
Packit 8f70b4
	    new_job->cmdline.move_here(cmdline);
Packit 8f70b4
	 AddNewJob(new_job);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::AddNewJob(Job *new_job)
Packit 8f70b4
{
Packit 8f70b4
   if(new_job->jobno<0)
Packit 8f70b4
      new_job->AllocJobno();
Packit 8f70b4
   new_job->SetParentFg(this,!background);
Packit 8f70b4
   exit_code=0;
Packit 8f70b4
   AddWaiting(new_job);
Packit 8f70b4
   if(background) {
Packit 8f70b4
      Roll(new_job);
Packit 8f70b4
      if(!new_job->Done())
Packit 8f70b4
	 SuspendJob(new_job);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::SuspendJob(Job *j)
Packit 8f70b4
{
Packit 8f70b4
   j->Bg();
Packit 8f70b4
   if(interactive)
Packit 8f70b4
      j->ListOneJob(0,0,"&";;
Packit 8f70b4
   last_bg=j->jobno;
Packit 8f70b4
   exit_code=0;
Packit 8f70b4
   RemoveWaiting(j);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::ExecParsed(ArgV *a,FDStream *o,bool b)
Packit 8f70b4
{
Packit 8f70b4
   Enter();
Packit 8f70b4
Packit 8f70b4
   args=a;
Packit 8f70b4
   output=o;
Packit 8f70b4
   background=b;
Packit 8f70b4
   condition=COND_ANY;
Packit 8f70b4
   exec_parsed_command();
Packit 8f70b4
Packit 8f70b4
   Leave();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool CmdExec::Idle()
Packit 8f70b4
{
Packit 8f70b4
   return(waiting_num==0 && builtin==BUILTIN_NONE && (cmd_buf.Size()==0 || partial_cmd));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int CmdExec::Done()
Packit 8f70b4
{
Packit 8f70b4
   Enter();
Packit 8f70b4
   bool done = (feeder==0 && Idle())
Packit 8f70b4
      || (auto_terminate_in_bg && NumberOfChildrenJobs()==0 && !in_foreground_pgrp());
Packit 8f70b4
   Leave();
Packit 8f70b4
   return done;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::RemoveFeeder()
Packit 8f70b4
{
Packit 8f70b4
   free_used_aliases();
Packit 8f70b4
Packit 8f70b4
   if(!feeder)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   // save old cwd if necessary
Packit 8f70b4
   if(interactive && feeder->prev==0)
Packit 8f70b4
      cwd_history.Set(session);
Packit 8f70b4
Packit 8f70b4
   cmd_buf.Empty();
Packit 8f70b4
   cmd_buf.Put(feeder->saved_buf);
Packit 8f70b4
   partial_cmd=false;
Packit 8f70b4
   if(feeder==queue_feeder)
Packit 8f70b4
      queue_feeder=0;
Packit 8f70b4
   delete replace_value(feeder,feeder->prev);
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
   SetInteractive();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::ReuseSavedSession()
Packit 8f70b4
{
Packit 8f70b4
   saved_session=0;
Packit 8f70b4
}
Packit 8f70b4
void CmdExec::RevertToSavedSession()
Packit 8f70b4
{
Packit 8f70b4
   if(saved_session==0)
Packit 8f70b4
      return;
Packit 8f70b4
   ChangeSession(saved_session.borrow());
Packit 8f70b4
}
Packit 8f70b4
void CmdExec::ChangeSlot(const char *n)
Packit 8f70b4
{
Packit 8f70b4
   if(!n || !*n)
Packit 8f70b4
   {
Packit 8f70b4
      slot.set(0);
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   slot.set(n);
Packit 8f70b4
   const FileAccess *s=ConnectionSlot::FindSession(n);
Packit 8f70b4
   if(!s)
Packit 8f70b4
      ConnectionSlot::Set(n,session);
Packit 8f70b4
   else
Packit 8f70b4
      ChangeSession(s->Clone());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::AtFinish()
Packit 8f70b4
{
Packit 8f70b4
   if(queue_feeder && queue_feeder->JobCount())
Packit 8f70b4
      return;
Packit 8f70b4
   if(!fed_at_finish && NumAwaitedJobs()==0 && cmd_buf.Size()==0) {
Packit 8f70b4
      FeedCmd(ResMgr::Query(queue_feeder?"cmd:at-queue-finish":"cmd:at-finish",0));
Packit 8f70b4
      FeedCmd("\n");
Packit 8f70b4
      fed_at_finish=true;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int CmdExec::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
Packit 8f70b4
   if(builtin!=BUILTIN_NONE)
Packit 8f70b4
   {
Packit 8f70b4
      int res;
Packit 8f70b4
      switch(builtin)
Packit 8f70b4
      {
Packit 8f70b4
      case(BUILTIN_CD):
Packit 8f70b4
	 res=session->Done();
Packit 8f70b4
	 if(res==FA::OK)
Packit 8f70b4
	 {
Packit 8f70b4
	    // done
Packit 8f70b4
	    if(status_line)
Packit 8f70b4
	       status_line->Clear();
Packit 8f70b4
	    if(interactive || verbose)
Packit 8f70b4
	    {
Packit 8f70b4
	       const char *cwd=session->GetCwd();
Packit 8f70b4
	       eprintf(_("cd ok, cwd=%s\n"),cwd?cwd:"~");
Packit 8f70b4
	       cwd_history.Set(session,old_cwd);
Packit 8f70b4
	    }
Packit 8f70b4
	    if(slot)
Packit 8f70b4
	       ConnectionSlot::SetCwd(slot,session->GetCwd());
Packit 8f70b4
	    session->Close();
Packit 8f70b4
	    exit_code=0;
Packit 8f70b4
	    builtin=BUILTIN_NONE;
Packit 8f70b4
	    redirections=0;
Packit 8f70b4
	    beep_if_long();
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(res<0)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(res==FA::FILE_MOVED)
Packit 8f70b4
	    {
Packit 8f70b4
	       // cd to another url.
Packit 8f70b4
	       const char *loc_c=session->GetNewLocation();
Packit 8f70b4
	       int max_redirections=ResMgr::Query("xfer:max-redirections",0);
Packit 8f70b4
	       if(loc_c && max_redirections>0)
Packit 8f70b4
	       {
Packit 8f70b4
		  eprintf(_("%s: received redirection to `%s'\n"),"cd",loc_c);
Packit 8f70b4
		  if(++redirections>max_redirections)
Packit 8f70b4
		  {
Packit 8f70b4
		     eprintf("cd: %s\n",_("Too many redirections"));
Packit 8f70b4
		     goto cd_err_done;
Packit 8f70b4
		  }
Packit 8f70b4
Packit 8f70b4
		  char *loc=alloca_strdup(loc_c);
Packit 8f70b4
		  ParsedURL u(loc,true);
Packit 8f70b4
		  if(!u.proto)
Packit 8f70b4
		  {
Packit 8f70b4
		     bool is_file=(last_char(loc)!='/');
Packit 8f70b4
		     FileAccess::Path new_cwd(session->GetNewCwd());
Packit 8f70b4
		     new_cwd.Change(0,is_file,loc);
Packit 8f70b4
		     session->PathVerify(new_cwd);
Packit 8f70b4
		     session->Roll();
Packit 8f70b4
		     return MOVED;
Packit 8f70b4
		  }
Packit 8f70b4
		  session->Close();
Packit 8f70b4
		  exit_code=0;
Packit 8f70b4
		  builtin=BUILTIN_NONE;
Packit 8f70b4
		  PrependCmd(xstring::get_tmp("open ").append_quoted(loc));
Packit 8f70b4
		  return MOVED;
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	    // error
Packit 8f70b4
	    if(status_line)
Packit 8f70b4
	       status_line->Clear();
Packit 8f70b4
	    eprintf("%s: %s\n",args->getarg(0),session->StrError(res));
Packit 8f70b4
	 cd_err_done:
Packit 8f70b4
	    session->Close();
Packit 8f70b4
	    builtin=BUILTIN_NONE;
Packit 8f70b4
	    redirections=0;
Packit 8f70b4
	    beep_if_long();
Packit 8f70b4
	    exit_code=1;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 break;
Packit 8f70b4
Packit 8f70b4
      case(BUILTIN_OPEN):
Packit 8f70b4
	 res=session->Done();
Packit 8f70b4
	 if(res==FA::OK)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(status_line)
Packit 8f70b4
	       status_line->Clear();
Packit 8f70b4
	    session->Close();
Packit 8f70b4
	    ReuseSavedSession();
Packit 8f70b4
	    builtin=BUILTIN_NONE;
Packit 8f70b4
	    redirections=0;
Packit 8f70b4
	    beep_if_long();
Packit 8f70b4
	    exit_code=0;
Packit 8f70b4
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(res<0)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(status_line)
Packit 8f70b4
	       status_line->Clear();
Packit 8f70b4
	    eprintf("%s: %s\n",args->getarg(0),session->StrError(res));
Packit 8f70b4
	    session->Close();
Packit 8f70b4
	    RevertToSavedSession();
Packit 8f70b4
	    builtin=BUILTIN_NONE;
Packit 8f70b4
	    redirections=0;
Packit 8f70b4
	    beep_if_long();
Packit 8f70b4
	    exit_code=1;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 break;
Packit 8f70b4
      case(BUILTIN_GLOB):
Packit 8f70b4
	 if(glob->Error())
Packit 8f70b4
	 {
Packit 8f70b4
	    if(status_line)
Packit 8f70b4
	       status_line->Clear();
Packit 8f70b4
	    eprintf("%s: %s\n",args->getarg(0),glob->ErrorText());
Packit 8f70b4
	 }
Packit 8f70b4
	 else if(glob->Done())
Packit 8f70b4
	 {
Packit 8f70b4
	    FileSet &list=*glob->GetResult();
Packit 8f70b4
	    for(int i=0; list[i]; i++)
Packit 8f70b4
	       args_glob->Append(list[i]->name);
Packit 8f70b4
	 }
Packit 8f70b4
	 if(glob->Done() || glob->Error())
Packit 8f70b4
	 {
Packit 8f70b4
	    const char *pat=args->getnext();
Packit 8f70b4
	    if(!pat)
Packit 8f70b4
	    {
Packit 8f70b4
	       glob=0;
Packit 8f70b4
	       // it was last argument
Packit 8f70b4
	       args=args_glob.borrow();
Packit 8f70b4
	       builtin=BUILTIN_NONE;
Packit 8f70b4
	       redirections=0;
Packit 8f70b4
	       if(status_line)
Packit 8f70b4
		  status_line->Clear();
Packit 8f70b4
	       exit_code=prev_exit_code;
Packit 8f70b4
	       RevertToSavedSession();
Packit 8f70b4
	       exec_parsed_command();
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    glob->NewGlob(pat);
Packit 8f70b4
	    m=MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 break;
Packit 8f70b4
Packit 8f70b4
      case(BUILTIN_NONE):
Packit 8f70b4
      case(BUILTIN_EXEC_RESTART):
Packit 8f70b4
	 abort(); // can't happen
Packit 8f70b4
      }
Packit 8f70b4
      if(interactive)
Packit 8f70b4
      {
Packit 8f70b4
	 if(SignalHook::GetCount(SIGINT))
Packit 8f70b4
	 {
Packit 8f70b4
	    if(status_line)
Packit 8f70b4
	       status_line->WriteLine(_("Interrupt"));
Packit 8f70b4
	    return AcceptSig(SIGINT);
Packit 8f70b4
	 }
Packit 8f70b4
	 if(SignalHook::GetCount(SIGTSTP))
Packit 8f70b4
	 {
Packit 8f70b4
	    if(builtin==BUILTIN_CD || builtin==BUILTIN_OPEN)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(status_line)
Packit 8f70b4
		  status_line->Clear();
Packit 8f70b4
	       if(builtin==BUILTIN_CD)
Packit 8f70b4
		  session->ChdirAccept();
Packit 8f70b4
	       session->Close();
Packit 8f70b4
	       exit_code=0;
Packit 8f70b4
	       builtin=BUILTIN_NONE;
Packit 8f70b4
	       redirections=0;
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    else
Packit 8f70b4
	    {
Packit 8f70b4
	       SignalHook::ResetCount(SIGTSTP);
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 if(SignalHook::GetCount(SIGHUP))
Packit 8f70b4
	 {
Packit 8f70b4
	    SetInteractive(false);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(status_line && show_status && status_line->CanShowNow())
Packit 8f70b4
	 ShowRunStatus(status_line);   // this is only for top level CmdExec.
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(waiting_num>0)
Packit 8f70b4
   {
Packit 8f70b4
      Job *j;
Packit 8f70b4
      while((j=FindDoneAwaitedJob())!=0)
Packit 8f70b4
      {
Packit 8f70b4
	 j->Bg();
Packit 8f70b4
	 if(status_line)
Packit 8f70b4
	    status_line->Clear();
Packit 8f70b4
	 if(interactive || verbose)
Packit 8f70b4
	    j->SayFinal(); // final phrase like 'rm succeed'
Packit 8f70b4
	 exit_code=j->ExitCode();
Packit 8f70b4
	 RemoveWaiting(j);
Packit 8f70b4
	 Delete(j);
Packit 8f70b4
	 beep_if_long();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(interactive)
Packit 8f70b4
      {
Packit 8f70b4
	 if(SignalHook::GetCount(SIGINT))
Packit 8f70b4
	 {
Packit 8f70b4
	    for(int i=0; i
Packit 8f70b4
	       waiting[i]->Bg();
Packit 8f70b4
	    SignalHook::ResetCount(SIGINT);
Packit 8f70b4
	    if(status_line)
Packit 8f70b4
	       status_line->WriteLine(_("Interrupt"));
Packit 8f70b4
	    return AcceptSig(SIGINT);
Packit 8f70b4
	 }
Packit 8f70b4
	 if(SignalHook::GetCount(SIGTSTP))
Packit 8f70b4
	 {
Packit 8f70b4
	    while(waiting_num>0)
Packit 8f70b4
	       SuspendJob(waiting[0]);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(SignalHook::GetCount(SIGHUP))
Packit 8f70b4
	 {
Packit 8f70b4
	    SetInteractive(false);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(status_line && show_status && status_line->CanShowNow())
Packit 8f70b4
	 ShowRunStatus(status_line);   // this is only for top level CmdExec.
Packit 8f70b4
      if(m != STALL || interactive || waiting_num >= max_waiting)
Packit 8f70b4
	 return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!interactive)
Packit 8f70b4
   {
Packit 8f70b4
      BuryDoneJobs();
Packit 8f70b4
      if(FindJob(last_bg)==0)
Packit 8f70b4
	 last_bg=-1;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
try_get_cmd:
Packit 8f70b4
   if(cmd_buf.Size()==0 || partial_cmd)
Packit 8f70b4
   {
Packit 8f70b4
      if(feeder)
Packit 8f70b4
      {
Packit 8f70b4
	 if(interactive && !partial_cmd)
Packit 8f70b4
	 {
Packit 8f70b4
	    ListDoneJobs();
Packit 8f70b4
	    BuryDoneJobs();
Packit 8f70b4
	    if(FindJob(last_bg)==0)
Packit 8f70b4
	       last_bg=-1;
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 if(status_line && show_status)
Packit 8f70b4
	 {
Packit 8f70b4
	    const char *def_title = FormatPrompt(ResMgr::Query("cmd:default-title",getenv("TERM")));
Packit 8f70b4
	    status_line->DefaultTitle(def_title);
Packit 8f70b4
	    status_line->Clear();
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 const char *prompt=MakePrompt();
Packit 8f70b4
	 feeder_called=true;
Packit 8f70b4
	 if(fg)
Packit 8f70b4
	    feeder->Fg();
Packit 8f70b4
	 const char *cmd=feeder->NextCmd(this,prompt);
Packit 8f70b4
	 feeder_called=false;
Packit 8f70b4
	 if(!cmd)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(cmd_buf.Size()>0 && partial_cmd)
Packit 8f70b4
	    {
Packit 8f70b4
	       const char *next_cmd=cmd_buf.Get();
Packit 8f70b4
	       if(last_char(next_cmd)!='\n')
Packit 8f70b4
	       {
Packit 8f70b4
		  // missing EOL on last line, add it
Packit 8f70b4
		  FeedCmd("\n");
Packit 8f70b4
		  goto try_get_cmd;
Packit 8f70b4
	       }
Packit 8f70b4
	       fprintf(stderr,_("Warning: discarding incomplete command\n"));
Packit 8f70b4
	    }
Packit 8f70b4
	    if(!feeder->RealEOF() && top_level)
Packit 8f70b4
	    {
Packit 8f70b4
	       cmd_buf.Empty();
Packit 8f70b4
	       FeedCmd("exit;");
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    if(waiting_num > 0)
Packit 8f70b4
	      return m;
Packit 8f70b4
	    RemoveFeeder();
Packit 8f70b4
	    m=MOVED;
Packit 8f70b4
	    goto try_get_cmd;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(cmd[0])
Packit 8f70b4
	 {
Packit 8f70b4
	    auto_terminate_in_bg=false;
Packit 8f70b4
	    FeedCmd(cmd);
Packit 8f70b4
	    Roll();
Packit 8f70b4
	    if(!Idle())
Packit 8f70b4
	       fed_at_finish=false;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    if(SignalHook::GetCount(SIGINT)>0)
Packit 8f70b4
	    {
Packit 8f70b4
	       SignalHook::ResetCount(SIGINT);
Packit 8f70b4
	       cmd_buf.Empty();	 // flush unparsed command
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   parse_result res = parse_one_cmd();
Packit 8f70b4
Packit 8f70b4
   if(alias_field<=0)
Packit 8f70b4
      free_used_aliases();
Packit 8f70b4
Packit 8f70b4
   switch(res)
Packit 8f70b4
   {
Packit 8f70b4
   case(PARSE_ERR):
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   case(PARSE_AGAIN):
Packit 8f70b4
      partial_cmd=true;
Packit 8f70b4
      goto try_get_cmd;
Packit 8f70b4
   case(PARSE_OK):
Packit 8f70b4
      if(feeder)
Packit 8f70b4
	 feeder->Bg();
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   if(args==0 || args->count()==0) {
Packit 8f70b4
      AtFinish();
Packit 8f70b4
      return MOVED;  // empty command
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(interactive)
Packit 8f70b4
      session->DontSleep(); // We don't want to get a delay just after user
Packit 8f70b4
			    // entered a command.
Packit 8f70b4
Packit 8f70b4
   exec_parsed_command();
Packit 8f70b4
   return MOVED;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::ShowRunStatus(const SMTaskRef<StatusLine>& s)
Packit 8f70b4
{
Packit 8f70b4
   switch(builtin)
Packit 8f70b4
   {
Packit 8f70b4
   case(BUILTIN_CD):
Packit 8f70b4
      if(session->IsOpen())
Packit 8f70b4
	 s->Show("cd `%s' [%s]",squeeze_file_name(args->getarg(1),s->GetWidthDelayed()-40),session->CurrentStatus());
Packit 8f70b4
      break;
Packit 8f70b4
   case(BUILTIN_OPEN):
Packit 8f70b4
      if(session->IsOpen())
Packit 8f70b4
	 s->Show("open `%s' [%s]",session->GetHostName(),session->CurrentStatus());
Packit 8f70b4
      break;
Packit 8f70b4
   case(BUILTIN_GLOB):
Packit 8f70b4
      s->Show("%s",glob->Status());
Packit 8f70b4
      break;
Packit 8f70b4
   case(BUILTIN_EXEC_RESTART):
Packit 8f70b4
      abort(); // can't happen
Packit 8f70b4
   case(BUILTIN_NONE):
Packit 8f70b4
      if(waiting_num>0)
Packit 8f70b4
	 Job::ShowRunStatus(s);
Packit 8f70b4
      else
Packit 8f70b4
	 s->Clear();
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& CmdExec::FormatStatus(xstring& s,int v,const char *prefix)
Packit 8f70b4
{
Packit 8f70b4
   SessionJob::FormatStatus(s,v,prefix);
Packit 8f70b4
   if(builtin)
Packit 8f70b4
   {
Packit 8f70b4
      xstring_ca ac(args->Combine());
Packit 8f70b4
      return s.appendf(_("\tExecuting builtin `%s' [%s]\n"),ac.get(),session->CurrentStatus());
Packit 8f70b4
   }
Packit 8f70b4
   if(queue_feeder)
Packit 8f70b4
   {
Packit 8f70b4
      if(IsSuspended())
Packit 8f70b4
	 s.appendf("%s%s\n",prefix,_("Queue is stopped."));
Packit 8f70b4
      BuryDoneJobs();
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
      {
Packit 8f70b4
	 if(i==0)
Packit 8f70b4
	    s.appendf("%s%s ",prefix,_("Now executing:"));
Packit 8f70b4
	 if(v==0)
Packit 8f70b4
	    waiting[i]->FormatOneJob(s,v);
Packit 8f70b4
	 else
Packit 8f70b4
	    waiting[i]->FormatJobTitle(s);
Packit 8f70b4
	 if(i+1
Packit 8f70b4
	    s.appendf("%s\t-",prefix);
Packit 8f70b4
      }
Packit 8f70b4
      return queue_feeder->FormatStatus(s,v,prefix);
Packit 8f70b4
   }
Packit 8f70b4
   if(waiting_num==1)
Packit 8f70b4
      return s.appendf(_("\tWaiting for job [%d] to terminate\n"),waiting[0]->jobno);
Packit 8f70b4
   else if(waiting_num>1)
Packit 8f70b4
   {
Packit 8f70b4
      s.appendf(_("\tWaiting for termination of jobs: "));
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
      {
Packit 8f70b4
	 s.appendf("[%d]",waiting[i]->jobno);
Packit 8f70b4
	 s.append(i+1
Packit 8f70b4
      }
Packit 8f70b4
      return s;
Packit 8f70b4
   }
Packit 8f70b4
   if(cmd_buf.Size()>0)
Packit 8f70b4
      s.append(_("\tRunning\n"));
Packit 8f70b4
   else if(feeder)
Packit 8f70b4
      s.append(_("\tWaiting for command\n"));
Packit 8f70b4
   return s;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::init(LocalDirectory *c)
Packit 8f70b4
{
Packit 8f70b4
   // add this to chain
Packit 8f70b4
   next=chain;
Packit 8f70b4
   chain=this;
Packit 8f70b4
Packit 8f70b4
   background=false;
Packit 8f70b4
Packit 8f70b4
   interactive=false;
Packit 8f70b4
   show_status=true;
Packit 8f70b4
   top_level=false;
Packit 8f70b4
   auto_terminate_in_bg=false;
Packit 8f70b4
   feeder=0;
Packit 8f70b4
   feeder_called=false;
Packit 8f70b4
   used_aliases=0;
Packit 8f70b4
Packit 8f70b4
   partial_cmd=false;
Packit 8f70b4
   alias_field=0;
Packit 8f70b4
   default_output=0;
Packit 8f70b4
   condition=COND_ANY;
Packit 8f70b4
   prev_exit_code=0;
Packit 8f70b4
   exit_code=0;
Packit 8f70b4
   failed_exit_code=0;
Packit 8f70b4
   last_bg=-1;
Packit 8f70b4
   fed_at_finish=true;
Packit 8f70b4
Packit 8f70b4
   cwd=c;
Packit 8f70b4
   if(!cwd)
Packit 8f70b4
      SaveCWD();
Packit 8f70b4
Packit 8f70b4
   remote_completion=false;
Packit 8f70b4
   long_running=0;
Packit 8f70b4
   csh_history=false;
Packit 8f70b4
   verify_host=verify_path=true;
Packit 8f70b4
   verify_path_cached=false;
Packit 8f70b4
Packit 8f70b4
   start_time=0;
Packit 8f70b4
Packit 8f70b4
   redirections=0;
Packit 8f70b4
Packit 8f70b4
   queue_feeder=0;
Packit 8f70b4
   max_waiting=1;
Packit 8f70b4
Packit 8f70b4
   saved_session=0;
Packit 8f70b4
Packit 8f70b4
   builtin=BUILTIN_NONE;
Packit 8f70b4
Packit 8f70b4
   Reconfig();
Packit 8f70b4
}
Packit 8f70b4
CmdExec::CmdExec(FileAccess *s,LocalDirectory *c)
Packit 8f70b4
   : SessionJob(s?s:new DummyProto), parent_exec(0)
Packit 8f70b4
{
Packit 8f70b4
   init(c);
Packit 8f70b4
}
Packit 8f70b4
CmdExec::CmdExec(CmdExec *parent)
Packit 8f70b4
   : SessionJob(parent->session->Clone()), parent_exec(parent)
Packit 8f70b4
{
Packit 8f70b4
   init(parent->cwd->Clone());
Packit 8f70b4
}
Packit 8f70b4
CmdExec::~CmdExec()
Packit 8f70b4
{
Packit 8f70b4
   // remove this from chain.
Packit 8f70b4
   for(CmdExec **scan=&chain; *scan; scan=&(*scan)->next)
Packit 8f70b4
   {
Packit 8f70b4
      if(this==*scan)
Packit 8f70b4
      {
Packit 8f70b4
	 *scan=(*scan)->next;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   free_used_aliases();
Packit 8f70b4
   if(cwd_owner==this)
Packit 8f70b4
      cwd_owner=0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *CmdExec::FormatPrompt(const char *scan)
Packit 8f70b4
{
Packit 8f70b4
   const char *cwd=session->GetCwd();
Packit 8f70b4
   if(cwd==0 || cwd[0]==0)
Packit 8f70b4
      cwd="~";
Packit 8f70b4
   {
Packit 8f70b4
      const char *home=session->GetHome();
Packit 8f70b4
      int home_len=xstrlen(home);
Packit 8f70b4
      if(home_len>1 && !strncmp(cwd,home,home_len)
Packit 8f70b4
      && (cwd[home_len]=='/' || cwd[home_len]==0))
Packit 8f70b4
      {
Packit 8f70b4
	 cwd=xstring::format("~%s",cwd+home_len);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   const char *cwdb=session->GetCwd();
Packit 8f70b4
   if(cwdb==0 || cwdb[0]==0)
Packit 8f70b4
      cwdb="~";
Packit 8f70b4
   const char *p=strrchr(cwdb,'/');
Packit 8f70b4
   if(p && p>cwdb)
Packit 8f70b4
      cwdb=p+1;
Packit 8f70b4
Packit 8f70b4
   const char *lcwd=this->cwd->GetName();
Packit 8f70b4
   {
Packit 8f70b4
      const char *home=get_home();
Packit 8f70b4
      int home_len=xstrlen(home);
Packit 8f70b4
      if(home_len>1 && !strncmp(lcwd,home,home_len)
Packit 8f70b4
      && (lcwd[home_len]=='/' || lcwd[home_len]==0))
Packit 8f70b4
      {
Packit 8f70b4
	 lcwd=xstring::format("~%s",lcwd+home_len);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   const char *lcwdb=this->cwd->GetName();
Packit 8f70b4
   p=strrchr(lcwdb,'/');
Packit 8f70b4
   if(p && p>lcwdb)
Packit 8f70b4
      lcwdb=p+1;
Packit 8f70b4
Packit 8f70b4
   static const char StartIgn[]={RL_PROMPT_START_IGNORE,0};
Packit 8f70b4
   static const char EndIgn[]={RL_PROMPT_END_IGNORE,0};
Packit 8f70b4
Packit 8f70b4
   subst_t subst[] = {
Packit 8f70b4
      { 'a', "\007" },
Packit 8f70b4
      { 'e', "\033" },
Packit 8f70b4
      { 'n', "\n" },
Packit 8f70b4
      { 's', "lftp" },
Packit 8f70b4
      { 'v', VERSION },
Packit 8f70b4
Packit 8f70b4
      { 'h', session->GetHostName() },
Packit 8f70b4
      { 'u', session->GetUser() },
Packit 8f70b4
 // @ if non-default user
Packit 8f70b4
      { '@', session->GetUser()?"@":"" },
Packit 8f70b4
      { 'U', session->GetConnectURL() },
Packit 8f70b4
      { 'S', slot?slot.get():"" },
Packit 8f70b4
      { 'w', cwd },
Packit 8f70b4
      { 'W', cwdb },
Packit 8f70b4
      { 'l', lcwd },
Packit 8f70b4
      { 'L', lcwdb },
Packit 8f70b4
      { '[', StartIgn },
Packit 8f70b4
      { ']', EndIgn },
Packit 8f70b4
      { 0, "" }
Packit 8f70b4
   };
Packit 8f70b4
   static xstring prompt;
Packit 8f70b4
   SubstTo(prompt, scan, subst);
Packit 8f70b4
Packit 8f70b4
   return(prompt);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *CmdExec::MakePrompt()
Packit 8f70b4
{
Packit 8f70b4
   if(partial_cmd)
Packit 8f70b4
      return "> ";
Packit 8f70b4
   return FormatPrompt(ResMgr::Query("cmd:prompt",getenv("TERM")));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::beep_if_long()
Packit 8f70b4
{
Packit 8f70b4
   if(start_time!=0 && long_running!=0
Packit 8f70b4
   && now.UnixTime()>start_time+long_running
Packit 8f70b4
   && interactive && Idle() && isatty(1))
Packit 8f70b4
      write(1,"\007",1);
Packit 8f70b4
   AtFinish();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::Reconfig(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   const char *c=0;
Packit 8f70b4
   if(session)
Packit 8f70b4
      c = session->GetConnectURL(FA::NO_PATH);
Packit 8f70b4
Packit 8f70b4
   long_running=ResMgr::Query("cmd:long-running",c);
Packit 8f70b4
   remote_completion=ResMgr::QueryBool("cmd:remote-completion",c);
Packit 8f70b4
   csh_history=ResMgr::QueryBool("cmd:csh-history",0);
Packit 8f70b4
   verify_path=ResMgr::QueryBool("cmd:verify-path",c);
Packit 8f70b4
   verify_path_cached=ResMgr::QueryBool("cmd:verify-path-cached",c);
Packit 8f70b4
   verify_host=ResMgr::QueryBool("cmd:verify-host",c);
Packit 8f70b4
   verbose=ResMgr::QueryBool("cmd:verbose",0);
Packit 8f70b4
   if(top_level || queue_feeder)
Packit 8f70b4
      max_waiting=ResMgr::Query(queue_feeder?"cmd:queue-parallel":"cmd:parallel",c);
Packit 8f70b4
   if(name && !strcmp(name,"cmd:interactive"))
Packit 8f70b4
      SetInteractive();
Packit 8f70b4
   show_status=ResMgr::QueryBool("cmd:show-status",0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::pre_stdout()
Packit 8f70b4
{
Packit 8f70b4
   if(status_line)
Packit 8f70b4
      status_line->Clear(false);
Packit 8f70b4
   if(feeder_called)
Packit 8f70b4
      feeder->clear();
Packit 8f70b4
   current->TimeoutS(1);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::top_vfprintf(FILE *file,const char *f,va_list v)
Packit 8f70b4
{
Packit 8f70b4
   pre_stdout();
Packit 8f70b4
   ::vfprintf(file,f,v);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::SetCmdFeeder(CmdFeeder *new_feeder)
Packit 8f70b4
{
Packit 8f70b4
   new_feeder->prev=feeder;
Packit 8f70b4
   new_feeder->saved_buf.set(cmd_buf.Get());
Packit 8f70b4
   feeder=new_feeder;
Packit 8f70b4
   cmd_buf.Empty();
Packit 8f70b4
   SetInteractive();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int CmdExec::AcceptSig(int sig)
Packit 8f70b4
{
Packit 8f70b4
   if(sig!=SIGINT && sig!=SIGTERM)
Packit 8f70b4
      return STALL;
Packit 8f70b4
   if(builtin)
Packit 8f70b4
   {
Packit 8f70b4
      switch(builtin)
Packit 8f70b4
      {
Packit 8f70b4
      case(BUILTIN_CD):
Packit 8f70b4
	 session->Close();
Packit 8f70b4
	 break;
Packit 8f70b4
      case(BUILTIN_OPEN):
Packit 8f70b4
	 session->Close();
Packit 8f70b4
	 RevertToSavedSession();
Packit 8f70b4
	 break;
Packit 8f70b4
      case(BUILTIN_GLOB):
Packit 8f70b4
	 glob=0;
Packit 8f70b4
	 args_glob=0;
Packit 8f70b4
	 break;
Packit 8f70b4
      case(BUILTIN_NONE):
Packit 8f70b4
      case(BUILTIN_EXEC_RESTART):
Packit 8f70b4
	 abort(); // should not happen
Packit 8f70b4
      }
Packit 8f70b4
      builtin=BUILTIN_NONE;
Packit 8f70b4
      redirections=0;
Packit 8f70b4
      exit_code=1;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(waiting_num>0)
Packit 8f70b4
   {
Packit 8f70b4
      int limit=waiting_num;
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
      {
Packit 8f70b4
	 Job *r=waiting[i];
Packit 8f70b4
	 int res=r->AcceptSig(sig);
Packit 8f70b4
	 if(res==WANTDIE)
Packit 8f70b4
	 {
Packit 8f70b4
	    exit_code=1;
Packit 8f70b4
	    RemoveWaiting(r);
Packit 8f70b4
	    Delete(r);
Packit 8f70b4
	    i--;
Packit 8f70b4
	    limit--;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(waiting_num==0 && parent!=0)
Packit 8f70b4
	 return WANTDIE;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(parent!=0)
Packit 8f70b4
      return WANTDIE;
Packit 8f70b4
   return STALL;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::SetInteractive(bool i)
Packit 8f70b4
{
Packit 8f70b4
   if(interactive==i)
Packit 8f70b4
      return;
Packit 8f70b4
   if(i)
Packit 8f70b4
   {
Packit 8f70b4
      SignalHook::DoCount(SIGINT);
Packit 8f70b4
      SignalHook::DoCount(SIGTSTP);
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      SignalHook::Restore(SIGINT);
Packit 8f70b4
      SignalHook::Restore(SIGTSTP);
Packit 8f70b4
   }
Packit 8f70b4
   interactive=i;
Packit 8f70b4
}
Packit 8f70b4
void CmdExec::SetInteractive()
Packit 8f70b4
{
Packit 8f70b4
   if(!top_level)
Packit 8f70b4
      return;
Packit 8f70b4
   bool def=feeder?feeder->IsInteractive():false;
Packit 8f70b4
   SetInteractive(ResMgr::QueryTriBool("cmd:interactive",0,def));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xstring& xstring::append_quoted(const char *str,int len)
Packit 8f70b4
{
Packit 8f70b4
   if(!CmdExec::needs_quotation(str,len))
Packit 8f70b4
      return append(str);
Packit 8f70b4
Packit 8f70b4
   append('"');
Packit 8f70b4
   while(len>0)
Packit 8f70b4
   {
Packit 8f70b4
      if(*str=='"' || *str=='\\')
Packit 8f70b4
	 append('\\');
Packit 8f70b4
      append(*str++);
Packit 8f70b4
      len--;
Packit 8f70b4
   }
Packit 8f70b4
   return append('"');
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool CmdExec::needs_quotation(const char *buf,int len)
Packit 8f70b4
{
Packit 8f70b4
   while(len>0)
Packit 8f70b4
   {
Packit 8f70b4
      if(*buf==' ' || *buf=='\t')
Packit 8f70b4
	 return true;
Packit 8f70b4
      if(strchr("\"'\\&|;;",*buf))
Packit 8f70b4
	 return true;
Packit 8f70b4
      buf++;
Packit 8f70b4
      len--;
Packit 8f70b4
   }
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::FeedQuoted(const char *c)
Packit 8f70b4
{
Packit 8f70b4
   FeedCmd(xstring::get_tmp("").append_quoted(c));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// implementation is here because it depends on CmdExec.
Packit 8f70b4
xstring& ArgV::CombineQuotedTo(xstring& res,int start) const
Packit 8f70b4
{
Packit 8f70b4
   res.nset("",0);
Packit 8f70b4
   if(start>=Count())
Packit 8f70b4
      return res;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      const char *arg=String(start++);
Packit 8f70b4
      res.append_quoted(arg);
Packit 8f70b4
      if(start>=Count())
Packit 8f70b4
	 return(res);
Packit 8f70b4
      res.append(' ');
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
xstring& ArgV::CombineCmdTo(xstring& res,int i) const
Packit 8f70b4
{
Packit 8f70b4
   return i>=count()-1 ? CombineTo(res,i) : CombineQuotedTo(res,i);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *CmdExec::GetFullCommandName(const char *cmd)
Packit 8f70b4
{
Packit 8f70b4
   const CmdExec::cmd_rec *c;
Packit 8f70b4
   int part=CmdExec::find_cmd(cmd,&c);
Packit 8f70b4
   if(part==1)
Packit 8f70b4
      return c->name;
Packit 8f70b4
   return cmd;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::AtExit()
Packit 8f70b4
{
Packit 8f70b4
   FeedCmd(ResMgr::Query("cmd:at-exit",0));
Packit 8f70b4
   FeedCmd("\n");
Packit 8f70b4
   /* Clear the title, and ensure we don't write anything else
Packit 8f70b4
    * to it in case we're being backgrounded. */
Packit 8f70b4
   status_line=0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::AtExitBg()
Packit 8f70b4
{
Packit 8f70b4
   FeedCmd(ResMgr::Query("cmd:at-exit-bg",0));
Packit 8f70b4
   FeedCmd("\n");
Packit 8f70b4
}
Packit 8f70b4
void CmdExec::AtExitFg()
Packit 8f70b4
{
Packit 8f70b4
   FeedCmd(ResMgr::Query("cmd:at-exit-fg",0));
Packit 8f70b4
   FeedCmd("\n");
Packit 8f70b4
}
Packit 8f70b4
void CmdExec::AtBackground()
Packit 8f70b4
{
Packit 8f70b4
   FeedCmd(ResMgr::Query("cmd:at-background",0));
Packit 8f70b4
   FeedCmd("\n");
Packit 8f70b4
}
Packit 8f70b4
void CmdExec::AtTerminate()
Packit 8f70b4
{
Packit 8f70b4
   FeedCmd(ResMgr::Query("cmd:at-terminate",0));
Packit 8f70b4
   FeedCmd("\n");
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::EmptyCmds()
Packit 8f70b4
{
Packit 8f70b4
   cmd_buf.Empty();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool CmdExec::WriteCmds(int fd) const
Packit 8f70b4
{
Packit 8f70b4
   const char *buf;
Packit 8f70b4
   int len;
Packit 8f70b4
   cmd_buf.Get(&buf,&len;;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      if(len==0)
Packit 8f70b4
	 return true;
Packit 8f70b4
      int res=write(fd,buf,len);
Packit 8f70b4
      if(res<=0)
Packit 8f70b4
	 return false;
Packit 8f70b4
      buf+=res;
Packit 8f70b4
      len-=res;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool CmdExec::ReadCmds(int fd)
Packit 8f70b4
{
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      int size=0x1000;
Packit 8f70b4
      size=read(fd,cmd_buf.GetSpace(size),size);
Packit 8f70b4
      if(size==-1)
Packit 8f70b4
	 return false;
Packit 8f70b4
      if(size==0)
Packit 8f70b4
	 return true;
Packit 8f70b4
      cmd_buf.SpaceAdd(size);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::free_used_aliases()
Packit 8f70b4
{
Packit 8f70b4
   if(used_aliases)
Packit 8f70b4
   {
Packit 8f70b4
      TouchedAlias::FreeChain(used_aliases);
Packit 8f70b4
      used_aliases=0;
Packit 8f70b4
   }
Packit 8f70b4
   alias_field=0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::skip_cmd(int len)
Packit 8f70b4
{
Packit 8f70b4
   cmd_buf.Skip(len);
Packit 8f70b4
   alias_field-=len;
Packit 8f70b4
   if(alias_field<=0)
Packit 8f70b4
      free_used_aliases();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int CmdExec::cmd_rec::cmp(const CmdExec::cmd_rec *a,const CmdExec::cmd_rec *b)
Packit 8f70b4
{
Packit 8f70b4
   return strcmp(a->name,b->name);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xarray<CmdExec::cmd_rec> CmdExec::dyn_cmd_table;
Packit 8f70b4
void CmdExec::RegisterCommand(const char *name,cmd_creator_t creator,const char *short_desc,const char *long_desc)
Packit 8f70b4
{
Packit 8f70b4
   if(dyn_cmd_table==0)
Packit 8f70b4
      dyn_cmd_table.nset(static_cmd_table,static_cmd_table_length);
Packit 8f70b4
   cmd_rec new_entry={name,creator,short_desc,long_desc};
Packit 8f70b4
   int i;
Packit 8f70b4
   if(dyn_cmd_table.bsearch(new_entry,cmd_rec::cmp,&i))
Packit 8f70b4
   {
Packit 8f70b4
      cmd_rec *const c=&dyn_cmd_table[i];
Packit 8f70b4
      c->creator=creator;
Packit 8f70b4
      if(short_desc)
Packit 8f70b4
	 c->short_desc=short_desc;
Packit 8f70b4
      if(long_desc || strlen(c->long_desc)<2)
Packit 8f70b4
	 c->long_desc=long_desc;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   dyn_cmd_table.insert(new_entry,i);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::ChangeSession(FileAccess *new_session)
Packit 8f70b4
{
Packit 8f70b4
   session=new_session;
Packit 8f70b4
   session->SetPriority(fg?1:0);
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
   if(slot)
Packit 8f70b4
      ConnectionSlot::Set(slot,session);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *CmdExec::CmdByIndex(int i)
Packit 8f70b4
{
Packit 8f70b4
   if(dyn_cmd_table)
Packit 8f70b4
   {
Packit 8f70b4
      if(i
Packit 8f70b4
	 return dyn_cmd_table[i].name;
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   return static_cmd_table[i].name;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool CmdExec::load_cmd_module(const char *op)
Packit 8f70b4
{
Packit 8f70b4
   const char *modname=xstring::cat("cmd-",op,NULL);
Packit 8f70b4
   if(module_init_preloaded(modname))
Packit 8f70b4
      return true;
Packit 8f70b4
#ifdef WITH_MODULES
Packit 8f70b4
   if(module_load(modname,0,0)==0)
Packit 8f70b4
   {
Packit 8f70b4
      eprintf("%s\n",module_error_message());
Packit 8f70b4
      return false;
Packit 8f70b4
   }
Packit 8f70b4
   return true;
Packit 8f70b4
#else
Packit 8f70b4
   eprintf(_("%s: command `%s' is not compiled in.\n"),op,op);
Packit 8f70b4
   return false;
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Job *CmdExec::default_cmd()
Packit 8f70b4
{
Packit 8f70b4
   const char *op=args->a0();
Packit 8f70b4
   if(load_cmd_module(op)) {
Packit 8f70b4
      builtin=BUILTIN_EXEC_RESTART;
Packit 8f70b4
      return this;
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Job *CmdExec::builtin_local()
Packit 8f70b4
{
Packit 8f70b4
   if(args->count()<2) {
Packit 8f70b4
      eprintf(_("Usage: %s cmd [args...]\n"),args->a0());
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   saved_session=session.borrow();
Packit 8f70b4
   session=FileAccess::New("file");
Packit 8f70b4
   if(!session) {
Packit 8f70b4
      eprintf(_("%s: cannot create local session\n"),args->a0());
Packit 8f70b4
      RevertToSavedSession();
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   session->SetCwd(cwd->GetName());
Packit 8f70b4
   args->delarg(0);
Packit 8f70b4
   builtin=BUILTIN_EXEC_RESTART;
Packit 8f70b4
   return this;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void CmdExec::FeedArgV(const ArgV *args,int start)
Packit 8f70b4
{
Packit 8f70b4
   xstring cmd;
Packit 8f70b4
   args->CombineCmdTo(cmd,start);
Packit 8f70b4
   FeedCmd(cmd);
Packit 8f70b4
   FeedCmd("\n");
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool CmdExec::SameQueueParameters(CmdExec *scan,const char *this_url)
Packit 8f70b4
{
Packit 8f70b4
   return !strcmp(this_url,scan->session->GetConnectURL(FA::NO_PATH))
Packit 8f70b4
      && this->slot.eq(scan->slot);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
/* return the CmdExec containing a queue feeder; create if necessary */
Packit 8f70b4
CmdExec  *CmdExec::GetQueue(bool create)
Packit 8f70b4
{
Packit 8f70b4
   const char *this_url=session->GetConnectURL(FA::NO_PATH);
Packit 8f70b4
   // future GetConnectURL overwrite the static buffer, save it.
Packit 8f70b4
   this_url=alloca_strdup(this_url);
Packit 8f70b4
   for(CmdExec *scan=chain; scan; scan=scan->next)
Packit 8f70b4
   {
Packit 8f70b4
      if(scan->queue_feeder && SameQueueParameters(scan,this_url))
Packit 8f70b4
	 return scan;
Packit 8f70b4
   }
Packit 8f70b4
   if(!create)
Packit 8f70b4
      return NULL;
Packit 8f70b4
Packit 8f70b4
   CmdExec *queue=new CmdExec(session->Clone(),cwd->Clone());
Packit 8f70b4
   queue->slot.set(slot);
Packit 8f70b4
Packit 8f70b4
   queue->SetParentFg(this,false);
Packit 8f70b4
   queue->AllocJobno();
Packit 8f70b4
   const char *url=session->GetConnectURL(FA::NO_PATH);
Packit 8f70b4
   queue->cmdline.vset("queue (",url,slot?"; ":"",slot?slot.get():"",")",NULL);
Packit 8f70b4
   queue->queue_feeder=new QueueFeeder(session->GetCwd(), cwd->GetName());
Packit 8f70b4
   queue->SetCmdFeeder(queue->queue_feeder);
Packit 8f70b4
   queue->Reconfig(0);
Packit 8f70b4
Packit 8f70b4
   return queue;
Packit 8f70b4
}