1 /* io.c
2  *
3  * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
4  * All rights reserved.
5  *
6  */
7 
8 #include "syshdrs.h"
9 
10 static int gGotBrokenData = 0;
11 
12 #if defined(WIN32) || defined(_WINDOWS)
13 #	define ASCII_TRANSLATION 0
14 #endif
15 
16 #ifndef ASCII_TRANSLATION
17 #	define ASCII_TRANSLATION 1
18 #endif
19 
20 #if !defined(NO_SIGNALS) && (USE_SIO || !defined(SIGALRM) || !defined(SIGPIPE) || !defined(SIGINT))
21 #	define NO_SIGNALS 1
22 #endif
23 
24 #ifndef NO_SIGNALS
25 
26 #ifdef HAVE_SIGSETJMP
27 static sigjmp_buf gBrokenDataJmp;
28 #else
29 static jmp_buf gBrokenDataJmp;
30 #endif	/* HAVE_SIGSETJMP */
31 static int gCanBrokenDataJmp = 0;
32 
33 #endif	/* NO_SIGNALS */
34 
35 
36 #ifndef O_BINARY
37 	/* Needed for platforms using different EOLN sequence (i.e. DOS) */
38 #	ifdef _O_BINARY
39 #		define O_BINARY _O_BINARY
40 #	else
41 #		define O_BINARY 0
42 #	endif
43 #endif
44 
45 static int WaitForRemoteInput(const FTPCIPtr cip);
46 static int WaitForRemoteOutput(const FTPCIPtr cip);
47 
48 
49 #ifndef NO_SIGNALS
50 
51 static void
52 BrokenData(int signum)
53 {
54 	gGotBrokenData = signum;
55 	if (gCanBrokenDataJmp != 0) {
56 		gCanBrokenDataJmp = 0;
57 #ifdef HAVE_SIGSETJMP
58 		siglongjmp(gBrokenDataJmp, 1);
59 #else
60 		longjmp(gBrokenDataJmp, 1);
61 #endif	/* HAVE_SIGSETJMP */
62 	}
63 }	/* BrokenData */
64 
65 #endif	/* NO_SIGNALS */
66 
67 
68 
69 
70 void
71 FTPInitIOTimer(const FTPCIPtr cip)
72 {
73 	cip->bytesTransferred = (longest_int) 0;
74 	cip->expectedSize = kSizeUnknown;
75 	cip->mdtm = kModTimeUnknown;
76 	cip->rname = NULL;
77 	cip->lname = NULL;
78 	cip->kBytesPerSec = -1.0;
79 	cip->percentCompleted = -1.0;
80 	cip->sec = -1.0;
81 	cip->secLeft = -1.0;
82 	cip->nextProgressUpdate = 0;
83 	cip->stalled = 0;
84 	cip->dataTimedOut = 0;
85 	cip->useProgressMeter = 1;
86 	(void) gettimeofday(&cip->t0, NULL);
87 }	/* FTPInitIOTimer */
88 
89 
90 
91 
92 void
93 FTPStartIOTimer(const FTPCIPtr cip)
94 {
95 	(void) gettimeofday(&cip->t0, NULL);
96 	if (cip->progress != (FTPProgressMeterProc) 0)
97 		(*cip->progress)(cip, kPrInitMsg);
98 }	/* FTPStartIOTimer */
99 
100 
101 
102 
103 void
104 FTPUpdateIOTimer(const FTPCIPtr cip)
105 {
106 	double sec;
107 	struct timeval *t0, t1;
108 	time_t now;
109 
110 	(void) time(&now);
111 	if (now < cip->nextProgressUpdate)
112 		return;
113 	now += 1;
114 	cip->nextProgressUpdate = now;
115 
116 	(void) gettimeofday(&t1, NULL);
117 	t0 = &cip->t0;
118 
119 	if (t0->tv_usec > t1.tv_usec) {
120 		t1.tv_usec += 1000000;
121 		t1.tv_sec--;
122 	}
123 	sec = ((double) (t1.tv_usec - t0->tv_usec) * 0.000001)
124 		+ (t1.tv_sec - t0->tv_sec);
125 	if (sec > 0.0) {
126 		cip->kBytesPerSec = ((double) cip->bytesTransferred) / (1024.0 * sec);
127 	} else {
128 		cip->kBytesPerSec = -1.0;
129 	}
130 	if (cip->expectedSize == kSizeUnknown) {
131 		cip->percentCompleted = -1.0;
132 		cip->secLeft = -1.0;
133 	} else if (cip->expectedSize <= 0) {
134 		cip->percentCompleted = 100.0;
135 		cip->secLeft = 0.0;
136 	} else {
137 		cip->percentCompleted = ((double) (100.0 * (cip->bytesTransferred + cip->startPoint))) / ((double) cip->expectedSize);
138 		if (cip->percentCompleted >= 100.0) {
139 			cip->percentCompleted = 100.0;
140 			cip->secLeft = 0.0;
141 		} else if (cip->percentCompleted <= 0.0) {
142 			cip->secLeft = 999.0;
143 		}
144 		if (cip->kBytesPerSec > 0.0) {
145 			cip->secLeft = ((cip->expectedSize - cip->bytesTransferred - cip->startPoint) / 1024.0) / cip->kBytesPerSec;
146 			if (cip->secLeft < 0.0)
147 				cip->secLeft = 0.0;
148 		}
149 	}
150 	cip->sec = sec;
151 	if ((cip->progress != (FTPProgressMeterProc) 0) && (cip->useProgressMeter != 0))
152 		(*cip->progress)(cip, kPrUpdateMsg);
153 }	/* FTPUpdateIOTimer */
154 
155 
156 
157 
158 void
159 FTPStopIOTimer(const FTPCIPtr cip)
160 {
161 	cip->nextProgressUpdate = 0;	/* force last update */
162 	FTPUpdateIOTimer(cip);
163 	if (cip->progress != (FTPProgressMeterProc) 0)
164 		(*cip->progress)(cip, kPrEndMsg);
165 }	/* FTPStopIOTimer */
166 
167 
168 
169 
170 /* This isn't too useful -- it mostly serves as an example so you can write
171  * your own function to do what you need to do with the listing.
172  */
173 int
174 FTPList(const FTPCIPtr cip, const int outfd, const int longMode, const char *const lsflag)
175 {
176 	const char *cmd;
177 	char line[512];
178 	char secondaryBuf[768];
179 #ifndef NO_SIGNALS
180 	char *secBufPtr, *secBufLimit;
181 	int nread;
182 	volatile int result;
183 #else	/* NO_SIGNALS */
184 	SReadlineInfo lsSrl;
185 	int result;
186 #endif	/* NO_SIGNALS */
187 
188 	if (cip == NULL)
189 		return (kErrBadParameter);
190 	if (strcmp(cip->magic, kLibraryMagic))
191 		return (kErrBadMagic);
192 
193 	cmd = (longMode != 0) ? "LIST" : "NLST";
194 	if ((lsflag == NULL) || (lsflag[0] == '\0')) {
195 		result = FTPStartDataCmd(cip, kNetReading, kTypeAscii, (longest_int) 0, "%s", cmd);
196 	} else {
197 		result = FTPStartDataCmd(cip, kNetReading, kTypeAscii, (longest_int) 0, "%s %s", cmd, lsflag);
198 	}
199 
200 
201 #ifdef NO_SIGNALS
202 
203 	if (result == 0) {
204 		if (InitSReadlineInfo(&lsSrl, cip->dataSocket, secondaryBuf, sizeof(secondaryBuf), (int) cip->xferTimeout, 1) < 0) {
205 			/* Not really fdopen, but close in what we're trying to do. */
206 			result = kErrFdopenR;
207 			cip->errNo = kErrFdopenR;
208 			Error(cip, kDoPerror, "Could not fdopen.\n");
209 			return (result);
210 		}
211 
212 		for (;;) {
213 			result = SReadline(&lsSrl, line, sizeof(line) - 2);
214 			if (result == kTimeoutErr) {
215 				/* timeout */
216 				Error(cip, kDontPerror, "Could not directory listing data -- timed out.\n");
217 				cip->errNo = kErrDataTimedOut;
218 				return (cip->errNo);
219 			} else if (result == 0) {
220 				/* end of listing -- done */
221 				cip->numListings++;
222 				break;
223 			} else if (result < 0) {
224 				/* error */
225 				Error(cip, kDoPerror, "Could not read directory listing data");
226 				result = kErrLISTFailed;
227 				cip->errNo = kErrLISTFailed;
228 				break;
229 			}
230 
231 			(void) write(outfd, line, strlen(line));
232 		}
233 
234 		DisposeSReadlineInfo(&lsSrl);
235 		if (FTPEndDataCmd(cip, 1) < 0) {
236 			result = kErrLISTFailed;
237 			cip->errNo = kErrLISTFailed;
238 		}
239 	} else if (result == kErrGeneric) {
240 		result = kErrLISTFailed;
241 		cip->errNo = kErrLISTFailed;
242 	}
243 
244 
245 #else	/* NO_SIGNALS */
246 
247 	if (result == 0) {
248 		/* This line sets the buffer pointer so that the first thing
249 		 * BufferGets will do is reset and fill the buffer using
250 		 * real I/O.
251 		 */
252 		secBufPtr = secondaryBuf + sizeof(secondaryBuf);
253 		secBufLimit = (char *) 0;
254 
255 		for (;;) {
256 			if (cip->xferTimeout > 0)
257 				(void) alarm(cip->xferTimeout);
258 			nread = BufferGets(line, sizeof(line), cip->dataSocket, secondaryBuf, &secBufPtr, &secBufLimit, sizeof(secondaryBuf));
259 			if (nread <= 0) {
260 				if (nread < 0)
261 					break;
262 			} else {
263 				cip->bytesTransferred += (longest_int) nread;
264 				(void) STRNCAT(line, "\n");
265 				(void) write(outfd, line, strlen(line));
266 			}
267 		}
268 		if (cip->xferTimeout > 0)
269 			(void) alarm(0);
270 		result = FTPEndDataCmd(cip, 1);
271 		if (result < 0) {
272 			result = kErrLISTFailed;
273 			cip->errNo = kErrLISTFailed;
274 		}
275 		result = kNoErr;
276 		cip->numListings++;
277 	} else if (result == kErrGeneric) {
278 		result = kErrLISTFailed;
279 		cip->errNo = kErrLISTFailed;
280 	}
281 #endif	/* NO_SIGNALS */
282 	return (result);
283 }	/* FTPList */
284 
285 
286 
287 
288 static void
289 FTPRequestMlsOptions(const FTPCIPtr cip)
290 {
291 	int f;
292 	char optstr[128];
293 	size_t optstrlen;
294 
295 	if (cip->usedMLS == 0) {
296 		/* First MLSD/MLST ? */
297 		cip->usedMLS = 1;
298 
299 		f = cip->mlsFeatures & kPreferredMlsOpts;
300 		optstr[0] = '\0';
301 
302 		/* TYPE */
303 		if ((f & kMlsOptType) != 0) {
304 			STRNCAT(optstr, "type;");
305 		}
306 
307 		/* SIZE */
308 		if ((f & kMlsOptSize) != 0) {
309 			STRNCAT(optstr, "size;");
310 		}
311 
312 		/* MODTIME */
313 		if ((f & kMlsOptModify) != 0) {
314 			STRNCAT(optstr, "modify;");
315 		}
316 
317 		/* MODE */
318 		if ((f & kMlsOptUNIXmode) != 0) {
319 			STRNCAT(optstr, "UNIX.mode;");
320 		}
321 
322 		/* PERM */
323 		if ((f & kMlsOptPerm) != 0) {
324 			STRNCAT(optstr, "perm;");
325 		}
326 
327 		/* OWNER */
328 		if ((f & kMlsOptUNIXowner) != 0) {
329 			STRNCAT(optstr, "UNIX.owner;");
330 		}
331 
332 		/* UID */
333 		if ((f & kMlsOptUNIXuid) != 0) {
334 			STRNCAT(optstr, "UNIX.uid;");
335 		}
336 
337 		/* GROUP */
338 		if ((f & kMlsOptUNIXgroup) != 0) {
339 			STRNCAT(optstr, "UNIX.group;");
340 		}
341 
342 		/* GID */
343 		if ((f & kMlsOptUNIXgid) != 0) {
344 			STRNCAT(optstr, "UNIX.gid;");
345 		}
346 
347 		/* UNIQUE */
348 		if ((f & kMlsOptUnique) != 0) {
349 			STRNCAT(optstr, "unique;");
350 		}
351 
352 		/* Tell the server what we prefer. */
353 		optstrlen = strlen(optstr);
354 		if (optstrlen > 0) {
355 			if (optstr[optstrlen - 1] == ';')
356 				optstr[optstrlen - 1] = '\0';
357 			(void) FTPCmd(cip, "OPTS MLST %s", optstr);
358 		}
359 	}
360 }	/* FTPRequestMlsOptions */
361 
362 
363 
364 
365 int
366 FTPListToMemory2(const FTPCIPtr cip, const char *const pattern, const LineListPtr llines, const char *const lsflags, const int blankLines, int *const tryMLSD)
367 {
368 	char secondaryBuf[768];
369 	char line[512];
370 	char lsflags1[128];
371 	const char *command = "NLST";
372 	const char *scp;
373 	char *dcp, *lim;
374 #ifndef NO_SIGNALS
375 	char *secBufPtr, *secBufLimit;
376 	volatile FTPSigProc osigpipe;
377 	volatile FTPCIPtr vcip;
378 	int sj;
379 	int nread;
380 	volatile int result;
381 #else	/* NO_SIGNALS */
382 	SReadlineInfo lsSrl;
383 	int result;
384 #endif	/* NO_SIGNALS */
385 
386 	if (cip == NULL)
387 		return (kErrBadParameter);
388 	if (strcmp(cip->magic, kLibraryMagic))
389 		return (kErrBadMagic);
390 
391 	if ((llines == NULL) || (pattern == NULL) || (lsflags == NULL))
392 		return (kErrBadParameter);
393 
394 	if ((tryMLSD != (int *) 0) && (*tryMLSD != 0) && (cip->hasMLSD == kCommandAvailable)) {
395 		command = "MLSD";
396 		if ((lsflags[0] == '-') && (strchr(lsflags, 'd') != NULL) && (cip->hasMLST == kCommandAvailable))
397 			command = "MLST";
398 		lsflags1[0] = '\0';
399 		FTPRequestMlsOptions(cip);
400 	} else {
401 		/* Not using MLSD. */
402 		if (tryMLSD != (int *) 0)
403 			*tryMLSD = 0;
404 		if (lsflags[0] == '-') {
405 			/* See if we should use LIST instead. */
406 			scp = lsflags + 1;
407 			dcp = lsflags1;
408 			lim = lsflags1 + sizeof(lsflags1) - 2;
409 			for (; *scp != '\0'; scp++) {
410 				if (*scp == 'l') {
411 					/* do not add the 'l' */
412 					command = "LIST";
413 				} else if (dcp < lim) {
414 					if (dcp == lsflags1)
415 						*dcp++ = '-';
416 					*dcp++ = *scp;
417 				}
418 			}
419 			*dcp = '\0';
420 		} else {
421 			(void) STRNCPY(lsflags1, lsflags);
422 		}
423 	}
424 
425 	InitLineList(llines);
426 
427 	result = FTPStartDataCmd(
428 		cip,
429 		kNetReading,
430 		kTypeAscii,
431 		(longest_int) 0,
432 		"%s%s%s%s%s",
433 		command,
434 		(lsflags1[0] == '\0') ? "" : " ",
435 		lsflags1,
436 		(pattern[0] == '\0') ? "" : " ",
437 		pattern
438 	);
439 
440 #ifdef NO_SIGNALS
441 
442 	if (result == 0) {
443 		if (InitSReadlineInfo(&lsSrl, cip->dataSocket, secondaryBuf, sizeof(secondaryBuf), (int) cip->xferTimeout, 1) < 0) {
444 			/* Not really fdopen, but close in what we're trying to do. */
445 			result = kErrFdopenR;
446 			cip->errNo = kErrFdopenR;
447 			Error(cip, kDoPerror, "Could not fdopen.\n");
448 			return (result);
449 		}
450 
451 		for (;;) {
452 			result = SReadline(&lsSrl, line, sizeof(line) - 1);
453 			if (result == kTimeoutErr) {
454 				/* timeout */
455 				Error(cip, kDontPerror, "Could not directory listing data -- timed out.\n");
456 				cip->errNo = kErrDataTimedOut;
457 				return (cip->errNo);
458 			} else if (result == 0) {
459 				/* end of listing -- done */
460 				cip->numListings++;
461 				break;
462 			} else if (result < 0) {
463 				/* error */
464 				Error(cip, kDoPerror, "Could not read directory listing data");
465 				result = kErrLISTFailed;
466 				cip->errNo = kErrLISTFailed;
467 				break;
468 			}
469 
470 			if (line[result - 1] == '\n')
471 				line[result - 1] = '\0';
472 
473 			if ((blankLines == 0) && (result <= 1))
474 				continue;
475 
476 			/* Valid directory listing line of output */
477 			if ((line[0] == '.') && ((line[1] == '\0') || ((line[1] == '.') && ((line[2] == '\0') || (iscntrl(line[2]))))))
478 				continue;	/* Skip . and .. */
479 
480 			(void) AddLine(llines, line);
481 		}
482 
483 		DisposeSReadlineInfo(&lsSrl);
484 		if (FTPEndDataCmd(cip, 1) < 0) {
485 			result = kErrLISTFailed;
486 			cip->errNo = kErrLISTFailed;
487 		}
488 	} else if (result == kErrGeneric) {
489 		result = kErrLISTFailed;
490 		cip->errNo = kErrLISTFailed;
491 	}
492 
493 
494 #else	/* NO_SIGNALS */
495 	vcip = cip;
496 	osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
497 
498 	gGotBrokenData = 0;
499 	gCanBrokenDataJmp = 0;
500 
501 #ifdef HAVE_SIGSETJMP
502 	sj = sigsetjmp(gBrokenDataJmp, 1);
503 #else
504 	sj = setjmp(gBrokenDataJmp);
505 #endif	/* HAVE_SIGSETJMP */
506 
507 	if (sj != 0) {
508 		(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
509 		FTPShutdownHost(vcip);
510 		vcip->errNo = kErrRemoteHostClosedConnection;
511 		return(vcip->errNo);
512 	}
513 	gCanBrokenDataJmp = 1;
514 
515 	if (result == 0) {
516 		/* This line sets the buffer pointer so that the first thing
517 		 * BufferGets will do is reset and fill the buffer using
518 		 * real I/O.
519 		 */
520 		secBufPtr = secondaryBuf + sizeof(secondaryBuf);
521 		secBufLimit = (char *) 0;
522 		memset(secondaryBuf, 0, sizeof(secondaryBuf));
523 
524 		for (;;) {
525 			memset(line, 0, sizeof(line));
526 			if (cip->xferTimeout > 0)
527 				(void) alarm(cip->xferTimeout);
528 			nread = BufferGets(line, sizeof(line), cip->dataSocket, secondaryBuf, &secBufPtr, &secBufLimit, sizeof(secondaryBuf));
529 			if (nread <= 0) {
530 				if (nread < 0)
531 					break;
532 				if (blankLines != 0)
533 					(void) AddLine(llines, line);
534 			} else {
535 				cip->bytesTransferred += (longest_int) nread;
536 
537 				if ((line[0] == '.') && ((line[1] == '\0') || ((line[1] == '.') && ((line[2] == '\0') || (iscntrl(line[2]))))))
538 					continue;	/* Skip . and .. */
539 
540 				(void) AddLine(llines, line);
541 			}
542 		}
543 		if (cip->xferTimeout > 0)
544 			(void) alarm(0);
545 		result = FTPEndDataCmd(cip, 1);
546 		if (result < 0) {
547 			result = kErrLISTFailed;
548 			cip->errNo = kErrLISTFailed;
549 		}
550 		result = kNoErr;
551 		cip->numListings++;
552 	} else if (result == kErrGeneric) {
553 		result = kErrLISTFailed;
554 		cip->errNo = kErrLISTFailed;
555 	}
556 	(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
557 #endif	/* NO_SIGNALS */
558 	return (result);
559 }	/* FTPListToMemory2 */
560 
561 
562 
563 
564 static void
565 AutomaticallyUseASCIIModeDependingOnExtension(const FTPCIPtr cip, const char *const pathName, int *const xtype)
566 {
567 	if ((*xtype == kTypeBinary) && (cip->asciiFilenameExtensions != NULL)) {
568 		if (FilenameExtensionIndicatesASCII(pathName, cip->asciiFilenameExtensions)) {
569 			/* Matched -- send this file in ASCII mode
570 			 * instead of binary since it's extension
571 			 * appears to be that of a text file.
572 			 */
573 			*xtype = kTypeAscii;
574 		}
575 	}
576 }	/* AutomaticallyUseASCIIModeDependingOnExtension */
577 
578 
579 
580 
581 /* The purpose of this is to provide updates for the progress meters
582  * during lags.  Return zero if the operation timed-out.
583  */
584 static int
585 WaitForRemoteOutput(const FTPCIPtr cip)
586 {
587 	fd_set ss, ss2;
588 	struct timeval tv;
589 	int result;
590 	int fd;
591 	int wsecs;
592 	int xferTimeout;
593 	int ocancelXfer;
594 
595 	xferTimeout = cip->xferTimeout;
596 	if (xferTimeout < 1)
597 		return (1);
598 
599 	fd = cip->dataSocket;
600 	if (fd < 0)
601 		return (1);
602 
603 	ocancelXfer = cip->cancelXfer;
604 	wsecs = 0;
605 	cip->stalled = 0;
606 
607 	while ((xferTimeout <= 0) || (wsecs < xferTimeout)) {
608 		if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) {
609 			/* leave cip->stalled -- could have been stalled and then canceled. */
610 			return (1);
611 		}
612 		FD_ZERO(&ss);
613 		FD_SET(fd, &ss);
614 		ss2 = ss;
615 		tv.tv_sec = 1;
616 		tv.tv_usec = 0;
617 		result = select(fd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &ss2, &tv);
618 		if (result == 1) {
619 			/* ready */
620 			cip->stalled = 0;
621 			return (1);
622 		} else if (result < 0) {
623 			if (errno != EINTR) {
624 				perror("select");
625 				cip->stalled = 0;
626 				return (1);
627 			}
628 		} else {
629 			wsecs++;
630 			cip->stalled = wsecs;
631 		}
632 		FTPUpdateIOTimer(cip);
633 	}
634 
635 #if !defined(NO_SIGNALS)
636 	/* Shouldn't get here -- alarm() should have
637 	 * went off by now.
638 	 */
639 	(void) kill(getpid(), SIGALRM);
640 #endif	/* NO_SIGNALS */
641 
642 	cip->dataTimedOut = 1;
643 	return (0);	/* timed-out */
644 }	/* WaitForRemoteOutput */
645 
646 
647 
648 
649 static int
650 FTPPutOneF(
651 	const FTPCIPtr cip,
652 	const char *const file,
653 	const char *volatile dstfile,
654 	int xtype,
655 	const int fdtouse,
656 	const int appendflag,
657 	const char *volatile tmppfx,
658 	const char *volatile tmpsfx,
659 	const int resumeflag,
660 	const int deleteflag,
661 	const ConfirmResumeUploadProc resumeProc)
662 {
663 	char *buf, *cp;
664 	const char *cmd;
665 	const char *odstfile;
666 	size_t bufSize;
667 	size_t l;
668 	int tmpResult, result;
669 	int nread, nwrote;
670 	volatile int fd;
671 	char dstfile2[512];
672 #if ASCII_TRANSLATION
673 	char *src, *srclim, *dst;
674 	int ntowrite;
675 	char inbuf[256];
676 #endif
677 	int fstatrc, statrc;
678 	longest_int startPoint = 0;
679 	struct Stat st;
680 	time_t mdtm;
681 #if !defined(NO_SIGNALS)
682 	int sj;
683 	volatile FTPSigProc osigpipe;
684 	volatile FTPCIPtr vcip;
685 	volatile int vfd, vfdtouse;
686 #endif	/* NO_SIGNALS */
687 	volatile int vzaction;
688 	int zaction = kConfirmResumeProcSaidBestGuess;
689 
690 	if (cip->buf == NULL) {
691 		Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
692 		cip->errNo = kErrNoBuf;
693 		return (cip->errNo);
694 	}
695 
696 	cip->usingTAR = 0;
697 	if (fdtouse < 0) {
698 		fd = Open(file, O_RDONLY|O_BINARY, 0);
699 		if (fd < 0) {
700 			Error(cip, kDoPerror, "Cannot open local file %s for reading.\n", file);
701 			cip->errNo = kErrOpenFailed;
702 			return (cip->errNo);
703 		}
704 	} else {
705 		fd = fdtouse;
706 	}
707 
708 	fstatrc = Fstat(fd, &st);
709 	if ((fstatrc == 0) && (S_ISDIR(st.st_mode))) {
710 		if (fdtouse < 0) {
711 			(void) close(fd);
712 		}
713 		Error(cip, kDontPerror, "%s is a directory.\n", (file != NULL) ? file : "that");
714 		cip->errNo = kErrOpenFailed;
715 		return (cip->errNo);
716 	}
717 
718 	/* For Put, we can't recover very well if it turns out restart
719 	 * didn't work, so check beforehand.
720 	 */
721 	if (cip->hasREST == kCommandAvailabilityUnknown) {
722 		(void) FTPSetTransferType(cip, kTypeBinary);
723 		if (SetStartOffset(cip, (longest_int) 1) == kNoErr) {
724 			/* Now revert -- we still may not end up
725 			 * doing it.
726 			 */
727 			SetStartOffset(cip, (longest_int) -1);
728 		}
729 	}
730 
731 	if (fdtouse < 0) {
732 		AutomaticallyUseASCIIModeDependingOnExtension(cip, dstfile, &xtype);
733 		(void) FTPFileSizeAndModificationTime(cip, dstfile, &startPoint, xtype, &mdtm);
734 
735 		if (appendflag == kAppendYes) {
736 			zaction = kConfirmResumeProcSaidAppend;
737 		} else if (
738 				(cip->hasREST == kCommandNotAvailable) ||
739 				(xtype != kTypeBinary) ||
740 				(fstatrc < 0)
741 		) {
742 			zaction = kConfirmResumeProcSaidOverwrite;
743 		} else if (resumeflag == kResumeYes) {
744 			zaction = kConfirmResumeProcSaidBestGuess;
745 		} else {
746 			zaction = kConfirmResumeProcSaidOverwrite;
747 		}
748 
749 		statrc = -1;
750 		if ((mdtm != kModTimeUnknown) || (startPoint != kSizeUnknown)) {
751 			/* Then we know the file exists.  We will
752 			 * ask the user what to do, if possible, below.
753 			 */
754 			statrc = 0;
755 		} else if ((resumeProc != NoConfirmResumeUploadProc) && (cip->hasMDTM != kCommandAvailable) && (cip->hasSIZE != kCommandAvailable)) {
756 			/* We already checked if the file had a filesize
757 			 * or timestamp above, but if the server indicated
758 			 * it did not support querying those directly,
759 			 * we now need to try to determine if the file
760 			 * exists in a few other ways.
761 			 */
762 			statrc = FTPFileExists2(cip, dstfile, 0, 0, 0, 1, 1);
763 		}
764 
765 		if (
766 			(resumeProc != NoConfirmResumeUploadProc) &&
767 			(statrc == 0)
768 		) {
769 			zaction = (*resumeProc)(file, (longest_int) st.st_size, st.st_mtime, &dstfile, startPoint, mdtm, &startPoint);
770 		}
771 
772 		if (zaction == kConfirmResumeProcSaidCancel) {
773 			/* User wants to cancel this file and any
774 			 * remaining in batch.
775 			 */
776 			cip->errNo = kErrUserCanceled;
777 			return (cip->errNo);
778 		}
779 
780 		if (zaction == kConfirmResumeProcSaidBestGuess) {
781 			if ((mdtm != kModTimeUnknown) && (st.st_mtime > (mdtm + 1))) {
782 				/* Local file is newer than remote,
783 				 * overwrite the remote file instead
784 				 * of trying to resume it.
785 				 *
786 				 * Note:  Add one second fudge factor
787 				 * for Windows' file timestamps being
788 				 * imprecise to one second.
789 				 */
790 				zaction = kConfirmResumeProcSaidOverwrite;
791 			} else if ((longest_int) st.st_size == startPoint) {
792 				/* Already sent file, done. */
793 				zaction = kConfirmResumeProcSaidSkip;
794 			} else if ((startPoint != kSizeUnknown) && ((longest_int) st.st_size > startPoint)) {
795 				zaction = kConfirmResumeProcSaidResume;
796 			} else {
797 				zaction = kConfirmResumeProcSaidOverwrite;
798 			}
799 		}
800 
801 		if (zaction == kConfirmResumeProcSaidSkip) {
802 			/* Nothing done, but not an error. */
803 			if (fdtouse < 0) {
804 				(void) close(fd);
805 			}
806 			if (deleteflag == kDeleteYes) {
807 				if (unlink(file) < 0) {
808 					cip->errNo = kErrLocalDeleteFailed;
809 					return (cip->errNo);
810 				}
811 			}
812 			return (kNoErr);
813 		} else if (zaction == kConfirmResumeProcSaidResume) {
814 			/* Resume; proc set the startPoint. */
815 			if ((longest_int) st.st_size == startPoint) {
816 				/* Already sent file, done. */
817 				if (fdtouse < 0) {
818 					(void) close(fd);
819 				}
820 
821 				if (deleteflag == kDeleteYes) {
822 					if (unlink(file) < 0) {
823 						cip->errNo = kErrLocalDeleteFailed;
824 						return (cip->errNo);
825 					}
826 				}
827 				return (kNoErr);
828 			} else if (Lseek(fd, (off_t) startPoint, SEEK_SET) != (off_t) -1) {
829 				cip->startPoint = startPoint;
830 			}
831 		} else if (zaction == kConfirmResumeProcSaidAppend) {
832 			/* append: leave startPoint at zero, we will append everything. */
833 			cip->startPoint = startPoint = 0;
834 		} else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
835 			/* overwrite: leave startPoint at zero */
836 			cip->startPoint = startPoint = 0;
837 		}
838 	}
839 
840 	if ((cip->numUploads == 0) && (cip->dataSocketSBufSize > 0)) {
841 		/* If dataSocketSBufSize is non-zero, it means you
842 		 * want to explicitly try to set the size of the
843 		 * socket's I/O buffer.
844 		 *
845 		 * If it is zero, it means you want to just use the
846 		 * TCP stack's default value, which is typically
847 		 * between 8 and 64 kB.
848 		 *
849 		 * If you try to set the buffer larger than 64 kB,
850 		 * the TCP stack should try to use RFC 1323 to
851 		 * negotiate "TCP Large Windows" which may yield
852 		 * significant performance gains.
853 		 */
854 		if (cip->hasSTORBUFSIZE == kCommandAvailable)
855 			(void) FTPCmd(cip, "SITE STORBUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
856 		else if (cip->hasSBUFSIZ == kCommandAvailable)
857 			(void) FTPCmd(cip, "SITE SBUFSIZ %lu", (unsigned long) cip->dataSocketSBufSize);
858 		else if (cip->hasSBUFSZ == kCommandAvailable)
859 			(void) FTPCmd(cip, "SITE SBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize);
860 		/* At least one server implemenation has RBUFSZ but not
861 		 * SBUFSZ and instead uses RBUFSZ for both.
862 		 */
863 		else if ((cip->hasSBUFSZ != kCommandAvailable) && (cip->hasRBUFSZ == kCommandAvailable))
864 			(void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketSBufSize);
865 		else if (cip->hasBUFSIZE == kCommandAvailable)
866 			(void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
867 	}
868 
869 #ifdef NO_SIGNALS
870 	vzaction = zaction;
871 #else	/* NO_SIGNALS */
872 	vcip = cip;
873 	vfdtouse = fdtouse;
874 	vfd = fd;
875 	vzaction = zaction;
876 	osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
877 
878 	gGotBrokenData = 0;
879 	gCanBrokenDataJmp = 0;
880 
881 #ifdef HAVE_SIGSETJMP
882 	sj = sigsetjmp(gBrokenDataJmp, 1);
883 #else
884 	sj = setjmp(gBrokenDataJmp);
885 #endif	/* HAVE_SIGSETJMP */
886 
887 	if (sj != 0) {
888 		(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
889 		if (vfdtouse < 0) {
890 			(void) close(vfd);
891 		}
892 		FTPShutdownHost(vcip);
893 		vcip->errNo = kErrRemoteHostClosedConnection;
894 		return(vcip->errNo);
895 	}
896 	gCanBrokenDataJmp = 1;
897 #endif	/* NO_SIGNALS */
898 
899 	if (vzaction == kConfirmResumeProcSaidAppend) {
900 		cmd = "APPE";
901 		tmppfx = "";	/* Can't use that here. */
902 		tmpsfx = "";
903 	} else {
904 		cmd = "STOR";
905 		if (tmppfx == NULL)
906 			tmppfx = "";
907 		if (tmpsfx == NULL)
908 			tmpsfx = "";
909 	}
910 
911 	odstfile = dstfile;
912 	if ((tmppfx[0] != '\0') || (tmpsfx[0] != '\0')) {
913 		cp = strrchr(dstfile, '/');
914 		if (cp == NULL)
915 			cp = strrchr(dstfile, '\\');
916 		if (cp == NULL) {
917 			(void) STRNCPY(dstfile2, tmppfx);
918 			(void) STRNCAT(dstfile2, dstfile);
919 			(void) STRNCAT(dstfile2, tmpsfx);
920 		} else {
921 			cp++;
922 			l = (size_t) (cp - dstfile);
923 			(void) STRNCPY(dstfile2, dstfile);
924 			dstfile2[l] = '\0';	/* Nuke stuff after / */
925 			(void) STRNCAT(dstfile2, tmppfx);
926 			(void) STRNCAT(dstfile2, cp);
927 			(void) STRNCAT(dstfile2, tmpsfx);
928 		}
929 		dstfile = dstfile2;
930 	}
931 
932 	tmpResult = FTPStartDataCmd(
933 		cip,
934 		kNetWriting,
935 		xtype,
936 		startPoint,
937 		"%s %s",
938 		cmd,
939 		dstfile
940 	);
941 
942 	if (tmpResult < 0) {
943 		cip->errNo = tmpResult;
944 		if (fdtouse < 0) {
945 			(void) close(fd);
946 		}
947 #if !defined(NO_SIGNALS)
948 		(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
949 #endif	/* NO_SIGNALS */
950 		return (cip->errNo);
951 	}
952 
953 	if ((startPoint != 0) && (cip->startPoint == 0)) {
954 		/* Remote could not or would not set the start offset
955 		 * to what we wanted.
956 		 *
957 		 * So now we have to undo our seek.
958 		 */
959 		if (Lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
960 			cip->errNo = kErrLseekFailed;
961 			if (fdtouse < 0) {
962 				(void) close(fd);
963 			}
964 #if !defined(NO_SIGNALS)
965 			(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
966 #endif	/* NO_SIGNALS */
967 			return (cip->errNo);
968 		}
969 		startPoint = 0;
970 	}
971 
972 	result = kNoErr;
973 	buf = cip->buf;
974 	bufSize = cip->bufSize;
975 
976 	FTPInitIOTimer(cip);
977 	if ((fstatrc == 0) && (S_ISREG(st.st_mode) != 0)) {
978 		cip->expectedSize = (longest_int) st.st_size;
979 		cip->mdtm = st.st_mtime;
980 	}
981 	cip->lname = file;	/* could be NULL */
982 	cip->rname = odstfile;
983 	if (fdtouse >= 0)
984 		cip->useProgressMeter = 0;
985 	FTPStartIOTimer(cip);
986 
987 	/* Note: On Windows, we don't have to do anything special
988 	 * for ASCII mode, since Net ASCII's end-of-line sequence
989 	 * corresponds to the same thing used for DOS/Windows.
990 	 */
991 
992 #if ASCII_TRANSLATION
993 	if (xtype == kTypeAscii) {
994 		/* ascii */
995 		for (;;) {
996 #if !defined(NO_SIGNALS)
997 			gCanBrokenDataJmp = 0;
998 #endif	/* NO_SIGNALS */
999 			nread = read(fd, inbuf, sizeof(inbuf));
1000 			if (nread < 0) {
1001 				if (errno == EINTR) {
1002 					continue;
1003 				} else {
1004 					result = kErrReadFailed;
1005 					cip->errNo = kErrReadFailed;
1006 					Error(cip, kDoPerror, "Local read failed.\n");
1007 				}
1008 				break;
1009 			} else if (nread == 0) {
1010 				break;
1011 			}
1012 			cip->bytesTransferred += (longest_int) nread;
1013 
1014 #if !defined(NO_SIGNALS)
1015 			gCanBrokenDataJmp = 1;
1016 #endif	/* NO_SIGNALS */
1017 			src = inbuf;
1018 			srclim = src + nread;
1019 			dst = cip->buf;		/* must be 2x sizeof inbuf or more. */
1020 			while (src < srclim) {
1021 				if (*src == '\n')
1022 					*dst++ = '\r';
1023 				*dst++ = *src++;
1024 			}
1025 			ntowrite = (size_t) (dst - cip->buf);
1026 			cp = cip->buf;
1027 
1028 #if !defined(NO_SIGNALS)
1029 			if (cip->xferTimeout > 0)
1030 				(void) alarm(cip->xferTimeout);
1031 #endif	/* NO_SIGNALS */
1032 			do {
1033 				if (! WaitForRemoteOutput(cip)) {	/* could set cancelXfer */
1034 					cip->errNo = result = kErrDataTimedOut;
1035 					Error(cip, kDontPerror, "Remote write timed out.\n");
1036 					goto brk;
1037 				}
1038 				if (cip->cancelXfer > 0) {
1039 					FTPAbortDataTransfer(cip);
1040 					result = cip->errNo = kErrDataTransferAborted;
1041 					goto brk;
1042 				}
1043 
1044 #ifdef NO_SIGNALS
1045 				nwrote = SWrite(cip->dataSocket, cp, (size_t) ntowrite, (int) cip->xferTimeout, kNoFirstSelect);
1046 				if (nwrote < 0) {
1047 					if (nwrote == kTimeoutErr) {
1048 						cip->errNo = result = kErrDataTimedOut;
1049 						Error(cip, kDontPerror, "Remote write timed out.\n");
1050 					} else if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1051 						cip->errNo = result = kErrSocketWriteFailed;
1052 						errno = EPIPE;
1053 						Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1054 					} else if (errno == EINTR) {
1055 						continue;
1056 					} else {
1057 						cip->errNo = result = kErrSocketWriteFailed;
1058 						Error(cip, kDoPerror, "Remote write failed.\n");
1059 					}
1060 					(void) shutdown(cip->dataSocket, 2);
1061 					goto brk;
1062 				}
1063 #else	/* NO_SIGNALS */
1064 				nwrote = write(cip->dataSocket, cp, ntowrite);
1065 				if (nwrote < 0) {
1066 					if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1067 						cip->errNo = result = kErrSocketWriteFailed;
1068 						errno = EPIPE;
1069 						Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1070 					} else if (errno == EINTR) {
1071 						continue;
1072 					} else {
1073 						cip->errNo = result = kErrSocketWriteFailed;
1074 						Error(cip, kDoPerror, "Remote write failed.\n");
1075 					}
1076 					(void) shutdown(cip->dataSocket, 2);
1077 					goto brk;
1078 				}
1079 #endif	/* NO_SIGNALS */
1080 				cp += nwrote;
1081 				ntowrite -= nwrote;
1082 			} while (ntowrite > 0);
1083 			FTPUpdateIOTimer(cip);
1084 		}
1085 	} else
1086 #endif	/* ASCII_TRANSLATION */
1087 	{
1088 		/* binary */
1089 		for (;;) {
1090 #if !defined(NO_SIGNALS)
1091 			gCanBrokenDataJmp = 0;
1092 #endif	/* NO_SIGNALS */
1093 			cp = buf;
1094 			nread = read(fd, cp, bufSize);
1095 			if (nread < 0) {
1096 				if (errno == EINTR) {
1097 					continue;
1098 				} else {
1099 					result = kErrReadFailed;
1100 					cip->errNo = kErrReadFailed;
1101 					Error(cip, kDoPerror, "Local read failed.\n");
1102 				}
1103 				break;
1104 			} else if (nread == 0) {
1105 				break;
1106 			}
1107 			cip->bytesTransferred += (longest_int) nread;
1108 
1109 #if !defined(NO_SIGNALS)
1110 			gCanBrokenDataJmp = 1;
1111 			if (cip->xferTimeout > 0)
1112 				(void) alarm(cip->xferTimeout);
1113 #endif	/* NO_SIGNALS */
1114 			do {
1115 				if (! WaitForRemoteOutput(cip)) {	/* could set cancelXfer */
1116 					cip->errNo = result = kErrDataTimedOut;
1117 					Error(cip, kDontPerror, "Remote write timed out.\n");
1118 					goto brk;
1119 				}
1120 				if (cip->cancelXfer > 0) {
1121 					FTPAbortDataTransfer(cip);
1122 					result = cip->errNo = kErrDataTransferAborted;
1123 					goto brk;
1124 				}
1125 
1126 #ifdef NO_SIGNALS
1127 				nwrote = SWrite(cip->dataSocket, cp, (size_t) nread, (int) cip->xferTimeout, kNoFirstSelect);
1128 				if (nwrote < 0) {
1129 					if (nwrote == kTimeoutErr) {
1130 						cip->errNo = result = kErrDataTimedOut;
1131 						Error(cip, kDontPerror, "Remote write timed out.\n");
1132 					} else if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1133 						cip->errNo = result = kErrSocketWriteFailed;
1134 						errno = EPIPE;
1135 						Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1136 					} else if (errno == EINTR) {
1137 						continue;
1138 					} else {
1139 						cip->errNo = result = kErrSocketWriteFailed;
1140 						Error(cip, kDoPerror, "Remote write failed.\n");
1141 					}
1142 					(void) shutdown(cip->dataSocket, 2);
1143 					cip->dataSocket = -1;
1144 					goto brk;
1145 				}
1146 #else	/* NO_SIGNALS */
1147 				nwrote = write(cip->dataSocket, cp, nread);
1148 				if (nwrote < 0) {
1149 					if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1150 						cip->errNo = result = kErrSocketWriteFailed;
1151 						errno = EPIPE;
1152 						Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1153 					} else if (errno == EINTR) {
1154 						continue;
1155 					} else {
1156 						cip->errNo = result = kErrSocketWriteFailed;
1157 						Error(cip, kDoPerror, "Remote write failed.\n");
1158 					}
1159 					(void) shutdown(cip->dataSocket, 2);
1160 					cip->dataSocket = -1;
1161 					goto brk;
1162 				}
1163 #endif	/* NO_SIGNALS */
1164 				cp += nwrote;
1165 				nread -= nwrote;
1166 			} while (nread > 0);
1167 			FTPUpdateIOTimer(cip);
1168 		}
1169 	}
1170 brk:
1171 
1172 	if (fdtouse < 0) {
1173 		(void) Fstat(fd, &st);
1174 	}
1175 
1176 	if (fdtouse < 0) {
1177 		if (shutdown(fd, 1) == 0) {
1178 			/* This looks very bizarre, since
1179 			 * we will be checking the socket
1180 			 * for readability here!
1181 			 *
1182 			 * The reason for this is that we
1183 			 * want to be able to timeout a
1184 			 * small put.  So, we close the
1185 			 * write end of the socket first,
1186 			 * which tells the server we're
1187 			 * done writing.  We then wait
1188 			 * for the server to close down
1189 			 * the whole socket, which tells
1190 			 * us that the file was completed.
1191 			 */
1192 			(void) WaitForRemoteInput(cip);	/* Close could block. */
1193 		}
1194 	}
1195 
1196 #if !defined(NO_SIGNALS)
1197 	gCanBrokenDataJmp = 0;
1198 	if (cip->xferTimeout > 0)
1199 		(void) alarm(0);
1200 #endif	/* NO_SIGNALS */
1201 	tmpResult = FTPEndDataCmd(cip, 1);
1202 	if ((tmpResult < 0) && (result == kNoErr)) {
1203 		cip->errNo = result = kErrSTORFailed;
1204 	}
1205 	FTPStopIOTimer(cip);
1206 
1207 	if (fdtouse < 0) {
1208 		/* If they gave us a descriptor (fdtouse >= 0),
1209 		 * leave it open, otherwise we opened it, so
1210 		 * we need to dispose of it.
1211 		 */
1212 		(void) close(fd);
1213 		fd = -1;
1214 	}
1215 
1216 	if (result == kNoErr) {
1217 		/* The store succeeded;  If we were
1218 		 * uploading to a temporary file,
1219 		 * move the new file to the new name.
1220 		 */
1221 		cip->numUploads++;
1222 
1223 		if ((tmppfx[0] != '\0') || (tmpsfx[0] != '\0')) {
1224 			if ((result = FTPRename(cip, dstfile, odstfile)) < 0) {
1225 				/* May fail if file was already there,
1226 				 * so delete the old one so we can move
1227 				 * over it.
1228 				 */
1229 				if (FTPDelete(cip, odstfile, kRecursiveNo, kGlobNo) == kNoErr) {
1230 					result = FTPRename(cip, dstfile, odstfile);
1231 					if (result < 0) {
1232 						Error(cip, kDontPerror, "Could not rename %s to %s: %s.\n", dstfile, odstfile, FTPStrError(cip->errNo));
1233 					}
1234 				} else {
1235 					Error(cip, kDontPerror, "Could not delete old %s, so could not rename %s to that: %s\n", odstfile, dstfile, FTPStrError(cip->errNo));
1236 				}
1237 			}
1238 		}
1239 
1240 		if (FTPUtime(cip, odstfile, st.st_atime, st.st_mtime, st.st_ctime) != kNoErr) {
1241 			if (cip->errNo != kErrUTIMENotAvailable)
1242 				Error(cip, kDontPerror, "Could not preserve times for %s: %s.\n", odstfile, FTPStrError(cip->errNo));
1243 		}
1244 
1245 		if (deleteflag == kDeleteYes) {
1246 			if (unlink(file) < 0) {
1247 				result = cip->errNo = kErrLocalDeleteFailed;
1248 			}
1249 		}
1250 	}
1251 
1252 #if !defined(NO_SIGNALS)
1253 	(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1254 #endif	/* NO_SIGNALS */
1255 	return (result);
1256 }	/* FTPPutOneF */
1257 
1258 
1259 
1260 
1261 int
1262 FTPPutOneFile3(
1263 	const FTPCIPtr cip,
1264 	const char *const file,
1265 	const char *const dstfile,
1266 	const int xtype,
1267 	const int fdtouse,
1268 	const int appendflag,
1269 	const char *const tmppfx,
1270 	const char *const tmpsfx,
1271 	const int resumeflag,
1272 	const int deleteflag,
1273 	const ConfirmResumeUploadProc resumeProc,
1274 	int UNUSED(reserved))
1275 {
1276 	int result;
1277 
1278 	LIBNCFTP_USE_VAR(reserved);
1279 	if (cip == NULL)
1280 		return (kErrBadParameter);
1281 	if (strcmp(cip->magic, kLibraryMagic))
1282 		return (kErrBadMagic);
1283 
1284 	if ((dstfile == NULL) || (dstfile[0] == '\0'))
1285 		return (kErrBadParameter);
1286 	if (fdtouse < 0) {
1287 		if ((file == NULL) || (file[0] == '\0'))
1288 			return (kErrBadParameter);
1289 	}
1290 	result = FTPPutOneF(cip, file, dstfile, xtype, fdtouse, appendflag, tmppfx, tmpsfx, resumeflag, deleteflag, resumeProc);
1291 	return (result);
1292 }	/* FTPPutOneFile3 */
1293 
1294 
1295 
1296 
1297 int
1298 FTPPutFiles3(
1299 	const FTPCIPtr cip,
1300 	const char *const pattern,
1301 	const char *const dstdir1,
1302 	const int recurse,
1303 	const int doGlob,
1304 	const int xtype,
1305 	int appendflag,
1306 	const char *const tmppfx,
1307 	const char *const tmpsfx,
1308 	const int resumeflag,
1309 	const int deleteflag,
1310 	const ConfirmResumeUploadProc resumeProc,
1311 	int UNUSED(reserved))
1312 {
1313 	LineList globList;
1314 	FileInfoList files;
1315 	FileInfoPtr filePtr;
1316 	int batchResult;
1317 	int result;
1318 	const char *dstdir;
1319 	char dstdir2[512];
1320 
1321 	LIBNCFTP_USE_VAR(reserved);
1322 	if (cip == NULL)
1323 		return (kErrBadParameter);
1324 	if (strcmp(cip->magic, kLibraryMagic))
1325 		return (kErrBadMagic);
1326 
1327 	if (dstdir1 == NULL) {
1328 		dstdir = NULL;
1329 	} else {
1330 		dstdir = STRNCPY(dstdir2, dstdir1);
1331 		StrRemoveTrailingLocalPathDelim(dstdir2);
1332 	}
1333 
1334 	(void) FTPLocalGlob(cip, &globList, pattern, doGlob);
1335 	if (recurse == kRecursiveYes) {
1336 		appendflag = kAppendNo;
1337 		(void) FTPLocalRecursiveFileList(cip, &globList, &files);
1338 		if (files.first == NULL) {
1339 			cip->errNo = kErrNoValidFilesSpecified;
1340 			return (kErrNoValidFilesSpecified);
1341 		}
1342 		(void) ComputeRNames(&files, dstdir, 0, 1);
1343 	} else {
1344 		(void) LineListToFileInfoList(&globList, &files);
1345 		(void) ComputeLNames(&files, NULL, NULL, 1);
1346 		(void) ComputeRNames(&files, dstdir, 0, 0);
1347 	}
1348 	DisposeLineListContents(&globList);
1349 
1350 #if 0
1351 	for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
1352 		PrintF(cip, "  R=%s, L=%s, 2=%s, size=%d, mdtm=%u, type=%c\n",
1353 			filePtr->rname,
1354 			filePtr->lname,
1355 			filePtr->rlinkto ? filePtr->rlinkto : "",
1356 			filePtr->size,
1357 			(unsigned int) filePtr->mdtm,
1358 			filePtr->type
1359 		);
1360 	}
1361 #endif
1362 
1363 	batchResult = kNoErr;
1364 	for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
1365 		if (cip->connected == 0) {
1366 			if (batchResult == kNoErr)
1367 				batchResult = kErrRemoteHostClosedConnection;
1368 			break;
1369 		}
1370 		if (filePtr->type == 'd') {
1371 			/* mkdir */
1372 			StrRemoveTrailingLocalPathDelim(filePtr->rname);
1373 			result = FTPMkdir(cip, filePtr->rname, kRecursiveNo);
1374 			if (result != kNoErr)
1375 				batchResult = result;
1376 #ifdef HAVE_SYMLINK
1377 		} else if (filePtr->type == 'l') {
1378 			/* symlink */
1379 			/* no RFC way to create the link, though. */
1380 			if ((filePtr->rlinkto != NULL) && (filePtr->rlinkto[0] != '\0'))
1381 				(void) FTPSymlink(cip, filePtr->rname, filePtr->rlinkto);
1382 #endif
1383 		} else if (recurse != kRecursiveYes) {
1384 			result = FTPPutOneF(cip, filePtr->lname, filePtr->rname, xtype, -1, appendflag, tmppfx, tmpsfx, resumeflag, deleteflag, resumeProc);
1385 			if (files.nFileInfos == 1) {
1386 				if (result != kNoErr)
1387 					batchResult = result;
1388 			} else {
1389 				if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
1390 					batchResult = result;
1391 			}
1392 			if (result == kErrUserCanceled)
1393 				cip->cancelXfer = 1;
1394 			if (cip->cancelXfer > 0)
1395 				break;
1396 		} else {
1397 			result = FTPPutOneF(cip, filePtr->lname, filePtr->rname, xtype, -1, appendflag, tmppfx, tmpsfx, resumeflag, deleteflag, resumeProc);
1398 			if (files.nFileInfos == 1) {
1399 				if (result != kNoErr)
1400 					batchResult = result;
1401 			} else {
1402 				if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
1403 					batchResult = result;
1404 			}
1405 			if (result == kErrUserCanceled)
1406 				cip->cancelXfer = 1;
1407 			if (cip->cancelXfer > 0)
1408 				break;
1409 		}
1410 	}
1411 	DisposeFileInfoListContents(&files);
1412 	if (batchResult < 0)
1413 		cip->errNo = batchResult;
1414 	return (batchResult);
1415 }	/* FTPPutFiles3 */
1416 
1417 
1418 
1419 
1420 /* The purpose of this is to provide updates for the progress meters
1421  * during lags.  Return zero if the operation timed-out.
1422  */
1423 static int
1424 WaitForRemoteInput(const FTPCIPtr cip)
1425 {
1426 	fd_set ss, ss2;
1427 	struct timeval tv;
1428 	int result;
1429 	int fd;
1430 	int wsecs;
1431 	int xferTimeout;
1432 	int ocancelXfer;
1433 
1434 	xferTimeout = cip->xferTimeout;
1435 	if (xferTimeout < 1)
1436 		return (1);
1437 
1438 	fd = cip->dataSocket;
1439 	if (fd < 0)
1440 		return (1);
1441 
1442 	ocancelXfer = cip->cancelXfer;
1443 	wsecs = 0;
1444 	cip->stalled = 0;
1445 
1446 	while ((xferTimeout <= 0) || (wsecs < xferTimeout)) {
1447 		if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) {
1448 			/* leave cip->stalled -- could have been stalled and then canceled. */
1449 			return (1);
1450 		}
1451 		FD_ZERO(&ss);
1452 		FD_SET(fd, &ss);
1453 		ss2 = ss;
1454 		tv.tv_sec = 1;
1455 		tv.tv_usec = 0;
1456 		result = select(fd + 1, SELECT_TYPE_ARG234 &ss, NULL, SELECT_TYPE_ARG234 &ss2, &tv);
1457 		if (result == 1) {
1458 			/* ready */
1459 			cip->stalled = 0;
1460 			return (1);
1461 		} else if (result < 0) {
1462 			if (result != EINTR) {
1463 				perror("select");
1464 				cip->stalled = 0;
1465 				return (1);
1466 			}
1467 		} else {
1468 			wsecs++;
1469 			cip->stalled = wsecs;
1470 		}
1471 		FTPUpdateIOTimer(cip);
1472 	}
1473 
1474 #if !defined(NO_SIGNALS)
1475 	/* Shouldn't get here -- alarm() should have
1476 	 * went off by now.
1477 	 */
1478 	(void) kill(getpid(), SIGALRM);
1479 #endif	/* NO_SIGNALS */
1480 
1481 	cip->dataTimedOut = 1;
1482 	return (0);	/* timed-out */
1483 }	/* WaitForRemoteInput */
1484 
1485 
1486 
1487 
1488 /* Nice for UNIX, but not necessary otherwise. */
1489 #ifdef TAR
1490 
1491 static int
1492 OpenTar(const FTPCIPtr cip, const char *const dstdir, int *const pid)
1493 {
1494 	int pipe1[2];
1495 	int pid1;
1496 	int i;
1497 	char *argv[8];
1498 
1499 	*pid = -1;
1500 
1501 	if (access(TAR, X_OK) < 0) {
1502 		/* Path to TAR is invalid. */
1503 		return (-1);
1504 	}
1505 
1506 	if (pipe(pipe1) < 0) {
1507 		Error(cip, kDoPerror, "pipe to Tar failed");
1508 		return (-1);
1509 	}
1510 
1511 	pid1 = (int) fork();
1512 	if (pid1 < 0) {
1513 		(void) close(pipe1[0]);
1514 		(void) close(pipe1[1]);
1515 		return (-1);
1516 	} else if (pid1 == 0) {
1517 		/* Child */
1518 		if ((dstdir != NULL) && (dstdir[0] != '\0') && (chdir(dstdir) < 0)) {
1519 			Error(cip, kDoPerror, "tar chdir to %s failed", dstdir);
1520 			exit(1);
1521 		}
1522 		(void) close(pipe1[1]);		/* close write end */
1523 		(void) dup2(pipe1[0], 0);	/* use read end on stdin */
1524 		(void) close(pipe1[0]);
1525 
1526 		for (i=3; i<256; i++)
1527 			(void) close(i);
1528 
1529 		argv[0] = (char *) "tar";
1530 		argv[1] = (char *) "xpf";
1531 		argv[2] = (char *) "-";
1532 		argv[3] = NULL;
1533 
1534 		(void) execv(TAR, argv);
1535 		exit(1);
1536 	}
1537 
1538 	/* Parent */
1539 	*pid = pid1;
1540 
1541 	(void) close(pipe1[0]);		/* close read end */
1542 	return (pipe1[1]);		/* use write end */
1543 }	/* OpenTar */
1544 
1545 
1546 
1547 
1548 static int
1549 FTPGetOneTarF(const FTPCIPtr cip, const char *file, const char *const dstdir)
1550 {
1551 	char *buf;
1552 	size_t bufSize;
1553 	int tmpResult;
1554 	volatile int result;
1555 	int nread, nwrote;
1556 	volatile int fd;
1557 	volatile int vfd;
1558 	const char *volatile vfile;
1559 #ifndef NO_SIGNALS
1560 	int sj;
1561 	volatile FTPSigProc osigpipe;
1562 	volatile FTPCIPtr vcip;
1563 #endif
1564 	int pid, status;
1565 	char savedCwd[512];
1566 	char *volatile basecp;
1567 
1568 	result = kNoErr;
1569 	cip->usingTAR = 0;
1570 
1571 	if ((file[0] == '\0') || ((file[0] == '/') && (file[1] == '\0'))) {
1572 		/* It was "/"
1573 		 * We can't do that, because "get /.tar"
1574 		 * or "get .tar" does not work.
1575 		 */
1576 		result = kErrOpenFailed;
1577 		cip->errNo = kErrOpenFailed;
1578 		return (result);
1579 	}
1580 
1581 	if (FTPCmd(cip, "MDTM %s.tar", file) == 2) {
1582 		/* Better not use this method since there is
1583 		 * no way to tell if the server would use the
1584 		 * existing .tar or do a new one on the fly.
1585 		 */
1586 		result = kErrOpenFailed;
1587 		cip->errNo = kErrOpenFailed;
1588 		return (result);
1589 	}
1590 
1591 	basecp = strrchr(file, '/');
1592 	if (basecp != NULL)
1593 		basecp = strrchr(file, '\\');
1594 	if (basecp != NULL) {
1595 		/* Need to cd to the parent directory and get it
1596 		 * from there.
1597 		 */
1598 		if (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != 0) {
1599 			result = kErrOpenFailed;
1600 			cip->errNo = kErrOpenFailed;
1601 			return (result);
1602 		}
1603 		result = FTPChdir(cip, file);
1604 		if (result != kNoErr) {
1605 			return (result);
1606 		}
1607 		result = FTPChdir(cip, "..");
1608 		if (result != kNoErr) {
1609 			(void) FTPChdir(cip, savedCwd);
1610 			return (result);
1611 		}
1612 		file = basecp + 1;
1613 	}
1614 
1615 	fd = OpenTar(cip, dstdir, &pid);
1616 	if (fd < 0) {
1617 		result = kErrOpenFailed;
1618 		cip->errNo = kErrOpenFailed;
1619 		if (basecp != NULL)
1620 			(void) FTPChdir(cip, savedCwd);
1621 		return (result);
1622 	}
1623 
1624 	vfd = fd;
1625 	vfile = file;
1626 
1627 #ifndef NO_SIGNALS
1628 	vcip = cip;
1629 	osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
1630 
1631 	gGotBrokenData = 0;
1632 	gCanBrokenDataJmp = 0;
1633 
1634 #ifdef HAVE_SIGSETJMP
1635 	sj = sigsetjmp(gBrokenDataJmp, 1);
1636 #else
1637 	sj = setjmp(gBrokenDataJmp);
1638 #endif	/* HAVE_SIGSETJMP */
1639 
1640 	if (sj != 0) {
1641 		(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1642 		FTPShutdownHost(vcip);
1643 
1644 		(void) signal(SIGPIPE, SIG_IGN);
1645 		(void) close(vfd);
1646 		for (;;) {
1647 #ifdef HAVE_WAITPID
1648 			if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
1649 				break;
1650 #else
1651 			if ((wait(&status) < 0) && (errno != EINTR))
1652 				break;
1653 #endif
1654 			if (WIFEXITED(status) || WIFSIGNALED(status))
1655 				break;		/* done */
1656 		}
1657 		if (basecp != NULL)
1658 			(void) FTPChdir(cip, savedCwd);
1659 		vcip->errNo = kErrRemoteHostClosedConnection;
1660 		return(vcip->errNo);
1661 	}
1662 	gCanBrokenDataJmp = 1;
1663 
1664 #endif	/* NO_SIGNALS */
1665 
1666 	tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, (longest_int) 0, "RETR %s.tar", vfile);
1667 
1668 	if (tmpResult < 0) {
1669 		result = tmpResult;
1670 		if (result == kErrGeneric)
1671 			result = kErrRETRFailed;
1672 		cip->errNo = result;
1673 
1674 #ifndef NO_SIGNALS
1675 		(void) signal(SIGPIPE, SIG_IGN);
1676 #endif
1677 		(void) close(vfd);
1678 		for (;;) {
1679 #ifdef HAVE_WAITPID
1680 			if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
1681 				break;
1682 #else
1683 			if ((wait(&status) < 0) && (errno != EINTR))
1684 				break;
1685 #endif
1686 			if (WIFEXITED(status) || WIFSIGNALED(status))
1687 				break;		/* done */
1688 		}
1689 
1690 #ifndef NO_SIGNALS
1691 		(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1692 #endif
1693 		if (basecp != NULL)
1694 			(void) FTPChdir(cip, savedCwd);
1695 		return (result);
1696 	}
1697 
1698 	cip->usingTAR = 1;
1699 	buf = cip->buf;
1700 	bufSize = cip->bufSize;
1701 
1702 	FTPInitIOTimer(cip);
1703 	cip->lname = vfile;	/* could be NULL */
1704 	cip->rname = vfile;
1705 	FTPStartIOTimer(cip);
1706 
1707 	/* Binary */
1708 	for (;;) {
1709 		if (! WaitForRemoteInput(cip)) {	/* could set cancelXfer */
1710 			cip->errNo = result = kErrDataTimedOut;
1711 			Error(cip, kDontPerror, "Remote read timed out.\n");
1712 			break;
1713 		}
1714 		if (cip->cancelXfer > 0) {
1715 			FTPAbortDataTransfer(cip);
1716 			result = cip->errNo = kErrDataTransferAborted;
1717 			break;
1718 		}
1719 #if !defined(NO_SIGNALS)
1720 		gCanBrokenDataJmp = 1;
1721 		if (cip->xferTimeout > 0)
1722 			(void) alarm(cip->xferTimeout);
1723 #endif	/* NO_SIGNALS */
1724 #ifdef NO_SIGNALS
1725 		nread = SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
1726 		if (nread == kTimeoutErr) {
1727 			cip->errNo = result = kErrDataTimedOut;
1728 			Error(cip, kDontPerror, "Remote read timed out.\n");
1729 			break;
1730 		} else if (nread < 0) {
1731 			if (errno == EINTR)
1732 				continue;
1733 			Error(cip, kDoPerror, "Remote read failed.\n");
1734 			result = kErrSocketReadFailed;
1735 			cip->errNo = kErrSocketReadFailed;
1736 			break;
1737 		} else if (nread == 0) {
1738 			break;
1739 		}
1740 #else
1741 		nread = read(cip->dataSocket, buf, bufSize);
1742 		if (nread < 0) {
1743 			if (errno == EINTR)
1744 				continue;
1745 			Error(cip, kDoPerror, "Remote read failed.\n");
1746 			result = kErrSocketReadFailed;
1747 			cip->errNo = kErrSocketReadFailed;
1748 			break;
1749 		} else if (nread == 0) {
1750 			break;
1751 		}
1752 		gCanBrokenDataJmp = 0;
1753 #endif
1754 
1755 		nwrote = write(fd, buf, nread);
1756 		if (nwrote != nread) {
1757 			if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1758 				result = kErrWriteFailed;
1759 				cip->errNo = kErrWriteFailed;
1760 				errno = EPIPE;
1761 			} else {
1762 				Error(cip, kDoPerror, "Local write failed.\n");
1763 				result = kErrWriteFailed;
1764 				cip->errNo = kErrWriteFailed;
1765 			}
1766 			break;
1767 		}
1768 		cip->bytesTransferred += (longest_int) nread;
1769 		FTPUpdateIOTimer(cip);
1770 	}
1771 
1772 #if !defined(NO_SIGNALS)
1773 	if (cip->xferTimeout > 0)
1774 		(void) alarm(0);
1775 	gCanBrokenDataJmp = 0;
1776 #endif	/* NO_SIGNALS */
1777 
1778 	(void) close(fd);
1779 	for (;;) {
1780 #ifdef HAVE_WAITPID
1781 		if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
1782 			break;
1783 #else
1784 		if ((wait(&status) < 0) && (errno != EINTR))
1785 			break;
1786 #endif
1787 		if (WIFEXITED(status) || WIFSIGNALED(status))
1788 			break;		/* done */
1789 	}
1790 
1791 	tmpResult = FTPEndDataCmd(cip, 1);
1792 	if ((tmpResult < 0) && (result == 0)) {
1793 		result = kErrRETRFailed;
1794 		cip->errNo = kErrRETRFailed;
1795 	}
1796 	FTPStopIOTimer(cip);
1797 #if !defined(NO_SIGNALS)
1798 	(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1799 #endif
1800 
1801 	if ((result == 0) && (cip->bytesTransferred == 0)) {
1802 		result = kErrOpenFailed;
1803 		cip->errNo = kErrOpenFailed;
1804 	}
1805 	if (basecp != NULL)
1806 		(void) FTPChdir(cip, savedCwd);
1807 	return (result);
1808 }	/* FTPGetOneTarF */
1809 
1810 #endif	/* TAR */
1811 
1812 
1813 
1814 
1815 
1816 static int
1817 FTPGetOneF(
1818 	const FTPCIPtr cip,
1819 	const char *const file,
1820 	const char *dstfile,
1821 	int xtype,
1822 	const int fdtouse,
1823 	longest_int expectedSize,
1824 	time_t mdtm,
1825 	const int resumeflag,
1826 	const int appendflag,
1827 	const int deleteflag,
1828 	const ConfirmResumeDownloadProc resumeProc)
1829 {
1830 	char *buf;
1831 	size_t bufSize;
1832 	int tmpResult;
1833 	volatile int result;
1834 	int nread, nwrote;
1835 	volatile int fd;
1836 #if ASCII_TRANSLATION
1837 	char *src, *srclim;
1838 	char *dst, *dstlim;
1839 	char outbuf[512];
1840 #endif
1841 	volatile longest_int startPoint = 0;
1842 	struct utimbuf ut;
1843 	struct Stat st;
1844 #if !defined(NO_SIGNALS)
1845 	volatile FTPSigProc osigpipe;
1846 	volatile FTPCIPtr vcip;
1847 	volatile int vfd, vfdtouse;
1848 	int sj;
1849 #endif	/* NO_SIGNALS */
1850 	volatile int created = 0;
1851 	int zaction = kConfirmResumeProcSaidBestGuess;
1852 	int statrc;
1853 	int noMdtmCheck;
1854 	time_t now;
1855 
1856 	if (cip->buf == NULL) {
1857 		Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
1858 		cip->errNo = kErrNoBuf;
1859 		return (cip->errNo);
1860 	}
1861 
1862 	result = kNoErr;
1863 	cip->usingTAR = 0;
1864 
1865 	if (fdtouse < 0) {
1866 		/* Only ask for extended information
1867 		 * if we have the name of the file
1868 		 * and we didn't already have the
1869 		 * info.
1870 		 *
1871 		 * Always ask for the modification time,
1872 		 * because even if it was passed in it
1873 		 * may not be accurate.  This is often
1874 		 * the case when it came from an ls
1875 		 * listing, in which the local time
1876 		 * zone could be a factor.
1877 		 *
1878 		 */
1879 
1880 		AutomaticallyUseASCIIModeDependingOnExtension(cip, file, &xtype);
1881 		if (expectedSize == kSizeUnknown) {
1882 			(void) FTPFileSizeAndModificationTime(cip, file, &expectedSize, xtype, &mdtm);
1883 		} else {
1884 			(void) FTPFileModificationTime(cip, file, &mdtm);
1885 		}
1886 
1887 		/* For Get, we can't recover very well if it turns out restart
1888 		 * didn't work, so check beforehand.
1889 		 */
1890 		if ((resumeflag == kResumeYes) || (resumeProc != NoConfirmResumeDownloadProc)) {
1891 			if (cip->hasREST == kCommandAvailabilityUnknown) {
1892 				(void) FTPSetTransferType(cip, kTypeBinary);
1893 				if (SetStartOffset(cip, (longest_int) 1) == kNoErr) {
1894 					/* Now revert -- we still may not end up
1895 					 * doing it.
1896 					 */
1897 					SetStartOffset(cip, (longest_int) -1);
1898 				}
1899 			}
1900 		}
1901 
1902 		if (appendflag == kAppendYes) {
1903 			zaction = kConfirmResumeProcSaidAppend;
1904 		} else if (cip->hasREST == kCommandNotAvailable) {
1905 			zaction = kConfirmResumeProcSaidOverwrite;
1906 		} else if (resumeflag == kResumeYes) {
1907 			zaction = kConfirmResumeProcSaidBestGuess;
1908 		} else {
1909 			zaction = kConfirmResumeProcSaidOverwrite;
1910 		}
1911 
1912 		statrc = Stat(dstfile, &st);
1913 		if (statrc == 0) {
1914 			if (resumeProc != NULL) {
1915 				zaction = (*resumeProc)(
1916 						&dstfile,
1917 						(longest_int) st.st_size,
1918 						st.st_mtime,
1919 						file,
1920 						expectedSize,
1921 						mdtm,
1922 						&startPoint
1923 				);
1924 			}
1925 
1926 			if (zaction == kConfirmResumeProcSaidBestGuess) {
1927 				if (expectedSize != kSizeUnknown) {
1928 					/* We know the size of the remote file,
1929 					 * and we have a local file too.
1930 					 *
1931 					 * Try and decide if we need to get
1932 					 * the entire file, or just part of it.
1933 					 */
1934 
1935 					startPoint = (longest_int) st.st_size;
1936 					zaction = kConfirmResumeProcSaidResume;
1937 
1938 					/* If the local file exists and has a recent
1939 					 * modification time (< 12 hours) and
1940 					 * the remote file's modtime is not recent,
1941 					 * then heuristically conclude that the
1942 					 * local modtime should not be trusted
1943 					 * (i.e. user killed the process before
1944 					 * the local modtime could be preserved).
1945 					 */
1946 					noMdtmCheck = 0;
1947 					if (mdtm != kModTimeUnknown) {
1948 						time(&now);
1949 						if ((st.st_mtime > now) || (((now - st.st_mtime) < 46200) && ((now - mdtm) >= 46200)))
1950 							noMdtmCheck = 1;
1951 					}
1952 
1953 					if ((mdtm == kModTimeUnknown) || (noMdtmCheck != 0)) {
1954 						/* Can't use the timestamps as an aid. */
1955 						if (startPoint == expectedSize) {
1956 							/* Don't go to all the trouble of downloading nothing. */
1957 							cip->errNo = kErrLocalSameAsRemote;
1958 							if (deleteflag == kDeleteYes)
1959 								(void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
1960 							return (cip->errNo);
1961 						} else if (startPoint > expectedSize) {
1962 							/* Panic;  odds are the file we have
1963 							 * was a different file altogether,
1964 							 * since it is larger than the
1965 							 * remote copy.  Re-do it all.
1966 							 */
1967 							zaction = kConfirmResumeProcSaidOverwrite;
1968 						} /* else resume at startPoint */
1969 					} else if ((mdtm == st.st_mtime) || (mdtm == (st.st_mtime - 1)) || (mdtm == (st.st_mtime + 1))) {
1970 						/* File has the same time.
1971 						 * Note: Windows' file timestamps can be off by one second!
1972 						 */
1973 						if (startPoint == expectedSize) {
1974 							/* Don't go to all the trouble of downloading nothing. */
1975 							cip->errNo = kErrLocalSameAsRemote;
1976 							if (deleteflag == kDeleteYes)
1977 								(void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
1978 							return (cip->errNo);
1979 						} else if (startPoint > expectedSize) {
1980 							/* Panic;  odds are the file we have
1981 							 * was a different file altogether,
1982 							 * since it is larger than the
1983 							 * remote copy.  Re-do it all.
1984 							 */
1985 							zaction = kConfirmResumeProcSaidOverwrite;
1986 						} else {
1987 							/* We have a file by the same time,
1988 							 * but smaller start point.  Leave
1989 							 * the startpoint as is since it
1990 							 * is most likely valid.
1991 							 */
1992 						}
1993 					} else if (mdtm < st.st_mtime) {
1994 						/* Remote file is older than
1995 						 * local file.  Don't overwrite
1996 						 * our file.
1997 						 */
1998 						cip->errNo = kErrLocalFileNewer;
1999 						return (cip->errNo);
2000 					} else /* if (mdtm > st.st_mtime) */ {
2001 						/* File has a newer timestamp
2002 						 * altogether, assume the remote
2003 						 * file is an entirely new file
2004 						 * and replace ours with it.
2005 						 */
2006 						zaction = kConfirmResumeProcSaidOverwrite;
2007 					}
2008 				} else {
2009 						zaction = kConfirmResumeProcSaidOverwrite;
2010 				}
2011 			}
2012 		} else {
2013 			zaction = kConfirmResumeProcSaidOverwrite;
2014 		}
2015 
2016 		if (zaction == kConfirmResumeProcSaidCancel) {
2017 			/* User wants to cancel this file and any
2018 			 * remaining in batch.
2019 			 */
2020 			cip->errNo = kErrUserCanceled;
2021 			return (cip->errNo);
2022 		} else if (zaction == kConfirmResumeProcSaidSkip) {
2023 			/* Nothing done, but not an error. */
2024 			if (deleteflag == kDeleteYes)
2025 				(void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
2026 			return (kNoErr);
2027 		} else if (zaction == kConfirmResumeProcSaidResume) {
2028 			/* Resume; proc set the startPoint. */
2029 			if (startPoint == expectedSize) {
2030 				/* Don't go to all the trouble of downloading nothing. */
2031 				/* Nothing done, but not an error. */
2032 				if (deleteflag == kDeleteYes)
2033 					(void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
2034 				return (kNoErr);
2035 			} else if (startPoint > expectedSize) {
2036 				/* Cannot set start point past end of remote file */
2037 				cip->errNo = result = kErrSetStartPoint;
2038 				return (result);
2039 			}
2040 			fd = Open(dstfile, O_WRONLY|O_APPEND|O_BINARY, 00666);
2041 		} else if (zaction == kConfirmResumeProcSaidAppend) {
2042 			/* leave startPoint at zero, we will append everything. */
2043 			startPoint = (longest_int) 0;
2044 			fd = Open(dstfile, O_WRONLY|O_CREAT|O_APPEND|O_BINARY, 00666);
2045 		} else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
2046 			created = 1;
2047 			startPoint = (longest_int) 0;
2048 			fd = Open(dstfile, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 00666);
2049 		}
2050 
2051 		if (fd < 0) {
2052 			Error(cip, kDoPerror, "Cannot open local file %s for writing.\n", dstfile);
2053 			result = kErrOpenFailed;
2054 			cip->errNo = kErrOpenFailed;
2055 			return (result);
2056 		}
2057 
2058 		if ((expectedSize == (longest_int) 0) && (startPoint <= (longest_int) 0) && (zaction != kConfirmResumeProcSaidOverwrite)) {
2059 			/* Don't go to all the trouble of downloading nothing. */
2060 #if defined(WIN32) || defined(_WINDOWS)
2061 			/* Note: Windows doesn't allow zero-size files. */
2062 			(void) write(fd, "\r\n", 2);
2063 #endif
2064 			(void) close(fd);
2065 			if (mdtm != kModTimeUnknown) {
2066 				cip->mdtm = mdtm;
2067 				(void) time(&ut.actime);
2068 				ut.modtime = mdtm;
2069 				(void) utime(dstfile, &ut);
2070 			}
2071 			if (deleteflag == kDeleteYes)
2072 				(void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
2073 			return (kNoErr);
2074 		}
2075 	} else {
2076 		fd = fdtouse;
2077 	}
2078 
2079 	if ((cip->numDownloads == 0) && (cip->dataSocketRBufSize > 0)) {
2080 		/* If dataSocketSBufSize is non-zero, it means you
2081 		 * want to explicitly try to set the size of the
2082 		 * socket's I/O buffer.
2083 		 *
2084 		 * If it is zero, it means you want to just use the
2085 		 * TCP stack's default value, which is typically
2086 		 * between 8 and 64 kB.
2087 		 *
2088 		 * If you try to set the buffer larger than 64 kB,
2089 		 * the TCP stack should try to use RFC 1323 to
2090 		 * negotiate "TCP Large Windows" which may yield
2091 		 * significant performance gains.
2092 		 */
2093 		if (cip->hasRETRBUFSIZE == kCommandAvailable)
2094 			(void) FTPCmd(cip, "SITE RETRBUFSIZE %lu", (unsigned long) cip->dataSocketRBufSize);
2095 		else if (cip->hasRBUFSIZ == kCommandAvailable)
2096 			(void) FTPCmd(cip, "SITE RBUFSIZ %lu", (unsigned long) cip->dataSocketRBufSize);
2097 		else if (cip->hasRBUFSZ == kCommandAvailable)
2098 			(void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketRBufSize);
2099 		else if (cip->hasBUFSIZE == kCommandAvailable)
2100 			(void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
2101 	}
2102 
2103 #ifdef NO_SIGNALS
2104 #else	/* NO_SIGNALS */
2105 	vcip = cip;
2106 	vfdtouse = fdtouse;
2107 	vfd = fd;
2108 	osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
2109 
2110 	gGotBrokenData = 0;
2111 	gCanBrokenDataJmp = 0;
2112 
2113 #ifdef HAVE_SIGSETJMP
2114 	sj = sigsetjmp(gBrokenDataJmp, 1);
2115 #else
2116 	sj = setjmp(gBrokenDataJmp);
2117 #endif	/* HAVE_SIGSETJMP */
2118 
2119 	if (sj != 0) {
2120 		(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2121 		if (vfdtouse < 0) {
2122 			(void) close(vfd);
2123 		}
2124 		FTPShutdownHost(vcip);
2125 		vcip->errNo = kErrRemoteHostClosedConnection;
2126 		return(vcip->errNo);
2127 	}
2128 	gCanBrokenDataJmp = 1;
2129 #endif	/* NO_SIGNALS */
2130 
2131 	tmpResult = FTPStartDataCmd(cip, kNetReading, xtype, startPoint, "RETR %s", file);
2132 
2133 	if (tmpResult < 0) {
2134 		result = tmpResult;
2135 		if (result == kErrGeneric)
2136 			result = kErrRETRFailed;
2137 		cip->errNo = result;
2138 		if (fdtouse < 0) {
2139 			(void) close(fd);
2140 			if ((created != 0) && (appendflag == kAppendNo) && (cip->startPoint == 0))
2141 				(void) unlink(dstfile);
2142 		}
2143 #if !defined(NO_SIGNALS)
2144 		(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2145 #endif	/* NO_SIGNALS */
2146 		return (result);
2147 	}
2148 
2149 	if ((startPoint != 0) && (cip->startPoint == 0)) {
2150 		/* Remote could not or would not set the start offset
2151 		 * to what we wanted.
2152 		 *
2153 		 * So now we have to undo our seek.
2154 		 */
2155 		if (Lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
2156 			cip->errNo = kErrLseekFailed;
2157 			if (fdtouse < 0) {
2158 				(void) close(fd);
2159 			}
2160 #if !defined(NO_SIGNALS)
2161 			(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2162 #endif	/* NO_SIGNALS */
2163 			return (cip->errNo);
2164 		}
2165 		startPoint = 0;
2166 	}
2167 
2168 	buf = cip->buf;
2169 	bufSize = cip->bufSize;
2170 
2171 	FTPInitIOTimer(cip);
2172 	cip->mdtm = mdtm;
2173 	(void) time(&ut.actime);
2174 	ut.modtime = mdtm;
2175 	cip->expectedSize = expectedSize;
2176 	cip->lname = dstfile;	/* could be NULL */
2177 	cip->rname = file;
2178 	if (fdtouse >= 0)
2179 		cip->useProgressMeter = 0;
2180 	FTPStartIOTimer(cip);
2181 
2182 #if ASCII_TRANSLATION
2183 	if (xtype == kTypeAscii) {
2184 		/* Ascii */
2185 		for (;;) {
2186 			if (! WaitForRemoteInput(cip)) {	/* could set cancelXfer */
2187 				cip->errNo = result = kErrDataTimedOut;
2188 				Error(cip, kDontPerror, "Remote read timed out.\n");
2189 				break;
2190 			}
2191 			if (cip->cancelXfer > 0) {
2192 				FTPAbortDataTransfer(cip);
2193 				result = cip->errNo = kErrDataTransferAborted;
2194 				break;
2195 			}
2196 #ifdef TESTING_ABOR
2197 			if (cip->bytesTransferred > 0) {
2198 				cip->cancelXfer = 1;
2199 				FTPAbortDataTransfer(cip);
2200 				result = cip->errNo = kErrDataTransferAborted;
2201 				break;
2202 			}
2203 #endif /* TESTING_ABOR */
2204 #ifdef NO_SIGNALS
2205 			nread = SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
2206 			if (nread == kTimeoutErr) {
2207 				cip->errNo = result = kErrDataTimedOut;
2208 				Error(cip, kDontPerror, "Remote read timed out.\n");
2209 				break;
2210 			} else if (nread < 0) {
2211 				if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2212 					result = cip->errNo = kErrSocketReadFailed;
2213 					errno = EPIPE;
2214 					Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2215 				} else if (errno == EINTR) {
2216 					continue;
2217 				} else {
2218 					Error(cip, kDoPerror, "Remote read failed.\n");
2219 					result = kErrSocketReadFailed;
2220 					cip->errNo = kErrSocketReadFailed;
2221 				}
2222 				break;
2223 			} else if (nread == 0) {
2224 				break;
2225 			}
2226 #else
2227 			gCanBrokenDataJmp = 1;
2228 			if (cip->xferTimeout > 0)
2229 				(void) alarm(cip->xferTimeout);
2230 			nread = read(cip->dataSocket, buf, bufSize);
2231 			if (nread < 0) {
2232 				if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2233 					result = cip->errNo = kErrSocketReadFailed;
2234 					errno = EPIPE;
2235 					Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2236 					(void) shutdown(cip->dataSocket, 2);
2237 				} else if (errno == EINTR) {
2238 					continue;
2239 				} else {
2240 					result = cip->errNo = kErrSocketReadFailed;
2241 					Error(cip, kDoPerror, "Remote read failed.\n");
2242 					(void) shutdown(cip->dataSocket, 2);
2243 				}
2244 				break;
2245 			} else if (nread == 0) {
2246 				break;
2247 			}
2248 
2249 			gCanBrokenDataJmp = 0;
2250 #endif	/* NO_SIGNALS */
2251 
2252 			src = buf;
2253 			srclim = src + nread;
2254 			dst = outbuf;
2255 			dstlim = dst + sizeof(outbuf);
2256 			while (src < srclim) {
2257 				if (*src == '\r') {
2258 					src++;
2259 					continue;
2260 				}
2261 				if (dst >= dstlim) {
2262 					nwrote = write(fd, outbuf, (size_t) (dst - outbuf));
2263 					if (nwrote == (int) (dst - outbuf)) {
2264 						/* Success. */
2265 						dst = outbuf;
2266 					} else if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2267 						result = kErrWriteFailed;
2268 						cip->errNo = kErrWriteFailed;
2269 						errno = EPIPE;
2270 						(void) shutdown(cip->dataSocket, 2);
2271 						goto brk;
2272 					} else {
2273 						Error(cip, kDoPerror, "Local write failed.\n");
2274 						result = kErrWriteFailed;
2275 						cip->errNo = kErrWriteFailed;
2276 						(void) shutdown(cip->dataSocket, 2);
2277 						goto brk;
2278 					}
2279 				}
2280 				*dst++ = *src++;
2281 			}
2282 			if (dst > outbuf) {
2283 				nwrote = write(fd, outbuf, (size_t) (dst - outbuf));
2284 				if (nwrote != (int) (dst - outbuf)) {
2285 					if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2286 						result = kErrWriteFailed;
2287 						cip->errNo = kErrWriteFailed;
2288 						errno = EPIPE;
2289 						(void) shutdown(cip->dataSocket, 2);
2290 						goto brk;
2291 					} else {
2292 						Error(cip, kDoPerror, "Local write failed.\n");
2293 						result = kErrWriteFailed;
2294 						cip->errNo = kErrWriteFailed;
2295 						(void) shutdown(cip->dataSocket, 2);
2296 						goto brk;
2297 					}
2298 				}
2299 			}
2300 
2301 			if (mdtm != kModTimeUnknown) {
2302 				(void) utime(dstfile, &ut);
2303 			}
2304 			cip->bytesTransferred += (longest_int) nread;
2305 			FTPUpdateIOTimer(cip);
2306 		}
2307 	} else
2308 #endif	/* ASCII_TRANSLATION */
2309 	{
2310 		/* Binary */
2311 		for (;;) {
2312 			if (! WaitForRemoteInput(cip)) {	/* could set cancelXfer */
2313 				cip->errNo = result = kErrDataTimedOut;
2314 				Error(cip, kDontPerror, "Remote read timed out.\n");
2315 				break;
2316 			}
2317 			if (cip->cancelXfer > 0) {
2318 				FTPAbortDataTransfer(cip);
2319 				result = cip->errNo = kErrDataTransferAborted;
2320 				break;
2321 			}
2322 #ifdef TESTING_ABOR
2323 			if (cip->bytesTransferred > 0) {
2324 				cip->cancelXfer = 1;
2325 				FTPAbortDataTransfer(cip);
2326 				result = cip->errNo = kErrDataTransferAborted;
2327 				break;
2328 			}
2329 #endif /* TESTING_ABOR */
2330 #ifdef NO_SIGNALS
2331 			nread = SRead(cip->dataSocket, buf, bufSize, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
2332 			if (nread == kTimeoutErr) {
2333 				cip->errNo = result = kErrDataTimedOut;
2334 				Error(cip, kDontPerror, "Remote read timed out.\n");
2335 				break;
2336 			} else if (nread < 0) {
2337 				if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2338 					result = cip->errNo = kErrSocketReadFailed;
2339 					errno = EPIPE;
2340 					Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2341 				} else if (errno == EINTR) {
2342 					continue;
2343 				} else {
2344 					Error(cip, kDoPerror, "Remote read failed.\n");
2345 					result = kErrSocketReadFailed;
2346 					cip->errNo = kErrSocketReadFailed;
2347 				}
2348 				break;
2349 			} else if (nread == 0) {
2350 				break;
2351 			}
2352 #else
2353 			gCanBrokenDataJmp = 1;
2354 			if (cip->xferTimeout > 0)
2355 				(void) alarm(cip->xferTimeout);
2356 			nread = read(cip->dataSocket, buf, bufSize);
2357 			if (nread < 0) {
2358 				if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2359 					result = cip->errNo = kErrSocketReadFailed;
2360 					errno = EPIPE;
2361 					Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2362 				} else if (errno == EINTR) {
2363 					continue;
2364 				} else {
2365 					result = cip->errNo = kErrSocketReadFailed;
2366 					Error(cip, kDoPerror, "Remote read failed.\n");
2367 				}
2368 				(void) shutdown(cip->dataSocket, 2);
2369 				break;
2370 			} else if (nread == 0) {
2371 				break;
2372 			}
2373 			gCanBrokenDataJmp = 0;
2374 #endif	/* NO_SIGNALS */
2375 
2376 			nwrote = write(fd, buf, nread);
2377 			if (nwrote != nread) {
2378 				if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2379 					result = kErrWriteFailed;
2380 					cip->errNo = kErrWriteFailed;
2381 					errno = EPIPE;
2382 				} else {
2383 					Error(cip, kDoPerror, "Local write failed.\n");
2384 					result = kErrWriteFailed;
2385 					cip->errNo = kErrWriteFailed;
2386 				}
2387 				(void) shutdown(cip->dataSocket, 2);
2388 				break;
2389 			}
2390 
2391 			/* Ugggh... do this after each write operation
2392 			 * so it minimizes the chance of a user killing
2393 			 * the process before we reset the timestamps.
2394 			 */
2395 			if (mdtm != kModTimeUnknown) {
2396 				(void) utime(dstfile, &ut);
2397 			}
2398 			cip->bytesTransferred += (longest_int) nread;
2399 			FTPUpdateIOTimer(cip);
2400 		}
2401 	}
2402 
2403 #if ASCII_TRANSLATION
2404 brk:
2405 #endif
2406 
2407 #if !defined(NO_SIGNALS)
2408 	if (cip->xferTimeout > 0)
2409 		(void) alarm(0);
2410 	gCanBrokenDataJmp = 0;
2411 #endif	/* NO_SIGNALS */
2412 
2413 	if (fdtouse < 0) {
2414 		/* If they gave us a descriptor (fdtouse >= 0),
2415 		 * leave it open, otherwise we opened it, so
2416 		 * we need to close it.
2417 		 */
2418 		(void) close(fd);
2419 		fd = -1;
2420 	}
2421 
2422 	tmpResult = FTPEndDataCmd(cip, 1);
2423 	if ((tmpResult < 0) && (result == 0)) {
2424 		result = kErrRETRFailed;
2425 		cip->errNo = kErrRETRFailed;
2426 	}
2427 	FTPStopIOTimer(cip);
2428 #if !defined(NO_SIGNALS)
2429 	(void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2430 #endif	/* NO_SIGNALS */
2431 
2432 	if ((mdtm != kModTimeUnknown) && (cip->bytesTransferred > 0)) {
2433 		(void) utime(dstfile, &ut);
2434 	}
2435 
2436 	if (result == kNoErr) {
2437 		cip->numDownloads++;
2438 
2439 		if (deleteflag == kDeleteYes) {
2440 			result = FTPDelete(cip, file, kRecursiveNo, kGlobNo);
2441 		}
2442 	}
2443 
2444 	return (result);
2445 }	/* FTPGetOneF */
2446 
2447 
2448 
2449 
2450 int
2451 FTPGetOneFile3(
2452 	const FTPCIPtr cip,
2453 	const char *const file,
2454 	const char *const dstfile,
2455 	const int xtype,
2456 	const int fdtouse,
2457 	const int resumeflag,
2458 	const int appendflag,
2459 	const int deleteflag,
2460 	const ConfirmResumeDownloadProc resumeProc,
2461 	int UNUSED(reserved))
2462 {
2463 	int result;
2464 
2465 	LIBNCFTP_USE_VAR(reserved);
2466 	if (cip == NULL)
2467 		return (kErrBadParameter);
2468 	if (strcmp(cip->magic, kLibraryMagic))
2469 		return (kErrBadMagic);
2470 
2471 	if ((file == NULL) || (file[0] == '\0'))
2472 		return (kErrBadParameter);
2473 	if (fdtouse < 0) {
2474 		if ((dstfile == NULL) || (dstfile[0] == '\0'))
2475 			return (kErrBadParameter);
2476 	}
2477 
2478 	result = FTPGetOneF(cip, file, dstfile, xtype, fdtouse, kSizeUnknown, kModTimeUnknown, resumeflag, appendflag, deleteflag, resumeProc);
2479 	return (result);
2480 }	/* FTPGetOneFile3 */
2481 
2482 
2483 
2484 
2485 int
2486 FTPGetFiles3(
2487 	const FTPCIPtr cip,
2488 	const char *pattern1,
2489 	const char *const dstdir1,
2490 	const int recurse,
2491 	int doGlob,
2492 	const int xtype,
2493 	const int resumeflag,
2494 	int appendflag,
2495 	const int deleteflag,
2496 	const int tarflag,
2497 	const ConfirmResumeDownloadProc resumeProc,
2498 	int UNUSED(reserved))
2499 {
2500 	LineList globList;
2501 	LinePtr itemPtr;
2502 	FileInfoList files;
2503 	FileInfoPtr filePtr;
2504 	int batchResult;
2505 	int result;
2506 	char *ldir;
2507 	char *cp;
2508 	const char *dstdir;
2509 	const char *pattern;
2510 	char *pattern2, *dstdir2;
2511 	char c;
2512 	int recurse1;
2513 	int errRc;
2514 
2515 	LIBNCFTP_USE_VAR(reserved);
2516 	if (cip == NULL)
2517 		return (kErrBadParameter);
2518 	if (strcmp(cip->magic, kLibraryMagic))
2519 		return (kErrBadMagic);
2520 	if (pattern1 == NULL)
2521 		return (kErrBadParameter);
2522 
2523 	dstdir2 = NULL;
2524 	pattern2 = NULL;
2525 
2526 	if (dstdir1 == NULL) {
2527 		dstdir = NULL;
2528 	} else {
2529 		dstdir2 = StrDup(dstdir1);
2530 		if (dstdir2 == NULL) {
2531 			errRc = kErrMallocFailed;
2532 			goto return_err;
2533 		}
2534 		StrRemoveTrailingLocalPathDelim(dstdir2);
2535 		dstdir = dstdir2;
2536 	}
2537 
2538 	pattern2 = StrDup(pattern1);
2539 	if (pattern2 == NULL) {
2540 		errRc = kErrMallocFailed;
2541 		goto return_err;
2542 	}
2543 	StrRemoveTrailingSlashes(pattern2);
2544 	pattern = pattern2;
2545 
2546 	if (pattern[0] == '\0') {
2547 		if (recurse == kRecursiveNo) {
2548 			errRc = kErrBadParameter;
2549 			goto return_err;
2550 		}
2551 		pattern = ".";
2552 		doGlob = kGlobNo;
2553 	} else if (strcmp(pattern, ".") == 0) {
2554 		if (recurse == kRecursiveNo) {
2555 			errRc = kErrBadParameter;
2556 			goto return_err;
2557 		}
2558 		doGlob = kGlobNo;
2559 	}
2560 	if (recurse == kRecursiveYes)
2561 		appendflag = kAppendNo;
2562 
2563 	batchResult = FTPRemoteGlob(cip, &globList, pattern, doGlob);
2564 	if (batchResult != kNoErr) {
2565 		errRc = batchResult;
2566 		goto return_err;
2567 	}
2568 
2569 	cip->cancelXfer = 0;	/* should already be zero */
2570 
2571 	for (itemPtr = globList.first; itemPtr != NULL; itemPtr = itemPtr->next) {
2572 		if ((recurse == kRecursiveYes) && (FTPIsDir(cip, itemPtr->line) > 0)) {
2573 #ifdef TAR
2574 			if ((tarflag == kTarYes) && (xtype == kTypeBinary) && (appendflag == kAppendNo) && (deleteflag == kDeleteNo) && (FTPGetOneTarF(cip, itemPtr->line, dstdir) == kNoErr)) {
2575 				/* Great! */
2576 				continue;
2577 			}
2578 #endif	/* TAR */
2579 			(void) FTPRemoteRecursiveFileList1(cip, itemPtr->line, &files);
2580 			(void) ComputeLNames(&files, itemPtr->line, dstdir, 1);
2581 			recurse1 = recurse;
2582 		} else {
2583 			recurse1 = kRecursiveNo;
2584 			(void) LineToFileInfoList(itemPtr, &files);
2585 			(void) ComputeRNames(&files, ".", 0, 1);
2586 			(void) ComputeLNames(&files, NULL, dstdir, 0);
2587 		}
2588 		if (cip->cancelXfer > 0) {
2589 			DisposeFileInfoListContents(&files);
2590 			break;
2591 		}
2592 
2593 #if 0
2594 		for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
2595 			PrintF(cip, "  R=%s, L=%s, 2=%s, size=%d, mdtm=%u, type=%c\n",
2596 				filePtr->rname,
2597 				filePtr->lname,
2598 				filePtr->rlinkto ? filePtr->rlinkto : "",
2599 				filePtr->size,
2600 				(unsigned int) filePtr->mdtm,
2601 				filePtr->type
2602 			);
2603 		}
2604 #endif
2605 
2606 
2607 		for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
2608 			if (cip->connected == 0) {
2609 				if (batchResult == kNoErr)
2610 					batchResult = kErrRemoteHostClosedConnection;
2611 				break;
2612 			}
2613 			if (filePtr->type == 'd') {
2614 #if defined(WIN32) || defined(_WINDOWS)
2615 				(void) MkDirs(filePtr->lname, 00777);
2616 #else
2617 				(void) mkdir(filePtr->lname, 00777);
2618 #endif
2619 			} else if (filePtr->type == 'l') {
2620 				/* skip it -- we do that next pass. */
2621 			} else if (recurse1 != kRecursiveYes) {
2622 				result = FTPGetOneF(cip, filePtr->rname, filePtr->lname, xtype, -1, filePtr->size, filePtr->mdtm, resumeflag, appendflag, deleteflag, resumeProc);
2623 				if (files.nFileInfos == 1) {
2624 					if (result != kNoErr)
2625 						batchResult = result;
2626 				} else {
2627 					if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
2628 						batchResult = result;
2629 				}
2630 				if (result == kErrUserCanceled)
2631 					cip->cancelXfer = 1;
2632 				if (cip->cancelXfer > 0)
2633 					break;
2634 			} else {
2635 				ldir = filePtr->lname;
2636 				cp = StrRFindLocalPathDelim(ldir);
2637 				if (cp != NULL) {
2638 					while (cp > ldir) {
2639 						if (! IsLocalPathDelim(*cp)) {
2640 							++cp;
2641 							break;
2642 						}
2643 						--cp;
2644 					}
2645 					if (cp > ldir) {
2646 						c = *cp;
2647 						*cp = '\0';
2648 						if (MkDirs(ldir, 00777) < 0) {
2649 							Error(cip, kDoPerror, "Could not create local directory \"%s\"\n", ldir);
2650 							batchResult = kErrGeneric;
2651 							*cp = c;
2652 							continue;
2653 						}
2654 						*cp = c;
2655 					}
2656 				}
2657 				result = FTPGetOneF(cip, filePtr->rname, filePtr->lname, xtype, -1, filePtr->size, filePtr->mdtm, resumeflag, appendflag, deleteflag, resumeProc);
2658 
2659 				if (files.nFileInfos == 1) {
2660 					if (result != kNoErr)
2661 						batchResult = result;
2662 				} else {
2663 					if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
2664 						batchResult = result;
2665 				}
2666 				if (result == kErrUserCanceled)
2667 					cip->cancelXfer = 1;
2668 				if (cip->cancelXfer > 0)
2669 					break;
2670 			}
2671 		}
2672 		if (cip->cancelXfer > 0) {
2673 			DisposeFileInfoListContents(&files);
2674 			break;
2675 		}
2676 
2677 #ifdef HAVE_SYMLINK
2678 		for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
2679 			if (filePtr->type == 'l') {
2680 				(void) unlink(filePtr->lname);
2681 				if (symlink(filePtr->rlinkto, filePtr->lname) < 0) {
2682 					Error(cip, kDoPerror, "Could not symlink %s to %s\n", filePtr->rlinkto, filePtr->lname);
2683 					/* Note: not worth setting batchResult */
2684 				}
2685 			}
2686 		}
2687 #endif	/* HAVE_SYMLINK */
2688 
2689 
2690 		DisposeFileInfoListContents(&files);
2691 	}
2692 
2693 	DisposeLineListContents(&globList);
2694 	if (batchResult < 0)
2695 		cip->errNo = batchResult;
2696 	errRc = batchResult;
2697 
2698 return_err:
2699 	if (dstdir2 != NULL)
2700 		free(dstdir2);
2701 	if (pattern2 != NULL)
2702 		free(pattern2);
2703 	return (errRc);
2704 }	/* FTPGetFiles3 */
2705 
2706 
2707 
2708 
2709 /*------------------------- wrappers for old routines ----------------------*/
2710 
2711 int
2712 FTPGetOneFile(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2713 {
2714 	return (FTPGetOneFile3(cip, file, dstfile, kTypeBinary, -1, kResumeNo, kAppendNo, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
2715 }	/* FTPGetOneFile */
2716 
2717 
2718 
2719 
2720 int
2721 FTPGetOneFile2(const FTPCIPtr cip, const char *const file, const char *const dstfile, const int xtype, const int fdtouse, const int resumeflag, const int appendflag)
2722 {
2723 	return (FTPGetOneFile3(cip, file, dstfile, xtype, fdtouse, resumeflag, appendflag, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
2724 }	/* FTPGetOneFile2 */
2725 
2726 
2727 
2728 
2729 int
2730 FTPGetFiles(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2731 {
2732 	return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeBinary, kResumeNo, kAppendNo, kDeleteNo, kTarYes, (ConfirmResumeDownloadProc) 0, 0));
2733 }	/* FTPGetFiles */
2734 
2735 
2736 
2737 
2738 int
2739 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)
2740 {
2741 	return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, xtype, resumeflag, appendflag, kDeleteNo, kTarYes, (ConfirmResumeDownloadProc) 0, 0));
2742 }	/* FTPGetFiles2 */
2743 
2744 
2745 
2746 
2747 int
2748 FTPGetOneFileAscii(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2749 {
2750 	return (FTPGetOneFile3(cip, file, dstfile, kTypeAscii, -1, kResumeNo, kAppendNo, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
2751 }	/* FTPGetOneFileAscii */
2752 
2753 
2754 
2755 
2756 int
2757 FTPGetFilesAscii(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2758 {
2759 	return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeAscii, kResumeNo, kAppendNo, kDeleteNo, kTarNo, (ConfirmResumeDownloadProc) 0, 0));
2760 }	/* FTPGetFilesAscii */
2761 
2762 
2763 
2764 
2765 int
2766 FTPPutOneFile(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2767 {
2768 	return (FTPPutOneFile3(cip, file, dstfile, kTypeBinary, -1, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2769 }	/* FTPPutOneFile */
2770 
2771 
2772 
2773 
2774 int
2775 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)
2776 {
2777 	return (FTPPutOneFile3(cip, file, dstfile, xtype, fdtouse, appendflag, tmppfx, tmpsfx, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2778 }	/* FTPPutOneFile2 */
2779 
2780 
2781 
2782 
2783 int
2784 FTPPutFiles(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2785 {
2786 	return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeBinary, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2787 }	/* FTPPutFiles */
2788 
2789 
2790 
2791 
2792 int
2793 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)
2794 {
2795 	return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, xtype, appendflag, tmppfx, tmpsfx, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2796 }	/* FTPPutFiles2 */
2797 
2798 
2799 
2800 
2801 int
2802 FTPPutOneFileAscii(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2803 {
2804 	return (FTPPutOneFile3(cip, file, dstfile, kTypeAscii, -1, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2805 }	/* FTPPutOneFileAscii */
2806 
2807 
2808 
2809 
2810 int
2811 FTPPutFilesAscii(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2812 {
2813 	return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeAscii, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2814 }	/* FTPPutFilesAscii */
2815 
2816 
2817 
2818 int
2819 FTPListToMemory(const FTPCIPtr cip, const char *const pattern, const LineListPtr llines, const char *const lsflags)
2820 {
2821 	return (FTPListToMemory2(cip, pattern, llines, lsflags, 1, (int *) 0));
2822 }	/* FTPListToMemory */
2823 
2824 /* eof IO.c */
2825