I sent this out a four months ago but got no response. I am sending the whole thing again after fixing a missing bit of whitespace in an error message.
--- The patch below implements the following changes to ftp(1), beginning with the least disruptive and ending with the most disruptive. 1) Change ftp->newer's behavior to match ftp->reget's behavior when stat(2) on the local file fails: do the file transfer from offset 0 instead of from whatever offset is stored in the global variable restart_point. 2) Document ftp->newer's behavior of declining to do the transfer if the modification time of the remote file cannot be determined. Make ftp->newer emit an alert when this happens instead of being silent. Document that ftp -C transfers files using the logic of ftp->reget (unless I've misread the code, that's literally what happens in fetch.c) 3) Make the options -c and -n of ftp>mget for using reget or newer exclusive instead of allowing ftp>mget -ncncncncccnc with the last option winning. 4) Improve ftp>reget's behavior so that a local file is considered to be a partially complete transfer only if its modification time is determined to be more recent than the modification time of the remote file. If so, the transfer is continued from the interruption point, otherwise, the transfer is restarted from the beginning, overwriting the local file. 5) Change ftp->reget's behavior to match ftp->newer's behaviour when the modification time of the remote file cannot be determined: decline the transfer (and alert!) instead of doing the transer anyway. The actual diff implements these as follows. 1-5) a rewrite of the restartit conditional in getit() in small.c 3) Simple change to mget() in small.c 2-5) appropriate changes to ftp.1 Additionally, I enacted a few style improvements before the restartit block in getit() in small.c: I removed some unnecessary braces, eliminated a '!' from a boolean test, and changed an increment of the 0-initialized variable loc to an explict assignment of 1. --Vladimir Sotirov Index: ftp.1 =================================================================== RCS file: /cvs/src/usr.bin/ftp/ftp.1,v retrieving revision 1.101 diff -u -p -r1.101 ftp.1 --- ftp.1 5 Nov 2015 16:25:57 -0000 1.101 +++ ftp.1 24 Jun 2016 09:26:03 -0000 @@ -123,7 +123,9 @@ to bypass the normal login procedure and Continue a previously interrupted file transfer. .Nm will continue transferring from an offset equal to the length of -.Ar file . +.Ar file +using the logic of +.Ic reget . .Pp Resuming HTTP(S) transfers are only supported if the remote server supports the @@ -628,7 +630,8 @@ on the remote machine. A synonym for .Ic mls . .It Xo Ic mget -.Op Fl cnr +.Op Fl r +.Op Fl c | n .Op Fl d Ar depth .Ar remote-files .Xc @@ -746,8 +749,9 @@ directories. A synonym for .Ic mput . .It Ic newer Ar remote-file Op Ar local-file -Get the file only if the modification time of the remote file is more -recent than the file on the current system. +Get the file only if the modification time of the remote file can be +determined and is more +recent than the modification time of the file on the current system. If the file does not exist on the current system, the remote file is considered .Ic newer . @@ -1073,10 +1077,12 @@ The arguments specified are sent, verbat A synonym for .Ic get . .It Ic reget Ar remote-file Op Ar local-file -Reget acts like get, except that if +Reget acts like get, except that it transfers the +.Ar remote-file +only if its modification time can be determined, in which case if .Ar local-file exists and is -smaller than +smaller and more recent than .Ar remote-file , .Ar local-file is presumed to be Index: small.c =================================================================== RCS file: /cvs/src/usr.bin/ftp/small.c,v retrieving revision 1.6 diff -u -p -r1.6 small.c --- small.c 25 May 2016 15:36:01 -0000 1.6 +++ small.c 24 Jun 2016 09:26:03 -0000 @@ -215,7 +215,7 @@ getit(int argc, char *argv[], int restar if (argc == 2) { argc++; argv[2] = argv[1]; - loc++; + loc = 1; } #ifndef SMALL if (argc < 2 && !another(&argc, &argv, "remote-file")) @@ -238,16 +238,14 @@ usage: if (loc && mcase) { char *tp = argv[1], *tp2, tmpbuf[PATH_MAX]; - while (*tp && !islower((unsigned char)*tp)) { + while (*tp && !islower((unsigned char)*tp)) tp++; - } - if (!*tp) { + if (*tp == '\0') { tp = argv[2]; tp2 = tmpbuf; while ((*tp2 = *tp) != '\0') { - if (isupper((unsigned char)*tp2)) { + if (isupper((unsigned char)*tp2)) *tp2 = tolower((unsigned char)*tp2); - } tp++; tp2++; } @@ -262,26 +260,29 @@ usage: if (restartit) { struct stat stbuf; int ret; + time_t mtime; + mtime = remotemodtime(argv[1], 0); ret = stat(argv[2], &stbuf); - if (restartit == 1) { - restart_point = (ret < 0) ? 0 : stbuf.st_size; - } else { - if (ret == 0) { - time_t mtime; - - mtime = remotemodtime(argv[1], 0); - if (mtime == -1) - goto freegetit; - if (stbuf.st_mtime >= mtime) { - rval = 1; - fprintf(ttyout, - "Local file \"%s\" is newer "\ - "than remote file \"%s\".\n", - argv[2], argv[1]); - goto freegetit; - } - } + + if (mtime == -1) { + rval = 1; + fprintf(ttyout, + "Modifcation time of remote file " + "\"%s\" could not be determined.\n", + argv[1]); + goto freegetit; + } else if (ret == -1 || mtime > stbuf.st_mtime) + restart_point = 0; + else if (restartit == 1) + restart_point = stbuf.st_size; + else { + rval = 1; + fprintf(ttyout, + "Local file \"%s\" is newer " + "than remote file \"%s\".\n", + argv[2], argv[1]); + goto freegetit; } } #endif /* !SMALL */ @@ -345,6 +346,8 @@ mget(int argc, char *argv[]) while ((ch = getopt(argc, argv, "cd:nr")) != -1) { switch(ch) { case 'c': + if (restartit) + goto usage; restartit = 1; break; case 'd': @@ -357,6 +360,8 @@ mget(int argc, char *argv[]) } break; case 'n': + if (restartit) + goto usage; restartit = -1; break; case 'r': @@ -369,8 +374,8 @@ mget(int argc, char *argv[]) if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) { usage: - fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n", - argv[0]); + fprintf(ttyout, "usage: %s [-r] [-c | -n] [-d depth] remote-files\n", + argv[0]); code = -1; return; }