/* touch -- change modification and access times of files This is the touch utility
Copyright (C) 1987-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
and Randy Smith. */
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include <assert.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "argmatch.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "fd-reopen.h" ...!includes auto-comment...
#include "parse-datetime.h" ...!includes auto-comment...
#include "posixtm.h" ...!includes auto-comment...
#include "posixver.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "stat-time.h" ...!includes auto-comment...
#include "utimens.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "touch" Line 39
#define AUTHORS \ Line 41
proper_name ("Paul Rubin"), \ Line 42
proper_name ("Arnold Robbins"), \ Line 43
proper_name ("Jim Kingdon"), \ Line 44
proper_name ("David MacKenzie"), \ Line 45
proper_name ("Randy Smith") Line 46
/* Bitmasks for 'change_times'. */
#define CH_ATIME 1 Line 49
#define CH_MTIME 2 Line 50
/* Which timestamps to change. */
static int change_times; Line 53
/* (-c) If true, don't create if not already there. */
static bool no_create; Line 56
/* (-r) If true, use times from a reference file. */
static bool use_ref; Line 59
/* (-h) If true, change the times of an existing symlink, if possible. */
static bool no_dereference; Line 62
/* If true, the only thing we have to do is change both the
modification and access time to the current time, so we don't
have to own the file, just be able to read and write it.
On some systems, we can do this if we own the file, even though
we have neither read nor write access to it. */
static bool amtime_now; Line 69
/* New access and modification times to use when setting time. */
static struct timespec newtime[2]; Line 72
/* File to use for -r. */
static char *ref_file; Line 75
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 79
{
TIME_OPTION = CHAR_MAX + 1 Line 81
}; Block 1
static struct option const longopts[] = Line 84
{
{"time", required_argument, NULL, TIME_OPTION}, Line 86
{"no-create", no_argument, NULL, 'c'}, Line 87
{"date", required_argument, NULL, 'd'}, Line 88
{"reference", required_argument, NULL, 'r'}, Line 89
{"no-dereference", no_argument, NULL, 'h'}, Line 90
{GETOPT_HELP_OPTION_DECL}, Line 91
{GETOPT_VERSION_OPTION_DECL}, Line 92
{NULL, 0, NULL, 0} Line 93
}; Block 2
/* Valid arguments to the '--time' option. */
static char const* const time_args[] = Line 97
{
"atime", "access", "use", "mtime", "modify", NULL Line 99
}; Block 3
/* The bits in 'change_times' that those arguments set. */
static int const time_masks[] = Line 103
{
CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME Line 105
}; Block 4
/* Store into *RESULT the result of interpreting FLEX_DATE as a date,
relative to NOW. If NOW is null, use the current time. */
static void Line 111
get_reldate (struct timespec *result, Line 112
char const *flex_date, struct timespec const *now) Line 113
{
if (! parse_datetime (result, flex_date, now)) Line 115
die (EXIT_FAILURE, 0, _("invalid date format %s"), quote (flex_date)); Line 116
} Block 5
/* Update the time of file FILE according to the options given.
Return true if successful. */
static bool Line 122
touch (const char *file) Line 123
{
bool ok; Line 125
int fd = -1; Line 126
int open_errno = 0; Line 127
struct timespec const *t = newtime; Line 128
if (STREQ (file, "-")) Line 130
fd = STDOUT_FILENO; Line 131
else if (! (no_create || no_dereference)) Line 132
{
/* Try to open FILE, creating it if necessary. */
fd = fd_reopen (STDIN_FILENO, file, Line 135...!syscalls auto-comment...
O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY, MODE_RW_UGO); Line 136
/* Don't save a copy of errno if it's EISDIR, since that would lead
touch to give a bogus diagnostic for e.g., 'touch /' (assuming
we don't own / or have write access to it). On Solaris 5.6,
and probably other systems, it is EINVAL. On SunOS4, it's EPERM. */
if (fd == -1 && errno != EISDIR && errno != EINVAL && errno != EPERM) Line 142
open_errno = errno; Line 143
}
if (change_times != (CH_ATIME | CH_MTIME)) Line 146
{
/* We're setting only one of the time values. */
if (change_times == CH_MTIME) Line 149
newtime[0].tv_nsec = UTIME_OMIT; Line 150
else Line 151
{
assert (change_times == CH_ATIME); Line 153
newtime[1].tv_nsec = UTIME_OMIT; Line 154
}
}
if (amtime_now) Line 158
{
/* Pass NULL to futimens so it will not fail if we have
write access to the file, but don't own it. */
t = NULL; Line 162
}
ok = (fdutimensat (fd, AT_FDCWD, (fd == STDOUT_FILENO ? NULL : file), t, Line 165
(no_dereference && fd == -1) ? AT_SYMLINK_NOFOLLOW : 0) Line 166
== 0); Line 167
if (fd == STDIN_FILENO) Line 169
{
if (close (STDIN_FILENO) != 0) Line 171...!syscalls auto-comment...
{
error (0, errno, _("failed to close %s"), quoteaf (file)); Line 173
return false; Line 174
}
}
else if (fd == STDOUT_FILENO) Line 177
{
/* Do not diagnose "touch -c - >&-". */
if (!ok && errno == EBADF && no_create) Line 180
return true; Line 181
}
if (!ok) Line 184
{
if (open_errno) Line 186
{
/* The wording of this diagnostic should cover at least two cases:
- the file does not exist, but the parent directory is unwritable
- the file exists, but it isn't writable
I think it's not worth trying to distinguish them. */
error (0, open_errno, _("cannot touch %s"), quoteaf (file)); Line 192
}
else Line 194
{
if (no_create && errno == ENOENT) Line 196
return true; Line 197
error (0, errno, _("setting times of %s"), quoteaf (file)); Line 198
}
return false; Line 200
}
return true; Line 203
} Block 6
void Line 206
usage (int status) Line 207
{
if (status != EXIT_SUCCESS) Line 209
emit_try_help (); ...!common auto-comment...
else Line 211
{
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name); Line 213
fputs (_("\ Line 214
Update the access and modification times of each FILE to the current time.\n\ Line 215
\n\
A FILE argument that does not exist is created empty, unless -c or -h\n\ Line 217
is supplied.\n\ Line 218
\n\
A FILE argument string of - is handled specially and causes touch to\n\ Line 220
change the times of the file associated with standard output.\n\ Line 221
"), stdout); Line 222
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 226
-a change only the access time\n\ Line 227
-c, --no-create do not create any files\n\ Line 228
-d, --date=STRING parse STRING and use it instead of current time\n\ Line 229
-f (ignored)\n\ Line 230
"), stdout); Line 231
fputs (_("\ Line 232
-h, --no-dereference affect each symbolic link instead of any referenced\n\ Line 233
file (useful only on systems that can change the\n\ Line 234
timestamps of a symlink)\n\ Line 235
-m change only the modification time\n\ Line 236
"), stdout); Line 237
fputs (_("\ Line 238
-r, --reference=FILE use this file's times instead of current time\n\ Line 239
-t STAMP use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\ Line 240
--time=WORD change the specified time:\n\ Line 241
WORD is access, atime, or use: equivalent to -a\n\ Line 242
WORD is modify or mtime: equivalent to -m\n\ Line 243
"), stdout); Line 244
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 245
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 246
fputs (_("\ Line 247
\n\
Note that the -d and -t options accept different time-date formats.\n\ Line 249
"), stdout); Line 250
emit_ancillary_info (PROGRAM_NAME); Line 251
}
exit (status); Line 253
} Block 7
int
main (int argc, char **argv) Line 257
{
int c; Line 259
bool date_set = false; Line 260
bool ok = true; Line 261
char const *flex_date = NULL; Line 262
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
change_times = 0; Line 272
no_create = use_ref = false; Line 273
while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) != -1) Line 275
{
switch (c) Line 277
{
case 'a': Line 279
change_times |= CH_ATIME; Line 280
break; Line 281
case 'c': Line 283
no_create = true; Line 284
break; Line 285
case 'd': Line 287
flex_date = optarg; Line 288
break; Line 289
case 'f': Line 291
break; Line 292
case 'h': Line 294
no_dereference = true; Line 295
break; Line 296
case 'm': Line 298
change_times |= CH_MTIME; Line 299
break; Line 300
case 'r': Line 302
use_ref = true; Line 303
ref_file = optarg; Line 304
break; Line 305
case 't': Line 307
if (! posixtime (&newtime[0].tv_sec, optarg, Line 308
PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)) Line 309
die (EXIT_FAILURE, 0, _("invalid date format %s"), Line 310
quote (optarg)); Line 311
newtime[0].tv_nsec = 0; Line 312
newtime[1] = newtime[0]; Line 313
date_set = true; Line 314
break; Line 315
case TIME_OPTION: /* --time */ Line 317
change_times |= XARGMATCH ("--time", optarg, Line 318
time_args, time_masks); Line 319
break; Line 320
case_GETOPT_HELP_CHAR; Line 322
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 324
default: Line 326
usage (EXIT_FAILURE); Line 327
}
}
if (change_times == 0) Line 331
change_times = CH_ATIME | CH_MTIME; Line 332
if (date_set && (use_ref || flex_date)) Line 334
{
error (0, 0, _("cannot specify times from more than one source")); Line 336
usage (EXIT_FAILURE); Line 337
}
if (use_ref) Line 340
{
struct stat ref_stats; Line 342
/* Don't use (no_dereference?lstat:stat) (args), since stat
might be an object-like macro. */
if (no_dereference ? lstat (ref_file, &ref_stats) Line 345...!syscalls auto-comment...
: stat (ref_file, &ref_stats)) Line 346...!syscalls auto-comment...
die (EXIT_FAILURE, errno, Line 347
_("failed to get attributes of %s"), quoteaf (ref_file)); Line 348
newtime[0] = get_stat_atime (&ref_stats); Line 349
newtime[1] = get_stat_mtime (&ref_stats); Line 350
date_set = true; Line 351
if (flex_date) Line 352
{
if (change_times & CH_ATIME) Line 354
get_reldate (&newtime[0], flex_date, &newtime[0]); Line 355
if (change_times & CH_MTIME) Line 356
get_reldate (&newtime[1], flex_date, &newtime[1]); Line 357
}
}
else Line 360
{
if (flex_date) Line 362
{
struct timespec now; Line 364
gettime (&now); Line 365
get_reldate (&newtime[0], flex_date, &now); Line 366
newtime[1] = newtime[0]; Line 367
date_set = true; Line 368
/* If neither -a nor -m is specified, treat "-d now" as if
it were absent; this lets "touch" succeed more often in
the presence of restrictive permissions. */
if (change_times == (CH_ATIME | CH_MTIME) Line 373
&& newtime[0].tv_sec == now.tv_sec Line 374
&& newtime[0].tv_nsec == now.tv_nsec) Line 375
{
/* Check that it really was "-d now", and not a timestamp
that just happens to be the current time. */
struct timespec notnow, notnow1; Line 379
notnow.tv_sec = now.tv_sec ^ 1; Line 380
notnow.tv_nsec = now.tv_nsec; Line 381
get_reldate (¬now1, flex_date, ¬now); Line 382
if (notnow1.tv_sec == notnow.tv_sec Line 383
&& notnow1.tv_nsec == notnow.tv_nsec) Line 384
date_set = false; Line 385
}
}
}
/* The obsolete 'MMDDhhmm[YY]' form is valid IFF there are
two or more non-option arguments. */
if (!date_set && 2 <= argc - optind && posix2_version () < 200112 Line 392
&& posixtime (&newtime[0].tv_sec, argv[optind], Line 393
PDS_TRAILING_YEAR | PDS_PRE_2000)) Line 394
{
newtime[0].tv_nsec = 0; Line 396
newtime[1] = newtime[0]; Line 397
date_set = true; Line 398
if (! getenv ("POSIXLY_CORRECT")) Line 400
{
struct tm const *tm = localtime (&newtime[0].tv_sec); Line 402
/* Technically, it appears that even a deliberate attempt to cause
the above localtime to return NULL will always fail because our
posixtime implementation rejects all dates for which localtime
would fail. However, skip the warning if it ever fails. */
if (tm) Line 408
error (0, 0, Line 409
_("warning: 'touch %s' is obsolete; use " Line 410
"'touch -t %04ld%02d%02d%02d%02d.%02d'"), Line 411
argv[optind], Line 412
tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday, Line 413
tm->tm_hour, tm->tm_min, tm->tm_sec); Line 414
}
optind++; Line 417
}
if (!date_set) Line 420
{
if (change_times == (CH_ATIME | CH_MTIME)) Line 422
amtime_now = true; Line 423
else Line 424
newtime[1].tv_nsec = newtime[0].tv_nsec = UTIME_NOW; Line 425
}
if (optind == argc) Line 428
{
error (0, 0, _("missing file operand")); Line 430
usage (EXIT_FAILURE); Line 431
}
for (; optind < argc; ++optind) Line 434
ok &= touch (argv[optind]); Line 435
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 437
} Block 8