1 /* touch -- change modification and access times of files
2 Copyright (C) 1987, 1989-1991, 1995-2005, 2007-2011 Free Software
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
24 #include <sys/types.h>
30 #include "fd-reopen.h"
31 #include "parse-datetime.h"
35 #include "stat-time.h"
38 /* The official name of this program (e.g., no `g' prefix). */
39 #define PROGRAM_NAME "touch"
42 proper_name ("Paul Rubin"), \
43 proper_name ("Arnold Robbins"), \
44 proper_name ("Jim Kingdon"), \
45 proper_name ("David MacKenzie"), \
46 proper_name ("Randy Smith")
48 /* Bitmasks for `change_times'. */
52 /* Which timestamps to change. */
53 static int change_times
;
55 /* (-c) If true, don't create if not already there. */
56 static bool no_create
;
58 /* (-r) If true, use times from a reference file. */
61 /* (-h) If true, change the times of an existing symlink, if possible. */
62 static bool no_dereference
;
64 /* If true, the only thing we have to do is change both the
65 modification and access time to the current time, so we don't
66 have to own the file, just be able to read and write it.
67 On some systems, we can do this if we own the file, even though
68 we have neither read nor write access to it. */
69 static bool amtime_now
;
71 /* New access and modification times to use when setting time. */
72 static struct timespec newtime
[2];
74 /* File to use for -r. */
75 static char *ref_file
;
77 /* For long options that have no equivalent short option, use a
78 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
81 TIME_OPTION
= CHAR_MAX
+ 1
84 static struct option
const longopts
[] =
86 {"time", required_argument
, NULL
, TIME_OPTION
},
87 {"no-create", no_argument
, NULL
, 'c'},
88 {"date", required_argument
, NULL
, 'd'},
89 {"reference", required_argument
, NULL
, 'r'},
90 {"no-dereference", no_argument
, NULL
, 'h'},
91 {GETOPT_HELP_OPTION_DECL
},
92 {GETOPT_VERSION_OPTION_DECL
},
96 /* Valid arguments to the `--time' option. */
97 static char const* const time_args
[] =
99 "atime", "access", "use", "mtime", "modify", NULL
102 /* The bits in `change_times' that those arguments set. */
103 static int const time_masks
[] =
105 CH_ATIME
, CH_ATIME
, CH_ATIME
, CH_MTIME
, CH_MTIME
108 /* Store into *RESULT the result of interpreting FLEX_DATE as a date,
109 relative to NOW. If NOW is null, use the current time. */
112 get_reldate (struct timespec
*result
,
113 char const *flex_date
, struct timespec
const *now
)
115 if (! parse_datetime (result
, flex_date
, now
))
116 error (EXIT_FAILURE
, 0, _("invalid date format %s"), quote (flex_date
));
119 /* Update the time of file FILE according to the options given.
120 Return true if successful. */
123 touch (const char *file
)
128 struct timespec
const *t
= newtime
;
130 if (STREQ (file
, "-"))
132 else if (! (no_create
|| no_dereference
))
134 /* Try to open FILE, creating it if necessary. */
135 int default_permissions
=
136 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
;
137 fd
= fd_reopen (STDIN_FILENO
, file
,
138 O_WRONLY
| O_CREAT
| O_NONBLOCK
| O_NOCTTY
,
139 default_permissions
);
141 /* Don't save a copy of errno if it's EISDIR, since that would lead
142 touch to give a bogus diagnostic for e.g., `touch /' (assuming
143 we don't own / or have write access to it). On Solaris 5.6,
144 and probably other systems, it is EINVAL. On SunOS4, it's EPERM. */
145 if (fd
== -1 && errno
!= EISDIR
&& errno
!= EINVAL
&& errno
!= EPERM
)
149 if (change_times
!= (CH_ATIME
| CH_MTIME
))
151 /* We're setting only one of the time values. */
152 if (change_times
== CH_MTIME
)
153 newtime
[0].tv_nsec
= UTIME_OMIT
;
156 assert (change_times
== CH_ATIME
);
157 newtime
[1].tv_nsec
= UTIME_OMIT
;
163 /* Pass NULL to futimens so it will not fail if we have
164 write access to the file, but don't own it. */
168 ok
= (fdutimensat (fd
, AT_FDCWD
, (fd
== STDOUT_FILENO
? NULL
: file
), t
,
169 (no_dereference
&& fd
== -1) ? AT_SYMLINK_NOFOLLOW
: 0)
172 if (fd
== STDIN_FILENO
)
174 if (close (STDIN_FILENO
) != 0)
176 error (0, errno
, _("closing %s"), quote (file
));
180 else if (fd
== STDOUT_FILENO
)
182 /* Do not diagnose "touch -c - >&-". */
183 if (!ok
&& errno
== EBADF
&& no_create
)
191 /* The wording of this diagnostic should cover at least two cases:
192 - the file does not exist, but the parent directory is unwritable
193 - the file exists, but it isn't writable
194 I think it's not worth trying to distinguish them. */
195 error (0, open_errno
, _("cannot touch %s"), quote (file
));
199 if (no_create
&& errno
== ENOENT
)
201 error (0, errno
, _("setting times of %s"), quote (file
));
212 if (status
!= EXIT_SUCCESS
)
213 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
217 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
219 Update the access and modification times of each FILE to the current time.\n\
221 A FILE argument that does not exist is created empty, unless -c or -h\n\
224 A FILE argument string of - is handled specially and causes touch to\n\
225 change the times of the file associated with standard output.\n\
229 Mandatory arguments to long options are mandatory for short options too.\n\
232 -a change only the access time\n\
233 -c, --no-create do not create any files\n\
234 -d, --date=STRING parse STRING and use it instead of current time\n\
238 -h, --no-dereference affect each symbolic link instead of any referenced\n\
239 file (useful only on systems that can change the\n\
240 timestamps of a symlink)\n\
241 -m change only the modification time\n\
244 -r, --reference=FILE use this file's times instead of current time\n\
245 -t STAMP use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
246 --time=WORD change the specified time:\n\
247 WORD is access, atime, or use: equivalent to -a\n\
248 WORD is modify or mtime: equivalent to -m\n\
250 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
251 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
254 Note that the -d and -t options accept different time-date formats.\n\
256 emit_ancillary_info ();
262 main (int argc
, char **argv
)
265 bool date_set
= false;
267 char const *flex_date
= NULL
;
269 initialize_main (&argc
, &argv
);
270 set_program_name (argv
[0]);
271 setlocale (LC_ALL
, "");
272 bindtextdomain (PACKAGE
, LOCALEDIR
);
273 textdomain (PACKAGE
);
275 atexit (close_stdout
);
278 no_create
= use_ref
= false;
280 while ((c
= getopt_long (argc
, argv
, "acd:fhmr:t:", longopts
, NULL
)) != -1)
285 change_times
|= CH_ATIME
;
300 no_dereference
= true;
304 change_times
|= CH_MTIME
;
313 if (! posixtime (&newtime
[0].tv_sec
, optarg
,
314 PDS_LEADING_YEAR
| PDS_CENTURY
| PDS_SECONDS
))
315 error (EXIT_FAILURE
, 0, _("invalid date format %s"),
317 newtime
[0].tv_nsec
= 0;
318 newtime
[1] = newtime
[0];
322 case TIME_OPTION
: /* --time */
323 change_times
|= XARGMATCH ("--time", optarg
,
324 time_args
, time_masks
);
327 case_GETOPT_HELP_CHAR
;
329 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
332 usage (EXIT_FAILURE
);
336 if (change_times
== 0)
337 change_times
= CH_ATIME
| CH_MTIME
;
339 if (date_set
&& (use_ref
|| flex_date
))
341 error (0, 0, _("cannot specify times from more than one source"));
342 usage (EXIT_FAILURE
);
347 struct stat ref_stats
;
348 /* Don't use (no_dereference?lstat:stat) (args), since stat
349 might be an object-like macro. */
350 if (no_dereference
? lstat (ref_file
, &ref_stats
)
351 : stat (ref_file
, &ref_stats
))
352 error (EXIT_FAILURE
, errno
,
353 _("failed to get attributes of %s"), quote (ref_file
));
354 newtime
[0] = get_stat_atime (&ref_stats
);
355 newtime
[1] = get_stat_mtime (&ref_stats
);
359 if (change_times
& CH_ATIME
)
360 get_reldate (&newtime
[0], flex_date
, &newtime
[0]);
361 if (change_times
& CH_MTIME
)
362 get_reldate (&newtime
[1], flex_date
, &newtime
[1]);
371 get_reldate (&newtime
[0], flex_date
, &now
);
372 newtime
[1] = newtime
[0];
375 /* If neither -a nor -m is specified, treat "-d now" as if
376 it were absent; this lets "touch" succeed more often in
377 the presence of restrictive permissions. */
378 if (change_times
== (CH_ATIME
| CH_MTIME
)
379 && newtime
[0].tv_sec
== now
.tv_sec
380 && newtime
[0].tv_nsec
== now
.tv_nsec
)
382 /* Check that it really was "-d now", and not a time
383 stamp that just happens to be the current time. */
384 struct timespec notnow
, notnow1
;
385 notnow
.tv_sec
= now
.tv_sec
^ 1;
386 notnow
.tv_nsec
= now
.tv_nsec
;
387 get_reldate (¬now1
, flex_date
, ¬now
);
388 if (notnow1
.tv_sec
== notnow
.tv_sec
389 && notnow1
.tv_nsec
== notnow
.tv_nsec
)
395 /* The obsolete `MMDDhhmm[YY]' form is valid IFF there are
396 two or more non-option arguments. */
397 if (!date_set
&& 2 <= argc
- optind
&& posix2_version () < 200112
398 && posixtime (&newtime
[0].tv_sec
, argv
[optind
],
399 PDS_TRAILING_YEAR
| PDS_PRE_2000
))
401 newtime
[0].tv_nsec
= 0;
402 newtime
[1] = newtime
[0];
405 if (! getenv ("POSIXLY_CORRECT"))
407 struct tm
const *tm
= localtime (&newtime
[0].tv_sec
);
409 /* Technically, it appears that even a deliberate attempt to cause
410 the above localtime to return NULL will always fail because our
411 posixtime implementation rejects all dates for which localtime
412 would fail. However, skip the warning if it ever fails. */
415 _("warning: `touch %s' is obsolete; use "
416 "`touch -t %04ld%02d%02d%02d%02d.%02d'"),
418 tm
->tm_year
+ 1900L, tm
->tm_mon
+ 1, tm
->tm_mday
,
419 tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
427 if (change_times
== (CH_ATIME
| CH_MTIME
))
430 newtime
[1].tv_nsec
= newtime
[0].tv_nsec
= UTIME_NOW
;
435 error (0, 0, _("missing file operand"));
436 usage (EXIT_FAILURE
);
439 for (; optind
< argc
; ++optind
)
440 ok
&= touch (argv
[optind
]);
442 exit (ok
? EXIT_SUCCESS
: EXIT_FAILURE
);