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