From: Benjamin Lee McQueen Subject: Re: ftp(1): parse fractional seconds in MDTM responses To: Stuart Henderson , Phillip Guenther Cc: obsd-tech Date: Thu, 12 Feb 2026 17:30:31 -0600 sorry for the bad formatting and incompatible diff, i'll put the new one here On 260212-123226, Stuart Henderson wrote: >also, ftp(1) changes (at least outside of !small) need testing on >ramdisks, see release(8) for info on building just to clarify, you want me to build and test the full installation media? i don't know if i have the time currently to do this. can somebody else with a buildenv test this? either way here's the diff: --- util.c.orig Thu Feb 12 17:02:44 2026 +++ util.c Thu Feb 12 17:03:59 2026 @@ -602,16 +602,16 @@ /* * determine last modification time (in GMT) of remote file */ -time_t +struct timespec remotemodtime(const char *file, int noisy) { - int overbose; - time_t rtime; - int ocode; + struct timespec result; + int ocode, overbose; overbose = verbose; ocode = code; - rtime = -1; + result.tv_sec = -1; + result.tv_nsec = 0; #ifndef SMALL if (!debug) #endif /* !SMALL */ @@ -619,12 +619,14 @@ if (command("MDTM %s", file) == COMPLETE) { struct tm timebuf; int yy, mo, day, hour, min, sec; + char *cp, *ep; + long frac, nsec; + int i, multiplier, num_digits; /* * time-val = 14DIGIT [ "." 1*DIGIT ] * YYYYMMDDHHMMSS[.sss] * mdtm-response = "213" SP time-val CRLF / error-response */ - /* TODO: parse .sss as well, use timespecs. */ char *timestr = reply_string; /* Repair `19%02d' bug on server side */ @@ -649,15 +651,38 @@ timebuf.tm_mon = mo - 1; timebuf.tm_year = yy - 1900; timebuf.tm_isdst = -1; - rtime = mktime(&timebuf); - if (rtime == -1 && (noisy + + cp = strchr(reply_string, '.'); + if (cp != NULL) { + cp++; + ep = cp; + while (isdigit((unsigned char)*ep)) + ep++; + num_digits = ep - cp; + if (num_digits == 0 || num_digits > 9) + nsec = 0; + else { + frac = 0; + for (i = 0; i < num_digits; i++) + frac = frac * 10 + (cp[i] - '0'); + multiplier = 1; + for (i = num_digits; i < 9; i++) + multiplier *= 10; + nsec = frac * multiplier; + } + } else + nsec = 0; + + result.tv_sec = mktime(&timebuf); + result.tv_nsec = nsec; + if (result.tv_sec == -1 && (noisy #ifndef SMALL || debug #endif /* !SMALL */ )) fprintf(ttyout, "Can't convert %s to a time.\n", reply_string); else - rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */ + result.tv_sec += timebuf.tm_gmtoff; /* conv. local -> GMT */ } else if (noisy #ifndef SMALL && !debug @@ -667,9 +692,9 @@ fputc('\n', ttyout); } verbose = overbose; - if (rtime == -1) + if (result.tv_sec == -1) code = ocode; - return (rtime); + return (result); } /* --- cmds.c.orig Thu Feb 12 17:09:54 2026 +++ cmds.c Wed Feb 11 18:57:19 2026 @@ -1594,7 +1594,7 @@ code = -1; return; } - mtime = remotemodtime(argv[1], 1); + mtime = remotemodtime(argv[1], 1).tv_sec; if (mtime != -1) fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); code = mtime; --- ftp.c.orig Thu Feb 12 17:13:56 2026 +++ ftp.c Wed Feb 11 18:56:48 2026 @@ -1204,7 +1204,7 @@ if (bytes > 0) ptransfer(0); if (preserve && (closefunc == fclose)) { - mtime = remotemodtime(remote, 0); + mtime = remotemodtime(remote, 0).tv_sec; if (mtime != -1) { struct timespec times[2]; --- small.c.orig Thu Feb 12 17:16:29 2026 +++ small.c Wed Feb 11 18:56:17 2026 @@ -269,7 +269,7 @@ if (ret == 0) { time_t mtime; - mtime = remotemodtime(argv[1], 0); + mtime = remotemodtime(argv[1], 0).tv_sec; if (mtime == -1) goto freegetit; if (stbuf.st_mtime >= mtime) { --- extern.h.orig Thu Feb 12 17:17:11 2026 +++ extern.h Wed Feb 11 19:42:14 2026 @@ -101,7 +101,7 @@ const char *, int, int); char *remglob(char **, int, char **); off_t remotesize(const char *, int); -time_t remotemodtime(const char *, int); +struct timespec remotemodtime(const char *, int); void reset(int, char **); void rmthelp(int, char **); void sethash(int, char **); :wq ben