Switching to GPL 3.
[rsync/rsync.git] / options.c
index a269a51..1a110e1 100644 (file)
--- a/options.c
+++ b/options.c
@@ -6,7 +6,7 @@
  * Copyright (C) 2002-2007 Wayne Davison
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
+ * it under the terms of the GNU General Public License version 3 as
  * published by the Free Software Foundation.
  *
  * This program is distributed in the hope that it will be useful,
@@ -15,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ * with this program; if not, visit the http://fsf.org website.
  */
 
 #include "rsync.h"
@@ -72,7 +71,7 @@ int protocol_version = PROTOCOL_VERSION;
 int sparse_files = 0;
 int do_compression = 0;
 int def_compress_level = Z_DEFAULT_COMPRESSION;
-int am_root = 0;
+int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
 int am_server = 0;
 int am_sender = 0;
 int am_generator = 0;
@@ -85,6 +84,7 @@ int force_delete = 0;
 int io_timeout = 0;
 int allowed_lull = 0;
 int prune_empty_dirs = 0;
+int use_qsort = 0;
 char *files_from = NULL;
 int filesfrom_fd = -1;
 char *filesfrom_host = NULL;
@@ -177,6 +177,11 @@ int list_only = 0;
 #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
 char *batch_name = NULL;
 
+#ifdef ICONV_OPTION
+int need_unsorted_flist = 0;
+char *iconv_opt = ICONV_OPTION;
+#endif
+
 struct chmod_mode_struct *chmod_modes = NULL;
 
 static int daemon_opt;   /* sets am_daemon after option error-reporting */
@@ -198,28 +203,29 @@ char *bind_address;
 
 static void print_rsync_version(enum logcode f)
 {
-       char buf[32];
+       char *subprotocol = "";
        char const *got_socketpair = "no ";
        char const *have_inplace = "no ";
        char const *hardlinks = "no ";
        char const *acls = "no ";
        char const *xattrs = "no ";
        char const *links = "no ";
+       char const *iconv = "no ";
        char const *ipv6 = "no ";
        STRUCT_STAT *dumstat;
 
+#if SUBPROTOCOL_VERSION != 0
+       asprintf(&subprotocol, ".PR%d", SUBPROTOCOL_VERSION);
+#endif
 #ifdef HAVE_SOCKETPAIR
        got_socketpair = "";
 #endif
-
 #ifdef HAVE_FTRUNCATE
        have_inplace = "";
 #endif
-
 #ifdef SUPPORT_HARD_LINKS
        hardlinks = "";
 #endif
-
 #ifdef SUPPORT_ACLS
        acls = "";
 #endif
@@ -229,17 +235,15 @@ static void print_rsync_version(enum logcode f)
 #ifdef SUPPORT_LINKS
        links = "";
 #endif
-
 #ifdef INET6
        ipv6 = "";
 #endif
+#ifdef ICONV_OPTION
+       iconv = "";
+#endif
 
-       if (SUBPROTOCOL_VERSION)
-               snprintf(buf, sizeof buf, ".PR%d", SUBPROTOCOL_VERSION);
-       else
-               *buf = '\0';
        rprintf(f, "%s  version %s  protocol version %d%s\n",
-               RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, buf);
+               RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
        rprintf(f, "Copyright (C) 1996-2007 by Andrew Tridgell, Wayne Davison, and others.\n");
        rprintf(f, "Web site: http://rsync.samba.org/\n");
        rprintf(f, "Capabilities:\n");
@@ -250,8 +254,8 @@ static void print_rsync_version(enum logcode f)
                (int)(sizeof (int64) * 8));
        rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
                got_socketpair, hardlinks, links, ipv6, have_inplace);
-       rprintf(f, "    %sappend, %sACLs, %sxattrs\n",
-               have_inplace, acls, xattrs);
+       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv\n",
+               have_inplace, acls, xattrs, iconv);
 
 #ifdef MAINTAINER_MODE
        rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
@@ -333,6 +337,9 @@ void usage(enum logcode F)
   rprintf(F," -t, --times                 preserve times\n");
   rprintf(F," -O, --omit-dir-times        omit directories when preserving times\n");
   rprintf(F,"     --super                 receiver attempts super-user activities\n");
+#ifdef SUPPORT_XATTRS
+  rprintf(F,"     --fake-super            store/recover privileged attrs using xattrs\n");
+#endif
   rprintf(F," -S, --sparse                handle sparse files efficiently\n");
   rprintf(F," -n, --dry-run               show what would have been transferred\n");
   rprintf(F," -W, --whole-file            copy files whole (without rsync algorithm)\n");
@@ -401,6 +408,9 @@ void usage(enum logcode F)
   rprintf(F,"     --only-write-batch=FILE like --write-batch but w/o updating destination\n");
   rprintf(F,"     --read-batch=FILE       read a batched update from FILE\n");
   rprintf(F,"     --protocol=NUM          force an older protocol version to be used\n");
+#ifdef ICONV_OPTION
+  rprintf(F,"     --iconv=CONVERT_SPEC    request charset conversion of filesnames\n");
+#endif
 #ifdef INET6
   rprintf(F," -4, --ipv4                  prefer IPv4\n");
   rprintf(F," -6, --ipv6                  prefer IPv6\n");
@@ -433,15 +443,17 @@ static struct poptOption long_options[] = {
   {"no-motd",          0,  POPT_ARG_VAL,    &output_motd, 0, 0, 0 },
   {"stats",            0,  POPT_ARG_NONE,   &do_stats, 0, 0, 0 },
   {"human-readable",  'h', POPT_ARG_NONE,   0, 'h', 0, 0},
+  {"no-human-readable",0,  POPT_ARG_VAL,    &human_readable, 0, 0, 0},
+  {"no-h",             0,  POPT_ARG_VAL,    &human_readable, 0, 0, 0},
   {"dry-run",         'n', POPT_ARG_NONE,   &dry_run, 0, 0, 0 },
   {"archive",         'a', POPT_ARG_NONE,   0, 'a', 0, 0 },
   {"recursive",       'r', POPT_ARG_VAL,    &recurse, 2, 0, 0 },
   {"no-recursive",     0,  POPT_ARG_VAL,    &recurse, 0, 0, 0 },
-  {"ir",               0,  POPT_ARG_VAL,    &allow_inc_recurse, 1, 0, 0 },
+  {"no-r",             0,  POPT_ARG_VAL,    &recurse, 0, 0, 0 },
   {"inc-recursive",    0,  POPT_ARG_VAL,    &allow_inc_recurse, 1, 0, 0 },
-  {"no-ir",            0,  POPT_ARG_VAL,    &allow_inc_recurse, 0, 0, 0 },
   {"no-inc-recursive", 0,  POPT_ARG_VAL,    &allow_inc_recurse, 0, 0, 0 },
-  {"no-r",             0,  POPT_ARG_VAL,    &recurse, 0, 0, 0 },
+  {"ir",               0,  POPT_ARG_VAL,    &allow_inc_recurse, 1, 0, 0 },
+  {"no-ir",            0,  POPT_ARG_VAL,    &allow_inc_recurse, 0, 0, 0 },
   {"dirs",            'd', POPT_ARG_VAL,    &xfer_dirs, 2, 0, 0 },
   {"no-dirs",          0,  POPT_ARG_VAL,    &xfer_dirs, 0, 0, 0 },
   {"no-d",             0,  POPT_ARG_VAL,    &xfer_dirs, 0, 0, 0 },
@@ -462,6 +474,7 @@ static struct poptOption long_options[] = {
   {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
   {"super",            0,  POPT_ARG_VAL,    &am_root, 2, 0, 0 },
   {"no-super",         0,  POPT_ARG_VAL,    &am_root, 0, 0, 0 },
+  {"fake-super",       0,  POPT_ARG_VAL,    &am_root, -1, 0, 0 },
   {"owner",           'o', POPT_ARG_VAL,    &preserve_uid, 1, 0, 0 },
   {"no-owner",         0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
   {"no-o",             0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
@@ -525,13 +538,17 @@ static struct poptOption long_options[] = {
   {"whole-file",      'W', POPT_ARG_VAL,    &whole_file, 1, 0, 0 },
   {"no-whole-file",    0,  POPT_ARG_VAL,    &whole_file, 0, 0, 0 },
   {"no-W",             0,  POPT_ARG_VAL,    &whole_file, 0, 0, 0 },
-  {"checksum",        'c', POPT_ARG_NONE,   &always_checksum, 0, 0, 0 },
+  {"checksum",        'c', POPT_ARG_VAL,    &always_checksum, 1, 0, 0 },
+  {"no-checksum",      0,  POPT_ARG_VAL,    &always_checksum, 0, 0, 0 },
+  {"no-c",             0,  POPT_ARG_VAL,    &always_checksum, 0, 0, 0 },
   {"block-size",      'B', POPT_ARG_LONG,   &block_size, 0, 0, 0 },
   {"compare-dest",     0,  POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
   {"copy-dest",        0,  POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
   {"link-dest",        0,  POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
   {"fuzzy",           'y', POPT_ARG_NONE,   &fuzzy_basis, 0, 0, 0 },
   {"compress",        'z', POPT_ARG_NONE,   0, 'z', 0, 0 },
+  {"no-compress",      0,  POPT_ARG_VAL,    &do_compression, 0, 0, 0 },
+  {"no-z",             0,  POPT_ARG_VAL,    &do_compression, 0, 0, 0 },
   {"compress-level",   0,  POPT_ARG_INT,    &def_compress_level, 'z', 0, 0 },
   {0,                 'P', POPT_ARG_NONE,   0, 'P', 0, 0 },
   {"progress",         0,  POPT_ARG_VAL,    &do_progress, 1, 0, 0 },
@@ -539,15 +556,20 @@ static struct poptOption long_options[] = {
   {"partial",          0,  POPT_ARG_VAL,    &keep_partial, 1, 0, 0 },
   {"no-partial",       0,  POPT_ARG_VAL,    &keep_partial, 0, 0, 0 },
   {"partial-dir",      0,  POPT_ARG_STRING, &partial_dir, 0, 0, 0 },
-  {"delay-updates",    0,  POPT_ARG_NONE,   &delay_updates, 0, 0, 0 },
+  {"delay-updates",    0,  POPT_ARG_VAL,    &delay_updates, 1, 0, 0 },
+  {"no-delay-updates", 0,  POPT_ARG_VAL,    &delay_updates, 0, 0, 0 },
   {"prune-empty-dirs",'m', POPT_ARG_NONE,   &prune_empty_dirs, 0, 0, 0 },
   {"log-file",         0,  POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
   {"log-file-format",  0,  POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
   {"out-format",       0,  POPT_ARG_STRING, &stdout_format, 0, 0, 0 },
   {"log-format",       0,  POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, /* DEPRECATED */
   {"itemize-changes", 'i', POPT_ARG_NONE,   0, 'i', 0, 0 },
+  {"no-itemize-changes",0, POPT_ARG_VAL,    &itemize_changes, 0, 0, 0 },
+  {"no-i",             0,  POPT_ARG_VAL,    &itemize_changes, 0, 0, 0 },
   {"bwlimit",          0,  POPT_ARG_INT,    &bwlimit, 0, 0, 0 },
-  {"backup",          'b', POPT_ARG_NONE,   &make_backups, 0, 0, 0 },
+  {"no-bwlimit",       0,  POPT_ARG_VAL,    &bwlimit, 0, 0, 0 },
+  {"backup",          'b', POPT_ARG_VAL,    &make_backups, 1, 0, 0 },
+  {"no-backup",        0,  POPT_ARG_VAL,    &make_backups, 0, 0, 0 },
   {"backup-dir",       0,  POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
   {"suffix",           0,  POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
   {"list-only",        0,  POPT_ARG_VAL,    &list_only, 2, 0, 0 },
@@ -555,17 +577,24 @@ static struct poptOption long_options[] = {
   {"write-batch",      0,  POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
   {"only-write-batch", 0,  POPT_ARG_STRING, &batch_name, OPT_ONLY_WRITE_BATCH, 0, 0 },
   {"files-from",       0,  POPT_ARG_STRING, &files_from, 0, 0, 0 },
-  {"from0",           '0', POPT_ARG_NONE,   &eol_nulls, 0, 0, 0},
-  {"numeric-ids",      0,  POPT_ARG_NONE,   &numeric_ids, 0, 0, 0 },
+  {"from0",           '0', POPT_ARG_VAL,    &eol_nulls, 1, 0, 0},
+  {"no-from0",         0,  POPT_ARG_VAL,    &eol_nulls, 0, 0, 0},
+  {"numeric-ids",      0,  POPT_ARG_VAL,    &numeric_ids, 1, 0, 0 },
+  {"no-numeric-ids",   0,  POPT_ARG_VAL,    &numeric_ids, 0, 0, 0 },
   {"timeout",          0,  POPT_ARG_INT,    &io_timeout, 0, 0, 0 },
+  {"no-timeout",       0,  POPT_ARG_VAL,    &io_timeout, 0, 0, 0 },
   {"rsh",             'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
   {"rsync-path",       0,  POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
   {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
+#ifdef ICONV_OPTION
+  {"iconv",            0,  POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
+#endif
 #ifdef INET6
   {"ipv4",            '4', POPT_ARG_VAL,    &default_af_hint, AF_INET, 0, 0 },
   {"ipv6",            '6', POPT_ARG_VAL,    &default_af_hint, AF_INET6, 0, 0 },
 #endif
   {"8-bit-output",    '8', POPT_ARG_NONE,   &allow_8bit_chars, 0, 0, 0 },
+  {"qsort",            0,  POPT_ARG_NONE,   &use_qsort, 0, 0, 0 },
   {"address",          0,  POPT_ARG_STRING, &bind_address, 0, 0, 0 },
   {"port",             0,  POPT_ARG_INT,    &rsync_port, 0, 0, 0 },
   {"sockopts",         0,  POPT_ARG_STRING, &sockopts, 0, 0, 0 },
@@ -621,9 +650,9 @@ static struct poptOption long_daemon_options[] = {
   {"ipv6",            '6', POPT_ARG_VAL,    &default_af_hint, AF_INET6, 0, 0 },
 #endif
   {"detach",           0,  POPT_ARG_VAL,    &no_detach, 0, 0, 0 },
+  {"no-detach",        0,  POPT_ARG_VAL,    &no_detach, 1, 0, 0 },
   {"log-file",         0,  POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
   {"log-file-format",  0,  POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
-  {"no-detach",        0,  POPT_ARG_VAL,    &no_detach, 1, 0, 0 },
   {"port",             0,  POPT_ARG_INT,    &rsync_port, 0, 0, 0 },
   {"sockopts",         0,  POPT_ARG_STRING, &sockopts, 0, 0, 0 },
   {"protocol",         0,  POPT_ARG_INT,    &protocol_version, 0, 0, 0 },
@@ -833,6 +862,11 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        if (am_daemon)
                set_refuse_options("log-file*");
 
+#ifdef ICONV_OPTION
+       if (!am_daemon && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
+               iconv_opt = strdup(arg);
+#endif
+
        /* TODO: Call poptReadDefaultConfig; handle errors. */
 
        /* The context leaks in case of an error, but if there's a
@@ -858,6 +892,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                                    long_options, 0);
                                am_server = 1;
                        }
+#ifdef ICONV_OPTION
+                       iconv_opt = NULL;
+#endif
                        break;
 
                case OPT_SENDER:
@@ -875,6 +912,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                        sizeof err_buf);
                                return 0;
                        }
+#ifdef ICONV_OPTION
+                       iconv_opt = NULL;
+#endif
                        poptFreeContext(pc);
                        pc = poptGetContext(RSYNC_NAME, *argc, *argv,
                                            long_daemon_options, 0);
@@ -1145,7 +1185,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
 
                case 'X':
 #ifdef SUPPORT_XATTRS
-                       preserve_xattrs = 1;
+                       preserve_xattrs++;
                        preserve_perms = 1;
                        break;
 #else
@@ -1176,6 +1216,15 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                exit_cleanup(0);
        }
 
+#ifdef ICONV_OPTION
+       if (iconv_opt) {
+               if (!am_server && strcmp(iconv_opt, "-") == 0)
+                       iconv_opt = NULL;
+               else
+                       need_unsorted_flist = 1;
+       }
+#endif
+
 #ifndef SUPPORT_LINKS
        if (preserve_links && !am_sender) {
                snprintf(err_buf, sizeof err_buf,
@@ -1194,6 +1243,14 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        }
 #endif
 
+#ifndef SUPPORT_XATTRS
+       if (am_root < 0) {
+               snprintf(err_buf, sizeof err_buf,
+                        "--fake-super requires an rsync with extended attributes enabled\n");
+               return 0;
+       }
+#endif
+
        if (write_batch && read_batch) {
                snprintf(err_buf, sizeof err_buf,
                        "--write-batch and --read-batch can not be used together\n");
@@ -1619,8 +1676,11 @@ void server_options(char **args,int *argc)
                argstr[x++] = 'A';
 #endif
 #ifdef SUPPORT_XATTRS
-       if (preserve_xattrs)
+       if (preserve_xattrs) {
                argstr[x++] = 'X';
+               if (preserve_xattrs > 1)
+                       argstr[x++] = 'X';
+       }
 #endif
        if (recurse)
                argstr[x++] = 'r';
@@ -1648,6 +1708,15 @@ void server_options(char **args,int *argc)
        if (list_only == 1 && !recurse)
                argstr[x++] = 'r';
 
+#if SUBPROTOCOL_VERSION != 0
+       /* If we're speaking a pre-release version of a protocol, we tell
+        * the server about this by (ab)using the -e option. */
+       if (protocol_version == PROTOCOL_VERSION) {
+               x += snprintf(argstr+x, sizeof argstr - x,
+                             "e%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
+       }
+#endif
+
        argstr[x] = '\0';
 
        if (x != 1)
@@ -1688,6 +1757,19 @@ void server_options(char **args,int *argc)
                        args[ac++] = "--log-format=X";
        }
 
+#ifdef ICONV_OPTION
+       if (iconv_opt) {
+               char *set = strchr(iconv_opt, ',');
+               if (set)
+                       set++;
+               else
+                       set = iconv_opt;
+               if (asprintf(&arg, "--iconv=%s", set) < 0)
+                       goto oom;
+               args[ac++] = arg;
+       }
+#endif
+
        if (block_size) {
                if (asprintf(&arg, "-B%lu", block_size) < 0)
                        goto oom;
@@ -1792,35 +1874,37 @@ void server_options(char **args,int *argc)
        if (numeric_ids)
                args[ac++] = "--numeric-ids";
 
-       if (ignore_existing && am_sender)
-               args[ac++] = "--ignore-existing";
+       if (am_sender) {
+               if (ignore_existing)
+                       args[ac++] = "--ignore-existing";
+
+               /* Backward compatibility: send --existing, not --ignore-non-existing. */
+               if (ignore_non_existing)
+                       args[ac++] = "--existing";
 
-       /* Backward compatibility: send --existing, not --ignore-non-existing. */
-       if (ignore_non_existing && am_sender)
-               args[ac++] = "--existing";
+               if (tmpdir) {
+                       args[ac++] = "--temp-dir";
+                       args[ac++] = tmpdir;
+               }
+
+               if (basis_dir[0]) {
+                       /* the server only needs this option if it is not the sender,
+                        *   and it may be an older version that doesn't know this
+                        *   option, so don't send it if client is the sender.
+                        */
+                       int i;
+                       for (i = 0; i < basis_dir_cnt; i++) {
+                               args[ac++] = dest_option;
+                               args[ac++] = basis_dir[i];
+                       }
+               }
+       }
 
        if (append_mode)
                args[ac++] = "--append";
        else if (inplace)
                args[ac++] = "--inplace";
 
-       if (tmpdir && am_sender) {
-               args[ac++] = "--temp-dir";
-               args[ac++] = tmpdir;
-       }
-
-       if (basis_dir[0] && am_sender) {
-               /* the server only needs this option if it is not the sender,
-                *   and it may be an older version that doesn't know this
-                *   option, so don't send it if client is the sender.
-                */
-               int i;
-               for (i = 0; i < basis_dir_cnt; i++) {
-                       args[ac++] = dest_option;
-                       args[ac++] = basis_dir[i];
-               }
-       }
-
        if (files_from && (!am_sender || filesfrom_host)) {
                if (filesfrom_host) {
                        args[ac++] = "--files-from";