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