/* mv -- move or rename files                                                   This is the mv utility
   Copyright (C) 1986-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 Mike Parker, David MacKenzie, and Jim Meyering */                 
                                                                                
#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 <selinux/selinux.h>                                                    ...!includes auto-comment......!includes auto-comment...
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
#include "backupfile.h"                                                         ...!includes auto-comment...
#include "copy.h"                                                               ...!includes auto-comment...
#include "cp-hash.h"                                                            ...!includes auto-comment......!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "filenamecat.h"                                                        ...!includes auto-comment...
#include "remove.h"                                                             ...!includes auto-comment...
#include "renameatu.h"                                                          ...!includes auto-comment...
#include "root-dev-ino.h"                                                       ...!includes auto-comment......!includes auto-comment...
#include "priv-set.h"                                                           ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "mv"                                                       Line 39
                                                                                
#define AUTHORS \                                                               Line 41
  proper_name ("Mike Parker"), \                                                Line 42
  proper_name ("David MacKenzie"), \                                            Line 43
  proper_name ("Jim Meyering")                                                  Line 44
                                                                                
/* 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 48
{                                                                               
  STRIP_TRAILING_SLASHES_OPTION = CHAR_MAX + 1                                  Line 50
};                                                                              Block 1
                                                                                
/* Remove any trailing slashes from each SOURCE argument.  */                   
static bool remove_trailing_slashes;                                            Line 54
                                                                                
static struct option const long_options[] =                                     Line 56
{                                                                               
  {"backup", optional_argument, NULL, 'b'},                                     Line 58
  {"context", no_argument, NULL, 'Z'},                                          Line 59
  {"force", no_argument, NULL, 'f'},                                            Line 60
  {"interactive", no_argument, NULL, 'i'},                                      Line 61
  {"no-clobber", no_argument, NULL, 'n'},                                       Line 62
  {"no-target-directory", no_argument, NULL, 'T'},                              Line 63
  {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION}, Line 64
  {"suffix", required_argument, NULL, 'S'},                                     Line 65
  {"target-directory", required_argument, NULL, 't'},                           Line 66
  {"update", no_argument, NULL, 'u'},                                           Line 67
  {"verbose", no_argument, NULL, 'v'},                                          Line 68
  {GETOPT_HELP_OPTION_DECL},                                                    Line 69
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 70
  {NULL, 0, NULL, 0}                                                            Line 71
};                                                                              Block 2
                                                                                
static void                                                                     Line 74
rm_option_init (struct rm_options *x)                                           Line 75
{                                                                               
  x->ignore_missing_files = false;                                              Line 77
  x->remove_empty_directories = true;                                           Line 78
  x->recursive = true;                                                          Line 79
  x->one_file_system = false;                                                   Line 80
                                                                                
  /* Should we prompt for removal, too?  No.  Prompting for the 'move'          
     part is enough.  It implies removal.  */                                   
  x->interactive = RMI_NEVER;                                                   Line 84
  x->stdin_tty = false;                                                         Line 85
                                                                                
  x->verbose = false;                                                           Line 87
                                                                                
  /* Since this program may well have to process additional command             
     line arguments after any call to 'rm', that function must preserve         
     the initial working directory, in case one of those is a                   
     '.'-relative name.  */                                                     
  x->require_restore_cwd = true;                                                Line 93
                                                                                
  {                                                                             
    static struct dev_ino dev_ino_buf;                                          Line 96
    x->root_dev_ino = get_root_dev_ino (&dev_ino_buf);                          Line 97
    if (x->root_dev_ino == NULL)                                                Line 98
      die (EXIT_FAILURE, errno, _("failed to get attributes of %s"),            Line 99
           quoteaf ("/"));                                                      Line 100
  }                                                                             
                                                                                
  x->preserve_all_root = false;                                                 Line 103
}                                                                               Block 3
                                                                                
static void                                                                     Line 106
cp_option_init (struct cp_options *x)                                           Line 107
{                                                                               
  bool selinux_enabled = (0 < is_selinux_enabled ());                           ...!common auto-comment...
                                                                                
  cp_options_default (x);                                                       Line 111
  x->copy_as_regular = false;  /* FIXME: maybe make this an option */           Line 112
  x->reflink_mode = REFLINK_AUTO;                                               Line 113
  x->dereference = DEREF_NEVER;                                                 Line 114
  x->unlink_dest_before_opening = false;                                        Line 115
  x->unlink_dest_after_failed_open = false;                                     Line 116
  x->hard_link = false;                                                         Line 117
  x->interactive = I_UNSPECIFIED;                                               Line 118
  x->move_mode = true;                                                          Line 119
  x->install_mode = false;                                                      Line 120
  x->one_file_system = false;                                                   Line 121
  x->preserve_ownership = true;                                                 Line 122
  x->preserve_links = true;                                                     Line 123
  x->preserve_mode = true;                                                      Line 124
  x->preserve_timestamps = true;                                                Line 125
  x->explicit_no_preserve_mode= false;                                          Line 126
  x->preserve_security_context = selinux_enabled;                               Line 127
  x->set_security_context = false;                                              Line 128
  x->reduce_diagnostics = false;                                                Line 129
  x->data_copy_required = true;                                                 Line 130
  x->require_preserve = false;  /* FIXME: maybe make this an option */          Line 131
  x->require_preserve_context = false;                                          Line 132
  x->preserve_xattr = true;                                                     Line 133
  x->require_preserve_xattr = false;                                            Line 134
  x->recursive = true;                                                          Line 135
  x->sparse_mode = SPARSE_AUTO;  /* FIXME: maybe make this an option */         Line 136
  x->symbolic_link = false;                                                     Line 137
  x->set_mode = false;                                                          Line 138
  x->mode = 0;                                                                  Line 139
  x->stdin_tty = isatty (STDIN_FILENO);                                         Line 140
                                                                                
  x->open_dangling_dest_symlink = false;                                        Line 142
  x->update = false;                                                            Line 143
  x->verbose = false;                                                           Line 144
  x->dest_info = NULL;                                                          Line 145
  x->src_info = NULL;                                                           Line 146
}                                                                               
                                                                                
/* FILE is the last operand of this command.  Return true if FILE is a          
   directory.  But report an error if there is a problem accessing FILE, other  
   than nonexistence (errno == ENOENT).  */                                     
                                                                                
static bool                                                                     Line 153
target_directory_operand (char const *file)                                     Line 154
{                                                                               
  struct stat st;                                                               Line 156
  int err = (stat (file, &st) == 0 ? 0 : errno);                                Line 157...!syscalls auto-comment...
  bool is_a_dir = !err && S_ISDIR (st.st_mode);                                 Line 158
  if (err && err != ENOENT)                                                     Line 159
    die (EXIT_FAILURE, err, _("failed to access %s"), quoteaf (file));          Line 160
  return is_a_dir;                                                              Line 161
}                                                                               Block 5
                                                                                
/* Move SOURCE onto DEST.  Handles cross-file-system moves.                     
   If SOURCE is a directory, DEST must not exist.                               
   Return true if successful.  */                                               
                                                                                
static bool                                                                     Line 168
do_move (const char *source, const char *dest, const struct cp_options *x)      Line 169
{                                                                               
  bool copy_into_self;                                                          Line 171
  bool rename_succeeded;                                                        Line 172
  bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded);  Line 173
                                                                                
  if (ok)                                                                       Line 175
    {                                                                           
      char const *dir_to_remove;                                                Line 177
      if (copy_into_self)                                                       Line 178
        {                                                                       
          /* In general, when copy returns with copy_into_self set, SOURCE is   
             the same as, or a parent of DEST.  In this case we know it's a     
             parent.  It doesn't make sense to move a directory into itself, and
             besides in some situations doing so would give highly nonintuitive 
             results.  Run this 'mkdir b; touch a c; mv * b' in an empty        
             directory.  Here's the result of running echo $(find b -print):    
             b b/a b/b b/b/a b/c.  Notice that only file 'a' was copied         
             into b/b.  Handle this by giving a diagnostic, removing the        
             copied-into-self directory, DEST ('b/b' in the example),           
             and failing.  */                                                   
                                                                                
          dir_to_remove = NULL;                                                 Line 191
          ok = false;                                                           Line 192
        }                                                                       
      else if (rename_succeeded)                                                Line 194
        {                                                                       
          /* No need to remove anything.  SOURCE was successfully               
             renamed to DEST.  Or the user declined to rename a file.  */       
          dir_to_remove = NULL;                                                 Line 198
        }                                                                       
      else                                                                      Line 200
        {                                                                       
          /* This may mean SOURCE and DEST referred to different devices.       
             It may also conceivably mean that even though they referred        
             to the same device, rename wasn't implemented for that device.     
                                                                                
             E.g., (from Joel N. Weber),                                        
             [...] there might someday be cases where you can't rename          
             but you can copy where the device name is the same, especially     
             on Hurd.  Consider an ftpfs with a primitive ftp server that       
             supports uploading, downloading and deleting, but not renaming.    
                                                                                
             Also, note that comparing device numbers is not a reliable         
             check for 'can-rename'.  Some systems can be set up so that        
             files from many different physical devices all have the same       
             st_dev field.  This is a feature of some NFS mounting              
             configurations.                                                    
                                                                                
             We reach this point if SOURCE has been successfully copied         
             to DEST.  Now we have to remove SOURCE.                            
                                                                                
             This function used to resort to copying only when rename           
             failed and set errno to EXDEV.  */                                 
                                                                                
          dir_to_remove = source;                                               Line 224
        }                                                                       
                                                                                
      if (dir_to_remove != NULL)                                                Line 227
        {                                                                       
          struct rm_options rm_options;                                         Line 229
          enum RM_status status;                                                Line 230
          char const *dir[2];                                                   Line 231
                                                                                
          rm_option_init (&rm_options);                                         Line 233
          rm_options.verbose = x->verbose;                                      Line 234
          dir[0] = dir_to_remove;                                               Line 235
          dir[1] = NULL;                                                        Line 236
                                                                                
          status = rm ((void*) dir, &rm_options);                               Line 238
          assert (VALID_STATUS (status));                                       Line 239
          if (status == RM_ERROR)                                               Line 240
            ok = false;                                                         Line 241
        }                                                                       
    }                                                                           
                                                                                
  return ok;                                                                    Line 245
}                                                                               Block 6
                                                                                
/* Move file SOURCE onto DEST.  Handles the case when DEST is a directory.      
   Treat DEST as a directory if DEST_IS_DIR.                                    
   Return true if successful.  */                                               
                                                                                
static bool                                                                     Line 252
movefile (char *source, char *dest, bool dest_is_dir,                           Line 253
          const struct cp_options *x)                                           Line 254
{                                                                               
  bool ok;                                                                      Line 256
                                                                                
  /* This code was introduced to handle the ambiguity in the semantics          
     of mv that is induced by the varying semantics of the rename function.     
     Some systems (e.g., GNU/Linux) have a rename function that honors a        
     trailing slash, while others (like Solaris 5,6,7) have a rename            
     function that ignores a trailing slash.  I believe the GNU/Linux           
     rename semantics are POSIX and susv2 compliant.  */                        
                                                                                
  if (remove_trailing_slashes)                                                  Line 265
    strip_trailing_slashes (source);                                            Line 266
                                                                                
  if (dest_is_dir)                                                              Line 268
    {                                                                           
      /* Treat DEST as a directory; build the full filename.  */                
      char const *src_basename = last_component (source);                       Line 271
      char *new_dest = file_name_concat (dest, src_basename, NULL);             Line 272
      strip_trailing_slashes (new_dest);                                        Line 273
      ok = do_move (source, new_dest, x);                                       Line 274
      free (new_dest);                                                          Line 275
    }                                                                           
  else                                                                          Line 277
    {                                                                           
      ok = do_move (source, dest, x);                                           Line 279
    }                                                                           
                                                                                
  return ok;                                                                    Line 282
}                                                                               Block 7
                                                                                
void                                                                            Line 285
usage (int status)                                                              Line 286
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 288
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 290
    {                                                                           
      printf (_("\                                                              Line 292
Usage: %s [OPTION]... [-T] SOURCE DEST\n\                                       Line 293
  or:  %s [OPTION]... SOURCE... DIRECTORY\n\                                    Line 294
  or:  %s [OPTION]... -t DIRECTORY SOURCE...\n\                                 Line 295
"),                                                                             Line 296
              program_name, program_name, program_name);                        Line 297
      fputs (_("\                                                               Line 298
Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\                       Line 299
"), stdout);                                                                    Line 300
                                                                                
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 304
      --backup[=CONTROL]       make a backup of each existing destination file\ Line 305
\n\                                                                             
  -b                           like --backup but does not accept an argument\n\ Line 307
  -f, --force                  do not prompt before overwriting\n\              Line 308
  -i, --interactive            prompt before overwrite\n\                       Line 309
  -n, --no-clobber             do not overwrite an existing file\n\             Line 310
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ Line 311
"), stdout);                                                                    Line 312
      fputs (_("\                                                               Line 313
      --strip-trailing-slashes  remove any trailing slashes from each SOURCE\n\ Line 314
                                 argument\n\                                    Line 315
  -S, --suffix=SUFFIX          override the usual backup suffix\n\              Line 316
"), stdout);                                                                    Line 317
      fputs (_("\                                                               Line 318
  -t, --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\ Line 319
  -T, --no-target-directory    treat DEST as a normal file\n\                   Line 320
  -u, --update                 move only when the SOURCE file is newer\n\       Line 321
                                 than the destination file or when the\n\       Line 322
                                 destination file is missing\n\                 Line 323
  -v, --verbose                explain what is being done\n\                    Line 324
  -Z, --context                set SELinux security context of destination\n\   Line 325
                                 file to default type\n\                        Line 326
"), stdout);                                                                    Line 327
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 328
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 329
      emit_backup_suffix_note ();                                               Line 330
      emit_ancillary_info (PROGRAM_NAME);                                       Line 331
    }                                                                           
  exit (status);                                                                Line 333
}                                                                               Block 8
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 337
{                                                                               
  int c;                                                                        Line 339
  bool ok;                                                                      Line 340
  bool make_backups = false;                                                    Line 341
  char const *backup_suffix = NULL;                                             Line 342
  char *version_control_string = NULL;                                          Line 343
  struct cp_options x;                                                          Line 344
  char *target_directory = NULL;                                                Line 345
  bool no_target_directory = false;                                             Line 346
  int n_files;                                                                  Line 347
  char **file;                                                                  Line 348
  bool selinux_enabled = (0 < is_selinux_enabled ());                           ...!common auto-comment...
                                                                                
  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_stdin);                                                         Close stdout on exit (see gnulib)
                                                                                
  cp_option_init (&x);                                                          Line 359
                                                                                
  /* Try to disable the ability to unlink a directory.  */                      
  priv_set_remove_linkdir ();                                                   Line 362
                                                                                
  while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))     Line 364
         != -1)                                                                 Line 365
    {                                                                           
      switch (c)                                                                Line 367
        {                                                                       
        case 'b':                                                               Line 369
          make_backups = true;                                                  Line 370
          if (optarg)                                                           Line 371
            version_control_string = optarg;                                    Line 372
          break;                                                                Line 373
        case 'f':                                                               Line 374
          x.interactive = I_ALWAYS_YES;                                         Line 375
          break;                                                                Line 376
        case 'i':                                                               Line 377
          x.interactive = I_ASK_USER;                                           Line 378
          break;                                                                Line 379
        case 'n':                                                               Line 380
          x.interactive = I_ALWAYS_NO;                                          Line 381
          break;                                                                Line 382
        case STRIP_TRAILING_SLASHES_OPTION:                                     Line 383
          remove_trailing_slashes = true;                                       Line 384
          break;                                                                Line 385
        case 't':                                                               Line 386
          if (target_directory)                                                 Line 387
            die (EXIT_FAILURE, 0, _("multiple target directories specified"));  Line 388
          else                                                                  Line 389
            {                                                                   
              struct stat st;                                                   Line 391
              if (stat (optarg, &st) != 0)                                      Line 392...!syscalls auto-comment...
                die (EXIT_FAILURE, errno, _("failed to access %s"),             Line 393
                     quoteaf (optarg));                                         Line 394
              if (! S_ISDIR (st.st_mode))                                       Line 395
                die (EXIT_FAILURE, 0, _("target %s is not a directory"),        Line 396
                     quoteaf (optarg));                                         Line 397
            }                                                                   
          target_directory = optarg;                                            Line 399
          break;                                                                Line 400
        case 'T':                                                               Line 401
          no_target_directory = true;                                           Line 402
          break;                                                                Line 403
        case 'u':                                                               Line 404
          x.update = true;                                                      Line 405
          break;                                                                Line 406
        case 'v':                                                               Line 407
          x.verbose = true;                                                     Line 408
          break;                                                                Line 409
        case 'S':                                                               Line 410
          make_backups = true;                                                  Line 411
          backup_suffix = optarg;                                               Line 412
          break;                                                                Line 413
        case 'Z':                                                               Line 414
          /* As a performance enhancement, don't even bother trying             
             to "restorecon" when not on an selinux-enabled kernel.  */         
          if (selinux_enabled)                                                  Line 417
            {                                                                   
              x.preserve_security_context = false;                              Line 419
              x.set_security_context = true;                                    Line 420
            }                                                                   
          break;                                                                Line 422
        case_GETOPT_HELP_CHAR;                                                  Line 423
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 424
        default:                                                                Line 425
          usage (EXIT_FAILURE);                                                 Line 426
        }                                                                       
    }                                                                           
                                                                                
  n_files = argc - optind;                                                      Line 430
  file = argv + optind;                                                         Line 431
                                                                                
  if (n_files <= !target_directory)                                             Line 433
    {                                                                           
      if (n_files <= 0)                                                         Line 435
        error (0, 0, _("missing file operand"));                                Line 436
      else                                                                      Line 437
        error (0, 0, _("missing destination file operand after %s"),            Line 438
               quoteaf (file[0]));                                              Line 439
      usage (EXIT_FAILURE);                                                     Line 440
    }                                                                           
                                                                                
  if (no_target_directory)                                                      Line 443
    {                                                                           
      if (target_directory)                                                     Line 445
        die (EXIT_FAILURE, 0,                                                   Line 446
             _("cannot combine --target-directory (-t) "                        Line 447
               "and --no-target-directory (-T)"));                              Line 448
      if (2 < n_files)                                                          Line 449
        {                                                                       
          error (0, 0, _("extra operand %s"), quoteaf (file[2]));               Line 451
          usage (EXIT_FAILURE);                                                 Line 452
        }                                                                       
    }                                                                           
  else if (!target_directory)                                                   Line 455
    {                                                                           
      assert (2 <= n_files);                                                    Line 457
      if (n_files == 2)                                                         Line 458
        x.rename_errno = (renameatu (AT_FDCWD, file[0], AT_FDCWD, file[1],      Line 459
                                     RENAME_NOREPLACE)                          Line 460
                          ? errno : 0);                                         Line 461
      if (x.rename_errno != 0 && target_directory_operand (file[n_files - 1]))  Line 462
        {                                                                       
          x.rename_errno = -1;                                                  Line 464
          target_directory = file[--n_files];                                   Line 465
        }                                                                       
      else if (2 < n_files)                                                     Line 467
        die (EXIT_FAILURE, 0, _("target %s is not a directory"),                Line 468
             quoteaf (file[n_files - 1]));                                      Line 469
    }                                                                           
                                                                                
  if (x.interactive == I_ALWAYS_NO)                                             Line 472
    x.update = false;                                                           Line 473
                                                                                
  if (make_backups && x.interactive == I_ALWAYS_NO)                             Line 475
    {                                                                           
      error (0, 0,                                                              Line 477
             _("options --backup and --no-clobber are mutually exclusive"));    Line 478
      usage (EXIT_FAILURE);                                                     Line 479
    }                                                                           
                                                                                
  x.backup_type = (make_backups                                                 Line 482
                   ? xget_version (_("backup type"),                            Line 483
                                   version_control_string)                      Line 484
                   : no_backups);                                               Line 485
  set_simple_backup_suffix (backup_suffix);                                     Line 486
                                                                                
  hash_init ();                                                                 Line 488
                                                                                
  if (target_directory)                                                         Line 490
    {                                                                           
      /* Initialize the hash table only if we'll need it.                       
         The problem it is used to detect can arise only if there are           
         two or more files to move.  */                                         
      if (2 <= n_files)                                                         Line 495
        dest_info_init (&x);                                                    Line 496
                                                                                
      ok = true;                                                                Line 498
      for (int i = 0; i < n_files; ++i)                                         Line 499
        {                                                                       
          x.last_file = i + 1 == n_files;                                       Line 501
          ok &= movefile (file[i], target_directory, true, &x);                 Line 502
        }                                                                       
    }                                                                           
  else                                                                          Line 505
    {                                                                           
      x.last_file = true;                                                       Line 507
      ok = movefile (file[0], file[1], false, &x);                              Line 508
    }                                                                           
                                                                                
  return ok ? EXIT_SUCCESS : EXIT_FAILURE;                                      Line 511
}                                                                               Block 9