// @(#)root/net:$Id$
// Author: Fons Rademakers   13/02/2001

/*************************************************************************
 * Copyright (C) 1995-2001, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TFTP                                                                 //
//                                                                      //
// This class provides all infrastructure for a performant file         //
// transfer protocol. It works in conjuction with the rootd daemon      //
// and can use parallel sockets to improve performance over fat pipes.  //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "RConfig.h"

#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#ifndef R__WIN32
#   include <unistd.h>
#else
#   define ssize_t int
#   include <io.h>
#   include <sys/types.h>
#endif

#include "TFTP.h"
#include "TPSocket.h"
#include "TUrl.h"
#include "TStopwatch.h"
#include "TSystem.h"
#include "TEnv.h"
#include "TROOT.h"
#include "TError.h"
#include "NetErrors.h"
#include "TRegexp.h"
#include "TVirtualMutex.h"

#if defined(R__UNIX) || defined(R__MACOSX)
#define HAVE_MMAP
#endif

#ifdef HAVE_MMAP
#   include <sys/mman.h>
#ifndef MAP_FILE
#define MAP_FILE 0           /* compatability flag */
#endif
#endif


Long64_t TFTP::fgBytesWrite = 0;
Long64_t TFTP::fgBytesRead  = 0;


ClassImp(TFTP)

//______________________________________________________________________________
TFTP::TFTP(const char *url, Int_t par, Int_t wsize, TSocket *sock)
{
   // Open connection to host specified by the url using par parallel sockets.
   // The url has the form: [root[s,k]://]host[:port].
   // If port is not specified the default rootd port (1094) will be used.
   // Using wsize one can specify the tcp window size. Normally this is not
   // needed when using parallel sockets.
   // An existing connection (TSocket *sock) can also be used to establish
   // the FTP session.

   fSocket = sock;

   TString s = url;
   if (s.Contains("://")) {
      if (!s.BeginsWith("root")) {
         Error("TFTP",
               "url must be of the form \"[root[up,s,k,g,h,ug]://]host[:port]\"");
         MakeZombie();
         return;
      }
   } else
      s = "root://" + s;

   Init(s, par, wsize);
}

//______________________________________________________________________________
void TFTP::Init(const char *surl, Int_t par, Int_t wsize)
{
   // Set up the actual connection.

   TUrl url(surl);
   TString hurl(url.GetProtocol());
   if (hurl.Contains("root")) {
      hurl.Insert(4,"dp");
   } else {
      hurl = "rootdp";
   }
   hurl += TString(Form("://%s@%s:%d",
                        url.GetUser(), url.GetHost(), url.GetPort()));
   fSocket = TSocket::CreateAuthSocket(hurl, par, wsize, fSocket);
   if (!fSocket || !fSocket->IsAuthenticated()) {
      if (par > 1)
         Error("TFTP", "can't open %d-stream connection to rootd on "
               "host %s at port %d", par, url.GetHost(), url.GetPort());
      else
         Error("TFTP", "can't open connection to rootd on "
               "host %s at port %d", url.GetHost(), url.GetPort());
      goto zombie;
   }

   fProtocol = fSocket->GetRemoteProtocol();
   fUser = fSocket->GetSecContext()->GetUser();

   fHost       = url.GetHost();
   fPort       = url.GetPort();
   fParallel   = par;
   fWindowSize = wsize;
   fLastBlock  = 0;
   fRestartAt  = 0;
   fBlockSize  = kDfltBlockSize;
   fMode       = kBinary;
   fBytesWrite = 0;
   fBytesRead  = 0;

   // Replace our socket in the list with this
   // for consistency during the final cleanup
   // (The socket will be delete by us when everything is ok remotely)
   {
      R__LOCKGUARD2(gROOTMutex);
      gROOT->GetListOfSockets()->Remove(fSocket);
      gROOT->GetListOfSockets()->Add(this);
   }
   return;

zombie:
   MakeZombie();
   SafeDelete(fSocket);
}

//______________________________________________________________________________
TFTP::~TFTP()
{
   // TFTP dtor. Send close message and close socket.

   Close();
}

//______________________________________________________________________________
void TFTP::Print(Option_t *) const
{
   // Print some info about the FTP connection.

   TString secCont;

   Printf("Local host:           %s", gSystem->HostName());
   Printf("Remote host:          %s [%d]", fHost.Data(), fPort);
   Printf("Remote user:          %s", fUser.Data());
   if (fSocket->IsAuthenticated())
      Printf("Security context:     %s",
                                      fSocket->GetSecContext()->AsString(secCont));
   Printf("Rootd protocol vers.: %d", fSocket->GetRemoteProtocol());
   if (fParallel > 1) {
      Printf("Parallel sockets:     %d", fParallel);
   }
   Printf("TCP window size:      %d",   fWindowSize);
   Printf("Rootd protocol:       %d",   fProtocol);
   Printf("Transfer block size:  %d",   fBlockSize);
   Printf("Transfer mode:        %s",   fMode ? "ascii" : "binary");
   Printf("Bytes sent:           %lld", fBytesWrite);
   Printf("Bytes received:       %lld", fBytesRead);
}

//______________________________________________________________________________
void TFTP::PrintError(const char *where, Int_t err) const
{
   // Print error string depending on error code.

   Error(where, "%s", gRootdErrStr[err]);
}

//______________________________________________________________________________
Int_t TFTP::Recv(Int_t &status, EMessageTypes &kind) const
{
   // Return status from rootd server and message kind. Returns -1 in
   // case of error otherwise 8 (sizeof 2 words, status and kind).

   kind   = kROOTD_ERR;
   status = 0;

   if (!fSocket) return -1;

   Int_t what;
   Int_t n = fSocket->Recv(status, what);
   kind = (EMessageTypes) what;
   return n;
}

//______________________________________________________________________________
void TFTP::SetBlockSize(Int_t blockSize)
{
   // Make sure the block size is a power of two, with a minimum of 32768.

   if (blockSize < 32768) {
      fBlockSize = 32768;
      return;
   }

   int i;
   for (i = 0; i < int(sizeof(blockSize)*8); i++)
      if ((blockSize >> i) == 1)
         break;

   fBlockSize = 1 << i;
}

//______________________________________________________________________________
Long64_t TFTP::PutFile(const char *file, const char *remoteName)
{
   // Transfer file to remote host. Returns number of bytes
   // sent or < 0 in case of error. Error -1 connection is still
   // open, error -2 connection has been closed. In case of failure
   // fRestartAt is set to the number of bytes correclty transfered.
   // Calling PutFile() immediately afterwards will restart at fRestartAt.
   // If this is not desired call SetRestartAt(0) before calling PutFile().
   // If rootd reports that the file is locked, and you are sure this is not
   // the case (e.g. due to a crash), you can force unlock it by prepending
   // the remoteName with a '-'.

   if (!IsOpen() || !file || !*file) return -1;

#if defined(R__WIN32) || defined(R__WINGCC)
   Int_t fd = open(file, O_RDONLY | O_BINARY);
#elif defined(R__SEEK64)
   Int_t fd = open64(file, O_RDONLY);
#else
   Int_t fd = open(file, O_RDONLY);
#endif
   if (fd < 0) {
      Error("PutFile", "cannot open %s in read mode", file);
      return -1;
   }

   Long64_t size;
   Long_t id, flags, modtime;
   if (gSystem->GetPathInfo(file, &id, &size, &flags, &modtime) == 0) {
      if (flags > 1) {
         Error("PutFile", "%s not a regular file (%ld)", file, flags);
         close(fd);
         return -1;
      }
   } else {
      Warning("PutFile", "could not stat %s", file);
      close(fd);
      return -1;
   }

   if (!remoteName)
      remoteName = file;

   Long64_t restartat = fRestartAt;

   // check if restartat value makes sense
   if (restartat && (restartat >= size))
      restartat = 0;

   if (fSocket->Send(Form("%s %d %d %lld %lld", remoteName, fBlockSize, fMode,
                     size, restartat), kROOTD_PUTFILE) < 0) {
      Error("PutFile", "error sending kROOTD_PUTFILE command");
      close(fd);
      return -2;
   }

   Int_t         stat;
   EMessageTypes kind;

   if (Recv(stat, kind) < 0 || kind == kROOTD_ERR) {
      PrintError("PutFile", stat);
      close(fd);
      return -1;
   }

   Info("PutFile", "sending file %s (%lld bytes, starting at %lld)",
        file, size, restartat);

   TStopwatch timer;
   timer.Start();

   Long64_t pos = restartat & ~(fBlockSize-1);
   Int_t skip = restartat - pos;

#ifndef HAVE_MMAP
   char *buf = new char[fBlockSize];
#if defined(R__SEEK64)
   lseek64(fd, pos, SEEK_SET);
#elif defined(R__WIN32)
   _lseeki64(fd, pos, SEEK_SET);
#else
   lseek(fd, pos, SEEK_SET);
#endif
#endif

   while (pos < size) {
      Long64_t left = Long64_t(size - pos);
      if (left > fBlockSize)
         left = fBlockSize;
#ifdef HAVE_MMAP
#if defined(R__SEEK64)
      char *buf = (char*) mmap64(0, left, PROT_READ, MAP_FILE | MAP_SHARED, fd, pos);
#else
      char *buf = (char*) mmap(0, left, PROT_READ, MAP_FILE | MAP_SHARED, fd, pos);
#endif
      if (buf == (char *) -1) {
         Error("PutFile", "mmap of file %s failed", file);
         close(fd);
         return -1;
      }
#else
      Int_t siz;
      while ((siz = read(fd, buf, left)) < 0 && TSystem::GetErrno() == EINTR)
         TSystem::ResetErrno();
      if (siz < 0 || siz != left) {
         Error("PutFile", "error reading from file %s", file);
         // Send urgent message to rootd to stop tranfer
         delete [] buf;
         close(fd);
         return -1;
      }
#endif

      if (fSocket->SendRaw(buf+skip, left-skip) < 0) {
         Error("PutFile", "error sending buffer");
         // Send urgent message to rootd to stop transfer
#ifdef HAVE_MMAP
         munmap(buf, left);
#else
         delete [] buf;
#endif
         close(fd);
         return -2;
      }

      fBytesWrite  += left-skip;
      fgBytesWrite += left-skip;

      fRestartAt = pos;   // bytes correctly sent up till now

      pos += left;
      skip = 0;

#ifdef HAVE_MMAP
      munmap(buf, left);
#endif
   }

#ifndef HAVE_MMAP
   delete [] buf;
#endif

   close(fd);

   fRestartAt = 0;

   // get acknowlegdement from server that file was stored correctly
   if (Recv(stat, kind) < 0 || kind == kROOTD_ERR) {
      PrintError("PutFile", stat);
      close(fd);
      return -1;
   }

   // provide timing numbers
   Double_t speed, t = timer.RealTime();
   if (t > 0)
      speed = Double_t(size - restartat) / t;
   else
      speed = 0.0;
   if (speed > 524288)
      Info("PutFile", "%.3f seconds, %.2f Mbytes per second",
           t, speed / 1048576);
   else if (speed > 512)
      Info("PutFile", "%.3f seconds, %.2f Kbytes per second",
           t, speed / 1024);
   else
      Info("PutFile", "%.3f seconds, %.2f bytes per second",
           t, speed);

   return Long64_t(size - restartat);
}

//______________________________________________________________________________
Long64_t TFTP::GetFile(const char *file, const char *localName)
{
   // Transfer file from remote host. Returns number of bytes
   // received or < 0 in case of error. Error -1 connection is still
   // open, error -2 connection has been closed. In case of failure
   // fRestartAt is set to the number of bytes correclty transfered.
   // Calling GetFile() immediately afterwards will restart at fRestartAt.
   // If this is not desired call SetRestartAt(0) before calling GetFile().
   // If rootd reports that the file is locked, and you are sure this is not
   // the case (e.g. due to a crash), you can force unlock it by prepending
   // the file name with a '-'.

   if (!IsOpen() || !file || !*file) return -1;

   if (!localName) {
      if (file[0] == '-')
         localName = file+1;
      else
         localName = file;
   }

   Long64_t restartat = fRestartAt;

   if (fSocket->Send(Form("%s %d %d %lld", file, fBlockSize, fMode,
                     restartat), kROOTD_GETFILE) < 0) {
      Error("GetFile", "error sending kROOTD_GETFILE command");
      return -2;
   }

   Int_t         stat;
   EMessageTypes kind;

   if (Recv(stat, kind) < 0 || kind == kROOTD_ERR) {
      PrintError("GetFile", stat);
      return -1;
   }

   // get size of remote file
   Long64_t size;
   Int_t    what;
   char     mess[128];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("GetFile", "error receiving remote file size");
      return -2;
   }
#ifdef R__WIN32
   sscanf(mess, "%I64d", &size);
#else
   sscanf(mess, "%lld", &size);
#endif

   // check if restartat value makes sense
   if (restartat && (restartat >= size))
      restartat = 0;

   // open local file
   Int_t fd;
   if (!restartat) {
#if defined(R__WIN32) || defined(R__WINGCC)
      if (fMode == kBinary)
         fd = open(localName, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY,
                   S_IREAD | S_IWRITE);
      else
         fd = open(localName, O_CREAT | O_TRUNC | O_WRONLY,
                   S_IREAD | S_IWRITE);
#elif defined(R__SEEK64)
      fd = open64(localName, O_CREAT | O_TRUNC | O_WRONLY, 0600);
#else
      fd = open(localName, O_CREAT | O_TRUNC | O_WRONLY, 0600);
#endif
   } else {
#if defined(R__WIN32) || defined(R__WINGCC)
      if (fMode == kBinary)
         fd = open(localName, O_WRONLY | O_BINARY, S_IREAD | S_IWRITE);
      else
         fd = open(localName, O_WRONLY, S_IREAD | S_IWRITE);
#elif defined(R__SEEK64)
      fd = open64(localName, O_WRONLY, 0600);
#else
      fd = open(localName, O_WRONLY, 0600);
#endif
   }

   if (fd < 0) {
      Error("GetFile", "cannot open %s", localName);
      // send urgent message to rootd to stop tranfer
      return -1;
   }

   // check file system space
   if (strcmp(localName, "/dev/null")) {
      Long_t id, bsize, blocks, bfree;
      if (gSystem->GetFsInfo(localName, &id, &bsize, &blocks, &bfree) == 0) {
         Long64_t space = (Long64_t)bsize * (Long64_t)bfree;
         if (space < size - restartat) {
            Error("GetFile", "not enough space to store file %s", localName);
            // send urgent message to rootd to stop tranfer
            close(fd);
            return -1;
         }
      } else
         Warning("GetFile", "could not determine if there is enough free space to store file");
   }

   // seek to restartat position
   if (restartat) {
#if defined(R__SEEK64)
      if (lseek64(fd, restartat, SEEK_SET) < 0) {
#elif defined(R__WIN32)
      if (_lseeki64(fd, restartat, SEEK_SET) < 0) {
#else
      if (lseek(fd, restartat, SEEK_SET) < 0) {
#endif
         Error("GetFile", "cannot seek to position %lld in file %s",
               restartat, localName);
         // if cannot seek send urgent message to rootd to stop tranfer
         close(fd);
         return -1;
      }
   }

   Info("GetFile", "getting file %s (%lld bytes, starting at %lld)",
        localName, size, restartat);

   TStopwatch timer;
   timer.Start();

   char *buf = new char[fBlockSize];
   char *buf2 = 0;
   if (fMode == kAscii)
      buf2 = new char[fBlockSize];

   Long64_t pos = restartat & ~(fBlockSize-1);
   Int_t skip = restartat - pos;

   while (pos < size) {
      Long64_t left = size - pos;
      if (left > fBlockSize)
         left = fBlockSize;

      Int_t n;
      while ((n = fSocket->RecvRaw(buf, Int_t(left-skip))) < 0 &&
             TSystem::GetErrno() == EINTR)
         TSystem::ResetErrno();

      if (n != Int_t(left-skip)) {
         Error("GetFile", "error receiving buffer of length %d, got %d",
               Int_t(left-skip), n);
         close(fd);
         delete [] buf; delete [] buf2;
         return -2;
      }

      // in case of ascii file, loop over buffer and remove \r's
      ssize_t siz;
      if (fMode == kAscii) {
         Int_t i = 0, j = 0;
         while (i < n) {
            if (buf[i] == '\r')
               i++;
            else
               buf2[j++] = buf[i++];
         }
         n = j;
         while ((siz = write(fd, buf2, n)) < 0 && TSystem::GetErrno() == EINTR)
            TSystem::ResetErrno();
      } else {
         while ((siz = write(fd, buf, n)) < 0 && TSystem::GetErrno() == EINTR)
            TSystem::ResetErrno();
      }

      if (siz < 0) {
         SysError("GetFile", "error writing file %s", localName);
         // send urgent message to rootd to stop tranfer
         close(fd);
         delete [] buf; delete [] buf2;
         return -1;
      }

      if (siz != n) {
         Error("GetFile", "error writing all requested bytes to file %s, wrote %ld of %d",
               localName, (Long_t)siz, n);
         // send urgent message to rootd to stop tranfer
         close(fd);
         delete [] buf; delete [] buf2;
         return -1;
      }

      fBytesRead  += left-skip;
      fgBytesRead += left-skip;

      fRestartAt = pos;   // bytes correctly received up till now

      pos += left;
      skip = 0;
   }

   delete [] buf; delete [] buf2;

#ifndef R__WIN32
   fchmod(fd, 0644);
#endif

   close(fd);

   fRestartAt = 0;

   // provide timing numbers
   Double_t speed, t = timer.RealTime();
   if (t > 0)
      speed = Double_t(size - restartat) / t;
   else
      speed = 0.0;
   if (speed > 524288)
      Info("GetFile", "%.3f seconds, %.2f Mbytes per second",
           t, speed / 1048576);
   else if (speed > 512)
      Info("GetFile", "%.3f seconds, %.2f Kbytes per second",
           t, speed / 1024);
   else
      Info("GetFile", "%.3f seconds, %.2f bytes per second",
           t, speed);

   return Long64_t(size - restartat);
}

//______________________________________________________________________________
Int_t TFTP::ChangeDirectory(const char *dir) const
{
   // Change the remote directory. If the remote directory contains a .message
   // file and it is < 1024 characters then the contents is echoed back.
   // Returns 0 in case of success and -1 in case of failure.

   if (!IsOpen()) return -1;

   if (!dir || !*dir) {
      Error("ChangeDirectory", "illegal directory name specified");
      return -1;
   }

   if (fSocket->Send(Form("%s", dir), kROOTD_CHDIR) < 0) {
      Error("ChangeDirectory", "error sending kROOTD_CHDIR command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("ChangeDirectory", "error receiving chdir confirmation");
      return -1;
   }
   if (what == kMESS_STRING) {
      Printf("%s\n", mess);

      if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
         Error("ChangeDirectory", "error receiving chdir confirmation");
         return -1;
      }
   }

   Info("ChangeDirectory", "%s", mess);

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::MakeDirectory(const char *dir, Bool_t print) const
{
   // Make a remote directory. Anonymous users may not create directories.
   // Returns 0 in case of success and -1 in case of failure.

   if (!IsOpen()) return -1;

   if (!dir || !*dir) {
      Error("MakeDirectory", "illegal directory name specified");
      return -1;
   }

   if (fSocket->Send(Form("%s", dir), kROOTD_MKDIR) < 0) {
      Error("MakeDirectory", "error sending kROOTD_MKDIR command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("MakeDirectory", "error receiving mkdir confirmation");
      return -1;
   }

   if (print)
      Info("MakeDirectory", "%s", mess);

   if (!strncmp(mess,"OK:",3))
      return 1;

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::DeleteDirectory(const char *dir) const
{
   // Delete a remote directory. Anonymous users may not delete directories.
   // Returns 0 in case of success and -1 in case of failure.

   if (!IsOpen()) return -1;

   if (!dir || !*dir) {
      Error("DeleteDirectory", "illegal directory name specified");
      return -1;
   }

   if (fSocket->Send(Form("%s", dir), kROOTD_RMDIR) < 0) {
      Error("DeleteDirectory", "error sending kROOTD_RMDIR command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("DeleteDirectory", "error receiving rmdir confirmation");
      return -1;
   }

   Info("DeleteDirectory", "%s", mess);

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::ListDirectory(Option_t *cmd) const
{
   // List remote directory. With cmd you specify the options and directory
   // to be listed to ls. Returns 0 in case of success and -1 in case of
   // failure.

   if (!IsOpen()) return -1;

   if (!cmd || !*cmd)
      cmd = "ls .";

   if (fSocket->Send(Form("%s", cmd), kROOTD_LSDIR) < 0) {
      Error("ListDirectory", "error sending kROOTD_LSDIR command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   do {
      if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
         Error("ListDirectory", "error receiving lsdir confirmation");
         return -1;
      }
      printf("%s", mess);
   } while (what == kMESS_STRING);

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::PrintDirectory() const
{
   // Print path of remote working directory. Returns 0 in case of succes and
   // -1 in cse of failure.

   if (!IsOpen()) return -1;

   if (fSocket->Send("", kROOTD_PWD) < 0) {
      Error("DeleteDirectory", "error sending kROOTD_PWD command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("PrintDirectory", "error receiving pwd confirmation");
      return -1;
   }

   Info("PrintDirectory", "%s", mess);

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::RenameFile(const char *file1, const char *file2) const
{
   // Rename a remote file. Anonymous users may not rename files.
   // Returns 0 in case of success and -1 in case of failure.

   if (!IsOpen()) return -1;

   if (!file1 || !file2 || !*file1 || !*file2) {
      Error("RenameFile", "illegal file names specified");
      return -1;
   }

   if (fSocket->Send(Form("%s %s", file1, file2), kROOTD_MV) < 0) {
      Error("RenameFile", "error sending kROOTD_MV command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("RenameFile", "error receiving mv confirmation");
      return -1;
   }

   Info("RenameFile", "%s", mess);

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::DeleteFile(const char *file) const
{
   // Delete a remote file. Anonymous users may not delete files.
   // Returns 0 in case of success and -1 in case of failure.

   if (!IsOpen()) return -1;

   if (!file || !*file) {
      Error("DeleteFile", "illegal file name specified");
      return -1;
   }

   if (fSocket->Send(Form("%s", file), kROOTD_RM) < 0) {
      Error("DeleteFile", "error sending kROOTD_RM command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("DeleteFile", "error receiving rm confirmation");
      return -1;
   }

   Info("DeleteFile", "%s", mess);

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::ChangePermission(const char *file, Int_t mode) const
{
   // Change permissions of a remote file. Anonymous users may not
   // chnage permissions. Returns 0 in case of success and -1 in case
   // of failure.

   if (!IsOpen()) return -1;

   if (!file || !*file) {
      Error("ChangePermission", "illegal file name specified");
      return -1;
   }

   if (fSocket->Send(Form("%s %d", file, mode), kROOTD_CHMOD) < 0) {
      Error("ChangePermission", "error sending kROOTD_CHMOD command");
      return -1;
   }

   Int_t what;
   char  mess[1024];

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("ChangePermission", "error receiving chmod confirmation");
      return -1;
   }

   Info("ChangePermission", "%s", mess);

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::Close()
{
   // Close ftp connection. Returns 0 in case of success and -1 in case of
   // failure.

   if (!IsOpen()) return -1;

   if (fSocket->Send(kROOTD_CLOSE) < 0) {
      Error("Close", "error sending kROOTD_CLOSE command");
      return -1;
   }

   // Ask for remote shutdown
   if (fProtocol > 6)
      fSocket->Send(kROOTD_BYE);

   // Remove from the list of Sockets
   {
      R__LOCKGUARD2(gROOTMutex);
      gROOT->GetListOfSockets()->Remove(this);
   }

   // Delete socket here
   SafeDelete(fSocket);

   return 0;
}

//______________________________________________________________________________
Bool_t TFTP::OpenDirectory(const char *dir, Bool_t print)
{
   // Open a directory via rootd.
   // Returns kTRUE in case of success.
   // Returns kFALSE in case of error.

   fDir = kFALSE;

   if (!IsOpen()) return fDir;

   if (fProtocol < 12) {
      Error("OpenDirectory", "call not supported by remote rootd");
      return fDir;
   }

   if (!dir || !*dir) {
      Error("OpenDirectory", "illegal directory name specified");
      return fDir;
   }

   if (fSocket->Send(Form("%s", dir), kROOTD_OPENDIR) < 0) {
      Error("OpenDirectory", "error sending kROOTD_OPENDIR command");
      return fDir;
   }

   Int_t what;
   char  mess[1024];;

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("OpenDirectory", "error receiving opendir confirmation");
      return fDir;
   }

   if (print)
      Info("OpenDirectory", "%s", mess);

   if (!strncmp(mess,"OK:",3)) {
      fDir = kTRUE;
      return fDir;
   }
   return fDir;
}

//______________________________________________________________________________
void TFTP::FreeDirectory(Bool_t print)
{
   // Free a remotely open directory via rootd.

   if (!IsOpen() || !fDir) return;

   if (fProtocol < 12) {
      Error("FreeDirectory", "call not supported by remote rootd");
      return;
   }

   if (fSocket->Send(kROOTD_FREEDIR) < 0) {
      Error("FreeDirectory", "error sending kROOTD_FREEDIR command");
      return;
   }

   Int_t what;
   char  mess[1024];;

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("FreeDirectory", "error receiving freedir confirmation");
      return;
   }

   if (print)
      Info("FreeDirectory", "%s", mess);

   return;
}

//______________________________________________________________________________
const char *TFTP::GetDirEntry(Bool_t print)
{
   // Get directory entry via rootd.
   // Returns 0 in case no more entries or in case of error.

   static char dirent[1024] = {0};

   if (!IsOpen() || !fDir) return 0;

   if (fProtocol < 12) {
      Error("GetDirEntry", "call not supported by remote rootd");
      return 0;
   }

   if (fSocket->Send(kROOTD_DIRENTRY) < 0) {
      Error("GetDirEntry", "error sending kROOTD_DIRENTRY command");
      return 0;
   }

   Int_t what;
   char  mess[1024];;

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("GetDirEntry", "error receiving dir entry confirmation");
      return 0;
   }

   if (print)
      Info("GetDirEntry", "%s", mess);

   if (!strncmp(mess,"OK:",3)) {
      strlcpy(dirent,mess+3, sizeof(dirent));
      return (const char *)dirent;
   }

   return 0;
}

//______________________________________________________________________________
Int_t TFTP::GetPathInfo(const char *path, FileStat_t &buf, Bool_t print)
{
   // Get info about a file. Info is returned in the form of a FileStat_t
   // structure (see TSystem.h).
   // The function returns 0 in case of success and 1 if the file could
   // not be stat'ed.

   TUrl url(path);

   if (!IsOpen()) return 1;

   if (fProtocol < 12) {
      Error("GetPathInfo", "call not supported by remote rootd");
      return 1;
   }

   if (!path || !*path) {
      Error("GetPathInfo", "illegal path name specified");
      return 1;
   }

   if (fSocket->Send(Form("%s", path), kROOTD_FSTAT) < 0) {
      Error("GetPathInfo", "error sending kROOTD_FSTAT command");
      return 1;
   }

   Int_t what;
   char  mess[1024];;

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("GetPathInfo", "error receiving fstat confirmation");
      return 1;
   }
   if (print)
      Info("GetPathInfo", "%s", mess);

   Int_t    mode, uid, gid, islink;
   Long_t   id, flags, dev, ino, mtime;
   Long64_t size;
   if (fProtocol > 12) {
#ifdef R__WIN32
      sscanf(mess, "%ld %ld %d %d %d %I64d %ld %d", &dev, &ino, &mode,
             &uid, &gid, &size, &mtime, &islink);
#else
      sscanf(mess, "%ld %ld %d %d %d %lld %ld %d", &dev, &ino, &mode,
             &uid, &gid, &size, &mtime, &islink);
#endif
      if (dev == -1)
         return 1;
      buf.fDev    = dev;
      buf.fIno    = ino;
      buf.fMode   = mode;
      buf.fUid    = uid;
      buf.fGid    = gid;
      buf.fSize   = size;
      buf.fMtime  = mtime;
      buf.fIsLink = (islink == 1);
   } else {
#ifdef R__WIN32
      sscanf(mess, "%ld %I64d %ld %ld", &id, &size, &flags, &mtime);
#else
      sscanf(mess, "%ld %lld %ld %ld", &id, &size, &flags, &mtime);
#endif
      if (id == -1)
         return 1;
      buf.fDev    = (id >> 24);
      buf.fIno    = (id & 0x00FFFFFF);
      if (flags == 0)
         buf.fMode = kS_IFREG;
      if (flags & 1)
         buf.fMode = (kS_IFREG|kS_IXUSR|kS_IXGRP|kS_IXOTH);
      if (flags & 2)
         buf.fMode = kS_IFDIR;
      if (flags & 4)
         buf.fMode = kS_IFSOCK;
      buf.fSize   = size;
      buf.fMtime  = mtime;
   }

   return 0;
}

//______________________________________________________________________________
Bool_t TFTP::AccessPathName(const char *path, EAccessMode mode, Bool_t print)
{
   // Returns kFALSE if one can access a file using the specified access mode.
   // Mode is the same as for the Unix access(2) function.
   // Attention, bizarre convention of return value!!

   if (!IsOpen()) return kTRUE;

   if (fProtocol < 12) {
      Error("AccessPathName", "call not supported by remote rootd");
      return kTRUE;
   }

   if (!path || !*path) {
      Error("AccessPathName", "illegal path name specified");
      return kTRUE;
   }

   if (fSocket->Send(Form("%s %d", path, mode), kROOTD_ACCESS) < 0) {
      Error("AccessPathName", "error sending kROOTD_ACCESS command");
      return kTRUE;
   }

   Int_t what;
   char  mess[1024];;

   if (fSocket->Recv(mess, sizeof(mess), what) < 0) {
      Error("AccessPathName", "error receiving access confirmation");
      return kTRUE;
   }
   if (print)
      Info("AccessPathName", "%s", mess);

   if (!strncmp(mess,"OK",2))
      return kFALSE;
   else
      return kTRUE;
}
 TFTP.cxx:1
 TFTP.cxx:2
 TFTP.cxx:3
 TFTP.cxx:4
 TFTP.cxx:5
 TFTP.cxx:6
 TFTP.cxx:7
 TFTP.cxx:8
 TFTP.cxx:9
 TFTP.cxx:10
 TFTP.cxx:11
 TFTP.cxx:12
 TFTP.cxx:13
 TFTP.cxx:14
 TFTP.cxx:15
 TFTP.cxx:16
 TFTP.cxx:17
 TFTP.cxx:18
 TFTP.cxx:19
 TFTP.cxx:20
 TFTP.cxx:21
 TFTP.cxx:22
 TFTP.cxx:23
 TFTP.cxx:24
 TFTP.cxx:25
 TFTP.cxx:26
 TFTP.cxx:27
 TFTP.cxx:28
 TFTP.cxx:29
 TFTP.cxx:30
 TFTP.cxx:31
 TFTP.cxx:32
 TFTP.cxx:33
 TFTP.cxx:34
 TFTP.cxx:35
 TFTP.cxx:36
 TFTP.cxx:37
 TFTP.cxx:38
 TFTP.cxx:39
 TFTP.cxx:40
 TFTP.cxx:41
 TFTP.cxx:42
 TFTP.cxx:43
 TFTP.cxx:44
 TFTP.cxx:45
 TFTP.cxx:46
 TFTP.cxx:47
 TFTP.cxx:48
 TFTP.cxx:49
 TFTP.cxx:50
 TFTP.cxx:51
 TFTP.cxx:52
 TFTP.cxx:53
 TFTP.cxx:54
 TFTP.cxx:55
 TFTP.cxx:56
 TFTP.cxx:57
 TFTP.cxx:58
 TFTP.cxx:59
 TFTP.cxx:60
 TFTP.cxx:61
 TFTP.cxx:62
 TFTP.cxx:63
 TFTP.cxx:64
 TFTP.cxx:65
 TFTP.cxx:66
 TFTP.cxx:67
 TFTP.cxx:68
 TFTP.cxx:69
 TFTP.cxx:70
 TFTP.cxx:71
 TFTP.cxx:72
 TFTP.cxx:73
 TFTP.cxx:74
 TFTP.cxx:75
 TFTP.cxx:76
 TFTP.cxx:77
 TFTP.cxx:78
 TFTP.cxx:79
 TFTP.cxx:80
 TFTP.cxx:81
 TFTP.cxx:82
 TFTP.cxx:83
 TFTP.cxx:84
 TFTP.cxx:85
 TFTP.cxx:86
 TFTP.cxx:87
 TFTP.cxx:88
 TFTP.cxx:89
 TFTP.cxx:90
 TFTP.cxx:91
 TFTP.cxx:92
 TFTP.cxx:93
 TFTP.cxx:94
 TFTP.cxx:95
 TFTP.cxx:96
 TFTP.cxx:97
 TFTP.cxx:98
 TFTP.cxx:99
 TFTP.cxx:100
 TFTP.cxx:101
 TFTP.cxx:102
 TFTP.cxx:103
 TFTP.cxx:104
 TFTP.cxx:105
 TFTP.cxx:106
 TFTP.cxx:107
 TFTP.cxx:108
 TFTP.cxx:109
 TFTP.cxx:110
 TFTP.cxx:111
 TFTP.cxx:112
 TFTP.cxx:113
 TFTP.cxx:114
 TFTP.cxx:115
 TFTP.cxx:116
 TFTP.cxx:117
 TFTP.cxx:118
 TFTP.cxx:119
 TFTP.cxx:120
 TFTP.cxx:121
 TFTP.cxx:122
 TFTP.cxx:123
 TFTP.cxx:124
 TFTP.cxx:125
 TFTP.cxx:126
 TFTP.cxx:127
 TFTP.cxx:128
 TFTP.cxx:129
 TFTP.cxx:130
 TFTP.cxx:131
 TFTP.cxx:132
 TFTP.cxx:133
 TFTP.cxx:134
 TFTP.cxx:135
 TFTP.cxx:136
 TFTP.cxx:137
 TFTP.cxx:138
 TFTP.cxx:139
 TFTP.cxx:140
 TFTP.cxx:141
 TFTP.cxx:142
 TFTP.cxx:143
 TFTP.cxx:144
 TFTP.cxx:145
 TFTP.cxx:146
 TFTP.cxx:147
 TFTP.cxx:148
 TFTP.cxx:149
 TFTP.cxx:150
 TFTP.cxx:151
 TFTP.cxx:152
 TFTP.cxx:153
 TFTP.cxx:154
 TFTP.cxx:155
 TFTP.cxx:156
 TFTP.cxx:157
 TFTP.cxx:158
 TFTP.cxx:159
 TFTP.cxx:160
 TFTP.cxx:161
 TFTP.cxx:162
 TFTP.cxx:163
 TFTP.cxx:164
 TFTP.cxx:165
 TFTP.cxx:166
 TFTP.cxx:167
 TFTP.cxx:168
 TFTP.cxx:169
 TFTP.cxx:170
 TFTP.cxx:171
 TFTP.cxx:172
 TFTP.cxx:173
 TFTP.cxx:174
 TFTP.cxx:175
 TFTP.cxx:176
 TFTP.cxx:177
 TFTP.cxx:178
 TFTP.cxx:179
 TFTP.cxx:180
 TFTP.cxx:181
 TFTP.cxx:182
 TFTP.cxx:183
 TFTP.cxx:184
 TFTP.cxx:185
 TFTP.cxx:186
 TFTP.cxx:187
 TFTP.cxx:188
 TFTP.cxx:189
 TFTP.cxx:190
 TFTP.cxx:191
 TFTP.cxx:192
 TFTP.cxx:193
 TFTP.cxx:194
 TFTP.cxx:195
 TFTP.cxx:196
 TFTP.cxx:197
 TFTP.cxx:198
 TFTP.cxx:199
 TFTP.cxx:200
 TFTP.cxx:201
 TFTP.cxx:202
 TFTP.cxx:203
 TFTP.cxx:204
 TFTP.cxx:205
 TFTP.cxx:206
 TFTP.cxx:207
 TFTP.cxx:208
 TFTP.cxx:209
 TFTP.cxx:210
 TFTP.cxx:211
 TFTP.cxx:212
 TFTP.cxx:213
 TFTP.cxx:214
 TFTP.cxx:215
 TFTP.cxx:216
 TFTP.cxx:217
 TFTP.cxx:218
 TFTP.cxx:219
 TFTP.cxx:220
 TFTP.cxx:221
 TFTP.cxx:222
 TFTP.cxx:223
 TFTP.cxx:224
 TFTP.cxx:225
 TFTP.cxx:226
 TFTP.cxx:227
 TFTP.cxx:228
 TFTP.cxx:229
 TFTP.cxx:230
 TFTP.cxx:231
 TFTP.cxx:232
 TFTP.cxx:233
 TFTP.cxx:234
 TFTP.cxx:235
 TFTP.cxx:236
 TFTP.cxx:237
 TFTP.cxx:238
 TFTP.cxx:239
 TFTP.cxx:240
 TFTP.cxx:241
 TFTP.cxx:242
 TFTP.cxx:243
 TFTP.cxx:244
 TFTP.cxx:245
 TFTP.cxx:246
 TFTP.cxx:247
 TFTP.cxx:248
 TFTP.cxx:249
 TFTP.cxx:250
 TFTP.cxx:251
 TFTP.cxx:252
 TFTP.cxx:253
 TFTP.cxx:254
 TFTP.cxx:255
 TFTP.cxx:256
 TFTP.cxx:257
 TFTP.cxx:258
 TFTP.cxx:259
 TFTP.cxx:260
 TFTP.cxx:261
 TFTP.cxx:262
 TFTP.cxx:263
 TFTP.cxx:264
 TFTP.cxx:265
 TFTP.cxx:266
 TFTP.cxx:267
 TFTP.cxx:268
 TFTP.cxx:269
 TFTP.cxx:270
 TFTP.cxx:271
 TFTP.cxx:272
 TFTP.cxx:273
 TFTP.cxx:274
 TFTP.cxx:275
 TFTP.cxx:276
 TFTP.cxx:277
 TFTP.cxx:278
 TFTP.cxx:279
 TFTP.cxx:280
 TFTP.cxx:281
 TFTP.cxx:282
 TFTP.cxx:283
 TFTP.cxx:284
 TFTP.cxx:285
 TFTP.cxx:286
 TFTP.cxx:287
 TFTP.cxx:288
 TFTP.cxx:289
 TFTP.cxx:290
 TFTP.cxx:291
 TFTP.cxx:292
 TFTP.cxx:293
 TFTP.cxx:294
 TFTP.cxx:295
 TFTP.cxx:296
 TFTP.cxx:297
 TFTP.cxx:298
 TFTP.cxx:299
 TFTP.cxx:300
 TFTP.cxx:301
 TFTP.cxx:302
 TFTP.cxx:303
 TFTP.cxx:304
 TFTP.cxx:305
 TFTP.cxx:306
 TFTP.cxx:307
 TFTP.cxx:308
 TFTP.cxx:309
 TFTP.cxx:310
 TFTP.cxx:311
 TFTP.cxx:312
 TFTP.cxx:313
 TFTP.cxx:314
 TFTP.cxx:315
 TFTP.cxx:316
 TFTP.cxx:317
 TFTP.cxx:318
 TFTP.cxx:319
 TFTP.cxx:320
 TFTP.cxx:321
 TFTP.cxx:322
 TFTP.cxx:323
 TFTP.cxx:324
 TFTP.cxx:325
 TFTP.cxx:326
 TFTP.cxx:327
 TFTP.cxx:328
 TFTP.cxx:329
 TFTP.cxx:330
 TFTP.cxx:331
 TFTP.cxx:332
 TFTP.cxx:333
 TFTP.cxx:334
 TFTP.cxx:335
 TFTP.cxx:336
 TFTP.cxx:337
 TFTP.cxx:338
 TFTP.cxx:339
 TFTP.cxx:340
 TFTP.cxx:341
 TFTP.cxx:342
 TFTP.cxx:343
 TFTP.cxx:344
 TFTP.cxx:345
 TFTP.cxx:346
 TFTP.cxx:347
 TFTP.cxx:348
 TFTP.cxx:349
 TFTP.cxx:350
 TFTP.cxx:351
 TFTP.cxx:352
 TFTP.cxx:353
 TFTP.cxx:354
 TFTP.cxx:355
 TFTP.cxx:356
 TFTP.cxx:357
 TFTP.cxx:358
 TFTP.cxx:359
 TFTP.cxx:360
 TFTP.cxx:361
 TFTP.cxx:362
 TFTP.cxx:363
 TFTP.cxx:364
 TFTP.cxx:365
 TFTP.cxx:366
 TFTP.cxx:367
 TFTP.cxx:368
 TFTP.cxx:369
 TFTP.cxx:370
 TFTP.cxx:371
 TFTP.cxx:372
 TFTP.cxx:373
 TFTP.cxx:374
 TFTP.cxx:375
 TFTP.cxx:376
 TFTP.cxx:377
 TFTP.cxx:378
 TFTP.cxx:379
 TFTP.cxx:380
 TFTP.cxx:381
 TFTP.cxx:382
 TFTP.cxx:383
 TFTP.cxx:384
 TFTP.cxx:385
 TFTP.cxx:386
 TFTP.cxx:387
 TFTP.cxx:388
 TFTP.cxx:389
 TFTP.cxx:390
 TFTP.cxx:391
 TFTP.cxx:392
 TFTP.cxx:393
 TFTP.cxx:394
 TFTP.cxx:395
 TFTP.cxx:396
 TFTP.cxx:397
 TFTP.cxx:398
 TFTP.cxx:399
 TFTP.cxx:400
 TFTP.cxx:401
 TFTP.cxx:402
 TFTP.cxx:403
 TFTP.cxx:404
 TFTP.cxx:405
 TFTP.cxx:406
 TFTP.cxx:407
 TFTP.cxx:408
 TFTP.cxx:409
 TFTP.cxx:410
 TFTP.cxx:411
 TFTP.cxx:412
 TFTP.cxx:413
 TFTP.cxx:414
 TFTP.cxx:415
 TFTP.cxx:416
 TFTP.cxx:417
 TFTP.cxx:418
 TFTP.cxx:419
 TFTP.cxx:420
 TFTP.cxx:421
 TFTP.cxx:422
 TFTP.cxx:423
 TFTP.cxx:424
 TFTP.cxx:425
 TFTP.cxx:426
 TFTP.cxx:427
 TFTP.cxx:428
 TFTP.cxx:429
 TFTP.cxx:430
 TFTP.cxx:431
 TFTP.cxx:432
 TFTP.cxx:433
 TFTP.cxx:434
 TFTP.cxx:435
 TFTP.cxx:436
 TFTP.cxx:437
 TFTP.cxx:438
 TFTP.cxx:439
 TFTP.cxx:440
 TFTP.cxx:441
 TFTP.cxx:442
 TFTP.cxx:443
 TFTP.cxx:444
 TFTP.cxx:445
 TFTP.cxx:446
 TFTP.cxx:447
 TFTP.cxx:448
 TFTP.cxx:449
 TFTP.cxx:450
 TFTP.cxx:451
 TFTP.cxx:452
 TFTP.cxx:453
 TFTP.cxx:454
 TFTP.cxx:455
 TFTP.cxx:456
 TFTP.cxx:457
 TFTP.cxx:458
 TFTP.cxx:459
 TFTP.cxx:460
 TFTP.cxx:461
 TFTP.cxx:462
 TFTP.cxx:463
 TFTP.cxx:464
 TFTP.cxx:465
 TFTP.cxx:466
 TFTP.cxx:467
 TFTP.cxx:468
 TFTP.cxx:469
 TFTP.cxx:470
 TFTP.cxx:471
 TFTP.cxx:472
 TFTP.cxx:473
 TFTP.cxx:474
 TFTP.cxx:475
 TFTP.cxx:476
 TFTP.cxx:477
 TFTP.cxx:478
 TFTP.cxx:479
 TFTP.cxx:480
 TFTP.cxx:481
 TFTP.cxx:482
 TFTP.cxx:483
 TFTP.cxx:484
 TFTP.cxx:485
 TFTP.cxx:486
 TFTP.cxx:487
 TFTP.cxx:488
 TFTP.cxx:489
 TFTP.cxx:490
 TFTP.cxx:491
 TFTP.cxx:492
 TFTP.cxx:493
 TFTP.cxx:494
 TFTP.cxx:495
 TFTP.cxx:496
 TFTP.cxx:497
 TFTP.cxx:498
 TFTP.cxx:499
 TFTP.cxx:500
 TFTP.cxx:501
 TFTP.cxx:502
 TFTP.cxx:503
 TFTP.cxx:504
 TFTP.cxx:505
 TFTP.cxx:506
 TFTP.cxx:507
 TFTP.cxx:508
 TFTP.cxx:509
 TFTP.cxx:510
 TFTP.cxx:511
 TFTP.cxx:512
 TFTP.cxx:513
 TFTP.cxx:514
 TFTP.cxx:515
 TFTP.cxx:516
 TFTP.cxx:517
 TFTP.cxx:518
 TFTP.cxx:519
 TFTP.cxx:520
 TFTP.cxx:521
 TFTP.cxx:522
 TFTP.cxx:523
 TFTP.cxx:524
 TFTP.cxx:525
 TFTP.cxx:526
 TFTP.cxx:527
 TFTP.cxx:528
 TFTP.cxx:529
 TFTP.cxx:530
 TFTP.cxx:531
 TFTP.cxx:532
 TFTP.cxx:533
 TFTP.cxx:534
 TFTP.cxx:535
 TFTP.cxx:536
 TFTP.cxx:537
 TFTP.cxx:538
 TFTP.cxx:539
 TFTP.cxx:540
 TFTP.cxx:541
 TFTP.cxx:542
 TFTP.cxx:543
 TFTP.cxx:544
 TFTP.cxx:545
 TFTP.cxx:546
 TFTP.cxx:547
 TFTP.cxx:548
 TFTP.cxx:549
 TFTP.cxx:550
 TFTP.cxx:551
 TFTP.cxx:552
 TFTP.cxx:553
 TFTP.cxx:554
 TFTP.cxx:555
 TFTP.cxx:556
 TFTP.cxx:557
 TFTP.cxx:558
 TFTP.cxx:559
 TFTP.cxx:560
 TFTP.cxx:561
 TFTP.cxx:562
 TFTP.cxx:563
 TFTP.cxx:564
 TFTP.cxx:565
 TFTP.cxx:566
 TFTP.cxx:567
 TFTP.cxx:568
 TFTP.cxx:569
 TFTP.cxx:570
 TFTP.cxx:571
 TFTP.cxx:572
 TFTP.cxx:573
 TFTP.cxx:574
 TFTP.cxx:575
 TFTP.cxx:576
 TFTP.cxx:577
 TFTP.cxx:578
 TFTP.cxx:579
 TFTP.cxx:580
 TFTP.cxx:581
 TFTP.cxx:582
 TFTP.cxx:583
 TFTP.cxx:584
 TFTP.cxx:585
 TFTP.cxx:586
 TFTP.cxx:587
 TFTP.cxx:588
 TFTP.cxx:589
 TFTP.cxx:590
 TFTP.cxx:591
 TFTP.cxx:592
 TFTP.cxx:593
 TFTP.cxx:594
 TFTP.cxx:595
 TFTP.cxx:596
 TFTP.cxx:597
 TFTP.cxx:598
 TFTP.cxx:599
 TFTP.cxx:600
 TFTP.cxx:601
 TFTP.cxx:602
 TFTP.cxx:603
 TFTP.cxx:604
 TFTP.cxx:605
 TFTP.cxx:606
 TFTP.cxx:607
 TFTP.cxx:608
 TFTP.cxx:609
 TFTP.cxx:610
 TFTP.cxx:611
 TFTP.cxx:612
 TFTP.cxx:613
 TFTP.cxx:614
 TFTP.cxx:615
 TFTP.cxx:616
 TFTP.cxx:617
 TFTP.cxx:618
 TFTP.cxx:619
 TFTP.cxx:620
 TFTP.cxx:621
 TFTP.cxx:622
 TFTP.cxx:623
 TFTP.cxx:624
 TFTP.cxx:625
 TFTP.cxx:626
 TFTP.cxx:627
 TFTP.cxx:628
 TFTP.cxx:629
 TFTP.cxx:630
 TFTP.cxx:631
 TFTP.cxx:632
 TFTP.cxx:633
 TFTP.cxx:634
 TFTP.cxx:635
 TFTP.cxx:636
 TFTP.cxx:637
 TFTP.cxx:638
 TFTP.cxx:639
 TFTP.cxx:640
 TFTP.cxx:641
 TFTP.cxx:642
 TFTP.cxx:643
 TFTP.cxx:644
 TFTP.cxx:645
 TFTP.cxx:646
 TFTP.cxx:647
 TFTP.cxx:648
 TFTP.cxx:649
 TFTP.cxx:650
 TFTP.cxx:651
 TFTP.cxx:652
 TFTP.cxx:653
 TFTP.cxx:654
 TFTP.cxx:655
 TFTP.cxx:656
 TFTP.cxx:657
 TFTP.cxx:658
 TFTP.cxx:659
 TFTP.cxx:660
 TFTP.cxx:661
 TFTP.cxx:662
 TFTP.cxx:663
 TFTP.cxx:664
 TFTP.cxx:665
 TFTP.cxx:666
 TFTP.cxx:667
 TFTP.cxx:668
 TFTP.cxx:669
 TFTP.cxx:670
 TFTP.cxx:671
 TFTP.cxx:672
 TFTP.cxx:673
 TFTP.cxx:674
 TFTP.cxx:675
 TFTP.cxx:676
 TFTP.cxx:677
 TFTP.cxx:678
 TFTP.cxx:679
 TFTP.cxx:680
 TFTP.cxx:681
 TFTP.cxx:682
 TFTP.cxx:683
 TFTP.cxx:684
 TFTP.cxx:685
 TFTP.cxx:686
 TFTP.cxx:687
 TFTP.cxx:688
 TFTP.cxx:689
 TFTP.cxx:690
 TFTP.cxx:691
 TFTP.cxx:692
 TFTP.cxx:693
 TFTP.cxx:694
 TFTP.cxx:695
 TFTP.cxx:696
 TFTP.cxx:697
 TFTP.cxx:698
 TFTP.cxx:699
 TFTP.cxx:700
 TFTP.cxx:701
 TFTP.cxx:702
 TFTP.cxx:703
 TFTP.cxx:704
 TFTP.cxx:705
 TFTP.cxx:706
 TFTP.cxx:707
 TFTP.cxx:708
 TFTP.cxx:709
 TFTP.cxx:710
 TFTP.cxx:711
 TFTP.cxx:712
 TFTP.cxx:713
 TFTP.cxx:714
 TFTP.cxx:715
 TFTP.cxx:716
 TFTP.cxx:717
 TFTP.cxx:718
 TFTP.cxx:719
 TFTP.cxx:720
 TFTP.cxx:721
 TFTP.cxx:722
 TFTP.cxx:723
 TFTP.cxx:724
 TFTP.cxx:725
 TFTP.cxx:726
 TFTP.cxx:727
 TFTP.cxx:728
 TFTP.cxx:729
 TFTP.cxx:730
 TFTP.cxx:731
 TFTP.cxx:732
 TFTP.cxx:733
 TFTP.cxx:734
 TFTP.cxx:735
 TFTP.cxx:736
 TFTP.cxx:737
 TFTP.cxx:738
 TFTP.cxx:739
 TFTP.cxx:740
 TFTP.cxx:741
 TFTP.cxx:742
 TFTP.cxx:743
 TFTP.cxx:744
 TFTP.cxx:745
 TFTP.cxx:746
 TFTP.cxx:747
 TFTP.cxx:748
 TFTP.cxx:749
 TFTP.cxx:750
 TFTP.cxx:751
 TFTP.cxx:752
 TFTP.cxx:753
 TFTP.cxx:754
 TFTP.cxx:755
 TFTP.cxx:756
 TFTP.cxx:757
 TFTP.cxx:758
 TFTP.cxx:759
 TFTP.cxx:760
 TFTP.cxx:761
 TFTP.cxx:762
 TFTP.cxx:763
 TFTP.cxx:764
 TFTP.cxx:765
 TFTP.cxx:766
 TFTP.cxx:767
 TFTP.cxx:768
 TFTP.cxx:769
 TFTP.cxx:770
 TFTP.cxx:771
 TFTP.cxx:772
 TFTP.cxx:773
 TFTP.cxx:774
 TFTP.cxx:775
 TFTP.cxx:776
 TFTP.cxx:777
 TFTP.cxx:778
 TFTP.cxx:779
 TFTP.cxx:780
 TFTP.cxx:781
 TFTP.cxx:782
 TFTP.cxx:783
 TFTP.cxx:784
 TFTP.cxx:785
 TFTP.cxx:786
 TFTP.cxx:787
 TFTP.cxx:788
 TFTP.cxx:789
 TFTP.cxx:790
 TFTP.cxx:791
 TFTP.cxx:792
 TFTP.cxx:793
 TFTP.cxx:794
 TFTP.cxx:795
 TFTP.cxx:796
 TFTP.cxx:797
 TFTP.cxx:798
 TFTP.cxx:799
 TFTP.cxx:800
 TFTP.cxx:801
 TFTP.cxx:802
 TFTP.cxx:803
 TFTP.cxx:804
 TFTP.cxx:805
 TFTP.cxx:806
 TFTP.cxx:807
 TFTP.cxx:808
 TFTP.cxx:809
 TFTP.cxx:810
 TFTP.cxx:811
 TFTP.cxx:812
 TFTP.cxx:813
 TFTP.cxx:814
 TFTP.cxx:815
 TFTP.cxx:816
 TFTP.cxx:817
 TFTP.cxx:818
 TFTP.cxx:819
 TFTP.cxx:820
 TFTP.cxx:821
 TFTP.cxx:822
 TFTP.cxx:823
 TFTP.cxx:824
 TFTP.cxx:825
 TFTP.cxx:826
 TFTP.cxx:827
 TFTP.cxx:828
 TFTP.cxx:829
 TFTP.cxx:830
 TFTP.cxx:831
 TFTP.cxx:832
 TFTP.cxx:833
 TFTP.cxx:834
 TFTP.cxx:835
 TFTP.cxx:836
 TFTP.cxx:837
 TFTP.cxx:838
 TFTP.cxx:839
 TFTP.cxx:840
 TFTP.cxx:841
 TFTP.cxx:842
 TFTP.cxx:843
 TFTP.cxx:844
 TFTP.cxx:845
 TFTP.cxx:846
 TFTP.cxx:847
 TFTP.cxx:848
 TFTP.cxx:849
 TFTP.cxx:850
 TFTP.cxx:851
 TFTP.cxx:852
 TFTP.cxx:853
 TFTP.cxx:854
 TFTP.cxx:855
 TFTP.cxx:856
 TFTP.cxx:857
 TFTP.cxx:858
 TFTP.cxx:859
 TFTP.cxx:860
 TFTP.cxx:861
 TFTP.cxx:862
 TFTP.cxx:863
 TFTP.cxx:864
 TFTP.cxx:865
 TFTP.cxx:866
 TFTP.cxx:867
 TFTP.cxx:868
 TFTP.cxx:869
 TFTP.cxx:870
 TFTP.cxx:871
 TFTP.cxx:872
 TFTP.cxx:873
 TFTP.cxx:874
 TFTP.cxx:875
 TFTP.cxx:876
 TFTP.cxx:877
 TFTP.cxx:878
 TFTP.cxx:879
 TFTP.cxx:880
 TFTP.cxx:881
 TFTP.cxx:882
 TFTP.cxx:883
 TFTP.cxx:884
 TFTP.cxx:885
 TFTP.cxx:886
 TFTP.cxx:887
 TFTP.cxx:888
 TFTP.cxx:889
 TFTP.cxx:890
 TFTP.cxx:891
 TFTP.cxx:892
 TFTP.cxx:893
 TFTP.cxx:894
 TFTP.cxx:895
 TFTP.cxx:896
 TFTP.cxx:897
 TFTP.cxx:898
 TFTP.cxx:899
 TFTP.cxx:900
 TFTP.cxx:901
 TFTP.cxx:902
 TFTP.cxx:903
 TFTP.cxx:904
 TFTP.cxx:905
 TFTP.cxx:906
 TFTP.cxx:907
 TFTP.cxx:908
 TFTP.cxx:909
 TFTP.cxx:910
 TFTP.cxx:911
 TFTP.cxx:912
 TFTP.cxx:913
 TFTP.cxx:914
 TFTP.cxx:915
 TFTP.cxx:916
 TFTP.cxx:917
 TFTP.cxx:918
 TFTP.cxx:919
 TFTP.cxx:920
 TFTP.cxx:921
 TFTP.cxx:922
 TFTP.cxx:923
 TFTP.cxx:924
 TFTP.cxx:925
 TFTP.cxx:926
 TFTP.cxx:927
 TFTP.cxx:928
 TFTP.cxx:929
 TFTP.cxx:930
 TFTP.cxx:931
 TFTP.cxx:932
 TFTP.cxx:933
 TFTP.cxx:934
 TFTP.cxx:935
 TFTP.cxx:936
 TFTP.cxx:937
 TFTP.cxx:938
 TFTP.cxx:939
 TFTP.cxx:940
 TFTP.cxx:941
 TFTP.cxx:942
 TFTP.cxx:943
 TFTP.cxx:944
 TFTP.cxx:945
 TFTP.cxx:946
 TFTP.cxx:947
 TFTP.cxx:948
 TFTP.cxx:949
 TFTP.cxx:950
 TFTP.cxx:951
 TFTP.cxx:952
 TFTP.cxx:953
 TFTP.cxx:954
 TFTP.cxx:955
 TFTP.cxx:956
 TFTP.cxx:957
 TFTP.cxx:958
 TFTP.cxx:959
 TFTP.cxx:960
 TFTP.cxx:961
 TFTP.cxx:962
 TFTP.cxx:963
 TFTP.cxx:964
 TFTP.cxx:965
 TFTP.cxx:966
 TFTP.cxx:967
 TFTP.cxx:968
 TFTP.cxx:969
 TFTP.cxx:970
 TFTP.cxx:971
 TFTP.cxx:972
 TFTP.cxx:973
 TFTP.cxx:974
 TFTP.cxx:975
 TFTP.cxx:976
 TFTP.cxx:977
 TFTP.cxx:978
 TFTP.cxx:979
 TFTP.cxx:980
 TFTP.cxx:981
 TFTP.cxx:982
 TFTP.cxx:983
 TFTP.cxx:984
 TFTP.cxx:985
 TFTP.cxx:986
 TFTP.cxx:987
 TFTP.cxx:988
 TFTP.cxx:989
 TFTP.cxx:990
 TFTP.cxx:991
 TFTP.cxx:992
 TFTP.cxx:993
 TFTP.cxx:994
 TFTP.cxx:995
 TFTP.cxx:996
 TFTP.cxx:997
 TFTP.cxx:998
 TFTP.cxx:999
 TFTP.cxx:1000
 TFTP.cxx:1001
 TFTP.cxx:1002
 TFTP.cxx:1003
 TFTP.cxx:1004
 TFTP.cxx:1005
 TFTP.cxx:1006
 TFTP.cxx:1007
 TFTP.cxx:1008
 TFTP.cxx:1009
 TFTP.cxx:1010
 TFTP.cxx:1011
 TFTP.cxx:1012
 TFTP.cxx:1013
 TFTP.cxx:1014
 TFTP.cxx:1015
 TFTP.cxx:1016
 TFTP.cxx:1017
 TFTP.cxx:1018
 TFTP.cxx:1019
 TFTP.cxx:1020
 TFTP.cxx:1021
 TFTP.cxx:1022
 TFTP.cxx:1023
 TFTP.cxx:1024
 TFTP.cxx:1025
 TFTP.cxx:1026
 TFTP.cxx:1027
 TFTP.cxx:1028
 TFTP.cxx:1029
 TFTP.cxx:1030
 TFTP.cxx:1031
 TFTP.cxx:1032
 TFTP.cxx:1033
 TFTP.cxx:1034
 TFTP.cxx:1035
 TFTP.cxx:1036
 TFTP.cxx:1037
 TFTP.cxx:1038
 TFTP.cxx:1039
 TFTP.cxx:1040
 TFTP.cxx:1041
 TFTP.cxx:1042
 TFTP.cxx:1043
 TFTP.cxx:1044
 TFTP.cxx:1045
 TFTP.cxx:1046
 TFTP.cxx:1047
 TFTP.cxx:1048
 TFTP.cxx:1049
 TFTP.cxx:1050
 TFTP.cxx:1051
 TFTP.cxx:1052
 TFTP.cxx:1053
 TFTP.cxx:1054
 TFTP.cxx:1055
 TFTP.cxx:1056
 TFTP.cxx:1057
 TFTP.cxx:1058
 TFTP.cxx:1059
 TFTP.cxx:1060
 TFTP.cxx:1061
 TFTP.cxx:1062
 TFTP.cxx:1063
 TFTP.cxx:1064
 TFTP.cxx:1065
 TFTP.cxx:1066
 TFTP.cxx:1067
 TFTP.cxx:1068
 TFTP.cxx:1069
 TFTP.cxx:1070
 TFTP.cxx:1071
 TFTP.cxx:1072
 TFTP.cxx:1073
 TFTP.cxx:1074
 TFTP.cxx:1075
 TFTP.cxx:1076
 TFTP.cxx:1077
 TFTP.cxx:1078
 TFTP.cxx:1079
 TFTP.cxx:1080
 TFTP.cxx:1081
 TFTP.cxx:1082
 TFTP.cxx:1083
 TFTP.cxx:1084
 TFTP.cxx:1085
 TFTP.cxx:1086
 TFTP.cxx:1087
 TFTP.cxx:1088
 TFTP.cxx:1089
 TFTP.cxx:1090
 TFTP.cxx:1091
 TFTP.cxx:1092
 TFTP.cxx:1093
 TFTP.cxx:1094
 TFTP.cxx:1095
 TFTP.cxx:1096
 TFTP.cxx:1097
 TFTP.cxx:1098
 TFTP.cxx:1099
 TFTP.cxx:1100
 TFTP.cxx:1101
 TFTP.cxx:1102
 TFTP.cxx:1103
 TFTP.cxx:1104
 TFTP.cxx:1105
 TFTP.cxx:1106
 TFTP.cxx:1107
 TFTP.cxx:1108
 TFTP.cxx:1109
 TFTP.cxx:1110
 TFTP.cxx:1111
 TFTP.cxx:1112
 TFTP.cxx:1113
 TFTP.cxx:1114
 TFTP.cxx:1115
 TFTP.cxx:1116
 TFTP.cxx:1117
 TFTP.cxx:1118
 TFTP.cxx:1119
 TFTP.cxx:1120
 TFTP.cxx:1121
 TFTP.cxx:1122
 TFTP.cxx:1123
 TFTP.cxx:1124
 TFTP.cxx:1125
 TFTP.cxx:1126
 TFTP.cxx:1127
 TFTP.cxx:1128
 TFTP.cxx:1129
 TFTP.cxx:1130
 TFTP.cxx:1131
 TFTP.cxx:1132
 TFTP.cxx:1133
 TFTP.cxx:1134
 TFTP.cxx:1135
 TFTP.cxx:1136
 TFTP.cxx:1137
 TFTP.cxx:1138
 TFTP.cxx:1139
 TFTP.cxx:1140
 TFTP.cxx:1141
 TFTP.cxx:1142
 TFTP.cxx:1143
 TFTP.cxx:1144