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