Sunday, February 22, 2026

Re: ftp(1): parse fractional seconds in MDTM responses

? cmds.d
? cmdtab.d
? complete.d
? cookie.d
? domacro.d
? fetch.d
? ftp
? ftp.d
? list.d
? main.d
? ruserpass.d
? small.d
? stringlist.d
? util.d
Index: cmds.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/cmds.c,v
diff -u -p -u -p -r1.85 cmds.c
--- cmds.c 8 Mar 2023 04:43:11 -0000 1.85
+++ cmds.c 18 Feb 2026 22:48:04 -0000
@@ -1587,19 +1587,39 @@ sizecmd(int argc, char *argv[])
void
modtime(int argc, char *argv[])
{
- time_t mtime;
-
+ struct timespec mtime;
if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
fprintf(ttyout, "usage: %s file\n", argv[0]);
code = -1;
return;
}
- mtime = remotemodtime(argv[1], 1);
- if (mtime != -1)
- fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
- code = mtime;
-}
+ mtime = remotemodtime(argv[1], 1);
+ if (mtime.tv_sec != -1) {
+ char timebuf[64];
+ struct tm *tm;
+
+ tm = localtime(&mtime.tv_sec);
+ strftime(timebuf, sizeof(timebuf), "%a %b %d %H:%M:%S", tm);

+ if (mtime.tv_nsec == 0) {
+ fprintf(ttyout, "%s\t%s %d\n", argv[1], timebuf,
+ tm->tm_year + 1900);
+ } else {
+ char nsbuf[16];
+ char *end;
+
+ snprintf(nsbuf, sizeof(nsbuf), "%09ld", mtime.tv_nsec);
+ end = nsbuf + strlen(nsbuf) - 1;
+ while (end > nsbuf && *end == '0')
+ end--;
+ *(end + 1) = '\0';
+ fprintf(ttyout, "%s\t%s.%s %d\n", argv[1], timebuf,
+ nsbuf, tm->tm_year + 1900);
+ }
+
+ }
+ code = mtime.tv_sec;
+}
/*
* Show status on remote machine
*/
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/ftp/extern.h,v
diff -u -p -u -p -r1.54 extern.h
--- extern.h 21 May 2024 05:00:48 -0000 1.54
+++ extern.h 18 Feb 2026 22:48:04 -0000
@@ -101,7 +101,7 @@ void recvrequest(const char *, const cha
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 **);
Index: ftp.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/ftp.c,v
diff -u -p -u -p -r1.109 ftp.c
--- ftp.c 8 Mar 2023 04:43:11 -0000 1.109
+++ ftp.c 18 Feb 2026 22:48:04 -0000
@@ -1204,7 +1204,7 @@ break2:
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];

Index: small.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/small.c,v
diff -u -p -u -p -r1.13 small.c
--- small.c 8 Mar 2023 04:43:11 -0000 1.13
+++ small.c 18 Feb 2026 22:48:04 -0000
@@ -269,7 +269,7 @@ usage:
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) {
Index: util.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/util.c,v
diff -u -p -u -p -r1.99 util.c
--- util.c 21 Dec 2025 07:29:03 -0000 1.99
+++ util.c 18 Feb 2026 22:48:04 -0000
@@ -602,29 +602,32 @@ remotesize(const char *file, int noisy)
/*
* 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 rtime;
+ int ocode, overbose;

- overbose = verbose;
- ocode = code;
- rtime = -1;
+ overbose = verbose;
+ ocode = code;
+ rtime.tv_sec = -1;
+ rtime.tv_nsec = 0;
#ifndef SMALL
if (!debug)
#endif /* !SMALL */
verbose = -1;
if (command("MDTM %s", file) == COMPLETE) {
- struct tm timebuf;
- int yy, mo, day, hour, min, sec;
+ fprintf(ttyout, "DEBUG: reply_string = '%s'\n", reply_string);
+ 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 +652,40 @@ remotemodtime(const char *file, int nois
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;
+
+ rtime.tv_sec = mktime(&timebuf);
+ rtime.tv_nsec = nsec;
+ if (rtime.tv_sec == -1 && (noisy
#ifndef SMALL
|| debug
#endif /* !SMALL */
))
- fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
+ fprintf(ttyout, "Can't convert %s to a time.\n",
+ reply_string);
else
- rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
+ /* conv. local -> GMT */
+ rtime.tv_sec += timebuf.tm_gmtoff;
} else if (noisy
#ifndef SMALL
&& !debug
@@ -667,11 +695,10 @@ remotemodtime(const char *file, int nois
fputc('\n', ttyout);
}
verbose = overbose;
- if (rtime == -1)
+ if (rtime.tv_sec == -1)
code = ocode;
return (rtime);
}
-
/*
* Ensure file is in or under dir.
* Returns 1 if so, 0 if not (or an error occurred).
hello,

i updated the patch, to actually show the parsed fractional seconds.

here is the format it will show in (timestamp not date)

06:00:00.PARSED_FRACTIONAL_SECONDS

here is the diff:

Index: cmds.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/cmds.c,v
diff -u -p -u -p -r1.85 cmds.c
--- cmds.c 8 Mar 2023 04:43:11 -0000 1.85
+++ cmds.c 18 Feb 2026 22:48:04 -0000
@@ -1587,19 +1587,39 @@ sizecmd(int argc, char *argv[])
void
modtime(int argc, char *argv[])
{
- time_t mtime;
-
+ struct timespec mtime;
if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
fprintf(ttyout, "usage: %s file\n", argv[0]);
code = -1;
return;
}
- mtime = remotemodtime(argv[1], 1);
- if (mtime != -1)
- fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
- code = mtime;
-}
+ mtime = remotemodtime(argv[1], 1);
+ if (mtime.tv_sec != -1) {
+ char timebuf[64];
+ struct tm *tm;
+
+ tm = localtime(&mtime.tv_sec);
+ strftime(timebuf, sizeof(timebuf), "%a %b %d %H:%M:%S", tm);

+ if (mtime.tv_nsec == 0) {
+ fprintf(ttyout, "%s\t%s %d\n", argv[1], timebuf,
+ tm->tm_year + 1900);
+ } else {
+ char nsbuf[16];
+ char *end;
+
+ snprintf(nsbuf, sizeof(nsbuf), "%09ld", mtime.tv_nsec);
+ end = nsbuf + strlen(nsbuf) - 1;
+ while (end > nsbuf && *end == '0')
+ end--;
+ *(end + 1) = '\0';
+ fprintf(ttyout, "%s\t%s.%s %d\n", argv[1], timebuf,
+ nsbuf, tm->tm_year + 1900);
+ }
+
+ }
+ code = mtime.tv_sec;
+}
/*
* Show status on remote machine
*/
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/ftp/extern.h,v
diff -u -p -u -p -r1.54 extern.h
--- extern.h 21 May 2024 05:00:48 -0000 1.54
+++ extern.h 18 Feb 2026 22:48:04 -0000
@@ -101,7 +101,7 @@ void recvrequest(const char *, const cha
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 **);
Index: ftp.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/ftp.c,v
diff -u -p -u -p -r1.109 ftp.c
--- ftp.c 8 Mar 2023 04:43:11 -0000 1.109
+++ ftp.c 18 Feb 2026 22:48:04 -0000
@@ -1204,7 +1204,7 @@ break2:
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];

Index: small.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/small.c,v
diff -u -p -u -p -r1.13 small.c
--- small.c 8 Mar 2023 04:43:11 -0000 1.13
+++ small.c 18 Feb 2026 22:48:04 -0000
@@ -269,7 +269,7 @@ usage:
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) {
Index: util.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/util.c,v
diff -u -p -u -p -r1.99 util.c
--- util.c 21 Dec 2025 07:29:03 -0000 1.99
+++ util.c 18 Feb 2026 22:48:04 -0000
@@ -602,29 +602,32 @@ remotesize(const char *file, int noisy)
/*
* 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 rtime;
+ int ocode, overbose;

- overbose = verbose;
- ocode = code;
- rtime = -1;
+ overbose = verbose;
+ ocode = code;
+ rtime.tv_sec = -1;
+ rtime.tv_nsec = 0;
#ifndef SMALL
if (!debug)

No comments:

Post a Comment