Change from getopt to popt.
authorMartin Pool <mbp@samba.org>
Fri, 23 Feb 2001 01:02:31 +0000 (01:02 +0000)
committerMartin Pool <mbp@samba.org>
Fri, 23 Feb 2001 01:02:31 +0000 (01:02 +0000)
Include more details in --version output: say whether symlinks and
hardlinks are supported.
When hardlink support is missing, explain whether the problem is on
the client or server.
When a bad option is encountered, don't just print it to stderr but
send it to the rsync log mechanism.  (However, server errors currently
seem to get lost in transit because of bugs in logging.)

options.c

index a58df93..c49f948 100644 (file)
--- a/options.c
+++ b/options.c
@@ -1,6 +1,7 @@
 /*  -*- c-file-style: "linux" -*-
     
-    Copyright (C) 1998-2000 by Andrew Tridgell 
+    Copyright (C) 1998-2001 by Andrew Tridgell <tridge@samba.org>
+    Copyright (C) 2000-2001 by Martin Pool <mbp@samba.org>
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-/* options parsing code */
-
 #include "rsync.h"
-
+#include "popt.h"
 
 int make_backups = 0;
 int whole_file = 0;
@@ -97,34 +96,43 @@ static int modify_window_set;
 struct in_addr socket_address = {INADDR_ANY};
 
 
-static void print_rsync_version(void)
+static void print_rsync_version(int f)
 {
         char const *got_socketpair = "no ";
+        char const *hardlinks = "no ";
+        char const *links = "no ";
 
 #ifdef HAVE_SOCKETPAIR
         got_socketpair = "";
 #endif
-        
-        rprintf(FINFO, "rsync version %s  protocol version %d\n",
-                VERSION, PROTOCOL_VERSION);
-        rprintf(FINFO, "Configuration: %d-bit files, %ssocketpairs\n\n",
+
+#if SUPPORT_HARD_LINKS
+        hardlinks = "";
+#endif
+
+#if SUPPORT_LINKS
+        links = "";
+#endif
+
+        rprintf(f, "%s  version %s  protocol version %d\n",
+                RSYNC_NAME, VERSION, PROTOCOL_VERSION);
+        rprintf(f,
+                "Copyright (C) 1996-2001 by Andrew Tridgell, Paul Mackerras and others\n");
+        rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, "
+                "%shard links, %ssymlinks\n\n",
                 sizeof(int64) * 8,
-                got_socketpair);
-        rprintf(FINFO, "Written by Andrew Tridgell and Paul Mackerras "
-                "<http://rsync.samba.org/>\n\n");
+                got_socketpair,
+                hardlinks, links);
+
 #ifdef NO_INT64
-        rprintf(FINFO, "WARNING: no 64-bit integers on this platform!\n");
+        rprintf(logcode, "WARNING: no 64-bit integers on this platform!\n");
 #endif
 }
 
 
 void usage(enum logcode F)
 {
-  rprintf(F,"rsync version %s Copyright Andrew Tridgell and Paul Mackerras\n\n",
-         VERSION);
-#ifdef NO_INT64
-  rprintf(F, "WARNING: no 64-bit integers on this platform!\n");
-#endif
+  print_rsync_version(F);
 
   rprintf(F,"rsync is a file transfer program capable of efficient remote update\nvia a fast differencing algorithm.\n\n");
 
@@ -203,10 +211,10 @@ void usage(enum logcode F)
   rprintf(F,"\n");
 
   rprintf(F,"\nPlease see the rsync(1) and rsyncd.conf(5) man pages for full documentation\n");
-  rprintf(F,"See http://rsync.samba.org/ for updates and bug reports\n");
+  rprintf(F,"See http://rsync.samba.org/ for updates, bug reports, and answers\n");
 }
 
-enum {OPT_VERSION, OPT_SUFFIX, OPT_SENDER, OPT_SERVER, OPT_EXCLUDE,
+enum {OPT_VERSION = 1000, OPT_SUFFIX, OPT_SENDER, OPT_SERVER, OPT_EXCLUDE,
       OPT_EXCLUDE_FROM, OPT_DELETE, OPT_DELETE_EXCLUDED, OPT_NUMERIC_IDS,
       OPT_RSYNC_PATH, OPT_FORCE, OPT_TIMEOUT, OPT_DAEMON, OPT_CONFIG, OPT_PORT,
       OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_STATS, OPT_PARTIAL, OPT_PROGRESS,
@@ -216,86 +224,88 @@ enum {OPT_VERSION, OPT_SUFFIX, OPT_SENDER, OPT_SERVER, OPT_EXCLUDE,
       OPT_IGNORE_ERRORS, OPT_BWLIMIT, OPT_BLOCKING_IO,
       OPT_MODIFY_WINDOW};
 
-static char *short_options = "oblLWHpguDCtcahvqrRIxnSe:B:T:zP";
-
-static struct option long_options[] = {
-  {"version",     0,     0,    OPT_VERSION},
-  {"server",      0,     0,    OPT_SERVER},
-  {"sender",      0,     0,    OPT_SENDER},
-  {"existing",    0,     0,    OPT_EXISTING},
-  {"delete",      0,     0,    OPT_DELETE},
-  {"delete-excluded", 0, 0,    OPT_DELETE_EXCLUDED},
-  {"force",       0,     0,    OPT_FORCE},
-  {"numeric-ids", 0,     0,    OPT_NUMERIC_IDS},
-  {"exclude",     1,     0,    OPT_EXCLUDE},
-  {"exclude-from",1,     0,    OPT_EXCLUDE_FROM},
-  {"include",     1,     0,    OPT_INCLUDE},
-  {"include-from",1,     0,    OPT_INCLUDE_FROM},
-  {"rsync-path",  1,     0,    OPT_RSYNC_PATH},
-  {"password-file", 1, 0,     OPT_PASSWORD_FILE},
-  {"one-file-system",0,  0,    'x'},
-  {"ignore-times",0,     0,    'I'},
-  {"size-only",   0,     0,    OPT_SIZE_ONLY},
-  {"modify-window",1,    0,    OPT_MODIFY_WINDOW},
-  {"help",        0,     0,    'h'},
-  {"dry-run",     0,     0,    'n'},
-  {"sparse",      0,     0,    'S'},
-  {"cvs-exclude", 0,     0,    'C'},
-  {"archive",     0,     0,    'a'},
-  {"checksum",    0,     0,    'c'},
-  {"backup",      0,     0,    'b'},
-  {"update",      0,     0,    'u'},
-  {"verbose",     0,     0,    'v'},
-  {"quiet",       0,     0,    'q'},
-  {"recursive",   0,     0,    'r'},
-  {"relative",    0,     0,    'R'},
-  {"devices",     0,     0,    'D'},
-  {"perms",       0,     0,    'p'},
-  {"links",       0,     0,    'l'},
-  {"copy-links",  0,     0,    'L'},
-  {"copy-unsafe-links", 0, 0,  OPT_COPY_UNSAFE_LINKS},
-  {"safe-links",  0,     0,    OPT_SAFE_LINKS},
-  {"whole-file",  0,     0,    'W'},
-  {"hard-links",  0,     0,    'H'},
-  {"owner",       0,     0,    'o'},
-  {"group",       0,     0,    'g'},
-  {"times",       0,     0,    't'},
-  {"rsh",         1,     0,    'e'},
-  {"suffix",      1,     0,    OPT_SUFFIX},
-  {"block-size",  1,     0,    'B'},
-  {"timeout",     1,     0,    OPT_TIMEOUT},
-  {"temp-dir",    1,     0,    'T'},
-  {"compare-dest", 1,    0,    OPT_COMPARE_DEST},
-  {"compress",   0,     0,    'z'},
-  {"daemon",      0,     0,    OPT_DAEMON},
-  {"stats",       0,     0,    OPT_STATS},
-  {"progress",    0,     0,    OPT_PROGRESS},
-  {"partial",     0,     0,    OPT_PARTIAL},
-  {"delete-after",0,     0,    OPT_DELETE_AFTER},
-  {"ignore-errors",0,     0,   OPT_IGNORE_ERRORS},
-  {"blocking-io" ,0,     0,    OPT_BLOCKING_IO},
-  {"config",      1,     0,    OPT_CONFIG},
-  {"port",        1,     0,    OPT_PORT},
-  {"log-format",  1,     0,    OPT_LOG_FORMAT},
-  {"bwlimit",    1,     0,    OPT_BWLIMIT},
-  {"address",     1,     0,    OPT_ADDRESS},
-  {"max-delete",  1,     0,    OPT_MAX_DELETE},
-  {"backup-dir",  1,     0,    OPT_BACKUP_DIR},
-  {0,0,0,0}};
+static struct poptOption long_options[] = {
+  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+  {"version",          0,  POPT_ARG_NONE,   0,             OPT_VERSION},
+  {"suffix",           0,  POPT_ARG_STRING, &backup_suffix},
+  {"rsync-path",       0,  POPT_ARG_STRING, &rsync_path},
+  {"password-file",    0,  POPT_ARG_STRING, &password_file},
+  {"ignore-times",    'I', POPT_ARG_NONE,   &ignore_times},
+  {"size-only",        0,  POPT_ARG_NONE,   &size_only},
+  {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW},
+  {"one-file-system", 'x', POPT_ARG_NONE,   &one_file_system},
+  {"delete",           0,  POPT_ARG_NONE,   &delete_mode},
+  {"existing",         0,  POPT_ARG_NONE,   &only_existing},
+  {"delete-after",     0,  POPT_ARG_NONE,   &delete_after},
+  {"delete-excluded",  0,  POPT_ARG_NONE,   0,              OPT_DELETE_EXCLUDED},
+  {"force",            0,  POPT_ARG_NONE,   &force_delete},
+  {"numeric-ids",      0,  POPT_ARG_NONE,   &numeric_ids},
+  {"exclude",          0,  POPT_ARG_STRING, 0,              OPT_EXCLUDE},
+  {"include",          0,  POPT_ARG_STRING, 0,              OPT_INCLUDE},
+  {"exclude-from",     0,  POPT_ARG_STRING, 0,              OPT_EXCLUDE_FROM},
+  {"include-from",     0,  POPT_ARG_STRING, 0,              OPT_INCLUDE_FROM},
+  {"safe-links",       0,  POPT_ARG_NONE,   &safe_symlinks},
+  {"help",            'h', POPT_ARG_NONE,   0,              'h'},
+  {"backup",          'b', POPT_ARG_NONE,   &make_backups},
+  {"dry-run",         'n', POPT_ARG_NONE,   &dry_run},
+  {"sparse",          'S', POPT_ARG_NONE,   &sparse_files},
+  {"cvs-exclude",     'C', POPT_ARG_NONE,   &cvs_exclude},
+  {"update",          'u', POPT_ARG_NONE,   &update_only},
+  {"links",           'l', POPT_ARG_NONE,   &preserve_links},
+  {"copy-links",      'L', POPT_ARG_NONE,   &copy_links},
+  {"whole",           'W', POPT_ARG_NONE,   &whole_file},
+  {"copy-unsafe-links", 0, POPT_ARG_NONE,   &copy_unsafe_links},
+  {"perms",           'p', POPT_ARG_NONE,   &preserve_perms},
+  {"owner",           'o', POPT_ARG_NONE,   &preserve_uid},
+  {"group",           'g', POPT_ARG_NONE,   &preserve_gid},
+  {"devices",         'D', POPT_ARG_NONE,   &preserve_devices},
+  {"times",           't', POPT_ARG_NONE,   &preserve_times},
+  {"checksum",        'c', POPT_ARG_NONE,   &always_checksum},
+  {"verbose",         'v', POPT_ARG_NONE,   0,               'v'},
+  {"quiet",           'q', POPT_ARG_NONE,   0,               'q'},
+  {"archive",         'a', POPT_ARG_NONE,   0,               'a'}, 
+  {"server",           0,  POPT_ARG_NONE,   &am_server},
+  {"sender",           0,  POPT_ARG_NONE,   0,               OPT_SENDER},
+  {"recurse",         'r', POPT_ARG_NONE,   &recurse},
+  {"relative",        'R', POPT_ARG_NONE,   &relative_paths},
+  {"rsh",             'e', POPT_ARG_STRING, &shell_cmd},
+  {"block-size",      'B', POPT_ARG_INT,    &block_size},
+  {"max-delete",       0,  POPT_ARG_INT,    &max_delete},
+  {"timeout",          0,  POPT_ARG_INT,    &io_timeout},
+  {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir},
+  {"compare-dest",     0,  POPT_ARG_NONE,   &compare_dest},
+  /* TODO: Should this take an optional int giving the compression level? */
+  {"compress",        'z', POPT_ARG_NONE,   &do_compression},
+  {"daemon",           0,  POPT_ARG_NONE,   &am_daemon},
+  {"stats",            0,  POPT_ARG_NONE,   &do_stats},
+  {"progress",         0,  POPT_ARG_NONE,   &do_progress},
+  {"partial",          0,  POPT_ARG_NONE,   &keep_partial},
+  {"ignore-errors",    0,  POPT_ARG_NONE,   &ignore_errors},
+  {"blocking-io",      0,  POPT_ARG_NONE,   &blocking_io},
+  {0,                 'P', POPT_ARG_NONE,   0,               'P'},
+  {"config",           0,  POPT_ARG_STRING, &config_file},
+  {"port",             0,  POPT_ARG_INT,    &rsync_port},
+  {"log-format",       0,  POPT_ARG_STRING, &log_format},
+  {"bwlimit",          0,  POPT_ARG_INT,    &bwlimit},
+  {"address",          0,  POPT_ARG_STRING, 0,               OPT_ADDRESS},
+  {"backup-dir",       0,  POPT_ARG_STRING, &backup_dir},
+  {"hard-links",      'H', POPT_ARG_NONE,   &preserve_hard_links},
+  {0,0,0,0}
+};
 
 
 static char err_buf[100];
 
+
 void option_error(void)
 {
        if (err_buf[0]) {
-               rprintf(FLOG,"%s", err_buf);
-               rprintf(FERROR,"%s", err_buf);
+               rprintf(FLOG, "%s", err_buf);
+               rprintf(FERROR, "%s: %s", RSYNC_NAME, err_buf);
        } else {
                rprintf(FLOG,"Error parsing options - unsupported option?\n");
                rprintf(FERROR,"Error parsing options - unsupported option?\n");
        }
-       exit_cleanup(RERR_UNSUPPORTED);
 }
 
 /* check to see if we should refuse this option */
@@ -305,13 +315,13 @@ static int check_refuse_options(char *ref, int opt)
        char *p;
        const char *name;
 
-       for (i=0; long_options[i].name; i++) {
+       for (i=0; long_options[i].longName; i++) {
                if (long_options[i].val == opt) break;
        }
        
-       if (!long_options[i].name) return 0;
+       if (!long_options[i].longName) return 0;
 
-       name = long_options[i].name;
+       name = long_options[i].longName;
        len = strlen(name);
 
        while ((p = strstr(ref,name))) {
@@ -327,171 +337,94 @@ static int check_refuse_options(char *ref, int opt)
 }
 
 
-int parse_arguments(int argc, char *argv[], int frommain)
+static int count_args(char const **argv)
+{
+        int i = 0;
+
+        while (argv[i] != NULL)
+                i++;
+        
+        return i;
+}
+
+
+/* Process command line arguments.  Called on both local and remote.
+ * Returns if all options are OK, otherwise fills in err_buf and
+ * returns 0. */
+int parse_arguments(int *argc, const char ***argv, int frommain)
 {
        int opt;
-       int option_index;
        char *ref = lp_refuse_options(module_id);
+        poptContext pc;
 
-       while ((opt = getopt_long(argc, argv, 
-                                 short_options, long_options, &option_index)) 
-              != -1) {
+        /* TODO: Call poptReadDefaultConfig; handle errors. */
 
+        /* The context leaks in case of an error, but if there's a
+         * problem we always exit anyhow. */
+        pc = poptGetContext(RSYNC_NAME, *argc, (const char **) *argv,
+                            long_options, 0);
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
                if (ref) {
                        if (check_refuse_options(ref, opt)) return 0;
                }
 
+                /* most options are handled automatically by popt;
+                 * only special cases are returned and listed here. */
+
                switch (opt) {
                case OPT_VERSION:
-                        print_rsync_version();
+                        print_rsync_version(FINFO);
                        exit_cleanup(0);
                        
-               case OPT_SUFFIX:
-                       backup_suffix = optarg;
-                       break;
-                       
-               case OPT_RSYNC_PATH:
-                       rsync_path = optarg;
-                       break;
-       
-               case OPT_PASSWORD_FILE:
-                       password_file =optarg;
-                       break;          
-
-               case 'I':
-                       ignore_times = 1;
-                       break;
-
-               case OPT_SIZE_ONLY:
-                       size_only = 1;
-                       break;
-
                case OPT_MODIFY_WINDOW:
-                       modify_window = atoi(optarg);
+                        /* The value has already been set by popt, but
+                         * we need to remember that we're using a
+                         * non-default setting. */
                        modify_window_set = 1;
                        break;
                        
-               case 'x':
-                       one_file_system=1;
-                       break;
-
-               case OPT_DELETE:
-                       delete_mode = 1;
-                       break;
-
-               case OPT_EXISTING:
-                       only_existing = 1;
-                       break;
-
-               case OPT_DELETE_AFTER:
-                       delete_after = 1;
-                       break;
-
                case OPT_DELETE_EXCLUDED:
                        delete_excluded = 1;
                        delete_mode = 1;
                        break;
 
-               case OPT_FORCE:
-                       force_delete = 1;
-                       break;
-
-               case OPT_NUMERIC_IDS:
-                       numeric_ids = 1;
-                       break;
-
                case OPT_EXCLUDE:
-                       add_exclude(optarg, 0);
+                       add_exclude(poptGetOptArg(pc), 0);
                        break;
 
                case OPT_INCLUDE:
-                       add_exclude(optarg, 1);
+                       add_exclude(poptGetOptArg(pc), 1);
                        break;
 
                case OPT_EXCLUDE_FROM:
-                       add_exclude_file(optarg,1, 0);
+                       add_exclude_file(poptGetOptArg(pc), 1, 0);
                        break;
 
                case OPT_INCLUDE_FROM:
-                       add_exclude_file(optarg,1, 1);
-                       break;
-
-               case OPT_COPY_UNSAFE_LINKS:
-                       copy_unsafe_links=1;
-                       break;
-
-               case OPT_SAFE_LINKS:
-                       safe_symlinks=1;
+                       add_exclude_file(poptGetOptArg(pc), 1, 1);
                        break;
 
                case 'h':
                        usage(FINFO);
                        exit_cleanup(0);
 
-               case 'b':
-                       make_backups=1;
-                       break;
-
-               case 'n':
-                       dry_run=1;
-                       break;
-
-               case 'S':
-                       sparse_files=1;
-                       break;
-
-               case 'C':
-                       cvs_exclude=1;
-                       break;
-
-               case 'u':
-                       update_only=1;
-                       break;
-
-               case 'l':
-                       preserve_links=1;
-                       break;
-
-               case 'L':
-                       copy_links=1;
-                       break;
-
-               case 'W':
-                       whole_file=1;
-                       break;
-
                case 'H':
 #if SUPPORT_HARD_LINKS
                        preserve_hard_links=1;
-#else 
-                       slprintf(err_buf,sizeof(err_buf),"hard links are not supported on this server\n");
+#else
+                        /* FIXME: Don't say "server" if this is
+                         * happening on the client. */
+                        /* FIXME: Why do we have the duplicated
+                         * rprintf?  Everybody who gets this message
+                         * ought to send it to the client and also to
+                         * the logs. */
+                       slprintf(err_buf,sizeof(err_buf),
+                                 "hard links are not supported on this %s\n",
+                                am_server ? "server" : "client");
                        rprintf(FERROR,"ERROR: hard links not supported on this platform\n");
                        return 0;
-#endif
-                       break;
-
-               case 'p':
-                       preserve_perms=1;
-                       break;
-
-               case 'o':
-                       preserve_uid=1;
-                       break;
-
-               case 'g':
-                       preserve_gid=1;
-                       break;
-
-               case 'D':
-                       preserve_devices=1;
-                       break;
-
-               case 't':
-                       preserve_times=1;
-                       break;
-
-               case 'c':
-                       always_checksum=1;
+#endif /* SUPPORT_HARD_LINKS */
                        break;
 
                case 'v':
@@ -514,10 +447,6 @@ int parse_arguments(int argc, char *argv[], int frommain)
                        preserve_devices=1;
                        break;
 
-               case OPT_SERVER:
-                       am_server = 1;
-                       break;
-
                case OPT_SENDER:
                        if (!am_server) {
                                usage(FERROR);
@@ -526,87 +455,11 @@ int parse_arguments(int argc, char *argv[], int frommain)
                        am_sender = 1;
                        break;
 
-               case 'r':
-                       recurse = 1;
-                       break;
-
-               case 'R':
-                       relative_paths = 1;
-                       break;
-
-               case 'e':
-                       shell_cmd = optarg;
-                       break;
-
-               case 'B':
-                       block_size = atoi(optarg);
-                       break;
-
-               case OPT_MAX_DELETE:
-                       max_delete = atoi(optarg);
-                       break;
-
-               case OPT_TIMEOUT:
-                       io_timeout = atoi(optarg);
-                       break;
-
-               case 'T':
-                       tmpdir = optarg;
-                       break;
-
-               case OPT_COMPARE_DEST:
-                       compare_dest = optarg;
-                       break;
-
-               case 'z':
-                       do_compression = 1;
-                       break;
-
-               case OPT_DAEMON:
-                       am_daemon = 1;
-                       break;
-
-               case OPT_STATS:
-                       do_stats = 1;
-                       break;
-
-               case OPT_PROGRESS:
-                       do_progress = 1;
-                       break;
-
-               case OPT_PARTIAL:
-                       keep_partial = 1;
-                       break;
-
-               case OPT_IGNORE_ERRORS:
-                       ignore_errors = 1;
-                       break;
-
-               case OPT_BLOCKING_IO:
-                       blocking_io = 1;
-                       break;
-
                case 'P':
                        do_progress = 1;
                        keep_partial = 1;
                        break;
 
-               case OPT_CONFIG:
-                       config_file = optarg;
-                       break;
-
-               case OPT_PORT:
-                       rsync_port = atoi(optarg);
-                       break;
-
-               case OPT_LOG_FORMAT:
-                       log_format = optarg;
-                       break;
-       
-               case OPT_BWLIMIT:
-                       bwlimit = atoi(optarg);
-                       break;
-
                case OPT_ADDRESS:
                        {
                                struct in_addr *ia;
@@ -616,32 +469,30 @@ int parse_arguments(int argc, char *argv[], int frommain)
                        }
                        break;
 
-               case OPT_BACKUP_DIR:
-                       backup_dir = optarg;
-                       break;
-
                default:
-                        /* FIXME: This can be confusing when the error message
-                         * is coming from a remote server which has an older
-                         * rsync version than the local version.  The user
-                         * sees the option documented in the local --help, but
-                         * then the program seems to refuse to execute it.  So
-                         * perhaps the message should give the hostname or
-                         * something.
-                         * 
-                         * In rsync 3.x, it might be nice if options which
-                         * only affect one end didn't have to bother the
-                         * remote end. */
-                       slprintf(err_buf,sizeof(err_buf),"unrecognised option\n");
-                       return 0;
+                        /* FIXME: If --daemon is specified, then errors for later
+                         * parameters seem to disappear. */
+                        slprintf(err_buf, sizeof(err_buf),
+                                 "%s%s: %s\n",
+                                 am_server ? "on remote machine: " : "",
+                                 poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+                                 poptStrerror(opt));
+                        return 0;
                }
        }
+
+        *argv = poptGetArgs(pc);
+        if (*argv)
+                *argc = count_args(*argv);
+        else
+                *argc = 0;
+
        return 1;
 }
 
 
-/* need to pass all the valid options from the client to the server */
-
+/* Construct a filtered list of options to pass through from the
+ * client to the server */
 void server_options(char **args,int *argc)
 {
        int ac = *argc;