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

Reply via email to