home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / f / futi14as.zip / MV.C < prev    next >
C/C++ Source or Header  |  1992-02-22  |  14KB  |  601 lines

  1. /* mv -- move or rename files
  2.    Copyright (C) 1986, 1989, 1990 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 1, 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. /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
  18.    This port is also distributed under the terms of the
  19.    GNU General Public License as published by the
  20.    Free Software Foundation.
  21.  
  22.    Please note that this file is not identical to the
  23.    original GNU release, you should have received this
  24.    code as patch to the official release.  */
  25.  
  26. #ifdef MSDOS
  27. static char RCS_Id[] =
  28.   "$Header: e:/gnu/fileutil/RCS/mv.c 1.4.0.2 90/09/19 12:11:15 tho Exp $";
  29.  
  30. static char Program_Id[] = "mv";
  31. static char RCS_Revision[] = "$Revision: 1.4.0.2 $";
  32.  
  33. #define VERSION \
  34.   "GNU %s, Version %.*s (compiled %s %s for MS-DOS)\n", Program_Id, \
  35.   (sizeof RCS_Revision - 14), (RCS_Revision + 11), __DATE__, __TIME__
  36.  
  37. #define COPYING \
  38.   "This is free software, distributed under the terms of the\n" \
  39.   "GNU General Public License.  For details, see the file COPYING.\n"
  40. #endif /* MSDOS */
  41.  
  42. /* Options:
  43.    -f, +force        Assume a 'y' answer to all questions it would
  44.             normally ask, and not ask the questions.
  45.  
  46.    -i, +interactive    Require confirmation from the user before
  47.             performing any move that would destroy an
  48.             existing file. 
  49.  
  50.    -u, +update        Do not move a nondirectory that has an
  51.             existing destination with the same or newer
  52.             modification time.  
  53.  
  54.    -v, +verbose        List the name of each file as it is moved, and
  55.             the name it is moved to. 
  56.  
  57.    -b, +backup
  58.    -S, +suffix
  59.    -V, +version-control
  60.             Backup file creation.  See README.
  61.  
  62.    Written by Mike Parker and David MacKenzie */
  63.  
  64. #include <stdio.h>
  65. #include <getopt.h>
  66. #include <errno.h>
  67. #include <sys/types.h>
  68. #include "system.h"
  69. #include "backupfile.h"
  70.  
  71. #ifdef STDC_HEADERS
  72. #include <stdlib.h>
  73. #else
  74. char *getenv ();
  75.  
  76. extern int errno;
  77. #endif
  78.  
  79. #ifdef MSDOS
  80.  
  81. #include <io.h>
  82. #include <gnulib.h>
  83.  
  84. extern  void main (int argc, char **argv);
  85. extern  void error (int status, int errnum, char *message, ...);
  86. extern  int eaccess_stat (struct stat *statp, int mode);
  87. extern  enum backup_type get_version (char *version);
  88. static  int isdir (char *fn);
  89. static  int movefile (char *from, char *to);
  90. static  int do_move (char *from, char *to);
  91. static  int copy (char *from, char *to);
  92. static  int yesno (void );
  93. static  void strip_trailing_slashes (char **path);
  94. static  int force_unlink (char *filename);
  95. static  void usage (void );
  96.  
  97. #define unlink(name)        force_unlink (name)
  98. #define rename(from, to)    (unlink (to) || rename (from, to))
  99. #define strip_trailing_slashes(path)    strip_trailing_slashes (&path)
  100.  
  101. #else /* not MSDOS */
  102.  
  103. enum backup_type get_version ();
  104. int copy ();
  105. int do_move ();
  106. int isdir ();
  107. int movefile ();
  108. int yesno ();
  109. void error ();
  110. void strip_trailing_slashes ();
  111. void usage ();
  112.  
  113. #endif /* not MSDOS */
  114.  
  115. /* The name this program was run with. */
  116. char *program_name;
  117.  
  118. /* If nonzero, query the user before overwriting files. */
  119. int interactive;
  120.  
  121. /* If nonzero, do not move a nondirectory that has an existing destination
  122.    with the same or newer modification time. */
  123. int update = 0;
  124.  
  125. /* If nonzero, list each file as it is moved. */
  126. int verbose;
  127.  
  128. struct option long_options[] =
  129. {
  130. #ifdef MSDOS
  131.   {"copying", 0, NULL, 30},
  132.   {"version", 0, NULL, 31},
  133. #endif
  134.   {"backup", 0, NULL, 'b'},
  135.   {"force", 0, NULL, 'f'},
  136.   {"interactive", 0, NULL, 'i'},
  137.   {"suffix", 1, NULL, 'S'},
  138.   {"update", 0, &update, 1},
  139.   {"verbose", 0, &verbose, 1},
  140.   {"version-control", 1, NULL, 'V'},
  141.   {NULL, 0, NULL, 0}
  142. };
  143.  
  144. void
  145. main (argc, argv)
  146.      int argc;
  147.      char **argv;
  148. {
  149.   int c;
  150.   int ind;
  151.   int errors;
  152.   int make_backups = 0;
  153.   char *version;
  154.  
  155.   version = getenv ("SIMPLE_BACKUP_SUFFIX");
  156.   if (version)
  157.     simple_backup_suffix = version;
  158.   version = getenv ("VERSION_CONTROL");
  159.   program_name = argv[0];
  160.   interactive = verbose = update = 0;
  161.   errors = 0;
  162.  
  163.   while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options, &ind))
  164.      != EOF)
  165.     {
  166.       switch (c)
  167.     {
  168.     case 0:
  169.       break;
  170.     case 'b':
  171.       make_backups = 1;
  172.       break;
  173.     case 'f':
  174.       interactive = 0;
  175.       break;
  176.     case 'i':
  177.       interactive = 1;
  178.       break;
  179.     case 'u':
  180.       update = 1;
  181.       break;
  182.     case 'v':
  183.       verbose = 1;
  184.       break;
  185.     case 'S':
  186.       simple_backup_suffix = optarg;
  187.       break;
  188.     case 'V':
  189.       version = optarg;
  190.       break;
  191. #ifdef MSDOS
  192.     case 30:
  193.       fprintf (stderr, COPYING);
  194.       exit (0);
  195.       break;
  196.     case 31:
  197.       fprintf (stderr, VERSION);
  198.       exit (0);
  199.       break;
  200. #endif
  201.     default:
  202.       usage ();
  203.     }
  204.     }
  205.   if (argc < optind + 2)
  206.     usage ();
  207.  
  208.   if (make_backups)
  209.     backup_type = get_version (version);
  210.  
  211.   strip_trailing_slashes (argv[argc - 1]);
  212.  
  213.   if (argc > optind + 2 && !isdir (argv[argc - 1]))
  214.     error (1, 0, "when moving multiple files, last argument must be a directory");
  215.  
  216.   /* Move each arg but the last onto the last. */
  217.   for (; optind < argc - 1; ++optind)
  218.     errors |= movefile (argv[optind], argv[argc - 1]);
  219.  
  220.   exit (errors);
  221. }
  222.  
  223. /* Move file FROM onto TO.  Handles the case when TO is a directory.
  224.    Return 0 if successful, 1 if an error occurred.  */
  225.  
  226. int
  227. movefile (from, to)
  228.      char *from;
  229.      char *to;
  230. {
  231.   strip_trailing_slashes (from);
  232.   if (isdir (to))
  233.     {
  234.       /* Target is a directory; build full target filename. */
  235.       char *cp;
  236.       char *newto;
  237.  
  238.       cp = rindex (from, '/');
  239.       if (cp)
  240.     cp++;
  241.       else
  242.     cp = from;
  243.  
  244.       newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1);
  245. #ifdef MSDOS
  246.       /* Here a trailing slash might still be present (needed for
  247.      stat()'ing root directories), take care of that. */
  248.       if (to[strlen(to) - 1] == '/')
  249.     sprintf (newto, "%s%s", to, cp);
  250.       else
  251. #endif /* MSDOS */
  252.       sprintf (newto, "%s/%s", to, cp);
  253.       return do_move (from, newto);
  254.     }
  255.   else
  256.     return do_move (from, to);
  257. }
  258.  
  259. struct stat to_stats, from_stats;
  260.  
  261. /* Move FROM onto TO.  Handles cross-filesystem moves.
  262.    If TO is a directory, FROM must be also.
  263.    Return 0 if successful, 1 if an error occurred.  */
  264.  
  265. int
  266. do_move (from, to)
  267.      char *from;
  268.      char *to;
  269. {
  270.   char *to_backup = NULL;
  271.  
  272.   if (lstat (from, &from_stats) != 0)
  273.     {
  274.       error (0, errno, "%s", from);
  275.       return 1;
  276.     }
  277.  
  278.   if (lstat (to, &to_stats) == 0)
  279.     {
  280. #ifdef MSDOS
  281.       if (strcmp (to, from) == 0)
  282.     {
  283.       error (0, 0, "`%s': can't move file to itself", from);
  284.       return 1;
  285.     }
  286. #else /* not MSDOS */
  287.       if (from_stats.st_dev == to_stats.st_dev
  288.       && from_stats.st_ino == to_stats.st_ino)
  289.     {
  290.       error (0, 0, "`%s' and `%s' are the same file", from, to);
  291.       return 1;
  292.     }
  293. #endif /* not MSDOS */
  294.  
  295.       if ((to_stats.st_mode & S_IFMT) == S_IFDIR)
  296.     {
  297.       error (0, 0, "%s: cannot overwrite directory", to);
  298.       return 1;
  299.     }
  300.  
  301.       if ((from_stats.st_mode & S_IFMT) != S_IFDIR && update
  302.       && from_stats.st_mtime <= to_stats.st_mtime)
  303.     return 0;
  304.  
  305.       if (interactive)
  306.     {
  307.       fprintf (stderr, "%s: replace `%s'? ", program_name, to);
  308.       if (!yesno ())
  309.         return 0;
  310.     }
  311.  
  312.       if (backup_type != none)
  313.     {
  314.       to_backup = find_backup_file_name (to);
  315.       if (to_backup == NULL)
  316.         error (1, 0, "virtual memory exhausted");
  317.       if (rename (to, to_backup))
  318.         {
  319.           if (errno != ENOENT)
  320.         {
  321.           error (0, errno, "cannot backup `%s'", to);
  322.           free (to_backup);
  323.           return 1;
  324.         }
  325.           else
  326.         {
  327.           free (to_backup);
  328.           to_backup = NULL;
  329.         }
  330.         }
  331.     }
  332.     }
  333.   else if (errno != ENOENT)
  334.     {
  335.       error (0, errno, "%s", to);
  336.       return 1;
  337.     }
  338.  
  339.   if (verbose)
  340.     printf ("%s -> %s\n", from, to);
  341.  
  342.   if (rename (from, to) == 0)
  343.     {
  344.       if (to_backup)
  345.     free (to_backup);
  346.       return 0;
  347.     }
  348.  
  349.   if (errno != EXDEV)
  350.     {
  351.       error (0, errno, "cannot move `%s' to `%s'", from, to);
  352.       goto un_backup;
  353.     }
  354.  
  355.   /* rename failed on cross-filesystem link.  Copy the file instead. */
  356.  
  357.   if (copy (from, to))
  358.     goto un_backup;
  359.   
  360.   if (to_backup)
  361.     free (to_backup);
  362.  
  363.   if (unlink (from))
  364.     {
  365.       error (0, errno, "cannot remove `%s'", from);
  366.       return 1;
  367.     }
  368.  
  369.   return 0;
  370.  
  371.  un_backup:
  372.   if (to_backup)
  373.     {
  374.       if (rename (to_backup, to))
  375.     error (0, errno, "cannot un-backup `%s'", to);
  376.       free (to_backup);
  377.     }
  378.   return 1;
  379. }
  380.  
  381. /* Copy file FROM onto file TO.
  382.    Return 1 if an error occurred, 0 if successful. */
  383.  
  384. int
  385. copy (from, to)
  386.      char *from, *to;
  387. {
  388.   int ifd;
  389.   int ofd;
  390.   char buf[1024 * 8];
  391.   int len;            /* Number of bytes read into `buf'. */
  392.   
  393.   if ((from_stats.st_mode & S_IFMT) != S_IFREG)
  394.     {
  395.       error (0, 0, "cannot move `%s' across filesystems: Not a regular file",
  396.          from);
  397.       return 1;
  398.     }
  399.   
  400.   if (unlink (to) && errno != ENOENT)
  401.     {
  402.       error (0, errno, "cannot remove `%s'", to);
  403.       return 1;
  404.     }
  405.  
  406. #ifdef MSDOS
  407.   ifd = open (from, O_RDONLY | O_BINARY, 0);
  408. #else /* not MSDOS */
  409.   ifd = open (from, O_RDONLY, 0);
  410. #endif /* not MSDOS */
  411.   if (ifd < 0)
  412.     {
  413.       error (0, errno, "%s", from);
  414.       return 1;
  415.     }
  416.  
  417. #ifdef MSDOS
  418.   ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0777);
  419. #else /* not MSDOS */
  420.   ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0777);
  421. #endif /* not MSDOS */
  422.   if (ofd < 0)
  423.     {
  424.       error (0, errno, "%s", to);
  425.       close (ifd);
  426.       return 1;
  427.     }
  428.   if (
  429. #ifdef FCHMOD_MISSING
  430.       chmod (to, from_stats.st_mode & 0777)
  431. #else
  432.       fchmod (ofd, from_stats.st_mode & 0777)
  433. #endif
  434.       )
  435.       {
  436.     error (0, errno, "%s", to);
  437.     close (ifd);
  438.     close (ofd);
  439.     unlink (to);
  440.     return 1;
  441.       }
  442.   
  443.   while ((len = read (ifd, buf, sizeof (buf))) > 0)
  444.     {
  445.       int wrote = 0;
  446.       char *bp = buf;
  447.       
  448.       do
  449.     {
  450.       wrote = write (ofd, bp, len);
  451.       if (wrote < 0)
  452.         {
  453.           error (0, errno, "%s", to);
  454.           close (ifd);
  455.           close (ofd);
  456.           unlink (to);
  457.           return 1;
  458.         }
  459.       bp += wrote;
  460.       len -= wrote;
  461.     } while (len > 0);
  462.     }
  463.   if (len < 0)
  464.     {
  465.       error (0, errno, "%s", from);
  466.       close (ifd);
  467.       close (ofd);
  468.       unlink (to);
  469.       return 1;
  470.     }
  471.   close (ifd);
  472.   close (ofd);
  473.   
  474.   /* Try to copy the old file's modtime and access time.  */
  475.   {
  476.     struct utimbuf tv;
  477.  
  478.     tv.actime = from_stats.st_atime;
  479.     tv.modtime = from_stats.st_mtime;
  480.     if (utime (to, &tv))
  481.       {
  482.     error (0, errno, "%s", to);
  483.     return 1;
  484.       }
  485.   }
  486.   return 0;
  487. }
  488.  
  489. /* Read one line from standard input
  490.    and return nonzero if that line begins with y or Y,
  491.    otherwise return 0. */
  492.  
  493. int
  494. yesno ()
  495. {
  496.   int c;
  497.   int rv;
  498.  
  499.   fflush (stderr);
  500.   c = getchar ();
  501.   rv = (c == 'y') || (c == 'Y');
  502.   while (c != EOF && c != '\n')
  503.     c = getchar ();
  504.  
  505.   return rv;
  506. }
  507.  
  508. /* Return nonzero if FN is a directory or a symlink to a directory,
  509.    zero if not. */
  510.  
  511. int
  512. isdir (fn)
  513.      char *fn;
  514. {
  515.   struct stat stats;
  516.  
  517.   return (stat (fn, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR);
  518. }
  519.  
  520. /* Remove trailing slashes from PATH. */
  521.  
  522.  
  523. #ifdef MSDOS
  524. #undef strip_trailing_slashes
  525.  
  526. void
  527. strip_trailing_slashes (char **path)
  528. {
  529.   char *new_path = _fullpath (NULL, *path, 0);
  530.   free (*path);
  531.   *path = msdos_format_filename (new_path);
  532. }
  533.  
  534. #else /* not MSDOS */
  535.  
  536. void
  537. strip_trailing_slashes (path)
  538.      char *path;
  539. {
  540.   int last;
  541.  
  542.   last = strlen (path) - 1;
  543.   while (last > 0 && path[last] == '/')
  544.     path[last--] = '\0';
  545. }
  546.  
  547. #endif /* not MSDOS */
  548.  
  549. void
  550. usage ()
  551. {
  552. #ifdef MSDOS
  553.   fprintf (stderr, "\
  554. Usage: %s [-bfiuv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  555.        [+backup] [+force] [+interactive] [+update] [+verbose]\n\
  556.        [+suffix backup-suffix] [+version-control {numbered,existing,simple}]\n\
  557.        [+copying] [+version] source dest\n\
  558. \n\
  559.        %s [-bfiuv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  560.        [+backup] [+force] [+interactive] [+update] [+verbose]\n\
  561.        [+suffix backup-suffix] [+version-control {numbered,existing,simple}]\n\
  562.        [+copying] [+version] source... directory\n",
  563.        program_name, program_name);
  564. #else /* not MSDOS */
  565.   fprintf (stderr, "\
  566. Usage: %s [-bfiuv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  567.        [+backup] [+force] [+interactive] [+update] [+verbose]\n\
  568.        [+suffix backup-suffix] [+version-control {numbered,existing,simple}]\n\
  569.        source dest\n\
  570. \n\
  571.        %s [-bfiuv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  572.        [+backup] [+force] [+interactive] [+update] [+verbose]\n\
  573.        [+suffix backup-suffix] [+version-control {numbered,existing,simple}]\n\
  574.        source... directory\n",
  575.        program_name, program_name);
  576. #endif /* not MSDOS */
  577.   exit (1);
  578. }
  579.  
  580. #ifdef MSDOS
  581. #undef unlink                    /* nasty tricks ... */
  582.  
  583. int
  584. force_unlink (char *filename)
  585. {
  586.   if (access (filename, 0))    /* file doesn't exist, pretend success */
  587.     return 0;
  588.   else
  589.     {
  590.       if (access (filename, 2)              /* no write permission */
  591.       && chmod (filename, S_IREAD|S_IWRITE))  /* can't force it ...  */
  592.     {
  593.       error (0, errno, "can't force write permission for %s", filename);
  594.       return -1;
  595.     }
  596.       else
  597.     return unlink (filename);
  598.     }
  599. }
  600. #endif /* MSDOS */
  601.