- Implemented a new handling of the recurse variable: if it is
[rsync/rsync.git] / options.c
index fb9dd7b..c34047d 100644 (file)
--- a/options.c
+++ b/options.c
@@ -76,6 +76,7 @@ int filesfrom_fd = -1;
 char *remote_filesfrom_file = NULL;
 int eol_nulls = 0;
 int recurse = 0;
+int keep_dirs = 0;
 int am_daemon = 0;
 int daemon_over_rsh = 0;
 int do_stats = 0;
@@ -84,6 +85,7 @@ int keep_partial = 0;
 int safe_symlinks = 0;
 int copy_unsafe_links = 0;
 int size_only = 0;
+int daemon_bwlimit = 0;
 int bwlimit = 0;
 size_t bwlimit_writemax = 0;
 int delete_after = 0;
@@ -96,7 +98,7 @@ int modify_window = 0;
 int blocking_io = -1;
 int checksum_seed = 0;
 int inplace = 0;
-unsigned int block_size = 0;
+long block_size = 0; /* "long" because popt can't set an int32. */
 
 
 /** Network address family. **/
@@ -120,7 +122,7 @@ unsigned int backup_dir_remainder;
 char *backup_suffix = NULL;
 char *tmpdir = NULL;
 char *partial_dir = NULL;
-char *compare_dest = NULL;
+char *basis_dir[MAX_BASIS_DIRS+1];
 char *config_file = NULL;
 char *shell_cmd = NULL;
 char *log_format = NULL;
@@ -128,8 +130,11 @@ char *password_file = NULL;
 char *rsync_path = RSYNC_PATH;
 char *backup_dir = NULL;
 char backup_dir_buf[MAXPATHLEN];
-int rsync_port = RSYNC_PORT;
+int rsync_port = 0;
+int compare_dest = 0;
+int copy_dest = 0;
 int link_dest = 0;
+int basis_dir_cnt = 0;
 
 int verbose = 0;
 int quiet = 0;
@@ -141,6 +146,7 @@ char *batch_name = NULL;
 
 static int daemon_opt;   /* sets am_daemon after option error-reporting */
 static int modify_window_set;
+static char *dest_option = NULL;
 static char *max_size_arg;
 
 /** Local address to bind.  As a character string because it's
@@ -194,7 +200,7 @@ static void print_rsync_version(enum logcode f)
        rprintf(f, "              %sinplace, %sIPv6, %d-bit system inums, %d-bit internal inums\n",
                have_inplace, ipv6,
                (int) (sizeof dumstat->st_ino * 8),
-               (int) (sizeof (uint64) * 8));
+               (int) (sizeof (int64) * 8));
 #ifdef MAINTAINER_MODE
        rprintf(f, "              panic action: \"%s\"\n",
                get_panic_action());
@@ -243,7 +249,7 @@ void usage(enum logcode F)
   rprintf(F,"     --backup-dir            make backups into this directory\n");
   rprintf(F,"     --suffix=SUFFIX         backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
   rprintf(F," -u, --update                update only (don't overwrite newer files)\n");
-  rprintf(F,"     --inplace               update destination files inplace (SEE MAN PAGE)\n");
+  rprintf(F,"     --inplace               update destination files in-place (SEE MAN PAGE)\n");
   rprintf(F," -K, --keep-dirlinks         treat symlinked dir on receiver as dir\n");
   rprintf(F," -l, --links                 copy symlinks as symlinks\n");
   rprintf(F," -L, --copy-links            copy the referent of all symlinks\n");
@@ -281,7 +287,8 @@ void usage(enum logcode F)
   rprintf(F,"     --modify-window=NUM     compare mod times with reduced accuracy\n");
   rprintf(F," -T, --temp-dir=DIR          create temporary files in directory DIR\n");
   rprintf(F,"     --compare-dest=DIR      also compare destination files relative to DIR\n");
-  rprintf(F,"     --link-dest=DIR         create hardlinks to DIR for unchanged files\n");
+  rprintf(F,"     --copy-dest=DIR         ... and include copies of unchanged files\n");
+  rprintf(F,"     --link-dest=DIR         hardlink to files in DIR when unchanged\n");
   rprintf(F," -P                          equivalent to --partial --progress\n");
   rprintf(F," -z, --compress              compress file data\n");
   rprintf(F," -C, --cvs-exclude           auto ignore files in the same way CVS does\n");
@@ -292,6 +299,7 @@ void usage(enum logcode F)
   rprintf(F,"     --files-from=FILE       read FILE for list of source-file names\n");
   rprintf(F," -0, --from0                 all *-from file lists are delimited by nulls\n");
   rprintf(F,"     --version               print version number\n");
+  rprintf(F,"     --port=PORT             specify double-colon alternate port number\n");
   rprintf(F,"     --blocking-io           use blocking I/O for the remote shell\n");
   rprintf(F,"     --no-blocking-io        turn off --blocking-io\n");
   rprintf(F,"     --stats                 give some file transfer stats\n");
@@ -313,7 +321,8 @@ void usage(enum logcode F)
 }
 
 enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
-      OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST,
+      OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED,
+      OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST,
       OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
       OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, OPT_MAX_SIZE,
       OPT_REFUSED_BASE = 9000};
@@ -368,13 +377,14 @@ static struct poptOption long_options[] = {
   {"relative",        'R', POPT_ARG_VAL,    &relative_paths, 1, 0, 0 },
   {"no-relative",      0,  POPT_ARG_VAL,    &relative_paths, 0, 0, 0 },
   {"rsh",             'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
-  {"block-size",      'B', POPT_ARG_INT,    &block_size, 0, 0, 0 },
+  {"block-size",      'B', POPT_ARG_LONG,   &block_size, 0, 0, 0 },
   {"max-delete",       0,  POPT_ARG_INT,    &max_delete, 0, 0, 0 },
   {"max-size",         0,  POPT_ARG_STRING, &max_size_arg,  OPT_MAX_SIZE, 0, 0 },
   {"timeout",          0,  POPT_ARG_INT,    &io_timeout, OPT_TIMEOUT, 0, 0 },
   {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
-  {"compare-dest",     0,  POPT_ARG_STRING, &compare_dest, 0, 0, 0 },
-  {"link-dest",        0,  POPT_ARG_STRING, &compare_dest, OPT_LINK_DEST, 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 },
   /* TODO: Should this take an optional int giving the compression level? */
   {"compress",        'z', POPT_ARG_NONE,   &do_compression, 0, 0, 0 },
   {"stats",            0,  POPT_ARG_NONE,   &do_stats, 0, 0, 0 },
@@ -385,6 +395,7 @@ static struct poptOption long_options[] = {
   {"blocking-io",      0,  POPT_ARG_VAL,    &blocking_io, 1, 0, 0 },
   {"no-blocking-io",   0,  POPT_ARG_VAL,    &blocking_io, 0, 0, 0 },
   {0,                 'P', POPT_ARG_NONE,   0, 'P', 0, 0 },
+  {"port",             0,  POPT_ARG_INT,    &rsync_port, 0, 0, 0 },
   {"log-format",       0,  POPT_ARG_STRING, &log_format, 0, 0, 0 },
   {"bwlimit",          0,  POPT_ARG_INT,    &bwlimit, 0, 0, 0 },
   {"backup-dir",       0,  POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
@@ -405,7 +416,6 @@ static struct poptOption long_options[] = {
   {"config",           0,  POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
   {"daemon",           0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
   {"no-detach",        0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
-  {"port",             0,  POPT_ARG_INT,    0, OPT_DAEMON, 0, 0 },
   {0,0,0,0, 0, 0, 0}
 };
 
@@ -415,6 +425,7 @@ static void daemon_usage(enum logcode F)
 
   rprintf(F,"\nUsage: rsync --daemon [OPTION]...\n");
   rprintf(F,"     --address=ADDRESS       bind to the specified address\n");
+  rprintf(F,"     --bwlimit=KBPS          limit I/O bandwidth, KBytes per second\n");
   rprintf(F,"     --config=FILE           specify alternate rsyncd.conf file\n");
   rprintf(F,"     --no-detach             do not detach from the parent\n");
   rprintf(F,"     --port=PORT             specify alternate rsyncd port number\n");
@@ -431,6 +442,7 @@ static void daemon_usage(enum logcode F)
 static struct poptOption long_daemon_options[] = {
   /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
   {"address",          0,  POPT_ARG_STRING, &bind_address, 0, 0, 0 },
+  {"bwlimit",          0,  POPT_ARG_INT,    &daemon_bwlimit, 0, 0, 0 },
   {"config",           0,  POPT_ARG_STRING, &config_file, 0, 0, 0 },
   {"daemon",           0,  POPT_ARG_NONE,   &daemon_opt, 0, 0, 0 },
 #ifdef INET6
@@ -548,6 +560,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        /* The context leaks in case of an error, but if there's a
         * problem we always exit anyhow. */
        pc = poptGetContext(RSYNC_NAME, *argc, *argv, long_options, 0);
+       poptReadDefaultConfig(pc, 0);
 
        while ((opt = poptGetNextOpt(pc)) != -1) {
                /* most options are handled automatically by popt;
@@ -693,10 +706,10 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                break;
                        }
                        if (max_size <= 0) {
-                               rprintf(FERROR,
+                               snprintf(err_buf, sizeof err_buf,
                                        "--max-size value is invalid: %s\n",
                                        max_size_arg);
-                               exit_cleanup(RERR_SYNTAX);
+                               return 0;
                        }
                        break;
 
@@ -708,7 +721,8 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                case OPT_LINK_DEST:
 #if HAVE_LINK
                        link_dest = 1;
-                       break;
+                       dest_option = "--link-dest";
+                       goto set_dest_dir;
 #else
                        snprintf(err_buf, sizeof err_buf,
                                 "hard links are not supported on this %s\n",
@@ -716,6 +730,27 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        return 0;
 #endif
 
+               case OPT_COPY_DEST:
+                       copy_dest = 1;
+                       dest_option = "--copy-dest";
+                       goto set_dest_dir;
+
+               case OPT_COMPARE_DEST:
+                       compare_dest = 1;
+                       dest_option = "--compare-dest";
+               set_dest_dir:
+                       if (basis_dir_cnt >= MAX_BASIS_DIRS) {
+                               snprintf(err_buf, sizeof err_buf,
+                                       "ERROR: at most %d %s args may be specified\n",
+                                       MAX_BASIS_DIRS, dest_option);
+                               return 0;
+                       }
+                       arg = poptGetOptArg(pc);
+                       if (sanitize_paths)
+                               arg = sanitize_path(NULL, arg, NULL, 0);
+                       basis_dir[basis_dir_cnt++] = (char *)arg;
+                       break;
+
                default:
                        /* A large opt value means that set_refuse_options()
                         * turned this option off (opt-BASE is its index). */
@@ -798,6 +833,12 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                return 0;
        }
 
+       if (compare_dest + copy_dest + link_dest > 1) {
+               snprintf(err_buf, sizeof err_buf,
+                       "You may not mix --compare-dest, --copy-dest, and --link-dest.\n");
+               return 0;
+       }
+
        if (archive_mode) {
                if (!files_from)
                        recurse = 1;
@@ -810,6 +851,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                preserve_uid = 1;
                preserve_devices = 1;
        }
+       if (recurse) {
+               keep_dirs = 1;
+       }
 
        if (relative_paths < 0)
                relative_paths = files_from? 1 : 0;
@@ -825,8 +869,6 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        tmpdir = sanitize_path(NULL, tmpdir, NULL, 0);
                if (partial_dir)
                        partial_dir = sanitize_path(NULL, partial_dir, NULL, 0);
-               if (compare_dest)
-                       compare_dest = sanitize_path(NULL, compare_dest, NULL, 0);
                if (backup_dir)
                        backup_dir = sanitize_path(NULL, backup_dir, NULL, 0);
                if (files_from)
@@ -834,6 +876,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        }
        if (server_exclude_list.head && !am_sender) {
                struct exclude_list_struct *elp = &server_exclude_list;
+               int i;
                if (tmpdir) {
                        clean_fname(tmpdir, 1);
                        if (check_exclude(elp, tmpdir, 1) < 0)
@@ -844,9 +887,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        if (check_exclude(elp, partial_dir, 1) < 0)
                                goto options_rejected;
                }
-               if (compare_dest) {
-                       clean_fname(compare_dest, 1);
-                       if (check_exclude(elp, compare_dest, 1) < 0)
+               for (i = 0; i < basis_dir_cnt; i++) {
+                       clean_fname(basis_dir[i], 1);
+                       if (check_exclude(elp, basis_dir[i], 1) < 0)
                                goto options_rejected;
                }
                if (backup_dir) {
@@ -897,6 +940,8 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        if (do_progress && !verbose)
                verbose = 1;
 
+       if (daemon_bwlimit && (!bwlimit || bwlimit > daemon_bwlimit))
+               bwlimit = daemon_bwlimit;
        if (bwlimit) {
                bwlimit_writemax = (size_t)bwlimit * 128;
                if (bwlimit_writemax < 512)
@@ -917,12 +962,6 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                         am_server ? "server" : "client");
                return 0;
 #endif
-               if (compare_dest) {
-                       snprintf(err_buf, sizeof err_buf,
-                                "--inplace does not yet work with %s\n",
-                                link_dest ? "--link-dest" : "--compare-dest");
-                       return 0;
-               }
        } else {
                if (keep_partial && !partial_dir)
                        partial_dir = getenv("RSYNC_PARTIAL_DIR");
@@ -968,6 +1007,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                return 0;
                        }
                }
+               keep_dirs = 1;
        }
 
        return 1;
@@ -984,8 +1024,8 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
  **/
 void server_options(char **args,int *argc)
 {
+       static char argstr[50+MAX_BASIS_DIRS*2];
        int ac = *argc;
-       static char argstr[50];
        char *arg;
 
        int i, x;
@@ -1072,7 +1112,7 @@ void server_options(char **args,int *argc)
                args[ac++] = argstr;
 
        if (block_size) {
-               if (asprintf(&arg, "-B%u", block_size) < 0)
+               if (asprintf(&arg, "-B%lu", block_size) < 0)
                        goto oom;
                args[ac++] = arg;
        }
@@ -1173,13 +1213,16 @@ void server_options(char **args,int *argc)
                args[ac++] = tmpdir;
        }
 
-       if (compare_dest && am_sender) {
+       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.
                 */
-               args[ac++] = link_dest ? "--link-dest" : "--compare-dest";
-               args[ac++] = compare_dest;
+               int i;
+               for (i = 0; i < basis_dir_cnt; i++) {
+                       args[ac++] = dest_option;
+                       args[ac++] = basis_dir[i];
+               }
        }
 
        if (files_from && (!am_sender || remote_filesfrom_file)) {
@@ -1192,7 +1235,11 @@ void server_options(char **args,int *argc)
                        args[ac++] = "--files-from=-";
                        args[ac++] = "--from0";
                }
+               if (!relative_paths)
+                       args[ac++] = "--no-relative";
        }
+       if (!implied_dirs && !am_sender)
+               args[ac++] = "--no-implied-dirs";
 
        *argc = ac;
        return;