/* 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