Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / modules / rosapps / applications / net / ncftp / libncftp / io.c
diff --git a/modules/rosapps/applications/net/ncftp/libncftp/io.c b/modules/rosapps/applications/net/ncftp/libncftp/io.c
new file mode 100644 (file)
index 0000000..b92dd04
--- /dev/null
@@ -0,0 +1,2824 @@
+/* io.c
+ *
+ * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
+ * All rights reserved.
+ *
+ */
+
+#include "syshdrs.h"
+
+static int gGotBrokenData = 0;
+
+#if defined(WIN32) || defined(_WINDOWS)
+#      define ASCII_TRANSLATION 0
+#endif
+
+#ifndef ASCII_TRANSLATION
+#      define ASCII_TRANSLATION 1
+#endif
+
+#if !defined(NO_SIGNALS) && (USE_SIO || !defined(SIGALRM) || !defined(SIGPIPE) || !defined(SIGINT))
+#      define NO_SIGNALS 1
+#endif
+
+#ifndef NO_SIGNALS
+
+#ifdef HAVE_SIGSETJMP
+static sigjmp_buf gBrokenDataJmp;
+#else
+static jmp_buf gBrokenDataJmp;
+#endif /* HAVE_SIGSETJMP */
+static int gCanBrokenDataJmp = 0;
+
+#endif /* NO_SIGNALS */
+
+
+#ifndef O_BINARY
+       /* Needed for platforms using different EOLN sequence (i.e. DOS) */
+#      ifdef _O_BINARY
+#              define O_BINARY _O_BINARY
+#      else
+#              define O_BINARY 0
+#      endif
+#endif
+
+static int WaitForRemoteInput(const FTPCIPtr cip);
+static int WaitForRemoteOutput(const FTPCIPtr cip);
+
+
+#ifndef NO_SIGNALS
+
+static void
+BrokenData(int signum)
+{
+       gGotBrokenData = signum;
+       if (gCanBrokenDataJmp != 0) {
+               gCanBrokenDataJmp = 0;
+#ifdef HAVE_SIGSETJMP
+               siglongjmp(gBrokenDataJmp, 1);
+#else
+               longjmp(gBrokenDataJmp, 1);
+#endif /* HAVE_SIGSETJMP */
+       }
+}      /* BrokenData */
+
+#endif /* NO_SIGNALS */
+
+
+
+
+void
+FTPInitIOTimer(const FTPCIPtr cip)
+{
+       cip->bytesTransferred = (longest_int) 0;
+       cip->expectedSize = kSizeUnknown;
+       cip->mdtm = kModTimeUnknown;
+       cip->rname = NULL;
+       cip->lname = NULL;
+       cip->kBytesPerSec = -1.0;
+       cip->percentCompleted = -1.0;
+       cip->sec = -1.0;
+       cip->secLeft = -1.0;
+       cip->nextProgressUpdate = 0;
+       cip->stalled = 0;
+       cip->dataTimedOut = 0;
+       cip->useProgressMeter = 1;
+       (void) gettimeofday(&cip->t0, NULL);
+}      /* FTPInitIOTimer */
+
+
+
+
+void
+FTPStartIOTimer(const FTPCIPtr cip)
+{
+       (void) gettimeofday(&cip->t0, NULL);
+       if (cip->progress != (FTPProgressMeterProc) 0)
+               (*cip->progress)(cip, kPrInitMsg);
+}      /* FTPStartIOTimer */
+
+
+
+
+void
+FTPUpdateIOTimer(const FTPCIPtr cip)
+{
+       double sec;
+       struct timeval *t0, t1;
+       time_t now;
+
+       (void) time(&now);
+       if (now < cip->nextProgressUpdate)
+               return;
+       now += 1;
+       cip->nextProgressUpdate = now;
+
+       (void) gettimeofday(&t1, NULL);
+       t0 = &cip->t0;
+
+       if (t0->tv_usec > t1.tv_usec) {
+               t1.tv_usec += 1000000;
+               t1.tv_sec--;
+       }
+       sec = ((double) (t1.tv_usec - t0->tv_usec) * 0.000001)
+               + (t1.tv_sec - t0->tv_sec);
+       if (sec > 0.0) {
+               cip->kBytesPerSec = ((double) cip->bytesTransferred) / (1024.0 * sec);
+       } else {
+               cip->kBytesPerSec = -1.0;
+       }
+       if (cip->expectedSize == kSizeUnknown) {
+               cip->percentCompleted = -1.0;
+               cip->secLeft = -1.0;
+       } else if (cip->expectedSize <= 0) {
+               cip->percentCompleted = 100.0;
+               cip->secLeft = 0.0;
+       } else {
+               cip->percentCompleted = ((double) (100.0 * (cip->bytesTransferred + cip->startPoint))) / ((double) cip->expectedSize);
+               if (cip->percentCompleted >= 100.0) {
+                       cip->percentCompleted = 100.0;
+                       cip->secLeft = 0.0;
+               } else if (cip->percentCompleted <= 0.0) {
+                       cip->secLeft = 999.0;
+               }
+               if (cip->kBytesPerSec > 0.0) {
+                       cip->secLeft = ((cip->expectedSize - cip->bytesTransferred - cip->startPoint) / 1024.0) / cip->kBytesPerSec;
+                       if (cip->secLeft < 0.0)
+                               cip->secLeft = 0.0;
+               }
+       }
+       cip->sec = sec;
+       if ((cip->progress != (FTPProgressMeterProc) 0) && (cip->useProgressMeter != 0))
+               (*cip->progress)(cip, kPrUpdateMsg);
+}      /* FTPUpdateIOTimer */
+
+
+
+
+void
+FTPStopIOTimer(const FTPCIPtr cip)
+{
+       cip->nextProgressUpdate = 0;    /* force last update */
+       FTPUpdateIOTimer(cip);
+       if (cip->progress != (FTPProgressMeterProc) 0)
+               (*cip->progress)(cip, kPrEndMsg);
+}      /* FTPStopIOTimer */
+
+
+
+
+/* This isn't too useful -- it mostly serves as an example so you can write
+ * your own function to do what you need to do with the listing.
+ */
+int
+FTPList(const FTPCIPtr cip, const int outfd, const int longMode, const char *const lsflag)
+{
+       const char *cmd;
+       char line[512];
+       char secondaryBuf[768];
+#ifndef NO_SIGNALS
+       char *secBufPtr, *secBufLimit;
+       int nread;
+       volatile int result;
+#else  /* NO_SIGNALS */
+       SReadlineInfo lsSrl;
+       int result;
+#endif /* NO_SIGNALS */
+
+       if (cip == NULL)
+               return (kErrBadParameter);
+       if (strcmp(cip->magic, kLibraryMagic))
+               return (kErrBadMagic);
+
+       cmd = (longMode != 0) ? "LIST" : "NLST";
+       if ((lsflag == NULL) || (lsflag[0] == '\0')) {
+               result = FTPStartDataCmd(cip, kNetReading, kTypeAscii, (longest_int) 0, "%s", cmd);
+       } else {
+               result = FTPStartDataCmd(cip, kNetReading, kTypeAscii, (longest_int) 0, "%s %s", cmd, lsflag);
+       }
+
+
+#ifdef NO_SIGNALS
+
+       if (result == 0) {
+               if (InitSReadlineInfo(&lsSrl, cip->dataSocket, secondaryBuf, sizeof(secondaryBuf), (int) cip->xferTimeout, 1) < 0) {
+                       /* Not really fdopen, but close in what we're trying to do. */
+                       result = kErrFdopenR;
+                       cip->errNo = kErrFdopenR;
+                       Error(cip, kDoPerror, "Could not fdopen.\n");
+                       return (result);
+               }
+
+               for (;;) {
+                       result = SReadline(&lsSrl, line, sizeof(line) - 2);
+                       if (result == kTimeoutErr) {
+                               /* timeout */
+                               Error(cip, kDontPerror, "Could not directory listing data -- timed out.\n");
+                               cip->errNo = kErrDataTimedOut;
+                               return (cip->errNo);
+                       } else if (result == 0) {
+                               /* end of listing -- done */
+                               cip->numListings++;
+                               break;
+                       } else if (result < 0) {
+                               /* error */
+                               Error(cip, kDoPerror, "Could not read directory listing data");
+                               result = kErrLISTFailed;
+                               cip->errNo = kErrLISTFailed;
+                               break;
+                       }
+
+                       (void) write(outfd, line, strlen(line));
+               }
+
+               DisposeSReadlineInfo(&lsSrl);
+               if (FTPEndDataCmd(cip, 1) < 0) {
+                       result = kErrLISTFailed;
+                       cip->errNo = kErrLISTFailed;
+               }
+       } else if (result == kErrGeneric) {
+               result = kErrLISTFailed;
+               cip->errNo = kErrLISTFailed;
+       }
+
+
+#else  /* NO_SIGNALS */
+
+       if (result == 0) {
+               /* This line sets the buffer pointer so that the first thing
+                * BufferGets will do is reset and fill the buffer using
+                * real I/O.
+                */
+               secBufPtr = secondaryBuf + sizeof(secondaryBuf);
+               secBufLimit = (char *) 0;
+
+               for (;;) {
+                       if (cip->xferTimeout > 0)
+                               (void) alarm(cip->xferTimeout);
+                       nread = BufferGets(line, sizeof(line), cip->dataSocket, secondaryBuf, &secBufPtr, &secBufLimit, sizeof(secondaryBuf));
+                       if (nread <= 0) {
+                               if (nread < 0)
+                                       break;
+                       } else {
+                               cip->bytesTransferred += (longest_int) nread;
+                               (void) STRNCAT(line, "\n");
+                               (void) write(outfd, line, strlen(line));
+                       }
+               }
+               if (cip->xferTimeout > 0)
+                       (void) alarm(0);
+               result = FTPEndDataCmd(cip, 1);
+               if (result < 0) {
+                       result = kErrLISTFailed;
+                       cip->errNo = kErrLISTFailed;
+               }
+               result = kNoErr;
+               cip->numListings++;
+       } else if (result == kErrGeneric) {
+               result = kErrLISTFailed;
+               cip->errNo = kErrLISTFailed;
+       }
+#endif /* NO_SIGNALS */
+       return (result);
+}      /* FTPList */
+
+
+
+
+static void
+FTPRequestMlsOptions(const FTPCIPtr cip)
+{
+       int f;
+       char optstr[128];
+       size_t optstrlen;
+
+       if (cip->usedMLS == 0) {
+               /* First MLSD/MLST ? */
+               cip->usedMLS = 1;
+
+               f = cip->mlsFeatures & kPreferredMlsOpts;
+               optstr[0] = '\0';
+
+               /* TYPE */
+               if ((f & kMlsOptType) != 0) {
+                       STRNCAT(optstr, "type;");
+               }
+
+               /* SIZE */
+               if ((f & kMlsOptSize) != 0) {
+                       STRNCAT(optstr, "size;");
+               }
+
+               /* MODTIME */
+               if ((f & kMlsOptModify) != 0) {
+                       STRNCAT(optstr, "modify;");
+               }
+
+               /* MODE */
+               if ((f & kMlsOptUNIXmode) != 0) {
+                       STRNCAT(optstr, "UNIX.mode;");
+               }
+
+               /* PERM */
+               if ((f & kMlsOptPerm) != 0) {
+                       STRNCAT(optstr, "perm;");
+               }
+
+               /* OWNER */
+               if ((f & kMlsOptUNIXowner) != 0) {
+                       STRNCAT(optstr, "UNIX.owner;");
+               }
+
+               /* UID */
+               if ((f & kMlsOptUNIXuid) != 0) {
+                       STRNCAT(optstr, "UNIX.uid;");
+               }
+
+               /* GROUP */
+               if ((f & kMlsOptUNIXgroup) != 0) {
+                       STRNCAT(optstr, "UNIX.group;");
+               }
+
+               /* GID */
+               if ((f & kMlsOptUNIXgid) != 0) {
+                       STRNCAT(optstr, "UNIX.gid;");
+               }
+
+               /* UNIQUE */
+               if ((f & kMlsOptUnique) != 0) {
+                       STRNCAT(optstr, "unique;");
+               }
+
+               /* Tell the server what we prefer. */
+               optstrlen = strlen(optstr);
+               if (optstrlen > 0) {
+                       if (optstr[optstrlen - 1] == ';')
+                               optstr[optstrlen - 1] = '\0';
+                       (void) FTPCmd(cip, "OPTS MLST %s", optstr);
+               }
+       }
+}      /* FTPRequestMlsOptions */
+
+
+
+
+int
+FTPListToMemory2(const FTPCIPtr cip, const char *const pattern, const LineListPtr llines, const char *const lsflags, const int blankLines, int *const tryMLSD)
+{
+       char secondaryBuf[768];
+       char line[512];
+       char lsflags1[128];
+       const char *command = "NLST";
+       const char *scp;
+       char *dcp, *lim;
+#ifndef NO_SIGNALS
+       char *secBufPtr, *secBufLimit;
+       volatile FTPSigProc osigpipe;
+       volatile FTPCIPtr vcip;
+       int sj;
+       int nread;
+       volatile int result;
+#else  /* NO_SIGNALS */
+       SReadlineInfo lsSrl;
+       int result;
+#endif /* NO_SIGNALS */
+
+       if (cip == NULL)
+               return (kErrBadParameter);
+       if (strcmp(cip->magic, kLibraryMagic))
+               return (kErrBadMagic);
+
+       if ((llines == NULL) || (pattern == NULL) || (lsflags == NULL))
+               return (kErrBadParameter);
+
+       if ((tryMLSD != (int *) 0) && (*tryMLSD != 0) && (cip->hasMLSD == kCommandAvailable)) {
+               command = "MLSD";
+               if ((lsflags[0] == '-') && (strchr(lsflags, 'd') != NULL) && (cip->hasMLST == kCommandAvailable))
+                       command = "MLST";
+               lsflags1[0] = '\0';
+               FTPRequestMlsOptions(cip);
+       } else {
+               /* Not using MLSD. */
+               if (tryMLSD != (int *) 0)
+                       *tryMLSD = 0;
+               if (lsflags[0] == '-') {
+                       /* See if we should use LIST instead. */
+                       scp = lsflags + 1;
+                       dcp = lsflags1;
+                       lim = lsflags1 + sizeof(lsflags1) - 2;
+                       for (; *scp != '\0'; scp++) {
+                               if (*scp == 'l') {
+                                       /* do not add the 'l' */
+                                       command = "LIST";
+                               } else if (dcp < lim) {
+                                       if (dcp == lsflags1)
+                                               *dcp++ = '-';
+                                       *dcp++ = *scp;
+                               }
+                       }
+                       *dcp = '\0';
+               } else {
+                       (void) STRNCPY(lsflags1, lsflags);
+               }
+       }
+
+       InitLineList(llines);
+
+       result = FTPStartDataCmd(
+               cip,
+               kNetReading,
+               kTypeAscii,
+               (longest_int) 0,
+               "%s%s%s%s%s",
+               command,
+               (lsflags1[0] == '\0') ? "" : " ",
+               lsflags1,
+               (pattern[0] == '\0') ? "" : " ",
+               pattern
+       );
+
+#ifdef NO_SIGNALS
+
+       if (result == 0) {
+               if (InitSReadlineInfo(&lsSrl, cip->dataSocket, secondaryBuf, sizeof(secondaryBuf), (int) cip->xferTimeout, 1) < 0) {
+                       /* Not really fdopen, but close in what we're trying to do. */
+                       result = kErrFdopenR;
+                       cip->errNo = kErrFdopenR;
+                       Error(cip, kDoPerror, "Could not fdopen.\n");
+                       return (result);
+               }
+
+               for (;;) {
+                       result = SReadline(&lsSrl, line, sizeof(line) - 1);
+                       if (result == kTimeoutErr) {
+                               /* timeout */
+                               Error(cip, kDontPerror, "Could not directory listing data -- timed out.\n");
+                               cip->errNo = kErrDataTimedOut;
+                               return (cip->errNo);
+                       } else if (result == 0) {
+                               /* end of listing -- done */
+                               cip->numListings++;
+                               break;
+                       } else if (result < 0) {
+                               /* error */
+                               Error(cip, kDoPerror, "Could not read directory listing data");
+                               result = kErrLISTFailed;
+                               cip->errNo = kErrLISTFailed;
+                               break;
+                       }
+
+                       if (line[result - 1] == '\n')
+                               line[result - 1] = '\0';
+
+                       if ((blankLines == 0) && (result <= 1))
+                               continue;
+
+                       /* Valid directory listing line of output */
+                       if ((line[0] == '.') && ((line[1] == '\0') || ((line[1] == '.') && ((line[2] == '\0') || (iscntrl(line[2]))))))
+                               continue;       /* Skip . and .. */
+
+                       (void) AddLine(llines, line);
+               }
+
+               DisposeSReadlineInfo(&lsSrl);
+               if (FTPEndDataCmd(cip, 1) < 0) {
+                       result = kErrLISTFailed;
+                       cip->errNo = kErrLISTFailed;
+               }
+       } else if (result == kErrGeneric) {
+               result = kErrLISTFailed;
+               cip->errNo = kErrLISTFailed;
+       }
+
+
+#else  /* NO_SIGNALS */
+       vcip = cip;
+       osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
+
+       gGotBrokenData = 0;
+       gCanBrokenDataJmp = 0;
+
+#ifdef HAVE_SIGSETJMP
+       sj = sigsetjmp(gBrokenDataJmp, 1);
+#else
+       sj = setjmp(gBrokenDataJmp);
+#endif /* HAVE_SIGSETJMP */
+
+       if (sj != 0) {
+               (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+               FTPShutdownHost(vcip);
+               vcip->errNo = kErrRemoteHostClosedConnection;
+               return(vcip->errNo);
+       }
+       gCanBrokenDataJmp = 1;
+
+       if (result == 0) {
+               /* This line sets the buffer pointer so that the first thing
+                * BufferGets will do is reset and fill the buffer using
+                * real I/O.
+                */
+               secBufPtr = secondaryBuf + sizeof(secondaryBuf);
+               secBufLimit = (char *) 0;
+               memset(secondaryBuf, 0, sizeof(secondaryBuf));
+
+               for (;;) {
+                       memset(line, 0, sizeof(line));
+                       if (cip->xferTimeout > 0)
+                               (void) alarm(cip->xferTimeout);
+                       nread = BufferGets(line, sizeof(line), cip->dataSocket, secondaryBuf, &secBufPtr, &secBufLimit, sizeof(secondaryBuf));
+                       if (nread <= 0) {
+                               if (nread < 0)
+                                       break;
+                               if (blankLines != 0)
+                                       (void) AddLine(llines, line);
+                       } else {
+                               cip->bytesTransferred += (longest_int) nread;
+
+                               if ((line[0] == '.') && ((line[1] == '\0') || ((line[1] == '.') && ((line[2] == '\0') || (iscntrl(line[2]))))))
+                                       continue;       /* Skip . and .. */
+
+                               (void) AddLine(llines, line);
+                       }
+               }
+               if (cip->xferTimeout > 0)
+                       (void) alarm(0);
+               result = FTPEndDataCmd(cip, 1);
+               if (result < 0) {
+                       result = kErrLISTFailed;
+                       cip->errNo = kErrLISTFailed;
+               }
+               result = kNoErr;
+               cip->numListings++;
+       } else if (result == kErrGeneric) {
+               result = kErrLISTFailed;
+               cip->errNo = kErrLISTFailed;
+       }
+       (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif /* NO_SIGNALS */
+       return (result);
+}      /* FTPListToMemory2 */
+
+
+
+
+static void
+AutomaticallyUseASCIIModeDependingOnExtension(const FTPCIPtr cip, const char *const pathName, int *const xtype)
+{
+       if ((*xtype == kTypeBinary) && (cip->asciiFilenameExtensions != NULL)) {
+               if (FilenameExtensionIndicatesASCII(pathName, cip->asciiFilenameExtensions)) {
+                       /* Matched -- send this file in ASCII mode
+                        * instead of binary since it's extension
+                        * appears to be that of a text file.
+                        */
+                       *xtype = kTypeAscii;
+               }
+       }
+}      /* AutomaticallyUseASCIIModeDependingOnExtension */
+
+
+
+
+/* The purpose of this is to provide updates for the progress meters
+ * during lags.  Return zero if the operation timed-out.
+ */
+static int
+WaitForRemoteOutput(const FTPCIPtr cip)
+{
+       fd_set ss, ss2;
+       struct timeval tv;
+       int result;
+       int fd;
+       int wsecs;
+       int xferTimeout;
+       int ocancelXfer;
+
+       xferTimeout = cip->xferTimeout;
+       if (xferTimeout < 1)
+               return (1);
+
+       fd = cip->dataSocket;
+       if (fd < 0)
+               return (1);
+
+       ocancelXfer = cip->cancelXfer;
+       wsecs = 0;
+       cip->stalled = 0;
+
+       while ((xferTimeout <= 0) || (wsecs < xferTimeout)) {
+               if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) {
+                       /* leave cip->stalled -- could have been stalled and then canceled. */
+                       return (1);
+               }
+               FD_ZERO(&ss);
+               FD_SET(fd, &ss);
+               ss2 = ss;
+               tv.tv_sec = 1;
+               tv.tv_usec = 0;
+               result = select(fd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &ss2, &tv);
+               if (result == 1) {
+                       /* ready */
+                       cip->stalled = 0;
+                       return (1);
+               } else if (result < 0) {
+                       if (errno != EINTR) {
+                               perror("select");
+                               cip->stalled = 0;
+                               return (1);
+                       }
+               } else {
+                       wsecs++;
+                       cip->stalled = wsecs;
+               }
+               FTPUpdateIOTimer(cip);
+       }
+
+#if !defined(NO_SIGNALS)
+       /* Shouldn't get here -- alarm() should have
+        * went off by now.
+        */
+       (void) kill(getpid(), SIGALRM);
+#endif /* NO_SIGNALS */
+
+       cip->dataTimedOut = 1;
+       return (0);     /* timed-out */
+}      /* WaitForRemoteOutput */
+
+
+
+
+static int
+FTPPutOneF(
+       const FTPCIPtr cip,
+       const char *const file,
+       const char *volatile dstfile,
+       int xtype,
+       const int fdtouse,
+       const int appendflag,
+       const char *volatile tmppfx,
+       const char *volatile tmpsfx,
+       const int resumeflag,
+       const int deleteflag,
+       const ConfirmResumeUploadProc resumeProc)
+{
+       char *buf, *cp;
+       const char *cmd;
+       const char *odstfile;
+       size_t bufSize;
+       size_t l;
+       int tmpResult, result;
+       int nread, nwrote;
+       volatile int fd;
+       char dstfile2[512];
+#if ASCII_TRANSLATION
+       char *src, *srclim, *dst;
+       int ntowrite;
+       char inbuf[256];
+#endif
+       int fstatrc, statrc;
+       longest_int startPoint = 0;
+       struct Stat st;
+       time_t mdtm;
+#if !defined(NO_SIGNALS)
+       int sj;
+       volatile FTPSigProc osigpipe;
+       volatile FTPCIPtr vcip;
+       volatile int vfd, vfdtouse;
+#endif /* NO_SIGNALS */
+       volatile int vzaction;
+       int zaction = kConfirmResumeProcSaidBestGuess;
+
+       if (cip->buf == NULL) {
+               Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
+               cip->errNo = kErrNoBuf;
+               return (cip->errNo);
+       }
+
+       cip->usingTAR = 0;
+       if (fdtouse < 0) {
+               fd = Open(file, O_RDONLY|O_BINARY, 0);
+               if (fd < 0) {
+                       Error(cip, kDoPerror, "Cannot open local file %s for reading.\n", file);
+                       cip->errNo = kErrOpenFailed;
+                       return (cip->errNo);
+               }
+       } else {
+               fd = fdtouse;
+       }
+
+       fstatrc = Fstat(fd, &st);
+       if ((fstatrc == 0) && (S_ISDIR(st.st_mode))) {
+               if (fdtouse < 0) {
+                       (void) close(fd);
+               }
+               Error(cip, kDontPerror, "%s is a directory.\n", (file != NULL) ? file : "that");
+               cip->errNo = kErrOpenFailed;
+               return (cip->errNo);
+       }
+
+       /* For Put, we can't recover very well if it turns out restart
+        * didn't work, so check beforehand.
+        */
+       if (cip->hasREST == kCommandAvailabilityUnknown) {
+               (void) FTPSetTransferType(cip, kTypeBinary);
+               if (SetStartOffset(cip, (longest_int) 1) == kNoErr) {
+                       /* Now revert -- we still may not end up
+                        * doing it.
+                        */
+                       SetStartOffset(cip, (longest_int) -1);
+               }
+       }
+
+       if (fdtouse < 0) {
+               AutomaticallyUseASCIIModeDependingOnExtension(cip, dstfile, &xtype);
+               (void) FTPFileSizeAndModificationTime(cip, dstfile, &startPoint, xtype, &mdtm);
+
+               if (appendflag == kAppendYes) {
+                       zaction = kConfirmResumeProcSaidAppend;
+               } else if (
+                               (cip->hasREST == kCommandNotAvailable) ||
+                               (xtype != kTypeBinary) ||
+                               (fstatrc < 0)
+               ) {
+                       zaction = kConfirmResumeProcSaidOverwrite;
+               } else if (resumeflag == kResumeYes) {
+                       zaction = kConfirmResumeProcSaidBestGuess;
+               } else {
+                       zaction = kConfirmResumeProcSaidOverwrite;
+               }
+
+               statrc = -1;
+               if ((mdtm != kModTimeUnknown) || (startPoint != kSizeUnknown)) {
+                       /* Then we know the file exists.  We will
+                        * ask the user what to do, if possible, below.
+                        */
+                       statrc = 0;
+               } else if ((resumeProc != NoConfirmResumeUploadProc) && (cip->hasMDTM != kCommandAvailable) && (cip->hasSIZE != kCommandAvailable)) {
+                       /* We already checked if the file had a filesize
+                        * or timestamp above, but if the server indicated
+                        * it did not support querying those directly,
+                        * we now need to try to determine if the file
+                        * exists in a few other ways.
+                        */
+                       statrc = FTPFileExists2(cip, dstfile, 0, 0, 0, 1, 1);
+               }
+
+               if (
+                       (resumeProc != NoConfirmResumeUploadProc) &&
+                       (statrc == 0)
+               ) {
+                       zaction = (*resumeProc)(file, (longest_int) st.st_size, st.st_mtime, &dstfile, startPoint, mdtm, &startPoint);
+               }
+
+               if (zaction == kConfirmResumeProcSaidCancel) {
+                       /* User wants to cancel this file and any
+                        * remaining in batch.
+                        */
+                       cip->errNo = kErrUserCanceled;
+                       return (cip->errNo);
+               }
+
+               if (zaction == kConfirmResumeProcSaidBestGuess) {
+                       if ((mdtm != kModTimeUnknown) && (st.st_mtime > (mdtm + 1))) {
+                               /* Local file is newer than remote,
+                                * overwrite the remote file instead
+                                * of trying to resume it.
+                                *
+                                * Note:  Add one second fudge factor
+                                * for Windows' file timestamps being
+                                * imprecise to one second.
+                                */
+                               zaction = kConfirmResumeProcSaidOverwrite;
+                       } else if ((longest_int) st.st_size == startPoint) {
+                               /* Already sent file, done. */
+                               zaction = kConfirmResumeProcSaidSkip;
+                       } else if ((startPoint != kSizeUnknown) && ((longest_int) st.st_size > startPoint)) {
+                               zaction = kConfirmResumeProcSaidResume;
+                       } else {
+                               zaction = kConfirmResumeProcSaidOverwrite;
+                       }
+               }
+
+               if (zaction == kConfirmResumeProcSaidSkip) {
+                       /* Nothing done, but not an error. */
+                       if (fdtouse < 0) {
+                               (void) close(fd);
+                       }
+                       if (deleteflag == kDeleteYes) {
+                               if (unlink(file) < 0) {
+                                       cip->errNo = kErrLocalDeleteFailed;
+                                       return (cip->errNo);
+                               }
+                       }
+                       return (kNoErr);
+               } else if (zaction == kConfirmResumeProcSaidResume) {
+                       /* Resume; proc set the startPoint. */
+                       if ((longest_int) st.st_size == startPoint) {
+                               /* Already sent file, done. */
+                               if (fdtouse < 0) {
+                                       (void) close(fd);
+                               }
+
+                               if (deleteflag == kDeleteYes) {
+                                       if (unlink(file) < 0) {
+                                               cip->errNo = kErrLocalDeleteFailed;
+                                               return (cip->errNo);
+                                       }
+                               }
+                               return (kNoErr);
+                       } else if (Lseek(fd, (off_t) startPoint, SEEK_SET) != (off_t) -1) {
+                               cip->startPoint = startPoint;
+                       }
+               } else if (zaction == kConfirmResumeProcSaidAppend) {
+                       /* append: leave startPoint at zero, we will append everything. */
+                       cip->startPoint = startPoint = 0;
+               } else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
+                       /* overwrite: leave startPoint at zero */
+                       cip->startPoint = startPoint = 0;
+               }
+       }
+
+       if ((cip->numUploads == 0) && (cip->dataSocketSBufSize > 0)) {
+               /* If dataSocketSBufSize is non-zero, it means you
+                * want to explicitly try to set the size of the
+                * socket's I/O buffer.
+                *
+                * If it is zero, it means you want to just use the
+                * TCP stack's default value, which is typically
+                * between 8 and 64 kB.
+                *
+                * If you try to set the buffer larger than 64 kB,
+                * the TCP stack should try to use RFC 1323 to
+                * negotiate "TCP Large Windows" which may yield
+                * significant performance gains.
+                */
+               if (cip->hasSTORBUFSIZE == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE STORBUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
+               else if (cip->hasSBUFSIZ == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE SBUFSIZ %lu", (unsigned long) cip->dataSocketSBufSize);
+               else if (cip->hasSBUFSZ == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE SBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize);
+               /* At least one server implemenation has RBUFSZ but not
+                * SBUFSZ and instead uses RBUFSZ for both.
+                */
+               else if ((cip->hasSBUFSZ != kCommandAvailable) && (cip->hasRBUFSZ == kCommandAvailable))
+                       (void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize);
+               else if (cip->hasBUFSIZE == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
+       }
+
+#ifdef NO_SIGNALS
+       vzaction = zaction;
+#else  /* NO_SIGNALS */
+       vcip = cip;
+       vfdtouse = fdtouse;
+       vfd = fd;
+       vzaction = zaction;
+       osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
+
+       gGotBrokenData = 0;
+       gCanBrokenDataJmp = 0;
+
+#ifdef HAVE_SIGSETJMP
+       sj = sigsetjmp(gBrokenDataJmp, 1);
+#else
+       sj = setjmp(gBrokenDataJmp);
+#endif /* HAVE_SIGSETJMP */
+
+       if (sj != 0) {
+               (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+               if (vfdtouse < 0) {
+                       (void) close(vfd);
+               }
+               FTPShutdownHost(vcip);
+               vcip->errNo = kErrRemoteHostClosedConnection;
+               return(vcip->errNo);
+       }
+       gCanBrokenDataJmp = 1;
+#endif /* NO_SIGNALS */
+
+       if (vzaction == kConfirmResumeProcSaidAppend) {
+               cmd = "APPE";
+               tmppfx = "";    /* Can't use that here. */
+               tmpsfx = "";
+       } else {
+               cmd = "STOR";
+               if (tmppfx == NULL)
+                       tmppfx = "";
+               if (tmpsfx == NULL)
+                       tmpsfx = "";
+       }
+
+       odstfile = dstfile;
+       if ((tmppfx[0] != '\0') || (tmpsfx[0] != '\0')) {
+               cp = strrchr(dstfile, '/');
+               if (cp == NULL)
+                       cp = strrchr(dstfile, '\\');
+               if (cp == NULL) {
+                       (void) STRNCPY(dstfile2, tmppfx);
+                       (void) STRNCAT(dstfile2, dstfile);
+                       (void) STRNCAT(dstfile2, tmpsfx);
+               } else {
+                       cp++;
+                       l = (size_t) (cp - dstfile);
+                       (void) STRNCPY(dstfile2, dstfile);
+                       dstfile2[l] = '\0';     /* Nuke stuff after / */
+                       (void) STRNCAT(dstfile2, tmppfx);
+                       (void) STRNCAT(dstfile2, cp);
+                       (void) STRNCAT(dstfile2, tmpsfx);
+               }
+               dstfile = dstfile2;
+       }
+
+       tmpResult = FTPStartDataCmd(
+               cip,
+               kNetWriting,
+               xtype,
+               startPoint,
+               "%s %s",
+               cmd,
+               dstfile
+       );
+
+       if (tmpResult < 0) {
+               cip->errNo = tmpResult;
+               if (fdtouse < 0) {
+                       (void) close(fd);
+               }
+#if !defined(NO_SIGNALS)
+               (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif /* NO_SIGNALS */
+               return (cip->errNo);
+       }
+
+       if ((startPoint != 0) && (cip->startPoint == 0)) {
+               /* Remote could not or would not set the start offset
+                * to what we wanted.
+                *
+                * So now we have to undo our seek.
+                */
+               if (Lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
+                       cip->errNo = kErrLseekFailed;
+                       if (fdtouse < 0) {
+                               (void) close(fd);
+                       }
+#if !defined(NO_SIGNALS)
+                       (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif /* NO_SIGNALS */
+                       return (cip->errNo);
+               }
+               startPoint = 0;
+       }
+
+       result = kNoErr;
+       buf = cip->buf;
+       bufSize = cip->bufSize;
+
+       FTPInitIOTimer(cip);
+       if ((fstatrc == 0) && (S_ISREG(st.st_mode) != 0)) {
+               cip->expectedSize = (longest_int) st.st_size;
+               cip->mdtm = st.st_mtime;
+       }
+       cip->lname = file;      /* could be NULL */
+       cip->rname = odstfile;
+       if (fdtouse >= 0)
+               cip->useProgressMeter = 0;
+       FTPStartIOTimer(cip);
+
+       /* Note: On Windows, we don't have to do anything special
+        * for ASCII mode, since Net ASCII's end-of-line sequence
+        * corresponds to the same thing used for DOS/Windows.
+        */
+
+#if ASCII_TRANSLATION
+       if (xtype == kTypeAscii) {
+               /* ascii */
+               for (;;) {
+#if !defined(NO_SIGNALS)
+                       gCanBrokenDataJmp = 0;
+#endif /* NO_SIGNALS */
+                       nread = read(fd, inbuf, sizeof(inbuf));
+                       if (nread < 0) {
+                               if (errno == EINTR) {
+                                       continue;
+                               } else {
+                                       result = kErrReadFailed;
+                                       cip->errNo = kErrReadFailed;
+                                       Error(cip, kDoPerror, "Local read failed.\n");
+                               }
+                               break;
+                       } else if (nread == 0) {
+                               break;
+                       }
+                       cip->bytesTransferred += (longest_int) nread;
+
+#if !defined(NO_SIGNALS)
+                       gCanBrokenDataJmp = 1;
+#endif /* NO_SIGNALS */
+                       src = inbuf;
+                       srclim = src + nread;
+                       dst = cip->buf;         /* must be 2x sizeof inbuf or more. */
+                       while (src < srclim) {
+                               if (*src == '\n')
+                                       *dst++ = '\r';
+                               *dst++ = *src++;
+                       }
+                       ntowrite = (size_t) (dst - cip->buf);
+                       cp = cip->buf;
+
+#if !defined(NO_SIGNALS)
+                       if (cip->xferTimeout > 0)
+                               (void) alarm(cip->xferTimeout);
+#endif /* NO_SIGNALS */
+                       do {
+                               if (! WaitForRemoteOutput(cip)) {       /* could set cancelXfer */
+                                       cip->errNo = result = kErrDataTimedOut;
+                                       Error(cip, kDontPerror, "Remote write timed out.\n");
+                                       goto brk;
+                               }
+                               if (cip->cancelXfer > 0) {
+                                       FTPAbortDataTransfer(cip);
+                                       result = cip->errNo = kErrDataTransferAborted;
+                                       goto brk;
+                               }
+
+#ifdef NO_SIGNALS
+                               nwrote = SWrite(cip->dataSocket, cp, (size_t) ntowrite, (int) cip->xferTimeout, kNoFirstSelect);
+                               if (nwrote < 0) {
+                                       if (nwrote == kTimeoutErr) {
+                                               cip->errNo = result = kErrDataTimedOut;
+                                               Error(cip, kDontPerror, "Remote write timed out.\n");
+                                       } else if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               errno = EPIPE;
+                                               Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                                       } else if (errno == EINTR) {
+                                               continue;
+                                       } else {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               Error(cip, kDoPerror, "Remote write failed.\n");
+                                       }
+                                       (void) shutdown(cip->dataSocket, 2);
+                                       goto brk;
+                               }
+#else  /* NO_SIGNALS */
+                               nwrote = write(cip->dataSocket, cp, ntowrite);
+                               if (nwrote < 0) {
+                                       if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               errno = EPIPE;
+                                               Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                                       } else if (errno == EINTR) {
+                                               continue;
+                                       } else {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               Error(cip, kDoPerror, "Remote write failed.\n");
+                                       }
+                                       (void) shutdown(cip->dataSocket, 2);
+                                       goto brk;
+                               }
+#endif /* NO_SIGNALS */
+                               cp += nwrote;
+                               ntowrite -= nwrote;
+                       } while (ntowrite > 0);
+                       FTPUpdateIOTimer(cip);
+               }
+       } else
+#endif /* ASCII_TRANSLATION */
+       {
+               /* binary */
+               for (;;) {
+#if !defined(NO_SIGNALS)
+                       gCanBrokenDataJmp = 0;
+#endif /* NO_SIGNALS */
+                       cp = buf;
+                       nread = read(fd, cp, bufSize);
+                       if (nread < 0) {
+                               if (errno == EINTR) {
+                                       continue;
+                               } else {
+                                       result = kErrReadFailed;
+                                       cip->errNo = kErrReadFailed;
+                                       Error(cip, kDoPerror, "Local read failed.\n");
+                               }
+                               break;
+                       } else if (nread == 0) {
+                               break;
+                       }
+                       cip->bytesTransferred += (longest_int) nread;
+
+#if !defined(NO_SIGNALS)
+                       gCanBrokenDataJmp = 1;
+                       if (cip->xferTimeout > 0)
+                               (void) alarm(cip->xferTimeout);
+#endif /* NO_SIGNALS */
+                       do {
+                               if (! WaitForRemoteOutput(cip)) {       /* could set cancelXfer */
+                                       cip->errNo = result = kErrDataTimedOut;
+                                       Error(cip, kDontPerror, "Remote write timed out.\n");
+                                       goto brk;
+                               }
+                               if (cip->cancelXfer > 0) {
+                                       FTPAbortDataTransfer(cip);
+                                       result = cip->errNo = kErrDataTransferAborted;
+                                       goto brk;
+                               }
+
+#ifdef NO_SIGNALS
+                               nwrote = SWrite(cip->dataSocket, cp, (size_t) nread, (int) cip->xferTimeout, kNoFirstSelect);
+                               if (nwrote < 0) {
+                                       if (nwrote == kTimeoutErr) {
+                                               cip->errNo = result = kErrDataTimedOut;
+                                               Error(cip, kDontPerror, "Remote write timed out.\n");
+                                       } else if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               errno = EPIPE;
+                                               Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                                       } else if (errno == EINTR) {
+                                               continue;
+                                       } else {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               Error(cip, kDoPerror, "Remote write failed.\n");
+                                       }
+                                       (void) shutdown(cip->dataSocket, 2);
+                                       cip->dataSocket = -1;
+                                       goto brk;
+                               }
+#else  /* NO_SIGNALS */
+                               nwrote = write(cip->dataSocket, cp, nread);
+                               if (nwrote < 0) {
+                                       if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               errno = EPIPE;
+                                               Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                                       } else if (errno == EINTR) {
+                                               continue;
+                                       } else {
+                                               cip->errNo = result = kErrSocketWriteFailed;
+                                               Error(cip, kDoPerror, "Remote write failed.\n");
+                                       }
+                                       (void) shutdown(cip->dataSocket, 2);
+                                       cip->dataSocket = -1;
+                                       goto brk;
+                               }
+#endif /* NO_SIGNALS */
+                               cp += nwrote;
+                               nread -= nwrote;
+                       } while (nread > 0);
+                       FTPUpdateIOTimer(cip);
+               }
+       }
+brk:
+
+       if (fdtouse < 0) {
+               (void) Fstat(fd, &st);
+       }
+
+       if (fdtouse < 0) {
+               if (shutdown(fd, 1) == 0) {
+                       /* This looks very bizarre, since
+                        * we will be checking the socket
+                        * for readability here!
+                        *
+                        * The reason for this is that we
+                        * want to be able to timeout a
+                        * small put.  So, we close the
+                        * write end of the socket first,
+                        * which tells the server we're
+                        * done writing.  We then wait
+                        * for the server to close down
+                        * the whole socket, which tells
+                        * us that the file was completed.
+                        */
+                       (void) WaitForRemoteInput(cip); /* Close could block. */
+               }
+       }
+
+#if !defined(NO_SIGNALS)
+       gCanBrokenDataJmp = 0;
+       if (cip->xferTimeout > 0)
+               (void) alarm(0);
+#endif /* NO_SIGNALS */
+       tmpResult = FTPEndDataCmd(cip, 1);
+       if ((tmpResult < 0) && (result == kNoErr)) {
+               cip->errNo = result = kErrSTORFailed;
+       }
+       FTPStopIOTimer(cip);
+
+       if (fdtouse < 0) {
+               /* If they gave us a descriptor (fdtouse >= 0),
+                * leave it open, otherwise we opened it, so
+                * we need to dispose of it.
+                */
+               (void) close(fd);
+               fd = -1;
+       }
+
+       if (result == kNoErr) {
+               /* The store succeeded;  If we were
+                * uploading to a temporary file,
+                * move the new file to the new name.
+                */
+               cip->numUploads++;
+
+               if ((tmppfx[0] != '\0') || (tmpsfx[0] != '\0')) {
+                       if ((result = FTPRename(cip, dstfile, odstfile)) < 0) {
+                               /* May fail if file was already there,
+                                * so delete the old one so we can move
+                                * over it.
+                                */
+                               if (FTPDelete(cip, odstfile, kRecursiveNo, kGlobNo) == kNoErr) {
+                                       result = FTPRename(cip, dstfile, odstfile);
+                                       if (result < 0) {
+                                               Error(cip, kDontPerror, "Could not rename %s to %s: %s.\n", dstfile, odstfile, FTPStrError(cip->errNo));
+                                       }
+                               } else {
+                                       Error(cip, kDontPerror, "Could not delete old %s, so could not rename %s to that: %s\n", odstfile, dstfile, FTPStrError(cip->errNo));
+                               }
+                       }
+               }
+
+               if (FTPUtime(cip, odstfile, st.st_atime, st.st_mtime, st.st_ctime) != kNoErr) {
+                       if (cip->errNo != kErrUTIMENotAvailable)
+                               Error(cip, kDontPerror, "Could not preserve times for %s: %s.\n", odstfile, FTPStrError(cip->errNo));
+               }
+
+               if (deleteflag == kDeleteYes) {
+                       if (unlink(file) < 0) {
+                               result = cip->errNo = kErrLocalDeleteFailed;
+                       }
+               }
+       }
+
+#if !defined(NO_SIGNALS)
+       (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif /* NO_SIGNALS */
+       return (result);
+}      /* FTPPutOneF */
+
+
+
+
+int
+FTPPutOneFile3(
+       const FTPCIPtr cip,
+       const char *const file,
+       const char *const dstfile,
+       const int xtype,
+       const int fdtouse,
+       const int appendflag,
+       const char *const tmppfx,
+       const char *const tmpsfx,
+       const int resumeflag,
+       const int deleteflag,
+       const ConfirmResumeUploadProc resumeProc,
+       int UNUSED(reserved))
+{
+       int result;
+
+       LIBNCFTP_USE_VAR(reserved);
+       if (cip == NULL)
+               return (kErrBadParameter);
+       if (strcmp(cip->magic, kLibraryMagic))
+               return (kErrBadMagic);
+
+       if ((dstfile == NULL) || (dstfile[0] == '\0'))
+               return (kErrBadParameter);
+       if (fdtouse < 0) {
+               if ((file == NULL) || (file[0] == '\0'))
+                       return (kErrBadParameter);
+       }
+       result = FTPPutOneF(cip, file, dstfile, xtype, fdtouse, appendflag, tmppfx, tmpsfx, resumeflag, deleteflag, resumeProc);
+       return (result);
+}      /* FTPPutOneFile3 */
+
+
+
+
+int
+FTPPutFiles3(
+       const FTPCIPtr cip,
+       const char *const pattern,
+       const char *const dstdir1,
+       const int recurse,
+       const int doGlob,
+       const int xtype,
+       int appendflag,
+       const char *const tmppfx,
+       const char *const tmpsfx,
+       const int resumeflag,
+       const int deleteflag,
+       const ConfirmResumeUploadProc resumeProc,
+       int UNUSED(reserved))
+{
+       LineList globList;
+       FileInfoList files;
+       FileInfoPtr filePtr;
+       int batchResult;
+       int result;
+       const char *dstdir;
+       char dstdir2[512];
+
+       LIBNCFTP_USE_VAR(reserved);
+       if (cip == NULL)
+               return (kErrBadParameter);
+       if (strcmp(cip->magic, kLibraryMagic))
+               return (kErrBadMagic);
+
+       if (dstdir1 == NULL) {
+               dstdir = NULL;
+       } else {
+               dstdir = STRNCPY(dstdir2, dstdir1);
+               StrRemoveTrailingLocalPathDelim(dstdir2);
+       }
+
+       (void) FTPLocalGlob(cip, &globList, pattern, doGlob);
+       if (recurse == kRecursiveYes) {
+               appendflag = kAppendNo;
+               (void) FTPLocalRecursiveFileList(cip, &globList, &files);
+               if (files.first == NULL) {
+                       cip->errNo = kErrNoValidFilesSpecified;
+                       return (kErrNoValidFilesSpecified);
+               }
+               (void) ComputeRNames(&files, dstdir, 0, 1);
+       } else {
+               (void) LineListToFileInfoList(&globList, &files);
+               (void) ComputeLNames(&files, NULL, NULL, 1);
+               (void) ComputeRNames(&files, dstdir, 0, 0);
+       }
+       DisposeLineListContents(&globList);
+
+#if 0
+       for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
+               PrintF(cip, "  R=%s, L=%s, 2=%s, size=%d, mdtm=%u, type=%c\n",
+                       filePtr->rname,
+                       filePtr->lname,
+                       filePtr->rlinkto ? filePtr->rlinkto : "",
+                       filePtr->size,
+                       (unsigned int) filePtr->mdtm,
+                       filePtr->type
+               );
+       }
+#endif
+
+       batchResult = kNoErr;
+       for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
+               if (cip->connected == 0) {
+                       if (batchResult == kNoErr)
+                               batchResult = kErrRemoteHostClosedConnection;
+                       break;
+               }
+               if (filePtr->type == 'd') {
+                       /* mkdir */
+                       StrRemoveTrailingLocalPathDelim(filePtr->rname);
+                       result = FTPMkdir(cip, filePtr->rname, kRecursiveNo);
+                       if (result != kNoErr)
+                               batchResult = result;
+#ifdef HAVE_SYMLINK
+               } else if (filePtr->type == 'l') {
+                       /* symlink */
+                       /* no RFC way to create the link, though. */
+                       if ((filePtr->rlinkto != NULL) && (filePtr->rlinkto[0] != '\0'))
+                               (void) FTPSymlink(cip, filePtr->rname, filePtr->rlinkto);
+#endif
+               } else if (recurse != kRecursiveYes) {
+                       result = FTPPutOneF(cip, filePtr->lname, filePtr->rname, xtype, -1, appendflag, tmppfx, tmpsfx, resumeflag, deleteflag, resumeProc);
+                       if (files.nFileInfos == 1) {
+                               if (result != kNoErr)
+                                       batchResult = result;
+                       } else {
+                               if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
+                                       batchResult = result;
+                       }
+                       if (result == kErrUserCanceled)
+                               cip->cancelXfer = 1;
+                       if (cip->cancelXfer > 0)
+                               break;
+               } else {
+                       result = FTPPutOneF(cip, filePtr->lname, filePtr->rname, xtype, -1, appendflag, tmppfx, tmpsfx, resumeflag, deleteflag, resumeProc);
+                       if (files.nFileInfos == 1) {
+                               if (result != kNoErr)
+                                       batchResult = result;
+                       } else {
+                               if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
+                                       batchResult = result;
+                       }
+                       if (result == kErrUserCanceled)
+                               cip->cancelXfer = 1;
+                       if (cip->cancelXfer > 0)
+                               break;
+               }
+       }
+       DisposeFileInfoListContents(&files);
+       if (batchResult < 0)
+               cip->errNo = batchResult;
+       return (batchResult);
+}      /* FTPPutFiles3 */
+
+
+
+
+/* The purpose of this is to provide updates for the progress meters
+ * during lags.  Return zero if the operation timed-out.
+ */
+static int
+WaitForRemoteInput(const FTPCIPtr cip)
+{
+       fd_set ss, ss2;
+       struct timeval tv;
+       int result;
+       int fd;
+       int wsecs;
+       int xferTimeout;
+       int ocancelXfer;
+
+       xferTimeout = cip->xferTimeout;
+       if (xferTimeout < 1)
+               return (1);
+
+       fd = cip->dataSocket;
+       if (fd < 0)
+               return (1);
+
+       ocancelXfer = cip->cancelXfer;
+       wsecs = 0;
+       cip->stalled = 0;
+
+       while ((xferTimeout <= 0) || (wsecs < xferTimeout)) {
+               if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) {
+                       /* leave cip->stalled -- could have been stalled and then canceled. */
+                       return (1);
+               }
+               FD_ZERO(&ss);
+               FD_SET(fd, &ss);
+               ss2 = ss;
+               tv.tv_sec = 1;
+               tv.tv_usec = 0;
+               result = select(fd + 1, SELECT_TYPE_ARG234 &ss, NULL, SELECT_TYPE_ARG234 &ss2, &tv);
+               if (result == 1) {
+                       /* ready */
+                       cip->stalled = 0;
+                       return (1);
+               } else if (result < 0) {
+                       if (result != EINTR) {
+                               perror("select");
+                               cip->stalled = 0;
+                               return (1);
+                       }
+               } else {
+                       wsecs++;
+                       cip->stalled = wsecs;
+               }
+               FTPUpdateIOTimer(cip);
+       }
+
+#if !defined(NO_SIGNALS)
+       /* Shouldn't get here -- alarm() should have
+        * went off by now.
+        */
+       (void) kill(getpid(), SIGALRM);
+#endif /* NO_SIGNALS */
+
+       cip->dataTimedOut = 1;
+       return (0);     /* timed-out */
+}      /* WaitForRemoteInput */
+
+
+
+
+/* Nice for UNIX, but not necessary otherwise. */
+#ifdef TAR
+
+static int
+OpenTar(const FTPCIPtr cip, const char *const dstdir, int *const pid)
+{
+       int pipe1[2];
+       int pid1;
+       int i;
+       char *argv[8];
+
+       *pid = -1;
+
+       if (access(TAR, X_OK) < 0) {
+               /* Path to TAR is invalid. */
+               return (-1);
+       }
+
+       if (pipe(pipe1) < 0) {
+               Error(cip, kDoPerror, "pipe to Tar failed");
+               return (-1);
+       }
+
+       pid1 = (int) fork();
+       if (pid1 < 0) {
+               (void) close(pipe1[0]);
+               (void) close(pipe1[1]);
+               return (-1);
+       } else if (pid1 == 0) {
+               /* Child */
+               if ((dstdir != NULL) && (dstdir[0] != '\0') && (chdir(dstdir) < 0)) {
+                       Error(cip, kDoPerror, "tar chdir to %s failed", dstdir);
+                       exit(1);
+               }
+               (void) close(pipe1[1]);         /* close write end */
+               (void) dup2(pipe1[0], 0);       /* use read end on stdin */
+               (void) close(pipe1[0]);
+
+               for (i=3; i<256; i++)
+                       (void) close(i);
+
+               argv[0] = (char *) "tar";
+               argv[1] = (char *) "xpf";
+               argv[2] = (char *) "-";
+               argv[3] = NULL;
+
+               (void) execv(TAR, argv);
+               exit(1);
+       }
+
+       /* Parent */
+       *pid = pid1;
+
+       (void) close(pipe1[0]);         /* close read end */
+       return (pipe1[1]);              /* use write end */
+}      /* OpenTar */
+
+
+
+
+static int
+FTPGetOneTarF(const FTPCIPtr cip, const char *file, const char *const dstdir)
+{
+       char *buf;
+       size_t bufSize;
+       int tmpResult;
+       volatile int result;
+       int nread, nwrote;
+       volatile int fd;
+       volatile int vfd;
+       const char *volatile vfile;
+#ifndef NO_SIGNALS
+       int sj;
+       volatile FTPSigProc osigpipe;
+       volatile FTPCIPtr vcip;
+#endif
+       int pid, status;
+       char savedCwd[512];
+       char *volatile basecp;
+
+       result = kNoErr;
+       cip->usingTAR = 0;
+
+       if ((file[0] == '\0') || ((file[0] == '/') && (file[1] == '\0'))) {
+               /* It was "/"
+                * We can't do that, because "get /.tar"
+                * or "get .tar" does not work.
+                */
+               result = kErrOpenFailed;
+               cip->errNo = kErrOpenFailed;
+               return (result);
+       }
+
+       if (FTPCmd(cip, "MDTM %s.tar", file) == 2) {
+               /* Better not use this method since there is
+                * no way to tell if the server would use the
+                * existing .tar or do a new one on the fly.
+                */
+               result = kErrOpenFailed;
+               cip->errNo = kErrOpenFailed;
+               return (result);
+       }
+
+       basecp = strrchr(file, '/');
+       if (basecp != NULL)
+               basecp = strrchr(file, '\\');
+       if (basecp != NULL) {
+               /* Need to cd to the parent directory and get it
+                * from there.
+                */
+               if (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != 0) {
+                       result = kErrOpenFailed;
+                       cip->errNo = kErrOpenFailed;
+                       return (result);
+               }
+               result = FTPChdir(cip, file);
+               if (result != kNoErr) {
+                       return (result);
+               }
+               result = FTPChdir(cip, "..");
+               if (result != kNoErr) {
+                       (void) FTPChdir(cip, savedCwd);
+                       return (result);
+               }
+               file = basecp + 1;
+       }
+
+       fd = OpenTar(cip, dstdir, &pid);
+       if (fd < 0) {
+               result = kErrOpenFailed;
+               cip->errNo = kErrOpenFailed;
+               if (basecp != NULL)
+                       (void) FTPChdir(cip, savedCwd);
+               return (result);
+       }
+
+       vfd = fd;
+       vfile = file;
+
+#ifndef NO_SIGNALS
+       vcip = cip;
+       osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
+
+       gGotBrokenData = 0;
+       gCanBrokenDataJmp = 0;
+
+#ifdef HAVE_SIGSETJMP
+       sj = sigsetjmp(gBrokenDataJmp, 1);
+#else
+       sj = setjmp(gBrokenDataJmp);
+#endif /* HAVE_SIGSETJMP */
+
+       if (sj != 0) {
+               (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+               FTPShutdownHost(vcip);
+
+               (void) signal(SIGPIPE, SIG_IGN);
+               (void) close(vfd);
+               for (;;) {
+#ifdef HAVE_WAITPID
+                       if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
+                               break;
+#else
+                       if ((wait(&status) < 0) && (errno != EINTR))
+                               break;
+#endif
+                       if (WIFEXITED(status) || WIFSIGNALED(status))
+                               break;          /* done */
+               }
+               if (basecp != NULL)
+                       (void) FTPChdir(cip, savedCwd);
+               vcip->errNo = kErrRemoteHostClosedConnection;
+               return(vcip->errNo);
+       }
+       gCanBrokenDataJmp = 1;
+
+#endif /* NO_SIGNALS */
+
+       tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, (longest_int) 0, "RETR %s.tar", vfile);
+
+       if (tmpResult < 0) {
+               result = tmpResult;
+               if (result == kErrGeneric)
+                       result = kErrRETRFailed;
+               cip->errNo = result;
+
+#ifndef NO_SIGNALS
+               (void) signal(SIGPIPE, SIG_IGN);
+#endif
+               (void) close(vfd);
+               for (;;) {
+#ifdef HAVE_WAITPID
+                       if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
+                               break;
+#else
+                       if ((wait(&status) < 0) && (errno != EINTR))
+                               break;
+#endif
+                       if (WIFEXITED(status) || WIFSIGNALED(status))
+                               break;          /* done */
+               }
+
+#ifndef NO_SIGNALS
+               (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif
+               if (basecp != NULL)
+                       (void) FTPChdir(cip, savedCwd);
+               return (result);
+       }
+
+       cip->usingTAR = 1;
+       buf = cip->buf;
+       bufSize = cip->bufSize;
+
+       FTPInitIOTimer(cip);
+       cip->lname = vfile;     /* could be NULL */
+       cip->rname = vfile;
+       FTPStartIOTimer(cip);
+
+       /* Binary */
+       for (;;) {
+               if (! WaitForRemoteInput(cip)) {        /* could set cancelXfer */
+                       cip->errNo = result = kErrDataTimedOut;
+                       Error(cip, kDontPerror, "Remote read timed out.\n");
+                       break;
+               }
+               if (cip->cancelXfer > 0) {
+                       FTPAbortDataTransfer(cip);
+                       result = cip->errNo = kErrDataTransferAborted;
+                       break;
+               }
+#if !defined(NO_SIGNALS)
+               gCanBrokenDataJmp = 1;
+               if (cip->xferTimeout > 0)
+                       (void) alarm(cip->xferTimeout);
+#endif /* NO_SIGNALS */
+#ifdef NO_SIGNALS
+               nread = SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
+               if (nread == kTimeoutErr) {
+                       cip->errNo = result = kErrDataTimedOut;
+                       Error(cip, kDontPerror, "Remote read timed out.\n");
+                       break;
+               } else if (nread < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       Error(cip, kDoPerror, "Remote read failed.\n");
+                       result = kErrSocketReadFailed;
+                       cip->errNo = kErrSocketReadFailed;
+                       break;
+               } else if (nread == 0) {
+                       break;
+               }
+#else
+               nread = read(cip->dataSocket, buf, bufSize);
+               if (nread < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       Error(cip, kDoPerror, "Remote read failed.\n");
+                       result = kErrSocketReadFailed;
+                       cip->errNo = kErrSocketReadFailed;
+                       break;
+               } else if (nread == 0) {
+                       break;
+               }
+               gCanBrokenDataJmp = 0;
+#endif
+
+               nwrote = write(fd, buf, nread);
+               if (nwrote != nread) {
+                       if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                               result = kErrWriteFailed;
+                               cip->errNo = kErrWriteFailed;
+                               errno = EPIPE;
+                       } else {
+                               Error(cip, kDoPerror, "Local write failed.\n");
+                               result = kErrWriteFailed;
+                               cip->errNo = kErrWriteFailed;
+                       }
+                       break;
+               }
+               cip->bytesTransferred += (longest_int) nread;
+               FTPUpdateIOTimer(cip);
+       }
+
+#if !defined(NO_SIGNALS)
+       if (cip->xferTimeout > 0)
+               (void) alarm(0);
+       gCanBrokenDataJmp = 0;
+#endif /* NO_SIGNALS */
+
+       (void) close(fd);
+       for (;;) {
+#ifdef HAVE_WAITPID
+               if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
+                       break;
+#else
+               if ((wait(&status) < 0) && (errno != EINTR))
+                       break;
+#endif
+               if (WIFEXITED(status) || WIFSIGNALED(status))
+                       break;          /* done */
+       }
+
+       tmpResult = FTPEndDataCmd(cip, 1);
+       if ((tmpResult < 0) && (result == 0)) {
+               result = kErrRETRFailed;
+               cip->errNo = kErrRETRFailed;
+       }
+       FTPStopIOTimer(cip);
+#if !defined(NO_SIGNALS)
+       (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif
+
+       if ((result == 0) && (cip->bytesTransferred == 0)) {
+               result = kErrOpenFailed;
+               cip->errNo = kErrOpenFailed;
+       }
+       if (basecp != NULL)
+               (void) FTPChdir(cip, savedCwd);
+       return (result);
+}      /* FTPGetOneTarF */
+
+#endif /* TAR */
+
+
+
+
+
+static int
+FTPGetOneF(
+       const FTPCIPtr cip,
+       const char *const file,
+       const char *dstfile,
+       int xtype,
+       const int fdtouse,
+       longest_int expectedSize,
+       time_t mdtm,
+       const int resumeflag,
+       const int appendflag,
+       const int deleteflag,
+       const ConfirmResumeDownloadProc resumeProc)
+{
+       char *buf;
+       size_t bufSize;
+       int tmpResult;
+       volatile int result;
+       int nread, nwrote;
+       volatile int fd;
+#if ASCII_TRANSLATION
+       char *src, *srclim;
+       char *dst, *dstlim;
+       char outbuf[512];
+#endif
+       volatile longest_int startPoint = 0;
+       struct utimbuf ut;
+       struct Stat st;
+#if !defined(NO_SIGNALS)
+       volatile FTPSigProc osigpipe;
+       volatile FTPCIPtr vcip;
+       volatile int vfd, vfdtouse;
+       int sj;
+#endif /* NO_SIGNALS */
+       volatile int created = 0;
+       int zaction = kConfirmResumeProcSaidBestGuess;
+       int statrc;
+       int noMdtmCheck;
+       time_t now;
+
+       if (cip->buf == NULL) {
+               Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
+               cip->errNo = kErrNoBuf;
+               return (cip->errNo);
+       }
+
+       result = kNoErr;
+       cip->usingTAR = 0;
+
+       if (fdtouse < 0) {
+               /* Only ask for extended information
+                * if we have the name of the file
+                * and we didn't already have the
+                * info.
+                *
+                * Always ask for the modification time,
+                * because even if it was passed in it
+                * may not be accurate.  This is often
+                * the case when it came from an ls
+                * listing, in which the local time
+                * zone could be a factor.
+                *
+                */
+
+               AutomaticallyUseASCIIModeDependingOnExtension(cip, file, &xtype);
+               if (expectedSize == kSizeUnknown) {
+                       (void) FTPFileSizeAndModificationTime(cip, file, &expectedSize, xtype, &mdtm);
+               } else {
+                       (void) FTPFileModificationTime(cip, file, &mdtm);
+               }
+
+               /* For Get, we can't recover very well if it turns out restart
+                * didn't work, so check beforehand.
+                */
+               if ((resumeflag == kResumeYes) || (resumeProc != NoConfirmResumeDownloadProc)) {
+                       if (cip->hasREST == kCommandAvailabilityUnknown) {
+                               (void) FTPSetTransferType(cip, kTypeBinary);
+                               if (SetStartOffset(cip, (longest_int) 1) == kNoErr) {
+                                       /* Now revert -- we still may not end up
+                                        * doing it.
+                                        */
+                                       SetStartOffset(cip, (longest_int) -1);
+                               }
+                       }
+               }
+
+               if (appendflag == kAppendYes) {
+                       zaction = kConfirmResumeProcSaidAppend;
+               } else if (cip->hasREST == kCommandNotAvailable) {
+                       zaction = kConfirmResumeProcSaidOverwrite;
+               } else if (resumeflag == kResumeYes) {
+                       zaction = kConfirmResumeProcSaidBestGuess;
+               } else {
+                       zaction = kConfirmResumeProcSaidOverwrite;
+               }
+
+               statrc = Stat(dstfile, &st);
+               if (statrc == 0) {
+                       if (resumeProc != NULL) {
+                               zaction = (*resumeProc)(
+                                               &dstfile,
+                                               (longest_int) st.st_size,
+                                               st.st_mtime,
+                                               file,
+                                               expectedSize,
+                                               mdtm,
+                                               &startPoint
+                               );
+                       }
+
+                       if (zaction == kConfirmResumeProcSaidBestGuess) {
+                               if (expectedSize != kSizeUnknown) {
+                                       /* We know the size of the remote file,
+                                        * and we have a local file too.
+                                        *
+                                        * Try and decide if we need to get
+                                        * the entire file, or just part of it.
+                                        */
+
+                                       startPoint = (longest_int) st.st_size;
+                                       zaction = kConfirmResumeProcSaidResume;
+
+                                       /* If the local file exists and has a recent
+                                        * modification time (< 12 hours) and
+                                        * the remote file's modtime is not recent,
+                                        * then heuristically conclude that the
+                                        * local modtime should not be trusted
+                                        * (i.e. user killed the process before
+                                        * the local modtime could be preserved).
+                                        */
+                                       noMdtmCheck = 0;
+                                       if (mdtm != kModTimeUnknown) {
+                                               time(&now);
+                                               if ((st.st_mtime > now) || (((now - st.st_mtime) < 46200) && ((now - mdtm) >= 46200)))
+                                                       noMdtmCheck = 1;
+                                       }
+
+                                       if ((mdtm == kModTimeUnknown) || (noMdtmCheck != 0)) {
+                                               /* Can't use the timestamps as an aid. */
+                                               if (startPoint == expectedSize) {
+                                                       /* Don't go to all the trouble of downloading nothing. */
+                                                       cip->errNo = kErrLocalSameAsRemote;
+                                                       if (deleteflag == kDeleteYes)
+                                                               (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
+                                                       return (cip->errNo);
+                                               } else if (startPoint > expectedSize) {
+                                                       /* Panic;  odds are the file we have
+                                                        * was a different file altogether,
+                                                        * since it is larger than the
+                                                        * remote copy.  Re-do it all.
+                                                        */
+                                                       zaction = kConfirmResumeProcSaidOverwrite;
+                                               } /* else resume at startPoint */
+                                       } else if ((mdtm == st.st_mtime) || (mdtm == (st.st_mtime - 1)) || (mdtm == (st.st_mtime + 1))) {
+                                               /* File has the same time.
+                                                * Note: Windows' file timestamps can be off by one second!
+                                                */
+                                               if (startPoint == expectedSize) {
+                                                       /* Don't go to all the trouble of downloading nothing. */
+                                                       cip->errNo = kErrLocalSameAsRemote;
+                                                       if (deleteflag == kDeleteYes)
+                                                               (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
+                                                       return (cip->errNo);
+                                               } else if (startPoint > expectedSize) {
+                                                       /* Panic;  odds are the file we have
+                                                        * was a different file altogether,
+                                                        * since it is larger than the
+                                                        * remote copy.  Re-do it all.
+                                                        */
+                                                       zaction = kConfirmResumeProcSaidOverwrite;
+                                               } else {
+                                                       /* We have a file by the same time,
+                                                        * but smaller start point.  Leave
+                                                        * the startpoint as is since it
+                                                        * is most likely valid.
+                                                        */
+                                               }
+                                       } else if (mdtm < st.st_mtime) {
+                                               /* Remote file is older than
+                                                * local file.  Don't overwrite
+                                                * our file.
+                                                */
+                                               cip->errNo = kErrLocalFileNewer;
+                                               return (cip->errNo);
+                                       } else /* if (mdtm > st.st_mtime) */ {
+                                               /* File has a newer timestamp
+                                                * altogether, assume the remote
+                                                * file is an entirely new file
+                                                * and replace ours with it.
+                                                */
+                                               zaction = kConfirmResumeProcSaidOverwrite;
+                                       }
+                               } else {
+                                               zaction = kConfirmResumeProcSaidOverwrite;
+                               }
+                       }
+               } else {
+                       zaction = kConfirmResumeProcSaidOverwrite;
+               }
+
+               if (zaction == kConfirmResumeProcSaidCancel) {
+                       /* User wants to cancel this file and any
+                        * remaining in batch.
+                        */
+                       cip->errNo = kErrUserCanceled;
+                       return (cip->errNo);
+               } else if (zaction == kConfirmResumeProcSaidSkip) {
+                       /* Nothing done, but not an error. */
+                       if (deleteflag == kDeleteYes)
+                               (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
+                       return (kNoErr);
+               } else if (zaction == kConfirmResumeProcSaidResume) {
+                       /* Resume; proc set the startPoint. */
+                       if (startPoint == expectedSize) {
+                               /* Don't go to all the trouble of downloading nothing. */
+                               /* Nothing done, but not an error. */
+                               if (deleteflag == kDeleteYes)
+                                       (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
+                               return (kNoErr);
+                       } else if (startPoint > expectedSize) {
+                               /* Cannot set start point past end of remote file */
+                               cip->errNo = result = kErrSetStartPoint;
+                               return (result);
+                       }
+                       fd = Open(dstfile, O_WRONLY|O_APPEND|O_BINARY, 00666);
+               } else if (zaction == kConfirmResumeProcSaidAppend) {
+                       /* leave startPoint at zero, we will append everything. */
+                       startPoint = (longest_int) 0;
+                       fd = Open(dstfile, O_WRONLY|O_CREAT|O_APPEND|O_BINARY, 00666);
+               } else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
+                       created = 1;
+                       startPoint = (longest_int) 0;
+                       fd = Open(dstfile, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 00666);
+               }
+
+               if (fd < 0) {
+                       Error(cip, kDoPerror, "Cannot open local file %s for writing.\n", dstfile);
+                       result = kErrOpenFailed;
+                       cip->errNo = kErrOpenFailed;
+                       return (result);
+               }
+
+               if ((expectedSize == (longest_int) 0) && (startPoint <= (longest_int) 0) && (zaction != kConfirmResumeProcSaidOverwrite)) {
+                       /* Don't go to all the trouble of downloading nothing. */
+#if defined(WIN32) || defined(_WINDOWS)
+                       /* Note: Windows doesn't allow zero-size files. */
+                       (void) write(fd, "\r\n", 2);
+#endif
+                       (void) close(fd);
+                       if (mdtm != kModTimeUnknown) {
+                               cip->mdtm = mdtm;
+                               (void) time(&ut.actime);
+                               ut.modtime = mdtm;
+                               (void) utime(dstfile, &ut);
+                       }
+                       if (deleteflag == kDeleteYes)
+                               (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
+                       return (kNoErr);
+               }
+       } else {
+               fd = fdtouse;
+       }
+
+       if ((cip->numDownloads == 0) && (cip->dataSocketRBufSize > 0)) {
+               /* If dataSocketSBufSize is non-zero, it means you
+                * want to explicitly try to set the size of the
+                * socket's I/O buffer.
+                *
+                * If it is zero, it means you want to just use the
+                * TCP stack's default value, which is typically
+                * between 8 and 64 kB.
+                *
+                * If you try to set the buffer larger than 64 kB,
+                * the TCP stack should try to use RFC 1323 to
+                * negotiate "TCP Large Windows" which may yield
+                * significant performance gains.
+                */
+               if (cip->hasRETRBUFSIZE == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE RETRBUFSIZE %lu", (unsigned long) cip->dataSocketRBufSize);
+               else if (cip->hasRBUFSIZ == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE RBUFSIZ %lu", (unsigned long) cip->dataSocketRBufSize);
+               else if (cip->hasRBUFSZ == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketRBufSize);
+               else if (cip->hasBUFSIZE == kCommandAvailable)
+                       (void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
+       }
+
+#ifdef NO_SIGNALS
+#else  /* NO_SIGNALS */
+       vcip = cip;
+       vfdtouse = fdtouse;
+       vfd = fd;
+       osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
+
+       gGotBrokenData = 0;
+       gCanBrokenDataJmp = 0;
+
+#ifdef HAVE_SIGSETJMP
+       sj = sigsetjmp(gBrokenDataJmp, 1);
+#else
+       sj = setjmp(gBrokenDataJmp);
+#endif /* HAVE_SIGSETJMP */
+
+       if (sj != 0) {
+               (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+               if (vfdtouse < 0) {
+                       (void) close(vfd);
+               }
+               FTPShutdownHost(vcip);
+               vcip->errNo = kErrRemoteHostClosedConnection;
+               return(vcip->errNo);
+       }
+       gCanBrokenDataJmp = 1;
+#endif /* NO_SIGNALS */
+
+       tmpResult = FTPStartDataCmd(cip, kNetReading, xtype, startPoint, "RETR %s", file);
+
+       if (tmpResult < 0) {
+               result = tmpResult;
+               if (result == kErrGeneric)
+                       result = kErrRETRFailed;
+               cip->errNo = result;
+               if (fdtouse < 0) {
+                       (void) close(fd);
+                       if ((created != 0) && (appendflag == kAppendNo) && (cip->startPoint == 0))
+                               (void) unlink(dstfile);
+               }
+#if !defined(NO_SIGNALS)
+               (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif /* NO_SIGNALS */
+               return (result);
+       }
+
+       if ((startPoint != 0) && (cip->startPoint == 0)) {
+               /* Remote could not or would not set the start offset
+                * to what we wanted.
+                *
+                * So now we have to undo our seek.
+                */
+               if (Lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
+                       cip->errNo = kErrLseekFailed;
+                       if (fdtouse < 0) {
+                               (void) close(fd);
+                       }
+#if !defined(NO_SIGNALS)
+                       (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif /* NO_SIGNALS */
+                       return (cip->errNo);
+               }
+               startPoint = 0;
+       }
+
+       buf = cip->buf;
+       bufSize = cip->bufSize;
+
+       FTPInitIOTimer(cip);
+       cip->mdtm = mdtm;
+       (void) time(&ut.actime);
+       ut.modtime = mdtm;
+       cip->expectedSize = expectedSize;
+       cip->lname = dstfile;   /* could be NULL */
+       cip->rname = file;
+       if (fdtouse >= 0)
+               cip->useProgressMeter = 0;
+       FTPStartIOTimer(cip);
+
+#if ASCII_TRANSLATION
+       if (xtype == kTypeAscii) {
+               /* Ascii */
+               for (;;) {
+                       if (! WaitForRemoteInput(cip)) {        /* could set cancelXfer */
+                               cip->errNo = result = kErrDataTimedOut;
+                               Error(cip, kDontPerror, "Remote read timed out.\n");
+                               break;
+                       }
+                       if (cip->cancelXfer > 0) {
+                               FTPAbortDataTransfer(cip);
+                               result = cip->errNo = kErrDataTransferAborted;
+                               break;
+                       }
+#ifdef TESTING_ABOR
+                       if (cip->bytesTransferred > 0) {
+                               cip->cancelXfer = 1;
+                               FTPAbortDataTransfer(cip);
+                               result = cip->errNo = kErrDataTransferAborted;
+                               break;
+                       }
+#endif /* TESTING_ABOR */
+#ifdef NO_SIGNALS
+                       nread = SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
+                       if (nread == kTimeoutErr) {
+                               cip->errNo = result = kErrDataTimedOut;
+                               Error(cip, kDontPerror, "Remote read timed out.\n");
+                               break;
+                       } else if (nread < 0) {
+                               if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                       result = cip->errNo = kErrSocketReadFailed;
+                                       errno = EPIPE;
+                                       Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                               } else if (errno == EINTR) {
+                                       continue;
+                               } else {
+                                       Error(cip, kDoPerror, "Remote read failed.\n");
+                                       result = kErrSocketReadFailed;
+                                       cip->errNo = kErrSocketReadFailed;
+                               }
+                               break;
+                       } else if (nread == 0) {
+                               break;
+                       }
+#else
+                       gCanBrokenDataJmp = 1;
+                       if (cip->xferTimeout > 0)
+                               (void) alarm(cip->xferTimeout);
+                       nread = read(cip->dataSocket, buf, bufSize);
+                       if (nread < 0) {
+                               if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                       result = cip->errNo = kErrSocketReadFailed;
+                                       errno = EPIPE;
+                                       Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                                       (void) shutdown(cip->dataSocket, 2);
+                               } else if (errno == EINTR) {
+                                       continue;
+                               } else {
+                                       result = cip->errNo = kErrSocketReadFailed;
+                                       Error(cip, kDoPerror, "Remote read failed.\n");
+                                       (void) shutdown(cip->dataSocket, 2);
+                               }
+                               break;
+                       } else if (nread == 0) {
+                               break;
+                       }
+
+                       gCanBrokenDataJmp = 0;
+#endif /* NO_SIGNALS */
+
+                       src = buf;
+                       srclim = src + nread;
+                       dst = outbuf;
+                       dstlim = dst + sizeof(outbuf);
+                       while (src < srclim) {
+                               if (*src == '\r') {
+                                       src++;
+                                       continue;
+                               }
+                               if (dst >= dstlim) {
+                                       nwrote = write(fd, outbuf, (size_t) (dst - outbuf));
+                                       if (nwrote == (int) (dst - outbuf)) {
+                                               /* Success. */
+                                               dst = outbuf;
+                                       } else if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                               result = kErrWriteFailed;
+                                               cip->errNo = kErrWriteFailed;
+                                               errno = EPIPE;
+                                               (void) shutdown(cip->dataSocket, 2);
+                                               goto brk;
+                                       } else {
+                                               Error(cip, kDoPerror, "Local write failed.\n");
+                                               result = kErrWriteFailed;
+                                               cip->errNo = kErrWriteFailed;
+                                               (void) shutdown(cip->dataSocket, 2);
+                                               goto brk;
+                                       }
+                               }
+                               *dst++ = *src++;
+                       }
+                       if (dst > outbuf) {
+                               nwrote = write(fd, outbuf, (size_t) (dst - outbuf));
+                               if (nwrote != (int) (dst - outbuf)) {
+                                       if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                               result = kErrWriteFailed;
+                                               cip->errNo = kErrWriteFailed;
+                                               errno = EPIPE;
+                                               (void) shutdown(cip->dataSocket, 2);
+                                               goto brk;
+                                       } else {
+                                               Error(cip, kDoPerror, "Local write failed.\n");
+                                               result = kErrWriteFailed;
+                                               cip->errNo = kErrWriteFailed;
+                                               (void) shutdown(cip->dataSocket, 2);
+                                               goto brk;
+                                       }
+                               }
+                       }
+
+                       if (mdtm != kModTimeUnknown) {
+                               (void) utime(dstfile, &ut);
+                       }
+                       cip->bytesTransferred += (longest_int) nread;
+                       FTPUpdateIOTimer(cip);
+               }
+       } else
+#endif /* ASCII_TRANSLATION */
+       {
+               /* Binary */
+               for (;;) {
+                       if (! WaitForRemoteInput(cip)) {        /* could set cancelXfer */
+                               cip->errNo = result = kErrDataTimedOut;
+                               Error(cip, kDontPerror, "Remote read timed out.\n");
+                               break;
+                       }
+                       if (cip->cancelXfer > 0) {
+                               FTPAbortDataTransfer(cip);
+                               result = cip->errNo = kErrDataTransferAborted;
+                               break;
+                       }
+#ifdef TESTING_ABOR
+                       if (cip->bytesTransferred > 0) {
+                               cip->cancelXfer = 1;
+                               FTPAbortDataTransfer(cip);
+                               result = cip->errNo = kErrDataTransferAborted;
+                               break;
+                       }
+#endif /* TESTING_ABOR */
+#ifdef NO_SIGNALS
+                       nread = SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
+                       if (nread == kTimeoutErr) {
+                               cip->errNo = result = kErrDataTimedOut;
+                               Error(cip, kDontPerror, "Remote read timed out.\n");
+                               break;
+                       } else if (nread < 0) {
+                               if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                       result = cip->errNo = kErrSocketReadFailed;
+                                       errno = EPIPE;
+                                       Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                               } else if (errno == EINTR) {
+                                       continue;
+                               } else {
+                                       Error(cip, kDoPerror, "Remote read failed.\n");
+                                       result = kErrSocketReadFailed;
+                                       cip->errNo = kErrSocketReadFailed;
+                               }
+                               break;
+                       } else if (nread == 0) {
+                               break;
+                       }
+#else
+                       gCanBrokenDataJmp = 1;
+                       if (cip->xferTimeout > 0)
+                               (void) alarm(cip->xferTimeout);
+                       nread = read(cip->dataSocket, buf, bufSize);
+                       if (nread < 0) {
+                               if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                       result = cip->errNo = kErrSocketReadFailed;
+                                       errno = EPIPE;
+                                       Error(cip, kDoPerror, "Lost data connection to remote host.\n");
+                               } else if (errno == EINTR) {
+                                       continue;
+                               } else {
+                                       result = cip->errNo = kErrSocketReadFailed;
+                                       Error(cip, kDoPerror, "Remote read failed.\n");
+                               }
+                               (void) shutdown(cip->dataSocket, 2);
+                               break;
+                       } else if (nread == 0) {
+                               break;
+                       }
+                       gCanBrokenDataJmp = 0;
+#endif /* NO_SIGNALS */
+
+                       nwrote = write(fd, buf, nread);
+                       if (nwrote != nread) {
+                               if ((gGotBrokenData != 0) || (errno == EPIPE)) {
+                                       result = kErrWriteFailed;
+                                       cip->errNo = kErrWriteFailed;
+                                       errno = EPIPE;
+                               } else {
+                                       Error(cip, kDoPerror, "Local write failed.\n");
+                                       result = kErrWriteFailed;
+                                       cip->errNo = kErrWriteFailed;
+                               }
+                               (void) shutdown(cip->dataSocket, 2);
+                               break;
+                       }
+
+                       /* Ugggh... do this after each write operation
+                        * so it minimizes the chance of a user killing
+                        * the process before we reset the timestamps.
+                        */
+                       if (mdtm != kModTimeUnknown) {
+                               (void) utime(dstfile, &ut);
+                       }
+                       cip->bytesTransferred += (longest_int) nread;
+                       FTPUpdateIOTimer(cip);
+               }
+       }
+
+#if ASCII_TRANSLATION
+brk:
+#endif
+
+#if !defined(NO_SIGNALS)
+       if (cip->xferTimeout > 0)
+               (void) alarm(0);
+       gCanBrokenDataJmp = 0;
+#endif /* NO_SIGNALS */
+
+       if (fdtouse < 0) {
+               /* If they gave us a descriptor (fdtouse >= 0),
+                * leave it open, otherwise we opened it, so
+                * we need to close it.
+                */
+               (void) close(fd);
+               fd = -1;
+       }
+
+       tmpResult = FTPEndDataCmd(cip, 1);
+       if ((tmpResult < 0) && (result == 0)) {
+               result = kErrRETRFailed;
+               cip->errNo = kErrRETRFailed;
+       }
+       FTPStopIOTimer(cip);
+#if !defined(NO_SIGNALS)
+       (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
+#endif /* NO_SIGNALS */
+
+       if ((mdtm != kModTimeUnknown) && (cip->bytesTransferred > 0)) {
+               (void) utime(dstfile, &ut);
+       }
+
+       if (result == kNoErr) {
+               cip->numDownloads++;
+
+               if (deleteflag == kDeleteYes) {
+                       result = FTPDelete(cip, file, kRecursiveNo, kGlobNo);
+               }
+       }
+
+       return (result);
+}      /* FTPGetOneF */
+
+
+
+
+int
+FTPGetOneFile3(
+       const FTPCIPtr cip,
+       const char *const file,
+       const char *const dstfile,
+       const int xtype,
+       const int fdtouse,
+       const int resumeflag,
+       const int appendflag,
+       const int deleteflag,
+       const ConfirmResumeDownloadProc resumeProc,
+       int UNUSED(reserved))
+{
+       int result;
+
+       LIBNCFTP_USE_VAR(reserved);
+       if (cip == NULL)
+               return (kErrBadParameter);
+       if (strcmp(cip->magic, kLibraryMagic))
+               return (kErrBadMagic);
+
+       if ((file == NULL) || (file[0] == '\0'))
+               return (kErrBadParameter);
+       if (fdtouse < 0) {
+               if ((dstfile == NULL) || (dstfile[0] == '\0'))
+                       return (kErrBadParameter);
+       }
+
+       result = FTPGetOneF(cip, file, dstfile, xtype, fdtouse, kSizeUnknown, kModTimeUnknown, resumeflag, appendflag, deleteflag, resumeProc);
+       return (result);
+}      /* FTPGetOneFile3 */
+
+
+
+
+int
+FTPGetFiles3(
+       const FTPCIPtr cip,
+       const char *pattern1,
+       const char *const dstdir1,
+       const int recurse,
+       int doGlob,
+       const int xtype,
+       const int resumeflag,
+       int appendflag,
+       const int deleteflag,
+       const int tarflag,
+       const ConfirmResumeDownloadProc resumeProc,
+       int UNUSED(reserved))
+{
+       LineList globList;
+       LinePtr itemPtr;
+       FileInfoList files;
+       FileInfoPtr filePtr;
+       int batchResult;
+       int result;
+       char *ldir;
+       char *cp;
+       const char *dstdir;
+       const char *pattern;
+       char *pattern2, *dstdir2;
+       char c;
+       int recurse1;
+       int errRc;
+
+       LIBNCFTP_USE_VAR(reserved);
+       if (cip == NULL)
+               return (kErrBadParameter);
+       if (strcmp(cip->magic, kLibraryMagic))
+               return (kErrBadMagic);
+       if (pattern1 == NULL)
+               return (kErrBadParameter);
+
+       dstdir2 = NULL;
+       pattern2 = NULL;
+
+       if (dstdir1 == NULL) {
+               dstdir = NULL;
+       } else {
+               dstdir2 = StrDup(dstdir1);
+               if (dstdir2 == NULL) {
+                       errRc = kErrMallocFailed;
+                       goto return_err;
+               }
+               StrRemoveTrailingLocalPathDelim(dstdir2);
+               dstdir = dstdir2;
+       }
+
+       pattern2 = StrDup(pattern1);
+       if (pattern2 == NULL) {
+               errRc = kErrMallocFailed;
+               goto return_err;
+       }
+       StrRemoveTrailingSlashes(pattern2);
+       pattern = pattern2;
+
+       if (pattern[0] == '\0') {
+               if (recurse == kRecursiveNo) {
+                       errRc = kErrBadParameter;
+                       goto return_err;
+               }
+               pattern = ".";
+               doGlob = kGlobNo;
+       } else if (strcmp(pattern, ".") == 0) {
+               if (recurse == kRecursiveNo) {
+                       errRc = kErrBadParameter;
+                       goto return_err;
+               }
+               doGlob = kGlobNo;
+       }
+       if (recurse == kRecursiveYes)
+               appendflag = kAppendNo;
+
+       batchResult = FTPRemoteGlob(cip, &globList, pattern, doGlob);
+       if (batchResult != kNoErr) {
+               errRc = batchResult;
+               goto return_err;
+       }
+
+       cip->cancelXfer = 0;    /* should already be zero */
+
+       for (itemPtr = globList.first; itemPtr != NULL; itemPtr = itemPtr->next) {
+               if ((recurse == kRecursiveYes) && (FTPIsDir(cip, itemPtr->line) > 0)) {
+#ifdef TAR
+                       if ((tarflag == kTarYes) && (xtype == kTypeBinary) && (appendflag == kAppendNo) && (deleteflag == kDeleteNo) && (FTPGetOneTarF(cip, itemPtr->line, dstdir) == kNoErr)) {
+                               /* Great! */
+                               continue;
+                       }
+#endif /* TAR */
+                       (void) FTPRemoteRecursiveFileList1(cip, itemPtr->line, &files);
+                       (void) ComputeLNames(&files, itemPtr->line, dstdir, 1);
+                       recurse1 = recurse;
+               } else {
+                       recurse1 = kRecursiveNo;
+                       (void) LineToFileInfoList(itemPtr, &files);
+                       (void) ComputeRNames(&files, ".", 0, 1);
+                       (void) ComputeLNames(&files, NULL, dstdir, 0);
+               }
+               if (cip->cancelXfer > 0) {
+                       DisposeFileInfoListContents(&files);
+                       break;
+               }
+
+#if 0
+               for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
+                       PrintF(cip, "  R=%s, L=%s, 2=%s, size=%d, mdtm=%u, type=%c\n",
+                               filePtr->rname,
+                               filePtr->lname,
+                               filePtr->rlinkto ? filePtr->rlinkto : "",
+                               filePtr->size,
+                               (unsigned int) filePtr->mdtm,
+                               filePtr->type
+                       );
+               }
+#endif
+
+
+               for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
+                       if (cip->connected == 0) {
+                               if (batchResult == kNoErr)
+                                       batchResult = kErrRemoteHostClosedConnection;
+                               break;
+                       }
+                       if (filePtr->type == 'd') {
+#if defined(WIN32) || defined(_WINDOWS)
+                               (void) MkDirs(filePtr->lname, 00777);
+#else
+                               (void) mkdir(filePtr->lname, 00777);
+#endif
+                       } else if (filePtr->type == 'l') {
+                               /* skip it -- we do that next pass. */
+                       } else if (recurse1 != kRecursiveYes) {
+                               result = FTPGetOneF(cip, filePtr->rname, filePtr->lname, xtype, -1, filePtr->size, filePtr->mdtm, resumeflag, appendflag, deleteflag, resumeProc);
+                               if (files.nFileInfos == 1) {
+                                       if (result != kNoErr)
+                                               batchResult = result;
+                               } else {
+                                       if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
+                                               batchResult = result;
+                               }
+                               if (result == kErrUserCanceled)
+                                       cip->cancelXfer = 1;
+                               if (cip->cancelXfer > 0)
+                                       break;
+                       } else {
+                               ldir = filePtr->lname;
+                               cp = StrRFindLocalPathDelim(ldir);
+                               if (cp != NULL) {
+                                       while (cp > ldir) {
+                                               if (! IsLocalPathDelim(*cp)) {
+                                                       ++cp;
+                                                       break;
+                                               }
+                                               --cp;
+                                       }
+                                       if (cp > ldir) {
+                                               c = *cp;
+                                               *cp = '\0';
+                                               if (MkDirs(ldir, 00777) < 0) {
+                                                       Error(cip, kDoPerror, "Could not create local directory \"%s\"\n", ldir);
+                                                       batchResult = kErrGeneric;
+                                                       *cp = c;
+                                                       continue;
+                                               }
+                                               *cp = c;
+                                       }
+                               }
+                               result = FTPGetOneF(cip, filePtr->rname, filePtr->lname, xtype, -1, filePtr->size, filePtr->mdtm, resumeflag, appendflag, deleteflag, resumeProc);
+
+                               if (files.nFileInfos == 1) {
+                                       if (result != kNoErr)
+                                               batchResult = result;
+                               } else {
+                                       if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
+                                               batchResult = result;
+                               }
+                               if (result == kErrUserCanceled)
+                                       cip->cancelXfer = 1;
+                               if (cip->cancelXfer > 0)
+                                       break;
+                       }
+               }
+               if (cip->cancelXfer > 0) {
+                       DisposeFileInfoListContents(&files);
+                       break;
+               }
+
+#ifdef HAVE_SYMLINK
+               for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
+                       if (filePtr->type == 'l') {
+                               (void) unlink(filePtr->lname);
+                               if (symlink(filePtr->rlinkto, filePtr->lname) < 0) {
+                                       Error(cip, kDoPerror, "Could not symlink %s to %s\n", filePtr->rlinkto, filePtr->lname);
+                                       /* Note: not worth setting batchResult */
+                               }
+                       }
+               }
+#endif /* HAVE_SYMLINK */
+
+
+               DisposeFileInfoListContents(&files);
+       }
+
+       DisposeLineListContents(&globList);
+       if (batchResult < 0)
+               cip->errNo = batchResult;
+       errRc = batchResult;
+
+return_err:
+       if (dstdir2 != NULL)
+               free(dstdir2);
+       if (pattern2 != NULL)
+               free(pattern2);
+       return (errRc);
+}      /* FTPGetFiles3 */
+
+
+
+
+/*------------------------- wrappers for old routines ----------------------*/
+
+int
+FTPGetOneFile(const FTPCIPtr cip, const char *const file, const char *const dstfile)
+{
+       return (FTPGetOneFile3(cip, file, dstfile, kTypeBinary, -1, kResumeNo, kAppendNo, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
+}      /* FTPGetOneFile */
+
+
+
+
+int
+FTPGetOneFile2(const FTPCIPtr cip, const char *const file, const char *const dstfile, const int xtype, const int fdtouse, const int resumeflag, const int appendflag)
+{
+       return (FTPGetOneFile3(cip, file, dstfile, xtype, fdtouse, resumeflag, appendflag, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
+}      /* FTPGetOneFile2 */
+
+
+
+
+int
+FTPGetFiles(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
+{
+       return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeBinary, kResumeNo, kAppendNo, kDeleteNo, kTarYes, (ConfirmResumeDownloadProc) 0, 0));
+}      /* FTPGetFiles */
+
+
+
+
+int
+FTPGetFiles2(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob, const int xtype, const int resumeflag, const int appendflag)
+{
+       return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, xtype, resumeflag, appendflag, kDeleteNo, kTarYes, (ConfirmResumeDownloadProc) 0, 0));
+}      /* FTPGetFiles2 */
+
+
+
+
+int
+FTPGetOneFileAscii(const FTPCIPtr cip, const char *const file, const char *const dstfile)
+{
+       return (FTPGetOneFile3(cip, file, dstfile, kTypeAscii, -1, kResumeNo, kAppendNo, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
+}      /* FTPGetOneFileAscii */
+
+
+
+
+int
+FTPGetFilesAscii(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
+{
+       return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeAscii, kResumeNo, kAppendNo, kDeleteNo, kTarNo, (ConfirmResumeDownloadProc) 0, 0));
+}      /* FTPGetFilesAscii */
+
+
+
+
+int
+FTPPutOneFile(const FTPCIPtr cip, const char *const file, const char *const dstfile)
+{
+       return (FTPPutOneFile3(cip, file, dstfile, kTypeBinary, -1, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
+}      /* FTPPutOneFile */
+
+
+
+
+int
+FTPPutOneFile2(const FTPCIPtr cip, const char *const file, const char *const dstfile, const int xtype, const int fdtouse, const int appendflag, const char *const tmppfx, const char *const tmpsfx)
+{
+       return (FTPPutOneFile3(cip, file, dstfile, xtype, fdtouse, appendflag, tmppfx, tmpsfx, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
+}      /* FTPPutOneFile2 */
+
+
+
+
+int
+FTPPutFiles(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
+{
+       return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeBinary, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
+}      /* FTPPutFiles */
+
+
+
+
+int
+FTPPutFiles2(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob, const int xtype, const int appendflag, const char *const tmppfx, const char *const tmpsfx)
+{
+       return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, xtype, appendflag, tmppfx, tmpsfx, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
+}      /* FTPPutFiles2 */
+
+
+
+
+int
+FTPPutOneFileAscii(const FTPCIPtr cip, const char *const file, const char *const dstfile)
+{
+       return (FTPPutOneFile3(cip, file, dstfile, kTypeAscii, -1, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
+}      /* FTPPutOneFileAscii */
+
+
+
+
+int
+FTPPutFilesAscii(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
+{
+       return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeAscii, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
+}      /* FTPPutFilesAscii */
+
+
+
+int
+FTPListToMemory(const FTPCIPtr cip, const char *const pattern, const LineListPtr llines, const char *const lsflags)
+{
+       return (FTPListToMemory2(cip, pattern, llines, lsflags, 1, (int *) 0));
+}      /* FTPListToMemory */
+
+/* eof IO.c */