diff options
author | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-08-20 06:44:25 +0000 |
---|---|---|
committer | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-08-20 06:44:25 +0000 |
commit | 638c0e373ae6ea3d3e42301aca4e803708f5bb95 (patch) | |
tree | 90f23d8b85936e910fea47451074c9df5b4e538a | |
parent | 57f3a3fa7c3d3ec71f96f30e2f2bbc13689a0b7d (diff) |
Add nanosecond precision and support for -d option (new in POSIX 2008)
ok millert@, manpage fixes jmc@
-rw-r--r-- | usr.bin/touch/touch.1 | 39 | ||||
-rw-r--r-- | usr.bin/touch/touch.c | 163 |
2 files changed, 130 insertions, 72 deletions
diff --git a/usr.bin/touch/touch.1 b/usr.bin/touch/touch.1 index a13476550d9..d67144478a5 100644 --- a/usr.bin/touch/touch.1 +++ b/usr.bin/touch/touch.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: touch.1,v 1.17 2010/09/03 11:09:29 jmc Exp $ +.\" $OpenBSD: touch.1,v 1.18 2011/08/20 06:44:24 guenther Exp $ .\" $NetBSD: touch.1,v 1.8 1995/08/31 22:10:05 jtc Exp $ .\" .\" Copyright (c) 1991, 1993 @@ -33,7 +33,7 @@ .\" .\" @(#)touch.1 8.3 (Berkeley) 4/28/95 .\" -.Dd $Mdocdate: September 3 2010 $ +.Dd $Mdocdate: August 20 2011 $ .Dt TOUCH 1 .Os .Sh NAME @@ -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 +.Sq 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 +.Sq 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 diff --git a/usr.bin/touch/touch.c b/usr.bin/touch/touch.c index 45d6ab23d26..19a1d0a404f 100644 --- a/usr.bin/touch/touch.c +++ b/usr.bin/touch/touch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: touch.c,v 1.17 2007/08/06 19:16:06 sobrado Exp $ */ +/* $OpenBSD: touch.c,v 1.18 2011/08/20 06:44:24 guenther Exp $ */ /* $NetBSD: touch.c,v 1.11 1995/08/31 22:10:06 jtc Exp $ */ /* @@ -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 *tvp) } 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,32 +251,77 @@ stime_arg2(char *arg, int year, struct timeval *tvp) 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 usage(void) { - extern char *__progname; - (void)fprintf(stderr, - "usage: %s [-acm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ...\n", - __progname); +"usage: touch [-acm] [-d YYYY-MM-DDThh:mm:SS[.frac][Z]] [-r file]\n" +" [-t [[CC]YY]MMDDhhmm[.SS]] file ...\n"); exit(1); } |