This diff adds to touch(1) support for the -d option specified by POSIX
2008 that permits specifying subsecond timestamps, as well as nanosecond
support in the -r option, and a general simplification over the main loop.
oks?
Philip
Index: usr.bin/touch/touch.1
===================================================================
RCS file: /cvs/src/usr.bin/touch/touch.1,v
retrieving revision 1.17
diff -u -p -r1.17 touch.1
--- usr.bin/touch/touch.1 3 Sep 2010 11:09:29 -0000 1.17
+++ usr.bin/touch/touch.1 18 Aug 2011 04:26:44 -0000
@@ -42,6 +42,7 @@
.Sh SYNOPSIS
.Nm touch
.Op Fl acm
+.Op Fl d Ar YYYY-MM-DDThh:mm:SS[.frac][Z]
.Op Fl r Ar file
.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS]
.Ar
@@ -65,6 +66,38 @@ The
.Nm
utility does not treat this as an error.
No error messages are displayed and the exit value is not affected.
+.It Fl d Ar YYYY-MM-DDThh:mm:SS[.frac][Z]
+Change the access and modification times to the specified time.
+The argument should be in the form
+.Dq YYYY-MM-DDThh:mm:SS[.frac][Z]
+where the parts of the argument represent the following:
+.Pp
+.Bl -tag -width .frac -compact -offset indent
+.It Ar YYYY
+The four digits of the year.
+.It Ar MM
+The month of the year, from 1 to 12.
+.It Ar DD
+The day of the month, from 1 to 31.
+.It Ar T
+Either the capital letter
+.Dq T
+or a single space.
+.It Ar hh
+The hour of the day, from 0 to 23.
+.It Ar mm
+The minute of the hour, from 0 to 59.
+.It Ar SS
+The second of the minute, from 0 to 60.
+.It Ar .frac
+The decimal fraction of the second, either a period or comma, followed
+by one or more decimal digits.
+.It Ar Z
+The timezone specifier, a capital letter
+.Dq Z
+indicating that the time is in UTC.
+If not specified, the time is in the local timezone.
+.El
.It Fl m
Change the modification time of the file.
The access time of the file is not changed unless the
@@ -105,7 +138,7 @@ The hour of the day, from 0 to 23.
.It Ar mm
The minute of the hour, from 0 to 59.
.It Ar SS
-The second of the minute, from 0 to 61.
+The second of the minute, from 0 to 60.
.El
.Pp
If the
Index: usr.bin/touch/touch.c
===================================================================
RCS file: /cvs/src/usr.bin/touch/touch.c,v
retrieving revision 1.17
diff -u -p -r1.17 touch.c
--- usr.bin/touch/touch.c 6 Aug 2007 19:16:06 -0000 1.17
+++ usr.bin/touch/touch.c 18 Aug 2011 04:26:44 -0000
@@ -46,26 +46,23 @@
#include <tzfile.h>
#include <unistd.h>
-void stime_arg1(char *, struct timeval *);
-void stime_arg2(char *, int, struct timeval *);
-void stime_file(char *, struct timeval *);
+void stime_arg1(char *, struct timespec *);
+void stime_arg2(char *, int, struct timespec *);
+void stime_argd(char *, struct timespec *);
+void stime_file(char *, struct timespec *);
__dead void usage(void);
int
main(int argc, char *argv[])
{
- struct stat sb;
- struct timeval tv[2];
+ struct timespec ts[2];
int aflag, cflag, mflag, ch, fd, len, rval, timeset;
char *p;
(void)setlocale(LC_ALL, "");
aflag = cflag = mflag = timeset = 0;
- if (gettimeofday(&tv[0], NULL))
- err(1, "gettimeofday");
-
- while ((ch = getopt(argc, argv, "acfmr:t:")) != -1)
+ while ((ch = getopt(argc, argv, "acd:fmr:t:")) != -1)
switch (ch) {
case 'a':
aflag = 1;
@@ -73,6 +70,10 @@ main(int argc, char *argv[])
case 'c':
cflag = 1;
break;
+ case 'd':
+ timeset = 1;
+ stime_argd(optarg, ts);
+ break;
case 'f':
break;
case 'm':
@@ -80,11 +81,11 @@ main(int argc, char *argv[])
break;
case 'r':
timeset = 1;
- stime_file(optarg, tv);
+ stime_file(optarg, ts);
break;
case 't':
timeset = 1;
- stime_arg1(optarg, tv);
+ stime_arg1(optarg, ts);
break;
default:
usage();
@@ -105,63 +106,42 @@ main(int argc, char *argv[])
len = p - argv[0];
if (*p == '\0' && (len == 8 || len == 10)) {
timeset = 1;
- stime_arg2(*argv++, len == 10, tv);
+ stime_arg2(*argv++, len == 10, ts);
}
}
/* Otherwise use the current time of day. */
if (!timeset)
- tv[1] = tv[0];
+ ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
+
+ if (!aflag)
+ ts[0].tv_nsec = UTIME_OMIT;
+ if (!mflag)
+ ts[1].tv_nsec = UTIME_OMIT;
if (*argv == NULL)
usage();
for (rval = 0; *argv; ++argv) {
- /* See if the file exists. */
- if (stat(*argv, &sb)) {
- if (!cflag) {
- /* Create the file. */
- fd = open(*argv,
- O_WRONLY | O_CREAT, DEFFILEMODE);
- if (fd == -1 || fstat(fd, &sb) || close(fd)) {
- rval = 1;
- warn("%s", *argv);
- continue;
- }
-
- /* If using the current time, we're done. */
- if (!timeset)
- continue;
- } else
- continue;
- }
-
- if (!aflag)
- TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
- if (!mflag)
- TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
-
- /* Try utimes(2). */
- if (!utimes(*argv, tv))
+ /* Update the file's timestamp if it exists. */
+ if (! utimensat(AT_FDCWD, *argv, ts, 0))
continue;
-
- /* If the user specified a time, nothing else we can do. */
- if (timeset) {
+ if (errno != ENOENT) {
rval = 1;
warn("%s", *argv);
+ continue;
}
- /*
- * System V and POSIX 1003.1 require that a NULL argument
- * set the access/modification times to the current time.
- * The permission checks are different, too, in that the
- * ability to write the file is sufficient. Take a shot.
- */
- if (!utimes(*argv, NULL))
+ /* Didn't exist; should we create it? */
+ if (cflag)
continue;
- rval = 1;
- warn("%s", *argv);
+ /* Create the file. */
+ fd = open(*argv, O_WRONLY | O_CREAT, DEFFILEMODE);
+ if (fd == -1 || futimens(fd, ts) || close(fd)) {
+ rval = 1;
+ warn("%s", *argv);
+ }
}
exit(rval);
}
@@ -169,14 +149,14 @@ main(int argc, char *argv[])
#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] -
'0'))
void
-stime_arg1(char *arg, struct timeval *tvp)
+stime_arg1(char *arg, struct timespec *tsp)
{
struct tm *lt;
time_t tmptime;
int yearset;
char *dot, *p;
/* Start with the current time. */
- tmptime = tvp[0].tv_sec;
+ tmptime = tsp[0].tv_sec;
if ((lt = localtime(&tmptime)) == NULL)
err(1, "localtime");
/* [[CC]YY]MMDDhhmm[.SS] */
@@ -233,21 +213,21 @@ stime_arg1(char *arg, struct timeval *tv
}
lt->tm_isdst = -1; /* Figure out DST. */
- tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt);
- if (tvp[0].tv_sec == -1)
+ tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
+ if (tsp[0].tv_sec == -1)
terr: errx(1,
"out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
- tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
}
void
-stime_arg2(char *arg, int year, struct timeval *tvp)
+stime_arg2(char *arg, int year, struct timespec *tsp)
{
struct tm *lt;
time_t tmptime;
/* Start with the current time. */
- tmptime = tvp[0].tv_sec;
+ tmptime = tsp[0].tv_sec;
if ((lt = localtime(&tmptime)) == NULL)
err(1, "localtime");
@@ -271,23 +251,70 @@ stime_arg2(char *arg, int year, struct t
lt->tm_sec = 0;
lt->tm_isdst = -1; /* Figure out DST. */
- tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt);
- if (tvp[0].tv_sec == -1)
+ tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
+ if (tsp[0].tv_sec == -1)
terr: errx(1,
"out of range or illegal time specification: MMDDhhmm[YY]");
- tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
}
void
-stime_file(char *fname, struct timeval *tvp)
+stime_file(char *fname, struct timespec *tsp)
{
struct stat sb;
if (stat(fname, &sb))
err(1, "%s", fname);
- TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
- TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
+ tsp[0] = sb.st_atim;
+ tsp[1] = sb.st_mtim;
+}
+
+void
+stime_argd(char *arg, struct timespec *tsp)
+{
+ struct tm tm;
+ char *frac, *p;
+ int utc = 0;
+
+ /* accept YYYY-MM-DD(T| )hh:mm:ss[(.|,)frac][Z] */
+ memset(&tm, 0, sizeof(tm));
+ p = strptime(arg, "%F", &tm);
+ if (p == NULL || (*p != 'T' && *p != ' '))
+ goto terr;
+ p = strptime(p + 1, "%T", &tm);
+ if (p == NULL)
+ goto terr;
+ tsp[0].tv_nsec = 0;
+ if (*p == '.' || *p == ',') {
+ frac = ++p;
+ while (isdigit((unsigned char)*p)) {
+ if (p - frac < 9) {
+ tsp[0].tv_nsec = tsp[0].tv_nsec * 10 +
+ *p - '0';
+ }
+ p++;
+ }
+ if (p == frac)
+ goto terr;
+
+ /* fill in the trailing zeros */
+ while (p - frac-- < 9)
+ tsp[0].tv_nsec *= 10;
+ }
+ if (*p == 'Z') {
+ utc = 1;
+ p++;
+ }
+ if (*p != '\0')
+ goto terr;
+
+ tm.tm_isdst = -1;
+ tsp[0].tv_sec = utc ? timegm(&tm) : mktime(&tm);
+ if (tsp[0].tv_sec == -1)
+terr: errx(1,
+ "out of range or illegal time specification: YYYY-MM-DDThh:mm:ss[.frac][Z]");
+ tsp[1] = tsp[0];
}
__dead void