Made parse_size_arg()'s parsing a little stricter.
[rsync/rsync.git] / options.c
index 34a25c5..a5b862f 100644 (file)
--- a/options.c
+++ b/options.c
@@ -94,11 +94,12 @@ int daemon_bwlimit = 0;
 int bwlimit = 0;
 int fuzzy_basis = 0;
 size_t bwlimit_writemax = 0;
-int only_existing = 0;
-int opt_ignore_existing = 0;
+int ignore_existing = 0;
+int ignore_non_existing = 0;
 int need_messages_from_generator = 0;
 int max_delete = 0;
 OFF_T max_size = 0;
+OFF_T min_size = 0;
 int ignore_errors = 0;
 int modify_window = 0;
 int blocking_io = -1;
@@ -167,7 +168,7 @@ static int itemize_changes = 0;
 static int refused_delete, refused_archive_part;
 static int refused_partial, refused_progress, refused_delete_before;
 static int refused_inplace;
-static char *max_size_arg;
+static char *max_size_arg, *min_size_arg;
 static char partialdir_for_delayupdate[] = ".~tmp~";
 
 /** Local address to bind.  As a character string because it's
@@ -251,16 +252,15 @@ void usage(enum logcode F)
 
   rprintf(F,"\nrsync is a file transfer program capable of efficient remote update\nvia a fast differencing algorithm.\n\n");
 
-  rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n");
+  rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... DEST\n");
+  rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n");
+  rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n");
+  rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n");
   rprintf(F,"  or   rsync [OPTION]... [USER@]HOST:SRC [DEST]\n");
-  rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... DEST\n");
   rprintf(F,"  or   rsync [OPTION]... [USER@]HOST::SRC [DEST]\n");
-  rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n");
   rprintf(F,"  or   rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]\n");
-  rprintf(F,"  or   rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n");
-  rprintf(F,"SRC on single-colon remote HOST will be expanded by remote shell\n");
-  rprintf(F,"SRC on server remote HOST may contain shell wildcards or multiple\n");
-  rprintf(F,"  sources separated by space as long as they have same top-level\n");
+  rprintf(F,"The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect\n");
+  rprintf(F,"to an rsync daemon, and require SRC or DEST to start with a module name.\n");
   rprintf(F,"\nOptions\n");
   rprintf(F," -v, --verbose               increase verbosity\n");
   rprintf(F," -q, --quiet                 suppress non-error messages\n");
@@ -309,6 +309,7 @@ void usage(enum logcode F)
   rprintf(F,"     --force                 force deletion of directories even if not empty\n");
   rprintf(F,"     --max-delete=NUM        don't delete more than NUM files\n");
   rprintf(F,"     --max-size=SIZE         don't transfer any file larger than SIZE\n");
+  rprintf(F,"     --min-size=SIZE         don't transfer any file smaller than SIZE\n");
   rprintf(F,"     --partial               keep partially transferred files\n");
   rprintf(F,"     --partial-dir=DIR       put a partially transferred file into DIR\n");
   rprintf(F,"     --delay-updates         put all updated files into place at transfer's end\n");
@@ -362,7 +363,7 @@ void usage(enum logcode F)
 
 enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
       OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST,
-      OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
+      OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE,
       OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
       OPT_REFUSED_BASE = 9000};
 
@@ -372,6 +373,7 @@ static struct poptOption long_options[] = {
   {"version",          0,  POPT_ARG_NONE,   0, OPT_VERSION, 0, 0},
   {"verbose",         'v', POPT_ARG_NONE,   0, 'v', 0, 0 },
   {"no-verbose",       0,  POPT_ARG_VAL,    &verbose, 0, 0, 0 },
+  {"no-v",             0,  POPT_ARG_VAL,    &verbose, 0, 0, 0 },
   {"quiet",           'q', POPT_ARG_NONE,   0, 'q', 0, 0 },
   {"stats",            0,  POPT_ARG_NONE,   &do_stats, 0, 0, 0 },
   {"dry-run",         'n', POPT_ARG_NONE,   &dry_run, 0, 0, 0 },
@@ -386,10 +388,10 @@ static struct poptOption long_options[] = {
   {"no-perms",         0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
   {"no-p",             0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
   {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
-  {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 2, 0, 0 },
-  {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
   {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
   {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
+  {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 2, 0, 0 },
+  {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 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 },
@@ -415,9 +417,11 @@ static struct poptOption long_options[] = {
   {"size-only",        0,  POPT_ARG_NONE,   &size_only, 0, 0, 0 },
   {"one-file-system", 'x', POPT_ARG_NONE,   &one_file_system, 0, 0, 0 },
   {"update",          'u', POPT_ARG_NONE,   &update_only, 0, 0, 0 },
-  {"existing",         0,  POPT_ARG_NONE,   &only_existing, 0, 0, 0 },
-  {"ignore-existing",  0,  POPT_ARG_NONE,   &opt_ignore_existing, 0, 0, 0 },
-  {"max-size",         0,  POPT_ARG_STRING, &max_size_arg,  OPT_MAX_SIZE, 0, 0 },
+  {"existing",         0,  POPT_ARG_NONE,   &ignore_non_existing, 0, 0, 0 },
+  {"ignore-existing",  0,  POPT_ARG_NONE,   &ignore_existing, 0, 0, 0 },
+  {"ignore-non-existing",0,POPT_ARG_NONE,   &ignore_non_existing, 0, 0, 0 },
+  {"max-size",         0,  POPT_ARG_STRING, &max_size_arg, OPT_MAX_SIZE, 0, 0 },
+  {"min-size",         0,  POPT_ARG_STRING, &min_size_arg, OPT_MIN_SIZE, 0, 0 },
   {"sparse",          'S', POPT_ARG_NONE,   &sparse_files, 0, 0, 0 },
   {"inplace",          0,  POPT_ARG_NONE,   &inplace, 0, 0, 0 },
   {"append",           0,  POPT_ARG_VAL,    &append_mode, 1, 0, 0 },
@@ -529,7 +533,10 @@ static struct poptOption long_daemon_options[] = {
   {"port",             0,  POPT_ARG_INT,    &rsync_port, 0, 0, 0 },
   {"protocol",         0,  POPT_ARG_INT,    &protocol_version, 0, 0, 0 },
   {"server",           0,  POPT_ARG_NONE,   &am_server, 0, 0, 0 },
+  {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
   {"verbose",         'v', POPT_ARG_NONE,   0, 'v', 0, 0 },
+  {"no-verbose",       0,  POPT_ARG_VAL,    &verbose, 0, 0, 0 },
+  {"no-v",             0,  POPT_ARG_VAL,    &verbose, 0, 0, 0 },
   {"help",            'h', POPT_ARG_NONE,   0, 'h', 0, 0 },
   {0,0,0,0, 0, 0, 0}
 };
@@ -644,31 +651,58 @@ static int count_args(const char **argv)
 }
 
 
-static OFF_T parse_size_arg(const char *size_arg)
+static OFF_T parse_size_arg(char **size_arg, char def_suf)
 {
-       const char *arg;
-       OFF_T size;
+       int mult, make_compatible = 0;
+       const char *arg, *p;
+       OFF_T size = 0;
 
-       for (arg = size_arg; isdigit(*(uchar*)arg); arg++) {}
+       for (arg = *size_arg; isdigit(*(uchar*)arg); arg++) {}
        if (*arg == '.')
                for (arg++; isdigit(*(uchar*)arg); arg++) {}
-       switch (*arg) {
+       if (*arg && (arg[1] == 't' || arg[1] == 'T'))
+               mult = 1000, make_compatible = 1;
+       else
+               mult = 1024;
+       if ((p = strstr(arg, "+1")) != NULL
+        || (p = strstr(arg, "-1")) != NULL) {
+               if (p[2] != '\0')
+                       return -1;
+               size = atoi(p);
+               make_compatible = 1;
+       }
+       switch (*arg && arg != p ? *arg : def_suf) {
+       case 'b': case 'B':
+               size += atof(*size_arg);
+               break;
        case 'k': case 'K':
-               size = atof(size_arg) * 1024;
+               size += atof(*size_arg) * mult;
                break;
        case 'm': case 'M':
-               size = atof(size_arg) * 1024*1024;
+               size += atof(*size_arg) * mult*mult;
                break;
        case 'g': case 'G':
-               size = atof(size_arg) * 1024*1024*1024;
-               break;
-       case '\0':
-               size = atof(size_arg);
+               size += atof(*size_arg) * mult*mult*mult;
                break;
        default:
-               size = 0;
+               size = -1;
                break;
        }
+       if (size > 0 && make_compatible) {
+               /* We convert this manually because we may need %lld precision,
+                * and that's not a portable sprintf() escape. */
+               char buf[128], *s = buf + sizeof buf;
+               OFF_T num = size;
+               *--s = '\0';
+               while (num) {
+                       if (s == buf) /* impossible... */
+                               out_of_memory("parse_size_arg@buf");
+                       *--s = (num % 10) + '0';
+                       num /= 10;
+               }
+               if (!(*size_arg = strdup(s)))
+                       out_of_memory("parse_size_arg");
+       }
        return size;
 }
 
@@ -747,6 +781,13 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                        goto daemon_error;
                                }
                        }
+
+                       if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
+                               snprintf(err_buf, sizeof err_buf,
+                                        "the --temp-dir path is WAY too long.\n");
+                               return 0;
+                       }
+
                        if (!daemon_opt) {
                                rprintf(FERROR, "Daemon option(s) used without --daemon.\n");
                            daemon_error:
@@ -754,6 +795,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                    "(Type \"rsync --daemon --help\" for assistance with daemon mode.)\n");
                                exit_cleanup(RERR_SYNTAX);
                        }
+
                        *argv = poptGetArgs(pc);
                        *argc = count_args(*argv);
                        am_starting_up = 0;
@@ -875,7 +917,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        break;
 
                case OPT_MAX_SIZE:
-                       if ((max_size = parse_size_arg(max_size_arg)) <= 0) {
+                       if ((max_size = parse_size_arg(&max_size_arg, 'b')) <= 0) {
                                snprintf(err_buf, sizeof err_buf,
                                        "--max-size value is invalid: %s\n",
                                        max_size_arg);
@@ -883,6 +925,15 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        }
                        break;
 
+               case OPT_MIN_SIZE:
+                       if ((min_size = parse_size_arg(&min_size_arg, 'b')) <= 0) {
+                               snprintf(err_buf, sizeof err_buf,
+                                       "--min-size value is invalid: %s\n",
+                                       min_size_arg);
+                               return 0;
+                       }
+                       break;
+
                case OPT_LINK_DEST:
 #ifdef HAVE_LINK
                        link_dest = 1;
@@ -1011,7 +1062,7 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        "You may not combine multiple --delete-WHEN options.\n");
                return 0;
        }
-       if (!recurse) {
+       if (!xfer_dirs) {
                delete_before = delete_during = delete_after = 0;
                delete_mode = delete_excluded = 0;
        } else if (delete_before || delete_during || delete_after)
@@ -1158,6 +1209,14 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        bwlimit_writemax = 512;
        }
 
+       if (sparse_files && inplace) {
+               /* Note: we don't check for this below, because --append is
+                * OK with --sparse (as long as redos are handled right). */
+               snprintf(err_buf, sizeof err_buf,
+                        "--sparse cannot be used with --inplace\n");
+               return 0;
+       }
+
        if (append_mode) {
                if (whole_file > 0) {
                        snprintf(err_buf, sizeof err_buf,
@@ -1395,6 +1454,11 @@ void server_options(char **args,int *argc)
                args[ac++] = arg;
        }
 
+       if (min_size && am_sender) {
+               args[ac++] = "--min-size";
+               args[ac++] = min_size_arg;
+       }
+
        if (max_size && am_sender) {
                args[ac++] = "--max-size";
                args[ac++] = max_size_arg;
@@ -1479,12 +1543,13 @@ void server_options(char **args,int *argc)
        if (numeric_ids)
                args[ac++] = "--numeric-ids";
 
-       if (only_existing && am_sender)
-               args[ac++] = "--existing";
-
-       if (opt_ignore_existing && am_sender)
+       if (ignore_existing && am_sender)
                args[ac++] = "--ignore-existing";
 
+       /* Backward compatibility: send --existing, not --ignore-non-existing. */
+       if (ignore_non_existing && am_sender)
+               args[ac++] = "--existing";
+
        if (append_mode)
                args[ac++] = "--append";
        else if (inplace)