home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / a / bin / fileutil.12 / fileutil / fileutils-3.12 / src / mv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  11.2 KB  |  493 lines

  1. /* mv -- move or rename files
  2.    Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Options:
  19.    -f, --force        Assume a 'y' answer to all questions it would
  20.             normally ask, and not ask the questions.
  21.  
  22.    -i, --interactive    Require confirmation from the user before
  23.             performing any move that would destroy an
  24.             existing file.
  25.  
  26.    -u, --update        Do not move a nondirectory that has an
  27.             existing destination with the same or newer
  28.             modification time.
  29.  
  30.    -v, --verbose        List the name of each file as it is moved, and
  31.             the name it is moved to.
  32.  
  33.    -b, --backup
  34.    -S, --suffix
  35.    -V, --version-control
  36.             Backup file creation.
  37.  
  38.    Written by Mike Parker and David MacKenzie */
  39.  
  40. #ifdef _AIX
  41.  #pragma alloca
  42. #endif
  43.  
  44. #include <config.h>
  45. #include <stdio.h>
  46. #include <getopt.h>
  47. #include <sys/types.h>
  48. #include "system.h"
  49. #include "backupfile.h"
  50. #include "version.h"
  51. #include "safe-lstat.h"
  52.  
  53. #ifndef _POSIX_VERSION
  54. uid_t geteuid ();
  55. #endif
  56.  
  57. char *basename ();
  58. enum backup_type get_version ();
  59. int isdir ();
  60. int yesno ();
  61. void error ();
  62. int safe_read ();
  63. int full_write ();
  64. void strip_trailing_slashes ();
  65. int eaccess_stat ();
  66. char *stpcpy ();
  67.  
  68. static int copy_reg ();
  69. static int do_move ();
  70. static int movefile ();
  71. static void usage ();
  72.  
  73. /* The name this program was run with. */
  74. char *program_name;
  75.  
  76. /* If nonzero, query the user before overwriting files. */
  77. static int interactive;
  78.  
  79. /* If nonzero, do not query the user before overwriting unwritable
  80.    files. */
  81. static int override_mode;
  82.  
  83. /* If nonzero, do not move a nondirectory that has an existing destination
  84.    with the same or newer modification time. */
  85. static int update = 0;
  86.  
  87. /* If nonzero, list each file as it is moved. */
  88. static int verbose;
  89.  
  90. /* If nonzero, stdin is a tty. */
  91. static int stdin_tty;
  92.  
  93. /* This process's effective user ID.  */
  94. static uid_t myeuid;
  95.  
  96. /* If non-zero, display usage information and exit.  */
  97. static int show_help;
  98.  
  99. /* If non-zero, print the version on standard output and exit.  */
  100. static int show_version;
  101.  
  102. static struct option const long_options[] =
  103. {
  104.   {"backup", no_argument, NULL, 'b'},
  105.   {"force", no_argument, NULL, 'f'},
  106.   {"interactive", no_argument, NULL, 'i'},
  107.   {"suffix", required_argument, NULL, 'S'},
  108.   {"update", no_argument, &update, 1},
  109.   {"verbose", no_argument, &verbose, 1},
  110.   {"version-control", required_argument, NULL, 'V'},
  111.   {"help", no_argument, &show_help, 1},
  112.   {"version", no_argument, &show_version, 1},
  113.   {NULL, 0, NULL, 0}
  114. };
  115.  
  116. void
  117. main (argc, argv)
  118.      int argc;
  119.      char **argv;
  120. {
  121.   int c;
  122.   int errors;
  123.   int make_backups = 0;
  124.   char *version;
  125.  
  126.   version = getenv ("SIMPLE_BACKUP_SUFFIX");
  127.   if (version)
  128.     simple_backup_suffix = version;
  129.   version = getenv ("VERSION_CONTROL");
  130.   program_name = argv[0];
  131.   myeuid = geteuid ();
  132.   interactive = override_mode = verbose = update = 0;
  133.   errors = 0;
  134.  
  135.   while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options, (int *) 0))
  136.      != EOF)
  137.     {
  138.       switch (c)
  139.     {
  140.     case 0:
  141.       break;
  142.     case 'b':
  143.       make_backups = 1;
  144.       break;
  145.     case 'f':
  146.       interactive = 0;
  147.       override_mode = 1;
  148.       break;
  149.     case 'i':
  150.       interactive = 1;
  151.       override_mode = 0;
  152.       break;
  153.     case 'u':
  154.       update = 1;
  155.       break;
  156.     case 'v':
  157.       verbose = 1;
  158.       break;
  159.     case 'S':
  160.       simple_backup_suffix = optarg;
  161.       break;
  162.     case 'V':
  163.       version = optarg;
  164.       break;
  165.     default:
  166.       usage (1);
  167.     }
  168.     }
  169.  
  170.   if (show_version)
  171.     {
  172.       printf ("%s\n", version_string);
  173.       exit (0);
  174.     }
  175.  
  176.   if (show_help)
  177.     usage (0);
  178.  
  179.   if (argc < optind + 2)
  180.     {
  181.       error (0, 0, "missing file argument%s", argc == optind ? "s" : "");
  182.       usage (1);
  183.     }
  184.  
  185.   if (make_backups)
  186.     backup_type = get_version (version);
  187.  
  188.   stdin_tty = isatty (0);
  189.  
  190.   if (argc > optind + 2 && !isdir (argv[argc - 1]))
  191.     error (1, 0, "when moving multiple files, last argument must be a directory");
  192.  
  193.   /* Move each arg but the last onto the last. */
  194.   for (; optind < argc - 1; ++optind)
  195.     errors |= movefile (argv[optind], argv[argc - 1]);
  196.  
  197.   exit (errors);
  198. }
  199.  
  200. /* If PATH is an existing directory, return nonzero, else 0.  */
  201.  
  202. static int
  203. is_real_dir (path)
  204.      char *path;
  205. {
  206.   struct stat stats;
  207.  
  208.   return SAFE_LSTAT (path, &stats) == 0 && S_ISDIR (stats.st_mode);
  209. }
  210.  
  211. /* Move file SOURCE onto DEST.  Handles the case when DEST is a directory.
  212.    Return 0 if successful, 1 if an error occurred.  */
  213.  
  214. static int
  215. movefile (source, dest)
  216.      char *source;
  217.      char *dest;
  218. {
  219.   strip_trailing_slashes (source);
  220.  
  221.   if ((dest[strlen (dest) - 1] == '/' && !is_real_dir (source))
  222.       || isdir (dest))
  223.     {
  224.       /* Target is a directory; build full target filename. */
  225.       char *base;
  226.       char *new_dest;
  227.  
  228.       base = basename (source);
  229.       new_dest = (char *) alloca (strlen (dest) + 1 + strlen (base) + 1);
  230.       stpcpy (stpcpy (stpcpy (new_dest, dest), "/"), base);
  231.       return do_move (source, new_dest);
  232.     }
  233.   else
  234.     return do_move (source, dest);
  235. }
  236.  
  237. static struct stat dest_stats, source_stats;
  238.  
  239. /* Move SOURCE onto DEST.  Handles cross-filesystem moves.
  240.    If DEST is a directory, SOURCE must be also.
  241.    Return 0 if successful, 1 if an error occurred.  */
  242.  
  243. static int
  244. do_move (source, dest)
  245.      char *source;
  246.      char *dest;
  247. {
  248.   char *dest_backup = NULL;
  249.  
  250.   if (SAFE_LSTAT (source, &source_stats) != 0)
  251.     {
  252.       error (0, errno, "%s", source);
  253.       return 1;
  254.     }
  255.  
  256.   if (SAFE_LSTAT (dest, &dest_stats) == 0)
  257.     {
  258.       if (source_stats.st_dev == dest_stats.st_dev
  259.       && source_stats.st_ino == dest_stats.st_ino)
  260.     {
  261.       error (0, 0, "`%s' and `%s' are the same file", source, dest);
  262.       return 1;
  263.     }
  264.  
  265.       if (S_ISDIR (dest_stats.st_mode))
  266.     {
  267.       error (0, 0, "%s: cannot overwrite directory", dest);
  268.       return 1;
  269.     }
  270.  
  271.       if (!S_ISDIR (source_stats.st_mode) && update
  272.       && source_stats.st_mtime <= dest_stats.st_mtime)
  273.     return 0;
  274.  
  275.       if (!override_mode && (interactive || stdin_tty)
  276.       && eaccess_stat (&dest_stats, W_OK, dest))
  277.     {
  278.       fprintf (stderr, "%s: replace `%s', overriding mode %04o? ",
  279.            program_name, dest,
  280.            (unsigned int) (dest_stats.st_mode & 07777));
  281.       if (!yesno ())
  282.         return 0;
  283.     }
  284.       else if (interactive)
  285.     {
  286.       fprintf (stderr, "%s: replace `%s'? ", program_name, dest);
  287.       if (!yesno ())
  288.         return 0;
  289.     }
  290.  
  291.       if (backup_type != none)
  292.     {
  293.       char *tmp_backup = find_backup_file_name (dest);
  294.       if (tmp_backup == NULL)
  295.         error (1, 0, "virtual memory exhausted");
  296.       dest_backup = (char *) alloca (strlen (tmp_backup) + 1);
  297.       strcpy (dest_backup, tmp_backup);
  298.       free (tmp_backup);
  299.       if (rename (dest, dest_backup))
  300.         {
  301.           if (errno != ENOENT)
  302.         {
  303.           error (0, errno, "cannot backup `%s'", dest);
  304.           return 1;
  305.         }
  306.           else
  307.         dest_backup = NULL;
  308.         }
  309.     }
  310.     }
  311.   else if (errno != ENOENT)
  312.     {
  313.       error (0, errno, "%s", dest);
  314.       return 1;
  315.     }
  316.  
  317.   if (verbose)
  318.     printf ("%s -> %s\n", source, dest);
  319.  
  320.   if (rename (source, dest) == 0)
  321.     {
  322.       return 0;
  323.     }
  324.  
  325.   if (errno != EXDEV)
  326.     {
  327.       error (0, errno, "cannot move `%s' to `%s'", source, dest);
  328.       goto un_backup;
  329.     }
  330.  
  331.   /* rename failed on cross-filesystem link.  Copy the file instead. */
  332.  
  333.   if (copy_reg (source, dest))
  334.     goto un_backup;
  335.  
  336.   if (unlink (source))
  337.     {
  338.       error (0, errno, "cannot remove `%s'", source);
  339.       return 1;
  340.     }
  341.  
  342.   return 0;
  343.  
  344.  un_backup:
  345.   if (dest_backup)
  346.     {
  347.       if (rename (dest_backup, dest))
  348.     error (0, errno, "cannot un-backup `%s'", dest);
  349.     }
  350.   return 1;
  351. }
  352.  
  353. /* Copy regular file SOURCE onto file DEST.
  354.    Return 1 if an error occurred, 0 if successful. */
  355.  
  356. static int
  357. copy_reg (source, dest)
  358.      char *source, *dest;
  359. {
  360.   int ifd;
  361.   int ofd;
  362.   char buf[1024 * 8];
  363.   int len;            /* Number of bytes read into `buf'. */
  364.  
  365.   if (!S_ISREG (source_stats.st_mode))
  366.     {
  367.       error (0, 0, "cannot move `%s' across filesystems: Not a regular file",
  368.          source);
  369.       return 1;
  370.     }
  371.  
  372.   if (unlink (dest) && errno != ENOENT)
  373.     {
  374.       error (0, errno, "cannot remove `%s'", dest);
  375.       return 1;
  376.     }
  377.  
  378.   ifd = open (source, O_RDONLY, 0);
  379.   if (ifd < 0)
  380.     {
  381.       error (0, errno, "%s", source);
  382.       return 1;
  383.     }
  384.   ofd = open (dest, O_WRONLY | O_CREAT | O_TRUNC, 0600);
  385.   if (ofd < 0)
  386.     {
  387.       error (0, errno, "%s", dest);
  388.       close (ifd);
  389.       return 1;
  390.     }
  391.  
  392.   while ((len = safe_read (ifd, buf, sizeof (buf))) > 0)
  393.     {
  394.       if (full_write (ofd, buf, len) < 0)
  395.     {
  396.       error (0, errno, "%s", dest);
  397.       close (ifd);
  398.       close (ofd);
  399.       unlink (dest);
  400.       return 1;
  401.     }
  402.     }
  403.   if (len < 0)
  404.     {
  405.       error (0, errno, "%s", source);
  406.       close (ifd);
  407.       close (ofd);
  408.       unlink (dest);
  409.       return 1;
  410.     }
  411.  
  412.   if (close (ifd) < 0)
  413.     {
  414.       error (0, errno, "%s", source);
  415.       close (ofd);
  416.       return 1;
  417.     }
  418.   if (close (ofd) < 0)
  419.     {
  420.       error (0, errno, "%s", dest);
  421.       return 1;
  422.     }
  423.  
  424.   /* chown turns off set[ug]id bits for non-root,
  425.      so do the chmod last.  */
  426.  
  427.   /* Try to copy the old file's modtime and access time.  */
  428.   {
  429.     struct utimbuf tv;
  430.  
  431.     tv.actime = source_stats.st_atime;
  432.     tv.modtime = source_stats.st_mtime;
  433.     if (utime (dest, &tv))
  434.       {
  435.     error (0, errno, "%s", dest);
  436.     return 1;
  437.       }
  438.   }
  439.  
  440.   /* Try to preserve ownership.  For non-root it might fail, but that's ok.
  441.      But root probably wants to know, e.g. if NFS disallows it.  */
  442.   if (chown (dest, source_stats.st_uid, source_stats.st_gid)
  443.       && (errno != EPERM || myeuid == 0))
  444.     {
  445.       error (0, errno, "%s", dest);
  446.       return 1;
  447.     }
  448.  
  449.   if (chmod (dest, source_stats.st_mode & 07777))
  450.     {
  451.       error (0, errno, "%s", dest);
  452.       return 1;
  453.     }
  454.  
  455.   return 0;
  456. }
  457.  
  458. static void
  459. usage (status)
  460.      int status;
  461. {
  462.   if (status != 0)
  463.     fprintf (stderr, "Try `%s --help' for more information.\n",
  464.          program_name);
  465.   else
  466.     {
  467.       printf ("\
  468. Usage: %s [OPTION]... SOURCE DEST\n\
  469.   or:  %s [OPTION]... SOURCE... DIRECTORY\n\
  470. ",
  471.           program_name, program_name);
  472.       printf ("\
  473. \n\
  474.   -b, --backup                 make backup before removal\n\
  475.   -f, --force                  remove existing destinations, never prompt\n\
  476.   -i, --interactive            prompt before overwrite\n\
  477.   -u, --update                 move only older or brand new files\n\
  478.   -v, --verbose                explain what is being done\n\
  479.   -S, --suffix=SUFFIX          override the usual backup suffix\n\
  480.   -V, --version-control=WORD   override the usual version control\n\
  481.       --help                   display this help and exit\n\
  482.       --version                output version information and exit\n\
  483. \n\
  484. The backup suffix is ~, unless set with SIMPLE_BACKUP_SUFFIX.  The\n\
  485. version control may be set with VERSION_CONTROL, values are:\n\
  486. \n\
  487.   t, numbered     make numbered backups\n\
  488.   nil, existing   numbered if numbered backups exist, simple otherwise\n\
  489.   never, simple   always make simple backups  \n");
  490.     }
  491.   exit (status);
  492. }
  493.