bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

proposed cp/ln/mv/install option: --no-target-directory


From: Paul Eggert
Subject: proposed cp/ln/mv/install option: --no-target-directory
Date: Tue, 29 Jun 2004 00:23:29 -0700
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

I think we've pretty much beaten the "ln x d/" discussion to death, so
with its lessons in mind here's a proposed patch to add a
--no-target-directory option to cp, ln, mv, and install.  I hope the
proposed doc change justifies it sufficiently (if not, then the
documentation needs further improving :-).

If you accept this patch, I'm thinking of following up with a further
suggestion: to add short options -t and -T for --target-directory and
--no-target-directory, with the eventual dream of getting these
options into POSIX.

This patch assumes the "install --target-directory" patch that I
submitted a couple of days ago
<http://lists.gnu.org/archive/html/bug-coreutils/2004-06/msg00153.html>.

2004-06-29  Paul Eggert  <address@hidden>

        Add support for --no-target-directory option.

        * NEWS: Document it.
        * doc/coreutils.texi (Common options, Target directory, cp
        invocation, install invocation, mv invocation, ln invocation):
        Likewise.
        (link invocation): Explain how to rewrite link using ln now
        that we have --no-target-directory.
        (ln invocation): Explain that --no-target-directory subsumes
        --no-dereference.
        (unlink invocation): Modify wording to match new wording in
        link invocation.

        * src/cp.c (NO_TARGET_DIRECTORY_OPTION): New constant.
        (long_opts, usage, do_copy, main): Add support for
        --no-target-directory,
        * src/install.c (NO_TARGET_DIRECTORY_OPTION, long_options, main,
        usage): Likewise.
        * src/ln.c (NO_TARGET_DIRECTORY_OPTION, long_options, usage,
        main): Likewise.
        * src/mv.c (NO_TARGET_DIRECTORY_OPTION, long_options, usage,
        main): Likewise.
        * src/mv.c (enum): Sort values.
        
--- cu-remove/NEWS      2004-06-28 12:26:57 -0700
+++ cu-no-target-directory/NEWS 2004-06-28 23:57:44 -0700
@@ -75,6 +75,8 @@ GNU coreutils NEWS                      
   pwd now works even when run from a working directory whose name
   is longer than PATH_MAX.
 
+  cp, install, ln, and mv have a new --no-target-directory option.
+
   cp -pu and mv -u (when copying) now don't bother to update the
   destination if the resulting time stamp would be no newer than the
   preexisting time stamp.  This saves work in the common case when
--- cu-remove/doc/coreutils.texi        2004-06-25 21:47:22 -0700
+++ cu-no-target-directory/doc/coreutils.texi   2004-06-29 00:03:02 -0700
@@ -530,6 +530,15 @@ Specify the destination @var{directory}.
 @xref{Target directory}.
 @end macro
 
address@hidden optNoTargetDirectory
address@hidden --no-target-directory
address@hidden --no-target-directory
address@hidden target directory
address@hidden destination directory
+Do not treat the last operand specially when it is a directory or a
+symbolic link to a directory.  @xref{Target directory}.
address@hidden macro
+
 @macro optSi
 @itemx --si
 @opindex --si
@@ -915,17 +924,40 @@ equivalent to @option{--block-size=si}.
 
 @cindex target directory
 
-Some @sc{gnu} programs (at least @command{cp}, @command{install},
address@hidden, and @command{mv}) allow you to specify the target directory
-via this option:
+The @command{cp}, @command{install}, @command{ln}, and @command{mv}
+commands normally treat the last operand specially when it is a
+directory or a symbolic link to a directory.  For example, @samp{cp
+source dest} is equivalent to @samp{cp source dest/source} if
address@hidden is a directory.  Sometimes this behavior is not exactly
+what is wanted, so these commands support the following options to
+allow more fine-grained control:
 
 @table @samp
 
address@hidden --no-target-directory
address@hidden --no-target-directory
address@hidden target directory
address@hidden destination directory
+Do not treat the last operand specially when it is a directory or a
+symbolic link to a directory.  This can help avoid race conditions in
+programs that operate in a shared area.  For example, when the command
address@hidden /tmp/source /tmp/dest} succeeds, there is no guarantee that
address@hidden/tmp/source} was renamed to @file{/tmp/dest}: it could have been
+renamed to @file{/tmp/dest/source} instead, if some other process
+created @file{/tmp/dest} as a directory.  However, if @file{mv
+--no-target-directory /tmp/source /tmp/dest} succeeds, there is no
+question that @file{/tmp/source} was renamed to @file{/tmp/dest}.
+
+In the opposite situation, where you want the last operand to be
+treated as a directory and want a diagnostic otherwise, you can use
+the @option{--target-directory} option instead.
+
 @itemx @address@hidden@var{directory}}
 @opindex --target-directory
 @cindex target directory
 @cindex destination directory
-Specify the destination @var{directory}.
+Use @var{directory} as the directory component of each destination
+file name.
 
 The interface for most programs is that after processing options and a
 finite (possibly zero) number of fixed-position arguments, the remaining
@@ -972,6 +1004,10 @@ find . -mindepth 1 -maxdepth 1 -print0 \
 
 @end table
 
address@hidden
+The @option{--target-directory} and @option{--no-target-directory}
+options cannot be combined.
+
 @node Trailing slashes
 @section Trailing slashes
 
@@ -6261,6 +6297,8 @@ Make hard links instead of copies of non
 @opindex --dereference
 Always follow symbolic links.
 
address@hidden
+
 @item -P
 @itemx --no-dereference
 @opindex -P
@@ -6780,6 +6818,8 @@ which can be either an octal number, or 
 permissions}).  The default mode is @samp{u=rwx,go=rx}---read, write,
 and execute for the owner, and read and execute for group and other.
 
address@hidden
+
 @item -o @var{owner}
 @itemx address@hidden
 @opindex -o
@@ -6896,6 +6936,8 @@ Prompt whether to overwrite each existin
 of its permissions.  If the response does not begin with @samp{y} or
 @samp{Y}, the file is skipped.
 
address@hidden
+
 @itemx @address@hidden@var{how}]}
 @opindex --reply
 @cindex interactivity
@@ -7308,6 +7350,8 @@ Besides directories, other special file 
 It is a minimalist interface to the system-provided
 @code{link} function.  @xref{Hard Links, , , libc,
 The GNU C Library Reference Manual}.
+It avoids the bells and whistles of the more commonly-used
address@hidden command (@pxref{ln invocation}).
 Synopsis:
 
 @example
@@ -7319,6 +7363,12 @@ must specify a nonexistent entry in an e
 @command{link} simply calls @code{link (@var{filename}, @var{linkname})}
 to create the link.
 
+On a @acronym{GNU} system, this command acts like @samp{ln --directory
+--no-target-directory @var{filename} @var{linkname}}.  However, the
address@hidden and @option{--no-target-directory} options are
+not specified by @acronym{POSIX}, and the @command{link} command is
+more portable in practice.
+
 @exitstatus
 
 
@@ -7346,7 +7396,7 @@ ln address@hidden@dots{} @address@hidden
 @item If the last argument names an existing directory, @command{ln} creates a
 link to each @var{target} file in that directory, using the
 @var{target}s' names.  (But see the description of the
address@hidden option below.)
address@hidden and @option{--no-target-directory} options below.)
 
 @item If two filenames are given, @command{ln} creates a link from the
 second to the first.
@@ -7417,8 +7467,8 @@ Prompt whether to remove existing destin
 @itemx --no-dereference
 @opindex -n
 @opindex --no-dereference
-When given an explicit destination that is a symlink to a directory,
-treat that destination as if it were a normal file.
+Do not treat the last operand specially when it is a symbolic link to
+a directory.  Instead, treat it as if it were a normal file.
 
 When the destination is an actual directory (not a symlink to one),
 there is no ambiguity.  The link is created in that directory.
@@ -7431,6 +7481,11 @@ must delete or backup that symlink befor
 The default is to treat a destination that is a symlink to a directory
 just like a directory.
 
+This option is weaker than the @option{--no-target-directory} option,
+and has no effect if @option{--no-target-directory} is also given.
+
address@hidden
+
 @item -s
 @itemx --symbolic
 @opindex -s
@@ -7761,6 +7816,8 @@ Give a diagnostic for each successful re
 It is a minimalist interface to the system-provided
 @code{unlink} function.  @xref{Deleting Files, , , libc,
 The GNU C Library Reference Manual}.  Synopsis:
+It avoids the bells and whistles of the more commonly-used
address@hidden command (@pxref{rm invocation}).
 
 @example
 unlink @var{filename}
--- cu-remove/src/cp.c  2004-06-28 12:27:01 -0700
+++ cu-no-target-directory/src/cp.c     2004-06-28 13:47:41 -0700
@@ -70,6 +70,7 @@ enum
 {
   COPY_CONTENTS_OPTION = CHAR_MAX + 1,
   NO_PRESERVE_ATTRIBUTES_OPTION,
+  NO_TARGET_DIRECTORY_OPTION,
   PARENTS_OPTION,
   PRESERVE_ATTRIBUTES_OPTION,
   REPLY_OPTION,
@@ -128,6 +129,7 @@ static struct option const long_opts[] =
   {"link", no_argument, NULL, 'l'},
   {"no-dereference", no_argument, NULL, 'P'},
   {"no-preserve", required_argument, NULL, NO_PRESERVE_ATTRIBUTES_OPTION},
+  {"no-target-directory", no_argument, NULL, NO_TARGET_DIRECTORY_OPTION},
   {"one-file-system", no_argument, NULL, 'x'},
   {"parents", no_argument, NULL, PARENTS_OPTION},
   {"path", no_argument, NULL, PARENTS_OPTION},   /* Deprecated.  */
@@ -212,6 +214,7 @@ Mandatory arguments to long options are 
   -s, --symbolic-link          make symbolic links instead of copying\n\
   -S, --suffix=SUFFIX          override the usual backup suffix\n\
       --target-directory=DIRECTORY  copy all SOURCE arguments into DIRECTORY\n\
+      --no-target-directory    treat DEST as a normal file\n\
 "), stdout);
       fputs (_("\
   -u, --update                 copy only when the SOURCE file is newer\n\
@@ -495,7 +498,7 @@ target_directory_operand (char const *fi
 
 static int
 do_copy (int n_files, char **file, const char *target_directory,
-        struct cp_options *x)
+        bool no_target_directory, struct cp_options *x)
 {
   struct stat sb;
   int new_dst = 0;
@@ -511,7 +514,19 @@ do_copy (int n_files, char **file, const
       usage (EXIT_FAILURE);
     }
 
-  if (!target_directory)
+  if (no_target_directory)
+    {
+      if (target_directory)
+       error (EXIT_FAILURE, 0,
+              _("Cannot combine --target-directory "
+                "and --no-target-directory"));
+      if (2 < n_files)
+       {
+         error (0, 0, _("extra operand %s"), quote (file[2]));
+         usage (EXIT_FAILURE);
+       }
+    }
+  else if (!target_directory)
     {
       if (2 <= n_files
          && target_directory_operand (file[n_files - 1], &sb, &new_dst))
@@ -793,6 +808,7 @@ main (int argc, char **argv)
   struct cp_options x;
   int copy_contents = 0;
   char *target_directory = NULL;
+  bool no_target_directory = false;
 
   initialize_main (&argc, &argv);
   program_name = argv[0];
@@ -881,6 +897,10 @@ main (int argc, char **argv)
          decode_preserve_arg (optarg, &x, 0);
          break;
 
+       case NO_TARGET_DIRECTORY_OPTION:
+         no_target_directory = true;
+         break;
+
        case PRESERVE_ATTRIBUTES_OPTION:
          if (optarg == NULL)
            {
@@ -1014,7 +1034,8 @@ main (int argc, char **argv)
 
   hash_init ();
 
-  exit_status = do_copy (argc - optind, argv + optind, target_directory, &x);
+  exit_status = do_copy (argc - optind, argv + optind,
+                        target_directory, no_target_directory, &x);
 
   forget_all ();
 
--- cu-remove/src/install.c     2004-06-25 23:41:22 -0700
+++ cu-no-target-directory/src/install.c        2004-06-26 00:10:56 -0700
@@ -94,7 +94,8 @@ void usage (int status);
    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
 enum
 {
-  TARGET_DIRECTORY_OPTION = CHAR_MAX + 1
+  NO_TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
+  TARGET_DIRECTORY_OPTION
 };
 
 /* The name this program was run with, for error messages. */
@@ -130,6 +131,7 @@ static struct option const long_options[
   {"directory", no_argument, NULL, 'd'},
   {"group", required_argument, NULL, 'g'},
   {"mode", required_argument, NULL, 'm'},
+  {"no-target-directory", no_argument, NULL, NO_TARGET_DIRECTORY_OPTION},
   {"owner", required_argument, NULL, 'o'},
   {"preserve-timestamps", no_argument, NULL, 'p'},
   {"strip", no_argument, NULL, 's'},
@@ -211,6 +213,7 @@ main (int argc, char **argv)
   int mkdir_and_install = 0;
   struct cp_options x;
   char const *target_directory = NULL;
+  bool no_target_directory = false;
   int n_files;
   char **file;
 
@@ -278,6 +281,9 @@ main (int argc, char **argv)
        case 'm':
          specified_mode = optarg;
          break;
+       case NO_TARGET_DIRECTORY_OPTION:
+         no_target_directory = true;
+         break;
        case 'o':
          owner_name = optarg;
          break;
@@ -339,7 +345,19 @@ main (int argc, char **argv)
       usage (EXIT_FAILURE);
     }
 
-  if (!target_directory)
+  if (no_target_directory)
+    {
+      if (target_directory)
+       error (EXIT_FAILURE, 0,
+              _("Cannot combine --target-directory "
+                "and --no-target-directory"));
+      if (2 < n_files)
+       {
+         error (0, 0, _("extra operand %s"), quote (file[2]));
+         usage (EXIT_FAILURE);
+       }
+    }
+  else if (!target_directory)
     {
       if (2 <= n_files && target_directory_operand (file[n_files - 1]))
        target_directory = file[--n_files];
@@ -679,6 +697,7 @@ Mandatory arguments to long options are 
   -s, --strip         strip symbol tables, only for 1st and 2nd formats\n\
   -S, --suffix=SUFFIX override the usual backup suffix\n\
       --target-directory=DIRECTORY  copy all SOURCE arguments into DIRECTORY\n\
+      --no-target-directory  treat DEST as a normal file\n\
   -v, --verbose       print the name of each directory as it is created\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
--- cu-remove/src/ln.c  2004-06-28 12:27:01 -0700
+++ cu-no-target-directory/src/ln.c     2004-06-28 13:42:28 -0700
@@ -43,7 +43,8 @@
    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
 enum
 {
-  TARGET_DIRECTORY_OPTION = CHAR_MAX + 1
+  NO_TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
+  TARGET_DIRECTORY_OPTION
 };
 
 int link ();                   /* Some systems don't declare this anywhere. */
@@ -126,6 +127,7 @@ static struct option const long_options[
   {"backup", optional_argument, NULL, 'b'},
   {"directory", no_argument, NULL, 'F'},
   {"no-dereference", no_argument, NULL, 'n'},
+  {"no-target-directory", no_argument, NULL, NO_TARGET_DIRECTORY_OPTION},
   {"force", no_argument, NULL, 'f'},
   {"interactive", no_argument, NULL, 'i'},
   {"suffix", required_argument, NULL, 'S'},
@@ -386,6 +388,7 @@ Mandatory arguments to long options are 
   -S, --suffix=SUFFIX         override the usual backup suffix\n\
       --target-directory=DIRECTORY  specify the DIRECTORY in which to create\n\
                                 the links\n\
+      --no-target-directory   treat LINK_NAME as a normal file\n\
   -v, --verbose               print name of each file before linking\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -417,6 +420,7 @@ main (int argc, char **argv)
   char *backup_suffix_string;
   char *version_control_string = NULL;
   char *target_directory = NULL;
+  bool no_target_directory = false;
   int n_files;
   char **file;
 
@@ -471,6 +475,9 @@ main (int argc, char **argv)
        case 'n':
          dereference_dest_dir_symlinks = 0;
          break;
+       case NO_TARGET_DIRECTORY_OPTION:
+         no_target_directory = true;
+         break;
        case 's':
 #ifdef S_ISLNK
          symbolic_link = 1;
@@ -517,7 +524,24 @@ main (int argc, char **argv)
       usage (EXIT_FAILURE);
     }
 
-  if (!target_directory)
+  if (no_target_directory)
+    {
+      if (target_directory)
+       error (EXIT_FAILURE, 0,
+              _("Cannot combine --target-directory "
+                "and --no-target-directory"));
+      if (n_files != 2)
+       {
+         if (n_files < 2)
+           error (0, 0,
+                  _("missing destination file operand after %s"),
+                  quote (file[0]));
+         else
+           error (0, 0, _("extra operand %s"), quote (file[2]));
+         usage (EXIT_FAILURE);
+       }
+    }
+  else if (!target_directory)
     {
       if (n_files < 2)
        target_directory = ".";
--- cu-remove/src/mv.c  2004-06-28 12:27:02 -0700
+++ cu-no-target-directory/src/mv.c     2004-06-28 13:43:03 -0700
@@ -49,9 +49,10 @@
    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
 enum
 {
-  TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
+  NO_TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
+  REPLY_OPTION,
   STRIP_TRAILING_SLASHES_OPTION,
-  REPLY_OPTION
+  TARGET_DIRECTORY_OPTION
 };
 
 /* The name this program was run with. */
@@ -77,6 +78,7 @@ static struct option const long_options[
   {"backup", optional_argument, NULL, 'b'},
   {"force", no_argument, NULL, 'f'},
   {"interactive", no_argument, NULL, 'i'},
+  {"no-target-directory", no_argument, NULL, NO_TARGET_DIRECTORY_OPTION},
   {"reply", required_argument, NULL, REPLY_OPTION},
   {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
   {"suffix", required_argument, NULL, 'S'},
@@ -329,6 +331,7 @@ Mandatory arguments to long options are 
 "), stdout);
       fputs (_("\
       --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
+      --no-target-directory    treat DEST as a normal file\n\
   -u, --update                 move only when the SOURCE file is newer\n\
                                  than the destination file or when the\n\
                                  destination file is missing\n\
@@ -364,6 +367,7 @@ main (int argc, char **argv)
   char *version_control_string = NULL;
   struct cp_options x;
   char *target_directory = NULL;
+  bool no_target_directory = false;
   int n_files;
   char **file;
 
@@ -408,6 +412,9 @@ main (int argc, char **argv)
        case 'i':
          x.interactive = I_ASK_USER;
          break;
+       case NO_TARGET_DIRECTORY_OPTION:
+         no_target_directory = true;
+         break;
        case REPLY_OPTION:
          x.interactive = XARGMATCH ("--reply", optarg,
                                     reply_args, reply_vals);
@@ -459,7 +466,19 @@ main (int argc, char **argv)
       usage (EXIT_FAILURE);
     }
 
-  if (!target_directory)
+  if (no_target_directory)
+    {
+      if (target_directory)
+       error (EXIT_FAILURE, 0,
+              _("Cannot combine --target-directory "
+                "and --no-target-directory"));
+      if (2 < n_files)
+       {
+         error (0, 0, _("extra operand %s"), quote (file[2]));
+         usage (EXIT_FAILURE);
+       }
+    }
+  else if (!target_directory)
     {
       if (2 <= n_files && target_directory_operand (file[n_files - 1]))
        target_directory = file[--n_files];




reply via email to

[Prev in Thread] Current Thread [Next in Thread]