(re_protect): Use ASSIGN_STRDUPA rather than alloca and strcpy.
[platform/upstream/coreutils.git] / src / cp.c
1 /* cp.c  -- file copying (main routines)
2    Copyright (C) 89, 90, 91, 1995-2003 Free Software Foundation.
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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18    Written by Torbjorn Granlund, David MacKenzie, and Jim Meyering. */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <assert.h>
24 #include <getopt.h>
25
26 #include "system.h"
27 #include "argmatch.h"
28 #include "backupfile.h"
29 #include "copy.h"
30 #include "cp-hash.h"
31 #include "error.h"
32 #include "dirname.h"
33 #include "path-concat.h"
34 #include "quote.h"
35 #include "utimens.h"
36
37 #define ASSIGN_BASENAME_STRDUPA(Dest, File_name)        \
38   do                                                    \
39     {                                                   \
40       char *tmp_abns_;                                  \
41       ASSIGN_STRDUPA (tmp_abns_, (File_name));          \
42       strip_trailing_slashes (tmp_abns_);               \
43       Dest = base_name (tmp_abns_);                     \
44     }                                                   \
45   while (0)
46
47 /* The official name of this program (e.g., no `g' prefix).  */
48 #define PROGRAM_NAME "cp"
49
50 #define AUTHORS "Torbjorn Granlund", "David MacKenzie", "Jim Meyering"
51
52 #ifndef _POSIX_VERSION
53 uid_t geteuid ();
54 #endif
55
56 /* Used by do_copy, make_path_private, and re_protect
57    to keep a list of leading directories whose protections
58    need to be fixed after copying. */
59 struct dir_attr
60 {
61   int is_new_dir;
62   int slash_offset;
63   struct dir_attr *next;
64 };
65
66 /* For long options that have no equivalent short option, use a
67    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
68 enum
69 {
70   COPY_CONTENTS_OPTION = CHAR_MAX + 1,
71   NO_PRESERVE_ATTRIBUTES_OPTION,
72   PARENTS_OPTION,
73   PRESERVE_ATTRIBUTES_OPTION,
74   REPLY_OPTION,
75   SPARSE_OPTION,
76   STRIP_TRAILING_SLASHES_OPTION,
77   TARGET_DIRECTORY_OPTION,
78   UNLINK_DEST_BEFORE_OPENING
79 };
80
81 /* Initial number of entries in each hash table entry's table of inodes.  */
82 #define INITIAL_HASH_MODULE 100
83
84 /* Initial number of entries in the inode hash table.  */
85 #define INITIAL_ENTRY_TAB_SIZE 70
86
87 /* The invocation name of this program.  */
88 char *program_name;
89
90 /* If nonzero, the command "cp x/e_file e_dir" uses "e_dir/x/e_file"
91    as its destination instead of the usual "e_dir/e_file." */
92 static int flag_path = 0;
93
94 /* Remove any trailing slashes from each SOURCE argument.  */
95 static int remove_trailing_slashes;
96
97 static char const *const sparse_type_string[] =
98 {
99   "never", "auto", "always", 0
100 };
101
102 static enum Sparse_type const sparse_type[] =
103 {
104   SPARSE_NEVER, SPARSE_AUTO, SPARSE_ALWAYS
105 };
106
107 /* Valid arguments to the `--reply' option. */
108 static char const* const reply_args[] =
109 {
110   "yes", "no", "query", 0
111 };
112
113 /* The values that correspond to the above strings. */
114 static int const reply_vals[] =
115 {
116   I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER
117 };
118
119 /* The error code to return to the system. */
120 static int exit_status = 0;
121
122 static struct option const long_opts[] =
123 {
124   {"archive", no_argument, NULL, 'a'},
125   {"backup", optional_argument, NULL, 'b'},
126   {"copy-contents", no_argument, NULL, COPY_CONTENTS_OPTION},
127   {"dereference", no_argument, NULL, 'L'},
128   {"force", no_argument, NULL, 'f'},
129   {"interactive", no_argument, NULL, 'i'},
130   {"link", no_argument, NULL, 'l'},
131   {"no-dereference", no_argument, NULL, 'P'},
132   {"no-preserve", required_argument, NULL, NO_PRESERVE_ATTRIBUTES_OPTION},
133   {"one-file-system", no_argument, NULL, 'x'},
134   {"parents", no_argument, NULL, PARENTS_OPTION},
135   {"path", no_argument, NULL, PARENTS_OPTION},   /* Deprecated.  */
136   {"preserve", optional_argument, NULL, PRESERVE_ATTRIBUTES_OPTION},
137   {"recursive", no_argument, NULL, 'R'},
138   {"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING},
139   {"reply", required_argument, NULL, REPLY_OPTION},
140   {"sparse", required_argument, NULL, SPARSE_OPTION},
141   {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
142   {"suffix", required_argument, NULL, 'S'},
143   {"symbolic-link", no_argument, NULL, 's'},
144   {"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
145   {"update", no_argument, NULL, 'u'},
146   {"verbose", no_argument, NULL, 'v'},
147   {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */
148   {GETOPT_HELP_OPTION_DECL},
149   {GETOPT_VERSION_OPTION_DECL},
150   {NULL, 0, NULL, 0}
151 };
152
153 void
154 usage (int status)
155 {
156   if (status != 0)
157     fprintf (stderr, _("Try `%s --help' for more information.\n"),
158              program_name);
159   else
160     {
161       printf (_("\
162 Usage: %s [OPTION]... SOURCE DEST\n\
163   or:  %s [OPTION]... SOURCE... DIRECTORY\n\
164   or:  %s [OPTION]... --target-directory=DIRECTORY SOURCE...\n\
165 "),
166               program_name, program_name, program_name);
167       fputs (_("\
168 Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\
169 \n\
170 "), stdout);
171       fputs (_("\
172 Mandatory arguments to long options are mandatory for short options too.\n\
173 "), stdout);
174       fputs (_("\
175   -a, --archive                same as -dpR\n\
176       --backup[=CONTROL]       make a backup of each existing destination file\n\
177   -b                           like --backup but does not accept an argument\n\
178       --copy-contents          copy contents of special files when recursive\n\
179   -d                           same as --no-dereference --preserve=link\n\
180 "), stdout);
181       fputs (_("\
182       --no-dereference         never follow symbolic links\n\
183   -f, --force                  if an existing destination file cannot be\n\
184                                  opened, remove it and try again\n\
185   -i, --interactive            prompt before overwrite\n\
186   -H                           follow command-line symbolic links\n\
187 "), stdout);
188       fputs (_("\
189   -l, --link                   link files instead of copying\n\
190   -L, --dereference            always follow symbolic links\n\
191   -p                           same as --preserve=mode,ownership,timestamps\n\
192       --preserve[=ATTR_LIST]   preserve the specified attributes (default:\n\
193                                  mode,ownership,timestamps), if possible\n\
194                                  additional attributes: links, all\n\
195 "), stdout);
196       fputs (_("\
197       --no-preserve=ATTR_LIST  don't preserve the specified attributes\n\
198       --parents                append source path to DIRECTORY\n\
199   -P                           same as `--no-dereference'\n\
200 "), stdout);
201       fputs (_("\
202   -R, -r, --recursive          copy directories recursively\n\
203       --remove-destination     remove each existing destination file before\n\
204                                  attempting to open it (contrast with --force)\n\
205 "), stdout);
206       fputs (_("\
207       --reply={yes,no,query}   specify how to handle the prompt about an\n\
208                                  existing destination file\n\
209       --sparse=WHEN            control creation of sparse files\n\
210       --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
211                                  argument\n\
212 "), stdout);
213       fputs (_("\
214   -s, --symbolic-link          make symbolic links instead of copying\n\
215   -S, --suffix=SUFFIX          override the usual backup suffix\n\
216       --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
217 "), stdout);
218       fputs (_("\
219   -u, --update                 copy only when the SOURCE file is newer\n\
220                                  than the destination file or when the\n\
221                                  destination file is missing\n\
222   -v, --verbose                explain what is being done\n\
223   -x, --one-file-system        stay on this file system\n\
224 "), stdout);
225       fputs (HELP_OPTION_DESCRIPTION, stdout);
226       fputs (VERSION_OPTION_DESCRIPTION, stdout);
227       fputs (_("\
228 \n\
229 By default, sparse SOURCE files are detected by a crude heuristic and the\n\
230 corresponding DEST file is made sparse as well.  That is the behavior\n\
231 selected by --sparse=auto.  Specify --sparse=always to create a sparse DEST\n\
232 file whenever the SOURCE file contains a long enough sequence of zero bytes.\n\
233 Use --sparse=never to inhibit creation of sparse files.\n\
234 \n\
235 "), stdout);
236       fputs (_("\
237 The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
238 The version control method may be selected via the --backup option or through\n\
239 the VERSION_CONTROL environment variable.  Here are the values:\n\
240 \n\
241 "), stdout);
242       fputs (_("\
243   none, off       never make backups (even if --backup is given)\n\
244   numbered, t     make numbered backups\n\
245   existing, nil   numbered if numbered backups exist, simple otherwise\n\
246   simple, never   always make simple backups\n\
247 "), stdout);
248       fputs (_("\
249 \n\
250 As a special case, cp makes a backup of SOURCE when the force and backup\n\
251 options are given and SOURCE and DEST are the same name for an existing,\n\
252 regular file.\n\
253 "), stdout);
254       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
255     }
256   exit (status);
257 }
258
259 /* Ensure that the parent directories of CONST_DST_PATH have the
260    correct protections, for the --parents option.  This is done
261    after all copying has been completed, to allow permissions
262    that don't include user write/execute.
263
264    SRC_OFFSET is the index in CONST_DST_PATH of the beginning of the
265    source directory name.
266
267    ATTR_LIST is a null-terminated linked list of structures that
268    indicates the end of the filename of each intermediate directory
269    in CONST_DST_PATH that may need to have its attributes changed.
270    The command `cp --parents --preserve a/b/c d/e_dir' changes the
271    attributes of the directories d/e_dir/a and d/e_dir/a/b to match
272    the corresponding source directories regardless of whether they
273    existed before the `cp' command was given.
274
275    Return 0 if the parent of CONST_DST_PATH and any intermediate
276    directories specified by ATTR_LIST have the proper permissions
277    when done, otherwise 1. */
278
279 static int
280 re_protect (const char *const_dst_path, int src_offset,
281             struct dir_attr *attr_list, const struct cp_options *x)
282 {
283   struct dir_attr *p;
284   char *dst_path;               /* A copy of CONST_DST_PATH we can change. */
285   char *src_path;               /* The source name in `dst_path'. */
286   uid_t myeuid = geteuid ();
287
288   ASSIGN_STRDUPA (dst_path, const_dst_path);
289   src_path = dst_path + src_offset;
290
291   for (p = attr_list; p; p = p->next)
292     {
293       struct stat src_sb;
294
295       dst_path[p->slash_offset] = '\0';
296
297       if ((*(x->xstat)) (src_path, &src_sb))
298         {
299           error (0, errno, _("failed to get attributes of %s"),
300                  quote (src_path));
301           return 1;
302         }
303
304       /* Adjust the times (and if possible, ownership) for the copy.
305          chown turns off set[ug]id bits for non-root,
306          so do the chmod last.  */
307
308       if (x->preserve_timestamps)
309         {
310           struct timespec timespec[2];
311
312           timespec[0].tv_sec = src_sb.st_atime;
313           timespec[0].tv_nsec = TIMESPEC_NS (src_sb.st_atim);
314           timespec[1].tv_sec = src_sb.st_mtime;
315           timespec[1].tv_nsec = TIMESPEC_NS (src_sb.st_mtim);
316
317           if (utimens (dst_path, timespec))
318             {
319               error (0, errno, _("failed to preserve times for %s"),
320                      quote (dst_path));
321               return 1;
322             }
323         }
324
325       if (x->preserve_ownership)
326         {
327           /* If non-root uses -p, it's ok if we can't preserve ownership.
328              But root probably wants to know, e.g. if NFS disallows it,
329              or if the target system doesn't support file ownership.  */
330           if (chown (dst_path, src_sb.st_uid, src_sb.st_gid)
331               && ((errno != EPERM && errno != EINVAL) || myeuid == 0))
332             {
333               error (0, errno, _("failed to preserve ownership for %s"),
334                      quote (dst_path));
335               return 1;
336             }
337         }
338
339       if (x->preserve_mode || p->is_new_dir)
340         {
341           if (chmod (dst_path, src_sb.st_mode & x->umask_kill))
342             {
343               error (0, errno, _("failed to preserve permissions for %s"),
344                      quote (dst_path));
345               return 1;
346             }
347         }
348
349       dst_path[p->slash_offset] = '/';
350     }
351   return 0;
352 }
353
354 /* Ensure that the parent directory of CONST_DIRPATH exists, for
355    the --parents option.
356
357    SRC_OFFSET is the index in CONST_DIRPATH (which is a destination
358    path) of the beginning of the source directory name.
359    Create any leading directories that don't already exist,
360    giving them permissions MODE.
361    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
362    string for printing a message after successfully making a directory.
363    The format should take two string arguments: the names of the
364    source and destination directories.
365    Creates a linked list of attributes of intermediate directories,
366    *ATTR_LIST, for re_protect to use after calling copy.
367    Sets *NEW_DST to 1 if this function creates parent of CONST_DIRPATH.
368
369    Return 0 if parent of CONST_DIRPATH exists as a directory with the proper
370    permissions when done, otherwise 1. */
371
372 /* FIXME: find a way to synch this function with the one in lib/makepath.c. */
373
374 static int
375 make_path_private (const char *const_dirpath, int src_offset, int mode,
376                    const char *verbose_fmt_string, struct dir_attr **attr_list,
377                    int *new_dst, int (*xstat)())
378 {
379   struct stat stats;
380   char *dirpath;                /* A copy of CONST_DIRPATH we can change. */
381   char *src;                    /* Source name in `dirpath'. */
382   char *dst_dirname;            /* Leading path of `dirpath'. */
383   size_t dirlen;                /* Length of leading path of `dirpath'. */
384
385   ASSIGN_STRDUPA (dirpath, const_dirpath);
386
387   src = dirpath + src_offset;
388
389   dirlen = dir_len (dirpath);
390   dst_dirname = alloca (dirlen + 1);
391   memcpy (dst_dirname, dirpath, dirlen);
392   dst_dirname[dirlen] = '\0';
393
394   *attr_list = NULL;
395
396   if ((*xstat) (dst_dirname, &stats))
397     {
398       /* Parent of CONST_DIRNAME does not exist.
399          Make all missing intermediate directories. */
400       char *slash;
401
402       slash = src;
403       while (*slash == '/')
404         slash++;
405       while ((slash = strchr (slash, '/')))
406         {
407           /* Add this directory to the list of directories whose modes need
408              fixing later. */
409           struct dir_attr *new = xmalloc (sizeof *new);
410           new->slash_offset = slash - dirpath;
411           new->next = *attr_list;
412           *attr_list = new;
413
414           *slash = '\0';
415           if ((*xstat) (dirpath, &stats))
416             {
417               /* This element of the path does not exist.  We must set
418                  *new_dst and new->is_new_dir inside this loop because,
419                  for example, in the command `cp --parents ../a/../b/c e_dir',
420                  make_path_private creates only e_dir/../a if ./b already
421                  exists. */
422               *new_dst = 1;
423               new->is_new_dir = 1;
424               if (mkdir (dirpath, mode))
425                 {
426                   error (0, errno, _("cannot make directory %s"),
427                          quote (dirpath));
428                   return 1;
429                 }
430               else
431                 {
432                   if (verbose_fmt_string != NULL)
433                     printf (verbose_fmt_string, src, dirpath);
434                 }
435             }
436           else if (!S_ISDIR (stats.st_mode))
437             {
438               error (0, 0, _("%s exists but is not a directory"),
439                      quote (dirpath));
440               return 1;
441             }
442           else
443             {
444               new->is_new_dir = 0;
445               *new_dst = 0;
446             }
447           *slash++ = '/';
448
449           /* Avoid unnecessary calls to `stat' when given
450              pathnames containing multiple adjacent slashes.  */
451           while (*slash == '/')
452             slash++;
453         }
454     }
455
456   /* We get here if the parent of `dirpath' already exists. */
457
458   else if (!S_ISDIR (stats.st_mode))
459     {
460       error (0, 0, _("%s exists but is not a directory"), quote (dst_dirname));
461       return 1;
462     }
463   else
464     {
465       *new_dst = 0;
466     }
467   return 0;
468 }
469
470 /* Scan the arguments, and copy each by calling copy.
471    Return 0 if successful, 1 if any errors occur. */
472
473 static int
474 do_copy (int n_files, char **file, const char *target_directory,
475          struct cp_options *x)
476 {
477   const char *dest;
478   struct stat sb;
479   int new_dst = 0;
480   int ret = 0;
481   int dest_is_dir = 0;
482
483   if (n_files <= 0)
484     {
485       error (0, 0, _("missing file argument"));
486       usage (EXIT_FAILURE);
487     }
488   if (n_files == 1 && !target_directory)
489     {
490       error (0, 0, _("missing destination file"));
491       usage (EXIT_FAILURE);
492     }
493
494   if (target_directory)
495     dest = target_directory;
496   else
497     {
498       dest = file[n_files - 1];
499       --n_files;
500     }
501
502   /* Initialize these hash tables only if we'll need them.
503      The problems they're used to detect can arise only if
504      there are two or more files to copy.  */
505   if (n_files >= 2)
506     {
507       dest_info_init (x);
508       src_info_init (x);
509     }
510
511   if (lstat (dest, &sb))
512     {
513       if (errno != ENOENT)
514         {
515           error (0, errno, _("accessing %s"), quote (dest));
516           return 1;
517         }
518
519       new_dst = 1;
520     }
521   else
522     {
523       struct stat sbx;
524
525       /* If `dest' is not a symlink to a nonexistent file, use
526          the results of stat instead of lstat, so we can copy files
527          into symlinks to directories. */
528       if (stat (dest, &sbx) == 0)
529         sb = sbx;
530
531       dest_is_dir = S_ISDIR (sb.st_mode);
532     }
533
534   if (!dest_is_dir)
535     {
536       if (target_directory || 1 < n_files)
537         {
538           char const *msg;
539           if (new_dst)
540             msg = N_("%s: specified destination directory does not exist");
541           else if (target_directory)
542             msg = N_("%s: specified target is not a directory");
543           else /* n_files > 1 */
544             msg =
545           N_("copying multiple files, but last argument %s is not a directory");
546
547           error (0, 0, _(msg), quote (dest));
548           usage (EXIT_FAILURE);
549         }
550     }
551
552   if (dest_is_dir)
553     {
554       /* cp file1...filen edir
555          Copy the files `file1' through `filen'
556          to the existing directory `edir'. */
557       int i;
558
559       for (i = 0; i < n_files; i++)
560         {
561           char *dst_path;
562           int parent_exists = 1; /* True if dir_name (dst_path) exists. */
563           struct dir_attr *attr_list;
564           char *arg_in_concat = NULL;
565           char *arg = file[i];
566
567           /* Trailing slashes are meaningful (i.e., maybe worth preserving)
568              only in the source file names.  */
569           if (remove_trailing_slashes)
570             strip_trailing_slashes (arg);
571
572           if (flag_path)
573             {
574               char *arg_no_trailing_slash;
575
576               /* Use `arg' without trailing slashes in constructing destination
577                  file names.  Otherwise, we can end up trying to create a
578                  directory via `mkdir ("dst/foo/"...', which is not portable.
579                  It fails, due to the trailing slash, on at least
580                  NetBSD 1.[34] systems.  */
581               ASSIGN_STRDUPA (arg_no_trailing_slash, arg);
582               strip_trailing_slashes (arg_no_trailing_slash);
583
584               /* Append all of `arg' (minus any trailing slash) to `dest'.  */
585               dst_path = path_concat (dest, arg_no_trailing_slash,
586                                       &arg_in_concat);
587               if (dst_path == NULL)
588                 xalloc_die ();
589
590               /* For --parents, we have to make sure that the directory
591                  dir_name (dst_path) exists.  We may have to create a few
592                  leading directories. */
593               parent_exists = !make_path_private (dst_path,
594                                                   arg_in_concat - dst_path,
595                                                   S_IRWXU,
596                                                   (x->verbose
597                                                    ? "%s -> %s\n" : NULL),
598                                                   &attr_list, &new_dst,
599                                                   x->xstat);
600             }
601           else
602             {
603               char *arg_base;
604               /* Append the last component of `arg' to `dest'.  */
605
606               ASSIGN_BASENAME_STRDUPA (arg_base, arg);
607               /* For `cp -R source/.. dest', don't copy into `dest/..'. */
608               dst_path = (STREQ (arg_base, "..")
609                           ? xstrdup (dest)
610                           : path_concat (dest, arg_base, NULL));
611             }
612
613           if (!parent_exists)
614             {
615               /* make_path_private failed, so don't even attempt the copy. */
616               ret = 1;
617             }
618           else
619             {
620               int copy_into_self;
621               ret |= copy (arg, dst_path, new_dst, x, &copy_into_self, NULL);
622
623               if (flag_path)
624                 {
625                   ret |= re_protect (dst_path, arg_in_concat - dst_path,
626                                      attr_list, x);
627                 }
628             }
629
630           free (dst_path);
631         }
632       return ret;
633     }
634   else /* if (n_files == 1) */
635     {
636       char *new_dest;
637       char *source;
638       int unused;
639       struct stat source_stats;
640
641       if (flag_path)
642         {
643           error (0, 0,
644                _("when preserving paths, the destination must be a directory"));
645           usage (EXIT_FAILURE);
646         }
647
648       source = file[0];
649
650       /* When the force and backup options have been specified and
651          the source and destination are the same name for an existing
652          regular file, convert the user's command, e.g.,
653          `cp --force --backup foo foo' to `cp --force foo fooSUFFIX'
654          where SUFFIX is determined by any version control options used.  */
655
656       if (x->unlink_dest_after_failed_open
657           && x->backup_type != none
658           && STREQ (source, dest)
659           && !new_dst && S_ISREG (sb.st_mode))
660         {
661           static struct cp_options x_tmp;
662
663           new_dest = find_backup_file_name (dest, x->backup_type);
664           /* Set x->backup_type to `none' so that the normal backup
665              mechanism is not used when performing the actual copy.
666              backup_type must be set to `none' only *after* the above
667              call to find_backup_file_name -- that function uses
668              backup_type to determine the suffix it applies.  */
669           x_tmp = *x;
670           x_tmp.backup_type = none;
671           x = &x_tmp;
672
673           if (new_dest == NULL)
674             xalloc_die ();
675         }
676
677       /* When the destination is specified with a trailing slash and the
678          source exists but is not a directory, convert the user's command
679          `cp source dest/' to `cp source dest/basename(source)'.  Doing
680          this ensures that the command `cp non-directory file/' will now
681          fail rather than performing the copy.  COPY diagnoses the case of
682          `cp directory non-directory'.  */
683
684       else if (dest[strlen (dest) - 1] == '/'
685           && lstat (source, &source_stats) == 0
686           && !S_ISDIR (source_stats.st_mode))
687         {
688           char *source_base;
689
690           ASSIGN_BASENAME_STRDUPA (source_base, source);
691           new_dest = alloca (strlen (dest) + strlen (source_base) + 1);
692           stpcpy (stpcpy (new_dest, dest), source_base);
693         }
694       else
695         {
696           new_dest = (char *) dest;
697         }
698
699       return copy (source, new_dest, new_dst, x, &unused, NULL);
700     }
701
702   /* unreachable */
703 }
704
705 static void
706 cp_option_init (struct cp_options *x)
707 {
708   x->copy_as_regular = 1;
709   x->dereference = DEREF_UNDEFINED;
710   x->unlink_dest_before_opening = 0;
711   x->unlink_dest_after_failed_open = 0;
712   x->hard_link = 0;
713   x->interactive = I_UNSPECIFIED;
714   x->myeuid = geteuid ();
715   x->move_mode = 0;
716   x->one_file_system = 0;
717
718   x->preserve_ownership = 0;
719   x->preserve_links = 0;
720   x->preserve_mode = 0;
721   x->preserve_timestamps = 0;
722
723   x->require_preserve = 0;
724   x->recursive = 0;
725   x->sparse_mode = SPARSE_AUTO;
726   x->symbolic_link = 0;
727   x->set_mode = 0;
728   x->mode = 0;
729
730   /* Not used.  */
731   x->stdin_tty = 0;
732
733   /* Find out the current file creation mask, to knock the right bits
734      when using chmod.  The creation mask is set to be liberal, so
735      that created directories can be written, even if it would not
736      have been allowed with the mask this process was started with.  */
737   x->umask_kill = ~ umask (0);
738
739   x->update = 0;
740   x->verbose = 0;
741   x->dest_info = NULL;
742   x->src_info = NULL;
743 }
744
745 /* Given a string, ARG, containing a comma-separated list of arguments
746    to the --preserve option, set the appropriate fields of X to ON_OFF.  */
747 static void
748 decode_preserve_arg (char const *arg, struct cp_options *x, int on_off)
749 {
750   enum File_attribute
751     {
752       PRESERVE_MODE,
753       PRESERVE_TIMESTAMPS,
754       PRESERVE_OWNERSHIP,
755       PRESERVE_LINK,
756       PRESERVE_ALL
757     };
758   static enum File_attribute const preserve_vals[] =
759     {
760       PRESERVE_MODE, PRESERVE_TIMESTAMPS,
761       PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL
762     };
763
764   /* Valid arguments to the `--preserve' option. */
765   static char const* const preserve_args[] =
766     {
767       "mode", "timestamps",
768       "ownership", "links", "all", 0
769     };
770
771   char *arg_writable = xstrdup (arg);
772   char *s = arg_writable;
773   do
774     {
775       /* find next comma */
776       char *comma = strchr (s, ',');
777       enum File_attribute val;
778
779       /* If we found a comma, put a NUL in its place and advance.  */
780       if (comma)
781         *comma++ = 0;
782
783       /* process S.  */
784       val = XARGMATCH ("--preserve", s, preserve_args, preserve_vals);
785       switch (val)
786         {
787         case PRESERVE_MODE:
788           x->preserve_mode = on_off;
789           break;
790
791         case PRESERVE_TIMESTAMPS:
792           x->preserve_timestamps = on_off;
793           break;
794
795         case PRESERVE_OWNERSHIP:
796           x->preserve_ownership = on_off;
797           break;
798
799         case PRESERVE_LINK:
800           x->preserve_links = on_off;
801           break;
802
803         case PRESERVE_ALL:
804           x->preserve_mode = on_off;
805           x->preserve_timestamps = on_off;
806           x->preserve_ownership = on_off;
807           x->preserve_links = on_off;
808           break;
809
810         default:
811           abort ();
812         }
813       s = comma;
814     }
815   while (s);
816
817   free (arg_writable);
818 }
819
820 int
821 main (int argc, char **argv)
822 {
823   int c;
824   int make_backups = 0;
825   char *backup_suffix_string;
826   char *version_control_string = NULL;
827   struct cp_options x;
828   int copy_contents = 0;
829   char *target_directory = NULL;
830
831   initialize_main (&argc, &argv);
832   program_name = argv[0];
833   setlocale (LC_ALL, "");
834   bindtextdomain (PACKAGE, LOCALEDIR);
835   textdomain (PACKAGE);
836
837   atexit (close_stdout);
838
839   cp_option_init (&x);
840
841   /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
842      we'll actually use backup_suffix_string.  */
843   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
844
845   while ((c = getopt_long (argc, argv, "abdfHilLprsuvxPRS:V:", long_opts, NULL))
846          != -1)
847     {
848       switch (c)
849         {
850         case 0:
851           break;
852
853         case SPARSE_OPTION:
854           x.sparse_mode = XARGMATCH ("--sparse", optarg,
855                                      sparse_type_string, sparse_type);
856           break;
857
858         case 'a':               /* Like -dpPR. */
859           x.dereference = DEREF_NEVER;
860           x.preserve_links = 1;
861           x.preserve_ownership = 1;
862           x.preserve_mode = 1;
863           x.preserve_timestamps = 1;
864           x.require_preserve = 1;
865           x.recursive = 1;
866           break;
867
868         case 'V':  /* FIXME: this is deprecated.  Remove it in 2001.  */
869           error (0, 0,
870                  _("warning: --version-control (-V) is obsolete;  support for\
871  it\nwill be removed in some future release.  Use --backup=%s instead."
872                    ), optarg);
873           /* Fall through.  */
874
875         case 'b':
876           make_backups = 1;
877           if (optarg)
878             version_control_string = optarg;
879           break;
880
881         case COPY_CONTENTS_OPTION:
882           copy_contents = 1;
883           break;
884
885         case 'd':
886           x.preserve_links = 1;
887           x.dereference = DEREF_NEVER;
888           break;
889
890         case 'f':
891           x.unlink_dest_after_failed_open = 1;
892           break;
893
894         case 'H':
895           x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
896           break;
897
898         case 'i':
899           x.interactive = I_ASK_USER;
900           break;
901
902         case 'l':
903           x.hard_link = 1;
904           break;
905
906         case 'L':
907           x.dereference = DEREF_ALWAYS;
908           break;
909
910         case 'P':
911           x.dereference = DEREF_NEVER;
912           break;
913
914         case NO_PRESERVE_ATTRIBUTES_OPTION:
915           decode_preserve_arg (optarg, &x, 0);
916           break;
917
918         case PRESERVE_ATTRIBUTES_OPTION:
919           if (optarg == NULL)
920             {
921               /* Fall through to the case for `p' below.  */
922             }
923           else
924             {
925               decode_preserve_arg (optarg, &x, 1);
926               x.require_preserve = 1;
927               break;
928             }
929
930         case 'p':
931           x.preserve_ownership = 1;
932           x.preserve_mode = 1;
933           x.preserve_timestamps = 1;
934           x.require_preserve = 1;
935           break;
936
937         case PARENTS_OPTION:
938           flag_path = 1;
939           break;
940
941         case 'r':
942         case 'R':
943           x.recursive = 1;
944           break;
945
946         case REPLY_OPTION:
947           x.interactive = XARGMATCH ("--reply", optarg,
948                                      reply_args, reply_vals);
949           break;
950
951         case UNLINK_DEST_BEFORE_OPENING:
952           x.unlink_dest_before_opening = 1;
953           break;
954
955         case STRIP_TRAILING_SLASHES_OPTION:
956           remove_trailing_slashes = 1;
957           break;
958
959         case 's':
960 #ifdef S_ISLNK
961           x.symbolic_link = 1;
962 #else
963           error (EXIT_FAILURE, 0,
964                  _("symbolic links are not supported on this system"));
965 #endif
966           break;
967
968         case TARGET_DIRECTORY_OPTION:
969           target_directory = optarg;
970           break;
971
972         case 'u':
973           x.update = 1;
974           break;
975
976         case 'v':
977           x.verbose = 1;
978           break;
979
980         case 'x':
981           x.one_file_system = 1;
982           break;
983
984         case 'S':
985           make_backups = 1;
986           backup_suffix_string = optarg;
987           break;
988
989         case_GETOPT_HELP_CHAR;
990
991         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
992
993         default:
994           usage (EXIT_FAILURE);
995         }
996     }
997
998   if (x.hard_link && x.symbolic_link)
999     {
1000       error (0, 0, _("cannot make both hard and symbolic links"));
1001       usage (EXIT_FAILURE);
1002     }
1003
1004   if (backup_suffix_string)
1005     simple_backup_suffix = xstrdup (backup_suffix_string);
1006
1007   x.backup_type = (make_backups
1008                    ? xget_version (_("backup type"),
1009                                    version_control_string)
1010                    : none);
1011
1012   if (x.preserve_mode == 1)
1013     x.umask_kill = ~ (mode_t) 0;
1014
1015   if (x.dereference == DEREF_UNDEFINED)
1016     {
1017       if (x.recursive)
1018         /* This is compatible with FreeBSD.  */
1019         x.dereference = DEREF_NEVER;
1020       else
1021         x.dereference = DEREF_ALWAYS;
1022     }
1023
1024   /* The key difference between -d (--no-dereference) and not is the version
1025      of `stat' to call.  */
1026
1027   if (x.dereference == DEREF_NEVER)
1028     x.xstat = lstat;
1029   else
1030     {
1031       /* For DEREF_COMMAND_LINE_ARGUMENTS, x.xstat must be stat for
1032          each command line argument, but must later be `lstat' for
1033          any symlinks that are found via recursive traversal.  */
1034       x.xstat = stat;
1035     }
1036
1037   if (x.recursive)
1038     x.copy_as_regular = copy_contents;
1039
1040   /* If --force (-f) was specified and we're in link-creation mode,
1041      first remove any existing destination file.  */
1042   if (x.unlink_dest_after_failed_open && (x.hard_link || x.symbolic_link))
1043     x.unlink_dest_before_opening = 1;
1044
1045   /* Allocate space for remembering copied and created files.  */
1046
1047   hash_init ();
1048
1049   exit_status |= do_copy (argc - optind, argv + optind, target_directory, &x);
1050
1051   forget_all ();
1052
1053   exit (exit_status);
1054 }