Blame src/lftp.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2013 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
Packit 8f70b4
#include "modconfig.h"
Packit 8f70b4
Packit 8f70b4
#include "trio.h"
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/stat.h> // for mkdir()
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include <locale.h>
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
Packit 8f70b4
#include "xstring.h"
Packit 8f70b4
#include "xmalloc.h"
Packit 8f70b4
#include "alias.h"
Packit 8f70b4
#include "CmdExec.h"
Packit 8f70b4
#include "SignalHook.h"
Packit 8f70b4
#include "GetPass.h"
Packit 8f70b4
#include "History.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "DummyProto.h"
Packit 8f70b4
#include "ResMgr.h"
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
#include "IdNameCache.h"
Packit 8f70b4
#include "LocalDir.h"
Packit 8f70b4
#include "ConnectionSlot.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "ArgV.h"
Packit 8f70b4
#include "attach.h"
Packit 8f70b4
Packit 8f70b4
#include "configmake.h"
Packit 8f70b4
Packit 8f70b4
#include "lftp_rl.h"
Packit 8f70b4
#include "complete.h"
Packit 8f70b4
Packit 8f70b4
CDECL_BEGIN
Packit 8f70b4
#include <glob.h>
Packit 8f70b4
CDECL_END
Packit 8f70b4
Packit 8f70b4
#define top_exec CmdExec::top
Packit 8f70b4
Packit 8f70b4
void  hook_signals()
Packit 8f70b4
{
Packit 8f70b4
   SignalHook::DoCount(SIGHUP);
Packit 8f70b4
   SignalHook::Ignore(SIGTTOU);
Packit 8f70b4
   ProcWait::Signal(true);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
ResDecl res_save_cwd_history
Packit 8f70b4
   ("cmd:save-cwd-history","yes",ResMgr::BoolValidate,ResMgr::NoClosure);
Packit 8f70b4
ResDecl res_save_rl_history
Packit 8f70b4
   ("cmd:save-rl-history","yes",ResMgr::BoolValidate,ResMgr::NoClosure);
Packit 8f70b4
ResDecl res_stifle_rl_history
Packit 8f70b4
   ("cmd:stifle-rl-history","500",ResMgr::UNumberValidate,ResMgr::NoClosure);
Packit 8f70b4
Packit 8f70b4
class ReadlineFeeder : public CmdFeeder, private ResClient
Packit 8f70b4
{
Packit 8f70b4
   bool tty:1;
Packit 8f70b4
   bool ctty:1;
Packit 8f70b4
   bool add_newline:1;
Packit 8f70b4
   int eof_count;
Packit 8f70b4
   xstring_c cmd_buf;
Packit 8f70b4
   xstring_c for_history;
Packit 8f70b4
Packit 8f70b4
   static bool readline_inited;
Packit 8f70b4
   void readline_init()
Packit 8f70b4
   {
Packit 8f70b4
      if(readline_inited)
Packit 8f70b4
	 return;
Packit 8f70b4
      readline_inited=true;
Packit 8f70b4
      lftp_readline_init();
Packit 8f70b4
      lftp_rl_read_history();
Packit 8f70b4
      if(for_history)
Packit 8f70b4
      {
Packit 8f70b4
	 lftp_add_history_nodups(for_history);
Packit 8f70b4
	 for_history.set(0);
Packit 8f70b4
      }
Packit 8f70b4
      Reconfig(0);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
public:
Packit 8f70b4
   ReadlineFeeder(const ArgV *args)
Packit 8f70b4
   {
Packit 8f70b4
      tty=isatty(0);
Packit 8f70b4
      ctty=(tcgetpgrp(0)!=(pid_t)-1);
Packit 8f70b4
      add_newline=false;
Packit 8f70b4
      eof_count=0;
Packit 8f70b4
      if(args && args->count()>1)
Packit 8f70b4
	 args->CombineQuotedTo(for_history);
Packit 8f70b4
   }
Packit 8f70b4
   virtual ~ReadlineFeeder()
Packit 8f70b4
   {
Packit 8f70b4
      if(readline_inited)
Packit 8f70b4
      {
Packit 8f70b4
	 if(res_save_cwd_history.QueryBool(0))
Packit 8f70b4
	    cwd_history.Save();
Packit 8f70b4
	 if(res_save_rl_history.QueryBool(0))
Packit 8f70b4
	    lftp_rl_write_history();
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   bool IsInteractive() const
Packit 8f70b4
   {
Packit 8f70b4
      return tty;
Packit 8f70b4
   }
Packit 8f70b4
   bool RealEOF()
Packit 8f70b4
   {
Packit 8f70b4
      return !tty || eof_count>3;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const char *NextCmd(class CmdExec *exec,const char *prompt)
Packit 8f70b4
   {
Packit 8f70b4
      if(add_newline)
Packit 8f70b4
      {
Packit 8f70b4
	 add_newline=false;
Packit 8f70b4
	 return "\n";
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      ::completion_shell=exec;
Packit 8f70b4
      ::remote_completion=exec->remote_completion;
Packit 8f70b4
Packit 8f70b4
      if(tty)
Packit 8f70b4
      {
Packit 8f70b4
	 readline_init();
Packit 8f70b4
Packit 8f70b4
	 if(ctty) // controlling terminal
Packit 8f70b4
	 {
Packit 8f70b4
	    if(!in_foreground_pgrp())
Packit 8f70b4
	    {
Packit 8f70b4
	       // looks like we are in background. Can't read from tty
Packit 8f70b4
	       exec->Timeout(500);
Packit 8f70b4
	       return "";
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
Packit 8f70b4
	 SignalHook::ResetCount(SIGINT);
Packit 8f70b4
	 cmd_buf.set_allocated(lftp_readline(prompt));
Packit 8f70b4
	 xmalloc_register_block(cmd_buf.get_non_const());
Packit 8f70b4
	 if(cmd_buf && *cmd_buf)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(exec->csh_history)
Packit 8f70b4
	    {
Packit 8f70b4
	       char *history_value0=0;
Packit 8f70b4
	       int expanded = lftp_history_expand (cmd_buf, &history_value0);
Packit 8f70b4
	       if (expanded)
Packit 8f70b4
	       {
Packit 8f70b4
		  if(history_value0)
Packit 8f70b4
		     xmalloc_register_block(history_value0);
Packit 8f70b4
		  xstring_ca history_value(history_value0);
Packit 8f70b4
Packit 8f70b4
		  if (expanded < 0)
Packit 8f70b4
		     fprintf (stderr, "%s\n", history_value.get());
Packit 8f70b4
Packit 8f70b4
		  /* If there was an error, return nothing. */
Packit 8f70b4
		  if (expanded < 0 || expanded == 2)	/* 2 == print only */
Packit 8f70b4
		  {
Packit 8f70b4
		     exec->Timeout(0);  // and retry immediately
Packit 8f70b4
		     return "";
Packit 8f70b4
		  }
Packit 8f70b4
Packit 8f70b4
		  cmd_buf.move_here(history_value);
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	    lftp_add_history_nodups(cmd_buf);
Packit 8f70b4
	 }
Packit 8f70b4
	 else if(cmd_buf==0 && exec->interactive)
Packit 8f70b4
	    puts("exit");
Packit 8f70b4
Packit 8f70b4
	 if(cmd_buf==0)
Packit 8f70b4
	    eof_count++;
Packit 8f70b4
	 else
Packit 8f70b4
	    eof_count=0;
Packit 8f70b4
      }
Packit 8f70b4
      else // not a tty
Packit 8f70b4
      {
Packit 8f70b4
	 if(exec->interactive)
Packit 8f70b4
	 {
Packit 8f70b4
	    while(*prompt)
Packit 8f70b4
	    {
Packit 8f70b4
	       char ch=*prompt++;
Packit 8f70b4
	       if(ch!=1 && ch!=2)
Packit 8f70b4
		  putchar(ch);
Packit 8f70b4
	    }
Packit 8f70b4
	    fflush(stdout);
Packit 8f70b4
	 }
Packit 8f70b4
	 cmd_buf.set_allocated(readline_from_file(0));
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      ::completion_shell=0;
Packit 8f70b4
Packit 8f70b4
      if(cmd_buf && last_char(cmd_buf)!='\n')
Packit 8f70b4
      {
Packit 8f70b4
	 exec->Timeout(0);
Packit 8f70b4
	 add_newline=true;
Packit 8f70b4
      }
Packit 8f70b4
      return cmd_buf;
Packit 8f70b4
   }
Packit 8f70b4
   void clear()
Packit 8f70b4
      {
Packit 8f70b4
	 if(!tty)
Packit 8f70b4
	    return;
Packit 8f70b4
	 lftp_rl_clear();
Packit 8f70b4
      }
Packit 8f70b4
   void Reconfig(const char *) {
Packit 8f70b4
      lftp_rl_history_stifle(res_stifle_rl_history.Query(0));
Packit 8f70b4
   }
Packit 8f70b4
};
Packit 8f70b4
bool ReadlineFeeder::readline_inited;
Packit 8f70b4
Packit 8f70b4
#define args	  (parent->args)
Packit 8f70b4
#define exit_code (parent->exit_code)
Packit 8f70b4
#define output	  (parent->output)
Packit 8f70b4
#define session	  (parent->session)
Packit 8f70b4
#define eprintf	  parent->eprintf
Packit 8f70b4
CMD(history)
Packit 8f70b4
{
Packit 8f70b4
   enum { READ, WRITE, CLEAR, LIST } mode = LIST;
Packit 8f70b4
   const char *fn = NULL;
Packit 8f70b4
   static struct option history_options[]=
Packit 8f70b4
   {
Packit 8f70b4
      {"read",required_argument,0,'r'},
Packit 8f70b4
      {"write",required_argument,0,'w'},
Packit 8f70b4
      {"clear",no_argument,0,'c'},
Packit 8f70b4
      {"list",required_argument,0,'l'},
Packit 8f70b4
      {0,0,0,0}
Packit 8f70b4
   };
Packit 8f70b4
Packit 8f70b4
   exit_code=0;
Packit 8f70b4
   int opt;
Packit 8f70b4
   while((opt=args->getopt_long("+r:w:cl",history_options,0))!=EOF) {
Packit 8f70b4
      switch(opt) {
Packit 8f70b4
      case 'r':
Packit 8f70b4
	 mode = READ;
Packit 8f70b4
	 fn = optarg;
Packit 8f70b4
	 break;
Packit 8f70b4
      case 'w':
Packit 8f70b4
	 mode = WRITE;
Packit 8f70b4
	 fn = optarg;
Packit 8f70b4
	 break;
Packit 8f70b4
      case 'c':
Packit 8f70b4
	 mode = CLEAR;
Packit 8f70b4
	 break;
Packit 8f70b4
      case 'l':
Packit 8f70b4
	 mode = LIST;
Packit 8f70b4
	 break;
Packit 8f70b4
      case '?':
Packit 8f70b4
	 eprintf(_("Try `help %s' for more information.\n"),args->a0());
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   int cnt = 16;
Packit 8f70b4
   if(const char *arg = args->getcurr()) {
Packit 8f70b4
      if(!strcasecmp(arg, "all"))
Packit 8f70b4
	 cnt = -1;
Packit 8f70b4
      else if(isdigit((unsigned char)arg[0]))
Packit 8f70b4
	 cnt = atoi(arg);
Packit 8f70b4
      else {
Packit 8f70b4
	 eprintf(_("%s: %s - not a number\n"), args->a0(), args->getcurr());
Packit 8f70b4
	 exit_code=1;
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   switch(mode) {
Packit 8f70b4
   case READ:
Packit 8f70b4
      if(int err = lftp_history_read(fn)) {
Packit 8f70b4
	 eprintf("%s: %s: %s\n", args->a0(), fn, strerror(err));
Packit 8f70b4
	 exit_code=1;
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case WRITE:
Packit 8f70b4
      if(int err = lftp_history_write(fn)) {
Packit 8f70b4
	 eprintf("%s: %s: %s\n", args->a0(), fn, strerror(err));
Packit 8f70b4
	 exit_code=1;
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case LIST:
Packit 8f70b4
      lftp_history_list(cnt);
Packit 8f70b4
      break;
Packit 8f70b4
   case CLEAR:
Packit 8f70b4
      lftp_history_clear();
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
CMD(attach)
Packit 8f70b4
{
Packit 8f70b4
   const char *pid_s=args->getarg(1);
Packit 8f70b4
   if(!pid_s) {
Packit 8f70b4
      xstring& path=AcceptTermFD::get_sock_path(1);
Packit 8f70b4
      path.rtrim('1');
Packit 8f70b4
      path.append('*');
Packit 8f70b4
      glob_t g;
Packit 8f70b4
      glob(path, 0, NULL, &g);
Packit 8f70b4
      for(size_t i=0; i
Packit 8f70b4
	 const char *sock_path=g.gl_pathv[i];
Packit 8f70b4
	 pid_s=strrchr(sock_path,'-');
Packit 8f70b4
	 if(!pid_s)
Packit 8f70b4
	    continue;
Packit 8f70b4
	 pid_s++;
Packit 8f70b4
	 int p=atoi(pid_s);
Packit 8f70b4
	 if(p<=1) {
Packit 8f70b4
	    pid_s=0;
Packit 8f70b4
	    continue;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(kill(p,0)==-1) {
Packit 8f70b4
	    if(errno==ESRCH) {
Packit 8f70b4
	       eprintf("%s: removing stale socket `%s'.\n",args->a0(),sock_path);
Packit 8f70b4
	       if(unlink(sock_path)==-1)
Packit 8f70b4
		  eprintf("%s: unlink(%s): %s\n",args->a0(),sock_path,strerror(errno));
Packit 8f70b4
	    }
Packit 8f70b4
	    pid_s=0;
Packit 8f70b4
	    continue;
Packit 8f70b4
	 }
Packit 8f70b4
	 pid_s=alloca_strdup(pid_s);
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      globfree(&g);
Packit 8f70b4
      if(!pid_s) {
Packit 8f70b4
	 eprintf("%s: no backgrounded lftp processes found.\n",args->a0());
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   int pid=atoi(pid_s);
Packit 8f70b4
   SMTaskRef<SendTermFD> term_sender(new SendTermFD(pid));
Packit 8f70b4
   while(!term_sender->Done()) {
Packit 8f70b4
      SMTask::Schedule();
Packit 8f70b4
      SMTask::Block();
Packit 8f70b4
   }
Packit 8f70b4
   exit_code=0;
Packit 8f70b4
   if(term_sender->Failed()) {
Packit 8f70b4
      eprintf("%s\n",term_sender->ErrorText());
Packit 8f70b4
      exit_code=1;
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
#undef args
Packit 8f70b4
#undef exit_code
Packit 8f70b4
#undef output
Packit 8f70b4
#undef session
Packit 8f70b4
#undef eprintf
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
static void sig_term(int sig)
Packit 8f70b4
{
Packit 8f70b4
   printf(_("[%u] Terminated by signal %d. %s\n"),(unsigned)getpid(),sig,SMTask::now.IsoDateTime());
Packit 8f70b4
   if(top_exec) {
Packit 8f70b4
      top_exec->KillAll();
Packit 8f70b4
      alarm(30);
Packit 8f70b4
      while(Job::NumberOfJobs()>0) {
Packit 8f70b4
	 SMTask::Schedule();
Packit 8f70b4
	 SMTask::Block();
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   exit(1);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static void detach()
Packit 8f70b4
{
Packit 8f70b4
   SignalHook::Ignore(SIGINT);
Packit 8f70b4
   SignalHook::Ignore(SIGHUP);
Packit 8f70b4
   SignalHook::Ignore(SIGTSTP);
Packit 8f70b4
Packit 8f70b4
   const char *home=get_lftp_data_dir();
Packit 8f70b4
   if(home)
Packit 8f70b4
   {
Packit 8f70b4
      xstring& log=xstring::get_tmp(home);
Packit 8f70b4
      if(access(log,F_OK)==-1)
Packit 8f70b4
	 log.append("_log");
Packit 8f70b4
      else
Packit 8f70b4
	 log.append("/log");
Packit 8f70b4
Packit 8f70b4
      int fd=open(log,O_WRONLY|O_APPEND|O_CREAT,0600);
Packit 8f70b4
      if(fd>=0)
Packit 8f70b4
      {
Packit 8f70b4
	 dup2(fd,1); // stdout
Packit 8f70b4
	 dup2(fd,2); // stderr
Packit 8f70b4
	 if(fd!=1 && fd!=2)
Packit 8f70b4
	    close(fd);
Packit 8f70b4
      }
Packit 8f70b4
      const char *c="debug";
Packit 8f70b4
      ResMgr::Set("log:show-pid",c,"yes");
Packit 8f70b4
      ResMgr::Set("log:show-time",c,"yes");
Packit 8f70b4
      ResMgr::Set("log:show-ctx",c,"yes");
Packit 8f70b4
   }
Packit 8f70b4
   close(0);	  // close stdin.
Packit 8f70b4
   open("/dev/null",O_RDONLY); // reopen it, just in case.
Packit 8f70b4
Packit 8f70b4
#ifdef HAVE_SETSID
Packit 8f70b4
   setsid();	  // start a new session.
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
   SignalHook::Handle(SIGTERM,sig_term);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static void move_to_background()
Packit 8f70b4
{
Packit 8f70b4
   // notify jobs
Packit 8f70b4
   Job::lftpMovesToBackground_ToAll();
Packit 8f70b4
   // wait they do something, but no more than 1 sec.
Packit 8f70b4
   SMTask::RollAll(TimeInterval(1,0));
Packit 8f70b4
   // if all jobs terminated, don't really move to bg.
Packit 8f70b4
   if(Job::NumberOfJobs()==0)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   top_exec->AtBackground();
Packit 8f70b4
   top_exec->WaitDone();
Packit 8f70b4
   if(Job::NumberOfJobs()==0)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   fflush(stdout);
Packit 8f70b4
   fflush(stderr);
Packit 8f70b4
Packit 8f70b4
   pid_t pid=fork();
Packit 8f70b4
   switch(pid)
Packit 8f70b4
   {
Packit 8f70b4
   case(0): // child
Packit 8f70b4
   {
Packit 8f70b4
      pid=getpid();
Packit 8f70b4
      detach();
Packit 8f70b4
      printf(_("[%u] Started.  %s\n"),(unsigned)pid,SMTask::now.IsoDateTime());
Packit 8f70b4
      SMTaskRef<AcceptTermFD> term_acceptor(new AcceptTermFD());
Packit 8f70b4
      for(;;)
Packit 8f70b4
      {
Packit 8f70b4
	 SMTask::Schedule();
Packit 8f70b4
	 if(Job::NumberOfJobs()==0)
Packit 8f70b4
	    break;
Packit 8f70b4
	 SMTask::Block();
Packit 8f70b4
	 if(term_acceptor->Accepted()) {
Packit 8f70b4
	    hook_signals();
Packit 8f70b4
	    top_exec->SetInteractive();
Packit 8f70b4
	    top_exec->SetStatusLine(new StatusLine(1));
Packit 8f70b4
	    top_exec->SetCmdFeeder(new ReadlineFeeder(0));
Packit 8f70b4
	    for(;;)
Packit 8f70b4
	    {
Packit 8f70b4
	       SMTask::Schedule();
Packit 8f70b4
	       if(top_exec->Done() || term_acceptor->Detached()) {
Packit 8f70b4
		  if(Job::NumberOfJobs()>0) {
Packit 8f70b4
		     printf(_("[%u] Detaching from the terminal to complete transfers...\n"),(unsigned)pid);
Packit 8f70b4
		  } else if(top_exec->Done()) {
Packit 8f70b4
		     printf(_("[%u] Exiting and detaching from the terminal.\n"),(unsigned)pid);
Packit 8f70b4
		  }
Packit 8f70b4
		  fflush(stdout);
Packit 8f70b4
		  term_acceptor->Detach();
Packit 8f70b4
		  detach();
Packit 8f70b4
		  printf(_("[%u] Detached from terminal. %s\n"),(unsigned)pid,SMTask::now.IsoDateTime());
Packit 8f70b4
		  break;
Packit 8f70b4
	       }
Packit 8f70b4
	       SMTask::Block();
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      top_exec->AtExitBg();
Packit 8f70b4
      top_exec->AtTerminate();
Packit 8f70b4
      top_exec->WaitDone();
Packit 8f70b4
      printf(_("[%u] Finished. %s\n"),(unsigned)pid,SMTask::now.IsoDateTime());
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   default: // parent
Packit 8f70b4
      printf(_("[%u] Moving to background to complete transfers...\n"),
Packit 8f70b4
	       (unsigned)pid);
Packit 8f70b4
      fflush(stdout);
Packit 8f70b4
      _exit(0);
Packit 8f70b4
   case(-1):
Packit 8f70b4
      perror("fork()");
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int lftp_slot(int count,int key)
Packit 8f70b4
{
Packit 8f70b4
   if(!top_exec)
Packit 8f70b4
      return 0;
Packit 8f70b4
   char slot[2];
Packit 8f70b4
   slot[0]=key;
Packit 8f70b4
   slot[1]=0;
Packit 8f70b4
   top_exec->ChangeSlot(slot);
Packit 8f70b4
   lftp_rl_set_prompt(top_exec->MakePrompt());
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void  source_if_exist(const char *rc)
Packit 8f70b4
{
Packit 8f70b4
   if(access(rc,R_OK)!=-1)
Packit 8f70b4
   {
Packit 8f70b4
      top_exec->FeedCmd("source ");
Packit 8f70b4
      top_exec->FeedCmd(rc);
Packit 8f70b4
      top_exec->FeedCmd("\n");
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static void tty_clear()
Packit 8f70b4
{
Packit 8f70b4
   if(top_exec)
Packit 8f70b4
      top_exec->pre_stdout();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// look for the option, remove it and return true if found
Packit 8f70b4
static bool pick_option(int& argc,char **argv,const char *option)
Packit 8f70b4
{
Packit 8f70b4
   for(int i=1; i
Packit 8f70b4
      if(!strcmp(argv[i],option)) {
Packit 8f70b4
	 // remove the option, move trailing NULL too.
Packit 8f70b4
	 memmove(argv+i,argv+i+1,(argc-i)*sizeof(*argv));
Packit 8f70b4
	 argc--;
Packit 8f70b4
	 return true;
Packit 8f70b4
      }
Packit 8f70b4
      if(!strcmp(argv[i],"--"))	 // end of options
Packit 8f70b4
	 break;
Packit 8f70b4
   }
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
char *program_name;
Packit 8f70b4
Packit 8f70b4
int   main(int argc,char **argv)
Packit 8f70b4
{
Packit 8f70b4
   program_name=argv[0];
Packit 8f70b4
Packit 8f70b4
#ifdef SOCKS4
Packit 8f70b4
   SOCKSinit(program_name);
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
   setlocale (LC_ALL, "");
Packit 8f70b4
   setlocale (LC_NUMERIC, "C");
Packit 8f70b4
   bindtextdomain (PACKAGE, LOCALEDIR);
Packit 8f70b4
   textdomain (PACKAGE);
Packit 8f70b4
Packit 8f70b4
   CmdExec::RegisterCommand("history",cmd_history,
Packit 8f70b4
	 N_("history -w file|-r file|-c|-l [cnt]"),
Packit 8f70b4
	 N_(" -w <file> Write history to file.\n"
Packit 8f70b4
	 " -r <file> Read history from file; appends to current history.\n"
Packit 8f70b4
	 " -c  Clear the history.\n"
Packit 8f70b4
	 " -l  List the history (default).\n"
Packit 8f70b4
	 "Optional argument cnt specifies the number of history lines to list,\n"
Packit 8f70b4
	 "or \"all\" to list all entries.\n"));
Packit 8f70b4
   CmdExec::RegisterCommand("attach",cmd_attach,"attach [PID]",
Packit 8f70b4
      N_("Attach the terminal to specified backgrounded lftp process.\n"));
Packit 8f70b4
Packit 8f70b4
   top_exec=new CmdExec(0,0);
Packit 8f70b4
   hook_signals();
Packit 8f70b4
   top_exec->SetStatusLine(new StatusLine(1));
Packit 8f70b4
   Log::global=new Log("debug");
Packit 8f70b4
   Log::global->SetCB(tty_clear);
Packit 8f70b4
Packit 8f70b4
   source_if_exist(SYSCONFDIR"/lftp.conf");
Packit 8f70b4
Packit 8f70b4
   if(!pick_option(argc,argv,"--norc")) {
Packit 8f70b4
      const char *home=getenv("HOME");
Packit 8f70b4
      if(home)
Packit 8f70b4
	 source_if_exist(dir_file(home,".lftprc"));
Packit 8f70b4
      home=get_lftp_config_dir();
Packit 8f70b4
      if(home)
Packit 8f70b4
	 source_if_exist(dir_file(home,"rc"));
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   top_exec->WaitDone();
Packit 8f70b4
   top_exec->SetTopLevel();
Packit 8f70b4
   top_exec->Fg();
Packit 8f70b4
Packit 8f70b4
   Ref<ArgV> args(new ArgV(argc,argv));
Packit 8f70b4
   args->setarg(0,"lftp");
Packit 8f70b4
Packit 8f70b4
   lftp_feeder=new ReadlineFeeder(args);
Packit 8f70b4
Packit 8f70b4
   top_exec->ExecParsed(args.borrow());
Packit 8f70b4
   top_exec->WaitDone();
Packit 8f70b4
   int exit_code=top_exec->ExitCode();
Packit 8f70b4
Packit 8f70b4
   top_exec->AtExit();
Packit 8f70b4
   top_exec->WaitDone();
Packit 8f70b4
Packit 8f70b4
   if(Job::NumberOfJobs()>0)
Packit 8f70b4
   {
Packit 8f70b4
      top_exec->SetInteractive(false);
Packit 8f70b4
      move_to_background();
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      top_exec->AtExitFg();
Packit 8f70b4
      top_exec->AtTerminate();
Packit 8f70b4
      top_exec->WaitDone();
Packit 8f70b4
   }
Packit 8f70b4
   top_exec->KillAll();
Packit 8f70b4
   top_exec=0;
Packit 8f70b4
Packit 8f70b4
   Job::Cleanup();
Packit 8f70b4
   ConnectionSlot::Cleanup();
Packit 8f70b4
   SessionPool::ClearAll();
Packit 8f70b4
   FileAccess::ClassCleanup();
Packit 8f70b4
   ProcWait::DeleteAll();
Packit 8f70b4
   IdNameCacheCleanup();
Packit 8f70b4
   SignalHook::Cleanup();
Packit 8f70b4
   Log::Cleanup();
Packit 8f70b4
   SMTask::Cleanup();
Packit 8f70b4
   return exit_code;
Packit 8f70b4
}