Submitted for your consideration:
This diff adds an option '-h' to touch(1) for getting/setting timestamps
from symbolic links directly, rather than the linked-to file. This is
useful if you want to set the timestamp on a link (e.g. to copy the
timestamp from the linked-to file, or from another link).
This implementation is not entirely compatible with FreeBSD. I believe
the differences are improvements. First, in FreeBSD -h imples -c, which
means you cannot create new files *and* modify existing links both in
the same command. This seems like an artificial restriction, and not
a particularly useful one.
Second, in FreeBSD the -h option does not apply to -r, which
means you can't copy a timestamp from a symbolic link.
With the below diff, the following two examples are now possible:
Example 1: copy the timestamp of link1 to link2
$ touch -h -r link1 link2
Example 2: set the timestamp of link1 to be the same as the timestamp
of the linked-to file:
$ touch -r link1 -h link1
Note that in the second example, the -h option must be specified
*after* the -r option, so that the link *is* followed for the read
of the source timestamp but is *not* followed for the write of the
target timestamp.
If you want to copy a timestamp from a link to the target of a link,
that takes two steps, alas:
$ touch -h -r link1 tmpfile
$ touch -r tmpfile link2
-ken
Index: touch.1
===================================================================
RCS file: /cvs/src/usr.bin/touch/touch.1,v
retrieving revision 1.35
diff -u -p -r1.35 touch.1
--- touch.1 25 Sep 2015 17:02:57 -0000 1.35
+++ touch.1 26 Dec 2016 20:49:33 -0000
@@ -41,7 +41,7 @@
.Nd change file access and modification times
.Sh SYNOPSIS
.Nm touch
-.Op Fl acm
+.Op Fl achm
.Oo Fl d
.Sm off
.Ar ccyy No - Ar mm No - Ar dd Cm T Ar HH : MM : SS Oo \&. Ar frac Oc Op Cm Z
@@ -116,6 +116,11 @@ The timezone specifier: a capital letter
indicating that the time is in UTC.
If not specified, the time is in the local timezone.
.El
+.It Fl h
+Do not follow symbolic links; modify links instead of their targets.
+This option also applies to
+.Fl r
+if it precedes it.
.It Fl m
Change the modification time of the file.
The access time of the file is not changed unless the
Index: touch.c
===================================================================
RCS file: /cvs/src/usr.bin/touch/touch.c,v
retrieving revision 1.25
diff -u -p -r1.25 touch.c
--- touch.c 9 Oct 2015 01:37:09 -0000 1.25
+++ touch.c 26 Dec 2016 20:49:33 -0000
@@ -48,7 +48,7 @@
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 *);
+void stime_file(char *, int, struct timespec *);
__dead void usage(void);
int
@@ -56,6 +56,7 @@ main(int argc, char *argv[])
{
struct timespec ts[2];
int aflag, cflag, mflag, ch, fd, len, rval, timeset;
+ int at_flag;
char *p;
(void)setlocale(LC_ALL, "");
@@ -63,8 +64,8 @@ main(int argc, char *argv[])
if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
err(1, "pledge");
- aflag = cflag = mflag = timeset = 0;
- while ((ch = getopt(argc, argv, "acd:fmr:t:")) != -1)
+ aflag = cflag = mflag = timeset = at_flag = 0;
+ while ((ch = getopt(argc, argv, "acd:fhmr:t:")) != -1)
switch (ch) {
case 'a':
aflag = 1;
@@ -78,12 +79,15 @@ main(int argc, char *argv[])
break;
case 'f':
break;
+ case 'h':
+ at_flag |= AT_SYMLINK_NOFOLLOW;
+ break;
case 'm':
mflag = 1;
break;
case 'r':
timeset = 1;
- stime_file(optarg, ts);
+ stime_file(optarg, at_flag, ts);
break;
case 't':
timeset = 1;
@@ -126,7 +130,7 @@ main(int argc, char *argv[])
for (rval = 0; *argv; ++argv) {
/* Update the file's timestamp if it exists. */
- if (! utimensat(AT_FDCWD, *argv, ts, 0))
+ if (! utimensat(AT_FDCWD, *argv, ts, at_flag))
continue;
if (errno != ENOENT) {
rval = 1;
@@ -267,11 +271,11 @@ terr: errx(1,
}
void
-stime_file(char *fname, struct timespec *tsp)
+stime_file(char *fname, int at_flag, struct timespec *tsp)
{
struct stat sb;
- if (stat(fname, &sb))
+ if (fstatat(AT_FDCWD, fname, &sb, at_flag))
err(1, "%s", fname);
tsp[0] = sb.st_atim;
tsp[1] = sb.st_mtim;
@@ -328,7 +332,7 @@ __dead void
usage(void)
{
(void)fprintf(stderr,
-"usage: touch [-acm] [-d ccyy-mm-ddTHH:MM:SS[.frac][Z]] [-r file]\n"
+"usage: touch [-achm] [-d ccyy-mm-ddTHH:MM:SS[.frac][Z]] [-r file]\n"
" [-t [[cc]yy]mmddHHMM[.SS]] file ...\n");
exit(1);
}