maint: revert "build: update gnulib submodule to latest"
[coreutils/ericb.git] / src / touch.c
blob18f81c8aa80bb1d76471d8c468cedb3d668c5e4d
1 /* touch -- change modification and access times of files
2 Copyright (C) 1987, 1989-1991, 1995-2005, 2007-2011 Free Software
3 Foundation, Inc.
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,
19 and Randy Smith. */
21 #include <config.h>
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <sys/types.h>
25 #include <assert.h>
27 #include "system.h"
28 #include "argmatch.h"
29 #include "error.h"
30 #include "fd-reopen.h"
31 #include "parse-datetime.h"
32 #include "posixtm.h"
33 #include "posixver.h"
34 #include "quote.h"
35 #include "stat-time.h"
36 #include "utimens.h"
38 /* The official name of this program (e.g., no `g' prefix). */
39 #define PROGRAM_NAME "touch"
41 #define AUTHORS \
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'. */
49 #define CH_ATIME 1
50 #define CH_MTIME 2
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. */
59 static bool use_ref;
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. */
79 enum
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},
93 {NULL, 0, NULL, 0}
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. */
111 static void
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. */
122 static bool
123 touch (const char *file)
125 bool ok;
126 int fd = -1;
127 int open_errno = 0;
128 struct timespec const *t = newtime;
130 if (STREQ (file, "-"))
131 fd = STDOUT_FILENO;
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)
146 open_errno = errno;
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;
154 else
156 assert (change_times == CH_ATIME);
157 newtime[1].tv_nsec = UTIME_OMIT;
161 if (amtime_now)
163 /* Pass NULL to futimens so it will not fail if we have
164 write access to the file, but don't own it. */
165 t = NULL;
168 ok = (fdutimensat (fd, AT_FDCWD, (fd == STDOUT_FILENO ? NULL : file), t,
169 (no_dereference && fd == -1) ? AT_SYMLINK_NOFOLLOW : 0)
170 == 0);
172 if (fd == STDIN_FILENO)
174 if (close (STDIN_FILENO) != 0)
176 error (0, errno, _("closing %s"), quote (file));
177 return false;
180 else if (fd == STDOUT_FILENO)
182 /* Do not diagnose "touch -c - >&-". */
183 if (!ok && errno == EBADF && no_create)
184 return true;
187 if (!ok)
189 if (open_errno)
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));
197 else
199 if (no_create && errno == ENOENT)
200 return true;
201 error (0, errno, _("setting times of %s"), quote (file));
203 return false;
206 return true;
209 void
210 usage (int status)
212 if (status != EXIT_SUCCESS)
213 fprintf (stderr, _("Try `%s --help' for more information.\n"),
214 program_name);
215 else
217 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
218 fputs (_("\
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\
222 is supplied.\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\
227 "), stdout);
228 fputs (_("\
229 Mandatory arguments to long options are mandatory for short options too.\n\
230 "), stdout);
231 fputs (_("\
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\
235 -f (ignored)\n\
236 "), stdout);
237 fputs (_("\
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\
242 "), stdout);
243 fputs (_("\
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\
249 "), stdout);
250 fputs (HELP_OPTION_DESCRIPTION, stdout);
251 fputs (VERSION_OPTION_DESCRIPTION, stdout);
252 fputs (_("\
254 Note that the -d and -t options accept different time-date formats.\n\
255 "), stdout);
256 emit_ancillary_info ();
258 exit (status);
262 main (int argc, char **argv)
264 int c;
265 bool date_set = false;
266 bool ok = true;
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);
277 change_times = 0;
278 no_create = use_ref = false;
280 while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) != -1)
282 switch (c)
284 case 'a':
285 change_times |= CH_ATIME;
286 break;
288 case 'c':
289 no_create = true;
290 break;
292 case 'd':
293 flex_date = optarg;
294 break;
296 case 'f':
297 break;
299 case 'h':
300 no_dereference = true;
301 break;
303 case 'm':
304 change_times |= CH_MTIME;
305 break;
307 case 'r':
308 use_ref = true;
309 ref_file = optarg;
310 break;
312 case 't':
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"),
316 quote (optarg));
317 newtime[0].tv_nsec = 0;
318 newtime[1] = newtime[0];
319 date_set = true;
320 break;
322 case TIME_OPTION: /* --time */
323 change_times |= XARGMATCH ("--time", optarg,
324 time_args, time_masks);
325 break;
327 case_GETOPT_HELP_CHAR;
329 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
331 default:
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);
345 if (use_ref)
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);
356 date_set = true;
357 if (flex_date)
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]);
365 else
367 if (flex_date)
369 struct timespec now;
370 gettime (&now);
371 get_reldate (&newtime[0], flex_date, &now);
372 newtime[1] = newtime[0];
373 date_set = true;
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 (&notnow1, flex_date, &notnow);
388 if (notnow1.tv_sec == notnow.tv_sec
389 && notnow1.tv_nsec == notnow.tv_nsec)
390 date_set = false;
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];
403 date_set = true;
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. */
413 if (tm)
414 error (0, 0,
415 _("warning: `touch %s' is obsolete; use "
416 "`touch -t %04ld%02d%02d%02d%02d.%02d'"),
417 argv[optind],
418 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
419 tm->tm_hour, tm->tm_min, tm->tm_sec);
422 optind++;
425 if (!date_set)
427 if (change_times == (CH_ATIME | CH_MTIME))
428 amtime_now = true;
429 else
430 newtime[1].tv_nsec = newtime[0].tv_nsec = UTIME_NOW;
433 if (optind == argc)
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);