github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/config/configflags/configflags.go (about)

     1  // Package configflags defines the flags used by rclone.  It is
     2  // decoupled into a separate package so it can be replaced.
     3  package configflags
     4  
     5  // Options set by command line flags
     6  import (
     7  	"log"
     8  	"net"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/rclone/rclone/fs"
    14  	"github.com/rclone/rclone/fs/config"
    15  	"github.com/rclone/rclone/fs/config/flags"
    16  	fsLog "github.com/rclone/rclone/fs/log"
    17  	"github.com/rclone/rclone/fs/rc"
    18  	"github.com/sirupsen/logrus"
    19  	"github.com/spf13/pflag"
    20  )
    21  
    22  var (
    23  	// these will get interpreted into fs.Config via SetFlags() below
    24  	verbose         int
    25  	quiet           bool
    26  	configPath      string
    27  	cacheDir        string
    28  	tempDir         string
    29  	dumpHeaders     bool
    30  	dumpBodies      bool
    31  	deleteBefore    bool
    32  	deleteDuring    bool
    33  	deleteAfter     bool
    34  	bindAddr        string
    35  	disableFeatures string
    36  	dscp            string
    37  	uploadHeaders   []string
    38  	downloadHeaders []string
    39  	headers         []string
    40  	metadataSet     []string
    41  	partialSuffix   string
    42  )
    43  
    44  // AddFlags adds the non filing system specific flags to the command
    45  func AddFlags(ci *fs.ConfigInfo, flagSet *pflag.FlagSet) {
    46  	rc.AddOption("main", ci)
    47  	// NB defaults which aren't the zero for the type should be set in fs/config.go NewConfig
    48  	flags.CountVarP(flagSet, &verbose, "verbose", "v", "Print lots more stuff (repeat for more)", "Logging,Important")
    49  	flags.BoolVarP(flagSet, &quiet, "quiet", "q", false, "Print as little stuff as possible", "Logging")
    50  	flags.DurationVarP(flagSet, &ci.ModifyWindow, "modify-window", "", ci.ModifyWindow, "Max time diff to be considered the same", "Copy")
    51  	flags.IntVarP(flagSet, &ci.Checkers, "checkers", "", ci.Checkers, "Number of checkers to run in parallel", "Performance")
    52  	flags.IntVarP(flagSet, &ci.Transfers, "transfers", "", ci.Transfers, "Number of file transfers to run in parallel", "Performance")
    53  	flags.StringVarP(flagSet, &configPath, "config", "", config.GetConfigPath(), "Config file", "Config")
    54  	flags.StringVarP(flagSet, &cacheDir, "cache-dir", "", config.GetCacheDir(), "Directory rclone will use for caching", "Config")
    55  	flags.StringVarP(flagSet, &tempDir, "temp-dir", "", os.TempDir(), "Directory rclone will use for temporary files", "Config")
    56  	flags.BoolVarP(flagSet, &ci.CheckSum, "checksum", "c", ci.CheckSum, "Check for changes with size & checksum (if available, or fallback to size only).", "Copy")
    57  	flags.BoolVarP(flagSet, &ci.SizeOnly, "size-only", "", ci.SizeOnly, "Skip based on size only, not modtime or checksum", "Copy")
    58  	flags.BoolVarP(flagSet, &ci.IgnoreTimes, "ignore-times", "I", ci.IgnoreTimes, "Don't skip items that match size and time - transfer all unconditionally", "Copy")
    59  	flags.BoolVarP(flagSet, &ci.IgnoreExisting, "ignore-existing", "", ci.IgnoreExisting, "Skip all files that exist on destination", "Copy")
    60  	flags.BoolVarP(flagSet, &ci.IgnoreErrors, "ignore-errors", "", ci.IgnoreErrors, "Delete even if there are I/O errors", "Sync")
    61  	flags.BoolVarP(flagSet, &ci.DryRun, "dry-run", "n", ci.DryRun, "Do a trial run with no permanent changes", "Config,Important")
    62  	flags.BoolVarP(flagSet, &ci.Interactive, "interactive", "i", ci.Interactive, "Enable interactive mode", "Config,Important")
    63  	flags.DurationVarP(flagSet, &ci.ConnectTimeout, "contimeout", "", ci.ConnectTimeout, "Connect timeout", "Networking")
    64  	flags.DurationVarP(flagSet, &ci.Timeout, "timeout", "", ci.Timeout, "IO idle timeout", "Networking")
    65  	flags.DurationVarP(flagSet, &ci.ExpectContinueTimeout, "expect-continue-timeout", "", ci.ExpectContinueTimeout, "Timeout when using expect / 100-continue in HTTP", "Networking")
    66  	flags.BoolVarP(flagSet, &dumpHeaders, "dump-headers", "", false, "Dump HTTP headers - may contain sensitive info", "Debugging")
    67  	flags.BoolVarP(flagSet, &dumpBodies, "dump-bodies", "", false, "Dump HTTP headers and bodies - may contain sensitive info", "Debugging")
    68  	flags.BoolVarP(flagSet, &ci.InsecureSkipVerify, "no-check-certificate", "", ci.InsecureSkipVerify, "Do not verify the server SSL certificate (insecure)", "Networking")
    69  	flags.BoolVarP(flagSet, &ci.AskPassword, "ask-password", "", ci.AskPassword, "Allow prompt for password for encrypted configuration", "Config")
    70  	flags.FVarP(flagSet, &ci.PasswordCommand, "password-command", "", "Command for supplying password for encrypted configuration", "Config")
    71  	flags.BoolVarP(flagSet, &deleteBefore, "delete-before", "", false, "When synchronizing, delete files on destination before transferring", "Sync")
    72  	flags.BoolVarP(flagSet, &deleteDuring, "delete-during", "", false, "When synchronizing, delete files during transfer", "Sync")
    73  	flags.BoolVarP(flagSet, &deleteAfter, "delete-after", "", false, "When synchronizing, delete files on destination after transferring (default)", "Sync")
    74  	flags.Int64VarP(flagSet, &ci.MaxDelete, "max-delete", "", -1, "When synchronizing, limit the number of deletes", "Sync")
    75  	flags.FVarP(flagSet, &ci.MaxDeleteSize, "max-delete-size", "", "When synchronizing, limit the total size of deletes", "Sync")
    76  	flags.BoolVarP(flagSet, &ci.TrackRenames, "track-renames", "", ci.TrackRenames, "When synchronizing, track file renames and do a server-side move if possible", "Sync")
    77  	flags.StringVarP(flagSet, &ci.TrackRenamesStrategy, "track-renames-strategy", "", ci.TrackRenamesStrategy, "Strategies to use when synchronizing using track-renames hash|modtime|leaf", "Sync")
    78  	flags.IntVarP(flagSet, &ci.Retries, "retries", "", 3, "Retry operations this many times if they fail", "Config")
    79  	flags.DurationVarP(flagSet, &ci.RetriesInterval, "retries-sleep", "", 0, "Interval between retrying operations if they fail, e.g. 500ms, 60s, 5m (0 to disable)", "Config")
    80  	flags.IntVarP(flagSet, &ci.LowLevelRetries, "low-level-retries", "", ci.LowLevelRetries, "Number of low level retries to do", "Config")
    81  	flags.BoolVarP(flagSet, &ci.UpdateOlder, "update", "u", ci.UpdateOlder, "Skip files that are newer on the destination", "Copy")
    82  	flags.BoolVarP(flagSet, &ci.UseServerModTime, "use-server-modtime", "", ci.UseServerModTime, "Use server modified time instead of object metadata", "Config")
    83  	flags.BoolVarP(flagSet, &ci.NoGzip, "no-gzip-encoding", "", ci.NoGzip, "Don't set Accept-Encoding: gzip", "Networking")
    84  	flags.IntVarP(flagSet, &ci.MaxDepth, "max-depth", "", ci.MaxDepth, "If set limits the recursion depth to this", "Filter")
    85  	flags.BoolVarP(flagSet, &ci.IgnoreSize, "ignore-size", "", false, "Ignore size when skipping use modtime or checksum", "Copy")
    86  	flags.BoolVarP(flagSet, &ci.IgnoreChecksum, "ignore-checksum", "", ci.IgnoreChecksum, "Skip post copy check of checksums", "Copy")
    87  	flags.BoolVarP(flagSet, &ci.IgnoreCaseSync, "ignore-case-sync", "", ci.IgnoreCaseSync, "Ignore case when synchronizing", "Copy")
    88  	flags.BoolVarP(flagSet, &ci.FixCase, "fix-case", "", ci.FixCase, "Force rename of case insensitive dest to match source", "Sync")
    89  	flags.BoolVarP(flagSet, &ci.NoTraverse, "no-traverse", "", ci.NoTraverse, "Don't traverse destination file system on copy", "Copy")
    90  	flags.BoolVarP(flagSet, &ci.CheckFirst, "check-first", "", ci.CheckFirst, "Do all the checks before starting transfers", "Copy")
    91  	flags.BoolVarP(flagSet, &ci.NoCheckDest, "no-check-dest", "", ci.NoCheckDest, "Don't check the destination, copy regardless", "Copy")
    92  	flags.BoolVarP(flagSet, &ci.NoUnicodeNormalization, "no-unicode-normalization", "", ci.NoUnicodeNormalization, "Don't normalize unicode characters in filenames", "Config")
    93  	flags.BoolVarP(flagSet, &ci.NoUpdateModTime, "no-update-modtime", "", ci.NoUpdateModTime, "Don't update destination modtime if files identical", "Copy")
    94  	flags.BoolVarP(flagSet, &ci.NoUpdateDirModTime, "no-update-dir-modtime", "", ci.NoUpdateModTime, "Don't update directory modification times", "Copy")
    95  	flags.StringArrayVarP(flagSet, &ci.CompareDest, "compare-dest", "", nil, "Include additional comma separated server-side paths during comparison", "Copy")
    96  	flags.StringArrayVarP(flagSet, &ci.CopyDest, "copy-dest", "", nil, "Implies --compare-dest but also copies files from paths into destination", "Copy")
    97  	flags.StringVarP(flagSet, &ci.BackupDir, "backup-dir", "", ci.BackupDir, "Make backups into hierarchy based in DIR", "Sync")
    98  	flags.StringVarP(flagSet, &ci.Suffix, "suffix", "", ci.Suffix, "Suffix to add to changed files", "Sync")
    99  	flags.BoolVarP(flagSet, &ci.SuffixKeepExtension, "suffix-keep-extension", "", ci.SuffixKeepExtension, "Preserve the extension when using --suffix", "Sync")
   100  	flags.BoolVarP(flagSet, &ci.UseListR, "fast-list", "", ci.UseListR, "Use recursive list if available; uses more memory but fewer transactions", "Listing")
   101  	flags.Float64VarP(flagSet, &ci.TPSLimit, "tpslimit", "", ci.TPSLimit, "Limit HTTP transactions per second to this", "Networking")
   102  	flags.IntVarP(flagSet, &ci.TPSLimitBurst, "tpslimit-burst", "", ci.TPSLimitBurst, "Max burst of transactions for --tpslimit", "Networking")
   103  	flags.StringVarP(flagSet, &bindAddr, "bind", "", "", "Local address to bind to for outgoing connections, IPv4, IPv6 or name", "Networking")
   104  	flags.StringVarP(flagSet, &disableFeatures, "disable", "", "", "Disable a comma separated list of features (use --disable help to see a list)", "Config")
   105  	flags.StringVarP(flagSet, &ci.UserAgent, "user-agent", "", ci.UserAgent, "Set the user-agent to a specified string", "Networking")
   106  	flags.BoolVarP(flagSet, &ci.Immutable, "immutable", "", ci.Immutable, "Do not modify files, fail if existing files have been modified", "Copy")
   107  	flags.BoolVarP(flagSet, &ci.AutoConfirm, "auto-confirm", "", ci.AutoConfirm, "If enabled, do not request console confirmation", "Config")
   108  	flags.IntVarP(flagSet, &ci.StatsFileNameLength, "stats-file-name-length", "", ci.StatsFileNameLength, "Max file name length in stats (0 for no limit)", "Logging")
   109  	flags.FVarP(flagSet, &ci.LogLevel, "log-level", "", "Log level DEBUG|INFO|NOTICE|ERROR", "Logging")
   110  	flags.FVarP(flagSet, &ci.StatsLogLevel, "stats-log-level", "", "Log level to show --stats output DEBUG|INFO|NOTICE|ERROR", "Logging")
   111  	flags.FVarP(flagSet, &ci.BwLimit, "bwlimit", "", "Bandwidth limit in KiB/s, or use suffix B|K|M|G|T|P or a full timetable", "Networking")
   112  	flags.FVarP(flagSet, &ci.BwLimitFile, "bwlimit-file", "", "Bandwidth limit per file in KiB/s, or use suffix B|K|M|G|T|P or a full timetable", "Networking")
   113  	flags.FVarP(flagSet, &ci.BufferSize, "buffer-size", "", "In memory buffer size when reading files for each --transfer", "Performance")
   114  	flags.FVarP(flagSet, &ci.StreamingUploadCutoff, "streaming-upload-cutoff", "", "Cutoff for switching to chunked upload if file size is unknown, upload starts after reaching cutoff or when file ends", "Copy")
   115  	flags.FVarP(flagSet, &ci.Dump, "dump", "", "List of items to dump from: "+fs.DumpFlagsList, "Debugging")
   116  	flags.FVarP(flagSet, &ci.MaxTransfer, "max-transfer", "", "Maximum size of data to transfer", "Copy")
   117  	flags.DurationVarP(flagSet, &ci.MaxDuration, "max-duration", "", 0, "Maximum duration rclone will transfer data for", "Copy")
   118  	flags.FVarP(flagSet, &ci.CutoffMode, "cutoff-mode", "", "Mode to stop transfers when reaching the max transfer limit HARD|SOFT|CAUTIOUS", "Copy")
   119  	flags.IntVarP(flagSet, &ci.MaxBacklog, "max-backlog", "", ci.MaxBacklog, "Maximum number of objects in sync or check backlog", "Copy,Check")
   120  	flags.IntVarP(flagSet, &ci.MaxStatsGroups, "max-stats-groups", "", ci.MaxStatsGroups, "Maximum number of stats groups to keep in memory, on max oldest is discarded", "Logging")
   121  	flags.BoolVarP(flagSet, &ci.StatsOneLine, "stats-one-line", "", ci.StatsOneLine, "Make the stats fit on one line", "Logging")
   122  	flags.BoolVarP(flagSet, &ci.StatsOneLineDate, "stats-one-line-date", "", ci.StatsOneLineDate, "Enable --stats-one-line and add current date/time prefix", "Logging")
   123  	flags.StringVarP(flagSet, &ci.StatsOneLineDateFormat, "stats-one-line-date-format", "", ci.StatsOneLineDateFormat, "Enable --stats-one-line-date and use custom formatted date: Enclose date string in double quotes (\"), see https://golang.org/pkg/time/#Time.Format", "Logging")
   124  	flags.BoolVarP(flagSet, &ci.ErrorOnNoTransfer, "error-on-no-transfer", "", ci.ErrorOnNoTransfer, "Sets exit code 9 if no files are transferred, useful in scripts", "Config")
   125  	flags.BoolVarP(flagSet, &ci.Progress, "progress", "P", ci.Progress, "Show progress during transfer", "Logging")
   126  	flags.BoolVarP(flagSet, &ci.ProgressTerminalTitle, "progress-terminal-title", "", ci.ProgressTerminalTitle, "Show progress on the terminal title (requires -P/--progress)", "Logging")
   127  	flags.BoolVarP(flagSet, &ci.Cookie, "use-cookies", "", ci.Cookie, "Enable session cookiejar", "Networking")
   128  	flags.BoolVarP(flagSet, &ci.UseMmap, "use-mmap", "", ci.UseMmap, "Use mmap allocator (see docs)", "Config")
   129  	flags.StringArrayVarP(flagSet, &ci.CaCert, "ca-cert", "", ci.CaCert, "CA certificate used to verify servers", "Networking")
   130  	flags.StringVarP(flagSet, &ci.ClientCert, "client-cert", "", ci.ClientCert, "Client SSL certificate (PEM) for mutual TLS auth", "Networking")
   131  	flags.StringVarP(flagSet, &ci.ClientKey, "client-key", "", ci.ClientKey, "Client SSL private key (PEM) for mutual TLS auth", "Networking")
   132  	flags.FVarP(flagSet, &ci.MultiThreadCutoff, "multi-thread-cutoff", "", "Use multi-thread downloads for files above this size", "Copy")
   133  	flags.IntVarP(flagSet, &ci.MultiThreadStreams, "multi-thread-streams", "", ci.MultiThreadStreams, "Number of streams to use for multi-thread downloads", "Copy")
   134  	flags.FVarP(flagSet, &ci.MultiThreadWriteBufferSize, "multi-thread-write-buffer-size", "", "In memory buffer size for writing when in multi-thread mode", "Copy")
   135  	flags.FVarP(flagSet, &ci.MultiThreadChunkSize, "multi-thread-chunk-size", "", "Chunk size for multi-thread downloads / uploads, if not set by filesystem", "Copy")
   136  	flags.BoolVarP(flagSet, &ci.UseJSONLog, "use-json-log", "", ci.UseJSONLog, "Use json log format", "Logging")
   137  	flags.StringVarP(flagSet, &ci.OrderBy, "order-by", "", ci.OrderBy, "Instructions on how to order the transfers, e.g. 'size,descending'", "Copy")
   138  	flags.StringArrayVarP(flagSet, &uploadHeaders, "header-upload", "", nil, "Set HTTP header for upload transactions", "Networking")
   139  	flags.StringArrayVarP(flagSet, &downloadHeaders, "header-download", "", nil, "Set HTTP header for download transactions", "Networking")
   140  	flags.StringArrayVarP(flagSet, &headers, "header", "", nil, "Set HTTP header for all transactions", "Networking")
   141  	flags.StringArrayVarP(flagSet, &metadataSet, "metadata-set", "", nil, "Add metadata key=value when uploading", "Metadata")
   142  	flags.BoolVarP(flagSet, &ci.RefreshTimes, "refresh-times", "", ci.RefreshTimes, "Refresh the modtime of remote files", "Copy")
   143  	flags.BoolVarP(flagSet, &ci.NoConsole, "no-console", "", ci.NoConsole, "Hide console window (supported on Windows only)", "Config")
   144  	flags.StringVarP(flagSet, &dscp, "dscp", "", "", "Set DSCP value to connections, value or name, e.g. CS1, LE, DF, AF21", "Networking")
   145  	flags.DurationVarP(flagSet, &ci.FsCacheExpireDuration, "fs-cache-expire-duration", "", ci.FsCacheExpireDuration, "Cache remotes for this long (0 to disable caching)", "Config")
   146  	flags.DurationVarP(flagSet, &ci.FsCacheExpireInterval, "fs-cache-expire-interval", "", ci.FsCacheExpireInterval, "Interval to check for expired remotes", "Config")
   147  	flags.BoolVarP(flagSet, &ci.DisableHTTP2, "disable-http2", "", ci.DisableHTTP2, "Disable HTTP/2 in the global transport", "Networking")
   148  	flags.BoolVarP(flagSet, &ci.HumanReadable, "human-readable", "", ci.HumanReadable, "Print numbers in a human-readable format, sizes with suffix Ki|Mi|Gi|Ti|Pi", "Config")
   149  	flags.DurationVarP(flagSet, &ci.KvLockTime, "kv-lock-time", "", ci.KvLockTime, "Maximum time to keep key-value database locked by process", "Config")
   150  	flags.BoolVarP(flagSet, &ci.DisableHTTPKeepAlives, "disable-http-keep-alives", "", ci.DisableHTTPKeepAlives, "Disable HTTP keep-alives and use each connection once.", "Networking")
   151  	flags.BoolVarP(flagSet, &ci.Metadata, "metadata", "M", ci.Metadata, "If set, preserve metadata when copying objects", "Metadata,Copy")
   152  	flags.BoolVarP(flagSet, &ci.ServerSideAcrossConfigs, "server-side-across-configs", "", ci.ServerSideAcrossConfigs, "Allow server-side operations (e.g. copy) to work across different configs", "Copy")
   153  	flags.FVarP(flagSet, &ci.TerminalColorMode, "color", "", "When to show colors (and other ANSI codes) AUTO|NEVER|ALWAYS", "Config")
   154  	flags.FVarP(flagSet, &ci.DefaultTime, "default-time", "", "Time to show if modtime is unknown for files and directories", "Config,Listing")
   155  	flags.BoolVarP(flagSet, &ci.Inplace, "inplace", "", ci.Inplace, "Download directly to destination file instead of atomic download to temp/rename", "Copy")
   156  	flags.StringVarP(flagSet, &partialSuffix, "partial-suffix", "", ci.PartialSuffix, "Add partial-suffix to temporary file name when --inplace is not used", "Copy")
   157  	flags.FVarP(flagSet, &ci.MetadataMapper, "metadata-mapper", "", "Program to run to transforming metadata before upload", "Metadata")
   158  }
   159  
   160  // ParseHeaders converts the strings passed in via the header flags into HTTPOptions
   161  func ParseHeaders(headers []string) []*fs.HTTPOption {
   162  	opts := []*fs.HTTPOption{}
   163  	for _, header := range headers {
   164  		parts := strings.SplitN(header, ":", 2)
   165  		if len(parts) == 1 {
   166  			log.Fatalf("Failed to parse '%s' as an HTTP header. Expecting a string like: 'Content-Encoding: gzip'", header)
   167  		}
   168  		option := &fs.HTTPOption{
   169  			Key:   strings.TrimSpace(parts[0]),
   170  			Value: strings.TrimSpace(parts[1]),
   171  		}
   172  		opts = append(opts, option)
   173  	}
   174  	return opts
   175  }
   176  
   177  // SetFlags converts any flags into config which weren't straight forward
   178  func SetFlags(ci *fs.ConfigInfo) {
   179  	if dumpHeaders {
   180  		ci.Dump |= fs.DumpHeaders
   181  		fs.Logf(nil, "--dump-headers is obsolete - please use --dump headers instead")
   182  	}
   183  	if dumpBodies {
   184  		ci.Dump |= fs.DumpBodies
   185  		fs.Logf(nil, "--dump-bodies is obsolete - please use --dump bodies instead")
   186  	}
   187  	if ci.Dump != 0 && verbose < 2 && ci.LogLevel != fs.LogLevelDebug {
   188  		fs.Logf(nil, "Automatically setting -vv as --dump is enabled")
   189  		verbose = 2
   190  	}
   191  
   192  	if verbose >= 2 {
   193  		ci.LogLevel = fs.LogLevelDebug
   194  	} else if verbose >= 1 {
   195  		ci.LogLevel = fs.LogLevelInfo
   196  	}
   197  	if (ci.DryRun || ci.Interactive) && ci.StatsLogLevel > fs.LogLevelNotice {
   198  		ci.StatsLogLevel = fs.LogLevelNotice
   199  	}
   200  	if quiet {
   201  		if verbose > 0 {
   202  			log.Fatalf("Can't set -v and -q")
   203  		}
   204  		ci.LogLevel = fs.LogLevelError
   205  	}
   206  	logLevelFlag := pflag.Lookup("log-level")
   207  	if logLevelFlag != nil && logLevelFlag.Changed {
   208  		if verbose > 0 {
   209  			log.Fatalf("Can't set -v and --log-level")
   210  		}
   211  		if quiet {
   212  			log.Fatalf("Can't set -q and --log-level")
   213  		}
   214  	}
   215  	if ci.UseJSONLog {
   216  		logrus.AddHook(fsLog.NewCallerHook())
   217  		logrus.SetFormatter(&logrus.JSONFormatter{
   218  			TimestampFormat: "2006-01-02T15:04:05.999999-07:00",
   219  		})
   220  		logrus.SetLevel(logrus.DebugLevel)
   221  		switch ci.LogLevel {
   222  		case fs.LogLevelEmergency, fs.LogLevelAlert:
   223  			logrus.SetLevel(logrus.PanicLevel)
   224  		case fs.LogLevelCritical:
   225  			logrus.SetLevel(logrus.FatalLevel)
   226  		case fs.LogLevelError:
   227  			logrus.SetLevel(logrus.ErrorLevel)
   228  		case fs.LogLevelWarning, fs.LogLevelNotice:
   229  			logrus.SetLevel(logrus.WarnLevel)
   230  		case fs.LogLevelInfo:
   231  			logrus.SetLevel(logrus.InfoLevel)
   232  		case fs.LogLevelDebug:
   233  			logrus.SetLevel(logrus.DebugLevel)
   234  		}
   235  	}
   236  
   237  	switch {
   238  	case deleteBefore && (deleteDuring || deleteAfter),
   239  		deleteDuring && deleteAfter:
   240  		log.Fatalf(`Only one of --delete-before, --delete-during or --delete-after can be used.`)
   241  	case deleteBefore:
   242  		ci.DeleteMode = fs.DeleteModeBefore
   243  	case deleteDuring:
   244  		ci.DeleteMode = fs.DeleteModeDuring
   245  	case deleteAfter:
   246  		ci.DeleteMode = fs.DeleteModeAfter
   247  	default:
   248  		ci.DeleteMode = fs.DeleteModeDefault
   249  	}
   250  
   251  	if len(ci.CompareDest) > 0 && len(ci.CopyDest) > 0 {
   252  		log.Fatalf(`Can't use --compare-dest with --copy-dest.`)
   253  	}
   254  
   255  	switch {
   256  	case len(ci.StatsOneLineDateFormat) > 0:
   257  		ci.StatsOneLineDate = true
   258  		ci.StatsOneLine = true
   259  	case ci.StatsOneLineDate:
   260  		ci.StatsOneLineDateFormat = "2006/01/02 15:04:05 - "
   261  		ci.StatsOneLine = true
   262  	}
   263  
   264  	if bindAddr != "" {
   265  		addrs, err := net.LookupIP(bindAddr)
   266  		if err != nil {
   267  			log.Fatalf("--bind: Failed to parse %q as IP address: %v", bindAddr, err)
   268  		}
   269  		if len(addrs) != 1 {
   270  			log.Fatalf("--bind: Expecting 1 IP address for %q but got %d", bindAddr, len(addrs))
   271  		}
   272  		ci.BindAddr = addrs[0]
   273  	}
   274  
   275  	if disableFeatures != "" {
   276  		if disableFeatures == "help" {
   277  			log.Fatalf("Possible backend features are: %s\n", strings.Join(new(fs.Features).List(), ", "))
   278  		}
   279  		ci.DisableFeatures = strings.Split(disableFeatures, ",")
   280  	}
   281  
   282  	if len(uploadHeaders) != 0 {
   283  		ci.UploadHeaders = ParseHeaders(uploadHeaders)
   284  	}
   285  	if len(downloadHeaders) != 0 {
   286  		ci.DownloadHeaders = ParseHeaders(downloadHeaders)
   287  	}
   288  	if len(headers) != 0 {
   289  		ci.Headers = ParseHeaders(headers)
   290  	}
   291  	if len(headers) != 0 {
   292  		ci.Headers = ParseHeaders(headers)
   293  	}
   294  	if len(metadataSet) != 0 {
   295  		ci.MetadataSet = make(fs.Metadata, len(metadataSet))
   296  		for _, kv := range metadataSet {
   297  			equal := strings.IndexRune(kv, '=')
   298  			if equal < 0 {
   299  				log.Fatalf("Failed to parse '%s' as metadata key=value.", kv)
   300  			}
   301  			ci.MetadataSet[strings.ToLower(kv[:equal])] = kv[equal+1:]
   302  		}
   303  		fs.Debugf(nil, "MetadataUpload %v", ci.MetadataSet)
   304  	}
   305  	if len(dscp) != 0 {
   306  		if value, ok := parseDSCP(dscp); ok {
   307  			ci.TrafficClass = value << 2
   308  		} else {
   309  			log.Fatalf("--dscp: Invalid DSCP name: %v", dscp)
   310  		}
   311  	}
   312  
   313  	// Set path to configuration file
   314  	if err := config.SetConfigPath(configPath); err != nil {
   315  		log.Fatalf("--config: Failed to set %q as config path: %v", configPath, err)
   316  	}
   317  
   318  	// Set path to cache dir
   319  	if err := config.SetCacheDir(cacheDir); err != nil {
   320  		log.Fatalf("--cache-dir: Failed to set %q as cache dir: %v", cacheDir, err)
   321  	}
   322  
   323  	// Set path to temp dir
   324  	if err := config.SetTempDir(tempDir); err != nil {
   325  		log.Fatalf("--temp-dir: Failed to set %q as temp dir: %v", tempDir, err)
   326  	}
   327  
   328  	// Set whether multi-thread-streams was set
   329  	multiThreadStreamsFlag := pflag.Lookup("multi-thread-streams")
   330  	ci.MultiThreadSet = multiThreadStreamsFlag != nil && multiThreadStreamsFlag.Changed
   331  
   332  	if len(partialSuffix) > 16 {
   333  		log.Fatalf("--partial-suffix: Expecting suffix length not greater than %d but got %d", 16, len(partialSuffix))
   334  	}
   335  	ci.PartialSuffix = partialSuffix
   336  
   337  	// Make sure some values are > 0
   338  	nonZero := func(pi *int) {
   339  		if *pi <= 0 {
   340  			*pi = 1
   341  		}
   342  	}
   343  	nonZero(&ci.LowLevelRetries)
   344  	nonZero(&ci.Transfers)
   345  	nonZero(&ci.Checkers)
   346  }
   347  
   348  // parseHeaders converts DSCP names to value
   349  func parseDSCP(dscp string) (uint8, bool) {
   350  	if s, err := strconv.ParseUint(dscp, 10, 6); err == nil {
   351  		return uint8(s), true
   352  	}
   353  	dscp = strings.ToUpper(dscp)
   354  	switch dscp {
   355  	case "BE":
   356  		fallthrough
   357  	case "DF":
   358  		fallthrough
   359  	case "CS0":
   360  		return 0x00, true
   361  	case "CS1":
   362  		return 0x08, true
   363  	case "AF11":
   364  		return 0x0A, true
   365  	case "AF12":
   366  		return 0x0C, true
   367  	case "AF13":
   368  		return 0x0E, true
   369  	case "CS2":
   370  		return 0x10, true
   371  	case "AF21":
   372  		return 0x12, true
   373  	case "AF22":
   374  		return 0x14, true
   375  	case "AF23":
   376  		return 0x16, true
   377  	case "CS3":
   378  		return 0x18, true
   379  	case "AF31":
   380  		return 0x1A, true
   381  	case "AF32":
   382  		return 0x1C, true
   383  	case "AF33":
   384  		return 0x1E, true
   385  	case "CS4":
   386  		return 0x20, true
   387  	case "AF41":
   388  		return 0x22, true
   389  	case "AF42":
   390  		return 0x24, true
   391  	case "AF43":
   392  		return 0x26, true
   393  	case "CS5":
   394  		return 0x28, true
   395  	case "EF":
   396  		return 0x2E, true
   397  	case "CS6":
   398  		return 0x30, true
   399  	case "LE":
   400  		return 0x01, true
   401  	default:
   402  		return 0, false
   403  	}
   404  }