3 * Copyright (c) 1992-2001 by Mike Gleason.
23 /* This was the directory path when the user logged in. For anonymous users,
24 * this should be "/", but for real users, it should be their home directory.
25 * This variable is used later when we want to calculate a relative path
26 * from the starting directory.
30 /* The pathname to the current working directory. This always needs to be
31 * current, so whenever a directory change is done this should be changed.
35 /* Same, but the previous directory the user was in, or empty string if
38 char gPrevRemoteCWD
[512];
40 /* Another buffer we use just temporarily when switching directories. */
41 char gScratchCWD
[512];
43 /* The only reason we do this is to get gcc/lint to shut up
44 * about unused parameters.
47 #define ARGSUSED(x) x = (argc != 0) || (argv != 0) || (cmdp != 0) || (aip != 0)
49 /* Used temporarily, but put it here because it's big. */
50 FTPConnectionInfo gTmpURLConn
;
52 /* If the user doesn't want to be prompted for a batch of files,
53 * they can tell us to answer this answer for each item in the batch.
57 extern FTPLibraryInfo gLib
;
58 extern FTPConnectionInfo gConn
;
59 extern char gOurDirectoryPath
[];
60 extern Command gCommands
[];
61 extern char gVersion
[];
62 extern size_t gNumCommands
;
63 extern int gDebug
, gDoneApplication
;
65 extern int gOptInd
, gGotSig
;
66 extern int gFirstTimeUser
;
67 extern unsigned int gFirewallPort
;
68 extern int gFirewallType
;
69 extern char gFirewallHost
[64];
70 extern char gFirewallUser
[32];
71 extern char gFirewallPass
[32];
72 extern char gFirewallExceptionList
[];
73 extern char gPager
[], gHome
[], gShell
[];
75 extern int gAutoResume
, gRedialDelay
;
76 extern int gAutoSaveChangesToExistingBookmarks
;
78 extern int gLoadedBm
, gConfirmClose
, gSavePasswords
, gScreenColumns
;
79 extern char gLocalCWD
[512], gPrevLocalCWD
[512], gOurInstallationPath
[];
80 extern int gMayCancelJmp
;
81 #if defined(WIN32) || defined(_WINDOWS)
82 #elif defined(HAVE_SIGSETJMP)
83 extern sigjmp_buf gCancelJmp
;
84 #else /* HAVE_SIGSETJMP */
85 extern jmp_buf gCancelJmp
;
86 #endif /* HAVE_SIGSETJMP */
91 /* Open the users $PAGER, or just return stdout. Make sure to use
92 * ClosePager(), and not fclose/pclose directly.
100 (void) fflush(stdout
);
102 fp
= popen((pprog
[0] == '\0') ? "more" : pprog
, "w");
111 /* Close (maybe) a file previously created by OpenPager. */
113 ClosePager(FILE *pagerfp
)
119 if ((pagerfp
!= NULL
) && (pagerfp
!= stdout
)) {
121 osigpipe
= (sigproc_t
) NcSignal(SIGPIPE
, SIG_IGN
);
123 (void) pclose(pagerfp
);
125 (void) NcSignal(SIGPIPE
, osigpipe
);
133 /* Fills in the bookmarkName field of the Bookmark. */
135 PromptForBookmarkName(BookmarkPtr bmp
)
140 DefaultBookmarkName(dfltname
, sizeof(dfltname
), gConn
.host
);
141 if (dfltname
[0] == '\0') {
142 (void) printf("Enter a name for this bookmark: ");
144 (void) printf("Enter a name for this bookmark, or hit enter for \"%s\": ", dfltname
);
147 (void) FGets(bmname
, sizeof(bmname
), stdin
);
148 if (bmname
[0] != '\0') {
149 (void) STRNCPY(bmp
->bookmarkName
, bmname
);
151 } else if (dfltname
[0] != '\0') {
152 (void) STRNCPY(bmp
->bookmarkName
, dfltname
);
156 } /* PromptForBookmarkName */
161 CurrentURL(char *dst
, size_t dsize
, int showpass
)
166 memset(&bm
, 0, sizeof(bm
));
167 (void) STRNCPY(bm
.name
, gConn
.host
);
168 if ((gConn
.user
[0] != '\0') && (! STREQ(gConn
.user
, "anonymous")) && (! STREQ(gConn
.user
, "ftp"))) {
169 (void) STRNCPY(bm
.user
, gConn
.user
);
170 (void) STRNCPY(bm
.pass
, (showpass
== 0) ? "PASSWORD" : gConn
.pass
);
171 (void) STRNCPY(bm
.acct
, gConn
.acct
);
174 bm
.port
= gConn
.port
;
176 /* We now save relative paths, because the pathname in URLs are
177 * relative by nature. This makes non-anonymous FTP URLs shorter
178 * because it doesn't have to include the pathname of their
181 (void) STRNCPY(dir
, gRemoteCWD
);
182 AbsoluteToRelative(bm
.dir
, sizeof(bm
.dir
), dir
, gStartDir
, strlen(gStartDir
));
184 BookmarkToURL(&bm
, dst
, dsize
);
190 /* Fills in the fields of the Bookmark structure, based on the FTP current
194 FillBookmarkInfo(BookmarkPtr bmp
)
198 (void) STRNCPY(bmp
->name
, gConn
.host
);
199 if ((STREQ(gConn
.user
, "anonymous")) || (STREQ(gConn
.user
, "ftp"))) {
204 (void) STRNCPY(bmp
->user
, gConn
.user
);
205 (void) STRNCPY(bmp
->pass
, gConn
.pass
);
206 (void) STRNCPY(bmp
->acct
, gConn
.acct
);
209 /* We now save relative paths, because the pathname in URLs are
210 * relative by nature. This makes non-anonymous FTP URLs shorter
211 * because it doesn't have to include the pathname of their
214 (void) STRNCPY(dir
, gRemoteCWD
);
215 AbsoluteToRelative(bmp
->dir
, sizeof(bmp
->dir
), dir
, gStartDir
, strlen(gStartDir
));
216 bmp
->port
= gConn
.port
;
217 (void) time(&bmp
->lastCall
);
218 bmp
->hasSIZE
= gConn
.hasSIZE
;
219 bmp
->hasMDTM
= gConn
.hasMDTM
;
220 bmp
->hasPASV
= gConn
.hasPASV
;
221 bmp
->hasUTIME
= gConn
.hasUTIME
;
222 if (gFirewallType
== kFirewallNotInUse
)
223 (void) STRNCPY(bmp
->lastIP
, gConn
.ip
);
224 } /* FillBookmarkInfo */
229 /* Saves the current FTP session settings as a bookmark. */
231 SaveCurrentAsBookmark(void)
236 /* gBm.bookmarkName must already be set. */
237 FillBookmarkInfo(&gBm
);
239 saveBm
= gSavePasswords
;
242 if ((saveBm
< 0) && (gBm
.pass
[0] != '\0')) {
243 (void) printf("\n\nYou logged into this site using a password.\nWould you like to save the password with this bookmark?\n\n");
244 (void) printf("Save? [no] ");
245 (void) memset(ans
, 0, sizeof(ans
));
247 (void) fgets(ans
, sizeof(ans
) - 1, stdin
);
248 if ((saveBm
= StrToBool(ans
)) == 0) {
249 (void) printf("\nNot saving the password.\n");
252 if (PutBookmark(&gBm
, saveBm
) < 0) {
253 (void) fprintf(stderr
, "Could not save bookmark.\n");
255 /* Also marks whether we saved it. */
257 (void) printf("Bookmark \"%s\" saved.\n", gBm
.bookmarkName
);
261 } /* SaveCurrentAsBookmark */
266 /* If the user did not explicitly bookmark this site already, ask
267 * the user if they want to save one.
270 SaveUnsavedBookmark(void)
276 if ((gConfirmClose
!= 0) && (gLoadedBm
== 0) && (gOurDirectoryPath
[0] != '\0')) {
277 FillBookmarkInfo(&gBm
);
278 BookmarkToURL(&gBm
, url
, sizeof(url
));
279 (void) printf("\n\nYou have not saved a bookmark for this site.\n");
281 (void) printf("\nWould you like to save a bookmark to:\n\t%s\n\n", url
);
283 (void) printf("Save? (yes/no) ");
284 (void) memset(ans
, 0, sizeof(ans
));
286 if (fgets(ans
, sizeof(ans
) - 1, stdin
) == NULL
) {
291 if ((c
== 'n') || (c
== 'y'))
296 } else if (c
== 'Y') {
302 (void) printf("Not saved. (If you don't want to be asked this, \"set confirm-close no\")\n\n\n");
304 } else if (PromptForBookmarkName(&gBm
) < 0) {
305 (void) printf("Nevermind.\n");
307 SaveCurrentAsBookmark();
309 } else if ((gLoadedBm
== 1) && (gOurDirectoryPath
[0] != '\0') && (strcmp(gOurDirectoryPath
, gBm
.dir
) != 0)) {
310 /* Bookmark has changed. */
311 if (gAutoSaveChangesToExistingBookmarks
!= 0) {
312 SaveCurrentAsBookmark();
315 } /* SaveUnsavedBookmark */
319 /* Save the current host session settings for later as a "bookmark", which
320 * will be referred to by a bookmark abbreviation name.
323 BookmarkCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
325 /* The only reason we do this is to get gcc/lint to shut up
326 * about unused parameters.
328 ARGSUSED(gUnusedArg
);
330 if (gOurDirectoryPath
[0] == '\0') {
331 (void) printf("Sorry, configuration information is not saved for this user.\n");
332 } else if ((argc
<= 1) || (argv
[1][0] == '\0')) {
333 /* No name specified on the command line. */
334 if (gBm
.bookmarkName
[0] == '\0') {
335 /* Not previously bookmarked. */
336 if (PromptForBookmarkName(&gBm
) < 0) {
337 (void) printf("Nevermind.\n");
339 SaveCurrentAsBookmark();
342 /* User wants to update an existing bookmark. */
343 SaveCurrentAsBookmark();
346 (void) STRNCPY(gBm
.bookmarkName
, argv
[1]);
347 SaveCurrentAsBookmark();
354 /* Dump a remote file to the screen. */
356 CatCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
361 ARGSUSED(gUnusedArg
);
362 for (i
=1; i
<argc
; i
++) {
363 result
= FTPGetOneFile2(&gConn
, argv
[i
], NULL
, kTypeAscii
, STDOUT_FILENO
, kResumeNo
, kAppendNo
);
364 FTPPerror(&gConn
, result
, kErrCouldNotStartDataTransfer
, "cat", argv
[i
]);
371 NcFTPCdResponseProc(const FTPCIPtr cipUNUSED
, ResponsePtr rp
)
376 gUnusedArg
= (cipUNUSED
!= NULL
);
377 if ((rp
->printMode
& kResponseNoPrint
) != 0)
380 for (lp
= llp
->first
; lp
!= NULL
; lp
= lp
->next
) {
381 if ((lp
== llp
->first
) && (rp
->codeType
== 2)) {
382 if (ISTRNCMP(lp
->line
, "CWD command", 11) == 0)
384 if (lp
->line
[0] == '"')
385 continue; /* "/pub/foo" is... */
387 (void) printf("%s\n", lp
->line
);
389 } /* NcFTPCdResponseProc */
394 /* Manually print a response obtained from the remote FTP user. */
396 PrintResp(LineListPtr llp
)
401 for (lp
= llp
->first
; lp
!= NULL
; lp
= lp
->next
) {
402 if ((lp
== llp
->first
) && (ISTRNCMP(lp
->line
, "CWD command", 11) == 0))
404 (void) printf("%s\n", lp
->line
);
412 /* Do a chdir, and update our notion of the current directory.
413 * Some servers return it back as part of the CWD response,
414 * otherwise do a CWD command followed by a PWD.
417 nFTPChdirAndGetCWD(const FTPCIPtr cip
, const char *cdCwd
, const int quietMode
)
422 #ifdef USE_WHAT_SERVER_SAYS_IS_CWD
428 return (kErrBadParameter
);
429 if (strcmp(cip
->magic
, kLibraryMagic
))
430 return (kErrBadMagic
);
432 if ((cdCwd
== NULL
) || (cdCwd
[0] == '\0')) {
433 result
= kErrInvalidDirParam
;
434 cip
->errNo
= kErrInvalidDirParam
;
438 result
= kErrMallocFailed
;
439 cip
->errNo
= kErrMallocFailed
;
440 /* Error(cip, kDontPerror, "Malloc failed.\n"); */
442 cdCwdLen
= strlen(cdCwd
);
443 if (strcmp(cdCwd
, "..") == 0) {
444 result
= RCmd(cip
, rp
, "CDUP");
446 result
= RCmd(cip
, rp
, "CWD %s", cdCwd
);
449 #ifdef USE_WHAT_SERVER_SAYS_IS_CWD
450 (void) STRNCPY(gScratchCWD
, gRemoteCWD
);
452 if ((r
= strrchr(rp
->msg
.first
->line
, '"')) != NULL
) {
453 /* "xxxx" is current directory.
454 * Strip out just the xxxx to copy into the remote cwd.
456 * This is nice because we didn't have to do a PWD.
458 l
= strchr(rp
->msg
.first
->line
, '"');
459 if ((l
!= NULL
) && (l
!= r
) && (l
== rp
->msg
.first
->line
)) {
462 (void) Strncpy(gRemoteCWD
, l
, sizeof(gRemoteCWD
));
463 *r
= '"'; /* Restore, so response prints correctly. */
469 rp
->printMode
|= kResponseNoPrint
;
470 NcFTPCdResponseProc(cip
, rp
);
471 DoneWithResponse(cip
, rp
);
473 result
= FTPGetCWD(cip
, gRemoteCWD
, sizeof(gRemoteCWD
));
474 if (result
!= kNoErr
) {
475 PathCat(gRemoteCWD
, sizeof(gRemoteCWD
), gScratchCWD
, cdCwd
);
479 #else /* USE_CLIENT_SIDE_CALCULATED_CWD */
481 rp
->printMode
|= kResponseNoPrint
;
482 NcFTPCdResponseProc(cip
, rp
);
483 DoneWithResponse(cip
, rp
);
484 (void) STRNCPY(gScratchCWD
, gRemoteCWD
);
485 PathCat(gRemoteCWD
, sizeof(gRemoteCWD
), gScratchCWD
, cdCwd
);
488 } else if (result
> 0) {
489 result
= kErrCWDFailed
;
490 cip
->errNo
= kErrCWDFailed
;
491 DoneWithResponse(cip
, rp
);
493 DoneWithResponse(cip
, rp
);
498 } /* nFTPChdirAndGetCWD */
504 Chdirs(FTPCIPtr cip
, const char *const cdCwd
)
511 return (kErrBadParameter
);
512 if (strcmp(cip
->magic
, kLibraryMagic
))
513 return (kErrBadMagic
);
516 result
= kErrInvalidDirParam
;
517 cip
->errNo
= kErrInvalidDirParam
;
521 if ((cdCwd
[0] == '\0') || (strcmp(cdCwd
, ".") == 0)) {
527 cp
[cip
->bufSize
- 2] = '\0';
528 if ((cdCwd
[0] == '.') && (cdCwd
[1] == '.') && ((cdCwd
[2] == '\0') || IsLocalPathDelim(cdCwd
[2]))) {
529 PathCat(cip
->buf
, cip
->bufSize
, gRemoteCWD
, cdCwd
);
531 (void) Strncpy(cip
->buf
, cdCwd
, cip
->bufSize
);
533 if (cp
[cip
->bufSize
- 2] != '\0')
534 return (kErrBadParameter
);
536 StrRemoveTrailingLocalPathDelim(cp
);
539 cp
= StrFindLocalPathDelim(cp
+ 0);
543 lastSubDir
= (cp
== NULL
);
544 result
= nFTPChdirAndGetCWD(cip
, (*startcp
!= '\0') ? startcp
: "/", lastSubDir
? 0 : 1);
548 } while ((!lastSubDir
) && (result
== 0));
556 /* Remote change of working directory command. */
558 ChdirCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
564 ARGSUSED(gUnusedArg
);
567 if (gStartDir
[0] != '\0') {
568 (void) STRNCPY(gPrevRemoteCWD
, gRemoteCWD
);
569 result
= Chdirs(&gConn
, gStartDir
);
570 if (result
!= kNoErr
) {
571 /* State is incoherent if this happens! */
572 FTPPerror(&gConn
, result
, kErrCWDFailed
, "Could not chdir to", gStartDir
);
579 result
= FTPRemoteGlob(&gConn
, &ll
, argv
[1], (aip
->noglobargv
[1] != 0) ? kGlobNo
: kGlobYes
);
581 FTPPerror(&gConn
, result
, kErrGlobFailed
, argv
[0], argv
[1]);
584 if ((lp
!= NULL
) && (lp
->line
!= NULL
)) {
585 if ((strcmp(lp
->line
, "-") == 0) && (gPrevRemoteCWD
[0] != '\0')) {
587 lp
->line
= StrDup(gPrevRemoteCWD
);
588 if (lp
->line
== NULL
) {
589 result
= kErrMallocFailed
;
590 gConn
.errNo
= kErrMallocFailed
;
592 (void) STRNCPY(gPrevRemoteCWD
, gRemoteCWD
);
593 result
= Chdirs(&gConn
, lp
->line
);
596 (void) STRNCPY(gPrevRemoteCWD
, gRemoteCWD
);
597 result
= Chdirs(&gConn
, lp
->line
);
599 if (result
!= kNoErr
)
600 FTPPerror(&gConn
, result
, kErrCWDFailed
, "Could not chdir to", lp
->line
);
603 DisposeLineListContents(&ll
);
610 /* Chmod files on the remote host, if it supports it. */
612 ChmodCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
616 ARGSUSED(gUnusedArg
);
617 for (i
=2; i
<argc
; i
++) {
619 &gConn
, argv
[i
], argv
[1],
620 (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
623 FTPPerror(&gConn
, result
, kErrChmodFailed
, "chmod", argv
[i
]);
628 /* Really should just flush only the modified directories... */
635 /* Close the current session to a remote FTP server. */
637 CloseCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
639 ARGSUSED(gUnusedArg
);
640 if (gConn
.connected
== 0)
641 (void) printf("Already closed.\n");
648 /* User interface to the program's debug-mode setting. */
650 DebugCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
652 ARGSUSED(gUnusedArg
);
654 SetDebug(atoi(argv
[1]));
662 /* Delete files on the remote host. */
664 DeleteCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
668 int recursive
= kRecursiveNo
;
670 ARGSUSED(gUnusedArg
);
672 while ((c
= Getopt(argc
, argv
, "rf")) > 0) switch(c
) {
674 recursive
= kRecursiveYes
;
684 for (i
=gOptInd
; i
<argc
; i
++) {
686 &gConn
, argv
[i
], recursive
,
687 (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
690 FTPPerror(&gConn
, result
, kErrDELEFailed
, "delete", argv
[i
]);
695 /* Really should just flush only the modified directories... */
702 /* Command shell echo command. This is mostly useful for testing the command
703 * shell, as a sample command which prints some output.
706 EchoCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
714 ARGSUSED(gUnusedArg
);
715 for (i
=1; i
<argc
; i
++) {
717 result
= FTPLocalGlob(&gConn
, &ll
, argv
[i
], (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
);
719 FTPPerror(&gConn
, result
, kErrGlobFailed
, "local glob", argv
[i
]);
721 for (lp
= ll
.first
; lp
!= NULL
; lp
= lp
->next
) {
722 if (lp
->line
!= NULL
) {
725 (void) printf("%s", lp
->line
);
730 DisposeLineListContents(&ll
);
739 NcFTPConfirmResumeDownloadProc(
740 const char *volatile *localpath
,
741 volatile longest_int localsize
,
742 volatile time_t localmtime
,
743 const char *volatile remotepath
,
744 volatile longest_int remotesize
,
745 volatile time_t remotemtime
,
746 volatile longest_int
*volatile startPoint
)
748 int zaction
= kConfirmResumeProcSaidBestGuess
;
749 char tstr
[80], ans
[32];
750 static char newname
[128]; /* arrggh... static. */
752 if (gResumeAnswerAll
!= kConfirmResumeProcNotUsed
)
753 return (gResumeAnswerAll
);
755 if (gAutoResume
!= 0)
756 return (kConfirmResumeProcSaidBestGuess
);
758 tstr
[sizeof(tstr
) - 1] = '\0';
759 (void) strftime(tstr
, sizeof(tstr
) - 1, "%c", localtime((time_t *) &localmtime
));
761 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
762 "\nThe local file \"%s\" already exists.\n\tLocal: %12lld bytes, dated %s.\n",
763 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
764 "\nThe local file \"%s\" already exists.\n\tLocal: %12qd bytes, dated %s.\n",
765 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
766 "\nThe local file \"%s\" already exists.\n\tLocal: %12I64d bytes, dated %s.\n",
768 "\nThe local file \"%s\" already exists.\n\tLocal: %12ld bytes, dated %s.\n",
775 if ((remotemtime
!= kModTimeUnknown
) && (remotesize
!= kSizeUnknown
)) {
776 (void) strftime(tstr
, sizeof(tstr
) - 1, "%c", localtime((time_t *) &remotemtime
));
778 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
779 "\tRemote: %12lld bytes, dated %s.\n",
780 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
781 "\tRemote: %12qd bytes, dated %s.\n",
782 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
783 "\tRemote: %12I64d bytes, dated %s.\n",
785 "\tRemote: %12ld bytes, dated %s.\n",
790 if ((remotemtime
== localmtime
) && (remotesize
== localsize
)) {
791 (void) printf("\t(Files are identical, skipped)\n\n");
792 return (kConfirmResumeProcSaidSkip
);
794 } else if (remotesize
!= kSizeUnknown
) {
796 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
797 "\tRemote: %12lld bytes, date unknown.\n",
798 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
799 "\tRemote: %12qd bytes, date unknown.\n",
800 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
801 "\tRemote: %12I64d bytes, date unknown.\n",
803 "\tRemote: %12ld bytes, date unknown.\n",
807 } else if (remotemtime
!= kModTimeUnknown
) {
808 (void) strftime(tstr
, sizeof(tstr
) - 1, "%c", localtime((time_t *) &remotemtime
));
810 "\tRemote: size unknown, dated %s.\n",
816 (void) memset(ans
, 0, sizeof(ans
));
818 (void) printf("\t[O]verwrite?");
819 if ((gConn
.hasREST
== kCommandAvailable
) && (remotesize
!= kSizeUnknown
) && (remotesize
> localsize
))
820 printf(" [R]esume?");
821 printf(" [A]ppend to? [S]kip? [N]ew Name?\n");
822 (void) printf("\t[O!]verwrite all?");
823 if ((gConn
.hasREST
== kCommandAvailable
) && (remotesize
!= kSizeUnknown
) && (remotesize
> localsize
))
824 printf(" [R!]esume all?");
825 printf(" [S!]kip all? [C]ancel > ");
827 (void) fgets(ans
, sizeof(ans
) - 1, stdin
);
828 switch ((int) ans
[0]) {
833 zaction
= kConfirmResumeProcSaidCancel
;
838 zaction
= kConfirmResumeProcSaidOverwrite
;
842 if ((gConn
.hasREST
!= kCommandAvailable
) || (remotesize
== kSizeUnknown
)) {
843 printf("\tResume is not available on this server.\n\n");
846 } else if (remotesize
< localsize
) {
847 printf("\tCannot resume when local file is already larger than the remote file.\n\n");
850 } else if (remotesize
<= localsize
) {
851 printf("\tLocal file is already the same size as the remote file.\n\n");
856 *startPoint
= localsize
;
857 zaction
= kConfirmResumeProcSaidResume
;
858 if (OneTimeMessage("auto-resume") != 0) {
859 printf("\n\tNOTE: If you want NcFTP to guess automatically, \"set auto-resume yes\"\n\n");
865 zaction
= kConfirmResumeProcSaidSkip
;
871 zaction
= kConfirmResumeProcSaidOverwrite
;
877 zaction
= kConfirmResumeProcSaidAppend
;
882 zaction
= kConfirmResumeProcSaidBestGuess
;
892 (void) memset(newname
, 0, sizeof(newname
));
893 printf("\tSave as: ");
895 (void) fgets(newname
, sizeof(newname
) - 1, stdin
);
896 newname
[strlen(newname
) - 1] = '\0';
897 if (newname
[0] == '\0') {
899 printf("Skipped %s.\n", remotepath
);
900 zaction
= kConfirmResumeProcSaidSkip
;
902 *localpath
= newname
;
907 gResumeAnswerAll
= zaction
;
909 } /* NcFTPConfirmResumeDownloadProc */
914 /* Download files from the remote system. */
916 GetCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
920 int recurseFlag
= kRecursiveNo
;
921 int appendFlag
= kAppendNo
;
922 int resumeFlag
= kResumeYes
;
923 int tarflag
= kTarYes
;
924 const char *dstdir
= NULL
;
928 int xtype
= gBm
.xferType
;
930 int deleteFlag
= kDeleteNo
;
933 ConfirmResumeDownloadProc confirmProc
;
935 confirmProc
= NcFTPConfirmResumeDownloadProc
;
936 gResumeAnswerAll
= kConfirmResumeProcNotUsed
; /* Ask at least once each time */
937 ARGSUSED(gUnusedArg
);
939 while ((opt
= Getopt(argc
, argv
, "aAzfrRTD")) >= 0) switch (opt
) {
944 /* Append to local files, instead of truncating
947 appendFlag
= kAppendYes
;
951 /* Do not try to resume a download, even if it
952 * appeared that some of the file was transferred
955 resumeFlag
= kResumeNo
;
956 confirmProc
= NoConfirmResumeDownloadProc
;
959 /* Special flag that lets you specify the
960 * destination file. Normally a "get" will
961 * write the file by the same name as the
962 * remote file's basename.
968 /* If the item is a directory, get the
969 * directory and all its contents.
971 recurseFlag
= kRecursiveYes
;
974 /* If they said "-R", they may want to
975 * turn off TAR mode if they are trying
976 * to resume the whole directory.
977 * The disadvantage to TAR mode is that
978 * it always downloads the whole thing,
979 * which is why there is a flag to
985 /* You can delete the remote file after
986 * you downloaded it successfully by using
987 * the -DD option. It requires two -D's
988 * to minimize the odds of accidentally
999 deleteFlag
= kDeleteYes
;
1001 if (renameMode
!= 0) {
1002 if (gOptInd
> argc
- 2) {
1003 PrintCmdUsage(cmdp
);
1004 (void) fprintf(stderr
, "\nFor get with rename, try \"get -z remote-path-name local-path-name\".\n");
1007 osigint
= NcSignal(SIGINT
, XferCanceller
);
1008 rc
= FTPGetOneFile3(&gConn
, argv
[gOptInd
], argv
[gOptInd
+ 1], xtype
, (-1), resumeFlag
, appendFlag
, deleteFlag
, NoConfirmResumeDownloadProc
, 0);
1010 FTPPerror(&gConn
, rc
, kErrCouldNotStartDataTransfer
, "get", argv
[gOptInd
]);
1012 osigint
= NcSignal(SIGINT
, XferCanceller
);
1013 for (i
=gOptInd
; i
<argc
; i
++) {
1014 doGlob
= (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
;
1015 STRNCPY(pattern
, argv
[i
]);
1016 StrRemoveTrailingSlashes(pattern
);
1017 rc
= FTPGetFiles3(&gConn
, pattern
, dstdir
, recurseFlag
, doGlob
, xtype
, resumeFlag
, appendFlag
, deleteFlag
, tarflag
, confirmProc
, 0);
1019 FTPPerror(&gConn
, rc
, kErrCouldNotStartDataTransfer
, "get", argv
[i
]);
1022 (void) NcSignal(SIGINT
, osigint
);
1023 (void) fflush(stdin
);
1025 if (deleteFlag
== kDeleteYes
) {
1026 /* Directory is now out of date */
1034 /* Display some brief help for specified commands, or a list of commands. */
1036 HelpCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1039 int showall
= 0, helpall
= 0;
1044 int len
, widestName
;
1046 const char *cmdnames
[64];
1048 ARGSUSED(gUnusedArg
);
1049 assert(gNumCommands
< (sizeof(cmdnames
) / sizeof(char *)));
1051 showall
= (strcmp(argv
[1], "showall") == 0);
1052 helpall
= (strcmp(argv
[1], "helpall") == 0);
1054 if (argc
== 1 || showall
) {
1056 Commands may be abbreviated. 'help showall' shows hidden and unsupported \n\
1057 commands. 'help <command>' gives a brief description of <command>.\n\n");
1059 /* First, see how many commands we will be printing to the screen.
1060 * Unless 'showall' was given, we won't be printing the hidden
1061 * (i.e. not very useful to the end-user) commands.
1065 for (n
= 0; n
< (int) gNumCommands
; c
++, n
++)
1066 if ((!iscntrl((int) c
->name
[0])) && (!(c
->flags
& kCmdHidden
) || showall
))
1069 (void) memset((char *) cmdnames
, 0, sizeof(cmdnames
));
1071 /* Now form the list we'll be printing, and noting what the maximum
1072 * length of a command name was, so we can use that when determining
1073 * how to print in columns.
1078 for (n
= 0; n
< (int) gNumCommands
; c
++, n
++) {
1079 if ((!iscntrl((int) c
->name
[0])) && (!(c
->flags
& kCmdHidden
) || showall
)) {
1080 cmdnames
[i
++] = c
->name
;
1081 len
= (int) strlen(c
->name
);
1082 if (len
> widestName
)
1087 if ((cp
= (char *) getenv("COLUMNS")) == NULL
)
1090 screenColumns
= atoi(cp
);
1092 /* Leave an extra bit of whitespace for the margins between columns. */
1095 nCols
= (screenColumns
+ 0) / widestName
;
1096 nRows
= nCmds2Print
/ nCols
;
1097 if ((nCmds2Print
% nCols
) > 0)
1100 for (i
= 0; i
< nRows
; i
++) {
1101 for (j
= 0; j
< nCols
; j
++) {
1103 if (k
< nCmds2Print
) {
1104 (void) sprintf(spec
, "%%-%ds",
1105 (j
< nCols
- 1) ? widestName
: widestName
- 2
1107 (void) printf(spec
, cmdnames
[k
]);
1110 (void) printf("\n");
1112 } else if (helpall
) {
1113 /* Really intended for me, so I can debug the help strings. */
1114 for (c
= gCommands
, n
= 0; n
< (int) gNumCommands
; c
++, n
++) {
1119 /* For each command name specified, print its help stuff. */
1120 for (i
=1; i
<argc
; i
++) {
1121 c
= GetCommandByName(argv
[i
], 0);
1122 if (c
== kAmbiguousCommand
) {
1123 (void) printf("%s: ambiguous command name.\n", argv
[i
]);
1124 } else if (c
== kNoCommand
) {
1125 (void) printf("%s: no such command.\n", argv
[i
]);
1128 (void) printf("\n");
1139 /* Displays the list of saved bookmarks, so that the user can then choose
1154 bookmarks
= OpenBookmarkFile(NULL
);
1155 if (bookmarks
== NULL
)
1159 osigpipe
= (sigproc_t
) NcSignal(SIGPIPE
, SIG_IGN
);
1161 pager
= OpenPager();
1163 while (GetNextBookmark(bookmarks
, &bm
) == 0) {
1167 (void) fprintf(pager
, "--BOOKMARK----------URL--------------------------------------------------------\n");
1169 BookmarkToURL(&bm
, url
, sizeof(url
));
1170 (void) fprintf(pager
, " %-16s %s\n", bm
.bookmarkName
, url
);
1174 CloseBookmarkFile(bookmarks
);
1177 (void) NcSignal(SIGPIPE
, osigpipe
);
1187 RunBookmarkEditor(char *selectedBmName
, size_t dsize
)
1189 #if defined(WIN32) || defined(_WINDOWS)
1190 char ncftpbookmarks
[260];
1194 char msg
[kNcFTPBookmarksMailslotMsgSize
];
1198 if (selectedBmName
!= NULL
)
1199 memset(selectedBmName
, 0, dsize
);
1200 if (gOurInstallationPath
[0] == '\0') {
1201 (void) fprintf(stderr
, "Cannot find path to %s. Please re-run Setup.\n", "ncftpbookmarks.exe");
1204 prog
= ncftpbookmarks
;
1205 OurInstallationPath(ncftpbookmarks
, sizeof(ncftpbookmarks
), "ncftpbookmarks.exe");
1208 hMailSlot
= CreateMailslot(kNcFTPBookmarksMailslot
, kNcFTPBookmarksMailslotMsgSize
, MAILSLOT_WAIT_FOREVER
, NULL
);
1210 if (hMailSlot
== INVALID_HANDLE_VALUE
) {
1211 SysPerror("CreateMailslot");
1212 (void) fprintf(stderr
, "Could not create communication channel with %s.\n", "ncftpbookmarks.exe");
1213 (void) fprintf(stderr
, "%s", "This means if you select a bookmark to connect to that NcFTP\n");
1214 (void) fprintf(stderr
, "%s", "will not get the message from %s.\n", "ncftpbookmarks.exe");
1217 winExecResult
= WinExec(prog
, SW_SHOWNORMAL
);
1218 if (winExecResult
<= 31) switch (winExecResult
) {
1219 case ERROR_BAD_FORMAT
:
1220 fprintf(stderr
, "Could not run %s: %s\n", prog
, "The .EXE file is invalid");
1222 case ERROR_FILE_NOT_FOUND
:
1223 fprintf(stderr
, "Could not run %s: %s\n", prog
, "The specified file was not found.");
1225 case ERROR_PATH_NOT_FOUND
:
1226 fprintf(stderr
, "Could not run %s: %s\n", prog
, "The specified path was not found.");
1229 fprintf(stderr
, "Could not run %s: Unknown error #%d.\n", prog
, winExecResult
);
1233 if (hMailSlot
!= INVALID_HANDLE_VALUE
) {
1234 fprintf(stdout
, "Waiting for %s to exit...\n", "ncftpbookmarks.exe");
1235 ZeroMemory(msg
, sizeof(msg
));
1246 SysPerror("ReadFile");
1248 msg
[sizeof(msg
) - 1] = '\0';
1249 Strncpy(selectedBmName
, msg
, dsize
);
1250 Trace(0, "Selected bookmark from editor: [%s]\n", selectedBmName
);
1252 CloseHandle(hMailSlot
);
1258 char ncftpbookmarks
[256];
1262 char bmSelectionFile
[256];
1266 if (selectedBmName
!= NULL
)
1267 memset(selectedBmName
, 0, dsize
);
1268 STRNCPY(ncftpbookmarks
, BINDIR
);
1269 STRNCAT(ncftpbookmarks
, "/");
1270 STRNCAT(ncftpbookmarks
, "ncftpbookmarks");
1272 STRNCPY(bmSelectionFile
, "view");
1273 if ((selectedBmName
!= NULL
) && (gOurDirectoryPath
[0] != '\0')) {
1274 sprintf(pidStr
, ".%u", (unsigned int) getpid());
1275 OurDirectoryPath(bmSelectionFile
, sizeof(bmSelectionFile
), kOpenSelectedBookmarkFileName
);
1276 STRNCAT(bmSelectionFile
, pidStr
);
1279 if (access(ncftpbookmarks
, X_OK
) == 0) {
1283 } else if (pid
== 0) {
1286 av
[0] = (char *) "ncftpbookmarks";
1287 av
[1] = bmSelectionFile
;
1289 execv(ncftpbookmarks
, av
);
1295 if ((waitpid(pid
, &status
, 0) < 0) && (errno
!= EINTR
))
1298 if ((wait(&status
) < 0) && (errno
!= EINTR
))
1301 if (WIFEXITED(status
) || WIFSIGNALED(status
))
1305 if (strcmp(bmSelectionFile
, "view") != 0) {
1306 fp
= fopen(bmSelectionFile
, FOPEN_READ_TEXT
);
1308 (void) FGets(selectedBmName
, dsize
, fp
);
1310 (void) unlink(bmSelectionFile
);
1311 Trace(0, "Selected bookmark from editor: [%s]\n", selectedBmName
);
1319 /* Not installed. */
1322 #endif /* Windows */
1323 } /* RunBookmarkEditor */
1327 /* This just shows the list of saved bookmarks. */
1329 HostsCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1334 ARGSUSED(gUnusedArg
);
1335 /* Skip visual mode if "-l". */
1336 if ((argc
== 1) && (RunBookmarkEditor(bm
, sizeof(bm
)) == 0)) {
1337 if (bm
[0] != '\0') {
1341 OpenCmd(2, av
, (CommandPtr
) 0, (ArgvInfoPtr
) 0);
1345 if (PrintHosts() <= 0) {
1346 (void) printf("You haven't bookmarked any FTP sites.\n");
1347 (void) printf("Before closing a site, you can use the \"bookmark\" command to save the current\nhost and directory for next time.\n");
1349 (void) printf("\nTo use a bookmark, use the \"open\" command with the name of the bookmark.\n");
1356 /* Show active background ncftp (ncftpbatch) jobs. */
1358 JobsCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1360 ARGSUSED(gUnusedArg
);
1367 /* Do a "ls" on the remote system and display the directory contents to our
1368 * user. This command handles "dir" (ls -l), "ls", and "nlist" (ls -1).
1371 ListCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1377 volatile int listmode
;
1378 FILE *volatile stream
;
1379 volatile int paging
;
1380 #if defined(WIN32) || defined(_WINDOWS)
1383 vsigproc_t osigpipe
, osigint
;
1386 ARGSUSED(gUnusedArg
);
1390 if (argv
[0][0] == 'd') {
1393 } else if (argv
[0][0] == 'n') {
1396 } else if (argv
[0][0] == 'p') {
1400 if (argv
[0][1] == 'd') {
1403 } else if (argv
[0][1] == 'n') {
1417 for (i
=1; i
<argc
; i
++) {
1418 if (argv
[i
][0] != '-')
1420 if (argv
[i
][1] == '-') {
1421 if (argv
[i
][2] == '\0') {
1422 /* end of options. */
1426 /* GNU-esque long --option? */
1427 PrintCmdUsage(cmdp
);
1431 option
[0] = argv
[i
][j
];
1432 if (argv
[i
][j
] == '\0')
1434 switch (argv
[i
][j
]) {
1445 (void) STRNCAT(options
, option
);
1454 stream
= OpenPager();
1455 if (stream
== NULL
) {
1459 #if defined(WIN32) || defined(_WINDOWS)
1460 #elif defined(HAVE_SIGSETJMP)
1461 osigpipe
= osigint
= (sigproc_t
) 0;
1462 sj
= sigsetjmp(gCancelJmp
, 1);
1463 #else /* HAVE_SIGSETJMP */
1464 osigpipe
= osigint
= (sigproc_t
) 0;
1465 sj
= setjmp(gCancelJmp
);
1466 #endif /* HAVE_SIGSETJMP */
1468 #if defined(WIN32) || defined(_WINDOWS)
1471 /* Caught a signal. */
1472 (void) NcSignal(SIGPIPE
, (FTPSigProc
) SIG_IGN
);
1474 (void) NcSignal(SIGPIPE
, osigpipe
);
1475 (void) NcSignal(SIGINT
, osigint
);
1476 Trace(0, "Canceled because of signal %d.\n", gGotSig
);
1477 (void) fprintf(stderr
, "Canceled.\n");
1481 osigpipe
= NcSignal(SIGPIPE
, Cancel
);
1482 osigint
= NcSignal(SIGINT
, Cancel
);
1488 if (argv
[i
] == NULL
) {
1489 /* No directory specified, use cwd. */
1490 Ls(NULL
, listmode
, options
, stream
);
1492 /* List each item. */
1493 for ( ; i
<argc
; i
++) {
1494 Ls(argv
[i
], listmode
, options
, stream
);
1500 #if defined(WIN32) || defined(_WINDOWS)
1502 (void) NcSignal(SIGPIPE
, osigpipe
);
1503 (void) NcSignal(SIGINT
, osigint
);
1512 /* Does a change of working directory on the local host. */
1514 LocalChdirCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1520 ARGSUSED(gUnusedArg
);
1521 dir
= ((argc
< 2) ? gHome
: argv
[1]);
1522 if ((dir
[0] == '-') && (dir
[1] == '\0')) {
1523 if (gPrevLocalCWD
[0] == '\0') {
1524 (void) fprintf(stderr
, "No previous local working directory to switch to.\n");
1527 dir
= gPrevLocalCWD
;
1529 } else if (dir
[0] == '~') {
1530 if (dir
[1] == '\0') {
1532 } else if (dir
[1] == '/') {
1533 (void) STRNCPY(buf
, gHome
);
1534 dir
= STRNCAT(buf
, dir
+ 1);
1537 result
= chdir(dir
);
1541 (void) STRNCPY(gPrevLocalCWD
, gLocalCWD
);
1542 (void) FTPGetLocalCWD(gLocalCWD
, sizeof(gLocalCWD
));
1545 } /* LocalChdirCmd */
1550 /* Does directory listing on the local host. */
1552 LocalListCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1554 #if defined(WIN32) || defined(_WINDOWS)
1559 volatile int listmode
;
1560 FILE *volatile stream
;
1561 volatile int paging
;
1564 ARGSUSED(gUnusedArg
);
1568 if (argv
[0][1] == 'd') {
1571 } else if (argv
[0][1] == 'n') {
1581 for (i
=1; i
<argc
; i
++) {
1582 if (argv
[i
][0] != '-')
1584 if (argv
[i
][1] == '-') {
1585 if (argv
[i
][2] == '\0') {
1586 /* end of options. */
1590 /* GNU-esque long --option? */
1591 PrintCmdUsage(cmdp
);
1595 option
[0] = argv
[i
][j
];
1596 if (argv
[i
][j
] == '\0')
1598 switch (argv
[i
][j
]) {
1609 (void) STRNCAT(options
, option
);
1617 if (argv
[i
] == NULL
) {
1618 /* No directory specified, use cwd. */
1619 LLs(NULL
, listmode
, options
, stream
);
1621 /* List each item. */
1622 for ( ; i
<argc
; i
++) {
1623 LLs(argv
[i
], listmode
, options
, stream
);
1628 FILE *volatile outfp
;
1629 FILE *volatile infp
;
1635 vsigproc_t osigpipe
, osigint
;
1637 ARGSUSED(gUnusedArg
);
1638 (void) fflush(stdin
);
1639 outfp
= OpenPager();
1641 (void) STRNCPY(incmd
, "/bin/ls");
1642 for (i
=1, dashopts
=0; i
<argc
; i
++) {
1643 (void) STRNCAT(incmd
, " ");
1644 if (argv
[i
][0] == '-')
1646 (void) STRNCAT(incmd
, argv
[i
]);
1649 if (dashopts
== 0) {
1650 (void) STRNCPY(incmd
, "/bin/ls -CF");
1651 for (i
=1; i
<argc
; i
++) {
1652 (void) STRNCAT(incmd
, " ");
1653 (void) STRNCAT(incmd
, argv
[i
]);
1657 infp
= popen(incmd
, "r");
1664 #ifdef HAVE_SIGSETJMP
1665 sj
= sigsetjmp(gCancelJmp
, 1);
1666 #else /* HAVE_SIGSETJMP */
1667 sj
= setjmp(gCancelJmp
);
1668 #endif /* HAVE_SIGSETJMP */
1671 /* Caught a signal. */
1672 (void) NcSignal(SIGPIPE
, (FTPSigProc
) SIG_IGN
);
1675 (void) pclose(infp
);
1676 (void) NcSignal(SIGPIPE
, osigpipe
);
1677 (void) NcSignal(SIGINT
, osigint
);
1678 (void) fprintf(stderr
, "Canceled.\n");
1679 Trace(0, "Canceled because of signal %d.\n", gGotSig
);
1683 osigpipe
= NcSignal(SIGPIPE
, Cancel
);
1684 osigint
= NcSignal(SIGINT
, Cancel
);
1688 while (fgets(line
, sizeof(line
) - 1, infp
) != NULL
)
1689 (void) fputs(line
, outfp
);
1690 (void) fflush(outfp
);
1692 (void) pclose(infp
);
1697 (void) NcSignal(SIGPIPE
, osigpipe
);
1698 (void) NcSignal(SIGINT
, osigint
);
1701 } /* LocalListCmd */
1707 Sys(const int argc
, const char **const argv
, const ArgvInfoPtr aip
, const char *syscmd
, int noDQuote
)
1712 (void) STRNCPY(cmd
, syscmd
);
1713 for (i
= 1; i
< argc
; i
++) {
1714 if (aip
->noglobargv
[i
] != 0) {
1715 (void) STRNCAT(cmd
, " '");
1716 (void) STRNCAT(cmd
, argv
[i
]);
1717 (void) STRNCAT(cmd
, "'");
1718 } else if (noDQuote
!= 0) {
1719 (void) STRNCAT(cmd
, " ");
1720 (void) STRNCAT(cmd
, argv
[i
]);
1722 (void) STRNCAT(cmd
, " \"");
1723 (void) STRNCAT(cmd
, argv
[i
]);
1724 (void) STRNCAT(cmd
, "\" ");
1727 #if defined(WIN32) || defined(_WINDOWS)
1728 fprintf(stderr
, "Cannot run command: %s\n", cmd
);
1730 Trace(0, "Sys: %s\n", cmd
);
1738 #if defined(WIN32) || defined(_WINDOWS)
1741 LocalChmodCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1743 ARGSUSED(gUnusedArg
);
1744 Sys(argc
, argv
, aip
, "/bin/chmod", 1);
1745 } /* LocalChmodCmd */
1751 LocalMkdirCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1753 #if defined(WIN32) || defined(_WINDOWS)
1757 for (i
= 1; i
< argc
; i
++) {
1759 if (MkDirs(arg
, 00755) < 0) {
1764 ARGSUSED(gUnusedArg
);
1765 Sys(argc
, argv
, aip
, "/bin/mkdir", 0);
1767 } /* LocalMkdirCmd */
1772 #if defined(WIN32) || defined(_WINDOWS)
1775 LocalPageCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1777 ARGSUSED(gUnusedArg
);
1778 Sys(argc
, argv
, aip
, gPager
, 0);
1779 } /* LocalPageCmd */
1786 LocalRenameCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1788 #if defined(WIN32) || defined(_WINDOWS)
1789 if (rename(argv
[1], argv
[2]) < 0) {
1793 ARGSUSED(gUnusedArg
);
1794 Sys(argc
, argv
, aip
, "/bin/mv", 1);
1796 } /* LocalRenameCmd */
1802 LocalRmCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1804 #if defined(WIN32) || defined(_WINDOWS)
1810 ARGSUSED(gUnusedArg
);
1811 for (i
=1; i
<argc
; i
++) {
1813 result
= FTPLocalGlob(&gConn
, &ll
, argv
[i
], (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
);
1815 FTPPerror(&gConn
, result
, kErrGlobFailed
, "local glob", argv
[i
]);
1817 for (lp
= ll
.first
; lp
!= NULL
; lp
= lp
->next
) {
1818 if (lp
->line
!= NULL
) {
1819 if (remove(lp
->line
) < 0)
1824 DisposeLineListContents(&ll
);
1827 ARGSUSED(gUnusedArg
);
1828 Sys(argc
, argv
, aip
, "/bin/rm", 1);
1836 LocalRmdirCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1838 #if defined(WIN32) || defined(_WINDOWS)
1844 ARGSUSED(gUnusedArg
);
1845 for (i
=1; i
<argc
; i
++) {
1847 result
= FTPLocalGlob(&gConn
, &ll
, argv
[i
], (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
);
1849 FTPPerror(&gConn
, result
, kErrGlobFailed
, "local glob", argv
[i
]);
1851 for (lp
= ll
.first
; lp
!= NULL
; lp
= lp
->next
) {
1852 if (lp
->line
!= NULL
) {
1853 if (rmdir(lp
->line
) < 0)
1858 DisposeLineListContents(&ll
);
1861 ARGSUSED(gUnusedArg
);
1862 Sys(argc
, argv
, aip
, "/bin/rmdir", 1);
1864 } /* LocalRmdirCmd */
1869 /* Displays the current local working directory. */
1871 LocalPwdCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1873 ARGSUSED(gUnusedArg
);
1874 if (FTPGetLocalCWD(gLocalCWD
, sizeof(gLocalCWD
)) != NULL
) {
1875 Trace(-1, "%s\n", gLocalCWD
);
1882 /* This is a simple interface to name service. I prefer using this instead
1886 LookupCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1892 struct in_addr ip_address
;
1896 ARGSUSED(gUnusedArg
);
1900 while ((opt
= Getopt(argc
, argv
, "v")) >= 0) {
1904 PrintCmdUsage(cmdp
);
1909 for (i
=gOptInd
; i
<argc
; i
++) {
1910 hp
= GetHostEntry((host
= argv
[i
]), &ip_address
);
1911 if ((i
> gOptInd
) && (shortMode
== 0))
1914 Trace(-1, "Unable to get information about site %s.\n", host
);
1915 } else if (shortMode
) {
1916 MyInetAddr(ipStr
, sizeof(ipStr
), hp
->h_addr_list
, 0);
1917 Trace(-1, "%-40s %s\n", hp
->h_name
, ipStr
);
1919 Trace(-1, "%s:\n", host
);
1920 Trace(-1, " Name: %s\n", hp
->h_name
);
1921 for (cpp
= hp
->h_aliases
; *cpp
!= NULL
; cpp
++)
1922 Trace(-1, " Alias: %s\n", *cpp
);
1923 for (j
= 0, cpp
= hp
->h_addr_list
; *cpp
!= NULL
; cpp
++, ++j
) {
1924 MyInetAddr(ipStr
, sizeof(ipStr
), hp
->h_addr_list
, j
);
1925 Trace(-1, " Address: %s\n", ipStr
);
1933 /* Directory listing in a machine-readable format;
1934 * Mostly for debugging, since NcFTP uses MLSD automatically when it needs to.
1937 MlsCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
1941 LinePtr linePtr
, nextLinePtr
;
1943 LineList dirContents
;
1947 ARGSUSED(gUnusedArg
);
1949 while ((opt
= Getopt(argc
, argv
, "dt")) >= 0) {
1950 if ((opt
== 'd') || (opt
== 't')) {
1951 /* Use MLST instead of MLSD,
1952 * which is similar to using "ls -d" instead of "ls".
1956 PrintCmdUsage(cmdp
);
1963 /* No args, do current directory. */
1966 if ((result
= FTPListToMemory2(&gConn
, item
, &dirContents
, (mlsd
== 0) ? "-d" : "", 1, &x
)) < 0) {
1968 FTPPerror(&gConn
, result
, 0, "Could not MLSD", item
);
1970 FTPPerror(&gConn
, result
, 0, "Could not MLST", item
);
1973 for (linePtr
= dirContents
.first
;
1975 linePtr
= nextLinePtr
)
1977 nextLinePtr
= linePtr
->next
;
1978 (void) fprintf(stdout
, "%s\n", linePtr
->line
);
1979 Trace(0, "%s\n", linePtr
->line
);
1984 for ( ; i
<argc
; i
++) {
1987 if ((result
= FTPListToMemory2(&gConn
, item
, &dirContents
, (mlsd
== 0) ? "-d" : "", 1, &x
)) < 0) {
1989 FTPPerror(&gConn
, result
, 0, "Could not MLSD", item
);
1991 FTPPerror(&gConn
, result
, 0, "Could not MLST", item
);
1994 for (linePtr
= dirContents
.first
;
1996 linePtr
= nextLinePtr
)
1998 nextLinePtr
= linePtr
->next
;
1999 (void) fprintf(stdout
, "%s\n", linePtr
->line
);
2000 Trace(0, "%s\n", linePtr
->line
);
2009 /* Create directories on the remote system. */
2011 MkdirCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2016 int recurseFlag
= kRecursiveNo
;
2018 ARGSUSED(gUnusedArg
);
2020 while ((opt
= Getopt(argc
, argv
, "p")) >= 0) {
2022 /* Try creating intermediate directories if they
2025 * For example if only /pub/stuff existed, and you
2026 * do a "mkdir -p /pub/stuff/a/b/c", the "a" and "b"
2027 * directories would also be created.
2029 recurseFlag
= kRecursiveYes
;
2031 PrintCmdUsage(cmdp
);
2036 for (i
=gOptInd
; i
<argc
; i
++) {
2037 result
= FTPMkdir(&gConn
, argv
[i
], recurseFlag
);
2039 FTPPerror(&gConn
, result
, kErrMKDFailed
, "Could not mkdir", argv
[i
]);
2042 /* Really should just flush only the modified directories... */
2050 OpenMsg(const char *const fmt
, ...)
2056 padlim
= (size_t) gScreenColumns
;
2057 if ((size_t) gScreenColumns
> (sizeof(buf
) - 1))
2058 padlim
= sizeof(buf
) - 1;
2061 #ifdef HAVE_VSNPRINTF
2062 len
= (size_t) vsnprintf(buf
, sizeof(buf
) - 1, fmt
, ap
);
2064 buf
[sizeof(buf
) - 1] = '\0';
2066 (void) vsprintf(buf
, fmt
, ap
);
2071 Trace(9, "%s\n", buf
);
2072 for (; len
< padlim
; len
++)
2077 (void) fprintf(stdout
, "\r%s", buf
);
2079 (void) fprintf(stdout
, "%s\r", buf
);
2081 (void) fflush(stdout
);
2088 NcFTPOpenPrintResponseProc(const FTPCIPtr cipUNUSED
, ResponsePtr rp
)
2090 gUnusedArg
= (cipUNUSED
!= NULL
);
2091 if ((rp
->printMode
& kResponseNoPrint
) != 0)
2094 if (rp
->code
== 331) /* Skip: "331 User name okay, need password." */
2097 /* This is only used to print errors. */
2101 PrintResp(&rp
->msg
);
2102 } /* NcFTPOpenPrintResponseProc */
2107 NcFTPOnConnectMessageProc(const FTPCIPtr cipUNUSED
, ResponsePtr rp
)
2109 gUnusedArg
= (cipUNUSED
!= NULL
);
2110 (void) printf("\n");
2111 PrintResp(&rp
->msg
);
2112 OpenMsg("Logging in...");
2113 } /* NcFTPOnConnectMessageProc */
2118 NcFTPOnLoginMessageProc(const FTPCIPtr cipUNUSED
, ResponsePtr rp
)
2120 gUnusedArg
= (cipUNUSED
!= NULL
);
2121 (void) printf("\n");
2122 PrintResp(&rp
->msg
);
2123 OpenMsg("Logging in...");
2124 } /* NcFTPOnLoginMessageProc */
2130 NcFTPRedialStatusProc(const FTPCIPtr cipUNUSED
, int mode
, int val
)
2132 gUnusedArg
= (cipUNUSED
!= NULL
);
2133 if (mode
== kRedialStatusDialing
) {
2135 OpenMsg("Redialing (try %d)...", val
);
2136 sleep(1); /* Give time for message to stay */
2138 } else if (mode
== kRedialStatusSleeping
) {
2139 OpenMsg("Sleeping %d seconds...", val
);
2141 } /* NcFTPRedialStatusProc */
2147 NcFTPGetPassphraseProc(const FTPCIPtr cip
, LineListPtr pwPrompt
, char *pass
, size_t dsize
)
2151 (void) printf("\nPassword requested by %s for user \"%s\".\n\n",
2156 for (lp
= pwPrompt
->first
; lp
!= NULL
; lp
= lp
->next
) {
2157 (void) printf(" %s\n", lp
->line
);
2159 (void) printf("\n");
2160 (void) gl_getpass("Password: ", pass
, (int) dsize
);
2161 } /* NcFTPGetPassphraseProc */
2166 /* Attempts to establish a new FTP connection to a remote host. */
2178 if (gConn
.firewallType
== kFirewallNotInUse
) {
2179 (void) STRNCPY(ohost
, gConn
.host
);
2180 OpenMsg("Resolving %s...", ohost
);
2181 if ((gLoadedBm
!= 0) && (gBm
.lastIP
[0] != '\0')) {
2182 result
= GetHostByName(ipstr
, sizeof(ipstr
), ohost
, 3);
2184 (void) STRNCPY(ipstr
, gBm
.lastIP
);
2187 result
= GetHostByName(ipstr
, sizeof(ipstr
), ohost
, 7);
2190 result
= GetHostByName(ipstr
, sizeof(ipstr
), ohost
, 10);
2193 (void) printf("\n");
2194 (void) printf("Unknown host \"%s\".\n", ohost
);
2197 (void) STRNCPY(gConn
.host
, ipstr
);
2198 OpenMsg("Connecting to %s...", ipstr
);
2200 OpenMsg("Connecting to %s via %s...", gConn
.host
, gConn
.firewallHost
);
2201 Trace(0, "Fw: %s Type: %d User: %s Pass: %s Port: %u\n",
2205 (gConn
.firewallPass
[0] == '\0') ? "(none)" : "********",
2208 Trace(0, "FwExceptions: %s\n", gFirewallExceptionList
);
2209 if (strchr(gLib
.ourHostName
, '.') == NULL
) {
2210 Trace(0, "NOTE: Your domain name could not be detected.\n");
2211 if (gConn
.firewallType
!= kFirewallNotInUse
) {
2212 Trace(0, " Make sure you manually add your domain name to firewall-exception-list.\n");
2217 if (gConn
.firewallPass
[0] == '\0') {
2218 switch (gConn
.firewallType
) {
2219 case kFirewallNotInUse
:
2221 case kFirewallUserAtSite
:
2223 case kFirewallLoginThenUserAtSite
:
2224 case kFirewallSiteSite
:
2225 case kFirewallOpenSite
:
2226 case kFirewallUserAtUserPassAtPass
:
2227 case kFirewallFwuAtSiteFwpUserPass
:
2228 case kFirewallUserAtSiteFwuPassFwp
:
2229 (void) printf("\n");
2230 (void) STRNCPY(prompt
, "Password for firewall user \"");
2231 (void) STRNCAT(prompt
, gConn
.firewallUser
);
2232 (void) STRNCAT(prompt
, "\" at ");
2233 (void) STRNCAT(prompt
, gConn
.firewallHost
);
2234 (void) STRNCAT(prompt
, ": ");
2235 (void) gl_getpass(prompt
, gConn
.firewallPass
, sizeof(gConn
.firewallPass
));
2240 if ((gConn
.user
[0] != '\0') && (strcmp(gConn
.user
, "anonymous") != 0) && (strcmp(gConn
.user
, "ftp") != 0)) {
2241 gConn
.passphraseProc
= NcFTPGetPassphraseProc
;
2244 /* Register our callbacks. */
2245 gConn
.printResponseProc
= NcFTPOpenPrintResponseProc
;
2246 gConn
.onConnectMsgProc
= NcFTPOnConnectMessageProc
;
2247 gConn
.onLoginMsgProc
= NcFTPOnLoginMessageProc
;
2248 gConn
.redialStatusProc
= NcFTPRedialStatusProc
;
2251 osigalrm
= NcSignal(SIGALRM
, (FTPSigProc
) SIG_IGN
);
2252 result
= FTPOpenHost(&gConn
);
2253 (void) NcSignal(SIGALRM
, osigalrm
);
2255 result
= FTPOpenHost(&gConn
);
2258 if (gConn
.firewallType
== kFirewallNotInUse
)
2259 (void) STRNCPY(gConn
.host
, ohost
); /* Put it back. */
2261 (void) time(&gBm
.lastCall
);
2262 LogOpen(gConn
.host
);
2263 OpenMsg("Logged in to %s.", gConn
.host
);
2264 (void) printf("\n");
2266 /* Remove callback. */
2267 gConn
.printResponseProc
= 0;
2269 /* Need to note what our "root" was before we change it. */
2270 if (gConn
.startingWorkingDirectory
== NULL
) {
2271 (void) STRNCPY(gRemoteCWD
, "/"); /* Guess! */
2273 (void) STRNCPY(gRemoteCWD
, gConn
.startingWorkingDirectory
);
2275 (void) STRNCPY(gStartDir
, gRemoteCWD
);
2277 /* If the bookmark specified a remote directory, change to it now. */
2278 if ((gLoadedBm
!= 0) && (gBm
.dir
[0] != '\0')) {
2279 result
= Chdirs(&gConn
, gBm
.dir
);
2281 FTPPerror(&gConn
, result
, kErrCWDFailed
, "Could not chdir to previous directory", gBm
.dir
);
2283 Trace(-1, "Current remote directory is %s.\n", gRemoteCWD
);
2286 /* If the bookmark specified a local directory, change to it now. */
2287 if ((gLoadedBm
!= 0) && (gBm
.ldir
[0] != '\0')) {
2288 (void) chdir(gBm
.ldir
);
2289 (void) STRNCPY(gPrevLocalCWD
, gLocalCWD
);
2290 if (FTPGetLocalCWD(gLocalCWD
, sizeof(gLocalCWD
)) != NULL
) {
2291 Trace(-1, "Current local directory is %s.\n", gLocalCWD
);
2295 /* Identify the FTP client type to the server. Most don't understand this yet. */
2296 if (gConn
.hasCLNT
!= kCommandNotAvailable
)
2297 (void) FTPCmd(&gConn
, "CLNT NcFTP %.5s %s", gVersion
+ 11, gOS
);
2300 FTPPerror(&gConn
, result
, 0, "Could not open host", gConn
.host
);
2303 /* Remove callback. */
2304 gConn
.printResponseProc
= 0;
2305 (void) printf("\n");
2312 /* Chooses a new remote system to connect to, and attempts to establish
2313 * a new FTP connection. This function is in charge of collecting the
2314 * information needed to do the open, and then doing it.
2317 OpenCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2326 int directoryURL
= 0;
2331 ARGSUSED(gUnusedArg
);
2335 InitConnectionInfo();
2337 /* Need to find the host argument first. */
2339 while ((c
= Getopt(argc
, argv
, "aP:u:p:J:rd:g:")) > 0) switch(c
) {
2341 uOptInd
= gOptInd
+ 1;
2347 if (gOptInd
< argc
) {
2348 (void) STRNCPY(gConn
.host
, argv
[gOptInd
]);
2349 (void) STRNCPY(url
, argv
[gOptInd
]);
2350 } else if (uOptInd
> argc
) {
2351 /* Special hack for v2.4.2 compatibility */
2352 (void) STRNCPY(gConn
.host
, argv
[argc
- 1]);
2353 (void) STRNCPY(url
, argv
[argc
- 1]);
2357 PrintCmdUsage(cmdp
);
2358 } else if (RunBookmarkEditor(gConn
.host
, sizeof(gConn
.host
)) == 0) {
2359 if (gConn
.host
[0] != '\0') {
2361 /* okay, now fall through */
2365 } else if (PrintHosts() <= 0) {
2366 (void) printf("You haven't bookmarked any FTP sites.\n");
2367 (void) printf("Before closing a site, you can use the \"bookmark\" command to save the current\nhost and directory for next time.\n");
2370 (void) printf("\nTo use a bookmark, use the \"open\" command with the name of the bookmark.\n");
2375 InitLineList(&cdlist
);
2377 if (GetBookmark(gConn
.host
, &gBm
) >= 0) {
2379 (void) STRNCPY(gConn
.host
, gBm
.name
);
2380 (void) STRNCPY(gConn
.user
, gBm
.user
);
2381 (void) STRNCPY(gConn
.pass
, gBm
.pass
);
2382 (void) STRNCPY(gConn
.acct
, gBm
.acct
);
2383 gConn
.hasSIZE
= gBm
.hasSIZE
;
2384 gConn
.hasMDTM
= gBm
.hasMDTM
;
2385 gConn
.hasUTIME
= gBm
.hasUTIME
;
2386 gConn
.port
= gBm
.port
;
2388 /* Note: Version 3 only goes off of the
2389 * global "gDataPortMode" setting instead of
2390 * setting the dataPortMode on a per-site
2393 gConn
.hasPASV
= gBm
.hasPASV
;
2395 SetBookmarkDefaults(&gBm
);
2397 memcpy(&gTmpURLConn
, &gConn
, sizeof(gTmpURLConn
));
2398 rc
= DecodeDirectoryURL(&gTmpURLConn
, url
, &cdlist
, urlfile
, sizeof(urlfile
));
2399 if (rc
== kMalformedURL
) {
2400 (void) fprintf(stdout
, "Malformed URL: %s\n", url
);
2401 DisposeLineListContents(&cdlist
);
2403 } else if (rc
== kNotURL
) {
2407 if (urlfile
[0] != '\0') {
2408 /* It was obviously not a directory */
2409 (void) fprintf(stdout
, "Use ncftpget or ncftpput to handle file URLs.\n");
2410 DisposeLineListContents(&cdlist
);
2413 memcpy(&gConn
, &gTmpURLConn
, sizeof(gConn
));
2418 if (MayUseFirewall(gConn
.host
, gFirewallType
, gFirewallExceptionList
) != 0) {
2419 gConn
.firewallType
= gFirewallType
;
2420 (void) STRNCPY(gConn
.firewallHost
, gFirewallHost
);
2421 (void) STRNCPY(gConn
.firewallUser
, gFirewallUser
);
2422 (void) STRNCPY(gConn
.firewallPass
, gFirewallPass
);
2423 gConn
.firewallPort
= gFirewallPort
;
2427 while ((c
= Getopt(argc
, argv
, "aP:u:p:J:j:rd:g:")) > 0) switch(c
) {
2430 (void) STRNCPY(gConn
.acct
, gOptArg
);
2433 (void) STRNCPY(gConn
.user
, "anonymous");
2434 (void) STRNCPY(gConn
.pass
, "");
2435 (void) STRNCPY(gConn
.acct
, "");
2438 gConn
.port
= atoi(gOptArg
);
2441 if (uOptInd
<= argc
)
2442 (void) STRNCPY(gConn
.user
, gOptArg
);
2445 (void) STRNCPY(gConn
.pass
, gOptArg
); /* Don't recommend doing this! */
2448 /* redial is on by default */
2457 gConn
.redialDelay
= n
;
2460 PrintCmdUsage(cmdp
);
2461 DisposeLineListContents(&cdlist
);
2465 if (uOptInd
> argc
) {
2466 (void) STRNCPY(prompt
, "Username at ");
2467 (void) STRNCAT(prompt
, gConn
.host
);
2468 (void) STRNCAT(prompt
, ": ");
2469 (void) gl_getpass(prompt
, gConn
.user
, sizeof(gConn
.user
));
2473 if ((rc
>= 0) && (directoryURL
!= 0)) {
2474 for (lp
= cdlist
.first
; lp
!= NULL
; lp
= lp
->next
) {
2475 rc
= FTPChdir(&gConn
, lp
->line
);
2477 FTPPerror(&gConn
, rc
, kErrCWDFailed
, "Could not chdir to", lp
->line
);
2481 rc
= FTPGetCWD(&gConn
, gRemoteCWD
, sizeof(gRemoteCWD
));
2483 FTPPerror(&gConn
, rc
, kErrPWDFailed
, NULL
, NULL
);
2485 (void) printf("Current remote directory is %s.\n", gRemoteCWD
);
2488 DisposeLineListContents(&cdlist
);
2494 /* View a remote file through the users $PAGER. */
2496 PageCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2500 FILE *volatile stream
;
2501 #if defined(WIN32) || defined(_WINDOWS)
2504 vsigproc_t osigpipe
, osigint
;
2507 ARGSUSED(gUnusedArg
);
2508 stream
= OpenPager();
2509 if (stream
== NULL
) {
2513 #if defined(WIN32) || defined(_WINDOWS)
2516 #ifdef HAVE_SIGSETJMP
2517 osigpipe
= osigint
= (sigproc_t
) 0;
2518 sj
= sigsetjmp(gCancelJmp
, 1);
2519 #else /* HAVE_SIGSETJMP */
2520 osigpipe
= osigint
= (sigproc_t
) 0;
2521 sj
= setjmp(gCancelJmp
);
2522 #endif /* HAVE_SIGSETJMP */
2525 /* Caught a signal. */
2526 (void) NcSignal(SIGPIPE
, (FTPSigProc
) SIG_IGN
);
2528 (void) NcSignal(SIGPIPE
, osigpipe
);
2529 (void) NcSignal(SIGINT
, osigint
);
2530 (void) fprintf(stderr
, "Canceled.\n");
2531 Trace(0, "Canceled because of signal %d.\n", gGotSig
);
2535 osigpipe
= NcSignal(SIGPIPE
, Cancel
);
2536 osigint
= NcSignal(SIGINT
, Cancel
);
2542 for (i
=1; i
<argc
; i
++) {
2543 result
= FTPGetOneFile2(&gConn
, argv
[i
], NULL
, kTypeAscii
, _fileno(stream
), kResumeNo
, kAppendNo
);
2545 if (errno
!= EPIPE
) {
2548 FTPPerror(&gConn
, result
, kErrCouldNotStartDataTransfer
, argv
[0], argv
[i
]);
2554 #if defined(WIN32) || defined(_WINDOWS)
2557 (void) NcSignal(SIGPIPE
, (FTPSigProc
) SIG_IGN
);
2559 (void) NcSignal(SIGPIPE
, osigpipe
);
2560 (void) NcSignal(SIGINT
, osigint
);
2569 NcFTPConfirmResumeUploadProc(
2570 const char *volatile localpath
,
2571 volatile longest_int localsize
,
2572 volatile time_t localmtime
,
2573 const char *volatile *remotepath
,
2574 volatile longest_int remotesize
,
2575 volatile time_t remotemtime
,
2576 volatile longest_int
*volatile startPoint
)
2578 int zaction
= kConfirmResumeProcSaidBestGuess
;
2579 char tstr
[80], ans
[32];
2580 static char newname
[128]; /* arrggh... static. */
2582 if (gResumeAnswerAll
!= kConfirmResumeProcNotUsed
)
2583 return (gResumeAnswerAll
);
2585 if (gAutoResume
!= 0)
2586 return (kConfirmResumeProcSaidBestGuess
);
2588 printf("\nThe remote file \"%s\" already exists.\n", *remotepath
);
2590 if ((localmtime
!= kModTimeUnknown
) && (localsize
!= kSizeUnknown
)) {
2591 (void) strftime(tstr
, sizeof(tstr
) - 1, "%c", localtime((time_t *) &localmtime
));
2593 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2594 "\tLocal: %12lld bytes, dated %s.\n",
2595 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2596 "\tLocal: %12qd bytes, dated %s.\n",
2597 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2598 "\tLocal: %12I64d bytes, dated %s.\n",
2600 "\tLocal: %12ld bytes, dated %s.\n",
2605 if ((remotemtime
== localmtime
) && (remotesize
== localsize
)) {
2606 (void) printf("\t(Files are identical, skipped)\n\n");
2607 return (kConfirmResumeProcSaidSkip
);
2609 } else if (localsize
!= kSizeUnknown
) {
2611 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2612 "\tLocal: %12lld bytes, date unknown.\n",
2613 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2614 "\tLocal: %12qd bytes, date unknown.\n",
2615 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2616 "\tLocal: %12I64d bytes, date unknown.\n",
2618 "\tLocal: %12ld bytes, date unknown.\n",
2622 } else if (localmtime
!= kModTimeUnknown
) {
2623 (void) strftime(tstr
, sizeof(tstr
) - 1, "%c", localtime((time_t *) &localmtime
));
2625 "\tLocal: size unknown, dated %s.\n",
2630 tstr
[sizeof(tstr
) - 1] = '\0';
2631 if ((remotemtime
!= kModTimeUnknown
) && (remotesize
!= kSizeUnknown
)) {
2632 (void) strftime(tstr
, sizeof(tstr
) - 1, "%c", localtime((time_t *) &remotemtime
));
2634 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2635 "\tRemote: %12lld bytes, dated %s.\n",
2636 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2637 "\tRemote: %12qd bytes, dated %s.\n",
2638 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2639 "\tRemote: %12I64d bytes, dated %s.\n",
2641 "\tRemote: %12ld bytes, dated %s.\n",
2646 } else if (remotesize
!= kSizeUnknown
) {
2648 #if defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_LLD)
2649 "\tRemote: %12lld bytes, date unknown.\n",
2650 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_QD)
2651 "\tRemote: %12qd bytes, date unknown.\n",
2652 #elif defined(HAVE_LONG_LONG) && defined(PRINTF_LONG_LONG_I64D)
2653 "\tRemote: %12I64d bytes, date unknown.\n",
2655 "\tRemote: %12ld bytes, date unknown.\n",
2659 } else if (remotemtime
!= kModTimeUnknown
) {
2660 (void) strftime(tstr
, sizeof(tstr
) - 1, "%c", localtime((time_t *) &remotemtime
));
2662 "\tRemote: size unknown, dated %s.\n",
2669 (void) memset(ans
, 0, sizeof(ans
));
2671 (void) printf("\t[O]verwrite?");
2672 if ((gConn
.hasREST
== kCommandAvailable
) && (remotesize
< localsize
))
2673 printf(" [R]esume?");
2674 printf(" [A]ppend to? [S]kip? [N]ew Name?\n");
2675 (void) printf("\t[O!]verwrite all?");
2676 if ((gConn
.hasREST
== kCommandAvailable
) && (remotesize
< localsize
))
2677 printf(" [R!]esume all?");
2678 printf(" [S!]kip all? [C]ancel > ");
2679 (void) fgets(ans
, sizeof(ans
) - 1, stdin
);
2680 switch ((int) ans
[0]) {
2685 zaction
= kConfirmResumeProcSaidCancel
;
2690 zaction
= kConfirmResumeProcSaidOverwrite
;
2694 if (gConn
.hasREST
!= kCommandAvailable
) {
2695 printf("\tResume is not available on this server.\n\n");
2698 } else if (remotesize
> localsize
) {
2699 printf("\tCannot resume when remote file is already larger than the local file.\n\n");
2702 } else if (remotesize
== localsize
) {
2703 printf("\tRemote file is already the same size as the local file.\n\n");
2708 *startPoint
= remotesize
;
2709 zaction
= kConfirmResumeProcSaidResume
;
2710 if (OneTimeMessage("auto-resume") != 0) {
2711 printf("\n\tNOTE: If you want NcFTP to guess automatically, \"set auto-resume yes\"\n\n");
2717 zaction
= kConfirmResumeProcSaidSkip
;
2723 zaction
= kConfirmResumeProcSaidOverwrite
;
2729 zaction
= kConfirmResumeProcSaidAppend
;
2734 zaction
= kConfirmResumeProcSaidBestGuess
;
2743 if (ans
[0] == 'N') {
2744 (void) memset(newname
, 0, sizeof(newname
));
2745 printf("\tSave as: ");
2747 (void) fgets(newname
, sizeof(newname
) - 1, stdin
);
2748 newname
[strlen(newname
) - 1] = '\0';
2749 if (newname
[0] == '\0') {
2751 printf("Skipped %s.\n", localpath
);
2752 zaction
= kConfirmResumeProcSaidSkip
;
2754 *remotepath
= newname
;
2759 gResumeAnswerAll
= zaction
;
2761 } /* NcFTPConfirmResumeUploadProc */
2766 /* Upload files to the remote system, permissions permitting. */
2768 PutCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2772 int recurseFlag
= kRecursiveNo
;
2773 int appendFlag
= kAppendNo
;
2774 const char *dstdir
= NULL
;
2778 int xtype
= gBm
.xferType
;
2780 int deleteFlag
= kDeleteNo
;
2781 int resumeFlag
= kResumeYes
;
2784 ConfirmResumeUploadProc confirmProc
;
2786 confirmProc
= NcFTPConfirmResumeUploadProc
;
2787 gResumeAnswerAll
= kConfirmResumeProcNotUsed
; /* Ask at least once each time */
2788 ARGSUSED(gUnusedArg
);
2790 while ((opt
= Getopt(argc
, argv
, "AafZzrRD")) >= 0) switch (opt
) {
2795 /* Append to remote files, instead of truncating
2798 appendFlag
= kAppendYes
;
2802 /* Do not try to resume a download, even if it
2803 * appeared that some of the file was transferred
2806 resumeFlag
= kResumeNo
;
2807 confirmProc
= NoConfirmResumeUploadProc
;
2810 /* Special flag that lets you specify the
2811 * destination file. Normally a "put" will
2812 * write the file by the same name as the
2813 * local file's basename.
2819 recurseFlag
= kRecursiveYes
;
2820 /* If the item is a directory, get the
2821 * directory and all its contents.
2823 recurseFlag
= kRecursiveYes
;
2826 /* You can delete the local file after
2827 * you uploaded it successfully by using
2828 * the -DD option. It requires two -D's
2829 * to minimize the odds of accidentally
2830 * using a single -D.
2835 PrintCmdUsage(cmdp
);
2840 deleteFlag
= kDeleteYes
;
2842 if (renameMode
!= 0) {
2843 if (gOptInd
> (argc
- 2)) {
2844 PrintCmdUsage(cmdp
);
2845 (void) fprintf(stderr
, "\nFor put with rename, try \"put -z local-path-name remote-path-name\".\n");
2848 osigint
= NcSignal(SIGINT
, XferCanceller
);
2849 rc
= FTPPutOneFile3(&gConn
, argv
[gOptInd
], argv
[gOptInd
+ 1], xtype
, (-1), appendFlag
, NULL
, NULL
, resumeFlag
, deleteFlag
, confirmProc
, 0);
2851 FTPPerror(&gConn
, rc
, kErrCouldNotStartDataTransfer
, "put", argv
[gOptInd
+ 1]);
2853 osigint
= NcSignal(SIGINT
, XferCanceller
);
2854 for (i
=gOptInd
; i
<argc
; i
++) {
2855 doGlob
= (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
;
2856 STRNCPY(pattern
, argv
[i
]);
2857 StrRemoveTrailingSlashes(pattern
);
2858 rc
= FTPPutFiles3(&gConn
, pattern
, dstdir
, recurseFlag
, doGlob
, xtype
, appendFlag
, NULL
, NULL
, resumeFlag
, deleteFlag
, confirmProc
, 0);
2860 FTPPerror(&gConn
, rc
, kErrCouldNotStartDataTransfer
, "put", argv
[i
]);
2864 /* Really should just flush only the modified directories... */
2867 (void) NcSignal(SIGINT
, osigint
);
2868 (void) fflush(stdin
);
2874 /* Displays the current remote working directory path. */
2876 PwdCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2882 ARGSUSED(gUnusedArg
);
2883 #ifdef USE_WHAT_SERVER_SAYS_IS_CWD
2884 result
= FTPGetCWD(&gConn
, gRemoteCWD
, sizeof(gRemoteCWD
));
2885 CurrentURL(url
, sizeof(url
), 0);
2887 FTPPerror(&gConn
, result
, kErrPWDFailed
, "Could not get remote working directory", NULL
);
2889 Trace(-1, "%s\n", url
);
2891 #else /* USE_CLIENT_SIDE_CALCULATED_CWD */
2893 /* Display the current working directory, as
2896 CurrentURL(url
, sizeof(url
), 0);
2897 Trace(-1, " %s\n", url
);
2898 olddir
[sizeof(olddir
) - 2] = '\0';
2899 STRNCPY(olddir
, gRemoteCWD
);
2901 /* Now see what the server reports as the CWD.
2902 * Because of symlinks, it could be different
2903 * from what we are using.
2905 result
= FTPGetCWD(&gConn
, gRemoteCWD
, sizeof(gRemoteCWD
));
2906 if ((result
== kNoErr
) && (strcmp(gRemoteCWD
, olddir
) != 0)) {
2907 Trace(-1, "This URL is also valid on this server:\n");
2908 CurrentURL(url
, sizeof(url
), 0);
2909 Trace(-1, " %s\n", url
);
2910 if (olddir
[sizeof(olddir
) - 2] == '\0') {
2911 /* Go back to using the non-resolved version. */
2912 STRNCPY(gRemoteCWD
, olddir
);
2921 /* Sets a flag so that the command shell exits. */
2923 QuitCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2925 ARGSUSED(gUnusedArg
);
2926 gDoneApplication
= 1;
2932 /* Send a command string to the FTP server, verbatim. */
2934 QuoteCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2939 ARGSUSED(gUnusedArg
);
2940 (void) STRNCPY(cmdbuf
, argv
[1]);
2941 for (i
=2; i
<argc
; i
++) {
2942 (void) STRNCAT(cmdbuf
, " ");
2943 (void) STRNCAT(cmdbuf
, argv
[i
]);
2945 (void) FTPCmd(&gConn
, "%s", cmdbuf
);
2946 PrintResp(&gConn
.lastFTPCmdResultLL
);
2952 /* Expands a remote regex. Mostly a debugging command. */
2954 RGlobCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2962 ARGSUSED(gUnusedArg
);
2963 for (i
=1; i
<argc
; i
++) {
2965 result
= FTPRemoteGlob(&gConn
, &ll
, argv
[i
], (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
);
2967 FTPPerror(&gConn
, result
, kErrGlobFailed
, "remote glob", argv
[i
]);
2969 for (lp
= ll
.first
; lp
!= NULL
; lp
= lp
->next
) {
2970 if (lp
->line
!= NULL
) {
2973 (void) printf("%s", lp
->line
);
2978 DisposeLineListContents(&ll
);
2980 (void) printf("\n");
2986 /* Renames a remote file. */
2988 RenameCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
2992 ARGSUSED(gUnusedArg
);
2993 result
= FTPRename(&gConn
, argv
[1], argv
[2]);
2995 FTPPerror(&gConn
, result
, kErrRenameFailed
, "rename", argv
[1]);
2997 /* Really should just flush only the modified directories... */
3005 /* Removes a directory on the remote host. */
3007 RmdirCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
3011 int recursive
= kRecursiveNo
;
3013 ARGSUSED(gUnusedArg
);
3015 while ((c
= Getopt(argc
, argv
, "rf")) > 0) switch(c
) {
3017 recursive
= kRecursiveYes
;
3023 PrintCmdUsage(cmdp
);
3026 for (i
=gOptInd
; i
<argc
; i
++) {
3027 result
= FTPRmdir(&gConn
, argv
[i
], recursive
, (aip
->noglobargv
[i
] != 0) ? kGlobNo
: kGlobYes
);
3029 FTPPerror(&gConn
, result
, kErrRMDFailed
, "rmdir", argv
[i
]);
3032 /* Really should just flush only the modified directories... */
3039 /* Asks the remote server for help on how to use its server. */
3041 RmtHelpCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
3047 ARGSUSED(gUnusedArg
);
3049 result
= FTPRemoteHelp(&gConn
, NULL
, &ll
);
3051 FTPPerror(&gConn
, result
, kErrHELPFailed
, "HELP failed", NULL
);
3053 for (lp
= ll
.first
; lp
!= NULL
; lp
= lp
->next
)
3054 (void) printf("%s\n", lp
->line
);
3056 DisposeLineListContents(&ll
);
3058 for (i
=1; i
<argc
; i
++) {
3059 result
= FTPRemoteHelp(&gConn
, argv
[i
], &ll
);
3061 FTPPerror(&gConn
, result
, kErrHELPFailed
, "HELP failed for", argv
[i
]);
3063 for (lp
= ll
.first
; lp
!= NULL
; lp
= lp
->next
)
3064 (void) printf("%s\n", lp
->line
);
3066 DisposeLineListContents(&ll
);
3074 /* Show and/or change customizable program settings. These changes are saved
3075 * at the end of the program's run.
3078 SetCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
3080 ARGSUSED(gUnusedArg
);
3086 Set(argv
[1], argv
[2]);
3092 /* Local shell escape. */
3094 ShellCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
3096 #if defined(WIN32) || defined(_WINDOWS)
3103 osigint
= NcSignal(SIGINT
, (FTPSigProc
) SIG_IGN
);
3104 ARGSUSED(gUnusedArg
);
3106 if (pid
< (pid_t
) 0) {
3108 } else if (pid
== 0) {
3109 cp
= strrchr(gShell
, '/');
3111 cp
= gShell
; /* bug */
3115 execl(gShell
, cp
, NULL
);
3119 execvp(argv
[1], (char **) argv
+ 1);
3127 if ((waitpid(pid
, &status
, 0) < 0) && (errno
!= EINTR
))
3130 if ((wait(&status
) < 0) && (errno
!= EINTR
))
3133 if (WIFEXITED(status
) || WIFSIGNALED(status
))
3137 (void) NcSignal(SIGINT
, osigint
);
3144 /* Send a command string to the FTP server, verbatim. */
3146 SiteCmd(const int argc
, const char **const argv
, const CommandPtr cmdp
, const ArgvInfoPtr aip
)
3151 ARGSUSED(gUnusedArg
);
3152 (void) STRNCPY(cmdbuf
, "SITE");
3153 for (i
=1; i
<argc
; i
++) {
3154 (void) STRNCAT(cmdbuf
, " ");
3155 (void) STRNCAT(cmdbuf
, argv
[i
]);
3157 (void) FTPCmd(&gConn
, "%s", cmdbuf
);
3158 PrintResp(&gConn
.lastFTPCmdResultLL
);
3165 GetStartSpoolDate(const char *s
)
3170 int toff
, n
, c
, hr
, min
;
3174 cp
= strchr(s2
, ':');
3175 if ((s2
[0] == 'n') || (s2
[0] == '+')) {
3176 /* "now + XX hours" or
3179 cp
= strchr(s2
, '+');
3181 return ((time_t) -1);
3185 (void) sscanf(cp
, "%d%n", &toff
, &n
);
3186 if ((n
<= 0) || (toff
<= 0))
3187 return ((time_t) -1);
3189 while ((*cp
!= '\0') && (!isalpha(*cp
)))
3196 } else if (c
== 'm') {
3199 } else if (c
== 'h') {
3202 } else if (c
== 'd') {
3206 /* unrecognized unit */
3207 return ((time_t) -1);
3210 when
= now
+ (time_t) toff
;
3211 } else if (cp
!= NULL
) {
3212 /* HH:MM, as in "23:38" */
3214 ltp
= localtime(&now
);
3216 return ((time_t) -1); /* impossible */
3221 (void) sscanf(s2
, "%d%d", &hr
, &min
);
3222 if ((hr
< 0) || (min
< 0))
3223 return ((time_t) -1);
3227 if ((when
== (time_t) -1) || (when
== (time_t) 0))
3230 when
+= (time_t) 86400;
3235 } /* GetStartSpoolDate */
3242 if (CanSpool() < 0) {
3243 #if defined(WIN32) || defined(_WINDOWS)
3244 (void) printf("Sorry, spooling isn't allowed until you run Setup.exe.\n");
3246 (void) printf("Sorry, spooling isn't allowed because this user requires that the NCFTPDIR\nenvironment variable be set to a directory to write datafiles to.\n");
3249 } else if (HaveSpool() == 0) {
3250 #if defined(WIN32) || defined(_WINDOWS)
3251 (void) printf("Sorry, the \"ncftpbatch\" program could not be found.\nPlease re-run Setup to correct this problem.\n");
3253 (void) printf("Sorry, the \"ncftpbatch\" program could not be found.\nThis program must be installed and in your PATH in order to use this feature.\n");