Blame src/CmdExec.cc

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